Front end notes: virtual Dom and diff of vue2. X

introvert-y 2021-05-03 14:45:02
end notes virtual dom diff


This content note comes from 【 Silicon Valley 】Vue Source code analysis of virtual DOM and diff Algorithm Video Explanation of , Learning from excellent students' notes blog.csdn.net/wanghuan102…

snabbdom

brief introduction

  • snabbdom It's a Swedish word , The original meaning of the word is “ Speed ”,
  • snabbdom It's the famous virtual DOM library , yes diff The originator of algorithms ,Vue The source code is a reference snabbdom
  • official Git:github.com/snabbdom/sn…

install

  • stay git Upper snabbdom Source code is used TypeScript Written ,git It doesn't provide compiled JavaScript edition
  • If you want to use it directly build Coming out JavaScript Version of snabbdom library , It can be downloaded from npm Upload and download :npm i -S snabbdom
  • At the bottom of the library , I suggest you read the original code , It's better to have the original comments of the library author . This will greatly improve your ability to read the source code .
  • The version currently in use is 3.0.1 Unlike in the video , If you want to stay the same as the author of the article , That is to use yarn Installation dependency

Look at the source code tips

Locate folder

stay vsCode If you enter the package name directly in the resource manager, you will be able to locate the current package directly

image.png

When the content is inconsistent

When our package version numbers are different , The content will change . So when you see that the source code of some articles on the Internet is inconsistent with yours , We should pay attention to whether the package version number is different , It is recommended to use yarn To install the dependency , This keeps dependencies consistent

Test environment construction

First, create a new empty folder

perform npm init -y Create a package.json file

Create a new file at the root webpack.config.js

// https://webpack.docschina.org/
const path = require('path')
module.exports = {
// entrance 
entry: './src/index.js',
// exit 
output: {
// Virtual packaging path , That is to say, the folder will not really generate , But in 8080 Port virtual generation , It's not really physically generated 
publicPath: 'xuni',
// The name of the packed file 
filename: 'bundle.js'
},
devServer: {
// Port number 
port: 8080,
// Static resource folder 
contentBase: 'www'
}
}
 Copy code 

Create the following directory structure

image.png

/** src/index.js */
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
const patch = init([
// Init patch function with chosen modules
classModule, // makes it easy to toggle classes
propsModule, // for setting properties on DOM elements
styleModule, // handles styling on elements with support for animations
eventListenersModule, // attaches event listeners
]);
const container = document.getElementById("container");
const vnode = h("div#container.two.classes", { on: { click: () => {} } }, [
h("span", { style: { fontWeight: "bold" } }, "This is bold"),
" and this is just normal text",
h("a", { props: { href: "/foo" } }, "I'll take you places!"),
]);
// Patch into empty DOM element – this modifies the DOM as a side effect
patch(container, vnode);
const newVnode = h(
"div#container.two.classes",
{ on: { click: () => {} } },
[
h(
"span",
{ style: { fontWeight: "normal", fontStyle: "italic" } },
"This is now italic type"
),
" and this is still just normal text",
h("a", { props: { href: "/bar" } }, "I'll take you places!"),
]
);
// Second `patch` invocation
patch(vnode, newVnode); // Snabbdom efficiently updates the old view to the new state
 Copy code 
<!-- www/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn"> Press me to change dom</button>
<div id="container"></div> // convenient patch Up a tree
<script src="xuni/bundle.js"></script>
</body>
</html>
</html>
 Copy code 

snabbdom Kuo is DOM library , Of course not in nodejs Environment is running , So we need to build webpack and webpack-dev-server development environment

package.json

// package.json
{
"name": "vue_test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.36.2",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.2"
},
"dependencies": {
"snabbdom": "^3.0.1"
}
}
 Copy code 

It is judged that the construction is successful

  • Terminal operation npm run dev
  • visit :http://localhost:8080/ and http://127.0.0.1:8080/xuni/bundle.js, You can see www/index.html and xuni/bundle.js The content of the document
  • stay src/index.js Run through snabbdom official git On the front page demo Program , It proves that the debugging environment has been built successfully

(ps: Need to put click The following function is changed to () => {})

Explain the beggar version of virtual dom and diff The role of each function in

Why the beggar version :

  1. h Function fixed the number of arguments , And how it's used
  2. patchVnode Regardless of the presence of a string or number in front of a node
  3. Components are not taken into account

in summary , There are many special cases that have not been considered , Only consider the core functions and processes under normal circumstances

fictitious dom relevant

1. h function

The function is to change the input parameter to vnone

2. vnode function

Change the incoming parameters into fixed format objects

diff relevant

1. patch function

patch.png

2. createElement function

Actually creating nodes take vnode Create as DOM

3. patchVnode function

It's the fine comparison part in the figure above

patchVnode.png

4. updateChildren

