美文网首页前端工程化
webpack5 优化产出代码

webpack5 优化产出代码

作者: miao8862 | 来源:发表于2021-07-06 23:41 被阅读0次

    webpack产出代码做性能优化的效果无非是下面三种效果:

    • 体积更小
    • 合理分包,不重复加载
    • 速度更快、内存使用更少

    下面将通过介绍以下方案来达到以上目的:

    1. 小图片使用base64编码:url-loader
    2. bundlehash:应用缓存
    3. 使用import懒加载模块
    4. 使用splitChunks提取公共组件
    5. 使用IgnorePlugin忽略无用的模块
    6. 使用CDN加速:添加publicPath
    7. 使用production模式:
      • 自动开启代码压缩,使得打包体积更小
      • Vue,React等会自动删除调试代码(比如开发环境的warning警告),体积会比开发时更小
      • 启动tree-shaking,删除无用的代码
    8. 使用作用域提升:Scope Hosting

    其实里面很多方案,我们在基础配置和高级配置时已经提及过:

    小图片base64编码

    通过设置url-loader的阀值来控制,小于阀值的图片使用base64编码,以此来减少http请求

      module: {
        noParse: /jquery|lodash/,
        rules: [
          {
            test: /\.(jpg|png|jpeg|gif)$/,
            use: {
              loader:'url-loader',
              options: {
                limit: 8 * 1024,    //限制 8kb 以下使用base64
                esMoudle: false,
                name: '[name]-[hash:10].[ext]',
                // 打包到/images目录下
                outputPath: 'images'
              }
            }
          }
        ]
      },
    

    bundle 加 hash 值

    给产出的文件增加hash值,当内容没有变化时,生成的文件名将不会变化,浏览器就会应用缓存,从而提升加载速度

      output: {
        filename: '[name].[contenthash:8].js',
        path: distPath  // 输出目录
      },
    

    懒加载

    使用import方式懒加载组件

    // index.js
    setTimeout(() => {
      // 直接使用import导入即可,这样加载的模块,相当于一个独立的chunk存在
      import('./common/dynamicData.js').then(res => {
        console.log(res.default.msg);
      })
    }, 1000)
    

    提取公共代码

    optimization中配置splitChunks属性,设置cacheGroups分组,一般分为两个组:verdors(用于提取第三方库)和common(用于提取自定义的公共模块)
    针对第三方库,几乎不会改变,提取出来后,改动业务代码不会动到第三方库的打包文件,这样,就能命中缓存,加快加载速度
    针对公共模块,只用加载一次,就可以在多个模块中使用

    // webpack.prod.js
      optimization: {
        splitChunks: {
          chunks: 'all', // 表示要分割的chunk类型:initial只处理同步的; async只处理异步的;all都处理
          // 缓存分组
          cacheGroups: {
            // 第三方模块
            verdors: {
              name: 'verdor', // chunk名称
              test: /node_modules/,  // 设置命中目录规则
              priority: 1, // 优先级,数值越大,优先级越高
              minSize: 0, // 小于这个大小的文件,不分割
              minChunks: 1 // 最少复用几次,这里意思是只要用过一次就分割出来
            },
            // 公共模块
            common: {
              name: 'common',
              minChunks: 2,
              priority: 0,
              minSize: 0,
              minChunks: 2  // 只要引用过2次,就分割成公共代码
            }
          }
        }
      }
    

    IgnorePlugin

    IgnorePlugin可以帮助我们忽略某些库不需要的模块,从而实现按需加载,减少打包体积

    // webpack.prod.js或webpack.common.js都可以
    const webpack = require('webpack')
    const prodConfig = {
      plugins: [
        // webpack4写法
        // new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
    
        // webpack5写法
        new webpack.IgnorePlugin({
          resourceRegExp: /^\.\/locale$/,   // 忽略content设置的库中的某个文件夹
          contextRegExp: /moment$/, // 要被忽略某部分内容的库
        })
      ]
    }
    

    使用 CDN 加速

    在输出文件配置中将CDN路径添加至publicPath中(针对js和css文件);
    针对图片,也可以在loader的配置中添加publicPath

      output: {
        // filename: 'bundle.[chunkhash].js',  // 输出文件名,一般要加上hash
        filename: '[name].[contenthash:8].js',
        path: distPath,  // 输出目录
        publicPath: 'http://cdn.xxx.com'
      },
    
    // 或者是将图片放到cdn中
    module: { 
      rules: [
        {
            test: /\.(jpg|png|jpeg|gif)$/,
            use: {
              loader:'url-loader',
              options: {
                limit: 8 * 1024,    //限制 8kb 以下使用base64
                esMoudle: false,
                name: '[name]-[hash:10].[ext]',
                // 打包到/images目录下
                // outputPath: 'images', // 有了publicPath,会自动忽略outputPath,这是针对放在静态资源服务器上的目录
                publicPath: 'http://cdn.xxx.com'
              }
            }
          }
      ]
    }
    

    这样打包出来的index.html中的 静态资源,会自动添加CND地址前缀

    <script defer="defer" src="http://cdn.xxx.com/verdor.03535a89.js"></script>
    <script defer="defer" src="http://cdn.xxx.com/index.c6d170da.js"></script>
    
    图片放至cdn服务器

    这一步还需要我们将对应的资源文件给放至对应的CND服务器地址中

    启用production模式

    mode: 'production', // 生产环境
    

    启用production模式,会使打包的体积更小,webpack4以后,只要开启生产模式,就会自己帮我们实现以下功能:

    1. 自动开启代码压缩,比如删除注释、空格等,当然可以视项目情况,来判断要不要使用webpack-parallel-uglify-plugin开启多进程压缩
    2. Vue,React等会自动删除调试代码(比如开发环境的warning警告),体积会比开发时更小
    3. 启用tree-shaking,删除无用的代码,当注意,只有在ES Module才可以使用
      • ·ES6 Module是静态引入,编译时时就引入模块的,所以才可以做静态分析,实现tree-shaking
      • Commonjs是动态引入的,是执行时才引入,需要执行代码时,才知道引不引入,所以没法做静态分析,也就无法实现tree-shaking

    Scope Hosting

    webpack分析依赖打包出来的文件,一般是一个模块生成一个函数,比如:

    // test.js
    export default 'module test'
    // index.js
    import test from './module-test'
    console.log(test)
    

    打包后大概是这样的:

    [
      function (module, exports, require) {
        var module_test = require(1)
        console.log(module_test['default'])
      },
      function (module, exports, require) {
        exports['default'] = 'module test'
      }
    ]
    

    这样,当我们引用的模块很多时,我们打包时就会产生很多个函数,一个函数会生成一个函数作用域,当多个模块时,就会产生多个函数作用域,这样步骤创建函数作用域和执行函数、销毁函数,对js代码的执行和内存是很不友好的

    但如果我们按引用顺序,把它们合成一个函数来执行,所有操作都在一个函数执行,那么性能就会好很多,比如

    // bundle.js
    [
      function (module, exports, require) {
        // ./module-a.js
        var module_test_defaultExport = 'module test'
    
        // ./index.js
        console.log(module_test_defaultExport)
      }
    ]
    

    它带来的好处有:

    • 减小代码体积
    • 创建的函数作用域更少
    • 代码可读性会更好

    但是,Scope Hosting的实现也是有限制的,它必须是在ES Module环境下使用

    知道了原理,我们来看下Scope Hosting如何配置,它是webpack内置的一个模块,可以直接使用:

    const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin')
      resolve: {
        // 针对npm中的第三方库模块,优先采用jsnext:main中指向ES6模块化语法的文件
        mainFields: ['jsnext:main', 'browser', 'main']
      },
      plugins: [
        // 开启scope hoisting,作用域提升
        new ModuleConcatenationPlugin()
      ]
    

    相关文章

      网友评论

        本文标题:webpack5 优化产出代码

        本文链接:https://www.haomeiwen.com/subject/mlklultx.html