Создание библиотеки компонентов Re и процесс выпуска с нуля

внешний интерфейс
Создание библиотеки компонентов Re и процесс выпуска с нуля

начало

Много раз, чтобы повторно использовать или суммировать, мы будем извлекать компоненты и отправлять их вnpmначальство. Но в этом процессе вы обнаружите проблему, заключающуюся в том, как сделать это лучше.发布а также管理сохранить эти компоненты. В конце концов вы обнаружите, что другие учебники в Интернете либо слишком разбросаны, либо некоторые детали не на месте. Здесь я пользуюсь этой возможностью, чтобы подвести итог. Если вы сочтете это полезным после прочтения, вы также можете, обратите внимание, ха-ха.

После понимания мы можем

  1. Напишите и опубликуйте полезную библиотеку компонентов
  2. способенimport { demoComponent } from 'xxxUI'способ представить
  3. так же может бытьimport demoComponent from xxxUI/component/demoComponentспособ представить
  4. отдельные компоненты打包相对独立, не мешая друг другу
  5. Выходной компонент может简单易用и имеет хороший兼容性
  6. Библиотека компонентов может быть реализована в соответствии с конфигурацией пользователя.按需加载
  7. Библиотека компонентов может быть реализована в соответствии с конфигурацией пользователя.Tree Shaking
  8. компоненты проходят单元测试
  9. упаковать и опубликовать вnpm

Вот так, хе-хе.

image

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

Структура проекта

Структурный анализ

Давайте сначала посмотрим на структуру проекта библиотеки компонентов.

image

Ну, это выглядит очень сложно. Первое впечатление должно быть связано с какими-то грязными файлами. Давайте сначала объясним это.

  • srcсохранить основной код
  • distСохраните окончательный упакованный выходной код
  • sassСтили разделены и размещены отдельно (конечно, их можно ставить вместе с компонентами. Цель здесь в том, что даже если связанные компоненты не используются, не проблема использовать связанные стили отдельно)
  • __mocks__(ложный объект),coverage(покрытие),test,jest.config.js(конфигурация шутки) Все они связаны с модульным тестированием и будут подробно рассмотрены в следующей главе.
  • .npmignoreа также.gitignoreАналогичный эффект
  • .babelrcЗнаменитый Вавилон должен знать каждый
  • Другие должны быть очень знакомы, и если вы введете это дальше, вас заподозрят в том, что вы составляете количество слов.

ключевой каталог

Давайте сосредоточимся наsrcВ каталоге основного кода сначала мы сохраняем компоненты вcomponentво внешнем слоеindexЦитироватьcomponentкомпоненты в , так как не указан конкретный путь,importПри импорте он будет найден по умолчаниюindex. Таким образом, после упаковки и вывода вы можете пройтиimport { demoComponent } from 'xxxUI'Это способ ссылки на компоненты.

// index.jsx
import demoComponent from './component/demoComponent';

export {
 demoComponent
};
// demoComponent.jsx
export default class demoComponent extends Component {
    render() {
        return (
            <div>
                hello world
            </div>
        );
    }
}

Затем используйте эту форму для экспорта компонентов, вы можете передатьimport demoComponent from xxxUI/component/demoComponentЭта форма вводит компоненты отдельно.

Библиотеки компонентов загружаются по запросу

Согласно приведенной выше структуре каталогов и методу импорта, мы можем узнать, что черезimport { demoComponent } from 'xxxUI'Эта форма введения заставит всю библиотеку компонентов быть введена в проект разработки, а иногда нужно использовать только два или три компонента, чего мы не хотим видеть. и черезimport demoComponent from xxxUI/component/demoComponentСсылаясь в таком виде, можно ввести только определенный компонент, который необходимо использовать, что как раз и решает эту проблему. Но писать такую ​​длинную строку каждый раз при импорте очень неудобно. В это время необходимо использоватьbabel-plugin-importэтот плагин.

import { demoComponent, demoComponent1, demoComponent2 } from 'xxxUI'

// 使用babel-plugin-import插件能自动将以上这种调用形式在AST(抽象语法树)中改写成以下形式。
// 这样就能方便地引入相关组件,又不用担心一次全部引入导致包过大的问题

import demoComponent from xxxUI/component/demoComponent
import demoComponent1 from xxxUI/component/demoComponent
import demoComponent2 from xxxUI/component/demoComponent

