Appearance
46.Vue 中使用了哪些设计模式?
1.单例模式
单例模式就是整个程序中有且仅有一个实例 (pinia 中的某一个 store)
js
import { useCounterStore } from "./counterStore.js";
// 组件 1
const counterStore1 = useCounterStore();
// 组件 2
const counterStore2 = useCounterStore();
// 在组件1和组件2中使用的是同一个存储实例
import { useCounterStore } from "./counterStore.js";
// 组件 1
const counterStore1 = useCounterStore();
// 组件 2
const counterStore2 = useCounterStore();
// 在组件1和组件2中使用的是同一个存储实例
store 实现原理
js
function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric {
// ...
if (!pinia._s.has(id)) {
if (isSetupStore) { // 缓存实例
createSetupStore(id, setup, options, pinia)
} else {
createOptionsStore(id, options as any, pinia)
}
}
const store: StoreGeneric = pinia._s.get(id)!
return store as any
}
function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric {
// ...
if (!pinia._s.has(id)) {
if (isSetupStore) { // 缓存实例
createSetupStore(id, setup, options, pinia)
} else {
createOptionsStore(id, options as any, pinia)
}
}
const store: StoreGeneric = pinia._s.get(id)!
return store as any
}
2.工厂模式
传入参数即可创建实例 (
createComponentInstance
)
js
export function createComponentInstance(
vnode: VNode,
parent: ComponentInternalInstance | null,
suspense: SuspenseBoundary | null
) {
const instance: ComponentInternalInstance = {
vnode,
parent,
suspense,
// ...
};
return instance;
}
export function createComponentInstance(
vnode: VNode,
parent: ComponentInternalInstance | null,
suspense: SuspenseBoundary | null
) {
const instance: ComponentInternalInstance = {
vnode,
parent,
suspense,
// ...
};
return instance;
}
3.发布订阅模式
订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心,由调度中心统一调度订阅者注册到调度中心的处理代码。(
on、emit
)
vue
<!-- 事件绑定 -->
<MyComponent @myFn="a" @myFn="b"></MyComponent>
<!-- 触发事件 emit("myFn") -->
<!-- 事件绑定 -->
<MyComponent @myFn="a" @myFn="b"></MyComponent>
<!-- 触发事件 emit("myFn") -->
js
export function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_MyComponent = _resolveComponent("MyComponent");
return (
_openBlock(),
_createBlock(
_component_MyComponent,
{
onMyFn: [_ctx.a, _ctx.b], // 订阅
},
null,
8
)
);
}
export function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_MyComponent = _resolveComponent("MyComponent");
return (
_openBlock(),
_createBlock(
_component_MyComponent,
{
onMyFn: [_ctx.a, _ctx.b], // 订阅
},
null,
8
)
);
}
4.代理模式
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。(
toRef
)
js
class ObjectRefImpl<T extends object, K extends keyof T> {
public readonly __v_isRef = true
constructor(
private readonly _object: T,
private readonly _key: K,
private readonly _defaultValue?: T[K]
) {}
// ref.value.key -> 访问原始 object.key
get value() {
const val = this._object[this._key]
return val === undefined ? this._defaultValue! : val
}
set value(newVal) {
this._object[this._key] = newVal
}
get dep(): Dep | undefined {
return getDepFromReactive(toRaw(this._object), this._key)
}
}
class ObjectRefImpl<T extends object, K extends keyof T> {
public readonly __v_isRef = true
constructor(
private readonly _object: T,
private readonly _key: K,
private readonly _defaultValue?: T[K]
) {}
// ref.value.key -> 访问原始 object.key
get value() {
const val = this._object[this._key]
return val === undefined ? this._defaultValue! : val
}
set value(newVal) {
this._object[this._key] = newVal
}
get dep(): Dep | undefined {
return getDepFromReactive(toRaw(this._object), this._key)
}
}
5.中介者模式
中介者是一个行为设计模式,通过提供一个统一的接口让系统的不同部分进行通信。 (
pinia
)
6.外观模式
提供了统一的接口,用来访问子系统中的一群接口,以便隐藏底层复杂性。(
baseCompile
)
js
// 可以粗略理解成外观模式 (主要目标是编译而不是隐藏复杂性,但抽象了底层的编译细节)
export function baseCompile(
template: string | RootNode,
options: CompilerOptions = {}
): CodegenResult {
// ...
// 1.编译
const ast = isString(template) ? baseParse(template, options) : template;
const [nodeTransforms, directiveTransforms] =
getBaseTransformPreset(prefixIdentifiers);
// 2.转化
transform(
ast,
extend({}, options, {
prefixIdentifiers,
nodeTransforms: [
...nodeTransforms,
...(options.nodeTransforms || []), // user transforms
],
directiveTransforms: extend(
{},
directiveTransforms,
options.directiveTransforms || {} // user transforms
),
})
);
// 3.生成代码
return generate(
ast,
extend({}, options, {
prefixIdentifiers,
})
);
}
// 可以粗略理解成外观模式 (主要目标是编译而不是隐藏复杂性,但抽象了底层的编译细节)
export function baseCompile(
template: string | RootNode,
options: CompilerOptions = {}
): CodegenResult {
// ...
// 1.编译
const ast = isString(template) ? baseParse(template, options) : template;
const [nodeTransforms, directiveTransforms] =
getBaseTransformPreset(prefixIdentifiers);
// 2.转化
transform(
ast,
extend({}, options, {
prefixIdentifiers,
nodeTransforms: [
...nodeTransforms,
...(options.nodeTransforms || []), // user transforms
],
directiveTransforms: extend(
{},
directiveTransforms,
options.directiveTransforms || {} // user transforms
),
})
);
// 3.生成代码
return generate(
ast,
extend({}, options, {
prefixIdentifiers,
})
);
}
7.装饰模式
Vue2
装饰器的用法 (对功能进行增强 @) 古老写法
js
import { Vue, Component, Prop } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Prop(Number) readonly propA: number | undefined
@Prop({ default: 'default value' }) readonly propB!: string
@Prop([String, Boolean]) readonly propC: string | boolean | undefined
}
import { Vue, Component, Prop } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Prop(Number) readonly propA: number | undefined
@Prop({ default: 'default value' }) readonly propB!: string
@Prop([String, Boolean]) readonly propC: string | boolean | undefined
}
8.策略模式
策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现方案。 (
Vue2中mergeOptions
)
js
function mergeField(key: any) {
const strat = strats[key] || defaultStrat;
options[key] = strat(parent[key], child[key], vm, key);
}
return options;
// ...
strats.data = fn1;
strats[hook] = fn2;
strats[type + "s"] = fn3;
strats.watch = fn4;
strats.props = strats.methods = strats.inject = strats.computed = fn5;
strats.provide = fn6;
function mergeField(key: any) {
const strat = strats[key] || defaultStrat;
options[key] = strat(parent[key], child[key], vm, key);
}
return options;
// ...
strats.data = fn1;
strats[hook] = fn2;
strats[type + "s"] = fn3;
strats.watch = fn4;
strats.props = strats.methods = strats.inject = strats.computed = fn5;
strats.provide = fn6;
9.观察者模式
Vue2 中
watcher
&dep
的关系,无需用户主动触发更新,状态变化时会自动更新。
js
export default class Dep {
addSub(sub: DepTarget) { // 访问属性时, 订阅对应的watcher
this.subs.push(sub)
}
notify(info?: DebuggerEventExtraInfo) { // 属性变化时,发布对应的watcher
const subs = this.subs.filter(s => s) as DepTarget[]
for (let i = 0, l = subs.length; i < l; i++) {
const sub = subs[i]
sub.update()
}
}
}
export default class Dep {
addSub(sub: DepTarget) { // 访问属性时, 订阅对应的watcher
this.subs.push(sub)
}
notify(info?: DebuggerEventExtraInfo) { // 属性变化时,发布对应的watcher
const subs = this.subs.filter(s => s) as DepTarget[]
for (let i = 0, l = subs.length; i < l; i++) {
const sub = subs[i]
sub.update()
}
}
}
js
export default class Watcher implements DepTarget {
//...更新逻辑
update() {
if (this.lazy) {
this.dirty = true;
} else if (this.sync) {
this.run();
} else {
queueWatcher(this);
}
}
}
export default class Watcher implements DepTarget {
//...更新逻辑
update() {
if (this.lazy) {
this.dirty = true;
} else if (this.sync) {
this.run();
} else {
queueWatcher(this);
}
}
}
...