Vue虚拟DOM原理 |
您所在的位置:网站首页 › vue虚拟dom如何渲染成真实dom › Vue虚拟DOM原理 |
知识脉络 案例一: import { h, init } from 'snabbdom' // 1. hello world // 参数:数组,模块 // 返回值:patch函数,作用对比两个vnode的差异更新到真实DOM let patch = init([]) // h函数有多个参数,以其中最常用的两个为例 // 第一个参数:标签+选择器 // 第二个参数:如果是字符串的话就是标签中的内容,如果有别的节点可以用数组 let vnode = h('div#container.cls','Hello World') let app = document.querySelector('#app') // patch函数有两个参数 // 第一个参数:旧的VNode,也可以是个DOM元素,内部会把DOM元素转换成VNode // 第二个参数:新的VNode // 返回值:VNode,这个返回的VNode会作为下一次老的VNode let oldVnode = patch(app, vnode) // 假设对htmlwe文件中的#app的div进行更新,用下面的div及内容进行替换 vnode = h('div#container.xxx', 'Hello Snabbdom') patch(oldVnode, vnode) 案例二: // 2. div中放置子元素 h1,p import { h, init } from 'snabbdom' let patch = init([]) let vnode = h('div#container', [ h('h1', 'Hello Snabbdom'), h('p', '这是一个p标签') ]) let app = document.querySelector('#app') // app是选择要渲染到的位置 let oldVnode = patch(app, vnode) // vnode 是当前创建的节点,patch方法时把当前创建的虚拟DOM渲染到app位置处,并返回渲染后的界面并转换成虚拟DOM,渲染后的oldVnode将作为以后的老节点 setTimeout(() => { vnode = h('div#container', [ h('h1', 'Hello World'), h('p', 'Hello P') ]) patch(oldVnode, vnode) // 清空div中的内容 patch(oldVnode, h('!')) // ! 将会渲染成注释标签 }, 2000); snabbdom模块模块的作用snabbdom 的核心库并不能处理DOM元素的属性/样式/事件等,可以通过注册snabbdom默认提供的模块来实现(类似于插件机制)snabbdom中的模块可以用来扩展snabbdom的功能snabbdom中的模块的实现时通过注册全局钩子函数来实现的,是整个vnode生命周期过程中被触发的函数官方提供的模块attributes - 用来设置DOM的属性,通过setAttributes来实现props - 用来设置DOM属性,通过 . 的形式添加的dataset - 用来处理HTML5中的data- 自定义属性的class - 用来切换类样式,不是定义类样式,定义的话直接在h函数的第一个参数 . 的形式style - 用来设置行内样式,并且很容易设置动画eventlisteners - 用来注册或者移除事件的模块的使用步骤导入需要的模块init() 中注册模块h() 函数的第二个参数处设置成对象的形式,并传入模块中使用的数据import { init, h } from 'snabbdom' // 1. 导入模块 import {styleModule} from 'snabbdom/modules/style' import {eventlistenersModule} from 'snabbdom/modules/eventlisteners' // 2. 注册模块 let patch = init([ styleModule, eventlistenersModule ]) // 3. 使用 h() 函数的第二个参数传入模块需要的数据(对象) let vnode = h('div', [ h('h1', { style: { backgroundColor: 'red'} }, 'Hello Snabbdom'), h('p', { on: {click: eventHandler} }, '这是p标签') ]) function eventHandler () { console.log('点击我了') } let app = document.querySelector('#app') let oldVnode = patch(app, vnode) vnode = h('div', 'hello') patch(oldVnode, vnode) snabbdom源码概述如何学习源码先宏观了解带着目标看源码看源码的过程要不求甚解调试参考资料源码地址:https://github.com/snabbdom/snabbdomsnabbdom的核心init() 设置模块,创建patch函数使用h() 函数创建JavaScript对象(VNode) 来描述真实的DOMpatch() 比较新旧两个VNode把变化的内容更新到真实的DOM树Snabbdom 源码实现h函数在使用 Vue 的时候的 h() 函数和snabbdom中的h函数功能一样都是用来穿件VNode对象,但是Vue中增强了 h 函数,实现了组件的机制new Vue({ router, store, render: h => h(App) }).$mount('#app') h() 函数最早见于 hyperscript,使用 JavaScript 创建超文本,也就是 html 字符串Snabbdom 中的 h() 函数源于 hyperscript,但是不是用来创建超文本,而是创建 VNode函数重载概念参数个数或类型不同的函数JavaScript 中没有重载的概念,同名函数会被后者覆盖TypeScript 中有重载,不过重载的实现还是通过代码调整参数重载示例// 参数个数不同 function add (a: number, b: number) { console.log(a + b) } function add (a: number, b: number, c: number) { console.log(a + b + c) } add(1, 2) // 调用第一个add add(1, 2, 3) // 调用第二个add // 参数类型不同 function add (a: number, b: number) { console.log(a + b) } function add (a: number, b: string) { console.log(a + b + c) } add(1, 2) // 调用第一个add add(1, "2") // 调用第二个add h函数实现源码位置src/h.ts// h 函数的重载 export function h(sel: string): VNode; export function h(sel: string, data: VNodeData): VNode; export function h(sel: string, children: VNodeChildren): VNode; export function h(sel: string, data: VNodeData, children: VNodeChildren): VNode; export function h(sel: any, b?: any, c?: any): VNode { var data: VNodeData = {}, children: any, text: any, i: number; // 处理参数,实现重载的机制 if (c !== undefined) { // 处理三个参数的情况 // sel、data、children/text data = b; // 如果 c 是数组 if (is.array(c)) { children = c; } // 如果 c 是字符串或者数字 else if (is.primitive(c)) { text = c; } // 如果 c 是 VNode else if (c && c.sel) { children = [c]; } } else if (b !== undefined) { // 处理两个参数的情况 // 如果 b 是数组 if (is.array(b)) { children = b; } // 如果 b 是字符串或者数字 else if (is.primitive(b)) { text = b; } // 如果 b 是 VNode else if (b && b.sel) { children = [b]; } else { data = b; } } if (children !== undefined) { // 处理 children 中的原始值(string/number) for (i = 0; i这种遍历方式最终会有两种情况: 当老节点的所有子节点先遍历完 (oldStartIdx > oldEndIdx),循环结束新节点的所有子节点先遍历完 (newStartIdx > newEndIdx),循环结束如果老节点的数组先遍历完(新节点个数 > 老节点个数),说明新节点有剩余,把剩余节点批量插入到右边2.如果新节点的数组先遍历完(老节点个数> 新节点个数),说明老节点有剩余,把剩余节点批量删除 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |