Исследуйте исходный код приложения create-реагировать

JavaScript React.js NPM
Исследуйте исходный код приложения create-реагировать

Ссылка на эту статью:JSON в 1993.GitHub.IO/2018/05/пока горячо…

Адрес второй серии

В последнее время работа стабилизировалась, сверхурочной работы не так много... Так что у меня появляется свободное время, чтобы подучиться фронтенду

Раньше один крупный парень в компании писал скаффолдинг, похожий на create-react-app, для создания проектов компании. Я никогда не знал принцип реализации внутри, воспользуйтесь этой возможностью, чтобы изучитьcreate-react-appИсходный код, чтобы понять механизм, работающий внутри.

Не бойтесь читать исходный код, как только увидите его, сейчас такие отличные проекты с открытым исходным кодом, плюс поддержка различных IDE очень хорошая, просто нажмите точку останова и зайдите в отладку, контур легко увидеть. Вы также можете использовать эту идею, чтобы узнать о других проектах с открытым исходным кодом.

Эмммм первый раз пишу текст ~ принимаю любые жалобы

Быстрое понимание

Для тех, кто хочет узнать быстро, просто просмотрите этот

create-react-app на самом деле использует узел для запуска некоторых процессов установки пакетов и тестирования демонстрации шаблона файла в соответствующем каталоге.

Упрощенно его можно разделить на следующие этапы:

  • Определить версию узла
  • Выполните некоторую инициализацию обработки командной строки, например ввод-helpзатем вывести содержимое справки
  • Определяем есть ли входное имя проекта, если есть, запускаем установку пакета по параметрам, по умолчанию стоит метод установки пряжи, например:yarn add react react-dom react-scripts
  • Измените установленную версию зависимости в package.json от точной версии16.0.0Изменить на ^ Версию, совместимую с предыдущими версиями^16.0.0и присоединитьсяstart,buildсценарий запуска ожидания
  • копироватьreact-scriptsПодtemplateв целевой файл, которыйpublic,srcВ ожидании папки, на самом деле это простая работающая демонстрация
  • END~

Друзья, которые продолжают читать, могут следовать шаг за шагом, чтобы понять логику реализации внутри, и сначала объяснить версию среды:

create-react-app v1.1.4
macOS 10.13.4
node v8.9.4
npm 6.0.0
yarn 1.6.0
vsCode 1.22.2

Инициализация проекта

Сначала зайдите на github, чтобы вытащить код проекта, вытащите его и переключитесь на указанныйtag

  • git clone https://github.com/facebook/create-react-app.git
  • git checkout v1.1.4
  • yarn//Если вам не нужна отладка точки останова, этот шаг можно пропустить

Если версия пряжи здесь может быть слишком низкой, будет сообщено о серии ошибок.Раньше использовалась версия 0.x, и ее можно без проблем обновить до 1.x.

Ниже мы используемrootВместо корневого каталога проекта легко понять

Во-первых, когда мы открываем проект, мы видим кучу файлов конфигурации и две папки: файлы конфигурации eslint, конфигурации развертывания travis, конфигурации пряжи, журналы изменений, объявления с открытым исходным кодом и т. д. Мы все можем их игнорировать, тогда нам нужно Где исходный код ядра посмотреть?

Фокус:Если проект не знает, с чего начать, начните с файла package.json.

root/package.json

{
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "start": "cd packages/react-scripts && node scripts/start.js",
  },
  "devDependencies": {
  },
  "lint-staged": {
  }
}

Откройте корневой каталог package.json, и мы увидим, что это очень просто: команды скрипта npm, зависимости разработки и обработчики коммитов, остальное — это то, на что нам нужно обратить внимание.workspacesЗдесь он указывает на"packages/*", поэтому наше внимание сейчас сосредоточено наpackagesпапка

В папке пакетов также есть несколько папок.Именование папок здесь очень стандартизировано.Вы можете сразу увидеть разделение функций, так что это все еще старая рутина, чтобы смотреть на это напрямую.root/packages/create-react-app/package.json

packages/create-react-app/package.json

{
  "name": "create-react-app",
  "version": "1.5.2",
  "license": "MIT",
  "engines": {
  },
  "bugs": {
  },
  "files": [
    "index.js",
    "createReactApp.js"
  ],
  "bin": {
    "create-react-app": "./index.js"
  },
  "dependencies": {
  }
}

