Webpack实战-04-01-使用新语言来开发

193 阅读6分钟

使用ES6语言

2015年发布的下一代js语言标准,支持不全 所以一般es6编写的代码转化成目前已经支持良好的es5代码,包含以下两件事

  • 将新的es6语法用es5实现
  • 为新的api注入polyfill

认识Babel

是一个js编译器,将es6代码转化成es5代码,可以通过插件机制灵活扩展 Babel执行变异过程中,会从根目录下的.babelrc文件中读取配置。.babelrc是一个json格式文件,大致内容如下:

{
    "plugins": [
        [
            "transform-runtime",
            {
                "polyfill": false
            }
        ]
    ],
    "presets": [
        [
            "es2015",
            {
                "modules": false
            }
        ],
        "stage-2",
        "react"
    ]
}

Plugins

告诉babel要使用哪些插件,这些插件可以控制如何转换代码

transform-runtime对应的插件全名:babel-plugin-transform-runtime,即再前面加上了babel-plugin-,让bable正常运行,安装插件:npm i -D babel-plugin-transform-runtime

babel-plugin-transform-runtime插件作用是减少冗余代码

  • 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-plugin-transform-runtime来了,将原本注入js文件里面的辅助函数替换成一条导入语句var _extent = require(babel-runtime/helpers/_extent');,以此来减小babel编译出来的代码文件大小

babel-plugin-transform-runtime 和 babel-runtime配套使用,使用babel-plugin-transform-runtime后一定回使用babel-runtime,不要忘记安装babel-runtime

Presets

告诉babel要转换的源码使用了哪些新的语法特性

一个Presets对一组新语法的特性提供支持,多个Presets可以叠加 Presets其实是一组Plugins的集合,每个Plugin完成一个新语法的转换工作。

Presets是按照ECMAScript草案来组织的,通常分为三大类

  • 已经被写入ECMAScript标准里的:ES2015、ES2016、ES2017、Env 他们的包含关系如下:包含关系

  • 被社区提出来但还未写入ECMAScript标准里面的:stage0、stage1、stage2、stage3、stage4(美好激进的想法不确定会不会被纳入标准、值得被纳入标准的、特性规范已经被起草将会被纳入的、在接下来一年例会加入标准的)

  • 用于支持一些特定场景下的语法的特性,和ECMAScript标准没有关系

接入Babel

通过Loader接入

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                use: ['babel-loader'],
            },
        ]
    },
    // 输出source-map以方便直接调试es6源码
    devtool: 'source-map'
};

以上配置命中了项目目录下所有js文件。并通过babel-loader调用babel完成转换工作。

在重新执行构建前,需要先安装新引入的依赖:

# Webpack接入Babel必须依赖的模块
npm i -D babel-core babel-loader
# 根据我们的需求选择不同的Plugins或Presets
npm i -D babel-preset-env

使用TypeScript语言

认识TypeScript

TypeScript是js的一个超集,主要提供了类型监察系统和对es6语法的支持,但不支持新的api,目前没有任何环境支持运行原声的ts代码,所以必须通过构建将它转换成js代码后才能运行

ts官方提供了能将ts转化成js的编译器

在项目根目录下新建一个用于配置编译选项的tsconfig.json文件,编译器会默认读取和使用这个文件,内容如下

{
    "compilerOptions": {
        "module": "commonjs", // 编译出的代码采用的模块规范
        "target": "es5", // 编译出的代码采用es的哪个版本
        "sourceMap": true // 输出Source Map以方便调试
    },
    "exclude": [ // 不编译这些目录里的文件
        "node_modules"
    ]
}

通过npm install -g typescript安装编译器到全局后,可以通过tsc hello.ts命令编译出hello.js和hello.js.map文件

减少冗余代码

和babel相似,防止辅助函数重复出现,可以开启ts编译器的importHelpers选项,tsconfig.json修改如下

{
    "compilerOptions": {
        "importHelpers": true
    }
}

原理和babel-plugin-transform-runtime相似,会将辅助函数转换成导入语句

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

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

集成Webpack

让webpack支持ts,需要解决的问题

  • 通过Loader将ts转换成js
  • webpack在寻找模块对应文件时需要尝试ts后缀

问题一解决方法: 社区已经出现几个可用的Loader(速度更快的是awesome-typeScript-loader) 问题二解决方法: 修改默认的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'
            }
        ]
    },
    detool: 'source-map', // 输出Source Map以方便在浏览器调试ts代码
}

构建前需要安装的依赖:npm i -D typescript awesome-typescript-loader

使用Flow检查器

认识Flow

是开源的一个js静态类型检查器,是js语言的超集。(在需要的地方加上类型检查) Flow通过类型推断检查出在js代码中潜在的bug 使用效果:

// @flow
// 静态类型检查
function squarel(n: number): number {
    return n * n;
}
squarel('2'); // Error: squarel需要传入number作为参数
// 类型推断检查
function squarel2(n) {
    return n * n; // Error: 传入的string类型不能做乘法运算
}
squarel('2');

该段代码的第一行// @flow告诉Flow检查器这个文件需要被检查

使用Flow

Flow检查器➡由高性能且跨平台的OCaml语言编写,他的可执行文件可以通过 npm i -D flow-bin安装,安装完成后配置Npm Script:

"scripts": {
    "flow": "flow
}

再通过npm run flow执行代码检查。(也可以安装到全局npm i -g flow-bin

  • 采用来Flow静态类型语法的js无法直接在目前已有的js引擎中运行,要让代码可以运行,要将这些静态类型的语法去掉,如:
// 采用Flow的源代码
function f00(one: any, two: number, three?): string {}
// 去掉静态类型语法后输出代码
function foo(one, two, three) {}

有两种方式可以做到这一点

  • flow-remove-types:可单独使用,速度快
  • babel-preset-flow:与babel集成

集成webpack

使用了Flow的项目一般都会使用es6语法,所以一般借助babel

  • 安装npm i -D babel-preset-flow依赖到项目
  • 修改.babelrc配置文件,加入Flow Preset:
"pressets": {
    ...[],
    "flow"
}

向源码加入静态类型后重新构建项目,发现采用了Flow的远吗还是能在浏览器正常运行。 明确构建的目的知识去除源码中的Flow静态类型语法,而代码检查和构建无关。 许多编辑器已经整合了Flow,可以实时在代码中高亮显示Flow检查出的问题。

使用SCSS语言

认识SCSS

一种css预处理器,语法和css相似,但是加入了变量,逻辑等编程元素,代码类似这样

$blue: #1875e7;
div {
    color: $blue;
}

scss可以提升编码效率,但是必须将scss源代码编译成可以直接在浏览器匀净下运行的css代码,scss官方提供了以多种语言实现的编译器,就跟着书看一下node-sass

# 安装到全局
npm i -g node-sass
# 执行编译命令
# 将main.scss源文件编译成main.css文件
node-sass main.scss main.css

接入Webpack

webpack官方提供了sass-loader,配置如下

module.exports = {
    module: {
        rules: [
            {
                // 增加对scss文件的支持
                test: /\.scss/,
                // scss文件的处理顺序先sass-loader,再css-loader,再style-loader
                use: ['style-loader', 'css-loader', 'sass-loader'],
            }
        ]
    }
}

具体处理流程讲解:

  • 通过sass-loader将scss文件转化成css代码,再将css代码交给css-loader处理
  • css-loader会找出css代码中@import和url() 这样的导入语句,告诉webpack依赖这些资源,同时支持css modules,压缩css等功能,处理完交给style-loader处理
  • style-loader将css代码转化成字符串后,注入js代码中,通过js向dom增加样式;(如果我们将xss代码提取到一个单独文件中,而不是和js混在一起,则可以使用ExtractTextPlugin)

由于接入sass-loader,所以项目需要安装新的依赖

# 安装webpackLoader依赖
npm i -D sass-loader css-loader style-loader
# sass-loader依赖node-sass
npm i -D node-sass

使用PostCSS

认识PostCSS

是一个CSS处理工具,不同于SASS的是它可以通过插件灵活扩展其支持的属性 (可以项css自动加前缀,使用下一代css语法等)

  • postcss和css的关系就像babel和js的关系,解除了语法上的禁锢,通过插件机制扩展语言本身
  • postcss和sass的关系像babel和ts的关系,postcss更灵活,可扩张性强,scss内置了大量的功能但不能扩展 举个栗子: 1、为css自动增加前缀
/* 输入 */
h1 {
    display: flex;
}
/* 输出 */
h1 {
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
}

2、使用下一代语法

/* 输入 */
:root {
    --red: #d33;
}
h1 {
    color: var(--red);
}
/* 输出 */
h1 {
    color: #d33;
}

使用postcss启动,从postcss.config.js文件读取所需要的配置,所以需要新建该文件,文件大致内容如下

module.exports = {
    plugins: [
        // 需要使用的插件列表
        require('postcss-cssnext')
    ]
}
// postcss-cssnext插件可以让我们使用下一代css语法编写代码

接入webpack

使用了postcss后,文件的后缀还是.css,单必须将这些文件先交给postcss-loader处理一遍后再交给css-loader

module.exports = {
    module: {
        rules: [
            {
                // 使用PostCSS处理CSS文件
                test: /\.css/,
                use: ['style-loader', 'css-loader', 'postcss-loader']
            },
        ]
    },
}

介入PostCSS需要安装的依赖

# 安装webpack loader依赖
npm i -D poatcss-loader css-loader style-loader
# 根据我们使用的特性安装对应的postcss依赖
npm i -D postcss-cssnext