Webpack 已经内置了对 JavaScript 模块的处理,默认就可以加载 JS 文件,并且支持模块化开发。但是不是特别完备,并不会对 ES6+ 等代码进行转换。因此需要指定对应的 Loader 来完成想要的功能。
babel-loader
:会自动使用 Babel 工具对加载的 JavaScript 文件进行转换。可以用来转换 ES6+ 语法和 JSX 语法。
使用 babel-loader
来转换 ES6+ 语法:
- 新建
src/index.js
并编写代码。// src/index.js setTimeout(() => { console.log('Hello') })
- 运行
webpack
命令进行打包,会发现,虽然能够正常打包,但是打包后生成的文件后并没有对箭头函数进行转换。如果运行在浏览器中,不认识箭头函数语法的浏览器将会报错。
使用插件来转换 ES6+ 语法::
- 安装 Babel:
npm install @babel/core --save-dev
。 - 安装
babel-loader
:npm install babel-loader --save-dev
。 - 安装需要的 Babel 插件:
npm install @babel/plugin-transform-arrow-functions --save-dev
。 - 在
webpack.config.js
配置文件中进行配置。// webpack.config.js module.exports = { module: { rules: [ // 使用 babael-loader 对 JS 文件进行转换 { test: /\.js$/, use: { loader: 'babel-loader', options: { // 转换不同的功能就使用不同的插件。此处使用 Babel 中转换箭头函数的插件来转换箭头函数 plugins: [ '@babel/plugin-transform-arrow-functions', ] } } } ] } }
- 此时,再去运行
webpack
,会发现打包生成的文件中箭头函数被转换成 ES5 中的函数语法了。
使用预设来转换 ES6+ 语法:
@babel/preset-env
:是 Babel 提前预设好的一系列 Babel 中转换 ES6+ 语法的插件的组合,默认会根据目标浏览器来决定使用哪些插件。
-
安装
@babel/preset-env
预设:npm install @babel/preset-env --save-dev
。 -
在
webpack.config.js
配置文件中进行配置。// webpack.config.js module.exports = { module: { rules: [ // 使用 babael-loader 对 JS 文件进行转换 { test: /\.js$/, use: { loader: 'babel-loader', options: { // 使用 Babel 中的 @babel/preset-env 预设 presets: [ '@babel/preset-env', ] } } } ] } }
-
此时,运行
webpack
,会发现打包生成的文件中箭头函数也被转换成 ES5 中的函数语法了。会转换哪些语法取决于项目的目标浏览器。
设置 @babel/preset-env
预设的目标浏览器:
- 可以通过配置
@babel/preset-env
预设的 target 属性来修改目标浏览器。// webpack.config.js module.exports = { module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', // 设置 @babel/preset-env 预设的目标浏览器,优先级高于 Browserslist 中的设置 { target: ['chrome 88'], }, ] ] } } } ] } }
- 也可以通过 Browserslist 来修改目标浏览器。更推荐这种方式,因为类似 PostCSS 也会使用 Browserslist ,以便全局统一目标浏览器。
Babel7 之前的 @babel/preset-env
预设的写法:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
// Babel7 之前 @babel/preset-env 预设的写法。含义是把 es2015、react、stage-3 的代码转换成目标浏览器支持的代码
// es2015 和 stage-3 对应的就是现在的 preset-env,react 对应的就是现在的 preset-react
presets: ['es2015', 'react', 'stage-3']
}
}
}
]
}
}
从 Babel7 开始,建议使用新的预设来代替。
TC39 是指技术委员会第 39 号,它是 ECMA 机构的一部分(ECMA 是 ECMAScript 规范下的 JavaScript 语言标准化的机构)。
TC39 遵循的原则是:分阶段加入不同的语言特性,新流程涉及四个不同的 Stage:
Stage 0
:strawman 稻草人。任何尚未提交作为正式方案的讨论、想法变更或者补充都被认为是第 0 阶段的稻草人。Stage 1
:proposal 提案。提案已经被正式化,并期望解决此问题,还需要观察与其他提案的相互影响。Stage 2
:draft 草稿。提案应提供规范的草稿,此时,语言的实现着开始观察 runtime 的具体实现是否合体。Stage 3
:candidate 候补。提案成为被建议的候选提案,在这个阶段,规范的编辑人员和评审人员必须在最终规范上签字,提案不会有太大的改变,在对外发布之前只是修正一些问题。Stage 4
:finished 完成。进入这个阶段的天将包含在 ECMAScript 的下一个修订版本中。
使用 Polyfill 来处理 ES6+ 语法::
- 在
src/index.js
中编写代码。// src/index.js new Promise(function(resolve) { console.log('Hello') resolve() })
- 运行
webpack
命令进行打包,会发现,打包生成的文件中 Promise 仍然存在,但是显然有些浏览器是不认识 Promise 的,运行在那些浏览器上就会报错。
通过配置 @babel/preset-env
预设的 useBuiltIns 属性来使用 Polyfill:
Babel7.4.0 之前,可以使用 @babel/polyfill
包,但是该包现在已经不推荐使用了。
Babel7.4.0 之后,可以通过单独引入 core-js
和 regenerator-runtime
来完成 Polyfill 的使用。
安装
@babel/prolyfill
时应该使用npm install @babel/prolyfill
,而不是npm install @babel/prolyfill --save-dev
,因为@babel/prolyfill
在生产环境下也需要使用。
但是如果安装到了本地依赖下,也不会出现问题,因为 Webpack 打包时会从入口文件开始将所有依赖的东西打包进去,因此即使安装到了本地依赖下,Webpack 在打包时也会将它打包进去。
但从规范上来讲,还是应该使用npm install @babel/prolyfill
来安装@babel/prolyfill
。
安装core-js
和regenerator-runtime
同理。
- 安装
core-js
和regenarator-runtime
:npm install core-js regenerator-runtime
。 - 在
webpack.config.js
配置文件中进行配置。// webpack.config.js module.exports = { module: { rules: [ { test: /\.js$/, // 某些第三方库可能本身已经实现了 Polyfill,因此不对这部分内容使用 Polyfill,避免产生冲突 exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: [ ['@babel/preset-env', { // 设置预设以什么样的方式来使用 Polyfill useBuiltIns: 'usage', // 设置 corejs 的版本。不指定使用哪个版本的话,默认使用 V2。由于目前安装的 core-js 版本为 3.32.2,因此此处设置使用 V3 corejs: 3, }] ] } } } ] } }
- 此时,运行
webpack
命令,会发现打包生成的文件中多了实现 Promise 的代码。
通过配置 plugin-transform-runtime
插件来使用 Polyfill:
配置预设的 useBuiltIns 属性会将 Polyfill 特性放到全局作用域中;而配置
plugin-transform-runtime
插件不会将 Polyfill 特性放到全局作用域中。
因此,如果是编写一个第三方的工具库,推荐使用plugin-transform-runtime
插件,以避免污染全局的代码。
-
安装
plugin-transform-runtime
插件:npm install @babel/plugin-transform-runtime --save-dev
。 -
安装
@babel/runtime-corejs3
:npm install @babel/runtime-corejs3 --save-dev
。@babel/runtime-corejs3
提供了一组与 ECMAScript 新特性相关的运行时支持和 Polyfill。 -
在
webpack.config.js
配置文件中进行配置。// webpack.config.js module.exports = { module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { plugins: [ // 配置 plugin-transform-runtime 插件。如果此处 corejs 设置的是 2,则需要 @babel/runtime-corejs2 ['@babel/plugin-transform-runtime', {corejs: 3}] ] } } } ] }, }
-
此时,运行
webpack
命令,会发现打包生成的文件中多了实现 Promise 的代码。
使用 babel-loader
来转换 React 中的 JSX 语法:
- 安装 react 和
react-dom
:npm install react react-dom
。 - 新建
src/index.jsx
文件并编写代码。// src/index.jsx import React from 'react' import ReactDOM from 'react-dom' class App extends React.Component { state = { title: 'Hello World', } render() { return ( <div>{this.state.title}</div> ) } } ReactDOM.render(<App/>, document.getElementById('app'))
- 修改 Webpack 打包的入口文件。
// webpack.config.js module.exports = { entry: './src/index.jsx', }
- 运行
webpack
命令进行打包,会发现报错了,Webpack 不认识 JSX 语法。
使用插件来转换 React 中的 JSX 语法:
对 JSX 语法进行转换需要使用 @babel/plugin-syntax-jsx
、@babel/plugin-transform-react-jsx
、@babel/plugin-transform-react-display0name
这三个插件。
使用预设来转换 React 中的 JSX 语法:
一个一个安装配置插件比较繁琐,可以使用 Babel 中的预设 preset-react
。
- 安装
preset-react
预设:npm install @babel/preset-react --save-dev
。 - 在
webpack.config.js
配置文件中进行配置。// webpack.config.js module.exports = { entry: './src/index.jsx', module: { rules: [ { test: /\.jsx$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react'] } }, } ] } }
- 此时,运行
webpack
命令进行打包,会发现打包成功了。
使用 babel-loader
来转换 TypeScript 语法:
使用
babel-loader
和ts-loader
都能转换 TypeScript 代码,它们之前的区别是:
babel-loader
不依赖于 TypeScript Compiler,因此不需要安装 TypeScript ;ts-loader
依赖于 TypeScript Compiler ,因此需要安装 TypeScript。babel-loader
不会对类型进行校验,即使类型错误也能打包成功;ts-loader
会对类型进行校验,如果类型错误将会打包失败。- 如果 TypeScript 代码中有 Promise 等新特性,
babel-loader
可以通过使用'@babel/preset-env'
预设方便添加对应的 Polyfill,但是ts-loader
就无能为力了。
TypeScript 官方的使用建议:
- 新建
src/index.ts
并编写代码。// src/index.ts var title:string = 'Hello World' function getInfo (info: string) { console.log(info) } getInfo(title)
- 修改 Webpack 打包的入口文件。
// webpack.config.js module.exports = { entry: './src/index.ts', }
- 运行
webpack
命令进行打包,会发现报错了,Webpack 不认识 TypeScript 语法。
使用插件来处理 TypeScript 语法:
对 TypeScript 语法进行转换需要使用 @babel/transform-typescript
插件。
使用预设来处理 TypeScript 语法:
相比于插件,更推荐使用 Babel 中的 @babel/preset-typescript
预设来转换 TypeScript 语法。
- 安装
@babel/preset-typescript
预设:npm install @babel/preset-typescript --save-dev
。 - 在
webpack.config.js
配置文件中进行配置。// webpack.config.js module.exports = { entry: './src/index.ts', module: { rules: [ { test: /\.ts$/, use: { loader: 'babel-loader', options: { // 使用 @babel/preset-typescript 预设来对 ts 代码进行转换 presets: ['@babel/preset-typescript'] } } } ] }, }
- 此时,运行
webpack
命令进行打包,会发现打包成功了。
Babel 的配置文件:
可以将 Babel 的配置信息放到独立的文件中,babel-loader
会自动去加载对应的配置文件。
Babel 提供了两种配置文件的写法:
.babelrc.json
(.babelrc
/.js
/.cjs
/.mjs
)文件:早期使用较多的配置方式。但是对于配置 Monorepos 项目是比较麻烦的。babel.config.json
(.js
/.cjs
/.mjs
)文件:Babel7 中更加推荐这种方式。可以直接作用于 Monorepos 项目的子包。Monorepos:一个仓库中有多个包;Multirepos:多个仓库多个包。
目前很多项目都采用了多包管理的方式。例如:Babel、element-llus
、umi
等。
使用 Babel 的配置文件:
- 在
webpack.config.js
配置文件中配置使用babale-loader
。// webpack.config.js module.exports = { module: { rules: [ { test: /\.js$/, use: 'babel-loader', } ] } }
- 在
babel.config.js
配置文件中进行babale-loader
的具体配置。// babel.config.js module.exports = { presets: [ '@babel/preset-env' ] }