В это время нетworkspacesпункт, мы можем видетьbinФункция bin заключается в сопоставлении команд с исполняемыми файлами.Подробнее см.package Document

Здесь можно просто понять, что когда мы устанавливаем его глобальноcreate-react-appПосле этого запуститеcreate-react-app my-react-appСистема поможет нам запуститьpackages/create-react-app/index.js my-react-app

Наконец-то нашел запись исходного кода. Для простого исходного кода мы можем видеть его напрямую. Для более сложных или желающих увидеть значение этих переменных при выполнении каждой строки кода нам нужно использовать IDE или другие инструменты для точки останова. , Отладьте код сейчас.

Настроить отладку точки останова

Для тех, кто больше знаком с vscode или отладкой узлов, вы можете пропустить его и посмотреть его напрямую.Начать чтение исходного кода точки останова

vscode debug

Для пользователей vscode отладка очень проста, нажмите значок маленького жука на боковой панели, нажмите «Настройки». Затем напрямую измените значение «программы». После изменения нажмите зеленую стрелку в верхнем левом углу, чтобы запустить. Если вы хотите прерваться в определенный момент, напримерcreate-react-app/index.jsточки останова line39, просто щелкните мышью слева от номера строки

Конфигурация запуска.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "启动程序",
      "program": "${workspaceFolder}/packages/create-react-app/index.js",
    }
  ]
}

отладка узла

Если вы обычно не разрабатываете с помощью vscode или привыкли к chrome-devtool, вы можете напрямую запустить команду узла, а затем выполнить отладку в chrome. Сначала убедитесь, что версия узла выше 6 Затем запустите в корневом каталоге проектаnode --inspect-brk packages/create-react-app/index.jsВведите chrome://inspect/#devices в адресную строку Chrome, и вы увидите скрипт, который мы хотим отладить. Подробности об отладке node chrome-devtool можно найти здесь.портал

Терминал запускает отладку узла
(ps: здесь видно, что node реализован в модуляризации, обернув его функцией, а затем передав параметры, такие как экспорт, запрошенные для использования)

Начать чтение исходного кода точки останова

packages/create-react-app/index.js файловый портал гитхаб

packages/creat-react-app/index.js
Этот файл очень прост, просто используйте его как входной файл, чтобы судить о версии узла, если он меньше, чем 4.x, подскажите и завершите программу, если он нормальный, загрузите его../createReactAppЭтот файл, основная логика реализована в этом файле.

packages/create-react-app/createReactApp.js файловый портал гитхаб

Следуйте нашей точке останова вcreateReactApp.jsВ этом файле 750 строк, на первый взгляд много, а зависимостей в шапке файла внесено больше десятка, но не пугайтесь, вообще больше половины таких качественных open source проектов — это комментарии и безошибочная информация.

Здесь рекомендуется, чтобы друзья, у которых нет отладки точек останова, попробовали скопировать код в другой файл js, а затем сначала проигнорировать предыдущие зависимости, а затем перейти в npm, чтобы проверить, что он делает. Не обманывайте себя, видя одну зависимость за другой, но не основной код. Затем после прочтения части кода удалите эту часть кода.Например, когда я прочитал 200 строк, я удалил первые 200 строк, чтобы остальные 500 строк не были так виноваты. Конечно, рекомендуется использовать точки останова для отладки и чтения, логика будет понятнее.

Во-первых, в шапке файла очень много зависимостей, на них мы пока не будем обращать внимание, а проверим позже, когда будем их использовать.

const validateProjectName = require('validate-npm-package-name');
const chalk = require('chalk');
const commander = require('commander');
const fs = require('fs-extra');
const path = require('path');
const execSync = require('child_process').execSync;
const spawn = require('cross-spawn');
const semver = require('semver');
const dns = require('dns');
const tmp = require('tmp');
const unpack = require('tar-pack').unpack;
const url = require('url');
const hyperquest = require('hyperquest');
const envinfo = require('envinfo');

обработчик командной строки командира

Далее, после нашей точки останова, первая строка кода, которая должна быть выполнена:L56

