美文网首页前端大宝剑
12-脚手架create react app源码分析(2)

12-脚手架create react app源码分析(2)

作者: 七玄之主 | 来源:发表于2019-07-17 17:06 被阅读2次

    前文中分析了 create react app 中关于 Webpack 的配置,本文将关注 Babelcreate react app 中是如何配置的。

    我们在yarn eject 后的项目根目录的 package.json 文件中可以看到如下节点:

    {
      ...
      "babel": {
        "presets": [
          "react-app"
        ]
      }
    }
    

    由此可知,create react app 已经将 Babel 有关的配置打包成预制件来使用的,我们可以直接在 node_modules 找到对应的预制件。如下所示:


    看到这些文件, Babel 配置貌似也区分环境了,看来以前还是很傻很天真。

    creat.js分析

    presets组

    对于 @babel/preset-env 这个插件预设,之前的 Babel 配置中已经使用过,它区别于 babel-preset-latest,不会进行多余的转换,可以根据配置,按需加载插件。默认行为和 babel-preset-latest 相同。而在 create react app 中,对于 @babel/preset-env 的使用,区分了测试与开发,产品环境。

    presets: [
          isEnvTest && [
            // 测试环境,根据目标机器的Node版来转换
            require('@babel/preset-env').default,
            {
              targets: {
                node: 'current',
              },
            },
          ],
          (isEnvProduction || isEnvDevelopment) && [
            // Latest stable ECMAScript features
            require('@babel/preset-env').default,
            {
              // 在入口文件根据browserlist定义来选择polyfills插入
              useBuiltIns: 'entry',
              // 指定corejs版本消除控制台警告 
              corejs: 3,
              // 指定ES6模块转换类型,false为不转换
              modules: false,
              // 排除@babel/plugin-transform-typeof-symbol插件
              exclude: ['transform-typeof-symbol'],
            },
          ],
          ...
        ].filter(Boolean),
    

    测试要求执行速度快,避免无效或无意义的转换,这里直接指定了机器 nodejs 版本,保持最新版本就能减少无效的转换,新版本的 nodejs 已经逐渐支持新语法。而开发,生成环境,这里使用的是在入口处添加,我们之前使用的按需添加的方式,似乎那样更加优雅。

    presets: [
          ...
          [
            // 转换react 预制
            require('@babel/preset-react').default,
            {
              // 开启利于开发环境的插件,例如@ babel / plugin-transform-react-jsx-self和@ babel / plugin-transform-react-jsx-source。
              development: isEnvDevelopment || isEnvTest,
              // 将使用本机内置而不是尝试对任何需要的插件进行polyfill行为。
              useBuiltIns: true,
            },
          ],
          // 转换typescript 预制
          isTypeScriptEnabled && [require('@babel/preset-typescript').default],
        ].filter(Boolean),
    

    @babel/preset-react 预制件中开启了开发环境的一些插件支持。

    plugins组

    由于不考虑追加 flow 的支持,相关插件功能分析就直接略过。

    babel-plugin-macros

    这东西简单来说就是 babel 插件的一个简单写法,往常使用某一插件,需要在对应的 babel 配置中追加,如果使用由 Babel宏 实现的宏,我们只需要配置中增加 babel-plugin-macros 的支持后,就可以任意将宏插件安装到我们的开发环境依赖中,无需任何其他配置。官网介绍中描述了以下优点:

    • 只需要一个入口配置 babel-plugin-macros,之后所有项目中使用的 macros 都可以无配置任意使用。
    • 类似 create-react-app 之类的工具已经默认支持了,无需任何其他配置。
    • 宏的使用更加明确,你需要明确导入才能使用对应的宏,而插件配置以后你就可以使用,例如类似 console.scope 这样的插件可能会误导你认为是浏览器自带函数。
    • 宏更安全,更容易编写,因为直接接收AST节点来做处理。
    • 使用插件你没配置的话,那无法在编译时发现错误;相反,使用宏,如果你没配置 babel-plugin-macros,编译时就会报错。

    @babel/plugin-transform-destructuring

    开启数组及对象解构语法支持。

    @babel/plugin-proposal-decorators

    开启装饰器语法支持。该插件必须放在 @babel/plugin-proposal-class-properties' 之前。

    @babel/plugin-transform-runtime

    避免多次编译出helper函数。

    babel-plugin-transform-react-remove-prop-types

    产品环境移除 Proptype 定义。

    优化配置

    我们将修改 babel.config.js 以根据 NODE_ENV 变量生成对应环境的配置。

    const getPresets = env => {
        const isEnvDevelopment = env === 'development';
        const isEnvTest = env === 'test';
    
        return [
            [
                "@babel/preset-env",
                !isEnvTest ?
                    // 开发,产品环境
                    {
                        // 配置如何处理 polyfills
                        // "usage" | "entry" | false, defaults to false.
                        // usage 目前是个实验性的用法,在具体使用的文件中导入具体被使用的polyfills
                        useBuiltIns: "usage",
                        // 指定corejs版本为2.x
                        corejs: 2,
                        // 指定ES6模块转换类型,false为不转换
                        modules: false,
                        // 排除@babel/plugin-transform-typeof-symbol插件
                        exclude: ['transform-typeof-symbol'],
                    } :
                    // 测试环境
                    {
                        targets: {
                            node: 'current',
                        },
                    },
            ],
            [
                "@babel/preset-react",
                {
                    // 开启利于开发环境的插件,例如@babel/plugin-transform-react-jsx-self和@babel/plugin-transform-react-jsx-source。
                    development: isEnvDevelopment || isEnvTest,
                    // 将使用本机内置而不是尝试对任何需要的插件进行polyfill行为.
                    useBuiltIns: true,
                },
            ],
            ["@babel/preset-typescript"]
        ];
    }
    
    const getPlugins = () => {
        return [
            // 开启实验性的babel宏插件,宏插件使用是免配置,对应包需要安装到开发环境依赖中
            ['babel-plugin-macros'],
            // 支持装饰器
            ['@babel/plugin-proposal-decorators', false],
            ["@babel/plugin-syntax-dynamic-import"],
            ["@babel/plugin-proposal-class-properties", { loose: true }],
        ];
    };
    
    module.exports = function (api) {
        // 基于NODE_ENV执行缓存,如果NODE_ENV发生变化,则重新获取配置更新缓存
        api.cache.using(() => process.env.NODE_ENV);
    
        return {
            presets: getPresets(process.env.NODE_ENV),
            plugins: getPlugins()
        };
    };
    

    增加了对 Babel 宏及装饰器语法支持,而对于 polyfill 的处理方式无变化。这样再次编译时,Babel 会根据不同环境适用对应的插件或加载不同配置。

    这里我们试试最新添加的 Babel 宏,选择安装 reactive.macro 包。更多宏包请查看 Awesome babel macros

    About.tsx

    import * as React from "react";
    import { state, bind } from "reactive.macro";
    import styled from "styled-components";
    
    const Title = styled.h1`
      font-size: 1.5em;
      text-align: center;
      color: palevioletred;
    `;
    
    const Wrapper = styled.section`
      padding: 4em;
      background: papayawhip;
    `;
    
    const About: React.SFC = () => {
      const a = state(1);
      const b = state(2);
    
      return (
        <Title>
          <Wrapper>About</Wrapper>
          <div>
            <input type="number" value={bind(a)} />
            <button onClick={b => b += 1}>b+</button>
    
            <p>
              {a} + {b} = {a + b}
            </p>
          </div>
        </Title>
      );
    };
    
    export default About;
    

    无任何多余配置完美执行。

    相关文章

      网友评论

        本文标题:12-脚手架create react app源码分析(2)

        本文链接:https://www.haomeiwen.com/subject/wgwflctx.html