diff Algorithm

  • Four hit search methods :
    ① New and old
    ② New and old
    ③ After the new and before the old ( This kind of hit , Involving mobile nodes , So the old node , Move to after old )
    ④ Before and after ( This kind of hit , Involving mobile nodes , So the old node points to , Move to before )
  • If none of them hit , You need to use the loop to find

image.png

// updateChildren.js
import createElement from './createElement'
import patchVnode from './patchVnode'
// Determine whether it is the same virtual node 
function checkSameVnode(a, b) {
return a.sel === b.sel && a.key === b.key
}
export default function updateChildren(parentElm, oldCh, newCh) {
// console.log(' I am a updateChildren')
// console.log(oldCh, newCh)
let oldStartIdx = 0 // Before 
let newStartIdx = 0 // New front 
let oldEndIdx = oldCh.length - 1 // Old queen 
let newEndIdx = newCh.length - 1 // New post 
let oldStartVnode = oldCh[oldStartIdx] // Old nodes 
let oldEndVnode = oldCh[oldEndIdx] // Old back node 
let newStartVnode = newCh[newStartIdx] // New front node 
let newEndVnode = newCh[newEndIdx] // New back node 
let keyMap = null
// console.log(oldStartIdx, newEndIdx)
// Start the cycle 
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
// console.log('*')
// First of all, it's not a judgment hit , It's about skimming over what's been added undefined Things marked 
if (oldStartVnode === null || oldCh[oldStartIdx] === undefined) {
oldStartVnode = oldCh[++oldStartIdx]
} else if (oldEndVnode === null || oldCh[oldEndIdx] === undefined) {
oldEndVnode = oldCh[--oldEndIdx]
} else if (newStartVnode === null || newCh[newStartIdx] === undefined) {
newStartVnode = newCh[++newStartIdx]
} else if (newEndVnode === null || newCh[newEndIdx] === undefined) {
newEndVnode = newCh[--newEndIdx]
} else if (checkSameVnode(oldStartVnode, newStartVnode)) {
// New and old 
console.log('① New and old hits ')
patchVnode(oldStartVnode, newStartVnode)
oldStartVnode = oldCh[++oldStartIdx]
newStartVnode = newCh[++newStartIdx]
} else if (checkSameVnode(oldEndVnode, newEndVnode)) {
// New and old 
console.log('② New post and old post hit ')
patchVnode(oldEndVnode, newEndVnode)
oldEndVnode = oldCh[--oldEndIdx]
newEndVnode = newCh[--newEndIdx]
} else if (checkSameVnode(oldStartVnode, newEndVnode)) {
// New and old 
console.log('③ New and old hits ')
patchVnode(oldStartVnode, newEndVnode)
// When ③ When new and old hits , At this time, we need to move the node . Move the new node to the back of the old node 
// How to move nodes ? As long as you insert one already in DOM Nodes on the tree , It will be moved 
parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling)
oldStartVnode = oldCh[++oldStartIdx]
newEndVnode = newCh[--newEndIdx]
} else if (checkSameVnode(oldEndVnode, newStartVnode)) {
// Before and after 
console.log('④ New before and old after hit ')
patchVnode(oldEndVnode, newStartVnode)
// When ④ When new front and old back hit , At this time, we need to move the node . Move the new node to the old node 
parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm)
oldEndVnode = oldCh[--oldEndIdx]
newStartVnode = newCh[++newStartIdx]
} else {
// None of the four hits were found 
// Make keyMap, cache 
if (!keyMap) {
keyMap = {}
// from oldStartIdx Start , To oldEndIdx end , establish keyMap Mapping objects 
for (let i = oldStartIdx; i <= oldEndIdx; i++) {
const key = oldCh[i].key
if (key !== undefined) {
keyMap[key] = i
}
}
}
// console.log(keyMap)
// Look for the current newStartIdx This is in the keyMap The sequence number of the map in 
const idxInOld = keyMap[newStartVnode.key]
if (idxInOld === undefined) {
// Judge , If idxInOld yes undefined Indicates that it is a brand new item 
// Added items ( Namely newStartVnode the ) Now it's not real DOM
parentElm.insertBefore(createElement(newStartVnode), oldStartVnode.elm)
} else {
// Judge , If idxInOld No undefined Indicates that it is not a new item , Need to move 
const elmToMove = oldCh[idxInOld]
patchVnode(elmToMove, newStartVnode)
// Set this to undefined, It means it's finished 
oldCh[idxInOld] = undefined
// Move , call insertBefore
parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm)
}
// The pointer moves down , Just move the new head 
newStartVnode = newCh[++newStartIdx]
}
}
// Go ahead and see if there's any left The cycle is over newStartIdx It's better than newEndIdx Small 
if (newStartIdx <= newEndIdx) {
// new There are still nodes left to process 
for (let i = newStartIdx; i <= newEndIdx; i++) {
// insertBefore Automatic identification null, If it is null It'll automatically line up at the end of the line . and appendChild It's consistent 
// newCh[i] It's not really DOM, So we need to call createElement
parentElm.insertBefore(createElement(newCh[i]), oldCh[oldStartIdx].elm)
}
} else if (oldStartIdx <= oldEndIdx) {
// old There are still nodes left to process 
// Batch deletion oldStartIdx~oldEndIdx Between the items 
for (let i = oldStartIdx; i <= oldEndIdx; i++) {
if (oldCh[i]) {
parentElm.removeChild(oldCh[i].elm)
}
}
}
}
 Copy code 

