【进阶 11 期】 webpack 系列之入门

648 阅读6分钟

前言

看了很多webpack相关文章,一直读不进去(或者记不住),无非将webpack官文档的配置说明,重新梳理下顺序或者翻译不好部分的再重新处理一下。很少以项目角度去讨论一个webpack工程项目如何从0到1搭建,搭建完成之后随着项目的复杂度提高又是如何一步步优化,达到最佳实践过程。

本系列是基于以下webpack版本来展开的讨论的

    "webpack": "^4.46.0",
    "webpack-dev-server": "^3.11.2"

【一】 项目初始化

新建一个文件夹webpack-demo,执行下面命令。

npm init -y
npm install webpack webpack-cli -D

【二】 配置打包的输入(entry)与输出(output)

src/index.js路径下写点代码,配置webpack.config.js

module.exports = {
    entry:'./src/index.js', // 输入输出
    output: {
        path: path.resolve(__dirname, "./dist"), // 打包后的目录
        filename:"bundle.js"
    }
}  

【三】 配置构建命令

1、 在 package.json scripts 添加构建打包命令

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

2、验证: 终端执行打包命令npm run build 将会在dist目录下生成一个bundle.js文件。

【四】自动引入打包生成的文件bundle.js

  1. 安装依赖: html-webpack-pluginclean-webpack-plugin
npm i -D html-webpack-plugin clean-webpack-plugin
  1. 配置 webpack.config.js,以插件形式引入
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
    // ... 省略其它配置项目
    plugins:[
        new CleanWebpackPlugin({ // 每次打包前清空打包输出目录
            // cleanOnceBeforeBuildPatterns:['**/*', '!dll', '!dll/**'] // 不删除dll目录下的文件
        }), // 清空打包输出目录
        new HtmlWebpackPlugin({
            template: './index.html' // 源模板文件
            // filename: './index.html' // 指定输出文件
        })
    ]
}
  1. 验证: 终端执行命令npm run build将打包dist生成文件除了bundle.js,还有index.html 。这样打包之后bundle.js自动注入到index.html

【五】搭建本地服务器

  1. 安装依赖 webpack-dev-server
npm i -D webpack-dev-server

2、配合服务启动目录、端口

module.exports = {
    // ... 省略其它配置项目
  devServer: {
    port: 3001,
    hot: true,
    contentBase: "./dist",
  }
}
  1. 在 package.json scripts 配置本地服务启动命令
   scripts:{
    "serve": "webpack-dev-server --config webpack.config.js --open"
   }

4、验证: 在终端执行npm run serve,会自动开口浏览器启动本地服务

【六】 配置解析和转换文件的策略 module

常见loader

loder功能
babel-loader解析 .js 和 .jsx 文件
tsx-loader处理 ts 文件
less-loader处理 less 文件,并将其编译为 css
sass-loader处理 sass、scss 文件,并将其编译为 css
css-loader处理 css 文件,返回一个数组,包含文件名和css样式内容,通常与style-loader一起使用
style-loader将 css 注入到 DOM(通过js创建一个style)
file-loader将文件上的import/require 解析为 url,并将该文件输出到输出目录中
url-loader用于将文件转换成 base64 uri 的 webpack 加载程序
html-loader将 HTML 导出为字符串, 当编译器要求时,将 HTML 最小化

6.1 处理js(引入babel-loader)

由于不同浏览器对高级语法的支持性并不是非常好,为了向下兼容考虑将他们转换为ES5标准

  1. 安装依赖 babel-loader()、@babel/core@babel/preset-env
npm install -D babel-loader @babel/core @babel/preset-env
  1. 配置webpack.config.js
module.exports={
    module:{
        rules:[
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/ // 
            }
        ]
    }
}
  1. 根目录新建.babelrc
{
  "presets": [
    "@babel/preset-env" // 官方推荐使用,包含了所有现代js(es2015 es2016等)的所有新特性
  ]
}
  1. 如果你希望 加入懒加载
npm i -D @babel/plugin-syntax-dynamic-import

同时更新 .babelrc 文件,加入plugins配置

{
  "presets": [
    "@babel/preset-env" 
  ],
  "plugins": [
    "syntax-dynamic-import"
  ]
}
  1. 验证:在index.js 加入箭头函数,执行构建命令查看bundle.js是否将ES6箭头函数转换为普通函数
// Babel Input: ES2015 arrow function
[1, 2, 3].map((n) => n + 1);

// Babel Output: ES5 equivalent
[1, 2, 3].map(function(n) {
  return n + 1;
});

6.2 处理css(scss less),这里以scss举例

  1. 为支持css import,安装依赖style-loader css-loader
npm i -D style-loader css-loader
  1. 如果我们使用sass来构建样式,则需要多安装两个node-sass sass-loader
npm i -D node-sass sass-loader

3、为css增加浏览器前缀

npm i -D postcss-loader autoprefixer  

3、配置 webpack.config.js,通过loader形式让webpack 知道以何种loader加载对应后缀名(.cssscss)的文件,从而打包进bundle.js

