Skip to content

工程化层面优化

常见问题:如何让 webpack 打包更快

并发:使用多进程打包

缓存:打包时利用缓存

打包量要小:缩小文件搜索范围,减小不必要的编译工作

webpack优化

单独打包 runtime

splitChunks 将 react、vue 依赖单独打包

modulelds

多页面

common插件

code split 代码分割

  • runtime
  • node modules -> verdors 全局
  • common 公共模块
  • self(main/admin)自身

为什么 runtime 单独打包

额外的代码就是runtime

因为如果不单独打包,那么我们在修改webpack 配置的时候会导致用户的缓存失效,必须下载最新的main.js

如果单独打包,只要我不修改 index.js 里的内容,main.js 就不需要重新请求,节省用户带宽,用户访问速度就会加快

splitChunks 将 react、vue 依赖单独打包

vendors 表示 第三方的意思

webpack 多页面

common chunks 共有文件

打包优化

  • webpack
    • loader
    • dll
    • happypack
    • 代码压缩
    • tree shaking
    • scope hositing
    • code splitting
  • 图片 base64,cdn

如何做页面的性能优化

1.减少体积(打包优化、babel 优化)

webpack 打包,使用 webpack content-hash

文件指纹是打包后输出的文件名的后缀

  • Hash :和整个项目的构建相关,只要项目文件有修改,整个项目构建的 hash 值就会更改

  • Chunkhash:和 webpack 打包的 chunk 有关,不同的入口会有不同的 chunkhash

  • Contenthash:根据文件内容来定义 hash,文件内容不变,则 contenthash 不变

    2.延迟加载(资源的异步加载)

资源合并

CSS 雪碧图 -> Icon Font -> SVG Symbols

webpack

资源内敛

小图片 -> data url

小 CSS 文件 -> <style>代码</style>

小 JS 文件 -><script>代码</script>

webpack 配置即刻

资源压缩(gzip)

nginx 、 Apache、Node.js

资源精简

HTML -> 删空格、删闭合

CSS -> 删未用

JS -> 改名、tree shaking

SVG -> 删无用标签、属性

图片 -> 减少体积(有损/无损)

4kb 新域名

CDN 的原理和实施

CDN 内容分发网络

  1. 分离 Manifest
  2. Code Splitting(代码分割)
  3. Bundle Splitting(打包分割)
  4. Tree Shaking(删除死代码)
  5. 开启 gzip

分离 Manifest

javascript
module.exports = (config, resolve) => {
  return() => {
    config
      .optimization
      .runtimeChunk({
        name: "manifest"
      })
  }
}

Code Splitting

  1. 使用动态 import 或者 require.ensure 语法
  2. 使用 babel-plugin-import 插件按需引入一些组件库

Bundle Splitting

将公共的包提取到 vendors 里面,比如你

Tree Shaking

javascript
config.optimization.usedExports(true);

