Лидер попросил меня за три дня собрать набор "реакт-кли", и я согласился...

внешний интерфейс Webpack
Лидер попросил меня за три дня собрать набор "реакт-кли", и я согласился...

предисловие

Постепенно мы все стали тем человеком (титульным участником), которого больше всего ненавидели в то время.

u=1525319938,700047175&fm=26&fmt=auto.webp

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

Исследования и обсуждение

Хотя проекту не уделяется много внимания, процесс проекта все равно должен существовать. Во-первых, два члена были завербованы через Амли. Просто пообщайтесь с вами на встрече:

1. Исследование основных решений на рынке

image.png

Во-первых, когда дело доходит до скаффолдинга, он должен быть неотделим от мейнстрима в реагирующем сообществе.create-react-app, но был неожиданно всеми исключен в первый раз. В основном потому, что его конфигурация недостаточно гибкая, правильноwebpackПакет слишком мёртв, что делает его немного бессильным при работе со сложными проектами, и хотя предоставляемые им функции очень общие, они также достаточно просты. Я думаю, в этот момент кто-то скажет, вы можетеejectДавай, выложи исходный код? Но в данном случае мне нет смысла его использовать, а стоимость вторичной разработки на основе его исходников точно не будет низкой.

image.png

Затем мы повернули цель кvue-cli, Неожиданно завоевал единодушную похвалу всех. Я думаю, что самое лучшее в нем то, что он обеспечивает хороший баланс между универсальностью и гибкостью своих функций. Это сvue.jsФилософия фреймворка очень последовательна, поэтому сvueСовмещая девелоперские проекты, будет ощущение текущей воды, и с этим согласны все.cli(Интерактивный инструмент командной строки) Мне это нравится, и это то, что ниже. Те, кто его использовал, должны быть впечатлены.

image.png

Сказать, что если у него есть какие-то серьезные проблемы, подумайте об этом, считается ли это отсутствием поддержки?

image.png

Затем наша цель — разработать метод использования, аналогичныйvue-cliРеагировать на строительные леса. до@vue/cliПонимание этого остается только на уровне использования, поэтому сначала необходимо понять его реализацию. Как говорится, легко реализовать (скопировать), познав себя и познав другого.

2. Анализ исходного кода @vue/cli

мой клон4.5.14версии, когда была написана новая версия 5.0, и она все еще находилась в стадии бета-версии, пропустите ее.

image.png

Общий код должен использоватьlerna + yarn-workspaceПакеты, которые поддерживаются и связаны с официальными функциями Vue, включены в@vueпод этим доменом npm (кто-то также назвал организацию npm).

Как показано выше, полный@vue/cliФункция в основном состоит из трех частей:

  • @vue/cliОтвечает за сбор параметров командной строки.
  • @vue/cli-serviceЭтот пакет является движком и ядром запуска vue и содержит конфигурацию веб-пакета.
  • @vue/plugin-xxxПлагин vue-cli, один плагин соответствует одной функции и взаимно-однозначное соответствие с пользовательскими параметрами функций, указанными выше.

Разделение cli и основного пакета функций является основным методом распаковки.webpack-cli & webpack,@babel/cli & @babel/coreВсе они реализованы таким образом, что относится к многоуровневой архитектуре разработки программы: cli (верхний уровень) должен зависеть от основной службы (нижний уровень), но основная служба (нижний уровень) не зависит от cli ( верхний слой) и по-прежнему может работать независимо.

Первый шаг в чтении исходного кода, начните сpackage.jsonНачать. Этот тип пакета, используемый командой, передается черезpackage.jsonизbinполе для реализации.

  "bin": {
    "vue": "bin/vue.js"
  },

Следуй за виноградной лозой, открывайbin/vue.js,здесь использовать здесьCommanderЭтот инструмент синтаксического анализа командной строки регистрирует некоторые глобальные команды, обратите внимание только здесьcreateЭта команда в порядке.

глобальныеvue create xxxПри выполнении команды запускается именно этот файл.image.pngкогдаvue create xxxКогда команда сработает, она будет выполнена../lib/createЭтот файл поместит имя проектаnameПередайте это, продолжайте следить../lib/create.js.

