当前位置: 首页 > 编程日记 > 正文

7 种 Javascript 常用设计模式学习笔记

7 种 Javascript 常用设计模式学习笔记

由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用。

有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式

本文的脉络:

  • 设计与模式
  • 5 大设计原则
  • 7 种常见的设计模式

    • 一句话解释含义
    • 列举生活中的场景 、 业务代码场景
    • js 代码演示

设计与模式

之前一直以为「设计模式」是一个完整的名词

其实「设计」和「模式」是要分开来说的

  • 「设计」:5 个常见的设计原则
  • 「模式」:代码中常见的"套路",被程序员总结成了相对固定的写法,称之为「模式」

也就是说学习"设计模式",首先肯定要学习和理解 5 个设计原则。

因为所有的模式,都是肯定是符合 5 大设计原则中的几个,至少不违背设计原则的。

所以我们在学习模式时,不能上来就把 23 种模式的 js 实现看一遍,这样是很难理解的。

我觉得正确的学习顺序是:

  • 先学习 5 大设计原则
  • 再学习 23 种模式

而「模式」的学习顺序是:

  • 1.尝试用一句话概括这个模式。 (不理解也没关系,至少对定义有一个大致印象)
  • 2.联想在生活中的例子。(模式往往反映一种思想)
  • 3.更重要的是理解「模式」的代码中的应用(有的模式思想在生活中可能没有合适的例子)
  • 4.最后才是用 js 代码去演示和实现「模式」

「模式」的学习步骤中,我觉得理解在代码中应用场景是最重要的

这也是为什么所谓 23 种「模式」,这个文章只是大致讲了 7 种。

因为我觉得没有很多场景的模式,学习了意义也不大。

5 大设计原则

5 大设计原则,可能书和书的叫法不太一样。

但是我觉得最重要的是,理解意思即可,毕竟实际上并不是所有「模式」都满足 5 大设计原则

往往只是满足 1、2 条,然后不违反其他原则,这样。

  • 1.单一职责原则
  • 2.开放-封闭原则
  • 3.面向接口编程
  • 4.李氏置换原则
  • 5.接口独立原则

个人理解 前 3 点 比较常见,后面的 2 个原则我也不太理解,或者可能前端场景下比较少见。

单一职责原则

一个函数只做一件事,如果功能过于复杂,最好让一段代码只负责一部分逻辑。

开放-封闭原则

对拓展开放,对修改封闭。

  • 比如常见的 Vue 和 jQuery 都提供插件拓展机制,你如果希望增加功能,可以拓展某一个技术栈的生态。而不是直接修改源码
面向接口编程

调用者时,只按照接口,不必清楚接口内部的类如何实现。

  • 比如前后分离开发时,前端只需要关注接口文档,不必了解具体实现
  • 比如设计 UI 组件时,我只需要考虑用户需要如何调用,不用让他操心,我的内部实现。

7 种常见的设计模式

我们要说的 7 种常见设计模式如下:

  • 工厂模式
  • 单例模式
  • 适配器模式
  • 装饰器模式
  • 代理模式
  • 观察者模式
  • 迭代器模式

每 1 个模式的学习顺序遵循以下 3 点

    1. 一句话概括这个模式的思想
    1. 生活场景的应用 和 业务场景的应用(重点理解业务场景)
    1. js 演示该模式

工厂模式

一句话描述: 工厂模式将 new 操作符封装,拓展一个 create 的接口开发给调用者

业务场景

  • jQuery 的$()
window.$ = function(selector) {return new jQuery(selector);
};

比如 jQuery 把 $ 暴露给开发者,类似于 create 方法。

有了 $ 一般我们也不会使用 new jQuery() 来创建 jquery 对象

这样做的好处:

  • $作为 create 写起来更加简单
  • 做了一层拓展,这样暴露给用户的是$,内部源码可以做各种修改,甚至不叫jQuery叫xQuery也可以,只要最终还是暴露$,对用户来说就是一样的。