const program = new commander.Command(packageJson.name)
  .version(packageJson.version) // create-react-app -v 时输出 ${packageJson.version}
  .arguments('<project-directory>') // 这里用<> 包着project-directory 表示 project-directory为必填项
  .usage(`${chalk.green('<project-directory>')} [options]`) // 用绿色字体输出 <project-directory>
  .action(name => {
    projectName = name;
  }) // 获取用户传入的第一个参数作为 projectName **下面就会用到**
  .option('--verbose', 'print additional logs') // option用于配置`create-react-app -[option]`的选项,比如这里如果用户参数带了 --verbose, 会自动设置program.verbose = true;
  .option('--info', 'print environment debug info') // 后面会用到这个参数,用于打印出环境调试的版本信息
  .option(
    '--scripts-version <alternative-package>',
    'use a non-standard version of react-scripts'
  )
  .option('--use-npm')
  .allowUnknownOption()
   // on('option', cb) 输入 create-react-app --help 自动执行后面的操作输出帮助
  .on('--help', () => {
    console.log(`    Only ${chalk.green('<project-directory>')} is required.`);
    console.log();
    console.log(
      `    A custom ${chalk.cyan('--scripts-version')} can be one of:`
    );
    console.log(`      - a specific npm version: ${chalk.green('0.8.2')}`);
    console.log(
      `      - a custom fork published on npm: ${chalk.green(
        'my-react-scripts'
      )}`
    );
    console.log(
      `      - a .tgz archive: ${chalk.green(
        'https://mysite.com/my-react-scripts-0.8.2.tgz'
      )}`
    );
    console.log(
      `      - a .tar.gz archive: ${chalk.green(
        'https://mysite.com/my-react-scripts-0.8.2.tar.gz'
      )}`
    );
    console.log(
      `    It is not needed unless you specifically want to use a fork.`
    );
    console.log();
    console.log(
      `    If you have any problems, do not hesitate to file an issue:`
    );
    console.log(
      `      ${chalk.cyan(
        'https://github.com/facebookincubator/create-react-app/issues/new'
      )}`
    );
    console.log();
  })
  .parse(process.argv); // 解析传入的参数 可以不用理会

Здесь используется зависимость командира, в это время мы можем перейти к npm для поиска его функции. Описание на официальном сайте естьThe complete solution for node.js command-line interfaces, inspired by Ruby's commander.API documentationПеревод представляет собой законченное решение интерфейса командной строки node.js.Основные функции можно посмотреть в комментариях.Наверное можно понять,что такое есть,и найти способ сделать потом.гитхаб-портал.

Определить, передано ли имя проекта

if (typeof projectName === 'undefined') {
  if (program.info) { // 如果命令行有带 --info 参数,输出 react,react-dom,react-scripts版本 然后退出
    envinfo.print({
      packages: ['react', 'react-dom', 'react-scripts'],
      noNativeIDE: true,
      duplicates: true,
    });
    process.exit(0);
  }
  ...
  这里输出了一些错误提示信息
  ...
  process.exit(1);
}

Взгляд вниз - это параметр, который должен быть передан для оценкиprojectName,здесьprojectNameВыше.action(name => { projectName = name;})полученный. Судя по тому, что если ввода нет, то напрямую делать какие-то информационные подсказки, а потом завершать программу. Если этот параметр передается в--info, он будет выполняться до тех пор, покаenvinfo.print. Daily npm search envinfo Это система, используемая для вывода некоторой системной информации о текущей системе среды, такой как версия системы, npm и т. д., а также версия пакетов react, react-dom, react-scripts, очень простая в использовании. Текущая версия этого пакета сильно отличается от версии create-react-app, но это не влияет на наше использование~портал eninfo npm

Если вы используете конфигурацию отладки vscode, которую я предоставил выше, программа должна закончиться здесь, потому что когда мы запускали службу отладки, мы не передавали параметры скрипту какprojectName, поэтому давайте изменим vscodelaunch.jsonдобавить несколько полей"args": ["test-create-react-app"] Забыл как настроить нажмите здесь~ прошел вprojectNameпараметры, а затем перезапустите службу отладки

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "启动程序",
      "program": "${workspaceFolder}/packages/create-react-app/index.js",
      "args": [
        "test-create-react-app"
      ]
    }
  ]
}

скрытые аргументы командира

Затем, оценив имя проекта, подойдите кLine140

const hiddenProgram = new commander.Command()
  .option(
    '--internal-testing-template <path-to-template>',
    '(internal usage only, DO NOT RELY ON THIS) ' +
      'use a non-standard application template'
  )
  .parse(process.argv);

Вы можете видеть, что это скрытая опция отладки, задающая параметр для передачи в пути к шаблону, чтобы разработчики могли отлаживать... Все в порядке, не беспокойте его

