Babel理解和应用,Webpack+Babel+ts

  • Post author:
  • Post category:其他




Babel 编译器

Babel是一个Javascript 编译器。将ECMA2015及以上版本的代码通过babel转为向下兼容的javascript。这也是你可以使用更先进语法的原因。
浏览器并不兼容ts,jsx,或者es6语法,但是通过babel插件类如typescript @babel/preset-env,可将代码编译为兼容的javascript。以便浏览器识别。



Babel执行过程

img



  • parse解析代码 生成AST树

    var { parse } = require('@babel/parser');
    var code = `const name = "jyy";`; 
    // 源代码生成的ast
    var ast = parse(code);  
    console.log(ast)
    

    去掉部分信息

    {
        "type":"File",
        "program":{
            "type":"Program",
            "sourceType":"script",
            "body":[
                {
                    "type":"VariableDeclaration",
                    "declarations":[
                        {
                            "type":"VariableDeclarator",
                            "id":{
                                "type":"Identifier",
                                "name":"name"
                            },
                            "init":{
                                "type":"StringLiteral",
                                "extra":Object{...},
                                "value":"jyy"
                            }
                        }
                    ],
                    "kind":"const"
                }
            ],
            "directives":[]
        },
        "comments":Array[0]
    }
    


  • transform AST转换

    babel 是个工具链,如果直接将AST转为javascript代码进行输出,那么前后没有任何区别。
    
    这一步叫做转换,就是将AST进行增删改。这也就是插件的功能。
    
    @babel/plugin-transform-react-jsx 就是将react中的jsx代码转为react 的节点对象。
    
    @babel/plugin-proposal-optional-chaining 支持代码中书写可选链(target?.pro)
    
    • @babel/traverse 插件 可以定义回调函数,回调函数的参数提供了丰富的增、删、改、查以及类型断言

    • @babel/types 插件 它的作用是创建、修改、删除、查找ast节点‘

    • @babel/plugin-proposal-optional-chaining 。。。

      var { parse } = require('@babel/parser');
      var { default: traverse } = require('@babel/traverse');
      var { default: generate } = require('@babel/generator');
      var t = require('@babel/types');
      
      var code = `const target = stringify({a:1});`;
      var ast = parse(code);
      
      traverse(ast, {
        Identifier(path) {
          const { node } = path;
          if (node && node.name === 'stringify') {
             const nNode = t.memberExpression(t.identifier('JSON'), t.identifier('stringify'));
             path.replaceWith(nNode);
             path.stop();
          }
        }
      })
      
      const newCode = generate(ast, {}, code).code;
      //'const target = JSON.stringify({\n  a: 1\n});'
      


  • AST 输出。生成新的javascript代码

    ​ 插件是@babel/generator,其作用就是将转换好的ast重新生成代码。[见上]



  • 总结

    // js=>AST=>js
    1:parser 解析代码到AST
    2:transform AST 。插件
       2.1:type 它的作用是创建、修改、删除、查找ast节点
       2.2:traverse 遍历AST 定义回调函数,回调函数的参数提供了丰富的增、删、改、查以及类型断言的方法
       2.3:....
    3:generator AST生成js代码
    



Babel/Core 整合基础插件

​ @babel/parse、@babel/generator都是提供了代码转换的基本功能,

​ @babel/types、@babel/traverser起作用是提供操作ast节点的功能

​ @babel/helper-module-transforms

​ @babel/template 用于从字符串形式的代码来构建 AST 树节点

​ …

​ 另外加入了其他功能,比如读取、分析配置文件



  • dome

    var babel = require("@babel/core");
    var code = "<div class='c'>jyy</div>";  // 代码
    babel.transform(code,{plugins: ["@babel/plugin-transform-react-jsx"],},function(err, result){
        console.log(result.code);
        // React.createElement("div", {
        //   class: "c"
        // }, "jyy");
    });
    



Babel插件

​ babel已经开发了基础插件,包含code==>AST=转换=>New_AST==>code。

​ 这里所说的插件针对于AST的转换。用于支持新的javascript 新的语法和语法糖。



  • 插件

    var babel = require("@babel/core");
    var code = `num => {return num ** 2;}`;  // 代码
    babel.transform(code,{
        plugins: [
            "@babel/plugin-transform-arrow-functions",// 兼容箭头函数
            "@babel/plugin-transform-exponentiation-operator",// 兼容 幂运算符
            //。。。。
        ]
    },function(err, result){
        /***
        (function (num) {
      		return Math.pow(num, 2);
    	});
        */
    });
    


  • 预设preset

    为兼容越来越多的新语法,开发者可能要导入很多的插件。
    这里增加了预设,预设就是插件集合。再也不用一个一个导入
    1:@babel/preset-env 将最新javascript转为es6
    2:@babel/preset-flow
    3:@babel/preset-react 支持react
    4:@babel/preset-typescript
    
    


  • preset-env

    查看所有依赖插件

    ​ @babel/preset-env 等价于 @babel/env

    ​ “@babel/babel-plugin-name” 和 “@babel/name”是等价的

    var babel = require("@babel/core");
    var code = `num => {return num ** 2;}`; 
    // 
    babel.transform(code,{
        plugins:[],
        presets: ["@babel/preset-env"]
    },function(err, result){
        console.log(result.code);
    });
    



