Home Web Front-end JS Tutorial Implement virtual dom patch in vue (detailed tutorial)

Implement virtual dom patch in vue (detailed tutorial)

Jun 02, 2018 am 10:58 AM
patch Tutorial detailed

This article mainly introduces the patch source code analysis of vue virtual dom. Now I will share it with you and give you a reference.

This article introduces the patch source code analysis of vue virtual dom and shares it with everyone. The details are as follows:

Source code directory: src/core/vdom/patch.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {

 let oldStartIdx = 0

 let newStartIdx = 0

 let oldEndIdx = oldCh.length - 1

 let oldStartVnode = oldCh[0]

 let oldEndVnode = oldCh[oldEndIdx]

 let newEndIdx = newCh.length - 1

 let newStartVnode = newCh[0]

 let newEndVnode = newCh[newEndIdx]

 let oldKeyToIdx, idxInOld, vnodeToMove, refElm

 

   const canMove = !removeOnly

 

 while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { // 开始索引大于结束索引,进不了

  if (isUndef(oldStartVnode)) {

   oldStartVnode = oldCh[++oldStartIdx] // Vnode已经被移走了。

  } else if (isUndef(oldEndVnode)) {

   oldEndVnode = oldCh[--oldEndIdx]

  } else if (sameVnode(oldStartVnode, newStartVnode)) {

   patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)

   oldStartVnode = oldCh[++oldStartIdx] // 索引加1。是去对比下一个节点。比如之前start=a[0],那现在start=a[1],改变start的值后再去对比start这个vnode

   newStartVnode = newCh[++newStartIdx]

     

  } else if (sameVnode(oldEndVnode, newEndVnode)) {

   patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue)

   oldEndVnode = oldCh[--oldEndIdx]

   newEndVnode = newCh[--newEndIdx]

  } else if (sameVnode(oldStartVnode, newEndVnode)) {

   patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)

   canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))// 把节点b移到树的最右边

   oldStartVnode = oldCh[++oldStartIdx]

   newEndVnode = newCh[--newEndIdx]

     

  } else if (sameVnode(oldEndVnode, newStartVnode)) {  old.end.d=new.start.d

   patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)

   canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)// Vnode moved left,把d移到c的左边。=old.start->old.end

   oldEndVnode = oldCh[--oldEndIdx]

   newStartVnode = newCh[++newStartIdx]

 

  } else {

   if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)

   idxInOld = isDef(newStartVnode.key)

    ? oldKeyToIdx[newStartVnode.key]

    : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)

   if (isUndef(idxInOld)) {

    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm) // 创建新节点,后面执行了nodeOps.insertBefore(parent, elm, ref)

   } else {

    vnodeToMove = oldCh[idxInOld]

    /* istanbul ignore if */

    if (process.env.NODE_ENV !== &#39;production&#39; && !vnodeToMove) {

     warn(

      &#39;It seems there are duplicate keys that is causing an update error. &#39; +

      &#39;Make sure each v-for item has a unique key.&#39;

     )

    }

    if (sameVnode(vnodeToMove, newStartVnode)) {

     patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue)

     oldCh[idxInOld] = undefined

     canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)

    } else {

     // same key but different element. treat as new element

     createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)

    }

   }

   newStartVnode = newCh[++newStartIdx]

   

  }

 }

 if (oldStartIdx > oldEndIdx) {

  refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm

  addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)

 } else if (newStartIdx > newEndIdx) {

  removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) // 删除旧的c,removeNode(ch.elm)

 

 }

}

Copy after login

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

function sameVnode (a, b) {

 return (

  a.key === b.key && (

   (

    a.tag === b.tag &&

    a.isComment === b.isComment &&

    isDef(a.data) === isDef(b.data) &&

    sameInputType(a, b)

   ) || (

    isTrue(a.isAsyncPlaceholder) &&

    a.asyncFactory === b.asyncFactory &&

    isUndef(b.asyncFactory.error)

   )

  )

 )

}

 

