脚本宝典收集整理的这篇文章主要介绍了

Vue渲染过程浅析

脚本宝典小编觉得挺不错的,现在分享给大家,也给大家做个参考,希望能帮助你少写一行代码,多一份安全和惬意。
<p><code></p> <p><a href="http://www.js-code.com/tag/vue" title="Vue" target="_blank">Vue</a> 推荐在绝大多数情况下使用 template 来创建你的 <a href="http://www.js-code.com/tag/html" title="HTML" target="_blank">HTML</a>。但是模板毕竟是模板,不是真实的dom节点。从模板到真实dom节点还需要经过一些步骤</p> <ol> <li>把模板编译为render函数</li> <li>实例进行挂载, 根据根节点render函数的调用,递归的生成虚拟dom</li> <li>对比虚拟dom,渲染到真实dom</li> <li>组件内部data发生变化,组件和子组件引用data作为props重新调用render函数,生成虚拟dom, 返回到步骤3</li> </ol> <h2 id="articleHeader0">第一步: 模板到render</h2> <p>在我们使用<a href="http://www.js-code.com/tag/vue" title="Vue" target="_blank">Vue</a>的组件化进行开发应用的时候, 如果仔细的查看我们要引入的组件, 例子如下</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// App.vue <template></p> <div> hello word </div> <p></template></p> <p><script></p> <p><a href="http://www.js-code.com/tag/export" title="export" target="_blank">export</a> default { }</p> <p></script></p> <style> </style> <p>" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">// App.vue </span> &lt;template&gt; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name"><a href="http://www.js-code.com/tag/div" title="浏览关于“div”的文章" target="_blank" class="tag_link">div</a></span>&gt;</span> hello word <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span> &lt;<span class="hljs-regexp">/template&gt; &lt;script&gt; <a href="http://www.js-code.com/tag/export" title="浏览关于“export”的文章" target="_blank" class="tag_link">export</a> default { } &lt;/</span>script&gt; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="undefined"> </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span></code></pre> <p>在我们的主入口main.js</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="import Vue from 'vue' import App from './App' console.log(App) new Vue({ render: h => h(App)<br /> }).$mount('#app')<br /> " title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword">import</span> <a href="http://www.js-code.com/tag/vue" title="浏览关于“Vue”的文章" target="_blank" class="tag_link">Vue</a> <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span> <span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span> <span class="hljs-built_in">console</span>.log(App) <span class="hljs-keyword">new</span> Vue({ <span class="hljs-attr">render</span>: <span class="hljs-function"><span class="hljs-params">h</span> =&gt;</span> h(App) }).$mount(<span class="hljs-string">'#app'</span>) </code></pre> <p><span class="img-wrap"><img data-src="/img/bVbpyO2?w=1143&amp;h=140" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="clipboard.png" title="clipboard.png" style="cursor: pointer;"></span></p> <p>我们能够看到在我们引入的App这个模块,里面是一个对象,对象里面存在一个方法叫做render。在说render函数之前,我们可以想一想,每一次加载一个组件,然后对模板进行解析,解析完后,生成Dom,挂载到页面上。这样会导致效率很低效。而使用Vue-cli进行组件化开发,在我们引入组件的后,其实会有一个解析器(<code>vue-loader</code>)对此模板进行了解析,生成了render函数。当然,如果没有通过解析器解析为render函数,也没有关系,在组件第一次挂载的时候,Vue会自己进行解析。源码请参考: <a href="https://github.com/vuejs/vue/blob/dev/src/platforms/web/entry-runtime-with-compiler.js" rel="nofollow noreferrer" target="_blank">https://github.com/vuejs/vue/...</a> <br />这样,能保证组件每次调用的都是render函数,使用render函数生成VNode。</p> <h2 id="articleHeader1">第二步:虚拟节点VNode</h2> <p>我们把Vue的实例挂载到<code>#app</code>, 会调用实例里面的render方法,生成虚拟<a href="http://www.js-code.com/tag/dom" title="DOM" target="_blank">DOM</a>。来看看什么是虚拟节点,把例子修改一下。</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="new Vue({ render: h => {<br /> let root = h(App)<br /> console.log('root:', root)<br /> return root<br /> }<br /> }).$mount('#app')" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs typescript"><code><span class="hljs-keyword">new</span> Vue({ render: <span class="hljs-function"><span class="hljs-params">h</span> =&gt;</span> { <span class="hljs-keyword"><a href="http://www.js-code.com/tag/let" title="浏览关于“let”的文章" target="_blank" class="tag_link">let</a></span> root = h(App) <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'root:'</span>, root) <span class="hljs-keyword">return</span> root } }).$mount(<span class="hljs-string">'#app'</span>)</code></pre> <p><span class="img-wrap"><img data-src="/img/bVbpAmx?w=1146&amp;h=414" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="clipboard.png" title="clipboard.png" style="cursor: pointer;"></span></p> <p>上面生成的VNode就是虚拟节点,虚拟节点里面有一个属性<strong><code>elm</code></strong>, 这个属性指向真实的<a href="http://www.js-code.com/tag/dom" title="DOM" target="_blank">DOM</a>节点。因为VNode指向了真实的<a href="http://www.js-code.com/tag/dom" title="浏览关于“DOM”的文章" target="_blank" class="tag_link">DOM</a>节点,那么虚拟节点经过对比后,生成的DOM节点就可以直接进行替换。<br /><strong>这样有什么好处呢?</strong> <br />一个组件对象,如果内部的<code>data</code>发生变化,触发了render函数,重新生成了VNode节点。那么就可以直接找到所对应的节点,然后直接替换。那么这个过程只会在本组件内发生,不会影响其他的组件。于是组件与组件是隔离的。<br />例子如下:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// main.js const root = new Vue({ data: { state: true }, mounted() { setTimeout(() => {<br /> console.log(this)<br /> this.state = false<br /> }, 1000)<br /> },<br /> render: function(h) {<br /> const { state } = this // state 变化重新触发render<br /> let root = h(App)<br /> console.log('root:', root)<br /> return root<br /> }<br /> }).$mount('#app')" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">// main.js</span> <span class="hljs-keyword"><a href="http://www.js-code.com/tag/const" title="浏览关于“const”的文章" target="_blank" class="tag_link">const</a></span> root = <span class="hljs-keyword">new</span> Vue({ <span class="hljs-attr">data</span>: { <span class="hljs-attr">state</span>: <span class="hljs-literal">true</span> }, mounted() { setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> { <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword"><a href="http://www.js-code.com/tag/this" title="浏览关于“this”的文章" target="_blank" class="tag_link">this</a></span>) <span class="hljs-keyword">this</span>.state = <span class="hljs-literal">false</span> }, <span class="hljs-number">1000</span>) }, <span class="hljs-attr">render</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">h</span>) </span>{ <span class="hljs-keyword">const</span> { state } = <span class="hljs-keyword">this</span> <span class="hljs-comment">// state 变化重新触发render</span> <span class="hljs-keyword">let</span> root = h(App) <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'root:'</span>, root) <span class="hljs-keyword">return</span> root } }).$mount(<span class="hljs-string">'#app'</span>)</code></pre> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// App.vue <script> export default { render: (h) => { <a href="http://www.js-code.com/tag/let" title="let" target="_blank">let</a> app = h('h1', ['hello world']) console.log('app:', app) return app } } </script>" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">// App.vue</span> &lt;script&gt; <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> { <span class="hljs-attr">render</span>: <span class="hljs-function">(<span class="hljs-params">h</span>) =&gt;</span> { <span class="hljs-keyword">let</span> app = h(<span class="hljs-string">'h1'</span>, [<span class="hljs-string">'hello world'</span>]) <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'app:'</span>, app) <span class="hljs-keyword">return</span> app } } &lt;<span class="hljs-regexp">/script&gt;</span></code></pre> <p><span class="img-wrap"><img data-src="/img/bVbpAvU?w=1203&amp;h=285" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="clipboard.png" title="clipboard.png" style="cursor: pointer;"></span><br />我们可以看到,当<code>main.js</code>中重新触发render函数的时候,render方法里面有引用App.vue这个子组件。但是并没有触发App.vue组件的的render函数。</p> <p><strong><code>在一个组件内,什么情况会触发render?</code></strong>。</p> <h2 id="articleHeader2">如何才能触发组件的render</h2> <p>数据劫持是Vue的一大特色,原理官方已经讲的很多了<a href="https://cn.vuejs.org/v2/guide/reactivity.html" rel="nofollow noreferrer" target="_blank">深入响应式原理</a>。在我们给组件的data的属性进行的赋值的时候(set),此属性如果在组件内部初次渲染过程被引用(<code>data的属性被访问,也就是数据劫持的get</code>), 包括生命周期方法或者render方法。于是会触发组件的update(beforeUpdate -&gt; render -&gt; updated)。</p> <blockquote><p>注: 为了防止data被多次set从而触发多次update, Vue把update存放到异步队列中。这样就能保证多次data的set只会触发一次update。</p></blockquote> <p><strong><code>当props会触发组件的重新渲染是怎么发生的呢?</code></strong></p> <p>把父组件的data通过props传递给子组件的时候,子组件在初次渲染的时候生命周期或者render方法,有调用data相关的props的属性, 这样子组件也被添加到父组件的data的相关属性依赖中,这样父组件的data在set的时候,就相当于触发自身和子组件的update。<br />例子如下:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// main.vue import Vue from 'vue' import App from './App' const root = new Vue({ data: { state: false }, mounted() { setTimeout(() => {<br /> this.state = true<br /> }, 1000)<br /> },<br /> render: function(h) {<br /> const { state } = this // state 变化重新触发render<br /> let root = h(App, { props: { status: state } })<br /> console.log('root:', root)<br /> return root<br /> }<br /> }).$mount('#app')</p> <p>window.root = root" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">// main.vue</span> <span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span> <span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span> <span class="hljs-keyword">const</span> root = <span class="hljs-keyword">new</span> Vue({ <span class="hljs-attr">data</span>: { <span class="hljs-attr">state</span>: <span class="hljs-literal">false</span> }, mounted() { setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> { <span class="hljs-keyword">this</span>.state = <span class="hljs-literal">true</span> }, <span class="hljs-number">1000</span>) }, <span class="hljs-attr">render</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">h</span>) </span>{ <span class="hljs-keyword">const</span> { state } = <span class="hljs-keyword">this</span> <span class="hljs-comment">// state 变化重新触发render</span> <span class="hljs-keyword">let</span> root = h(App, { <span class="hljs-attr">props</span>: { <span class="hljs-attr">status</span>: state } }) <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'root:'</span>, root) <span class="hljs-keyword">return</span> root } }).$mount(<span class="hljs-string">'#app'</span>) <span class="hljs-built_in">window</span>.root = root</code></pre> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// App.vue <script> <a href="http://www.js-code.com/tag/export" title="export" target="_blank">export</a> default { props: { status: Boolean }, render: function (h){ <a href="http://www.js-code.com/tag/const" title="const" target="_blank">const</a> { status } = <a href="http://www.js-code.com/tag/this" title="this" target="_blank">this</a> <a href="http://www.js-code.com/tag/let" title="let" target="_blank">let</a> app = h('h1', ['hello world']) console.log('app:', app) return app } } </script>" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">// App.vue</span> &lt;script&gt; <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> { <span class="hljs-attr">props</span>: { <span class="hljs-attr">status</span>: <span class="hljs-built_in">Boolean</span> }, <span class="hljs-attr">render</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">h</span>)</span>{ <span class="hljs-keyword">const</span> { status } = <span class="hljs-keyword">this</span> <span class="hljs-keyword">let</span> app = h(<span class="hljs-string">'h1'</span>, [<span class="hljs-string">'hello world'</span>]) <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'app:'</span>, app) <span class="hljs-keyword">return</span> app } } &lt;<span class="hljs-regexp">/script&gt;</span></code></pre> <p>截图如下:</p> <p><span class="img-wrap"><img data-src="/img/bVbpLBL?w=937&amp;h=164" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="clipboard.png" title="clipboard.png" style="cursor: pointer;"></span><br />在<code>main.js</code>中 <strong> state </strong>状态发生了变化,由<code>false</code> =&gt; <code>true</code>, 触发了<strong>自身</strong>与<strong>子组件</strong>的render方法。</p> <h2 id="articleHeader3">补充</h2> <p>上面的内容是本人的一些使用心得,由于水平有限, 内容有些错误或者表达不当。多欢迎大神来指导</p> <p></code></p>

总结

以上是脚本宝典为你收集整理的

Vue渲染过程浅析

全部内容,希望文章能够帮你解决

Vue渲染过程浅析

所遇到的程序开发问题,欢迎加入QQ群277859234一起讨论学习。如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典网站推荐给程序员好友。 本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。

80%的人都看过