12-脚手架create react app源码分析(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 (
    
      <Wrapper>About</Wrapper>
      <div>
        <input type="number" value={bind(a)} />
        <button onClick={b => b += 1}>b+</button>

        <p>
          {a} + {b} = {a + b}
        </p>
      </div>
    
  );
};

export default About;

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

你可能感兴趣的:(12-脚手架create react app源码分析(2))