1、组件基础
Vue的特点:组件开发。页面将为是一颗嵌套的组件树
src目录下有个components,组件都放在其中。组件首字母一般为大写。组件是带有名称可复用的实例。
1.1 vue2做法
1、在components下新建Content.vue的文件
2、在App.vue中引入,并且components注册,标准写法Content:Content,前为方便别名后为引入的组件。也可以直接简写如下
export default {
data() {
return {
};
},
components:{
Content//简写
}
};
3、使用组件。ps.直接写<Content然后会跳出来选项自动补全代码。
<div>
<Content></Content>
</div>
组件嵌套,在Content.vue中引入另一个组件
<template>
<div>
<h2>我是组件内容</h2>
<Hello></Hello>
</div>
</template>
<script>
import Hello from './Hello.vue'
export default{
components:{
Hello
}
}
</script>
组件复用
父组件和子组件,在上面的例子中App.vue是根组件,Content.vue是Hello.vue的父组件,组件是单独功能模块的封装。
组件数据存放
父子组件并不可以随意的获取对方的数据,需要使用对应的方法。并且连续使用三个同样的组件,修改其中某一组件的data(),另外两个组件是独立的不受影响的。
data()是一个函数,每一次执行完都是返回一个新的对象
data(){
return{msg:"helloworld"}
},
除非定义const,让三个组件用的同一个obj。但这是不推荐的,会造成数据的污染。
const obj={
msg:"helloworld"
}
export default{
data(){
return obj
},
components:{
Hello
}
}
1.2 父传子Prop
Prop是可以在组件上注册的一些自定义的属性
Content.vue中
data(){
return{
msg:"helloworld"
}
},
<Hello :message='msg' aaa="123"></Hello>
Hello.vue中
export default{
props:['message','aaa'],
}
<div>
<h2>--{{message}}--{{aaa}}</h2>
</div>
以数组的形式传递数据过去,其中message是动态而且绑定的,aaa静态,两个都可以传
Prop常用法
不以数组而是对象的形式
1 限制类型,可以限制传递过来数据的格式,可多种
props:{
message:String
//message:[String,Number]
}
2 设置默认值default
3 设置必传值required
props:{
message:{
type:String,
default:"你好",
required:true,
}
}
对于默认值对象和数组的默认值必须从一个工厂返回
props:{
···
list:{
type:Array,
//default:[],错误写法
default(){
return []
},
}
}
单项数据流
prop让父子之间相乘一个单向下行绑定,父更新会流动到子中,反过来却不行。
1.3 子传父$emit监听
在App.vue中监听Content.vue子组件数据,通过自定义事件
1、在子组件Content.vue中通过$emit
来触发事件
this.$emit(' 自定义事件的名称 ',' 发送的事件参数 ')
methods:{
sendParent:function(){
this.$emit('injectMsg',this.msg)
}
},
<button @click="sendParent">提交数据给父组件</button>
2、在父组件App.vue中,通过v-on监听子组件的自定义事件
通过@绑定了在子组件里写的injectMsg事件,点击按钮事件触发,进而运行getChildMsg方法。
<Content @injectMsg="getChildMsg"></Content>
methods:{
getChildMsg:function(value){
console.log(value);
}
},
3、在父组件App.vue中,存储获得数据
默认参数value就是传过来的数据
data() {
return {
message: ""
};
},
methods: {
getChildMsg: function (value) {
this.message = value;
}
},
1.4 父访问子 $refs
父组件访问子组件,$refs和$children。vue3已经废弃$children,他有缺陷因为传递的是数组类型。
$refs
需要搭配一个ref
指令来使用,ref:用来给元素或者子组件注册引用信息,开发中经常用到,给子组件绑定自己的起的id名。
在子组件Content.vue中
<Hello :message='msg' aaa="123" :list="list" ref="hello"></Hello>
<p ref="p"></p>
export default {
···
mounted(){//生命周期函数
console.log(this.$refs);
console.log(this.$refs.hello.aaa);
},
···
}
通过控制台即可看到访问成功,通过$refs.hello.aaa即可拿到其中的内容,又或者$refs.p.id拿到p标签的属性。
1.5 子访问父$parent
但是在实际开发中不推荐使用this.$parent.*去获取父组件。因为组件复用性很高,会像吕布一样分不清是哪个爸爸。
更推荐使用props传值,需要就从父传过来不需要就不传。
export default {
···
mounted(){//生命周期函数
console.log(this.$parent);
},
···
}
1.6 子访问根$root
更常用
export default {
···
mounted(){//生命周期函数
console.log(this.$root);
},
···
}
2、插槽
2.1 基本使用
在移动端开发中用的较多。以导航栏举例:大多数移动应用导航栏都是左返回;中页面名称;右三点展开更多。
这时候就可以把左中右看成三个坑(插槽),由父组件来决定我需要填进什么萝卜。
在Content.vue
中通过Vue自定<slot>
作为一个插入内容的占位符。
<div>
<slot></slot>
</div>
在父组件中引用后使用它
<Content><button>按钮</button></Content>
<Content><input type="text"/></Content>
在Content.vue
中,如果父组件没有提供内容的时候默认展示备用内容
<slot name="button"><button>默认按钮</button></slot>
2.2 具名插槽
如果有多个值,同时放入组件进行替换式,一起作为替换元素。当子组件功能复杂,需要具体插槽具体对待。vue3和vue2区别
<slot name="button"></slot>
<slot name="input"></slot>
<slot name="h2"></slot>
vue3做法
在template
元素上使用v-slot
指令
<Content>
<template v-slot:button><button>按钮</button></template>
<template v-slot:input><input type="text"/></template>
<template v-slot:h2><h2>插槽</h2></template>
</Content>
vue2做法
<button slot="button">按钮</button>
2.3 作用域插槽
当App.vue
和Content.vue
中data都存在massage的数据,这种方式将会显示App中的massage。
类似局部变量,父模板中所有内容都是在父级作用域中编制的,这个massage拿不到子的数据。
<template v-slot:button>
<button>按钮{{ massage }}</button>
</template>
作用于插槽是父组件替换插槽的标签,但是数据由子组件提供。vue3和vue2区别
vue3做法
Content.vue
中绑定在slot上的属性值被成为插槽prop
export default {
data() {
return {
message: '你好',
list: [1, 2, 3, 5, 6],
}
}
}
<slot :list="list" :msg="message"></slot>
App.vue
中使用带值的v-slot定义插槽名字,就会得到传过来的{ "list": [ 1, 2, 3, 5, 6 ], "msg": "你好" }
的数据。
这里用过插槽prop的对象命名为slotProps
,可以随便取
<Content>
<template v-slot:default="slotProps">
{{ slotProps }}
</template>
</Content>
通过.list
和.msg
拿到数据。(但实际上推荐Content.vue
中:message="message",msg只是为了区分传过来的是等号前的自定义名字)
展示无序列表
<Content>
<template v-slot:default="slotProps">
<ul>
<li v-for="item in slotProps.list" :key="item">{{ item }}</li>
</ul>
</template>
</Content>
展示有序列表
<ol>
<li v-for="item in slotProps.list" :key="item">{{ item }}</li>
</ol>
3、组件跨级通信
3.1 Provide / Inject
祖先组件和孙子组件跨级通信,实际中一级级传递数据不现实。不论层次结构,父组件provide
提供数据,子组件inject
使用数据。(此处源码演示到了day2)
HelloWorld.vue
祖先组件、Content.vue
父组件、Hello.vue
子组件。从Hello.vue
拿HelloWorld.vue
数据
HelloWorld.vue
中provide以key-value的形式传字符串
export default{
data(){
return{
massage:'HelloWorld'
}
},
provide:{
message:'HelloWorld'
},
···
}
Hello.vue
中inject
export default {
data() {
return {};
},
inject: ['message']
}
<h2>我是Hello组件</h2>
<h2>{{ message }}</h2>
其中需要注意HelloWorld.vue
中,这种写法报错
provide:{
message:this.message
},
如果想要访问组件实例的属性需要以函数的形式
provide(){
return{
massage:this.massage
}
},
3.2 与prop区别
1、不会像prop父组件数据改变子组件也变,Provide / Inject不是响应式。在非组合式API下,传响应式对象or函数形式
传响应式对象
HelloWorld.vue
中
data() {
return {
obj:{message: 'HelloWorld',}
}
},
provide() {
return {obj:this.obj}
},
Hello.vue
中
export default {
···
inject: ["obj"]
}
<h2>{{ obj.message }}</h2>
函数形式
HelloWorld.vue
中
data() {
return {message: 'HelloWorld',}
},
provide() {
return {//箭头函数返回响应式数据
message: () => this.message
}
},
Hello.vue
中
export default {
···
inject: ["message"]
}
<h2>{{ message() }}</h2>
此处为了方便将函数直接卸载{{}}中,这是不推荐的。应该使用computed
属性