module.exports = {
    // ... 省略其它配置项目
   module:{
        rules:[
            {
              test:/\.css$/,
              use:['style-loader','css-loader'] // 从右向左解析原则
            },
            {
              test:/\.scss$/,
              use:['style-loader','css-loader','postcss-loader','sass-loader'] // 从右向左解析原则
            }
          ]

    }
}

4、验证: 新建一个.sccs文件,写一些sass 语法样式,以及css3语法,查看实际输出结果

// 变量定义
$color: blue;

body {
    /*嵌套*/
  .test {
    height: 100px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: $color;
    color: #fff;
  }
}

6.3 处理图片/字体等多媒体文件

  1. 为支持import 字体、图片等其它多媒体格式文件,安装依赖url-loader file-loader
npm i -D url-loader file-loader
  1. 配置 webpack.config.js,通过loader形式让webpack知道以何种loader加载这些多媒体格式文件的 注意:url-loader 一般与file-loader搭配使用,功能与file-loader类似,如果文件小于限制的大小。则会返回base64编码,否则使用 file-loader 将文件移动到输出的目录中
module.exports = {
    // ... 省略其它配置
    module:{
        rules:{
              {
                test: /\.(jpe?g|png|gif)$/i, //图片文件
                use: [
                  {
                    loader: 'url-loader',
                    options: {
                      limit: 10240,
                      fallback: {
                        loader: 'file-loader',
                        options: {
                            name: 'img/[name].[hash:8].[ext]'
                        }
                      }
                    }
                  }
                ]
              },
              {
                test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
                use: [
                  {
                    loader: 'url-loader',
                    options: {
                      limit: 10240,
                      fallback: {
                        loader: 'file-loader',
                        options: {
                          name: 'media/[name].[hash:8].[ext]'
                        }
                      }
                    }
                  }
                ]
              },
              {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
                use: [
                  {
                    loader: 'url-loader',
                    options: {
                      limit: 10240,
                      fallback: {
                        loader: 'file-loader',
                        options: {
                          name: 'fonts/[name].[hash:8].[ext]'
                        }
                      }
                    }
                  }
                ]
              },
            ]
        }
    }
}

【七】】其它

7.1 mode

mode 配置选项告用于告诉webpack相应地使用其内置的优化。可选配置有 developmentproductionnone,具体可查看官方文档

7.2 devtool

sourcemap 是生成的代码和源码之间的映射关系,通过它就能映射到源码。在了解Source Map用处之后,你就会容易理解通过devtool的配置 webpack 如何生成 Source Map,用来增强调试过程。不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。

  • 生产环境(mode=production):默认为 null ,一般不设置( none )或 nosources-source-map
  • 开发环境(mode=development):默认为 eval ,一般设置为 eval、cheap-eval-source-map 、cheap-module-eval-source-map

sourcemap 生成几个策略:

原理优缺点
eval通过eval包裹每一个module模块,编译后不会生成sourcemap文件,仅仅是在每一个模块后,增加sourceURL来关联模块处理前后对应的关系优点是:打包速度非常快,因为不需要生成sourcemap文件。缺点是:由于会映射到转换后的代码,而不是映射到原始代码,所以不能正确的显示行数。
source-map为每一个打包后的模块生成独立的sourcemap文件,打包后的代码最后面一句代码是 // #sourceMappingURL=bundle.js.map ,同时在dist目录下会针对每一个模块生成响应的 .map文件拥有完整的源代码信息,便于调试,但影响持续构建
inline不会生成独立的 .map文件,而是将.map文件以dataURL的形式插入缺点:打包输出文件变大
cheap打包后同样会为每一个文件模块生成 .map文件,但是与source-map的区别在于cheap-source-map生成的 map文件会忽略原始代码中的列信息可以大幅提高souremap生成的效率,但没有列信息(会映射到转换后的代码,而不是映射到原始代码),通常我们调试并不关心列信息
module同样生成一个没有列的信息的sourcemap文件,同时loader的sourcemap也被简化成为只包含对应行支持babel这种预编译工具,找到最初源码信息

如何根据不同环境选择相应sourceMap 生成策略?

    1. 源代码中的列信息是没有任何作用,因此我们打包后的文件不希望包含列相关信息,只有行信息能建立打包前后的依赖关系。因此不管是开发环境或生产环境,我们都希望添加cheap的基本类型来忽略打包前后的列信息。
    1. 不管是开发环境还是正式环境,我们都希望能定位到bug的源代码具体的位置,比如说某个vue文件报错了,我们希望能定位到具体的vue文件,因此我们也需要module配置。
    1. 我们需要生成map文件的形式,因此我们需要增加source-map属性。
    1. 我们介绍了eval打包代码的时候,知道eval打包后的速度非常快,因为它不生成map文件,但是可以对eval组合使用eval-source-map使用会将map文件以DataURL的形式存在打包后的js文件中

因此建议:

// 在开发环境中我们可以使用 , eval加快打包效率
module.exports = {
  devtool: 'cheap-module-eval-source-map'
}

// 在正式环境中我们可以使用  
module.exports = { 
  devtool: 'cheap-module-source-map';
}