create-react-app start 命令源码解析

419 阅读2分钟

准备工作

调试 cra start 命令源码步骤

  1. 打开源文件输入 debugger
  2. 点击 package.json 里 scripts 上的 debug 按钮, 选择 start。 (有很多方式,不一一列举)

image.png

image.png

入口文件 react-scripts.js

const args = process.argv.slice(2); // react-scripts start

process.argv 第一个是 node 可执行命令路径, 第二个是当前执行的文件, 第三个才是命令行参数 start, 如下。

1. '/Users/xxx/.nvm/versions/node/v18.12.1/bin/node', 
2. '/Users/xxx/workspace/cra/node_modules/.bin/react-scripts'
3. 'start'

1. 读取用户命令行参数,找到对应执行文件

const scriptIndex = args.findIndex(
  x => x === 'build' || x === 'eject' || x === 'start' || x === 'test'
); // 0
const script = scriptIndex === -1 ? args[0] : args[scriptIndex]; // start
const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : []; // []

if (['build', 'eject', 'start', 'test'].includes(script)) {
  const result = spawn.sync(
    process.execPath, // Node.js 进程的可执行文件的绝对路径名
    nodeArgs 
      .concat(require.resolve('../scripts/' + script))
      .concat(args.slice(scriptIndex + 1)),
    { stdio: 'inherit' } 
  );
}

spawn.sync(xxxx) 代码其实就是执行 ../scripts/start.js 文件, 如下。
node "User/xxx/cra/node_modules/react-scripts/scripts/start.js"

核心代码

进入 start.js

process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
// 设置环境变量为开发环境变量

require('../config/env'); 
// 加载 .env 文件。具体内容在上一篇 build 命令源码里有讲

const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
const HOST = process.env.HOST || '0.0.0.0';
// 定义默认端口跟域名

const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
  .then(() => {
    // 检查端口是否被占用
    return choosePort(HOST, DEFAULT_PORT);
  })
  .then(port => {
    if (port == null) {
      return;
    }

    const config = configFactory('development');
    // configFactory 就是 webpack.config.js。里面有 cra 帮我们写好的 webpack 配置
    
    const appName = require(paths.appPackageJson).name;
    // 获取 cra 创建出来的项目的 package.json 里的 name 值

    const useTypeScript = fs.existsSync(paths.appTsConfig);
    // 查看项目是否有 tsconfig.json
    
    const compiler = createCompiler({
      appName,
      config,
      urls,
      useYarn,
      useTypeScript,
      webpack,
    });
    // 生成 compiler
    
    const serverConfig = {
      ...createDevServerConfig(proxyConfig, urls.lanUrlForConfig),
      host: HOST,
      port,
    };
    // 生成 webpack-dev-server 配置
    
    const devServer = new WebpackDevServer(serverConfig, compiler);
    // webpack-dev-server 是个 class 类, 它会帮我们启动一个 http 服务器, 具体 api 可以看文档
    
    devServer.startCallback(() => {
      if (isInteractive) { // 
        clearConsole(); // // 看标准输出流是否连接到终端, 如果连接, 清空终端日志
      }

      openBrowser(urls.localUrlForBrowser);
    });
    // 启动服务, 启动之后打开浏览器
})

核心代码都在这里了, 大家如果对一些方法感兴趣的话可自行去源码中查看, 代码还是很简单的。

总结

用一句话概括 start 命令做了什么。就是生成 compiler, 用 webpack-dev-server 启动 http 服务器来访问项目。

相关文章

create-react-app build 命令源码解析

文档参考

cra 官方文档: create-react-app
cra github: create-react-app
webpack-dev-server github: webpack-dev-server