如果想要成为一名优秀的前端开发人员,除了对基础编程语言的掌握以外。项目工程化也是需要了解和掌握的,目前大部分开发人员使用webpack作为项目构建工具。而我对于webpack的了解不是很多。正好趁这段时间学习一下webpack的基础及配置。

webpack核心概念

webpack作为一个现代 JavaScript 应用程序的静态模块打包器有以下几个核心概念:

  • 入口(entry)
  • 输出(output)
  • loader
  • 插件(plugins)

入口(entry)

入口(entry)一般是webpack指定哪个模块(文件)作为构建内部依赖的开始。从入口文件开始,webpack会找出哪些模块和库是入口文件依赖的,每个依赖被处理最后将输出到 bundles 的文件中。

// webpack.config.js

module.exports = {
    entry: './path/to/my/entry/file.js'
};

出口(output)

出口(output)是作为webpack通过编译输出的目录名称,主要是用来接收处理后文件保存的位置,默认值为’./dist’。


const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};

loader

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块。


const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  }
};

以上配置对一个module定义了rules属性,里面包含两个必须属性:test和use。这相当于告诉webpack:

“嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 ‘.txt’ 的路径」时,在你对它打包之前,先使用 raw-loader 转换一下。”

插件(plugin)

上面讲到loader被用于转换某些类型的模块(如转换.vue、.jpeg、.jsx)等。而插件(plugin)可以处理更广的任务,从打包优化和压缩,一直到重新定义环境中的变量。

插件使用的方法一般是通过require引入,然后将其添加到plugin数组中。


const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

const config = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
}

从0开始我们的配置

1.1 初始化项目

新建一个目录,初始化npm

    npm i

安装webpack包

    npm i -D webpack webpack-cli

新建一个 src 文件,然后创建一个main.js,然后写点测试代码

   console.log('hello world');

配置package.json命令


  "scripts": {
    "build":"webpack src/main.js"
  }

如果生成了dist目录,就说明内部含有main.js文件打包成功了

1.2 开始自己的配置

以上只是webpack默认配置,如果想要实现更灵活丰富的配置还需要自己动手进行自定义配置。首先新建build文件夹,里面新建一个webpack.config.js文件。


//webpack.config.js

const path = require('path');

module.exports = {
    mode:'development', // 开发模式
    entry: path.resolve(__dirname,'../src/main.js'),    // 入口文件
    output: {
        filename: 'output.js',      // 打包后的文件名称
        path: path.resolve(__dirname,'../dist')  // 打包后的目录
    }
}

然后更改下打包命令


 "scripts": {
    "build":"webpack --config build/webpack.config.js"
  },

执行 npm run build 就在dist目录下生成了 output.js 文件,这个文件就是我们需要在浏览器中实际运行的文件。

1.3配置HTML模板

以上JS打包好后,需要引入到HTML文件中,但是不可能每次都要手动引入。且为了考虑服务器缓存优化,每次更新后的js文件名称需要动态改变。基于这两点,我们开始自定义配置:

为了服务器缓存,首先改变每次输出js文件名称


module.exports = {
    output: {
        filename: '[name].[hash:8].js', //打包后生产唯一8位的hash值
        path:path.resolve(__dirname,'../dist') //打包后的目录
    }
}  

接下来新建一个public文件夹,里面新建index.html文件,然后需要借助一个插件:

  npm i -D html-webpack-plugin

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    mode: 'development',
    entry: path.resolve(__dirname, '../src/main.js'), //入口文件
    output: {
        filename: '[name].[hash:8].js', //打包后的文件名称
        path:path.resolve(__dirname,'../dist') //打包后的文件夹
    },
    plugins: [
        new HtmlWebpackPlugin(
            {
                template:path.resolve(__dirname,'../public/index.html') //HTML模板文件
            }
        )
    ]
 }

此时执行 npm run build 会发现 dist 目录下会出现index.html,并且html文件动态引入最新生成的js文件。

1.3.1 clean-webpack-plugin

由于我们每次执行npm run build 会发现dist文件夹里会残留上次打包的文件,这里推荐一个plugin来帮我们在打包输出前清空文件夹clean-webpack-plugin


const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
    // ...省略其他配置
    plugins:[new CleanWebpackPlugin()]
}

1.4引用CSS

首先在我们的入口文件 main.js 中引入css文件,引入css文件后需要配置css-loader来解析


//main.js
import './assets/index.css'
import './assets/test.css'

  npm i -D style-loader css-loader

如果是less来构建则多需要安装两个loader


  npm i -D less less-loader

配置文件如下:


// webpack.config.js
module.exports = {
    // ...省略其他配置
    module:{
      rules:[
        {
          test:/\.css$/,
          use:['style-loader','css-loader'] // 从右向左解析原则
        },
        {
          test:/\.less$/,
          use:['style-loader','css-loader','less-loader'] // 从右向左解析原则
        }
      ]
    }
}

执行命令后会重新打包,打开HTML后会发现css已经起作用了,但是打开控制台发现样式是嵌入式。

