Может ли Yarn Plug'n'Play помочь вам избавиться от проблем с node_modules?

Node.js
Может ли Yarn Plug'n'Play помочь вам избавиться от проблем с node_modules?

Используйте механизм Plug'n'Play Yarn (v1.12+) вместо node_modules. В настоящее время это экспериментальная функция.

Оригинальная ссылка:Может ли Yarn Plug'n'Play помочь вам избавиться от проблем с node_modules?


задний план

node_modulesОбъекты, которые давно были национальной рвотой, а разработчики других языков видят Node_Modules, чтобы увидеть узел, Используйте слово, чтобы описать «вес!».

Если вы не понимаете механизм поиска модулей Node, нажмитеинтерпретация исходного кода require()

Простой интерфейсный проект (create-react-app) и количество файлов:

frontend-project

И macOS/LibraryРазмер каталога с количеством файлов:

macos library

одна линияhello worldВам необходимо установить более 130 МБ зависимых модулей, а количество файлов32,313, В отличие от macOS/Libraryзанимает 9,02 ГБ места, а количество файлов всего в два раза больше, чем у прежнего (67,890Таким образом, можно увидеть, что характеристики node_modules:

  • Структура дерева каталогов сложна
  • Количество файлов велико, и они относительно малы
  • Зависимостей много, простому проекту нужно установить несколько тонн зависимостей

Так что node_modules — это кошмар для механических жестких дисков, я помню, как однажды коллега удалил node_modules и не получил его весь день. Для фронтенд-разработчиков у нас есть N потребностейnpm installпроект 😹.

Кроме того, модульный механизм Node имеет следующиенедостаток:

  • Сам узел не имеет концепции модулей, он ищет и загружает их во время выполнения.Этот недостаток аналогичен *'Сравнению плюсов и минусов динамических языков и статических языков'*, Возможно, вы работаете нормально в среде разработки, но не можете работать онлайн, потому что модуль не был добавлен в package.json.

  • Стратегия поиска модулей Node очень расточительна, этот недостаток можно оптимизировать в большинстве фронтенд-проектов, Например, webpack может ограничить поиск только node_modules в корневом каталоге проекта, но для вложенных зависимостей все равно требуется более 2 поисков.

  • node_modules не может эффективно обрабатывать дубликаты пакетов Два пакета с одинаковым именем, но разными версиями не могут сосуществовать в одном каталоге. Таким образом, это приведет к вложенным модулям node_modules, и эти «зависимости» проекта не могут быть разделены с проектами или другими зависимостями:

    # ① 假设项目依赖a,b,c三个模块, 依赖树为:
    #  +- a
    #    +- react@15
    #  +- b
    #    +- react@16
    #  +- c
    #    +- react@16
    # yarn安装时会按照项目被依赖的次数作为权重, 将依赖提升(hoisting),
    # 安装后的node_modules结构为:
      .
      └── node_modules
          ├── a
          │   ├── index.js
          │   ├── node_modules
          │   │   └── react  # @15
          │   └── package.json
          ├── b
          │   ├── index.js
          │   └── package.json
          ├── c
          │   ├── index.js
          │   └── package.json
          └── react  # @16 被依赖了两次, 所以进行提升
    
    # ② 现在假设在①的基础上, 根项目依赖了react@15, 对于项目自己的依赖肯定是要放在node_modules根目录的,
    # 由于一个目录下不能存在同名目录, 所以react@16没有的提升机会. 
    # 安装后node_moduels结构为
      .
      └── node_modules
          ├── a
          │   ├── index.js
          │   └── package.json # react@15 提升
          ├── b
          │   ├── index.js
          │   ├── node_modules
          │   │   └── react  # @16
          │   └── package.json
          ├── c
          │   ├── index.js
          │   ├── node_modules
          │   │   └── react  # @16
          │   └── package.json
          └── react  # @15
    # 上面的结果可以看出, react@16出现了重复
    

Пряжа интегрированаPlug'n'Play(называемый pnp), китайское имя можно назвать «подключи и работай», чтобы решить «ад» node_modules.


Фундаментальный

Согласно обычному последующему процессу, Yarn создаст каталог node_modules, а затем Node будет искать в каталоге node_modules в соответствии со своими правилами поиска модулей. Но на самом деле Node не знает, что это за модуль, он ищет в node_modules, если не находит, то ищет в node_modules в родительском каталоге и так далее. Эта эффективность очень низкая.

Но как менеджер пакетов, Yarn знает дерево зависимостей вашего проекта.Может ли Yarn сообщить Node?Пусть он идет прямо в каталог для загрузки модулей. Таким образом можно повысить эффективность поиска модулей Node, а также уменьшить количество копий файла node_modules.Plug'n'Playосновной принцип.

В режиме pnp Yarn не создает каталог node_modules, вместо этого.png.jsфайл, который является программой узла, Этот файл содержит информацию о дереве зависимостей проекта, алгоритм поиска модулей, а также код исправления для средства поиска модулей (в Node переопределяет метод Module._load).


Следующее с использованием механизма pnpпреимущество:

  • избавиться от node_modules.
    • Время: по сравнению с работой в среде горячего кэша.yarn installЭкономьте 70% своего времени
    • Space: в режиме pnp все модули npm будут храниться в каталоге глобального кеша, дерево зависимостей сглажено, а копирование и повторение исключены.
  • Повышение эффективности загрузки модулей.Node должен вызывать много системных вызовов stat и readdir, чтобы найти модули. pnp получает информацию о модуле через Yarn и напрямую находит модуль
  • Больше не ограничивается node_modules, разные версии модулей с одинаковыми именами не могут находиться в одном каталоге.

Скорость установки Yarn под Mac очень высока, а при горячем кеше это занимает всего несколько секунд, причина в механизме копирования при записи SSD + APFS. Это позволяет копии файла не занимать места, что эквивалентно созданию ссылки, поэтому скорость копирования и удаления очень высока. Но сложная структура каталогов и слишком много файлов node_modules по-прежнему требуют большого количества системных вызовов, что также замедлит процесс установки.
💡 Если вы считаете pnp громоздким или ненадежным, то быстро используйте SSD с файловой системой, поддерживающей копирование при записи.


используя pnpриск:

Различные инструменты в сообществе интерфейсов в настоящее время полагаются на механизм поиска модулей node_modules.

  • Node
  • Электрон, электрон-строитель и т.д.
  • Webpack
  • Typescript: найти файл объявления типа
  • Babel: таргетинг на плагины и пресеты
  • Eslint: таргетинг на плагины и пресеты, правила
  • Jest
  • Редактор, например VsCode
  • ...😿

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

В дополнение к механизму поиска модулей есть некоторые инструменты, которые делают другие вещи непосредственно в node_modules, такие как кэширование, хранение временных сертификатов, например.cache-loader, webpack-dev-server


включить pnp

Если это простой проект Node, процесс миграции относительно прост.package.jsonВключить режим установки pnp:

{
  "installConfig": {
    "pnp": true
  }
}

Затем установите зависимости:

yarn add express

После установки появится корневая директория проекта.pnp.jsЗатем напишите код:

// index.js
const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => res.send('Hello World!'));

