还未看的,可以点击查看上两篇文章哟:Webpack 最佳实践总结(一)、Webpack 最佳实践总结(二)
好了,这篇是第三篇,也是完结篇,我感觉这一篇是最乱的一篇,凑合着看吧,不会让你失望的
整合 CSS 加工流
有时候,前端项目中除了 JavaScript 外,还有一个更重要的 CSS 需要我们花点精力进去。这里主要陈述一下如何将 CSS 加工流整合到 webpack 中,因为涉及 CSS Modules 的情况比较复杂,所有暂还未打算介绍更多关于 CSS Modules 的内容
CSS 工作流指什么?好的工作流可以提供开发效率,节约开发成本。这里要介绍的是 CSS 工作流中的一种很普遍的代码加工流程:正常的 CSS 业务逻辑开发流程需要经过 CSS 预处理器(如 Sass 或 Less),然后再经过后处理器(如 PostCSS)进行深加工。Sass 和 less 让我们吃上'语法糖'去快捷编写 CSS,PostCSS 可以让我们不再关心每条语句是否兼顾不同和不同版本的浏览器
在 webpack 上整合 CSS 加工流实现方式如下:
配置预处理器
这里以 Sass 作为预处理器,如下:
// webpack.config.js
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
module: {
rules: [
// ...
{
test: /\.scss$/,
exclude: /node_modules/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader', options: { minimize: true } },
'postcss-loader',
'sass-loader'
]
})
}
]
}
}
配置后处理器
这里以 PostCSS 作为后处理器,如下:
// webpack.config.js
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
module.exports = {
plugins: [
new webpack.LoaderOptionsPlugin({
options: {
postcss: [
autoprefixer({
browsers: [
'last 3 version',
'ie >= 10'
]
})
],
context: staticSourcePath
}
})
]
}
设置外联
// webpack.config.js
const ExtractTextPlugin = require('extract-text-webpack-plugin');
// 存放静态资源,诸如图片或者是 normalize.css
const staticSourcePath = path.join(__dirname, 'static');
module.exports = {
// ...
entry: {
// 设置入口文件,顺序是静态资源 -> custom.scss -> 项目里其他 scss
base: path.resolve(staticSourcePath, 'src/public/custom.scss')
},
// ...
plugins: [
// 创建 <link> 标签,并将 src 指向最终生成的 CSS 文件,需要 html-webpack-plugin
new ExtractTextPlugin({
filename: '[name].[contenthash].css',
allChunks: true
})
]
}
压缩第三方库
以 Moment.js 和 Lodash 为例
Moment.js
Moment.js(v2.18.1) 是一个用于日期的 JavaScript 库,默认情况下,只有你安装它到你的项目中,即使压缩后,也会占据217kb大小。相对于在2017年8月1日的统计,对比与 JavaScript 的 446kb 的平均大小,这是实在是太大了。不过 webpack 可以去掉 Moment.js 其中无用的代码。
其中有 165kb 的大小是用于本地化的语言包,即便你不去用它们,它们在默认的情况下也会被包含进来。如下代码来自 moment 的 gitihub
// moment/src/lib/locale/locales.js
function loadLocale(name) {
var oldLocale = null;
// TODO: Find a better way to register and load all the locales in Node
if (!locales[name] && (typeof module !== 'undefined') &&
module && module.exports) {
try {
oldLocale = globalLocale._abbr;
require('./locale/' + name);
// because defineLocale currently also sets the global locale, we
// want to undo that for lazy loaded locales
getSetGlobalLocale(oldLocale);
} catch (e) { }
}
return locales[name];
}
上面的代码会使 Moment.js 在运行期间动态地选择相应文件去加载。
要解决它需要用到 ContextReplacementPlugin,一款替换上下文的插件,例子如下:
// webpack.config.js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.ContextReplacementPlugin(
// 需要被处理的文件目录位置
/moment[\/\\]locale/,
// 正则匹配需要被包括进来的文件
/(en|zh-cn)\.js/
)
]
};
Lodash
Lodash 是一款方便开发者的 JavaScript 工具集合,测试版本为4.17.4。
当你项目包含有 Lodash 的时候,你打包出来的文件至少增加 75kb,多出来的大小包含了 316 个 Lodash 的函数。如果你只是使用了其中少数,例如 20 个,那么大概有 65 kb 是多余的。下面将列出两种去掉这些多余的代码的方法:
方法1:
还记得 webpack最佳实践(一) 提及的 Tree-shaking 吗?正因为有它,我们可以利用这个特性非常容易做到按需引用,如下:
import _ from 'lodash';
_.get();
修改为
import get from 'lodash/get';
get();
代码量从 72kb 压缩到 8.27kb
方法2:
方法1只适合刚开始玩一个项目的时候,并不怎么适合玩开了的项目,除非重写一次,这工作量太大了,另外一个原因是 lodash 的方法名会容易跟自定义的函数名冲突,造成隐藏性bug。方法2就是解决这两个问题,那就是使用babel-plugin-lodash
babel-plugin-lodash
是一款通过 babel 去实现将 lodash 的import
用法编译为最佳实践的插件,配置如下:
打开.babelrc
,添加下面配置
{
"plugins": ["lodash"]
}
更多的配置方式可以查看文档,这里不再作太多介绍。更具体的优化效果看下面:
import _ from 'lodash';
_.get({ a: { b: 5 } }, 'a.b');
上面的代码是没有使用babel-plugin-lodash
,使用之后,会被重新编译为下面:
import _get from 'lodash/get';
_get({ a: { b: 5 } }, 'a.b');
跟方法1一样,代码量从 72kb 压缩到 8.27kb
当然如果你想更进一步压缩代码,可以尝试与lodash-webpack-plugin
搭配,它会更深一步地去删除一些lodash的方法里的代码。例如_.get
默认支持深路径查询,如果你不需要支持深路径查询,你可以开启这个插件,这个方法就会被去掉这个支持:
只使用babel-plugin-lodash
import _ from 'lodash';
_.get({ a: { b: 5 } }, 'a.b');
// → returns 5
使用babel-plugin-lodash
和lodash-webpack-plugin
之后
import _get from 'lodash/get';
_get({ a: { b: 5 } }, 'a.b');
// → returns undefined
代码量从72kb 压缩到 772b
启用 scope hoisting
scope hoisting
对于 webpack 来说,就是将以前的模块引用链拍扁为一个但又不会影响到已有的代码。更好理解scope hoisting
推荐阅读:here
目前只有 webpack v3 以上版本才支持scope hoisting
,开启它是需要手动配置,如下:
// webpack.config.js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
};
其他好用的插件
preload-webpack-plugin
让静态资源支持 DNS 预解析和预加载,配置如下:
// webpack.config.js
const PreloadWebpackPlugin = require('preload-webpack-plugin');
module.exports = {
// ...
plugins: [
new PreloadWebpackPlugin({
rel: 'preload',
as: 'script',
include: 'all',
fileBlacklist: [/\.(css|map)$/, /base?.+/]
})
]
}
script-ext-html-webpack-plugin
让 js 加载方式支持 Async 或 defer,配置如下:
// webpack.config.js
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
module.exports = {
// ...
plugins: [
new ScriptExtHtmlWebpackPlugin({
defaultAttribute: 'defer'
})
]
}
总结
有点乱,不好总结,大概就是整合 CSS 代码加工流程到 webpack 中、压缩第三方库(Moment.js 和 Lodash )、启用scope hoisting
和其他好用的插件,关于基础入门,我之前写过一篇:《webpack2基础教程》
大概就这样,内容较多~
本文由 Chakhsu Lau 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。