突破常规的前端技巧与方法(二)

简介: 突破常规的前端技巧与方法(二)

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对象。可以通过vbind="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>


目录
相关文章
|
23天前
|
存储 前端开发 JavaScript
前端的全栈之路Meteor篇(四):RPC方法注册及调用-更轻量的服务接口提供方式
RPC机制通过前后端的`callAsync`方法实现了高效的数据交互。后端通过`Meteor.methods()`注册方法,支持异步操作;前端使用`callAsync`调用后端方法,代码更简洁、易读。本文详细介绍了Methods注册机制、异步支持及最佳实践。
|
2月前
|
前端开发 JavaScript
前端基础(九)_this基本使用、this指向判断、改变this指向的方法
本文介绍了JavaScript中this的基本使用、this指向的判断以及改变this指向的方法。
44 1
前端基础(九)_this基本使用、this指向判断、改变this指向的方法
|
2月前
|
前端开发
前端基础(十四)_隐藏元素的方法
本文介绍了几种在前端开发中隐藏元素的方法,包括使用`display:none`、`visibility:hidden`、`opacity:0`等CSS属性,并提供了相应的示例代码。此外,还提到了其他隐藏元素的技巧,如通过设置元素位置、使用`overflow`属性和`filter`属性以及`rgba`颜色值来实现元素的隐藏。
65 1
前端基础(十四)_隐藏元素的方法
|
1月前
|
前端开发 JavaScript
掌握微前端架构:构建现代Web应用的新方法
本文介绍了微前端架构的概念及其在现代Web应用开发中的优势与实施方法。微前端架构通过将应用拆分成独立模块,提升了开发效率和灵活性。其核心优势包括技术栈灵活性、独立部署、团队协作及易于维护。文章详细阐述了定义边界、选择框架、管理状态和通信等关键步骤,并讨论了状态同步、样式隔离及安全性等挑战。微前端架构有望成为未来Web开发的重要趋势。
|
30天前
|
JavaScript 前端开发 应用服务中间件
vue前端开发中,通过vue.config.js配置和nginx配置,实现多个入口文件的实现方法
vue前端开发中,通过vue.config.js配置和nginx配置,实现多个入口文件的实现方法
137 0
|
30天前
|
存储 前端开发 API
前端开发中,Web Storage的存储数据的方法localstorage和sessionStorage的使用及区别
前端开发中,Web Storage的存储数据的方法localstorage和sessionStorage的使用及区别
90 0
|
2月前
|
前端开发
前端基础(十一)_Float浮动、清除浮动的几种方法
本文介绍了浮动的概念、属性、特性以及清除浮动的几种方法,并通过实例演示了如何使用CSS实现元素的浮动和处理浮动带来的问题。
78 3
|
1月前
|
前端开发
前端常用方法防抖(debounce)和节流(throttle)的示例演示及应用场景说明
前端常用方法防抖(debounce)和节流(throttle)的示例演示及应用场景说明
23 0
|
2月前
|
前端开发 JavaScript
前端ES5 | js —添加元素方法
前端ES5 | js —添加元素方法
|
3月前
|
JavaScript 前端开发 开发者
JS 继承之谜:究竟有哪些神秘方法?Web 前端开发者必知的关键技巧待你揭开谜底!
【8月更文挑战第23天】JavaScript (JS) 是 Web 前端开发的关键语言,其中继承是面向对象编程的重要概念。本文探讨了 JS 中几种继承机制:原型链继承、构造函数继承及组合继承。原型链继承利用原型对象实现属性和方法的共享;构造函数继承通过在子类构造器内调用父类构造器实现私有属性的复制;组合继承结合两者优点,既支持属性共享又避免了属性被意外覆盖的风险。理解这些模式有助于开发者更高效地组织代码结构,提升程序质量。
34 1