image.png

существуетcreateв основной функции. Сначала укажите целевой путь, по которому будет создан проект.targetDir(В большинстве случаев здесь стучитеvue createКаталог, в котором находится команда + входящий проектname).

Далее идет обработка элемента с таким же названием, который уже существует на диске. Далее дело в том,new CreatorЭтот класс официально запускает основной процесс создания проекта.

image.png

найти этоCreatorclass, вы можете обнаружить, что он наследует узелEventEmiterЭтот класс обработки событий должен реализовать весь свой механизм подключаемых модулей, который аналогичен процессу написания подключаемых модулей веб-пакетов, который требует подписки на некоторые внутренние перехватчики для реализации режима событий на основе публикации и подписки, что будет объяснено ниже. подробно ниже.

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

image.png

Похожа ли опция взаимодействия в красном поле слева? Да, соответствующий интерактивный интерфейс появился выше.

image.png

Чтобы облегчить выбор пользователя, vue предварительно устанавливает некоторые наборы функций.CreatorКатегорияconstuctorОн просто инициализирует некоторые атрибутивные переменные, а ядром является следующий вызовcreateМетод экземпляра.

image.png

передачаpromptAndResolvePreset, появится интерфейс предустановленных параметров, ✅ вариант по умолчанию, продолжайте;

image.png

можно увидеть слеваpresetsЭто указывает на то, что данные выбранного пресета уже содержат данные, содержащиеся в пресете.babel、 eslintи другие плагины.

Изменить пресет и выбрать ручной режим? Как показано ниже: получается та же структура данных.

image.png

Далее стоит датьpresetsвнутреннийpluginsДля каждого плагина в массиве инициализируйте некоторые параметры по умолчанию.

image.png

Далее инициализируем генерациюpackage.jsonНеобходимые данные.

image.png

Вставьте плагин, включенный в пресет, прямо сейчасdevDependenciesзависимости развития.

image.png

Вставка прошла успешно, и вы уже можете видеть, что 3 зависимости разработки включены в стек отладки

image.png

Далее будетpackage.jsonЗаписать на диск.

image.png

воплощать в жизньnpm install, установить зависимости.

image.png

Затем сделайте еще кое-что: напишитеnpmrc、yarnrcфайл, инициализироватьgitСклады и прочие неважные вещи проходят быстро.

image.png

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

image.png

this.resolvePlugins(preset.plugins, pkg)Этот метод имеет решающее значение и используется для внедрения плагинов. Но прежде чем представить этот метод, я хотел бы рассказать о механизме плагинов фреймворка и его использовании вvue/cliДизайн и реализация в .

Механизм плагина:

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

Плагины, вы должны сначала определить, как подключить. Это также самый важный момент вставного механизма, т.Разработчики фреймворка и разработчики подключаемых модулей должны согласовать фиксированный метод доступа к подключаемым модулям.. Это соглашение, отраженное в vue/cli, показано на следующем рисунке.

image.png

Каждый плагин имеетgeneratorпапка должна бытьindex.js,Потомindex.jsФункцию необходимо экспортировать, а в теле функции можно вызывать некоторые методы инструментов внедрения внешнего тела, например,

  • api.injectImport()Вставьте модуль импорта в проект.
  • api.extendPackage()package.json проекта расширения
  • api.render('./template')Используйте ejs для рендеринга файлов шаблонов (условная компиляция)

Оглянись на вершинуthis.resolvePlugins(preset.plugins, pkg)метод, полученное здесь применение на самом деле является функцией, экспортируемой соглашением о подключаемом модуле, но здесь следует отметить, что это толькоВременно сохраните метод применения, и вызываться не будет.

image.png

Оригиналplugins, после обработки этот метод преобразуется в новый, содержащий определенные функции плагинаplugins.
{ id: options } => [{ id, apply, options }]

image.png

Затем передайте отсортированные плагиныGeneratorВ этом классе начинается последний и самый существенный шаг:построить проект.

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

image.png

newПосле этого последовалgenerateЭтот метод экземпляра, поэтому далее сосредоточьтесь на реализации этого метода