/**

   * 比较新旧vnode节点,根据不同的状态对dom做合理的更新操作(添加,移动,删除)整个过程还会依次调用prepatch,update,postpatch等钩子函数,在编译阶段生成的一些静态子树,在这个过程

   * @param oldVnode 中由于不会改变而直接跳过比对,动态子树在比较过程中比较核心的部分就是当新旧vnode同时存在children,通过updateChildren方法对子节点做更新,

   * @param vnode

   * @param insertedVnodeQueue

   * @param removeOnly

   */

 function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {

  if (oldVnode === vnode) {

   return

  }

 

  const elm = vnode.elm = oldVnode.elm

 

  if (isTrue(oldVnode.isAsyncPlaceholder)) {

   if (isDef(vnode.asyncFactory.resolved)) {

    hydrate(oldVnode.elm, vnode, insertedVnodeQueue)

   } else {

    vnode.isAsyncPlaceholder = true

   }

   return

  }

 

   // 用于静态树的重用元素。

    // 注意,如果vnode是克隆的,我们只做这个。

    // 如果新节点不是克隆的,则表示呈现函数。

    // 由热重加载api重新设置,我们需要进行适当的重新渲染。

  if (isTrue(vnode.isStatic) &&

   isTrue(oldVnode.isStatic) &&

   vnode.key === oldVnode.key &&

   (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))

  ) {

   vnode.componentInstance = oldVnode.componentInstance

   return

  }

 

  let i

  const data = vnode.data

  if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {

   i(oldVnode, vnode)

  }

 

  const oldCh = oldVnode.children

  const ch = vnode.children

  if (isDef(data) && isPatchable(vnode)) {

   for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)

   if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)

  }

  if (isUndef(vnode.text)) {

   if (isDef(oldCh) && isDef(ch)) {

    if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)

   } else if (isDef(ch)) {

    if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, &#39;&#39;)

    addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)

   } else if (isDef(oldCh)) {

    removeVnodes(elm, oldCh, 0, oldCh.length - 1)

   } else if (isDef(oldVnode.text)) {

    nodeOps.setTextContent(elm, &#39;&#39;)

   }

  } else if (oldVnode.text !== vnode.text) {

   nodeOps.setTextContent(elm, vnode.text)

  }

  if (isDef(data)) {

   if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)

  }

 }

 

function insertBefore (parentNode, newNode, referenceNode) {

 parentNode.insertBefore(newNode, referenceNode);

}

 

/**

   *

   * @param vnode根据vnode的数据结构创建真实的dom节点,如果vnode有children则会遍历这些子节点,递归调用createElm方法,

   * @param insertedVnodeQueue记录子节点创建顺序的队列,每创建一个dom元素就会往队列中插入当前的vnode,当整个vnode对象全部转换成为真实的dom 树时,会依次调用这个队列中vnode hook的insert方法

   * @param parentElm

   * @param refElm

   * @param nested

   */

 

   let inPre = 0

 function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) {

  vnode.isRootInsert = !nested // 过渡进入检查

  if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {

   return

  }

 

  const data = vnode.data

  const children = vnode.children

  const tag = vnode.tag

  if (isDef(tag)) {

   if (process.env.NODE_ENV !== &#39;production&#39;) {

    if (data && data.pre) {

     inPre++

    }

    if (

     !inPre &&

     !vnode.ns &&

     !(

      config.ignoredElements.length &&

      config.ignoredElements.some(ignore => {

       return isRegExp(ignore)

        ? ignore.test(tag)

        : ignore === tag

      })

     ) &&

     config.isUnknownElement(tag)

    ) {

     warn(

      &#39;Unknown custom element: <&#39; + tag + &#39;> - did you &#39; +

      &#39;register the component correctly? For recursive components, &#39; +

      &#39;make sure to provide the "name" option.&#39;,

      vnode.context

     )

    }

   }

   vnode.elm = vnode.ns

    ? nodeOps.createElementNS(vnode.ns, tag)

    : nodeOps.createElement(tag, vnode)

   setScope(vnode)

 

   /* istanbul ignore if */

   if (__WEEX__) {

    // in Weex, the default insertion order is parent-first.

    // List items can be optimized to use children-first insertion

    // with append="tree".

    const appendAsTree = isDef(data) && isTrue(data.appendAsTree)

    if (!appendAsTree) {

     if (isDef(data)) {

      invokeCreateHooks(vnode, insertedVnodeQueue)

     }

     insert(parentElm, vnode.elm, refElm)

    }

    createChildren(vnode, children, insertedVnodeQueue)

    if (appendAsTree) {

     if (isDef(data)) {

      invokeCreateHooks(vnode, insertedVnodeQueue)

     }

     insert(parentElm, vnode.elm, refElm)

    }

   } else {

    createChildren(vnode, children, insertedVnodeQueue)

    if (isDef(data)) {

     invokeCreateHooks(vnode, insertedVnodeQueue)

    }

    insert(parentElm, vnode.elm, refElm)

   }

 

   if (process.env.NODE_ENV !== &#39;production&#39; && data && data.pre) {

    inPre--

   }

  } else if (isTrue(vnode.isComment)) {

   vnode.elm = nodeOps.createComment(vnode.text)

   insert(parentElm, vnode.elm, refElm)

  } else {

   vnode.elm = nodeOps.createTextNode(vnode.text)

   insert(parentElm, vnode.elm, refElm)

  }

 }

