前端手艺演进(五):当代前端交互框架

这个来自之前做的培训,删减了一些营业相干的,参考了许多材料(
参考材料列表),感谢先辈们,么么哒 😘

跟着前端手艺的生长,前端框架也在不停的转变。

操纵DOM时期

DOM(Document Object Model,文档对象模子)将 HTML 文档表达为树构造,并定义了接见和操纵 HTML 文档的规范要领。

《前端手艺演进(五):当代前端交互框架》

前端开辟基础上都邑涉及到HTML页面,也就防止不了和DOM打交道。

最初期的Web前端,就是一个静态的黄页,网页上的内容不能更新。

逐步的,用户可以在Web页面上举行一些简朴操纵了,比方提交表单,文件上传。然则全部页面的部份或许团体的更新,照样靠革新页面来完成的。

跟着AJAX手艺的涌现,前端页面上的用户操纵愈来愈多,愈来愈庞杂,所以就进入了对DOM元素的直接操纵时期。要对DOM元素操纵,就要运用DOM API,罕见的DOM API有:

范例要领
节点查询getElementById、getElementsByName、getElementsByClassName、getElementsByTagName、querySelector、querySelectorAll
节点建立createElement、createDocumentFragment、createTextNode、cloneNode
节点修正appendChild、replaceChild、removeChild、insertBefore、innerHTML
节点关联parentNode、previousSibling、childNodes
节点属性innerHTML、attributes、getAttribute、setAttribure、getComputedStyle
内容加载XMLHttpRequest、ActiveX

运用DOM API可以完成前端页面中的任何操纵,然则跟着网站运用的庞杂化,运用原生的API异常低效。所以 jQuery 这个用来操纵DOM的交互框架就诞生了。

jQuery 为何能成为在这个时期最盛行的框架呢?主假如他帮前端开辟人员处置惩罚了太多题目:

  • 封装了DOM API,供应了一致和轻易的挪用体式格局。
  • 简化了元素的挑选,可以很快的选取到想要的元素。
  • 供应了AJAX接口,对XMLHttpRequest和ActiveX一致封装。
  • 一致了事宜处置惩罚。
  • 供应异步处置惩罚机制。
  • 兼容大部份主流浏览器。

除了处置惩罚了上面这些题目,jQuery还具有优越的生态,海量的插件拿来即用,让前端开辟比之前流通许多。尤其是在IE6、IE7时期,没有jQuery,意味着无限的兼容性处置惩罚。

// DOM API:
document.querySelectorAll('#container li');

// jQuery
$('#container').find('li');

跟着HTML5手艺的生长,jQuery供应的许多要领已在原生的规范中完成了,逐步的,jQuery的必要性在逐步下降。http://youmightnotneedjquery.com/

渐渐地,SPA(Single Page Application,单页面运用)最先被普遍承认,全部运用的内容都在一个页面中并完全经由历程异步交互来加载差别的内容,这时刻运用 jQuery 直接操纵DOM的体式格局就不轻易治理了,页面上事宜的绑定会变得杂沓,在这类情况下,迫切须要一个可以自动治理页面上DOM和数据之间交互操纵的框架。

MV* 形式

MVC,MVP和MVVM都是罕见的软件架构设想形式(Architectural Pattern),它经由历程星散关注点来革新代码的构造体式格局。

纯真从观点上,很难辨别和感觉出来这三种形式在前端框架中有什么差别。我们经由历程一个例子来体味一下:有一个可以对数值举行加减操纵的组件:上面显现数值,两个按钮可以对数值举行加减操纵,操纵后的数值会更新显现。

《前端手艺演进(五):当代前端交互框架》

Model层用于封装和运用程序的营业逻辑相干的数据以及对数据的处置惩罚要领。这里我们把须要用到的数值变量封装在Model中,并定义了add、sub、getVal三种操纵数值要领。

var myapp = {}; // 建立这个运用对象

myapp.Model = function() {
    var val = 0; // 须要操纵的数据

    /* 操纵数据的要领 */
    this.add = function(v) {
        if (val < 100) val += v;
    };

    this.sub = function(v) {
        if (val > 0) val -= v;
    };

    this.getVal = function() {
        return val;
    };
};

View作为视图层,重要担任数据的展现。

myapp.View = function() {

    /* 视图元素 */
    var $num = $('#num'),
        $incBtn = $('#increase'),
        $decBtn = $('#decrease');

    /* 衬着数据 */
    this.render = function(model) {
        $num.text(model.getVal() + 'rmb');
    };
};

这里,经由历程Model&View完成了数据从模子层到视图层的逻辑。但关于一个运用程序,这远远是不够的,我们还须要响运用户的操纵、同步更新View和Model。

