时间:2021-07-25 08:38:56 | 栏目:vue | 点击:次
vue3.x已经发布了这么久,相关的生态也慢慢起来了,包括vite这个新的打包工具,在vue3.0学习过程中有一些实用性的api对比,希望能在开发中给大家做个示范,准确的使用对应的api去完成我们的项目开发
要特别说明一下的就是,setup 函数代替了 beforeCreate 和 created 两个生命周期函数,因此我们可以认为它的执行时间在beforeCreate 和 created 之间
Vue2 | Vue3 |
---|---|
beforeCreate | setup |
created | setup |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestory | onBeforeUnmount |
destoryed | onUnmounted |
了解过vue3的小伙伴儿都知道,现在使用都会用到setup函数,关于在setup函数操作数据,我们用例子说明会好一点
reactive 方法是用来创建一个响应式的数据对象,该API也很好地解决了Vue2通过 defineProperty 实现数据响应式的缺陷
用法很简单,只需将数据作为参数传入即可
<template> <div id="app"> <!-- 4. 访问响应式数据对象中的 count --> {{ state.count }} </div> </template> <script> // 1. 从 vue 中导入 reactive import {reactive} from 'vue' export default { name: 'App', setup() { // 2. 创建响应式的数据对象 const state = reactive({count: 3}) // 3. 将响应式数据对象state return 出去,供template使用 return {state} } } </script>
在介绍 setup 函数时,我们使用了 ref 函数包装了一个响应式的数据对象,这里表面上看上去跟 reactive 好像功能一模一样啊,确实差不多,因为 ref 就是通过 reactive 包装了一个对象 ,然后是将值传给该对象中的 value 属性,这也就解释了为什么每次访问时我们都需要加上 .value
我们可以简单地把 ref(obj) 理解为这个样子 reactive({value: obj})
<script> import {ref, reactive} from 'vue' export default { name: 'App', setup() { const obj = {count: 3} const state1 = ref(obj) const state2 = reactive(obj) console.log(state1) console.log(state2) } } </script>
注意: 这里指的 .value 是在 setup 函数中访问 ref 包装后的对象时才需要加的,在 template 模板中访问时是不需要的,因为在编译时,会自动识别其是否为 ref 包装过的
那么我们到底该如何选择 ref 和 reactive 呢?
建议:
<template> <div> <div ref="el">div元素</div> </div> </template> <script> import { ref, onMounted } from 'vue' export default { setup() { // 创建一个DOM引用,名称必须与元素的ref属性名相同 const el = ref(null) // 在挂载后才能通过 el 获取到目标元素 onMounted(() => { el.value.innerHTML = '内容被修改' }) // 把创建的引用 return 出去 return {el} } } </script>
获取元素的操作一共分为以下几个步骤:
补充:设置的元素引用变量只有在组件挂载后才能访问到,因此在挂载前对元素进行操作都是无效的
当然如果我们引用的是一个组件元素,那么获得的将是该组件的实例对象
toRef 是将某个对象中的某个值转化为响应式数据,其接收两个参数,第一个参数为 obj 对象;第二个参数为对象中的属性名
<script> // 1. 导入 toRef import {toRef} from 'vue' export default { setup() { const obj = {count: 3} // 2. 将 obj 对象中属性count的值转化为响应式数据 const state = toRef(obj, 'count') // 3. 将toRef包装过的数据对象返回供template使用 return {state} } } </script>
上面又有个ref,又有个toRef,不是冲突了吗?两个有不一样的功效:
<template> <p>{{ state1 }}</p> <button @click="add1">增加</button> <p>{{ state2 }}</p> <button @click="add2">增加</button> </template> <script> import {ref, toRef} from 'vue' export default { setup() { const obj = {count: 3} const state1 = ref(obj.count) const state2 = toRef(obj, 'count') function add1() { state1.value ++ console.log('原始值:', obj); console.log('响应式数据对象:', state1); } function add2() { state2.value ++ console.log('原始值:', obj); console.log('响应式数据对象:', state2); } return {state1, state2, add1, add2} } } </script>
ref 是对原数据的一个拷贝,不会影响到原始值,同时响应式数据对象值改变后会同步更新视图
toRef 是对原数据的一个引用,会影响到原始值,但是响应式数据对象值改变后会不会更新视图
将传入的对象里所有的属性的值都转化为响应式数据对象,该函数支持一个参数,即 obj 对象
<script> // 1. 导入 toRefs import {toRefs} from 'vue' export default { setup() { const obj = { name: '前端印象', age: 22, gender: 0 } // 2. 将 obj 对象中属性count的值转化为响应式数据 const state = toRefs(obj) // 3. 打印查看一下 console.log(state) } } </script>
返回的是一个对象,对象里包含了每一个包装过后的响应式数据对象
听这个API的名称就知道,这是一个浅层的 reactive,难道意思就是原本的 reactive 是深层的呗,没错,这是一个用于性能优化的API
<script> <template> <p>{{ state.a }}</p> <p>{{ state.first.b }}</p> <p>{{ state.first.second.c }}</p> <button @click="change1">改变1</button> <button @click="change2">改变2</button> </template> <script> import {shallowReactive} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowReactive(obj) function change1() { state.a = 7 } function change2() { state.first.b = 8 state.first.second.c = 9 console.log(state); } return {state} } } </script>
首先我们点击了第二个按钮,改变了第二层的 b 和第三层的 c,虽然值发生了改变,但是视图却没有进行更新;
当我们点击了第一个按钮,改变了第一层的 a 时,整个视图进行了更新;
由此可说明,shallowReactive 监听了第一层属性的值,一旦发生改变,则更新视图
这是一个浅层的 ref,与 shallowReactive 一样是拿来做性能优化的,配合triggerRef ,调用它就可以立马更新视图,其接收一个参数 state ,即需要更新的 ref 对象
shallowReactive 是监听对象第一层的数据变化用于驱动视图更新,那么 shallowRef 则是监听 .value 的值的变化来更新视图的
<template> <p>{{ state.a }}</p> <p>{{ state.first.b }}</p> <p>{{ state.first.second.c }}</p> <button @click="change">改变</button> </template> <script> import {shallowRef, triggerRef} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowRef(obj) console.log(state); function change() { state.value.first.b = 8 state.value.first.second.c = 9 // 修改值后立即驱动视图更新 triggerRef(state) console.log(state); } return {state, change} } } </script>
toRaw 方法是用于获取 ref 或 reactive 对象的原始数据的
<script> import {reactive, toRaw} from 'vue' export default { setup() { const obj = { name: '前端印象', age: 22 } const state = reactive(obj) const raw = toRaw(state) console.log(obj === raw) // true } } </script>
上述代码就证明了 toRaw 方法从 reactive 对象中获取到的是原始数据,因此我们就可以很方便的通过修改原始数据的值而不更新视图来做一些性能优化了
注意: 补充一句,当 toRaw 方法接收的参数是 ref 对象时,需要加上 .value 才能获取到原始数据对象
markRaw 方法可以将原始数据标记为非响应式的,即使用 ref 或 reactive 将其包装,仍无法实现数据响应式,其接收一个参数,即原始数据,并返回被标记后的数据。即使我们修改了值也不会更新视图了,即没有实现数据响应式
<template> <p>{{ state.name }}</p> <p>{{ state.age }}</p> <button @click="change">改变</button> </template> <script> import {reactive, markRaw} from 'vue' export default { setup() { const obj = { name: '前端印象', age: 22 } // 通过markRaw标记原始数据obj, 使其数据更新不再被追踪 const raw = markRaw(obj) // 试图用reactive包装raw, 使其变成响应式数据 const state = reactive(raw) function change() { state.age = 90 console.log(state); } return {state, change} } } </script>
watchEffect 它与 watch 的区别主要有以下几点:
<script> import {reactive, watchEffect} from 'vue' export default { setup() { const state = reactive({ count: 0, name: 'zs' }) watchEffect(() => { console.log(state.count) console.log(state.name) /* 初始化时打印: 0 zs 1秒后打印: 1 ls */ }) setTimeout(() => { state.count ++ state.name = 'ls' }, 1000) } } </script>
没有像 watch 方法一样先给其传入一个依赖,而是直接指定了一个回调函数
当组件初始化时,将该回调函数执行一次,自动获取到需要检测的数据是 state.count 和 state.name
根据以上特征,我们可以自行选择使用哪一个监听器
我们都知道在Vue2的任何一个组件中想要获取当前组件的实例可以通过 this 来得到,而在Vue3中我们大量的代码都在 setup 函数中运行,并且在该函数中 this 指向的是undefined,那么该如何获取到当前组件的实例呢?这时可以用到另一个方法,即 getCurrentInstance
<template> <p>{{ num }}</p> </template> <script> import {ref, getCurrentInstance} from 'vue' export default { setup() { const num = ref(3) const instance = getCurrentInstance() console.log(instance) return {num} } } </script>
instance 中重点关注 ctx 和 proxy 属性,这两个才是我们想要的 this。可以看到 ctx 和 proxy 的内容十分类似,只是后者相对于前者外部包装了一层 proxy,由此可说明 proxy 是响应式的
在Vue2中使用 Vuex,我们都是通过 this.$store 来与获取到Vuex实例,但上一部分说了原本Vue2中的 this 的获取方式不一样了,并且我们在Vue3的 getCurrentInstance().ctx 中也没有发现 $store 这个属性,那么如何获取到Vuex实例呢?这就要通过 vuex 中的一个方法了,即 useStore
// store 文件夹下的 index.js import Vuex from 'vuex' const store = Vuex.createStore({ state: { name: '前端印象', age: 22 }, mutations: { …… }, …… }) // example.vue <script> // 从 vuex 中导入 useStore 方法 import {useStore} from 'vuex' export default { setup() { // 获取 vuex 实例 const store = useStore() console.log(store) } } </script>
然后接下来就可以像之前一样正常使用 vuex 了