image.png

выполнение метода, первый вызовthis.initPlugins(), самое главное в этом методе — пройти и выполнить метод применения по умолчанию всех плагинов (то есть функцию, которая экспортируется по умолчанию в упомянутом выше механизме плагина),applyПервый параметрapiСодержатьrenderРендеринг шаблонов EJS и другие возможности, и эти возможности приходят изGeneratorAPIЭтот класс, обратите внимание на второй параметр, поставит текущийthisвходящий.

image.png

Далее продолжайте читатьGeneratorAPIреализация. сначала исключить@vue/cli-serviceэтот пакет, потому что этот пакет существует вpluginsВ массиве, но он очень особенный. Он относится к основному сервису. Он не относится к категории плагинов в строгом смысле. Его не нужно обрабатывать как плагин, а нужно только участвуйте в установке.Ничего особенного.

image.png

GeneratorAPIОбщая реализация принимает дизайн промежуточного программного обеспечения, на котором_injectFileMiddlewareметод, вызов метода в API будет сохранен первым в виде функции push и не будет выполняться в настоящее время.

взять то, что было сказано вышеapi.render()Пример метода шаблона рендеринга при вызове плагина exportedapplyметод, он вызываетapi.render()звонить, но не использоватьejsСкомпилируйте файлы шаблонов, но подготовьте ихthis.generator.fileMiddlewaresв этом промежуточном временном массиве.

image.png

image.png

Такapi.render()Где эти методы действительно работают?

image.png

image.png

Откройте немедленно выполненныйthis.resolveFilesметод, правда здесь.

image.png

использоватьfor ofпоследовательно вызывать каждыйmiddlewareфункция для получения окончательного содержимого файла, которое будет сгенерировано

image.png

сейчас,this.filesсохранить полныйПуть к файлуприбытьсодержание документаотображения, как показано в левой части рисунка выше.

image.png

Далее некоторые общие операции.sortPkgдаpackage.jsonЗависит от того, чтобы разобраться в порядке, чтобы удовлетворить чувства какого-то обсессивно-компульсивного расстройства.

Далее, согласноthis.files,передачаwriteFileTreeС удовольствием записывайте файлы на диск.

image.png

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

image.png

3. Определите функции и цели

передняя пара@vue/cliПодробный анализ можно свести к трем ключевым моментам.

    1. использоватьИнтерфейс командной строкивыбор функций
    1. cliа также核心服务@vue/cli-service (конфигурация веб-пакета) использует многоуровневый дизайн,независимый пакет
    1. Механизм плагина, создавать различные файлы шаблонов функций по мере необходимости

Такой дизайн необходим для поддержки всех разработчиков Vue, потому что он должен быть очень гибким. Однако для скаффолда, удовлетворяющего только конкретную команду, он слишком сложен, а стоимость разработки будет очень высока, просто для того, чтобы разделить различные функциональные плагины, требуется очень большая рабочая нагрузка. Во-вторых, самое главное для командной разработки проекта — это унификация и стандартизация.Многие конфигурации и функции могут быть встроены по умолчанию, например процессоры eslint, babel и css.Все это необходимые функции при командной разработке проекта, поэтомуРазнообразие конфигураций и гибкость не являются основными факторами. Следовательно, от третьего подключаемого механизма можно отказаться.

cli отделен от конфигурации веб-пакета, так что оба могут быть достигнутыОбновление автономной версии, что способствует непрерывному сопровождению пользовательских проектов, но стоимость этого заключается в том, что вам нужно определить наборwebpackКонфигурация сильно отличается от соглашения о конфигурации, это в@vue/cli-serviceв использованииvue.config.jsВ качестве файла конфигурации другие основные каркасы, такие какcreate-react-appЭто полностью. В этом случае также требуется подробный документ конфигурации скаффолдинга, а веб-пакет настраивается путем настройки скаффолдинга, что делает знания о конфигурации веб-пакета, которые мы освоили, бесполезными.chainWebpackЭто решение, но насколько сложно им пользоваться, это могут оценить только те, кто им пользовался. Нам может просто понадобиться прозрачныйwebpackконфигурация, всеrules、pluginsВсе они могут быть изменены напрямую, так что никакой документации не требуется, и все можно сделать с помощью конфигурации веб-пакета, тем более, что эта конфигурация веб-пакета уже готова в нашей команде, поэтому второй пункт, упомянутый выше, не требуется.

