总结一下使用JavaScript和Vue一些简化代码的功能

1、前言

如今也算上暂时闲下来了。算算已经很久没写文章了。这篇文章记录下我使用的一些前端小功能吧。若有错误,请及时指出。javascript

2、vue数字滚动小组件

开发中要用到数字上下滚动的组件。在github上找了找这种功能。找到了一个vue-digitroll。周末花了一下午的时间研究了vue-digitroll的源码,很不错。vue-digitroll并且还作了浏览器兼容。最终没有用在项目里,缘由有三点:php

  1. 项目中暂时不须要考虑这种兼容性。
  2. 项目中也不须要这么多的功能。
  3. vue-digitroll虽然很轻量,但毕竟也要安装。安装了就要多少占点体积。

基于上面三点考虑,我就参考了源码实现,本身写了一个简单的,易于理解的小组件。css

大概原理就是数字转为字符串,数字定高,宽度是本身的宽度。循环0到9,超出就往下排。经过overflow:hidden隐藏超出的数字。经过传入的数字找到对应数字的高度位置。translateY实现滚动效果。html

下面就贴出来源码:前端

<template>
  <div class="roll-wrap" :style="{fontSize:`${cellHeight}px` }">
    <ul class="roll-box">
      <li
        class="roll-item"
        v-for="(item, index) in numberArr"
        :key="index"
        :style="{ height: `${cellHeight}px`,lineHeight:`${cellHeight}px`}"
      >

        <!--小数点或其余状况-->
        <div v-if="isNaN(parseFloat(item))">{{ item }}</div>
        <div v-else :style="getStyles(index)">
          <!--数字0到9-->
          <div
            :style="{ height: `${cellHeight}px`,lineHeight:`${cellHeight}px`}"
            v-for="(subItem,subIndex) in oneToNineArr"
            :key="subIndex"
          >
{{ subItem }}</div>
        </div>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  props: {
    // 高度,默认30
    cellHeight: {
      typeNumber,
      default30
    },
    // 须要传入的滚动数字
    rollNumber: {
      type: [StringNumber],
      default0
    },
    // 滚动持续时间,单位ms.默认1.5s
    dur: {
      typeNumber,
      default1500
    },
    // 缓动函数,默认ease
    easeFn: {
      typeString,
      default'ease'
    }
  },
  data () {
    const { rollNumber } = this
    return {
      // 传入的数字
      number: `${rollNumber}`,
      // 传入的数字解析为数组
      numberArr: [],
      // 偏移量
      numberOffsetArr: [],
      // 0到9数组
      oneToNineArr: [0123456789]
    }
  },
  created () {
    this.numberArr = this.number.split('')
    this.resetState(this.numberArr.length)
  },
  watch: {
    rollNumber (value, oldVal) {
      this.number = `${value}`
      this.numberArr = `${value}`.split('')
      this.resetState(this.numberArr.length)
    }
  },
  methods: {
    resetState (len) {
      const newArr = new Array(len).join(',').split(',')
      this.numberOffsetArr = newArr.map(() => 0)
      // 延迟执行动画
      setTimeout(() => {
        // 设置传入的数字下标对应偏移量,从新赋值
        this.numberArr.forEach((num, i) => {
          this.$set(this.numberOffsetArr, i, num * this.cellHeight)
        })
      }, 30)
    },
    getStyles (index) {
      const style = { transition`${this.easeFn} ${this.dur}ms`transform`translate(0%, -${this.numberOffsetArr[index]}px)` }
      return style
    }
  }
}
</script>
<style lang="stylus" scoped>
.roll-wrap
  ul.roll-box
    display flex
    padding 0
    margin 0
    text-align center
    overflow hidden
    li
      overflow hidden
</style>
复制代码

使用方式也很简单,以下:vue

 <number-roll :roll-number="9999" />
复制代码

3、前端JS计算丢失精度问题

具体参考 JavaScript 浮点数陷阱及解法number-precision 这篇文章和number-precision开源库。java

我也看了看源码,进行了一些测试,摘出来了一些,下面就贴一下我摘出来的源码:node

