现在时代的开发最求高效率,高应用,高频率快速迭代开发!!!,JavaScript做为大前端的唯一开发语言,在很多的时候原生的js并不能满足热门的开发需求,于是各种高效开发的框架层出不穷,基于最近几年新出的这些框架的底层大多依赖于虚拟dom的快速开发,并不同于以往的jQuer的dom开发,极大地提高了开发人员的开发效率和维护效率
轮子会开固然很好,但是会开久了,就会深纠于如何自己造出轮子!!!
提起轮子那么一定要知道虚拟dom这个轮子到底替我们干了什么,下面一张图解释一下,浏览器解析js的过程 基于这张图我们不难看出我们平常工作的项目的页面原理与结构,其中将html与style结合起来的便是我们的Render Tree也就是渲染树,渲染树将两者结合使用JavaScript进行控制,将项目比作房屋,那么html是屋子的墙体骨骼,style就是屋子的粉饰装修,js就是各种房屋使用功能的控制开关!!!,虚拟dom就是简化我们创建房屋的高效工具
如果你写过那种php加jq的前后端不分离的老项目,那么你开发的时候,每次的调试一定伴随着大量的dom布局渲染,每次渲染都伴随着大量的内存被占用消耗加载,其中的原理如下图
计算机就是将我们的代码属性编译成我们所描写的格式,每次代码属性的更改就伴随着大量的重新计算,html的页面加载时从上到下的,js也是如此,每次先行计算你所书写的html的template代码然后计算js方法,最后是你的style属性,style属性也可以放到项目的上级,这个无伤大雅,反正都是要 执行一遍的,计算页面布局,计算js的逻辑和内存占比,计算无效变量的垃圾回收(所以为了不污染全局环境,养成一个良好的行为习惯,避免使用var的命名变量,减少消耗)
言归正传,开发人员为了避免使用大量节点的消耗,于是便使用起render tree的函数,写习惯react的小伙伴对此应该最为习惯!!!
render 函数即渲染函数,它是个函数,它的参数也是个函数——即 createElement,我们重点来说 createElement 参数。
render 函数的返回值(VNode)
VNode(即:虚拟节点),也就是我们要渲染的节点。
render 函数的参数(createElement)
createElement 是 render 函数 的参数,它本身也是个函数,并且有三个参数
createElement 函数的返回值(VNode)
createElement 函数的返回值是 VNode(即:虚拟节点)。
createElement 函数的参数(三个)
一个 HTML 标签字符串,组件选项对象,或者解析上述任何一种的一个 async 异步函数。类型:{String | Object | Function}。必需。
一个包含模板相关属性的数据对象你可以在 template 中使用这些特性。类型:{Object}。可选。
子虚拟节点 (VNodes),由 createElement() 构建而成,也可以使用字符串来生成“文本虚拟节点”。类型:{String | Array}。可选。
import {h,} from "snabbdom";
import './index.less'
const demoDOM = [
h('h1',
{
class: {
"show-demo-title": true
}
},
"Thanks You"),
h('div',
{
class: {
"show-demo-img-5": true
}
})
]
const content = {
desc: [
{
text: "本次分享主要介绍了浏览器渲染引擎的渲染过程以及虚拟DOM",
class: { "vnode-text": true }
},
{
text: "通过渲染过程我们可以清晰的了解前端代码是如何转换成图形展示在用户浏览器上。",
class: { "vnode-text": true }
},
{
text: "虚拟DOM在目前流行的几大框架中都作为核心的一部分使用,可见其性能的高效,虚拟DOM为前端开发提供更爽、更高效的研发模式,同时保持着还不错的性能。",
class: { "vnode-text": true }
}
]
}
const descList = content.desc.map(element => h("p", { class: element.class }, element.text))
const DOM = [...descList]
export default { demoDOM, DOM }
这里是一个小的页面dom的布局书写,借用的snabbdom这个强大的dom库!!!,写成一个render函数拿到主视图层进行渲染和控制!
import {
h,
} from "snabbdom";
import demo1 from "./demo1";
import demo2 from "./demo2";
import demo3 from "./demo3";
import demo4 from "./demo4";
import demo5 from "./demo5";
import "./index.less"
function handleSelect(index, state) {
let selectDOM = {
demoDOM: [],
DOM: [],
}
let style = {}
let showClass = {
"show-demo-box": true
}
if (state) {
style = {
opacity: "0",
transform: "scale(0)",
}
} else {
style = {
opacity: "1",
transform: "scale(1)"
}
}
switch (index) {
case 1:
selectDOM = demo1
break
case 2:
selectDOM = demo2
break
case 3:
selectDOM = demo3
break
case 4:
selectDOM = demo4
break
case 5:
selectDOM = demo5
break
default:
style = {
opacity: "0",
transform: "scale(0)"
}
break
}
return {
demoDOM: h('div',
{
key: "show-demo-box",
style: style,
class: showClass,
}, selectDOM.demoDOM),
DOM: selectDOM.DOM
}
}
export default handleSelect
在这里我们做视图层的html结构代码,并进行处理
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
import '@/css/vnode.less'
import demoDOM from "./components"
class Vnode {
constructor() {
this.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
]);
this.vnode = null
this.newVnode = null
this.vnodeDemo = null
this.newVnodeDemo = null
this.data = this.data()
this.demoDOM = demoDOM
}
run() {
window.addEventListener("DOMContentLoaded", () => {
this.newVnode = this.list()
this.vnode = this.patch(document.getElementById("container"), this.view())
this.newVnodeDemo = this.demoDOM().demoDOM
this.vnodeDemo = this.patch(document.getElementById("demoContainer"), this.newVnodeDemo)
this.render()
this.renderDemo()
});
}
render() {
this.vnode = this.patch(this.vnode, this.view())
}
renderDemo() {
this.vnodeDemo = this.patch(this.vnodeDemo, this.newVnodeDemo)
}
view() {
return h("div", { class: { "vnode-box": true } }, [
h("h2", { key: this.data.head, class: { "vnode-title": true }, }, this.data.head),
h(
"div", { class: { "vnode-content": true } }, this.newVnode
),
]);
}
select(row) {
if (row) {
const DOM = this.demoDOM(row?.rank)
this.data.head = row.title
this.data.selected = row.rank
this.newVnode = this.row(DOM.DOM)
this.newVnodeDemo = DOM.demoDOM
} else {
const DOM = this.demoDOM(this.data.selected, true)
this.data.head = "目录"
this.data.selected = 0
this.newVnode = this.list()
this.newVnodeDemo = DOM.demoDOM
}
this.render()
this.renderDemo()
}
list() {
const list = this.data.movies.map((movies) => {
return h("h3",
{
class: { "row": true },
on: {
click: () => {
this.select(movies)
}
}
}, movies.title)
})
return h("div",
{
key: "row-box",
class: { "row-box": true },
}, list)
}
row(DOM) {
const backBtn = h("div",
{
class: { "button-box": true },
},
[h("div",
{
class: { "button": true },
on: {
click: () => {
this.select()
}
}
}, '返回目录')])
return h("div",
{
key: "text-box",
class: { "text-box": true }
}, [backBtn, h("div",
{
key: "text-show-box",
class: { "text-show-box": true }
}, DOM)])
}
data() {
return {
selected: 0,
head: "目录",
movies: [
{
rank: 1,
title: "浏览器解析渲染过程",
},
{
rank: 2,
title: "Render Tree",
},
{
rank: 3,
title: "布局渲染",
},
{
rank: 4,
title: "虚拟DOM",
},
{
rank: 5,
title: "总结",
},
]
}
}
}
const domVnode = new Vnode()
domVnode.run()
<div class="main">
<div id="container"></div>
<div class="placeholder"></div>
<div id="demoContainer"></div>
</div>
这是他的主index.html是个很小很小的html结构,于是基于webpack搭建的一个小的demo就这样出现了!
项目demo
百度网盘:https://pan.baidu.com/s/1pioBqCO8TEwAobe57pUs2A
提取码:dae0