• entry
    • 如果是单页面应用,那么入口只有一个;如果是多个页面的项目,那么通常是一个页面会对应一个构建入口 ```typescript module.exports = { entry: ‘./src/index.js’ }

上述配置等同于

module.exports = { entry: { main: ‘./src/index.js’ } }

  1. - **output**
  2. - webpack 的输出即指 webpack 最终构建出来的静态文件,webpack 构建生成的文件名、路径等都是可以配置的,在配置文件中使用 output 字段来进行设置
  3. ```typescript
  4. module.exports = {
  5. // ...
  6. output: {
  7. path: path.resolve(__dirname, 'dist'),
  8. filename: 'bundle.js',
  9. },
  10. }
  11. # 或者使用 entry 的名称
  12. module.exports = {
  13. entry: {
  14. main: './src/index.js' // main 为 entry 的名称
  15. },
  16. output: {
  17. // 使用 [name] 来引用 entry 名称,在这里即为 main
  18. filename: '[name].js',
  19. // 输出文件目录(将来所有资源输出的公共目录)
  20. path: path.join(__dirname, 'dist'),
  21. // 所有资源引入公共路径前缀 'imgs/a.png' ——> '/imgs/a.png'
  22. publicPath: '/',
  23. // 非入口chunk名称
  24. chunkFilename: 'js/[name]_chunk.js',
  25. // 整个库向外暴露的变量名
  26. library: '[name]',
  27. // 变量名添加到哪个上
  28. libraryTarget: 'window|global|commonjs'
  29. },
  30. }
  • resolve

    // 解析模块的规则
    resolve: {
    // 配置解析路径的别名
      alias: {
          $css: resolve(__dirname, 'src/css')
      },
      // 配置省略文件路径的后缀名, 如果名字相同,则取第一个
      extensions: ['.js', '.json', '.jsx', '.css']
    }
    
  • loader

    • 在前端构建中会遇见需要使用各式各样的文件,例如 css 代码,图片,模板代码等。webpack 中提供一种处理多种文件格式的机制,便是使用 loader。可以把 loader 理解为是一个转换器,负责把某种文件格式的内容转换成 webpack 可以支持打包的模块
    • 在 module.rules 字段下来配置相关的规则,例如使用 Babel 来处理 .js 文件
    • babel-loader 可以使用 babel 来将 ES6 代码转译为浏览器可以执行的的 ES5 代码
      module: {
      // ...
      rules: [
      {
       test: /\.jsx?/, // 匹配文件路径的正则表达式,通常我们都是匹配文件类型后缀
       include: [
         path.resolve(__dirname, 'src') // 指定哪些路径下的文件需要经过 loader 处理
       ],
       use: { // 指定使用的 loader
         loader: 'babel-loader', // babel-loader 可以使用 babel 来将 ES6 代码转译为浏览器可以执行的的 ES5 代码
         options: {
           presets: ['@babel/preset-env'],
         },
       },
      },
      ],
      }
      
  • plugin

    • 添加需要的 plugin,可以满足更多构建中特殊的需求
    • 可以贯穿 Webpack 打包的生命周期,执行不同的任务 ```typescript npm install copy-webpack-plugin -D

      插件通常为第三方的 npm package,都需要安装后才能使用

      const CopyPlugin = require(‘copy-webpack-plugin’);

module.exports = { // …

plugins: [ new CopyPlugin([ { from: ‘src/public’, to: ‘public’ }, ]), ], };


- **区分环境**
   - mode(webpack v4 引入的新概念,用于方便快捷地指定一些常用的默认优化配置)
   - 取值:development、production、none 
   - 本地环境:
      - 需要更快的构建速度
      - 需要打印 debug 信息
      - 需要 live reload 或 hot reload 功能
      - 需要 sourcemap 方便定位问题
   - 生产环境:        
      - 需要更小的包体积,代码压缩+tree-shaking
      - 需要进行代码分割
      - 需要压缩图片体积
```javascript
# 安装
npm install cross-env -D

