webpack体积优化

基础

从webpack文件上来看,主要用到的有entry,output,resolve,module,plugins

  • entry入口
    用来写入口文件,SPA一般是一个入口。当然也有vendor写在这里。
  • output出口
    主要为webpack经过你设计的规则构建后输出的文件,指定输出位置,文件名等配置。
  • resolve解析
    定制你的解析规则,后面会说到。
  • module模块
    主要是装载器loaders书写的地方,webpack2已经改为rules。
  • plugins插件

详细请看配置

output

output除了用来指定输出位置外,还有一些hash和chunk的配置。hash是随机生成的,每一次都会改变。chunk这里指的是webpack分割的代码块,webpack在编译过程中会解析内容,通过内容生成chunkhash,chunkhash相对hash来说是相对不变的,我们也是利用这一点来做浏览器缓存。这里webpack提供了hash生成的算法,chunkhash失效时间等配置,一般来说我们不会用到。

主要用到的东西就是path,publicpath,filename,chunkFilename
path指的是webpack构建完成后的输出地址
publicpath指的是资源的访问地址
filename是生成文件的名字
chunkFilename非入口的文件名

resolve

resolve是webpack可定制的解析,他的配置决定了你在require或者import一个包的时候,webpack去哪里找这个文件。
resolve.modules指定查找包的文件夹,可用绝对路径和文件名。文件名的查找规则和node_modules查找规则一致。
resolve.descriptionFiles指定查找包的描述文件,一般为package.json
resolve.mainFields指定描述文件中的入口字段,一般为main

一般的解析就是通过modules查找到包,然后找包内的描述文件package.json,然后根据包内的描述文件入口字段mainFields来引入js文件
似乎mainFields指定的字段不支持Array类型,也就是说package.json中的main字段得是个String,这样你需要引入多个文件的时候就需要做个入口文件了。

loader和plugins的区别

其实他们两没有什么可比性,放在这里只是想说明一下他们在webpack中扮演的角色。
webpack是一个插件式的架构,采用Tapable事件流。
loader其实只是在webpack编译过程中的某一个时期执行,他的作用就是对符合规则的文件进行转换,比如常用的less-loader、css-loader、style-loader,less-loader先将less语法转为css,css-loader支持我们require引用css和处理css内部的import和url,style-loader则是将css文件转为style注入到页面中。loader支持链式调用,前面提到的例子就是将less转为style的一个链式调用。除此之外,各个loader之间是互不影响。
loader的调用方式分为三种,分别是命令行调用,内联调用和在配置文件webpack.config.js中配置。个人比较推荐第三种,这样对源码的影响将会是最小的。
plugins不同于loader,他贯穿整个webpack编译。利用webpack提供的很多hook,在不同的时期触发相应的操作,功能也多种多样。
感兴趣的可以了解一下webpack之loader和plugin简介webpack 源码解析

常用的loader

  • 样式:style-loader、css-loader、less-loader、sass-loader等
  • 文件:raw-loader、file-loader 、url-loader、json-loader等
  • 编译:babel-loader、coffee-loader 、ts-loader等
  • 校验测试:mocha-loader、jshint-loader 、eslint-loader等

常用的plugin

  • DefinePlugin
    全局常量定义
  • UglifyJsPlugin
    代码丑化
  • OccurenceOrderPlugin
  • HtmlWebpackPlugin
    自动生成html5文件
  • CommonsChunkPlugin
    公共代码整合
  • CompressionWebpackPlugin
    gzip压缩

详细请看awesome-webpackwebpack之loader和plugin简介webpack2.2中文文档

webpack执行流程

  • entry-option
    初始化option
  • run
    开始编译
  • make
    从entry开始递归的分析依赖,对每个依赖模块进行build
  • before-resolve - after-resolve
    对其中一个模块位置进行解析
  • build-module
    开始构建 (build) 这个module,这里将使用文件对应的loader加载
  • normal-module-loader
    对用loader加载完成的module(是一段js代码)进行编译,用 acorn 编译,生成ast抽象语法树。
  • program
    开始对ast进行遍历,当遇到require等一些调用表达式时,触发call require
    事件的handler执行,收集依赖,并。如:AMDRequireDependenciesBlockParserPlugin等
  • seal
    所有依赖build完成,下面将开始对chunk进行优化,比如合并,抽取公共模块,加hash
  • bootstrap
    生成启动代码
    emit
    把各个chunk输出到结果文件

包体积优化

包分析

通过插件webpack-bundle-analyzer进行分析。

npm i webpack-bundle-analyzer --save

这个插件在使用后会开启一个网页,一般为localhost:8888,当然这个可以配置。通过图像展示我们打包的每个文件组成,各个部分体积,总体积等一些信息。

详细的配置可以查看npm上面的文档,这里我也是用了npm的案例配置。

PS:个人认为优化还是要基于对项目的熟悉上,否则可能会无从下手。

lodash 优化

npm install babel-plugin-lodash lodash-webpack-plugin --save

const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');

