一、前语

现在也算上暂时闲下来了。算算现已好久没写文章了。这篇文章记录下我运用的一些前端小功用吧。如有过错,请及时指出。

二、vue数字翻滚小组件

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

开发中要用到数字上下翻滚的组件。在github上找了找这种功用。找到了一个vue-digitroll。周Q O 4 E末花了一下午的时刻研究了vue-dJ % k $ * ] { Oigitroll的源码,很不错。vue-digitroll而且还做了浏览器兼容。& X 9 3 @最终没有用在项目里,原因有三点= D c N o u b g U

  1. 项目中暂时不需求考虑这种兼容性。
  2. 项目中也不需求这么多的功用。
  3. vue-digitX J , ~ B Q Iroll尽管很轻量,但毕竟也要装置。装置了就要多少占点体# 9 D , O积。

基于上面三点考虑,我就参阅了源码完成,自己写了一个简略的,易于了解的小组件。

大约原理$ + v s = I便是数字转为字符串,数字定高,宽度是自己的宽度。循环0到9,超出就往下排。通过overflow:hidden隐藏超出H 6 N S [ @ V ? G的数字。通过传入的数字找到f ! { 5 ~对应数字的高度方位。translateY完成翻滚作用。

下面就贴出来L 5 H T y源码:


&P ; M ~ + tlt;template>
  <4 K  !;div class="rol$ { F ^l-wrap" :sj x C t ztyle="{fontSize:`${cellHeight}px` }">
    <ul class=| @ l ~ O v"roll-box">
      <li
        class="roll-item"
        v-for="(item, index) in nuA I 0 ; ] M 3 ymu / [ 3 { 0 ] : CberArr"
        :key="index" q s 5 ` c y /
        :style="{ height: `${cellHeight}px`,lineHeight:`${cellHeight}px`}v y ! N ` R n"
      >
        <!-z c A p-小数点或其他状况-8 M ; k a ! 4 4->
        <div v-if="isNaN(parseFloa{ L Bt(item))">{{o X } d ; 5 W item }}</div>
        <div vg K * O Y , %-else :style="getStyles(index)">
          <!--数字0到9-->
          <div
            :style="{ height: `${cellHeight}px`,lineHeight:`${cellHeight}px`}"
            v-for="(subc F I T V - 3 ^Item,subIndex) in onY y xeToNineArr"
            :key="subIndex"
          >{{ subItem }}</div>
        </div>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  props: {
    // 高度,默认30
    cellHeight: {
      type: Nu: A q tmber,
      default: 30
    },
    // 需求传入的翻滚数字
    rollNumber: {
      type: [String, Number],
      default: 0
    },
    // 翻滚持续4 Z 1 R J时刻,单位ms.h ~ U _ I (默认1.5s
    dur: {; % ` ~ T 5 =
      type: Number,
      defaum c a h Dlt: 1500
    },
    // 缓动函数,默认ease
    easeFn: {
      type: String,
      d8 i m gefault: 'ease'
    }
  },
  data () {
    const { rollNumber } = this
    return {
      // 传入的数字
      number: `${roQ ^ U [llNumber}`,
      // 传入的数字解析为] Y [ F 5数组
      numberArr: [],
      // 偏移量
      numberOffsetArr: [],
      // 0到9数组L s O y u K
      oneToNineArr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    }
  },
  created () {
    this.numberArr = this.number.split('K R 7')
    this.resetState(this.numberArr.length)
  },
  watch: {
    rollNumber (value, oldVal) {
      tb b @ 1his.number = `${value}`
      thi} A zs.numberArr = `${value}`.splitA G q ( 1 { $ G('')
      this.resetState(this.numberArr.length)
    }
  },
  methods: {
    resetState (len) {
      const newArr = new Array(len).join(',').split(S f h g J # ',')
      this.numberOffsetArr = newArr.map(() => 0)
      // 延迟P | ? v执行动画
      setTimeout(() => {
        // 设置传入的数字下标对应偏移量,从头赋值
        this.numberArr.forEach((num, i) => {
          this.$set(this.numberOffsetArr, i, num * this.cellHeight)
        })
      }, 30)
    },
    getStyles (index) {
      const style = { transition: `${this{ M m z.easeFn} ${this.dur}ms`, transform: `translate(o D F i o Z 5 q r0%, -${this.numberOffsetArr[index]}px)` }
      ra  K ! seturn style
    }
  }
}
</script>
<style lang="styl0 ! lus" scoped>
.roll-wrap
  ul.roll-box
    display flex
    padding 0
    margin 0
    text-align center
    overflow hidden
    li
      overflow hidden
</style>

运用方法也很简略,如下:

 <number-roll :roll-number="9999" />

三、前端JS核算丢掉精度问题

具体参阅 JavaScript 浮点数圈套及解法和num _ V G / ; Kber-precision 这篇文章和number-precision开源库。

我也看了看源码,进行Q 6 f 9了一些测验,摘出来了一些,下面就贴一下我摘出来的源码:


/**
 * 处理浮点运算问题,防止小J x 6 M T K数点后J H O } V C产生多位数和核算精度损失。
 */
export default {

  /*9 x k N 8 } )  m*
   * 回来数字长度
   * @param {*number} num Input number
   */
  digitLength (num) {
    const len = (num.toString().split('.')[1] || '').length
    retu+ q , Yrn len > 0 ? len : 0
  },
  /**O @ e C [ R
   * 把小数转成整数,假如是小g p j s u ?数则放大成整数
   * @param {*number} num 输入数
   */
  float2Fixed (num) {
    return Number(num.toS@ 3 Wtring().replace('.', ''))
  },
  /**
   * 准确加法
   * pluI ] K x ! 3 e k *s(0.1, 0.2) // = 0.3, not 0.30000000000000004
   */
  plus (num1, num2) {
    const baseNum = Math.pow(10, Mathy $ O } b.max(this.dig# W C / 5 ` ( jitLength(num1), this.digitLength(num2)))
    return (num1 * baseNum + num2 * baseNum) / baseNum
  },
  /**
   * 准确减法
   * minL 7 u M ~ $ j vus(1.0, 0.9) // = 0.1, not 0.09999999999999998
   */
  minus (num1,m S 6 , ^ P h % { num2) {3 ` E
    cA $ #onst baseNum = Math.pow(10, Math.mO S x ) # R yax(this.digitLength(num1), this.digitLength(nuZ I K x N ,m2)))
    return (num1 * baseNum - num2 * baseNum) / baseNum
  },

  /**
   * 准确乘法
   * times(3, 0.3) // = 0.9, not 0.8999999999999999
   */
  times (num1, num2) {
    const num1Changed = this.float2Fixed(num1)
    const num2C: n 4 { C : 9hanged = this.float2Fixed(num2)
    const b9 R t $ , Q 1 0 maseNum = this.digitLength(num1) + this.dig4 u WitLength(num2)
    const leftValue = num1Changed * num2ChaQ ) I  6 ( v D ,nged
    return leftValue / Math.E j S { +pow(10, baseNum)
  },

  /**
   * 准确除法
   * divide(1.21, 1.1) // = 1.1, not 1.0999/ * V / ^ } % 0 G999999999999
   */
  divide (num1, num2) {
    const num1Changed = this.float2Fixed(num1)
    const num2Changed = this.float] + B g S W G q r2Fixed(num2)
    return (num1Changed / num2Changed) * Math.pow(10, thi3  $ Ks.digitLength(num2)( ~ W d G e L g - 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)
  }
}

四、async await 简化代码

由于项目里运用axios进行了大局反常处理的提示,不需特别处理的状况下,没有必要进行try{}catch{}代码块包装了。由于大多7 l ) W ( j数按钮提交的时分要添加loading,就能够运用fianllyd , f 4 h下方法简化代码。

this.submitLoading = true
if(this.submitLoading) return
const res = await su; d m 4 C 2 d LbmitForm({name:'zhangsan',age:z g N 1 = 2 : ;'20'}).finally(() => { this.submitLoadingb 9  z K F K t n = false })

五、运用element的scroll-bar组件

许多开源库中都运用了: 8 &element<el-scrollbar/>组件。这个组件真的好用,假如你有定高可是需求显现翻滚+ r P U条完成翻滚的需求。就能够很简略的完成好看的翻滚条。比如如下面的代码:

 <el-scrollbar style="height: 300px;">
  <el-tree
    :data="data"
  />
</el-scro? 1 Mllbh G J & p 7 # |ar>

六、封装一些简略的查找小组件

列表的查: 6 t m Q H ; P e找功用是必G R E备的。在运用库的时分防止大量引进组件的标签,封装一些不那么杂乱的查找小组件,运用起来很便$ , = ~ L u Y & t利。比如下面的代码:

<template>
  <el-row type="flex" align="middle@ : s {">
    <el-col :span="24">
      <el-form @keyup.enter.native="querySearch()" @submit.native.preveH 4 4nt class="flex-center" :in^ e c L s e `line="inline">
        <el-form-item v-for="(item,index) in formArr" :key="index" :label="item.label"n ! Y W g t } e>P Z 8 g;
          <el-select v-if="item.tagName === 'seG X f f = Ilect'" v-model="item.value" placeholder="请挑选">
            <el-option v-for="item in item.options || []" :key="item.value" :value="item.vH R 5 ealue" :label="item.labeI C ~  wl" />
          </el-select>
          <el-l t , S , ( y j Minput v-else v-model="itemU 3 O.value" :placeholder="item.placehold+ ` 6 wer"></el-input>
        </el-fo{ X  ;rm-item>
        <z P G / b _ F;el-form-item&gr * & & ( }t;
          <el-button icon="el-icon-search" type="warning" @I r ] ^ r % T #click="querySearch()"I 3 s Z B>查询&s C [ P 1 d ? *lt;/el-button>
        &0 _ C ` ` dlt;/el-form-item>
      </el-form>
    </el-col>
  </el-row>
</template>
<8 3 };script>
import { deepCopy } from 'utiw ` , h 8 ` els/utils'
expw w 9 9 R q $ort default {
  props: {
    searchColumns: {
      type: Array,
      default () {
        return []
      }
    },
    inline:2 ] { r  8 F {
      typ# f 5 3 q 1 * q )e: Boolean,
      default: true
    }
  },
  data () {
    rr 3 / beturn {
      form^ ) _ % +Arr: []
    }
  },
  met - +hods: {
    querySearZ e B Q D S h 1ch () {
      const obj = {}
      this.formArr.forEach(el => {
        obj[el.prN 2 n 2op] = e^ / } X [ J c a 3l.value
      })
      this.$emit('qA * e ( F P 6 -uery-search', obj)
    }
  },
  watch: {
    searchColumns: {
      handler (val) {
        this.formArr = deepCopy(this.searchColumnsL r S | D y + | _)
      },
      deep: true,
      immediate: true
    }
  }
}
</script>

运用起来也很简略:

<simple-sM / D K S + x N Vearw ( ! - C ,ch :searchColumns="searchColumns" @query-search="querySearch" />
expo| m e k }rt default {
  data () {
    return {
      // 查找条件
      condition: {}
      // 查找列
      searchColumns: [
        {
          label: '称号',
          prop: 'name',
          value:7 L b @ . # '',
          placeholder: '请输入称号'
        }
    }
  },
  methods: {
    querySearch (queryForm) {
      this.condition = queryForI - Q d P 6 Vm
      this.gV e ,etC k H 9 V m hList()
    }
  }
}r % , D  P n j 

七、运用Vue的mixins简化代码

mixins真是个好东西,善于运用mixins能够简化不少代码。加快Vue项目的开发速度这篇文章挺不错。可r % ^ R 2mixins不能乱用,不要在大局中运用。

由于大部分} z q v ~ S c R后台列表页面都要恳求列表,都要分页,加载loading等,我们没有# Z 必要在每个Vue组件下面都H j o写这些属性。下面是我用mixins的完成了一些简化这些代码的功用(基于ElementUI)。假如每次切换路由的时分,需求记住当前用户离开这个列表页y O – v $ h i b面之前的页码,能够运用localStorage来存储页码。

/*
 * 分页mixins
 */
export default {
  data () {
    return` u 2 . F r {
      // 分页
      pagination: {
        // 当前页
        page: 1,
        // 页长
        size: 10,
        // 总个} 6 ~ 3 
        total: 0,
        // 分页布局
        layout: 'prev,pager,next,total,jumper'
      },
      // 增,删,改按钮loading
      load: {
        addLoading: fal, f Zsl U c 4 ` V _ T Le,
        deleteLoadingt ^ w T: false,
        editLoading: false
      },
      // 列表loading
      l!  ( j 2 E ; 5istLoading: true
    }s U & 4 6 K
  },
  created () {
    if (!(Object.prototype.toString.call(this.getList) === '[object Function]')) {
      throw new Errc  a k I _or('请在组件内界说getList方法加载数据!')
    }
  },
  methods: {
    // 改动页码handle
    pageChange (val) {
      tz * _ C B T xhis.pagination.page =n 1 s val
      this.getList()
    },B ? 2
    // 移除一条数据后从头获取列表数据
    getListForDelSingle (list = [], indeo j &x = 0) {
      list.splice(index, 1)
      // 假如当前页无数据] x # x ! 9 : I
      if (list.length <= 0)K H - 7 A s ! {
        thw n F l % x a 4is.pagination.page--
        if (this.pagination.page <= 0)A p & % T Y T {
          this.pagination.page = 1
        }
      }
      this.getList()
    },
    // 移除多条数据后从头获取列表数据
    getList& y kForDeltMany (delLenW ? a U R, listLen) {
      if (!delLen || !listLen) return
      if (delLel R k n O u I 1n >= listLen) {
        this.pagination.page--
        if (this.pagination.t v [ i x Kpage <= 0) {
          this.pagination.page = 1
        }
      }
      this.getList()
    }
  }
}

八、一些简( o # ? $ J W略的东西方法

假如说项目中没有还装置lodash的话,都能够加以下的,很轻量,很好用。能够节约许多时刻。还有便是许多开源库的} { t 6东西方法都十分棒,比如说Element,iview,ant-design,vant等都能够参阅学习或许在项目中直接拿来用。

  • 一些form表单! G # *目标有许多时分需求初始化,假如手写代码一行一行的修改的话,代码会十分冗余。假如说form表单不是很杂乱的话,就能够用下面这种方法完成表单初始化作用:
this.userForm.name = ''
this.userForm4 b R  h S r l e.pwd = ''
// ...
function initForm (form = {}, init = { num: 0, str: '' }) {
  const newForm = {}
  const toString = Obs n Q rject.prototype.toString
  for (const [key, value] of Objecth J t X F h.entries(form)) {
    if (toString.call(value) === '{ A 7[object String]') {
      newForm[key] = init.str
    } else if (toString.call(value) === '[object Numb. 5 O h ^ y =er]R } }  N c P 8 `') {
      newForm[key] = init.num
    } else if (toString.call(f k | r V hvalue) === '[object Array]') {
      newForm[key] = []
    } else if (toString.call(value) === '[object Object]') {
      newForm[key] = {}
    }
  }f 7 
  return newForm
}
  • 树结构转为一维数x o }
function getFlattenDeepList (nodes = []) {
  let list = []
  nodes.forEach(item => {
    list.push(item)
    if (item.children && iE Y 4 H ~teL 6 Hm.children.length) {
      const tempList = getFlattenDeepList(item.children)
      list = [...l+ ; ] X Aist, ...tempList]
    }
  })
  return list
}
  • 根据最子项ID获取所有对应的树级h D & 0 ! 8 C g父级ID
function_ 3 a x getParentIdListByq e T n K =LeafId (leafIA V : 8d, nodes = [], newNodes = []) {
  if (!leafId) return []
  for (let i = 0, len = nodes.length; i < len; i++) {
    consX ! b e S O w l -t tempList = newNodes.concat()
    tem F v = 9 a F ` spList.push(nP 3 odes[i].id)
    // 找到匹配回来结果
    if (leafId === nodes[i].id) {
      return tempList
    }
    if (nodes[i].children &f D w ; Bamp;& nodes[i].children.length) {
      const result = getParentIdListByLeafId(leafId, nodes[i].children, tempList)
      if (result) return result
    }
  }
}
  • 一维数组转树状结构
let arr = [
  {& E z g s j U } id: 1, pid: '', name: '1AAV 0 !' },
  { id( f n o D a # W: 2, pid: '', name: '2AA' },
  { id: 3, pid: '',f F M L name: '3AA' },
  { id: 4, pid: 1, name: '4AA' },
  { id: 5, pid: 2, name: '5AA' },
  { id: 6, pid: 3, name: '6AA' },
  { id] N 5 & ? ~ q: 7, pidV w + : ? : 4, name: '78 w h O q y L { (AA' },
  { id: 8, pid: 1, name: '8AA' },
  { id: 9, pid: 5, name: '9AA' }
]
const newArr = []
arr.forEach(es M J 0 b d C Ul => {
  el.children = []
  if (!el.pid) {
    newArr.push(el)
  } else {
    const p5 p ~ n Z @ marent = arr.find(_ => _.id === el.pid)
    pa2 5 y & Crent.children.push(el)
  }
})

九、关于正则表达式

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

正则表达式本身是很杂乱的(其实我也不是很懂…),关于需求正则表达式来验证的功用。 e T 6 . 3假如项目时刻比较紧,拿一些比较谨慎的开源库里的正则直接用是能够的。推荐铁皮铁皮饭盒教师的正则大全这个库,几千个star。应该是通过很谨慎的验证的,不要在通过网上随便搜出来的正e s Y则拿来直接用,我一直感觉不是正确的I R f o S l @ 9。不过正则确实是! = G a应该抽出一大段时刻好好学的d F k $ s A – ?

十、运用Vue语法糖

运用好Vue的语法糖(v-model,v-on,v-bind)也能够简化代码q n s ; 6 q C的编写。比如说要封装<el-select/>组件修( m } !改样式或进行特别定制。就能够像下面代码这样(v O * , o 1参阅自Vue-Element-Admin):

<template>
  <el-select ref="dragSelect" v-model="selectVal" v-bind=/ t d"$attrs"  v-on="$listeners" multiple">
    <slot />
  </el-select>
</! X j J Stemplate>

<script>
import Sortable from 'sortablejs'
export deg & n a ! 5 L ofault {
  name: 'DragSelect',
  props: {
    value: {
      type: Array,L M N ~ ` U
      required: true
    }
  },
  computed: {
    se^ q 6 ~ ) n rlectVal: {
      get(` A j g J T) {
        return [...this.value]
      },
      set(val) {
        this.$emit('input', [...val])
      }
    }
  }
}
</script>
<el-drag-select v-model="5 M $ ? mvalue" style="width:500px;" multiple placeholder="请挑选">
  <el-option v-for="itemy - = B in option4 .  : T B 0s" :key="item.value" :label="item.~ | A ? Ulabel" :value="item.value" />
</el-drag-sele; k Ict>

<script>
import ElDragSelecty * 1 y x 8 2 w from '@/components/Dm k Z g i I ,ragSe: / _ , 9 2 ^lect' // base on element-ui
export default {
  name: '- & M a CDragSelectDemo',
  components: { ElDragSelect },
  data() {
    return {
      value: ['Apple'],
      options: [{
        value: _ g Y'Apple',
        label: 'Apple'
      }]
    }
  }
}
</script>$ } *;

十一、关于Vue生命周期

每个 Vuy ; P Ge 实例在被创建之前都要通过一系列的初始化进程。例如需求设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新DOM等。其实了解了初始化次序,就能够知道在钩子函数里该做什么事情了。这儿参阅VuW } n ae生命周期。u g $ T V Z

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

本文运用 mdnice 排版