输入关键词开始搜索

分析React和Vue脚手架

164 字 约 1 分钟

React脚手架 ​

创建一个React应用:

shell
create-react-app react_app

查看package.json中的脚本:

json
"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
},

可以看到使用的是React封装的库react-scripts来运行项目的。

打开node_modules/.bin/react-scripts脚本,可看到执行的是/react-scripts/bin/react-scripts.js文件,查看核心代码:

js
// ...
const spawn = require('react-dev-utils/crossSpawn');
const args = process.argv.slice(2);

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

if (['build', 'eject', 'start', 'test'].includes(script)) {
  const result = spawn.sync(
    process.execPath,
    nodeArgs
      .concat(require.resolve('../scripts/' + script))
      .concat(args.slice(scriptIndex + 1)),
    { stdio: 'inherit' }
  );
  // ...
}
// ...

上面的代码很简单,如果执行的脚本是['build', 'eject', 'start', 'test']中的一个,就创建子进程去scripts/目录下执行对应的文件。

简单分析执行npm run dev的情况,执行scripts/start.js:

js
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';

process.on('unhandledRejection', err => {
  throw err;
});

// Ensure environment variables are read.
require('../config/env');

// ...

// Tools like Cloud9 rely on this.
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; // 默认端口3000
const HOST = process.env.HOST || '0.0.0.0';

// ...

// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
// 检测浏览器设置,没有则降级至browserslist中默认配置
checkBrowsers(paths.appPath, isInteractive)
  .then(() => {
    // We attempt to use the default port but if it is busy, we offer the user to
    // run on a different port. `choosePort()` Promise resolves to the next free port.
    return choosePort(HOST, DEFAULT_PORT); // 选择端口
  })
  .then(port => {
    if (port == null) {
      // We have not found a port.
      return;
    }

    // 生成webpack的配置文件
    const config = configFactory('development');
    const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
    const appName = require(paths.appPackageJson).name;

    const useTypeScript = fs.existsSync(paths.appTsConfig);
    const urls = prepareUrls(
      protocol,
      HOST,
      port,
      paths.publicUrlOrPath.slice(0, -1)
    );
    // Create a webpack compiler that is configured with custom messages.
    const compiler = createCompiler({
      // createCompiler -> compiler = webpack(config) 创建compiler
      appName,
      config,
      urls,
      useYarn,
      useTypeScript,
      webpack,
    });
    // Load proxy config
    const proxySetting = require(paths.appPackageJson).proxy;
    const proxyConfig = prepareProxy(
      proxySetting,
      paths.appPublic,
      paths.publicUrlOrPath
    );
    // Serve webpack assets generated by the compiler over a web server.
    const serverConfig = {
      ...createDevServerConfig(proxyConfig, urls.lanUrlForConfig),
      host: HOST,
      port,
    };
    // 创建devServer
    const devServer = new WebpackDevServer(serverConfig, compiler);
    // Launch WebpackDevServer.
    devServer.startCallback(() => {
			// ...
      openBrowser(urls.localUrlForBrowser);
    });

    // ...
  })
  .catch(err => {
    if (err && err.message) {
      console.log(err.message);
    }
    process.exit(1);
  });

做了三件事件:

  1. 生成各项配置,最主要通过configFactory工厂函数来生成Webpack的配置

  2. 通过内部封装的createCompiler函数创建Compiler

  3. 创建DevServer

configFactory工厂函数目的就是生成Webpack Config,涉及点详见react-scripts中的webpack.config.js文件

Vue脚手架 ​