Это третий день моего участия в Gengwen Challenge, смотрите подробности мероприятияОбновить вызов
Загружать самое раннее использование по запросу
и загружает по запросу
Конфигурация плагина
в vite.config.ts
import usePluginImport from "vite-plugin-importer";
export default defineConfig({
plugins: [
usePluginImport({
libraryName: "ant-design-vue",
libraryDirectory: "es",
style: "css",
}),
]
})
использовать
import { Button, List, Checkbox, Popconfirm, Input } from "ant-design-vue";
components: {
[Checkbox.name]: Checkbox,
[Input.name]: Input,
[List.name]: List,
[List.Item.name]: List.Item,
AListItemMeta: List.Item.Meta, //这里用框架List.Item.Meta.name注册不上只能写死,可能使用displayName可以
[Button.name]: Button,
[Popconfirm.name]: Popconfirm,
},
Болевые точки:
- Поскольку библиотека компонентов antd использует множество подкомпонентов, таких как
List
компоненты подListItem
, если вы зарегистрируете на один меньше, шаблон не будет парситься - Нужно ввести его снова, а потом зарегистрироваться
key
Напишите еще раз, снова напишите значение, один и тот же код нужно написать три раза, и обратите внимание на взаимосвязь между дочерним компонентом и родительским компонентом. - Некоторые компоненты не представлены
name
свойства, такие какAListItemMeta
Поэтому его нужно писать до смерти, что приводит к несовместимости стилей.
элемент-плюс нагрузки по запросу
Конфигурация плагина
в vite.config.ts
import styleImport from "vite-plugin-style-import";
export default defineConfig({
plugins: [
styleImport({
libs: [
{
libraryName: "element-plus",
esModule: true,
ensureStyleFile: true,
resolveStyle: name => {
return `element-plus/lib/theme-chalk/${name}.css`;
},
resolveComponent: name => {
return `element-plus/lib/${name}`;
},
},
],
}),
]
})
Как использовать
import { ElForm, ElButton, ElFormItem, ElInput } from "element-plus";
components: {
ElForm,
ElButton,
ElInput,
ElFormItem,
},
Болевые точки:
- Один и тот же компонент родитель-потомок должен быть введен дважды, например
Form
а такжеFormItem
,а такжеantd
разница в томelement
Родительский и дочерний компоненты должны быть представлены отдельно и могут использоваться толькоcomponents
метод регистрации, ноantd
Кроме того, он также поддерживает регистрацию подключаемых модулей, используяapp.use(xxx)
Улучшать
Чтобы решить проблему, вызванную компонентами «родитель-потомок», представленными компонентами «родитель-потомок» antd,antd
предоставленapp.use(xx)
То, как этот плагин зарегистрирован,antd
Проблема зависимостей между подкомпонентами автоматически решается внутри: например, чтобы использовать компонент List, нужно всего лишьapp.use(List)
готов использоватьList和ListItem,ListItemMeta
, что гораздо удобнее
Поэтому я написалuseComp
метод с использованием app.use(comp);
зарегистрироваться
В vue3 вам сначала нужно получить экземпляр приложения, vue3 предоставляетgetCurrentInstance
Этот метод может получить экземпляр текущего компонента, а затем получить объект приложения в глобальном контексте через текущий экземпляр,instance?.appContext.app;
, чтобы для регистрации можно было использовать app.use.Также следует отметить, что один и тот же компонент позволяет избежать повторной регистрации, и нужно записывать уже зарегистрированные компоненты.
Код useAntd.ts выглядит следующим образом:
import { Plugin, getCurrentInstance } from "vue";
interface Registed {
[key: string]: boolean;
}
let registed: Registed = {};
type Comp = {
displayName?: string;
name?: string;
} & Plugin;
type RegisteComps = (...comps: Comp[]) => void;
export const useComp: RegisteComps = (...comps) => {
comps.forEach(comp => {
const name = comp.displayName || comp.name;
if (name && !registed[name]) {
const instance = getCurrentInstance();
const app = instance?.appContext.app;
if (app) {
app.use(comp);
registed[name] = true;
}
}
});
};
Как использовать:
import { List, Table, Button, Space } from "ant-design-vue";
import { useComp } from "@/hooks/useAntd";
//...略
setup() {
useComp(List, Table, Button, Space);
return {}
}
Решить болевые точки:
- Нет необходимости в зависимостях между родительскими и дочерними компонентами
- уменьшать
components
зарегистрированный код - Какие компоненты используются, непосредственно
import
Название компонента в скобках, скопируйте и вставьте вuseComp
В методе это несложная операция.
Оставшиеся болевые точки:
-
element,naive
еще нужно использоватьcomponents
Зарегистрируйтесь по одному - По сравнению с tsx в настройке на одну строчку регистрационного кода больше.
Идеальная форма:
Используемые компоненты не должны заботиться об импорте и регистрации, как и полный импорт, их можно использовать непосредственно в шаблоне, который можно удобно написать, не беспокоясь о том, что вы забудете зарегистрировать компоненты.
Долго искал подобный метод, пока не зашел в сообщество и не нашел большого парняantfuс открытым исходным кодомvite-plugin-components
, именно то, что я искал.
Достигаемая им функция: автоматически анализировать компоненты, используемые в шаблоне, а затем автоматически вводить их по мере необходимости, и больше не требуется ручная регистрация.
Но в идеале очень пухленькая, в реальности очень худенькая, да и на некоторые ямы наступил
Конфигурация плагина:
import ViteComponents, {
AntDesignVueResolver,
ElementPlusResolver,
ImportInfo,
kebabCase,
NaiveUiResolver,
pascalCase,
} from "vite-plugin-components";
export default defineConfig({
plugins: [
ViteComponents({
customComponentResolvers: [
AntDesignVueResolver(),//官方插件提供
ElementPlusResolver(),//官方插件提供
NaiveUiResolver(),//官方插件提供
]
})
]
})
Результат попытки:
- naiveui идеальная поддержка, как!
- Официальный scss, используемый elementui, должен установить зависимости scss
- antdv имеет только несколько компонентов, которые можно разобрать, например
layout,list table
Этот общий компонент не может быть решен
решить:
element-plus перезаписывает распознаватель:
Причина перезаписи: Поскольку проект не использует scss и использует меньше, поэтому конвертируйте scss в css для загрузки стилей.
Официальная формулировка выглядит следующим образом:
const { importStyle = true } = options
if (name.startsWith('El')) {
const partialName = name[2].toLowerCase() + name.substring(3).replace(/[A-Z]/g, l => `-${l.toLowerCase()}`)
return {
path: `element-plus/es/el-${partialName}`,
sideEffects: importStyle ? `element-plus/packages/theme-chalk/src/${partialName}.scss` : undefined,
}
}
после перезаписи: изменение пути, импортированного из каталога lib, прямое использование имени компонента в качестве имени каталога файлов для импорта и отказ от сложного преобразования имен компонентов. код показывает, как показано ниже:
customComponentResolvers: [
// AntDesignVueResolver(),
// ElementPlusResolver(),
NaiveUiResolver(),
name => {
if (name.startsWith("El")) {
// Element UI
const partialName = kebabCase(name); //ElButton->el-button
return {
path: `element-plus/lib/${partialName}`,
sideEffects: `element-plus/lib/theme-chalk/${partialName}.css`,
};
}
}
]
antdv
Официальная практика такова:
export const AntDesignVueResolver = (): ComponentResolver => (name: string) => {
if (name.match(/^A[A-Z]/))
return { importName: name.slice(1), path: 'ant-design-vue/es' }
}
Существующие проблемы:
-
<a-list-item>
Этот компонент недоступенant-design-vue/es/list-item
Ищем этот каталог, такого каталога нет, на самом деле его настоящий каталогant-design-vue/es/list/Item.js
-
<a-layout-content
У компонента нет соответствующего пути, он генерируется методом генератора в макете и привязывается к объекту макета, его фактический путь должен бытьant-design-vue/es/layout/layout.js
свойство содержимого -
<a-select-option>
Этот компонент также должен быть выбран, но на самом деле он импортирован.vc-select/Option.js
Принадлежит к базовому компоненту -
<a-menu-item>
Компоненты — это подкомпоненты, принадлежащие меню, а каталог — этоant-design-vue/es/menu/MenuItem.js
, Это отличается от предыдущих правил. Я думал, что это должно называться Item, но здесь оно отличается, поэтому требует особого отношения. -
а также
<a-tab-pane>
Для такого компонента каталог стилей, который ему нужен, находится на вкладках, но на самом деле его файловый каталог находится вvc-tabs/src
, он также требует специального лечения
Вышеуказанные проблемы являются причинами того, что официальный метод записи не может нормально загружаться в соответствующие компоненты.Поэтому, чтобы решить вышеуказанные проблемы, я написал много логики суждений для разных ситуаций, чтобы исправить путь импорта компонентов, но все еще есть некоторые компоненты, которые нельзя импортировать в соответствующие компоненты, поскольку некоторые компоненты являются функциональными или генерируются генераторами, у них нет независимых файлов подкомпонентов, и не найдено подходящего метода для введения соответствующих свойств подкомпонентов.
разрешить путь к компонентуgetCompPath
метод, код выглядит следующим образом:
function getCompPath(
compName: string
): {
dirName: string;
compName: string;
styleDir: string;
importName?: string;
sideEffects?: ImportInfo;
} {
const hasSubComp = [
"Menu",
"Layout",
"Form",
"Table",
"Modal",
"Radio",
"Button",
"Checkbox",
"List",
"Collapse",
"Descriptions",
"Tabs",
"Mentions",
"Select",
"Anchor",
"Typography",
// "TreeSelect",
]; //包含子组件的组件
const keepSelf = [
"MenuItem",
"SubMenu",
"FormItem",
"RadioButton",
"CollapsePanel",
"TabPane",
"AnchorLink",
]; //保留原子组件名称
const keepFather = [
"LayoutHeader",
"LayoutContent",
"LayoutFooter",
"DescriptionsItem",
]; //需要使用父组件名称的子组件 LayoutFooter->'' 之所以转成空是因为最后拼接的结果是dirName+compName,避免重复
const rootName = hasSubComp.find((name: string) => compName.startsWith(name));
const usePrevLevelName = ["ListItemMeta"]; //使用当前组件的上一级名称 ListItemMeta->Item
const getPrevLevelName = () => {
const split = kebabCase(compName).split("-");
return pascalCase(split[split.length - 2]);
};
const fatherAlias = {
TabPane: "vc-tabs/src",
MentionsOption: "vc-mentions/src",
SelectOption: "vc-select",
TreeSelectNode: "vc-tree-select/src",
};
const compAlias = {
TreeSelectNode: "SelectNode",
};
const styleMap = {
TabPane: "tabs",
MentionsOption: "mentions",
SelectOption: "select",
TreeSelectNode: "tree-select",
};
// const importNameMap = {
// LayoutContent: "Content",
// LayoutHeader: "Header",
// LayoutFooter: "Footer",
// };
let dirName = rootName?.toLowerCase() ?? kebabCase(compName);
if (fatherAlias[compName]) {
dirName = fatherAlias[compName];
}
let compNameStr = "";
if (keepSelf.includes(compName)) {
compNameStr = compName;
} else if (keepFather.includes(compName)) {
compNameStr = "";
} else if (usePrevLevelName.includes(compName)) {
compNameStr = getPrevLevelName();
} else if (rootName) {
compNameStr = compName.replace(rootName, "");
}
const compRequired = {
TypographyTitle: "ant-design-vue/es/" + dirName + "/Base",
TypographyText: "ant-design-vue/es/" + dirName + "/Base",
};
return {
// importName: importNameMap[compName],
dirName: fatherAlias[compName] ?? dirName,
styleDir: `${styleMap[compName] ?? dirName}`,
compName: compAlias[compName] ?? compNameStr,
sideEffects: compRequired[compName]
? {
path: compRequired[compName],
}
: undefined,
};
}
Пользовательский преобразователь, код выглядит следующим образом
ViteComponents({
customComponentResolvers: [
name => {
if (name.match(/^A[A-Z]/)) {
//ant-design-vue
const importName = name.slice(1);
const dirName = kebabCase(importName);
const compName = pascalCase(importName); //AListItem->ListItem
const compPath = getCompPath(compName);//这里解析组件的真实路径
const sideEffects = [
{
path: `ant-design-vue/es/${compPath.styleDir}/style`,
},
];
if (compPath.sideEffects) {
sideEffects.push(compPath.sideEffects);
}
return {
path: `ant-design-vue/es/${compPath.dirName}/${compPath.compName}`,
sideEffects,
};
}
return null;
},
],
globalComponentsDeclaration: true,
}),
После анализа большинство компонентов можно использовать, а некоторые оставшиеся компоненты нельзя использовать в обычном режиме.
Файлы объявлений некоторых компонентов, сгенерированных плагином, выглядят следующим образом:
declare module 'vue' {
export interface GlobalComponents {
AMenuItem: typeof import('ant-design-vue/es/menu/MenuItem')['default']
AMenu: typeof import('ant-design-vue/es/menu/')['default']
ALayoutHeader: typeof import('ant-design-vue/es/layout/')['default']
ALayoutContent: typeof import('ant-design-vue/es/layout/')['default']
ALayoutFooter: typeof import('ant-design-vue/es/layout/')['default']
ALayout: typeof import('ant-design-vue/es/layout/')['default']
AButton: typeof import('ant-design-vue/es/button/')['default']
ADivider: typeof import('ant-design-vue/es/divider/')['default']
AInput: typeof import('ant-design-vue/es/input/')['default']
AFormItem: typeof import('ant-design-vue/es/form/FormItem')['default']
ASpace: typeof import('ant-design-vue/es/space/')['default']
AForm: typeof import('ant-design-vue/es/form/')['default']
ACheckbox: typeof import('ant-design-vue/es/checkbox/')['default']
AListItemMeta: typeof import('ant-design-vue/es/list/Item')['default']
APopconfirm: typeof import('ant-design-vue/es/popconfirm/')['default']
AListItem: typeof import('ant-design-vue/es/list/Item')['default']
AList: typeof import('ant-design-vue/es/list/')['default']
ATable: typeof import('ant-design-vue/es/table/')['default']
}
}
Конкретные вопросы заключаются в следующем:
-
layout
компоненты ниже,Content,Header,Footer
, так как по умолчанию только импортdefault
экспортируется, если импортируется что-то вроде['default']['Content']
, должно быть нормально, теперь проблема в том, что подкомпоненты типа Content разбираются как макеты, в результате чего проблемы со стилями - Импорт компонента ListItemMeta не является нормальным, и его можно импортировать только
['default']
, если можно ввести['default']['Meta']
должны быть решены - Компонент типографики Внедрение компонента заголовка сообщит об ошибке:
TypeError: baseProps is not a function
Но на самом деле компонент вводит этот метод, используя относительный путь
Я надеюсь, что большие ребята могут помочь нам увидеть, как решить вышеуказанные проблемы. Я чувствую, что текущий код немного проблематичен для написания. Если есть более элегантный метод, вы можете связаться со мной.
-------------обновление 20210625------------
Версия 2.2.0-бета.5:Официально поддерживает компоненты vite-plugin.
Небольшие проблемы, требующие исправления, в основном орфографические ошибки в именах.
- Запись LayoutContent в LayouContent
- Экспорт PassWord переименован в InputPassoword и другие имена компонентов.
Примечание: После проверки официалку починили, а версию еще не выпустили, ждем релиза beta.6~~~
После изменения исходного кода node_modules обнаруживается, что он нормально загружается. Тогда официальный резолвер vite-plugin-compoennts все еще не прост в использовании, его все равно нужно настраивать, код такой
const importName = name.slice(1);
return {
importName: importName,
path: `ant-design-vue/es`,
sideEffects: "ant-design-vue/es/style",
};
Гораздо удобнее, хвалите скорость отклика и поддержку босса команды antd-vue Тан! ! !
возобновить
---------20210626 обновление -------------
Версия 2.2.0-beta.6: Официально исправлена орфографическая ошибка импорта, и ее можно использовать в обычном режиме в соответствии с описанным выше методом импорта.
предложение: Вышеупомянутый метод стиля стиля является полным импортом. Я не знаю, есть ли способ импорта по запросу. После импорта с использованием распознавателя, предоставленного vite-plugin, стиль отсутствует, но об ошибке не сообщается.
--------обновление 20210627-------------
vite-plugin-components: пр объединен и можно использовать, то есть стиль полностью импортирован
Все еще слишком медленно импортировать стили полностью, загружать много бесполезных стилей и пытаться загружать стили по запросу.
Поскольку antdv официально решил проблему импорта js, остальная часть импорта стилей не кажется такой уж хлопотной, пока вы найдете правильный каталог, Ссылаясь на предыдущий метод записи, таблица конфигурации по-прежнему пишется в соответствии с каталогом компонента.Идея заключается в следующем:
- Подкомпоненты или компоненты, требующие специальной обработки, сопоставляются в соответствии с обычным шаблоном, и в случае совпадения возвращается сконфигурированный каталог.
- Если нет подходящего компонента, непосредственно преобразуйте форму kebabCase (форма кебаба)
Интерфейс интерфейса выглядит следующим образом:
interface IMatcher {
pattern: RegExp;
styleDir: string;
}
Информация о компоненте, которую необходимо настроить после тестирования, выглядит следующим образом:
const matchComponents: IMatcher[] = [
{
pattern: /^Avatar/,
styleDir: 'avatar',
},
{
pattern: /^AutoComplete/,
styleDir: 'auto-complete',
},
{
pattern: /^Anchor/,
styleDir: 'anchor',
},
{
pattern: /^Badge/,
styleDir: 'badge',
},
{
pattern: /^Breadcrumb/,
styleDir: 'breadcrumb',
},
{
pattern: /^Button/,
styleDir: 'button',
},
{
pattern: /^Checkbox/,
styleDir: 'checkbox',
},
{
pattern: /^Card/,
styleDir: 'card',
},
{
pattern: /^Collapse/,
styleDir: 'collapse',
},
{
pattern: /^Descriptions/,
styleDir: 'descriptions',
},
{
pattern: /^RangePicker|^WeekPicker|^MonthPicker/,
styleDir: 'date-picker',
},
{
pattern: /^Dropdown/,
styleDir: 'dropdown',
},
{
pattern: /^Form/,
styleDir: 'form',
},
{
pattern: /^InputNumber/,
styleDir: 'input-number',
},
{
pattern: /^Input|^Textarea/,
styleDir: 'input',
},
{
pattern: /^Statistic/,
styleDir: 'statistic',
},
{
pattern: /^CheckableTag/,
styleDir: 'tag',
},
{
pattern: /^Layout/,
styleDir: 'layout',
},
{
pattern: /^Menu|^SubMenu/,
styleDir: 'menu',
},
{
pattern: /^Table/,
styleDir: 'table',
},
{
pattern: /^Radio/,
styleDir: 'radio',
},
{
pattern: /^Image/,
styleDir: 'image',
},
{
pattern: /^List/,
styleDir: 'list',
},
{
pattern: /^Tab/,
styleDir: 'tabs',
},
{
pattern: /^Mentions/,
styleDir: 'mentions',
},
{
pattern: /^Mentions/,
styleDir: 'mentions',
},
{
pattern: /^Step/,
styleDir: 'steps',
},
{
pattern: /^Skeleton/,
styleDir: 'skeleton',
},
{
pattern: /^Select/,
styleDir: 'select',
},
{
pattern: /^TreeSelect/,
styleDir: 'tree-select',
},
{
pattern: /^Tree|^DirectoryTree/,
styleDir: 'tree',
},
{
pattern: /^Typography/,
styleDir: 'typography',
},
{
pattern: /^Timeline/,
styleDir: 'timeline',
},
]
Напишите цикл для прохождения, чтобы получить реальный путь стиля
const importName = name.slice(1);
let styleDir;
const total = matchComponents.length;
for (let i = 0; i < total; i++) {
const matcher = matchComponents[i];
if (importName.match(matcher.pattern)) {
styleDir = matcher.styleDir;
break;
}
}
if (!styleDir) {
styleDir = kebabCase(importName);
}
return {
importName: importName,
path: `ant-design-vue/es`,
sideEffects: `ant-design-vue/es/${styleDir}/style`,
};
Укажите пр, надеюсь смогу пройти как можно скорее:PR-ссылка
--------- 20210629 Обновление ------------
vite-plugin-components: antfu выпустил версию 0.11.4, чувствуйте себя комфортно~~~
добавить функцию:Увеличение загрузки CSS также вызвано этой проблемой. Я думал о загрузке index.css раньше, но я не думал, что смогу напрямую загрузить css.js. После загрузки в popconfirm он сообщит об ошибке, если индекс .css не найден, потому что самого стиля нет, все полагаются на стили двух других компонентов.Теперь загрузите css.js напрямую, чтобы решить эту проблему, просто подгрузите соответствующие стили в js по мере необходимости. Код меняется следующим образом:
const getSideEffects: (
compName: string,
opts: AntDesignVueResolverOptions
) => string | undefined = (compName, opts) => {
const { importStyle = true, importCss = true, importLess = false } = opts
if (importStyle) {
if (importLess) {
const styleDir = getStyleDir(compName)
return `ant-design-vue/es/${styleDir}/style`
}
else if (importCss) {
const styleDir = getStyleDir(compName)
return `ant-design-vue/es/${styleDir}/style/css`
}
}
}
уже пр
Конфигурация использования плагина выглядит следующим образом:
Всего добавляется три параметра, по умолчанию ничего не передается, если параметры передаются, то они передаются в виде объектов. По умолчанию:
importStyle: true
importCss:true
importLess:false
- загружать только css
AntDesignVueResolver()
- только грузить меньше
AntDesignVueResolver({
importLess: true
})
Описание: нужно открывать меньше javascriptEnabled: true,
- Не загружайте стили компонентов:
AntDesignVueResolver({
importStyle: false
})
Параметры плагина определяются следующим образом:
export interface AntDesignVueResolverOptions {
/**
* import style along with components
*
* @default true
*/
importStyle?: boolean
/**
* import css along with components
*
* @default true
*/
importCss?: boolean
/**
* import less along with components
*
* @default false
*/
importLess?: boolean
}
Тестирование производительности
Некоторые студенты предположили, что мой алгоритм цикла оптимизации должен оптимизировать временную сложность от O (n) до O (1), используя карту для отображения, Но у меня также есть свои компромиссы при использовании обычного сопоставления, потому что, если вы используете карту для хранения сопоставления компонентов и путей стиля, есть следующие недостатки.
- Необходимо написать много конфигурации сопоставления, например, ListItem и ListItemMeta необходимо настроить на два
var map = {
ListItem: 'list',
ListItemMeta: 'list'
}
С обычной нужно настроить только одну строку, и независимо от списка плюс подкомпоненты, однострочную регулярку тоже можно сопоставить, а приведенную выше карту нужно дополнительно настроить, что снижает ремонтопригодность.
var reg = {
pattern: /^List/
dir: 'list'
}
- Разница в производительности, чтобы досконально понять разницу между отображением и регуляризацией в этом небольшом количестве данных, я специально написал метод проверки производительности для проверки разницы, адрес кода:код sandbox.io/yes/regex pi…
Результат: В основном разница между ними составляет около 0,1 мс, и есть победители и проигравшие. Карта не имеет подавляющего преимущества. Возможно, объем данных небольшой, но может быть разница, когда объем данных велик. , но думаю разница в производительности не большая.В таком случае выбирайте более ремонтопригодное решение
Связанные проблемы и код
Проблема со стилями, загружаемыми по запросу
vite-plugin-components issue о проблеме
Код:github — адрес проекта todo
разное
В последнее время поддерживаются некоторые библиотеки компонентов vue2 (представление пользовательского интерфейса и пользовательского интерфейса элемента). Эти две статьи резюмируются следующим образом:
- vite-plugin-components — поддерживает библиотеку компонентов vue2
- vite2 + vue2 + tsx + pinia разработка todo-list фактический обмен сводками по бою
Добро пожаловать в один клик и три ссылки~