为了解决以上问题,我们可以使用官方推荐的 ExtractTextWebpackPlugin 插件来解决这个问题


const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
module.exports = {
    mode: 'development',
    entry: path.resolve(__dirname, '../src/main.js'),
    output: {
        filename: '[name].[hash:8].js',
        path: path.resolve(__dirname, '../dist')
    },
    plugins: [
        new HtmlWebpackPlugin(
            {
                template: path.resolve(__dirname, '../public/index.html')
            }
        ),
        new CleanWebpackPlugin(),
        new ExtractTextWebpackPlugin({
            filename:'css/[name][hash:8].css'
        }),
    ],
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ExtractTextWebpackPlugin.extract({
                    use: ['css-loader']
                })
            },
        ]
    }
}

1.5加载图片等媒体文件

file-loaderurl-loader 可以接收并加载任何文件,然后将其输出到构建目录。url-loader 一般与 file-loader 搭配使用,功能与 file-loader 类似,不过 url-loader 可以配置存储文件上限,如果超出配置的大小则会返回base编码。


module.exports = {
  //...省略其他配置
   {
      test: /\.(jpe?g|png|gif)$/i, //T图片文件
      use: [
          {
            loader: 'url-loader',
            options: {
                limit: 10240,
                fallback: {
                    loader: 'file-loader',
                    options: {
                        name: '[name].[hash:8].[ext]',
                        outputPath: 'images/' //打包后的文件目录
                    }
                }
            }
          }
      ]
  },
  {
      test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
      use: [
          {
            loader: 'url-loader',
            options: {
                limit: 10240,
                fallback: {
                    loader: 'file-loader',
                    options: {
                        name: '[name].[hash:8].[ext]',
                        outputPath: 'media/' //打包后的文件目录
                    }
                }
            }
          }
      ]
  }
}

1.6css引入图片资源导致的Bug

在我按照以上配置完成执行 npm run build 成功执行编译,但是执行打包后的HTML文件却不能正常显示背景图片,具体原因通过查看编译后的文件发现,造成bug的原因是css背景图片路径出现了问题。

webpack在打包后,css引入的资源路径是按照绝对路径引入的。这就当前css文件夹下的css文件引入资源失效。

解决css引入资源失效问题也非常好解决,就是在 css-loader 配置里面添加一个配置项:


module.exports = {
  //...省略其他配置
  module: {
    rules: [
        {
          test: /\.css$/,
          use: ExtractTextWebpackPlugin.extract({
              use: ['css-loader'],
              publicPath: '../'  //publicPath会输出解析文件目录
          })
        },
    ]
  }
}

1.7用babel转义js文件

首先安装babel-loader


  npm i -D babel-loader @babel/preset-env @babel/core

具体配置如下


module.exports = {
  //...省略其他配置

  module: {
     {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env']
            }
        }
     }
  }
}

1.8构建Vue开发环境


  npm i -D vue-loader vue-template-compiler vue-style-loader
  npm i -S vue

vue-loader 用于解析 .vue 后缀文件
vue-template-compiler 用于编译模板


  const vueLoaderPlugin = require('vue-loader/lib/plugin')
  module.exports = {
      module:{
          rules:[{
              test:/\.vue$/,
              use:['vue-loader']
          },]
      },
      resolve:{
          alias:{
            'vue$':'vue/dist/vue.runtime.esm.js',
            ' @':path.resolve(__dirname,'../src')
          },
          extensions:['*','.js','.json','.vue']
    },
    plugins:[
          new vueLoaderPlugin()
    ]
  }

按照以上配置完成后,接下来在 main.js 文件中导入Vue:


import Vue from 'vue'
import App from './app'

new Vue({
    render: h => h(App)
}).$mount('#app')

public文件夹下面的index.html


 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>ExampleWebpack</title>
 </head>
 <body>
     <div id="app"></div>
 </body>
 </html>

然后再Src文件夹下新建App.vue


  <template>
      <div class="div">{{msg}}</div>
  </template>

  <script>
  export default {
      data(){
          return {
              msg:'hello Vue'
          }
      }
  };
  </script>

  <style>
    .div{
        color:red;
        display: flex;
        font-size: 24px;
    }
  </style>

以上已完成了Vue项目的搭建,为了让项目更接近开发模式,引入 webpack-dev-server 热更新来辅助我们。

1.8.1 引入webpack-dev-server

  npm i -D webpack-dev-server

  const Webpack = require('webpack')
  module.exports = {
    // ...省略其他配置
    devServer:{
      port:3000,
      hot:true,
      contentBase:'../dist'
    },
    plugins:[
      new Webpack.HotModuleReplacementPlugin()
    ]
  }

1.9配置打包命令


  "scripts": {
    "dev": "webpack-dev-server --config build/webpack.config.js --open",
    "build": "webpack --config build/webpack.config.js",
  }

执行 npm run dev 之后浏览器将会在本地开启服务

webpack优化….