注册

前端哪有什么设计模式

前言



  • 常网IT源码上线啦!
  • 本篇录入吊打面试官专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
  • 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
  • 接下来想分享一些自己在项目中遇到的技术选型以及问题场景。




你生命的前半辈子或许属于别人,活在别人的认为里。那把后半辈子还给你自己,去追随你内在的声音。



1.jpg


一、前言


之前在讨论设计模式、算法的时候,一个后端组长冷嘲热讽的说:前端哪有什么设计模式、算法,就好像只有后端语言有一样,至今还记得那不屑的眼神。


今天想起来,就随便列几个,给这位眼里前端无设计模式的人,睁眼看世界。


二、观察者模式 (Observer Pattern)


观察者模式的核心是当数据发生变化时,自动通知并更新相关的视图。在 Vue 中,这通过其响应式系统实现。


Vue 2.x:Object.defineProperty


在 Vue 2.x 中,响应式系统是通过 Object.defineProperty 实现的。每当访问某个对象的属性时,getter 会被触发;当设置属性时,setter 会触发,从而实现数据更新时视图的重新渲染。


源码(简化版):


function defineReactive(obj, key, val) {
// 创建一个 dep 实例,用于收集依赖
const dep = new Dep();

Object.defineProperty(obj, key, {
get() {
// 当访问属性时,触发 getter,并把当前 watcher 依赖收集到 dep 中
if (Dep.target) {
dep.addDep(Dep.target);
}
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
dep.notify(); // 数据更新时,通知所有依赖重新渲染
}
}
});
}


  • Dep :它管理依赖,addDep 用于添加依赖,notify 用于通知所有依赖更新。

class Dep {
constructor() {
this.deps = [];
}

addDep(dep) {
this.deps.push(dep);
}

notify() {
this.deps.forEach(dep => dep.update());
}
}


  • 依赖收集:当 Vue 组件渲染时,会创建一个 watcher 对象,表示一个视图的更新需求。当视图渲染过程中访问数据时,getter 会触发,并将 watcher 添加到 dep 的依赖列表中。

Vue 3.x:Proxy


Vue 3.x 使用了 Proxy 来替代 Object.defineProperty,从而实现了更高效的响应式机制,支持深度代理。


源码(简化版):


function reactive(target) {
const handler = {
get(target, key) {
// 依赖收集:当访问某个属性时,触发 getter,收集依赖
track(target, key);
return target[key];
},
set(target, key, value) {
// 数据更新时,通知相关的视图更新
target[key] = value;
trigger(target, key);
return true;
}
};

return new Proxy(target, handler);
}



  • track:收集依赖,确保只有相关组件更新。
  • trigger:当数据发生变化时,通知所有依赖重新渲染。

三、发布/订阅模式 (Publish/Subscribe Pattern)


发布/订阅模式通过中央事件总线(Event Bus)实现不同组件间的解耦,Vue 2.x 中,组件间的通信就是基于这种模式实现的。


Vue 2.x:事件总线(Event Bus)


事件总线就是一个中央的事件处理器,Vue 实例可以充当事件总线,用来处理不同组件之间的消息传递。


// 创建一个 Vue 实例作为事件总线
const EventBus = new Vue();

// 组件 A 发布事件
EventBus.$emit('message', 'Hello from A');

// 组件 B 订阅事件
EventBus.$on('message', (msg) => {
console.log(msg); // 输出 'Hello from A'
});


  • $emit:用于发布事件。
  • $on:用于订阅事件。
  • $off:用于取消订阅事件。

四、工厂模式 (Factory Pattern)


工厂模式通过一个函数生成对象或实例,Vue 的组件化机制和动态组件加载就是通过工厂模式来实现的。


Vue 的 render 函数和 functional 组件支持动态生成组件实例。例如,functional 组件本质上是一个工厂函数,通过给定的 props 返回一个 VNode。


Vue.component('dynamic-component', {
functional: true,
render(h, context) {
// 工厂模式:根据传入的 props 创建不同的 VNode
return h(context.props.type);
}
});


  • functional 组件:它没有实例,所有的逻辑都是在 render 函数中处理,返回的 VNode 就是组件的“产物”。

五、单例模式 (Singleton Pattern)


单例模式确保某个类只有一个实例,Vue 实例就是全局唯一的。


在 Vue 中,全局的 Vue 构造函数本身就是一个单例对象,通常只会创建一个 Vue 实例,用于管理应用的生命周期和全局配置。


const app = new Vue({
data: {
message: 'Hello, Vue!'
}
});


  • 单例保证:整个应用只有一个 Vue 实例,所有全局的配置(如 Vue.config)都是共享的。

六、模板方法模式 (Template Method Pattern)