/**
 * 解决浮点运算问题,避免小数点后产生多位数和计算精度损失。
 */

export default {

  /**
   * 返回数字长度
   * @param {*number} num Input number
   */

  digitLength (num) {
    const len = (num.toString().split('.')[1] || '').length
    return len > 0 ? len : 0
  },
  /**
   * 把小数转成整数,若是是小数则放大成整数
   * @param {*number} num 输入数
   */

  float2Fixed (num) {
    return Number(num.toString().replace('.'''))
  },
  /**
   * 精确加法
   * plus(0.1, 0.2) // = 0.3, not 0.30000000000000004
   */

  plus (num1, num2) {
    const baseNum = Math.pow(10Math.max(this.digitLength(num1), this.digitLength(num2)))
    return (num1 * baseNum + num2 * baseNum) / baseNum
  },
  /**
   * 精确减法
   * minus(1.0, 0.9) // = 0.1, not 0.09999999999999998
   */

  minus (num1, num2) {
    const baseNum = Math.pow(10Math.max(this.digitLength(num1), this.digitLength(num2)))
    return (num1 * baseNum - num2 * baseNum) / baseNum
  },

  /**
   * 精确乘法
   * times(3, 0.3) // = 0.9, not 0.8999999999999999
   */

  times (num1, num2) {
    const num1Changed = this.float2Fixed(num1)
    const num2Changed = this.float2Fixed(num2)
    const baseNum = this.digitLength(num1) + this.digitLength(num2)
    const leftValue = num1Changed * num2Changed
    return leftValue / Math.pow(10, baseNum)
  },

  /**
   * 精确除法
   * divide(1.21, 1.1) // = 1.1, not 1.0999999999999999
   */

  divide (num1, num2) {
    const num1Changed = this.float2Fixed(num1)
    const num2Changed = this.float2Fixed(num2)
    return (num1Changed / num2Changed) * Math.pow(10this.digitLength(num2) - this.digitLength(num1))
  },
  /**
   * 四舍五入
   * round(0.105, 2); // = 0.11, not 0.1
   */

  round (num, ratio) {
    const base = Math.pow(10, ratio)
    return this.divide(Math.round(this.times(num, base)), base)
  }
}
复制代码

4、async await 简化代码

由于项目里使用axios进行了全局异常处理的提示,不需特殊处理的状况下,没有必要进行try{}catch{}代码块包装了。由于大多数按钮提交的时候要增长loading,就可使用fianlly如下方式简化代码。ios

this.submitLoading = true
if(this.submitLoading) return
const res = await submitForm({name:'zhangsan',age:'20'}).finally(() => { this.submitLoading = false })
复制代码

5、使用element的scroll-bar组件

不少开源库中都使用了element<el-scrollbar/>组件。这个组件真的好用,若是你有定高可是须要显示滚动条实现滚动的需求。就能够很简单的实现好看的滚动条。好比以下面的代码:git

 <el-scrollbar style="height: 300px;">
  <el-tree
    :data="data"
  />

</el-scrollbar>
复制代码

6、封装一些简单的搜索小组件

列表的搜索功能是必备的。在使用库的时候避免大量引入组件的标签,封装一些不那么复杂的搜索小组件,使用起来很方便。好比下面的代码:

<template>
  <el-row type="flex" align="middle">
    <el-col :span="24">
      <el-form @keyup.enter.native="querySearch()" @submit.native.prevent class="flex-center" :inline="inline">
        <el-form-item v-for="(item,index) in formArr" :key="index" :label="item.label">
          <el-select v-if="item.tagName === 'select'" v-model="item.value" placeholder="请选择">
            <el-option v-for="item in item.options || []" :key="item.value" :value="item.value" :label="item.label" />
          </el-select>
          <el-input v-else v-model="item.value" :placeholder="item.placeholder"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button icon="el-icon-search" type="warning" @click="querySearch()">查询</el-button>
        </el-form-item>
      </el-form>
    </el-col>
  </el-row>
