当前位置: 首页 > news >正文

佛山网站建设专业品牌谁有做网站的朋友的V信

佛山网站建设专业品牌,谁有做网站的朋友的V信,下列是网页制作软件,全球十大室内设计公司排名榜该系列会逐步更新#xff0c;完整的讲解目前主流框架中底层相通的技术#xff0c;接下来的代码内容都会更新在 这里 为什么需要 Virtual Dom 众所周知#xff0c;操作 DOM 是很耗费性能的一件事情#xff0c;既然如此#xff0c;我们可以考虑通过 JS 对象来模拟 DOM 对象完整的讲解目前主流框架中底层相通的技术接下来的代码内容都会更新在 这里 为什么需要 Virtual Dom 众所周知操作 DOM 是很耗费性能的一件事情既然如此我们可以考虑通过 JS 对象来模拟 DOM 对象毕竟操作 JS 对象比操作 DOM 省时的多。 举个例子 // 假设这里模拟一个 ul其中包含了 5 个 li [1, 2, 3, 4, 5] // 这里替换上面的 li [1, 2, 5, 4] 从上述例子中我们一眼就可以看出先前的 ul 中的第三个 li 被移除了四五替换了位置。 如果以上操作对应到 DOM 中那么就是以下代码 // 删除第三个 li ul.childNodes[2].remove() // 将第四个 li 和第五个交换位置 let fromNode ul.childNodes[4] let toNode node.childNodes[3] let cloneFromNode fromNode.cloneNode(true) let cloenToNode toNode.cloneNode(true) ul.replaceChild(cloneFromNode, toNode) ul.replaceChild(cloenToNode, fromNode)当然在实际操作中我们还需要给每个节点一个标识作为判断是同一个节点的依据。所以这也是 Vue 和 React 中官方推荐列表里的节点使用唯一的 key 来保证性能。 那么既然 DOM 对象可以通过 JS 对象来模拟反之也可以通过 JS 对象来渲染出对应的 DOM 以下是一个 JS 对象模拟 DOM 对象的简单实现 export default class Element {/*** param {String} tag div* param {Object} props { class: item }* param {Array} children [ Element1, text]* param {String} key option*/constructor(tag, props, children, key) {this.tag tagthis.props propsif (Array.isArray(children)) {this.children children} else if (isString(children)) {this.key childrenthis.children null}if (key) this.key key}// 渲染render() {let root this._createElement(this.tag,this.props,this.children,this.key)document.body.appendChild(root)return root}create() {return this._createElement(this.tag, this.props, this.children, this.key)}// 创建节点_createElement(tag, props, child, key) {// 通过 tag 创建节点let el document.createElement(tag)// 设置节点属性for (const key in props) {if (props.hasOwnProperty(key)) {const value props[key]el.setAttribute(key, value)}}if (key) {el.setAttribute(key, key)}// 递归添加子节点if (child) {child.forEach(element {let childif (element instanceof Element) {child this._createElement(element.tag,element.props,element.children,element.key)} else {child document.createTextNode(element)}el.appendChild(child)})}return el} }Virtual Dom 算法简述 既然我们已经通过 JS 来模拟实现了 DOM那么接下来的难点就在于如何判断旧的对象和新的对象之间的差异。 DOM 是多叉树的结构如果需要完整的对比两颗树的差异那么需要的时间复杂度会是 O(n ^ 3)这个复杂度肯定是不能接受的。于是 React 团队优化了算法实现了 O(n) 的复杂度来对比差异。 实现 O(n) 复杂度的关键就是只对比同层的节点而不是跨层对比这也是考虑到在实际业务中很少会去跨层的移动 DOM 元素。 所以判断差异的算法就分为了两步 首先从上至下从左往右遍历对象也就是树的深度遍历这一步中会给每个节点添加索引便于最后渲染差异一旦节点有子元素就去判断子元素是否有不同Virtual Dom 算法实现 树的递归 首先我们来实现树的递归算法在实现该算法前先来考虑下两个节点对比会有几种情况 新的节点的 tagName 或者 key 和旧的不同这种情况代表需要替换旧的节点并且也不再需要遍历新旧节点的子元素了因为整个旧节点都被删掉了新的节点的 tagName 和 key可能都没有和旧的相同开始遍历子树没有新的节点那么什么都不用做 import { StateEnums, isString, move } from ./util import Element from ./elementexport default function diff(oldDomTree, newDomTree) {// 用于记录差异let pathchs {}// 一开始的索引为 0dfs(oldDomTree, newDomTree, 0, pathchs)return pathchs }function dfs(oldNode, newNode, index, patches) {// 用于保存子树的更改let curPatches []// 需要判断三种情况// 1.没有新的节点那么什么都不用做// 2.新的节点的 tagName 和 key 和旧的不同就替换// 3.新的节点的 tagName 和 key可能都没有 和旧的相同开始遍历子树if (!newNode) {} else if (newNode.tag oldNode.tag newNode.key oldNode.key) {// 判断属性是否变更let props diffProps(oldNode.props, newNode.props)if (props.length) curPatches.push({ type: StateEnums.ChangeProps, props })// 遍历子树diffChildren(oldNode.children, newNode.children, index, patches)} else {// 节点不同需要替换curPatches.push({ type: StateEnums.Replace, node: newNode })}if (curPatches.length) {if (patches[index]) {patches[index] patches[index].concat(curPatches)} else {patches[index] curPatches}} }判断属性的更改 判断属性的更改也分三个步骤 遍历旧的属性列表查看每个属性是否还存在于新的属性列表中遍历新的属性列表判断两个列表中都存在的属性的值是否有变化在第二步中同时查看是否有属性不存在与旧的属性列列表中 function diffProps(oldProps, newProps) {// 判断 Props 分以下三步骤// 先遍历 oldProps 查看是否存在删除的属性// 然后遍历 newProps 查看是否有属性值被修改// 最后查看是否有属性新增let change []for (const key in oldProps) {if (oldProps.hasOwnProperty(key) !newProps[key]) {change.push({prop: key})}}for (const key in newProps) {if (newProps.hasOwnProperty(key)) {const prop newProps[key]if (oldProps[key] oldProps[key] ! newProps[key]) {change.push({prop: key,value: newProps[key]})} else if (!oldProps[key]) {change.push({prop: key,value: newProps[key]})}}}return change }判断列表差异算法实现 这个算法是整个 Virtual Dom 中最核心的算法且让我一一为你道来。 这里的主要步骤其实和判断属性差异是类似的也是分为三步 遍历旧的节点列表查看每个节点是否还存在于新的节点列表中遍历新的节点列表判断是否有新的节点在第二步中同时判断节点是否有移动PS该算法只对有 key 的节点做处理 function listDiff(oldList, newList, index, patches) {// 为了遍历方便先取出两个 list 的所有 keyslet oldKeys getKeys(oldList)let newKeys getKeys(newList)let changes []// 用于保存变更后的节点数据// 使用该数组保存有以下好处// 1.可以正确获得被删除节点索引// 2.交换节点位置只需要操作一遍 DOM// 3.用于 diffChildren 函数中的判断只需要遍历// 两个树中都存在的节点而对于新增或者删除的节点来说完全没必要// 再去判断一遍let list []oldList oldList.forEach(item {let key item.keyif (isString(item)) {key item}// 寻找新的 children 中是否含有当前节点// 没有的话需要删除let index newKeys.indexOf(key)if (index -1) {list.push(null)} else list.push(key)})// 遍历变更后的数组let length list.length// 因为删除数组元素是会更改索引的// 所有从后往前删可以保证索引不变for (let i length - 1; i 0; i--) {// 判断当前元素是否为空为空表示需要删除if (!list[i]) {list.splice(i, 1)changes.push({type: StateEnums.Remove,index: i})}}// 遍历新的 list判断是否有节点新增或移动// 同时也对 list 做节点新增和移动节点的操作newList newList.forEach((item, i) {let key item.keyif (isString(item)) {key item}// 寻找旧的 children 中是否含有当前节点let index list.indexOf(key)// 没找到代表新节点需要插入if (index -1 || key null) {changes.push({type: StateEnums.Insert,node: item,index: i})list.splice(i, 0, key)} else {// 找到了需要判断是否需要移动if (index ! i) {changes.push({type: StateEnums.Move,from: index,to: i})move(list, index, i)}}})return { changes, list } }function getKeys(list) {let keys []let textlist list.forEach(item {let keyif (isString(item)) {key [item]} else if (item instanceof Element) {key item.key}keys.push(key)})return keys } 遍历子元素打标识 对于这个函数来说主要功能就两个 判断两个列表差异给节点打上标记总体来说该函数实现的功能很简单 function diffChildren(oldChild, newChild, index, patches) {let { changes, list } listDiff(oldChild, newChild, index, patches)if (changes.length) {if (patches[index]) {patches[index] patches[index].concat(changes)} else {patches[index] changes}}// 记录上一个遍历过的节点let last nulloldChild oldChild.forEach((item, i) {let child item item.childrenif (child) {index last last.children ? index last.children.length 1 : index 1let keyIndex list.indexOf(item.key)let node newChild[keyIndex]// 只遍历新旧中都存在的节点其他新增或者删除的没必要遍历if (node) {dfs(item, node, index, patches)}} else index 1last item}) }渲染差异 通过之前的算法我们已经可以得出两个树的差异了。既然知道了差异就需要局部去更新 DOM 了下面就让我们来看看 Virtual Dom 算法的最后一步骤 这个函数主要两个功能 深度遍历树将需要做变更操作的取出来局部更新 DOM整体来说这部分代码还是很好理解的 let index 0 export default function patch(node, patchs) {let changes patchs[index]let childNodes node node.childNodes// 这里的深度遍历和 diff 中是一样的if (!childNodes) index 1if (changes changes.length patchs[index]) {changeDom(node, changes)}let last nullif (childNodes childNodes.length) {childNodes.forEach((item, i) {index last last.children ? index last.children.length 1 : index 1patch(item, patchs)last item})} }function changeDom(node, changes, noChild) {changes changes.forEach(change {let { type } changeswitch (type) {case StateEnums.ChangeProps:let { props } changeprops.forEach(item {if (item.value) {node.setAttribute(item.prop, item.value)} else {node.removeAttribute(item.prop)}})breakcase StateEnums.Remove:node.childNodes[change.index].remove()breakcase StateEnums.Insert:let domif (isString(change.node)) {dom document.createTextNode(change.node)} else if (change.node instanceof Element) {dom change.node.create()}node.insertBefore(dom, node.childNodes[change.index])breakcase StateEnums.Replace:node.parentNode.replaceChild(change.node.create(), node)breakcase StateEnums.Move:let fromNode node.childNodes[change.from]let toNode node.childNodes[change.to]let cloneFromNode fromNode.cloneNode(true)let cloenToNode toNode.cloneNode(true)node.replaceChild(cloneFromNode, toNode)node.replaceChild(cloenToNode, fromNode)breakdefault:break}}) } 最后 Virtual Dom 算法的实现也就是以下三步 通过 JS 来模拟创建 DOM 对象判断两个对象的差异渲染差异 let test4 new Element(div, { class: my-div }, [test4]) let test5 new Element(ul, { class: my-div }, [test5])let test1 new Element(div, { class: my-div }, [test4])let test2 new Element(div, { id: 11 }, [test5, test4])let root test1.render()let pathchs diff(test1, test2) console.log(pathchs)setTimeout(() {console.log(开始更新)patch(root, pathchs)console.log(结束更新) }, 1000)当然目前的实现还略显粗糙但是对于理解 Virtual Dom 算法来说已经是完全足够的了。 文章中的代码你可以在 这里 阅读。本系列更新的文章都会更新在这个仓库中有兴趣的可以关注下。 下篇文章的内容将会是状态管理敬请期待。 原文发布时间为2018年06月02日原文作者夕阳本文来源 掘金 如需转载请联系原作者
http://wiki.neutronadmin.com/news/342588/

