这个是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)
把它加到最后.
-
"last": refEl 被当成父亲,append 将会使用
默认情况下,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 举报,一经查实,本站将立刻删除。