一个好的前端开发人员必须掌握的前端代码整洁与开发技巧
前端代码整洁与开发技巧
为保证前端人员在团队项目开发过程中的规范化、统一化,特建立《前端代码整洁与开发技巧》文档,通过代码简洁推荐、开发技巧推荐等章节来帮助我们统一代码规范和编码风格,从而提升项目的可读性和可维护性。
目录
文章目录
- 前端代码整洁与开发技巧
- 目录
- 一、代码整洁推荐
- 1.1 三元(三目)运算符
- 1.2 短路判断简写
- 1.3 变量声明简写
- 1.4 if真值判断简写
- 1.5 For循环简写
- 1.6 对象属性简写
- 1.7 箭头函数简写
- 1.8 隐式返回简写
- 1.9 模板字符串
- 1.10 默认参数值
- 1.11 解构赋值简写
- 1.12 多条件判断简写
- 1.13 多变量赋值简写
- 1.14 解构时重命名
- 1.15 对象替代switch
- 1.16 链判断运算符
- 1.17 Null 判断运算符
- 1.18 逻辑运算符
- 1.19 扩展运算符
- 1.20 +运算符隐式转换
- 1.21 v-for中使用解构
- 1.22 使用Set给数组去重
- 1.23 Object[key] 重用代码块
- 1.24 禁用不必要的嵌套块
- 1.25 具有默认值的函数参数应该放到最后
- 1.26 **组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法**
- 1.27 计算属性使用解构赋值减少this滥用,提升性能
- 二、开发技巧推荐
- 2.1 vue开发技巧
- 2.1.1 使用v-bind="obj"将所有属性传给子组件
- 2.1.2 使用$on(‘hook:’)
- 2.1.3 如何为Vue组件添加非响应式数据
- 2.1.5 适时使用$options
- 2.1.6 如何解决Vue在main.js全局引入scss文件,组件里使用scss变量报错问题
- 2.2 javascript开发技巧
- 2.2.1 数组操作
- 1、使用 includes 简化 if 判断
- 2、使用every判断是否所有项都满足条件
- 3、使用some判断是否有一项满足条件
- 4、使用reduce遍历数组处理求和等复杂逻辑
- 2.2.2 对象操作
- 1、向对象有条件的添加属性
- 2、检查属性是否存在对象中
- 3、使用Object.entries() 返回一个给定对象自身可枚举属性的键值对数组
- 4 、使用Object.fromEntries( ) 方法把键值对列表转换为一个对象
- 5、使用Object.freeze冻结对象(可在vue data定义非响应式变量使用)
- 2.2.3 字符串操作
- 1、字符串不满两位补零
- 2、判断字符串前缀、后缀
- 3、使用replaceAll 方法替换所有匹配到的字符
- 2.3 css开发技巧
- 2.3.1 巧妙使用伪类生成表单必填标识
- 2.3.2 增强用户体验,使用伪元素实现增大点击热区
- 2.3.3 利用伪类实现鼠标移入时下划线向两边展开的效果
- 三、其它技巧
- 3.1.1 适时使用provide和inject
- 3.1.2 利用key值解决vue就地复用策略的问题
一、代码整洁推荐
1.1 三元(三目)运算符
如果只想在一行中编写if…else语句时,这是一个很好的节省代码的方式。
常规:
const x = 20
let answer
if(x > 10) {answer = '大于10'
}else {answer = '小于等于10'
}
简写:
const x = 20
const answer = x > 10? '大于10' : '小于等于10'
嵌套版三元运算:
const x = 20
const answer = x > 10? '大于10' : x < 5? '小于5' : '在5和10之间'// 第二层加上()增强可读性
const answer = x > 10? '大于10' : (x < 5? '小于5' : '在5和10之间')
注意:三元运算不要超过2层嵌套,否则可读性不强,如果是嵌套两层,要求第二层加上()帮助提升可读性。
1.2 短路判断简写
将变量值分配给另一个变量时,可能希望确保源变量不为null,undefined或为空。这时候可以编写带有多个条件的长 if 语句,也可以使用短路判断。
常规:
if(type !== null || type !== undefined || type !== '') {let wtType = type
} else {let wtType = '01'
}
简写:
let wtType = type || '01'
注意:如果type值为false或者数字0将取值为字符串’01’。
1.3 变量声明简写
在进行连续性的变量声明操作时,应尽可能的使用 一次声明语句来提升程序运行效率。
常规:
let x
let y
let z = 1
简写:
let x, y, z = 1
1.4 if真值判断简写
这可能是微不足道的,但值得一提。在执行“if 检查”时,有时可以省略全等运算符。
常规:
if(likeJavascript === true) {...
}
if(likeNode !== true) {...
}
简写:
if(likeJavascript) {...
}
if(!likeNode) {...
}
1.5 For循环简写
常规:
const arr = [1, 2, 3]
for(let i = 0; i < arr.length; i++) {console.log(i, arr[i])if(arr[i] > 1) {break}
}
// 0, 1
// 1, 2
如果不需要访问索引,请执行以下操作:
for(let item of arr) {console.log('item', item)if(item > 1) {break}
}
// 1
// 2
如果只想访问索引,请执行以下操作:
for(let index in arr) {console.log(index)
}
// 1
// 2
// 3
如果要访问对象中的键,请执行以下操作
const obj = {name: 'zhangsan', age: 18}
for(let key in obj) {console.log(key,obj[key])
}
// name
// age
注意:不能使用for…of 来遍历对象。
1.6 对象属性简写
ES6提供了一种更简单的方法来为对象分配属性。如果变量名称与对象键相同,则可以使用简写表示法。
常规:
let signType = '1'
let params = {signType: signType
}
简写:
let signType = '1'
let params = {signType
}
1.7 箭头函数简写
经典函数以简单的形式易于读写,但是一旦你开始将它们嵌套在其他函数调用中,它们往往会变得有点冗长和混乱。
常规:
function sayHello(name) {console.log('Hello', name)
}
setTimeout(function() {console.log('Loaded')
}, 2000)
list.forEach(function(item) {console.log(item)
})
简写:
const sayHello = name => console.log('Hello', name)
setTimeout(() => console.log('Loaded'), 2000)
list.forEach(item => console.log(item))
1.8 隐式返回简写
Return 是我们经常使用的关键字,用于返回函数的最终结果。具有单个语句的箭头函数将隐式返回其执行结果(函数可以省略大括号{}用()省略return关键字,具体可参考下例)。
要返回多行语句(例如对象),必须使用 () 而不是 {} 来包装函数体。这可确保将代码执行为单个语句。
常规:
function sum(x, y) {return x + y
}
function makeInfo(name, age) {return {name, age}
}
简写:
const sum = (x, y) => x + y
const makeInfo = (name, age) => ({name, age})
1.9 模板字符串
您是否厌倦了使用 ‘+’ 将多个变量连接成一个字符串?有没有更简单的方法?如果你能够使用ES6,那么你很幸运。您需要做的就是使用反引号,并使用 ${} 来包含变量。
常规:
const name = 'zhangsan'
const age = 18
let des = name + '今年' + age + '岁'
简写:
const name = 'zhangsan'
const age = 18
let des = `${name}今年${age}岁`
1.10 默认参数值
您可以使用if语句定义函数参数的默认值。在ES6中,您可以在函数声明本身中定义默认值。
常规:
function volume(l, w, h) {if(w === undefined) {w = 3}if(h === undefined) {h = 4}return l * w * h
}
简写:
const volume = (l, w = 3, h = 4) => l * w * h
volume(2)
// 24
注意:只有w,h为undefined时默认值才会生效。
结构赋值中的默认值也是如此,如果值为null默认值不会生效,如:
const res = {list: null,code: undefined
}
const { list = [], code = 1 } = res
console.log(list) // null
console.log(code) // 1let num = list.length // Uncaught TypeError: Cannot read properties of null (reading 'length')
注意:如果直接取list数组的长度或者利用[index]取里面的值在res.list为null或者不为数组的时候可能会发生报错,针对该问题,大家可以在解构的时候不赋默认值,在解构的下方利用null判断运算符来处理默认值,如:
const res = {list: null
}
let { list , code = 1 } = res
list = list ?? []
console.log(list) // []let num = list.length // 0
1.11 解构赋值简写
对数组和变量进行解构可以减少变量滥用,提升程序的运行效率。
常规:
let params = {wtId: this.transferDetail.wtId,newPic: type === '01' ? this.selectPersonId : '', // type是方法入参oldPic: this.transferDetail.picOld,recordId: this.applyId, // 转派记录replacementPicStatus: '02', // 转派状态 01-转派中 02-转派结束replacementReason: this.replacementReason
}
简写:
const { selectPersonId, applyId, replacementReason, transferDetail } = this
let params = {wtId: transferDetail.wtId,newPic: type === '01' ? selectPersonId : '', // type是方法入参oldPic: transferDetail.picOld,recordId: applyId, // 转派记录replacementPicStatus: '02', // 转派状态 01-转派中 02-转派结束replacementReason
}
解构知识点链接地址:https://es6.ruanyifeng.com/#docs/destructuring
1.12 多条件判断简写
常规:
function typeHandle(type) {if (type === 'test1') { test1(); } else if (type === 'test2') { test2(); } else if (type === 'test3') { test3(); } else if (type === 'test4') { test4(); } else { throw new Error('Invalid value ' + type); }
}
typeHandle('test3')
简写:
function typeHandle(type) { const types = { test1: test1, test2: test2, test3: test3, test4: test4 }; let func = types[type]; (!func) && throw new Error('Invalid value ' + type);func();
}
typeHandle('test3')
1.13 多变量赋值简写
常规:
let test1, test2, test3; test1 = 1;
test2 = 2;
test3 = 3; let name, age, sex;name = 'zhangsan';
age = '18';
sex = '男'
简写:(利用es6数组、对象的结构赋值,实现多变量赋值简写)
let [test1, test2, test3] = [1, 2, 3]; let {name, age, sex} = { name: 'zhangsan', age: '18', sex: '男'};
1.14 解构时重命名
常规:
const { ticketTypeName } = this.tciketDetail
let params = {wtName: ticketTypeName,...
}
简写:
const { ticketTypeName: wtName } = this.tciketDetail
let params = {wtName,...
}
对象解构赋值的简写形式:
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
1.15 对象替代switch
常规:
// 获取工作票名称
workTicketName(type) {switch (type) {case '01':return '变一'breakcase '02':return '变二'breakcase '03':return '配一'breakcase '04':return '配二'breakcase '05':return '低压'breakcase '06':return '作业卡'breakcase '07':return '派工单'breakdefault:break}
}
workTicketName('01') // 变一
简写:
// 获取工作票名称
workTicketName(type) {const typeObj = {'01': '变一','02': '变二','03': '配一','04': '配二','05': '低压','06': '作业卡','07': '派工单'}return typeObj[type]
}
workTicketName('01') // 变一
1.16 链判断运算符
编程实务中,如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。比如,读取message.body.user.firstName
这个属性,安全的写法是写成下面这样。
// 错误的写法
const firstName = message.body.user.firstName || 'default';// 正确的写法
const firstName = (message&& message.body&& message.body.user&& message.body.user.firstName) || 'default';// 但是这种写法如果层级过长会显得冗长,可读性也不强,这是我们可以利用链判断运算符来解决
//链判断运算符写法
const firstName = message?.body?.user?.firstName || 'default';
链判断运算符?.
有三种写法。
obj?.prop
// 对象属性是否存在obj?.[expr]
// 同上 也可以理解为 arr?.[index]func?.(...args)
// 函数或对象方法是否存在
下面是?.
运算符常见形式,以及不使用该运算符时的等价形式。
a?.b
// 等同于
a == null ? undefined : a.ba?.[x]
// 等同于
a == null ? undefined : a[x]a?.b()
// 等同于
a == null ? undefined : a.b()a?.()
// 等同于
a == null ? undefined : a()
常规:
// 计划 id
let planId = this.ticketDetail && this.ticketDetail.planDetailVoList && this.ticketDetail.planDetailVoList[0] && this.ticketDetail.planDetailVoList[0].planId
简写:
// 计划 id
let planId = this.ticketDetail?.planDetailVoList?.[0]?.planId
// 如果任意一个问号前面的值为null或者undefined都将直接返回undefined,不在向下取值
注意:链判断运算符只能在js代码块中使用,不能在template里面的标签上使用,否则会解析报错,如果想要在template里达到上述简写效果可以在计算属性中使用。
// 错误的写法
<template> <div class="wrap"> <span>计划编号:</span>{{ticketDetail && ticketDetail.planDetailVoList && ticketDetail.planDetailVoList[0] && ticketDetail.planDetailVoList[0].planNo || ''}} </div>
</template>
<script> export default { data() { return {ticketDetail: {planDetailVoList: [{planNo: 'T202304200940' // 计划编号}]} } }}
</script>//正确的写法
<template> <div class="wrap"> <span>计划编号:</span>{{ planNo }} </div>
</template>
<script> export default { data() { return {ticketDetail: {planDetailVoList: [{planNo: 'T202304200940' // 计划编号}]} } },// 计算属性computed: {// 计划编号planNo({ticketDetail}) {return ticketDetail?.planDetailVoList?.[0]?.planNo || ''}}}
</script>
链判断运算符知识点链接:https://es6.ruanyifeng.com/#docs/operator
1.17 Null 判断运算符
读取对象属性的时候,如果某个属性的值是null
或undefined
,有时候需要为它们指定默认值。常见做法是通过||
运算符指定默认值。
const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;
上面的三行代码都通过||
运算符指定默认值,但是这样写是错的。开发者的原意是,只要属性的值为null
或undefined
,默认值就会生效,但是属性的值如果为空字符串或false
或0
,默认值也会生效。
为了避免这种情况,ES2020 引入了一个新的 Null 判断运算符??
。它的行为类似||
,但是只有运算符左侧的值为null
或undefined
时,才会返回右侧的值。
const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;
上面代码中,默认值只有在左侧属性值为null
或undefined
时,才会生效。
1.18 逻辑运算符
使用逻辑运算符!! 快速进行布尔转换。
常规:
// 判断是否是ios环境
const isIOS = Boolean(window.navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/))
简写:
// 判断是否是ios环境
const isIOS = !!window.navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
1.19 扩展运算符
使用扩展运算符进行数组、对象合并。
常规:
let obj = { name: 'zhangsan', sex: '男' }
obj = Object.assign(obj, {age: 18})let arr = ['zhangsan', 'lisi']
arr = arr.concat(['wangwu', 'zhaoliu'])
简写:
let obj = { name: 'zhangsan', sex: '男' }
obj = {...obj, ...{age: 18}}
obj = {...obj, age: 18}let arr = ['zhangsan', 'lisi']
arr = [...arr, ...['wangwu', 'zhaoliu']]
1.20 +运算符隐式转换
使用+运算符不仅可以将字符串转为数字,还可以将时间转为时间戳。
常规:
let type = '1'
type = Number(type)let sjc = new Date().getTime()
简写:
let type = '1'
type = +typelet sjc = +new Date()
1.21 v-for中使用解构
常规:
<li v-for="item in users" :key="item.id" > {{ item.name }} </li>
简写:
<li v-for="{ name, id } in users" :key="id" > {{ name }} </li>
1.22 使用Set给数组去重
常规:
Array.prototype.distinct = function(){let arr = this,result = [],i,j,len = arr.length;for(i = 0; i < len; i++){for(j = i + 1; j < len; j++){if(arr[i] === arr[j]){j = ++i;}}result.push(arr[i]);}return result;
}
const arra = [1,2,3,4,4,1,1,2,1,1,1];
arra.distinct(); // [3,4,2,1]
简写:
const arr = [1, 1, 2, 3, 4, 4];
const uniqueArr = [...new Set(arr)];
const uniqueArr1 = Array.from(new Set(arr)); // [1,2,34]
1.23 Object[key] 重用代码块
常规:
function validate(values) {if(!values.first) {uni.showToast({title: '第一项不可为空',icon: 'none'})return false}if(!values.last) {uni.showToast({title: '最后一项不可为空',icon: 'none'})return false}return true
}
let isCheckPass = validate({first:'', last: ''})
console.log('是否检验通过', isCheckPass)
简写:
// 对象校验规则
const schema = {first: {required: true,failedTip: '第一项不可为空'},last: {required: true,failedTip: '最后一项不可为空'}
}
// 通用校验函数
const validate = (schema, values) => {for(field in schema) {if(schema[field].required) {if(!values[field]) {uni.showToast({title: schema[field].failedTip,icon: 'none'})return false}}}return true
}
let isCheckPass = validate(schema, {first: '1', last: '3'})
console.log('是否检验通过', isCheckPass)
1.24 禁用不必要的嵌套块
常规:
function submitTicket(){const { ticketCode, isDoubleSign } = thisif(ticketCode === '05') {if(isDoubleSign) {this.signType = '02'}}
}
简写:
function submitTicket(){const { ticketCode, isDoubleSign } = thisif(ticketCode === '05' && isDoubleSign) {this.signType = '02'}
}
1.25 具有默认值的函数参数应该放到最后
默认值放到最后可以让函数少传实参还能正常执行,收获预期结果。
常规:
求和函数,把具有默认值的参数放在参数列表「左边」
function sum(a = 10, b) {return a + b
}/*第1个实参 总是对应 第1个形参所以,3 赋值给 a, 替换掉默认值 10参数b没有传值,最终函数返回NaN
*/
sum(3) // returns NaN as b is undefined
简写:
求和函数,把具有默认值的参数放在参数列表「右边」
function sum(b,a = 10) {return a + b
}/*3 赋值给 ba 没有传值,使用默认值 10
*/
sum(3) // 13
1.26 组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法
Bad Code
<template> <div class="wrap"> <span>工作负责人:</span>{{planDetail.personInfo.name + (planDetail.personInfo.phone? '-' + planDetail.personInfo.phone : 'planDetail.personInfo.phone')}} </div>
</template><script> export default { data() { return {planDetail: {personInfo: {name: 'zhangsan',phone: '15236616216'} } } }}
</script>
Good Code
<template> <div class="wrap"> <span>工作负责人:</span>{{WorkLeader}} </div>
</template><script> export default { data() { return {planDetail: {personInfo: {name: 'zhangsan',phone: '15236616216'} },} },conputed: {// 复杂的表达式则应该重构为计算属性WorkLeader({planDetail}) {return planDetail.personInfo.name + (planDetail.personInfo.phone? '-' + planDetail.personInfo.phone : planDetail.personInfo.phone)}}}
</script>
注意:计算属性中必须包含return字段,且不能产生副作用(如修改外部变量,调用外部无关方法操作了dom等等)
1.27 计算属性使用解构赋值减少this滥用,提升性能
Bad Code
<script>
export default { data() { return { ticketDetail: {wtType: '03'},sendFlag: false,provPermitType: ''} }, computed: { // 票类型wtType() {return this.ticketDetail.wtType},// 应采取安全措施是否可编辑measureEdit() { // 配一工作票 非远程许可 非发送 可编辑if (this.ticketDetail.wtType === "03" && !this.sendFlag && !['01', '02'].includes(this.provPermitType)) {return true}return false}}
}
</script>
上述计算属性中存在滥用this去读取data数据的问题 , 使用this去读取data中数据时会去收集依赖,如果滥用this去读取data中数据,会多次重复地收集依赖,从而产生性能问题。
解决办法:
计算属性的值是一个函数,其参数是Vue的实例化this对象,在上述计算属性中滥用this的例子中可以这样优化。
Good Code
<script>
export default { data() { return { ticketDetail: {wtType: '03'},sendFlag: false,provPermitType: ''} }, computed: { // 票类型wtType({ticketDetail}) {return ticketDetail.wtType},// 应采取安全措施是否可编辑measureEdit({wtType, sendFlag, provPermitType}) { // 配一工作票 非远程许可 非发送 可编辑if (wtType === "03" && !sendFlag && !['01','02'].includes(provPermitType)) {return true}return false}}
}
</script>
二、开发技巧推荐
2.1 vue开发技巧
2.1.1 使用v-bind="obj"将所有属性传给子组件
应用场景1:
将自身接收到的所有props传递给它的子组件,子组件需要在其props:{} 中定义要接受的参数名,常用于对组件的二次封装中,如:对el-table、el-form利用json配置项来控制渲染加载等。
1、v-bind=“$props”
p r o p s :当前组件接收到的 p r o p s 对象。可以通过 v − b i n d = " props:当前组件接收到的 props 对象。可以通过 v-bind=" props:当前组件接收到的props对象。可以通过v−bind="props" 传 入 内 部 组 件 , 也 可 以 通 过 v m . props" 传入内部组件,还可以通过vm.props[name]的形式去获取。
应用场景2:
针对组件有过多的属性传参时,可以不用再一个一个的写,可以把较多的参数传递简化,减少组件本身的参数定义。
2、v-bind=“obj”
如:
<Children :name="name" :age="age" :sex="sex" :job="job"></Children>
改写:
<Children v-bind="{name,age,sex,job}"></Children>
2.1.2 使用$on(‘hook:’)
o n ( ‘ h o o k : ’ ) 能够注册 / 监听任意一个钩子函数。使用 on(‘hook:’)能够注册/监听任意一个钩子函数。使用 on(‘hook:’)能够注册/监听任意一个钩子函数。使用on(‘hook:’)方法,可以仅使用一种生命周期方法(而不是两种)来定义/删除事件 。
实现一个定时器的调用与销毁大家很可能会用以下方式实现:
export default{data(){timer:null // 需要创建实例},mounted(){this.timer = setInterval(()=>{//具体执行内容console.log('1');},1000);},beforeDestory(){clearInterval(this.timer);this.timer = null;}
}
这种方法存在的问题是:
1、vue实例中需要有这个定时器的实例,感觉有点多余。
2、创建的定时器代码和销毁定时器的代码没有放在一起,不容易维护,通常很容易忘记去清理这个定时器。
使用 o n ( ‘ h o o k : ’ ) 监听 b e f o r e D e s t o r y 生命周期可以避免该问题,并且因为只需要监听一次,所以可以使用 on(‘hook:’)监听beforeDestory生命周期可以避免该问题,并且因为只需要监听一次,所以可以使用 on(‘hook:’)监听beforeDestory生命周期可以避免该问题,并且因为只需要监听一次,所以可以使用once进行注册监听。
export default{mounted(){const timer = setInterval(()=>{console.log('1');},1000);this.$once('hook:beforeDestory',()=>{ // 监听一次即可clearInterval(timer);timer = null;})}
}
2.1.3 如何为Vue组件添加非响应式数据
在vue组件中内data内函数返回的对象默认是响应式的,vue的observe函数会遍历此对象所有的属性和子孙属性并转化为getter/setter, 使Vue能够追踪依赖,在属性被访问和修改时通知变更。这种响应式被用在模板更新、watch变更、computed依赖中非常有用。
为什么要设置非响应式数据?
如果我们的数据不会改变,或者只会整体改变,或者本身就不需要响应式,那么为深度响应式做的转化、依赖以及产生的闭包、watcher空间其实是多余的,白白浪费了时间和性能。
平时我们自己写的对象不会太复杂这种性能消耗并不明显,但当在引用第三方工具库,比如图表、地图、模型等,如果把多个不需要深度响应式的第三方实例或数据直接挂载到data属性上,又或者遇到大数据量列表,性能的影响就会比较明显。
利用Vue无法检测到对象属性的添加来实现
官方文档中有介绍
受现代 JavaScript 的限制 (而且 Object.observe 也已经被废弃),Vue 无法检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如:
let vm = new Vue({data:{a:1}
})
// `vm.a` 是响应式的
vm.b = 2
// `vm.b` 是非响应式的
因此,我们可以待实例完成初始化后,即created中加入
export default {data() {this.version = '' // 这样定义初始值也行return {}},created() {// 直接在created及后续的生命周期函数里定义赋值this.bigData = {···}}···
}
2.1.5 适时使用$options
vue
实例属性$options
是一个对象,可以调用vue的各个组件下的方法和数据 。
应用场景:
1、获取、调用data外定义的属性
<script>
export default {data() {return {};},//在data外面定义的属性和方法通过$options可以获取和调用name: "options_test",age: 18,testOptionsMethod() {console.log("hello options");},created() { console.log(this.$options.name); // options_testconsole.log(this.$options.age); // 18this.$options.testOptionsMethod(); // hello options},
</script>
2、复用过滤器filters中的方法
假设在时间选择控件里可以获取到一个返回的时间戳,需要转换为日期时间来显示,这个时候就可以用到一个filterTime函数来格式化处理时间戳返回想要显示的时间格式。如果这个时候后台要求我们在离线场景提交时增加一个同样时间格式的提交时间字段,这个时候我们就可以利用this.$options.filters来拿到这个函数进行转换,而不是重新在定义一个方法来处理。
<template> <div>{{ planStartSjc | filterText }}</div>
</template>
export default {data() {return {planStartSjc: 1681960059227 // 计划开始时间的时间戳} },filters: {// 时间戳转换filterTime: function (sjc) {let date = new Date(sjc)const year = date.getFullYear()let month = date.getMonth() + 1let day = date.getDate()let hour = date.getHours()let minute = date.getMinutes()let second = date.getSeconds()month = String(month).padStart(2, '0')day = String(day).padStart(2, '0')hour = String(hour).padStart(2, '0')minute = String(minute).padStart(2, '0')second = String(second).padStart(2, '0')let time = `${year}-${month}-${day} ${hour}:${minute}:${second}`return time}},methods:{// 计划提交submitPlan(){let filterTime = this.$options.filters.filterTimelet nowSjc = + new Date() // 此时的时间戳let planCreateTime = filterTime(nowSjc) // 转换后的时间let planStartTime = filterTime(this.planStartSjc) // 转换后的时间let params = {planCreateTime, // 计划创建时间planStartTime, // 计划开始时间}}, },
}
3、一键搞定之重置data中的某个数据
<script>export default {data() {return {// 表单searchForm: {input: '',name: '',isSelected: false}}},methods: {retset() {//重置某一个表单数据this.searchForm = this.$options.data().searchForm;}}}
</script>
4、重置整个data的数据
<script>export default {data() {return {// 表单searchForm: {input: '',name: '',isSelected: false},dataList: [],planId: ''}},methods: {// 更新测试testUpdata() {this.searchForm = {input: '输入',name: '张三',isSelected: true}this.dataList = [1,2]this.planId = '123456'},retset() {// 重置前先更新一些数据this.testUpdata()console.log('重置前的数据', this.$data)// 直接赋值重置即会导致报错产生(最新的Vue已经不允许这样直接对根实例$data进行赋值)// this.$data = this.$options.data(); // 改用以下写法重置Object.assign(this.$data, this.$options.data()); console.log('重置后的数据', this.$data)}}}
</script>
2.1.6 如何解决Vue在main.js全局引入scss文件,组件里使用scss变量报错问题
报错: Syntax Error: SassError: Undefined variable.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TSFtggSk-1682384948777)(前端代码整洁与开发技巧.assets/20210909143917135-1681975189904.png)]
我一开始的引入方式是这样的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c5PhE89y-1682384948778)(前端代码整洁与开发技巧.assets/20210909145443693-1681975204155.png)]
以为在main.js全局引入了就一劳永逸,写着常规css样式时还好好的,用到scss变量时就报错了。
- 解决方法一
在需要用到的 .vue 文件里单独引用 variable.scss 变量文件,但是达不到我们想要的“一劳永逸“效果,可能还会有奇奇怪怪的潜在问题,建议使用方法二。
- 解决方法二
通过配置,使得全局都能使用scss变量,而不用每个文件单独引入 。
1、vue-cli2创建的项目:
修改build中的utils.js文件,将 scss: generateLoaders(‘sass’),修改为如下:
scss: generateLoaders('sass').concat({loader: 'sass-resources-loader',options: {//你自己的scss全局文件的路径resources: path.resolve(__dirname, '../src/common/index.scss')}
}),
2.vue-cli3创建的项目:
module.exports = {css: {loaderOptions: {// 不同 sass-loader 版本对应关键字, // v8-: data v8: prependData v10+: additionalDatasass: {additionalData: `@import "@/assets/scss/index.scss";`},scss: {additionalData: `@import "@/assets/scss/index.scss";`},}}
}
注意:scss 配置后面需要加 分号 ‘;’,否则会报错 Syntax Error: SassError: media query expression must begin with ‘(’
参考文档:https://cli.vuejs.org/zh/guide/css.html#css-modules
2.2 javascript开发技巧
2.2.1 数组操作
1、使用 includes 简化 if 判断
Bad Code
if(ticketCode === '01' || ticketCode === '02' || ticketCode === '03') { }
Good Code
if(['01', '02', '03'].includes(ticketCode)) { }
// 下面这种也可以
if('01,02,03'.includes(ticketCode)) { }
2、使用every判断是否所有项都满足条件
Bad Code
let arr = [{checked: true}, {checked: false}, {checked: true}, {checked: false}]
let isAllchecked = true // 是否全部选中
arr.forEach(item => {if(!item.checked) {isAllchecked = false}
})
Good Code
数组的every方法只要有一项不满足条件就会返回false,不会再继续遍历
let arr = [{checked: true}, {checked: false}, {checked: true}, {checked: false}]
let isAllchecked = arr.every(item => {console.log('item', item)return !!item.checked
})
3、使用some判断是否有一项满足条件
Bad Code
let arr = [{checked: false}, {checked: true}, {checked: false}, {checked: true}]
let isHaveChecked = false // 是否有选中的
arr.forEach(item => {if(item.checked) {isHaveChecked = true}
})
Good Code
数组的some方法只要有一项满足条件就会返回true,不会再继续遍历
let arr = [{checked: false}, {checked: true}, {checked: false}, {checked: true}]
let isHaveChecked = arr.some(item => {console.log('item', item)return !!item.checked
})
4、使用reduce遍历数组处理求和等复杂逻辑
reduce()
方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值 。
第一次执行回调函数时,不存在“上一次的计算结果”。如果需要回调函数从数组索引为 0 的元素开始执行,则需要传递初始值。否则,数组索引为 0 的元素将被作为初始值 initialValue,迭代器将从第二个元素开始执行(索引为 1 而不是 0)。
语法
reduce(callbackFn)
reduce(callbackFn, initialValue)
参数
-
callbackFn
一个“reducer”函数,包含四个参数:
previousValue
:上一次调用callbackFn
时的返回值。在第一次调用时,若指定了初始值 initialValue,其值则为 initialValue,否则为数组索引为 0 的元素array[0]
。currentValue
:数组中正在处理的元素。在第一次调用时,若指定了初始值initialValue
,其值则为数组索引为 0 的元素array[0]
,否则为array[1]
。currentIndex
:数组中正在处理的元素的索引。若指定了初始值initialValue
,则起始索引号为 0,否则从索引 1 起始。array
:用于遍历的数组。 -
initialValue
可选作为第一次调用
callback
函数时参数 previousValue 的值。若指定了初始值initialValue
,则currentValue
则将使用数组第一个元素;否则previousValue
将使用数组第一个元素,而currentValue
将使用数组第二个元素。
返回值
使用“reducer”回调函数遍历整个数组后的结果。
示例
(1)计算数组中每个元素出现的次数
let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']let nameNum = names.reduce((pre,cur)=>{if(cur in pre) {pre[cur]++} else {pre[cur] = 1 }return pre
}, {})
console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}
(2)数组去重
let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((pre,cur)=>{if(!pre.includes(cur)){return pre.concat(cur)}else{return pre}
},[])
console.log(newArr);// [1, 2, 3, 4]// 简便写法
let newArr = [...new Set(arr)]
let newArr = Array.from(new Set(arr))
(3)将二维数组转化为一维
let arr = [[0, 1], [2, 3], [4, 5]]
let newArr = arr.reduce((pre,cur) => {return pre.concat(cur)
}, [])
console.log(newArr); // [0, 1, 2, 3, 4, 5]// 简便写法
let newArr = arr.flat() // 拉平二维数组
(4)将多维数组转化为一维
let arr = [[0, 1], [2, 3], [4,[5,6,7]]]
const newArr = function(arr){return arr.reduce((pre,cur)=>pre.concat(Array.isArray(cur)?newArr(cur):cur),[])
}
console.log(newArr(arr)); //[0, 1, 2, 3, 4, 5, 6, 7]
2.2.2 对象操作
1、向对象有条件的添加属性
使用场景:
表单提交时针对个性化开关控制的必填参数可以通过该方式来添加到固定参数集合中。
const condition = true;
const person = {id: 1,name: 'John Doe',...(condition && { age: 16 }),
};// 如果 condition 为 false,JavaScript 会做这样的事情:
const person = {id: 1,name: '前端小智',...(false),
};
// 展开 `false` 对对象没有影响
console.log(person); // { id: 1, name: 'John Doe' }
2、检查属性是否存在对象中
const example = {};
example.prop = 'exists';// hasOwn 只会对直接属性返回 true::
Object.hasOwn(example, 'prop'); // returns true
Object.hasOwn(example, 'toString'); // returns false
Object.hasOwn(example, 'hasOwnProperty'); // returns false// in 会对直接或继承的属性返回 true:
'prop' in example; // returns true
'toString' in example; // returns true
'hasOwnProperty' in example; // returns true
3、使用Object.entries() 返回一个给定对象自身可枚举属性的键值对数组
const object1 = {a: 'somestring',b: 42
};const arr = Object.entries(object1) //arr:[['a','somestring'],['b','42']]for (const [key, value] of arr) {console.log(`${key}: ${value}`);
}// Expected output:
// "a: somestring"
// "b: 42"
4 、使用Object.fromEntries( ) 方法把键值对列表转换为一个对象
const entries = new Map([['foo', 'bar'],['baz', 42]
]);const obj = Object.fromEntries(entries);console.log(obj);
// Expected output: Object { foo: "bar", baz: 42 }
5、使用Object.freeze冻结对象(可在vue data定义非响应式变量使用)
const obj = {prop: 42
};Object.freeze(obj);obj.prop = 33;
// Throws an error in strict modeconsole.log(obj.prop);
// Expected output: 42
2.2.3 字符串操作
1、字符串不满两位补零
ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()
用于头部补全,padEnd()
用于尾部补全。
padStart()
和padEnd()
一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax''x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
如果原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'
如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。
'abc'.padStart(10, '0123456789')
// '0123456abc'
如果省略第二个参数,默认使用空格补全长度。
'x'.padStart(4) // ' x'
'x'.padEnd(4) // 'x '
常用场景:
// 获取当前月份小于10前补0
let month = new Date().getMonth() +1
// 常规写法利用三木运算符处理
month = month < 10 ? '0' + month : month// 也可以利用pdaStart填充实现
month = String(month).padStart(2, '0')
// 还可以按照以下方式实现
month = ('0' + month).slice(-2)console.log(month);
// expected output: "01"// 银行卡号只显示后4位
const fullNumber = '2034399002125581';
const last4Digits = fullNumber.slice(-4);
const maskedNumber = last4Digits.padStart(fullNumber.length, '*');console.log(maskedNumber);
// expected output: "************5581"
2、判断字符串前缀、后缀
判断字符串前缀、后缀不要一言不合就使用正则表达式:
const url = "https://bili98.cn";
const isHTTPS = /^https:\/\//.test(url); // trueconst fileName = "main.py";
const isPythonCode = /\.py$/.test(fileName); // true
推荐使用 String.prototype.startsWith 和 String.prototype.endsWith,语义性更好:
const url = "https://bili98.cn";
const isHTTPS = url.startsWith("https://") // trueconst fileName = "main.py";
const isPythonCode = fileName.endsWith(".py"); // true
3、使用replaceAll 方法替换所有匹配到的字符
ES2021新特性-替换一个字符串中的所有指定字符 replaceAll()方法的使用
在 ES2021 之前,要替换掉一个字符串中的所有指定字符,我们可以这么做:
const str = '2-4-6-8-10'
const newStr = str.replace(/\-/g, '+')
console.log(newStr) // 2+4+6+8+10
ES2021 则提出了 replaceAll 方法,并将其挂载在 String 的原型上,可以这么用:现在可以用String.prototype.replaceAll()替换全部字符串而不需要使用正则。
const str = '2-4-6-8-10'
const newStr = str.replaceAll('-', '+')
console.log(newStr) // 2+4+6+8+10
2.3 css开发技巧
2.3.1 巧妙使用伪类生成表单必填标识
::befor {content: "*";color: red;margin-right: 4px;
}
示例:(element ui里面的表单项必填标识)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dGYZMG1q-1682384948779)(前端代码整洁与开发技巧.assets/1673255318557.png)]
2.3.2 增强用户体验,使用伪元素实现增大点击热区
在移动端,按钮通常都很小,但是有时由于设计稿限制,我们不能直接去改变按钮元素的高宽。那么这个时候有什么办法在不改变按钮原本大小的情况下去增加他的点击热区呢?
这里,伪元素也是可以代表其宿主元素来响应的鼠标交互事件的。借助伪元素可以轻松帮我们实现,如:
.btn::befoer{ content: ""; position: absolute; top: -10px; right: -10px; bottom: -10px; left: -10px;
}
2.3.3 利用伪类实现鼠标移入时下划线向两边展开的效果
<html lang="en">
<head><meta charset="UTF-8"><title>鼠标移入下划线展开</title><style type="text/css">#underline{width: 200px;height: 50px;background: #ddd;margin: 20px;position: relative;}#underline:after{content: "";width: 0;height: 5px;background: blue;position: absolute;top: 100%;left: 50%;transition: all .8s;}#underline:hover:after{left: 0%;width: 100%;}</style>
</head>
<body><div id="underline"></div>
</body>
</html>
效果展示:
1、在浏览器上随便找个网页打开f12;
2、点击元素栏选中html跟标签右键以html格式修改;
3、复制上述代码覆盖掉网页上的html代码完成修改;
4、鼠标移入显示元素即可看到效果。
三、其它技巧
3.1.1 适时使用provide和inject
provide 和 inject 主要为高阶插件/组件库提供用例,并不推荐直接用于应用程序代码中;并且这对选项需要一起使用以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,在上下游关系成立的时间里始终生效。
- provide:
Object | () => Object
- inject:
Array | { [key: string]: string | Symbol | Object }
//父组件:
provide: { // provide 提供一个属性和方法foo: '这是 foo',fooMethod: () => {console.log('父组件 fooMethod 被调用')}
},// 子或者孙子组件
inject: ['foo', 'fooMethod'], //数组或者对象,注入到子组件
mounted() {this.fooMethod()console.log(this.foo)
}
//在父组件下面所有的子组件都可以利用inject
provide 和 inject 绑定并不是可响应的。这是官方刻意为之的。
然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的,因为对象是引用类型
//父组件:
provide: { foo: '这是 foo'
},
mounted(){this.foo = '这是新的 foo'
}// 子或者孙子组件
inject: ['foo'],
mounted() {console.log(this.foo) //子组件打印的还是'这是 foo'
}//父组件:
provide() { return {foo: this.foo,nameList: this.nameList,personalInfo: this.personalInfo}
},
data() {return {foo: '这是foo',nameList: ['张三', '李四'],personalInfo: { age: 18 }}
},
mounted(){this.foo = '这是新的 foo'this.nameList.push('王五') // 改变堆内存中的值// this.nameList = ['王五', '赵六'] // 放开后子组件仍打印 ['张三', '李四', '王五'],this.personalInfo.age = 16 // 改变堆内存中的值this.personalInfo = {age: 12} // 将当前对象的指针指向新的堆内存地址
}// 子或者孙子组件
<template><div>{{foo}}</div><div>{{nameList}}</div><div>{{personalInfo && personalInfo.age}}</div>
</template>
<script>export default{inject: ['foo', 'nameList', 'personalInfo'], mounted() {setTimeout(() => {console.log(this.foo) // 这是foo -视图不会更新console.log(this.nameList) // ['张三', '李四', '王五'] -视图会更新console.log(this.personalInfo.age) // 16 foo -视图会更新}, 1000)}}
</script>
3.1.2 利用key值解决vue就地复用策略的问题
已知:throttle是封装过的节流指令,在规定时间仅第一次执行,后续点击不再执行,目前设置的时间是2秒,即每2秒内的重复点击只执行第一次。
// 节流指令
Vue.directive('throttle', {inserted: (el, binding) => {const throttleTime = binding.value || 2000 // 节流时间el.addEventListener('click', event => {if (el.nodeName === 'BUTTON' || el.type === 'button') {if (!el.disabled) {el.disabled = truesetTimeout(() => {el.disabled = false}, throttleTime)}} else {// 合理使用样式穿透使元素本身及其子元素鼠标事件失效 el.style.pointerEvents = 'none'el.style.color = 'red'el.setAttribute('data-flag', '0') // 只是为了打印setTimeout(() => {if (el && el.style) {el.style.pointerEvents = 'auto'el.style.color = '#000'el.setAttribute('data-flag', '1') // 只是为了打印}}, throttleTime)}}, false)}
})
通过以下代码可以发现点击完接受工作票按钮后不到2秒的时间内由于vue的就地复用策略导致工作许可按钮复用了原来的元素,继承了之前的样式和自定义属性,从而导致工作许可按钮在上述2s内的点击不会生效,且颜色也发生了短暂的改变。(利用项目加浏览器调试来演示说明)
<template><view><!-- 待接收 --><button v-if="stateNo == '10'" class="btn-test" @click="ticketReceive" v-throttle>接收工作票</button><!-- 待许可 --><button v-if="stateNo == '12'" class="btn-test" @click="permitTicket" v-throttle>工作许可</button> </view>
</template>
<script>
export default{data() {return {stateNo: '10'}},methods: {// 接收工作票ticketReceive($event) {console.log('$event', $event)this.stateNo = '12'this.$nextTick(() => {console.log('视图已完成更新', '工作许可按钮已显示')setTimeout(() => {let dom = document.querySelector('.btn-test')console.log('data-flag', dom.getAttribute('data-flag')) // 0}, 1000)setTimeout(() => {let dom = document.querySelector('.btn-test')console.log('data-flag', dom.getAttribute('data-flag')) // 1}, 2600)})},permitTicket() {console.log('点击了工作许可')},}
}
</script>
解决办法:给两个使用v-if的标签添加不同的key值,如:
<template><view><!-- 待接受 --><button v-if="stateNo == '10'" class="btn-test" @click="ticketReceive" v-throttle key="1">接受工作票</button><!-- 待许可 --><button v-if="stateNo == '12'" class="btn-test" @click="permitTicket" v-throttle key="2">工作许可</button> </view>
</template>
‘0’) // 只是为了打印
setTimeout(() => {
if (el && el.style) {
el.style.pointerEvents = ‘auto’
el.style.color = ‘#000’
el.setAttribute(‘data-flag’, ‘1’) // 只是为了打印
}
}, throttleTime)
}
}, false)
}
})
通过以下代码可以发现点击完接受工作票按钮后不到2秒的时间内由于vue的就地复用策略导致工作许可按钮复用了原来的元素,继承了之前的样式和自定义属性,从而导致工作许可按钮在上述2s内的点击不会生效,且颜色也发生了短暂的改变。(利用项目加浏览器调试来演示说明)```jsx
<template><view><!-- 待接收 --><button v-if="stateNo == '10'" class="btn-test" @click="ticketReceive" v-throttle>接收工作票</button><!-- 待许可 --><button v-if="stateNo == '12'" class="btn-test" @click="permitTicket" v-throttle>工作许可</button> </view>
</template>
<script>
export default{data() {return {stateNo: '10'}},methods: {// 接收工作票ticketReceive($event) {console.log('$event', $event)this.stateNo = '12'this.$nextTick(() => {console.log('视图已完成更新', '工作许可按钮已显示')setTimeout(() => {let dom = document.querySelector('.btn-test')console.log('data-flag', dom.getAttribute('data-flag')) // 0}, 1000)setTimeout(() => {let dom = document.querySelector('.btn-test')console.log('data-flag', dom.getAttribute('data-flag')) // 1}, 2600)})},permitTicket() {console.log('点击了工作许可')},}
}
</script>
解决办法:给两个使用v-if的标签添加不同的key值,如:
<template><view><!-- 待接受 --><button v-if="stateNo == '10'" class="btn-test" @click="ticketReceive" v-throttle key="1">接受工作票</button><!-- 待许可 --><button v-if="stateNo == '12'" class="btn-test" @click="permitTicket" v-throttle key="2">工作许可</button> </view>
</template>
相关文章:

一个好的前端开发人员必须掌握的前端代码整洁与开发技巧
前端代码整洁与开发技巧 为保证前端人员在团队项目开发过程中的规范化、统一化,特建立《前端代码整洁与开发技巧》文档,通过代码简洁推荐、开发技巧推荐等章节来帮助我们统一代码规范和编码风格,从而提升项目的可读性和可维护性。 目录 …...

【别再困扰于LeetCode接雨水问题了 | 从暴力法=>动态规划=>单调栈】
🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...

酒厂酒业IP网络广播系统建设方案-基于局域网的新一代交互智慧酒厂酒业IP广播设计指南
酒厂酒业IP网络广播系统建设方案-基于局域网的新一代交互智酒业酒厂IP广播系统设计指南 由北京海特伟业任洪卓发布于2023年4月25日 一、酒厂酒业IP网络广播系统建设需求 随着中国经济的快速稳步发展,中国白酒行业也迎来了黄金时期,产品规模、销售业绩等…...

OpenHarmony JS Demo开发讲解
项目结构 打开entry→src→main→js,工程的开发目录如图所示 其中, i18n文件夹:用于存放配置不同语言场景的资源,比如应用文本词条,图片路径等资源。en-US.json文件定义了在英文模式下页面显示的变量内容,…...

CentOS系统安装Intel E810 25G网卡驱动
因特尔网卡驱动给的都是二进制包,需要编译环境。 首先去Intel下载最新的驱动 E810驱动下载:https://www.intel.com/content/www/us/en/download/19630/intel-network-adapter-driver-for-e810-series-devices-under-linux.html?wapkwe810 里面有三个驱…...

Java经典的String面试题
Java经典的Spring面试题 String是基本数据类型吗? String你是基本数据类型String是可变的话? String是final类型的,不可变怎么比较两个字符串的值一样,怎么比较两个字符串是否同一对象? 比较字符串的值是否相同用equa…...

c# 结构体与类区别
在 C# 中,结构体(struct)和类(class)都是用户自定义类型,它们具有一些共同的特性,比如可以定义字段、属性、方法等。但它们也有一些区别。 下面是一些结构体和类的区别: 定义方式不…...

使用 patch 命令打补丁
之前的这篇文章 git 导出差异 diff 文件 写了导出 diff 、patch 文件。 拿到 patch 文件,用 patch 命令可以快速的把修改内容合入,合入后在 git 上是已修改的状态,如需提交还要 add 、commit 。 patch 语法 patch --help 可以看到 Usage:…...

C++——类和对象[上]
目录 1.初识面向对象 2.类的引入 3.类的定义 4.成员变量的命名规则 5.类的实例化 6.类对象模型 7.this指针 1.初识面向对象 C语言是一门面向过程的语言,它关注的是完成任务所需要的过程;C是一门面向对象的语言,将一个任务分为多个对…...

MySQL日志
目录 一 关于mysql的设计和运行逻辑 二 MySQL的三类日志 三 对于日志的利用 插入查询 1 备份 2 删除重复数据 一 关于mysql的设计和运行逻辑 mysql在启动的时候非常占空间,需要申请很大的空间,但是有时候内存并没有那么多,所以OS会把my…...

TinyURL 的加密与解密、猜数字游戏、 Fizz Buzz、相对名次----2023/4/28
TinyURL 的加密与解密----2023/4/28 TinyURL 是一种 URL 简化服务, 比如:当你输入一个 URL https://leetcode.com/problems/design-tinyurl 时,它将返回一个简化的URL http://tinyurl.com/4e9iAk 。请你设计一个类来加密与解密 TinyURL 。 加…...

Spring boot结合SkyWalking-Trace工具类实现日志打印请求链路traceid
背景: 随着业务的复杂化、解耦化,运维人员和开发人员需要对请求链路跟踪来快速发现和定位问题,基于应用已经集成了SkyWalking的前提下,如何通过获取SkyWalking生成的统一traceId并加入打印日志中,方便开发人员能够根据…...

精通ES=ElasticSearch
Elasticsearch 是一个分布式、高扩展、高实时的搜索与 数据分析引擎。它能很方便的使大量数据具有搜索、分析和探索的能力。充分利用Elasticsearch的水平 伸缩性,能使数据在 生产环境变得更有价值。Elasticsearch 的实现原理主要分为以下几个步骤,首先用…...

RabbitMQ-扇形交换机(Fanout )
扇形交换机:Fanout Exchange扇形交换机是最基本的交换机类型,它所能做的事情非常简单———广播消息。扇形交换机会把能接收到的消息全部发送给绑定在自己身上的队列。因为广播不需要“思考”,所以扇形交换机处理消息的速度也是所有的交换机类…...

Python 学习曲线 从 Python 新手到 Pro
Python 学习曲线 从 Python新手到 Pro 使用代码片段介绍: Python 是世界上最通用和使用最广泛的编程语言之一,以其简单性、可读性和多功能性而闻名。 在本文中,我们将探讨一系列示例场景,其中代码由具有三个不同专业知识水平的程序…...

薪资18K需要什么水平?来看看98年测试工程师的面试全过程…
我的情况 大概介绍一下个人情况,男,本科,三年多测试工作经验,懂python,会写脚本,会selenium,会性能,然而到今天都没有收到一份offer!从年后就开始准备简历,年…...

基于趋动云的 Stable Diffusion Webui 环境搭建
Stable Diffusion Webui 环境搭建,首先新建一个项目: 然后,选择镜像。注意点公开的,已近做好的这个镜像,superx创建,集成了miniconda3的镜像。 然后选择添加数据源,一样,还是点公开&…...

备忘录设计模式解读
目录 问题引进 游戏角色状态恢复问题 传统方案解决游戏角色恢复 传统的方式的问题分析 备忘录模式基本介绍 基本介绍 备忘录模式的原理类图 对原理类图的说明 游戏角色恢复状态实例 应用实例要求 思路分析和图解(类图) 代码实战 备忘录模式的注意事项和细节 问题引…...

股票期货模拟交易有用吗?股票期货模拟交易心得
股票期货市场为了满足新用户的需求,有专门的股票期货模拟交易平台,大家可以在这个平台上进行股票期货的模拟交易,这样可以通过不断总结,丰富我们的知识。下面整理的股票期货模拟交易实验心得,从股票期货模拟交易与实盘…...

2023年五月份图形化三级打卡试题
活动时间 从2023年5月1日至5月21日,每天一道编程题。 本次打卡的规则如下: 小朋友每天利用10~15分钟做一道编程题,遇到问题就来群内讨论,我来给大家答疑。 小朋友做完题目后,截图到朋友圈打卡并把打卡的截图发到活动群…...

【华为OD机试真题】字母组合(javapython)100%通过率 详细代码注释
字母组合 知识点回溯 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 每个数字对应多个字母,对应关系如下: 0: a,b,c 1: d,e,f 2: g,hi 3: j,k,l 4: m,n,o 5: p,q,r 6: s,t 7:u,v 8: w,x 9: y,z 输入一串数字后,通过数字和字母的对应关系可以得到多个字母字符串 (要…...

精彩!openEuler 社区年度顶级会议发生了啥?
2023年4月20-21日,万涓汇流,奔涌向前,openEuler Developer Day2023(以下简称“ODD2023”)在上海以线上线下的方式圆满举办。 本次大会由开放原子开源基金会指导,中国软件行业协会、openEuler社区、边缘计算产业联盟共同主办&#…...

Confidential Containers发布0.5.0版本,龙蜥将基于八大特性构建开箱即用的机密容器解决方案
文/段勇帅 01 前言 机密容器(Confidential Containers,简称CoCo)是 Cloud Native Computing Foundation(CNCF)Sandbox 项目。目前机密容器项目的核心参与者包括阿里云、AMD、ARM、IBM、Intel、Microsoft、Red Hat、R…...

独立储能的现货电能量与调频辅助服务市场出清协调机制(Matlab代码实现)
💥 💥 💞 💞 欢迎来到本博客 ❤️ ❤️ 💥 💥 🏆 博主优势: 🌞 🌞 🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 …...

使用 Luckysheet 可实现 Web 的 Excel
一、写在前面 工作中会遇到excel的导入和导出,换个角度看,假如有个 web 版本的excel ,且能上传现有的,修改编辑后再下载也是个不错的方案。 Luckysheet 是实现 web版Excel的一个优秀的框架。 Luckysheet ,一款纯前端类…...

时间序列预测(一)基于Prophet的销售额预测
时间序列预测(一)基于Prophet的销售额预测 小O:小H,有没有什么方法能快速的预测下未来的销售额啊 小H:Facebook曾经开源了一款时间序列预测算法fbprophet,简单又快速~ 传统的时间序列算法很多&a…...

【电科复试第一名】23上交819考研经验分享
笔者来自通信考研小马哥23上交819全程班学员 819,上岸经验贴,知无不言 初试第十一,复试第一,总分第七(与第六同分) 考研经历:本科就读与湖南某末985,大学时间没好好学习,天天打王者,玩steam上…...

每日学术速递4.24
CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Collaborative Diffusion for Multi-Modal Face Generation and Editing(CVPR 2023) 标题:多模态人脸生成和编辑的协同扩散 作者:Ziqi Huang, Kelvin C.K. …...

怎么把mkv文件转成mp4格式,3招立马处理
怎么把mkv文件转成mp4格式的方法你知道吗?我想很多朋友会遇到这样的情况,下载视频后发现无法打开。原来我们下载的视频格式是mkv,也许这个格式大家不是很熟悉的。那么今天就来认识一下,mkv是Matroska的一种媒体文件,mk…...

SEO机制算是让我玩明白了
获取当前时间时间戳,返回遵循ISO 8601扩展格式的日期 new Date(Date.now()).toISOString() 使用moment库转换回来 this.moment(new Date(Date.now()).toISOString()).format("YYYY-MM-DD") js去掉富文本中html标签和图片 filterHtmlTag(val) {if(!val){…...