Element Plus 组件库相关技术揭秘:7. 组件实现的基本流程及 Icon 组件的实现
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究
前言
本章节咱们即将完成 Icon 组件,Icon 组件应该是一切组件里边最简略的一个组件了,所以咱们由简入深,按部就班进行学习。Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程,经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理。
咱们其实在上篇《6. CSS 架构形式之 BEM 在组件库中的实践》现已完成了最简易的一个 Icon 组件,本章节将持续完善它。
组件目录结构
首先咱们按以下目录结构完善咱们的 Icon 组件目录,其他组件的根本目录结构跟此相似。
├── packages
│ ├── components
│ │ ├── icon
│ │ │ ├── __tests__ # 测验目录
│ │ │ ├── src # 组件入口目录
│ │ │ │ ├── icon.ts # 组件特点与 TS 类型
│ │ │ │ └── icon.vue # 组件模板内容
│ │ │ ├── style # 组件款式目录
│ │ │ └── index.ts # 组件入口文件
│ │ └── package.json
经过上面的 Icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码,然后完成高内聚,低耦合。上述 Icon 组件详细操作便是把组件特点与 TS 类型抽离放在了独立的一个文件中,这样就使得咱们的程序代码在可维护性和灵活性方面能够做得十分好,然后让咱们的项目维护本钱降低。 没有 Composition API
之前 Vue 相关业务的代码需求配置到 Option 的特定的区域,导致代码可复用性不高,这样当项目十分庞大的时分会让后期的维护变得比较困难,然后导致项目本钱增加。其实当你的项目十分庞大的时分,共享和复用代码则变得尤为重要。
界说组件特点 prop
咱们知道父组件能够经过 prop 向子组件传递数据。首先需求在组件内部注册一些自界说的特点,称为 prop,这些 prop 是在组件的 props 选项中界说的。在运用组件的时分,就能够将在组件 props 选项中界说的特点称号作为组件元素的特点名来运用,经过特点向组件传递数据。
一起界说组件特点也是组件封装的一项重要过程,首先咱们在封装组件的时分,就要考虑咱们的组件需求哪些特点,比方咱们 Element Plus 中的 Icon 组件就只有下面两项特点。
特点名 | 阐明 | 类型 | 默认值 |
---|---|---|---|
color |
svg 的 fill 色彩 | Pick<CSSProperties, 'color'> |
承继色彩 |
size |
SVG 图标的大小,size x size | number 、 string |
承继字体大小 |
咱们期望父组件经过 prop 传递的数据类型是契合要求的,能够例如 Vue 供给的 prop 验证机制,在界说 props 选项时,能够运用一个带验证需求的目标。即在 packages/components/icon/src/icon.ts
文件进行如下界说:
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
}
验证的类型(type)能够是下列原生结构函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbool
单向数据流
经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。依据这个特性在咱们运用 TypeScript 开发的时分,就会对 props 界说成只读特点。经过 as const
则能够快速将一个目标变成只读类型,常量断语能够把一个值标记为一个不可篡改的常量,然后让 TS 以最严厉的战略来进行类型推断。
export const iconProps = {
color: String,
size: [Number, String], // size 能够是数字,也能够是字符串
} as const
as const
是 TS 的语法,它告诉 TS 它所断语的值以及该值的一切层级的子特点都是不可篡改的,故对每一级子特点都会做最严厉的类型推断。
但 TypeScript 的自动类型推断并不能推断出咱们想要目标的类型。
在 TypeScript 中类有 2 种类型, 静态类型和实例类型, 假如是结构函数类型, 那么回来的则是实例类型。咱们在原生 Vue3 中界说 props 类型,其实是一个结构函数,比方上述咱们界说 color
的类型是 String
,但 String
仅仅一个结构函数,并不是 TypeScript 中的 string
类型,String
结构函数在 TypeScript 的类型是它的结构函数类型: StringConstructor
,但这并不是咱们需求的,咱们期望 String 结构函数回来的是字符串类型 string
。
在 Vue3 中供给了自带的 Props 类型声明:ExtractPropTypes
,它的效果是接纳一个类型,然后把对应的所接纳的 props 类型回来出来,一起假如是结构函数类型则转换成对应的类型,比方 StringConstructor
转换成 string
。
import type { ExtractPropTypes, PropType } from 'vue'
export const iconProps = {
color: String,
size: [Number, String] as PropType<number | string>,
} as const
export type Props = ExtractPropTypes<typeof iconProps>
其间数组项,还需求经过 Vue3 内置的 PropType
类型声明进行详细的类型断语声明。
此外咱们看到在导入相关类型声明的时分运用的是 import type
,在此咱们也略微弥补一些 import type
小常识:import type
仅仅导入被用于类型注解或声明的声明句子,它总是会被彻底删去,因此在运转时将不会留下任何代码。与此相似的 export type
也是仅仅供给一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删去。那么运用 import
的话,TypeScript 是无法判别你是想导出类型还是一个 JavaScript 的办法或许变量,而当你导入的是仅仅是类型的时分,当 TypeScript 编译之后,类型会被删去,你的代码就会报错,但经过TypeScript 的 isolatedModules 编译选项也能够进行预警这种写法是过错的。所以 TypeScript 供给了 import type or export type
,用来清晰表明我引进/导出的是一个类型,而不是一个变量或许办法。
import type xxx from 'xxx'
export type xxx
终究咱们还需求把 SFC 的 icon.vue 文件的实例类型回来出去:
import type Icon from './icon.vue'
export type IconInstance = InstanceType<typeof Icon>
TypeScript 中的 InstanceType 函数:该函数回来(结构) 由某个结构函数结构出来的实例类型组成的类型。
在 Element Plus 中在创立 props 的类型界说的 TypeScript 类型是十分复杂的,日后有时机将在单独 TypeScript 的章节来展开阐明,这儿更多讲解的是组件的逻辑流程。
经过 script setup 编写 SFC 组件
咱们在上一篇文章《6. CSS 架构形式之 BEM 在组件库中的实践》中现已完成了以下内容:
<template>
<i :class="bem.b()">
<slot />
</i>
</template>
<script setup lang="ts">
import { useNamespace } from '@cobyte-ui/hooks'
const bem = useNamespace('icon')
</script>
Icon 组件仅仅一个标签然后接纳一个图标,所以 template 部分十分简略,一个 i 标签经过插槽接纳图标内容,插槽也是父子组件通讯办法的一种,一起咱们在上一篇中完成了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
咱们在上文中现已在 icon.ts 文件中界说好了 Icon 组件的 Props,接下来咱们要在 icon.vue 中完成它。咱们是经过 script setup 办法编写的 SFC 组件,那么经过这种办法编写的组件,咱们则是经过 defineProps
编译宏指令来进行声明 props,一起声明的 props 会自动露出给模板。
import { iconProps } from './icon'
const props = defineProps(iconProps)
defineProps
会回来一个目标,其间包括了能够传递给组件的一切 props。
咱们在 icon.ts 中界说了两个 Icon 组件的 props: size 和 color,然后用户能够经过这两个特点设置 Icon 组件的款式。接下来咱们则要去完成这两个功能。
import type { CSSProperties } from 'vue'
// CSSProperties 是 Vue3 供给的 CSS 特点的类型
const style = computed<CSSProperties>(() => {
if (!props.size && !props.color) return {}
return {
fontSize: isUndefined(props.size) ? undefined : addUnit(props.size),
'--color': props.color, // 经过 CSS 变量办法进行设置 color
}
})
咱们经过计算特点去计算出经过 props 传递过来的 size 和 color 特点得到 Icon 组件的款式。终究咱们的 template 中需求在 i 标签中增加 style 的特点绑定::style="style"
。
Vue 组件中的 CSS 变量
CSS 变量(CSS variable)又叫做 “CSS 自界说特点”(CSS custom properties),声明变量的时分,变量名前面要加两根连词线(--
),例如上文的色彩变量:--color
。为什么挑选两根连词线(--
)表明变量?由于 $
被 Sass 用掉了,@
被 Less 用掉了。为了不发生抵触,官方的 CSS 变量就改用两根连词线了。
咱们知道 Icon 的图标有可能是一个字体类型的图标,或许是 SVG 的图标,字体类型的图标直接能够经过设置 CSS 的 color 特点来设置图标色彩;而 SVG 图标能够在 SVG 文件中更改 fill 特点进行修改图片,将 fill 特点改成 currentColor,然后经过承继父元素 color 特点能够改动色彩。这样就相同能够经过设置 CSS 的 color 特点来设置图标的色彩了。
在 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。运用 CSS 变量能够经过 var 要害进行获取界说的 CSS 变量,例如:var(--color)
。
template 中的设置:
<template>
<div class="content">
<i class="el-icon" :style="{'--color': color}">
</i>
</div>
</template>
script setup 中的设置:
import { computed, ref } from 'vue'
const color = ref('green')
经过以上设置就能够在 style 标签中经过 var 获取 .info
行内设置的 CSS 变量了。
.info {
color: var(--color)
}
终究烘托到页面的成果如下图:
变量规模
- CSS 变量能够在任何元素内界说
- 将 CSS 自界说特点增加到 :root 使其可用于页面中的一切元素
- 假如在某个挑选器内增加变量,则只能够在该挑选器中可运用,这也便是 CSS 的变量效果域
- 在 Vue 组件中,假如要该组件都能够运用,则有必要放置在根元素下
在 Vue3 中的 SFC 组件中,能够在 style 标签经过 vars 进行绑定:<style vars="{ color }">
。
<template>
<div class="text">稀土</div>
</template>
<script>
export default {
data() {
return {
color: "green",
};
},
};
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
其间的原理便是这些变量会直接绑定到组件的根元素上,这就契合咱们上面所说的 CSS 变量规模的规矩了。
终究咱们的主题款式中的 icon.scss 需求修改成以下内容:
@include b(icon) {
--color: inherit;
height: 1em;
width: 1em;
line-height: 1em;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
fill: currentColor; /* 常规css中是没有fill特点的,只在XML-CSS中存在,用于设置当时元素的填充内容,例如色彩,图片 */
color: var(--color);
font-size: inherit;
svg {
height: 1em;
width: 1em;
}
}
主要是字体色彩是经过 CSS 变量进行设置的,别的增加 fill 特点的内容,常规 CSS 中是没有 fill 特点的,只在XML-CSS 中存在,用于设置当时元素的填充内容,例如色彩,图片。这儿主要是将 fill 特点改成 currentColor,然后 SVG 图片能够经过承继父元素 color 特点能够改动色彩。
组件注册的办法
咱们去到 play 目录下的 src 目录中的 App.vue 文件中把上面写的 Icon 组件进行引进测验。在 Vue3 中有两种写 SFC 组件的办法,一种便是咱们上面所介绍到的 script setup 办法,假如是经过 script setup 办法,那么相关代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script setup lang="ts">
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
<style scoped></style>
还有一种便是不是运用 script setup 的办法,也便是运用 defineComponent 界说组件的办法,代码如下:
<template>
<div>
<el-icon :color="'green'" :size="12">Icon</el-icon>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
export default defineComponent({
name: 'App',
components: {
ElIcon,
},
setup() {},
})
</script>
<style scoped></style>
咱们能够看到运用 defineComponent 界说组件,引进其他组件需求运用 components 选项进行注册,才能在 template 中运用。这两种都是归于部分注册组件的办法,咱们知道除了部分注册组件,还能够进行大局注册组件。咱们在本专栏的榜首篇文章《Vue3 组件库的规划和完成原理》中现已解析了组件库的规划原理。在这儿咱们再进行温习和实践。
经过《Vue3 组件库的规划和完成原理》这篇文章咱们能够知道 Vue3 组件库的根本完成原理:便是为每一个组件进行装置一个插件,然后经过插件进行组件的大局装置,再把一切的组件设置到一个数组中组成一个组件库(其实便是一个包括各种组件的数组),再编写一个组件库插件,在组件库插件里边进行循环数组组件库里的每一个组件,由于每一个组件都具有插件所需的 install() 办法,所以每一个组件又是一个插件,又能够调用 use() 办法进行装置,终究就会履行每一个组件的 install() 办法,然后进行进行组件的大局装置,这样组件库里边的每个组件都将被注册到大局组件中去了。
又由于咱们的组件库又许多组件,咱们需求给每一个组件都增加一个 install 办法,咱们能够把这个办法进行封装成为一个公共办法。咱们在 packages/utils/vue/install.ts
中增加以下代码:
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 组件的注册称号参数暂时是写死了 ElIcon,在后面的末节,咱们再详细阐明怎么进行设置动态组件称号
app.component('ElIcon', comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
留意,此刻咱们在 app.component('ElIcon', comp as any)
组件的注册称号参数是写死了 ElIcon
,由于咱们的组件是经过 script setup 办法完成的,无法设置组件的称号。在后面的末节,咱们再详细阐明怎么进行设置动态组件称号。
packages/components/icon/index.ts
中的代码为:
import { withInstall } from '@cobyte-ui/utils'
import Icon from './src/icon.vue'
// 经过 withInstall 办法给 Icon 增加了一个 install 办法
const ElIcon = withInstall(Icon)
export default ElIcon
// 导出 Icon 组件的 props
export * from './src/icon'
接下来,咱们按组件库的办法在 play/main.ts
中进行大局装置引进 Icon 组件。
import { createApp } from 'vue'
import ElIcon from '@cobyte-ui/components/icon'
import '@cobyte-ui/theme-chalk/src/index.scss'
import App from './src/App.vue'
// 组件库
const components = [ElIcon]
// 是否已装置标识
const INSTALLED_KEY = Symbol('INSTALLED_KEY')
// 组件库插件
const ElementPlus = {
install(app: any) {
// 假如该组件库现已装置过了,则不进行装置
if (app[INSTALLED_KEY]) return
// 将标识值设置为 true,表明现已装置了
app[INSTALLED_KEY] = true
// 循环组件库中的每个组件进行装置
components.forEach((c) => app.use(c))
},
}
const app = createApp(App)
// 装置组件库
app.use(ElementPlus)
app.mount('#app')
这个时分咱们把 play/src/App.vue
中本来按需引进的 Icon 组件代码进行注释:
<script setup lang="ts">
// import ElIcon from '@cobyte-ui/components/icon'
// import '@cobyte-ui/theme-chalk/src/index.scss'
</script>
然后咱们再把 play 测验项目运转起来:pnpm run dev
这样咱们经过大局注册的办法也把组件运转起来了。
大局组件类型声明
Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理。
咱们能够看到经过大局注册的 Icon 组件此刻在模板中是没有任何类型提示的。咱们假如想要大局注册的组件在模板中取得类型提示,就需求运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。详细便是声明一个的 *d.ts
类型文件,经过运用声明文件对类型接口进行类型模块扩大并导出。
Vue3 SFC 文件的智能提示是 Volar 供给的, 其实在 Volar 的 README.md 文件中就有相关的提示:
Define Global Components
PR: vuejs/core#3399
Local components, Built-in components, native HTML elements Type-Checking is available with no configuration.
For Global components, you need to define GlobalComponents
interface, for example:
// components.d.ts
declare module '@vue/runtime-core' { // Vue 3
// declare module 'vue' { // Vue 2.7
// declare module '@vue/runtime-dom' { // Vue <= 2.6.14
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
/** 当咱们的 tsconfig.json 中的 isolatedModules 设置为 true 时,假如某个 ts 文件中没有一个
import or export 时,ts 则以为这个模块不是一个 ES Module 模块,它被以为是一个大局的脚本,
这个时分在文件中增加恣意一个 import or export 都能够处理这个问题。
**/
参阅 Volar 的文档,咱们能够在根目录的 typings 文件夹下新建一个 components.d.ts
文件进行以下代码的完成:
import type Icon from '@cobyte-ui/components/icon'
// For this project development
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
// GlobalComponents for Volar
export interface GlobalComponents {
ElIcon: typeof Icon
}
}
export {}
经过上述设置咱们的经过大局注册的 Icon 组件便有类型提示了。
script setup 组件的称号设置
咱们在前面的末节进行大局注册组件的时分,组件称号是写死了的,那么写死了肯定是不可,咱们需求动态设置组件的称号,咱们现在这个末节就来处理这个问题。
咱们知道经过 defineComponent 界说的 Vue3 组件是能够经过 name 特点进行设置组件的称号的
export default defineComponent({
name: 'App',
setup() {},
})
而经过 script setup 办法则 Vue3 并没有供给设置组件称号的 API,但能够经过一种别捏的办法完成,代码如下:
<script lang="ts">
export default{
name: 'App'
}
</script>
<script setup lang="ts">
// ...
</script>
能够经过两个 script 标签来完成,但这种办法太不高雅了,在 Element Plus 中,官方开发者三咲智子 则供给了一个叫 unplugin-vue-define-options
来处理这个问题。
unplugin-vue-define-options:
在<script setup>
中可运用defineOptions
宏,以便在<script setup>
中运用 Options API。 尤其是能够在一个函数中设置name
、props
、emit
和render
特点。
特性
- ✨ 有了这个宏,你就能够在
<script setup>
运用 Options API; - 开箱即用支撑 Vue 2 和 Vue 3;
- 彻底支撑 TypeScript;
- ⚡️ 支撑 Vite、Webpack、Vue CLI、Rollup、esbuild 等, 由 unplugin 供给支撑。
在根目录下进行装置
pnpm install unplugin-vue-define-options -D -w
TypeScript 支撑,在 tsconfig.web.json 文件中进行以下设置:
{
"compilerOptions": {
// ...
"types": ["unplugin-vue-define-options/macros-global" /* ... */]
}
}
又由于咱们的 play 项目中有运用到了 Vite,所以咱们还需求 play 项目中的 vite.config.ts 文件中进行以下设置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions()],
})
这样咱们就能够在 packages/components/icon/src/icon.vue
中运用defineOptions
宏,在<script setup>
中经过 Options API 设置组件的称号了。代码如下:
<script setup lang="ts">
defineOptions({
name: 'ElIcon',
})
</script>
终究咱们就能够在组件的装置插件的办法进步行动态设置组件称号了。
import type { Plugin } from 'vue'
// 经过 Vue 供给的 Plugin 类型和传进来的组件类型 T 的调集进行确认咱们的组件类型具有 Plugin 类型办法,如 install 办法
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => {
;(comp as SFCWithInstall<T>).install = function (app) {
// 动态设置组件的称号
const { name } = comp as unknown as { name: string }
app.component(name, comp as SFCWithInstall<T>)
}
return comp as SFCWithInstall<T>
}
Icon 中的图标
SVG 组件图标
咱们在上面现已略微讲过,Icon 中的图标有两种办法完成,其间一种是经过 SVG 图片,而经过 SVG 图片的完成办法实质便是完成一个 SVG 的组件。在 Vue3 中的完成是十分简略的,代码如下:
<template>
<svg
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
>
<path
fill="currentColor"
d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"
/>
<path
fill="currentColor"
d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"
/>
</svg>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EditIcon',
})
</script>
其间 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取办法有许多,咱们这儿供给其间一种,咱们能够把规划师规划的图标上传到阿里的 iconfont 图标库,然后在下载该图标的时分,能够挑选仿制 SVG 源码。
有了 SVG 图标的组件之后,咱们就能够直接导入在 Icon 组件中进行运用了。
<template>
<div>
<el-icon :color="'green'" :size="15"><EditIcon /></el-icon>
</div>
</template>
<script setup lang="ts">
import EditIcon from './EditIcon.vue'
</script>
<style scoped></style>
运转成果如下:
咱们能够看到咱们的图标成功烘托出来了。
字体图标
咱们还能够经过字体图标进行设置:
<template>
<div>
<el-icon :color="'green'" :size="15">
<i class="iconfont iconlogistics-car"></i>
</el-icon>
</div>
</template>
<script setup lang="ts"></script>
<style>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPIAAsAAAAACDAAAAN8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDMgqDKIJ8ATYCJAMUCwwABCAFhGcHXRszB8ieGjszMAURLRpbqK1NZLgai+Bpjd+83b37rkkUPJlGTXgTT1Q8iYdGtZBInqmizTP5JueqHACqHCL0ukm3u5zmUBE5JMlCIiqkp/Fvf/7/Tv0JTozFtidAC2TM7gl21TvSDl1Am7qfB7DYYs8/WVNbJGPOvRCigKCAWDUvXH2FqqsDgBEpuczPzki9YezKi3iOQLvV4mnu8nJLgemFOlUAR9qMziDOjQt9jgH63KDU5IZWoW67sYhHKtLtdMFD//vjn7WhT1JlxjH7T6WJwO4vsajNnU0n+Su3Q4CTEypkLF+ZKS7WOy7QCsPLaWkvG65tG6BdqySN2i+2qPxZahpiyd5U7eY/PEKSFaJmZHeCbShS+GlrIwQ/YxESPysRMj9rRW+nvALtsOg1wHOsmRzzL5IQXV0r2kzGaq3jaLnF+/VicyC71TjxsrzcdtZ20pCJG88Y4c4TKNefsuzjtxfkuCfx/iB25DlF3RiftC3VEO8xZFM2A0dvvO7J4xbdb/Dzbn5g56Vx48CdycHBDQeFt09l735ICJWYiystE0rLmTzH+aew04a1cR72nVcdHZHCyh8UhZbPmZ99R0vcYoHNW8yxRb45HFSQVL7tbneSLJMwwGzJPEoPP7L7Kzr+FhzVSmlQNP3r8HLHTW1uTh22uJ5mODzg2H9/M2RxRIVPtldGhle2zxbiIm7ETaq3xGEVEWLX0etr2H3G1qBe/aFD8yrrADTzzQyQLtFTkKU/+jve5Sz732AW+1+/o4AfK/GoX9YoMv6IUmvB/1Mea4laVB5SlrJm2yaoRvsTXom/1PDDPP3exlENtM4mtBqIIWkxAVmrKWwhLkGlw1qotdoM7ZZJ295hgIqWKG2Y0wMg9FqFpNsLyHqdYAvxHirDfkOt1z9odyBMDuswF6xNEEORCDa1Q55VKrClpn6vxYhrljGkKFCOiMBDHwzyD8zGMpACkSGWCC1cMKUYYqKUw3R6HZLJlFBFlBLEUn8xparogABc9iX+rFIOUhAIg0JEoCbtIB5LSQG76DT984ohnGYyDNISWx4mBDzsHQriL7AFNkOhaNVyJccIWnCCURQGYQRE5aB0bkNkSlgJUpWPk0BYlD9xj6BKtAATDbdV+M+vkj/gLmhnHFFFihwlqqh1XZhyvHAdKYIfyxCDO8jzo0ighmFbELUHAwA=')
format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconlogistics-car:before {
content: 'e616';
}
</style>
运转成果:
字体图标也是能够经过阿里的 iconfont 图标库进行设置,这儿就不作过多深化讲解了。
Element Plus 中的图标库
Element Plus 中的图标库又是经过一个独立的项目进行完成的,也便是你装置了 Element Plus 库之后,还要运用她的图标库,那么你还需求装置一个 npm 包:@element-plus/icons-vue
。它的完成原理也便是咱们上面的榜首种 SVG 图标组件的办法,经过遍历读取每个 SVG 图标的源码,然后生成各自的 SVG 图标组件,然后引进的时分是经过大局注册的办法进行运用。这儿就不作过多深化解析了。
总结
在本章节中咱们经过完成 Icon 组件进行了解 Element Plus 组件完成的根本原理,Icon 组件尽管简略,但它却包括了一个组件的悉数根底流程。
经过 icon 组件目录结构,咱们看出 Vue3 Composition API 的优势,能够依据逻辑功能来安排代码。经过界说组件特点 prop,咱们了解一些组件 prop 的特性,经过 prop 传递数据是单向的,父组件的特点改动会向下传递给子组件,可是反过来不可。这能够避免子组件意外改动父组件的状况,然后导致组件的数据流难以了解。
接着在完成 Icon 组件的进程,咱们学习了如安在 Vue 组件中运用 CSS 变量。 Vue 中能够经过在行内的 style 特点中界说 CSS 变量,然后就能够经过 Vue 的动态变量操控 CSS 变量,再在 style 标签中运用行内界说好的 CSS 变量。
接着咱们经过部分注册组件和大局注册组件的办法完成了 Icon 组件在 play 项目中的烘托展现。可是 Vue3 并没有对自界说大局组件做 TypeScript 类型支撑处理,咱们学习了运用 TypeScript 的增强类型体系,进行扩展 Vue3 本来的类型体系。
咱们还学习了 Element Plus 中经过运用defineOptions
宏,在<script setup>
中运用 Options API 来处理 <script setup>
无法设置组件称号的问题。
终究咱们讲解了在 Icon 组件中怎么完成图标的,主要有两种办法,一种是 SVG 组件图标,一种是字体图标。
欢迎重视本专栏,了解更多 Element Plus 组件库常识。
本专栏文章:
1. Vue3 组件库的规划和完成原理
2. 组件库工程化实战之 Monorepo 架构建立
3. ESLint 中心原理分析
4. ESLint 技能原理与实战及代码标准自动化详解
5. 从终端指令解析器说起谈谈 npm 包管理工具的运转原理
6. CSS 架构形式之 BEM 在组件库中的实践
7. 组件完成的根本流程及 Icon 组件的完成
8. 为什么组件库或插件需求界说 peerDependencies