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 = +type let 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 } = this if(ticketCode === '05') { if(isDoubleSign) { this.signType = '02' } } }
简写:
function submitTicket(){ const { ticketCode, isDoubleSign } = this if(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 赋值给 b a 没有传值,使用默认值 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_test console.log(this.$options.age); // 18 this.$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() + 1 let 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.filterTime let 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>