Babel配置

1:命令行 babel src --out-dir lib --presets=@babel/preset-env,@babel/react
2:.babelrc 项目根目录创建文件 {"presets":[],"plugins":[]}
3:babel.config.js 项目根目录创建文件 {module.exports={"presets":[],"plugins":[]}}
4package.json {"babel":{"presets":[],"plugins":[]}}



Babel简单应用



  • 简单的esnext规范代码解析为es6

    // ======================> src/app.js
    class MError extends Error {
      statusCode = null;
      code = 0;
      constructor(message, code, statusCode) {
        super(message);
        this.code = code;
        this.statusCode = statusCode;
      }
      get [Symbol.toStringTag]() {
        return 'MError';
      }
    }
    
    // ===============================> js 转换AST
    var babel = require("@babel/core");
    var fs = require("fs");
    const path = require("path");
    
    const source = path.join(__dirname, 'src', 'app.js');
    const target = path.join(__dirname, 'dist', 'app.js');
    babel.transformFile(source, {
      plugins: [],
      presets: ["@babel/preset-env"]
    }, function (err, result) {
      if (!!err === false) {
        fs.writeFileSync(target, result.code);
      }
    });
    
    //===============================> dist/app.js
    "use strict";
    ...省略声明代码
    var MError = /*#__PURE__*/function (_Error) {
      _inherits(MError, _Error);
      var _super = _createSuper(MError);
      function MError(message, code, statusCode) {
        var _this;
        _classCallCheck(this, MError);
        _this = _super.call(this, message);
        _defineProperty(_assertThisInitialized(_this), "statusCode", null);
        _defineProperty(_assertThisInitialized(_this), "code", 0);
        _this.code = code;
        _this.statusCode = statusCode;
        return _this;
      }
      _createClass(MError, [{
        key: _Symbol$toStringTag,
        get: function get() {
          return 'MError';
        }
      }]);
      return MError;
    }( /*#__PURE__*/_wrapNativeSuper(Error));
    


  • 支持typescript

    • 方式一 1:使用tsc命令 将ts语法转为js语法 2:使用preser/env将js 转为es6 。 TS > TS Compiler > JS > Babel > NEW_JS

    • 方式二 2: @babel/preset-typescript 整合typescript

      var babel = require("@babel/core");
      var fs = require("fs");
      const path = require("path");
      const source = path.join(__dirname, 'src', 'app.ts');
      const target = path.join(__dirname, 'dist', 'app.ts.js');
      babel.transformFile(source, {
        plugins: [
            '@babel/plugin-proposal-object-rest-spread',// 解构的支持
            '@babel/plugin-proposal-class-properties', // static 支持
        ],
        presets: [
          "@babel/preset-typescript", // 将ts=>JS 配置tsconfig.json 是无效的
          '@babel/preset-env',// JS=>JS
        ]
      }, function (err, result) {
        if (!!err === false) {
          fs.writeFileSync(target, result.code);
        }
      });
      


  • webpack + ts

    webpack+ts配置

    1:tsc 命令基本不和webpack结合。 webpack 有watch 参数
    2:webpack (ts-loader+ tsconfig.json)用于处理ts文件 + (babel-loader+@babel/preset-env) 再将js编译为es6
    3:webpack (无ts-loader ,使用 (@babel/preset-typescript + @babel/preset-env)
    
    // 方式2
    // webpack.config.js
    const path = require('path');
    module.exports = {
      mode: 'production',
      entry: {
        'index': path.join(__dirname, "src", 'index.ts'),
      },
      watch: false,
      output: {
        filename: "[name].js",
        path: path.join(__dirname, "cjs"),
      },
      module: {
        rules: [
          {
            test: /\.ts$/,// 处理ts
            exclude: /node_modules/,
            use: ['ts-loader']
          }, {
            test: /\.(js|jsx)$/,
            exclude: /(node_modules|bower_components|build)/,
            use: {
              loader: "babel-loader",
              options: {
                "plugins": [
                  ["@babel/plugin-proposal-optional-chaining"],
                ],
                presets: ["@babel/preset-env"]
              }
            },
          },]
      },
      externals: {},
      plugins: [],
      resolve: {
        extensions: [ '.js', '.jsx','.ts','.tsx'],
      },
    
    };
    
    // 方式3
    // webpack.dev.js
    
    module.exports = {
      // 其他配置见上
      module: {
        rules: [{
          test: /\.(js|jsx|ts)$/, // 新增 对ts文件处理
          exclude: /(node_modules|bower_components|build)/,
          use: {
            loader: "babel-loader",
            options: {
              "plugins": [
                ["@babel/plugin-proposal-optional-chaining"{}],
                '@babel/plugin-proposal-object-rest-spread',// 解构的支持
          		'@babel/plugin-proposal-class-properties', // static 支持
              ],
              presets: [
                "@babel/preset-typescript", // 对ts文件的预设
                "@babel/preset-env"
              ]
            }
          },
        },]
      },
      devtool: 'cheap-module-source-map',
    };
    



版权声明:本文为G_ZZH原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。