浅谈目前我开发的前端项目用到的设计模式

浅谈目前我开发的前端项目用到的设计模式

前言

设计模式很多,看到一个需求,项目,我们去开发的时候,肯定是做一个整体的设计进行开发,而在这次我项目中,我也做了一个整体的设计,为什么要设计,一个是我们要跟安卓进行交互,一个是参与的人员也是比较多,三四个前端的参与进来,如果不做整体的设计的规划,每个人都有自己的命名习惯,而且一些重复的的业务代码,进行了重复的造轮子使用,所以,就会用到了一些设计模式,规避这些重复的问题。其次也是为了跟安卓形成一个规范,然后按文档进行开发,它那边定义好方法,我这边按照规范进行使用,我这边定义好函数 ,它那边按照规范进行调用,避免很多的一个调试过程。本文将分享我在实际开发中使用的一些设计模式,并结合代码示例进行说明。

为什么要用设计模式?

设计模式是软件开发中被反复使用的、经过验证的、可复用的解决方案。它们通常用于解决常见的设计问题和代码结构问题,帮助开发人员以更高效、更可维护的方式编写代码。设计模式的主要作用包括:

  1. 提高代码可读性和可维护性

    • 通过使用设计模式,代码结构变得更加清晰和规范,其他开发人员可以更容易理解和维护代码。
  2. 促进代码重用

    • 提供了标准化的解决方案,这些解决方案可以在不同项目中重复使用,从而减少重复劳动。
  3. 提高开发效率

    • 设计模式提供了一套现成的解决方案,开发人员可以直接应用这些模式,而无需从头开始设计,从而加快开发速度。
  4. 改善代码的灵活性和可扩展性

    • 通过使用设计模式,代码可以更容易地进行修改和扩展,适应不断变化的需求。
  5. 提供通用的设计词汇

    • 设计模式为开发人员提供了一套通用的词汇,使他们可以更有效地交流和讨论设计问题和解决方案。

说到设计模式,大家想到的就是六大原则,23种模式。这么多模式,并非都要记住,但作为前端开发,对于前端出现率高的设计模式还是有必要了解并掌握的

那么,我们先了解六大原则

六大原则:

  • 依赖倒置原则(Dependence Inversion Principle):高层(业务层)不应该直接调用底层(基础层)模块
  • 开闭原则(Open Close Principle):单模块对拓展开放、对修改关闭
  • 单一原则(Single Responsibility Principle):单模块负责的职责必须是单一的
  • 迪米特法则(Law of Demeter):对外暴露接口应该简单
  • 接口隔离原则(Interface Segregation Principle):单个接口(类)都应该按业务隔离开
  • 里氏替换原则(Liskov Substitution Principle):子类可以替换父类

六大原则也可以用六个字替换:高内聚低耦合。

  • 层不直接依赖底层:依赖倒置原则
  • 部修改关闭,外部开放扩展:开闭原则
  • 合单一功能:单一原则
  • 知识接口,对外接口简单:迪米特法则
  • 合多个接口,不如隔离拆分:接口隔离原则
  • 并复用,子类可以替换父类:里氏替换原则

我们采用模式编写时,要尽可能遵守这六大原则

23 种设计模式分为“创建型”、“行为型”和“结构型”

  1. 创建型模式

    • 这些模式主要关注对象的创建过程,优化对象创建的灵活性和重用性。
    • 例子:单例模式(Singleton)、工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)、建造者模式(Builder)、原型模式(Prototype)。
  2. 结构型模式

    • 这些模式主要关注对象和类的组合,优化代码的结构和组织。
    • 例子:适配器模式(Adapter)、装饰器模式(Decorator)、代理模式(Proxy)、外观模式(Facade)、桥接模式(Bridge)、组合模式(Composite)、享元模式(Flyweight)。
  3. 行为型模式

    • 这些模式主要关注对象和类之间的交互和职责分配,优化算法和业务逻辑的实现。

    • 例子:观察者模式(Observer)、策略模式(Strategy)、命令模式(Command)、责任链模式(Chain of Responsibility)、状态模式(State)、模板方法模式(Template Method)、迭代器模式(Iterator)、访问者模式(Visitor)、中介者模式(Mediator)、备忘录模式(Memento)、解释器模式(Interpreter)。

