我个人以为 mvvm 框架里面最重要的一点就是 VM 这部分, 它要与 Model 层建立联系, 将 Model 层转换成可以被 View 层识别的数据结构; 其次也要同 View 建立联系, 将数据及时更新到 View 层上, 并且响应 View 对数据的更改, 同步到 Model 层.
MVVM 的具体例子, 可以看一下阮一峰老师的这篇博客 http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html .
我们提取其中比较关键的点:
Model 层存储数据
需要一个 View-Model 来对数据做中转, 响应数据变化, 同步到两端
View 层来负责展示数据, 接受用户事件
Model 层, 我们用一个对象来代表. 例如:
- let data = {
- text: 'foo'
- };
View 层对于我们而言, 可以认为是 DOM 节点. 例如:
- <div id="app">
- <p>text</p>
- </div>
为了方便注入内容, 改用 JS 来写, 可以写成
let str = `<div id="app"><p>test</p></div>`;
至于 View-Model, 我们要做两件事, 一是将数据及时同步到 View 层, 二是响应用户事件, 更改数据. 我们设计一个函数来完成这项工作.
- // 把对象转成可监听的
- const ob = function (data) {
- // 无 new 构造
- if (!(this instanceof ob)) {
- return new ob(data);
- }
- // 设定观察者列表
- let observerList = [];
- // 暴露添加观察者方法
- this.addOb = function (fn) {
- observerList.push(fn);
- }
- // 遍历属性, 通过 defineProperty 来对属性变化做监听
- for (let key in data) {
- let value = data[key];
- Object.defineProperty(data, key, {
- enumerable:true, // 枚举
- get () {
- return value;
- },
- set (newVal) {
- value = newVal;
- observerList.forEach((el)=>{
- el(newVal);
- })
- }
- })
- }
- };
为了简单起见, 把 View 的渲染封装成一个函数, 当然实际上不能这么操作.
- const render = (text) => {
- document.getElementById('app').innerHTML = '<p>'+ text +'</p>';
- }
然后将 render 和数据绑定起来.
- let testObj = {
- name: 'liu'
- };
- const vm = (data) => {
- render(testObj.name);
- let newOb = ob(data);
- newOb.addOb(function () {
- render(data.name);
- })
- };
- vm(testObj);
- // 如果直接设置 testObj.name = 'test'; 就会触发对应的修改
以上, 基本上实现了数据和视图间的绑定.
可以再继续改进, 在 render 之前, 加入 virtual dom 的逻辑, 或者加入一些语法特性, 比如类似 vue 和 React 的语法糖.
由于直接设置对象属性其实不大安全, 而且不易于追踪, 可以把状态统一提取出来, 进一步封装, 只能通过 action 去触发修改, 然后分发到各个调用方. 保证数据的单项流动.
这里是一个简单的例子 https://jsfiddle.net/liuyj/6jz82Lpw/3/ .
来源: https://www.cnblogs.com/liuyongjia/p/9231873.html