mini?vue渲染的简易实现

本文主要介绍了mini-vue渲染的简易实现,主要简单来实现一个虚拟dom渲染真实dom,以及更新的方法。具有一定的参考价值,感兴趣的小伙伴们可以参考一下

mini?vue渲染的简易实现,久久派带你了解更多相关信息。

目录
  • 前言
  • 目标
    • 第一步:
    • 第二步:
    • 第三步:
    • 第四步:
  • 总结

    前言

    目前的主流框架Vue、React 都是通过Virtual Dom(虚拟Dom)来实现的,通过Virtual Dom技术提高页面的渲染效率。Vue中我们通过在template模板中编写html代码,React中我们通过在内部的一个 render 函数里编写html代码,这个函数通过 jsx 编译后,实际会输出一个h函数,也就是我们的Virtual Dom(虚拟Dom),下面简单来实现一个虚拟dom渲染真实dom,以及更新的方法。

    目标

    主要实现以下三个功能:

    • 通过h函数返回Vnodes;
    • 通过 mount 函数将 虚拟dom 挂载到真实节点上;
    • 通过 patch 函数通过 newVnodes 与 oldVnodes比较来实现dom的更新;

    第一步:

    在body标签内创建一个id为app的节点,后面会将虚拟节点挂载到这个节点上,而renderer.js用来实现上面三个功能。

    <body>  <p id=\"app\"></p>  <script src=\"./renderer.js\"></script></body>

    第二步:

    编写h函数,用来返回tag(标签元素)、props(属性对象)、children(子节点),简单来说虚拟Dom就是一个普通javaScript对象。

    // renderer.js const h = (tag, props, children) => {  return {    tag,    props,    children  }}

    那么通过这个h函数,我们简单来看看下面一段代码会输出什么?

    const vdom = h(\"p\", {class: \"header\"}, [  h(\"h2\", null, \"Hello World\"),  h(\"h2\", {id: 0}, h(\"span\", null, \"啦啦啦啦\")) // 当props没有值,必须传一个null,不能不传]);console.log(vdom);

    由下图可以看出,通过h函数,给我们返回了一个javaScript对象,这个便是Virtual Dom(虚拟Dom),形成树状节点。

    mini?vue渲染的简易实现

    那么我们拿到这个vnodes,如何挂载到真实节点上呢?下面我们来看看第三步

    第三步:

    我们先创建一个 mount 函数,需要传入两个参数,第一个是我们刚刚通过h函数返回的vnodes,第二个参数是我们需要将这些vnode挂载到哪个节点上,接下来看代码:

    const mount = (vnodes, app) => {  // 通过vnodes里面的tag值,比如(\"p\", \"h2\"),创建一个节点  // 同样在vnodes对象里保存一份真实dom,方便以后进行更新,新增等操作  const el = vnodes.el = document.createElement(vnodes.tag);   // 拿到这个节点后,我们通过判断props值,进行添加属性  if (vnodes.props) {    for (let key in vnodes.props) {      // 这儿通过拿到props中key值后,在做判断      let value = vnodes.props[key];      if (key.startsWith(\'on\')) {        // 比如用户写了一个onClick=\"changeData\",处理为监听函数的事件        el.addEventListener(key.slice(2).toLowerCase(), value)      } else {        el.setAttribute(key, value)      }      // 这下面还有些判断,比如指令啊v-if等等,进行边界化处理    }  }  // 处理完props后,最后是Children节点  if (vnodes.children) {    if (typeof vnodes.children === \'string\') {      // 如果这儿是个字符串类型,那么就可以直接添加到节点中去      el.textContent = vnodes.children    } else {      // 这种情况为数组类型,含有子节点,通过遍历,再生成子节点      vnodes.children.forEach(vnode => {        // 通过递归,再次将子节点挂载到当前节点上去        mount(vnode, el)      })    }  }  // 最终将这个真实节点挂载到我们传入的app节点中去  app.appendChild(el);}const app = document.querySelector(\"#app\")mount(vdom, app)

    我们来看看通过mount函数挂载的实际效果:

    mini?vue渲染的简易实现

    那么到这里,我们就已经实现了通过虚拟dom来创建真实dom了,那在vue当中是如何对这些dom进行更新操作呢,接下来我们再创建一个patch函数(更新):

    第四步:

    通过patch函数,我们需要传入两个参数(vnodes1、vnodes2),分别是新的虚拟dom和旧的虚拟dom,通过比较新旧虚拟dom,来指定更新哪些节点。(这儿不考虑key值,需要参考key值可以查看链接:https://www.jb51.net/article/219078.htm)

    // patch函数 n1: 旧节点、 n2:新节点// 在vue源码中,旧vnode,新vnode分别用n1, n2表示const patch = (n1, n2) => {  // 在上面我们通过mount函数给n2添加了节点属性el,绑定到n2上  const el = n2.el = n1.el  // 首先,还是从两个中的tag入手  if (n1.tag == n2.tag) {    // n1、n2的tag相同,再对比props    const n1Props = n1.props || {};    const n2Props = n2.props || {};    // 分别取到n1,n2中的props,进行比较    for (let key in n2Props) {      // 取出n2中所有key,判断n2的key值和n1key值是否相同      const n1Value = n1Props[key] || \'\';      const n2Value = n2Props[key] || \'\';      if (n1Value !== n2Value) {        if (key.startsWith(\'on\')) {          // 比如用户写了一个onClick=\"changeData\",处理为监听函数的事件          el.addEventListener(key.slice(2).toLowerCase(), n2Value)        } else {          el.setAttribute(key, n2Value)        }      }      // 相同则不作处理    }    for (let key in n1Props) {      const oldValue = n1Props[key];      if (!(key in n2Props)) {        if (key.startsWith(\'on\')) {          el.removeEventListener(key.slice(2).toLowerCase(), oldValue)        } else {          el.removeAttribute(key)        }      }    }  } else {    // tag不同,拿到n1的父节点    const n1Parent = n1.el.parentElement;    // 通过removeChild将旧节点从父节点中移除,然后将n2挂载到父节点中    n1Parent.removeChild(n1.el); //n1.el是通过mount函数往对象里添加的真实dom节点    mount(n2, n1Parent)  }  // 最后处理children,相对于来说复杂些  // children可以为字符串也可以为数组,那么先看字符串时怎么处理  const n1Children = n1.children || [];  const n2Children = n2.children || [];  if (typeof n2Children === \"string\") {    // 如果新节点内容为字符串,直接使用innerhtml进行替换    el.innerHtml = n2Children;  } else {    // 下面情况是n2.children为数组情况时    if (typeof n1.children === \"string\") {      // n1.children为字符串,n2.children为数组      el.innerHtml = \'\'; // 先将节点内容情况,再讲新的内容添加进去      mount(n2.children, el)    } else {      // 两种都为数组类型时,这儿不考虑key值      const minLength = Math.min(n1Children.length, n2Children.length);      for (let i = 0 ; i < minLength ; i++) {        patch(n1Children[i], n2Children[i]);      }      if(n2Children.length > n1Children.length) {        n2Children.slice(minLength).forEach(item => {          mount(item, el)        })      }      if(n2Children.length < n1Children.length) {        n1Children.slice(minLength).forEach(item => {          el.removeChild(item.el)        })      }    }  }}

    上面简单的实现了patch的作用,其实就是我们说的diff算法(当然这儿没有考虑key值的情况,只能两个依次比较),同一层级进行比较。现在模拟演示一下,看看是否能更新成功:

    const vdom = h(\"p\", {class: \"header\"}, [  h(\"h2\", null, \"Hello World\"),  h(\"h2\", {id: 0}, [h(\"span\", null, \"啦啦啦啦\")]) // 当props没有值,必须传一个null,不能不传]);const app = document.querySelector(\"#app\")mount(vdom, app)setTimeout(()=> { // 3秒后向patch传入新旧Vnodes  const vdom1 = h(\"p\", {class: \"header\"}, [    h(\"h3\", null, \"Hello World\"),    h(\"span\", null, \"哈哈哈\")  ])  patch(vdom, vdom1)},3000)

    通过下图,我们可以看到已经简单的实现了虚拟dom更新节点。

    mini?vue渲染的简易实现

    总结

    简单的实现了下虚拟Dom生成真实节点,然后通过patch进行更新。再去看看源码,就能更好的理解vue的渲染器是如何实现的了。

    到此这篇关于mini-vue渲染的简易实现的文章就介绍到这了,更多相关mini-vue渲染内容请搜索趣讯吧以前的文章或继续浏览下面的相关文章希望大家以后多多支持趣讯吧!

    版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请发送邮件至 55@qq.com 举报,一经查实,本站将立刻删除。转转请注明出处:https://www.szhjjp.com/n/10843.html

    (0)
    nan
    上一篇 2021-08-08
    下一篇 2021-08-08

    相关推荐

    • 广东祖孙3人食用毒蘑菇致死?野蘑菇怎样识别有毒

      大家都以为毒蘑菇的颜色一定非常鲜艳,不鲜艳的蘑菇都是没毒的,错!现在毒蘑菇的品种太多了,有些蘑菇伪装的跟普通蘑菇一样,人吃了必死无疑,近日广东祖孙3人食用毒蘑菇致死,那么野蘑菇怎样识别有毒没毒?马上来了解一下吧!

      2021-09-09
      0
    • 7月上映!《碟中谍7》全新预告发布:阿汤哥街头飙车 3000米跳崖飞车 !

      5月18日消息,日前,“碟中谍系列电影”官微发布《碟中谍7:致命清算(上)》全新预告片,更多惊险刺激的动作场面出炉,包括摩托车跳3000米高山悬崖、火车顶打斗、街头飙车等等。本片分为上下两部上映,上篇将于7月12日上线北美院线,这两部或将成为整个系列的完结篇。《碟中谍7》由《碟中谍5》《碟中谍6》导

      热点头条 2023-05-19
      0
    • 瑞丽航空怎么样(瑞丽航空真的很差吗)

      【民航事儿】瑞丽航空于7月28日引进了第21架客机,这是无锡交通产业集团控股瑞丽航空之后引进的首架飞机,新飞机的引进将进一步扩充瑞丽航空的运力规模,为旅客提供更加便利的出行服务。7月28日17:40,编号为B-221G的客机顺利抵达昆明长水国际机场,

      2022-01-13
      0
    • 表扬一个人孝顺的句子(父慈子孝的名言名句)

      1、人之行,莫大于孝。孝,是为人之本;敬,乃做人之根。2、父慈而教,子孝而箴。出自司马光的《家范》。父亲慈祥而能训教,儿子孝顺而能自我规劝。3、慈孝之心,人皆有之。出自北宋文学家苏

      2022-01-19 热点头条
      0
    • 公鸡打鸣影响考生休息被报警 ! 主人:今晚吃鸡 !

      公鸡打鸣影响高考生休息被报警,接到民警电话后,主人当即表示“今天晚上就吃鸡了。”6月7日,高考首日,自贡市公安局自流井分局新街派出所接到群众报警,说自己家孩子今年参加高考,附近一只公鸡从早上五点多就开始鸣叫,影响到孩子休息了。接警后,派出所值班民警考虑到是高考首日,明天还有一天重要的考试,如果不及时

      热点头条 2023-06-09
      0
    • Django3基于WebSocket实现WebShell的详细过程

      最近工作中需要开发前端操作远程虚拟机的功能,简称WebShell,普通应用大部分用的都是wsgi.py配合nginx部署线上服务.这次主要使用asgi.py,具体实现过程跟随小编一起看看吧

      2021-08-26
      0

    发表回复

    登录后才能评论