app.listen(port, () => console.log(`Example app listening on port ${port}!`));

Следующим шагом является запуск кода Node, если вы напрямуюnode index.jsбудет сообщеноError: Cannot find module 'express'аномальный. Это связано с тем, что для patch Node еще нет средства поиска модулей, которое можно запустить с помощью следующей команды:

yarn node

# 或者

node --require="./.pnp.js" index.js

.pnp.jsФайл не должен помещаться в репозиторий, этот файл содержит жестко заданную директорию кеша. Будет рефакторинг в Yarn v2.


Как интегрироваться в существующий проект?

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


Многие проекты в сообществе интегрировали pnp:


Node

Для Node pnp не входит в стандартную комплектацию, используйте его напрямую--require="./.pnp.js"импорт.pnp.jsфайл,.pnp.jsОн исправит объект модуля Node и повторно реализует механизм поиска модуля.

Webpack

Средство поиска модулей, используемое Webpack,enhanced-resolve, способный пройтиpnp-webpack-pluginплагин для расширенияenhanced-resolveдля поддержки пнп.

const PnpWebpackPlugin = require(`pnp-webpack-plugin`);

module.exports = {
  resolve: {
    // 扩展模块查找器
    plugins: [PnpWebpackPlugin],
  },
  resolveLoader: {
    // 扩展loader模块查找器.
    plugins: [PnpWebpackPlugin.moduleLoader(module)],
  },
};

jest

jestподдержка черезresolverЧтобы настроить искатель:

module.exports = {
  resolver: require.resolve(`jest-pnp-resolver`),
};

Typescript

Typescript также использует свой собственный поиск модулей.По соображениям производительности команда TS временно не разрешает сторонним инструментам расширять поиск.Временно недоступен.

в этотissue, было предложено использовать"moduleResolution": "yarnpnp"или используйте что-то вродеts-loaderизresolveModuleNameспособ поддержки поиска модуля pnp.

Ответ от команды TS: pnp (или tink для npm) все еще находится на ранней стадии и может измениться в будущем, например..pnp.jsфайл, он явно не подходит для входа в яму так рано. Кроме того, чтобы оптимизировать и контролировать производительность компилятора, TS не планирует предоставлять интерфейсы для выполнения стороннего кода во время компиляции.

Итак, теперь Typescript не имеет механизма плагинов, подобного babel, если вы, например, не реализуете «хост компилятора TS».ts-loaderОн сам по себе расширяет механизм подключаемых модулей и механизм поиска модулей для поддержки аналогичныхts-import-pluginи т.д. плагины, поэтомуts-loaderpnp теперь поддерживается:

const PnpWebpackPlugin = require(`pnp-webpack-plugin`);

module.exports = {
  module: {
    rules: [
      {
        test: /\.ts$/,
        loader: require.resolve('ts-loader'),
        options: PnpWebpackPlugin.tsLoaderOptions(),
      },
    ],
  },
};

Суммировать,TypescriptВ настоящее время не поддерживается, а плана развития в ближайшее время нет, так чтоVsCodeНа это тоже не рассчитывайте.fork-ts-checker-webpack-pluginПока не догнал.Видимо Typescript первый камень преткновения для pnp


Другие инструменты


Суммировать

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

Так что на данном этапе для обычных разработчиков, если вы хотите улучшить скорость установки npm, вам все равно придется использовать SSD+Copy-On-Write!😂

Ниже представлена ​​интеграция различных проектов (✅(поддерживается)|🚧(запланировано или несовершенно)|❌(не поддерживается)):

проект
Webpack
rollup
browserify
gulp
jest
Node
Typescript/VScode IntelliSense
eslint 🚧
flow 🚧
create-react-app 🚧
ts-loader
fork-ts-checker-webpack-plugin 🚧

Ссылаться на

Связанные вопросы:

Другие варианты

  • npm tink: a dependency unwinder for javascript
  • pnpm Fast, disk space efficient package manager
  • Yarn WorkspacesНесколько проектов имеют общие зависимости