不废话直接上代码,代码可以在本地运行

# index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <input type="text" id="a" v-model="text">
        {{text}}
    </div>
    <script src="src/Dep.js"></script>
    <script src="src/Observe.js"></script>
    <script src="src/Watcher.js"></script>
    <script src="src/Compile.js"></script>
    <script src="src/MVVM.js"></script>
    <script>
        var vm = new Vue({
            el: 'app',
            data: {
                text: 'Hello world!'
            }
        })
    </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# MVVM.js
//提供了全局的Vue 模版
function Vue(options) {
    this.data = options.data //对象 {text:'hello world!'}
    var data = this.data  //局部变量加快代码速度 减少作用域链的查询
    //发布者 例如data 中text发生了变化,发布通知到订阅者
    observe(data, this)
    var id = options.el;
    var dom = new Compile(document.getElementById(id), this) //this Vue的实例 包含着data
    //编译完后,将dom返回倒app中
    document.getElementById(id).appendChild(dom)
}
1
2
3
4
5
6
7
8
9
10
11
# Compile.js
function Compile(node, vm) {
    if (node) {
        this.$frag = this.nodeToFragment(node, vm);
        return this.$frag
    }
}

Compile.prototype = {
    nodeToFragment: function (node, vm) {
        var self = this;
        var frag = document.createDocumentFragment();
        var child;
        while (child = node.firstChild) {
            self.compileElement(child, vm)
            frag.append(child)
        }
        return frag
    },
    compileElement: function (node, vm) {
        var reg = /\{\{(.*)\}\}/;
        //节点类型为元素
        if (node.nodeType === 1) {
            var attr = node.attributes;
            for (var i = 0; i < attr.length; i++) {
                if (attr[i].nodeName = 'v-model') {
                    //获取v-model绑定的属性名
                    var name = attr[i].nodeValue;
                    //给相应的data属性赋值,进而触发该属性的set方法
                    node.addEventListener('input', function (e) {
                        vm[name] = e.target.value;
                    })
                    //node.value = vm[name];将data的值赋值给该node
                    new Watcher(vm, node, name, 'value')
                }
            }

        }
        //节点为文本
        if (node.nodeType === 3) {
            if (reg.test(node.nodeValue)) {
                //获取匹配的字符串
                var name = RegExp.$1;
                name = name.trim();
                // node.nodeValue = vm[name];将data的值赋值给node
                new Watcher(vm, node, name, 'nodeValue')
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Watcher.js
function Watcher(vm, node, name, type) {
    Dep.target = this
    this.name = name
    this.node = node
    this.vm = vm
    this.type = type
    this.update();
    Dep.target = null
}

Watcher.prototype = {
    update: function () {
        this.get();
        //对应的哪行node节点 {{text}}[nodeValue] = (new Vue())['text']
        //{{text}} => Hello World
        this.node[this.type] = this.value;//订阅者执行相应操作
    },
    //获取data的属性
    get: function () {
        this.value = this.vm[this.name]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Observe.js
function defineReactive(obj, key, val) {//obj:vm对象 key:text val:hello world
    var dep = new Dep();

    Object.defineProperty(obj, key, {
        get: function () {
            //添加订阅者watcher到主题对象Dep
            //target 其实就是watcher对象
            if (Dep.target) {
                //js浏览器单线程特性,保证在一个时间内只有一个监听器使用
                dep.addSub(Dep.target)
            }
            return val
        },
        set: function (newVal) {
            if (newVal == val) return;
            val = newVal;
            console.log(val)
            //作为发布者发出通知
            dep.notify()
        }
    })
}
//obj: {text: 'Hello world!'}
function observe(obj, vm) {
    Object.keys(obj).forEach(key => {
        defineReactive(vm, key, obj[key])
    })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Dep.js
function Dep() {
    this.subs = [];
}
Dep.prototype = {
    addSub: function (sub) {
        this.subs.push(sub)
    },
    notify: function () {
        this.subs.forEach(sub => {
            sub.update();
        })
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13