Таким образом, нам нужно научиться в основном интерактивным инструментам командной строки и компиляции шаблонов ejs по запросу. В то же время мы уточняем функции, исходя из особенностей проекта нашей команды.После обсуждения с нашими партнерами у нас в основном есть следующие варианты:

(1) Мобильный или ПК?

Сторона ПК будет иметь встроенныйantdШаблон макета , мобильный терминал откроетсяpx2remАдаптация, встроенная в htmlflexible.jsсценарий.

(2) Создать одностраничный или многостраничный шаблон?

MPAа такжеSPAТребования существуют в сценариях наших проектов, поэтому имеет смысл различать их на уровне каркаса.

(3) Библиотека управления состояниями, версия React-Router установлена ​​по требованию

развитие функции

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

  • monorepo

Его преимущество перед единым хранилищем заключается в том, что он способствует эффективной совместной отладке между несколькими npm.node_modulesсовместное использование дискового пространства Поскольку продвижение зависимостей lerna слишком строгое в отношении номера версии зависимости и не хватает многихyarn-workspaceУникальная функция (особенно локальная обработка мягких ссылок для корзины и модуля). В настоящее время общепринятой практикой является использованиеyarnизworkspaceДо зависит от руководства,lernaАвтоматическое управление версиями и распространение пакетов npm. используется здесьmonorepoПричина в том, что такие проекты, как библиотеки компонентов или веб-плагины, могут быть разработаны на основе этих каркасов, чтобы облегчить совместную отладку между ними.

npm i -g lerna
lerna init

lerna.json

{
  "packages": [
    "packages/*"
  ],
  "version": "0.1.6",
  "npmClient": "yarn",
  "useWorkspaces": true
}

package.json

   ...
  "workspaces": [ 
    "packages/*"
  ],
  ...

Создать кли-пакет

lerna create react-booster-cli

Инициализировать следующий проект

yarn install 

image.png

Следующим шагом является реализация функции cli.Во-первых, при анализе исходного кода vue/cli было упомянуто, что глобальная команда, используемая глобальным пакетом, проходит черезpackage.jsonизbinПолевая реализация, мы делаем то же самое.

booster/packages/react-booster-cli/package.json
  "bin": {
    "booster": "bin/booster.js"
  },

Далее создайте./bin/booster.jsдокумент

#!/usr/bin/env node
// 命令行解析工具
const program = require('commander');

program
  .version(require("../package").version)
  .usage("<command> [options]");
  
  
  program.command("create <project-name>")
  .description("创建一个新的项目")
  .action((projectName)=>{
    require('../lib/create')(projectName)
  })

  program.parse(process.argv);  

#!/usr/bin/env nodeЭто предложение очень важно, заявив, что этот файл нужно использоватьnodeпрограмма для выполнения. используется внутриcommanderЭта библиотека разбора параметров командной строки, установите ее

yarn workspace react-booster-cli add commander

Теперь мы можем протестировать и запустить наш процесс и выполнить его в корневом каталоге бустера.

npx booster

Следующий экран появляется даже в случае успешного

image.png

Некоторым может быть любопытна причина успеха операции, я кратко объясню ее здесь. Прежде всего,npx xxxсначала пойдет в текущий каталогnode_modules/.bin/Найдите файл xxx в каталоге. Очевидно, он существует. Почему так?Хотя объявлена ​​глобальная команда, но написанный пакет cli не выдается, а другой не устанавливается.

image.png

На самом деле этоyarn installПобочная (супер приятная) функция , если быть точным, с помощьюworkspace,yarn installАвтоматически поможет решить проблемы установки и ссылки, принцип находится вnode_modules/.binСоздайте софт-цепочку в каталоге (по аналогии с ярлыком файла на win), ссылку наpackages/react-booster-cli/bin/booster.js.

