webpack进阶之路三、(实战一,使用ES6、ts、Flow、SCSS)

一、使用新语言来开发项目

1、使用ES6语言

通常我们需要将采用ES6编写的代码转换成目前已经支持良好的ES5代码,包含如下:

  • 将新的ES6语法用ES5实现,例如ES6的class语法用ES5的prototype实现;
  • 为新的API注入polyfill,例如使用新的fetch API时在注入对应的polyfill后才能让低端浏览器正常运行。
  1. 认识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-runtimebabel-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标准里的最新特性。

  2. 接入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

2. 使用TypeScript语言

  1. 认识TypeScript
    TypeScript是JavaScript的一个超集,主要提供了类型检查系统和对ES6语法的支持,但不支持新的API。目前没有任何环境支持运行原生的TypeScript代码,必须通过构建将它转换成JavaScript代码后才能运行。
    下面改造一下前面用过的例子,用TypeScript重写JavaScript。由于TypeScript是JavaScript的超集,直接将后缀.js改成.ts是可以的。但为了体现出TypeScript的不同,我们在这里重写JavaScript代码,并加入类型检查:
// 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文件。

  1. 减少代码冗余
    TypeScript编译器会有与3.1节中Babel同样的问题:在将ES6语法转换成ES5语法时需要注入辅助函数。为了不让同样的辅助函数重复出现在多个文件中,可以开启TypeScript编译器的importHelpers选项,需要修改tsconfig.json文件如下:
{
	"compilerOptions": {
		"importHelpers": true	
	}
}

该选项的原理和Babel中介绍的babel-plugin-transform-runtime非常类似,会将辅助函数转换成如下导入语句:

var _tslib = require('tslib');
_tslib._extend(target);

这会导致编译出的代码依赖tslib这个迷你库,但避免了代码冗余。

  1. 集成Webpack
    要让Webpack支持TypeScript,需要解决以下两个问题。
  • 通过Loader将TypeScript转换成JavaScript。
  • Webpack在寻找模块对应的文件时需要尝试ts后缀。
    我们需要修改默认的resolve.extensions配置项。
    相关的Webpack配置如下:
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编写的源码。

3. 使用Flow检查器

  1. 认识Flow
    Flow是Facebook开源的一个JavaScript静态类型检查器,它是JavaScript语言的超集。我们所需要做的就是在需要的地方加上类型检查,例如在两个由不同的人开发的模块对接的接口处加上静态类型检查,就能在编译阶段指出部分模块使用不当的问题。同时,Flow能通过类型推断检查出在JavaScript代码中潜在的Bug。
    Flow的使用效果如下:
    // @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检查器这个文件需要被检查。
  2. 使用Flow
    以上只是让我们了解Flow的功能,下面讲解如何运行Flow来检查代码。Flow检测器由高性能且跨平台的OCaml(http://ocaml.org)语言编写,它的可执行文件可以通过npm i -D flow-bin安装,安装完成后可先配置Npm Script:
    "scripts": {
    	"flow": "flow"
    }
    
    再通过npm run flow去调用Flow执行代码检查。
    除此之外,我们还可以通过npm i -g flow-bin将Flow安装到全局,再直接通过flow命令执行代码检查。
    安装成功后,在项目根目录下执行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){}
    
    有两种方式可以做到这一点。
    • flow-remove-types (https://github.com/flowtype/flow-remove-types):可以单独使用,速度快。
    • babel-preset-flow(https://babeljs.io/docs/plugins/preset-flow):与Babel集成。
  3. 集成Webpack
    由于使用了Flow项目一般都会使用ES6语法,所以将Flow集成到使用Webpack构建的项目里的最方便方法是借助Babel。下面修改3.1节中的代码,为其加入Flow代码检查,改动如下。
    (1)安装npm i -D babel-preset-flow依赖到项目。
    (2)修改.babelrc配置文件,加入Flow Preset:
    "presets": [ ...[], "flow" ]
    向源码里加入静态类型后重新构建项目,我们会发现采用了Flow的源码还是能在浏览器中正常运行的。
    要明确构建的目的只是去除源码中的Flow静态类型语法,而代码检查和构建无关,许多编辑器已经整合了Flow,可以实时在代码中高亮显示Flow检查出的问题。

4. 使用SCSS语言

  1. 认识SCSS
    SCSS(http://sass-lang.com)可以让我们用更灵活的方式写CSS。它是一种CSS预处理器,语法和CSS相似,但加入了变量、逻辑等编程元素,代码类似这样:
    $blue: #1875e7;
    div{
    	color: $blue;
    }
    
    SCSS又叫作SASS,区别在于SASS语法类似于Ruby,而SCSS语法类似于CSS,熟悉CSS的前端工程师会更喜欢SCSS。
    采用SCSS去写CSS的好处在于,可以方便的管理代码,抽离公共的部分,通过逻辑写出更灵活的代码。和SCSS类似的CSS预处理器还有LESS(http://lesscss.org)等。
    使用SCSS可以提升编码的效率,但是必须将SCSS源代码编译成可以直接在浏览器环境下运行的CSS代码。SCSS官方提供了以多种语言实现的编译器,由于本书更倾向于前端工程师使用的技术栈,所以主要介绍node-sass(http://github.com/sass/node-sass)。
    node-sass的核心模块是用C++编写的,再用Node.js封装了一层,以提供给其他Node.js调用。node-sass还支持通过命令进行调用,先将它安装到全局:
    npm i -g node-sass
    
    再执行编译命令:
    # 将main.scss源文件编译成main.css
    node-sass main.scss main.css
    
    就能在源码同目录下看到编译后的main.css文件。
  2. 接入Webpack
    我们曾在1.4节介绍过将SCSS源代码转换成CSS代码的最佳方式是使用Loader,Webpack官方提供了对应的sass-loader(https://github.com/webpack-contrib/sass-loader)。
    Webpack接入sass-loader的相关配置如下:
    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去处理。具体处理流程如下。
    • 通过sass-loader将SCSS源码转换为CSS代码,再将CSS代码交给css-loader处理。
    • css-loader会找出CSS代码中@import和url()这样的导入语句,告诉Webpack依赖这些资源。同时支持CSS Module、压缩CSS等功能。处理完后再将结果交给style-loader处理。
    • style-loader会将CSS代码转换成字符串后,注入JavaScript代码中,通过JavaScript向DOM增加样式。如果我们想将CSS代码提取到一个单独的文件中,而不是和JavaScript混在一起,则可以使用在1.5节中介绍过的ExtractTextPlugin。
      由于接入sass-loader,所以项目需要安装这些新的依赖:
    # 安装Webpack Loader依赖
    npm i -D sass-loader css-loader style-loader
    # sass-loader依赖node-sass
    npm i -D node-sass
    

本文为学习笔记:来源《深入浅出Webpack》

你可能感兴趣的:(webpack)