mvdom-基于node.js小型以DOM为中心的MVC框架

这个是mvdom的地址:

https://github.com/mvdom/mvdom

 

思想理念:

mvDom是一个非常轻量级的以DOM为中心的MVC库,它的特点就是以DOM作为框架的基础,而不是抛开DOM,也不是排斥DOM。

  • dom是你的朋友,不要同它作斗争, 张开双臂像爱人一样紧紧抱住它
  • DOM对于一些简单可伸缩的MVC模型来说就是天堂,是一部分卓越的基础组成
  • 过度使用组件和不使用 组件一样对有害
  • 黑魔法总暗藏花费
  • 框架 来来去去,不变的只有那些语言和runtimes留了下来 
  • 框架复杂的第一要素往往是 因为它的size,从小的和简单的框架入手,往往得到更好的伸缩性

总而言之: 坦然接受DOM,从小而简单的开始,只加入那些你觉得绝对需要的,这样就可以有一个可伸缩的mini MVC框架,只添加你需要的组件,去琢磨你要运行的环境,不要刻意去使用那些巨屌,这些巨屌往往很复杂又很抽象,最好的方式还是居中注意力在那些小的all-in-one框架上。

 

特征:

  • 零依赖,超小lib(< 15kb min,< 6kb gzip) 
  • 模板不可知论者(不可知论者什么意思请参考相关哲学资料)(字符串模板非常友好,如:Handlebars)
  • 死鬼,很简单的APIS啦(e.g. d.register(name,controller),d.display(name,parent))
  • 异步生命周期管理(hookable)
  • 增强的DOM事件(i.e.,d.on(el,type,selector,fn,{ns}) and off/trigger a la jquery,without wrappers)
  • 简单可伸展,且是最优化的DOM数据交换方式(d.push(el,data) & var data = d.pull(el)).
  • 最简化,但是功能强大的带topic和标签选择器的发布、订阅集成平台。(原文:
  • Minimalistic but powerful pub/sub (hub) with topic and label selectors.)

 

兼容性:

  •  在Chrome,Safari,Mobile Safari,Firefox,Edge等等主流浏览器上编写并测试通过
  • 需要权限,Array.forEach(对于IE11, https://polyfill.io是一个不错的腻子)
  • 使用js语法,与IE9+兼容(不需要从一种语言翻译为另一种语言)

 

安装: 

npm install mvdom

在源文件中的标准用法:

var mvdom = require("mvdom");

查看[#building]来了解如何手动构建发布。

 

API预览:

//-------------------------------view APIS-----------//
// 注册一个视图控制器(view controller)(异步生命周期async lifecycle)
mvdom.register("view name",{create,init,postDisplay,destroy}[,config]);
//在这个DOM元素el里显示一个view
mvdom.display("ViewName",parentEl [,config]); 
// 在这些阶段注册一个hook(willCreate,didCreate,willInit,...)
mvdom.hook("willCreate",fn(view){}); 

mvdom.empty(el); // 将会清空一个element的所有子元素,也会破坏相关的views
mvdom.remove(el); // 移除这个element,也会破坏与之相关联的views和子views
// --------- /View APIs --------- //


// --------- DOM Event Helpers --------- //
// 在一个或者多个elements上注册一个特定事件类型的监听器
mvdom.on(els,types,listener);
// 在一个或多个element上注册某种类型和选择器的监听器(with event.selectTarget when selector).  
mvdom.on(els,listener); 

// 使用可选名称空间或可选上下文注册侦听器 (this)
mvdom.on(els,[selector,] {ns,context});

// 取消注册的监听器
mvdom.off(els,] listener)
// 取消注册所有相关联的类型和eventual选择器的监听器
mvdom.off(els,type[,selector])
// 取消给定命名空间‘ns’的所有监听器
mvdom.off(els,{ns})

// 触发一个默认给定类型的自定义事件 
mvdom.trigger(els,"MyCustomEvent",{detail: "cool",cancelable: false});
// --------- DOM Event Helpers --------- //

// --------- DOM Query Shortcuts --------- //
var nodeList = mvdom.all(el,selector); //el.querySelectorAll的快捷方式
var nodeList = mvdom.all(selector); // 快捷方式:document.querySelectorAll from document

var element = mvdom.first(el,selector); // el.querySelector的快捷方式
var element = mvdom.first(selector); //document.querySelector from document的快捷方式
var element = mvdom.first(el); // 找到第一个子元素 (even for fragment for browsers that do not support it)

var element = mvdom.next(el[,selector]); // 找到下一个与某种可选选择器所关联的兄弟元素(快捷函数)
var element = mvdom.prev(el[,selector]); // 同上,找上一个
// --------- /DOM Query Shortcuts --------- //

// --------- DOM Helpers --------- //
// (添加孩子元素,refEl理解为父元素)Append child,refEl interpreted as parent
var newEl = mvdom.append(refEl,newEl); // 标准的refEl.appendChild(newEl)
var newEl = mvdom.append(refEl,newEl,"first"); // 插入newEl 作为refEl的第一个子元素.
var newEl = mvdom.append(refEl,"last"); // 对称,与上.
var newEl = mvdom.append(refEl,"empty"); // 在.appendChildrefEl.appendChild(newEL)前清空 refEl

// 附加兄弟姐妹,refEl 解释为兄弟姐妹
var newEl = mvdom.append(refEl,"after"); // 附加newEl在refEl之后,使用 appendChild 如果没有下一个兄弟元素
var newEl = mvdom.append(refEl,"before"); // 这里对称的,refEl.parentNode.insertBefore(newEl,refEl)

var frag = mvdom.frag("<div>any</div><td>html</td>"); // 创建一些html文档片段 (使用 '模板' 在可依附的文档上 )
// --------- /DOM Helpers --------- //

// --------- DOM Data eXchange (dx) push/pull --------- //
mvdom.push(el,] data); // will set the data.property to the matching selector (default ".dx") elements
var data = mvdom.pull(el[,selector]); // will 提取 the data from the matching elements (default selector ".dx")

