Vuex原理解析
vuex
的设计理念是:集中状态管理,状态变化可预测
插件
使用vuex
时,必须先安装Vue.use(Vuex)
,即安装vuex
插件。
-
如何实现插件?
很简单,
install()
方法暴露在外。 -
这个补充剂的主要功能是什么?
安装
$store
。由于Vue.use()
先执行,且new Vue()
的实例尚未创建,因此在延迟安装中使用mixin
进行混合。
function install(_Vue) {
//Vue.use() 可以接收到Vue实例
Vue = _Vue
Vue.mixin({
beforeCreate() {
// 因为使用混入,钩子在每个组件创建实例时都会调用
// 根实例才有该选项
if (this.$options.store) {
//挂载vue原型方便其他组件调用$store
Vue.prototype.$store = this.$options.store
}
}
})
}
状态 反应式状态管理
- 有多少种方法来实现数据响应?
Object.defineProperty()
Vue.util.defineReactive()
Vue.observable()
new Vue({data(){}})
这里使用第四种
new Vue({
data: {
//$$state为什么用$$不代理,外部不能访问
$$state:options.state
}
})
state
包装
//只读state,可以获取数据
get state() {
return this._vm._data.$$state
}
set state(v) {
console.error('please use replaceState to reset state');
}
实现commit()
dispatch()
方法
输入方法名称和参数,前往mutations
和actions
匹配
// 保存mutaions和actions选项
this._mutations = options.mutations;
this._actions = options.actions;
commit(type, payload) {
const entry = this._mutations[type]
if (!entry) {
console.error('unkown mutation type');
}
//传入state
entry(this.state, payload)
}
dispatch(type, payload) {
const entry = this._actions[type]
if (!entry) {
console.error('unkown action type');
}
//{commit}上下文对象就是当前实例this
entry(this, payload)
}
将 this
绑定到
this.commit = this.commit.bind(this)
this.dispatch = this.dispatch.bind(this)
到达getters
vue
的getters
使用计算属性vue
进行实时数据监控
this._wrappedGetters = options.getters
//外界通过$store.getters.xxx访问getters
this.getters = {};
//定义computed数据
const computed = {};
//获取当前this
const store =this;
Object.keys(this._wrappedGetters).forEach(key => {
//this指向发生改变,用上方定义好的this
const fn = store._wrappedGetters[key]
//computed是无参数函数,而getters是有参数函数,所以高阶函数包一下
computed[key] = function(){
return fn(store.state)
}
})
//指定getters为只读属性
Object.defineProperty(store.getters,key,{
get: () => store._vm[key]
})
this._vm = new Vue({
data: {
$$state: options.state // $$不代理,外部不能直接访问
},
computed
})
vue-router原理
如果在一页程序中更改url
,则视图
实现插件并安装$router
// 参数1是Vue.use调用时传入的
VueRouter.install = function(_Vue) {
Vue = _Vue;
// 1.挂载$router属性
//Vue.use()优先执行,此时new Vue()还没有执行,所以要延迟执行$router挂载
// 全局混入目的:延迟下面逻辑到router创建完毕并且附加到选项上时才执行
Vue.mixin({
//new Vue()第一个执行的钩子函数
beforeCreate() {
// 因为使用混入,钩子在每个组件创建实例时都会调用
// 根实例才有该选项
if (this.$options.router) {
//挂载vue原型方便其他组件调用$router
Vue.prototype.$router = this.$options.router;
}
},
});
更改网址页面不刷新
哈希
#/user/1
hash
模式将带有“#
”并且不会刷新浏览器。
跟随 hashchange
进行 url
历史
/user/1
history
正常url
形状
有监控popstate
监控url
变化
响应数据,url改变内容,重新渲染
Vue.util.defineReactive
指定响应数据
//定义响应式matched数组,存放当前路由匹配的嵌套组件
Vue.util.defineReactive(this,'matched',[]);
//遍历路由表routes
this.match();
match(routes){
//路由表
let routes = routes || this.$options.routes;
//递归遍历路由表
for(route of routes){
if(route.path === '/' && this.current === '/'){
//添加路由信息
this.matched.push(route);
return;
}
// /about/info
if(route.path !== '/' && this.current.indexOf(route.path) != -1){
//包含/about
this.matched.push(route);
//还有嵌套子路由,递归调用match
if(route.children.length > 0){
this.match(route.children)
}
return;
}
}
}
}
实现两个全局组件 router-link 和 router-view
实施路由器链路
实施标记 a
<a href="xxx">user</a>
Vue.component("router-link", {
props: {
to: {
type: String,
required: true,
},
},
render(h) {
// <a href="to">xxx</a>
// return <a href={'#'+this.to}>{this.$slots.default}</a>
return h(
"a",
{
attrs: {
href: "#" + this.to,
},
},
this.$slots.default
);
},
});
router-view的实现
深度判断router-view
实现布线嵌套;通过矩阵渲染嵌套组件 matched
Vue.component("router-view", {
render(h) {
//标记当前router-view深度
this.$vnode.data.routerView = true;
//深度标识
let depth = 0;
//向上查找routerView
let parent = this.$parent;
while(parent){
const vnodeData = parent.$vnode && parent.$vnode.data;
if(vnodeData.routerView){
//说明parent是个router-view
depth++;
}
parent = parent.$parent;
}
// 获取当前路由对应的组件
let component = null;
//matched是响应式的所以动态渲染路由
const route = this.$router.matched[depth];
if (route) {
component = route.component
}
console.log(this.$router.matched, component);
return h(component);
},
});
};
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。