资讯专栏INFORMATION COLUMN

web前端mvc库实现

Kylin_Mountain / 2123人阅读

摘要:如函数通过名,找到对应的数组,并触发所有数组内回调函数。核心代码如下效果图源码前端实现小节整篇文章基本是围绕着如下点,的观察者模式的实现展开,期间的销毁则取消与之有关联对象的关系,如销毁时,注销掉与之关联的的回调函数。

web前端mvc库实现 前言

随着前端应用日趋复杂,如今如angular,vue的mvvm框架,基于virtual dom的react等前端库基本成为了各个公司的首选。而以当初最流行的头号大哥backbone为代表的mvc库基本退出了历史舞台。

在现如今人人都说mvvm/react多好,backbone多差的时代。笔者看别人文章,看的时候总是感觉好像有点道理,看完之后如耳边风一般左耳朵进,右耳朵出。

so,痛定思痛之后,笔者定了个小目标,实现了份简易版的backbone库。以设计,实现的角度来对比其它类型库的差异。

ok,废话不多说,上正菜。

思路整理

MVC即将前端应用抽象为Model,View,Control三大模块。View为用户视图,通过浏览器事件接受用户输入。Model为数据模型,他可以随时和后端同步数据。Control则是具体实现View派发的事件,计算并改变Model的数据。

UI可以被抽象为模版+数据,随着用户不断的触发浏览器提供的各种事件,交互不断的进行,Control接受了View指令改变着Model的数据,而View则随着Model的改变做出响应,最终展现在用户面前。

流程图:

模块划分

本篇文章的思路来自于backbone,并屏弃了耦合的后端操作。早期MVC并没有对Control做严格的划分,也许是数据的改变计算并不那么复杂,所以Control功能在View的事件内完成了,也就是说View模块里面耦合了Control的功能。

但近几年flux的action,store的出现,View调用action,具体数据变化计算则在store内部实现,也算是把Control功能从View内部抽象出来了吧。

Event模块

为对象提供对事件的处理和回调,内部实现了观察者(订阅者)模式,如view订阅了model的变化,model变化之后则通知view。

基本方法。

on函数通过event名,在object上绑定callback函数,将回调函数存储在数组里。

off函数移除在object上绑定的callback函数

通过event名移除指定callback。如object.off("change", onChange)

通过event名移除所有callback。如object.off("change")

移除所有指定callback。如object.off(null, onChange);

移除所有指定context的callback。如object.off(null, null, context);

清空object所有的callback。如object.off()

trigger函数通过event名,找到object对应的数组,并触发所有数组内回调函数。

注意事项

其所有方法应该支持类似on(name,callback),on("name1 name2 name3",callback), on({name1:callback1,name2:callback2})

这时候则可以抽象内部公用方法。通过递归的方式,on({name1:callback1,name2:callback2})类型的和on("name1 name2 name3",callback)类型,最终转化为最基本的on(name,callback)类型。核心代码如下:

this.eventsApi = function (iteratee, name, callback, context) {
    let event;
    if (name && typeof name === "object") {
      Object.keys(name).forEach(key=> {
        event = this.eventsApi(key, name[key], context);
      })
    } else if (SEPARATE.test(name)) {
      var keys = name.split(SEPARATE);
      keys.forEach(key=> {
        event = iteratee.call(this,key, name[key], context);
      });
    } else {
      event = iteratee.call(this,name, callback, context);
    }
    return event;
};
View模块

无状态,实例化的时候可以对应多个model实例,并以观察者的身份观察这些model的变化,通过这些model数据,加上指定的模版渲染dom,展示UI。

销毁的时候注销掉所有model的观察,取消与相关model之间的关联。

实例化的时候通过事件委托注册浏览器事件

实现

_ensureElement,确保View有一层dom包裹,如果this.el这个dom不存在,则通过id,className,tagName创建一个dom并赋值于this.el。

listenTo,将model与view实例关联起来,并收集关联model,存储于listenTo数组内,内部实现则是调用model的on函数

stopListening,view销毁前调用,通过listenTo数组找到关联model,并取消view与这些model之间的观察者关系。

$,将dom的查找定位在 this.$el下

delegateEvents,事件委托,以{"click #toggle-all": "choose"}为例,为在this.el子节点的id等于toggle-all的dom注册click事件choose函数。核心代码如下:

delegateEvents: function (events) {

   var $el = this.$el;
   Object.keys(events).forEach(item=> {
     var arr = item.split(" ");
     if (arr.length === 2) {
       var event = arr[0];
       var dom = arr[1];
       $el.on(event + ".delegateEvents" + this.$id, dom, this[events[item]].bind(this));
     }
   })
 },

undelegateEvents,注销掉通过delegateEvents注册的dom事件

Model模块

Model在backbone里被抽象为object类型的Model和array类型的Collection

承载着应用的状态,可以随时和后端保持同步。

内部实现了对数据变化的监听,一旦发生变化则通知观察者View发生变化。

Model

监听数据的变化,对model的修改,删除之后调用对应的trigger函数,通知订阅了model变化的view。

set函数,改变model数据,并触发change事件

set: function (obj) {
    this._changing = true;
    this.changed = obj;
    this._previousAttributes = Object.assign({}, this.attributes);
    this.attributes = Object.assign({}, this.attributes, obj);
    const keys = [];
    Object.keys(obj).forEach(key=> {
      keys.push(key);
      this.trigger("change:" + key, this);
    }, this);

    if (keys.length > 0) {
      this.trigger("change", this);
    }
    this._changing = false;
 },

