Skip to content

转化ES6语法

本文开始看如何在webpack中处理js模块。

我们希望把ES6或者更高级的语法转换为ES5,就需要用到babel,babel就是用来转化我们的js的。

修改index.js文件,增加一些ES6的语法

js
// webpack-dev-1\src\index.js
let str = require('./a.js')

console.log('hello cheny');

console.log(str);

require('./index.css')

require('./index.less')

// 增加个箭头函数
let fn = () => {
    console.log('cheny && xzz');
}
fn()

然后我们先什么也不配,打包一下来看看,现在的配置文件如下

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
            // 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
                ]
            },
        ],
    }
}

开始打包,npm run build

image-20220108104800040

我们发现打包出来的仍然是ES6语法,并没有做任何转换。

使用babel来转换

  1. 我们希望转换语法,肯定先需要一个loader,babel-loader
  2. 转化的时候肯定还需要用到我们的babel,核心模块的名字叫 @babel/core,这个babel的核心模块会调用transform方法来对代码进行转化。
  3. 那我们还必须知道该如何转化,所以还有一个转化模块,叫 @babel/preset-env,可以把一些高级的语法转换为低级的语法

我们先来安装一下:

shell
# @babel/core babel-loader @babel/preset-env -D
yarn add @babel/core@^7.4.5 babel-loader@^8.0.6 @babel/preset-env@^7.4.5 -D

修改配置文件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
                        ]
                    }
                }
            },

            // 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
                ]
            },
        ],
    }
}

修改完配置文件后,把babel-loader给配置好,再来重新打包试一下 npm run build

我们发现,箭头函数已经被转换为普通函数了。

image-20220108110002804

转换更高级的语法

我们再修改index.js

js
// webpack-dev-1\src\index.js
let str = require('./a.js')

console.log('hello cheny');

console.log(str);

require('./index.css')

require('./index.less')

// 增加个箭头函数
let fn = () => {
    console.log('cheny && xzz');
}
fn()

class A { // let a = new A() a.a = 1
    a = 1 // 相当于给实例加了一个属性
}
let a = new A()
console.log(a.a, '22');

添加一些预设的语法,会需要配置一些预设的插件。

(现在好像不需要了,这种语法应该已经通过提案了,不过可以学习一下配置)

安装插件 @babel/plugin-proposal-class-properties

shell
# @babel/plugin-proposal-class-properties -D
yarn add @babel/plugin-proposal-class-properties@^7.4.4 -D

修改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-class-properties'
                        ]
                    }
                }
            },

            // 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
                ]
            },
        ],
    }
}

比如更高级的语法,装饰器,还是预设的语法,我们修改index.js

js
// webpack-dev-1\src\index.js
let str = require('./a.js')

console.log('hello cheny');

console.log(str);

require('./index.css')

require('./index.less')

// 增加个箭头函数
let fn = () => {
    console.log('cheny && xzz');
}
fn()

@log
class A { // let a = new A() a.a = 1
    a = 1 // 相当于给实例加了一个属性
}
let a = new A()
console.log(a.a, '22');

function log(target) {
    console.log(target, '26');
}

这时候再运行会报错,提示

image-20220108111556121

如果想支持这种扩展语法,需要安装 @babel/plugin-proposal-decorators 插件

安装一下

shell
# @babel/plugin-proposal-decorators -D
yarn add @babel/plugin-proposal-decorators@^7.4.4 -D

修改配置文件

json
{
    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 }],
            ]
        }
    }
},

然后重新运行,发现可以了,浏览器成功打印

image-20220108112622325

总结

因为打包后的文件需要跑在不同的浏览器上,所以很多高级语法不同的浏览器支持情况是不一样的。

所以就需要把高级语法转换为ES5的语法,这样就可以跑在各个浏览器上了,babel就是干这件事的。

  1. babel-loader 转换高级语法的js的loader
  2. @babel/core babel的核心模块,转换的时候就是这个核心模块去调用转换的方法
  3. @babel/preset-env 预设,告诉插件如何转换,转换的规则
  4. 如果有一些并未形成规范的语法,可以在预设中安装额外的babel插件来进行转换,比如装饰器语法,需要使用@babel/plugin-proposal-decorators插件来转换

参考

https://www.bilibili.com/video/BV1a4411e7Bz?p=7&spm_id_from=pageDriver

babel官网