【汽车之家】webpack打包优化总结

2,678 阅读3分钟

       在项目开发迭代的过程中,项目变得越来越庞大以后打包的时间会变长,体积会变大。这不仅会影响我们项目加载的体验,也会降低我们开发调试的效率。本文对打包时间和打包体积两个方面的优化进行了总结。

一、优化打包速度

       特定的项目都有自己的性能优化瓶颈,首先需要使用速度分析工具 speed-measure-webpack-plugin,这个插件可以测量各个插件和loader所花费的时间。从而做到根据实际项目具体情况有方向性的去优化。

配置方法如下:

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); 
const smp = new SpeedMeasurePlugin(); 
const webpackConfig = smp.wrap({   
    plugins: [      
        new MyPlugin(),      
        new MyOtherPlugin()   
    ]
});

分析结果:


那影响打包速度的因素都有哪些呢?如下:

1、项目过大需要打包编辑的文件太多

       因为webpack也是单线程模式,打包时只能一个文件一个文件的进行编译,当需要打包的文件过时,打包会变的非常慢。

我们知道,node 其实是可以 fork 子进程的,那么这里自然就有优化的余地,这里可以用到一个插件:开启多线程打包,通过happypack插件开启多线程打包,配置如下:

const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }); 
module.exports = {  
  module: {    
    rules: [{        
            test: /\.js$/,/把对.js 的文件处理交给id为happyBabel 的HappyPack 的实例执行        
            use: [{
                loader: 'happypack/loader',          
                options: {  id: 'happyBabel'  }        
            }],        
            //排除node_modules 目录下的文件       
            exclude: /node_modules/      
    }]
  },
  plugins: [   
      new HappyPack({
            id: 'happyBabel',     
            use: [{         
                loader: 'babel-loader',          
                options: {            
                    presets: ['react', 'stage-0', 'env' ],            
                    plugins: ['transform-runtime', 'transform-class-properties']         
                }        
            }],      
            //共享进程池      
            threadPool: happyThreadPool,      
            //允许 HappyPack 输出日志      
            verbose: true,    
      }) 
  ] 
}

2、不经常变化的依赖包不参与打包进程:

     通过配置webpack.config.dll.js,将不常变化的依赖库提前打包,减少打包这些库的次数。

     2.1 : cdn 引入资源,设置不打包这些文件

webpack.config.jsmodule.exports = {  
    //...  
    externals: {    jquery: 'jQuery'  }
};

     2.2:没有模块依赖关系的,如果一些第三方模块没有AMD/CommonJS规范版本,可以使用 noParse来标识这个模块,这样webpack会引入这些模块,但是不进行转化和解析,从而提升webpack的构建性能,例如:jquery、lodash。注意被忽略掉的文件里不应该包含import、require、define等模块化语句,不然会导致构建出的代码中包含无法在浏览器环境下执行的模块化语句。noParse属性的值是一个正则表达式或者是function。

webpack.config.jsmodule.exports = {    
    //...    
    module: {        
        noParse: /jquery|lodash/    
    }
}

3、优化loader的搜索范围

      loader对文件的转换工作比较耗时,需要让尽可能少的文件被Loader处理。通过include去命中只有哪些件需要被处理。

4、babel编译过的文件可以缓存起来

       cacheDirectory=true,loader 将使用默认的缓存目录 node_modules/.cache/babel-loader,如果在任何根目录下都没有找到node_modules目录,将会降级回退到操作系统默认的临时文件目录。

二:减小打包大小

       通过配置webpack-bundle-analyzer插件可以分析打包体积。umi内置了此插件,只需在umi的config文件中加入analyzer配置,并在package.json添加analyzer命令即可:

// package.json
"scripts":{  
    "analyze": "set ANALYZE=1 && umi build"
}

// umi 
config{  
    ...,    
    plugins: [       
        // ref: https://umijs.org/plugin/umi-plugin-react.html       
        [          
            'umi-plugin-react',         
             {
                ...,            
                analyze: {              
                    analyzerMode: 'server',              
                    analyzerPort: 8888,              
                    openAnalyzer: true,              
                    // generate stats file while ANALYZE_DUMP exist             
                    generateStatsFile: false,              
                    statsFilename: 'stats.json',              
                    logLevel: 'info',              
                    defaultSizes: 'parsed', 
                    // stat  
                    // gzip            
               },          
            }        
        ]  
    ]
}

1、优化依赖库的打包

       很多项目中依赖的第三方库,即使使用了很少的功能也会被全部打包进项目,或者多次引用会重复打包。如moment会把所有语言包都打进来,如果我们的项目不需要多语言,可以使用ContextReplacementPlugin插件来舍弃中文之外的其他语言文件,或者使用Day.js 替换 momentjs 优化打包大小:

new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn/)

       再如,lodash,antd等最好启用按需引入。他们都可以在引用的时候采用引入特定组件的方式实现按需引入,也可以通过插件 babel-plugin-import进行按需引入,使用方式:

npm install babel-plugin-import --save-dev

.babelrc 文件

{  
    "plugins": [  
        ["import", { "libraryName": "antd", "libraryDirectory": "lib"}, "ant"],  
        ["import", { "libraryName": "antd-mobile", "libraryDirectory": "lib"}, "antd-mobile"] 
    ]
}

2、tree shaking :

      webpack4 在在项目package.json文件中,添加一个"sideEffects" 属性后,在打包时会标记未引用代码为~unused harmony export square,在代码压缩的时候这些代码会被移除。umi中只需配置treeShaking: true,开启后效果还是很明显的:

3、公共资源分离:

在很多模块里,有相同的依赖,把这些依赖抽离为一个公共的文件,则可以有效地减少资源的体积,并可以充分利用浏览器缓存。不需要按照任何依赖,只需要在webpack config中加入optimization

splitChunksPlugin ,默认配置如下:

optimization:{  
    splitChunks: {      
        chunks: "async",// 选择分割哪些代码块,'all'(所有代码块),'async'(按需加载的代码块),'initial'(初始化代码块)。      
        minSize: 30000, // 模块的最小体积      
        minChunks: 1, // 模块的最小被引用次数      
        maxAsyncRequests: 5, // 按需加载的最大并行请求数      
        maxInitialRequests: 3, // 一个入口最大并行请求数     
        automaticNameDelimiter: '~', // 文件名的连接符      
        name: true,      
        cacheGroups: { 
            // 缓存组          
            vendors: {              
                test: /[\\/]node_modules[\\/]/,              
                priority: -10          
            },          
            default: {              
                minChunks: 2,              
                priority: -20,              
                reuseExistingChunk: true          
            }      
         }  
    }
}

以上为近期结合项目对webpack打包的一些总结,前端项目中更好地优化打包资源对于研发效率和产品体验都很重要,会持续关注和研究更好更好的优化方案。欢迎大家补充。


作者:汽车之家-前端体验部-王莹