Сборка JS SDK реализует Webpack VS Rollup

Webpack
Сборка JS SDK реализует Webpack VS Rollup

Зеро, предисловие

В последнее время я занимаюсь разработкой JavaScript SDK, уделяя особое внимание нативным, коротким, быстрым, понятным, читабельным, тестируемым и так далее.

JavaScript SDK Design Guide

Поскольку Rollup меньше после упаковки, Webpack более эффективен в dev. В конце концов решил использовать Rollup в качестве производственного сборщика и Webpack5 в качестве сборщика разработки.

Бросив самую классическую цитату: используйте webpack для приложений и Rollup для библиотек

Webpack---разобрать ну и по совпадению

Webpack был запущен в 2012 году Тобиасом Копперсом для решения проблемы, которую в то время не решали существующие инструменты: создание сложных одностраничных приложений (SPA). В частности, все изменили две особенности веб-пакета:

  1. Разделение кода

Позволяет разбить ваше приложение на управляемые фрагменты кода, которые можно загружать по запросу, что означает, что ваши пользователи могут быстро перейти на интерактивный веб-сайт, не дожидаясь загрузки и синтаксического анализа всего приложения. Конечно, вы можете сделать это вручную, так что удачи. 2. ### Статические активы Например, изображения и CSS можно импортировать в ваше приложение, а также использовать в качестве еще одного узла в графе зависимостей. Больше не нужно беспокоиться о том, находятся ли ваши файлы в правильной папке, больше не нужно хакерских скриптов для хеширования URL-адресов файлов, потому что webpack позаботится об этом за нас.

Rollup --- играй помаленьку, беги быстро

Rollup — это сборщик модулей JavaScript следующего поколения. Разработчики могут использовать модули ES2015 в вашем приложении или библиотеке, а затем эффективно упаковать их в один файл для браузера и использования Node.js. Самое интересное в Rollup — это то, что он может сделать размер файла пакета очень маленьким, Rollup всегда может сделать пакеты меньшего размера и более быстрыми:

  1. Tree-shaking

Отличная особенность свертывания, когда она была впервые запущена. Rollup анализирует избыточный код через статический анализ кода и удаляет избыточный код в окончательный упакованный файл, чтобы дополнительно уменьшить размер кода. 2. ### Модуль для упаковки модуля ES2015 Упаковка модуля ES2015 поддерживает это, какие другие инструменты сборки не имеют. Проверьте непосредственно не нужно преобразовать импорт в Commentjs «Требовать метод через Babel, который значительно использует преимущества модулей ES2015.

1. Конфигурация

Конфигурация упаковки Webpack5

Так как проект изначально был запакован Webpack, были настроены два конфига разработки и продакшена


> PS: Babel может появиться ниже, но мы его пропустим, потому что это не тема этой статьи.

УБПАК и соответствующая установка:

yarn add -D webpack@5.0.0-beta.22
yarn add -D webpack-cli
yarn add -D webpack-dev-server
yarn add -D webpack-merge

Добавьте следующие файлы конфигурации в проект: webpack.config.js

const {merge} = require('webpack-merge')
const productionConfig = require('./webpack.prod.conf.js') // 引入生产环境配置文件
const developmentConfig = require('./webpack.dev.conf.js') // 引入开发环境配置文件
const webpack = require('webpack')
const path = require('path')