image.png

выполнить следующийcreateКомандование пойдет командиромaction, передайте имя проекта методу создания в файле создания, что согласуется с vue/cli

image.png

Реализация функции

Реализовать метод создания

Есть много наборов инструментов, которые обычно используются в cli. Ниже есть примечания о назначении каждого инструмента. Многие на самом деле используются для того, чтобы сделать командную строку более красивой. Возможно, большая часть экосистемы npm — это фронтенд-разработчики, а типы и количество пакетов, украшающих командную строку, чрезвычайно богаты. Например, вы можете использоватьora,chalkОчень удобно реализовать некоторые эффекты загрузки командной строки, цветные шрифты и индикаторы выполнения, чтобы повысить удобство работы с командной строкой.

lib/create.js

const path = require("path");
const fs = require("fs");
// 检测目录是否存在
const exists = fs.existsSync;
// 删除文件
const rm = require("rimraf").sync;
//询问cli输入参数
const ask = require("./ask");
// 命令行交互工具
const inquirer = require("inquirer");
// 命令行loading
const ora = require("ora");
// 输出增色
const chalk = require("chalk");
// 检测版本
const checkVersion = require("./check-version");

const generate = require("./generate");

const { writeFileTree } = require("./util/file");
const runCommand = require("./util/run");

// loading
const spinner = ora();
async function create(projectName) {
  const cwd = process.cwd(); //当前运行node命令的目录
  const projectPath = path.resolve(cwd, projectName);
  // 假如当前已存在同名项目,询问是否覆盖
  if (exists(projectPath)) {
    const answers = await inquirer.prompt([
      {
        type: "confirm",
        message: "Target directory exists. Do you want to replace it?",
        name: "ok",
      },
    ]);
    if (answers.ok) {
      console.log(chalk.yellow("Deleting old project ..."));
      rm(projectPath);
      await create(projectName);
    }
  } else {

    // 收集用户输入选项
    const answers = await ask();
    spinner.start("check version");
    // 检测版本
    await checkVersion();
    spinner.succeed();
    console.log(`✨  Creating project in ${chalk.yellow(projectPath)}.`);
    // console.log(answers);
    // 更新 package.json
    const pkg = require("../template/package.json");

    // 生成项目配置文件,app.config.json
    const appConfig = {};
    const { platform, isMPA, stateLibrary,reactRouterVersion } = answers;
    if (platform === "mobile") {
      pkg.devDependencies["postcss-pxtorem"] = "^6.0.0";
      pkg.dependencies["lib-flexible"] = "^0.3.2";
    } else if (platform === "pc") {
      pkg.dependencies["antd"] = "latest";
    }
    pkg.dependencies[stateLibrary] = "latest";
    if (reactRouterVersion === "v5") {
      pkg.devDependencies["react-router"] = "5.1.2";
    } else if (reactRouterVersion === "v6") {
      pkg.dependencies["react-router"] = "^6.x";
    }

    appConfig.platform = platform;

    spinner.start("rendering template");
    const filesTreeObj = await generate(answers,projectPath);
    spinner.succeed();
    spinner.start("🚀 invoking generators...");
    await writeFileTree(projectPath, {
      ...filesTreeObj,
      "package.json": JSON.stringify(pkg, null, 2),
      "app.config.json": JSON.stringify(appConfig, null, 2),
    });
    spinner.succeed();
    console.log(`🗃  Initializing git repository...`)
    await runCommand('git init')
    
    console.log();
    console.log(
      `🎉  Successfully created project ${chalk.yellow(projectName)}.`
    );
    console.log(
        `👉  Get started with the following commands:\n\n` +
          chalk.cyan(` ${chalk.gray("$")} cd ${projectName}\n`) +
          chalk.cyan(` ${chalk.gray("$")} npm install or yarn\n`) +
          chalk.cyan(` ${chalk.gray("$")} npm run dev`)
      );
    console.log();
     
    
  }
}

module.exports = (...args) => {
  return create(...args).catch((err) => {
    spinner.fail("create error");
    console.error(chalk.red.dim("Error: " + err));
    process.exit(1);
  });
};

