Skip to content

处理JS语法及校验

上一节实现了如何将js的高级语法转低级语法。继续来学习

前言

1. 公用的代码没有提取出来

更改a.js文件

js
// webpack-dev-1\src\a.js
module.exports = 'xzz'

// 在b.js增加一个类 看一下打包效果
class B {

}

使用npm run build,打包看一下效果

会有一个非常恶心的事。

image-20220109112612221

我们之前在index.js中定义的A的类有一个classCallCheck,

然后刚刚定义的类B也有一个classCallCheck,

image-20220109112944519

每次校验时,都会生成一份这样的校验代码,非常冗余。

所以,公用的代码并没有提取出来。

2 高级语法转换后的语法没有定义出来

再来看一个问题。

我们继续修改a.js

js
// webpack-dev-1\src\a.js
module.exports = 'xzz'

// 在b.js增加一个类 看一下打包效果
class B {

}

// 增加一个generator语法
function* gen(parmas) {
    yield 1
}
console.log(gen().next());

然后我们运行在浏览器看一下npm run dev,会发现一个问题

虽然我们已经将ES6转化为了ES5,但是这个generator语法是内置的api,转化的时候并不会转化api,转化之后也不会内置转换的方法,

image-20220109113737521

为什么会报错呢,原因是,打包完之后将generator进行转化了,但是转换后的方法,并没有被定义出来,

image-20220109113912755

同样的,如果用更高级的promise也不会定义,这时候就需要安装一个包来帮我们干这件事。

是一个babel代码运行时的一个包。

@babel/plugin-transform-runtime,这个包只要配置基本都会被用到

安装一下

shell
# @babel/plugin-transform-runtime -D
yarn add @babel/plugin-transform-runtime@^7.4.4 -D

同时需要一个@babel/runtime,这个包上线的时候也需要,会为一些高级语法打一些补丁,所以安装的时候,不带-D了

shell
# @babel/runtime
yarn add @babel/runtime

修改一下配置文件webpack.config.js

js
// webpack-dev-1\webpack.config.js
// webpack是node写出来的,所以使用node的语法

let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin') // 引入插件
let MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入插件

let OptimizeCss = require('optimize-css-assets-webpack-plugin') // 引入插件 压缩css的
let UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 压缩js文件的插件

module.exports = {
    // 配置优化项
    optimization: {
        minimizer: [ // 是一个数组,还得优化js
            new UglifyJsPlugin({
                cache: true, // 是否用缓存
                parallel: true, // 是否并发压缩
                sourceMap: true,// 线上用来调试的映射文件
            }),
            new OptimizeCss(), // 开启优化css后,生产模式css文件也会被压缩,但是必须配置js压缩,如果不配置,js生产模式就不会被压缩了
        ]
    },

    devServer: { // 开发服务器的配置
        port: 3000, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './build', // 以哪个文件夹作为服务的根目录 
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩

    },

    mode: 'development', // 模式,默认两种模式 production 和 development

    entry: './src/index.js', // 入口

    output: {
        // 带hash戳的文件,:8限制一下hash戳的长度
        filename: 'bundle.[hash:8].js', // 打包后的文件名
        path: path.resolve(__dirname, 'build'), // 打包后的路径,必须是一个绝对路径
    },


    plugins: [ // 是一个数组,放着所有的webpack插件
        // 插件是一个类,通过new的方式来引用
        new HtmlWebpackPlugin({
            template: './src/index.html', // 告诉插件,是以这个目录下的index.html为模板
            filename: 'index.html', // 告诉插件,打包后的文件叫什么名字,这里也叫index.html
            hash: true, // 引用的时候可以加一个hash戳
        }),
        // 插件的使用就没有先后顺序了,随便放就行
        // 引入抽离css样式的插件,
        new MiniCssExtractPlugin({
            filename: 'main.css', // 告诉插件,抽离出的样式文件的名字叫什么,这里叫main.css
        }),
    ],

    module: { // 模块
        rules: [ // 规则,在这里面配置各种loader
            {
                test: /\.js$/, // 匹配以js结尾的文件
                use: {
                    loader: 'babel-loader',
                    options: { // 用babel-loader 需要把ES6转为ES5
                        // 配置可以写在这里,还可以写在外面
                        // 在这里添加一个预设
                        presets: [ // 这是一个大插件的集合
                            '@babel/preset-env', // 这个插件就可以把ES6转ES5
                        ],
                        // 如果有一些预设的属性,需要配置一些小插件来转换还不是标准的js语法
                        plugins: [
                            ["@babel/plugin-proposal-decorators", { "legacy": true }],
                            ['@babel/plugin-proposal-class-properties', { "loose": true }],
                            "@babel/plugin-transform-runtime"
                        ]
                    }
                },
                include: path.resolve(__dirname, 'src'), // 只找src目录下的js  包括
                exclude: /node_modules/ // node_modules文件夹下的文件不用找  排除
            },

            // css-loader 解析css文件,@import语法的
            // style-loader 把解析后的css文件 插入到head标签中
            // loader有个特点,希望单一,一个loader干一件事
            /* 
                loader的用法
                1. 只用字符串,就是只用一个loader
                2. 多个loader,需要一个数组 [],数组里可以放字符串,或者对象,对象的话就可以配置loader的参数了
            */
            // loader的顺序,默认是从右向左执行,从下往上执行
            {
                test: /\.css$/,
                use: [
                    // 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    // 应该在解析css之前增加前缀
                    'postcss-loader',
                ]
            },
            {
                test: /\.less$/,
                use: [
                    // 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
                    MiniCssExtractPlugin.loader, // 若果想抽离多个文件,可以在new一个出来,一个抽离css一个抽离less都行
                    // 这里就用一个了
                    'css-loader', // 解析 @import语法 解析 css
                    // 应该在解析css之前增加前缀
                    'postcss-loader',
                    'less-loader' // 把less 转换为 css
                ]
            },
        ],
    }
}

