webpack掠影-7

384 阅读6分钟

这是我参与8月更文挑战的第30天,活动详情查看:8月更文挑战

webpack 掠影-示例完整代码

前面的 webpack 掠影系列已经大致讲述了 webpack 的一些配置,这些配置可以应付大部分的工作内容,今天这一篇小札记作为 webpack 掠影的最后一篇,这一周多以来我一直在努力的整理,希望能帮到同样在搞 webpack 的你,虽然看小文章上瘾,但是不要贪多啊,最好的还是官方文档有营养,但是无论如希望这写小札记能预约你的学习路途吧;

加油,写完这一篇,下一个系列我准备写一些 HTTP 的入门内容,也当督促自己学习了,加油!加油!

一、模块懒加载

在日常的开发中,很多的模块并不需要立刻就加载,所以这些内容可以作为优化的内容在需要的再去加载,这就是资源的懒加载;webpack 同样支持这一常用的功能,webpack 的懒加载需要使用 ES6 的模块动态加载方法 import() 方法。被懒加载的模块被单独抽离成 js 文件,在需要加载的时候通过 AJAX 向服务器发起请求;import() 方法返回 Promise 实例对象;

let btn = document.getElementById('button');

btn.onclick = async function () {
  let sp = await import('./test-hmr')
  console.log(sp.p)
}

如上面的例子,在 btnonclick 时才会触发加载 test-hmr.js 模块,我们使用了一个 async 函数来处理,如果你喜欢使用 .then 的方式来处理这,只需要 import().then() 就可以; 一般开发时,我们使用 import() 来异步加载组件,包括 vue 的异步组件或者 react 都支持这一语法,用以减小代码的体积,提升性能;

二、 dllPlugin 动态链接库

dll(Dynamic-linked-library)动态链接库,由 Microsoft 引入的概念。它提供了一种通过对 bundle 的拆分来实现提升构建速度的方法;这个插件一般配置 dllReferencePlugin 一同使用;

举一个例子,我们项目开发时,我们使用 react 技术栈,一般情况下,我们使用某一个版本的 react 确定后短时间内不需要更改,但是像这种比较大的库,我们每次打包的时候都会被重新打包,这就导致构建速度十分缓慢,所以我们可以吧 reactreact-dom 单独抽离出来制成动态链接库,这样一来我们开发业务代码的时候再次打包就不会再打包 reactreact-dom ,打包速度将会得到明显的提升。

2.1 首先配置一个打包文件,把像 react、react-dom 这种第三方的库单独打包出来;

  • webpack.react.config.js 该配置文件需要在 output 中增加额外的配置项目:
  • library: 和下面的 libraryTarget 配合使用;把文件打成一个库,这个字段的值将会成为一个变量,这个变量代表的是上面 entry 的导出内容;可以修改,同样支持 [name] 的方式
  • libraryTarget: 和上面的 library 配合生效;表示用什么方式把 entry 导出的内容赋值给变量,默认值是 var

所以 librarylibraryTarget 设置的结论就是: var __dll_xx = react+react-dom 导出的内容;

let path = require('path');
let webpack = require('webpack');

module.exports = {
  mode: 'production',
  entry: {
    react: ['react', 'react-dom']
  },
  output: {
    filename: '__dll_xx.js', // 产生的 js 文件名
    path: path.resolve(__dirname, 'manifest'),
    library: '__dll_xx', 
    libraryTarget: 'var'
    // 所以 library 和 libraryTarget 设置的结论就是: var __dll_xx = react+react-dom 导出的内容;
  },
  plugins: [
    new webpack.DllPlugin({
      name: '__dll_xx',
      path: path.resolve(__dirname, 'manifest', 'manifest.json')
    })
  ]
}

2.2 执行 webpack 命令打包输出 manifest 文件

  • 在 package.json 中加入命令
"scripts": {
    "dev": "webpack-dev-server",
    "build": "webpack",
+    "build-react": "webpack --config ./webpack.react.config.js"
  },
  • 执行
npm run build-react

