webpack和babel知识整理

Babel

Babel是一个js的转码器,主要用于将es2015+ 的代码转换为向后兼容版本的js代码,从而可以在现有的环境中运行。

工作原理:

babel是一个转译器,转译过程分为三个阶段:parsing、transforming、generating。具体转译过程如下:

1
2
3
ES6代码输入 ==》 babylon进行解析 ==》 得到AST
==》 plugin用babel-traverse对AST树进行遍历转译 ==》 得到新的AST树
==》 用babel-generator通过AST树生成ES5代码

注意:babel只会转译新标准引入的语法,比如ES6的箭头函数转译成ES5的函数 ;但是新标准中引入的新的原生对象,部分原生对象新增的原型方法,新增的API等(如Proxy、Set等),这些babel是不会转译的 ,我们需要引入polyfill来解决。

1
2
3
babylon:js的词法解析器
babel-traverse:用于对AST(抽象语法树,想了解的请自行查询编译原理)的遍历,主要给plugin用
babel-generator:根据AST生成代码

下面是转译的一个例子:

1
2
3
4
5
6
7
// 转译前
input.map(item => item + 1);

// 转译后
input.map(function (item) {
return item + 1;
});

箭头函数是es6的新特性,不能在浏览器中直接运行,babel可以将它转为普通的函数,这样就可以运行了。

.babelrc

Babel的配置文件是.babelrc,存放在项目的根目录下。使用Babel的第一步,就是配置这个文件。

该文件用来设置转码规则和插件 ,基本格式如下:

1
2
3
4
{
"presets": [], // 设定转码规则,
"plugins": []
}

为了方便,Babel团队将一些Plugins集合在一起,并称之为preset。所以一个preset是一系列plugin的总和。

经常使用的规则集如下:

1
2
3
4
5
# js转码规则
npm install --save-dev babel-preset-env

# react转码规则
npm install --save-dev babel-preset-react

es20xx的preset只转译该年份批准的标准,而env则代指最新的标准,包括了latest和es20xx各年份.

之后在presets中加上。

1
2
3
4
5
6
7
{
"presets": [
"env",
"react"
],
"plugins": []
}

babel-cli

Babel提供babel-cli工具,用于命令行转码

1
npm install --save-dev babel-cli

基本用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 转码结果输出到标准输出
babel example.js

# 转码结果写入一个文件
# --out-file 或 -o 参数指定输出文件
babel example.js --out-file compiled.js
# 或者
babel example.js -o compiled.js

# 整个目录转码
# --out-dir 或 -d 参数指定输出目录
babel src --out-dir lib
# 或者
babel src -d lib

# -s 参数生成source map文件
babel src -d lib -s

本地安装babel-cli后可以将命令配置到package.json文件中的script脚本中来执行。

1
2
3
"scripts":{
"build":"babel demo.js --out-file bundle.js"
}

babel-core

babel转译器本身,提供了babel的转译API,如babel.transform等 ,用于对代码进行转译。像webpack的babel-loader就是调用这些API来完成转译过程的。 下面是babel-core中的几个api

  • babel.transform:用于字符串转码得到AST

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /*
    * @param {string} code 要转译的代码字符串
    * @param {object} options 可选,配置项
    * @return {object}
    */
    babel.transform(code: string, options?: Object)

    //返回一个对象(主要包括三个部分):
    {
    generated code, //生成码
    sources map, //源映射
    AST //即abstract syntax tree,抽象语法树
    }
  • babel.transformFile

    1
    2
    3
    4
    5
    6
    7
    //异步的文件转码方式,回调函数中的result与transform返回的对象一至。

    babel.transformFile("filename.js", options, function(err, result){

    result; // => { code, map, ast }

    });
  • babel.transformFileSync

    1
    2
    3
    //同步的文件转码方式,返回结果与transform返回的对象一至。

    babel.transformFileSync(filename, options) // => { code, map, ast }
  • babel.transformFromAst

    1
    2
    3
    //将ast进行转译

    const { code, map, ast } = babel.transformFromAst(ast, code, options);

babel-polyfill

abel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。

举例来说,ES6在Array对象上新增了Array.from方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。

1
npm install --save-dev babel-polyfill

然后,在脚本头部,加入如下一行代码。

1
2
3
import 'babel-polyfill';
// 或者
require('babel-polyfill');

runtime

babel编译时会在每个文件生成一些需要帮助函数,如果文件比较多,那么这些重复的代码会增加编译后的代码体积 。

1
2
3
4
5
6
7
8
9
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var G = function G() {
_classCallCheck(this, G);
};

// _classCallCheck就是一个内部生成的帮助函数

使用runtime后会直接从babel-runtime中引入帮助函数,就减少了文件体积。使用方法如下:

1、首先需要安装:

1
2
3
npm install --save-dev babel-plugin-transform-runtime

npm install --save babel-runtime