// 注册 自定义的 pushers / pullers (default ones are for html form elements and simple div innerHTML)
mvdom.pusher(selector,pusherFun(value){this /* dom element*/}); // pusher function set a value to a matching element
mvdom.puller(selector,pullerFun(){this /* dom element*/}); // puller function returns the value from a matching element element 
// --------- /DOM Data eXchange (dx) push/pull --------- //

// --------- Hub (pub/sub) --------- //
var myHub = mvdom.hub("myHub"); // create new hub

myHub.sub(topics,[labels,] handler[,opts]); // subscribe

myHub.pub(topic,[label,] data); // publish

myHub.unsub(opts.ns); // unsubscribe
// --------- /Hub (pub/sub) --------- //

 

视图注册:

mvdom.register(viewName,controller [,config])

使用mvdom.register注册一个新的视图,视图控制器在视图整个生命周期都是可回应的(原生异步)。The only require view controller method,is the .create([data,config]) which is reponsible to return the HTML.

// 注册一个视图控制器
mvdom.register("MainView",{
    // Returns a HTML String,Document Element,or Document Fragment
    // Can return a Promise that resolve in one of those three object time
    // Must be one Dome Element
    create: function(data,config){
        return `<div class='MainView'>
                  <div class=".but">${data.message}</div>
                </div>`;
    },// (optional) init() will be called after the component element is created
    // but before it is added to the screen (i.e. added to the parent)
    init: function(data,config){
        var view = this; // best practice
        view.el; // this is the top parent element created for this view
        // if return a Promise,the flow will wait until the promise is resolved
    },// (optional) postDisplay() will be called after the component element is added to the dom
    // and in another event (used a setTimeout 0). 
    // Best Practice: This is a good place to add bindings that are not related to UI layout,or need to be done
    // after the component is displayed
    postDisplay: function(data,config){
        // some non UI layout related,or actions that need to be performed after the component is displaye

        // if return a promise,the mvdom.display(...).then will resolve when the return promise will be resolve. 
        // however,the mvdom.display(...) promise resolution will always be this view,regardless of the object or promise returns by this function.
    },// (optional) will be called when this view is deleted (from d.remove or d.empty on a parent)
    // will be called after the view.el is removed from parent.
    // info: {parentEl} Simple js object containing the parentEl property.
    destroy: function(info){
        
    }

    // (optional) bind events to this view (support selector)
    events: {
        "click; .but": function(evt){
            var view = this; // this is the view
            console.log("click on .but",evt,view);
        }
    },// (optional) bind events to the document 
    // (will be unbind when destroy this view by calling d.remove on this element or parents or d.empty on any parent )
    docEvents: {
        "click; .do-logoff": function(evt){
            var view = this;            
        }
    },// (optional) same as above,but on window (good to handle window resize)
    winEvents: {
        "resize": function(evt){
            // do something when window is resize
        }
    },// (optional) subscribe to a hub by hub name,topic(s),and optional label(s)
    hubEvents: {
        dataServiceHub: {
            // subscribe on the dataServiceHub on the topic Task and any labels "create" "update" or "delete"
            "Task; create,update,delete": function(data,info){
                var view = this; // the this is this view object
                console.log("Task has been " + info.label + "d");
                // if d.hub("dataServiceHub").pub("Task","create",taskEntity)
                // this will print "Task has been created"
            }
        },// also support flat notation
        "dataServiceHub; Task; create,delete": function(){
           // same binding as above
        }
    }

})

 

