vue数据双向绑定是数据劫持结合发布订阅模式的方式实现的。
首先要对数据进行劫持监听,设置一个监听器observe用来监听所有属性。如果属性变化了,就通知订阅者watcher是否需要更新。
因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。
1、实现监听器observe(Object.defineProperty / proxy),用来劫持属性,当属性变化就告诉订阅者watcher
2、实现订阅者watcher,每一个watcher都绑定一个更新函数,watcher可以收到observe的属性变化执行对应函数,触发视图的更新。
3、实现解析器compile,可以扫描和解析每个节点上的指令(v-mode、v-on),如果节点上有对应指令的话,初始化这类节点的模板数据,再更新到视图上,最后初始化相对应的订阅者watcher。
Object.defineProperty在转换对象的属性的时候,是非常昂贵的操作
JavaScript引擎希望对象的结构越简单越好,在不断改变对象属性的时候,可优化的几率降低了.
Proxy是对原始对象进行proxy,不需要对原始对象进行过多的改动
proxy简单实现:
let input = document.getElementById('input'), p = document.getElementById('p'), obj = {}
let newobj = new Proxy(obj, {
get: function (target, propKey, receiver) {
return Reflect.get(target, propKey, receiver)
},
set: function (target, propKey, value, receiver) {
if (propKey === 'text') {
input.value = value;
p.innerHTML = value;
}
return Reflect.set(target, propKey, value, receiver)
}
})
input.addEventListener('keyup', function (e) {
newobj.text = e.target.value;
})
data为什么返回return而不是直接是一个对象:
vue组件是可复用的vue实例,那么如果data是普通的对象,所有复用这个实例的组件都引入了同一份数据,就造成了数据污染。
如果将data封装成函数,实例化组件的时候只是调用了这个函数生成的数据副本,避免了数据污染。
计算属性的本质是computed watcher
监听属性的本质是user watcher
计算属性适用于在模板渲染中,某个值是依赖于其他响应式属性甚至是计算属性而来
监听属性适用于监听某个值的改变,从而进行一系列的负责逻辑
vuex
function resetStoreVM (store, state, hot) {
Vue.config.silent = true
store._vm = new Vue({
data: {
$$state: state
},
computed
})
}
本质是将state作为了一个隐藏的vue实例的data,commit操作,其实就是修改了data的值,修改被defineReactive代理的对象值后,会将其收集到的依赖的watcher中的dirty设置为true,等到下一次访问该watcher中的值后重新获取最新值。
所以state的值必须预设好,动态添加的没有被defineReactive,依赖系统没有检测到,就无法更新
vue-router 更新视图但不重新请求页面
1、hash模式:HashHistory利用url中的Hash(#)
1 $router.push() //调用方法
2 HashHistory.push() //根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash= XXX)
3 History.transitionTo() //监测更新,更新则调用History.updateRoute()
4 History.updateRoute() //更新路由
5 {app._route= route} //替换当前app路由
6 vm.render() //更新视图
2、history:H5的history interface(pushState() replace())