早之前一直使用react,最近跳槽了开始用vue3了。算是对两个都有点了解的人,所以记录下使用中的想法。以下的内容都不是依据框架源码得出的,主要是从使用得角度出发。如果有不对的地方请指出。
虽然两个框架都是"数据驱动视图"的理念,但对于如何,何时更新组件两者还是很不一样。首先看一下react
现在大多都已经使用函数式组件了。函数式组件本身是一个会返回JSX形式组件的函数。而触发这个函数运行的有两个方式,一个是父组件的更新,另一个是props或者由useState定义的state发生了更新。虽说是两个,但本质还是一样的。因为函数式组件会形成一个闭包,那么父组件在更新时,其内部所有的东西,包括定义的各种变量和方法都是新的。返回的JSX组件也是新的,那么传递给子组件的props自然也是新值。所以就会引起像是父组件更新触发子组件更新,但本质还是子组件的自身的状态改变了,只是这种状态是外部输入的。
虽说两个组件都强调单向数据流,但如果真的向子组件传递一个对象,子组件去改变这个对象里某个属性值,分别在父子组件中打印这个对象,会发现修改是同步的。即使这个对象是由useState派生出来的。当然,如果直接改变这个对象,父子组件还是不同步
父组件

子组件
从最终打印结果看,父子组件中的obj.value均是最新值。但是这种变化并不会引起组件的更新,因为直接定义的obj对象并不能算是函数式组件的自身状态,而且state的值只能用setState方式去改变。所以父子两个组件函数并没有重新执行。
所以为了组件不必要的更新,react引入了React.memo,useMemo和useCallback。其实就是让传给子组件的props保持不变来避免组件更新。
再接下来就是useCallback了。通常对于useCallback都会讲"监听依赖项改变,执行对应的副作用函数"。这样讲似乎useCallback是主动运行的,只要监听的依赖项改变就会执行。事实上useCallback仍然是函数式组件这个"机器"的一部分。只有在组件这个函数重新执行时,useCallback才会被动的执行,并且判断依赖项是否发生变化依次来决定副作用函数是否执行。useCallback的依赖项可以是useState派生的变量,也可以是useRef派生的,函数式组件定义的,甚至是函数式组件外定义的变量。就像上面的例子中可以加上useCallback来试验这一点。
这样来看react的组件更新像是一个"牵一发而动全身"的系统。遇到react组件不更新或者意外更新的问题,都可以用上述的原理,去看父组件,props或者state有没有发生变化
可能是写多了react,刚开始写vue。我竟然觉得vue组件更新要比react麻烦。react的props类型都是JS最基础的引用类型和基本类型,但vue中多了一个响应式类型,虽然它也是JS中的proxy,但多一种类型算是多一心智负担吧。在项目中遇到过有人把普通对象传给子组件,而且传的层级巨深,并且在子组件加载第一时间就把这个对象里的值更改了。结果就是你会发现,明明不是响应式值,父组件取到的值却和加载时不一样。虽说这本质和框架没有联系,但遇到了还是很让人恼。
对于vue中的template,可以把他看作是一个巨大的watch函数,通过追踪template中的所有用到的变量来执行render函数,进而实现页面刷新。当然这些值得是响应式对象,不管是整个对象,或者只是响应式对象的某个属性。
这就又有一点vue和react的不同,vue组件更新时只是触发了template部分和onUpdated函数,而react是函数式组件,更新是整个函数从上至下全部执行。
vue中还有一点就是插槽。虽然插槽是写在父组件内的,但他的更新是被算在子组件的,所以它会触发子组件的onUpdated而不是父组件的。
所以当你的组件界面出现意料外的不更新或者更新,就需要考虑template内的数据是不是响应式对象。vue提供了onRenderTrack和onRenderTriggered来辅助响应式对象和更新之间的联系。
对于传给子组件的props,和react一样可以传任意类型,可以是一个普通对象,也可以是响应式对象或基本类型。传入普通对象的话,和react一样,可以改变该对象的属性值,但不会触发组件更新。
对于子组件来说,传入的props用defineProps来接收,在新版的vue中,即使
const {a,b,c} = defineProps(['a','b','c'])
这样写依然能保持响应式连接,而如果用
css
const props = defineProps(['a','b','c'])
const {a,b,c} = toRefs(props)
就得用toRefs来解构了
在子组件中props整体是一个响应式对象,其中的各个属性父组件传入的是什么类型,就是什么类型。并且传入的所有props都是只读类型的,vue不允许子组件修改props值
就像上面说的,template可以看作是一个watch函数,而且是一个深度监听。普通的watch是一个浅度的监听,也就是只能监听响应式对象本身的变化,想监听下一级属性的变化只能用deep=true。watchEffect只能是浅度的监听。
对于父子组件传值,最好的实践方式就是忘记这些数据类型,需要哪些值给子组件传入哪些值。如果要保证组件刷新,就要使用响应式。如果希望父子组件都能对这个数据做更改,那就用v-model。对于组件的意外更新,大部分时候都不需要刻意关注,vue用的这套模板系统效率还是很高,即使组件内部触发了更新但不反应在界面上都还可以。
以上就是有关这两个的一点想法,如果后面有新的再更新。有不正确的地方请指出。