符合开放-封闭原则

js 演示
class Product {constructor(options) {this.name = options.name;this.time = options.time;this.init();}init() {console.log(`产品名:${this.name} 保质期:${this.time}`);}
}
class Factory {create(options) {return new Product(options);}
}let factory = new Factory();
let product1 = factory.create({ name: "面包", time: "1个月" });

单例模式

一句话描述:一个类只有一个实例

业务场景

  • 场景 1: 比如 Vue 的插件机制。Vue.use()多次,也只存在第一个插件实例。
  • 场景 2: 比如 Vuex 的 Store 在实例化时,就算你实例化多个,也只存在一个 Store,这样才能保证共享数据嘛。
  • 场景 3: 创建一个购物车组件。因为购物车往往整个项目只需要 1 个

比如 Vue.use 时,我们知道 Vue 源码中会去做判断调用插件的 install 方法

但是只要 Vue.use 就直接调用的。

Vue 会把 Vue.use 的东西 push 到一个数组,每次执行 use 方法都会先检查数组里是否已经有这个插件了,没有就 push,有的话就不再执行后面的逻辑了。

这样保证项目,这个插件的 install 只初始化 1 次。

我觉得这就是单例模式思想的一种应用。来避免多次初始化可能造成的问题。

js 实现演示

实现思路

  • 给 SingleObject 添加一个静态方法 getInstance
  • 将来实例化时,不是通过 new,而是 SingleObject.getInstance()
  • getInstance 内部的实现就是,第一次调用是用变量缓存实例。之后调用时判断该变量是否有值,没有值就可以 new,有值就把这个变量 return
function SingleObject() {this.name = "单例";
}SingleObject.getInstance = function() {if (!this.instance) {this.instance = new SingleObject();} return this.instance;};var obj1 = SingleObject.getInstance();
var obj2 = SingleObject.getInstance();console.log(obj1 === obj2);

适配器模式

一句话描述: 接口不兼容时,对旧接口做一层包装,来适配新需求。

生活场景

插头的电压,不同国家是存在差异的嘛,经常有电源适配器来做一层包装,从而适配我们的电压。

业务场景
  • Vue 的 computed 提供给函数和对象 2 种写法。Vue 内部需要做一层适配
  • Node.js 中间层

比如 Vue 源码内部遍历 computed 对象后,需要把用户传递的函数作为该计算属性的 getter 的返回值嘛

但是用户有可能会传递 fn,也可能传递一个带 get、set 方法的对象

那么 Vue 面对用户提供的不同接口,它就需要做一层适配。

无论函数直接提供函数,还是提供的对象,Vue 都转化为一个函数

Vue 类似这种处理非常多,因为 Vue 提供给用户很宽松的写法嘛。比如你可以用 template 也可以用 render,但是最终 template 一定会被适配成 render 来调用

另一个例子,我觉得现在流行的 Node.js 中间层,也算是适配器模式的应用。

后端提供一些基础数据,但是移动端和 PC 端要求的数据格式是不同,且经常变化的。

Node.js 中间层的作用,可以让后端的基础数据不做变化,只是对数据在 Node 中再包装一次,来适配具体的业务场景

js 实现演示

重点理解这个适配器,在业务的应用。

感觉单纯用 js 代码演示可能比较呆板

// 新增加的适配器
class Adaptee {constructor() {this.name = "我是适配器";}parse() {}
}// 原来的旧代码
class OldApi{constructor(){this.name = '我是旧的接口'this.adaptee = new Adaptee()this.adaptee.parse()}
}var oldApi = new OldApi()

这样我们拓展了一个新的叫做适配器的类,利用它来处理旧数据,而不是直接去具体修改旧数据

对拓展开发,对修改封闭,典型的符合开发-封闭的设计原则

装饰器模式

一句话描述: 1.为对象装饰一些新功能,2.旧的功能属性全都保留

生活场景