前端 MVC 形式

《前端手艺演进(五):当代前端交互框架》

MVC(Model View Controller)是一种很典范的设想形式。用户对View的操纵交给了Controller处置惩罚,在Controller中响应View的事宜挪用Model的接口对数据举行操纵,一旦Model发作变化便关照相干视图举行更新。

Model层用来存储营业的数据,一旦数据发作变化,模子将关照有关的视图。

// Model
myapp.Model = function() {
    var val = 0;

    this.add = function(v) {
        if (val < 100) val += v;
    };

    this.sub = function(v) {
        if (val > 0) val -= v;
    };

    this.getVal = function() {
        return val;
    };

    /* 视察者形式 */
    var self = this, 
        views = [];

    this.register = function(view) {
        views.push(view);
    };

    this.notify = function() {
        for(var i = 0; i < views.length; i++) {
            views[i].render(self);
        }
    };
};

Model和View之间运用了视察者形式,View事前在此Model上注册,进而视察Model,以便更新在Model上发作转变的数据。

View和Controller之间运用了战略形式,这里View引入了Controller的实例来完成特定的响应战略,比方这个栗子中按钮的 click 事宜:

// View
myapp.View = function(controller) {
    var $num = $('#num'),
        $incBtn = $('#increase'),
        $decBtn = $('#decrease');

    this.render = function(model) {
        $num.text(model.getVal() + 'rmb');
    };

    /*  绑定事宜  */
    $incBtn.click(controller.increase);
    $decBtn.click(controller.decrease);
};

控制器是模子和视图之间的纽带,MVC将响应机制封装在Controller对象中,当用户和运用发生交互时,控制器中的事宜触发器就最先工作了。

// Controller
myapp.Controller = function() {
    var model = null,
        view = null;

    this.init = function() {
        /* 初始化Model和View */
        model = new myapp.Model();
        view = new myapp.View(this);

        /* View向Model注册,当Model更新就会去关照View啦 */
        model.register(view);
        model.notify();
    };

    /* 让Model更新数值并关照View更新视图 */
    this.increase = function() {
        model.add(1);
        model.notify();
    };

    this.decrease = function() {
        model.sub(1);
        model.notify();
    };
};

这里我们实例化View并向对应的Model实例注册,当Model发作变化时就去关照View做更新。

可以明显感觉到,MVC形式的营业逻辑重要集合在Controller,而前端的View实在已具有了自力处置惩罚用户事宜的才能,当每一个事宜都流经Controller时,这层会变得非常痴肥。而且MVC中View和Controller平常是一一对应的,绑缚起来示意一个组件,视图与控制器间的过于严密的衔接让Controller的复用性成了题目,假如想多个View共用一个Controller该怎么办呢?

前端 MVP 形式

MVP(Model-View-Presenter)是MVC形式的改进。和MVC的相同之处在于:Controller/Presenter担任营业逻辑,Model治理数据,View担任显现。

《前端手艺演进(五):当代前端交互框架》

在MVC里,View是可以直接接见Model的。而MVP中的View并不能直接运用Model,而是经由历程为Presenter供应接口,让Presenter去更新Model,再经由历程视察者形式更新View。

与MVC比拟,MVP形式经由历程解耦View和Model,完全星散视图和模子使职责分别越发清楚;由于View不依靠Model,可以将View抽离出来做成组件,它只须要供应一系列接口供应给上层操纵。

// Model
myapp.Model = function() {
    var val = 0;

    this.add = function(v) {
        if (val < 100) val += v;
    };

    this.sub = function(v) {
        if (val > 0) val -= v;
    };

    this.getVal = function() {
        return val;
    };
};

Model层依旧是重要与营业相干的数据和对应处置惩罚数据的要领,很简朴。

// View
myapp.View = function() {
    var $num = $('#num'),
        $incBtn = $('#increase'),
        $decBtn = $('#decrease');

    this.render = function(model) {
        $num.text(model.getVal() + 'rmb');
    };

    this.init = function() {
        var presenter = new myapp.Presenter(this);

        $incBtn.click(presenter.increase);
        $decBtn.click(presenter.decrease);
    };
};

MVP定义了Presenter和View之间的接口,用户对View的操纵都转移到了Presenter。比方这里的View暴露setter接口(render要领)让Presenter挪用,待Presenter关照Model更新后,Presenter挪用View供应的接口更新视图。

// Presenter
myapp.Presenter = function(view) {
    var _model = new myapp.Model();
    var _view = view;

    _view.render(_model);

    this.increase = function() {
        _model.add(1);
        _view.render(_model);
    };

    this.decrease = function() {
        _model.sub(1);
        _view.render(_model);
    };
};

