Element Plus 组件库相关技术: 组件实现的基本流程及 Icon 组件的实现
本文为稀土掘金技术社区首发签约文章,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 组件就只有下面两项属性。
属性名说明类型默认值colorsvg 的 fill 颜色PickCSSProperties, 'color'继承颜色sizeSVG 图标的大小,size x sizenumber 、 string继承字体大小我们希望父组件通过 prop 传递的数据类型是符合要求的,可以例如 Vue 提供的 prop 验证机制,在定义 props 选项时,可以使用一个带验证需求的对象。即在packages/components/icon/src/icon.ts文件进行如下定义:
exportconsticonProps={
color:String,
size:[Number,String],//size可以是数字,也可以是字符串
}
验证的类型(type)可以是下列原生构造函数中的一个:
StringNumberBooleanArrayObjectDateFunctionSymbool单向数据流
通过 prop 传递数据是单向的,父组件的属性变化会向下传递给子组件,但是反过来不行。这可以防止子组件意外改变父组件的状态,从而导致组件的数据流难以理解。基于这个特性在我们使用 TypeScript 开发的时候,就会对 props 定义成只读属性。通过as const则可以快速将一个对象变成只读类型,常量断言可以把一个值标记为一个不可篡改的常量,从而让 TS 以最严格的策略来进行类型推断。
exportconsticonProps={
color:String,
size:[Number,String],//size可以是数字,也可以是字符串
}asconst
as const是 TS 的语法,它告诉 TS 它所断言的值以及该值的所有层级的子属性都是不可篡改的,故对每一级子属性都会做最严格的类型推断。
但 TypeScript 的自动类型推断并不能推断出我们想要对象的类型。
autoType.png在 TypeScript 中类有 2 种类型,静态类型和实例类型, 如果是构造函数类型, 那么返回的则是实例类型。我们在原生 Vue3 中定义 props 类型,其实是一个构造函数,比如上述我们定义color的类型是String,但String只是一个构造函数,并不是 TypeScript 中的string类型,String构造函数在 TypeScript 的类型是它的构造函数类型:StringConstructor,但这并不是我们需要的,我们希望 String 构造函数返回的是字符串类型string。
在 Vue3 中提供了自带的 Props 类型声明:ExtractPropTypes,它的作用是接收一个类型,然后把对应的所接收的 props 类型返回出来,同时如果是构造函数类型则转换成对应的类型,比如StringConstructor转换成string。
importtype{ExtractPropTypes,PropType}from'vue'
exportconsticonProps={
color:String,
size:[Number,String]asPropTypenumber|string,
}asconst
exporttypeProps=ExtractPropTypestypeoficonProps
其中数组项,还需要通过 Vue3 内置的PropType类型声明进行具体的类型断言声明。
此外我们看到在导入相关类型声明的时候使用的是import type,在此我们也稍微补充一些import type小知识:import type仅仅导入被用于类型注解或声明的声明语句,它总是会被完全删除,因此在运行时将不会留下任何代码。与此类似的export type也是仅仅提供一个用于类型的导出,在 TypeScript 输出文件中,它也将会被删除。那么使用import的话,TypeScript 是无法判断你是想导出类型还是一个 JavaScript 的方法或者变量,而当你导入的是仅仅是类型的时候,当 TypeScript 编译之后,类型会被删除,你的代码就会报错,但通过TypeScript 的 isolatedModules 编译选项也可以进行预警这种写法是错误的。所以 TypeScript 提供了import type or export type,用来明确表示我引入/导出的是一个类型,而不是一个变量或者方法。
importtypexxxfrom'xxx'
exporttypexxx
最后我们还需要把 SFC 的 icon.vue 文件的实例类型返回出去:
importtypeIconfrom'./icon.vue'
exporttypeIconInstance=InstanceTypetypeofIcon
TypeScript 中的 InstanceType 函数:该函数返回(构造) 由某个构造函数构造出来的实例类型组成的类型。
在 Element Plus 中在创建 props 的类型定义的 TypeScript 类型是非常复杂的,日后有机会将在单独 TypeScript 的章节来展开说明,这里更多讲解的是组件的逻辑流程。
通过 script setup 编写 SFC 组件我们在上一篇文章《6. CSS 架构模式之 BEM 在组件库中的实践》中已经实现了以下内容:
template
i:class="bem.b()"
slot/
/i
/template
scriptsetuplang="ts"
import{useNamespace}from'@cobyte-ui/hooks'
constbem=useNamespace('icon')
/script
Icon 组件只是一个标签然后接收一个图标,所以 template 部分非常简单,一个 i 标签通过插槽接收图标内容,插槽也是父子组件通讯方式的一种,同时我们在上一篇中实现了 CSS 的 BEM 相关的逻辑,这一块内容本文便不再进行过多讲解了。
我们在上文中已经在 icon.ts 文件中定义好了 Icon 组件的 Props,接下来我们要在 icon.vue 中实现它。我们是通过 script setup 方式编写的 SFC 组件,那么通过这种方式编写的组件,我们则是通过defineProps编译宏命令来进行声明 props,同时声明的 props 会自动暴露给模板。
import{iconProps}from'./icon'
constprops=defineProps(iconProps)
defineProps会返回一个对象,其中包含了可以传递给组件的所有 props。
我们在 icon.ts 中定义了两个 Icon 组件的 props: size 和 color,然后用户可以通过这两个属性设置 Icon 组件的样式。接下来我们则要去实现这两个功能。
importtype{CSSProperties}from'vue'
//CSSProperties是Vue3提供的CSS属性的类型
conststyle=computedCSSProperties(()={
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
divclass="content"
iclass="el-icon":style="{'--color':color}"
/i
/div
/template
script setup 中的设置:
import{computed,ref}from'vue'
constcolor=ref('green')
通过以上设置就可以在 style 标签中通过 var 获取.info行内设置的 CSS 变量了。
.info {
color: var(--color)
}
最终渲染到页面的结果如下图:
--color.png变量范围
CSS 变量可以在任何元素内定义将 CSS 自定义属性添加到 :root 使其可用于页面中的所有元素如果在某个选择器内添加变量,则只可以在该选择器中可使用,这也就是 CSS 的变量作用域在 Vue 组件中,如果要该组件都可以使用,则必须放置在根元素下在 Vue3 中的 SFC 组件中,可以在 style 标签通过 vars 进行绑定:style vars="{ color }"。
template
divclass="text"稀土掘金/div
/template
script
exportdefault{
data(){
return{
color:"green",
},
};
/script
stylevars="{color}"
.text{
color:var(--color);
}
/style
其中的原理就是这些变量会直接绑定到组件的根元素上,这就符合我们上面所说的 CSS 变量范围的规则了。
最后我们的主题样式中的 icon.scss 需要修改成以下内容:
@includeb(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
scriptsetuplang="ts"
importElIconfrom'@cobyte-ui/components/icon'
import'@cobyte-ui/theme-chalk/src/index.scss'
/script
stylescoped/style
还有一种就是不是使用 script setup 的方式,也就是使用 defineComponent 定义组件的方式,代码如下:
template
div
el-icon:color="'green'":size="12"Icon/el-icon
/div
/template
scriptlang="ts"
import{defineComponent}from'vue'
importElIconfrom'@cobyte-ui/components/icon'
import'@cobyte-ui/theme-chalk/src/index.scss'
exportdefaultdefineComponent({
name:'App',
components:{
ElIcon,
},
setup(){},
})
/script
stylescoped/style
我们可以看到使用 defineComponent 定义组件,引入其他组件需要使用 components 选项进行注册,才能在 template 中使用。这两种都是属于局部注册组件的方式,我们知道除了局部注册组件,还可以进行全局注册组件。我们在本专栏的第一篇文章《Vue3 组件库的设计和实现原理》中已经解析了组件库的设计原理。在这里我们再进行复习和实践。
通过《Vue3 组件库的设计和实现原理》这篇文章我们可以知道 Vue3 组件库的基本实现原理:就是为每一个组件进行安装一个插件,然后通过插件进行组件的全局安装,再把所有的组件设置到一个数组中组成一个组件库(其实就是一个包含各种组件的数组),再编写一个组件库插件,在组件库插件里面进行循环数组组件库里的每一个组件,因为每一个组件都拥有插件所需的 install() 方法,所以每一个组件又是一个插件,又可以调用 use() 方法进行安装,最后就会执行每一个组件的 install() 方法,然后进行进行组件的全局安装,这样组件库里面的每个组件都将被注册到全局组件中去了。
又因为我们的组件库又许多组件,我们需要给每一个组件都添加一个 install 方法,我们可以把这个方法进行封装成为一个公共方法。我们在packages/utils/vue/install.ts中添加以下代码:
importtype{Plugin}from'vue'
//通过Vue提供的Plugin类型和传进来的组件类型T的集合进行确定我们的组件类型具有Plugin类型方法,如install方法
exporttypeSFCWithInstall=TPlugin
exportconstwithInstall=T(comp:T)={
(compasSFCWithInstall).install=function(app){
//组件的注册名称参数暂时是写死了ElIcon,在后面的小节,我们再详细说明如何进行设置动态组件名称
app.component('ElIcon',compasSFCWithInstall)
}
returncompasSFCWithInstall
}
注意,此时我们在app.component('ElIcon', comp as any)组件的注册名称参数是写死了ElIcon,因为我们的组件是通过 script setup 方式实现的,无法设置组件的名称。在后面的小节,我们再详细说明如何进行设置动态组件名称。
packages/components/icon/index.ts中的代码为:
import{withInstall}from'@cobyte-ui/utils'
importIconfrom'./src/icon.vue'
//通过withInstall方法给Icon添加了一个install方法
constElIcon=withInstall(Icon)
exportdefaultElIcon
//导出Icon组件的props
export*from'./src/icon'
接下来,我们按组件库的方式在play/main.ts中进行全局安装引入 Icon 组件。
import{createApp}from'vue'
importElIconfrom'@cobyte-ui/components/icon'
import'@cobyte-ui/theme-chalk/src/index.scss'
importAppfrom'./src/App.vue'
//组件库
constcomponents=[ElIcon]
//是否已安装标识
constINSTALLED_KEY=Symbol('INSTALLED_KEY')
//组件库插件
constElementPlus={
install(app:any){
//如果该组件库已经安装过了,则不进行安装
if(app[INSTALLED_KEY])return
//将标识值设置为true,表示已经安装了
app[INSTALLED_KEY]=true
//循环组件库中的每个组件进行安装
components.forEach((c)=app.use(c))
},
}
constapp=createApp(App)
//安装组件库
app.use(ElementPlus)
app.mount('#app')
这个时候我们把play/src/App.vue中原来按需引入的 Icon 组件代码进行注释:
scriptsetuplang="ts"
//importElIconfrom'@cobyte-ui/components/icon'
//import'@cobyte-ui/theme-chalk/src/index.scss'
/script
然后我们再把 play 测试项目运行起来:pnpm run dev
icon.png这样我们通过全局注册的方式也把组件运行起来了。
全局组件类型声明Vue3 并没有对自定义全局组件做 TypeScript 类型支持处理。
ts-type.png我们可以看到通过全局注册的 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 defineGlobalComponentsinterface, for example:
//components.d.ts
declaremodule'@vue/runtime-core'{//Vue3
//declaremodule'vue'{//Vue2.7
//declaremodule'@vue/runtime-dom'{//Vue=2.6.14
exportinterfaceGlobalComponents{
RouterLink:typeofimport('vue-router')['RouterLink']
RouterView:typeofimport('vue-router')['RouterView']
}
}
export{}
/**当我们的tsconfig.json中的isolatedModules设置为true时,如果某个ts文件中没有一个
importorexport时,ts则认为这个模块不是一个ESModule模块,它被认为是一个全局的脚本,
这个时候在文件中添加任意一个importorexport都可以解决这个问题。
**/
参考 Volar 的文档,我们可以在根目录的 typings 文件夹下新建一个components.d.ts文件进行以下代码的实现:
importtypeIconfrom'@cobyte-ui/components/icon'
//Forthisprojectdevelopment
import'@vue/runtime-core'
declaremodule'@vue/runtime-core'{
//GlobalComponentsforVolar
exportinterfaceGlobalComponents{
ElIcon:typeofIcon
}
}
export{}
通过上述设置我们的通过全局注册的 Icon 组件便有类型提示了。
el-icon-ts.pngscript setup 组件的名称设置我们在前面的小节进行全局注册组件的时候,组件名称是写死了的,那么写死了肯定是不行,我们需要动态设置组件的名称,我们现在这个小节就来解决这个问题。
我们知道通过 defineComponent 定义的 Vue3 组件是可以通过 name 属性进行设置组件的名称的
exportdefaultdefineComponent({
name:'App',
setup(){},
})
而通过 script setup 方式则 Vue3 并没有提供设置组件名称的 API,但可以通过一种别捏的方式实现,代码如下:
scriptlang="ts"
exportdefault{
name:'App'
}
/script
scriptsetuplang="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提供支持。在根目录下进行安装
pnpminstallunplugin-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'
importvuefrom'@vitejs/plugin-vue'
importDefineOptionsfrom'unplugin-vue-define-options/vite'
//https://vitejs.dev/config/
exportdefaultdefineConfig({
plugins:[vue(),DefineOptions()],
})
这样我们就可以在packages/components/icon/src/icon.vue中使用defineOptions宏,在script setup中通过 Options API 设置组件的名称了。代码如下:
scriptsetuplang="ts"
defineOptions({
name:'ElIcon',
})
/script
最后我们就可以在组件的安装插件的方法上进行动态设置组件名称了。
importtype{Plugin}from'vue'
//通过Vue提供的Plugin类型和传进来的组件类型T的集合进行确定我们的组件类型具有Plugin类型方法,如install方法
exporttypeSFCWithInstall=TPlugin
exportconstwithInstall=T(comp:T)={
(compasSFCWithInstall).install=function(app){
//动态设置组件的名称
const{name}=compasunknownas{name:string}
app.component(name,compasSFCWithInstall)
}
returncompasSFCWithInstall
}
Icon 中的图标SVG 组件图标我们在上面已经稍微讲过,Icon 中的图标有两种方式实现,其中一种是通过 SVG 图片,而通过 SVG 图片的实现方式本质就是实现一个 SVG 的组件。在 Vue3 中的实现是非常简单的,代码如下:
template
svg
viewBox="0010241024"
xmlns="http://www.w3.org/2000/svg"
data-v-029747aa=""
path
fill="currentColor"
d="M832512a3232011640v352a3232001-3232H160a3232001-32-32V160a323200132-32h352a3232001064H192v640h640V512z"
/
path
fill="currentColor"
d="m469.952554.2452.8-7.552L847.104222.4a3232010-45.248-45.248L477.44501.44l-7.55252.8zm422.4-422.4a96960010135.808l-331.84331.84a3232001-18.1129.088L436.8623.68a3232001-36.224-36.224l15.104-105.6a32320019.024-18.112l331.904-331.84a9696001135.7440z"
/
/svg
/template
scriptlang="ts"
import{defineComponent}from'vue'
exportdefaultdefineComponent({
name:'EditIcon',
})
/script
其中 template 中的内容便是一张 SVG 图片的源码,而 SVG 图片的源码获取方式有很多,我们这里提供其中一种,我们可以把设计师设计的图标上传到阿里的iconfont图标库,然后在下载该图标的时候,可以选择复制 SVG 源码。
SVG-code.png有了 SVG 图标的组件之后,我们就可以直接导入在 Icon 组件中进行使用了。
template
div
el-icon:color="'green'":size="15"EditIcon//el-icon
/div
/template
scriptsetuplang="ts"
importEditIconfrom'./EditIcon.vue'
/script
stylescoped/style
运行结果如下:
edit-icon-run.png我们可以看到我们的图标成功渲染出来了。
字体图标我们还可以通过字体图标进行设置:
template
div
el-icon:color="'green'":size="15"
iclass="iconfonticonlogistics-car"/i
/el-icon
/div
/template
scriptsetuplang="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
运行结果:
font-icon.png字体图标也是可以通过阿里的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 组件的实现
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线