Заменить библиотеку компонентов Vue для загрузки по требованию

Vue.js

Загружайте адрес склада DEMO по запросу

задний план

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

Зависимости библиотеки компонентов управляются на NPM, а библиотеки компонентов находятся в корневом каталоге проектаindex.jsВ качестве экспортного файла файл импортирует все компоненты проекта и предоставляет метод установки компонента.

index.js

import Button from "./button";
import Table from "./table";
import MusicPlayer from "./musicPlayer";

import utils from "../utils"
import * as directive from "../directive";
import * as filters from "../filters";

const components = {
    Button,
    Table,
    MusicPlayer
}

const install = (Vue) => {
    Object.keys(components).forEach(component => Vue.use(component));
    
    //  此处继续完成一些服务的挂载
}

if (typeof window !== 'undefined' && window.Vue) {
  install(Vue, true);
}

export default {
    install,
    ...components
}

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

import JRUI from 'jr-ui';
Vue.use(JRUI);

Компиляция библиотеки компонентов передается для компиляции в службу компиляции бизнес-системы.

То есть сам проект библиотеки компонентов компилироваться не будет, а только экспортируется как компонент. node_module — это как бесплатный облачный диск для хранения кода библиотеки компонентов.

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

Но есть и недостатки

  1. Более конкретный код нельзя использовать в библиотеке компонентов.

    vue-cli будет статически скомпилирован вnode_moduleСсылочные файлы .vue, но не компилируютсяnode_moduleВ других файлах, когда-то в коде библиотеки компонентов существуют специальные расширения синтаксиса (JSX) или специальные языки (TypeScript). В этот момент запуск проекта не запустится.

  2. библиотека компонентовwebpackспециальные переменные не будут иметь никакого эффекта

    библиотека компонентовwebpackКонфигурация не будет выполняться бизнес-системой, поэтому нельзя использовать такие атрибуты, как псевдонимы путей в библиотеке компонентов.

  3. Зависимости библиотеки компонентов полностью загружаются каждый раз

    index.jsЭто полный импорт компонентов, поэтому даже если бизнес-система использует только некоторые компоненты,index.jsВсе файлы компонентов (ресурсы изображений, зависимости) также будут упакованы, а том зависимостей всегда имеет полный размер.

Не существует такой вещи, как бизнес-система, использующая только один или два компонента, и каждой бизнес-системе требуется большинство компонентов.

Почти каждый проект будет использовать что-то вроде按钮,输入框,下拉选项,表格и другие общие базовые компоненты.

Только некоторые компоненты используются только в нескольких специальных направлениях бизнеса, таких как富文本编辑器,音乐播放器.

Классификация компонентов

Чтобы решить вышеуказанные проблемы и завершить эффект внедрения по требованию. Предоставляет два способа экспорта компонентов,Полный экспорт,Базовый экспорт.

Существует два типа экспорта компонентов.основные компоненты,Импорт компонентов по запросу.

Критерии оценки для компонентов по запросу:

  1. Меньше использования бизнес-системы
  2. Компонент содержит сторонние зависимости с большими объемами или большим количеством файлов ресурсов.
  3. Не ссылается внутри других компонентов

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

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

Ссылаться наelement-uiДля схемы экспорта библиотеки компонентов зависимости компонентов, экспортируемые библиотекой компонентов, должны быть снабжены отдельно упакованным файлом зависимостей для каждого компонента.

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

base.js

import Button from "./Button";
import Table from "./table";

const components = {
    Button,
    Table
}

const install = (Vue) => {
    Object.keys(components).forEach(component => Vue.use(component));
}

export default {
    install,
    ...components
}

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

vue.config.js

const devConfig = require('./build/config.dev');
const buildConfig = require('./build/config.build');

module.exports = process.env.NODE_ENV === 'development' ? devConfig : buildConfig;

config.build.js

const fs = require('fs');
const path = require('path');
const join = path.join;
//  获取基于当前路径的目标文件
const resolve = (dir) => path.join(__dirname, '../', dir);

/**
 * @desc 大写转横杠
 * @param {*} str
 */
function upperCasetoLine(str) {
  let temp = str.replace(/[A-Z]/g, function (match) {
    return "-" + match.toLowerCase();
  });
  if (temp.slice(0, 1) === '-') {
    temp = temp.slice(1);
  }
  return temp;
}

/**
* @desc 获取组件入口
* @param {String} path
*/
function getComponentEntries(path) {
    let files = fs.readdirSync(resolve(path));

    const componentEntries = files.reduce((fileObj, item) => {
      //  文件路径
      const itemPath = join(path, item);
      //  在文件夹中
      const isDir = fs.statSync(itemPath).isDirectory();
      const [name, suffix] = item.split('.');
    
      //  文件中的入口文件
      if (isDir) {
        fileObj[upperCasetoLine(item)] = resolve(join(itemPath, 'index.js'))
      }
      //  文件夹外的入口文件
      else if (suffix === "js") {
        fileObj[name] = resolve(`${itemPath}`);
      }
      return fileObj
    }, {});
    
    return componentEntries;
}

const buildConfig = {
  //  输出文件目录
  outputDir: resolve('lib'),
  //  webpack配置
  configureWebpack: {
    //  入口文件
    entry: getComponentEntries('src/components'),
    //  输出配置
    output: {
      //  文件名称
      filename: '[name]/index.js',
      //  构建依赖类型
      libraryTarget: 'umd',
      //  库中被导出的项
      libraryExport: 'default',
      //  引用时的依赖名
      library: 'jr-ui',
    }
  },
  css: {
    sourceMap: true,
    extract: {
      filename: '[name]/style.css'
    }
  },
  chainWebpack: config => {
    config.resolve.alias
      .set("@", resolve("src"))
      .set("@assets", resolve("src/assets"))
      .set("@images", resolve("src/assets/images"))
      .set("@themes", resolve("src/themes"))
      .set("@views", resolve("src/views"))
      .set("@utils", resolve("src/utils"))
      .set("@mixins", resolve("src/mixins"))
      .set("jr-ui", resolve("src/components/index.js"));
  }
}