destroy函数触发destroy事件

destroy: function () {
   this.stopListening();
   this.trigger("destroy", this);
},
Collection

提供数组类型models的push,unshift,pop,shift,remove,reset等功能。push,unshift实际调用add函数,pop,shift实际调用remove函数。

add函数支持任意索引插入指定数组,触发add事件。核心的代码如下:

    export const splice = (array, insert, at)=> {
      at = Math.max(0, Math.min(array.length, at));
      let len = insert.length;
      let tail = [];
      for (let i = at; i < array.length; i++) {
        tail.push(array[i]);
      }
      for (let i = 0; i < tail.length; i++) {
        array[i + at + len] = tail[i];
      }
      for (let i = 0; i < len; i++) {
        array[i + at] = insert[i];
      }
      return array;
    };

remove函数支持删除指定model,触发update事件。

_addReference,调用add方法新增model时,通过观察者模式增加该model与collection之间的关联,model的变化通知collection。核心代码如下:

    _addReference: function (model) {
      model.on("all",this._onModelEvent,this);
     }

_removeReference,调用remove,reset移除model时,取消该model与collection关联。核心代码如下:

    _removeReference: function(model) {
       if (this === model.collection) delete model.collection;
       model.off("all", this._onModelEvent, this);
    }
extend

生产环境下需要在保留原生View,Model类的功能情况下做一些业务拓展,这时候需要用到类的继承。

虽然es6支持extend继承,但这边我还是手写了一份。思路则是返回一个构造函数,该函数的原型为新的实例对象props,而props的原型对象则是父函数的原型(有点拗口,自己看代码理解)。
核心代码如下:

export const extend = function (props) {
  var parent = this;
  var child = function () {
    parent.apply(this, arguments);
  };
  child.prototype = Object.assign(Object.create(parent.prototype), props, { constructor: child });
  return child;
};
todomvc效果图

源码

web前端mvc实现

小节

整篇文章基本是围绕着如下2点

view-model,collection-model的观察者模式的实现展开,期间view,model的销毁则取消与之有关联对象的关系,如view销毁时,注销掉与之关联的model的回调函数。

监听数据变化,并通知观察者作出响应,如model变化后触发trigger("change")

好了,文章草草写到这了,多谢各位看官,以上也是纯个人观点,有问题欢迎各位web前端mvc设计指教。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82578.html

相关文章

  • 前端练级攻略(第二部分)

    摘要:是文档的一种表示结构。这些任务大部分都是基于它。这个实践的重点是把你在前端练级攻略第部分中学到的一些东西和结合起来。一旦你进入框架部分,你将更好地理解并使用它们。到目前为止,你一直在使用进行操作。它是在前端系统像今天这样复杂之前编写的。 本文是 前端练级攻略 第二部分,第一部分请看下面: 前端练级攻略(第一部分) 在第二部分,我们将重点学习 JavaScript 作为一种独立的语言,如...

    BWrong 评论0 收藏0
  • openresty 前端开发轻量级MVC框架封装二(渲染篇)

    摘要:我们已经用开发了版,还有微信版的应用,已经运行几个月了,很稳定,上手也简单,开发的时候不用编译,直接启动一个就搞定,部署的时候只需要几的内存,还可以用做各种事情,高并发防火墙,直接跑在里面,简直爽歪歪,有机会跟大家分享。示例代码参见部分 这一章主要介绍怎么使用模板,进行后端渲染,主要用到了lua-resty-template这个库,直接下载下来,放到lualib里面就行了,推荐第三方库...

    SimonMa 评论0 收藏0
  • 【译】前端练级攻略

    摘要:由于系统变得越来越复杂,人们提出了称为预处理器和后处理器的工具来管理复杂性。后处理器在由预处理器手写或编译后对应用更改。我之前建议的文章,,也涵盖了预处理器相关的知识。 译者:前端小智 原文:medium.freecodecamp.org/from-zero-t… medium.freecodecamp.org/from-zero-t… 我记得我刚开始学习前端开发的时候。我看到了很多文章及...

    wuyumin 评论0 收藏0
  • 2014 杭JS大会 会议盛况与技术热点现场报道(直播)

    摘要:中国开发者的年度盛会中国开发者大会,于年月日在杭州举办了本年度的杭会议我们的和将为在现场为您带来现场的报道,一览大牛风采,直击技术热点。签到中第日的会议即将开幕以下是与参会者和与博文视点的作者们合影 中国JS开发者的年度盛会JS中国开发者大会,于2014年6月21日在杭州举办了本年度的杭JS会议! 我们SegmentFault的 @integ 和 @shamiao 将为在现场为您带来...

    caige 评论0 收藏0
  • 前端清单第 27 期:React Patent License 回复,Shopify WebVR 购

    摘要:新闻热点国内国外,前端最新动态就开源许可证风波进行回复数周前,基金会决定禁止旗下项目使用,因为其在标准的许可证之外添加了专利声明此举引发了社区的广泛讨论,希望能够更新其开源许可证。 showImg(https://segmentfault.com/img/remote/1460000010777089); 前端每周清单第 27 期:React Patent License 回复,Sho...

    jeffrey_up 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<