createApp

createApp(
  projectName,
  program.verbose,
  program.scriptsVersion,
  program.useNpm,
  hiddenProgram.internalTestingTemplate
);

Затем вниз идет вызовcreateApp, значение переданных параметров:项目名,是否输出额外信息,传入的脚本版本,是否使用npm,调试的模板路径. Затем войдите в тело функции и посмотритеcreateAppчто именно делал.

function createApp(name, verbose, version, useNpm, template) {
  const root = path.resolve(name);
  const appName = path.basename(root);

  checkAppName(appName); // 检查传入的项目名合法性
  fs.ensureDirSync(name); // 这里的fs用的是 fs-extra, 对node的fs提供一些扩展方法
  // 判断新建这个文件夹是否是安全的 不安全直接退出
  if (!isSafeToCreateProjectIn(root, name)) {
    process.exit(1);
  }

  // 在新建的文件夹下写入 package.json 文件
  const packageJson = {
    name: appName,
    version: '0.1.0',
    private: true,
  };
  fs.writeFileSync(
    path.join(root, 'package.json'),
    JSON.stringify(packageJson, null, 2)
  );

  const useYarn = useNpm ? false : shouldUseYarn();
  const originalDirectory = process.cwd();
  process.chdir(root);
  // 如果是使用npm,检测npm是否在正确目录下执行
  if (!useYarn && !checkThatNpmCanReadCwd()) {
    process.exit(1);
  }

  // 判断node环境,输出一些提示信息, 并采用旧版本的 react-scripts
  if (!semver.satisfies(process.version, '>=6.0.0')) {
    // 输出一些提示更新信息
    version = 'react-scripts@0.9.x';
  }

  if (!useYarn) {
    // 检测npm版本 判断npm版本,如果低于3.x,使用旧版的 react-scripts旧版本
    const npmInfo = checkNpmVersion();
    if (!npmInfo.hasMinNpm) {
      version = 'react-scripts@0.9.x';
    }
  }

  // 判断结束之后,跑run 方法
  // 传入 项目路径,项目名, reactScripts版本, 是否输入额外信息, 运行的路径, 模板(开发调试用的), 是否使用yarn
  run(root, appName, version, verbose, originalDirectory, template, useYarn);
}

createReactApp.js Портал createAppЗдесь я упростил некоторые вещи, удалил часть выходной информации и добавил несколько комментариев.createAppГлавное, что нужно сделать, это сделать некоторые выводы о безопасности, такие как: проверить, является ли имя проекта допустимым, проверить, безопасно ли создавать новый, проверить версию npm, обработатьreact-scriptсовместимая версия В комментариях прописана конкретная логика выполнения, после серии проверок и обработки вызывается метод run и входящие параметры项目路径,项目名,reactScripts版本,是否输入额外信息,运行的路径,模板(开发调试用的), 是否使用yarn. Поняв общий процесс, давайте войдем и посмотрим по одной функции за раз.

checkAppName() // Проверяем действительность входящего имени проекта isSafeToCreateProjectIn(root, name) // Определяем, безопасна ли новая папка shouldUseYarn() // проверка пряжи checkThatNpmCanReadCwd() // проверка npm run() // После проверки вызываем run для выполнения установки и других операций

checkAppNameПроверьте, является ли имя проекта законным

function checkAppName(appName) {
  const validationResult = validateProjectName(appName);
  if (!validationResult.validForNewPackages) {
    // 判断是否符合npm规范如果不符合,输出提示并结束任务
  }

  const dependencies = ['react', 'react-dom', 'react-scripts'].sort();
  if (dependencies.indexOf(appName) >= 0) {
    // 判断是否重名,如果重名则输出提示并结束任务
  }
}

checkAppNameОн используется для оценки того, соответствует ли текущее имя проекта спецификации npm, например, оно не может быть написано с заглавной буквы и т. д. Он используетvalidate-npm-package-nameПакет NPM. Здесь упрощает большую часть кода ошибки, но не влияет на вкус.

shouldUseYarnЧтобы определить, установлена ​​​​ли пряжа, то же самое верноcheckThatNpmCanReadCwdИспользуется для оценки npm

function shouldUseYarn() {
  try {
    execSync('yarnpkg --version', { stdio: 'ignore' });
    return true;
  } catch (e) {
    return false;
  }
}

run

