Vue.use
会判断传入的为对象还是方法,如果传入的是一个对象则查看该对象的install属性,如果为方法则直接调用传入的方法。为了防止重复添加插件,使用了一个installedPlugins数组存储已注册的插件,使用indexOf来判断是否已经存在该插件。
Vue.use = function(plugin){
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
if(installedPlugins.indexOf(plugin)>-1){
return this;
}
<!-- 其他参数 -->
const args = toArray(arguments,1);
args.unshift(this);
if(typeof plugin.install === 'function'){
plugin.install.apply(plugin,args);
}else if(typeof plugin === 'function'){
plugin.apply(null,plugin,args);
}
installedPlugins.push(plugin);
return this;
}
vux原理
全部组件添加store实例(Vue.use()来调用 )
下面代码为使用vuex的代码,因为调用了Vue.use(),所以可以推断出vuex里面有一个install方法。
import Vuex from 'vuex';
Vue.use(Vuex)
使用mixin的方式,并且利用组件的创建顺序父beforeCreated —> 父created —> 父beforeMounted —> 子beforeCreated —> 子created —> 子beforeMounted —> 子mounted —> 父mounted,来给每一个组件进行store的引用的复制,从而使每个组件都拥有了同一个$store挂载到身上。
+ 为什么是使用beforeCreated生命周期(目的为了让store也实现响应式):因为creted的时候option以及初始化好了。即vuex的实例store存储的数据也是实现了数据的双向绑定,如果在created进行混入,即data上的数据已经执行完了初始化操作,这样的话store就不具备响应式效果了。
let install = function(Vue){
Vue.mixin({
beforeCreate(){
if (this.$options && this.$options.store){ // 如果是根组件
this.$store = this.$options.store
}else { //如果是子组件
this.$store = this.$parent && this.$parent.$store
}
}
})
}
构造函数(new Vuex.Store())
因为我们使用的时候是使用为以下模式 ,可以推断出vuex的源码里面存在一个Store的构造函数。
//注册实例
new Vuex.Store({
state:{},
mutation:{}
....
})
Store构造函数传入的参数为有state,mutition等属性的一个对象,因此store的入参可以确认为一个对象options
- state: state因为通过options传入的就是一个state的,我们可以直接在里面定义一个实例的state属性,然后就可以了,即如:
class Store{
constructor(options) {
this.state = options.state
}
}
但是这样操作的情况下,不具备vuex具有的响应式,所以我们可以使用vue的响应式来实现,通过new Vue来实现数据响应式。即:
class Store{
constructor(options) {
this.vm = new Vue({
data: {
state: options.state
}
})
}
}
又但是我们在使用state的时候使用方式为this.$store.state.xxx,但是如果现在需要调用xxx属性,则需要通过this.$store.vm.state.xxx来调用,因此我们可以使用Object.defineProperty来劫持用户使用this.$store.state.xxx获取属性的时候,触发get()从而调用this.$store.vm.state.xxx,即
class Store{
constructor(options) {
this.vm = new Vue({
data:{
state:options.state
}
})
}
//新增代码---效果等同于Object.defineProperty
get state(){
return this.vm.state
}
}
- getter: 首先我们知道getter的参数传入
getter:{getName(state){return state.name}}以及调用方式this.$store.getter.getName。我们只需要在调用this.$store.getter.getName的时候将通过options传入的对应的key的方法进行执行就可以了。因此我们需要监听this.$store.getter.getName,即使用Object.defineProperty监听用户调用了实例getter的某个key,然后通过key来调用传入的getter的对应key的方法。这也是为什么在使用getter的时候,传入的参数为方法,但是调用的时候为什么可以不写括号来调用的原因。
class Store{
constructor(options) {
this.vm = new Vue({
data: {
state: options.state
}
})
//新增
this.getter={}
Object.keys(option.getter).forEach(key => {
Object.defineProperty(this.getter,key,{
get(){
return option.getter[key](this.state)
}
})
})
}
get state(){
return this.vm.state
}
}
Object.defineProperty
- mutation: 首先我们得知道mutation的传入方式
mutation: { set_name(state,val) {state.name = val}}以及调用方式this.$store.commit('set_name','张三')。首先入参为带了state,因此可以判断实例是对了传入的mutation进行了遍历并且给每个方法都传入实例的state作为第一个参数,因为val是给用户传入的即对应的为commit的第二个参数,因此遍历完的mutation肯定是返回一个第一个带一个参数的方法,即。
class Store{
constructor(options) {
this.vm = new Vue({
data: {
state: options.state
}
})
Object.keys(option.getter).forEach(key => {
Object.defineProperty(this.getter,key,{
get(){
return option.getter(this.state)
}
})
})
//新增
this.mutation = {}
Object.keys(options.mutation).forEach(key => {
this.mutation[key] = function(arg){
options.mutation[key](this.state,arg)
}
})
}
//新增
commit(key,val){
return this.mutation[key](val)
}
get state(){
return this.vm.state
}
}
Object.defineProperty
- action: action原理和上面差不多,更多详细参考