阅读 524

Vue之虚拟dom(vue虚拟dom的理解)

1. 什么是虚拟dom?

虚拟dom本质上就是一个普通的JS对象,用于描述视图的界面结构

例子

<div id="app">     <h1>hello vue</h1> </div> 复制代码

{     tag:"div",     attrs: {         id: "app"     },     children:[         tag:"h1",         attrs: undefined,         children:[             tag:undefined,             text:"hello vue"         ]     ] } 复制代码

在vue中,每个组件都有一个render函数(即使没有直接的,也有间接生成的)

  • 有render函数

<!DOCTYPE html> <html>   <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>Document</title>   </head>   <body>     <div id="app">       <h1>{{title}}</h1>     </div>     <script src="./vue.min.js"></script>     <script>       var div1 = document.getElementById('app');       var vm = new Vue({         // 组件配置         el:"#app",         data: {           title: 'Hello Vue!!!',         },         template: '<ul><li>{{title}}</li></ul>',         render(h) {           // 目标,创建虚拟dom:这个组件到底要显示啥           // h是一个函数,专门用于创建虚拟dom           console.log('render');           var vnode = h(             'div',             {               // 虚拟dom的一些配置               attrs: {                 id: 'app',               },             },             // 子元素             [h('h1', `标题:${this.title}`)]           );           console.log(vnode);           return vnode;         },       });     </script>   </body> </html> 复制代码

  • 没有render,找template,并且有template;

  • 没有render,没有template,找el.outerHTML

  • -  


    • 编译template, 变成一个render函数

每个render函数都会返回一个虚拟dom树,这也就意味着每个组件都对应一棵虚拟DOM树

2. 为什么需要虚拟dom?

每次视图依赖的数据变化,那么重新执行render函数

vue中,渲染视图会调用render函数,这种渲染不仅发生在组件创建时,同时发生在视图依赖的数据更新时。

例子:

如果在渲染时,直接使用真实DOM,由于真实DOM的创建、更新、插入等操作会带来大量的性能损耗,从而就会极大的降低渲染效率。
因此,vue在渲染时,使用虚拟dom来替代真实dom,主要为解决渲染效率的问题。

做一个创建1千万个对象的dom节点的时间对比

<!DOCTYPE html> <html>   <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>Document</title>   </head>   <body>     <script>       var times = 10000000;       console.time('js object');       for (var i = 0; i < times; i++) {         var obj = {           tag: 'div',           attrs: {             class: 'asdfasdf',           },         };       }       console.timeEnd('js object');       console.time('html object');       for (var i = 0; i < times; i++) {         var obj = document.createElement('div');       }       console.timeEnd('html object');     </script>   </body> </html> js object: 103.89111328125 ms html object: 2967.419189453125 ms 复制代码

3. 虚拟dom是如何转换为真实dom的?

在一个组件实例首次被渲染时

  1. 先生成虚拟dom树

  2. 然后根据虚拟dom树创建真实dom,并把真实dom挂载到页面中合适的位置。

此时,每个虚拟dom便会对应一个真实的dom。

例子:

<!DOCTYPE html> <html>   <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>Document</title>   </head>   <body>     <div id="app">       <h1>{{title}}</h1>     </div>     <script src="./vue.min.js"></script>     <script>       var div1 = document.getElementById('app');       var vm = new Vue({         // 组件配置         el:"#app",         data: {           title: 'Hello Vue!!!',         },         template: '<ul><li>{{title}}</li></ul>',         render(h) {           // 目标,创建虚拟dom:这个组件到底要显示啥           // h是一个函数,专门用于创建虚拟dom           console.log('render');           var vnode = h(             'div',             {               // 虚拟dom的一些配置               attrs: {                 id: 'app',               },             },             // 子元素             [h('h1', `标题:${this.title}`)]           );           console.log(vnode);           return vnode;         },       });       var div2 = document.getElementById('app');       console.log(div1 === div2);        // false     </script>   </body> </html> 复制代码

虚拟dom有elm属性是标签


如果一个组件受响应式数据变化的影响,需要重新渲染时,它仍然会重新调用render函数,创建出一个新的虚拟dom树,用新树和旧树对比,通过对比,vue会找到最小更新量,然后更新必要的真实dom节点。

对比的是虚拟dom,改动的是真实dom。
这样一来,就保证了对真实dom达到最小的改动。

4. 模板和虚拟dom的关系


vue框架中有一个compile模块,它主要负责 将模板template转换(编译)为render函数,而render函数调用后将得到虚拟dom。

编译的过程分两步:


    1. 将模板字符串转换成为AST

    2. AST转换为render函数

运行时编译

如果使用传统的引入方式,则编译发生在组件第一次加载时,这称之为运行时编译。

比如直接引用vue.js

模板预编译

如果是在vue-cli的默认配置下,编译发生在打包时,这称之为模板预编译。

打包结果包含render函数,不再是template模板了


编译是一个极其耗费性能的操作,预编译可以有效的提高运行时的性能,而且,由于运行的时候已不需要编译,vue-cli在打包时会排除掉vue中的compile模块,以减少打包体积

要想打包的时候把编译器也打包进去,这样运行的时候也可以编译,需要添加配置文件vue.config.js

不建议这么做

module.exports = {   runtimeCompiler: true, }; 复制代码


模板的存在,仅仅是为了让开发人员更加方便的书写界面代码
vue最终运行的时候,最终需要的是render函数,而不是模板,因此,模板中的各种语法,在虚拟dom中都是不存在的,它们都会变成虚拟dom的配置

例子:模板template对应的render函数

<template>   <div id="app">     <button @click="show = !show">toggle</button>     <h1 v-if="show" @click="handleClick">hello vue</h1>   </div> </template> <script> // vue组件的配置对象 export default {   data() {     return {       show: true,     };   },   methods: {     handleClick() {       console.log('click!');     },   },   render(h) {     return  h('div', { attrs: { id: 'app' } }, [       h(         'button',         {           on: {             click: () => {               this.show = !this.show;             },           },         },         'toggle'       ),       this.show         ? h('h1', { on: { click: this.handleClick } }, 'hello vue')         : undefined,     ]);   }, }; </script> 复制代码

AST描述我们写的模板,比如v-for 100,对应AST的一个节点

虚拟dom描述了页面上应该有啥,比如就有100个节点

案例:自动生成目录

现在需要制作一个组件,该组件可以根据其插槽中的内容,自动生成一个目录

效果图

任务

1、根据传入的h元素生成a元素

2、设置每一个h元素的id

 const headingFlag = /^h\d$/; // 匹配h元素的正则 export default {   render(h) {     // 得到插槽中的所有虚拟dom     const vnodes = this.$slots.default;     // 拿到所有的标题元素     const headNodes = vnodes.filter((v) => headingFlag.test(v.tag));     const links = [];     // ...根据传入的h元素生成a元素并且设置每一个h元素的id     return h('div', [       h(         'div',         {           attrs: {             class: 'toc',           },         },         links       ),       h('div', vnodes),     ]);   }, }; 复制代码

组件使用方式如下:

<TOC>   <h1>Chapter 1</h1>   <p>     Lorem ipsum dolor sit amet consectetur adipisicing elit. Suscipit fugit     iure dolorem! Perspiciatis, ea! Ea ullam molestiae tempora obcaecati     quibusdam harum, beatae quidem quod aspernatur, nam odit eos voluptate     consequatur.   </p>   <h2>Chapter 1-1</h2>   <p>     Lorem ipsum, dolor sit amet consectetur adipisicing elit. Maiores est     esse ipsam molestiae fugit voluptate nihil corrupti, officia blanditiis     placeat quae ex beatae necessitatibus eius harum, quo facilis, assumenda     nam doloremque. Harum exercitationem tempore dicta voluptate optio quas     quia doloribus minus itaque facere, est soluta consectetur porro.     Deserunt, consequuntur doloribus?   </p>   <p>     Voluptatum, sint rem, mollitia exercitationem cum, iste debitis error ab     alias aperiam laudantium! Dolores in veritatis, iure ducimus ab, eveniet     modi voluptatum pariatur asperiores totam minus cum rem inventore saepe     voluptas, veniam id doloremque nisi consectetur similique consequuntur     magnam eligendi aliquid nostrum? Expedita, enim quam quis iure quia     quidem impedit.   </p>   <p>     Assumenda architecto repudiandae nemo ullam tempora quod a nobis magnam     laborum impedit culpa cupiditate magni, aspernatur, est iusto molestiae?     Dignissimos ullam ipsa nesciunt quas autem, nemo quaerat tempora cumque     accusamus quae odit! Cumque dolor exercitationem, sint similique qui     numquam repellendus officia quasi corrupti repellat est accusamus     perspiciatis. Vero, ipsa accusantium?   </p>   <h2>Chapter 1-2</h2>   <p>     Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex cupiditate,     consectetur incidunt fugiat mollitia amet quam nihil tempora odit vel     beatae asperiores sit iste, dignissimos error et repudiandae ducimus     tenetur!   </p>   <h1>Chapter 2</h1>   <p>     Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quis, harum     exercitationem! Exercitationem rerum odio at ab modi sequi sit iure     animi, expedita, culpa labore nihil architecto. Rem veniam et animi.   </p>   <h2>Chapter 2-1</h2>   <p>     Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit     facere libero nisi possimus, ex neque quod officiis inventore aliquam ad     blanditiis quas est non aut nulla saepe perspiciatis illum sequi ratione     molestiae in laborum facilis? Laborum dolor beatae optio quis quaerat     rerum aliquam eaque maxime, obcaecati, atque harum, enim delectus!   </p>   <p>     Aliquam harum, minima est alias enim cupiditate explicabo quae     reprehenderit quasi inventore consectetur provident neque labore? Nulla,     consequuntur aut debitis quasi assumenda error vitae repellendus iure     eius ex nostrum repudiandae, sint porro. Tempore enim illum natus,     ipsam, pariatur blanditiis nostrum nesciunt, odio sed quod iure a quo     aliquam aut aliquid!   </p>   <p>     Fuga ullam excepturi sit voluptatem. Optio molestias esse hic quae minus     accusantium aut sapiente ullam repellat excepturi! Obcaecati quam     mollitia beatae sit ullam expedita libero temporibus blanditiis.     Eligendi aliquid neque itaque exercitationem voluptas, hic, deserunt     molestias cumque inventore perferendis iusto quam ipsa, asperiores     magnam dignissimos. Natus iusto reprehenderit error neque.   </p>   <h2>Chapter 2-2</h2>   <p>     Lorem ipsum dolor sit amet consectetur adipisicing elit. Eveniet vel     neque rem quos nam voluptatibus sit recusandae ducimus facere. Illum, id     nemo dicta quisquam harum corporis dolor similique rerum iusto     cupiditate quidem dolorum delectus nobis voluptas, dolorem, natus     maiores animi totam atque consectetur laboriosam vero modi. Fugit     repellendus voluptatibus ratione.   </p>   <p>     Laudantium consequuntur pariatur nulla autem obcaecati repudiandae alias     eaque sed maxime impedit. Earum, cupiditate, quibusdam consequuntur     rerum omnis minus nostrum sapiente inventore similique in atque     deleniti. Adipisci doloribus necessitatibus aspernatur neque     accusantium, ducimus illo, ipsa pariatur, perspiciatis illum temporibus     quaerat maiores harum corrupti ea facilis magnam error dignissimos     accusamus dolores?   </p>   <p>     Nulla repudiandae voluptas velit. Necessitatibus pariatur nihil iure     illum voluptate error optio veniam consequuntur eius maiores dicta,     dolor praesentium obcaecati at architecto porro? Quas architecto dicta     recusandae et molestias, ab quo molestiae voluptas enim qui, nostrum rem     sed eveniet voluptates delectus iste! Officiis corrupti minus rem     mollitia, consequuntur cupiditate velit?   </p>   <h1>Chapter 3</h1>   <p>     Lorem ipsum, dolor sit amet consectetur adipisicing elit. Dolores,     molestiae. Iusto sapiente itaque libero expedita qui incidunt similique     voluptates asperiores laboriosam, aspernatur at excepturi quis voluptate     ut porro sunt rem!   </p>   <h2>Chapter 3-1</h2>   <p>     Lorem ipsum dolor, sit amet consectetur adipisicing elit. Iste ducimus     omnis quia, mollitia nemo officiis molestias deserunt explicabo illo ad     repellat eveniet id harum, rem provident, maxime autem quod. Aut ad     minima nihil facere illo, facilis, reiciendis quos nulla excepturi     maiores molestias id rerum unde fugiat sint natus dolor distinctio.   </p>   <p>     Sed ipsum optio veritatis alias repellat aut, a enim quia, perferendis     repellendus, inventore possimus quasi quibusdam provident repudiandae?     Unde, veritatis! Possimus dicta id facere maiores est voluptas vero     odit, nihil delectus, magnam inventore nemo libero accusamus architecto     magni blanditiis sint distinctio eos fugit pariatur, sed qui minima.     Veniam, aliquid tenetur!   </p>   <p>     Voluptate temporibus debitis minima ipsam ex quidem ipsa dolor ducimus.     Soluta commodi dignissimos, blanditiis architecto nisi animi corrupti     placeat quos ratione recusandae consequatur quidem. Recusandae, illo.     Rem suscipit repudiandae quisquam atque dicta deleniti ipsam cumque     voluptate repellendus porro necessitatibus facere dignissimos explicabo     nisi, laboriosam illum laudantium earum nostrum ullam omnis.   </p>   <h2>Chapter 3-2</h2>   <p>     Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime numquam     omnis quam quos, nobis magni mollitia quas! Vitae expedita tempore     laudantium optio, harum ipsam corporis a similique incidunt voluptatem     labore praesentium? Vitae magni, accusamus iusto labore asperiores fuga     et sequi inventore officiis optio neque illum eius molestias excepturi     obcaecati qui?   </p>   <p>     Vero quisquam illum earum provident minus quibusdam dolor, quasi dolores     aperiam fuga, ratione magnam quo unde perferendis cum tenetur animi     facilis doloremque laboriosam totam reprehenderit! Praesentium eum illo     expedita autem repudiandae omnis saepe animi illum quasi eos commodi     aspernatur magni dignissimos neque delectus fugiat, molestiae ratione.     Ipsum adipisci provident delectus?   </p>   <p>     Incidunt eaque unde voluptates labore dignissimos amet commodi saepe     inventore, doloremque vero sequi maxime minus cum provident quisquam     natus, magni eveniet at itaque? Quisquam, quibusdam ab. Voluptatem     vitae, nobis doloribus suscipit ut quaerat error officiis necessitatibus     maiores libero quia, atque veniam impedit architecto natus obcaecati!     Porro obcaecati ratione deleniti aut.   </p> </TOC> 复制代码

TOC.vue

<script> // 得到虚拟节点的文本内容 // function getText(vnode) { //   return vnode.children[0].text; // } function getText(vnode) {   if (!vnode.tag) {     // 文本节点     return vnode.text;   }   // ["asdfasdf", "Chapter 1"]   return vnode.children.map((v) => getText(v)).join(''); } // 给生成的文本去除首尾空格,用-连接 function generateId(title) {   return title     .toLowerCase()     .replace(/\s+/g, '-')     .replace(/(^-)|(-$)/, ''); } const headingFlag = /^h\d$/; export default {   render(h) {     // 得到插槽中的所有虚拟dom     const vnodes = this.$slots.default;     // 拿到所有的标题元素     const headNodes = vnodes.filter((v) => headingFlag.test(v.tag));     const links = [];     for (const head of headNodes) {       head.data = head.data || {};       head.data.attrs = head.data.attrs || {};       const title = getText(head);       const id = generateId(title);       head.data.attrs.id = id;       // 每个标题对应一个a元素       links.push(         h(           'a',           {             attrs: {               href: '#' + id,             },           },           title         )       );     }     return h('div', [       h(         'div',         {           attrs: {             class: 'toc',           },         },         links       ),       h('div', vnodes),     ]);   }, }; </script> <style> .toc {   position: fixed;   left: 0;   top: 0;   padding: 20px;   line-height: 2; } .toc a {   display: block; } html {   scroll-behavior: smooth; } </style> 复制代码

问题

  • 没有找后代元素,只找了children

  • 不同级目录显示应该不同

  • 如何控制样式


作者:一只前端小菜鸟
链接:https://juejin.cn/post/7032562142180032520


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