通常我们需要将采用ES6编写的代码转换成目前已经支持良好的ES5代码,包含如下:
认识Babel
Babel(https://babeljs.io) 可以方便地完成以上两件事。Babel是一个JavaScript编译器,能将ES6代码转为ES5代码转为ES5代码,让我们使用最新的语言特性而不用担心兼容性问题,并且可以通过插件机制根据需求灵活地扩展。在Babel执行编译的过程中,会从项目根目录下的 .babelrc 文件中读取配置。 .babelrc是一个JSON格式文件,内容大致如下:
{
"plugins": [
[
"transform-runtime",
{
"polyfill": false
}
]
],
"presets": [
[
"es2015",
{
"modules": false
}
],
"stage-2",
"react"
]
}
1.1 Plugins
plugins属性告诉Babel要使用哪些插件,这些插件可以控制如何转换代码。
以上配置文件里的transform-runtime
对应的插件全名叫作babel-plugin-transform-runtime
,即在前面加上了babel-plufing-
。要让Babel正常运行,我们必须先安装这个插件:npm i -D babel-plugin-transform-runtime
babel-plufin-transform-runtime
是Babel官方提供的一个插件,作用是减少冗余的代码。Babel在将ES6代码转换成ES5代码时,通常需要一些由ES5编写的辅助函数来完成新语法的实现,例如转换class extent
语法时会在转换后的ES5代码里注入_extent
辅助函数用于实现继承:
function _extent(target){
for(var i = 1; i < arguments.length; i++){
var source = arguments[i];
for(var key in source){
if(Object.prototype.hasOwnProperty.call(source, key)){
target[key] = source[key];
}
}
}
return target;
}
这个导致每个使用class extent
语法的文件都被注入重复的_extent辅助函数代码,babel-plufin-transform-runtime
的作用在于将原本注入JavaScript文件里的辅助函数替换成一条导入语句:
var _extent = require('babel-runtime/helpers/_extent');
这样能减少Babel编译出来的代码的文件大小。
同时需要注意的是,由于babel-plugin-transform-runtime
注入了require('babel-runtime/helpers/_extent')
语句到编译后的代码里,需要安装babel-runtime
依赖到我们的项目后,代码才能正常运行。也就是说babel-plugin-tranform-runtime
和babel-runtime
需要配套使用,在使用babel-plugin-transform-runtime
后一定需要使用babel-runtime
。
1.2 Presets
presets属性告诉Babel要转换的源码使用了哪些新的语法特性,一个Presets对一组新语法的特性提供了支持,多个Presets可以叠加。Presets其实是一组Plugins的集合,每个Plugin完成一个新语法的转换工作。Presets是按照ECMAScript草案来组织的,通常可以分为三大类。
(1)已经被写入ECMAScript标准里的特性,由于之前每年都有新特性被加入到标准里,所以又可细分如下。
+ ES2015(https://babeljs.io/docs/plugins/preset-es2015/): 包含在2015年加入的新特性。
+ ES2016(https://babeljs.io/docs/plugins/preset-es2016/): 包含在2016年加入的新特性。
+ ES2017 (https://babeljs.io/docs/plugins/preset-es2017/): 包含在2017年加入的新特性。
+ Env(https://babeljs.io/docs/plugins/preset-env),包含当前所有ECMAScript标准里的最新特性。
接入Babel
在了解Babel后,下一步就需要知道如何在Webpack中使用它。由于Babel所做的事情是转换代码,所以应该通过Loader去接入Babel。Webpack的配置如下:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader'],
},
]
},
// 输出source-map以方便直接调试ES6源码
devtool: 'source-map'
}
以上配置命中了项目目录下的所有JavaScript文件,并通过babel-loader调用Babel完成转换工作。在重新执行构建前,需要先安装新引入的依赖:
# Webpack 接入Babel必须依赖的模块
npm i -D babel-core babel-loader
# 根据我们的需求选择不同的Plugins或Presets
npm i -D babel-preset-env
// show.ts
// 通过DOM元素,将content显示到网页上
// 通过ES6模块规范导出show函数
// 为show函数增加类型检查
export function show(content: string){
window.document.getElementById('app').innerText = 'Hello,' + content;
}
// main.ts
// 通过ES6模块规范导入show函数
import { show } from './show';
// 执行show函数
show('Webpack');
TypeScript官方提供了能将TypeScript转换成JavaScript的编译器。我们需要在当前项目的根目录下新建一个用于配置编译选项的tsconfig.json文件,编译器默认会读取和使用这个文件,配置文件的内容大致如下:
{
“compilerOptions”: {
"module": "commonjs", // 编译出的代码采用的模块规范
"target": "es5", // 编译出的代码采用ES的哪个版本
"sourceMap": true // 输出Source Map以方便调试
},
"exclude": [ // 不编译这些目录里的文件
"node_modules"
]
}
通过npm install -g typescript 安装编译器到全局后,可以通过tschello.ts命令编译出hello.js和hello.js.map文件。
{
"compilerOptions": {
"importHelpers": true
}
}
该选项的原理和Babel中介绍的babel-plugin-transform-runtime非常类似,会将辅助函数转换成如下导入语句:
var _tslib = require('tslib');
_tslib._extend(target);
这会导致编译出的代码依赖tslib这个迷你库,但避免了代码冗余。
const path = require('path');
module.exports = {
// 执行入口文件
entry: './main',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist'),
},
resolve: {
// 先尝试以ts为后缀的TypeScript源码文件
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader'
}
]
},
devtool: 'source-map', // 输出Source Map以方便在浏览器里调试TypeScript代码
}
在运行构建前需要安装上面用到的依赖:
npm i -D typescript awesome-typescript-loader
安装成功后重新执行构建,我们将会在dist目录下看到输出的JavaScript文件bundle.js,以及对应的Source Map文件bundle.js.map。在浏览器里打开index.html页面后,可以在开发工具里看到和调试用TypeScript编写的源码。
// @flow
// 静态类型检查
function squarel(n: number): number {
return n * n;
}
squarel('2'); // Error: squarel需要传入number作为参数
// 类型推断检查
function square2(n){
return n * n; // Error: 传入的string类型不能做乘法运算
}
square2('2');
需要注意的是,该段代码的第一行//@flow告诉Flow检查器这个文件需要被检查。npm i -D flow-bin
安装,安装完成后可先配置Npm Script:"scripts": {
"flow": "flow"
}
再通过npm run flow
去调用Flow执行代码检查。npm i -g flow-bin
将Flow安装到全局,再直接通过flow命令执行代码检查。Error: show.js:6
6: export function show(content){
^^^^^^^ parameter `content`. Missing annotation Found 1 error
}
采用了Flow静态类型语法的JavaScript,是无法直接在目前已有的JavaScript引擎中运行的,要让代码可以运行,需要将这些静态类型的语法去掉。例如:// 采用Flow的源代码
function foo(one: any, two: number, three?): string{}
// 去掉静态类型语法后输出代码
function foo(one, two, three){}
有两种方式可以做到这一点。
npm i -D babel-preset-flow
依赖到项目。"presets": [ ...[], "flow" ]
$blue: #1875e7;
div{
color: $blue;
}
SCSS又叫作SASS,区别在于SASS语法类似于Ruby,而SCSS语法类似于CSS,熟悉CSS的前端工程师会更喜欢SCSS。npm i -g node-sass
再执行编译命令:# 将main.scss源文件编译成main.css
node-sass main.scss main.css
就能在源码同目录下看到编译后的main.css文件。module.exports = {
module: {
rules: [
{
// 增加对SCSS文件的支持,
test: /\.scss/,
// SCSS文件的处理顺序为先sass-loader,再css-loader,再style-loader
use: ['style-loader', 'css-loader', 'sass-loader'],
},
]
}
}
以上配置通过正则/.scss/匹配所有以.scss为后缀的SCSS文件,再分别使用3个Loader去处理。具体处理流程如下。
# 安装Webpack Loader依赖
npm i -D sass-loader css-loader style-loader
# sass-loader依赖node-sass
npm i -D node-sass
本文为学习笔记:来源《深入浅出Webpack》