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 (
    <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;

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348