Можно сказать, что предыдущие операции имеют дело с некоторой логикой суждения и совместимости.runЭто настоящая основная логика установки, __start для установки зависимостей, копирования шаблонов__ и т. д.

function run(...) {
  // 这里获取要安装的package,默认情况下是 `react-scripts`。 也可能是根据传参去拿对应的包
  const packageToInstall = getInstallPackage(version, originalDirectory);
  // 需要安装所有的依赖, react, react-dom, react-script
  const allDependencies = ['react', 'react-dom', packageToInstall];
  ...
}

runВ основном есть несколько вещей, которые нужно сделать, во-первых, в зависимости от поступающей версии.versionи оригинальный каталогoriginalDirectoryчтобы получить пакет для установки. Версия по умолчанию пуста, а полученное значение packageToInstall равноreact-scripts, ПотомpackageToInstallсоединиться сallDependenciesЭто означает, что все зависимости, которые необходимо установить. Скажи это здесьreact-scriptsПо сути, это набор конфигураций и шаблонов webpack, принадлежащихcreate-react-appБольшой модуль для другого ядра.портал

function run(...) {
  ...
  // 获取包名,支持 taz|tar格式、git仓库、版本号、文件路径等等
  getPackageName(packageToInstall)
    .then(packageName =>
      // 如果是yarn,判断是否在线模式(对应的就是离线模式),处理完判断就返回给下一个then处理
      checkIfOnline(useYarn).then(isOnline => ({
        isOnline: isOnline,
        packageName: packageName,
      }))
    )
    .then(info => {
      const isOnline = info.isOnline;
      const packageName = info.packageName;
      /** 开始核心的安装部分 传入`安装路径`,`是否使用yarn`,`所有依赖`,`是否输出额外信息`,`在线状态` **/
      /** 这里主要的操作是 根据传入的参数,开始跑 npm || yarn 安装react react-dom等依赖 **/
      /** 这里如果网络不好,可能会挂 **/
      return install(root, useYarn, allDependencies, verbose, isOnline).then(
        () => packageName
      );
    })
  ...
}

Затем, если метод установки нити используется в настоящее время, оценивается, находится ли он в автономном режиме. После оценки подключитесь к предыдущемуpackageToInstallа такжеallDependenciesсобрать это вместеinstallметод, затем поinstallметод запуска install.

метод запуска getInstallPackage(); // Получить пакет шаблона для установки.По умолчанию это react-scripts install(); // Передаем параметры для установки, отвечающие за установку всех зависимостей init(); // вызываем установленные react-scripts/script/init для копирования шаблона .catch(); // обработка ошибок

install

function install(root, useYarn, dependencies, verbose, isOnline) {
  // 主要根据参数拼装命令行,然后用node去跑安装脚本 如 `npm install react react-dom --save` 或者 `yarn add react react-dom`
  return new Promise((resolve, reject) => {
    let command;
    let args;

    // 开始拼装 yarn 命令行
    if (useYarn) {
      command = 'yarnpkg';
      args = ['add', '--exact']; // 使用确切版本模式
      // 判断是否是离线状态 加个状态
      if (!isOnline) {
        args.push('--offline');
      }
      [].push.apply(args, dependencies);
      // 将cwd设置为我们要安装的目录路径
      args.push('--cwd');
      args.push(root);
      // 如果是离线的话输出一些提示信息

    } else {

      // npm 安装模式,与yarn同理
      command = 'npm';
      args = [
        'install',
        '--save',
        '--save-exact',
        '--loglevel',
        'error',
      ].concat(dependencies);

    }

    // 如果有传verbose, 则加该参数 输出额外的信息
    if (verbose) {
      args.push('--verbose');
    }

    // 用 cross-spawn 跨平台执行命令行
    const child = spawn(command, args, { stdio: 'inherit' });

    //  关闭的处理
    child.on('close', code => {
      if (code !== 0) {
        return reject({ command: `${command} ${args.join(' ')}`, });
      }
      resolve();
    });
  });
}

Мы следуем точке останова отrunБежать кinstallметод, вы можете видеть, что код разделен на два метода обработки в зависимости от того, используется ли пряжа.if (useYarn) { yarn 安装逻辑 } else { npm 安装逻辑 }Методы обработки имеют одинаковую логику, в зависимости от поступающихdependenciesДля соединения зависимостей, которые необходимо установить, в основномreact,react-dom,react-script. судить сноваverboseа такжеisOnlineДобавьте некоторые параметры командной строки. Наконец, используйте узел для запуска команды.Если платформа отличается, воспользуйтесь помощьюcross-spawnЧтобы разобраться, не буду повторять их здесь. Для конкретной логики см. приведенный выше код, удалите неважный вывод информации, и код относительно прост для понимания.