const config = {
  plugins: [
    new LodashModuleReplacementPlugin({
      path: true,
      flattening: true
    })
  ]
};

.babelrc
plugins: ['transform-runtime', 'lodash'],

懒加载

npm i bundle-loader --save

require("bundle-loader?lazy&name=my-chunk!./file.js");
OR
import XXX from "bundle-loader?lazy&[name=[name]]!./file.js"

CDN优化

CDN优化依赖一个包管理器Bower

Bower算是一个很老的前端包管理器,虽说它叫包管理器,其实他只是提供了扁平化的下载和记录功能。

目前Bower已宣告终止开发,前端模块管理全面移向npm。

这里npm3已经号称是JavaScript的包管理器,而不仅仅是node的包管理器了。:)

#### 安装Bower

npm i -g bower

Bower部分命令

bower的命令和npm大同小异,以下是几个常用的命令:

  • 初始化,生成bower.json文件,功效和package.json差不多
    bower init
  • 查找包,当然也可以通过官网查找
    bower search XXX
  • 安装包,规则和npm一致,可以下载对应版本的包
    bower install XXX@XX --save

webpack和bower的连接

通过插件bower-webpack-plugin进行webpack和bower的连接。

npm i bower-webpack-plugin --save

这里其实他做的很类似resolve的alise,就是通过插件指定了require/import一个bower包的时候的地址和文件。

以下是它的配置:

var BowerWebpackPlugin = require("bower-webpack-plugin");
new BowerWebpackPlugin({
  modulesDirectories: ["bower_components"],
  manifestFiles:"bower.json",
  excludes: /.*\.less/
}),

通过modulesDirectories指向bower的依赖文件夹,manifestFiles指向入口文件。这里一般配置一般都是固定的,详细的配置可以查看NPM文档。
下面是bootstrap的bower.json文件,可以看出它是通过main字段来引入对应文件的。

bower包的引入和正常的npm包一样引入,通过import或者require,测试发现还是require效果好一点,少一点坑。

在升级到webpack2之后,发现这个插件已经不支持了,转采用bower-resolve-webpack-plugin
它的配置参考github文档,发现只能加载js文件,废弃!不过可以借鉴一下它写插件

bootstrap

bootstrap-loader用来解决bootstrap加载
查找网上webpack引入bootstrap一致推荐bootstrap-loader,测试后发现引入这个东西bootstrap会占用将近150kb的空间,记录一下
vendor占用544kb
app占用158kb

安装
npm install bootstrap-loader --save
npm install bootstrap-sass --save
npm install css-loader node-sass resolve-url-loader sass-loader style-loader url-loader --save

加载
require('bootstrap-loader')

详细请看如何把bootstrap用webpack打包

另一种办法是通过bower引入,但是bower中bootstrap给的入口文件有一个less,无法解决,只能修改less为css。这样bootstrap占用会只有不到50kb
vendor占用390kb
app占用190kb


webpack2 tree-shaking

node的import语法允许我们只引入我们需要的方法,这对于js的内存占用是一种极大的提升。同时在webpack打包的时候也利用了这种特性,只打包我们引入的方法。但是这一点和babel的转码会有冲突,具体原因看这里如下:

tree-shaking 是指借助es6 import export 语法静态性的特点来删掉export但是没有import过的东西,babel会在编译转化es6代码时把import export转换为cmd的module.export
原文请看Tree-shaking with webpack 2 and Babel 6

我们为了利用webpack2的这种特性,需要做一些配置,见原文。
经过测试,我发现这个特性能减少的包体积量几乎可以忽略不计。难怪会有此讨论->如何评价 Webpack 2 新引入的 Tree-shaking 代码优化技术?

打包速度优化

flag

webpack2入坑

  1. 入口和出口其实改动不大
  2. resolve改动会大一点,不过因为我们不经常用,所以不用太关注,这里需要注意的就是以下几点:
    • resolve.extensions配置,数组不需要在第一位加空字符串了
    • resolve.root, resolve.fallback, resolve.modulesDirectories合并为resolve.modules
  3. module语法改动
    • module.loaders改为module.rules
    • 不再支持简写loader,也就是说css以后要写成css-loader
    • 链式调用style!css!less改为use[]
  4. plugins改动
    这个就需要看各个插件的支持了,有部分插件是只支持1不支持2的,还有webpack内置插件的有部分语法改动,需要用的时候查看。改动最大的就是extract-text-webpack-plugin,不升级无法使用。

webpack2主要的变更就是对ES6的支持更好了,当然我升级到2以后除了踩了很多语法坑和插件坑之外,并没有体验到所谓的性能提升,很尴尬。利用了tree shaking特性后包的体积也并没有减小多少。只能说webpack2比1更大的规范化,并且目前webpack已经升级到了3.5,语法已经稳定,市面上的各类资料和插件也都是webpack2更完善、支持更好。

传送门

webpack之loader和plugin简介

webpack 源码解析

webpack中文官网

webpack入门(一)——webpack 介绍

[译] Webpack 2 有哪些新东西