Webpack打包优化

前端的打包工具从以前的browserify、grunt、gulp到现现在的rollup、webpack,涌现出了不少优秀的打包工具,而目前最火的无疑是webpack,不管是当前热门的框架仍是工具库不少都选择了它做为打包工具,所以在开发中webpack做为打包工具是一个很好的选择。在最近的项目开发中我也用到了webpack,其中也碰到了很多优化方面的问题,这里总结一下webpack打包优化的一些细节和方法。javascript

首先,此次项目用到的是vue的全家桶,在webpack的配置方面直接用的是vue-cli生成的默认配置,项目打包完成后发现生成的vendor.js文件体积特别大,其次打包过程至关缓慢,所以想尝试各类方式对其进行优化。html

定位体积大的模块

要想对打包体积进行优化,首先得找到体积大的模块,在这里咱们可使用webpack插件webpack-bundle-analyzer来查看整个项目的体积结构对比,它是以treemap的形式展示出来,很形象直观,还有一些具体的交互形式。既能够查看你项目中用到的全部依赖,也能够直观看到各个模块体积在整个项目中的占比。前端

webpack-bundle-analyzer

该插件的使用方法能够直接经过npm install webpack-bundle-analyzer --save-dev安装,并在webpack的配置信息中的plugins: [new BundleAnalyzerPlugin()]中添加便可。对于vue-cli中的配置方式,默认是安装了该插件,可是没有启用,找到config/index.js文件在build下面会有bundleAnalyzerReport的配置,默认是process.env.npm_config_report,这里建议在package.jsonscripts中添加一行"analyz": "npm_config_report=true npm run build",这样每次想启用该插件时只须要npm run analyze便可。vue

提取公共模块

对于webpack,它在模块化打包上有两点是其核心功能,一是它支持大量的模块类型,不管是TypeScriptCoffeeScript仍是sassstylus等语言它都支持,二是它能够经过配置来控制打包文件的粒度,这个下面会讲到。java

在开发中咱们每每会将全部的依赖库单独提取出来,而不与咱们的项目代码混在一块儿,这时咱们会用到一个webpack自带的插件CommonsChunkPlugin,从名字上就能够看出它是一个提取公共模块的插件。从它的文档中能够看出能够传入一个对象最为参数,在使用中经常使用的三个参数分别为:node

  • name(names)
  • minChunks
  • chunks

name好理解,指的就是最后打包文件的名字,而若是使用的是names的话,传入的必须是一个字符串数组。minChunks若是传入的是一个数字的话,指的是若是该模块被其余模块的引用次数达到了这个数值的话该模块就会被打包。若是传入的是一个函数的话,其返回值必须是布尔类型来指明这个模块是否应该被打包进公共模块。而chunks则会指定一个字符串数组,若是设置了该参数,则打包的时候只会从其中指定的模块中提取公共子模块。jquery

下面经过几个实例来讲明这个插件是如何工做的。webpack

假设有两个模块chunk1.jschunk2.js以及两个项目文件a.jsb.js,结构大体以下:web

// a.js
require('./chunk1');
require('./chunk2');
require('jquery');
// b.js
require('./chunk1');
require('./chunk2');
require('vue');
// webpack配置以下
module.exports = {
  entry: {
    main: './main.js', 
    main1: './main1.js',        
    jquery:["jquery"],     
    vue:["vue"]   
  },    
  output: {      
    path: __dirname + '/dist',   
    filename: '[name].js' 
  },   
  plugins: [  
    new CommonsChunkPlugin({   
      name: ["common","jquery","vue","load"],   
      minChunks:2 
    })   
  ] };
复制代码

最终的打包结果是:jquery被打包到jquery.jsvue被打包到vue.jscommon.js打包的是公共模块(chunk1和chunk2)。使用该插件打包时,会将知足minChunks的模块打包到name数组的第一个块里,而后数组后面的依次打包,首先从entry中找,若是没有则产生一个空块。name数组中最后一个块打包的是webpack的runtime代码,在使用的时候必须先加载该块。ajax

如今看一看vue-cli对于该插件的配置文件:

new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function (module, count) {
    // 将node_modules中的依赖模块所有提取到vendor文件中
    return (
      module.resource &&
      /\.js$/.test(module.resource) &&
      module.resource.indexOf(
        path.join(__dirname, '../node_modules')
      ) === 0
    )
  }
}),
// webpack在使用CommonsChunkPlugin时会生成一段runtime代码,而且打包进vendor中。
// 这样即便不改变vendor代码,每次打包时runtime会变化致使vendor的hash变化,这里
// 把独立的runtime代码抽离出来来解决这个问题
new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  chunks: ['vendor']
}),
复制代码

移除没必要要的文件

在项目中我经过方法必定位到几处体积占用较大的库,其中一个即是moment.js这个日期处理库。对于一个日期处理的功能,为什么这个库会占用如此大的体积,仔细查看发现当引用这个库的时候,全部的locale文件都被引入,而这些文件甚至在整个库的体积中占了大部分,所以当webpack打包时移除这部份内容会让打包文件的体积有所减少。