module.exports = buildConfig;

В это время нашnpm run buildВыполняемая команда - вышеприведенный абзацwebpackконфигурация.

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

configureWebpack: {
    //  入口文件
    entry: getComponentEntries('src/components'),
    //  输出配置
    output: {
      //  文件名称
      filename: '[name]/index.js',
      //  输出依赖类型
      libraryTarget: 'umd',
      //  库中被导出的项
      libraryExport: 'default',
      //  引用时的依赖名
      library: 'jr-ui',
    }
},
css: {
    sourceMap: true,
    extract: {
      filename: '[name]/style.css'
    }
}
function getComponentEntries(path) {
    let files = fs.readdirSync(resolve(path));

    const componentEntries = files.reduce((fileObj, item) => {
      //  文件路径
      const itemPath = join(path, item);
      //  在文件夹中
      const isDir = fs.statSync(itemPath).isDirectory();
      const [name, suffix] = item.split('.');
    
      //  文件中的入口文件
      if (isDir) {
        fileObj[upperCasetoLine(item)] = resolve(join(itemPath, 'index.js'))
      }
      //  文件夹外的入口文件
      else if (suffix === "js") {
        fileObj[name] = resolve(`${itemPath}`);
      }
      return fileObj;
    }, {});
    
    return componentEntries;
}

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

components                         组件文件目录
│                         
│— button                         
│   │— button.vue                  button组件
│   └─ index.js                    button组件导出文件
│
│— input                         
│   │— input.vue                   input组件
│   └─ index.js                    input组件导出文件
│
│— musicPlayer
│   │— musicPlayer.vue             musicPlayer组件
│   └─ index.js                    musicPlayer组件导出文件
│
│  base.js                         基础组件的导出文件
└─ index.js                        所有组件的导出文件

lib                                编译后的文件目录
│                         
│— button                         
│   │— style.css                   button组件依赖样式
│   └─ index.js                    button组件依赖文件
│
│— input                         
│   │— style.css                   input组件依赖样式
│   └─ index.js                    input组件依赖文件
│
│— music-player
│   │— style.css                   musicPlayer组件依赖样式
│   └─ index.js                    musicPlayer组件依赖文件
│
│— base                         
│   │— style.css                   基础组件依赖样式
│   └─ index.js                    基础组件依赖文件
│
└─ index                         
    │— style.css                   所有组件依赖样式
    └─ index.js                    所有组件依赖文件

При получении всех входов компонента выполните горизонтальную обработку для имени входа.upperCasetoLine,Потому чтоbabel-plugin-importПри импорте по требованию, если имя компонента — camelCase, путь будет преобразован в разделенный тире.

Например, внедрение бизнес-систем

import { MusicPlayer } from "jr-ui"

//  转化为
var MusicPlayer = require('jr-ui/lib/music-player');
require('jr-ui/lib/music-player/style.css');

Из-за соглашения об именах библиотек компонентов регистр имен папок компонентов не разделяется дефисом. Но для того, чтобы позволитьbabel-plugin-importОн работает правильно, поэтому здесь преобразуется имя входного файла каждого файла.

Если имя не конвертируется методом, вы также можете настроить конфигурацию плагина-импорта в babel.config.jscamel2DashComponentNameдляfalse, чтобы отключить перевод имени.

проблема с именами путей для импорта плагина babel

При использовании бизнес-системы

Полный экспорт экспортирует все компоненты по умолчанию

//  全量导出
import JRUI from "jr-ui";
import "jr-ui/lib/index/index.css";

Vue.use(JRUI);

Базовый экспорт экспортирует только базовые компоненты, если вам нужно использовать дополнительные компоненты, вам нужно установитьbabel-plugin-importплагин и конфигурацияbabel.config.jsчтобы завершить преобразование оператора импорта

npm i babel-plugin-import -D

Бизнес-система - конфигурация babel.config.js

module.exports = {
  presets: ["@vue/app", ["@babel/preset-env", { "modules": false }]],
  plugins: [
    [
      "import",
      {
        "libraryName": "jr-ui",
        "style": (name) => {
            return `${name}/style.css`;
        }
      }
    ]
  ]
}
//  基础导出
import JRUI_base from "jr-ui/lib/base";
import "jr-ui/lib/base/index.css";
Vue.use(JRUI_base);

//  按需使用额外引入的组件
import { MusicPlayer } from "jr-ui";
Vue.use(MusicPlayer);

Отладка кода библиотеки компонентов в бизнес-системе

Если вы все еще хотите отлаживать код библиотеки компонентов, при импорте компонента импортируйте компоненты напрямую в зависимость библиотеки компонентов.componentsэкспортировать файлы под компоненты и перезаписать установку. Целевой компонент можно отлаживать.

import button from "jr-ui/src/components/button";
Vue.use(button);

Эффект оптимизации

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

Адрес склада ящиков, Если у вас есть какие-либо вопросы или ошибки, пожалуйста, задавайте вопросы или указывайте на них.

С праздником Дня труда :)

Have a nice day.

использованная литература

vue-cli выполняет синтаксический анализ

babel-plugin-import