Наконец в.babelrcНастройте путь, который необходимо преобразовать в

// .babelrc
{
    ...
    "plugins":[
        "import", {
            "libraryName": "xxxUI",
            "libraryDirectory": "component",
        }
    ]
}

Следует отметить, что настраивать здесь должен пользователь библиотеки компонентов, а не тот, который прописан в библиотеке компонентов.babelrcсередина. Если библиотека компонентов поддерживает загрузку по требованию, эта конфигурация должна быть записана наREADME.mdВыбор остается за пользователем библиотеки компонентов. Плюсы и минусы загрузки по требованию зависят от конкретной среды проекта.需要具体情况具体分析.

image
Если загрузка по требованию не установлена, упаковывается вся библиотека компонентов.

image
Установлена ​​загрузка по требованию, и загружаются только используемые компоненты.

И вот так, с продуманной файловой структурой, цель2,3,6достигнуто.

войти

После уточнения структуры проекта следующим шагом будет сбор исходного кода компонента. Вообще говоря, необходимо толькоwebpackизentryТолько входной файл должен быть установлен в конфигурацииindexИменно такentry: path.resolve(__dirname, 'src', 'index.jsx'). Однако, поскольку нам нужно, чтобы каждый компонент был упакован независимо друг от друга, нам нужно импортировать компоненты один за другим, сохраняя при этом соответствующую файловую структуру.

function getFileCollection() {
    const globPath = './src/**/*.*(jsx|js)';
    const files = glob.sync(globPath);
    return files;
}

function entryConfig() {
    let entryObj = {};
    getFileCollection().forEach(item => {
        const filePath = item.replace('./src', '');
        entryObj[filePath] = path.resolve(__dirname, item);
    });
    return entryObj;
}

используется здесьglobЭтот очень полезный инструмент может легко сопоставить соответствующие файлы. Окончательный результат — это объект сопоставления пути к файлу, мы можем видеть, какие файлы вводятся в консоли.

image

Хорошо, следующий шаг — как поступить с этими исходными файлами.

Обработка компиляции и библиотека компонентов Tree Shaking

Процесс обработки здесь очень прост, логика заключается в конфигурацииbabelБудуes6+Исходный код обрабатывается вes5совместимый код, кстати тожеsvgМаленькие значки превращаются вbase64Формат встроенный. Это больше сделано для того, чтобы пользователи могли использовать эту библиотеку компонентов с минимально возможной настройкой и минимальными начальными затратами. Вот если бы при этом держатьes6код, который позволяет разработчикам свободно настраиватьTree Shaking(Например, в сценарии, где разработчик использует только метод в компоненте, нет необходимости представлять весь компонент). О том, как разработчики настраиваютTree ShakingРасскажу об этом в конце.

Модули Es6 обеспечивают модульность на уровне синтаксиса,Tree ShakingОн основан на модульности ES6 и может быть скомпилирован и упакован на узле вAST(Абстрактное синтаксическое дерево) статический анализ, удаление неиспользуемого кода. Наш скомпилированный и упакованный код es5 не может быть обработанTree Shakingиз.

// webpack.config中的loader配置
rules: [{
    test: /.jsx|.js$/,
    loader: 'babel-loader',
    exclude: /node_modules/
}, {
    test: /\.(jpg|png|gif|svg|jpeg)$/,
    loader: 'url-loader',
    exclude: /node_modules/
}]
// .babelrc
{
    "presets": [
        ["@babel/preset-env", {
            // 浏览器兼容方案配置
            "targets": {
                "browsers": [
                    ">0.25%",
                    "not ie 11",
                    "not op_mini all"
                ]
            }
        }],
        "@babel/preset-react",
    ],
    "plugins": [
        // 一些必备的转换插件
        "@babel/plugin-proposal-function-bind",
        "@babel/plugin-proposal-class-properties",
        // 解决编译中产生的重复的工具函数
        "@babel/plugin-transform-runtime",
        "transform-remove-console"
    ]
}

Достигнут пункт 7 цели.

выход

пакет, скомпилированный вывод вdistкаталог, обратите внимание, чтоdistСтруктура в каталоге должна быть такой же, какsrcКаталог должен быть последовательным, чтобы путь ссылок между компонентами и компонентами не был хаотичным, как это,distструктура каталогов сsrcсходство.

