Webpack & Babel学习
初始化
mkdir webpack-study && cd webpack-study //创建并进入项目文件夹
Babel
概念
Babel
是一个 JavaScript 编译器,主要用于把 ECMAScript 2015+ 版本语法的代码转换为 ECMAScript 5 及以下版本的语法,以便运行在当前和旧版本浏览器或其他环境中(node)
一般编译器编译过程分为 4 个阶段输入、解析、转换和输出。Babel
本身不参与解析和转换阶段,这两个阶段需要 Plugins(插件)
和 Presets(预设)
来实现
Plugins(插件)
用来告诉 babel
如何解析/转换代码。Presets(预设)
用来告诉 babel
转换代码时要使用那些新语法特性,它的本质就是一组 Plugin
的集合。
Babel
插件分为两类:
- 转换插件
- 语法插件
解析(parse)为特定类型的语法
plugins/presets 路径
如果插件用 npm 安装,那可以直接写插件的名称,babel 会自动检查 node_modules 目录里是否安装
{
"plugins":["babel-plugin-myPlugin"]
}
也可以指定插件的相对/绝对路径
{
"plugins": ["./myPlugin"]
}
还可以使用短名称
{
"plugins": [
"myPlugin",
"babel-plugin-myPlugin", // 两个插件实际是同一个
"@org/babel-plugin-name",
"@org/name" // 两个插件实际是同一个
]
}
plugins/presets 执行顺序:
插件执行的顺序是按照书写顺序依次执行的
Plugin
(插件)在Presets
前运行Plugin
(插件)顺序从前往后Preset
顺序是颠倒的(从后往前)
{
"plugins": ["transform-decorators-legacy", "transform-class-properties"]
}
如上所示,先执行 transform-decorators-legacy
,再执行 transform-class-properties
而 presets
的顺序是颠倒的
{
"presets": ["es2015", "react", "stage-2"]
}
先执行 stage-2
、react
然后是 es2015
插件参数:
插件参数由插件名和参数对象组成一个数组,可以在配置文件(.babelrc/babel.config.js)中设置
如果不指定参数,下面这几种形式都是一样的:
{
"plugins": ["pluginA", ["pluginA"], ["pluginA", {}]]
}
如果指定参数,则传递一个对象
{
"plugins": [
[
"transform-async-to-module-method",
{
"module": "blubird"
}
]
],
"presets": [
[
"env",
{
"modules": false
}
]
]
}
安装 Babel
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill
# 可选
npm install --save @babel/runtime
npm install --save-dev @babel/plugin-transform-runtime
- @babel/core
babel 的核心,必选 - @babel/cli
babel 命令行工具,执行 babel 编译 - @babel/preset-env
官方用于转换 ECMAScript 2015+语法的插件集合,可根据配置来按需加载插件 (向下支持维度) - @babel/polyfill
官方用来创建一个当前和旧版本浏览器能支持 ECMAScript 2015+语法的环境,比如让 IE8 支持Promise
。(向上支持维度) - @babel/runtime 和 @babel/plugin-transform-runtime
有时候语法的转换相对复杂,可能会需要额外的辅助函数,比如转换 ECMA 2015 的class
/* 源文件 */
class Person {}
/* 编译后文件 */
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Person = function Person() {
_classCallCheck(this, Person);
};
上面的转换需要一个_classCallCheck 辅助函数,试想假如我们多个文件中使用了class,那么每个文件都需要定义一遍
_classCallCheck 函数,这样不止代码冗余,也会增加文件大小。假如将这些辅助函数抽离到一个包当中,由所有文件共同引用那该多好,而 @babel/runtime
就是做这件事的。
我们已经知道 @babel/runtime
提供了各式各样的辅助函数,但是我们如何知道该引用那个辅助函数呢? Babel 提供了@babel/plugin-transform-runtime
插件帮我们做这件事情
同时@babel/plugin-transform-runtime
插件还为我们提供了一个沙箱环境(sandboxed environment),创建一个自己的内部作用域,防止污染全局变量,这在编写一些类库等公共设施代码的时候尤为重要。
需要注意的是:
1、两个包引入的范围不一样:一个在运行时引入,一个在开发时引入
2、plugin-transform-runtime 已经默认包括了 @babel/polyfill,因此不用在独立引入。
Babel 配置
Babel
的配置文件有两种格式 .babelrc
和 babel.config.js
.babelrc
和 bable.config.js
区别是 babel.config.js
通过编程的方式来创建配置的,.babelrc
就是一个配置文件, 相比 bable.config.js
来说是静态的
推荐使用 babel.config.js
来配置 Babel
- @babel/preset-env
指定运行环境,只有 IE8 及以上版本浏览器不支持的让ECMAScript 2015+
语法才会被转换
{
"presets": [
"@babel/preset-env",
{
"targets": {
"browsers": "ie >= 8"
},
"modules": false // 将ES 2015模块转换为其他模块规范,可选 "amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false 默认是 "auto"
}
]
}
- @babel/polyfill
按需 polyfill 用useBuiltIns
配置项来实现。
{
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"useBuiltIns": "entry",
"targets": {
"browsers": "ie >= 8"
}
}
]
]
}
useBuiltIns
的值可以是entry
和usage
。
entry
:会在入口处把所有 IE8 及以上浏览器不支持 ECMAScript 2015 的特性 polyfill 引入进来。
usage
:会扫描要转换的代码,只有代码中用到哪个新特性,它才会引入响应的 polyfill
- @babel/runtime 和 @babel/plugin-transform-runtime
{
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"targets": {
"browsers": "ie >= 8"
}
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 2
}
]
]
}
注意:
上面corejs
的值可以设置为false
或2
。为什么这样?
大家都知道 corejs 是给低版本浏览器提供接口的库,如 Promise,map,set 等。在 Babel 中你设置为 false 或者不设置,就是引入 corjs 中的库,而且是全局引入,会污染全局变量。如果不想全局引入,不要让引入的库影响全局,那就需要把 corejs 的值设为 2
还要注意一点,如果 corejs 值设为 2,那就需要再引入一个库
npm install --save core-js@2
npm install --save-dev @babel/runtime-corejs2
执行 Babel 编译命令
npx babel 源文件路径 --out-dir 编译后文件路径
webpack
概念
webpack 是一个 JavaScript 应用程序的静态模块打包器(module bundler)。
当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
entry(入口)
entry 指示 webpack 应该使用哪个模块来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点的依赖(直接和间接)。每个依赖项随机被处理,最后输出到称之为 bundules 的文件中
入口点可以有一个或多个,默认值为 ./src
output(出口)
output 指示 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist
注意,即使有多个 entry ,但只能有一个 output
loader(加载器)
loader 使 webpack 拥有处理那些非 JavaScript 文件的能力。loader 可以将所有类型的文件转换为 webpack 能够处理的模块,然后你就可以在 webpack 中处理这些模块
plugins(插件)
loader 被用于转换某些类型的模块,而 plugins 则可以用于执行范围更广的任务
mode(模式)
模式有两种,development
和 production
modules(模块)
substitutions(文件名占位符)
安装 webpack
npm install --save-dev webpack
npm install --save-dev webpack-cli //webpack命令行工具
webpack 配置
webpack 通过 webpack.config.js
文件来进行配置
// webpack。config.js
module.exports = {};
entry 配置
entry 的值可以接受字符串、对象、字符串数组
当值为字符串或对象(只有一个 key)或字符串数组时表示单个入口,如下所示
// 字符串
module.exports = {
entry: "./src/index.js",
};
// 对象
module.exports = {
entry: {
main: "./scr/index.js",
},
};
// 字符串数组
module.exports = {
entry: ["./src/polyfill.js", "./src/index.js"],
};
entry 值为数组表示数组中的文件一般是没有相互依赖关系的,但是又处于某些原因需要将它们打包在一起,如上面的 polyfill.js
和 index.js
会打包到一起
多个入口
实现多个入口配置只需要让 entry
属性值为对象就行了,一般会有两个应用场景
- 分离应用(app)和第三方库
module.exports = {
entry: {
app: "./src/index.js",
vendors: "./scr/lib/jquery.js",
},
};
分离第三方库不推荐上面的方法,可以使用 webpack 的 DllPlugin 插件
- 多页面应用(app)
module.exports = {
entry:{
pageOne:'./src/pageOne/index.js',
pageTwo:'./src/pageTwo/index.js'.
pageThree:'./src/pageThree/index.js'
}
}
output 配置
output 的值是一个对象,里面有
未完待续……
Babel 与 webpack 结合
npm install babel-loader -D
未完待续……