установить Определите, следует ли использовать пряжу или npm в соответствии с параметрами, переданными в Зависимости, необходимые для сборки Запустите команду для установки с помощью cross-spawn.

существуетinstallвернетPromiseПосле установки точка останова возвращается к нашемуrunФункция продолжается со следующей логикой.

function run() {
  ...
  getPackageName()
    .then(()=> {
      return install(root, useYarn, allDependencies, verbose, isOnline).then(
            () => packageName
      );
    })
  ...
}

Поскольку нашinstallЗависимости, необходимые для разработки, установлены, после чего мы можем приступить к определению запущенного в данный моментnodeСоответствует ли он тому, что мы установили?react-scriptsвнутриpackages.jsonТребуемая версия узла. Это предложение немного сбивает с толку.Короче говоря, оно предназначено для определения того, является ли текущая версия узла актуальной.react-scriptsТребуется для этой зависимости.

Затем приступайте к модификацииpackage.jsonВерсии зависимостей (реакция, реакция-дом, реакция-скрипты), которые мы установили, были изменены с исходной точной версии, например (16.0.0), на более высокую или равную версию, например (^ 16.0.0). После завершения этих процессов наш каталог выглядит так, за исключением установленных зависимостей иpackage.jsonСнаружи ничего нет. Итак, следующие шаги — сгенерировать некоторую конфигурацию веб-пакета и простую демо-версию, которую можно запустить.

Так как же он так быстро создал эти штуки? Помните в начале, что есть скрытый параметр командной строки--internal-testing-templateИспользуется ли он для отладки разработчиками, поэтому на самом деле способ их создания create-react-app заключается в прямом копировании шаблона определенного пути в соответствующее место. Это очень просто и грубо ххххх

run(...) {
  ...
    getPackageName(packageToInstall)
    .then(...)
    .then(info => install(...).then(()=> packageName))
    /** install 安装完之后的逻辑 **/
    /** 从这里开始拷贝模板逻辑 **/
    .then(packageName => {
      // 安装完 react, react-dom, react-scripts 之后检查当前环境运行的node版本是否符合要求
      checkNodeVersion(packageName);
      // 该项package.json里react, react-dom的版本范围,eg: 16.0.0 => ^16.0.0
      setCaretRangeForRuntimeDeps(packageName);

      // 加载script脚本,并执行init方法
      const scriptsPath = path.resolve(
        process.cwd(),
        'node_modules',
        packageName,
        'scripts',
        'init.js'
      );
      const init = require(scriptsPath);
      // init 方法主要执行的操作是
      // 写入package.json 一些脚本。eg: script: {start: 'react-scripts start'}
      // 改写README.MD
      // 把预设的模版拷贝到项目下
      // 输出成功与后续操作的信息
      init(root, appName, verbose, originalDirectory, template);

      if (version === 'react-scripts@0.9.x') {
        // 如果是旧版本的 react-scripts 输出提示
      }
    })
    .catch(reason => {
      // 出错的话,把安装了的文件全删了 并输出一些日志信息等
    });
}

После установки зависимостей здесь выполнитеcheckNodeVersionОпределите, соответствует ли версия узла зависимости. После сращивания путь к запуску目录/node_modules/react-scripts/scripts/init.js, передать параметры, чтобы позволить ему выполнить некоторые действия по инициализации. Затем выполните соответствующую обработку ошибки

/node_modules/react-scripts/script/init.js

целевая папка /node_modules/react-scripts/script/init.js

