Vue3 virtual DOM

Anthony Nuggets 2021-04-07 21:47:34
vue3 vue virtual dom


preface

It started on my blog thorough Vue3 fictitious DOM

translation :diving-into-the-vue-3s-virtual-dom-medium

author :Lachlan Miller

In this article, we will go deep into Vue3 fictitious DOM, And how it traverses to find the corresponding vnode Of .

Most of the time we don't have to think about Vue How components are built inside . But there are some libraries that will help us understand , such as Vue Test Utils Of findComponent function . There's another one we should all be familiar with Vue development tool —— Vue DevTools, It shows the component hierarchy of the application , And we can edit it and so on .

vuedevtool.png

What we are going to do in this article is : Realization Vue Test Utils API Part of , namely findComponent function .

Design findComponent

First , We all know fictitious DOM Is based on “ Lifting performance ” Proposed , When the data changes ,Vue Will determine if this needs to be updated 、 Or calculate the expression 、 Or make the final DOM to update .

Such as this :

- div
- span (show: true)
- 'Visible'
 Copy code 

Its internal hierarchical relationship is :

HTMLDivElement -> HTMLSpanElement -> TextNode
 Copy code 

If show Attribute becomes false.Vue fictitious DOM The following updates will be made :

- div
- span (show: false)
- 'Visible'
 Copy code 

next ,Vue Will update DOM, remove 'span' Elements .

that , Let's imagine ,findComponent function , Its call might be a structure like this :

const { createApp } = require('vue')
const App = {
template: `
<C>
<B>
<A />
</B>
</C>
`
}
const app = createApp(App).mount('#app')
const component = findComponent(A, { within: app })
// We go through findComponent The method has been found <A/> label .
 Copy code 

Print findComponent

next , Let's start with a few simple components , as follows :

// import jsdom-global. We need a global `document` for this to work.
require('jsdom-global')()
const { createApp, h } = require('vue')
// some components
const A = {
name: 'A',
data() {
return { msg: 'msg' }
},
render() {
return h('div', 'A')
}
}
const B = {
name: 'B',
render() {
return h('span', h(A))
}
}
const C = {
name: 'C',
data() {
return { foo: 'bar' }
},
render() {
return h('p', { id: 'a', foo: this.foo }, h(B))
}
}
// mount the app!
const app = createApp(C).mount(document.createElement('div'))
 Copy code 
  • We need to be in Node.js v14+ Environmental Science , Because we need to use Optional chain . And need to install Vue、jsdom and jsdom-global.

We can see A , B , C Three components , among A , C The components are data attribute , It's going to help us delve into VDOM.

You can print it :

console.log(app)
console.log(Object.keys(app))
 Copy code 

The result is {}, because Object.keys Only enumerable properties are displayed .

We can try to print Hidden, enumerable properties

console.log(app.$)
 Copy code 

You can get a lot of output information :