function insert (parent, elm, ref) {

  if (isDef(parent)) {

   if (isDef(ref)) {

    if (ref.parentNode === parent) {

     nodeOps.insertBefore(parent, elm, ref)

    }

   } else {

    nodeOps.appendChild(parent, elm)

   }

  }

 }

 

function removeVnodes (parentElm, vnodes, startIdx, endIdx) {

  for (; startIdx <= endIdx; ++startIdx) {

   const ch = vnodes[startIdx]

   if (isDef(ch)) {

    if (isDef(ch.tag)) {

     removeAndInvokeRemoveHook(ch)

     invokeDestroyHook(ch)

    } else { // Text node

     removeNode(ch.elm)

    }

   }

  }

 }

Copy after login

##updateChildren method mainly passes while Loop to compare the child nodes of the two trees to update dom, and change the old one by comparing the new one to achieve the purpose of unifying the old and new.

Let’s simulate it through an example:


Suppose there are two trees, old and new, and the child nodes in the tree are represented by

a, b, c, d, etc. , different codenames represent different vnode, such as:

After setting the status, we start the first comparison, at this time

oldStartVnode=a, newStartVnode=a;If the sameVnode(oldStartVnode,newStartVnode) logic is hit, directly call the patchVnode(oldStartVnode,newStartVnode,insertedVnodeQueue) method to update the node a, then set the oldStartIdx and newStartIdx indexes to 1 respectively, as shown in the figure:

After updating the node

aAfter that, we start the second comparison. At this time, oldStartVnode=b,newEndVnode=b; hits the sameVnode(oldStartVnode,newEndVnode) logic, and then calls patchVnode( oldStartVnode, newEndVnode, insertedVnodeQueue) method to update the nodeb, and then call canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm)), Move node b to the far right of the tree, and finally index oldStartIdx to 1 and newEndIdx to index -1, as shown in the figure:

After updating node

b, we start the third comparison. At this time, oldEndVnode=d, newStartVnode=d; hits sameVnode(oldEndVnode, newStartVnode) Logic, call patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue) method to update the node d, and then call canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode. elm), move d to the left of c. Finally, index oldEndIdx to -1 and newStartIdx to index 1, as shown in the figure:

##After updating

d

, we start the fourth comparison. At this time newStartVnode=e, node e does not exist in the old tree, so it should be inserted as a new element, call createElm( newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm), and then the nodeOps.insertBefore(parent, elm, ref) method is executed to insert e before c , then index newStartIdx as 1, as shown in the figure: After

is inserted into node

e

, we can see newStartIdx is already greater than newEndIdx, while loop has been completed. Then call removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) to delete the old c, as shown in the final figure:

updateChildren

has completed the update of the child nodes of the old tree through the above steps. In fact, only relatively small dom operations are used, which improves the performance, and as the child nodes become more complex, this The improvement effect is more obvious. vnodeAfter generating dom through the patch method, mounted hook will be called. At this point, the entire vue instance is created Completed, when the watcher of this vue instance observes data changes, it will call the render method twice to generate a new vnode, Then call the patch method to compare the old and new vnode to update dom. The above is what I compiled for everyone. I hope it will be helpful to everyone in the future. .

Related articles:

JQuery selects the selected value method of the select component


$set and array in vue.js Update method_vue.js


Vue and vue-i18n are combined to implement multi-language switching method of background data


The above is the detailed content of Implement virtual dom patch in vue (detailed tutorial). For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
2 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
2 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Tutorial on how to use Dewu Tutorial on how to use Dewu Mar 21, 2024 pm 01:40 PM

Dewu APP is currently a very popular brand shopping software, but most users do not know how to use the functions in Dewu APP. The most detailed usage tutorial guide is compiled below. Next is the Dewuduo that the editor brings to users. A summary of function usage tutorials. Interested users can come and take a look! Tutorial on how to use Dewu [2024-03-20] How to use Dewu installment purchase [2024-03-20] How to obtain Dewu coupons [2024-03-20] How to find Dewu manual customer service [2024-03-20] How to check the pickup code of Dewu [2024-03-20] Where to find Dewu purchase [2024-03-20] How to open Dewu VIP [2024-03-20] How to apply for return or exchange of Dewu

Quark browser usage tutorial Quark browser usage tutorial Feb 24, 2024 pm 04:10 PM