手机壳对于手机,就是一种装饰器

业务场景
  • ES7的装饰器语法

下面介绍了「ES7的 @ 装饰符」的使用,可以直接跳过。

  • 可以修饰类

例如

function decorator(target){target.type = '人类'
}@decorator
class Animal{}console.log(Animal.type)

不传递参数时 @ 就相当于 Animal = decorator(Animal) || Animal

也可以传递参数

function setType(type){return function(target){target.type = type}
}@setType('人类')
class Animal{}console.log(Animal.type)

传递参数时 @ 就相当于 Animal = setType(type)(Animal) || Animal

  • 也可以修饰类中的方法
class Person {@readonlyname() { return `${this.first} ${this.last}` }
}function readonly(target, name, descriptor){descriptor.writable = false;return descriptor;
}

readonly的3个参数含义如下

  • target: Person.prototype
  • name : key
  • descriptor : 描述器
js演示

实现东西

就是我有一个Circle类,用circle.draw()可以画一个圆

经过装饰后,可以画一个带红色边框的圆

class Circle {draw() {console.log("draw");}
}class Decorator{constructor(circle) {this.circle = circle}setRedBorder() {console.log('border装饰为红色')}draw() {this.circle.draw()this.setRedBorder()}
}let circle = new Circle()
let decorator = new Decorator(circle)circle.draw()
decorator.draw()

符合单一职责原则,和开放-封闭原则

代理模式

一句话描述: 无法直接访问时,通过代理来访问目标对象

这里区分一下适配器模式、代理模式、装饰器模式

    1. 适配器模式是在原来的基础上,做了一层包装。虽然没有动原来的接口,但最终我们是用包装好的适配后的数据,肯定有修改的。
    1. 代理模式,是通过代理,访问原数据。没有什么包装和修改。
    1. 装饰器模式,是原来的功能和函数都还要用的的基础上,增加一些拓展功能。而适配的话是在包装时做一些改造。
生活场景
  • 翻墙用的VPN
  • 海外代购
  • 各级代理商
业务场景
  • 绑定多个li时的事件代理
  • Vue的data、props被访问时,就做了代理。
  • ES6的proxy的代理

ul下多个li,通过给父元素绑定click事件,实现对多个li的监听。叫做事件代理

当然这里能实现代理,也依靠了事件冒泡。但是不过是利用什么机制做的代理。这种思想可以看做是代理的思想。

Vue的data,在Vue初始化时,this._init() ==> this.initState() ==> this.initData()

在initData中,除了递归循环把vm的data全都包装为响应式对象之外,还调用了proxy()

这个proxy内部实现的,就是当你访问this.msg时,实际上就访问了this.data.msg

毕竟我们不可能真的把msg挂到Vue.prototype上。

这也是代理的思想。但是当然不是冒泡机制实现的,这里是利用引用的传递。

ES6还提供了Proxy,被Proxy包装的对象,也是一种代理。

原对象和包装后的对象,很像明星和经纪人之间的关系

js演示

实现的效果就是proxyData.getName(),代理的是data.getName()


class Data{constructor(){this.name = '元数据'}getName(){console.log(this.name)}
}class ProxyData{constructor(data){this.data = data}getName(){this.data.getName()}
}let data = new Data()
let proxyData = new ProxyData(data)data.getName()
proxyData.getName()

代理模式符合开放-封闭原则

观察者模式

一句话描述:把watcher收集到一个队列,等到发布时再依次通知watcher,来实现异步的一种模式。

生活场景

观察者模式被广泛的应用在日常生活和开发实践中

  • 斗鱼主播是发布者,观众但是订阅者。
  • 猎头是发布者,候选人是订阅者
  • 赛跑时,裁判开枪来发布,所有的运动员就是订阅者。

这里订阅者也可以理解为观察者。