再次打包,就会发现出现了generator的语法包

image-20220109120233976

并且会把所有的公用代码抽离出去,比如刚才的classCallCheck,所有公共引用都通过了babel_runtime_helpers来引入的。

image-20220109120516798

这里的不知道版本哪里出问题了,在浏览器运行时有报错,先放一放,继续学下面的内容

3 还有一些更高级的语法需要使用pollyfill

3 如果使用更高级的语法,还是会报错 比如ES7的语法 includes

修改a.js文件

js
// webpack-dev-1\src\a.js
module.exports = 'xzz'

// 在b.js增加一个类 看一下打包效果
class B {

}

// 增加一个generator语法
function* gen(parmas) {
    yield 1
}
console.log(gen().next());

// 增加一个es7的includes语法,如果不配置仍旧不会转换
'abc'.includes('a')

打包看下效果

image-20220109121010699

发现并没有并转换,需要安装 @babel/polyfill 不加-D,因为需要引入到代码里

shell
yarn add  @babel/polyfill

不知道版本,安个最新的吧

image-20220109121335012

把polyfill引入代码里,修改a.js文件

js
// webpack-dev-1\src\a.js
module.exports = 'xzz'

// 在b.js增加一个类 看一下打包效果
class B {

}

// 增加一个generator语法
function* gen(parmas) {
    yield 1
}
console.log(gen().next());

require('@babel/polyfill') // 引入polyfill
// 会帮我们实现一些不兼容的语法,比如下面的includes会重写

// 增加一个es7的includes语法,如果不配置仍旧不会转换
'abc'.includes('a')

再重新打包一下看下效果,帮我们自己实现了一下。

image-20220109121741282

写代码时增加校验 Eslint

我们在写代码时都需要一个校验器,校验一下代码是否符合规范。

可以使用eslint,官网里又demo,在demo处可以勾选配置规则,然后下载,导入本地,我们随便勾选几个,直接默认了,然后把下载好的文件放在根目录,文件名.

json
// webpack-dev-1\.eslintrc.json
{
    "parserOptions": {
        "ecmaVersion": 9,
        "sourceType": "script",
        "ecmaFeatures": {}
    },
    "rules": {
        "constructor-super": 2,
        "for-direction": 2,
        "getter-return": 2,
        "no-async-promise-executor": 2,
        "no-case-declarations": 2,
        "no-class-assign": 2,
        "no-compare-neg-zero": 2,
        "no-cond-assign": 2,
        "no-const-assign": 2,
        "no-constant-condition": 2,
        "no-control-regex": 2,
        "no-debugger": 2,
        "no-delete-var": 2,
        "no-dupe-args": 2,
        "no-dupe-class-members": 2,
        "no-dupe-else-if": 2,
        "no-dupe-keys": 2,
        "no-duplicate-case": 2,
        "no-empty": 2,
        "no-empty-character-class": 2,
        "no-empty-pattern": 2,
        "no-ex-assign": 2,
        "no-extra-boolean-cast": 2,
        "no-extra-semi": 2,
        "no-fallthrough": 2,
        "no-func-assign": 2,
        "no-global-assign": 2,
        "no-import-assign": 2,
        "no-inner-declarations": 2,
        "no-invalid-regexp": 2,
        "no-irregular-whitespace": 2,
        "no-loss-of-precision": 2,
        "no-misleading-character-class": 2,
        "no-mixed-spaces-and-tabs": 2,
        "no-new-symbol": 2,
        "no-nonoctal-decimal-escape": 2,
        "no-obj-calls": 2,
        "no-octal": 2,
        "no-prototype-builtins": 2,
        "no-redeclare": 2,
        "no-regex-spaces": 2,
        "no-self-assign": 2,
        "no-setter-return": 2,
        "no-shadow-restricted-names": 2,
        "no-sparse-arrays": 2,
        "no-this-before-super": 2,
        "no-undef": 2,
        "no-unexpected-multiline": 2,
        "no-unreachable": 2,
        "no-unsafe-finally": 2,
        "no-unsafe-negation": 2,
        "no-unsafe-optional-chaining": 2,
        "no-unused-labels": 2,
        "no-unused-vars": 2,
        "no-useless-backreference": 2,
        "no-useless-catch": 2,
        "no-useless-escape": 2,
        "no-with": 2,
        "require-yield": 2,
        "use-isnan": 2,
        "valid-typeof": 2
    },
    "env": {}
}

