使用原生JavaScript实现简易MVVM框架详解
本文分享一个基于原生JavaScript实现的简易MVVM框架,代码仅300余行。框架支持属性绑定、复杂属性处理、自定义指令(如val、template)及事件监听。通过data-context绑定控制器,利用bind-和on-语法实现视图与模型的双向交互,适合用于理解前端响应式原理及扩展通用组件。
简易MVVM框架概述
此前多是在技术社区阅读文章并默默点赞,今日决定分享自己使用JavaScript编写的一个简易MVVM框架实现。
该框架最初仅具备自动事件绑定功能,后续参考了Vue、Knockout及Angular的实现思路,并结合个人在WPF开发中的经验,增加了属性绑定机制。经过整理与功能完善,完整代码已托管至码云:https://gitee.com/zlj_fy/Simple-MVVM。
以下简要介绍该框架的基本使用方法。
<form class="form-horizontal" role="form" data-context="TestController">
<div class="form-group">
<legend>Form title</legend>
</div>
<div class="form-group">
<div class="col-sm-6 col-sm-offset-2">
<input type="text" class="form-control" bind-val="age,format=format" style="margin:5px 0" />
<input type="text" class="form-control" bind-val="desc" style="margin:5px 0" />
<input type="range" min="10" max="300" bind-val="age" step="10" class="form-control" style="margin:5px 0" />
<input type="button" class="btn btn-primary" value="更新" style="margin:5px 0" on-click="update" />
</div>
</div>
</form>
<script>
var TestController = {
data: {
name: 'xiaoming',
age: 3,
desc: function() {
return this.name + ' likes looking little movie. he should take care of his body'
}
},
format: function(val) {
return val + '岁'
},
update: function() {
this.name = 'this is a test'
this.age = 18
}
}
$('body').controller()
</script>
控制器绑定与作用域
首先需定义一个控制器,其形式可以是JSON对象或函数。在顶层HTML元素上设置data-context属性为控制器名称,即可将该控制器绑定至该节点及其所有子元素。若子元素中存在嵌套的Controller,则其子元素的作用域将指向对应的子控制器。
属性监控与复杂属性处理
1. 监控属性及复杂属性处理
所有数据属性必须定义在data节点下。若某属性被定义为函数,则视为复杂属性(如desc示例)。复杂属性默认只读,若尝试直接重新赋值,系统将抛出错误提示。
HTML元素中的绑定格式为“{属性名, format=[控制器方法]}”。属性名支持嵌套访问(如a.b),但不支持表达式,因复杂属性已能替代表达式功能。当前限制在于业务逻辑复杂时可能导致大量复杂属性定义。属性名右侧为可选参数,目前仅支持format,即用于转换属性在HTML中显示格式的方法。
指令系统扩展机制
2. 指令系统扩展
指令绑定语法采用bind-{指令}形式。目前已实现val、attr、text、html及template指令。前四项均为对jQuery方法的简单封装,而template指令依赖jquery-tmpl插件。如需扩展更多指令,只需实现init方法(接收当前observer参数)及update方法(参数包括jQuery元素、最新值及当前控制器实例)。扩展已有指令将默认覆盖原有实现。具体示例如下:
$.controller.addDirective("val", {
init: function (observer) {
if (observer.$ele.is('input,select')) {
//监听onchange事件
observer.$ele.on('input propertychange', function () {
var newVal = $(this).val()
observer.writeValue(newVal)
})
}
},
update: function ($ele, newVal, controller) {
$ele.val && $ele.val(newVal)
}
})
事件绑定与生命周期钩子
3. 事件绑定机制
事件绑定语法为on-{事件}=“{控制器方法},type=on/one”。控制器方法右侧同样支持可选参数,目前仅支持绑定类型on或one,默认为on。控制器方法接收两个参数:一为可在对应事件元素上设置的初始参数,二为event事件对象。
<button type="button" class="btn btn-primary" data-page="1" on-click="refesh">查询</button>
4. 方法访问方式
在控制器内部,直接通过this.属性名即可访问data节点下的对应属性。
5. 生命周期钩子
框架提供了init和created两个钩子。init在监听所有属性之后、DOM编译之前执行,可用于初始化参数;created则在DOM元素编译完成后触发。
控制器默认实现了extend继承方法,用于继承其他控制器,且必须在init方法中调用。用户也可自行通过原型链继承方式实现。示例如下:
init: function () {
this.extend(PageController)
},
created: function () {
//TODO
},
通用组件扩展与最佳实践
6. 通用组件扩展建议
在项目开发中,通常有一套公用组件库。可通过如下方式扩展,将对应组件挂载至所有控制器实例下,从而在任意方法中直接调用,例如this.http.post()。
建议尽量统一将回调方法的作用域指向控制器,以避免开发过程中频繁出现作用域引用问题。
$.controller.extend({
utils: utils,
notify: $.notify,
modal: $.modal,
http: $.http,
alert: $.alert
})
7. 原理及代码分析(待续...)
整个JavaScript代码量仅300余行,实现较为简单,部分场景尚未考虑,部分功能计划实现但未完成。目前框架不支持数组变化检测及局部DOM更新。
以上关于使用JavaScript实现简易MVVM框架的示例内容即为全部分享,希望能对读者有所参考,也欢迎大家支持本站。
ES6模块化中js文件怎么用import和export交互
2026-11-05 17:00:48
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
