前言
本文是【向上俱乐部】博客开通后第一篇文章,心里自知要想高质量输出其实很难,所谓深入浅出,没有深入,怎么能很好地给别人讲明白呢?!但是凡事都有第一步,重要的是要迈开腿,执行力。所以第一篇文章就这样草草登场了。
本人是后端出身,半路自学前端,因为我不想做半拉程序员,我想要的是有一个全局观,所以我就靠着兴趣来到了这虐人的前端领域。众所周知,大家对前端领域的认识一般是门槛低,容易入门做东西,深入就很难了,光一个 css 就让人头疼,更别提我这半路自学前端的新手了。刚接触前端时,光一个模块化就让我喝了几个月的壶。只能说,前端领域是真的杂,面很广,而且技术更迭很快。
有什么办法,来到这个世界上总归要有些东西去突破的,去克服的,要不然有什么意思呢,有兴趣有时间让你折腾这已经就很不错了。废话不多说,上菜。
vue3 运行时
关于 vue 的背景不花时间去过多介绍了,直接进入主题——了解vue3运行时机制。
我们知道一个 vue 应用是一个巨大的组件树撑起来的,也就是 const app = vue3.createApp(RootComponent.vue); app.mount("#target DOM")
。我这里写的是伪代码,大家知道表达的意思即可。也就是说,vue应用,就是在一个目标DOM容器上挂载了一个元素,但是这个元素不是浏览器原生提供的 div
span
等,而是一个由 vue 框架提供运行时能力的组件对象。有了 vue 框架的运行时支持,你就可以自定义组件对象来实现新的元素,就好像浏览器原生提供的一样,达到了自定义扩展浏览器元素的效果。
对,你想的没错,这个自定义组件对象肯定有一定的写法,而且是固定的,要不然 vue 框架里的代码怎么去解析执行呢。还有一点,你想的也对,估计也早就了解过了,vue 操作浏览器DOM对象是通过一个叫做虚拟DOM(或叫虚拟节点)的东西来完成的。目的是什么呢,可能你会说操作这个对象总体上比操作浏览器DOM对象性价比高,也可能会说这样可以隔离目标环境,达到跨平台的目的,都没错,是那么回事,但是这是不是最优的最好的,maybe说不准,技术总会往前迭代的,哪天没浏览器了也有可能,但这是目前技术人员发现并实现的一个机制,也很好地起到了一定的效果,在当下看着是一个合适的选择。
到这,引申出了几个对象:
组件对象:用户自定义元素,什么形式呢,在 js 里,除了对象、函数,还能有啥可以代表一个东西嘛,没了,就是一个js对象(函数)而已,它里面应该有很多需要的属性,比如名字,得起个名字吧,数据呢,也得有,这个组件对象想要生成什么样子的元素效果呢,渲染函数自然不可少吧,渲染出啥呢?直接操作DOM api?不是的,渲染出中间的一个对象叫虚拟节点,然后 vue 里的代码会根据这个虚拟节点信息去操作DOM api。
虚拟节点:它应该也是一个js对象,源代码里单词叫VNode。这个对象上有好多属性,不管是啥样子的,它得有想要表达浏览器DOM元素效果的所有信息吧,比如标签属性,是
div
还是span
,有 children 属性吧,代表有哪些子元素啊。。。等等,vue 通过这个对象来表达一个浏览器元素,当有新的改动了,会某个地方生成一个新的 VNode 对象,这个新的 VNode 对象才是最新的元素效果,怎么办呢,直接将旧的元素卸载掉,然后根据这个新的 VNode 对象信息操作DOM api挂载新的元素,没问题,效果也能达到,但是每次怎么粗暴显然不是最优的,最优的方法你也能想到,那就是将新旧VNode对比,就像 git diff 一样,得到 patch 补丁,通过这个差异信息只更改必要的元素效果就可以了。这样虚拟节点这个对象才有存在的意义。
还有一个对象,需要了解也是很重要的,叫组件实例。什么意思,比如你写的一个元素<div>xxxx</div>
,这只是你写的一个html描述性代码而已,或者说叫配置信息,浏览器解析这个配置字符串信息会生成DOM对象来渲染和让你操作的啊,这个DOM对象不就是你这个想要表达的元素的元素实例嘛,它本身肯定会存一些状态信息也会有很多函数操作的,没问题,都能理解。自然地,在 vue 里它也少不了这个东西。
组件实例:就是组件对象的一个实例化对象而已,它保存着真实的数据状态等,渲染函数会根据这些状态渲染出不同的虚拟节点,这样才能达到修改组件实例的数据产生不一样的元素效果嘛。所以组件对象,就是在定义组件而已,定义了它该有哪些数据属性,有哪些函数,渲染函数是什么样子的,根据不同的数据值产生不同的渲染结果等等,组件实例就是在运行时,根据组件对象生成的一个实例对象,它才是有意义的存在,组件对象只是模板提供一些配置而已,你想页面上不同地方可以使用同一个组件元素效果吧,组件对象还是那个组件对象,模板嘛,但是不同地方上的组件实例肯定是不同的,有几个地方就有几个组件实例,否则不就共享状态产生混淆了嘛。组件实例的存在必不可少。
所以,vue3 的运行时逻辑就是这样子的:
我擦,一个小时过去了,麻痹的,我发现这个主题【运行时】有点大,vue3 提供的响应式机制也没介绍,不管它咋实现的,先弄明白上层设计目的就行了。目的是啥,目的就是实现响应式嘛,什么叫响应式,就是老婆喊了,你就得乖乖睡觉去了,当有个动作或消息发生时,自动地会产生预计的行为,这就是响应式嘛,就是听话。对应到js,就是,当js代码修改了某个对象的属性时,会自动调用跟这个属性有关系的一些函数,来产生一些连带效果。怎么实现?也很简单,你要是浏览器JS引擎主导者,你想提供这样的特性不是很简单嘛,当执行修改对象属性的代码时,我(js引擎代码)先或者后调用配置好的回调函数不就行了嘛,没什么新奇的,就是调用回调函数而已,这个回调函数哪里来,用户通过api配置的嘛。什么 Object.defineProperty
,当然这个机制没做太好,有一些限制不能很好地满足上层业务需要,后来又新增了什么 Proxy
对象,来实现更为完整的劫持代理效果,vue3 就是利用这个 Proxy
玩意来实现的响应式机制。没什么吧,就是调用浏览器提供的api完成了鱿鱼须想要完成的业务要而已。
第一篇水文到此结束,除了defineProperty这个词不会写查了下,其它的都是想到哪写到哪。这盘菜有点生。
评论区