业务场景
  • 1.Vue的收集依赖、派发更新
  • 2.浏览器事件机制
  • 3.Promise.then的异步事件
  • 4.Vue的生命周期钩子
  • 5.Node.js的eventEmitter
  • 场景1:Vue的收集依赖、派发更新

「订阅」: Vue的响应式数据,在页面渲染时,触发getter收集watcher依赖。

「发布」:数据变化时,触发setter,Notify所有的watcher调用他们的update方法

  • 场景2:浏览器事件机制

「订阅」: btn绑定了click事件,那个fn就被放到事件队列中
「发布」: 用户点击时,触发click事件。

  • 场景3 : Promise.then

「订阅」: then调用时,把then的成功函数保存在一个变量中

「发布」: 当调用resolve时,状态变化,并且把保存的then的成功函数调用

  • 场景4: Vue的生命周期钩子

「订阅」: 在option上写beforeCreate对应的函数

「发布」: 当组件初始化阶段,到了对应时机,vue帮你调用用户提供的函数

js实现一个eventEmiiter

class EventEmitter {constructor() {this.eventMap = {};}on( type, fn ) {if ( !this.eventMap[type] ) {this.eventMap[type] = []} this.eventMap[type].push(fn)}emit( type, ...params ) {this.eventMap[type].forEach(fn => {fn(...params);});}off( type, fn ) {let list = this.eventMap[type];let atIndex = list.indexOf(fn);if (atIndex !== -1) {list.splice(atIndex, 1);}}
}

迭代器模式

一句话描述: 1.按顺序访问集合, 2.调用者不用关系内部的数据结构

ES6之后,我们知道除了数组之外的有序集合挺多的

  • Array
  • Map
  • Set
  • nodelist
  • arguments

那数组的迭代,或者遍历可以用10几种API

那么如果要遍历Map/Set或者nodelist呢。如果给每一种数据结构都搞一套API就会很麻烦

包括JQ都有each遍历。

会导致api泛滥的,所以我们把遍历这种行为,抽象成一种模式。

场景
  • ES6的iterator

    • 为什么需要iterator
    • iterator是什么

了解一下iterator的内容,如果已经了解可以直接跳过

为什么需要iterator

因为js需要遍历的数据结构越来越多,如果给每一个数据结构都搞一套API会很麻烦。

所以,需要有一个统一的接口,可以遍历各种数据结构。

iterator是什么

就是比如Array.prototype就有一个[Symbol.iterator]属性

这个属性对应的是一个函数,函数调用后,得到一个迭代器

我们用这个迭代器就可以遍历各种符合iterator标准的数据结构啦。

怎么遍历呢? 迭代器有next,用while去迭代

比如

function each(data) {let iterator = data[Symbol.iterator]();let item = { done: false };while (item.done === false) {item = iterator.next();if ( item.done ) return itemconsole.log(item)}
}let arr = [1, 2, 3, 4];
let nodeList = document.querySelectorAll("p");
let m = new Map();
m.set("a", 100);
m.set("b", 100);each(arr)
each(nodeList)
each(m)

那么,难道每一个开发者,都需要自己封装一个each方法吗?

ES6提供的for...of就是类似于each,用来遍历符合iterator接口的数据结构

for ( let v of nodeList ) {console.log(v)
}
迭代器模式的js实现

这个要关注实现思路。

这里只是把数组设计成迭代器,有一个next方法和hasNext方法

需要有2个类

  • 1.Wrapper类,提供一个getIterator方法,可以生成一个迭代器
  • 2.Iterator类,迭代器。有next和hasNext方法
class Iterator{constructor(wrapper) {this.list = wrapper.listthis.index = 0}next() {if ( this.hasNext() ) {return this.list[this.index++]} else {return null            }}hasNext() {return this.index < this.list.length}
}class Wrapper {constructor(list) {this.list = list}getIterator(iterator) {return new Iterator(this)}
}var arr = [1, 2, 3]
var iterator = new Wrapper( arr ).getIterator()
while ( iterator.hasNext() ) {console.log(iterator.next())
}

