这是我参与「第五届青训营 」伴学笔记创作活动的第 10 天
vue组件库搭建实战-笔记
1.课前
1.涉及知识点
2.组件库的使用
1.下载组件库 yarn add itcast-ui 2.在main.js中全局的引入组件库 import ItcastUI from 'itcast-ui'
3.使用Vue.use(itcast-ui)来使用组件
补充知识
1.Vue.use()的作用是通过全局方法 Vue.use() 使用插件
需要在你调用 new Vue() 启动应用之前完成。
2.Vue.config.productionTip = false 的作用:将productionTip设置成false可以阻止vue在启动时生成生产提示,减小体积=>j减小开销
3.new Vue():每个vue应用都是通过创建vue实例开始的,当vue实例被创建时,它的所有data的属性都会被添加进vue的相应式系统中,也就是说,如果在实例被创建之后的添加的data的属性将不会有响应式的变化
4.render函数:vue是虚拟DOM,所以在拿到template模板的时候需要将起转义为VNode函数,但是使用render函数就免去了转义的过程,render函数传入的参数h是构建虚拟DOM的工具,它的名字是createElement,h只是简写
2.项目初始化
1.为什么要用sass,因为项目中的字体图标库需要用到sass
2.项目的运行 使用yarn serve运行
3.清空所有的APP.vue内的东西和component的东西以及静态资源文件得到最干净的一个vue项目
补充知识
1.npm run dev和npm run serve的区别,具体看是npm run ***,看的是package.json配置文件中script里面写的是什么
3.组件的封装
1.Button
步骤
1.搭架子
1.在文件component中创建单文件组件
2.在main.js中进行全局注册
首先进行导入,再通过Vue.component进行注册,第一个参数是组件的名字,第二个参数是导入的组件
3.在APP.vue中通过
<yf-button>......</yf-button>
标签来使用
4.在button.vue中用
<span><slot></slot></span>
在span里包裹一个slot插槽,这样可以用span来写样式,在app.vue中使用的时候也能直接通过标签里面的内容来改变button的名字
2.支持type属性
1.在app.vue中通过type属性来使用
<yf-button type="success">成功按钮</yf-button>
2.在button组件中,因为app.vue是button.vue的父组件,所以父向子传值,所以用props
props: {
// type属性
type: {
// 数据类型为String
type: String,
//未传值的默认default类型
default: 'default'
}
},
3.通过动态的class来生成按钮的type属性控制的样式
:class="[`yf-button-${type}`]"
4.在scss中根据不同的样式写css
3.plain属性(决定按钮是否是镂空的属性)
1.在app.vue中给按钮添加plain属性,该属性是一个布尔值
2.在button.vue中在props上添加plain属性
props: {
//...
// 朴素按钮属性
plain: {
type: Boolean,
default: false
}
},
3.在class里面加上一个对象元素,键为'is-plain',值为props中的plain
:class="[`yf-button--${type}`,{'is-plain':plain}]
4.round属性
1.在button.vue中的scss中写圆角属性
2,在props中添加该属性
3.在class的对象中添加对round属性的控制
5.circle属性
1.在button.vue中的scss中写circle属性
2,在props中添加该属性
3.在class的对象中添加对circle属性的控制
4.将字体图标文件放在asset文件夹下
5.在main.js中全局导入字体图标文件
6.字体图标
1.通过props进行传值,在props中定义该属性的类型
props: {
//...
icon: {
type: String,
default: ''
}
}
2.在template中用i标签来存放字体图标,通过v-if中icon是否有值来控制字体图标是否显示,通过:class="one-icon-${icon}来控制字体图标
<i v-if="icon" :class="`one-icon-${icon}`"></i>
3.为了使即有图标又有文字的情况下更美观,将图标的margin-left控制为5px,通过[class*=one-icon-]来进行属性选则
.one-button [class*=one-icon-]+span{
margin-left: 5px;
}
4.由于在i不显示时只要有span就会有margin,所以我们为了避免这种情况就用v-if来控制是否显示span,如果没有传入文字的话$slots.default为空
<span v-if="$slots.default"><slot></slot></span>
7.click事件
子组件click的时候会触发handleClick事件,在handleClick事件中通过$emit触发父组件的自定义事件click,父组件监听click事件触发自己的事件
1.在button.vue中的template中通过@click绑定一个点击事件
@click="handleClick"
2.在methods中写一个handleClick事件,通过$emit将父组件中的函数传进来
handleClick (e) {
this.$emit('click', e)
}
8.disabled
1.定义props中的disabled的属性
2.将disabled属性的属性值定为disabled,来使按钮禁用
3.设置禁用的样式
补充知识
1.所有的组件都是在component中创建单文件组件的,在main.js中进行全局的注册
2.user-select: none样式可以避免文字被选中
3.数组形式的props是比较宽松的,属于传值也可以,不传值也行,并且传过来什么就接受什么,不会进行props校验;一般情况下为了保证组件的严谨,我们都使用对象形式的props
4.我们可以给 :class (v-bind:class 的缩写) 传递一个对象来动态切换 class,“”里面可以是数组也可以是对象,props里面的值和data中的值一样可以直接作为变量使用。class是非常灵活的,涉及到布尔值的时候用对象会更方便一些
5.[class*=one-icon-]意思是class中是要有one-icon-的都会被选中
6.slots.default为空,$slots.default包含了所有没被包含在具名插槽内的节点
7.所有的vue的属性值都是要有引号的
:disabled="disabled"
'is-disabled':disabled//这里是一个对象的键值对,不是vue的属性与属性值
2.Dialog
步骤
1.搭架子
1.在component下创建dialog组件,在main.js中全局引入,在template中写好基础模板,在style中写好基础样式
main.js
import YfDialog from './components/dialog.vue'
Vue.component(YfDialog.name, YfDialog)
2.title
1.在app.vue中通过title属性,将title的值传入
2.在dialog.vue中添加props的title属性,在template标题处通过双花大括号修改标题
但是这样做的话仅仅能改变的是title的文本,如果我想让title是什么就是什么的话就需要用具名插槽
1.在dialog.vue中加上插槽,并通过name属性让其名字为title
<slot name="title">
<span class="yf-dialog_title">{{title}}</span>
</slot>
这样写还有一个好处是当使用的时候直接写title加title值时会调用默认的title样式
2.在app.vue中通过template和v-slot:title来使用
<template v-slot:title>
<h3>我是标题</h3>
</template>
3.宽和距离顶部的距离
1.使用props接受传递过来的宽高值
2.使用行内样式来动态控制宽高
:style="{width:width,marginTop:top}"
4.内容
使用默认插槽传递内容
5.底部
因为实际需求中可能是有些对话框需要底部有信息,有些不需要底部有信息。所以我们采用具名插槽的形式,为了消除掉不传底部信息时的插槽空间占用,我们采用v-if="$slots.footer"的方式
6.对话框的显示与隐藏
显示
1.使用props接受传递过来的visible 的值
2.通过v-show="visible"来控制显示与隐藏
隐藏
点击*可以隐藏,对话框,点击空白处也能隐藏对话框,点击取消按钮可以隐藏
1.在*按钮上绑定click事件,绑定的事件为handleClick,在整个对话框上通过.self修饰符绑定handleClick事件
<button class="yf-dialog_headerbtn" @click="handleClick">
<i class="one-icon-close"></i>
</button>
2.在handleClick事件上只需要改变visible的值就可以了,但是visible的值是不能直接改变的,因为visible的值是由父组件传递进来的,所以是不能直接修改props中的visible的
<div class="yf-dialog_wrapper" v-show="visible" @click.self="handleClick">
4.我们在handleClick事中通过$emit与父组件的事件建立连接,并将false传递给父组件
handleClick () {
this.$emit('close', false)
}
5.在父组件的close事件中改变visible的值
@close="close"
close (value) {
this.visible = value
}
6.在取消按钮上监听点击事件,在事件中使visible为0
使用.sync修饰符
1.在子组件dialog.vue中,将$emit的函数名字改了,改成了update:待修改属性名的形式
handleClick () {
this.$emit('update:visible', false)
}
2.在父组件app.vue中,在传值的属性后加上.sync修饰符
:visible.sync="visible"
7.动画
1.用transition标签把要实现动画效果的元素包起来
2.使用动画效果实现显示与隐藏的动画
.v-enter-active{
animation: run .5s;
}
.v-leave-active{
animation: run .5s reverse;
}
@keyframes run {
0%{
opacity: 0;
transform: translateY(-20px);
}
100%{
opacity: 1;
transform: translateY(0px);
}
}
transform: translateY处理了一下掉下来的感觉,使动画更逼真
补充知识
1.具名插槽的定义是slot标签加name属性来指定名字,具名插槽的使用是通过template标签加上v-slot来指定名字,这里name后面是等于,v-slot后面是冒号
2.通过事件修饰符.self实现点击遮罩触发隐藏对话框,点对话框本身不隐藏
3.sync修饰符是一个语法糖,用于子组件想修改父组件的值的缩写,子组件本来修改父组件的值时,父组件有一个传递给子组件的props, 和一个用于修改值的监听事件,但是现在可以将二者合并为一个,用:通过props传递的属性.sync="属性值"就可以了;
其在子组件并不会有什么优势,主要是父组件使用起来会简单很多,一般用于子组件想要修改父组件的值的情况下
4.用transition标签包裹元素后,这个元素在显示和隐藏的时候会自动添加一些类名,我们可以通过控制这些类名来实现特定的动画效果,总共有六个类名,分别为(这些都是在style中写的)
//进入前
.v-enter{
opacity:0;
}
//进入后
.v-enter-to{
opacity:1;
}
//进入时的过渡效果
.v-enter-active{
transition: all 0.5s;
}
//离开前
.v-leave{
opacity:1;
}
//离开后
.v-leave-to{
opacity:0;
}
//离开时的过渡效果
.v-leave-active{
transition: all 0.5s;
}
如果设置了name,那么将v改为name,如,name='yf',过渡时的类名就为yf-enter,以此类推
因为六个类名比较麻烦,所以我们可以简化处理
.v-enter-active{
animation: run .5s;
}
.v-leave=active{
animation: run .5s reverse;
}
@keyframes run{
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
5.scoped的作用:scoped会给当前也页面的所有元素都添加一个随机的属性选择器,同时也会给当前的所有样式添加对应的属性选择器,这样就保证了当前样式只在当前页面起作用
6.深度选择器,只要在样式前面加上深度选择器,scoped带来的作用就不会生效了,即不会给该元素添加这个随机属性了
深度选择器的写法
css: >>>
less: /deep
scss: ::v-deep
文档:vue-loader
3.input组件
步骤
1.搭架子
和之前的并无不同,在input.vue中搭框架,在main.js中进行组件的导入
2.基础属性
1.通过props将app.vue设置的placeholder传入,默认为空
2.通过props将app.vue设置的type属性传入,默认为文本框
3.通过props将app.vue设置的disabled属性传入,默认为false,并通过
:class="{
'is-disabled':disabled
}"
动态改变disabled的样式
3.v-model属性
1.在app.vue中使用v-model
2.根据v-model的原理在input组件中进行更新
3.通过props接受传递的value值
value: {
type: String,
default: ''
}
4.在template中为value属性绑定value值
:value="value"
5.绑定input事件,为input事件绑定handelInput回调函数
@input="handelInput"
6.在回调函数中通过$emit修改父组件的值
handelInput (e) {
this.$emit('input', e.target.value)
}
4.清空value值
1.在app.vue中设置clearble属性
2.在input.vue中用props接收传递过来的clearble
clearble: {
type: Boolean,
default: false
}
3.设置span标签,在标签中放clearble图标
<span class="yf-input_suffix" v-if="showSuffix">
<i class="on-input_icon one-icon-cancel" v-if="clearble&&value" @click="clear"></i>
</span>
4.在整个输入框上绑定类名yf-input_suffix,由showSuffix属性累控制该类名的显示与隐藏,
computed: {
showSuffix () {
return this.clearble || this.showPassword
}
}
只要有clearble或者showPassword,showSuffix 不为空,则显示该类,该类的作用是在输入框里面留一个padding用来放清除内容图标和显示隐藏密码文本图标
5.为清除图标注册click事件,点击click的时候为其绑定回调函数clear,来使内容清空
clear () {
this.$emit('input', '')
}
5.显示密码文本
1.为显示按钮注册事件@click="handlePassword"
<i class="on-input_icon one-icon-visible" v-if="showPassword" @click="handlePassword" ></i>
handlePassword () {
this.passwordVisible = !this.passwordVisible
}
2.如果传了show-password,判断是否需要切换密码显示,如果show-password为真,则根据passwordVisible来判断让type为text还是,如果show-password为假,直接让type=type
:type="showPassword?(passwordVisible ? 'text':'password') :type"
本来直接点击事件中修改type就好了,但是type是父组件传过来的,不能修改props,所以我们另外定义了passwordVisible,通过点击事件修改passwordVisible,进而来决定type属性
3.让小眼睛亮一下,增加一个one-icon-visible-active类,在类中设置一个颜色,并通过passwordVisible动态的去控制类名
:class="{'one-icon-visible-active':passwordVisible}"
.one-icon-visible-active{
color: #409eff;
}
补充知识
1.v-model就是一个语法糖
相当于
:value="username" @input="username=$event.target.value"
一个value加一个input事件
4.swich开关
步骤
1.开关自定义颜色
1.通过click事件来改变v-model的值
app.vue
<yf-swich v-model="value"></yf-swich>
value: false
swich.vue
@click="handleClick"
props: {
value: {
type: Boolean,
default: false
}
}
handleClick () {
this.$emit('input', !this.value)
}
2.在scss中写选中样式
3.通过value的值动态改变样式
:class="{
'is-checked':value
}"
4.在app.vue中通过属性active-color、inactive-color控制开关颜色
<yf-swich v-model="value" active-color="pink" inactive-color="skyBlue"></yf-swich>
5.用props传值,在子组件中接受参数
props:{
//...
activeColor: {
type: String,
default: ''
},
inactiveColor: {
type: String,
default: ''
}
}
6.封装改变颜色的函数setcolor
setColor () {
//如果传入的有颜色就往下执行
if (this.activeColor || this.inactiveColor) {//根据value值对应应该传递的颜色
const color = this.value ? this.activeColor : this.inactiveColor
//根据$refs来修改颜色
this.$refs.core.style.borderColor = color
this.$refs.core.style.backgroundColor = color
}
}
7.在mounted的和click的回调函数中改变调用改变颜色按钮
mounted () {
this.setColor()
}
handleClick () {
//...
this.$nextTick(function () {
this.setColor()
})
}
2.添加input框
用户在使用switch组件的时候,实质上是当成表单元素来使用的。因此可能会用到组件的name属性。所以需要在switch组件中添加一个checkbox,并且当值改变的时候,也需要设置checkbox的value值。
1.在app.vue中添加name属性
<yf-switch v-model="value" active-color="pink" inactive-color="skyBlue" name="username"></yf-switch>
data () {
return {
//...
username: 'zs',
}
}
2.在switch组件中通过input type="checkbox添加了一个复选框",使用refs来操作DOM
<input type="checkbox" class="yf-switch_input" :name="name" ref="input">
3.隐藏checkbox
.yf-switch_input{
position:absolute;
width: 0;
height: 0;
opacity: 0;
margin: 0;
}
4.将switch的值和checkbox的值统一
mounted () {
//...
this.$refs.input.checked = this.value
}
handleClick () {
//...
this.$nextTick(function () {
//...
this.$refs.input.checked = this.value
})
}
补充知识
1.如果要控制DOM,需要用到ref,ref="core"定义,$refs.core来表示该DOM
2.要等到父组件的value改变后在调用函数setColor,所以我们需要使用this.$nextTick
5.radio组件
步骤
1.基本结构
1.搭建基本结构
2.在app.vue中写理想的单选效果
<yf-radio label="1" v-model="gender">男</yf-radio>
<yf-radio label="0" v-model="gender">女</yf-radio>
3.在props接受父组件传递进来的值
props: {
label: {
//label的类型是以下数组中的类型选择其一
type: [String, Boolean, Number],
default: ''
},
value: null,
name: {
type: String,
default: ''
}
}
4.使用插槽来显示label的文本,如果不传文本的话默认将label属性作为文本来显示
<span class="yf-radio_label">
<slot></slot>
<template v-if="!$slots.default">{{label}}</template>
</span>
2.控制选中
1.radio.vue中为每一个input框加一个value属性,值为传进来的label,添加name属性,值为name
:value="label"
:name="name"
2.应该有一个v-model用于双向绑定,但是我们不能直接双向绑定父元素传进来的值,所以我们双向绑定当前组件的值,我们绑定一个计算属性model
v-model="model"
computed: {
model: {
get () {
return this.value
},
set (value) {
//触发父组件传递进来的input事件来修改value值
this.$emit('input', value)
}
}
}
3.根据label值是否等于value值来选中样式
:class="{
'is-checked':label==value
}"
3.radio-group组件封装
为了避免让每一个radio去双向绑定,所以我们提供了radio-group组件
1.创建radio-group组件并全局注册
2.在app.vue中写自己想要的效果
<yf-radio-group v-model="gender">
<yf-radio label="0" >女</yf-radio>
<yf-radio label="1" ></yf-radio>
</yf-radio-group>
3.在radio-group.vue中搭建框架
4.如果radioGroup和radio中嵌套了很多层,我们就不能通过props来传值了,我们可以通过provide和inject进行传值与接受。在radioGroup中通过provide将radioGroup自己传给子组件,子组件通过inject来接受,并设置一个默认值为空,以便在不传值的时候为空值
radio-group.vue
provide () {
return {
radioGroup: this
}
}
radio.vue
inject: {
radioGroup: {
default: ''
}
}
5.我们在radio.vue中添加一个计算属性,该计算属性用于判断radio是否被radioGroup包裹
isGroup () {
// 判断组件是否被包裹
return !!this.radioGroup
}
6.根据是否被包裹来确定获取的value值和触发的input事件
model: {
get () {
return this.isGroup ? this.radioGroup.value : this.value
},
set (value) {
this.isGroup ? this.radioGroup.$emit('input', value) : this.$emit('input', value)
}
}
如果被包裹,获取的value值是radioGroup的值,如果没有被包裹,获取的value值是当前组件的value值;如果被包裹,触发的事件是radioGroup的事件,如果没有被包裹,触发的事件是radio的事件
7.将原来的value改为model
'is-checked':label==model
补充知识
1.表单元素都有可能传name属性,所以我们在props校验的时候记得写上对name的校验
2.如果要双向绑定一个计算属性的话,的提供他的get和set
3.provide 与inject:祖先组件中用provide提供值,子组件中用inject接受值;如果我们需要提供依赖当前组件实例的状态 (比如那些由 data() 定义的数据属性),那么可以以函数形式使用 provide,比如,如果我们要传递this,那么provide必须要用函数式
6.checkbox组件
与radio很像,不一样的是使用的时候双向绑定 的是一个数组
显示样式是根据ischecked来控制的
ischecked () {
return this.isGroup ? this.model.includes(this.label) : this.model
}
7.form组件
步骤
1.创建form组件和form-item组件,并全局注册
2.在app.vue中写处理想情况
<template>
<div id="app">
<yf-form :model="model">
<yf-form-item label="用户名">
<yf-input placeholder="请输入用户名"></yf-input>
</yf-form-item>
</yf-form>
</div>
</template>
data () {
return {
model: {
}
}
}
3.在form.vue中用props接受app.vue的属性
props: {
model: {
type: Object,
required: true
}
}
4.搭建form-item基本结构
5.在props中传入label
props: {
label: String
}
6.app.vue中完善表单的运用
<template>
<div id="app">
<yf-form :model="model" label-width="100px">
<yf-form-item label="用户名">
<yf-input placeholder="请输入用户名" v-model="model.username"></yf-input>
</yf-form-item>
<yf-form-item label="选择">
<yf-switch v-model="model.active"></yf-switch>
</yf-form-item>
</yf-form>
</div>
</template>
model: {
username: '',
active: true
}
7.在form.vue中通过provide将form传递给子组件
provide () {
return {
form: this
}
}
8.在form-item.vue中通过inject接受form
inject: {
form: {
default: ''
}
}
在template中动态改变label的宽度
<label class="yf-form-item_label" :style="{width: this.form.labelWidth}">{{label}}</label>
补充知识
4.封装并发布
1.把packages处理成vue插件
1.用脚手架新建一个项目
2.新增examples文件夹和packages文件夹
3.修改vue.config.js文件
const path = require('path')
module.exports = {
pages:{
index:{
// 修改项目入口文件
entry:'examples/main.js',
template:'public/index.html',
filename:'index.html'
}
},
// 扩展webpack配置,使webpages加入编译
chainWebpack: config => {
config.module
.rule('js')
.include.add(path.resolve(__dirname,'packages')).end()
.use('babel')
.loader('babel-loader')
.tap(options => {
return options
})
}
}
4.将所有封装的组件和font文件夹都复制到packages文件夹里
5.配置vue.config.js
6.在packages目录下新建index.js文件,用于声明install对象
7.在pacakges.json中的script下加入打包组件的命令
8.在控制台运行npm run lib进行打包
9.在github上创建仓库
10.在本地初始化git,然后使用命令提交代码至git仓库
2.发布到npm上
1.修改package.json
2.根目录下增加.npmigore文件
3.上传到npm