模板方法模式定义了一个操作中的算法框架,而将一些步骤延迟到子类中。Vue 的生命周期钩子就是一个模板方法模式的实现。


Vue 定义了一系列生命周期钩子(如 createdmountedupdated 等),它们实现了组件从创建到销毁的完整过程。开发者可以在这些钩子中插入自定义逻辑。


Vue.component('my-component', {
data() {
return {
message: 'Hello, 泽!'
};
},
created() {
console.log('Component created');
},
mounted() {
console.log('Component mounted');
},
template: '<div>{{ message }}</div>'
});

Vue 组件的生命周期钩子实现了模板方法模式的核心思想,开发者可以根据需要重写生命周期钩子,而 Vue 保证生命周期的流程和框架。


七、策略模式 (Strategy Pattern)


策略模式通过定义一系列算法,将它们封装起来,使它们可以相互替换。Vue 的 计算属性(computed)方法(methods) 可以看作是策略模式的应用。


计算属性允许我们定义动态的属性,其值是基于其他属性的计算结果。Vue 会根据依赖关系缓存计算结果,只有在依赖的属性发生变化时,计算属性才会重新计算。


new Vue({
data() {
return {
num1: 10,
num2: 20
};
},
computed: {
sum() {
return this.num1 + this.num2;
}
}
});

八、装饰器模式 (Decorator Pattern)


装饰器模式允许动态地给对象添加功能,而无需改变其结构。在 Vue 中,指令就是一种装饰器模式的应用,它通过指令来动态地改变元素的行为。


<div v-bind:class="className"></div>
<div v-if="isVisible">谁的疯太谍</div>

这些指令动态地修改 DOM 元素的行为,类似于装饰器在不修改对象结构的情况下,动态地增强其功能。


九、代理模式 (Proxy Pattern)


代理模式通过创建一个代理对象来控制对目标对象的访问。在 Vue 3.x 中,响应式系统就是通过 Proxy 来代理对象的访问。


vue3


const state = reactive({
count: 0
});

state.count++; // 会触发依赖更新

reactive:使用 Proxy 对对象进行代理,当对象的属性被访问或修改时,都会触发代理器的 get 和 set 操作。


function reactive(target) {
const handler = {
get(target, key) {
// 依赖收集:当访问某个属性时,触发 getter,收集依赖
track(target, key);
return target[key];
},
set(target, key, value) {
// 数据更新时,触发依赖更新
target[key] = value;
trigger(target, key);
return true;
}
};

return new Proxy(target, handler);
}


  • track:当读取目标对象的属性时,收集依赖,这通常涉及到将当前的 watcher 加入到依赖列表中。
  • trigger:当对象的属性发生改变时,通知所有相关的依赖(如组件)更新。

这个 Proxy 机制使得 Vue 可以动态地观察和更新对象的变化,比 Object.defineProperty 更具灵活性。


十、适配器模式 (Adapter Pattern)


适配器模式用于将一个类的接口转换成客户端期望的另一个接口,使得原本不兼容的接口可以一起工作。Vue 的插槽(Slots)和组件的跨平台支持某种程度上借用了适配器模式的思想。


Vue 插槽机制


Vue 的插槽机制是通过提供一个适配层,将父组件传入的内容插入到子组件的指定位置。开发者可以使用具名插槽、作用域插槽等方式,实现灵活的插槽传递。


<template>
<child-component>
<template #header>
<h1>This is the header</h1>
</template>
<p>This is the default content</p>
</child-component>

</template>

父组件通过 #header 插槽插入了一个标题内容,而 child-component 会将其插入到适当的位置。这里,插槽充当了一个适配器,允许父组件插入的内容与子组件的内容结构灵活匹配。


十全十美


至此撒花~


后记


我相信技术不分界,不深入了解,就不要轻易断言。


一个圆,有了一个缺口,不知道的东西就更多了。


但是没有缺口,不知道的东西就少了。


这也就是为什么,知道得越多,不知道的就越多。


谢谢!


最后,祝君能拿下满意的offer。


我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车



👍 如果对您有帮助,您的点赞是我前进的润滑剂。



以往推荐


小小导出,我大前端足矣!


靓仔,说一下keep-alive缓存组件后怎么更新及原理?


面试官问我watch和computed的区别以及选择?


面试官问我new Vue阶段做了什么?


前端仔,快把dist部署到Nginx上


多图详解,一次性啃懂原型链(上万字)


Vue-Cli3搭建组件库


Vue实现动态路由(和面试官吹项目亮点)


项目中你不知道的Axios骚操作(手写核心原理、兼容性)


VuePress搭建项目组件文档


原文链接


juejin.cn/post/744421…


作者:Dignity_呱
来源:juejin.cn/post/7444215159289102347

0 个评论

要回复文章请先登录注册