相关文章:

从难免的线上bug说起代码的思考

经常是某司线上又出bug了&#xff0c;然后是给公司造成多少损失&#xff0c;追根究底总是可以找到一些原因&#xff0c;诸如&#xff1a;写代码逻辑考虑不全面&#xff0c;或者代码有硬伤&#xff0c;也有测试不充分&#xff0c;甚至不测试都有&#xff0c;也有是运维的问题等等…

PHP-Fpm应用池配置

//php.net php-fpm配置简介http://php.net/manual/zh/install.fpm.configuration.php//Global Options//pool defined[www]user nobodygroup nobodylisten 127.0.0.1:9000pm dynamicpm.max_children 5pm.start_servers 2pm.min_spare_servers 1pm.max_spare_servers 3s…

【Python】百度首页GIF动画的爬虫

今天百度首页的GIF动画很可爱&#xff0c;就想着用才学的爬虫爬取一下&#xff0c;虽然直接点击“图片另存为”就可以了 import requestsimport urllibclass Gif():def __init__(self):self.url "https://www.baidu.com/"self.headers {"User-Agent": …

CSS题目系列(3)- 实现文字切割效果

描述 有一天逛 Codepen 的时候&#xff0c;看到这么一个效果&#xff1a;将文字上下切开两半。 点进去看了一下代码&#xff0c;发现原理很简单&#xff0c;大概就是通过伪类使用attr()函数获取内容&#xff0c;然后进行定位。 你可以点下方链接查看效果&#xff1a; gd4ark.gi…

Java开发字符串JSON处理

需求很简单就是数据库存json。 数据库字段 varchar 入参request 定义 List<String> 如果不定义这个 而是定义String那么需要加"/转义比较难看 这样就只要入参传这个就行了&#xff1a; "xxxIds": ["33","44"], 数据库也是…

1.JSONObject与JSONArray的使用

参考文献&#xff1a; http://blog.csdn.net/huangwuyi/article/details/5412500 1.JAR包简介 要使程序可以运行必须引入JSON-lib包&#xff0c;JSON-lib包同时依赖于以下的JAR包&#xff1a; commons-lang.jarcommons-beanutils.jarcommons-collections.jarcommons-logging.ja…

【Python】Scrapy爬虫实战(豆瓣电影 Top 250)

今天一天都在弄Scrapy&#xff0c;虽然爬虫起来真的很快&#xff0c;很有效率&#xff0c;但是......捣鼓了一天 豆瓣电影 Top 250&#xff1a;https://movie.douban.com/top250 安装好的scrapy 在你想要的文件夹的目录下输入命令&#xff1a; scrapy startproject douban_m…

vmrun 批量创建vmware虚拟机

1 准备模板机 具体步骤如下&#xff1a; 1. 下载镜像安装系统 https://mirrors.aliyun.com/centos/7.5.1804/isos/x86_64/2. 安装完成配置好IP &#xff0c;关闭SELINUX ,关闭firewalld ,修改网卡名 3. 预设置好修改其他机器IP脚本 1.1 安装系统 略 1.2 模板机的设置 修改网卡名…

暗时间:开发效率为何如此低下

产品 开发 测试 三者都理解不一致。 产品怎么样表达出自己的诉求&#xff0c;是否写清文档就够了。 开发觉得自己沟通了&#xff0c;但是为什么测试一提测又许多问题。 测试的case看似都一起评审了。 而这样的结果必然是重新修修补补&#xff0c;怎么样事前把问题全部解…

【Python】Scrapy爬虫实战(传智播客老师简介)

在文件夹里创建一个爬虫项目 scrapy startproject ITcast 在spiders目录下&#xff1a; scrapy genspider itcast ------------------------------------------------------------------------------------------------------------------------------------------------------…

坑系列 --- 高可用架构的银弹

