基础
从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-webpack、webpack之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')
另一种办法是通过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入坑
- 入口和出口其实改动不大
- resolve改动会大一点,不过因为我们不经常用,所以不用太关注,这里需要注意的就是以下几点:
- resolve.extensions配置,数组不需要在第一位加空字符串了
- resolve.root, resolve.fallback, resolve.modulesDirectories合并为resolve.modules
- module语法改动
- module.loaders改为module.rules
- 不再支持简写loader,也就是说css以后要写成css-loader
- 链式调用style!css!less改为use[]
- plugins改动
这个就需要看各个插件的支持了,有部分插件是只支持1不支持2的,还有webpack内置插件的有部分语法改动,需要用的时候查看。改动最大的就是extract-text-webpack-plugin,不升级无法使用。
webpack2主要的变更就是对ES6的支持更好了,当然我升级到2以后除了踩了很多语法坑和插件坑之外,并没有体验到所谓的性能提升,很尴尬。利用了tree shaking特性后包的体积也并没有减小多少。只能说webpack2比1更大的规范化,并且目前webpack已经升级到了3.5,语法已经稳定,市面上的各类资料和插件也都是webpack2更完善、支持更好。
传送门