文章目录
1 可复用组件
- 在
vue
中,我们可以通过new Vue
来创建一个组件 - 不过通常它是作为整个应用的顶层根组件存在的,我们还可以通过另外的方式来注册一个更为通用的组件。
- 不同于根组件,同一个可复用组件,可以在同一个应用中多次使用。
1-1 可复用UI组件的创建
- 可复用组件在构建选项上与全局组件基本一致
- 可复用组件可以像标签一些在模板中使用
- 不能与原生的html标签重名
- 组件的options里面有一个选项:
components
,可以通过该选项来定义组件
全局组件
Vue.component('组件名称', {组件选项})
可以在任意位置(多个不同的 Vue 应用中)使用。
局部组件
new Vue({
...,
components: {
'组件名称': {组件选项}
}
})
局部组件只能在其定义的组件内使用,也不能在其子组件内部使用
<div id="app">
<my-component1></my-component1>
<my-component2>
<my2></my2>
</my-component2>
<!-- <my2></my2> -->
<my3></my3>
<my4></my4>
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component("my-component1", {
template: `<div>全局组件1</div>`,
})
Vue.component('my-component2', {
template: `<div>全局组件2</div>`,
components: {
'my2': {
template: `<div>My2</div>`
}
}
});
new Vue({
el: '#app',
components: {
'my3': {
template: `<div>局部组件</div>`
},
"my4": {
template: `<div>局部组件2</div>`
}
}
});
</script>
1-2 组件内部私有数据
可复用组件的 data
必须是函数,且该函数必须返回一个对象作为组件最终的 data
会使得复用的组件数据之间不会相互影响
如下点击事件只会影响点击的对应组件中的数据
<div id="app">
<my-component1></my-component1>
<my-component1></my-component1>
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component("my-component1", {
template: `<div @click="add">全局组件1 - {{a}} - {{b}}</div>`,
data: function() {
return {
a: 1,
b: 2,
}
},
methods: {
add(){
this.a++;
}
},
})
</script>
1-3 组件外部传入数据
如同一个函数一样,函数除了可以定义内部私有变量,有时候为了提高函数的复用性,我们通过会通过参数来接收外部传入的数据。组件也可以。
props
组件中内部私有数据存储中组件 data
中,通过外部传入的数据,则通过 props
选项接收。
首先,组件内部通过 props
来定义可以接收的数据名称,就像是函数的形参。
然后,在使用该组件的时候可以通过标签属性的方式进行传参(可配合 v-bind
传入表达式)。
注意事项
- 如果传入的
props
值为一个表达式,则必须使用v-bind
- 组件中的
data
和props
数据都可以通过组件实例进行直接访问 data
中的key
与props
中的key
不能冲突
案例:做一个分页组件
父级调用组件的时候将父级数据传递给组件
Vue.component('kkb-pagination', {
// 类似给函数定义形参,接收父级传来的数据
props: ['total', 'prepage', 'page'],
data() {
// data 中是可以通过this访问到 props 的
return {
pages: Math.ceil((this.total / this.prepage))
};
},
// template可以直接访问到props里的数据
template: `
<div class="pagination">
<a href="" class="prev">上一页</a>
<a
href="javascript:;"
v-for="p of pages"
:class="{current: p == page}"
@click="changePage(p)"
>{{p}}</a>
<a href="" class="next">下一页</a>
</div>
`,
});
1-4 组件通信
1 作用域隔离
为了保证数据安全性(传入的数据通常会在其它组件中也有使用),Vue 不推荐我们在组件内部直接修改传入的数据。
2 父组件到子组件内部的数据传递
父组件通过上述提到的 props
向一个子组件内部传递数据。
3 子组件内部到外部的数据传递
$emit()
vue
为每个组件对象提供了一个内置方法 $emit
,它等同于自定义事件中的 new Event
,trigger
等
this.$emit('自定义事件名称', 事件数据)
- 子级通过
$emit
方法通知父级来修改数据 - 事件数据就是中触发事件的同时携带传递的数据 -
event
- 父级在使用该组件的过程中,可以通过
@事件名称
来注册绑定事件函数 - 事件函数的第一个参数就是事件数据
4 数据双向绑定
v-model
v-model
是 vue
提供的一个用于实现数据双向绑定的指令,用来简化 props 到 data
,data 到 props
的操作流程。
model 选项
prop
指定要绑定的属性,默认是 value
event
指定要绑定触发的事件,默认是 input
事件
案例:使用v-model 实现分页点击修改
子组件写上$emit
事件
methods: {
changePage(p) {
this.$emit('change', p);
}
}
model配置里绑定page
数据与change
方法
当组件 emit
的事件名称是 event
指定,那么就根据上面的prop指定属性去更改对应的值
model: {
// v-model的数据是绑定到 props 中 page 属性,默认是value
prop: 'page',
// event,当组件 emit 的事件名称是 event 指定,那么就根据上面的prop指定属性去更改对应的值
event: 'change'
},
点击成功修改
.sync
通过 v-model
来进行双向绑定,会给状态维护带来一定的问题,因为修改比较隐蔽,
同时只能处理一个 prop
的绑定,我们还可以通过另外一种方式来达到这个目的。
update:[prop]
这里事件名称要使用 update
加上 prop
名称 的格式
案例:通过.sync 改写分页组件的数据双绑
先给v-bind加上修饰符.sync
触发一个update事件,使用p去更新page(page必须是通过 .sync 来进行绑定的)
methods: {
changePage(p) {
// 触发一个update事件,使用p去更新page(page必须是通过 .sync 来进行绑定的)
this.$emit('update:page', p);
},
// 将每页数据量大小改为5
changePrepage(e) {
e.preventDefault();
this.$emit('update:prepage', 5);
}
}
成功修改
1-5 ref 与 $refs
ref
被用来给元素或子组件注册引用信息。- 引用信息将会注册在父组件的
$refs
对象上。 - 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;
- 如果用在子组件上,引用就指向组件。
1-6 插槽
默认情况下,组件模板解析后会替换整个组件内容,
如果我们想在组件引用被包含的内容,可以通过 vue
提供的内置组件 slot
来获取。
slot
Vue
提供了一个内置的组件<slot>
,用于在组件模板内定义插槽。- 解析过程中,会被该组件嵌套的子组件(元素)对应的内容所替换。
默认替换
<slot></slot>
具名插槽
使用内置组件 template
与 v-slot
指令进行配置,用来命名插槽
在组件模板中,通过 <标签 v-slot:插槽名称>
来使用。
- 插槽名称默认为
default
,可以省略。
给插槽命名<slot name="left"...
<slot name="left" :pages="pages" a="1"></slot>
父组件使用v-slot:left
<template v-slot:left="data">
<span>一共有{{cartItems.length}}条数据,一共{{data.pages}}页,当前{{cPage}}页</span>
</template>
作用域插槽
组件内部与组件包含的内容属于不同的作用域(被包含的内容是组件父级下的作用域)。
组件到父级作用域下的子级通信。
- 组件内部通过
slot
的attributes
进行数据传递。 - 子级通过
v-slot
指令的值进行接收。
// 组件内部
<slot v-for="user of users" :user="user"></slot>
// 组件外部子级
<template v-slot:default="data">
<p>用户的姓名: {{data.user.username}}</p>
</template>
1-7 props 验证
组件的 props
就是组件的参数,为了确保传入的数据在可控的合理范围内,
我们需要对传入的 props
的值类型进行必要的验证。
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
1-8 其它 props 相关知识点
非 prop 特性
一个非 prop
特性是指传向一个组件,但是该组件并没有相应 prop
定义的特性,这些 props
会被自动添加到组件的根元素上。
替换/合并已有的特性
默认情况下,非prop
特性的属性会覆盖组件根元素上同名的内容,但是针对 style
和 class
有特殊的处理,它们会合并(同名样式还是会覆盖)。
比如父组件设置样式
子组件也设置样式
父组件就会覆盖子组件相同的样式,
会保留父组件没有的样式
inheritAttrs 禁用特性继承
如果不希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false
,我们可以通过组件的 this.$attrs
来获取这些属性。
注意 inheritAttrs: false
选项不会影响 style
和 class
的绑定。