父传子
单向数据流
单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。(子组件不能修改prop)
- 对象和数组是通过引用传入的,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
vue2语法
基本用法
传递静态或动态 Prop
<!-- 传入静态值 -->
<blog-post title="hai hai hai"></blog-post>
<!-- 传入变量值 -->
<blog-post :title="info.title"></blog-post>
传入一个对象的所有 property
post: {
id: 1,
title: 'My Journey with Vue'
}
<blog-post v-bind="post"></blog-post>
<!-- 等价于 -->
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
props接收
- 数组形式
props: ['propA', 'propB']
- 对象形式
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' }
}
//箭头函数语法,括号()内为js表达式,即return返回的值
//default: ()=>({message: 'hello'})
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].includes(value)
}
}
}
})
- Object,Array,Function等引用类型的默认值为函数形式(防止污染)
非 Prop 的 Attribute
父组件传递的属性,子组件并没有相应prop去接收
默认这些 attribute 会被添加到这个子组件的根元素上
例如:id、class、style等属性,子组件没有显示的用props接收
父组件传递数据:
<HelloWorld :name="name" :age="age" :test="123"></HelloWorld>
子组件接收数据:
没有props接收test,故test为非Prop的Attribute,默认test添加到子组件的根元素上
<template>
<div class="outer">
<div class="box">
<div>
<h1>{{ name }}</h1>
<h1>{{ age }}</h1>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
name: {
type: String,
default: 'none'
},
age: {
type: Number,
default: 0
},
}
}
</script>
指定Attribute绑定的元素
<template>
<div class="outer">
<div class="box">
<div>
<!-- 指定Attribute绑定的元素 -->
<h1 :test="$attrs.test">{{ name }}</h1>
<h1>{{ age }}</h1>
</div>
</div>
</div>
</template>
<script>
export default {
//禁用 Attribute继承
inheritAttrs:false,
props: {
name: {
type: String,
default: 'none'
},
age: {
type: Number,
default: 0
},
}
}
</script>
Vue3语法
基本用法
父组件App.vue
<template>
<div class="App">
<TestA :title="title" :arr="arr" :obj="obj"></TestA>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import TestA from './components/TestA.vue';
const title = ref('ccc')
const arr = ref([1,2,3])
const obj = ref({
id: 1,
gender: 'female'
})
</script>
子组件TestA.vue
- ts语法
<template>
<div class="TestA">
<h1>{{ title }}</h1>
<h1>{{ arr }}</h1>
<h1>{{ obj }}</h1>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
// ts语法
const props = withDefaults(defineProps<{
title: string,
arr: number[],
obj: {id:number,gender:string}
}
>(),{
title: 'defaultTitle',
arr: ()=>[0],
obj: ()=>({id:0,gender:'male'})
})
console.log(props.title);
console.log(props.arr);
console.log(props.obj);
</script>
- js语法
<template>
<div class="TestA">
<h1>{{ title }}</h1>
<h1>{{ arr }}</h1>
<h1>{{ obj }}</h1>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
// js语法
const props = defineProps({
title:{
type:String,
default: 'defaultTitle'
},
arr: {
type: Array,
default: ()=>[0]
},
obj: {
type: Object,
default: ()=>({})
}
})
console.log(props.title);
console.log(props.arr);
console.log(props.obj);
</script>
子传父
Vue2语法
子组件HelloWorld.vue
<template>
<div>
<button @click="handleClick(1)">+1</button>
<button @click="handleClick(2)">+2</button>
<button @click="handleClick(3)">+3</button>
</div>
</template>
<script>
export default {
// 1.自定义事件的声明
// 方式1 数组
// emits: ['add'],
// 方式2 对象(参数验证)
emits: {
add: function (payload) {
// add的值<=1时,抛出事件add
return payload <= 1 ? true : false
},
},
methods: {
handleClick(num) {
// 2.抛出自定义事件add
this.$emit('add', num)
},
},
}
</script>
父组件App.vue
<template>
<div>
<h1>{{ count }}</h1>
<!-- 1.监听自定义事件add -->
<hello-world @add="handleAdd"></hello-world>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
components: { HelloWorld },
data() {
return {
count: 0
}
},
methods: {
// 2.实现自定义事件add的回调函数
handleAdd(num) {
this.count += num
},
},
}
</script>
Vue3语法
子组件A.vue
- js语法
<template>
<div>
<button @click="handleClick(1)">+1</button>
<button @click="handleClick(2)">+2</button>
<button @click="handleClick(3)">+3</button>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
// 1.js语法
const emits = defineEmits(['addNum'])
function handleClick(num) {
// 抛出自定义事件
emits('addNum', num)
}
</script>
- ts语法
<template>
<div>
<button @click="handleClick('addNum',1)">+1</button>
<button @click="handleClick('addNum',2)">+2</button>
<button @click="handleClick('addNum',3)">+3</button>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
// 2.ts语法
const handleClick = defineEmits<{
(e:"addNum", num:number):void
}>()
</script>
父组件App.vue
<template>
<div>
<h1>{{ count }}</h1>
<A @addNum="addNum"></A>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import A from '@/components/A.vue'
const count = ref(100)
function addNum(payload) {
count.value += payload
}
</script>
插槽
provide与inject
Vue2中使用
变为响应式数据:computed函数
Vue3中使用
App.vue
<template>
<div>
<A></A>
<button @click="handleClick">+1</button>
</div>
</template>
<script setup>
import { provide, ref, reactive } from 'vue'
import A from '@/components/A.vue'
// provide的数据默认是非响应式的
// ref或reactive变为响应式
const name = ref("cjc")
const age = ref(0)
provide("name",name)
provide("age", age)
function handleClick() {
age.value++
}
</script>
A.vue
<template>
<div>
<h1>{{ name }}</h1>
<h1>{{ age }}</h1>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, inject } from 'vue'
const name = inject("name", '默认值')
const age = inject("age")
</script>
全局事件总线
使用第三方库mitt,tiny-emitter
mitt
ref 获取DOM或组件实例
vue2
获取具有ref属性的所有DOM元素和组件实例
App.vue
<template>
<div>
<h1 ref="h1Ref">{{ msg }}</h1>
<A ref="ARef"></A>
</div>
</template>
<script>
import A from '@/components/A.vue'
export default {
data() {
return {
msg: 'hi'
}
},
components: { A },
mounted() {
// 获取DOM元素
console.log(this.$refs.h1Ref);
// 获取组件
console.log(this.$refs.ARef);
// 获取组件的根元素 this.$refs.ARef.$el
// 多个根元素时,可以通过DOM方法获取到指定根元素
console.log(this.$refs.ARef.$el.nextSibling);
},
}
</script>
<style scoped></style>
A.vue
<template>
<div>
<h1>{{ msg }}</h1>
</div>
<div>123</div>
</template>
<script>
export default {
data() {
return {
msg: 'A'
}
}
}
</script>
Vue3
App.vue
<template>
<div>
<h1 ref="h1Ref">App</h1>
<TestA ref="TestARef"></TestA>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import TestA from './components/TestA.vue';
let h1Ref = ref<HTMLHeadingElement>()
let TestARef = ref<InstanceType<typeof TestA>>()
onMounted(() => {
// 1. 获取DOM元素
console.log('1:', h1Ref.value);
// 2. 获取组件实例,使用组件中defineExpose暴露出来的数据
console.log('2:', TestARef.value?.foo());
console.log('3:', TestARef.value?.msg);
})
</script>
A.vue
<template>
<div>
<h1>{{ msg }}</h1>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
const msg = ref('A')
function foo(){
return 'A.vue foo'
}
defineExpose({
msg,
foo
})
</script>
<style scoped></style>
vuex、pinia
路由传值
本地存储
总结
- 父传子,父组件传递属性(静态或动态(v-bind绑定)),子组件通过
props
接收 - 子传父,子组件使用
$emit
抛出自定义事件,父组件v-on监听自定义事件并实现回调函数 - 插槽
- 父组件
provide
提供数据,子孙组件inject
注入数据 - 通过全局事件总线
eventBus
进行跨组件值传递 - 通过
$ref
$parent
和$chidren
获取实例进而通信 - 通过
vuex、pinia
进行状态管理 - 路由传值
- localStorage、sessionStorage