Определить версию

lib/check-version.js

const request = require('request')
const semver = require('semver')
const chalk = require('chalk')
const packageConfig = require('../package.json')


module.exports = function checkVersion() {
    return new Promise((resolve,reject)=>{
        if (!semver.satisfies(process.version, packageConfig.engines.node)) {
            return console.log(chalk.red(
              `  You must upgrade node to >= ${packageConfig.engines.node} .x to use react-booster-cli`
            ))
          }
          request({
            url: 'https://registry.npmjs.org/react-booster-cli',
          }, (err, res, body) => {
            if (!err && res.statusCode === 200) {
              const latestVersion = JSON.parse(body)['dist-tags'].latest
              const localVersion = packageConfig.version
              if (semver.lt(localVersion, latestVersion)) {
                console.log()
                console.log(chalk.yellow('  A newer version of booster-cli is available.'))
                console.log()
                console.log(`  latest:     ${chalk.green(latestVersion)}`)
                console.log(`  installed:  ${chalk.red(localVersion)}`)
                console.log()
              }
              resolve()
            }else{
              reject()
            }
          })
    })
}

Выбор функций командной строки

Ядро заключается в использованииinquirerЭтот пакет используется для реализации интерактивного интерфейса командной строки.Этот очень мощный пакет предоставляет интерактивные методы, такие как одиночный выбор, множественный выбор, поле ввода и т. д., как и командная строка.formкакие!

lib/ask.js

const { prompt } = require('inquirer');//生成命令行交互界面

const questions = [
  {
    name: 'platform',
    type: 'list',
    message: '您的web应用需要运行在哪个端呢?',
    choices: [{
      name: 'PC端',
      value: 'pc',
    }, {
      name: '移动端',
      value: 'mobile',
    }]
  },
  {
    name: 'isMPA',
    type: 'list',
    message: '生成单页or多页模版?',
    choices: [{
      name: '单页(SPA)',
      value: false,
    }, {
      name: '多页(MPA)',
      value: true,
    }]
  },
  {
    name: 'stateLibrary',
    type: 'list',
    message: '您希望安装的状态管理库是?',
    choices: [{
      name: 'mobx',
      value: 'mobx',
    }, {
      name: 'redux',
      value: 'redux',
    }]
  },
  {
    name: 'reactRouterVersion',
    type: 'list',
    message: '选择react-router版本',
    choices: [{
      name: 'v5(推荐)',
      value: 'v5',
    }, {
      name: 'v6(对hook支持度较好,但api不够稳定)',
      value: 'v6',
    }]
  },
]

module.exports = function ask () {
  return prompt(questions)
}

Создание файлов проекта

lib/generate.js

const { isBinaryFileSync } = require("isbinaryfile");
const fs = require("fs");
const ejs = require("ejs");
const path = require("path");

/**
 * @name 渲染文件
 * @param {*} filePath 文件路径
 * @param {*} ejsOptions ejs注入数据对象
 * @returns 文件内容
 */
function renderFile(filePath, ejsOptions = {}) {
  // 二进制文件直接返回
  if (isBinaryFileSync(filePath)) {
    return fs.readFileSync(filePath);
  }
  const content = fs.readFileSync(filePath, "utf-8");

  //src目录下需要经过ejs动态编译
  if (/[\\/]src[\\/].+/.test(filePath)) {
    return ejs.render(content, ejsOptions);
  }
  // 其他文件,比如webpack的配置文件,直接读取返回
  return content;
}

/**
 * @name 生成项目文件
 * @param {*} answers 收集的问题
 * @returns 文件树 eg { '/path/a/b' : 文件内容 }
 */