相关文章:

  • 网站做弹窗网站安全检测网站
  • 用vs做网站后台泊头网站优化
  • 上海网站建设 迈济南网络招聘
  • 短视频seo厂家湖北seo公司
  • 绿色风格 网站WordPress文章如何折叠插件
  • 青岛网站制作公司 网络服务装修案例实景图
  • 珠宝网站设计文案泉州手工外发加工网
  • 建设一个简单的网站wordpress登录qq微信登录
  • 网站同城在线哪里做如何申请营业执照
  • 做网站需要哪些方面的支出wordpress 注册钩子
  • 一级a视网站 做爰片做采购 通常在什么网站看
  • 国家建设免费论文网站房地产新闻最新政策2022
  • 嘉兴网站开发中国建筑工程信息官网
  • 企业网站建设维护合同书wordpress怎么关注站点
  • 杭州宣传片制作公司广安网站seo
  • 做电商网站的感想wordpress 猜你喜欢
  • 许昌网站建设汉狮怎么样商品网站源码
  • 游戏网站建设成功案例wordpress 登录后页面空白
  • 郑州网站建设推荐美软科技关键词资源
  • 免费备案网站空间北京谁会做网站开发
  • 如何进行网站的宣传和推广深圳广告策划公司
  • 免费建设小学校网站怎样自己制作网站
  • 深圳网站建设公司团队网站综合营销方案设计
  • 郑州网站优化公司排名jsp网站开发目的及意义
  • 微网站建设哪里便宜服装网站建设平台
  • 大庆市建设局网站工程建设标准化
  • 网站建设公司十年乐云seo网站内部seo优化包括
  • 微信建微网站seo网站诊断
  • wordpress多站点好用吗彩票网站怎么做
  • 全球首个完全响应式网站自助建设平台在中国诞生网站制作课程介绍