1.父组件与子组件
组件中引用其他组件,那么本组件就是父组件,被引用的组件就是子组件,可以层层嵌套;
1.1 组件的引入
在组件中使用其他组件前,是要将要使用的组件引入,在vue中有全局引入和局部引入两种方式;
- 全局引入
- 在项目根目录下的main.js(也就是项目的入口文件,一般文件名都是main.js)中引入;
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 以下两句代码就是引入组件CompB的,这样CompB就可以在全局使用
import CompB from './CompB';
Vue.component(CompB.name,CompB)
// Root
new Vue({
// createElement方法
render: h => h(App), // 接收Vue组件的选项配置,创建一个组件内容,并渲染
}).$mount('#app')
- 局部引用
- 就是在父组件中引入子组件,这样引入的子组件只能在当前父组件中使用;
- import后还需要在components字段默认导出;
<script>
// 1. 引入一个组件
import CompA from './CompA'
export default {
name: 'App',
components: {
CompA
}
}
</script>
1.2 父子组件数据流问题
- 一般框架都遵循单向数据流规则,即数据由父组件传递到子组件,不允许子组件修改父组件的值;
- 看一个数据流变更问题,在父组件App.vue中引入子组件CompC,传递数组outterArr到CompC,想在CompC实现数组数据的修改;
<template>
<div id="app">
<h2>数据流的变更问题</h2>
<CompC :arr="outterArr" :changeFn="changeArr"/>
</div>
</template>
<script>
// 1. 引入一个组件
import CompA from './CompA'
// 数据变更的问题
import CompC from './CompC';
export default {
name: 'App',
components: {
CompC
}
}
</script>
- 此时的CompC
<template>
<div>
展示数据:
<ul>
<li v-for="item in arr">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
arr: {
type: Array,
}
}
}
</script>
- 方法一:直接覆盖覆盖父组件的数据(可以实现效果,但是不建议,而且会有一个vue发出的警告)
<template>
<div>
展示数据:
<ul>
<li v-for="item in arr">
{{ item }}
</li>
</ul>
<button @click="change1">更改数据1</button>
</div>
</template>
<script>
export default {
methods: {
change1() {
this.arr = [];// 不建议覆盖父组件的数据
},
props: {
arr: {
type: Array,
}
}
}
</script>
- 方法二:父组件给子组件传递一个可以改变outterArr数组值的方法,子组件调用父的方法,让其自己改
父组件:
<script>
// 数据变更的问题
import CompC from './CompC';
export default {
methods:{
//向子组件传递的方法
changeArr(){
this.outterArr = [];
}
},
data() {
return {
varText: 'Test',
outterArr:[{id:1},{id:2},{id:3}]
}
},
name: 'App',
components: {
CompC
}
}
</script>
子组件:
<template>
<div>
展示数据:
<ul>
<li v-for="item in arr">
{{ item }}
</li>
</ul>
<button @click="change1">更改数据1</button>
<hr />
<button @click="change2">更改数据2(遵循单向数据流思想)</button>
</div>
</template>
<script>
export default {
methods: {
change1() {
this.arr = [];// 不建议覆盖父组件的数据
},
change2() {
this.$attrs.changeFn();// 调用父的方法,让其自己改
},
props: {
arr: {
type: Array,
}
}
}
</script>
- 方法三:在子组件件内修改数组内部的值(因为直接修改为空,是修改数组值的地址指向,会报错,但是修改数组内的值并没有改变数组的内存指向,所以不会再有警告)
<template>
<div>
展示数据:
<ul>
<li v-for="item in arr">
{{ item }}
</li>
</ul>
<button @click="change1">更改数据1</button>
<hr />
<button @click="change2">更改数据2(遵循单向数据流思想)</button>
<hr />
<button @click="change3">更改数据3</button>
</div>
</template>
<script>
export default {
methods: {
change1() {
this.arr = [];// 不建议覆盖父组件的数据
},
change2() {
this.$attrs.changeFn();// 调用父的方法,让其自己改
},
change3() {
this.arr[0].id = 5;
this.arr[1].id = 15;
this.arr[2].id = 25;
}
},
props: {
arr: {
type: Array,
}
}
}
</script>
- 方法四:在子组件深拷贝一份,然后修改自己拷贝的这一份的值
<template>
<div>
展示数据:
<ul>
<li v-for="item in arr">
{{ item }}
</li>
</ul>
<button @click="change1">更改数据1</button>
<hr />
<button @click="change2">更改数据2(遵循单向数据流思想)</button>
<hr />
<button @click="change3">更改数据3</button>
<hr>
<h2>改变自己的数据</h2>
{{ selfArr }}
<button @click="change4">更改数据4</button>
</div>
</template>
<script>
export default {
methods: {
change1() {
this.arr = [];// 不建议覆盖父组件的数据
},
change2() {
this.$attrs.changeFn();// 调用父的方法,让其自己改
},
change3() {
this.arr[0].id = 5;
this.arr[1].id = 15;
this.arr[2].id = 25;
},
// 方式4: 单独copy一份
change4() {
this.selfArr = []
}
},
data() {
return {
selfArr: JSON.parse(JSON.stringify(this.arr))
}
},
props: {
arr: {
type: Array,
}
}
}
</script>
2. 插槽(slot标签)
2.1 使用全局组件来测试插槽
有以下4中使用方式:
- 默认插槽的内容#default;
- #content1="scope",然后取scope对象中的值scope.num1;
- #content2="{ num2 }",用{}取变量值;
- #content3="{ num1, num2, num3 },一次性传递多个变量值;
- 父组件App.vue(CompB组件在项目根目录下的main.js中全局引用了)
<template>
<div id="app">
<!-- 使用全局组件来测试插槽 -->
<CompB :data="[1]">
// 1. 默认插槽的内容#default
<template #default>
<button>默认插槽的内容(default可以省略template)</button>
</template>
<template #content1="scope">
// 2. #content1="scope",然后取scope对象中的值scope.num1
<button>默认插槽的内容1:{{ scope.num1 }}</button>
</template>
<template #content2="{ num2 }">
// 3. #content2="{ num2 }",用{}取变量值
<button>默认插槽的内容2:{{ num2 }}</button>
</template>
// 4. #content3="{ num1, num2, num3 },一次性传递多个变量值
<template #content3="{ num1, num2, num3 }">
<button>默认插槽的内容3:{{ num1 }} {{ num2 }} {{ num3 }}</button>
</template>
</CompB>
</div>
</template>
- 子组件CompB(接收参数的格式要与父组件传递参数的方式相结合)
<template>
<div>
<h2>传递进来的插槽slot</h2>
<div style="background:red">
// 1. 默认插槽的内容#default
<slot name="default"></slot>
</div>
<div v-for="item in data">
// 2. #content1="scope",然后取scope对象中的值scope.num1
<slot name="content1" :num1="item" ></slot>
<br/>
// 3. #content2="{ num2 }",用{}取变量值
<slot name="content2" :num2="item" ></slot>
<br/>
// 4. #content3="{ num1, num2, num3 },一次性传递多个变量值
<slot name="content3" v-bind="{num1:1,num2:2,num3:3}" ></slot>
</div>
</div>
</template>
<script>
export default {
props:{
data:{
type:Array,
default:()=>[]
}
},
name:'CompB'
}
</script>