module.exports = function(
  appPath,
  appName,
  verbose,
  originalDirectory,
  template
) {
  const ownPackageName = require(path.join(__dirname, '..', 'package.json'))
    .name;
  const ownPath = path.join(appPath, 'node_modules', ownPackageName);
  const appPackage = require(path.join(appPath, 'package.json'));
  const useYarn = fs.existsSync(path.join(appPath, 'yarn.lock'));

  // 1. 把启动脚本写入目标 package.json 
  appPackage.scripts = {
    start: 'react-scripts start',
    build: 'react-scripts build',
    test: 'react-scripts test --env=jsdom',
    eject: 'react-scripts eject',
  };

  fs.writeFileSync(
    path.join(appPath, 'package.json'),
    JSON.stringify(appPackage, null, 2)
  );

  // 2. 改写README.MD,把一些帮助信息写进去
  const readmeExists = fs.existsSync(path.join(appPath, 'README.md'));
  if (readmeExists) {
    fs.renameSync(
      path.join(appPath, 'README.md'),
      path.join(appPath, 'README.old.md')
    );
  }

  // 3. 把预设的模版拷贝到项目下,主要有 public, src/[APP.css, APP.js, index.js,....], .gitignore
  const templatePath = template
    ? path.resolve(originalDirectory, template)
    : path.join(ownPath, 'template');
  if (fs.existsSync(templatePath)) {
    fs.copySync(templatePath, appPath);
  } else {
    return;
  }
  fs.move(
    path.join(appPath, 'gitignore'),
    path.join(appPath, '.gitignore'),
    [],
    err => { /* 错误处理 */  }
  );

  // 这里再次进行命令行的拼接,如果后面发现没有安装react和react-dom,重新安装一次
  let command;
  let args;
  if (useYarn) {
    command = 'yarnpkg';
    args = ['add'];
  } else {
    command = 'npm';
    args = ['install', '--save', verbose && '--verbose'].filter(e => e);
  }
  args.push('react', 'react-dom');

  const templateDependenciesPath = path.join(
    appPath,
    '.template.dependencies.json'
  );
  if (fs.existsSync(templateDependenciesPath)) {
    const templateDependencies = require(templateDependenciesPath).dependencies;
    args = args.concat(
      Object.keys(templateDependencies).map(key => {
        return `${key}@${templateDependencies[key]}`;
      })
    );
    fs.unlinkSync(templateDependenciesPath);
  }

  if (!isReactInstalled(appPackage) || template) {
    const proc = spawn.sync(command, args, { stdio: 'inherit' });
    if (proc.status !== 0) {
      console.error(`\`${command} ${args.join(' ')}\` failed`);
      return;
    }
  }

  // 5. 输出成功的日志
};

initОсновная часть файла представляет собой логическую обработку в основном

  1. Измените package.json и напишите несколько сценариев запуска, напримерscript: {start: 'react-scripts start'}, используется для запуска проекта разработки
  2. Перепишите README.MD и поместите в него справочную информацию.
  3. Скопируйте предустановленный шаблон в проект, в основном включитеpublic, src/[APP.css, APP.js, index.js,....], .gitignore
  4. Сделайте совместимую обработку для старой версии узла.Вот дополнение.При выборе реактивных скриптов необходимо судить и выбирать более старую версию @0.9.x в соответствии с версией узла.
  5. Если соответствующая информация выводится, если это не удается, выполните некоторые операции, такие как вывод журнала.

Код здесь немного больше, поэтому удалите небольшую часть. Если вы заинтересованы в начальном коде, вы можете прыгнуть здесь.портал реагирования-скрипты/скрипты/init.js

END~

сюдаcreate-react-appЧасть основного процесса построения проекта завершена, давайте рассмотрим:

  1. Определите, меньше ли версия узла 4, и выйдите, в противном случае выполнитеcreateReactApp.jsдокумент
  2. createReactApp.jsСначала выполните некоторую обработку командной строки и обработку ответов, а затем определите, есть ли входящиеprojectNameПодсказка и выход, если нет
  3. По поступающимprojectNameсоздать каталог и создатьpackage.json.
  4. Определите, существуют ли особые требования для установки определенной версииreact-scripts, затем используйтеcross-spawnДля решения проблем с кросс-платформенной командной строкой используйтеyarnилиnpmУстановитьreact, react-dom, react-scripts.
  5. После установки запуститьreact-scripts/script/init.jsИсправлятьpackage.jsonверсии зависимостей, запустите сценарий и скопируйте соответствующий шаблон в каталог.
  6. После их обработки выведите подсказку пользователю.

Изначально я хотел закончить все приложение create-реагировать, но обнаружил, что написал так много для одного творения, так что если вы хотите продолжить чтение позжеreact-scriptsЕсли это так, другая статья будет открыта. Вы также можете посмотреть на точку останова самостоятельно на основе этой идеи, ноreact-scriptsОсновная причина может заключаться в том, что существует множество конфигураций webpack, и точки останова не должны сильно помочь.