while Inside , If they don't hit the target, they'll use the loop to find it

Why just move newStartIdx?
 // The pointer moves down , Just move the new head 
newStartVnode = newCh[++newStartIdx]
 Copy code 
Why insert oldStartVnode.elm Before ?
 parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm)
 Copy code 

The answer to these two questions is the same , Because the essence is to change the old node into a new node

while The post logic

It needs to be judged what kind of situation causes while end

  1. newStartIdx <= newEndIdx If it also meets the requirements, it means that the old node needs to be added
  2. oldStartIdx <= oldEndIdx The old node needs to be deleted

image.png

Check if it's moving dom

open F12 stay console Lateral Elements in , change dom The content of , Click the button again to change , If the content is consistent, it's mobile . If the content goes back to the beginning, it's new .

Source code address

github.com/introvert-y…

Last

Look at the source code to understand , Why? key stay for Loop is indispensable and cannot be misused , Let's start with the underlying logic and do some performance optimization , And reduce bug The birth of . Reprint please indicate the source , thank you .

版权声明
本文为[introvert-y]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/05/20210503144051138S.html

  1. Why did gitlab choose vue.js?
  2. HTTP-RPC: 轻量跨平台REST服务
  3. 继全面采用Node.js以后,PayPal分享大幅度踩坑GraphQL心得 - Mark Stuart
  4. vue组件化开发实战之滚动/轮播的实现
  5. Http-rpc: lightweight cross platform rest Service
  6. Following the full adoption of node.js, PayPal shares a great deal of graphql experience mark Stuart
  7. Implementation of rolling / carousel in Vue component development
  8. CSS是什么?这一篇全解,绝对有你想要的
  9. What is CSS? This is a complete solution, there is absolutely what you want
  10. 04-HTML5常用标签-HTML5极速入门
  11. 04-html5 common tags
  12. WEB前端全套零基础视频教程+软件2021最新编程视频
  13. Web front end full set of zero basic video tutorial + software 2021 latest programming video
  14. 使用Node, Mongo, React, Redux实现Token认证
  15. Using node, Mongo, react and Redux to realize token authentication
  16. 体面编码之CSS和HTML
  17. CSS and HTML for decent coding
  18. 使用Playwright基于多浏览器进行javascript自动化测试的简单教程- Applitools
  19. A simple tutorial for JavaScript automatic testing based on multi browser using playwright - applitools
  20. Minimum distance to target element
  21. 浅谈 React 中的 XSS 攻击
  22. XSS attack in react
  23. 自学前端教程整理,附不容错过的前端100篇文章合集
  24. Self taught front-end tutorial collation, with a collection of 100 front-end articles that can not be missed
  25. 使用OpenTracing跟踪Go中的HTTP请求延迟
  26. Using opentracing to track HTTP request latency in go
  27. Encapsulating databinding allows you to write less than 10000 lines of code
  28. 03-HTML5标签-HTML5极速入门
  29. 03-html5 tag-html5 quick start
  30. LayUI - 极易上手拿来即用的前端 UI 框架
  31. Layui - easy to use front end UI framework
  32. Interpretation of lodash source code (1)
  33. Why is the first parameter of node family callback error?
  34. 报告:JavaScript 开发者达1380 万,C#超越 PHP,Rust 增长最快
  35. Report: Javascript developers reach 13.8 million, C surpasses PHP, and rust grows fastest
  36. 小白前端入门笔记(10),怎么设置网站内部的超链接?
  37. How to set up hyperlinks inside the website?
  38. Using node and socket to realize online chat room
  39. The core competitiveness of Vue: data bidirectional binding
  40. React configuration agent
  41. CSS layout
  42. Application scenario explanation of Vue dynamic component
  43. Redux learning notes 04 -- using multiple reducers to manage data
  44. After three months of typescript writing, what have I learned?
  45. Node family - what is a callback?
  46. React -- a simple implementation of render & create element
  47. JS learning simple usage of jquery
  48. Seamless love
  49. 小白前端入门笔记(12),设置哑链接
  50. Small white front-end entry notes (12), set dumb links
  51. Vue2. X opens composition API and TSX
  52. Interview record and thinking of social recruitment for one and a half years (Alibaba, Tencent, baidu offer)
  53. Flex learning notes
  54. The most essential closure article in the eastern hemisphere
  55. 2021-05-03 hot news
  56. Sword finger offer -- reverse order pair in array (JS Implementation)
  57. Working process of scaffold
  58. Use decorator mode to strengthen your fetch
  59. [JS] scope (Introduction)
  60. Employment information statistics network (interface document)