精读《Vue3.0 Function API》
1.引言
Vue3.0的发布引起了轩然大波,让我们解读下它的functionapiRFC详细了解一下Vue团队是怎么想的吧!
首先官方回答了几个最受关注的问题:
Vue3.0是否有breakchange,就像Python3/Angular2一样?
不,100%兼容Vue2.0,且暂未打算废弃任何API(未来也不)。之前有草案试图这么做,但由于用户反馈太猛,被撤回了。
Vue3.0的设计盖棺定论了吗?
没有呀,这次精读的稿子就是RFC(RequestForComments),翻译成中文就是“意见征求稿”,还在征求大家意见中哦。
这RFC咋这么复杂?
RFC是写给贡献者/维护者的,要考虑许多边界情况与细节,所以当然会复杂很多喽!当然Vue本身使用起来还是很简单的。
Vue本身Mutable+Template就注定了是个用起来简单(约定+自然),实现起来复杂(解析+双绑)的框架。
这次改动很像在模仿React,为啥不直接用React?
首先Template机制还是没变,其次模仿的是Hooks而不是React全部,如果你不喜欢这个改动,那你更不会喜欢用React。
PS:问这个问题的人,一定没有同时理解React与Vue,其实这两个框架到现在差别蛮大的,后面精读会详细说明。
下面正式进入Vue3.0FunctionAPI的介绍。
2.概述
Vue函数式基本Demo:
countis{{count}} plusOneis{{plusOne}}
函数式风格的入口是setup函数,采用了函数式风格后可以享受如下好处:类型自动推导、减少打包体积。
setup函数返回值就是注入到页面模版的变量。我们也可以返回一个函数,通过使用value这个API产生属性并修改:
import{value}from'vue' constMyComponent={ setup(props){ constmsg=value('hello') constappendName=()=>{ msg.value=`hello${props.name}` } return{ msg, appendName } }, template:`{{msg}}` }
要注意的是,value()返回的是一个对象,通过.value才能访问到其真实值。
为何value()返回的是Wrappers而非具体值呢?原因是Vue采用双向绑定,只有对象形式访问值才能保证访问到的是最终值,这一点类似React的useRef()API的.current规则。
那既然所有value()返回的值都是Wrapper,那直接给模版使用时要不要调用.value呢?答案是否定的,直接使用即可,模版会自动Unwrapping:
constMyComponent={ setup(){ return{ count:value(0) } }, template:`` }
接下来是Hooks,下面是一个使用Hooks实现获得鼠标实时位置的例子:
functionuseMouse(){ constx=value(0) consty=value(0) constupdate=e=>{ x.value=e.pageX y.value=e.pageY } onMounted(()=>{ window.addEventListener('mousemove',update) }) onUnmounted(()=>{ window.removeEventListener('mousemove',update) }) return{x,y} } //inconsumingcomponent constComponent={ setup(){ const{x,y}=useMouse() const{z}=useOtherLogic() return{x,y,z} }, template:`{{x}}{{y}}{{z}}` }
可以看到,useMouse将所有与“处理鼠标位置”相关的逻辑都封装了进去,乍一看与ReactHooks很像,但是有两个区别:
- useMouse函数内改变x、y后,不会重新触发setup执行。
- xy拿到的都是Wrapper而不是原始值,且这个值会动态变化。
另一个重要API就是watch,它的作用类似ReactHooks的useEffect,但实现原理和调用时机其实完全不一样。
watch的目的是监听某些变量变化后执行逻辑,比如当id变化后重新取数:
constMyComponent={ props:{ id:Number }, setup(props){ constdata=value(null) watch(()=>props.id,async(id)=>{ data.value=awaitfetchData(id) }) } }
之所以要watch,因为在Vue中,setup函数仅执行一次,所以不像ReactFunctionComponent,每次组件props变化都会重新执行,因此无论是在变量、props变化时如果想做一些事情,都需要包裹在watch中。
后面还有unwatching、生命周期函数、依赖注入,都是一些语法定义,感兴趣可以继续阅读原文,笔者就不赘述了。
3.精读
对于Vue3.0的FunctionAPI+Hooks与ReactFunctionComponent+Hooks,笔者做一些对比。
Vue与React逻辑结构
ReactFunctionComponent与Hooks,虽然在实现原理上,与Vue3.0存在Immutable与Mutable、JSX与Template的区别,但逻辑理解上有着相通之处。
constMyComponent={ setup(props){ constx=value(0) constsetXRandom=()=>{ x.value=Math.random() } return{x,setXRandom} }, template:` {{x}} ` }
虽然在Vue中,setup函数仅执行一次,看上去与React函数完全不一样(React函数每次都执行),但其实Vue将渲染层(Template)与数据层(setup)分开了,而React合在了一起。
我们可以利用ReactHooks将数据层与渲染层完全隔离:
//类似vue的setup函数 functionuseMyComponentSetup(props){ const[x,setX]=useState(0) constsetXRandom=useCallback(()=>{ setX(Math.random()) },[setX]) return{x,setXRandom} } //类似vue的template函数 functionMyComponent(props:{name:String}){ const{x,setXRandom}=useMyComponentSetup(props) return({x} ) }
这源于JSX与Template的根本区别。JSX使模版与JS可以写在一起,因此数据层与渲染层可以耦合在一起写(也可以拆分),但Vue采取的Template思路使数据层强制分离了,这也使代码分层更清晰了。
而实际上Vue3.0的setup函数也是可选的,再配合其支持的TSX功能,与React真的只有Mutable的区别了:
//这是个Vue组件 constMyComponent=createComponent((props:{msg:string})=>{ return()=>h('div',props.msg) })
我们很难评价Template与JSX的好坏,但为了更透彻的理解Vue与React,需要抛开JSX&Template,Mutable&Immutable去看,其实去掉这两个框架无关的技术选型,React@16与Vue@3已经非常像了。
Vue3.0的精髓是学习了ReactHooks概念,因此正好可以用Hooks在React中模拟Vue的setup函数。
关于这两套技术选型,已经是相对完美的组合,不建议在JSX中再实现类似Mutable+JSX的花样来(因为喜欢Mutable可以用Vue呀):
- Vue:Mutable+Template
- React:Immutable+JSX
真正影响编码习惯的就是Mutable与Immutable,使用Vue就坚定使用Mutable,使用React就坚定使用Immutable,这样能最大程度发挥两套框架的价值。
VueHooks与ReactHooks的差异
先看ReactHooks的简单语法:
const[count,setCount]=useState(0) constsetToOne=()=>setCount(1)
VueHooks的简单语法:
constcount=value(0) constsetToOne=()=>count.value=1
之所以React返回的count是一个数字,是因为Immutable规则,而Vue返回的count是个对象,拥有count.value属性,也是因为VueMutable规则导致,这使得Vue定义的所有变量都类似React中useRef定义变量,因此不存Reactcapturevalue的特性。
关于capturevalue更多信息,可以阅读精读《FunctionVSClass组件》CaputeValue介绍
另外,对于Hooks的值变更机制也不同,我们看Vue的代码:
constComponent={ setup(){ const{x,y}=useMouse() const{z}=useOtherLogic() return{x,y,z} }, template:`{{x}}{{y}}{{z}}` }
由于setup函数仅执行一次,怎么做到当useMouse导致x、y值变化时,可以在setup中拿到最新的值?
在React中,useMouse如果修改了x的值,那么使用useMouse的函数就会被重新执行,以此拿到最新的x,而在Vue中,将Hooks与Immutable深度结合,通过包装x.value,使得当x变更时,引用保持不变,仅值发生了变化。所以Vue利用Proxy监听机制,可以做到setup函数不重新执行,但Template重新渲染的效果。
这就是Mmutable的好处,VueHooks中,不需要useMemouseCallbackuseRef等机制,仅需一个value函数,直观的Mutable修改,就可以实现React中一套Immutable性能优化后的效果,这个是Mutable的魅力所在。
VueHooks的优势
笔者对RFC中对Vue、ReactHooks的对比做一个延展解释:
首先最大的不同:setup仅执行一遍,而ReactFunctionComponent每次渲染都会执行。
Vue的代码使用更符合JS直觉。
这句话直截了当戳中了JS软肋,JS并非是针对Immutable设计的语言,所以Mutable写法非常自然,而Immutable的写法就比较别扭。
当Hooks要更新值时,Vue只要用等于号赋值即可,而ReactHooks需要调用赋值函数,当对象类型复杂时,还需借助第三方库才能保证进行了正确的Immutable更新。
对Hooks使用顺序无要求,而且可以放在条件语句里。
对ReactHooks而言,调用必须放在最前面,而且不能被包含在条件语句里,这是因为ReactHooks采用下标方式寻找状态,一旦位置不对或者Hooks放在了条件中,就无法正确找到对应位置的值。
而VueFunctionAPI中的Hooks可以放在任意位置、任意命名、被条件语句任意包裹的,因为其并不会触发setup的更新,只在需要的时候更新自己的引用值即可,而Template的重渲染则完全继承Vue2.0的依赖收集机制,它不管值来自哪里,只要用到的值变了,就可以重新渲染了。
不会再每次渲染重复调用,减少GC压力。
这确实是ReactHooks的一个问题,所有Hooks都在渲染闭包中执行,每次重渲染都有一定性能压力,而且频繁的渲染会带来许多闭包,虽然可以依赖GC机制回收,但会给GC带来不小的压力。
而VueHooks只有一个引用,所以存储的内容就非常精简,也就是占用内存小,而且当值变化时,也不会重新触发setup的执行,所以确实不会造成GC压力。
必须要总包裹useCallback函数确保不让子元素频繁重渲染。
ReactHooks有一个问题,就是完全依赖Immutable属性。而在FunctionComponent内部创建函数时,每次都会创建一个全新的对象,这个对象如果传给子组件,必然导致子组件无法做性能优化。因此React采取了useCallback作为优化方案:
constfn=useCallback(()=>/*..*/,[])
只有当第二个依赖参数变化时才返回新引用。但第二个依赖参数需要lint工具确保依赖总是正确的(关于为何要对依赖诚实,感兴趣可以移步精读《FunctionComponent入门》-永远对依赖诚实)。
回到Vue3.0,由于setup仅执行一次,因此函数本身只会创建一次,不存在多实例问题,不需要useCallback的概念,更不需要使用lint插件保证依赖书写正确,这对开发者是实实在在的友好。
不需要使用useEffectuseMemo等进行性能优化,所有性能优化都是自动的。
这也是实在话,毕竟Mutable+依赖自动收集就可以做到最小粒度的精确更新,根本不会触发不必要的Rerender,因此useMemo这个概念也不需要了。
而useEffect也需要传递第二个参数“依赖项”,在Vue中根本不需要传递“依赖项”,所以也不会存在用户不小心传错的问题,更不需要像React写一个lint插件保证依赖的正确性。(这也是笔者想对ReactHooks吐槽的点,React团队如何保障每个人都安装了lint?就算装了lint,如果IDE有BUG,导致没有生效,随时可能写出依赖不正确的“危险代码”,造成比如死循环等严重后果)
4.总结
通过对比VueHooks与ReactHooks可以发现,Vue3.0将Mutable特性完美与Hooks结合,规避了一些ReactHooks的硬伤。所以我们可以说Vue借鉴了ReactHooks的思想,但创造出来的确实一个更精美的艺术品。
但ReactHooks遵循的Immutable也有好的一面,就是每次渲染中状态被稳定的固化下来了,不用担心状态突然变更带来的影响(其实反而要注意状态用不变更带来的影响),对于数据记录、程序运行的稳定性都有较高的可预期性。
最后,对于喜欢Mutable的开发者,Vue3.0是你的最佳选择,基于React+Mutable搞的一些小轮子做到顶级可能还不如Vue3.0。对于React开发者来说,坚持你们的Immutable信仰吧,Vue3.0已经将Mutable发挥到极致,只有将ReactImmutable特性发挥到极致才能发挥React的最大价值。
讨论地址是:精读《Vue3.0FunctionAPI》·Issue#173·dt-fe/weekly
到此这篇关于精读《Vue3.0FunctionAPI》的文章就介绍到这了,更多相关Vue3.0FunctionAPI内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!