<ref *1> {
uid: 0,
vnode: {
__v_isVNode: true,
__v_skip: true,
type: {
name: 'C',
data: [Function: data],
render: [Function: render],
__props: []
}, // hundreds of lines ...
 Copy code 

Re print :

console.log(Object.keys(app.$))
 Copy code 

Output :

Press ENTER or type command to continue
[
'uid', 'vnode', 'type', 'parent', 'appContext', 'root', 'next', 'subTree', 'update', 'render', 'proxy', 'withProxy', 'effects', 'provides', 'accessCache', 'renderCache', 'ctx', 'data', 'props', 'attrs', 'slots', 'refs', 'setupState', 'setupContext', 'suspense', 'asyncDep', 'asyncResolved', 'isMounted', 'isUnmounted', 'isDeactivated', 'bc', 'c', 'bm', 'm', 'bu', 'u', 'um', 'bum', 'da', 'a', 'rtg', 'rtc', 'ec', 'emit', 'emitted'
]
 Copy code 

We can see some familiar properties : such as slotsdata,suspense It's a new feature ,emit Needless to say . And for example. attrsbccbm These are life cycle hooks :bc yes beforeCreate, c yes created. There are also some internal unique lifecycle hooks , Such as rtg, That is to say renderTriggered, When props or data When something changes , For update operations , And then render .

What we need to pay special attention to in this article is :vnodesubTreecomponenttype and children.

matching findComponent

Let's see vnode, It has many properties , Here's what we need to focus on type and component these two items. .

// Print console.log(app.$.vnode.component)

console.log(app.$.vnode.component)
<ref *1> {
uid: 0,
vnode: {
__v_isVNode: true,
__v_skip: true,
type: {
name: 'C',
data: [Function: data],
render: [Function: render],
__props: []
}, // ... many more things ... } }
 Copy code 

type It is interesting to ! It's different from what we defined before C Like components , We can see that it also has [Function: data]( We defined a msg Data is what we're looking for ). In fact, we try to print the following :

console.log(C === app.$.vnode.component.type) //=> true
 Copy code 

Oh, my God ! The two are equal !

console.log(C === app.$.vnode.type) //=> true
 Copy code 

This is also equal !

( Do you wonder why these two properties point to the same object ? Let's not press the table here 、 Self exploration .)

in any case , We've got a way to find components .

Through the search process here , We can further obtain the following equality relation :

console.log(
app.$
.subTree.children[0].component
.subTree.children[0].component.type === A) //=> true
 Copy code 

In this case ,div Node subTree.children The length of the array is 2 . We know that virtual DOM The recursive mechanism of , You can go in this direction :subTree -> children -> component Here's our recursive solution .

Realization findComponent

Let's first achieve matches function , It's used to judge whether it's current vnode Whether nodes and targets are equal .

function matches(vnode, target) {
return vnode?.type === target
}
 Copy code 

And then there was findComponent function , It's the common way we call and find internal recursive functions API.

function findComponent(comp, { within }) {
const result = find([within.$], comp)
if (result) {
return result
}
}
 Copy code 

Here find The implementation of the method is the focus of our discussion .

We know how to write recursion , The most important thing is to decide when it will end loop, therefore find The function should be like this first :

function find(vnodes, target) {
if (!Array.isArray(vnodes)) {
return
}
}
 Copy code 

then , In a traverse vnode when , If a matching component is found , Then return it to . If no matching component is found , You may need to check vnode.subTree.children Is it defined , So as to further query and match . Last , If not , We go back to the accumulator acc. therefore , The code is as follows :

function find(vnodes, target) {
if (!Array.isArray(vnodes)) {
return
}
return vnodes.reduce((acc, vnode) => {
if (matches(vnode, target)) {
return vnode
}
if (vnode?.subTree?.children) {
return find(vnode.subTree.children, target)
}
return acc
}, {})
}
 Copy code 

If you are in the if (vnode?.subTree?.children) { Here's a print console.log, You can find B Components , But our goal A The path of the component is as follows :

app.$
.subTree.children[0].component
.subTree.children[0].component.type === A) //=> true
 Copy code 

So we call again find Method :find(vnode.subTree.children, target), The first parameter to look up in the next iteration will be app.$.subTree.children, It is vnode Array of . We don't just need to check vnode.subTree.children, We need to check vnode.component.subTree.

therefore , Last find The method is as follows :

function find(vnodes, target) {
if (!Array.isArray(vnodes)) {
return
}
return vnodes.reduce((acc, vnode) => {
if (matches(vnode, target)) {
return vnode
}
if (vnode?.subTree?.children) {
return find(vnode.subTree.children, target)
}
if (vnode?.component?.subTree) {
return find(vnode.component.subTree.children, target)
}
return acc
}, {})
}
 Copy code 

And then we call it :

const result = findComponent(A, { within: app })
console.log( result.component.proxy.msg ) // => 'msg'
 Copy code 

We made it ! adopt findComponent, eureka msg!

If you have used Vue Test Utils, Maybe I've seen something like that wrapper.vm.msg, It's actually internal access proxy( about Vue 3) or vm( about Vue 2).

Summary

The realization of this article is not perfect , In reality, more checks need to be performed . for example , If you use template or Suspense When the component , More judgment is needed . But you can do that in Vue Test Utils Source code Can be seen in , Hope to help you further understand virtual DOM.

This article Source code address , Little hands move 、 It's easy to understand ~

All right. , This is the sharing ~

If you like , Like and focus on ~ I'm Anthony nuggets , Official account 【 Anthony Nuggets 】, Continuous output ing!

版权声明
本文为[Anthony Nuggets]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/04/20210407213623141O.html

  1. Behind the miracle of the sixth championship is the football with AI blessing in the Bundesliga
  2. An easy to use Visual Studio code extension - live server, suitable for front-end gadget development
  3. 用 Python 抓取公号文章保存成 HTML
  4. User login of front end spa project based on Vue and Quasar (2)
  5. Summary of common selectors in CSS
  6. Using Python to grab articles with public number and save them as HTML
  7. To "restless" you
  8. 【免费开源】基于Vue和Quasar的crudapi前端SPA项目实战—环境搭建 (一)
  9. 【微信小程序】引入阿里巴巴图标库iconfont
  10. layui表格点击排序按钮后,表格绑定事件失效解决方法
  11. Unity解析和显示/播放GIF图片,支持http url,支持本地file://,支持暂停、继续播放
  12. 【vue】 export、export default、import的用法和区别
  13. [free and open source] crudapi front end spa project based on Vue and Quasar
  14. [wechat applet] introduces Alibaba icon library iconfont
  15. Layui table click Sort button, table binding event failure solution
  16. Element树形控件Tree踩坑:修改current-node-key无效
  17. Unity parses and displays / plays GIF images, supports HTTP URL, supports local file: / /, supports pause and resume playback
  18. Element树形控件Tree踩坑:修改current-node-key无效
  19. The usage and difference of export, export default and import
  20. Element tree control: invalid to modify current node key
  21. Element tree control: invalid to modify current node key
  22. linux下安装apache(httpd-2.4.3版本)各种坑
  23. How to install Apache (httpd-2.4.3) under Linux
  24. 程序员业余时间写的代码也算公司的?Nginx之父被捕引发争议
  25. Nacos serialize for class [com.alibaba.nacos.common.http.HttpRestResult] failed.
  26. Do programmers write code in their spare time? Controversy over the arrest of nginx's father
  27. Nacos serialize for class [ com.alibaba.nacos . common.http.HttpRestResult ] failed.
  28. Seamless management of API documents using eolink and gitlab
  29. vue 的基础应用(上)
  30. 28岁开始零基础学前端,这些血的教训你一定要避免
  31. Basic application of Vue
  32. Starting at the age of 28, you must avoid these bloody lessons
  33. Ubuntu 16.04 can not connect to the wireless solution and QQ installation
  34. Industry security experts talk about the rapid development of digital economy, how to guarantee the security of data elements?
  35. 利用Vue实现一个简单的购物车功能
  36. Behind the "tireless classroom" and teacher training, can byte education really "work wonders"?
  37. Using Vue to realize a simple shopping cart function
  38. 【css】伪类和伪类元素的区别
  39. 【css效果】实现简单的下拉菜单
  40. 【vue】父子组件传值
  41. The difference between pseudo class and pseudo class elements
  42. [CSS effect] simple drop-down menu
  43. [Vue] value transfer by parent-child component
  44. 【css】设置table表格边框样式
  45. 【css】修改input,textarea中的placeholder样式
  46. vue-router的两种模式(hash和history)及区别
  47. CSS3的滤镜filter属性
  48. [CSS] set table border style
  49. [CSS] modify the placeholder style in input and textarea
  50. Two modes of Vue router (hash and History) and their differences
  51. Filter property of CSS3
  52. 全局安装gulp 报错问题解决
  53. Solution of error report in global installation of gulp
  54. 18个好用的自定义react hook
  55. 你应该知道的常用服务器HTTP状态码?
  56. 18 user defined react hooks
  57. What HTTP status codes should you know about common servers?
  58. 手把手教你打造属于自己团队的前端小报系统
  59. Hand in hand to teach you to build your own front-end tabloid system
  60. In 2021, enterprise SEO actual operation, how to less update, batch ranking regional words?