【尚硅谷】Vue源码解析之虚拟DOM和diff算法
【Vue源码】图解 diff算法 与 虚拟DOM-snabbdom-最小量更新原理解析-手写源码-updateChildren]
snabbdom(瑞典语,“速度”)是著名的虚拟DOM库,是diff算法的鼻祖
Vue源码借鉴了snabbdom
源码使用TypeScript写的https://github.com/snabbdom/snabbdom
从npm下载的是build出来的JavaScript版本
-D 是开发dev版本的依赖 -S是项目真正依赖
npm init
npm install -D snabbdom
下载好的源码在node_modules
需要读取一些init等文件,使用webpack5,不能是webpack4(读取的地址不对)。接下来安装webpack5
cnpm i -D webpack@5 webpack-cli@3 webpack-dev-server@3
配置webpack5
根目录下创建webpack.config.js
cnpm i -D webpack@5 webpack-cli@3 webpack-dev-server@3
“dev”: “webpack-dev-server” npm run dev实际跑的是npm run webpack-dev-server会读取webpack.config.js文件
webpack.config.js
配置文件
module.exports = {
// webpack5 不用配置mode
// 入口
entry: "./src/index.js",
// 出口
output: {
// 虚拟打包路径,文件夹不会真正生成,而是在8080端口虚拟生成
publicPath: "xuni",
// 打包出来的文件名
filename: "bundle.js",
},
// 配置webpack-dev-server
devServer: {
// 静态根目录
contentBase: 'www',
// 端口号
port: 8080,
},
};
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: function () { } } }, [
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: function () { } } },
[
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
根目录创建www文件夹,内部有index.html(默认访问该文件)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="container">div>
<script src="/xuni/bundle.js">script>
body>
html>
diff算法发生在 虚拟DOM上,新旧虚拟DOM的比较
h 函数产生虚拟节点
虚拟节点vnode的属性
{
children: undefined// 子元素 数组
data: {} // 属性、样式、key
elm: undefined // 对应的真正的dom节点(对象),undefined表示节点还没有上dom树
key: // 唯一标识
sel: "" // 选择器
text: "" // 文本内容
}
src/index.js
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
var myVnode1 = h('a', {
props: {
href: "http://www.baidu.com",
target: '_blank',
}
}, '百度')
// const myVnode2 = h('div', '我是盒子' )
const myVnode3 = h('div', {class:{'box': true}}, '我是盒子')
console.log(myVnode1)
// 将虚拟节点渲染成真实节点 需要使用patch函数
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");
patch(container, myVnode1)
const myVnode4 = h('ul', [
h('li', '苹果1'),
h('li', '苹果2'),
h('li', '苹果3'),
h('li', '苹果4'),
])
console.log(myVnode1)
// 将虚拟节点渲染成真实节点 需要使用patch函数
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");
patch(container, myVnode4)
const myVnode4 = h('ul', [
h('li', '苹果1'),
h('li', [
h('div', [
h('p', "香蕉皮"),
h('p', "苹果皮")
])
]),
h('li', h('span', '西瓜')),
h('li', '番茄'),
])