webpack入门学习

192 阅读5分钟

基于webpack 4.0版本

阅读 webpack文档 获取更详细的信息。

文中说明的属性,是本人在项目中用到的属性,比较少使用的属性,将不会进行说明。

1. webpack基础

mode

打包的环境检测,值是production 和 developemnt。当然如果要实现更细致的环境判断,可以设置自定义的环境常量(如REACT_ENV)。

mode: "production" || "development"

entry

打包入口,默认为 ./src 。值类型可以是字符串,数组,对象。常见的单页面开发入口的值是字符串(或者对象,如果vendor写在这里)。

entry: "./main.js"
entry: ["./main.js", "./dll/react.js"]
entry: {
  pageA: "./pageA.js",
  pageB: "./pageB.js"
}

output

输出的文件。

output: {
  // 输出目标路径
  path: path.resolve(__dirname, 'dist'),
  // 输出文件名称,name 对应入口文件的 name ,chunkhash 用于长效缓存。
  filename: 'static/js/[name]/[chunkhash:8].js',
  // 是输出解析文件目录,url是相对HTML页面的,这里也可以配置 `CDN` 信息
  publicPath: '' // 'https://cdn.example.com/'
}

module

模块。主要是关于 rules 的配置。一般配置如下:

module: {
  rules: [{
    // 匹配条件,正则或字符串
    test: /\.js?$/,
    // 必须匹配,使用绝对路径数组
    include: [path.resolve(__dirname, "app")],
    // 必不匹配路径,优先级高于test和include,使用绝对路径数组
    exclude: [path.resolve(__dirname, "node_modules,")],
    // 对应的loader, 解析上下文
    loader: "xxx-loader",
    // loader可选项,query是options的别名
    options: {...} 
  }, {
    test: /\.css|\.less|\.sass|\.scss/,
    // use 可以应用多个loader和选项
    use: [
    	"style-loader",
    	{
    		loader: "sass-loader",
    		options: {...}
  		}
    ]
  },{
    // 只使用这些嵌套规则之一
    oneOf: [/*rules*/]
  }]
}

resolve

设置模块如何被解析。

resolve: {
  // 别名, 
  // import xxx from '@components/xxx' === import xxx from '../src/components/xxx'
  alias: {
    '@components': path.resolve(__dirname, '../src/components')
  },
  // 自动解析确定的扩展,如文件扩展不在数组内,那需要写出来,在则可省略
  extensions: ['.jsx', '.js', '.json'],
  // 告诉webpack解析模块时应该搜索的目录,数组内次序是优先级的体现,前一个优先于后一个
  modules: ['node_modules'],
  // 译文中说的是:应该使用的额外的解析插件列表
  // 我理解的意思可能是,我们有自己对模块解析的插件,将它放到这里,让webpack去解析它
  plugins:[...]
}

optimization

优化配置。

optimization: {
  // 开启TerserPlugin压缩bundle,区分环境,一般开发环境不会开启
  minimize: true// 创建自定义的TerserPlugin实例
  minimizer: [
    new TerserPlugin({
      cache: true,
      parallel: true,
      sourceMap: true, // Must be set to true if using source-maps in production
      terserOptions: {
        // https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
      }
    }),
  ],
  // 敲黑板,划重点,这是必考点!更多信息请访问
 	// http://webpack.docschina.org/plugins/split-chunks-plugin/
  // 这文章也说得很清晰 https://imweb.io/topic/5b66dd601402769b60847149
  splitChunks: {
    // async 只优化动态加载的代码,其他类型的代码正常打包。
    // all 针对所有代码进行优化。
    // initial 针对原始 bundle 代码进行优化。
    // 三种模式详细情况请查看 https://github.com/wayou/wayou.github.io/issues/40
    chunks: 'all', // 'all', 'async', 'initial',
    // 表示抽取出来的文件在压缩前的最小大小,默认为 30000
    minSize: 30000,
    // 表示抽取出来的文件在压缩前的最大大小,默认为 0,表示不限制最大大小;
    maxSize: 0,
    // 表示被引用次数,默认为1;
    minChunks: 1,
    // 最大的按需(异步)加载次数,默认为 5;
    maxAsyncRequests: 5,
    // 最大的初始化加载次数,默认为 3;
    maxInitialRequests: 3,
    // 抽取出来的文件的自动生成名字的分割符,默认为 ~;
    automaticNameDelimiter: '~',
    // 抽取出来文件的名字,默认为 true,表示自动生成文件名;
    name: true,
    // 缓存组,配置的关键!!!它会继承/覆盖上面的参数值
    cacheGroup: {
      'react-vendors': {
            chunks: "all",
            // 要过滤的modules,可以正则匹配
            test: module => {
              return /react/.test(module.context);
            },
            name: 'react-vendors',
            // 抽取权重,数字越大,优先级越高
            priority: 10,
            // 表示是否使用已有的 chunk
            // 如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
            // reuseExistingChunk: true
          },
          node_vendor: {test: /[\\/]node_modules[\\/]/, name: "vendors", chunks: "all"},
          // 也能打包css,不过很少这样用,一般都是使用MiniCssExtractPlugin来抽取的
          style: {
            test: /\.css$/,
            chunks: 'all',
            enforce: true,
            priority: 20
          }
        }
    }
  },
  // 是否需要将模块信息清单内容(runtime)单独打包出来
  // 对runtimeChunk的作用的解释 https://segmentfault.com/q/1010000014954264
  // runtime内容是记录模块的信息,解析,加载,如果某个模块发生变更(如引入或删除模块,更改模块内容等)
  // 将会使得runtime内容发生变化,这部分如不抽离,将会导致某个含有runtime内容的文件不能长期缓存,
  // 即使该文件未发生变化。(webpack4版本以下的,需要配置CommonsChunkPlugin单独为runtime打包一个文件)
  // 这是解决文件内容没发生变化,但是chunkhash却变化的问题的关键
  runtimeChunk: false // false || true || 'single' || 'multiple',
  // runtimeChunk值是 true || multiple 时,会为每个单独包含runtime的入口起点添加一个额外的chunk
  // 该设置是以下设置的别名
  // runtimeChunk: {
  //   name: entrypoint => `runtime~${entrypoint.name}`
  // }
  // runtimeChunk值是single时,会创建一个在所有生成块之间共享的运行时文件
  // 该设置是以下设置的别名
  // runtimeChunk: {
  //     name: 'runtime'
  // }
}