# ./package.json配置
"scripts": {
  "dev": "cross-env NODE_ENV=dev webpack serve --mode development", 
  "test": "cross-env NODE_ENV=test webpack --mode production",
  "build": "cross-env NODE_ENV=prod webpack --mode production"
}
  • devServer ```javascript

    安装

    npm intall webpack-dev-server -D

配置本地服务 webpack.config.js

const config = { devServer: { contentBase: path.resolve(__dirname, ‘public’), // 静态文件目录 compress: true, //是否启动压缩 gzip port: 5000, // 端口号 open:true, // 是否自动打开浏览器 hot:true, // 开启HMR clientLog: ‘none’, // 不显示启动服务器日志信息 quiet: true, // 除了一些基本启动信息以外,其他内容不显示 overlay: false, // 如果出错,不要全屏提示 // 服务器代理,解决开发环境跨域问题 proxy: { // 一旦devServer(5000)服务器接收到以/api/xx开头的请求,就会把请求发送到另外一个服务器3000 ‘/api’: { target: ‘http:localhost:3000’, // 发送请求,路径重写 pathRewrite: { ‘^/api’:’’ } } } } } module.exports = (env, argv) => { console.log(‘argv.mode=’,argv.mode) // 打印 mode(模式) 值 // 这里可以通过不同的模式修改 config 配置 return config }


- **optimization**
```javascript
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all'
    },
    // 记录其他模块的hash打包成一个文件runtime
    // 解决: 修改a文件导致b文件的contentHash发生变化
    runtimeChunk: {
      name: entrypoint => `runtime-${entrypoint.name}`
    },
    minimizer: {
      // 配置生产环境的压缩方案:压缩js和css
      new TerserPlugin({
        cache: true, // 开启缓存
        parallel: true, // 开启多进程打包
        sourceMap: true, // 启动source-map
      })
    }
  }
}
  • 引入CSS
    • style-loader 就是将处理好的 css 通过 style 标签的形式添加到页面上(核心逻辑是通过动态添加 style 标签的方式,将样式引入页面)
    • Loader 的执行顺序是固定从后往前,即按 css-loader —> style-loader 的顺序执行 ```javascript npm install style-loader -D

rules: [ { test: /.css$/, //匹配所有的 css 文件 use: [‘style-loader’,’css-loader’] } ]


- **CSS兼容性**
   -  [postcss-loader](https://link.juejin.cn/?target=https%3A%2F%2Fwebpack.docschina.org%2Floaders%2Fpostcss-loader%2F)自动添加 CSS3 部分属性的浏览器前缀
   - 引入Less或Sass
```javascript
# 安装
npm install postcss postcss-loader postcss-preset-env -D
npm install sass-loader -D
# 淘宝镜像
npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

# 添加
rules: [
  {
    test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
    use: [
      'style-loader',
      'css-loader', 
      'postcss-loader',
      'sass-loader'
    ]
  }
]
# 创建 postcss 配置文件 postcss.config.js
// postcss.config.js
module.exports = {
  plugins: [require('postcss-preset-env')]
}
# 创建 postcss-preset-env 配置文件 .browserslistrc
# 换行相当于 and
last 2 versions # 回退两个浏览器版本
> 0.5% # 全球超过0.5%人使用的浏览器,可以通过 caniuse.com 查看不同浏览器不同版本占有率
IE 10 # 兼容IE 10
  • 分离样式文件
    • 通过 CSS 文件的形式引入到页面上,即link方式引入 ```javascript $ npm install mini-css-extract-plugin -D // 引入插件 const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’)

const config = { module: { rules: [ { test: /.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件 use: [ // ‘style-loader’, MiniCssExtractPlugin.loader, // 添加 loader ‘css-loader’, ‘postcss-loader’, ‘sass-loader’, ] } ] }, plugins:[ // 配置插件 new MiniCssExtractPlugin({ // 添加插件 filename: ‘[name].[hash:8].css’ }) ] }


- **图片和字体文件**
| Loader | 说明 |
| --- | --- |
| file-loader | 解决图片引入问题,并将图片 copy 到指定目录,默认为 dist |
| url-loader | 解依赖 file-loader,当图片小于 limit 值的时候,会将图片转为 base64 编码,大于 limit 值的时候依然是使用 file-loader 进行拷贝 |
| img-loader | 压缩图片 |

```javascript
npm install file-loader -D
npm install url-loader -D

