渲染优化:虚拟列表优化
老生常谈的话题,一般在视口内维护一个虚拟列表(仅渲染十几条条数据左右),监听视口位置变化,从而对视口内的虚拟列表进行控制。
在 React 中可采用以下库:
构建层优化
只介绍 Webpack
构建工具,其他构建工具也是大同小异按照此思路去配置就可以。
减少打包时间
- 避免不必要的选择,配置
include/exclude
减少Loader
对文件的搜索范围。
js
export default {
module:{
rules:[
{
exclude: /node_modules/, // node_modules 体积过于庞大,检索成本过高
include: /src/, // 根据自身情况去选择 代码一般都写在src目录下
test: /\.js$/,
use: "babel-loader"
}
]
}
}
- 编译时只编译修改过的文件,配置
cache
缓存loader
对文件的编译副本
js
// 大部分Loader/Plugin都会提供一个可使用编译缓存的选项,通常包含cache字眼。
// 以babel-loader和eslint-webpack-plugin为例。
import EslintPlugin from "eslint-webpack-plugin";
export default {
module: {
rules: [{
test: /\.js$/,
use: [{
loader: "babel-loader",
options: { cacheDirectory: true }
}]
}]
},
plugins: [ new EslintPlugin({ cache: true }) ]
};
- 定向指定必须文件路径,配置
resolve
提高文件的搜索速度,或者某些第三方库以常规形式引入可能报错或者希望程序自动索引特定类型文件都可以通过该方式解决。
js
// alias映射模块路径,extensions表明文件后缀,noParse过滤无依赖文件。通常配置alias和extensions就足够。
export default {
resolve: {
alias: {
"@": AbsPath("src"), // src目录快捷方式
swiper: "swiper/js/swiper.min.js"
}, // 模块导入快捷方式
extensions: [".js", ".ts", ".jsx", ".tsx", ".json", ".vue"] // import路径时文件可省略后缀名
}
};
-
并行构建,释放CPU多核并发的优势。配置
Thread
将Loader
单进程转换为多进程。在使用
webpack
构建项目时会有大量文件需要解析和处理,构建过程是计算密集型的操作,随着文件增多构建过程变得会越来越慢,因为运行在Node
里的Webpack
是单线程模型。可以通过thread-loader来根据CPU个数开启多线程,如果项目文件不多不建议使用,毕竟开启多个线程也会存在性能开销。
js
import Os from "os";
export default {
module: {
rules: [{
test: /\.js$/,
use: [{
loader: "thread-loader",
options: { workers: Os.cpus().length }
}, {
loader: "babel-loader",
options: { cacheDirectory: true }
}]
}]
}
};
- 分割代码减少重复代码出现的频率,分割各个模块代码,提取相同部分代码,
splitChunks
官网详细配置 这里列举一下平常用的配置
js
export default {
// ...
optimization: {
runtimeChunk: { name: "manifest" }, // 抽离WebpackRuntime函数
splitChunks: {
cacheGroups: {
common: {
minChunks: 2,
name: "common",
priority: 5,
reuseExistingChunk: true, // 重用已存在代码块
test: AbsPath("src")
},
vendor: {
chunks: "initial", // 代码分割类型
name: "vendor", // 代码块名称
priority: 10, // 优先级
test: /node_modules/ // 校验文件正则表达式
}
}, // 缓存组
chunks: "all" // 代码分割类型:all全部模块,async异步模块,initial入口模块
} // 代码块分割
}
};
- 移除重复代码和未使用代码,在
webpack
里只需将打包环境设置成生产环境就能生效。
js
export default {
mode: "production"
};
- 通过垫片服务根据UA返回当前浏览器代码垫片,好处是
无需将繁重的代码垫片打包进去
。每次构建都配置@babel/preset-env
和core-js
根据某些需求将Polyfill
打包进来,这无疑又为代码体积增加了贡献。
@babel/preset-env
提供的useBuiltIns
可按需导入Polyfill
。
- false :无视
target.browsers
将所有Polyfill
加载进来 - entry :根据
target.browsers
将部分Polyfill
加载进来(仅引入有浏览器不支持的Polyfill
,需在入口文件import "core-js/stable"
) - usage :根据
target.browsers
和检测代码里ES6的使用情况将部分Polyfill
加载进来(无需在入口文件import "core-js/stable"
)
在此推荐大家使用动态垫片
。动态垫片
可根据浏览器UserAgent
返回当前浏览器Polyfill
,其思路是根据浏览器的UserAgent
从browserlist
查找出当前浏览器哪些特性缺乏支持从而返回这些特性的Polyfill
。对这方面感兴趣的同学可参考polyfill-library和polyfill-service的源码。
在此提供两个动态垫片
服务,可在不同浏览器里点击以下链接看看输出不同的Polyfill
。相信IExplore
还是最多Polyfill
的,它自豪地说:我就是我,不一样的烟火
。
- 官方CDN服务 :polyfill.io/v3/polyfill...
- 阿里CDN服务 :polyfill.alicdn.com/polyfill.mi...
使用html-webpack-tags-plugin在打包时自动插入动态垫片
。
js
import HtmlTagsPlugin from "html-webpack-tags-plugin";
export default {
plugins: [
new HtmlTagsPlugin({
append: false, // 在生成资源后插入
publicPath: false, // 使用公共路径
tags: ["https://polyfill.alicdn.com/polyfill.min.js"] // 资源路径
})
]
};
- 按需加载,减轻首屏渲染的负担,合理利用路由懒加载,组件库按需加载,延迟加载第三方包