呵呵&#xff0c;题图是一队困在坑中的鸭子&#xff1a;&#xff09;作为一个搬砖的&#xff0c;我经常被困着。今天高考&#xff0c;想起15年前的今天&#xff08;哦&#xff0c;那时候是七月高考&#xff09;&#xff0c;恩&#xff0c;考完了&#xff0c;还不错&#xff0c;…

【TeeChart Pro ActiveX教程】(八):ADO数据库访问(上)

2019独角兽企业重金招聘Python工程师标准>>> 下载TeeChart Pro ActiveX最新版本 介绍 将TeeChart控件连接到ADO.NET数据库可以在设计时使用TeeChart编辑器完成&#xff0c;并在运行时使用几行代码完成。 任何Series都可以使用TeeChart Editor连接到ADO.NET表或查询。…

代码规范碎碎念

代码规范碎碎念 list条件多于2不要写命名上 controller (model-DTO) service (model) repository层 语义化构造 (entity->model) String转map 语义化数据结构 String转model 从数据库层增强语义 组装模型 DAO ( entity) mapper VO(admin) DTO(client) -------…

RRDTool原理简介

1.概述 RRDtool 代表 “Round Robin Database tool” &#xff0c;作者同时也是 MRTG 软件的发明人。官方站点位于http://oss.oetiker.ch/rrdtool/ 。 所谓的“Round Robin” 其实是一种存储数据的方式&#xff0c;使用固定大小的空间来存储数据&#xff0c;并有一个指针指向最…

【Python】Scrapy爬虫实战(腾讯社会招聘职位检索)

爬虫网页&#xff1a;https://hr.tencent.com/position.php 应用Scrapy框架&#xff0c;具体步骤就不详细说明&#xff0c;前面几篇Scrapy有一定的介绍 因为要涉及到翻页&#xff0c;下面的代码使用拼接的方式获取url&#xff0c;应用在一些没办法提取下一页链接的情况下 直…

一对一直播app源码功能操详解方案分享

一&#xff1a;登录页面&#xff1a;1.快捷登录&#xff1a;可以利用第三方账号进行快捷登录2.手机登录&#xff1a;可以让用户通过输入手机号码和密码进行登录.3.注册&#xff1a;可以使用手机号获取验证码注册账号二&#xff1a;打开一对一直播APP首页打开APP&#xff0c;会显…

从一个需求看问题的无限复杂化和简单化

一个需求 如果你一开始的出发点就错了&#xff0c;那么后续的设计只会非常复杂&#xff0c;而且还会有漏洞&#xff0c;也很难发现&#xff0c;发现了也很难解决。 先看数据结构&#xff1a; A表 主键id 其他各种字段不重要 &#xff0c;重要的就一个字段sort字段 aid1 …

使用自定义材质球,实现NGUI屏幕溶解和灰显

UITexture实现的溶解&#xff1a; 重设UITeture的材质球实现上述效果&#xff0c;把当前屏幕渲染的Texture2D丢给UITexture&#xff0c;即可实现UI屏幕特效&#xff0c;背景模糊等都可以。 难点主要是实时刷新问题 解决的比较粗暴&#xff0c;每次Update重设材质球&#xff0c;…

【Python】Tkinter 体验

import tkinter as tk root tk.Tk() root.title("work hard") #添加一个Label组件&#xff0c;Label组件是GUI程序中最常用的组件之一 #Label组件可以显示文本&#xff0c;图标或图片 #在这里我们让它显示指定文本 theLabel tk.Label(root,text"努力努力再努力…

debian手动安装java两种方法

2019独角兽企业重金招聘Python工程师标准>>> 方法一&#xff1a;下载后修改~/.bashrc文件 方法二&#xff1a;使用update-alternatives进行命令安装 相关配置记录 法一&#xff1a; 官网下载压缩包&#xff0c;解压&#xff0c;然后复制到/usr/lib/jvm目录下&#x…

【Python】Label组件 Button组件 Checkbutton组件