视图显示:

mvdom.diasplay(viewName,refEl,[data,config])

使用mvdom.display显示一个视图,例如:

// mvdom.display(viewName,data)
mvdom.display("MainView",mvdom.first("body"),{message:"hello from mvdom"});
// Note: mvdom.first is just a shortcut to document.querySelector

 

视图配置:

mvdom view中的可选参数config允许客制化view被操作的方式。这个参数在视图注册阶段就能设置,也能在每一次显示时覆盖.这里现在有一个简单的属性设置 .append,用来告诉我们怎么样增加新的view.el到DOM中.

  •  append:("last","first","before","after").   
    • "last":  refEl 被当成父亲,append 将会使用 refEl.appendChild(view.el)
    • "first": refEl 被当成父亲,append 将会使用 .insertBefore     refEl .firstChild  . 如果没有第一个元素,那么,将会使用正常的appendChild.
    • "empty": refEl 被当成父亲,首先, mvdom.empty(refEl) 被调用, 接着 refEl.appendChild(view.el).
    • "before": refEl 被当成兄弟,append 将使用 .insertBefore  refEl.
    • "after": refEl 被当成兄弟,我们 .insertBefore  efEl 的兄弟. 如果next兄弟是空,使用 refEl.parentNode.appendChild(view.el) 把它加到最后.

         默认情况下,config.append = "last" 表示refEl是父亲并且view element 将会被添加至最后(refEl.appendChild(view.el))

            如果没有数据,但是有config,输入null,像这样:mvdom.display("MainView",parentEl,null,{append:"first"})

            在mvdom.display的上下文中,config可以是一个string,在这种情况下它将被当成append的属性.因此这上面的等价于 mvdom.display("MainView","first")

Dom Event绑定

mvdom.on([el,] eventType,] eventHander(evt){}[,opts])

给一个或多个特定事件类型或者有可选选择器的dom element绑定一个事件handler.它还支持name spacing,以及绑定时的自定义容器。(如: eventHandler的"this" )

  • el:(可选的,默认document)绑定事件的最基本document element.也可以是一个数组或者装有element的nodeList

  • eventType:(必须得)支持多个,使用 “,” (如 "webkitTransitionEnd,transitionend")

  • selector:(可选的)HTML5选择器,如果设置了,那么只有和这个选择器相关联的目标才能触发eventHandler

  • eventHandler:(必要的)事件控制器,“this”可以通过opts.ctx设置

  • opts(可选的)

    • ctx:事件控制器的容器(如:this)

    • ns:绑定的命名空间

注意: 和jquery.on相似的,除了事件对象是本地的,而选择器是纯粹的H5选择器.

例子:

<div class="item">
   <div class="sub-item">text</div>
</div>
var baseEl = document;
mvdom.on(document,"click",".item",function(evt){
  evt.target; // can be the .sub-item or .item depending where the click occurs
  evt.currentTarget; // baseEl or document if not specified
  evt.selectTarget; // will always be .item element (even when .sub-item get clicked)
});

注意:.selectTarget只有当我们使用了selector才会设置

多事件绑定可以用 , 完成:

mvdom.on(someEl,"webkitTransitionEnd,transitionend",...)

mvdom.off(els,[type,listener][,opts])

取消绑定那些已经被mvdom.on绑定的事件

  • .off(els) 解绑所有通过mvdom.on绑定的事件
  • .off(els,type) 解绑所有通过mvdom.on绑定的type类型的事件
  • .off(els,selector)解绑所有通过mvdom.on绑定的type类型和固定选择器的事件
  • .off(els,listener)取消 绑定type类型和固定选择器和这类listener的事件
  •  .off(els,{ns}) 从命名空间ns中取消绑定所有事件

Dom 数据 eXchange(push/pull):

mvdom.push 和 mvdom.pull提供一个简单可扩展的方式来从DOM sub tree提取和注入数据.

mvdom.push(el,] data);

注入数据到选择器所选择的element中,默认选择器是.dx

mvdom.pull(el[,selector]);

提取数据从符合的elements中(默认选择器是.dx)

例如

<div id="myEl">
  <fieldset>
    <input class="dx" name="firstName" value="Mike">
    <input class="dx" name="lastName" value="Donavan">
  </fieldset>
  <div>
    <div class="dx dx-address-street">123 Main Street</div>
    <div class="dx" data-dx="address.city">San Francisco</div>
  </div>
</div>
var myEl = mvdom.first("#myEl"); // or document.getElementById

// Extract the data from the element. 
//   Will first do a ".dx" select,and for each element extract the property path and value from the element.
var data = mvdom.pull(myEl); 
// data: {firstName: "Mike",lastName: "Donavan",//        address: {street: "123 Main Street",city: "San Francisco"}}