image

посмотри сноваoutputDist можно вывести в следующей конфигурации, так как мы сохраняем информацию о пути к файлу во входном файле, поэтому здесь напрямую меняем суффикс.libraryTargetФункция заключается в установке формата упаковки, который используется здесьumdстандарт. если установленоlibrary, то он будет экспортирован как однократная ссылочная формаimport xxxUI from 'xxxUI', чего мы не хотим.libraryа такжеlibraryTargetЗначение отличается в зависимости от типа проекта.Подробнее см. здесь

output: {
    filename: (chunkData) => {
        let filePath = chunkData.chunk.name;
        const filename = filePath.replace('.jsx', '.js');
        return filename;
    },
    path: __dirname + '/dist',
    libraryTarget: 'umd',
    // library: 'xxxUI'
}

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

image

Нам нужно настроить так, отфильтроватьimportВходящие сторонние пакеты

externals: [
    function(context, request, callback) {
        // 允许编译以下后缀文件
        if (/.jsx|.jpg|.png|.gif|.svg|.jpeg$/g.test(request)) {
            return callback();
        }
        callback(null, request);
    }
]

image

Вы можете увидеть огромные изменения! Теперь весь размер упаковки составляет всего120kb(удалить стили)

Так как стиль独立抽离Выходи, просто скопируй стиль вdistКаталог может быть, конечно, плагин может быть настроен на автоматическое завершение.

new CopyPlugin([{
    from: './sass',
    to: './sass'
}])

достижения цели4,5точка

Окончательный релиз

  1. Перейти на официальный сайт для завершения регистрации
  2. npm loginАвторизоваться
  3. Добавить к.npmignoreфайл, перечислите файлы, которые необходимо игнорировать
  4. Добавить кREADME.md, напишите необходимые инструкции, это хорошая привычка
  5. существуетpackage.jsonизscriptдобавить команду вwebpack --mode production && npm publish ./dist.这里意思是采用生产模式打包并将distКаталог опубликован наnpm.

к концуREADME.mdМануал можно написать так

// 安装
npm i -S xxxUI

// webpack配置处理样式
{
    test: /\.scss$/,
    use: [MiniCssExtractPlugin.loader, 'css-loader', "postcss-loader", 'sass-loader'],
    include: [
        path.join(__dirname, 'node_modules/xxxUI/sass/')
    ]
}

// 在index.jsx中引入样式
import "xxxUI/sass/index.scss";

// 可选项---------------
// .babelrc 配置按需加载
"plugins": [
    [
        "import",
        {
            "libraryName": "xxxUI",
            "libraryDirectory": "component",
        }
    ],
    // ...
]

// 可选项---------------
// 配置Tree Shaking
// webpack.config.js
// ...
{
    test: /\.scss$/,
    use: [MiniCssExtractPlugin.loader, 'css-loader', "postcss-loader", 'sass-loader'],
    include: [
        path.join(__dirname, 'node_modules/xxxUI/sass/')
    ],
    // 样式无需进行Tree Shaking
    sideEffects: true
}
// ...
optimization: {
    usedExports: true,
    minimizer: [
       new TerserPlugin({})
    ]
}
// .babelrc
"presets": [
    [
        "@babel/preset-env",
        {
            // 想达到Tree Shaking效果这里
            "modules": false,
        }
    ]
]

Варианты модулей в babel:'amd' | 'umd' | 'systemjs' | 'commonjs' | falseОни, поскольку Tree Shaking основаны на модулях ES6, не могут быть преобразованы в другие стандарты здесь и могут быть выбраны толькоfalse, то есть с помощью модуля стандартной компиляции исходного файла.

Готово, практичная библиотека компонентов выпущена, заходите и пробуйте.

модульный тест

Подождите, кажется, что юнит-теста не хватает.На самом деле слишком много точек (кенг), чтобы обратить внимание.Я не могу закончить его все сразу.《Re从零开始的组件单元测试》Подробно развернуть.

конец

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

Демонстрация онлайн-компонента и исходный код библиотеки компонентов

Недавно я наконец-то разобрался с кодом, а предыдущее написание действительно не очень красивое.Онлайн-демонстрация компонента&Исходный код библиотеки компонентов

Как написать компоненты в библиотеке компонентов?

серия веб-безопасности