Библиотека компонентов на основе Vue3element-plusОфициально выпущенный, element-plus используетсяTypeScript + Composition APIРефакторинг нового проекта. Официальный список следующих крупных обновлений, в этой статье будет читатьсяelement-plus, проанализировать рефакторинг исходного кода со следующих аспектов в целом и в деталях.Рекомендуется клонировать код компонента перед чтением этой статьи.
- Разрабатывать с помощью TypeScript
- Уменьшите связанность и упростите логику с помощью Vue 3.0 Composition API.
- Используйте новые функции Vue 3.0 Teleport для рефакторинга компонентов класса монтирования.
- Глобальный API Vue 2.0 переключился на API экземпляра Vue 3.0
- интернационализация
- Официальная документация Веб-сайт Упаковка
- Библиотека компонентов и упаковка стилей
- Поддерживайте и управляйте проектами с Lerna
Связанный с машинописью
element-plus вводит машинописный текст в дополнение к настройке соответствующегоeslintПравила проверки, плагины, определенияtsconfig.jsonКроме того, упаковкаes-moduleПри форматировании библиотеки компонентов я использовал некоторыеrollupплагин.
- @rollup/plugin-node-resolve
- rollup-plugin-terser
- rollup-plugin-typescript2
- rollup-plugin-vue
// build/rollup.config.bundle.js
import { nodeResolve } from '@rollup/plugin-node-resolve'
import { terser } from 'rollup-plugin-terser'
import typescript from 'rollup-plugin-typescript2'
const vue = require('rollup-plugin-vue')
export default [{
// ... 省略前面部分内容
plugins: [
terser(),
nodeResolve(),
vue({
target: 'browser',
css: false,
exposeFilename: false,
}),
typescript({
tsconfigOverride: {
'include': [
'packages/**/*',
'typings/vue-shim.d.ts',
],
'exclude': [
'node_modules',
'packages/**/__tests__/*',
],
},
}),
],
}]
@rollup/plugin-node-resolveУпаковка зависимых пакетов npm
rollup-plugin-terserсжатый код
rollup-plugin-vueУпакуйте файл vue, стиль css будет упомянут позже.gulpобрабатывать.
rollup-plugin-typescript2компилируется сtypescriptДа, нод-модули и файлы, связанные с тестами, исключены из конфигурации.Помимо включения реализации компонента, включение также включаетtypings/vue-shim.d.tsдокумент.
используется в плагинеtypings/vue-shim.d.tsфайл объявления типа (начиная с.d.tsФайл в конце будет автоматически проанализирован), который определяет некоторые объявления глобального типа, и эти переменные ограничения типа могут использоваться непосредственно в файлах ts или vue. Также используйте расширенную пару шаблоновimport XX from XX.vueИмпортированные переменные дают подсказки типа.
// typings/vue-shim.d.ts
declare module '*.vue' {
import { defineComponent } from 'vue'
const component: ReturnType<typeof defineComponent>
export default component
}
declare type Nullable<T> = T | null;
declare type CustomizedHTMLElement<T> = HTMLElement & T
declare type Indexable<T> = {
[key: string]: T
}
declare type Hash<T> = Indexable<T>
declare type TimeoutHandle = ReturnType<typeof global.setTimeout>
declare type ComponentSize = 'large' | 'medium' | 'small' | 'mini'
Кромеd.tsВне файла объявление типа props в element-plus использует vue3propType. Уведомление к следующему примеру, использование типа реквизита выполняет проптип в соответствии с нашим определением конструктора самоуправления, а затем в сочетании с типографией Type Crought. Используются другие виды не-декларации реквизитinterface.
import { PropType } from 'vue'
export default defineComponent({
name: 'ElAlert',
props: {
type: {
type: String as PropType<'success' | 'info' | 'error' | 'warning'>,
default: 'info',
}
}
})
Дополнительную поддержку машинописного текста vue3 можно посмотретьофициальная документация
Composition API
Официальное описание использует API Vue 3.0 Composition для уменьшения связанности и упрощения логики. Использование Composition API и повторное использование хуковvue-3-playgroundИнтуитивно понятный и краткий пример представлен в реализации демонстрации корзины покупок.
Чтобы узнать об использовании часто используемого Composition API, вы можете ознакомиться с этой краткой статьей.Быстро используйте последние 15 распространенных API-интерфейсов Vue3.
В дополнение к переписыванию компонентов с использованием нового Composition API, element-pluspackages/hooksНесколько повторно используемых файлов ловушек извлекаются из каталога.
Используется элементами управления, такими как автозаполнение, ввод и т. д.use-attrsНапример, главное, что нужно сделать, это наследовать связанные свойства и события, что-то вроде$attrsа также$listenerфункция, но была выполнена некоторая фильтрация для удаления некоторых свойств и привязок событий, которые не нужно наследовать.
watchEffect(() => {
const res = entries(instance.attrs)
.reduce((acm, [key, val]) => {
if (
!allExcludeKeys.includes(key) &&
!(excludeListeners && LISTENER_PREFIX.test(key))
) {
acm[key] = val
}
return acm
}, {})
attrs.value = res
})
Vue3 по-прежнему сохраняет миксин, мы можем использовать миксин для повторного использования логики в определенных компонентах или глобально, а также вводить хуки, чтобы улучшить существование миксина.вопрос:
- Происхождение свойств, представленных в контексте рендеринга, неясно. Например, при использовании нескольких примесей для чтения шаблона компонента может быть сложно определить, из какой примеси введено конкретное свойство.
- Конфликт пространства имен. Миксины могут конфликтовать с именами свойств и методов.
Преимущества хуков
- Свойства, открытые для шаблонов, имеют явное происхождение, потому что они являются значениями, возвращаемыми функциями Hook.
- Значение, возвращаемое функцией Hook, может быть названо произвольно, поэтому конфликта пространств имен не возникает.
Использование телепорта
element-plus использует новые функции vue3 для нескольких монтируемых компонентов.Teleport, эта новая функция может помочь нам переместить элемент, который он обертывает, в указанный нами узел.
Teleport предоставляет простой способ, позволяющий нам контролировать, под каким родительским узлом HTML отображается в DOM, без необходимости прибегать к глобальному состоянию или разбивать его на два компонента. -- Официальная документация Vue
Заглянув на официальный сайт, мы обнаружим, что Dialog, Drawer, Tooltip и Popover с использованием Popper добавили новый.append-to-bodyАтрибуты. Возьмем диалог в качестве примера:
Если для appendToBody установлено значение false, телепорт будет отключен, а DOM все равно будет отображаться в текущей позиции.Если это значение равно true, содержимое диалогового окна будет помещено под телом.
<template>
<teleport to="body" :disabled="!appendToBody">
<transition
name="dialog-fade"
@after-enter="afterEnter"
@after-leave="afterLeave"
>
...
</transition>
</teleport>
</tamplate>
В исходном element-ui всплывающая подсказка и всплывающая подсказка также размещаются непосредственно в теле, которое изначально использовалось vue-popper.js.document.body.appendChildЧтобы добавить элементы в тело, element-plus использует Teleport для реализации соответствующей логики.
Глобальный API — Пример API
Когда мы устанавливаем библиотеку компонентов, метод use выполнит метод установки для глобального монтирования компонента.
Давайте сначала посмотрим, как глобальный API написан в Vue 2.x element-ui:
Метод Vue.component для привязки глобальных компонентов
Vue.use связывает глобальные пользовательские директивы
Vue.prototype связывает глобальные переменные и глобальные методы
const install = function(Vue, opts = {}) {
locale.use(opts.locale);
locale.i18n(opts.i18n);
// Vue.component 方法绑定全局组件
components.forEach(component => {
Vue.component(component.name, component);
});
// Vue.use 绑定全局自定义指令
Vue.use(InfiniteScroll);
Vue.use(Loading.directive);
// Vue.prototype 绑定全局变量和全局方法
Vue.prototype.$ELEMENT = {
size: opts.size || '',
zIndex: opts.zIndex || 2000
};
Vue.prototype.$loading = Loading.service;
Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$alert = MessageBox.alert;
Vue.prototype.$confirm = MessageBox.confirm;
Vue.prototype.$prompt = MessageBox.prompt;
Vue.prototype.$notify = Notification;
Vue.prototype.$message = Message;
};
Но в Vue 3.0 любой API, который меняет поведение Vue Globally, теперь будет перемещен в экземпляр приложения, то есть приложение, сгенерированное CreateApp, и соответствующий API также был соответственно изменен.
Давайте посмотрим на element-plus с использованием Vue 3.0, глобальный API переписан в API экземпляра.
import type { App } from 'vue'
const plugins = [
ElInfiniteScroll,
ElLoading,
ElMessage,
ElMessageBox,
ElNotification,
]
const install = (app: App, opt: InstallOptions): void => {
const option = Object.assign(defaultInstallOpt, opt)
use(option.locale)
app.config.globalProperties.$ELEMENT = option // 全局设置默认的size属性和z-index属性
// 全局注册所有除了plugins之外的组件
components.forEach(component => {
app.component(component.name, component)
})
plugins.forEach(plugin => {
app.use(plugin as any)
})
}
Кроме того, есть некоторые отличия в стиле написания.Компонент класса сообщений добавляет метод $global в element-plus.Он перемещен вindex.tsв,
Некоторые компоненты типа уведомлений о сообщениях размещены в плагинах с использованиемapp.useБудет вызываться метод установки в соответствующем компоненте index.ts, а код выглядит следующим образом:
(Message as any).install = (app: App): void => {
app.config.globalProperties.$message = Message
}
глобализация
В пакетах есть папка локали для управления переключением языков.packages/locale/index.tsЕсть 2 метода, методtи методuse, tконтрольvueперевод замена текста в файле,useспособ изменить глобальный язык
// packages/locale/index.ts
export const t = (path:string, option?): string => {
let value
const array = path.split('.')
let current = lang
for (let i = 0, j = array.length; i < j; i++) {
const property = array[i]
value = current[property]
if (i === j - 1) return template(value, option)
if (!value) return ''
current = value
}
return ''
}
Метод t в локали будет представлен в файле vue.
import { t } from '@element-plus/locale'
Затем вы можете использовать многоязычные значения ключей в шаблоне, например:label="t('el.datepicker.nextMonth')",tМетод поможет найти соответствующее значение в соответствующем языковом файле.
посмотри сноваuseметод, который бросаетuseМетод может установить глобальный тип языка, а также изменитьday.jsязыковая конфигурация. представлен в element-plusday.jsзаменить оригиналmoment.jsДля форматирования времени и обработки информации о часовом поясе.
export const use = (l): void => {
lang = l || lang
if (lang.name) {
dayjs.locale(lang.name)
}
}
После того, как наш бизнес-компонент представит element-plus, мы будем использовать этот метод использования для установки типа языка, см.официальная документация
Упаковка веб-сайта
Веб-сайт, также известный как веб-сайт документации, содержит примеры использования каждого элемента управления.website/entry.jsсередина
import ElementPlus from 'element-plus'
На самом деле, это должно было быть введеноpackages/element-plus/index.tsфайл, а затем вы можете использовать каждый компонент в пакетах в md, и изменения логики компонента могут вступить в силу немедленно.
В соответствии с element-ui, разработчик веб-сайта element-plus использует webpack для обслуживания и упаковки.vue-loaderиметь дело сvueфайл, использоватьbabel-loaderиметь дело сjs/tsфайлы, файлы стилей и значки шрифтов используют соответствующиеcss-loader,url-loaderЖдать.
Соответствующая конфигурация находится вwebsite/webpack.config.jsсередина
В документации показан основной файл md с использованиемwebsite/md-loader/index.jsсамореализованныйmd-loader, соответственно извлеченные из md<template>а также<script>content, преобразуйте пример компонента в md в строку vue, а затем передайтеvue-loaderобрабатывать.
rules: [
{
test: /\.vue$/,
use: 'vue-loader',
},
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.md$/,
use: [
{
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false,
},
},
},
{
loader: path.resolve(__dirname, './md-loader/index.js'),
},
],
},
{
test: /\.(svg|otf|ttf|woff2?|eot|gif|png|jpe?g)(\?\S*)?$/,
loader: 'url-loader',
// todo: 这种写法有待调整
query: {
limit: 10000,
name: path.posix.join('static', '[name].[hash:7].[ext]'),
},
},
]
Библиотека компонентов и упаковка стилей
Существует такой длинный список команд упаковки element-plus, среди которыхyarn build:libа такжеyarn build:lib-fullЭто полный пакет, который использует webpack для воспроизведения формата umd. Остальные используют rollup и gulp соответственно.
"build": "yarn bootstrap && yarn clean:lib && yarn build:esm-bundle && yarn build:lib && yarn build:lib-full && yarn build:esm && yarn build:utils && yarn build:locale && yarn build:locale-umd && yarn build:theme"
Пакеты компонентов упаковки с роллапом
В дополнение к использованию веб-пакета для упаковки компонентов, element-plus также предоставляет еще одинes-moduleМетод упаковки и, наконец, публикация в npm — это продукты, упакованные с помощью webpack, а такжеrollupУпакованный пакет es-module.
rollupСоответствующая логика находится вbuild/rollup.config.bundle.jsв файле
Запись экспорт всех компонентов/packages/element-plus/index.ts, используя спецификацию es-module и, наконец, упакованный вlib/index.esm.jsсередина. Из-за использования плагина Typescript при упаковке окончательный сгенерированный файл является дополнением к полномуindex.esm.js, и каждый компонент в отдельностиlibдокумент.
// build/rollup.config.bundle.js
export default [
{
input: path.resolve(__dirname, '../packages/element-plus/index.ts'),
output: {
format: 'es', // 打包格式为 es,可选cjs(commonJS) ,umd 等
file: 'lib/index.esm.js',
},
external(id) {
return /^vue/.test(id)
|| deps.some(k => new RegExp('^' + k).test(id))
},
}
]
Используйте gulp для упаковки файлов стилей и значков шрифтов.
Как и element-ui, файлы стилей и значки шрифтов упакованы с использованиемpackages/theme-chalk/gulpfile.js, упакуйте каждый файл scss в отдельный файл css, который содержит общие базовые стили, а также файлы стилей для каждого компонента.
// packages/theme-chalk/gulpfile.js
function compile() {
return src('./src/*.scss')
.pipe(sass.sync())
.pipe(autoprefixer({ cascade: false }))
.pipe(cssmin())
.pipe(rename(function (path) {
if(!noElPrefixFile.test(path.basename)) {
path.basename = `el-${path.basename}`
}
}))
.pipe(dest('./lib'))
}
function copyfont() {
return src('./src/fonts/**')
.pipe(cssmin())
.pipe(dest('./lib/fonts'))
}
пройти сноваnpm scriptНекоторые операции копирования и удаления файлов в пакете, файлы стилей и значков шрифтов после упаковки в конечном итоге будут помещены вlib/theme-chalkПод содержанием.
cp-cli packages/theme-chalk/lib lib/theme-chalk && rimraf packages/theme-chalk/lib
резюме
Мы видим, что библиотека компонентов использует 3 инструмента упаковки:rollup,webpack,gulp.
Vue,ReactКогда библиотека с открытым исходным кодом начнет использовать накопитель, сборка будет быстрее, и тогда проект приложения в основном использует веб-пакет, потому что веб-пакет может использовать плагины и различныеloaderОбрабатывать другие ресурсы, отличные от javascript. Пакет скинов и файлы шрифтов используют gulp, который может быть более кратким, чем webpack, и его не нужно представлять.url-loader,css-loaderЖдать. Насколько я понимаю, webpack — это законченное решение, полностью функциональное, но громоздкое в настройке. rollup и gulp подходят для использования, когда требования к упаковке относительно просты, они более легкие и настраиваемые.
Представьте лерну
Небольшое изменение в целом, element-plus принимаетlernaЧто касается управления пакетами, lerna может отвечать за управление версией element-plus и компонентом, а также может публиковать каждый компонент в виде пакета npm (но в настоящее время element-plus имеет только полные пакеты в npm, а также пакет скина и многоязычные файлы). одного компонента теперь также помещаются в папку вместо каждого компонента).
Каждый компонент имеет такойpackage.jsonдокумент
{
"name": "@element-plus/message",
"version": "0.0.0",
"main": "dist/index.js",
"license": "MIT",
"peerDependencies": {
"vue": "^3.0.0"
},
"devDependencies": {
"@vue/test-utils": "^2.0.0-beta.3"
}
}
а затем использовалworkspacesсоответствоватьpackagesкаталог, зависимости будут помещены в корневой каталогnode-modules, а не под каждым компонентом, чтобы можно было повторно использовать одни и те же зависимости, а структура каталогов была более четкой.
// package.json
"workspaces": [
"packages/*"
]
Сценарий element-plus также предоставляет сценарий оболочки для создания основных файлов при разработке новых компонентов с использованиемnpm run genБазовая папка компонента может быть создана в пакетах.
"gen": "bash ./scripts/gc.sh",
наконец
У element-plus каждый день появляется больше коммитов, а некоторые функции все еще улучшаются Мы можем изучить дизайн компонентов и новые функции Vue3, прочитав исходный код библиотеки компонентов, а также мы можем внести свой вклад в официальный запрос на извлечение.