webpack自带的两个库能够实现这个功能:

  • IgnorePlugin
  • ContextReplacementPlugin

IgnorePlugin的使用方法以下:

// 插件配置
plugins: [
  // 忽略moment.js中全部的locale文件
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
// 使用方式
const moment = require('moment');
// 引入zh-cn locale文件
require('moment/locale/zh-cn');
moment.locale('zh-cn');
复制代码

ContextReplacementPlugin的使用方法以下:

// 插件配置
plugins: [
  // 只加载locale zh-cn文件
  new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn/),
],
// 使用方式
const moment = require('moment');
moment.locale('zh-cn');
复制代码

经过以上两种方式,moment.js的体积大体能缩减为原来的四分之一。

模块化引入

在项目中我使用了lodash这个很经常使用的工具库,然而在代码定位的时候发现这个库也占了很多的体积。仔细想一想,咱们在使用这类工具库的时候每每只使用到了其中的不多的一部分功能,但却把整个库都引入了。所以这里也能够进一步优化,只引用须要的部分。

import {chain, cloneDeep} from 'lodash';
// 能够改写为
import chain from 'lodash/chain';
import cloneDeep from 'lodash/cloneDeep';
复制代码

这样就能够只打包咱们须要的部分功能。

经过CDN引用

对于一些必要的库,但又没法对该库进行更好的体积优化的话,能够尝试经过外部引入的方式来减少打包文件的体积。采用该方法只须要在cdn站点找到须要引用的库的外部连接,以及对webpack进行简单配置便可。

// 在html中添加script引用
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
复制代码
// 这里externals的key指的是使用时须要require的包名,value指的是该库经过script引入后在全局注册的变量名
externals: {
  jquery: 'jQuery'
}
// 使用方法
require('jquery')
复制代码

经过DLLPluginDLLReferencePlugin 拆分文件

若是项目过大,打包的时间会至关的长,若是频繁更新上线则会不断对代码进行编译打包,浪费不少时间。这时咱们即可以将那些不常更新的框架和库(如vue.js等)进行单独的编译打包,这样每次开发上线就只须要对咱们的开发文件进行编译打包,这样能够极大地省去没必要要的打包时间。而这种方法须要DLLPluginDLLReferencePlugin两个插件的配合来完成。

DLLPlugin

在使用这个插件时,咱们须要单首创建一个配置文件,这里命名为webpack.dll.config.js,配置以下:

module.exports = {
  entry: {
    lib: ['vue', 'vuex', 'vue-resource', 'vue-router']
  },
  output: {
    path: path.resolve(__dirname, '../dist', 'dll'),
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath,
    library: '[name]_library'
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': '"production"'
    }),
    /** * path: manifest.json输出文件路径 * name: dll对象名,跟output.library保持一致 */ 
    new webpack.DllPlugin({
      context: __dirname,
      path: path.resolve(__dirname, '../dist/dll', 'lib.manifest.json'),
      name: '[name]_library'
    })
  ]
}
复制代码

这里要注意几点:

  1. entry中写明全部要单独打包的模块
  2. outputlibrary属性能够将dll包暴露出来
  3. DLLPlugin的配置中,path指明manifest.json文件的生成路径,name暴露出dll的函数名

运行该配置文件即可生成打包文件和manifest.json文件。

DLLReferencePlugin

对于该插件的配置,不须要像上面同样单独写配置文件,只须要在生产配置文件中添加以下代码:

new webpack.DllReferencePlugin({       
  context: __dirname,                  // 同dll配置的路径保持一致
  manifest: require('../dist/dll/lib.manifest.json') // manifest的位置
}),
复制代码

而后运行webpack,发现打包的速度获得了极大地提高,也不用每次更新代码的时候重复编译打包这些依赖库了。

其余

对于webpack的打包优化我大体就总结了上面的一些方法,而为了让页面更快的加载,有更好的用户体验,咱们并不仅是从打包上优化,也能够有其余方面的优化,这里我也简单提一下我使用过的方法。

开启Gzip压缩

开启gzip压缩能够减小HTTP传输的数据量和时间,从而减小客户端请求的响应时间,因为下降了请求时间,页面的加载速度也会获得提高,会有更快的渲染速度,极大地改善了用户体验。因为如今基本上全部的主流浏览器都支持Gzip的压缩方式,只须要对服务器进行相关设置便可,这里就不具体讲如何配置服务器。

压缩混淆代码

咱们日常也会对代码进行压缩混淆,能够经过UglifyJS等工具来对js代码进行压缩,同时能够去掉没必要要的空格、注释、console信息等,也能够有效的减少代码体积。

总结

本文到这里就结束了,主要是对webpack的打包优化部分作了些讲解,固然能力和时间有限,只研究了部分方法,可能会有其余更多的优化方法,不管是从编译打包的体积仍是速度上都能有更好的优化。接触了一段时间的webpack发现做为一个打包工具实在是过于复杂,不管从开始的官方文档仍是到新的高级特性,都很难去彻底掌握,还得须要本身不断去实践去深刻研究才行。

相关文章
相关标签/搜索