学着写一个UI框架
在平时的工作开发中,若遇到可重复使用的内容,或者代码块过长,经常会把代码封装成组件,减少重复代码,提高开发效率。写的较多的还是业务组件,主要和业务绑定比较深入,最近,尝试写了一下ui组件
,模仿element.ui
的样式和使用写了一个aiyaya-ui
,并且发布在了npm
上。
总结一下这次ui组件
开发过程中遇到的点和经验:
Dialog
组件中sync
的妙用
我们需要对一个父组件传进来的visible
进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件两侧都没有明显的变更来源。只能通过$emit
,主动调起父组件中的事件。vue
官方推荐以update:myPropName
的模式触发事件来解决该问题
ui子组件代码中1
2
3
4
5
6<div class="ya-dialog__wrapper"
style="z-index: 2002"
v-show="visible"
@click.self="handleClick">
<!-- 其他代码 -->
</div>1
2
3
4handleClick() {
// 将false赋予给visible
this.$emit('update:visible', false);
}然后父组件可以监听那个事件并根据需要更新一个本地的数据 property
父组件:1
2
3
4
5<ya-dialog
width="60%"
:visible="show"
@update:visible="show= $event">
<ya-dialog>为了方便起见,我们为这种模式提供一个缩写,即
.sync
修饰符,所以.sync
其实可以被当作是上述写法的一种语法糖,主要用于能比较便捷地在子组件中实现对props
父组件传入变量的值更新。1
2
3
4<ya-dialog
width="60%"
:visible.sync="visible">
</ya-dialog>Dialog
组件中的动画效果
通过vue
动画实现对话框的淡入淡出效果Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡。
- 条件渲染 (使用 v-if)
- 条件展示 (使用 v-show)
- 动态组件
- 组件根节
1
2
3<transition name="dialog-transition">
<!-- content -->
</transition>1
2
3
4
5
6
7
8
9/* 进入/离开过渡生效时的状态 */
/* transition中的name + 动画的时刻*/
.dialog-transition-enter-active, .dialog-transition-leave-active {
transition: opacity .5s;
}
/* 进入/离开过度开始 */
.dialog-transition-enter, .dialog-transition-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}插槽
插槽是在开发ui组件的过程中必不可少的部分,扩展了组件的用途。在组件中实现
v-model
双向绑定
在我们使用像input
一类的自定义ui组件的时候,需要给input
双向绑定一个变量,来实现数据在view
层,model
层的相互交互。v-model
其实是value
属性和input
事件的语法糖1
2
3
4
5
6
7
8
9
10
11<!-- v-model双向绑定 -->
<input
class="ya-input__inner"
:class="{'is-disabled': disabled}"
:placeholder="placeholder"
:type="showPassword? (isShowPassword? 'text' : 'password'): type"
:disabled="disabled"
:name="name"
:value="value"
@input="changeVal"
/>1
2
3
4// 子组件修改父组件
changeVal(e) {
this.$emit('input', e.target.value)
},由此,实现了在子组件上的
v-model
双向绑定。
另外,在checkbox
和radio
组件中要实现v-model
, 也是类似的原理,checkbox
和radio
组件是input
组件的变体1
2
3
4
5
6<input
type="checkbox"
class="ya-checkbox__original"
:value="label"
:name="name"
v-model="checkStatus">1
2
3
4
5
6
7
8
9
10
11checkStatus: {
get() {
// label值
// console.log(this.CheckboxGroup.value)
return this.value
},
set(value) {
// 将checkStatus值传给父组件
this.$emit('input', value)
}
}还有一种写法,后面看
vue
官网看到的,使用model
选项允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。
model
选项
1 | model: { |
1 | <input |
在父组件中引用子组件:
1 | <ya-checkbox |
等同于
1 | <ya-checkbox |
inject
和provide
传值
在平时的业务开发过程中,很少用到inject/provide
传值,这种传值方式可以用在子孙组件之间,比较便捷。
在写-group
组件的时候,遇到一个场景,使用radio
组件的时候,需要给每个组件都绑定v-model
,可以使用radio-group
包裹radio
,将v-model
直接放在radio-group
组件之上,这个时候,在使用radio
组件的时候就需要判断我的最外层有没有被-group
包裹
groupcheckbox1
2
3
4
5
6provide() {
return {
// 将当前组件赋予给CheckboxGroup
CheckboxGroup: this,
}
}1
2
3
4
5inject: {
CheckboxGroup: {
default: ''
}
},
最后,组件开发完成,需要打包成一个组件库,并且发布到npm
上,这样子下次别的项目需要用就只需要install
然后引入使用了。
修改项目的文件夹,如下图
新增vue.config.js
配置文件
1 | const path = require('path') |
packages
添加入口文件index.js
1 | // 导入组件 |
一切准备就绪,发布到npm
,首先要有个npm
的账号,然后修改package.json
中的脚本命令
1 | "name": "aiyaya-ui", |
增加 .npmignore
文件,撇除不需要上传的文件
1 | # 忽略目录 |
最后运行发布命令,就ok啦
1 | npm login npm publish |