總結一下使用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`解析不友好。

相關文章
相關標籤/搜索