Label组件 Label组件是用于在界面上输出描述的标签。 #导入tkinter模块所有内容 from tkinter import *#创建一个主窗口&#xff0c;可以容纳整个GUI程序 root Tk() root.title("hhh")textLabel Label(root,text"努力努力再努力&#xff01;\n努力努力再努力…

大厂线上案例复盘--代码漏洞

万无一失却实际上是形同虚设的代码逻辑漏洞 这是一则发生在某大厂的真实案例&#xff0c;出于脱敏名字这且不说。 这个系统因为第一次上线&#xff0c;流量非常的大。 所以需要灰度上线&#xff0c;所谓灰度方案很短&#xff0c;比如按照地理位置&#xff0c;先选择某个访问量…

使用Vue+Spring Boot实现Excel上传

写在最前 在上期教程中我们介绍了读写Excel与使用Selenium的入门方法&#xff0c;本期将介绍通过VueSpring Boot实现在WebApp中上传Excel导入测试脚本的功能。使用前后端分离的技术是因为这样便于后续功能的迭代&#xff0c;在本文中我们只涉及一个简单的前端界面及一个简单的后…

国信优易首席科学家周涛:大数据的商业应用

国信优易首席科学家周涛认为&#xff0c;大数据已经成为一个非常热的概念&#xff0c;但是当一个概念热到一定程度就会存在风险&#xff0c;甚至风险比机会还多。在大数据沙龙中&#xff0c;他和数据从业者们分享了自己所经历的大数据应用。 以下为周涛发言&#xff1a; 大数据…

【Python】Radiobutton组件 LabelFrame组件 Entry组件

Radiobutton组件 Radiobutton组件跟Checkbutton组件的用法基本一致&#xff0c;唯一不同的是Radiobutton实现的是“单选”的效果。 要实现这种互斥的效果&#xff0c;同组内的所有Radiobutton只能共享一个variable选项&#xff0c;并且需要设置不同的value选项值。 注释掉的也…

索引与联合索引使用注意

索引和联合索引看似很简单但是往往不一定用的对。 假设数据库2个字段a,b都是查询条件 第一个问题&#xff1a;是建立2个索引还是一个联合索引&#xff1f; 第二个问题&#xff1a;如果建立联合索引那么字段的顺序有什么讲究&#xff1f; 原则&#xff1a; 1.如果需要多个字…

用canvas实现一个vue弹幕组件

看B站时&#xff0c;对弹幕的实现产生了兴趣&#xff0c;一开始想到用css3动画去实现&#xff0c;后来感觉这样性能不是很好&#xff0c;查了下资料&#xff0c;发现可以用canvas实现&#xff0c;于是就摸索着写了一个简单的弹幕。弹幕功能支持动态添加弹幕弹幕不重叠自定义弹幕…

系统架构的过程 浮现式设计

系统架构如果设计之初就设计错了&#xff0c;那么必然是南辕北辙。 很多人做系统设计总是东一下&#xff0c;西一下&#xff0c;杂乱无章&#xff0c;想到那是那&#xff0c;然后系统的边界很大&#xff0c;总会有疏漏。 那么系统架构应该怎么设计呢&#xff1f; 首先来说分…

【Python】Listbox组件 Scrollbar组件 Scale组件

Listbox组件 在选项特别多的时候&#xff0c;Listbox是以列表的形式显示出来&#xff0c;并支持滚动条操作&#xff0c;所以在对于需要提供大量选项的情况下会更适用。 from tkinter import * root Tk() theLB Listbox(root,setgridTrue) theLB.pack() for item in ["…

Hive 按某列的部分排序 以及 删列操作

Hive 按某列的部分排序 以及 删列操作 脑袋果然还是智商不足。 涉及到的小需求&#xff1a; 某个表test 有一列 tc&#xff1a; a字符串b字符串c字符串 拼接组成把test表&#xff0c;按b字符串排序 输出遇到的问题&#xff1a; select 里面必须包含 order by 的列按b字符串排序…