2、在babelrc文件中配置:

1
2
3
4
{
"presets": ["env"],
"plugins": ["transform-runtime"]
}

babel-register

这是babel的一个注册器,它在底层改写了node的require方法,引入babel-register之后所有require并以.es6, .es, .jsx 和 .js为后缀的模块都会经过babel的转译。

Webpack

模块打包工具

安装执行命令:

npm init 初始化

npm install webpack webpack-cli -g 全局安装webpack

npm install webpack webpack-cli -D == npm install webpack webpack-cli –save -dev 将webpack安装在项目内

npx webpack -v 查看webpack安装的版本

npm info webpack 查看webpack的所有版本

npm install webpack@4.16.5 webpack-cli -D 安装指定版本的webpack

npx webpack 打包命令

npx webpack –config webpackXXX.js 使用webpackXXX作为配置文件来打包, webpack.config.js文件是默认配置文件

entry和output:

1
2
3
4
5
6
7
8
9
10
const path = require('path');

module.exports = {
mode: 'production', // mode 打包模式 production为默认模式,当值为develpoment时代码不会被压缩
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build')
}
}

当有多个入口、出口文件时,且页面引用的js文件需要加一个前缀:

1
2
3
4
5
6
7
8
9
 entry: {
main: './src/index.js',
sub: './src/index.js'
},
output: {
publicPath: 'www.cdn.com',
filename: '[name].js',
path: path.resolve(__dirname, 'build')
}

scripts脚本:package.json文件中配置

1
2
3
"scripts": {
"build": "webpack"
},

那么,打包时就可以使用命令 npm run build, 不再需要npx webpack了。

loader:

​ webpack不能识别非js的文件,所以需要用laoder来告诉webpack如何去识别其他的文件。

​ loader加载顺序:从下到上,从右到左。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module:{
rules: [
{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'file-loader',
options: {
// placeholder 占位符
name: '[name]_[hash].[ext]', //打包后的图片名
outputPath: 'images/' //打包后的路径
}
}
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module:{
rules: [
{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'url-loader',
options: {
// placeholder 占位符
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 10240
}
}
}
]
}

file-loader与url-loader是很相似的,但使用file-loader时,是将图片变为base64形式的加载,可以减少文件的加载数,通过limit可以限制,当超过10240也就是10k时使用base64形式,否则还是打包成一个图片。

1
2
3
4
5
6
7
8
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}

使用scss的话需要安装 sass-loader 和node-sass两个包。

plguins:

​ plugin可以在webpack运行到某一时刻时,自动帮我们做一些事情,像是react中的生命周期函数一样。

htmlWebpackPlugin 插件:

​ 会在打包结束后,自动生成一个html文件,并吧打包生成的js自动引入到这个 html文件中。可以在temlplate中配置模板。

cleanWebpackPlugin插件:

​ 会首先执行,删除打包的目录。之后会继续生成新的打包目录。

​ webpack4版本写法:

const { CleanWebpackPlugin } = require(‘clean-webpack-plugin’);

1
2
3
4
5
6
plugins:[
new HtmlwebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],

source-map:

当打包生成的文件出错的时候,如果不用sourcemap,我们只能知道打包后的文件出错的地方在哪里,并不能知道源代码出错的地方在哪里,sourcemap可以帮助我们做这两个的文件映射。

cheap: 只提示哪一行出错,不会提示哪一列出错。只负责业务代码中的错误。

module: 不仅负责提示业务代码出现的错误,还会提示loader中出现的错误。

eval:被压缩文件有做module 编译速度最快。

常用配置:

1
2
3
devtool:  cheap-module-eval-source-map

devtool: cheap-module-source-map

devServer:

webpack-dev-server可以帮助我们快速开发应用程序

三种方式配置。

1、watch方式: 可以幫助我們自動打包,但是我們需要自己打开浏览器去运行。

1
2


2、webpack-dev-server 方式:可以帮助我们自动打包,同时会自动打开浏览器运行。默认8080端口。

1
2
3
"scripts": {
"start": "webpack-dev-server"
}
1
2
3
4
5
6
7
devServer: {
contentBase: './build',
open: true,
proxy: {
'/api': 'http://localhost:3000'
}
},

HMR:

hot module replacement, 热更新, 在程序运行过程中,修改时不需要重新加载整个页面。会只更新变更的内容,当我们修改源代码中的js/css文件时,会立刻在浏览器中进行更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const webpack = require('webpack');
plugins:[
new webpack.HotModuleReplacementPlugin()
]

devServer: {
contentBase: './build',
open: true,
proxy: {
'/api': 'http://localhost:3000'
},
hot: true,
hotOnly: true
}

Babel配置

首先安装包

1
2
npm install --save-dev babel-loader @babel/core
npm install --save-dev @babel/preset-env

然后配置规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [['@babel/preset-env',{
useBuiltIns: 'usage'
}]]
}
}
]
}