corejs从入门到踩坑完全指南
优化前:卡得不行
最近接手了一个老项目,用户反馈页面加载慢得要命,特别是在低端设备上,经常出现卡顿甚至白屏。Chrome DevTools显示JS执行时间高达15秒,bundle体积有3MB多,polyfill文件占了一半。我一看代码,core-js的引入方式简直是灾难现场,全量导入,什么版本兼容都来一份,难怪这么慢。
用户投诉说点击按钮要等好几秒才有反应,滚动也卡顿得不行。这种体验根本没法上线,必须重构core-js的使用方式。
找到瓶颈了!
用webpack-bundle-analyzer分析了一下,发现core-js占了整个bundle的40%,其中很多polyfill其实根本用不到。比如Array.from、Promise这些现代浏览器原生支持的API,却还在打补丁。而且babel-preset-env的配置也是默认全量导入,完全没有按需加载的概念。
性能监控工具显示,页面启动时大量polyfill的初始化占用了主线程,导致渲染阻塞。特别是移动端设备,CPU性能有限,这些不必要的polyfill更是雪上加霜。
按需导入是关键
第一件事就是改babel配置。以前是这样的:
// .babelrc - 老配置
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "entry",
"corejs": 3
}]
]
}
// main.js - 全量导入core-js
import 'core-js/stable';
import 'regenerator-runtime/runtime';
现在改成按需导入:
// .babelrc - 新配置
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "usage",
"corejs": 3,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}]
]
}
// main.js - 删除全量导入
// 删除这两行
// import 'core-js/stable';
// import 'regenerator-runtime/runtime';
这样babel会在编译时自动根据代码中使用的API来导入对应的polyfill,而不是一股脑全导入。比如代码里只用了Promise,就只导入Promise相关的polyfill。
手动控制polyfill
对于一些特殊的API,我还手动控制了polyfill的导入。比如项目中用到了Array.includes(),但只在某些模块需要,我就会在具体文件中导入:
// utils/array-helper.js
import 'core-js/features/array/includes';
export function checkIncludes(arr, value) {
return arr.includes(value);
}
或者针对特定浏览器的兼容问题,我还会条件导入:
// polyfills.js
if (!window.Promise) {
import('core-js/features/promise');
}
if (!Array.prototype.includes) {
import('core-js/features/array/includes');
}
这里需要注意,动态导入的polyfill要在业务代码之前执行完,否则可能会出错。
版本精简策略
原来项目为了兼容性,core-js版本很老,而且同时用了多个版本。清理后统一到core-js@3.21.1,这个版本对ES2021的支持比较完整,也不需要太多的polyfill补丁。
package.json里也做了清理:
{
"dependencies": {
"core-js": "^3.21.1",
"@babel/core": "^7.17.0",
"@babel/preset-env": "^7.16.11"
}
}
删除了原来的各种polyfill包,比如es6-promise、whatwg-fetch这些,统一用core-js管理。
构建配置优化
webpack配置也需要配合调整,确保tree-shaking生效:
// webpack.config.js
module.exports = {
optimization: {
usedExports: true,
sideEffects: ['*.css', '*.scss', './src/polyfills.js']
},
resolve: {
alias: {
'core-js': path.resolve(__dirname, 'node_modules/core-js')
}
}
};
sideEffects设置很重要,core-js的polyfill都是有副作用的,不能被摇树优化掉,所以要明确声明。
性能数据对比
优化后的效果还是很明显的:
- Bundle大小:从3.2MB降到1.1MB(减少65.6%)
- JS执行时间:从15.2秒降到4.1秒(减少73.0%)
- 首屏渲染时间:从8.3秒降到2.2秒(减少73.5%)
- polyfill文件:从1.8MB降到420KB(减少76.7%)
内存占用也有明显改善,原来平均占用180MB,现在只有85MB左右。用户体验提升很大,特别是低端设备上的表现。
当然,也不是所有polyfill都能优化掉,比如Promise、async/await相关的还是必须保留,毕竟现在大部分项目都需要。
踩坑提醒
这里我踩过几个坑,提醒一下:
第一,useBuiltIns: “usage”模式下,如果某个polyfill被动态代码分割导入,可能会导致运行时错误。解决办法是在入口文件预先导入可能用到的polyfill。
第二,某些老版本IE的兼容问题,core-js@3已经不再支持了,如果还需要支持IE11以下,就得降级到core-js@2,但这会增加很多不必要的polyfill。
第三,测试环境一定要覆盖各种目标浏览器,有些polyfill在开发环境下没问题,生产环境下可能出意外。
以上是我的优化经验
这次core-js的优化让我对前端性能有了更深的认识。有时候性能问题不是代码写的不好,而是工具配置不当导致的。按需导入确实是个好习惯,虽然配置起来稍微麻烦点,但收益很明显。
以上是我踩坑后的总结,希望对你有帮助。如果有更优的实现方式欢迎评论区交流。

暂无评论