Vue 3 学习笔记
setup
- 新的option, 所有的组合API函数都在此使用, 只在初始化时执行一次
- 函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用
ref
- 作用: 定义一个数据的响应式
- 语法: const xxx = ref(initValue):
- 创建一个包含响应式数据的引用(reference)对象
- js中操作数据: xxx.value
- 模板中操作数据: 不需要.value
- 一般用来定义一个基本类型的响应式数据
reactive
- 作用: 定义多个数据的响应式
- const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象
- 响应式转换是“深层的”:会影响对象内部所有嵌套的属性
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
1 2 3 4 5 6 7 8 9 10 11 12 13
| const obj = { name: "xiao", age: 18, wife: { name: "xiaohong", age: 20, cars: ["1", "2", "3"], }, };
const user = reactive<any>(obj);
|
注:直接使用目标对象的方式来更新目标对象中的成员的值,是不行的,只能使用代理对象的方式来更新数据(响应式数据)
1 2 3 4 5 6 7 8 9 10 11
| const updateUser = () => { user.name += "866hhhh"; user.gender="男" delete user.age; };
|
比较Vue2与Vue3的响应式
vue2的响应式
- 核心:
- 对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截)
- 数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
1 2 3 4
| Object.defineProperty(data, 'count', { get () {}, set () {} })
|
- 问题
- 对象直接新添加的属性或删除已有属性, 界面不会自动更新
- 直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {}
Vue3的响应式
- 核心:
- 通过 Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等…
- 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
- Proxy 是深层次的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <script type="text/javascript"> const user = { name: '为冒', age: 23, wife: { name: "耐俭预", age: 19, } } const proxyUser = new Proxy(user, { get(target, property) { console.log("get方法"); return Reflect.get(target, property) }, set(target, property,val) { console.log("set方法"); return Reflect.set(target, property,val) }, deleteProperty(target, property){ console.log("deleteProperty方法"); return Reflect.deleteProperty(target, property) } })
console.log(proxyUser.name); proxyUser.name = "hhhhh" console.log(user); delete proxyUser.name console.log(user); proxyUser.wife.name="当之贼" console.log(user); </script>
|
setup细节
- setup执行的时机
- 在beforeCreate之前执行(一次), 此时组件对象还没有创建
- this是undefined, 不能通过this来访问data/computed/methods / props
- 其实所有的composition API相关回调函数中也都不可以
- setup的返回值
- 一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法
- 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性
- 返回对象中的方法会与methods中的方法合并成功组件对象的方法
- 如果有重名, setup优先
- 注意:
- 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods
- setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <template> <h2>App</h2> <p>msg: {{msg}}</p> <button @click="fn('--')">更新</button>
<child :msg="msg" msg2="cba" @fn="fn"/> </template>
<script lang="ts"> import { reactive, ref, } from 'vue' import child from './child.vue'
export default {
components: { child },
setup () { const msg = ref('abc')
function fn (content: string) { msg.value += content } return { msg, fn } } } </script>
|
- setup的参数
- setup(props, context) / setup(props, {attrs, slots, emit})
- props: 包含props配置声明且传入了的所有属性的对象,里面有父级向子组件传递的数据,并且是在子级组件中使用props接收到的所有属性
- attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs
- slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
- emit: 用来分发自定义事件的函数, 相当于 this.$emit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| <template> <div> <h3>{{n}}</h3> <h3>{{m}}</h3> <h3>msg: {{msg}}</h3> <h3>msg2: {{$attrs.msg2}}</h3> <slot name="xxx"></slot> <button @click="update">更新</button> </div> </template>
<script lang="ts">
import { ref, defineComponent } from 'vue'
export default defineComponent({ name: 'child',
props: ['msg'],
emits: ['fn'],
data () { console.log('data', this) return { } },
beforeCreate () { console.log('beforeCreate', this) },
methods: { },
setup (props, {attrs, emit, slots}) {
console.log('setup', this) console.log(props.msg, attrs.msg2, slots, emit)
const m = ref(2) const n = ref(3)
function update () {
m.value += 2 n.value += 2
emit('fn', '++') }
return { m, n, update, } }, }) </script>
|
reactive与ref-细节
- 是Vue3的 composition API中2个最重要的响应式API
- ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式)
- 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象
- ref内部: 通过给value属性添加getter/setter来实现对数据的劫持
- reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据
- ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <template> <div> <h2>reactive和ref细节问题</h2> <h3>m1:{{ m1 }}</h3> <h3>m2:{{ m2 }}</h3> <h3>m3:{{ m3 }}</h3> <hr /> <button @click="update">update</button> </div> </template>
<script lang="ts"> import { defineComponent, reactive, ref } from "vue";
export default defineComponent({ setup() { const m1 = ref("abc"); console.log(m1); const m2 = reactive({ name: "m2", wife: { name: "m2-wife" } }); const m3 = ref({ name: "m3", wife: { name: "m3-wife" } }); console.log(m3); const update = () => { m1.value += "====="; m2.wife.name += "----"; m3.value.wife.name += "?????"; };
return { m1, m2, m3, update }; }, }); </script>
|
计算属性与监视
- computed函数:
- 与computed配置功能一致
- 只有getter
- 有getter和setter
- watch函数
- 与watch配置功能一致
- 监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
- 默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
- 通过配置deep为true, 来指定深度监视
- watchEffect函数
- 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
- 默认初始时就会执行第一次, 从而可以收集需要监视的数据
- 监视数据发生变化时回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| <template> <div> <h2>计算属性与监视</h2> <fieldset> <legend>姓名操作</legend> 姓氏:<input type="text" placeholder="请输入姓氏" v-model="user.firstName"/><br /> 名字:<input type="text" placeholder="请输入名字" v-model="user.lastName"/> </fieldset> <fieldset> <legend>计算属性与监视的演示</legend> 姓名:<input type="text" placeholder="显示姓名" v-model="fullName1"/><br /> 姓名:<input type="text" placeholder="显示姓名" v-model="fullName2"/><br /> 姓名:<input type="text" placeholder="显示姓名" v-model="fullName3" /> </fieldset> </div> </template>
<script lang="ts"> import { computed, defineComponent, reactive, ref, watch, watchEffect, } from "vue";
export default defineComponent({ setup() { const user = reactive({ firstName: "东方", lastName: "不败", }); const fullName1 = computed(() => { return user.firstName + "_" + user.lastName; }); const fullName2 = computed({ get() { return user.firstName + "_" + user.lastName; }, set(val: string) { const name = val.split("_"); user.firstName = name[0]; user.lastName = name[1]; }, }); const fullName3 = ref(""); watch(user,({ firstName, lastName }) => { fullName3.value = firstName + "_" + lastName; }, { immediate: true, deep: true } );
watchEffect(() => { const names = fullName3.value.split("_"); user.firstName = names[0]; user.lastName = names[1]; });
watch([()=>user.firstName, ()=>user.lastName],() => { console.log("======="); }); return { user, fullName1, fullName2, fullName3 }; }, }); </script>
<style scoped> </style>
|
生命周期
vue2.x的生命周期
vue3.x的生命周期
- vue2.x的
beforeDestroy
和destroyed
在vue3中更名为beforeUnmount
和unmounted
beforeDestroy
=>beforeUnmount
destroyed
=>unmounted
与 2.x 版本生命周期相对应的组合式 API
beforeCreate
-> 使用 setup()
created
-> 使用 setup()
beforeMount
-> onBeforeMount
mounted
-> onMounted
beforeUpdate
-> onBeforeUpdate
updated
-> onUpdated
beforeDestroy
-> onBeforeUnmount
destroyed
-> onUnmounted
errorCaptured
-> onErrorCaptured
新增的钩子函数
组合式 API 还提供了以下调试钩子函数:
- onRenderTracked
- onRenderTriggered
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| <!-- * @Author: 橘子味雪糕 * @Date: 2021-05-04 17:25:01 * @LastEditTime: 2021-05-05 17:15:05 * @FilePath: \blogd:\WorkingArea\VScodeWorkspace\vue3-learn\vue3_study_cli\src\components\child.vue --> <template> <div> <h2>子级组件</h2> <h4>msg:{{ msg }}</h4> <button @click="update">update</button> </div> </template>
<script lang="ts"> import { defineComponent, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated, ref } from "vue";
export default defineComponent({ beforeCreate() { console.log("2.x===>beforeCreate"); }, created() { console.log("2.x===>created"); }, beforeMount() { console.log("2.x===>beforeMount"); }, mounted() { console.log("2.x===>mounted"); }, beforeUpdate() { console.log("2.x===>beforeUpdate"); }, updated() { console.log("2.x===>updated"); }, beforeUnmount() { console.log("2.x===>beforeDestroy"); }, unmounted() { console.log("2.x===>destroyed"); }, setup() { console.log("%c 3.0===>setup",'color:blue;'); const msg = ref("abc"); const update = () => { msg.value += "hhhhh"; };
onBeforeMount(()=>{ console.log("%c 3.0===>onBeforeMount",'color:blue;'); }) onMounted(()=>{ console.log("%c 3.0===>onMounted",'color:blue;'); }) onBeforeUpdate(()=>{ console.log("%c 3.0===>onBeforeUpdate",'color:blue;'); }) onUpdated(()=>{ console.log("%c 3.0===>onUpdated",'color:blue;'); }) onBeforeUnmount(()=>{ console.log("%c 3.0===>onBeforeUnmount",'color:blue;'); }) onUnmounted(()=>{ console.log("%c 3.0===>onUnmounted",'color:blue;'); }) return { msg, update }; }, }); </script>
<style scoped>
</style>
|
自定义hook函数
- 使用Vue3的组合API封装的可复用的功能函数
- 自定义hook的作用类似于vue2中的mixin技术
- 自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { onBeforeUnmount, onMounted, ref } from "vue"
export default function(){ const x=ref(-1) const y=ref(-1) onMounted(()=>{ window.addEventListener('click',clickHandler) }) const clickHandler=(event:MouseEvent)=>{ x.value=event.pageX y.value=event.pageY console.log(event); } onBeforeUnmount(()=>{ window.removeEventListener('click',clickHandler) }) return {x,y} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <template> <div> <h2>自定义hook函数</h2> <h2>x:{{ x }}</h2> <h2>y:{{ y }}</h2> <h3 v-if="loading">加载中...</h3> <h3 v-else-if="errorMsg">错误信息:{{ errorMsg }}</h3> <h3 v-else>数据:{{ data }}</h3> </div> </template>
<script lang="ts"> import { defineComponent, watch } from "vue"; import useMousePosition from "./hooks/useMousePosition"; import useRequest from "./hooks/useRequest";
interface AddressData { id: number; name: string; age: number; }
export default defineComponent({ setup() { const { x, y } = useMousePosition(); const { loading, errorMsg, data } = useRequest<AddressData[]>( "http://localhost:3000/data/list.json" );
watch(data, () => { console.log(data.value.length); });
return { x, y, loading, errorMsg, data }; }, }); </script>
<style scoped> </style>
|
toRefs
把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref
应用: 当从合成函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用
问题: reactive 对象取出的所有属性值都是非响应式的
解决: 利用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <template> <div> <h2>toRefs的使用</h2> <!-- <h3>name:{{user.name}}</h3> <h3>age:{{user.age}}</h3> --> <h3>name:{{name}}</h3> <h3>age:{{age}}</h3> </div> </template>
<script lang="ts"> import { defineComponent, reactive, toRef, toRefs } from "vue";
export default defineComponent({ setup() { const user = reactive({ name: "阿斯达", age: 18, }); const {name,age}=toRefs(user) setInterval(()=>{ name.value+='=====' },1000) return { name, age, }; }, }); </script>
<style scoped> </style>
|
ref获取元素
利用ref函数获取组件中的标签元素
功能需求: 让输入框自动获取焦点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <template> <div> <h2>ref的另一个作用:获取页面元素</h2> <input type="text" ref="inputRef"> </div> </template>
<script lang="ts"> import { defineComponent,onMounted,ref } from "vue";
export default defineComponent({ setup() { const inputRef=ref<HTMLElement|null>(null) onMounted(()=>{ inputRef.value&&inputRef.value.focus() }) return {inputRef}; }, }); </script>
<style scoped> </style>
|
Composition API(其它部分)
shallowReactive 与 shallowRef
- shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式)
- shallowRef: 只处理了value的响应式, 不进行对象的reactive处理
- 什么时候用浅响应式呢?
- 一般情况下使用ref和reactive即可
- 如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
- 如果有一个对象数据, 后面会产生新的对象来替换 ===> shallowRef
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| <template> <div> <h2>shallowReactive 与 shallowRef</h2> <h3>m1:{{ m1 }}</h3> <h3>m2:{{ m2 }}</h3> <h3>m3:{{ m3 }}</h3> <h3>m4:{{ m4 }}</h3> <hr> <button @click="update">update</button> </div> </template>
<script lang="ts"> import { defineComponent, reactive, ref, shallowReactive, shallowRef, } from "vue";
export default defineComponent({ setup() { const m1 = reactive({ name: "vue", age: 1, cart: { name: "rafaBenitez", color: "red", }, }); const m2 = shallowReactive({ name: "vue", age: 1, cart: { name: "rafaBenitez", color: "red", }, }); const m3 = ref({ name: "vue", age: 1, cart: { name: "rafaBenitez", color: "red", }, }); const m4 = shallowRef({ name: "vue", age: 1, cart: { name: "rafaBenitez", color: "red", }, }); const update=()=>{ } return { m1, m2, m3, m4, update }; }, }); </script>
<style scoped> </style>
|
readonly 与 shallowReadonly
- readonly:
- 深度只读数据
- 获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。
- 只读代理是深层的:访问的任何嵌套 property 也是只读的。
- shallowReadonly
- 浅只读数据
- 创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换
- 应用场景:
- 在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <template> <div> <h2>readonly 与 shallowReadonly</h2> <h3>user:{{ user }}</h3> <h3>user1:{{ user1 }}</h3> <hr /> <button @click="update">update</button> </div> </template>
<script lang="ts"> import { defineComponent, reactive, readonly, shallowReadonly } from "vue";
export default defineComponent({ setup() { const user = reactive({ name: "米娅", age: 20, car: { name: "艺画开天", age: 3, }, }); const user1=readonly(user) const update = () => { user1.car.name += "===="; }; return { user,user1, update }; }, }); </script>
<style scoped> </style>
|
toRaw 与 markRaw
- toRaw
- 返回由
reactive
或 readonly
方法转换成响应式代理的普通对象。
- 这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发界面更新。
- markRaw
- 标记一个对象,使其永远不会转换为代理。返回对象本身
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类实例或 Vue 组件对象。
- 当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <template> <div> <h2>toRaw 与 markRaw</h2> <h3>user:{{ user }}</h3> <hr /> <button @click="testToRaw">test-toRaw</button> <button @click="testMarkRaw">test-markRaw</button> </div> </template>
<script lang="ts"> import { defineComponent, markRaw, reactive, toRaw } from "vue";
interface UserInfo { name: string; age: number; likes?: string[]; }
export default defineComponent({ setup() { const user = reactive<UserInfo>({ name: "小", age: 10 }); const testToRaw = () => { const user1 = toRaw(user); user1.name += "==="; console.log("hhhhh"); }; const testMarkRaw = () => { const likes = ["1", "2"]; user.likes = markRaw(likes); setInterval(() => { user.likes[0] += "===="; console.log("kkkk"); }, 1000); }; return { user, testToRaw, testMarkRaw }; }, }); </script>
<style scoped> </style>
|
toRef
- 为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的
- 区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响
- 应用: 当要将 某个prop 的 ref 传递给复合函数时,toRef 很有用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <template> <div> <h2>toRef的使用</h2> <h3>state:{{ state }}</h3> <h3>age:{{ age }}</h3> <h3>money:{{ money }}</h3> <hr /> <button @click="update">update</button> <Child :age="age"/> </div> </template>
<script lang="ts"> import { defineComponent, reactive, toRef, ref } from "vue"; import Child from "./components/child.vue"; export default defineComponent({ components: { Child }, setup() { const state = reactive({ age: 5, money: 100, }); const age = toRef(state, "age"); const money = ref(state.money); console.log(age); console.log(money); const update = () => { state.age+=2 console.log("sss"); }; return { state, age, money, update, }; }, }); </script>
<style scoped> </style>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <!-- * @Author: 橘子味雪糕 * @Date: 2021-05-07 14:07:38 * @LastEditTime: 2021-05-07 14:09:54 * @FilePath: \blogd:\WorkingArea\VScodeWorkspace\vue3-learn\vue3_study_vite2\src\components\child.vue --> <template> <h2>Child</h2> <h3>{{ age }}</h3> <h3>{{ length }}</h3> </template>
<script lang="ts"> import { computed, defineComponent, Ref, toRef } from "vue";
const component = defineComponent({ props: { age: { type: Number, require: true, }, },
setup(props, context) { const length = useFeatureX(toRef(props, "age"));
return { length, }; }, });
function useFeatureX(age: Ref) { const lenth = computed(() => age.value.toString().length);
return lenth; }
export default component; </script>
|
customRef
- 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
- 需求: 使用 customRef 实现 debounce 的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| <template> <div> <h2>customRef的使用</h2> <input type="text" v-model="keyword" /> <p>{{ keyword }}</p> </div> </template>
<script lang="ts"> import { customRef, defineComponent, ref } from "vue";
function useDebouncedRef<T>(value: T, delay = 200) { let timeOutId: number; return customRef((track, trigger) => { return { get(){ track() return value }, set(newValue:T){ clearTimeout(timeOutId) timeOutId=setTimeout(()=>{ value=newValue trigger() },delay) } }; }); }
export default defineComponent({ setup() { const keyword = useDebouncedRef("abc", 500); return { keyword, }; }, }); </script>
<style scoped> </style>
|
provide 与 inject
- provide
和
inject提供依赖注入,功能类似 2.x 的
provide/inject
- 实现跨层级组件(祖孙)间通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <template> <h1>父组件</h1> <p>当前颜色: {{color}}</p> <button @click="color='red'">红</button> <button @click="color='yellow'">黄</button> <button @click="color='blue'">蓝</button> <hr> <Son /> </template>
<script lang="ts"> import { provide, ref } from 'vue'
import Son from './Son.vue' export default { name: 'ProvideInject', components: { Son }, setup() { const color = ref('red')
provide('color', color)
return { color } } } </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <template> <div> <h2>子组件</h2> <hr> <GrandSon /> </div> </template>
<script lang="ts"> import GrandSon from './GrandSon.vue' export default { components: { GrandSon }, } </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <h3 :style="{color}">孙子组件: {{color}}</h3> </template>
<script lang="ts"> import { inject } from 'vue' export default { setup() { const color = inject('color')
return { color } } } </script>
|
响应式数据的判断
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由
reactive
创建的响应式代理
- isReadonly: 检查一个对象是否是由
readonly
创建的只读代理
- isProxy: 检查一个对象是否是由
reactive
或者 readonly
方法创建的代理
Composition API VS Option API
Option API的问题
- 在传统的Vue OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 ,滚动条反复上下移动
使用Compisition API
- 我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起