执行命令后,我们会发现在在 dist 目录下生成了两个文件,__dll_xx.jsmanifest.json。这个 js 就是一个由我们刚刚配置了 DllPlugin 打包输出的库,而这个 manifest.json 就是一个映射表文件;

  • 生成的文件如图:

js 文件的名字就是我们配置的 __dll_xx.js

wbp7-dll1.png

  • __dll_xx.js 代码中的第一个变量如图

var __dll_xx = func.... 就是:libraryTargetlibrary 指定的结果

wbp7-dll-library.png

2.3 在模板中引入 DLL 库

我们在模板 other.html 中引入,DLL 库的 js 文件;

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
<div id="root-other-page"></div>

<p>this is another page </p>

+ <script src="/__dll_xx.js"></script>

</body>
</html>

2.4 DllReferencePlugin

上面我们已经成功创建了 DLL 库,接下来我们需要在我们的业务代码中使用我们的 DLL 库;需要在 webpackplugins 中增加一条插件;

module.exports = {
    ....
  plugins: [
+    new webpack.DllReferencePlugin({
+      manifest: path.resolve(__dirname, 'dist', 'manifest.json')
+    })
  ]
  ...
}

三、 happypack 多线程打包

当项目很大的时候,打包速度很可能会很慢,webpack 支持多线程打包。这个功能依赖于 happypack

3.1 安装 happypack

yarn add happypack

3.2 导入 happypack

  • 修改配置文件
    • 修改 loader use: 'happypack/loader?id=js', 使用 happypack 时,需要将 loader 改成 happypack/loader?id=xx 这个 xx 是一个标识符,下面的 plugin 还需要用
    • 修改 plugins
const path = require('path');

const webpack= require('webpack');

const Happypack = require('happypack');

module.exports = {
  mode: 'production',
  devtool: 'eval-source-map',
  entry: {
    home: './src/index.js',
    other: './src/other.js',
    x: './src/x.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[hash].js' // 配合多页面应用 [name] 表示文件名,  [hash:5] 生成文件名后跟随 5 位 hash 戳记
  },
  devServer: {
   
  },
  module: {
    rules: [
+      {
+        test: /.(js|jsx)$/,
+        use: 'happypack/loader?id=js', // 使用 happypack 时,需要将 loader 改成 'happypack/loader?id=xx' 这个 xx 是一个标识符,下面的 plugin 还需要用
+        exclude: /node_modules/
      }
    ]
  },
  plugins: [

+    // 使用 happypack 多线程打包 js
+    new Happypack({
+      id: 'js', // 这个 'js' 就是上面配置 loader 时,写在 happypack/loader?id=js 中的 js
+      use: [
+        {
+          loader: 'babel-loader',
+          options: {
+            presets: ['@babel/preset-env', '@babel/preset-react'],
+            plugins: [
+              ['@babel/plugin-proposal-decorators', { legacy: true }], // 使用装饰器
+              ['@babel/plugin-proposal-class-properties', { loose: true }], // 使用 class-properties
+              ['@babel/plugin-transform-runtime'] // 使用 async/await/generator
+            ]
+          }
+        }
+      ]
+    })

  ],

  // 配置优化配置项
  optimization: {
 
  }

}

注意:使用 happypack 时,如果包体积很小,则改善不会十分明显,还有可能会比不使用的情况变的更差,这是因为使用 happypack 时会增加线程通信的开销,这些增量再打包时间不是很长的情况下就会变的不可忽略,所以不建议滥用!

四、小结

本次掠影主要介绍了2中常见的优化打包速度的解决方案:Dllhappypack,这两种方式本质上不能减小总代码体积的。

  1. 第一种 Dll 的解决方案是让由一部分代码不再参与打包,减轻打包的工作量。
  2. happypack 则是人海战术了,活儿多不怕,加人干就行了,但是注意值得一提的是,注意 happypack 本身是会带来一定的时间增量的,如果包体积不大,不建议滥用!

五、给八月

八月最后一天,最遗憾的就是第一天错过了,没有机会写满一个整月。拿什么奖品不重要了,重要的是我一次性把文章补了起来;

过了今天要歇歇了,感谢观众老爷的阅读!