const baseConfig = {
  module: {
    rules: [
      {
        test: /\.(js|ts|tsx)$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.(less|css)$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
          },
          {
            loader: 'less-loader',
            options: {
              lessOptions: {
                javascriptEnabled: true,
              },
            },
          },
        ],
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.js', '.tsx', '.jsx'],
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
  plugins: [
    new webpack.ProvidePlugin({
      h: ['dom-chef', 'h'],
    }),
  ],
}

module.exports = env => {
  // mode: 'production' || 'development',
  let config = env === 'production' ? productionConfig : developmentConfig
  console.log('当前模式:', env, config)
  return merge(baseConfig, config) // 合并 公共配置 和 环境配置
}

(Смешайте в Вавиле)

Установите Babel и его связанные с этим:

yarn add -D @babel/core
yarn add -D @babel/preset-env
yarn add -D @babel/preset-typescript
yarn add -D @babel/plugin-syntax-typescript
yarn add -D @babel/plugin-transform-react-jsx

babel.config.js

const presets = [
  [
    '@babel/preset-env',
    {
      targets: {
        edge: '17',
        firefox: '60',
        chrome: '67',
        safari: '11.1',
        ie: '11',
      },
      useBuiltIns: 'usage',
      corejs: 3,
    },
  ],
  ['@babel/preset-typescript'],
]
const plugins = [
  ['@babel/plugin-syntax-typescript'],
  [
    '@babel/plugin-transform-react-jsx',
    {
      pragma: 'h',
      pragmaFrag: 'DocumentFragment',
    },
  ],
]
module.exports = {presets, plugins}

webpack.prod.conf.js

const path = require('path')

module.exports = {
  mode: 'production',
  entry: {
    index: path.resolve(__dirname, './src/app.ts'),
  },
  output: {
    libraryTarget: 'umd',
    path: __dirname + '/dist',
    filename: 'index.js',
    library: '@efox/pay',
  },
  performance: {
    hints: 'error',
  },
  plugins: [],
  optimization: {
    minimize: true,
  },
}

webpack.dev.conf.js

const path = require('path')

module.exports = {
  mode: 'development',
  entry: {
    index: path.resolve(__dirname, './src/app.ts'), //入口
  },
  output: {
    libraryTarget: 'umd',
    path: __dirname + '/dist',
    filename: 'index.js',
    library: '@efox/pay',
  },
  devtool: 'source-map',
  performance: {
    hints: 'warning',
  },
}

запускать

webpack --env development --watch

cp src/index.d.ts dist && webpack --env production

使用上述配置 Webpack5 打包本 SDK ,包体积为 88kb

Конфигурация накопительной упаковки

Установите Rollup и все, что с ним связано:

yarn add -D rollup
yarn add -D rollup-plugin-commonjs
yarn add -D rollup-plugin-inject
yarn add -D rollup-plugin-terser
yarn add -D rollup-plugin-typescript2

rollup.config.js

import typescript from 'rollup-plugin-typescript2'
import babel from 'rollup-plugin-babel'
import {DEFAULT_EXTENSIONS} from '@babel/core'
import commonjs from 'rollup-plugin-commonjs'
import nodeResolve from 'rollup-plugin-node-resolve'
import json from 'rollup-plugin-json'
import inject from 'rollup-plugin-inject'
import {terser} from 'rollup-plugin-terser'

export default {
  input: 'src/app.ts', // 入口文件
  output: {
    name: 'efoxPay', // umd 模式必须要有 name  此属性作为全局变量访问打包结果
    file: `dist/index.js`,
    format: 'umd',
    sourcemap: true,
  },
  plugins: [
    json({
      // All JSON files will be parsed by default,
      // but you can also specifically include/exclude files
      include: 'node_modules/**',
      exclude: ['node_modules/foo/**', 'node_modules/bar/**'],

      // for tree-shaking, properties will be declared as
      // variables, using either `var` or `const`
      preferConst: true, // Default: false

      // specify indentation for the generated default export —
      // defaults to '\t'
      indent: '  ',

      // ignores indent and generates the smallest code
      compact: true, // Default: false

      // generate a named export for every property of the JSON object
      namedExports: true, // Default: true
    }),
    nodeResolve({
      jsnext: true,
      main: true,
    }),
    commonjs({
      include: 'node_modules/**', // Default: undefined
      namedExports: {
        'node_modules/dom-chef/index.js': ['dom-chef'],
        'node_modules/dom-chef/index.json': ['svg-tag-names/'],
      },
    }),
    typescript({
      tsconfigOverride: {
        compilerOptions: {
          declaration: false, // 输出时去除类型文件
        },
      },
    }),
    babel({
      extensions: [...DEFAULT_EXTENSIONS, 'ts', 'tsx', 'js'],
      runtimeHelpers: true,
      exclude: 'node_modules/**',
      babelrc: false,
      presets: ['@babel/preset-env'],
      plugins: [['@babel/plugin-transform-runtime', {useESModules: false}]],
    }),
    inject({
      h: ['dom-chef', 'h'],
    }),
    terser(),
  ],
}

запускать

rollup -c

使用上述配置 Rollup 打包本 SDK ,包体积为 33kb

2. Терсер

Terser — это интерпретатор ES6 + JavaScript и набор менеджеров/компрессоров, он может быть наиболее эффективным. Terser рекомендует использовать с пакетом Rollup, который будет создавать меньший код.

Если вы хотите использовать Terser с Rollup, вам нужно установить rollup-plugin-terser

yarn add -D rollup-plugin-terser

и импортируйте его на rollup.config.js

import {terser} from 'rollup-plugin-terser'
export default {
 plugins: [
 	terser(),
 ]
}

Хотя Rollup — хороший выбор, при использовании веб-пакета выше версии 4 по умолчанию используется Terser. Terser можно включить с помощью оптимизации.минимизировать следующим образом:

  optimization: {
    minimize: true,
  },

3. качание дерева

И Webpack5, и Rollup поставляются с встряхиванием деревьев. Импорт в код статически анализируется, и любой код, который фактически не используется, будет исключен. Это позволяет строить поверх существующих инструментов и модулей без добавления дополнительных зависимостей или увеличения размера проекта.

Например, при использовании CommonJS необходимо импортировать (Import) полный инструментальный (Tool) или библиотечный (Library) объект.

// 使用 CommonJS 导入(import)完整的 utils 对象
var utils = require( 'utils' );
var query = 'Rollup';
// 使用 utils 对象的 ajax 方法
utils.ajax( 'https://api.example.com?search=' + query ).then( handleResponse );

Однако, используя модуль ES6, без необходимости импортировать весь объект utils, мы можем только импортировать (импортировать) нужную нам функцию ajax:

// 使用 ES6 import 语句导入(import) ajax 函数
import { ajax } from 'utils';
var query = 'Rollup';
// 调用 ajax 函数
ajax( 'https://api.example.com?search=' + query ).then( handleResponse );

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

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

4. плагин-преобразование-среда выполнения

A plugin that enables the re-use of Babel's injected helper code to save on codesize.

Для обратной совместимости браузера @babel/preset-env будет использоваться для преобразования синтаксиса ES6 каждого файла, который мы называем вспомогательной функцией.

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

Установите пакет @ Babel / Runtime, чтобы обеспечить модули функциональных модулей HELPER, установите плагин Babel @ Babel / Plugin-Transfrance-Runtime, чтобы автоматически заменить функции помощника.

   yarn add -D @babel/preset-env
   yarn add -D @babel/runtime
   yarn add -D @babel/plugin-transform-runtime

и импортируйте его на rollup.config.js

import babel from 'rollup-plugin-babel'
export default {
 plugins: [
    babel({
      extensions: [...DEFAULT_EXTENSIONS, 'ts', 'tsx', 'js'],
      runtimeHelpers: true,
      exclude: 'node_modules/**',
      babelrc: false,
      presets: ['@babel/preset-env'],
      plugins: [['@babel/plugin-transform-runtime', {useESModules: false}]],
    }),
    ]
}

V. Резюме

  • Rollup лучше, чем встряхивание дерева Webpack5
  • Этот SDK к исходному файлу CSS исключал встроенный в файл TSX, поскольку поддержка подкладки статических ресурсов для поддержки бедных, прибылей и потерь, меньших анклавах. Уэбпак для ресурсов подразделения кода и статическим импорте обладает «естественными преимуществами», но естественным образом приведет к более крупным анклавам.
  • Plugin-Transform-Runtime — это плагин, который необходимо внедрять в пакеты, которые могут автоматически заменять дублирующиеся вспомогательные функции, экономя большой объем.

автор


Benny Shi