vlambda博客
学习文章列表

一起来玩一个模拟游戏吧:浅谈虚拟DOM

Done for me From 404 we found 03:53     


    出于各种机缘巧合,前端三大主流框架 VAR (Vue、Angular以及React)我选择了React。相对于新晋王者Vue,React上手无疑是相对较难的。但喜欢就是喜欢,也没有那么多理由能够解释为什么...


    三大主流框架之受欢迎的原因有很多:数据驱动、组件化思想...等等,在底层方面,最主要的一个原因便是:虚拟DOM


    虚拟DOM的诞生,在性能方面使得JQ这种轻量库渐渐被主流框架所替代。虚拟DOM的诞生,使得跨端开发成为可能。

      

    那么,一起来看看这个颠覆性的革命产物吧


    要想做到优化,做到性能提升,我们就必须得知道规则、知道底层原理。现在,假设我们不知道React,我们以一个开发者的视角来模拟一下数据驱动+模版语言的渲染过程。(如果不知道浏览器渲染原理可以康康这个  )


1.0 伟大的开始


    接触过React的伙伴应该知道,React的程序设计思想是数据驱动,组件之间通过单向数据流进行影响。React中还有一大亮点便是JSX——一中JS的语法拓展,其格式与模板语言相似。


    好啦,了解了React的两大特点后,我们就可以开始我们的模拟游戏啦


    首先,依照浏览器渲染原理,我们需要 state 状态数据以及 类似于HTML的 JSX模板。让两者结合生成 DOM 树(这是我们第一次渲染结果)


    我们知道,在React中,若 state 状态数据或 props 属性发生改变,都会导致 render 函数的调用从而造成重新渲染。


    那么,假设现在 state 被改变了。新的 state 会和 JSX 生成新的 DOM 树。


    

            (最近入手了iPad,真的好好用!别嫌我字丑嗷o(╥﹏╥)o)

            (原谅我读书读的少,模板的板老是写错,对不起)


    我们让新的DOM树代替覆盖原有的DOM树。如果知道浏览器渲染原理的朋友便会清楚,这是个很糟糕的做法!这也太伤性能了。所以,我们得开始第二种模拟方案:DOM对比。虽然第一种方案很糟糕,但至少我们开始了不是吗?无论多艰难,只要在路上了就好。


2.0 找不同


    通过浏览器渲染原理我们可以发现,第一个方案有一个耗性能的地方在于第五步:新生成的真实DOM替换原始的DOM。这么做会导致大量的回流、重绘,重新计算、布局、渲染,这都是非常损耗性能的。


    看到这,自然而然地就会想:如果我只是替换不一样的DOM节点呢?真的有必要完全替换成新的节点吗?


    所以,诞生了我们的第二种方案:在新的DOM生成,不进行挂载,让它与原始的DOM进行比对,对不同的节点进行局部替换

    

    (对不起,模板的板写错了,这图太难改了就懒得改了。对不起是我态度不端正)

    

3.0 性能革命

    

    通过方案二,我想我们的性能有了一定的提升,可是这种提升远远不够。我们复盘下前两个方案,发现有一步真的太太太太伤性能了:数据改变后,生成新的DOM树。不论是否采用对比方案,这个新的DOM结构都会由被调用的Web Api生成。不论是调用这种级别的API还是生成新的DOM树这个过程,都是会调用大量资源,对性能进行消费。


    那,我们有没有方法对这个过程进行降维处理?生成一个类似DOM结构的玩意?

    

    这便是虚拟DOM的核心思想!用一个JS对象来代替真实的DOM结构!



    

                                    (对不起,我模板的板写错了)

    

JSX与虚拟DOM


     刚刚一直在说JSX模板,那么JSX原理到底是什么呢?


  JSX其实有点像是一个语法糖。它在一定程度上等同于 React.createElement() 这个React内置方法。两者区别在于,React.createElement() 更倾向于底层的React。


    举个栗子:


render(){ // 1    return <div>item</div>    }  render(){                   // 2    return React.createElement('div',{},item);   // {} 是对元素属性的挂载}


1与2两段代码是完全等效的。


 每次编译JSX,其实是会先翻译成 React.createElement() 再进行编译,这也是为什么JSX与state数据结合生成JS对象的底层原理


关于Diff算法


    上面解决了生成新的DOM树而造成的性能损耗,接下来来看看React是如何解决虚拟DOM进行比对时的性能损耗

    

     在DOM结构的比对方面,React采用的是Diff算法。


     Diff -> Difference


     我们知道,虚拟DOM发生变化的先决条件是 setState 导致的 state 数据发生变化。

     

     而对于 setState,React采用了异步处理的方式避免了某个短时间间断内触发多次state数据变化而造成的不必要的虚拟DOM生成及比对。


     Diff算法的核心概念在于:同结构层比对,且一旦发现某一层不同,直接对该层及该层以下的所有层进行虚拟DOM替换。


    这样比对虽然会造成一定渲染上性能的浪费,可是它靠着此算法的高效性,大幅降低了算法上造成的性能。


虚拟DOM的优点

  

    为什么我会说虚拟DOM是一个颠覆性产物呢?不光光是因为它给性能提升上带来的颠覆,更是因为它对整个开发生态带来的颠覆。


    我们知道,DOM是个仅存于浏览器的结构对象。而虚拟DOM,就仅是一个JS对象。也就是说,能有JS出现的地方,虚拟DOM都能存在。所以使得跨端开发成为可能。


    

 道阻且长,Web开发的终点漫漫无期,只希望自己能在这条路上,守得初心且坚定不移。


  豁然想起一位Web前辈的话:JS,能写一切。

  这句话虽然有些夸张,但不失一抹豪情。心怀不羁,勇往直前。我想,这是JS教给我的一种道吧。