async function generate(answers, targetDir) {
  const globby = require("globby");

  // 匹配脚手架文件夹所有文件
  const fileList = await globby(["**/*"], {
    cwd: path.resolve(__dirname, "../template"),
    gitignore: true,
    dot: true,
  });
  const { isMPA } = answers;
  // ejs注入的模版变量
  const ejsData = {
    ...answers,
    projectDir: targetDir,
    pageName:'index'
  };
  // 生成文件树对象
  const filesTreeObj = {};
  fileList.forEach((oriPath) => {
    let targetPath = oriPath;
    const absolutePath = path.resolve(__dirname, "../template", oriPath);
  
    if (isMPA && /^src[\\/].+/.test(oriPath)) {
      // 针对多页场景,生成多页面模版
      const [dir, file] = oriPath.split(/[\\/]+/);
      ["index", "pageA", "pageB"].forEach((pageName) => {
        targetPath = `${dir}/pages/${pageName}/${file}`;
        filesTreeObj[targetPath] = renderFile(absolutePath, {
          ...ejsData,
          pageName,
        });
      });
    } else {
      const content = renderFile(absolutePath, ejsData);
      filesTreeObj[targetPath] = content;
    }
  });

  return filesTreeObj;
}

module.exports = generate;

Вставьте параметры, собранные из командной строки, в шаблон ejs.

Например, собирается из командной строки при взаимодействии с пользователем.platformЭтот параметр представляет веб-платформу.

image.png

Когда ejs рендерит,platform = mobile, а это значит, что выборка мобильная, достаточно вставить ее в тег head в html-шаблонеflexible.jsСкрипт, ПК, значит, не требуется. Это позволяет разделить окончательный сгенерированный код для различных функций.image.png

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

Опубликовать пакет npm

npm login
lerna publish

На этом этапе отправки посылки довольно легко наступить на яму Вот краткое изложение того, с чем я столкнулся:

    1. Имя пакета, предоставляемого npm, должно быть уникальным.

      лучше идти пораньшеnpmВыполните поиск на веб-сайте, чтобы узнать, существует ли уже имя пакета, который вы собираетесь отправить. Или заплатите за частный домен, что-то вроде @vue/xxx.

    1. Ударение Lerna Pream не вступает в силу.

      При запуске lerna publish, если в середине произойдет сбой выпуска пакета, при повторном запуске lerna publish из-за того, что тег git был помечен, пакет не будет повторно опубликован в NPM.

      Решение: запустите lerna publish из-git, пакет NPM, задействованный в текущем теге, будет снова опубликован, PS: package.json не будет обновляться, просто выполните npm publish

    1. Проблема, вызванная глобальной модификацией исходного кода Taobao npm
    • Есть задержка в синхронизации между источником Taobao и официальным источником

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

    • Если источник Taobao используется глобально, это приведет к сбою пакета.

      Потому что источник Taobao может только загружать пакеты, но не загружать пакеты. Но без исходного кода Taobao установка других зависимостей происходит медленно. Решение: Устанавливайте зависимости и единообразно извлекайте их из источников Taobao, чтобы обеспечить скорость установки зависимостей. При отправке пакета используйте package.json пакета npm.publishConfigПоле указывает официальный источник, чтобы убедиться, что пакет может быть отправлен успешно.

      "publishConfig": {
          "registry": "https://registry.npmjs.org/",
          "access": "public"
        },
      
    1. devDependenciesа такжеdependenciesразница

    Во-первых, в обычных бизнес-проектах между ними нет существенной разницы. То есть при установке, с --dev или без, это повлияет только на окончательныйpackage.jsonРасположение классификации в , в конечном итоге будет упаковано и построено с помощью таких инструментов, как веб-пакет, зависит только от того, есть ли на него ссылка в проекте.

    Но для проектов, размещенных на npm, это важно. Когда пользователь устанавливает ваш пакет,Только производственные зависимости будут установлены вместе, зависимости разработки не будет. Если он используется неправильно, например, при случайной загрузке производственной зависимости в зависимость для разработки, пользователь, установивший ваш пакет npm, выдаст ошибку и не сможет найти модуль xx.

наконец

адрес нпм,гитхаб-адрес, вы можете попробовать это,issues. Этот проект был разработан в свободное время,Приведенное выше название и сюжетная линия также являются чисто вымышленными.. Код полностью десенсибилизирован и общедоступен. Если у вашей команды есть похожие потребности, вы можете предоставить вам ссылку. Это моя лучшая прибыль.