Presenter作为View和Model之间的“中间人”,除了基础的营业逻辑外,另有大批代码须要对从View到Model和从Model到View的数据举行“手动同步”,如许Presenter显得很重,保护起来会比较难题。假如Presenter对视图衬着的需求增添,它不得不过量关注特定的视图,一旦视图需求发作转变,Presenter也须要修改。

前端 MVVM 形式

MVVM(Model-View-ViewModel)最早由微软提出。ViewModel指 “Model of View”——视图的模子。

《前端手艺演进(五):当代前端交互框架》

MVVM把View和Model的同步逻辑自动化了。之前Presenter担任的View和Model同步不再手动地举行操纵,而是交给框架所供应的数据绑定功用举行担任,只须要通知它View显现的数据对应的是Model哪一部份即可。

我们运用Vue来完成这个栗子。

在MVVM中,我们可以把Model称为数据层,由于它仅仅关注数据自身,不关心任何行动(格式化数据由View的担任),这里可以把它明白为一个相似json的数据对象。

// Model
var data = {
    val: 0
};

和MVC/MVP差别的是,MVVM中的View经由历程运用模板语法来声明式的将数据衬着进DOM,当ViewModel对Model举行更新的时刻,会经由历程数据绑定更新到View。

<!-- View -->
<div id="myapp">
    <div>
        <span>{{ val }}rmb</span>
    </div>
    <div>
        <button v-on:click="sub(1)">-</button>
        <button v-on:click="add(1)">+</button>
    </div>
</div>

ViewModel大抵上就是MVC的Controller和MVP的Presenter了,也是全部形式的重点,营业逻辑也重要集合在这里,个中的一大中心就是数据绑定。与MVP差别的是,没有了View为Presente供应的接口,之前由Presenter担任的View和Model之间的数据同步交给了ViewModel中的数据绑定举行处置惩罚,当Model发作变化,ViewModel就会自动更新;ViewModel变化,Model也会更新。

new Vue({
    el: '#myapp',
    data: data,
    methods: {
        add(v) {
            if(this.val < 100) {
                this.val += v;
            }
        },
        sub(v) {
            if(this.val > 0) {
                this.val -= v;
            }
        }
    }
});

团体来看,比MVC/MVP精简了许多,不单单议简化了营业与界面的依靠,还处置惩罚了数据频仍更新(之前用jQuery操纵DOM很烦琐)的题目。由于在MVVM中,View不晓得Model的存在,ViewModel和Model也发觉不到View,这类低耦合形式可以使开辟历程越发轻易,进步运用的可重用性。

数据绑定

《前端手艺演进(五):当代前端交互框架》

在Vue中,运用了双向绑定手艺(Two-Way-Data-Binding),就是View的变化能及时让Model发作变化,而Model的变化也能及时更新到View。实在双向数据绑定,可以简朴地明白为一个模版引擎,然则会依据数据更改及时衬着。

有人还不要脸的申请了专利:

《前端手艺演进(五):当代前端交互框架》

数据更改检测

差别的MVVM框架中,完成双向数据绑定的手艺有所差别。如今一些主流的完成数据绑定的体式格局大抵有以下几种:

手动触发绑定

手动触发指令绑定是比较直接的完成体式格局,重要思绪是经由历程在数据对象上定义get()要领和set()要领,挪用时手动触发get ()或set()函数来猎取、修正数据,转变数据后会主动触发get()和set()函数中View层的从新衬着功用。

脏检测机制

Angularjs是典范的运用脏检测机制的框架,经由历程搜检脏数据来举行View层操纵更新。

脏检测的基础原理是在ViewModel对象的某个属性值发作变化时找到与这个属性值相干的一切元素,然后再比较数据变化,假如变化则举行Directive 指令挪用,对这个元素举行从新扫描衬着。

前端数据对象挟制

数据挟制是如今运用比较普遍的体式格局。其基础思绪是运用 Object.defineProperty 和 Object.defineProperies 对ViewModel数据对象举行属性get ()和set()的监听,当有数据读取和赋值操纵时则扫描元素节点,运转指定对应节点的Directive指令,如许ViewModel运用通用的等号赋值就可以了。

Vue就是典范的采纳数据挟制和宣布定阅形式的框架。

《前端手艺演进(五):当代前端交互框架》

  • Observer 数据监听器:担任对数据对象的一切属性举行监听(数据挟制),监听到数据发作变化后关照定阅者。
  • Compiler 指令剖析器:扫描模板,并对指令举行剖析,然后绑定指定事宜。
  • Watcher 定阅者:关联Observer和Compile,可以定阅并收到属性更改的关照,实行指令绑定的响应操纵,更新视图。

ES6 Proxy

之前我们说过 Proxy 完成数据挟制的要领