plugins

用于以各种方式自定义 webpack 构建过程。这是一些插件。你也可以自己编写一个插件,很有趣的。小手指戳戳编写一个插件

DefaultPlugin 能允许你创建编译时配置的全局常量。可以区分开发环境和生产环境的不同的行为。如接口请求域名开发与生产环境的区分,打印日志功能启用。具体介绍请查看DefaultPlugin 。以前曾在这里设置JQuery为常量,以解决第三方插件因访问$是undefind报错的问题。

// webpack.config.js
...
plugins: [
  new webpack.DefaultPlugin({
    PRODUCTION: JSON.stringify(true)
  }),
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  // 你自己编写的插件也是放在这里的哦
]
... 
// index.js
if (!PRODUCTION) {
  console.log('Debug info');
}

if (PRODUCTION) {
  console.log('Production log');
}

// 结果
console.log('Production log');

当然,一般的第三方插件,也会写在这,比较典型的 HtmlWebpackPluginMiniCssExtractPlugin

plugins: [
  new MiniCssExtractPlugin({
    // Options similar to the same options in webpackOptions.output
    // both options are optional
    filename: 'static/css/[name].[contenthash:8].css',
    chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
  })
]

externals

提供了「从输出的 bundle 中排除依赖」的方法,值的类型有 stringobjectfunctionregex

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)

从CDN中引入JQuery而不是打包进去

// webpack.config.js
...
externals: {
  jquery: 'JQuery'
}
...

// index.html
<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous">
</script>

或许它能提供比较有趣的功能,可以点击这里查看更详细的信息,然后去探索吧!

其他配置项,暂不介绍了,待了解并实践后再总结

2. webpack优化打包速度

首先我们要知道,要达到目标效果,我们需要了解什么。打包慢是因为需要打包处理的东西多。那么想要加速,就必须解决这个痛点。那么怎么来解决呢?目前我了解的大概是:

其一,缓存长期不变的模块,只打包业务模块

其二,众人拾柴火焰高,一核有难,各核帮忙

其三,我自己的不要,我偏要别人的

总的来说,就是,缓存多线程负担减负

缓存:

  1. 有些loader可以设置缓存(如在 babel-loader 中,可以通过设置 cacheDirectory 来开启缓存)
  2. webpack针对loader的缓存插件cache-loader
  3. webpack的插件hard-source-webpack-plugin

多线程负担: (假如项目不大,效果并不出众)

  1. 盛名于江湖而又快要隐退的 happypack
  2. webpack官方文档中的 thread-loader

减负:

  1. 引用CDN,并在webpack的externals中配置一下
  2. 抽离长期不变的组件,先打包成一个文件,然后往后的打包中排除这些组件,只打包业务组件,可以参考webpack编译速度提升之DllPlugin ,如果要了解 dllPlugin ,可以点击了解。

但是,在实践中,不建议在持续集成(如jenkins打包项目)中去实践,因为有可能会出现以下问题:

  1. 应该是更新的操作,却命中了缓存,导致打包出来的依然是上一个版本的文件;
  2. 缓存有可能失效了(或者没有缓存),因为jenkins打包的服务器有可能是新的没打包过的;
  3. 命中了缓存是n代版本的缓存,因为jenkins打包的服务器有可能会经常性转换;
  4. dll文件没更新(可能忘了在打包业务的时候先更新dll打包文件);
  5. 打包到生产环境短时间内只会打包一次,然后jenkins经历一段时间后会自动清理缓存,而且打包的服务器没有固定,以上的优化,对于经常打包的行为较友好;
  6. 打包的服务器不一定是2核以上的,可能多线程打包效果会更慢,因为做了一些无意义的事情。

未来会持续学习,并持续更新

参考文章

webpack文档

Webpack SplitChunksPlugin 的三种模式

webpack 4 Code Splitting 的 splitChunks 配置探索

optimization.runtimeChunk 具体作用是什么?

Webpack 4进阶--从前的日色变得慢 ,一下午只够打一次包

Webpack优化——将你的构建效率提速翻倍

webpack编译速度提升之DllPlugin