</template>
<script>
import { deepCopy } from 'utils/utils'
export default {
  props: {
    searchColumns: {
      typeArray,
      default () {
        return []
      }
    },
    inline: {
      typeBoolean,
      defaulttrue
    }
  },
  data () {
    return {
      formArr: []
    }
  },
  methods: {
    querySearch () {
      const obj = {}
      this.formArr.forEach(el => {
        obj[el.prop] = el.value
      })
      this.$emit('query-search', obj)
    }
  },
  watch: {
    searchColumns: {
      handler (val) {
        this.formArr = deepCopy(this.searchColumns)
      },
      deeptrue,
      immediatetrue
    }
  }
}
</script>
复制代码

使用起来也很简单:

<simple-search :searchColumns="searchColumns" @query-search="querySearch" />
export default {
  data () {
    return {
      // 搜索条件
      condition: {}
      // 搜索列
      searchColumns: [
        {
          label: '名称',
          prop: 'name',
          value: '',
          placeholder: '请输入名称'
        }
    }
  },
  methods: {
    querySearch (queryForm) {
      this.condition = queryForm
      this.getList()
    }
  }
}
复制代码

7、使用Vue的mixins简化代码

mixins真是个好东西,善于使用mixins能够简化很多代码。加快Vue项目的开发速度这篇文章挺不错。可是mixins不能滥用,不要在全局中使用。

由于大部分后台列表页面都要请求列表,都要分页,加载loading等,咱们没有必要在每一个Vue组件下面都写这些属性。下面是我用mixins的实现了一些简化这些代码的功能(基于ElementUI)。若是每次切换路由的时候,须要记住当前用户离开这个列表页面以前的页码,可使用localStorage来存储页码。

/*
 * 分页mixins
 */

export default {
  data () {
    return {
      // 分页
      pagination: {
        // 当前页
        page: 1,
        // 页长
        size: 10,
        // 总个数
        total: 0,
        // 分页布局
        layout: 'prev,pager,next,total,jumper'
      },
      // 增,删,改按钮loading
      load: {
        addLoading: false,
        deleteLoading: false,
        editLoading: false
      },
      // 列表loading
      listLoading: true
    }
  },
  created () {
    if (!(Object.prototype.toString.call(this.getList) === '[object Function]')) {
      throw new Error('请在组件内定义getList方法加载数据!')
    }
  },
  methods: {
    // 改变页码handle
    pageChange (val) {
      this.pagination.page = val
      this.getList()
    },
    // 移除一条数据后从新获取列表数据
    getListForDelSingle (list = [], index = 0) {
      list.splice(index, 1)
      // 若是当前页无数据
      if (list.length <= 0) {
        this.pagination.page--
        if (this.pagination.page <= 0) {
          this.pagination.page = 1
        }
      }
      this.getList()
    },
    // 移除多条数据后从新获取列表数据
    getListForDeltMany (delLen, listLen) {
      if (!delLen || !listLen) return
      if (delLen >= listLen) {
        this.pagination.page--
        if (this.pagination.page <= 0) {
          this.pagination.page = 1
        }
      }
      this.getList()
    }
  }
}
复制代码

8、一些简单的工具方法

若是说项目中没有还安装lodash的话,均可以加如下的,很轻量,很好用。可以节省不少时间。还有就是好多开源库的工具方法都很是棒,好比说Element,iview,ant-design,vant等均可以参考学习或者在项目中直接拿来用。

  • 一些form表单对象有不少时候须要初始化,若是手写代码一行一行的修改的话,代码会很是冗余。若是说form表单不是很复杂的话,就能够用下面这种方式实现表单初始化效果:
this.userForm.name = ''
this.userForm.pwd = ''
复制代码
function initForm (form = {}, init = { num: 0, str: '' }{
  const newForm = {}
  const toString = Object.prototype.toString
  for (const [key, value] of Object.entries(form)) 
{
    if (toString.call(value) === '[object String]') {
      newForm[key] = init.str
    } else if (toString.call(value) === '[object Number]') {
      newForm[key] = init.num
    } else if (toString.call(value) === '[object Array]') {
      newForm[key] = []
    } else if (toString.call(value) === '[object Object]') {
      newForm[key] = {}
    }
  }
  return newForm
}
复制代码
  • 树结构转为一维数组
function getFlattenDeepList (nodes = []) {
  let list = []
  nodes.forEach(item => {
    list.push(item)
    if (item.children && item.children.length) {
      const tempList = getFlattenDeepList(item.children)
      list = [...list, ...tempList]
    }
  })
  return list
}
复制代码
  • 根据最子项ID获取全部对应的树级父级ID
function getParentIdListByLeafId (leafId, nodes = [], newNodes = []{
  if (!leafId) return []
  for (let i = 0, len = nodes.length; i < len; i++) {
    const tempList = newNodes.concat()
    tempList.push(nodes[i].id)
    // 找到匹配返回结果
    if (leafId === nodes[i].id) {
      return tempList
    }
    if (nodes[i].children && nodes[i].children.length) {
      const result = getParentIdListByLeafId(leafId, nodes[i].children, tempList)
      if (result) return result
    }
  }
}
复制代码
  • 一维数组转树状结构
let arr = [
  { id1pid''name'1AA' },
  { id2pid''name'2AA' },
  { id3pid''name'3AA' },
  { id4pid1name'4AA' },
  { id5pid2name'5AA' },
  { id6pid3name'6AA' },
  { id7pid4name'7AA' },
  { id8pid1name'8AA' },
  { id9pid5name'9AA' }
]
const newArr = []
arr.forEach(el => {
  el.children = []
  if (!el.pid) {
    newArr.push(el)
  } else {
    const parent = arr.find(_ => _.id === el.pid)
    parent.children.push(el)
  }
})
复制代码

9、关于正则表达式

正则表达式自己是很复杂的(其实我也不是很懂…),关于须要正则表达式来验证的功能。若是项目时间比较紧,拿一些比较严谨的开源库里的正则直接用是能够的。推荐铁皮铁皮饭盒老师正则大全这个库,几千个star。应该是通过很严谨的验证的,不要在经过网上随便搜出来的正则拿来直接用,我始终感受不是正确的。不过正则确实是应该抽出一大段时间好好学的。

10、使用Vue语法糖

使用好Vue的语法糖(v-model,v-on,v-bind)也能够简化代码的编写。好比说要封装<el-select/>组件修改样式或进行特殊定制。就能够像下面代码这样(参考自Vue-Element-Admin):

<template>
  <el-select ref="dragSelect" v-model="selectVal" v-bind="$attrs"  v-on="$listeners" multiple">
    <slot />
  </el-select>
</template>

<script>
import Sortable from 'sortablejs'
export default {
  name'DragSelect',
  props: {
    value: {
      typeArray,
      requiredtrue
    }
  },
  computed: {
    selectVal: {
      get() {
        return [...this.value]
      },
      set(val) {
        this.$emit('input', [...val])
      }
    }
  }
}
</script>
复制代码
<el-drag-select v-model="value" style="width:500px;" multiple placeholder="请选择">
  <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-drag-select>

<script>
import ElDragSelect from '@/components/DragSelect' // base on element-ui
export default {
  name'DragSelectDemo',
  components: { ElDragSelect },
  data() {
    return {
      value: ['Apple'],
      options: [{
        value'Apple',
        label'Apple'
      }]
    }
  }
}
</script>
复制代码

11、关于Vue生命周期

每一个 Vue 实例在被建立以前都要通过一系列的初始化过程。例如须要设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新DOM等。其实理解了初始化顺序,就能够知道在钩子函数里该作什么事情了。这里参考Vue生命周期

12、关于文章排版

文章排版使用MD2All。使用起来很简单,把在掘金上写的文字和代码复制到左侧黑色区域,而后点击一键排版,而后点保存,复制带样式的文字和代码后,在粘贴回来就能够了。注意如下,最好本身原来的文章留个备份,否则生成的不少样式代码没法理解。

有一个坑须要注意如下当使用``来解析代码的时候,注意后面不要加html或者js了。否则MD2All`解析不友好。

相关文章
相关标签/搜索