你好,我是本文作者南一。如果有发现错误或者可完善的地方,恳请斧正,万分感谢!!
背景
近期在做构建时间优化,必须面对的问题是babel对构建的影响,借此机会深度学习下babel相关知识。
babel 基础知识
日常工作中常用的babel功能:
语法转换:将新版本JavaScript语法转换为旧的语法,因此你可以尽情使用新语法来提高编码效率,而不需要顾虑浏览器的兼容问题。
垫片填充:新ECMAScript规范带来新的API,例如ES6带来了Promise,新特性兼容性较低,于是babel会在构建过程用polyfill替换新API,支持新特性。
babel 相关依赖
整理工程中的依赖,发现与babel相关的依赖很杂乱,且有未使用依赖。为了减轻依赖负担,需要先理清每个依赖的作用:
babel核心依赖
@babel/core Babel 的核心编译器,提供代码转换的基础功能。所有 Babel 插件和预设都依赖于此包。
@babel/preset-env 智能预设,根据目标浏览器/环境自动选择需要的 Babel 插件和 polyfills。
babel插件-语法提案支持
@babel/plugin-proposal-class-properties支持类属性语法(如 class MyClass { myProp = 42 }),允许直接在类中定义实例属性。
@babel/plugin-proposal-decorators 支持装饰器语法(如 @decorator class MyClass {}),用于元编程或扩展类/方法的行为。
@babel/plugin-proposal-export-namespace-from 支持 export * as ns from 'module' 语法,允许从模块中导出命名空间。
@babel/plugin-proposal-json-strings 支持 JSON 字符串中的特殊字符转义(如 "\u2028" 和 "\u2029"),解决 JSON 与 JavaScript 字符串的兼容性问题。
@babel/plugin-proposal-numeric-separator 支持数字分隔符(如 1_000_000),提高大数字的可读性。
@babel/plugin-proposal-object-rest-spread 支持对象展开和剩余操作符(如 const { a, ...rest } = obj),用于对象解构和合并。
@babel/plugin-transform-object-rest-spread(旧版插件)支持对象展开/剩余操作符,新版代码建议使用 @babel/plugin-proposal-object-rest-spread。
@babel/plugin-syntax-dynamic-import 支持动态 import() 语法(如 import('./module').then(...)),用于代码分割。
@babel/plugin-syntax-import-meta 支持 import.meta 语法(如 import.meta.url),提供模块元信息访问。
@babel/preset-env 包含以上插件支持的功能,所以删除上述依赖❌
babel插件-Node环境相关
@babel/plugin-transform-modules-commonjs 将 ES6 模块语法转换为 CommonJS 格式(require/module.exports),使代码能在 Node.js 等环境中运行。
@babel/register 在 Node.js 运行时动态编译文件(通过 require 钩子),常用于开发环境。
工程是一个web应用,无需支持Node环境,所以删除上述依赖❌
babel插件-JSX语法支持
下面两个插件结合起来支持vue2解析和转换jsx
@babel/plugin-syntax-jsx 支持解析 JSX 语法(如 ),但不进行转换(需配合 React/Vue 的转换插件)。
babel-plugin-transform-vue-jsx 将 Vue 的 JSX 语法转换为 h() 函数调用(Vue 的虚拟 DOM 渲染函数)。
工程使用vue2框架,且使用到jsx语法,保留依赖
babel工具集成
babel-loader Webpack 的 Babel 加载器,用于在构建过程中用 Babel 转译 JavaScript 文件。
babel-eslint 允许 ESLint 解析 Babel 支持的实验性语法(如装饰器、类属性等)。
@babel/preset-typescript 支持 TypeScript 语法转译,移除类型注解并转换为标准 JavaScript。工程代码中没有使用ts,所以删除依赖❌
babel-plugin-istanbul 集成 Istanbul 代码覆盖率工具,用于生成覆盖率报告。工程代码中没有使用单测,所以删除依赖❌
babel-plugin-component 用于按需加载 UI 库组件(常见于 Element UI 等库的优化)。工程代码中是全量引入,所以删除依赖❌
@babel/plugin-transform-runtime 复用 Babel 的辅助函数,减少代码体积,避免全局污染(需配合 @babel/runtime 使用)。工程的目标浏览器是现代浏览器,且不需要支持旧版浏览器,所以删除依赖❌
最后只保留了:
npm i -D @babel/core @babel/preset-env @babel/plugin-syntax-jsx babel-plugin-transform-vue-jsx babel-loader babel-eslint
babel 配置
清理完babel依赖,接着需要合理配置babel,主要是定位目标浏览器,以及引用插件
确定目标浏览器
借助数据分析平台,查询应用近一年用户访问情况,大部分用户使用的浏览器是 chrome 和 safari 占 90%;浏览器版本:chrome >= 75 safari >= 13;因此目标浏览器设置为chrome >= 75 or safari >= 13即可覆盖大部分用户。
使用.browserslistrc 文件,配置目标浏览器
# .browserslistrc 文件中的条件是 or 的关系
> 0.5%
last 2 versions
not dead
chrome >= 75
safari >= 13
配置解析:
> .5%:支持全球使用率超过 0.5% 的浏览器,基于浏览器的市场占有率(例如来自 Can I Use 或类似数据源)。last 2 versions:包含所有浏览器的最近两个主要版本。not dead:如果某个浏览器在过去 24 个月内没有更新或几乎没有用户使用,它会被标记为not dead。
browserslist提供一个网站查看目标浏览器的用户覆盖率browsersl.ist/
配置插件
再加上必要的插件transform-vue-jsx,babel的配置就完成了。
{
loader: 'babel-loader',
options: {
plugins: ['transform-vue-jsx'],
presets: ['@babel/preset-env']
}
}
优化效果
优化前:编译时间:1min20s 产物体积:26.65MB
优化后:编译时间:44.9s 产物体积:18.47MB
时间缩短:35.1s,体积减小:8.18MB 🎉🎉🎉
总结
工程中绝大部分代码都要经过babel-loader编译,而babel主要作用就是语法转换和polyfill引入,兼容目标浏览器,根据实际情况调整配置有时可以获得意外收益。