// Update the DOM with some data
var updateData = {address: {street: "124 Second Street"}};
mvdom.push(myEl,updateData)

 

更多信息(内部构件):

mvdom.push 和 mvdom.pull分如下四步执行:

1.首先,选择器用于选择所有DOM元素作为候选以进行值提取或注入,默认情况下,我们使用“DX”类选择器,因为这要比其他属性效率高,当然也可以提供自定义选择器.

2.其次,对于每一个候选element,mvdom从其中提取属性路径(从name属性,带dx-前缀的class,或带有html标记的data-dx)

3.Third,it looks default and registered for the appropriate pusher or puller function to inject or extract the value. Default pushers/pullers support html form elements (input,textarea,checkbox,radio) and basic innerHTML set and get,but custom ones can be registered (and will take precedence) by specifying the element matching selector.

  • d.pusher(selector,pusherFun(value){this /* dom element*/}); Register pusher function set a value to a matching dom element
  • d.puller(selector,pullerFun(){this /* dom element*/}); Register puller function returns the value from a matching dom

4.Fourth,it set the value to the appropriate property path (support nested properties as shown above)

 

Hub (pub/sub)

var d = window.mvdom; // just a best practice we have,but feel free to use mvdom as is.

var myHub = d.hub("myHub");

// Subcribe to a topic
// sub(topic,] handlerFunction,namespace)
myHub.sub("Task",function(data,info){
    console.log("topic: ",info.topic,",label: ",info.label,data: ",data);
},{ns:"namespace"});

// pub(topic,] data)
var newTask = {id: 123,title: "A first task"};
myHub.pub("Task",newTask);
// will print: 'topic: Task,label: create,data: {id: 123,title: "A first task"}'

// or can subscribe only to the create label (here info.label will always be "create")
myHub.sub("Task",info){...});

// unsubscribe
myHub.unsub(ns); // if no namespace provided,the ns will be the function,and used as Key

// Multiple labels,with common namespace
myHub.sub("Task","create,delete",function(data){...},"ns1");
myHub.sub("Project","ns1");

// Then,to remove all subscription with ns1
myHub.unsub("ns1");

var obj = {name: "myObject"};

myHub.sub("Project",function(data){
    this.name; // "myObject"
},{ns:"ns1",ctx: obj});

 

构建:

这个库使用gulp-and-webpack-free的构建文件,要求node.js的版本在8.0.0以上。

  • npm run build 生成发布文件:dist/mvdom.js dist/mvdom.js.map 和dist/mvdom.min.js  
  • npm run watch 开发时修改src中的js文件,将会自动重编译, 便于调试.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章主要介绍“基于nodejs的ssh2怎么实现自动化部署”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“基于nodejs...
本文小编为大家详细介绍“nodejs怎么实现目录不存在自动创建”,内容详细,步骤清晰,细节处理妥当,希望这篇“nodejs怎么实现目录不存在自动创建”文章能帮助大...
这篇“如何把nodejs数据传到前端”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这...
本文小编为大家详细介绍“nodejs如何实现定时删除文件”,内容详细,步骤清晰,细节处理妥当,希望这篇“nodejs如何实现定时删除文件”文章能帮助大家解决疑惑...
这篇文章主要讲解了“nodejs安装模块卡住不动怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来...
今天小编给大家分享一下如何检测nodejs有没有安装成功的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文...
本篇内容主要讲解“怎么安装Node.js的旧版本”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎...
这篇“node中的Express框架怎么安装使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家...
这篇文章主要介绍“nodejs如何实现搜索引擎”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“nodejs如何实现搜索引擎...
这篇文章主要介绍“nodejs中间层如何设置”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“nodejs中间层如何设置”文...
这篇文章主要介绍“nodejs多线程怎么实现”,在日常操作中,相信很多人在nodejs多线程怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法...
这篇文章主要讲解了“nodejs怎么分布式”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“nodejs怎么分布式”...
本篇内容介绍了“nodejs字符串怎么转换为数组”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情...
这篇文章主要介绍了nodejs如何运行在php服务器的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇nodejs如何运行在php服务器文章都...
本篇内容主要讲解“nodejs单线程如何处理事件”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“nodejs单线程如何...
这篇文章主要介绍“nodejs怎么安装ws模块”,在日常操作中,相信很多人在nodejs怎么安装ws模块问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法...
本篇内容介绍了“怎么打包nodejs代码”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!
本文小编为大家详细介绍“nodejs接收到的汉字乱码怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“nodejs接收到的汉字乱码怎么解决”文章能帮助大家解...
这篇“nodejs怎么同步删除文件”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇...
今天小编给大家分享一下nodejs怎么设置淘宝镜像的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希