SOURCE

console 命令行工具 X clear

                    
>
console
// 如何实现简单版本的vue响应式原理
/*
    响应式原理:从改变一个数据到发生改变的过程
    
                       get部分收集依赖
                              ↓                      
    数据改变触发set  →  set部分触发notify   →   更改对应的虚拟dom   →   更新render
*/
 
/*
     1.构造函数(实现数据的双向绑定)
*/

/*
    vue函数
    vue是function,function的原型prototype是Function,
    Function的原型prototype是Object

    这里的Vue是构造函数

    要点:实现上面的4个步骤
*/
function Vue() {
    //  定义数据
    this.$data = { a : 1 }
    //  获取id="app"的dom元素(真实dom)
    this.el = document.getElementById("app")
    //  定义虚拟dom
    this.virtualDom = ""
    //  监听数据变化
    this.observer(this.$data);
    //  重新渲染
    this.render()

    // 构造函数,隐式创建,隐式子返回
    //  var this = {}
    //  return this 
    console.log(this)
}
/*
    要点:vue原型prototype设置observer函数,监听数据变化(连接virtualDom,render,el,$data)
            +注册getter,setter
        实现12步骤,数据改变触发set+set部分触发notify(get部分收集依赖)
*/
Vue.prototype.observer = function (obj) {
    var value;                  //  定义初始值
    var self = this;            //  绑定当前作用域,this = vue函数,不然没有这句,嵌套函数内再访问this,this=window
    
    //  若$data = { a: {b:"aa"} }   a为对象
    //  for...in...遍历对象
    for (var key in obj) {
        value = obj[key]    //以上面为例子,obj为传入的参数this.$data,key="a",值=obj["a"]
        //  判断,如果value为对象,则再调用一次observer,参数为vaule(再一次调用自身,实现访问所有数据)
        if (typeof value === 'object') {
            this.observer(value)
        } else {
            //  如果value不是对象,typeof [] === 'object(注意)
            //  this指向最后调用它的对象(这句话在掘金看到的,提醒要时刻记住)
            Object.defineProperty(this.$data,key,{
                get:function () {
                    //  进行依赖收集(看图即可:get部分收集依赖)
                    return value
                },
                set:function (newValue) {
                    //  set部分通知更新(看图:set部分触发notify)
                    //  1.数据改变触发set   →   2.set部分通知更新 
                    value = newValue
                    //  4.重新render,这里要获取上层作用域,要使用self,嵌套函数内的this指向window,
                    self.render()
                }
            })
        }
    }

    console.log(this)   
}
/*
     要点:实现34步骤,更改对应的虚拟dom+重新render
*/
Vue.prototype.render  = function () {
    //  3.更改对应的虚拟dom
    this.virtualDom = 'i am ' + this.$data.a
    //  4.重新render (把新的虚拟dom赋值到真实dom)
    this.el.innerHTML = this.virtualDom

    console.log(this)  //   
}


/*
      2.创建实例,使用变时器setTImeout呈现效果(数据2秒后自动改变)
*/
var vm = new Vue()

setTimeout(function () {
    console.log("changes")
	console.log(vm.$data)	//	Object {a:1}
    //  1.  数据改变触发set
    vm.$data.a = 444
},2000)

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <div id="app">
        <!-- 一定要改设置:no Wrap in <body> -->
    </div>
</body>    
</html>