写出高质量的前端代码之消除代码中的重复
软件开发中,有个很重要的DRY原则,即Dont Repeat Yourself,也就是不要重复自己。
重复的代码会带来以下问题:
- 开发效率低,重复造轮子
- 不同人开发的同一类功能,质量层次不齐
- 修改问题时可能会遗漏,修了这个地方,忘了那个地方,导致一个bug反复修改多次。
重复产生的原因是多方面,有的是工作模式导致的,有的是编码导致的,弄清楚重复的原因,也就很容易找出消除重复的方法。
工作模式导致的重复
团队缺少沟通
同一个团队可能会针对相同或相似的功能进行编码,有时你封装了一个功能,却没有在团队内部大量使用, 究其原因可能是大家根本不知道有这么个功能,或者不知道怎么使用,干脆自己写,也或者虽然有点相似,但是并不完全适用自己。
这类问题核心还是沟通问题,所以要根本解决这类重复,我们必须在工作流程上做一些改进。
1.基础功能开发评审
当你要开发一个基础功能时,需要发起设计评审,这样首先起到了周知的作用,让大家知道有这么个东西, 其次大家会针对功能提出自己的建议,以方便他人后续使用。
2.基础功能宣贯
在开发完某个功能之后,需要及时的进行宣贯,让其他同事知道可以使用了,可以在每周的团队周会上安排一个环节, 进行公共功能的变更宣贯。
3.CodeReview
CodeReview中发现存在重复问题,及时提出修正。
缺少基础设施-公共库
在一个项目中,我们可能建一个公共的文件夹,比如common,用来存放我们的基础组件和库,但是一旦跨项目,这种方式就无效了, 如果没有公共库,就需要进行复制粘贴了,把一段代码从一个项目复制到另一个项目,这样就造成一个问题的修改需要同时修改多个项目, 如果赶上项目时间紧张,再加上可能有懒惰思想,慢慢的多个项目之间的基础功能就不再一致了,后续的维护更加复杂。
解决这类问题可以参考两个方法:
1.使用monorepo
monorepo也就是单一仓库管理多个项目,有些公司将所有代码存储在一个代码库中,由所有人共享, 因此monorepo可以非常大。例如,理论上谷歌拥有有史以来最大的代码库,每天有成百上千次提交,整个代码库超过 80 TB。 其他已知运营大型单一代码库的公司还有微软、Facebook 和 Twitter。
我们可以在monorepo中添加一个公共项目,用来存放我们的基础组件和utils工具库。
2.通过发布公共包
可以建设一个基础组件库,发包到npm或者公司内部的包管理系统。
monorepo和npm发包各有优缺点,根据自身情况进行选择。
知识的重复
在团队中不只是代码存在重复,在知识层面也会存在重复,比如大家重复的进行某个知识的学习,包括技术上的和业务上的, 整体上增加了团队工作的重复,降低了效率。
针对知识的重复我们可以定期举办内部分享。
1.新功能上线演示
针对一些大的功能上线,可以组织内部的上线演示,一方面增加大家对业务的了解,另一方面,减少大家后续重复学习的问题。
2.定期分享
团队内部定期举办业务、技术、工作方法/效率等方面的分享,减少对知识学习的重复工作。
编码层面的重复
没有意识到重复
有时候重复的代码并不是那么明显,可能只是几行代码,由于重复的行数较少,所以就很自然的采用复制粘贴, 没有意识到重复的发生。
比如针对一个应用的状态判断,应用存在多种状态,比如未安装(Uninstalled)、运行中(Running)、已销毁(Destroyed)等, 只有当应用处于未安装或者已销毁状态才允许安装,而这个条件可能在多处都使用。
// 应用列表页在进行安装操作时进行状态的判断
function install(app) {if (['Uninstalled', 'Destroyed'].includes(app.status)) {}
}
<!--应用详情页在进行安装时进行状态的判断-->
<template><button v-if="['Uninstalled', 'Destroyed'].includes(app.status)">安装</button>
</template>
每当安装条件发生变更时,都要四处寻找然后一个一个地方修改,针对这种也应该进行封装,虽然他很小。 我们可以封装一个判断是否能安装的方法,其他地方进行引用。
//抽象一个方法,判断是否能按照
function canInstall(app) {return ['Uninstalled', 'Destroyed'].includes(app.status)
}//在需要进行状态判断的地方引用封装的函数
function install(app) {if (canInstall(app)) {}
}
后续安装条件发生变更,只需要修改canInstall方法即可
//需求发生变化,InstallError状态和没有runtimeId也支持安装
function canInstall(app) {return ['Uninstalled', 'Destroyed', 'InstallError'].includes(app.status)|| !app.runtimeId
}
我们一般对大块的重复代码比较敏感,而对于小块的代码重复,则一般会忽略,但是重复是部分大小的,小段代码很可能在更多的地方使用。
缺少抽象和封装
比如要让你炸毁地球,不应该直接写一个炸毁地球的方法,而是写一个炸毁星球的方法,将地球作为参数传进去。
抽象的东西要比具体的东西复用性更强,因此要想提高复用性,就要对所做的功能进行抽象,而不是面向具体单一的业务需求开发。
示例1:组件的重复
比如在删除k8s资源时需要输入k8s资源的名称进行确认,输入正确后才能进行删除,于是针对这个业务场景封装了一个弹窗确认组件, 在调用显示弹窗方法showDialog时,传入了一个名为k8s资源名称(k8sResourceName)的参数。
<!--删除确认弹窗 DeleteResource.vue 的实现-->
<template><el-dialogtitle="删除确认":visible.sync="dialogVisible"@confirm="deleteResource"><div>请输入<span>{{ k8sResourceName }}</span>确定删除</div><el-input v-model.trim="inputName"/></el-dialog>
</template><script>
export default {data() {return {dialogVisible: false,k8sResourceName: '',inputName: ''};},methods: {showDialog(params) {this.k8sResourceName = params.k8sResourceName;this.dialogVisible = true;this.inputName = '';},deleteResource() {if (this.inputName === this.k8sResourceName) {this.dialogVisible = false;this.$emit('delete');}}}
};
</script>
其实这是一个很常用的功能,可能很多地方都会使用,比如在删除应用页面,也可能用到这个组件进行确认, 那么我们传递一个名为k8sResourceName的参数显然是很不合适的(这里只是简化了这块的实现,实际可能并不是简单改一个名称这么简单)。 显然,并不是写这块代码的同事, 没有能力封装一个通用的组件,而是缺少抽象的思维,导致写出了面向具体单一业务的组件。
试着利用抽象思维,写出更具复用性的代码。
示例2:样式的重复
比如在CSS中,经常会针对某段文字设置字体大小、颜色等,一般情况下,同一个网站的字体大小、颜色是存在共性的, 比如标题的颜色每个页面都是一样的,提示类型的文字颜色也是一样的,如果没有进行抽象,那么我们代码可能是这样的。
.news-title {color: #409EFF;font-size: 16px;
}.app-title {color: #409EFF;font-size: 16px;
}
这样的设置会在样式中存在大量的重复,大致有两个解决方法。
第一种就是对整个网站的css进行分层,比如样式分为全局样式、页面样式和组件样式。我们可以在全局样式中抽取共性的css样式。
比如抽取common.css,定义网站通用标题的样式。
.common-title {color: #409EFF;font-size: 16px;
}
另外一种就是通过抽取一些变量,在页面样式和组件样式中引用这些变量。
比如element中的var.scss
/* Color
-------------------------- */
/// color|1|Brand Color|0
$--color-primary: #409EFF !default;
/// color|1|Background Color|4
$--color-white: #FFFFFF !default;
/// color|1|Background Color|4
$--color-black: #000000 !default;/* Size
-------------------------- */
$--size-base: 14px !default;/* z-index
-------------------------- */
$--index-normal: 1 !default;
$--index-top: 1000 !default;
$--index-popper: 2000 !default;
后续如果想修改网站的配色,只需要修改模板文件即可。
示例3:网络请求的重复
网络请求也是重复容易滋生的地方,比如在一个组件内部请求文章的详情方法getNewsDetail,看看有那几处重复呢?
<script>
import axios from 'axios'export default {methods: {getNewsDetail(id) {axios.get('/api/v1/news/detail/' + id).then(res => {this.detail = res}).catch(e => {if (e.code === '401') {showToast('未登录,请先登录')} else {showToast('接口请求失败')}})}}
}
</script>
上述代码可能存在如下重复问题:
- 接口地址可能存在重复,别的页面也能会使用文章详情接口,如果接口地址发生变更需要修改多处
- 请求方法可能存在重复,别的页面如果也要请求文章详情,也需要写一个getNewsDetail方法
- 错误处理存在重复,多个网络请求可能都要进行相同的错误处理
- 请求依赖具体的第三方库axios,如果有一天要更换网络请求库,则需要进行大量的修改
接下来我们来逐一解决这些重复。
首先是依赖axios的问题,我们尽量不要直接依赖某个第三方插件,解决办法也很简单,就是增加一层封装, 这样把依赖局限在具体某个方法内部,后续要替换只需要更改一处即可,也就是上层业务不依赖底层实现。 不是axios有什么功能,我的网络请求怎么调用,而是我想怎么调用网络请求,怎么利用axios进行实现, 也就是要遵循"依赖倒置原则"。
我们可以实现一个request方法,来完成网络请求,内部调用axios,同时进行通用的错误处理。
// request.js
import axios from 'axios'export default function request(config) {return new Promise((function (resolve, reject) {axios(config).then(res => {if (res.code && res.code === 200) {resolve(res)} else if (res.code === 401) {//其他各种错误处理类似showToast("网络请求失败")window.location.href = '/#/login'} else {reject(res)}}).catch(e => {reject(e)})}))
}['get', 'post', 'put', 'delete'].forEach(method => {return function (url, params, body, options) {return request({method,url,params,body,...options})}
})
针对接口重复问题,我们可以创建一个 api.js文件,来配置各个接口地址。
export default {newDetail: '/api/v1/news/detail/:id'// 其他接口地址
}
针对接口调用重复问题,我们可以封装一个service层,所有页面调用均通过调用service层方法来实现。 service/news.js
import request from './utils/request'
import api from './api'export default {getNewsDetail(id) {return request.get(api.newDetail, {id}).then(res => {return res.data})}
}
修改之前和修改之后对比如下,通过增加中间层service.js、封装网络请求方法request.js以及封装接口地址配置常量api.js, 大大降低网络请求的复用,同时也不再依赖第三方axios,实现了解耦。
职责不单一
单一职责的函数或者组件,就像是一个积木块,而不满足单一职责特性的函数或组件就像是一个功能模块。
单一职责的积木块可以进行各种排列组合,绽放出强大的生命力,而多功能的模块,只适用于特定的业务场景,复用性则大大降低。
比如我们要实现一个下载一段文字的功能,这个功能分为两步,第一步将文字内容转为url,第二步通过创建a标签实现下载。 假如实现的downloadText方法如下:
function downloadText(text, filename) {//根据text内容,创建urllet blob = new Blob([content]);let url = URL.createObjectURL(blob);//创建a标签,通过模拟a标签的点击实现下载let eleLink = document.createElement('a');eleLink.download = filename;eleLink.style.display = 'none';eleLink.href = urldocument.body.appendChild(eleLink);eleLink.click();document.body.removeChild(eleLink);
}
很明显这里不符合单一职责,假如我现在不是根据text文本进行下载,而是给定一个具体src下载,可能还要再写个downloadUrl方法,很明显,二者之间有很多重复。
function downloadText(text, filename) {//根据text内容,创建urllet blob = new Blob([text]);let url = URL.createObjectURL(blob);//创建a标签,通过模拟a标签的点击实现下载let eleLink = document.createElement('a');eleLink.download = filename;eleLink.style.display = 'none';eleLink.href = urldocument.body.appendChild(eleLink);eleLink.click();document.body.removeChild(eleLink);
}function downloadUrl(url, filename) {//创建a标签,通过模拟a标签的点击实现下载let eleLink = document.createElement('a');eleLink.download = filename;eleLink.style.display = 'none';eleLink.href = urldocument.body.appendChild(eleLink);eleLink.click();document.body.removeChild(eleLink);
}
根据单一职责原则对downloadText方法进行拆分,可以拆成两个,一个根据文本生成url,一个根据url进行下载,如果还想保留downloadText, 只需要组合这两个小方法即可,这样我们一下就产出了3个通用方法。
function createUrlByText(text) {let blob = new Blob([text]);return URL.createObjectURL(blob);
}function downloadUrl(url, filename) {let eleLink = document.createElement('a');eleLink.download = filename;eleLink.style.display = 'none';eleLink.href = urldocument.body.appendChild(eleLink);eleLink.click();document.body.removeChild(eleLink);
}//对上面的单一职责功能进行组合
function downloadText(text, filename) {let url = createUrlByText(text)downloadUrl(url, filename)
}
其实功能实现并没有本质的区别,只是简单的进行拆分,使其满足单一职责原则,即大大提高了复用性。
数据之间的重复
数据之间的重复,也是经常出现的一种重复问题,也就是能用1个字段表示的数据,不要用2个或多个字段表示。 通常可以有1个基础的数据字段,其他字段可以通过这个基础字段来计算,而不是维护其他几个额外字段。
比如要实现如下的一个列表,一共有3个字段:列表数据list、已选中数量selectedCount、总数量totalCount。
假如维护这三个字段,每次当列表数据发生变化时(可能是选择状态变化,也可能是增删数据), 都要小心设置selectedCount、totalCount,否则就会出现数据不一致的bug,这其实是一种逻辑上的重复。
我们可以只维护一个基础数据list,selectedCount和totalCount都可以通过对list进行计算而得到, 可能是写一个方法每次刷新组件时重新计算,也可以利用vue中的计算属性。
<script>
export default {data() {return {list: [{name: '苹果',selected: false},{name: '橘子',selected: true},{name: '香蕉',selected: true}]}},computed: {selectedCount() {return this.list.filter(item => item.selected).length},totalCount() {return this.list.length}}
}
</script>
这样我们只需要对list进行处理即可,其他两个字段自动计算出来,避免数据不一致问题。
复用和耦合
复用虽好,但是也不能贪杯,因为复用就意味着耦合,不合理的复用会导致严重的耦合,代码可读性及可维护性变差。
不合理的复用,甚至不如不进行复用,所以在进行复用抽象时,应该慎重。
充斥着各种if-else的复用
有的复用并不是真正的复用,而是将一些功能集中到了一个函数内部,然后再内部进行大量的if-else判断,实际是把逻辑复杂性转移到了函数内部, 由于存在大量的分支判断,复杂度呈现指数式增长。
比如应用的安装、重启、销毁、切换版本等都使用一个函数,表面上都复用了operate函数,但是却在函数内部进行大量的分支判断,各自处理各自的事情, 而且由于各个操作需要的传参可能还不一样,导致虽然共用一个函数,但是传参不一样,内部处理逻辑也没有复用,还增加了耦合, 导致想要搞明白某个操作的处理流程,非常复杂,这样的复用有什么意义呢?
<!--不合理的复用-->
<template><div><el-button @click="operate(row, 'install')">部署</el-button><el-button @click="operate(row, 'restart')">重启</el-button><el-button @click="operate(row, 'destroy')">销毁</el-button><el-button @click="operate(row, 'upgrade-version', row.package_version)">切换版本</el-button></div>
</template>
<script>
export default {methods: {operate(row, opt, package_version) {if (opt === 'install') {//...}if (opt === 'destroy') {//...} else if (['install', 'update-values', 'update-default', 'upgrade-version'].includes(opt)) {//...} else {if (['restart'].includes(opt)) {//...} else {//...}}}}
}
</script>
上面这个,不如针对不同的操作,直接对应一个独立的处理函数,如果多个函数之间有复用,可以抽取出来公共函数,但是每个操作的处理流程是清晰的。 修改之后,每个交互对应的操作是明确的,由于拆分了,传参也简化了,每个操作内部没有了分支判断,逻辑也简化了。
<!--修改之后-->
<template><div><el-button @click="install(row)">部署</el-button><el-button @click="restart(row)">重启</el-button><el-button @click="destroy(row)">销毁</el-button><el-button @click="upgradeVersion(row,row.package_version)">切换版本</el-button></div>
</template>
<script>
export default {methods: {install(row) {//安装处理流程this.commonOperate()},restart(row) {//重启处理流程this.commonOperate()},destroy(row) {//销毁处理流程this.commonOperate()},upgradeVersion(row, version) {//切换版本处理流程this.commonOperate()},commonOperate() {//...}}
}
</script>
尽量不要复用不同业务的处理流程,不同业务的流程后续很可能向着不同的方向发展,强行复用只会增加复杂度和耦合,我们可以抽象各个流程的公共处理方法, 比如多个操作的接口请求是一致的,但是每个操作的参数是不同的,那么可以在不同的业务处理方法中准备不同的参数,然后调用统一的网络请求方法完成接口调用。
还有一种类似的问题,出现在UI的复用上,根据不同的属性,比如应用的操作类型(安装、重启等待),渲染不同的页面,由于充斥大量的分支, 很难搞明白某个操作对应那些UI,修改某个bug时很容易引入新的bug。
<template><div><div v-if="operate === 'install'">***</div><div v-else>***</div><div v-if="['install', 'start'].includes(operate)">***</div></div>
</template>
<script>
export default {props: ['operate']
}
</script>
综上,当遇到有大量分支判断时,就要考虑这个复用是否合理了。
入口文件尽量不要复用
所谓的入口文件就是和用户直接进行交互的文件,比如不同路由对应的页面是一种入口文件,不同按钮对应的handle函数也是一种入口文件, 入口文件耦合着业务逻辑,最好不进行复用。
比如两个路由对应的页面非常相似,如果我们直接复用这个路由文件,那么就会在路由文件的实现中,进行各种if-else判断,来区分环境, 而且很难了解不同路由到底会对页面产生哪些影响。
比如,route1和route2复用User组件,那么User组件内部肯定要进行各种条件判断,来针对route1和route2呈现不同的效果, 那么要问route1和route2的表现在User组件有什么不同,你必须去仔细阅读User的实现才能知晓。
//假设两个页面很相似,复用了路由入口文件
const routes = [{path: 'route1',name: 'route1',component: User,},{path: 'route2',name: 'route2',component: User,},
]
User组件实现
<template><div><!-- 入口文件只能通过路由不同的参数来区分到底是从哪个路由进来的,呈现不同效果 --><div v-if="$route.params.test === '**'">**</div><div v-else>**</div></div>
</template>
<script>
export default {mounted() {// 入口文件只能通过路由不同的参数来区分到底是从哪个路由进来的,呈现不同效果if (this.$route.params.test === '**') {//...} else {//...}}
}
</script>
复用了入口文件后,很难说清楚不同入口有什么区别,如果入口文件不复用,我们可以把公共内容封装成组件,然后在不同的入口文件中进行调用, 调用时传递明确的属性,很清楚整体的逻辑。
比如route1对应User1文件,route2对应User2文件
//假设两个页面很相似,复用了路由入口文件
const routes = [{path: 'route1',name: 'route1',component: User1,},{path: 'route2',name: 'route2',component: User2,},
]
User1组件实现如下,User1组件中调用组件User,同时传递属性过去
<template><User :can-add="true" :can-delete="false" />
</template>
User2组件实现如下,User2组件也调用复用的组件User,同时传递所需属性过去
<template><User :can-add="false" :can-delete="true" />
</template>
User组件实现
<template><div v-if="canAdd">添加</div><div v-if="canDelete">删除</div>
</template>
<script>
export default {props:['canAdd', 'canDelete']
}
</script>
我们通过抽取User组件,实现了主要功能的复用,同时又避免了入口文件的复用,每个入口文件传递什么属性都很容易看到,也很容易理解其中逻辑。
类似的,不同按钮对应的操作函数尽量不要复用,因为不同的按钮就代表不同的业务流程,很难说多个业务的流程始终能保持一致,可能初始时, 两个按钮操作基本一样,但是随着后续需求变化,这个复用的函数就开始增加各种if-else以应对需求变化,导致可读性越来越差,耦合越来越多。
不能因为长得相似就复用
假如某个管理系统的2个列表页非常相似,比如一个是资讯列表页,一个是商品列表页,它们都有一个添加按钮、一个按名称搜索的输入框,一个表格, 而且表格的列都只有名称、创建人、创建时间三列,那么我们应该复用吗?
很显然,这样的情况是不能复用的,随着业务的发展,资讯和商品,一定会向着两个不同的方向发展,不可避免的后续会增加各种逻辑判断, 而且因为这种耦合还会造成各种意外的bug。
我们应该复用其中的组件单元,比如统一的button组件、搜索表单、表格组件,通过组合基础组件,来完成两个页面的开发。
类似的还有表单校验的规则,比如名称和描述,可能初始的校验规则一致,就进行了复用,但是本质上,名称和描述是不同的业务元素, 他们的校验规则并没有完全的相关性,很可能后续往着不同的方向发展。但是如果是多个表单的同一个业务元素的校验,则是可以复用的, 可以预料到如果该元素在这个页面变化了校验,在其他页面也应该会变化。
是否能复用,要看逻辑上有没有相关性,而不是仅仅因为长得像就复用,复用的代价就是耦合。
最后
整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。




有需要的小伙伴,可以点击下方卡片领取,无偿分享
相关文章:
写出高质量的前端代码之消除代码中的重复
软件开发中,有个很重要的DRY原则,即Dont Repeat Yourself,也就是不要重复自己。 重复的代码会带来以下问题: 开发效率低,重复造轮子不同人开发的同一类功能,质量层次不齐修改问题时可能会遗漏,…...
怎么从零开始学黑客,黑客零基础怎么自学
很多朋友对成为黑客很感兴趣,很大原因是因为看到电影中黑客的情节觉的特别的酷,看到他们动动手指就能进入任何系统,还有很多走上黑客之路的朋友仅仅是因为自己的qq被盗了,或者游戏里的装备被别人偷了,想要自己盗回来&a…...
量化择时——资金流择时策略(第1部分—因子测算)
文章目录资金流模型概述资金流模型的有效性逻辑资金流向指标MFI(Money Flow Index)MFI指标测算测算规则测算结论资金流模型概述 通常,资金流是一种反映股票供给信息的指标,宏观上来讲,我们知道一个道理:僧…...
Openwrt中动态IPV6 防火墙的正确设置方法
环境:光猫桥接公网IPV6 问题:动态IPV6地址不知道怎么设置防火墙 解决办法:模糊匹配前缀,特定后缀 背景:将家中光猫桥接后,获得了公网的IPV6地址,可以从外部用IPV6访问家中的设备,但I…...
JS的基本数据类型和引用数据类型
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number&…...
mars3d基础项⽬常⻅报错
1.在⼤家使⽤mars3d基础项⽬的时候经常遇到这个报错,截图如下 回答: 1.原因是因为使⽤了cnpm安装依赖,导致了⼀些依赖问题 2.解决⽅式也很简答,重新使⽤ npm 或 yarn 或 pnpm安装依赖即可 2.本地加载地图时,出现报错回…...
【阿旭机器学习实战】【35】员工离职率预测---决策树与随机森林预测
【阿旭机器学习实战】系列文章主要介绍机器学习的各种算法模型及其实战案例,欢迎点赞,关注共同学习交流。 本文的主要任务是通过决策树与随机森林模型预测一个员工离职的可能性并帮助人事部门理解员工为何离职。 目录1.获取数据2.数据预处理3.分析数据3.…...
Python学习-----模块4.0(json字符串与json模块)
目录 1.json简介: 2.json对象 3.json模块 (1)json.dumps() 函数 (2)json.dumps() 函数 (3)json.loads() 函数 (4) json.load() 函数 4.总结: 1.json简介: SON(…...
open3d最大平面检测,平面分割
1.点云读入 读入文件(配套点云下载链接) # 读取点云 pcd o3d.io.read_point_cloud("point_cloud_00000.ply")配套点云颜色为白色,open3d的点云显示默认背景为白色,所以将点云颜色更改为黑色 pcd.colors o3d.utilit…...
【C++】4.类和对象(下)
1.再谈构造函数 1赋值 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;}private:int _year;int _month;int _day; };构造函数体中的语句只能将其称作为赋初值,而不能称作初始化。因为初始化只能初始化一次…...
自动驾驶仿真:ECU TEST 、VTD、VERISTAND连接配置
文章目录一、ECU TEST 连接配置简介二、TBC配置 test bench configuration三、TCF配置 test configuration提示:以下是本篇文章正文内容,下面案例可供参考 一、ECU TEST 连接配置简介 1、ECU TEST(简称ET),用于HIL仿…...
postgres数据库连接管理
1.连接命令psql -d postgres -h 10.0.0.51. -p 1921 -U postgres(-d指定数据库名字)2.pg防火墙介绍(pg实例层面的权限控制)pg_hba.conf文件配置文件分为5部分:配置示例#TYPE DATABASE USER ADDRESS METHODhost all loc…...
【华为OD机试模拟题】用 C++ 实现 - 环中最长子串(2023.Q1)
最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…...
Spring:@Async 注解和AsyncResult与CompletableFuture使用
Async概述 Spring中用Async注解标记的方法,称为异步方法,它会在调用方的当前线程之外的独立的线程中执行, 其实就相当于我们自己new Thread(()-> System.out.println("hello world !"))这样在另一个线程中去执行相应的业务逻辑…...
tidb ptca,ptcp考证
PingCAP 认证 TiDB 数据库专员 V6 考试(2023-02-23)https://learn.pingcap.com/learner/exam-market/list?categoryPCTA PingCAP 认证 TiDB 数据库管理专家(PCTP - DBA)认证考试范围指引 - ☄️ 学习与认证 - TiDB 的问答社区:lo…...
关于用windows开发遇到的各种乌龙事件之node版本管理---nvm install node之后 npm 找不到的问题
友情提醒,开发最好用nvm控制node版本 nrm 控制镜像源,能少掉很多头发开发过程中技术迭代更新的时候最要老命的就是 历史项目的node版本没有记录,导致开启旧项目的时候就会报错。尤其是npm 升级到8.x.x以后,各种版本不兼容。 真…...
JMeter做UI自动化
插件安装搜插件selenium,安装添加config添加线程组右键线程组->添加->配置元件->jpgc - Chrome Driver Configoption和proxy不解释了添加Sampler右键线程组->添加->取样器->jpgc - WebDriver Samplerscript language 选择:JavaScript&…...
Kibana与Elasticsearch
下载与安装Kibanahttps://www.elastic.co/cn/downloads/kibanaKibana的版本与Elasticsearch的版本是一致的,使用方法也和Elasticsearch一致。由于我的英文不是特别好,我们找到config/kibana.yml末尾添加i18n.locale: "zh-CN" ,汉化…...
[数据结构]:03-栈(C语言实现)
目录 前言 已完成内容 单链表实现 01-开发环境 02-文件布局 03-代码 01-主函数 02-头文件 03-StackCommon.cpp 04-StackFunction.cpp 结语 前言 此专栏包含408考研数据结构全部内容,除其中使用到C引用外,全为C语言代码。使用C引用主要是为了简…...
1W+企业都在用的数字化管理秘籍,快收藏!
企业数字化,绕不开的话题。 随着国家相继出台各种举措助力中小企业数字化转型,积极推动产业数字化转型,培育数字经济新生态,企业想要谋生存,求发展,必然需要做好数字化转型和管理。 本篇文章想跟大家一起…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
