概述
在使用nestjs梳理RBAC构架,顺便把前端框架升到vue3,详细看了下Vue3 Composition API,有点意思
Composition API
1.ref
- ref可以代理字符串、数字、boolean等基本类型值
- ref声明的值需要通过.value去改变
- ref目的是为了引用原始类型值,但仍然可以引用非基本类型值例如对象
- ref 本质也是reactive 可以简单地把 ref(1) 理解为这个样子 reactive({value: 1})
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
| //用法一 代理基本类型 import { ref } from 'vue'
const refVal = ref(1)
const add = () => { refVal.value++ //值改变,视图更新 }
//用法二 const refObj = ref({ foo: 1 })
const add = () => { //需要通过.value去访问 refObj.value.foo = 2 //值改变,触发视图更新 }
//用法三 //可以通过ref代理某个对象下面的值,复制修改响应式数据不影响原对象 const obj = { foo: 1 }
const refVal = ref(obj.foo)
const add = () => { refVal.value++ console.log(refVal.value) //值改变,视图更新 console.log(obj.foo) //值不变 }
|
2.reactive
- reactive接受一个可代理的对象,但不能是字符串、数字、boolean等基本类型值
- reactive不需要通过.value去访问属性值
1 2 3 4 5 6 7
| import { reactive } from 'vue'
const obj = reactive({ foo: 1 })
const add = () => { obj.foo++ //值改变,视图更新 }
|
3.shallowReactive
- 用法同reactive,用于定义一个浅响应数据只代理第一层,当数据结构比较复杂时,每层都用proxy代理消耗性能
1 2 3 4 5 6 7 8 9
| import { shallowReactive } from 'vue'
const obj = shallowReactive({ foo: { bar: 1 } })
const add = () => { obj.foo.bar = 2 //值改变,不触发视图更新 obj.foo = { bar: 2 } //值改变,视图更新 }
|
4.readonly
1 2 3 4 5 6 7 8
| import { readonly } from 'vue'
const obj = readonly({ text: 'hi' })
const add = () => { obj.text = 'hello' //报错 }
|
5.shallowReadonly
1 2 3 4 5 6 7 8 9
| import { shallowReadonly } from 'vue'
const obj = shallowReadonly({ foo: { bar: 1 } })
const add = () => { obj.foo = { bar: 2 } //报错 obj.foo.bar = 2 //有效 }
|
6.toRef
创建一个ref类型数据, 并和以前的数据关联
相当于引用, 修改响应式数据会影响原始数据
第一个参数为 obj 对象;第二个参数为对象中的属性名
应用场景:如果想让响应式数据和原始的数据关联起来, 并且更新响应式数据之后还不想更新UI, 那么就可以使用toRef
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| //数据发生改变, 视图也不会自动更新
const obj = { foo: 1 }
//const obj = reactive({ foo: 1}) //reactive创建的对象会触发视图更新
const refVal = toRef(obj, 'foo')
const add = () => { refVal.value++ console.log(refVal.value) //值改变,视图不更新 console.log(obj.foo) //值改变,视图不更新 }
|
7.toRefs
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
| //用法一
<template> <p @click="add">{{ foo }}</p> </template>
import { reactive, toRefs } from 'vue'
const obj = reactive({ foo: 1 })
return{ ...toRefs(obj) //将obj里的每个属性转化为ref响应式数据 }
//用法二
//批量创建ref类型数据, 并和以前数据关联,不触发视图更新
import { reactive, toRefs } from 'vue'
const obj = { foo: 1, num: 1 } //const obj = reactive({ foo: 1, num: 1 }) //reactive创建的对象会触发视图更新 const state = toRefs(obj)
const add = () => { state.foo.value = 2 state.num.value = 2 console.log(state.foo.value) // 2 值改变,视图不更新 console.log(obj.foo) // 2 值改变,视图不更新 }
|
8.shallowRef
这是一个浅层的 ref ,只代理.value 可用于优化性能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { shallowRef, triggerRef } from 'vue'
const obj = shallowRef({ foo: 1 })
//shallowRef只代理 ref 对象本身,也就是说只有 .value 是被代理的,而 .value 所引用的对象并没有被代理
const add = () => { obj.value.foo = 2 //值改变,视图不更新 triggerRef(obj) // 可通过修改值后立即驱动视图更新 obj.value = { foo: 2 } //值改变,视图更新 }
|
9.unref
unref接收一个值,如果这个值是 ref 就返回 .value,否则原样返回
10.markRaw
markRaw 方法可以将原始数据标记为非响应式的,即使用 ref 或 reactive 将其包装,仍无法实现数据响应式,其接收一个参数,即原始数据,并返回被标记后的数据
markRaw 函数所做的事情,就是在数据对象上定义 __v_skip 属性,从而跳过代理
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { markRaw, reactive } from 'vue'
//通过markRow代理过的对象,不会触发视图更新 //markRow可用来数据改变但不需要视图改变的情况,用于提升性能。 const obj = { foo: 1 } const obj2 = markRaw(obj) const state = reactive(obj2)
const add = () => { state.foo = 2 //值改变,视图不更新 console.log(state) //2 console.log(obj) //2 }
|
11.toRaw
toRaw方法用于拿到原始数据,对原始数据进行修改,不会更新UI界面,
与markRow()方法类似可用于提升性能,不同的是 markRow接收的不是被代理过的响应式数据
toRaw 方法是用于获取 ref 或 reactive 对象的原始数据的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { toRaw, reactive, ref } from 'vue'
//代理reactive对象 const obj = reactive({ foo: 1 }) const obj2 = toRaw(obj)
const add = () => { obj2.foo = 2 //值改变,视图不会更新 }
//代理ref创建的对象
const obj = ref({ foo: 1 }) const obj2 = toRaw(obj.value) //与reactive不同的是,需要用.value去获取原始数据,因为经过Vue处理之后,.value中保存的才是当初创建时传入的那个原始数据
const add = () => { obj2.foo = 2 //值改变,视图不会更新 console.log(obj.value) //输出 { foo: 2 } }
|
12.isRef
用于判断数据是否是ref创建的,Vue3创建ref的时候会增加__v_isRef: true属性来标识ref数据
1 2 3 4 5 6
| import { ref, isRef } from 'vue'
const val = ref(1)
console.log(isRef(val)) //true
|
13.isReactive
判断数据对象是否是 reactive
1 2 3 4 5 6
| import { ref, isReactive } from 'vue'
const obj = reactive({ foo: 1 })
console.log(isReactive(obj)) //true
|
14.isReadonly
判断数据对象是否是readonly只可读
1 2 3 4 5 6
| import { readonly, isReadonly } from 'vue'
const val = readonly({ foo: 1 })
console.log(isReadonly(val)) //true
|
15.isProxy
用于判断对象是否是reactive 或 readonly 创建的代理对象
1 2 3 4 5 6 7 8
| import { readonly, reactive, isProxy } from 'vue'
const obj = reactive({ foo: 1 }) const val = readonly({ foo: 1 })
console.log(isProxy(obj)) //true console.log(isProxy(val)) //true
|
16.computed
用法与Vue2中的computed一样
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
| import { ref, computed } from 'vue'
//写法- const val = ref(1)
//vue3中计算属性的函数中如果只传入一个回调函数,表示的是get const doubule = computed(() => val.value * 2)
const add = () => { val.value = 3 console.log(doubule.value) // 6 需要通过.value去访问 }
//写法2 <template> <input type="text" v-model="doubule"> </template>
const val = ref(1)
const doubule = computed(() => val.value * 2)
const doubule = computed({ get() { //dobule的返回值 return obj.foo * 2 }, set(value) { //写你的逻辑代码 val.value++ obj.foo = val.value } })
|
17.watch
watch监听数据变化,需手动传入监听的数据,返回新值和旧值
与vue2不同的是 vue2需要通过computed计算才会返回新值和旧值,否则返回的都是新值。
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
| import { ref, reactive, watch } from 'vue' //监听ref创建的数据
const val = ref(0)
watch(val,(newVal, oldVal) => { console.log(newVal) //1 输出新值 console.log(oldVal) //0 输出旧值 }, { immediate: false, //是否在初始化监听 deep: false //是否开启深度监听 } ) const add = () => { val.value = 1 //值改变 }
//监听reactive创建的数据,与ref不同的是需要用箭头函数指向要监听的数据 const obj = reactive({ foo: 0 })
watch(() => obj.foo,(newVal, oldVal) => { console.log(newVal) //1 输出新值 console.log(oldVal) //0 输出旧值 }, { immediate: false, //是否在初始化监听 deep: false //是否开启深度监听 } )
const add = () => { obj.foo = 1 //值改变 }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| //监听多个数据源 const val = ref(1)
const obj = reactive({ foo: 1 })
watch([() => obj.foo, val], ([newFoo, newVal], [oldFoo, oldVal]) => { console.log(newFoo, oldFoo) console.log(newVal, oldVal) })
const add = () => { val.value += 2 obj.foo++ }
//watch 接受一个stop
|
18.watchEffect
watchEffect也是监听数据变化。
与watch不同的是:
1.不需要手动传入依赖
2.每次初始化都会执行
3.无法获取到原值,只能得到变化后的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { ref, reactive, watchEffect } from 'vue'
const obj = reactive({ foo: 1 })
const val = ref(0)
watchEffect(() => { console.log(obj.foo) console.log(val.value) })
const add = () => { val.value++ obj.foo++ }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| //watchEffect还接受一个函数作为参数 ,可用于清除副作用 watchEffect(async () => { const data = await fetch(obj.foo) })
//当 obj.foo 变化后,意味着将会再次发送请求,那么之前的请求怎么办呢?是否应该将之前的请求标记为 invalidate
watchEffect(async (onInvalidate) => { let validate = true onInvalidate(() => { validate = false }) const data = await fetch(obj.foo) if (validate){ /* 正常使用 data */ } else { /* 说明当前副作用已经无效了,抛弃即可 */ } })
|
19.defineComponent & PropType
两者都是为了更好的推断TS类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { defineComponent, PropType } from 'vue'
interface Mylist { name: string age: number }
export default defineComponent({ props: { list: Object as PropType<Mylist[]> }, setup(){} })
|
20.生命周期函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| improt{ onBeforeMount onMounted onBeforeUpdate onUpdated onBeforeUnmount onUnmounted onActivated onDeactivated onErrorCaptured } from 'vue' vue3 新增的两个钩子
onRenderTracked onRenderTriggered
export default { onRenderTriggered(e) { debugger // 检查哪个依赖项导致组件重新呈现 } }
|
21.customRef
自定义 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
| //可以用customRef实现一个搜索框防抖
<template> <input type="text" v-model="text"> </template>
const useDebouncedRef = (value: string, delay = 1000) => { let timeout: any /** * customRef回调接受两个参数 * track用于追踪依赖 * trigger用于触发响应 * 回调需返回一个包含get和set方法的对象 */ return customRef((track, trigger) => { return { get() { track() //追踪该数据 return value }, set(newVal: string) { clearTimeout(timeout) timeout = setTimeout(() => { value = newVal trigger() // 数据被修改,更新ui界面 }, delay) } } }) } const text = useDebouncedRef('')
watch(text, async (newText) => { if (!newText) return void 0 console.log(newText) //停止输入1秒后输出。 })
return{ text }
|
22.defineProps & defineEmit
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
| //在setup中直接用于接受props和emit,可做ts类型推导 <script setup lang="ts"> import { defineProps, defineEmit } from 'vue'
const props = defineProps<{ foo: number, age?: number, }>()
//当有多个事件且参数不同时 const emit = defineEmit<{ (e: 'close', id: number): void (e: 'show', name: string, age: number): void }>()
emit('close', 1) emit('show', '1', 2)
//参数相同时 const emit = defineEmit<(e: 'close' | 'show', id: number) => void>()
emit('close', 1) emit('show', 1) </script>
|
23.defineAsyncComponent
1 2 3 4 5 6 7 8 9 10
| //用于引入组件
<script setup lang="ts"> import { defineAsyncComponent } from 'vue' const AsyncShow = defineAsyncComponent( () => import('@/components/AsyncShow.vue') ) </script>
|
24.script vars
支持将组件状态驱动的 CSS 变量注入到“单个文件组件”样式中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <template> //颜色改变 <p class="text">hello</p> </template> <script setup lang="ts"> const color = '#3b6af9' </script>
<style lang="scss" scoped> .text { color: v-bind(color); } </style>
|
25.provide && inject
与 Vue2中的 provide 和 inject 作用相同,只不过在Vue3中需要手动从 vue 中导入
这里简单说明一下这两个方法的作用:
provide :向子组件以及子孙组件传递数据。接收两个参数,第一个参数是 key,即数据的名称;第二个参数为 value,即数据的值
inject :接收父组件或祖先组件传递过来的数据。接收一个参数 key,即父组件或祖先组件传递的数据名称
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
| // A.vue <script> import {provide} from 'vue' export default { setup() { const obj= { name: '前端印象', age: 22 } // 向子组件以及子孙组件传递名为info的数据 provide('info', obj) } } </script> // B.vue <script> import {inject} from 'vue' export default { setup() { // 接收A.vue传递过来的数据 inject('info') // {name: '前端印象', age: 22} } } </script> // C.vue <script> import {inject} from 'vue' export default { setup() { // 接收A.vue传递过来的数据 inject('info') // {name: '前端印象', age: 22} } } </script>
|
26.getCurrentInstance
获取当前实例,和vue2中的this相同,用于setup函数中(不建议使用)
1 2 3 4
| import { getCurrentInstance } from 'vue' const { ctx } = getCurrentInstance() console.log(ctx)
|
27.vue-router里的hooks
1 2 3 4 5 6 7
| import { useRoute, useRouter } from 'vue-router' const route = useRoute() const router = useRouter() console.log(route.params.id) router.push('/xxx/xxx')
|
28.vuex 里的 hooks
1 2 3 4 5
| import { useStore } from 'vue-router'
const store = useStore()
|