一、前奏
1、组合API
- setup函数是组合API的入口函数
setup() {
// 定义一个名称叫count变量, 这个变量的初始值为0
// 这个变量发生改变后, Vue会自动更新UI
let count = ref(0)
// 在组合API中, 如果想定义方法, 不用定义到methods中, 直接定义即可
function myFn() {
alert(123);
}
//在组合API 中定义的变量/方法,要想在外界使用, 必须通过return {xx,xxx}暴露出去
return { count, myFn }
}
-
Composition API 和 Option API 可以混合使用
-
Composition API 本质
相当于将返回的属性/方法注入 到 data/methods中
-
setup执行时机
创建组件实例,然后初始化
props
,紧接着就调用setup
函数。从生命周期钩子的视角来看,它会在beforeCreate
钩子之前被调用 -
Setup 可传入两个参数, 第一个参数为props,第二个参数为一个上下文对象
-
Setup 注意点
- 由于在执行setup函数的时候,还没有执行created生命周期方法, 所以在setup函数中,是无法使用data和methods的
- 由于我们不能在setup函数中使用data和methods,因此Vue为了避免我们错误的使用, 他直接将setup函数中this修改成了undefined
2、reactive
-
什么是reactive?
- reactive是Vue3中提供的实现响应式数据的方法
- 在Vue2中响应式数据是通过defineProperty来实现的,而在Vue3中响应式数据是通过ES6的Proxy来实现的
-
reactive注意点:
-
reactive参数必须是对象(json/arr),如果在创建响应式数据的时候传递的不是一个对象, 将无法实现响应式。
-
如果给reactive传递了其他对象(如Date),默认情况下修改对象, 洁面不会更新, 若想更新,可以通过重新赋值的方式
setup(){ let state = reactive({ time: new Date() }); function myFn() { state.time.setData(state.time.getDate() + 1) // 数据改变,界面不刷新 // 重新赋值 const newTime = new Date(state.time.getTime()); newTime.setDate(state.time.getDate() + 1); state.time = newTime; // 数据改变,界面不刷新 } }
-
3、ref
-
什么是ref?
- ref和reactive一样, 也是用来实现响应式数据的方法
- 由于reactive必须传递一个对象,所以导致在开发中如果我们只想让某个变量实现响应式的时候会非常麻烦,所以Vue3就给我们提供了ref方法,实现对简单值的监听
-
ref本质:
//ref底层的本质其实还是reactive系统会自动根据我们给ref传入的值将它转换成 ref(xx) -> reactive ({value:xx})
-
ref注意点:
- 在Vue中使用ref的值不用通过value获取
- 在JS中使用ref的值必须通过value获取
4、ref和reactive区别:
-
如果在tempLate里使用的是ref类型的数据,那么Vue会自动帮我们添加. value,如果在tempLate里使用的是reactive类型的数据,那么Vue 不会自动帮我们添加。value
-
Vue是如何决定是否需要自动添加.value的
- Vue在解析数据之前,会自动判断这个数据是否是ref类型的,
- 如果是就自动添加 .vaLue,如果不是就不自动添加 .value
-
Vue是如何判断当前的数据是否是ref类型的
- 通过当前数据的__v_ref来判断的
- 如果有这个私有的属性,并且取值为true,那么 就代表是一个ref类型的数据
-
通过isRef,isReactive 判断
5、递归监听
- 默认情况下, 无论是通过ref还是reactive 都是递归监听
- 存在的问题: 如果数据量比较大, 非常消耗性能
6、非递归监听
-
使用 shallowReactive / shallowRef 创建响应式数据是非递归监听的, 只有第一层会被监听
-
注意点: 如果是用过shallowRef创建数据, 那么Vue监听的是.value的变化
-
如何触发非递归监听属性更新界面
- 如果是shallowRef类型数据, 可以通过triggerRef 来触发, Vue3只提供了triggerRef方法, 没有提供triggerReactive的方法,所以如果是reactive类型的数据, 那么是无法主动触发界面更新的
setup(){ // 1. shallowReactive let state = shallowReactive({ a: 'a', gf: { b: 'b', f: { c: 'c', s: { d: 'd' } } } }) function myFn() { state.a = '1'; state.gf.b = '2'; state.gf.f.c = '3'; state.gf.f.s.d = '4'; console.log(state) // Proxy {a: "1", gf: {…}} console.log(state.gf) //{b: "2", f: {…}} console.log(state.gf.f) // {c: "3", s: {…}} console.log(state.gf.f.s) // {d: "4"} } // 2. shallowRef let state = shallowRef({ a: 'a', gf: { b: 'b', f: { c: 'c', s: { d: 'd' } } } }) function myFn() { // state.value.a = '1'; state.value.gf.b = '2'; state.value.gf.f.c = '3'; state.value.gf.f.s.d = '4'; // triggerRef(state); console.log(state) console.log(state.value) console.log(state.value.gf) console.log(state.value.gf.f) console.log(state.value.gf.f.s) } }
-
shallowRef 本质
ref -> reactive ref(10) -> reactive ({value: 10}) shallowRef -> shallowReactive shallowRef(10) -> shallowReactive({value: 10})
7、toRaw
-
ref/reactive 数据类型特点:
每次修改都会被追踪,都会更新UI界面,但是这样其实是非常消耗性能的所以如果我们有一些操作不需 要追踪,不需要更新UI界面,那么这个时候,我们就可以通过toRaw方法拿到它的原始数据,对原始数据进行修改,这样就不会被追踪,这样就不会更新UI界面。
setup() { let obj = { name: 'zs', age: 18 }; let state = reactive(obj); let obj2 = toRaw(state); console.log(obj === state); //false console.log(obj === obj2); //true // state 和 obj 的关系: // 引用关系, state的本质是一个Proxy对象, 在这个Proxy对象中引用了obj function myFn() { // 如果直接修改obj, 那么是无法触发界面的更新的, 只有通过reactive包转之后的对象来修改, 才会触发界面的更新。 obj.name ='aaa'; console.log(obj); // {name: 'aaa', age: 18} console.log(state); // {name: 'aaa', age: 18} } }
8、组合式API之生命周期
* beforeCreate -> use setup()
* created -> use setup()
* beforeMount -> onBeforeMount
* mounted -> onMounted
* beforeUpdate -> onBeforeUpdate
* updated -> onUpdated
* beforeUnmount -> onBeforeUnmount
* unmounted -> onUnmounted
* errorCaptured -> onErrorCaptured
* renderTracked -> onRenderTracked
* renderTriggered -> onRenderTriggered
9、Suspense
- Vue3推出的一个内置的特殊组件
- 如果使用Suspense, 要返回一个Promise
- 使用具名插槽的形式将内容显示,default为我们资源加载完后的展示内容, fallback为等待时展示的内容
</template>
<Suspense>
<template #default>
<div>
<async-show />
<dog-show />
</div>
</template>
<template #fallback>
<h1>Loading !...</h1>
</template>
</Suspense>
</template>
10、Teleport
- vue3 新添加了一个默认的组件就叫 Teleport,我们可以拿过来直接使用,它上面有一个 to 的属性,它接受一个css query selector 作为参数,这就是代表要把这个组件渲染到哪个 dom 元素中
<teleport to="#modal">
<div id="center">
<h1>this is a modal</h1>
</div>
</teleport>
二、Vue3 模块化
1、获取鼠标位置
/* useMousePosition.ts */
import { ref, onMounted, onUnmounted} from 'vue'
function useMousePosition() {
const x = ref(0)
const y = ref(0)
const updateMouse = (e: MouseEvent) => {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
document.addEventListener('click', updateMouse)
})
onUnmounted(() => {
document.removeEventListener('click', updateMouse)
})
return {
x,
y
}
}
export default useMousePosition
2、请求资源加载
-
/* useURLLoader.ts */ import { ref } from 'vue' import axios from 'axios' function useURLLoader<T>(url: string){ const result = ref<T | null>(null) const loading = ref(true) const loaded = ref(false) const error = ref(null) axios.get(url).then((rawData) => { loading.value = false loaded.value = true result.value = rawData.data }).catch(e => { error.value = e loading.value = false }) return { result, loading, error, loaded } } export default useURLLoader
-
Suspense
部分代码
<!-- App.vue --> </template> <Suspense> <template #default> <div> <async-show /> <dog-show /> </div> </template> <template #fallback> <h1>Loading !...</h1> </template> </Suspense> </template> <script lang="ts"> import AsyncShow from "./AsyncShow.vue"; import DogShow from "./DogShow.vue"; export default defineComponent({ components: { AsyncShow, DogShow, }, }); </script>
<!-- AsyncShow.vue --> <template> <h1>{{result}}</h1> </template> <script lang="ts"> import { defineComponent } from 'vue' export default defineComponent({ setup() { return new Promise((resolve) => { setTimeout(() => { return resolve({ result: 42 }) }, 3000) }) } }); </script>
<!-- DogShow.vue --> <template> <img :src="result && result.message" alt=""> </template> <script lang="ts"> import axios from 'axios' import { defineComponent } from 'vue' export default defineComponent({ async setup() { const rawData = await axios.get('https://dog.ceo/api/breeds/image/random') return { result: rawData.data } } }); </script>