在这里插入图片描述

我项目中用到的设计模式

太多了,根本记不住,我也记不住,就说一下我在项目用用到了什么设计模式,我也是看一些优秀的代码,结合我的项目需求,进行使用。

代码片段1

一段代码,用到了四种设计模式,请看,解决什么问题呢,实现了一个桥接模块,使得前端可以方便地与原生 Android 代码进行异步通信,并且支持事件的订阅和发布。

interface Callback {resolve: (value: unknown) => void;reject: (reason?: any) => void;
}type EventListener = (...args: any[]) => void;declare global {interface Window {AndroidHome?: {showString: (message: string) => void;showToast:(message: string) => void;showUserLoginCamera:()=> void;getBoardInfo:()=> Promise<unknown> ;showFaceCheckDialog:()=>void;};onNativeCallback?: (callbackId: string, result: any, error?: any) => void;onNativeEvent?: (eventName: string, ...args: any[]) => void;show?: (a: any, b: any) => void;}
}const JSBridge = (function () {const callbackMap: Map<string, Callback> = new Map();const eventListeners: Map<string, Set<EventListener>> = new Map();let callbackCounter = 0;function callNative(method: string, params: any): Promise<unknown> {return new Promise((resolve, reject) => {const callbackId = `cb_${callbackCounter++}`;callbackMap.set(callbackId, { resolve, reject });const isAndroid = Boolean(window.AndroidHome);const message = { method, params, callbackId };try {if (isAndroid) {window.AndroidHome?.showString(JSON.stringify(message));} else {console.warn("Unsupported environment");return reject(new Error("Unsupported environment"));}} catch (error) {reject(error);}});}function onNativeCallback(callbackId: string, result: any, error?: any): void {const callback = callbackMap.get(callbackId);if (callback) {error ? callback.reject(error) : callback.resolve(result);callbackMap.delete(callbackId);}}function isJsonString(str: string) {try {JSON.parse(str);} catch (e) {return false;}return true;}function ensureJsonObject(input: string | null) {if (typeof input === 'string') {if (isJsonString(input)) {return JSON.parse(input);} else {try {return JSON.parse(JSON.stringify(input));} catch (e) {console.error("Conversion to JSON failed:", e);return null; // 或者根据需要返回其他值}}} else if (typeof input === 'object' && input !== null) {return input;} else {console.error("Input is neither a valid JSON string nor an object");return null; // 或者根据需要返回其他值}}function onNativeEvent(eventName: string, ...args: any[]): void {console.log(eventName, ...args);const firstArg = args[0]; //取第一个参数,进行转化const listeners = eventListeners.get(eventName);if (listeners) {listeners.forEach((listener) => listener(ensureJsonObject(firstArg)));}}function addEventListener(eventName: string, listener: EventListener){if (!eventListeners.has(eventName)) {eventListeners.set(eventName, new Set());}eventListeners.get(eventName)?.add(listener);return listener}function removeEventListener(eventName: string, listener: EventListener): void {eventListeners.get(eventName)?.delete(listener);}function removeAllEventListeners(eventName: string): void {eventListeners.delete(eventName);}window.onNativeCallback = onNativeCallback;window.onNativeEvent = onNativeEvent;return {callNative,addEventListener,removeEventListener,removeAllEventListeners,};
})();export default JSBridge;

页面使用以react为例子

   const androidData = (data: InteractionItem)=>{handlerData() //数据进行更新}//监听以及移出监听useEffect(()=>{JSBridge.addEventListener('campusActivityEvent',androidData);return(()=>{JSBridge.removeEventListener('campusActivityEvent',androidData)})},[]) //前端调用测试,window.onNativeEvent('campusActivityEvent', { method:'more', params:{test:'参数',web:''},timeStamp:''});

安卓调用

 webView.evaluateJavascript("window.onNativeEvent('" + eventName + "', " + eventData + ");", null);

这样前端就可以进行,注册监听对应的事件,监听安卓调用这个函数,传过来的事件,参数。从而达到通信效果,

1.模块模式:

**描述:**通过立即调用的函数表达式(IIFE)创建一个封装的模块,使内部变量和函数成为私有的,并返回一个包含公共接口的对象。

在代码中体现:

const JSBridge = (function () {// 私有变量和函数const callbackMap = new Map();const eventListeners = new Map();let callbackCounter = 0;// 公共函数function callNative(method, params) {// ...}function onNativeCallback(callbackId, result, error) {// ...}function onNativeEvent(eventName, ...args) {// ...}return {callNative,addEventListener,removeEventListener,removeAllEventListeners,};
})();
2.单例模式:

**描述:**确保一个类只有一个实例,并提供一个全局访问点来访问该实例。

在代码中体现:

JSBridge 模块本身就是一个单例,通过 IIFE 的方式确保 JSBridge 只会被初始化一次,并且可以被全局访问。

3.观察者模式:

**描述:**定义对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。

在代码中体现:

事件监听器的管理和事件的发布通知机制体现了观察者模式。

const eventListeners = new Map();function onNativeEvent(eventName, ...args) {console.log(eventName, ...args);const firstArg = args[0];const listeners = eventListeners.get(eventName);if (listeners) {listeners.forEach((listener) => listener(ensureJsonObject(firstArg)));}
}function addEventListener(eventName, listener) {if (!eventListeners.has(eventName)) {eventListeners.set(eventName, new Set());}eventListeners.get(eventName)?.add(listener);return listener;
}function removeEventListener(eventName, listener) {eventListeners.get(eventName)?.delete(listener);
}function removeAllEventListeners(eventName) {eventListeners.delete(eventName);
}
4.发布-订阅模式:

描述:这是观察者模式的一种具体实现,发布者和订阅者通过一个消息代理进行通信,而不需要显式地相互引用。

在代码中体现eventListeners 充当了消息代理的角色,管理事件的订阅和发布。

例如,管理事件监听器的添加和删除,以及事件的发布通知。

对于发布-订阅,我这边也实现了一个完整的事件类型

export class EventEmitter {events: any;constructor() {this.events = {};}// 订阅事件subscribe(eventName: string, callback: Function) {if (!this.events[eventName]) {this.events[eventName] = [];}this.events[eventName].push(callback);return () => this.unsubscribe(eventName, callback);}// 发布事件publish(eventName: string, data: any) {const eventCallbacks = this.events[eventName];if (eventCallbacks) {eventCallbacks.forEach((callback: Function) => callback(data));}}// 取消订阅unsubscribe(eventName: string, callback: Function) {if (this.events[eventName]) {this.events[eventName] = this.events[eventName].filter((cb: Function) => cb !== callback);}}// 只订阅一次once(eventName: string, callback: Function) {const onceWrapper = (...args) => {callback(...args);this.unsubscribe(eventName, onceWrapper);};return this.subscribe(eventName, onceWrapper);}
}
// // 使用示例
// const eventEmitter = new EventEmitter();
// // 订阅事件
// const unsubscribe = eventEmitter.subscribe('userLoggedIn', (data: any) => {
//     console.log('User logged in:', data);
// });
// // 发布事件
// eventEmitter.publish('userLoggedIn', { id: 1, name: 'John Doe' });// // 取消订阅
// unsubscribe();// // 只订阅一次
// eventEmitter.once('oneTimeEvent', (data: any) => {
//     console.log('This will only be called once:', data);
// });// eventEmitter.publish('oneTimeEvent', { message: 'Hello' });
// eventEmitter.publish('oneTimeEvent', { message: 'This wont be logged' });

代码片段2

这里定义了CardStaticDataSet 类,用于管理和操作组件的组静态数据。包括默认的静态数据,跟样式数据。当然,这是我得部分业务代码,有缺失的,并不完整,只是拿出来说明。

export class CardStaticDataSet {private staticDataList: Partial<CardStaticData>[] = [];constructor(params: Partial<CardStaticData>[]) {this.staticDataList = params}// 返回指定组件静态数据 returnStaticDataOfTheSpecifiedComponent = <T extends any>(key: SortModuleNameList): Partial<CardStaticDataDetail> & T => {try {if (this.staticDataList.map(o => o.moduleName).includes(key)) {let result = this.staticDataList.find(o => o.moduleName == key)?.cardDatareturn result as any} else {throw Error(key + '不存在对象中:' + JSON.stringify(this.staticDataList))}} catch (error) {return {} as any}};// 返回指定组件的样式信息returnTheStyleInformationOfTheSpecifiedComponent = (key: SortModuleNameList, componentRatio: number = 1): Partial<CuttingComponentStyleInformation> => {try {if (this.staticDataList.map(o => o.moduleName).includes(key)) {let result = this.staticDataList.find(o => o.moduleName == key)?.cardDataresult = Object.assign({}, result)delete result?.cardStaticDatareturn Object.assign({}, cuttingComponentRatio(result.cardSize[componentRatio].size), result.style)} else {throw Error(key + '不存在对象中:' + JSON.stringify(this.staticDataList))}} catch (error) {return {} as any}};// 返回组件类型以及名称returnComponentTypeAndName = (): CardFunctionInformation[] => {try {let functionList = this.staticDataList.map(o => o.cardData?.cardFunction)let functionListKey = [...(new Set(functionList.map(o => o?.functionType)))]let functionResult  = functionListKey.map((o) => {return functionList.find(fO => fO?.functionType == o)}) as CardFunctionInformation[];functionResult = functionResult.filter(o => !excludeDisplayedComponentTypes.includes(o?.functionType) )return functionResult ?? []} catch (error) {return {} as any}};}export const CardStatic = new CardStaticDataSet(returnStaticInformationOfTheCard())
5.工厂模式

描述:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到其子类。

在代码中的体现
CardStaticDataSet 类的构造函数本质上扮演了工厂角色,因为它接收参数并基于这些参数创建 CardStaticDataSet 实例。

constructor(params: Partial<CardStaticData>[]) {this.staticDataList = params;
}
6.策略模式

描述:定义一系列算法,把它们一个个封装起来,并且使它们可以互换。

在代码中的体现
returnComponentTypeAndName 方法中,通过 excludeDisplayedComponentTypes 策略来过滤组件类型。这些策略可以动态地改变,以影响方法的行为。

functionResult = functionResult.filter(o => !excludeDisplayedComponentTypes.includes(o?.functionType));
7.模板方法模式

CardStaticDataSet 类的各个方法(如 returnStaticDataOfTheSpecifiedComponentreturnTheStyleInformationOfTheSpecifiedComponentreturnComponentTypeAndName)中,都可以看到模板方法模式的应用。这些方法定义了一个算法的骨架,并处理一些通用的逻辑,如检查 key 是否存在于 staticDataList 中,并根据 key 查找对应的数据:

// 通用的检查和查找逻辑
if (this.staticDataList.map(o => o.moduleName).includes(key)) {let result = this.staticDataList.find(o => o.moduleName == key)?.cardData;// 进一步处理result
} else {throw new Error(key + '不存在对象中:' + JSON.stringify(this.staticDataList));
}

代码片段3

这是简单的路由

import { lazy } from 'react'; // 导入 React 的 lazy 函数,用于懒加载组件
// 路由配置对象
const routeConfig = {text: "首页", // 路由的显示名称selected: true, // 表示当前路由是否被选中darkMode: true, // 是否启用暗模式moduleId: 1291, // 路由对应的模块IDpath: "/index/index", // 路由的路径// 使用 React 的 lazy 函数实现懒加载组件lazyExoticComponent: lazy(() => import('../views/00_HomePage'))
};
// 示例:如何使用这个路由配置对象
// 假设我们有一个路由数组,包含多个路由配置
const routes = [routeConfig,// 其他路由配置...
];
// 在路由组件(例如 React Router 的 Route 组件)中使用这些路由配置
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Suspense } from 'react'; // 导入 Suspense 组件,用于处理懒加载的组件function App() {return (<Router><Suspense fallback={<div>Loading...</div>}><Switch>{routes.map((route, index) => (<Routekey={index}path={route.path}exactcomponent={route.lazyExoticComponent}/>))}</Switch></Suspense></Router>);
}export default App;
8.路由模式

**描述:**将页面的不同状态映射到不同的URL路径上,使得用户可以直接通过URL来访问页面的不同状态。

在代码体现:

一般来说,路由模式包含以下几个关键部分:

  1. 路由表:定义URL路径与页面组件的映射关系。

  2. 路由器:负责监听URL路径的变化,根据路由表匹配对应的页面组件,并将其渲染到页面上。

  3. 历史记录管理器:负责管理浏览器的历史记录,以便用户可以使用浏览器的前进和后退按钮导航应用程序的不同状态。

代码片段4

// 原始对象 - 图片
class Image {constructor(url) {this.url = url;}// 加载图片load() {console.log(`Image loaded: ${this.url}`);}
}
// 代理对象 - 图片
class ProxyImage {constructor(url) {this.url = url;this.image = null; // 延迟加载}// 加载图片load() {if (!this.image) {this.image = new Image(this.url); // 延迟加载图片console.log(`Placeholder loaded for ${this.url}`);}this.image.load(); // 显示图片}
}
// 代码
const img1 = new ProxyImage('https://example.com/image1.jpg');
const img2 = new ProxyImage('https://example.com/image2.jpg');img1.load();
img1.load(); 
img2.load(); 
9.代理模式

**描述:**它允许在不改变原始对象的情况下,通过引入一个代理对象来控制对原始对象的访问。代理对象充当原始对象的中介,客户端与代理对象交互,代理对象再将请求转发给原始对象。

在代码体现:

  1. ProxyImage 是代理对象,它控制对 Image 对象的访问。
  2. 当调用 img1.load() 时,代理对象 ProxyImage 首次创建并加载实际的图片对象 Image
  3. 当再次调用 img1.load() 时,代理对象 ProxyImage 不会重新创建 Image 对象,而是直接调用已经存在的 Image 对象的 load 方法。

最后

**这是针对我目前项目用到的设计模式,总的一次总结,其它模式,也是在学习,跟试探,如何实际用到项目中去,对项目做一个总体的优化。**这些设计模式帮助我解决了在跨平台(如与 Android 交互)开发过程中遇到的诸多挑战,优化了代码的结构和可扩展性,避免了重复造轮子,同时保证了不同开发人员之间的代码规范一致性。

如:

  • 模块模式:通过立即执行函数表达式(IIFE)封装了JSBridge模块,确保了其内部逻辑的私密性,同时暴露了必要的接口进行外部访问。这样一来,代码的封装性得到了保证,减少了全局污染。

  • 单例模式:JSBridge作为全局单例存在,确保了桥接模块在项目中的唯一性,避免了多次初始化和重复资源的浪费。

  • 观察者模式和发布-订阅模式:通过事件监听和发布通知机制,使前端与 Android 之间的通信更加灵活,前端能够动态响应 Android 端的事件,极大地提高了系统的解耦性和扩展性。

  • 工厂模式:在数据管理中,通过工厂模式的应用动态地生成和管理不同类型的数据,使得扩展新功能时不需要对原有代码进行过多修改。

  • 策略模式:在组件的样式处理中,利用策略模式提供不同的处理方式,使得不同场景下的样式适配可以灵活切换。

  • 模板方法模式:通过统一的模板方法,简化了多个方法的重复逻辑,在保证通用性的同时,也使得开发过程更加高效。

  • 代理模式:通过代理模式,延迟加载图片资源,优化了性能,并且通过代理对象控制了资源的访问,避免了重复加载。

总结

通过这些模式的合理应用,我在提升代码质量的同时,也使得项目架构更加清晰、灵活且易于扩展。设计模式不仅仅是解决问题的工具,更是一个提升团队协作效率、增强代码可维护性和可复用性的有效方法。

未来,在面对更复杂的需求时,我还会不断探索并运用更多的设计模式,以进一步提升系统的质量和开发效率。设计模式的学习和应用是一个持续的过程,通过总结和反思,我可以不断优化代码结构,提高开发效率,从而更好地应对项目中的各类挑战。

参考:

浅谈前端出现率高的设计模式浅谈前端曝光率高的九大设计模式。分别从创建型:构造器模式、共产模式、单例模式;结构型:适配器模 - 掘金

前端设计模式大全(汇总详细版)1. 工厂模式 工厂模式(Factory Pattern):将对象的创建和使用分离,由工厂 - 掘金

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/493245.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

批量DWG文件转dxf(CAD图转dxf)——c#插件实现

此插件可将指定文件夹及子文件夹下的dwg文件批量转为dxf文件。 &#xff08;使用方法&#xff1a;命令行输入 “netload” 加载插件&#xff0c;然后输入“dwg2dxf”运行&#xff0c;选择文件夹即可。&#xff09; 生成dxf在此新建的文件夹路径下&#xff0c;包含子文件夹内的…

Windows安全中心(病毒和威胁防护)的注册

文章目录 Windows安全中心&#xff08;病毒和威胁防护&#xff09;的注册1. 简介2. WSC注册初探3. WSC注册原理分析4. 关于AMPPL5. 参考 Windows安全中心&#xff08;病毒和威胁防护&#xff09;的注册 本文我们来分析一下Windows安全中心&#xff08;Windows Security Center…

linux---多线程

线程的基本概念 定义&#xff1a;在Linux中&#xff0c;线程是进程内部的一个执行单元&#xff0c;是进程的一个实体&#xff0c;它是CPU调度和分派的基本单位。一个进程可以包含多个线程&#xff0c;这些线程共享进程的资源&#xff0c;如代码段、数据段、打开的文件、信号处理…

将4G太阳能无线监控的视频接入电子监控大屏,要考虑哪些方面?

随着科技的飞速发展&#xff0c;4G太阳能无线监控系统以其独特的优势在远程监控领域脱颖而出。这种系统结合了太阳能供电的环保特性和4G无线传输的便捷性&#xff0c;为各种环境尤其是无电或电网不稳定的地区提供了一种高效、可靠的视频监控解决方案。将这些视频流接入大屏显示…

有监督学习 vs 无监督学习:机器学习的两大支柱

有监督学习 vs 无监督学习&#xff1a;机器学习的两大支柱 有监督学习 vs 无监督学习&#xff1a;机器学习的两大支柱一、有无“老师”来指导二、解决的问题类型不同三、模型的输出不同 有监督学习 vs 无监督学习&#xff1a;机器学习的两大支柱 在机器学习的奇妙世界里&#…

SLURM资料

SLURM资料 Quick Start 基本概念 job step&#xff1a; 作业步&#xff0c;单个作业可以有多个作业步partition&#xff1a;分区&#xff0c;作业需要在特定分区中运行&#xff08;理解为定义了队列&#xff0c;每个队列中包含不同节点&#xff09;QOS&#xff1a;服务质量&a…

App自动化之dom结构和元素定位方式(包含滑动列表定位)

DOM结构 先来看几个名词和解释&#xff1a; dom: Document Object Model 文档对象模型 dom应用: 最早应用于html和js的交互。界面的结构化描述&#xff0c; 常见的格式为html、xml。核心元素为节点和属性 xpath: xml路径语言&#xff0c;用于xml 中的节点定位&#xff0c;X…

Vulhub:Redis[漏洞复现]

4-unacc(Redis未授权代码执行) 启动漏洞环境 docker-compose up -d 阅读vulhub给出的漏洞文档 cat README.zh-cn.md # Redis 4.x/5.x 主从复制导致的命令执行 Redis是著名的开源Key-Value数据库&#xff0c;其具备在沙箱中执行Lua脚本的能力。 Redis未授权访问在4.x/5.0.5以…

imx6ull qt多页面控制系统(正点原子imx系列驱动开发)

开题答辩完了也考完了四六级&#xff0c;赶紧来更新一下一个月前留下的坑吧 QAQ首先&#xff0c;因为毕业设计需要用到这些知识所以就从网络上找了一个智能车机系统&#xff0c;借鉴了一下大佬的项目思路&#xff0c;缝缝补补一个月终于完成了这一内容。 在这里先感谢从两位大佬…

前端小白学习之路-Vben探索 vite 配置 - 1/50

目的 为ApiHug 寻找一个前端解决方案前端背景知识缺乏整盘操作&#xff1a;前后全栈80% 中小规模项目提效 30% 全员全栈快速构建高度模块化AI Native... 所以 裸学前端高举高打&#xff0c;直接从复杂项目拆解AI 助手高度依赖后端癖严重&#xff0c;高度模块&#xff0c; 结构化…

Docker:Dockerfile(补充四)

这里写目录标题 1. Dockerfile常见指令1.1 DockerFile例子 2. 一些其他命令 1. Dockerfile常见指令 简单的dockerFile文件 FROM openjdk:17LABEL authorleifengyangCOPY app.jar /app.jarEXPOSE 8080ENTRYPOINT ["java","-jar","/app.jar"]# 使…

谷歌浏览器的扩展市场使用指南

谷歌浏览器的扩展市场为用户提供了丰富多样的功能扩展&#xff0c;可以大幅提升浏览体验。本文将为你详细介绍如何使用谷歌浏览器的扩展市场&#xff0c;包括安装、管理和一些推荐的无障碍工具、图标重置方法和便捷操作技巧。&#xff08;本文由https://chrome.py010.cn/的作者…

04、Vue与Ajax

4.1 发送AJAX异步请求的方式 发送AJAX异步请求的常见方式包括&#xff1a; 4.1.1. 原生方式 使用浏览器内置的JS对象XMLHttpRequest const xhr new XMLHttpRequest() xhr.open() xhr.send() xhr.onreadystatechange function(){} 4.1.2. 原生方式 使用浏览器内置的JS函…

网络安全概论——防火墙原理与设计

一、防火墙概述 防火墙是一种装置&#xff0c;它是由软件/硬件设备组合而成&#xff0c;通常处于企业的内部局域网与 Internet 之间&#xff0c;限制 Internet 用户对内部网络的访问以及管理内部用户访问 Internet 的权限。换言之&#xff0c;一个防火墙在一个被认为是安全和可…

南城云趣:智能云平台,杜绝电动车充电安全隐患

电动自行车作为绿色低碳出行的主要方式之一,受到无数市民的推崇,而电动自行车数量的急剧上涨,也严重增加小区管理的负担。记者调查发现,目前电动自行车缺乏有效的管理,使得带车或电瓶上楼充电、乱停乱放、车辆容易被盗等安全问题日益突出,给社区消防安全和管理带来严峻的挑战。…

Linux 文件系统目录结构及其简要介绍

&#x1f44b; 欢迎来到“Linux学习&#xff1a;Linux 文件系统目录结构”篇&#xff01; 接下来让我们一起来学习一下Linux 文件系统目录结构吧&#xff01;祝你有所收获&#xff01; 文章目录 总结表格Linux 文件系统目录结构及其简要介绍补充小资源 小伙伴们都知道&#xff…

【服务器】MyBatis是如何在java中使用并进行分页的?

MyBatis 是一个支持普通 SQL 查询、存储过程和高级映射的持久层框架。它消除了几乎所有的 JDBC 代码和参数的手动设置以及结果集的检索。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java 的 POJO&#xff08;Plain Old Java Objects&#xff0c;普通老式 …

Elasticsearch-DSL高级查询操作

一、禁用元数据和过滤数据 1、禁用元数据_source GET product/_search {"_source": false, "query": {"match_all": {}} }查询结果不显示元数据 禁用之前: {"took" : 0,"timed_out" : false,"_shards" : {&quo…

使用 UniApp 在微信小程序中实现 SSE 流式响应

概述 服务端发送事件(Server-Sent Events, SSE)是一种允许服务器向客户端推送实时更新的技术。SSE 提供了一种单向的通信通道,服务器可以持续地向客户端发送数据,而不需要客户端频繁发起请求。这对于需要实时更新的应用场景非常有用。 流式传输的特点是将数据逐步传输给客…

【Tomcat】第六站(最后一站啦!):数据的返回

1. 引言 前端资源比如html页面&#xff0c;进行返回。截止到目前我们写的项目架构不支持前端页面&#xff08;静态资源 &#xff09;。 2. 数据的返回 2.1 准备 为了能够写前端页面&#xff0c;新建一个项目。选择Maven项目&#xff0c;下一步&#xff0c;下一步。 加载完…