Quark Browser is a very popular multi-functional browser at the moment, but most friends don’t know how to use the functions in Quark Browser. The most commonly used functions and techniques will be sorted out below. Next, the editor will guide users. Here is a summary of the multi-functional usage tutorials of Quark Browser. Interested users can come and take a look together! Tutorial on how to use Quark Browser [2024-01-09]: How to scan test papers to see answers on Quark [2024-01-09]: How to enable adult mode on Quark Browser [2024-01-09]: How to delete used space on Quark [2024 -01-09]: How to clean up the Quark network disk storage space [2024-01-09]: How to cancel the backup of Quark [2024-01-09]: Quark

Upgrading numpy versions: a detailed and easy-to-follow guide Upgrading numpy versions: a detailed and easy-to-follow guide Feb 25, 2024 pm 11:39 PM

How to upgrade numpy version: Easy-to-follow tutorial, requires concrete code examples Introduction: NumPy is an important Python library used for scientific computing. It provides a powerful multidimensional array object and a series of related functions that can be used to perform efficient numerical operations. As new versions are released, newer features and bug fixes are constantly available to us. This article will describe how to upgrade your installed NumPy library to get the latest features and resolve known issues. Step 1: Check the current NumPy version at the beginning

Tutorial on how to turn off the payment sound on WeChat Tutorial on how to turn off the payment sound on WeChat Mar 26, 2024 am 08:30 AM

1. First open WeChat. 2. Click [+] in the upper right corner. 3. Click the QR code to collect payment. 4. Click the three small dots in the upper right corner. 5. Click to close the voice reminder for payment arrival.

What software is photoshopcs5? -photoshopcs5 usage tutorial What software is photoshopcs5? -photoshopcs5 usage tutorial Mar 19, 2024 am 09:04 AM

PhotoshopCS is the abbreviation of Photoshop Creative Suite. It is a software produced by Adobe and is widely used in graphic design and image processing. As a novice learning PS, let me explain to you today what software photoshopcs5 is and how to use photoshopcs5. 1. What software is photoshop cs5? Adobe Photoshop CS5 Extended is ideal for professionals in film, video and multimedia fields, graphic and web designers who use 3D and animation, and professionals in engineering and scientific fields. Render a 3D image and merge it into a 2D composite image. Edit videos easily

In summer, you must try shooting a rainbow In summer, you must try shooting a rainbow Jul 21, 2024 pm 05:16 PM

After rain in summer, you can often see a beautiful and magical special weather scene - rainbow. This is also a rare scene that can be encountered in photography, and it is very photogenic. There are several conditions for a rainbow to appear: first, there are enough water droplets in the air, and second, the sun shines at a low angle. Therefore, it is easiest to see a rainbow in the afternoon after the rain has cleared up. However, the formation of a rainbow is greatly affected by weather, light and other conditions, so it generally only lasts for a short period of time, and the best viewing and shooting time is even shorter. So when you encounter a rainbow, how can you properly record it and photograph it with quality? 1. Look for rainbows. In addition to the conditions mentioned above, rainbows usually appear in the direction of sunlight, that is, if the sun shines from west to east, rainbows are more likely to appear in the east.

DisplayX (monitor testing software) tutorial DisplayX (monitor testing software) tutorial Mar 04, 2024 pm 04:00 PM

Testing a monitor when buying it is an essential part to avoid buying a damaged one. Today I will teach you how to use software to test the monitor. Method step 1. First, search and download the DisplayX software on this website, install it and open it, and you will see many detection methods provided to users. 2. The user clicks on the regular complete test. The first step is to test the brightness of the display. The user adjusts the display so that the boxes can be seen clearly. 3. Then click the mouse to enter the next link. If the monitor can distinguish each black and white area, it means the monitor is still good. 4. Click the left mouse button again, and you will see the grayscale test of the monitor. The smoother the color transition, the better the monitor. 5. In addition, in the displayx software we

Experts teach you! The Correct Way to Cut Long Pictures on Huawei Mobile Phones Experts teach you! The Correct Way to Cut Long Pictures on Huawei Mobile Phones Mar 22, 2024 pm 12:21 PM

With the continuous development of smart phones, the functions of mobile phones have become more and more powerful, among which the function of taking long pictures has become one of the important functions used by many users in daily life. Long screenshots can help users save a long web page, conversation record or picture at one time for easy viewing and sharing. Among many mobile phone brands, Huawei mobile phones are also one of the brands highly respected by users, and their function of cropping long pictures is also highly praised. This article will introduce you to the correct method of taking long pictures on Huawei mobile phones, as well as some expert tips to help you make better use of Huawei mobile phones.

See all articles