当前位置: 首页 > 科技观察

webpack2终极优化

时间:2023-03-19 02:09:21 科技观察

webpack是目前最流行的js打包工具,得益于日益复杂的web应用和js模块化的流行。webpack2加入了一些新特性,正式发布也有一段时间了。是时候告诉你如何使用webpack2来优化你的构建,使其能够构建更小的文件大小和更好的开发体验。优化输出包导致更小的尺寸,这可以使网页打开速度更快并减少带宽。以下几点可以用来压缩csscss-loader。在webpack2中,默认情况下不启用压缩。***生成的css文件中有很多空格和制表符。通过配置css-loader?minimize参数,可以开启压缩,输出最小的css。CSS压缩其实是通过cssnano实现的。tree-shakingtree-shaking是指利用es6importexport语法的静态特性,删除导出但不导入的东西。要使tree-shaking起作用,需要注意以下几点:配置babel使其在编译转换es6代码时不将importexport转换为cmdmodule.export,配置如下:"presets":[["es2015",{"modules":false}]]分发到npm库的大部分代码是es5,但一些库(redux、react-router等)开始支持tree-shaking。这些库发布到npm的代码包括es5和所有使用es6importexport语法的代码。以redux库为例,npm下载的目录结构如下:├──es│└──utils├──lib│└──utilslib目录是编译后的es5代码,es目录是编译后使用importexport语法的es5代码在redux的package.json文件中有这两个配置:main":"lib/index.js","jsnext:main":"es/index.js",意思是这个库的入口文件的位置,所以要让webpack读取es目录下的代码,需要使用jsnext:main字段配置的入口,为此,webpack需要这样配置:exports={resolve:{mainFields:['jsnext:main','main'],}};这会让webpack优先使用jsnext:main字段,没有的时候使用main字段,这样可以优化支持tree-shaking的库。优化UglifyJsPluginwebpack--optimize-minimize选项会启用UglifyJsPlugin压缩输出js,但默认的UglifyJsPlugin配置并没有将代码压缩到最小输出js。里面还有注释和空格,需要覆盖默认配置:newUglifyJsPlugin({//最紧凑的输出beautify:false,//删除所有注释comments:false,compress:{//不输出警告时UglifyJs删除未使用的代码warnings:false,//删除所有`console`语句//也兼容ie浏览器多次但未定义为变量引用reduce_vars:true,}})定义环境变量NODE_ENV=production许多库(如react)有部分代码是这样的:if(process.env.NODE_ENV!=='production'){//生产环境不需要的代码,比如control平台看到的warning}当环境变量NODE_ENV等于production时,UglifyJs会认为if语句中的死代码是压缩代码时删除。使用CommonsChunkPlugin提取公共代码CommonsChunkPlugin可以将多个代码块所依赖的模块提取出来,形成一个单一的模块。发挥CommonsChunkPlugin的作用还需要浏览器缓存机制的配合。在应用有多个页面的场景下,提取所有页面的公共代码,减少单个页面的代码。切换不同页面时,所有页面的公共代码之前已经加载,不需要重新加载。这种方法可以非常有效地提高应用程序性能。在生产环境中,根据文件内容的md5,将生产环境中hashwebpack编译好的js、css、图片、字体放到CDN上,然后根据文件的md5命名文件内容。使用缓存机制,用户只需要加载一次。第一次加载时直接访问缓存。后面修改的话,对应的文件会生成一个新的md5值。要做上面的事情,需要这样配置:{output:{publicPath:CND_URL,filename:'[name]_[chunkhash].js',},}知道上面的原理后,我们可以进一步优化:使用CommonsChunkPlugin提取所有使用过的页面依赖于基础运行环境。例如,对于最常见的React系统,你可以将基础库reactreact-domreduxreact-redux提取到一个单独的文件中,而不是将其与其他文件打包到一个文件中。这样做的好处是,只要你不升级他们的版本,这个文件就永远不会刷新。如果将这些基础库和业务代码打包在一个文件中,每次更改业务代码,浏览器都会重复下载包含基础库的代码。上面的配置是://vender.js文件将基础库提取到一个单独的文件中,防止后续业务代码被刷新//所有页面依赖的第三方库//reactbaseimport'react';import'react-dom';import'react-redux';//reduxbaseimport'redux';import'redux-thunk';//webpack配置{entry:{vendor:'./path/to/vendor.js',},}DedupePlugin和OccurrenceOrderPlugin经常使用webpack1中的DedupePlugin插件来剔除重复模块,使用OccurrenceOrderPlugin插件将依赖数量较多的模块分配给较小的id,以达到输出代码较少的目的。在webpack2中,这两个插件已经被移除,因为已经内置了功能。除了压缩文本代码,你还可以:使用imagemin-webpack-plugin压缩图片,使用webpack-spritesmith合并sprite图片,并使用支持es6的js运行环境babili。以上优化点只有在构建生产环境代码时才需要用到。在开发环境时永远不要关闭它们,因为它们很耗时。优化开发体验优化开发体验主要从更快的构建和更便捷的功能入手。更快地构建并缩小文件搜索范围。webpack的resolve.modules配置模块库(通常是node_modules)的位置。js中出现import'redux'时,不是相对路径也不是绝对路径,会去node_modules目录下寻找.但是默认的配置会使用向上递归查找的方式来查找node_modules,但是通常在项目根目录下的项目目录下只有一个node_modules,为了减少查找,我们直接写node_modules的全路径:module。exports={resolve:{modules:[path.resolve(__dirname,'node_modules')]}};另外,webpack在配置loader的时候也可以缩小文件搜索范围。加载器的测试正则表达式也应该尽可能简单。比如你的项目中只有.js文件,就不要把test写成/\.jsx?$/loader只使用include***需要处理的文件,比如babel-loader的这两个配置:只babel编译项目目录下src目录下的代码{test:/\.js$/,loader:'babel-loader',include:path.resolve(__dirname,'src')}项目中的所有js目录会用babel编译,包括巨大的node_modules下的js{test:/\.js$/,loader:'babel-loader'}。启用babel-loader缓存babel编译过程比较耗时。幸运的是,babel-loader提供了缓存编译结果的选项。重启webpack时,不需要创新编译,而是重用缓存的结果,减少编译过程。babel-loader缓存机制默认是关闭的。开启配置如下:module.exports={module:{loaders:[{test:/\.js$/,loader:'babel-loader?cacheDirectory',}]}};使用aliasresolve.alias配置路径映射。大多数发布到npm的库都包含两个目录,一个是放置cmd模块的lib目录,另一个是将所有文件合并为一个文件的dist目录。大多数入口文件指向lib。默认情况下,webpack会读取lib目录下的入口文件,然后递归加载其他依赖文件。这个过程很耗时。alias配置可以让webpack直接使用dist目录下的整体文件,减少文件递归解析。配置如下:module.exports={resolve:{alias:{'moment':'moment/min/moment.min.js','react':'react/dist/react.js','react-dom':'react-dom/dist/react-dom.js'}}};使用noParsemodule.noParse来配置哪些文件可以在没有webpack的情况下被解析。有些库是自包含的,不依赖其他没有模块化的库,比如jquey、momentjs、chart.js,必须整体导入才能使用。webpack是一个模块化的打包工具,完全不需要解析这些文件的依赖关系,因为它们不依赖其他文件,而且体积非常大,所以忽略它们,配置如下:module.exports={module:{noParse:/node_modules\/(jquey|moment|chart\.js)/}};另外提速的方式有很多:使用happypack多进程并行构建使用DllPlugin复用模块更方便功能模块热替换模块热替换是指在开发过程中修改代码后,不需要刷新页面直接用旧模块替换变化的模块,使页面呈现最佳效果。webpack-dev-server内置模块热替换,配置起来也很方便。让我们以React应用程序为例。步骤如下:启动webpack-dev-server时,带上--hot参数,开启模块热替换。启动后--热后对css的改动会自动热替换,但js涉及逻辑复杂,需要进一步配置。配置页面入口文件importAppfrom'./app';functionrun(){render(,document.getElementById('app'));}run();//开发模式下只配置模块热替换if(./app编译成模块替换旧的,替换后重新执行run函数渲染最新的效果,自动生成htmlwebpack只是做了资源打包的工作,没有把这些加载到html中运行的功能,非常繁琐在庞大的app中通过手写html来加载这些资源容易出错,我们需要自动正确的加载打包好的资源。.点击链接可以看到详细文档,使用方法大致如下:demo(https://github.com/gwuhaolin/web-webpack-plugin/tree/master/demo/out-html)webpack配置module.exports={entry:{A:'./a',B:'./b',},plugins:[newWebPlugin({//输出的html文件名,必填,注意不要to复制名称,这将覆盖相互文件。filename:'index.html',//这个html文件依赖的入口必须是一个数组。依赖资源的注入顺序遵循数组的顺序。要求:['A','B'],}),]};会输出一个index.html文件,里面会自动导入入口A和B生成的js文件,输出html:scriptsrc="A.js">输出目录结构├──A.js├──B.js└──index.html管理多个页面虽然webpack适用于单页面应用,但是复杂的系统往往是由多个单页面应用组成的。每页一个功能模块。webpack提供了一个js打包的方案,但是缺乏管理多页面的能力。web-webpack-plugin的AutoWebPlugin会自动为你系统中的每个单页应用生成一个html入口页面。这个入口会自动注入当前单页应用所依赖的资源。使用它,只需要下面几行代码:plugins:[//./src/pages/代表存放所有页面的根目录,该目录下的每个目录都被视为一个单页应用//newAutoWebPlugin('./src/pages/',{//使用单页应用的html模板文件,这里可以自定义配置模板:'./src/assets/template.html',}),]查看web-webpack-插件文档获取更多分析输出结果如果你对当前的配置输出或者构建速度不满意,webpack有一个叫做webpackanalyze的工具可以直观的分析构建进一步优化构建结果和速度的可视化方式。使用它需要在执行webpack时带上--json--profile2参数,意思是让webpack将构建结果以json形式输出,并带上构建性能信息,使用方法如下:webpack--json--profile>stats。json会生成一个stats.json文件,然后打开webpackanalyze上传这个文件开始分析。***附上本文提到的webpack整体配置,分为开发环境webpack.config.js和生产环境webpack-dist.config.js