Open cinyearchan opened 3 years ago
不要让 loader 做太多事情 以 babel-loader 为例:
module: {
rules: [{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}]
}
loader: 'babel-loader?cacheDirectory=true'
exclude 可以避免 babel-loader 对不必要的文件处理 但这个规则仅作用于这个 loader 一些类似 UglifyJsPlugin 的 webpack 插件在工作时依然会被庞大的第三方库拖累
不要放过第三方库 externals 不够聪明,一些情况下会引发重复打包的问题 CommonChunkPlugin 每次构建都会重新构建一次 vendor
DllPlugin 把第三方库单独打包到一个文件中,作为一个单纯的依赖库 这个依赖库不会跟随业务代码一起被重新打包,只有当依赖自身发生版本变化时才会重新打包 两步走:
const path = require('path')
const webpack = require('webpack')
module.exports = { entry: { // 依赖的库数组 vendor: [ 'prop-types', 'babel-polyfill', 'react', 'react-dom', 'react-router-dom' ] }, output: { path: path.join(dirname, 'dist'), filename: '[name].js', library: '[name][hash]' }, plugins: [ new webpack.DllPlugin({ // DllPlugin 的 name 属性需要和 library 保持一致 name: '[name][hash]', path: path.join(dirname, 'dist', '[name]-manifest.json'), // context 需要和 webpack.config.js 保持一致 context: __dirname }) ] }
得到 vendor-manifest.json 和 vendor.js
webpack.config.js 中对 dll 进行配置
```js
const path = require('path')
const webpack = require('webpack')
module.exports = {
module: 'production',
// 编译入口
entry: {
main: './src/index.js'
},
// 目标文件
output: {
path: path.join(__dirname, 'dist/'),
filename: '[name].js'
},
// dll 相关配置
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
// manifest 就是之前打包出来的 json 文件
manifest: require('./dist/vendor-manifest.json')
})
]
}
happypack 将 loader 由单进程转为多进程 把对 loader 的配置转移到 happypack 中,手动告诉 happypack 需要多少个并发的进程
const HappyPack = require('happypack')
// 手动创建进程池
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
module.exports = {
module: {
rules: [
...
{
test: /\.js$/,
// 问号后面的查询参数指定了处理这类文件的 HappyPack 实例的名字
loader: 'happypack/loader?id=happyBabel',
...
}
]
},
plugins: [
...
new HappyPack({
// 这个 HappyPack 的“名字”就叫做 happyBabel,和上面的查询参数对应
id: 'happyBabel',
// 指定进程池
threadPool: happyThreadPool,
loaders: ['babel-loader?cacheDirectory']
})
]
}
前置操作: 打包结果文件结构可视化—— webpack-bundle-analyzer 找出导致体积过大的原因
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
拆分资源 参考上述 DllPlugin
删除冗余代码 webpack3 中 UglifyJsPlugin
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
plugins: [
new UglifyJsPlugin({
// 允许并发
parallel: true,
// 开启缓存
cache: true,
compress: {
// 删除所有的 console 语句
drop_console: true
// 把使用多次的静态值自动定义为变量
reduce_vars: true
},
output: {
// 不保留注释
comment: false,
// 输出代码紧凑
beautify: false
}
})
]
}
webpack 4 默认使用 uglifyjs-webpack-plugin 对代码压缩 配置 optimization.minimize 与 optimization.minimizer 自定义压缩相关的操作
按需加载 不要一次加载完所有的文件内容,只加载此刻需要用到的那部分(提前做拆分) 当需要更多内容时,再对用到的内容进行即时加载
在正确的时机去触发相应的回调
webpack 配置文件
output: {
path: path.join(__dirname, '/../dist'),
filename: 'app.js',
publicPath: defaultSettings.publicPath,
// 指定 chunkFilename
chunkFilename: '[name].[chunkhash:5].chunk.js'
}
以路由代码为例
const getComponent = (location, cb) => {
require.ensure([], (require) => {
cb(null, require('../pages/BugComponent').default)
}, 'bug')
}
...
<Route path="/bug" getComponent={ getComponent } />
核心
require.ensure(dependencies, callback, chunkName)
React-Router 4 中,Code-Splitting 所用到的 Bundle-Loader,核心也是
require.ensure
开启 Gzip 压缩 request headers -> accept-encoding: gzip 原理:在一个文本文件中找出一些重复出现的字符串、临时替换它们,从而使整个文件变小
webpack 的 Gzip 和服务端的 Gzip 权衡、和谐统一
格式 | 关键词 | 优点 | 使用场景 | 缺陷 |
---|---|---|---|---|
JPEG/JPG | 有损压缩、体积小、加载快、不支持透明 | 高效压缩算法使图片轻巧,高质量压缩方式,压缩到 50% 以下仍能保住原有质量 60% | 大的背景图、轮播图、Banner图 | 处理矢量图形和 Logo 等线条感较强、颜色对比强烈的图片,人为压缩导致的图片模糊明显; 不支持透明度处理 |
PNG-8 与 PNG-24 | 无损压缩、质量高、体积小、支持透明 | 颜色:8位支持256中颜色,24位约1600万种颜色; 更强的色彩表现力,线条处理更细腻,良好的透明度支持 | 小的 Logo、颜色简单且对比强烈的图片或背景 | 体积太大 理论上,PNG-24(最佳显示效果、不在意文件体积); 实践中,规避体积问题,不用 PNG 处理较复杂的图像,遇到适合PNG的场景,优先选择PNG-8 看PNG-8输出的结果是否有肉眼可见的质量损耗,损耗是否在可接受范围内 |
SVG | 文本文件、体积小、不失真、兼容性好 | 文件体积更小、可压缩性更强、图片可无限放大而不失真、灵活性高 | 写在 HTML 里成为 DOM 的一部分; 写入以 .svg 为后缀的独立文件 | 渲染成本较高; 学习成本 |
Base64 | 文本文件、依赖编码、小图标解决方案 | 作为雪碧图的补充存在,雪碧图每次加载,都要单独向服务器请求; Base64 用于传输8Bit 字节码的编码方式,对图片进行 Base64 编码,直接将编码结果写入 HTML、CSS,减少 HTTP 请求的次数 | 图片实际尺寸很小; 图片无法以雪碧图的形式与其他小图结合(合成雪碧图是主要的减少 HTTP 请求的途径,Base64 是雪碧图的补充); 图片的更新频率非常低(不需要重复编码和修改文件内容,降低维护成本) | 编码后图片大小膨胀微原文件的 4/3,使用场景受限,不能用于大图 |
WebP | 细节丰富(JPEG) 支持透明(PNG) 支持显示动态图片(GIF) | 使用前必须考虑兼容性,准备降级方案 | 新生标准,兼容性不高,平台支持程度较低; 增加服务器的负担 |
网络层面与渲染层面
输入 URL 到显示页面整个过程,涉及网络层面:
HTTP 连接层面的优化是前端网络优化的核心