设计模式:创建型-单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点,这样的模式就叫做单例模式。
单例模式的实现思路
按照单例模式的定义,我们需要思考这样一个问题:如何才能保证一个类仅有一个实例?
一般情况下,当我们创建了一个类(本质是构造函数)后,可以通过new关键字生成任意多的实例对象。像这样:
class Single {
log(){
console.log('我是一个单例对象')
}
}
const s1 = new Single();
const s2 = new Single();
s1 === s2 // false
上面我们创建了一个s1对象,又new了一个对象s2,两者是相互独立的对象,各占一块内存空间。而单例模式想做的是,不管我们尝试去创建多少次,它都只给你返回第一次所创建的那唯一的一个实例。
要做到这一点,就需要函数具备判断自己是否已经创建过一个实例的能力。我们现在把这段判断逻辑写成一个静态方法:
class SingleDog {
show(){
console.log('我是一个单例对象')
}
static getInstance () {
// 判断是否已经new过一个实例
if(!SingleDog.instance){
// 未创建
SingleDog.instance = new SIngleDog()
}
return SingleDog.instance
}
}
const s1 = SingleDog.getInstance();
const s2 = SingleDog.getInstance();
s1 === s2 // true
getInstance的逻辑还可以通过闭包,来实现:
class SingleDog {
show(){
console.log('我是一个实例');
}
getInstance () {
let instance = null;
return function () {
if(!instance){
instance = new SingleDog();
}
return instance;
}
}
}
const s1 = SingleDog.getInstance;
const s2 = SingleDog.getInstance;
console.log(s1 === s2); // true
生产实践:Vuex中的单例模式
近年来,基于Flux架构的状态管理工具层出不穷,其中应用最广泛的要数Vuex和Redux。无论是Vuex还是Redux,他们都实现了一个全局的Store用于存储应用的所有状态。这个store的实现,正是单例模式的典型应用。
理解Vuex中的Store
Vuex使用单一状态树,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源(SSOT)”而存在。这也意味着,每个应用将仅仅包含一个store实例。单一的状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
------Vuex官方文档
在Vue中,组件之间是相互独立的,组件间通信最常用的办法是通过props(父子组件),稍微复杂一点的(比如兄弟组件之间的通信)我们通过自己实现简单的事件监听函数也能解决掉。但当组件非常多,关系复杂时,这种通信方式就变得复杂难以维护。这时最好的做法就是将共享的数据抽出来,放在全局,组件们按照一定的规则去存取数据,保证状态以一种可预测的方式发生变化。于是便有了Vuex,这个用来存放共享数据的唯一数据源就是Store。
Vuex如何确保Store的唯一性
我们先来看看如何在项目中引入Vuex:
// 安装vuex插件
Vue.use(Vuex);
// 将store注入到Vue实例中
new Vue({
el: '#app',
store
})
通过调用Vue.use()方法,我们将Vuex注入到Vue实例中,Vuex是一个对象,在内部实现了一个install方法,这个方法在引入时被调用,从而把Store注入到Vue实例里去。也就是说每引入一次,都会给Vue实例注入一个Store。
let Vue // 声明一个vue,类似上面的instanse
...
export function install (_Vue) {
// 判断传入的Vue实例对象是否已经被install过Vuex插件(是否有了唯一的state)
if (Vue && _Vue === Vue) {
if (process.env.NODE_ENV !== 'production') {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
// 若没有,则为这个Vue实例对象install一个唯一的Vuex
Vue = _Vue
// 将Vuex的初始化逻辑写进Vue的钩子函数里
applyMixin(Vue)
}
上面便是Vuex源码中单例模式的实现方法了,套路可以说跟我们的getInstance如出一辙。通过这种方式,可以保证Vue实例中只会拥有一个全局的Store。