1、什么是Babel
Babel诞生于ECMAScript 2015(即ES6)标准发布之初,它诞生的最大意义就是推动了JavaScript的发展。它的作者是澳大利亚的Sebastian McKenzie。
按照官方的解释就是:
Babel通过语法转换器支持JavaScript的最新版本。这些插件允许您在不等待浏览器支持的情况下使用新语法。
也就是说Babel是一个软件“工具”,这个工具的主要职能就是帮助开发者将编写的ES6代码转换为ES5(ECMAScript 2010)。这使得开发在编写JavaScript代码的时候再也不用担心语法版本的问题造成老版本浏览器对语法不能兼容的情况。
为什么说它推动了JavaScript的发展呢?
这是因为作为Web前端开发者的都应该知道,代码的兼容性一直是一个“世界级”的难题,到目前为止都没有一款很好的CSS或JS框架能做到各浏览器厂商、新老版本的全兼容,连Bootstrap和jQuery(1~3版本兼容情况各不一样)这些“巨星级”的UI库和类库都没有办到。
虽说Babel也没有办法兼容所有的浏览器版本(所以才有babel-polyfill和babel-runtime,本文后续会介绍到这两个插件),但它却解决了从ES6标准发布以来前端开发者的一个顾虑,就是在写代码的时候终于不用去担心现在的浏览器是否支持当前ES版本的语法,可以放心大胆地去使用“酷炫”的ES6、ES6+甚至是ES7(ES Next,ES的后续版本)语法,只需保证你的Babel程序是最新的即可(对于这点,npm包管理工具可以轻松办到)。
2、开始
创建一个基本的项目文件结构,并新建必要文件,然后通过以下命令创建
package.json
程序包管理文件
npm init
或
npm init --yes
完成后目录结构大致如下:
<注意>
js
文件夹内包含文件夹
origin
表示源文件,也可以命名为
src
,是需要编译的文件,而不是直接引用的文件,
index.html
直接引用的JS文件是
common.js
3、使用命令行安装babel:
npm install -g babel-cli
然后在当前项目目录下安装:
babel命令行界面工具(主要):
npm install babel-cli --save-dev
babel预编码格式工具,也就是将要处理的格式:
npm install babel-preset-env --save-dev
ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个
npm install babel-preset-stage-0 --save-dev
npm install babel-preset-stage-1 --save-dev
npm install babel-preset-stage-2 --save-dev
npm install babel-preset-stage-3 --save-dev
4、配置
然后在根目录下新建文件
.babelrc
然后为其配置
{
"presets"
: [
"env"
, // 原es2015,如果是复制在文件里的话该段注释需要删掉
"stage-3"
]
}
5、使用
经过上面的步骤后已经可以使用babel了,首先我们在文件夹
plugin
里添加一个jQuery文件
jquery.min.js
,并在
index.html
引用。现在项目文件结构如下:
现在的
index.html
是这样的:
现在的
common_es6.js
是这样的:
现在使用Babel命令,将目录
js/origin/
下的文件编译输出到
js/
目录下:
babel ./js/origin/common_es6.js -o ./js/common.js
其中
-o
是命令
--out-file
的简写,表示输出的文件夹,按照我们的理解,连起来读就是:
“使用bable编译js文件夹内的origin文件夹内的common_es6.js文件输出到js文件夹内,名称为common.js”。
经过编译后生成的
common.js
是这样的,我们在
index.html
中引用的就是这个文件:
可以发现,我们使用ES6语法的部分,已经被转换成了标准的ES5语法,而且还对汉字部分进行了Unicode编码(这样可以加速浏览器的JavaScript引擎对页面的渲染,因为JavaScript的底层就是由Unicode组成的)。对ES6模版字符串会保留空格和换行符的特性也使用了
如果希望(其实大部分时候都是这样)Babel自动监听我们的ES6文件并在代码编辑器里按下保存按钮(或快捷键)之后自动编译,那需要在之前的命令后面再加上
--watch
这个命令,这和使用webpack里的
--watch
监听并编译打包是一样的。按照上面文件的结构,也就是写成这样:
babel ./js/origin/common_es6.js -o ./js/common.js --watch
6、生产环境中的Babel
在生产环境中,我们的JS文件往往不止一个,如果每次都要指定一个JS的文件名肯定费时费力,还比较容易出错。如果一开始的时候我们使用以下命令就可以解决这个问题:
babel ./js/origin --out-dir ./js
或简写成:
babel ./js/origin -d ./js
但需要特别说明的一点是这种“懒惰”的方式,也会带来一个不足,就是通过这种方式编译的JS文件和原JS文件名字相同。这也是为什么一开始我们就不把编写ES6的JS文件和编译后的ES5文件放同一文件夹下的原因。
当然,和上面单个文件的编译一样,仍然可以使用
--watch
这个命令:
babel ./js/origin -d ./js --watch
如果想生成Source Map(资源映射)文件,可以这样写(
--watch
之前多了一个
-s
):
babel ./js/origin -d ./js -s --watch
<“Source Map(资源映射)文件”概念解释>
这种文件以源文件加上“.map”后缀作为名称,像下方图示这样:
它可以帮助你在浏览器开发者工具(目前只有google chrome浏览器支持该功能)的“Source”选项卡中找到编译前的源文件,方便开发者进行调试。
但首先得确保你开发者工具的设置里的这一项是处于勾选状态:
好了,到了这里生产环境中Babel的使用就基本介绍完了,但存在一个问题,就是需要手动输入的命令太多太杂,稍微不注意就会产生错误,我们这不符合“前端自动化开发时代”这一定义。所以,我们需要做一些流程简化。要使Babel的使用流程简化,我们得先打开
package.json
这个文件,然后配置“scripts”这个子对象,“babel”这个属性是自定义名称的,而后面的值则是我们上一次命令行中输入的内容。
内容配置完成之后,切换到命令行窗口输入
npm run babel
即可执行之前我们配置好的
babel ./js/origin -d ./js -s --watch
命令了,这样要简单很多,而且也更加容易管理。“管理”?是的,管理,因为有的时候在大型项目中我们需要编译的可能远不止是这个文件夹中的JS文件,这个时候我们就可以这样配置
package.json
文件:
"scripts"
: {
"babel_base"
:
"babel ./js/origin -d ./js -s --watch"
,
"babel_plugin"
:
"babel ./plugin -d ./js -s --watch"
,
"babel_vue"
:
"babel ./plugin/vue -d ./js -s --watch"
,
"babel_vue_router"
:
"babel ./plugin/vue/src -d ./js -s --watch"
,
"babel_..."
:
"babel ... -d ... -s --watch"
}
这样一来,我们只需要在需要的时候用
npm run scripts[name]
来触发我们需要的命令了。
当然,
package.json
肯定不是专门为babel服务的,所有的node项目,也就是所有可以用npm安装的程序包都可以使用这个JSON配置文件,也就是说这些项目任何复杂的命令行操作几乎都可以通过设置这个JSON配置文件里的“scripts”属性来进行简化,并通过输入
npm run scripts[name]
来触发。
7、使用Webstorm编辑器来配置Babel的自动编译
Babel的“watch”命令的确可以让代码实时的编译,但我们的一个项目不是一天就能做完的。也就是说我们每天开始着手项目之前都要打开编辑器,运行浏览器,接着打开项目所在的文件件,打开命令行,输入各种命令...其实我们完全可以利用现有的编辑器来省略一些步骤,一方面减少一些每日重复的简单工作,另一方面至少可以减少一些打开的窗口(开发项目过程中,操作系统的任务栏上总有很多的窗口,而窗口数目过多导致的难以检索也会一定程度的影响开发的效率)。
Webstorm作为一款现在的Web前端开发神器,作为前端开发者可谓无人不知,除了性能以外它几乎没有缺点,就连界面主题、字体风格、各种线形、代码格式化方式、代码关键词的颜色等都是可以进行设置的。对于现代的“前端开发自动化”的支持程度也可谓是无出其右的,那么我们下面就分步骤地介绍一下在Webstorm里面怎么配置Babel的自动化编译吧。
第一步,创建以下基本目录结构及文件:
这里的文件结构有几个地方需要注意,一个是
js
文件夹下还有一个叫做
es6
的文件夹,我们写的JavaScript代码都是在这个文件夹内写,另外一个就是那个叫做
node_modules
的文件夹虽然在我们后面做程序包依赖的时候会自动生成,但是由于Webstorm这个编辑器它会自动检索项目内所有文件的缓存,而这个文件内的文件又非常非常的多,所以很多时候会导致Webstorm卡死,所以要手动先创建好这个文件夹,然后对这个文件夹右键按照下图流程排除掉Webstorm对其进行缓存检索:
需要说明的是这个步骤只是让Webstorm这个编辑器不检索这个文件夹,而对于
node_modules
及相关功能来说是没有任何影响的。
根目录下的
.babelrc
文件和
package.json
文件的配置和我们之前做的一样,以下是.babelrc文件的配置:
package.json
文件用
npm init --yes
(注意简化命令需要保证项目名符合规范)生成即可。
准备工作基本完成,接下来建立编译所需的依赖程序,分别有:
npm install babel-cli --save-dev
npm babel-preset-env --save-dev
npm babel-preset-stage-3 --save-dev
也可以简写成(为了提升速度,还是推荐使用淘宝镜像的
cnpm
命令):
npm i babel-cli babel-preset-env babel-preset-stage-3 --save-dev
完成后检查
package.json
文件:
最后一个步骤,就是对Webstorm进行配置工作了。首先将Webstorm的对JavaScript的默认标准设置为ES6的,进入设置的流程为:
依次点击Webstorm菜单栏内的 File → Default Setting
在打开的窗口中选择“Languages & Frameworks”,如图所示:
所然后将右面的“JavaScript language version”设置为“ES6”然后点击“OK”(确认)按钮保存:
接下来创建并进入项目内的
js/es6/common.js
文件,上面会有一串英文提示,大致意思是说是否要将ES6的代码使用Babel进行编译,点击“yes”即可进入配置界面,如果没有这段提示,或要修改配置可以通过菜单栏“File → Setting”,在弹出的窗口内选择“Tools”下的“File watchers”,如图所示:
然点击窗口右侧的“+”号,添加Babel监听ES6的配置:
然后会看到这个窗口,其它配置默认,点击红圈地方做ES6文件的输入及ES5文件输出的配置:
将“Argumengts”这项的配置修改为下图所以代码,然后连续点击OK(确定)即完成配置:
可以直接复制以下配置代码进去:
$ProjectFileDir$/js/es6/
--out-dir
$ProjectFileDir$/js/
--source-maps
--presets env
这里简单说一下这些配置码的作用:
$ProjectFileDir$ 表示的是当前项目的的根目录,Webstorm可以通过这段代码自动识别
--out-dir和我们之前讲解过的一样表示即将要配置的输出目录
--source-maps也就是生成资源镜像了
而--presets env就是我们需要转编码版本的预设了
配置完成了,我们现在往
js/es6/common.js
文件里简单写一点代码,然后又Ctrl+S保存:
然后在Webstorm的文件目录树上就会新出现一个
js/common.js
文件和附带的
js/common.map
文件:
这里需要说明一下这个结构,Webstorm会将CSS文件的压缩文件(扩展名前带min的,如:
style.min.css
)或资源镜像文件(
xxx.map
)都视为当前文件的子集,实际上在系统资源管理器里面,它们还是一个同级关系:
以上工作都确定完成了,那最后一步工作就是确定我们的代码是否编译成功了,打开自动编译出来的这个文件检查:
编译成功。这样我们以后每天打开这个项目都不需要再去做和Babel相关的任何配置了(因为Webstorm会通过项目根目录的那个"
.idea
"文件做设置保存),只需要把精力都集中在编写的ES6代码上就是了。
8、webpack中使用babel -- babel-loader
“babel-loader”可以让webpack去操作babel及相关插件的运行,也就是说其配置的核心基本脱离了babel的
CMD
,除了需要在
package.json
里面注入babel的必须依赖,接下来的操作都和babel无关了,因为webpack会帮你完成。而且我们在写ES6的语法和使用ES6的相关框架和技术的时候不需要再分离出不同的文件夹了(输入和输出文件夹),就像这样:
以前我们的JS文件结构是这样的:
现在我的JS文件结构就简单多了:
我们也不需要担心对原始文件的破坏,因为使用过webpack的人都知道,我们页面中使用的JS文件并不是目前文件夹中的文件,而是通过webpack打包之后自动生成的JS文件(通常我们命名作
bundle.js
或者
xxx.bundle.js
,其中“
xxx
”是分模块的自定义名称)。
当然,如果项目比较大的话,还是建议将开发文件和打包编译后的文件分离。因为使用Webpack中entry的[name]进行分离打包之后,文件也会稍微多一些。将开发文件和打包分离不仅更利于检索,在文件管理少了会减少一些“致命”的错误。
现在我们开始来谈在webpack中使用babel的步骤。
首先,还是像平常那样,在项目中通过
npm init
来创建好
package.json
文件,然后安装以下依赖:
npm install webpack --save-dev
npm install babel-loader --save-dev
npm install babel-cli --save-dev
npm install babel-preset-env --save-dev
(env是原es2015)
当然也可以一次性地为项目安装这些依赖:
npm install webpack babel-loader babel-cli babel-preset-env --save-dev
如果项目中还要安装其它需要用到的库、框架和插件的话可以按上面的方法继续进行安装。如果是项目中经常都会使用到的程序包,建议写进
package.json
配置文件的“scripts”属性里,每次新建项目的时候把这段配置复制过来即可,省去每次创建项目还要时不时去安装一个程序包的麻烦,效率上会提高非常多,像这样:
这样一来我每个项目需要安装这些依赖程序包的时候只需要输入命令
npm run contextPackage
即可,它会启动里面配置的
npm install webpack ... babel-preset-stage-3 --save-dev
命令,一次性地帮我们安装这些程序包的依赖,当启动命令之后需要做的事就是去泡杯咖啡而已(速度会比较慢一些)。当命令行程序执行完成后,在
package.json
配置文件里下方的依赖列表就可以看到这样的景象了:(注:代码中的"babel-preset-es2015"已经替换为了“babel-preset-env”,env不仅包含了es2015,还包含了es2017,即ES7的部分功能)
看到这样的景象也不要被我们以前的“传统思维”所影响,认为东西这么多,那程序得有多庞大,那页面访问的速度有多慢啊。其实这些包大多都只是在开发环境中完成它们的使命而已,在上线的时候,我们的HTML文件实际上是这样的:
我没有截错图,就是这样的,只有13行代码。
而在我们使用
webpack
命令后发现里面引用的内容:
styles.css
和
bundle.js
的体积是这么大,这就是里面两个压缩插件配合提取器的功劳。
另外,需要补充一点的是,除了之前我们在
package.json
的“scripts”里加入很长一段命令这样去“复用”我们的依赖这种方式以外,我们还可以将这个现成的
package.json
复制到其它项目里,通过输入命令“npm install”(如果安装有淘宝镜像最好使用“cnpm install”会快一些),即可安装依赖列表里指定版本的程序包了。
以下为编译基本流程,只需大致了解即可
:
- webpack检查并初始化babel-loader,调用babel及相关插件的功能并执行,将参与打包的JS文件转译为ES5规范的JS文件
- webpack检索现有的入口文件(index.js)的相关依赖,进入这些依赖列表,检索这些被依赖文件的二层依赖(也可能包含三层或更多层级依赖)
- webpack检查style-loader、css-loader、file-loader、image-webpack-loader等加载器的配置和初始化
- 使用相关的加载器将多个(也可能是一个)打包为一个CSS文件(styles.css),准备将CSS文件也打包进打包后的JS文件
- 使用“extract-text-webpack-plugin”插件剥离出CSS的文件文本内容(结构和注释保留原有的)
- 使用“optimize-css-assets-webpack-plugin”插件将剥离出的CSS文件压缩
- 当所有依赖都检索完成后,将这些依赖按照webpack的CMD规则进行打包为一个新的JS文件(bundle.js)
这一流程可以在命令行工具输入
webpack
命令后进行观察,能帮助你理解webpack使用babel编译后并打包的一个大致流程,虽然在命令行里是隐藏了babel编译那个步骤了的。
从上面的流程可以看出,过程非常的繁琐,工具量十分巨大,要人工去进行这样的操作不仅非常耗时耗力,还非常容易出错,而使用webpack+babel却只需要进行一些配置,需要进行这项工作的时候只需要在命令行内输入一段简单的命令即可(如果使用“webpack-dev-server”的话会更省事),能达到“一劳永逸”的效果。
工作完成后,生成的页面效果是这样的,里面当然也包含了一些简单的动画效果、文本处理和点击事件。
另外,还需要特别补充的一点就是利用webpack启动babel不能输入babel的一些特有的命令,比如说没有办法利用babel的命令来生成Source Map(资源映射文件),这样会对我们开发的调试带来很大的麻烦。但是我们却可以利用webpack自带的一个开发工具来实现Source Map的生成工作,只需要在
webpack.config.js
文件里加入以下配置即可:
也就是源码截图中的这句话:
//
是否生成资源映射文件(开发环境启用)
devtool
:
'#cheap-module-source-map'
在浏览器的开发者工具(google chrome)里的Sources选项卡下方就会看到一个“webpack://”的可展开菜单,这里面就是我们用ES6来编写的源文件了,我们可以在这像平时调试JS代码那样在这里为程序打断点进行调试,srouce map会帮我们自动进行代码映射。
如果你对
Webpack及其相关工具插件的使用比较有兴趣,可以在这里“https://doc.webpack-china.org/concepts”了解Webpack更多的使用。
9、支持ES6的API插件 -- babel-polyfill
虽然babel能转换普通的ES6的语法,但是对于ES6中提供的一些其它API和方法都是不能进行转换的,如:Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise,又如Array.concat((数组)--“合并数组”、JSON.stringify(JS对象)--“将JS对象转换为JSON字符串”、Object.assign(源对象,其它对象)--“合并对象”、Array.from(类似数组)--“将类似数组转换为真正的数组”等等这些方法。除此之外还非常多,具体可以查看
<
这里
>
所列
。
如果这么多ES6的东西都不能使用,那babel的“威力”和“影响力”肯定不足以让它在前端开发的领域这么火了,能够让babel成为“现代项目”里差不多算是必备的程序包也是因为有很多弥补其功能不足的插件。比如“babel-polyfill”就是这样一个存在,它可以让babel支持上述中的各种ES6中新增API和方法,甚至,它还可以使用一些ES6规范里已经提出的但是现在的浏览器还没有实现的API和方法。
但要使用它就得先安装:
npm install babel-polyfill --save-dev
按照我们的“好习惯”安装好之后记得去一趟
package.json
文件检查一下依赖是否成功。这里不需要全局安装,因为我们需要使用它的
CMD
,而且它在全局里是已经安装好的
babel-cli\node_modules
文件夹里的插件,这是这个文件夹的部分截图:
我们需要的“babel-polyfill”也在这里面。
然后根据不同的情况在当前JS文件引入babel-polyfill,如下:
/**** “babel-polyfill”
的使用方法
****/
//
在常规的
MVC
开发中(使用AMD语法)
import BabelPolyfill from
'babel-polyfill'
;
//
在
node
环境中(当然这也包括
webpack
)
require
(
'babel-polyfill'
)
;
10、支持ES6的API插件 -- babel-runtime
标题描述和上一节中“
babel-polyfill
”的一样,是因为“babel-runtime”要干的事情和“
babel-polyfill
”是差不多的,差别在于“babel-runtime”不是笼统地全局支持哪些新特性,而是有选择的使用,从而减少编译后重复性的工具函数,加快编译速度。
要使用“babel-runtime”还是得先安装到项目内:
npm install babel-runtime --save-dev
然后按需去引用需要支持ES6的功能模块,如:
require
(
'babel-runtime/core-js/promise'
)
;
require
(
'babel-runtime/core-js/set'
)
;
r
equire
(
'babel-runtime/core-js/map'
)
;
require
(
'babel-runtime/core-js/array/concat'
)
;
require
(
'babel-runtime/core-js/json/stringify'
)
;
require
(
'babel-runtime/core-js/object/assign'
)
;
require
(
'babel-runtime/core-js/
等等
'
)
;
11、node环境使用ES6 -- babel-node
babel-cli工具自带一个babel-node命令,提供一个支持ES6的REPL环境。它支持Node的REPL(
交互式解释器环境
)的所有功能,而且可以直接运行ES6代码。
它不用单独安装,而是随babel-cli一起安装。然后,执行
babel-node
就进入PEPL。
比如现有一个叫做“
test.js
”的nodejs编写的文件,需要编译的时候只需要输入以下命令即可:
$ babel-node test.js
如果需要使用
package.json
中的简化("scripts"子对象)启动命令,则需要在项目中进行安装:
npm install babel-node --save-dev
然后再配置
package.json
中的“scripts”:
"scripts"
: {
"babel_node"
:
"babel-nod
e te
st.js"
}
12、改写require命令 -- babel-register
“babel-register” 是放在 node 里使用的。它的作用是替代 node 的 require 命令,与 node 自身的 require 不同,它可以加载 es2015、jsx 等类型文件。
它的使用任然不用全局安装,只需要输入下面的命令让它自动去从全局的babel-cli里导出依赖即可:
npm install babel-register --save-dev
13、编译React使用的JSX语法——babel-plugin-syntax-jsx
该插件可以让三大主流
JS框架(AngularJS、React、Vue)之一的React所使用的JSX语法被编译为浏览器所能解释执行的ES5标准的代码(“JSX”是一种比较独特的JavaScript语法,它允许将HTML和CSS代码都通过一定的形式写进JS文件内)。
它仍然是通过
npm在项目内进行依赖,命令如下:
npm install babel-plugin-syntax-jsx --save-dev
依赖安装完成后,在
.babelrc文件内的“plugins”里面加上以下配置项:
{
"plugins"
:
[ 插件1, 插件2,...,"syntax-jsx" ]
}
配置文件里面的
“插件1”、“插件2”是babel的其它插件配置,如果没有就不做配置,不影响现有功能。