MVVM机制浅析

栏目: 编程工具 · 发布时间: 6年前

内容简介:来公司一年多了,接触了angular,vue这两种主流的MVVM框架,之前一直没有去思考其实现的原理,但框架其实也是用基础的js方法实现的,所以还是很有必要去了解下其原理的,在这里简单的总结一下。实现数据绑定的大致几种做法:发布者-订阅者模式:一般通过sub,pub的方式实现数据和视图的绑定监听,更新数据方式通常做法是 vm.set('property', value),现在用的较少,这里不再阐述其原理了,有兴趣的同学可以点击这里

来公司一年多了,接触了angular,vue这两种主流的MVVM框架,之前一直没有去思考其实现的原理,但框架其实也是用基础的js方法实现的,所以还是很有必要去了解下其原理的,在这里简单的总结一下。

1,几种实现双向绑定的方法

实现数据绑定的大致几种做法:

  • 发布者-订阅者模式(backbone.js)
  • 脏值检查(angular.js)
  • 数据劫持(vue.js)

发布者-订阅者模式:一般通过sub,pub的方式实现数据和视图的绑定监听,更新数据方式通常做法是 vm.set('property', value),现在用的较少,这里不再阐述其原理了,有兴趣的同学可以点击这里

脏值检查:ng是通过脏检查的检测机制来对比数据是否发生了变更,来决定是否更新视图。那么脏检查是怎么触发的呢,大致有一下几种情况:

  • UI事件,如ng-click
  • ajax请求
  • timeout延迟
  • 执行$apply()(非ng事件时需我们手动触发脏检查)

这里我们就脏值检查举个小栗子:

// html
<div ng-app="app">
    <div ng-controller="myController">
        <span>{{count}}</span>
        <button ng-click="counter=counter+1">increase</button>
    </div>
</div>


// js
var app = angular.module('app', []);
app.controller('myController', function($scope) {
    $scope.count = 0;
})
复制代码

看下这时的效果

MVVM机制浅析

我们发下这时通过ng-click形成的数据更改是实时响应的。来,让我们换种实现方式:

<div ng-app="app">
    <div ng-controller="CounterCtrl">
        <span>{{data}}</span>
        <button my-click>click</button>
    </div>
</div>

app.controller('CounterCtrl', function($scope) {
    $scope.data = 0;
})
app.directive('myClick', function() {
    return function(scope, element, attr) {
        element.on('click', function() {
            scope.data++;
            console.log(scope.data);
        })
    }
})
复制代码

这时的效果

MVVM机制浅析

我们发现虽然data的值在更改,但是view却并没有更新,这是因为我没有用ng事件来触发UI事件,所以没有执行脏检查,这个时候我们可以手动加下$apply试下效果。

数据劫持:vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

2,数据劫持的代码实现

这里我们用数据劫持的方式来实现一个简易的数据绑定,我们来写一下:

简单的写下html

<div id='app'>
    <h3>姓名</h3>
    <p>{{name}}</p>
    <h3>年龄</h3>
    <p>{{age}}</p>
</div>
复制代码

接下来准备我们要展示数据

document.addEventListener('DOMContentLoaded', function() {
    let opt = {
        el:'#app', 
        data:{
            name:'准备中...', 
            age:30
        }
    }
    let vm = new MVVM(opt);
    setTimeout(() => {
        opt.data.name = '小明';
    }, 2000);
})
复制代码

好,到这里准备工作都做好了,接下来是重头戏,不多说,直接上代码:

class MVVM {
    constructor(opt) {
        this.opt = opt;
        this.observe(opt.data);
        let root = document.querySelector(opt.el);
        this.complie(root);
    }
    
    // 绑定观察者对象
    observe(data) {
        Object.keys(data).forEach(key => {
            // 声明一个观察者对象
            let obv = new Observer();
            
            // 记录data[key]的值,防止set的时候递归调用报错
            data['_' + key] = data[key];
            
            Object.defineProperty(data, key, {
                get() {
                    Observer.target && obv.addSubNode(Observer.target);
                    return data['_' + key];
                },
                set(newVal) {
                    obv.update(newVal);
                    data['_' + key] = newVal;
                }
            })
        })
    }
    
    // 初始化页面,遍历 DOM,收集每一个key变化时,随之调整的位置,以观察者方法存放起来    
    complie(node) {
        [].forEach.call(node.childNodes, child =>{
            if(!child.firstElementChild && /\{\{(.*)\}\}/.test(child.innerHTML)){
                let key = RegExp.$1.trim()
                child.innerHTML = child.innerHTML.replace(new RegExp('\\{\\{\\s*'+ key +'\\s*\\}\\}', 'gm'),this.opt.data[key]) 
                
                // 记录当前的DOM节点
                Observer.target = child;
                
                // 调用set函数
                this.opt.data[key];
                
                Observer.target = null;
            }
            else if (child.firstElementChild) {
                this.compile(child);
            }
        })
    }
}

// 常规观察者类
class Observer{
    constructor() {
        this.subNode = []    
    }
    addSubNode(node){
        this.subNode.push(node)
    }
    update(newVal){
        this.subNode.forEach(node=>{
            node.innerHTML = newVal
        })
    }
}

复制代码

好了至此就是所有的代码,我们可以把opt声明为全局变量,这样我们就可以在控制台修改相关数据,然后实时观察修改结果。

具体的实现代码参照了这篇文章 50行代码的MVVM,感受闭包的艺术 ,当时看到的时候有些地方比较困惑,理解了之后这里将其贴上来,注释上我自己的理解,仅供学习参考吧。(感觉data['_' + key] 这种记录方式不太好,引入了无关的值)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Parsing Techniques

Parsing Techniques

Dick Grune、Ceriel J.H. Jacobs / Springer / 2010-2-12 / USD 109.00

This second edition of Grune and Jacobs' brilliant work presents new developments and discoveries that have been made in the field. Parsing, also referred to as syntax analysis, has been and continues......一起来看看 《Parsing Techniques》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码