修改配置文件

js
// webpack-dev-1\webpack.config.js
// webpack是node写出来的,所以使用node的语法

let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin') // 引入插件
let MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入插件

let OptimizeCss = require('optimize-css-assets-webpack-plugin') // 引入插件 压缩css的
let UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 压缩js文件的插件

module.exports = {
    // 配置优化项
    optimization: {
        minimizer: [ // 是一个数组,还得优化js
            new UglifyJsPlugin({
                cache: true, // 是否用缓存
                parallel: true, // 是否并发压缩
                sourceMap: true,// 线上用来调试的映射文件
            }),
            new OptimizeCss(), // 开启优化css后,生产模式css文件也会被压缩,但是必须配置js压缩,如果不配置,js生产模式就不会被压缩了
        ]
    },

    devServer: { // 开发服务器的配置
        port: 3000, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './build', // 以哪个文件夹作为服务的根目录 
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩

    },

    mode: 'development', // 模式,默认两种模式 production 和 development

    entry: './src/index.js', // 入口

    output: {
        filename: 'bundle.js', // 打包后的文件名
        path: path.resolve(__dirname, 'build'), // 打包后的路径,必须是一个绝对路径
    },


    plugins: [ // 是一个数组,放着所有的webpack插件
        // 插件是一个类,通过new的方式来引用
        new HtmlWebpackPlugin({
            template: './src/index.html', // 告诉插件,是以这个目录下的index.html为模板
            filename: 'index.html', // 告诉插件,打包后的文件叫什么名字,这里也叫index.html
            hash: true, // 引用的时候可以加一个hash戳
        }),
        // 插件的使用就没有先后顺序了,随便放就行
        // 引入抽离css样式的插件,
        new MiniCssExtractPlugin({
            filename: 'main.css', // 告诉插件,抽离出的样式文件的名字叫什么,这里叫main.css
        }),
    ],

    module: { // 模块
        rules: [ // 规则,在这里面配置各种loader
            {
                test: /\.js$/,
                use: {
                    loader: 'eslint-loader',
                    options: {
                        enforce: 'pre', // loader默认从右向左 从下向上执行,增加这个属性,就代表最先执行它
                        // 默认是normal 还有一个值为 post 代表最后执行
                    }
                }
            },
            {
                test: /\.js$/, // 匹配以js结尾的文件
                use: {
                    loader: 'babel-loader',
                    options: { // 用babel-loader 需要把ES6转为ES5
                        // 配置可以写在这里,还可以写在外面
                        // 在这里添加一个预设
                        presets: [ // 这是一个大插件的集合
                            '@babel/preset-env', // 这个插件就可以把ES6转ES5
                        ],
                        // 如果有一些预设的属性,需要配置一些小插件来转换还不是标准的js语法
                        plugins: [
                            ["@babel/plugin-proposal-decorators", { "legacy": true }],
                            ['@babel/plugin-proposal-class-properties', { "loose": true }],
                            "@babel/plugin-transform-runtime"
                        ]
                    }
                },
                include: path.resolve(__dirname, 'src'), // 只找src目录下的js  包括
                exclude: /node_modules/ // node_modules文件夹下的文件不用找  排除
            },

            // css-loader 解析css文件,@import语法的
            // style-loader 把解析后的css文件 插入到head标签中
            // loader有个特点,希望单一,一个loader干一件事
            /* 
                loader的用法
                1. 只用字符串,就是只用一个loader
                2. 多个loader,需要一个数组 [],数组里可以放字符串,或者对象,对象的话就可以配置loader的参数了
            */
            // loader的顺序,默认是从右向左执行,从下往上执行
            {
                test: /\.css$/,
                use: [
                    // 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    // 应该在解析css之前增加前缀
                    'postcss-loader',
                ]
            },
            {
                test: /\.less$/,
                use: [
                    // 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
                    MiniCssExtractPlugin.loader, // 若果想抽离多个文件,可以在new一个出来,一个抽离css一个抽离less都行
                    // 这里就用一个了
                    'css-loader', // 解析 @import语法 解析 css
                    // 应该在解析css之前增加前缀
                    'postcss-loader',
                    'less-loader' // 把less 转换为 css
                ]
            },
        ],
    }
}

安装

shell
yarn add eslint@^5.16.0 eslint-loader@^2.1.2 -D

再次打包,就会出现语法校验

image-20220109122832315

总结

在配置babel时,一些高级语法或者不支持的语法,还需要配置额外的插件

比如 generator promise等

或者有一些需要polyfill的语法,直接在代码里引用第三方包

在进行代码校验时,配置eslint插件即可

参考

https://www.bilibili.com/video/BV1a4411e7Bz?p=8