开发环境:

  1. 值得去配置的:

    1. 优化开发体验

      1. 自动刷新 -> 模块热更新

        1. 实时预览反应更快,等待时间更短

        2. 不刷新浏览器能保留当前网页的运行状态

          需要在入口文件进行配置:

          javascript
          // 入口文件
          if (module.hot) {
              module.hot.accept(['./App'], () => {
                  render(<App />, document.getElementById('app'));
              });
          }

          PS: 模块热更新机制

          1. 当子模块发生更新时,更新事件会一层层往上传递,也就是从 App.js 文件传递到 main.js 文件,直到有某层的文件接受了当前变化的模块,也就是 main.js 文件中定义的 module.hot.appept(['./App', callback]),这时就会调用 callback 函数去执行自定义逻辑
          2. 如果事件一直往上抛到最外层都没有文件接受它,就会直接刷新网页
          

          webpack 方面也有相应的开启热跟新的处理

          javascript
          const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin');
          module.exports = webpackMerge(baseConfig, {
              plugins: [new HotModuleReplacementPlugin()],
              devServer: {
                  // 每次构建时候自动打开浏览器并访问网址
                  open: true,
                  // 开启热更新
                  hot: true,
                  // 设置静态资源地址如:/public,从这获取你想要的一些外链资源,图片。
                  contentBase: DIST_PATH,
                  // 设置端口号
                  port: 9000,
                  // 将热更新代码注入到模块中
                  inline: true,
                  // 如果你希望服务器外部可访问
                  host: '0.0.0.0',
                  // 设置 proxy 代理
                  proxy: {
                      context: ['/api'],
                      target: '//www.proxy.com',
                      pathRewrite: { '^/api': '' },
                  },
                  // 设置 https
                  https: true,
              },
          });
          1. 配置 sourcemap,方便调试
        javascript
        module.exports = {
            devtool: 'source-map',
        };

        PS: devtool 有各种选择,不同的选项构建速度不同

    2. 尽可能减少构建时间

      1. 减少模块朝招范围,缩小 Babel 的编译范围,并使用 webpack cache 缓存模块

        javascript
        module.exports = {
            // 减小模块的查找范围
            resolve: {
                modules: [path.resolve(__dirname, 'node_modules')],
            },
            module: {
                rules: [
                    {
                        test: /\.js?$/,
                        use: [
                            {
                                loader: 'babel-loader',
                                query: {
                                    // 将 babel 编译过的模块缓存在 webpack_cache 目录下,下次优先复用
                                    cacheDirectory: './webpack_cache/',
                                },
                            },
                        ],
                        // 减少 babel 编译范围,忘记设置会让 webpack 编译慢上好几倍
                        include: path.resolve(__dirname, 'src'),
                    },
                ],
            },
        };
        1. 使用 DllPlugin 预先打包好第三方库

            	1. ```javascript
           // dll.config.js
           const webpack = require('webpack');
           const path = require('path');
           const DllPlugin = require('webpack/lib/DllPlugin')
           const vendors = [
             'react',
             'react-dom',
             'react-router',
             'redux',
             'react-redux',
             'jquery',
             'antd',
             'lodash',
           ]
           module.exports = {
             entry: {
               'dll': vendors,
             },
             output: {
               filename: '[name].js',
               path: path.resolve(__dirname, 'public'),
               library: '__[name]__lib',
             },
             plugins: [
               new DllPlugin({
                 name: '__[name]__lib',
                 path: path.join(__dirname, 'build', '[name].manifest.json'),
               }),
             ]
           }
          
          具体配置
          
          3. 使用 Happypack 多线程加快构建

        webpack 构建慢是因为要解析和处理大量的文件,它需要一件件去做。 Happypack 的核心原理就是把这部分任务分解到多个进程去并行处理,从而减少总的构建时间

        需要配置哪些 loader 使用 Happypack 就要改写那些配置,比如你想要修改 babel 为多核编译:

        javascript
        module.exports = {
            module: {
                rules: [
                    {
                        test: /\.js?$/,
                        use: ['happypack/loader?id=babel'],
                        include: path.resolve(__dirname, 'src'),
                    },
                ],
            },
            plugins: [
                new HappyPack({
                    id: 'babel',
                    loaders: [
                        {
                            loader: 'babel-loader',
                            query: {
                                cacheDirectory: './webpack_cache/',
                            },
                        },
                    ],
                }),
            ],
        };
        1. 不使用 webpack css 模块方案

          css in js 会增加机器的编译时间,所以不使用

    3. 不值得去配置的

      1. 代码丑化
      	2. 模块拆包,持久化缓存
          	3. 减少打包文件大小
      

生存环境:

  1. 值得去配置的
    1. 模块拆包
    2. 尽可能减少打包文件大小
    3. 代码丑化压缩
    4. 尽可能减少构建时间
  2. 不值得去配置 1. 优化开发体验 2. 开发环境才需要的配置

预取/预加载模块(prefetch/preload module)

作用:先加载页面需要的,而异步操作等浏览器进程闲置的时候再会去加载

javascript
Import(/* webpackPrefetch: true */ ‘LoginModal’)

参考资料