A. 解决vue2.x中数据渲染以及vuex缓存的问题
最近在学习Vue.js,把自己遇到的问题做个记录,所以,今天添加一点小笔记。
在项目中遇到两个问题,简单的做个笔记来记录自己解决的问题,可能不是很好的处理办法,欢迎提出,自己还在不断优化中...
第一个是vue在加载页面的时候,会先加载静态资源,这个时候数据还没有请求回来,用户会先看到静态的内容(就是页面固定写死的),过一会才会有数据回来渲染,这体验是很差的,其实解决办法也很简单,就是用vue里的
v-if
来判断请求的数据是否返回...
<div
class="container"
id="app"
v-cloak>
<div
v-if='moneyInMsg.uuid'>
<in-account-msg
:money-in-msg="moneyInMsg"></in-account-msg>
</div>
</div>
这里的
v-if
=
'moneyInMsg.uuid'
就是来判断数据有没有请求回来,如果请求回来就让他显示,没有请求到数据,就让他loading,这样体验就会好很多。在这里还需要注意的是,v-if判断的数据源,是数据返回的字段,如果两个字段只能存在其一的话,可以v-if
='a
||
b'
来判断数据是否成功的返回;还要注意的一点是,不能直接在组件里用v-if判断,也不能直接在根标签里判断,直接嵌套一个div就可以解决,并不影响样式,只做数据是否正常返回的显示作用;
第二个就是在使用vuex时,有数据缓存;我遇到的情况是,在列表页点击进入详情页,返回到列表页,在进入另一个详情页的时候,数据会显示之前的数据,同时页面还在loading(接口返回的数据比较慢),过一会数据返回的时候,才重新渲染页面。可能是自己对vuex理解的不够深入,没有在vuex基础上解决这个问题。虽然曲折的解决了这个问题,但是不够zhuang,但是解决了问题,后期再做优化。
在之前解决的方案中,是进入页面的时候,重新刷新页面,重新请求数据,代码如下:
export
const
refresh
=
(title)
=>
{
document.title
=
title;
let
iframe
=
document.createElement('iframe');
iframe.src
=
require('./mm.jpg');
iframe.setAttribute('style',
'display:none;');
let
loadFn
=
function
()
{
iframe.removeEventListener('load',
loadFn);
document.body.removeChild(iframe);
console.info('Page
Title
IS
'
+
title);
iframe
=
null;
loadFn
=
null;
}
document.body.appendChild(iframe)
iframe.addEventListener('load',
loadFn);
}
但是没有达到预期的效果,依然会出现上面的情况...
丫的,抓狂了...(被别人催的感觉真的不爽...)
网络啊,google啊,都没有遇到这种情况的?找到一个,还是提问的,没有回答的,好吧,还是靠自己。自己动手,丰衣足食啊...
思路是,定义一个参数status为false,当数据没有请求回来,就不显示,也是用上面的方式来判断,一直loading(请求失败,去掉loading),当数据返回的时候,让status为true;使用$nextTick来更新数据...
贴上自己部分的代码作为参考:
<template>
<div
v-if='status
&&
order.name'>
//页面展示的数据
</div>
</template>
<script>
export
default{
data(){
return
{
status:false
}
},
created(){
var
_this
=
this;
this.setDd({res
=>{
_this.$nextTick(function(){
_this.status=
true
});
}})
},
computed:{
...mapGetters({//getter获取的数据})
},
methods:{
...mapActions(['setDd'])
//获取数据的方法
}
}
</script>
处理的方式比较丑陋...,但是实现了想要的效果;这里注意一点就是v-if的判断问题。(v-if='status
&&
order.name')这个用了并且,目的是有数据返回,才能让他显示,如果没有数据,会显示静态的值,数据都为underfind...
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
B. vue2中computed原理
要理解这个,首先要理解vue2的数据响应式原理,因为computed这个API的实现是建立在数据响应式的基础之上的。
这里附上vue响应式原理的地址: vue2数据响应式原理
在vue的watcher实例中配置了lazy,dirty,value属性,就是用来配合实现computed的API。vue在初始化computed时,会给每一个computed属性配置一个watcher,并配置lazy的值为true。在new Watcher时,还会设置dirty为true。由于lazy为true,这个时候并不会执行computed配置的get方法,也就是说不会去计算出这个计算属性的值出来,那么什么时候才计算呢?就是在执行render函数时,会去取计算属性的值,这个时候,会执行计算属性的getter。在getter里面,会先判断dirty的值(为true,则表示当前计算属性的值为脏值,已经过期了,需要重新计算出来;为false则表示当前的值是最新的,不需要重新计算,直接取缓存里面的值就行了,也就是value字段的值)。如果为true,则调用watcher.evaluate方法计算最新的值出来,然后将dirty设为false,把最新的值赋值给value。在计算最新的值时,也就是执行我们在computed对象中配置的相应的get函数。根据之前讲的响应式原理,会先将当前的watcher挂到dep的静态属性target上,然后执行get,在这个过程中,会使用到data中的属性,然后进行依赖收集,将computed的watcher存到data数据对应的dep中去。完了之后,将watcher从targetStack中弹出,这是dep的静态属性target的值又变成了render函数的watcher。以上步骤执行完后,会判断当前dep的target是否还有值,有的话,会执行watcher.depend()。这一步是为了让依赖的data对应的dep中不仅是收集到computed的wathcer,还要收集render函数的watcher,因为当我们依赖的data改变时,不仅要通知到computed的watcher,还要通知render函数的watcher,达到重渲染的目的。然后会把最新的value值返回,这时,render函数里面终于拿到了计算属性的最新值了。假如后面继续用到了这个计算属性,那么在执行计算属性的getter时,跟之前一样,会先判断dirty值,这个时候,dirty的值为false,则直接返回value值,也就是之前说的取缓存的值,不会再次运行一遍计算属性的get函数了。
当某个时候,计算属性依赖的data数据变了,则会先后触发computed的watcher,还有render函数的watcher。在将vue的数据响应式原理时,我们知道,数据改变时,是会触发watcher的update方法。在这个方法里面,首先判断lazy是否为true,这就是针对计算属性设计的。我们可以看看源码片段:
如果为true,则只做了一件非常简单的事,就是把dirty设为了true,然后就没了。这个时候会去执行render函数的watcher.update。也就是把render函数的执行交给了nextTick去管理,这也是之前讲过的。在执行render函数时,又使用到了这个计算属性,那么,则会执行这个计算属性的getter,判断dirty是否为true,由于刚刚在执行computed的wathcer.update时,把dirty设为了true,这个时候肯定会执行watcher.evaluate重新去计算最新的值,也就是执行我们配置在computed里面的get函数。接下来的其实跟前面所讲的都是一样的了。
这里附上关于computed初始化的源码片段:
以上就是个人对于computed原理的理解了。
C. vue2 + Composition API 实践
我们习惯了ES6的对象解构风格,但这在 composition- api 里可能会有陷阱。因为结构可能会让你的响应式对象失去预期中的响应特性。
比如这里,button的click时候,不会得到预期的 count 的增加。因为setup执行返回的 count 并不是响应式的。
也就是说,虽然你的click事件确实的改变了 data.count 的值,但是这个值并没有响应式的去改变其他引用这个值的地方。怎么去验证我们这个解释呢?
我们可以开着chrome的vue插件,定位到你的组件。然后你可以点击一下button,可以看到 count 并没有变化, data 呢?看起来好像也没有变化?这不是不符合逻辑吗?甚至我们在click的回调函数里打印一下发现是有执行data.count+1 这个操作的。
事实是, data.count 确实是执行了的,但是因为不是 reactive 的,所以插件里没有及时更新这个新数据。如果你把插件先切到别的组件上去,再切回来。你就会发现,data.count是符合预期的!
click操作前的插件看到的数据:
我做了2次click后,现在插件里把光标切到别的组件上,再切回来。就可以看到data的变化:
只是引用了 data.count 的地方没有被更新,这就说明引用 data.count 的地方是非 reactive 的。
我们要要做的,就是改造一下引用 data.count 的地方。我们在 setup 里的返回,可以用 computed 和 toRefs 来改造一下返回值。
再看一下toRefs改造后的demo:
第二个问题来了: toRefs 一定是安全应对解构的方案么?
不是的,因为 toRefs 的结构是浅解构的,对于我们demo里的这种简单的对象是work的。但是如果是一个嵌套很深的复杂Object,还是会有解构后响应式断裂的问题。如果数据的层级比较复杂,建议使用 computed 。
watchEffect 很像React里的 useEffect ,是一个副作用函数。用法也基本一致。
watch 的话,接收2个参数。第一个参数是 watch 的target,看ts结构,必须是一个 ref 对象或者 computedRef 对象;第二个参数是watch的回调函数。
React的hooks出来之后,有个很好用的东西就是Context,很像一个微型Rex,可以很好的跨组件传值,尤其是在组件粒度很细的时候,我们的组件间通信频率也会升高。
之前vue2的时代,其实一直有 provide 和 inject 可以用。但是 provide 和 inject 的对象一般是非响应式的。官网是这么记载的:
vue2的时候,我们一般不太注重如何把一个数据变成响应式的(也不是没有办法,比如 Vue.observable(obj) 可以把一个对象变成响应式的。如果我们把这个对象provide出去,那么传递的数据也就一直是响应式的了)。
vue3(或者vue2 + @vue/composition-api )后,我们更多的关注到了数据的 reactive 特性。比如用 ref 或者 reactive 关键字来构造一个响应式的对象。我们如果再用 provide 直接传递一个 reactive 的对象,岂不是可以模拟出类似React的 useContext 这样的结构?
本身vue3(or vue2 + @vue/composition-api )也是支持hooks的。
这样我们就可以按照React hooks的开发习惯去给vue抽hooks了。