总结来看,前端框架从直接DOM操纵到MVC设想形式,然后到MVP,再到MVVM框架,前端设想形式的革新准绳一向向着高效、易完成、易保护、易扩大的基础方向生长。虽然如今前端各种框架也已成熟并最先向高版本迭代,然则还没有完毕,我们如今的编程对象依旧没有离开DOM编程的基础套路,一次次框架的革新大大进步了开辟效力,然则DOM元素运转的效力依然没有变。关于这个题目的处置惩罚,有的框架提出了Virtual DOM的观点。

Virtual DOM

MVVM的前端交互形式大大进步了编程效力,自动双向数据绑定让我们可以将页面逻辑完成的中心转移到数据层的修正操纵上,而不再是在页面中直接操纵DOM。只管MVVM转变了前端开辟的逻辑体式格局,然则终究数据层反应到页面上View层的衬着和转变还是经由历程对应的指令举行DOM操纵来完成的,而且一般一次ViewModel的变化可能会触发页面上多个指令操纵DOM的变化,带来大批的页面构造层DOM操纵或衬着。

比方一段伪代码:

<ul>
    <li repeat="list">{{ list.value }}</li>
</ul>

let viewModel = new VM({
    data:{
        list:[{value: 1},{value: 2},{value: 3}]
    }
})

运用MVVM框架天生一个数字列表,此时假如须要显现的内容变成了 [{value: 1}, {value: 2}, {value: 3}, {value: 4}],在MVVM框架中平常会从新衬着全部列表,包含列表中不必转变的部份也会从新衬着一次。 但实际上假如直接操纵转变DOM的话,只须要在<ul>子元素末了插进去一个新的<li>元素就可以了。但在平常的MVVM框架中,我们一般不会如许做。毫无疑问,这类情况下MVVM的View层更新形式就斲丧了更多没必要的机能。

那末该如何对ViewModel举行革新,让浏览器晓得实际上只是增添了一个元素呢?经由历程对照

[{value: 1},{value: 2},{value: 3}][{value: 1}, {value: 2}, {value: 3}, {value: 4}]

实在只是增添了一个 {value: 4},那末该如何将这个增添的数据反应到View层上呢?可以将新的Model data 和旧的Model data 举行对照,然后纪录ViewModel的转变体式格局和位置,就晓得了此次View 层应当如何去更新,如许比直接从新衬着全部列表高效很多。

这里实在可以明白为,ViewModel 里的数据就是形貌页面View 内容的另一种数据构造标识,不过须要连系特定的MVVM形貌语法编译来天生完全的DOM构造。

可以用JavaScript对象的属性层级构造来形貌上面HTML DOM对象树的构造,当数据转变时,新天生一份转变后的Elements,并与本来的Elemnets构造举行对照,对照完成后,再决议转变哪些DOM元素。

《前端手艺演进(五):当代前端交互框架》

适才例子里的 ulElement 对象可以明白为VirtualDOM。一般以为,Virtual DOM是一个可以直接形貌一段HTMLDOM构造的JavaScript对象,浏览器可以依据它的构造根据肯定划定规矩建立出肯定唯一的HTML DOM构造。团体来看,Virtual DOM的交互形式减少了MVVM或其他框架中对DOM的扫描或操纵次数,并且在数据发作转变后只在适宜的处所依据JavaScript对象来举行
最小化的页面DOM操纵,防止大批从新衬着。

diff算法

Virtual-DOM的实行历程:

用JS对象模仿DOM树 -> 比较两棵假造DOM树的差别 -> 把差别运用到真正的DOM树上

在Virtual DOM中,最重要的一环就是经由历程对照找出两个Virtual DOM的差别性,获得一个差别树对象。

关于Virtual DOM的对照算法实际上是关于多叉树构造的遍历算法。然则找到恣意两个树之间最小的修正步骤,平常会轮回递归对节点举行顺次对照,算法庞杂度到达 O(n^3),这个庞杂度异常高,比方要展现1000多个节点,最消极要顺次实行上十亿次的比较。所以差别的框架采纳的对照算法实际上是一个略简化的算法。

拿React来讲,由于web运用中很少涌现将一个组件挪动到差别的层级,绝大多数情况下都是横向挪动。因而React尝试逐层的对照两棵树,一旦涌现不一致,基层就不再比较了,在丧失较小的情况下明显下降了比较算法的庞杂度。

《前端手艺演进(五):当代前端交互框架》

前端框架的演进异常快,所以只要晓得演进的缘由,才能去明白各个框架的好坏,从而依据运用的实际情况来挑选最适宜的框架。关于其他手艺也是云云。

    原文作者:姜小抖
    原文地址: https://segmentfault.com/a/1190000017381456
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