const config = {
  module: { 
    rules: [
      {
        test: /\.(jpe?g|png|gif)$/i,
        use:[
          {
            loader: 'file-loader',
            options: {
              name: '[name][hash:8].[ext]'
            }
          }
        ]
      },
      {
        loader: 'file-loader',
        options: {
          name: '[name][hash:8].[ext]'
        }
      },
      {
        test: /\.(jpe?g|png|gif)$/i,
        use:[
          {
            loader: 'url-loader',
            options: {
              name: '[name][hash:8].[ext]',
              // 文件小于 50k 会转换为 base64,大于则拷贝文件
              limit: 50 * 1024
            }
          }
        ]
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,  // 匹配字体文件
        use: [
          {
            loader: 'url-loader',
            options: {
              name: 'fonts/[name][hash:8].[ext]', // 体积大于 10KB 打包到 fonts 目录下 
              limit: 10 * 1024
            } 
          }
        ]
      }
    ]
  }
}
  • JS兼容性
    • babel-loader 将 ES6 语法转化为 ES5
    • babel的配置主要分为 presets预设 和 plugins插件
    • ES2018 新增了对象…展开,babel 提供 @babel/plugin-proposal-object-rest-spread来转换
    • ES2017新增了 asyncawaitbabel提供了 @babel/plugin-transform-async-to-generator 用于将async/await转换为 generator函数
    • babel-polyfill:大浏览器中添加缺失的新特性,补齐API和标准对齐
      • 缺点:全量引用所有的新特性垫片,这样引用的 polyfill 包会大增加构建的js体积大小,如果只用到了其中两三个新特性,那就存在很多冗余的垫片
    • @babel/preset-env:预设除了包含所有稳定的转码插件,还可以根据我们设定的目标环境进行针对性转码,核心配置项有 targetsuseBuiltInsmodulescorejs
      • targets:用于设置兼容到哪些目标浏览器,如果不配置,则尝试读取 package.json 和 .browserslistrc 中的配置, browserslist 的配置也同样作用于 autoprefixer、postcss等插件。 如果没有 targets 和 browserslist 配置,则转换所有的ES6语法为ES5版本
      • useBuiltIns:取值有 usage/entry/false,默认为 false
        • false:不使用 polyfill,只转换语法
        • entry :会根据目标浏览器环境,引用未支持的所有的 polyfill,需要在入口文件引用 @babel/polyfill
        • usage:会先分析代码中使用到的新特性,只为用到的新特性添加 polyfill,不需要手动添加 ;
          • 缺点: 语法转换后,代码是会注入一些辅助函数,如果有很多个js文件,每个文件顶部都会注入相关辅助函数,有可能会注入导致辅助函数重复,最后用构建工具打包出来的产物会非常大
      • corejs:取值为 2 或 3
      • modules:模块语法转换,用于将当前ES6的模块语法转换为其它模块化的语法,取值 amd/umd/systemjs/commonjs/cjs/auto/false,默认为 auto,会转换为 commonjs语法
        • 常见的模块化语法有两种:
          • ES6的模块法语法用的是 importexport
          • commonjs模块化语法是 requiremodule.exports
        • 一般建议设置为 false,不转换 es6 的模块语法,方便构建工具如webpack、rollup 对模块进行静态分析,实现 tree shaking 等优化措施
    • @babel/runtime:抽离公共辅助函数,为了自动使用@babel/runtime 里的函数,还需要 @babel/plugin-transform-runtime插件
    • 使用core-js@3 来 polyfill,会全局引入缺少的 API
    • @babel/runtime-corejs3 避免污染全局环境,相当于 @babel/runtime + 不污染环境的 core-js@3预设和
    • 插件的执行顺序
      • 插件比预设先执行
      • 插件执行顺序是插件数组从前向后执行
      • 预设执行顺序是预设数组从后向前执行
        @babel/preset-env
        @babel/preset-typescript
        @babel/preset-react
        @babel/preset-flow
        
        ```typescript // 普通的项目不怕污染全局环境可以用这个配置 npm install —save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime npm install —save @babel/runtime core-js@3

// babel.config.json 配置文件 { “presets”: [ [ “@babel/preset-env”, { “useBuiltIns”: “usage”, “corejs”: 3 } ] ], “plugins”: [ [“@babel/plugin-transform-runtime”] ] }

```typescript
// 开发工具类库为了不污染全局环境可以用这个配置
npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime
npm install --save @babel/runtime-corejs3

// babel.config.json 配置文件
{
  "presets": [
    [
      "@babel/preset-env"
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime", {
        "corejs": 3
      }
    ]
  ]
}
  • SourceMap 配置选择

    • SourceMap 是一种映射关系,当项目运行后,如果出现错误,可以利用 SourceMap 反向定位到源码位置
    • 速度:eval > inline > cheap > … | 关键字 | 含义 | | —- | —- | | source-map | 产生.map 文件 | | eval | 使用 eval 包裹模块代码 | | cheap | 精确到行,不包含列信息(关于列信息的解释下面会有详细介绍)也不包含 loader 的 sourcemap | | module | 包含 loader 的 sourcemap(比如 jsx to js ,babel 的 sourcemap),否则无法定义源文件 | | inline | 将.map 作为 DataURI 嵌入,不单独生成.map 文件(内联,体积大,不考虑) |

    • 推荐配置:

      • 本地开发:eval-cheap-module-source-map
      • 生产环境:(none)
        const config = {
        entry: './src/index.js', // 打包入口地址
        output: {
        filename: 'bundle.js', // 输出文件名
        path: path.join(__dirname, 'dist'), // 输出文件目录
        },
        devtool: 'source-map'
        }
        
  • 三种hash值

    • hash :任何一个文件改动,整个项目的构建 hash 值都会改变
    • chunkhash:文件的改动只会影响其所在 chunk 的 hash 值
      • 打包后的hash值会根据入口文件的不用而不一样,当某个入口文件修改后重新打包,会导致本入口文件关联的所有文件的hash值都修改,但是不会影响到其他入口文件的hash值
    • contenthash:每个文件都有单独的 hash 值,文件的改动只会影响自身的 hash 值
      • 每个文件的hash值都是根据自身内容而生成,当某个文件内容修改时,打包后只会修改其本身的hash值,不会影响其他文件的hash值 | 占位符 | 解释 | | —- | —- | | ext | 文件后缀名 | | name | 文件名 | | path | 文件相对路径 | | folder | 文件所在文件夹 | | hash | 每次构建生成的唯一 hash 值 | | chunkhash | 根据chunk生成hash值 | | contenthash | 根据文件内容生成hash 值 |