Vue 自定義指令的一個小例子

前端開發的同窗們應該對 Element UI 不陌生,而平常開發中,多少會存在一些組件沒法徹底知足咱們需求的狀況,或者說想在原組件的基礎上附加一些功能,當遇到須要附加一些不是特別複雜的小東西的時候,採用 Vue 的自定義指令來實現,將會顯得十分優雅

前景

咱們知道 Element UIel-input 組件 typetext 獲取 textarea 時,可經過設置 show-word-limit 屬性來展現字數統計。可是咱們當前項目使用的 Element UI 版本爲 2.5.21, 是沒有該屬性的,而咱們暫時又不方便升級 Element UI 版本,而產品經理堅持要求這個小東西,那麼看看如何解決這個問題前端

實際上這個小功能十分簡單,以下vue

<template>
    <div class="el-input-wrap">
      <el-input 
        type="textarea"    
        :autosize="{ minRows: 4, maxRows: 4 }" 
        v-model="desc"
        maxlength="30">
      </el-input>
      <span class="el-input-count">{{ desc.length }}/30</span>
  </div>
</template>
<script type="text/ecmascript-6">
  export default {
      data() {
        return {
          desc: ''
      }
    }
  }
</script>
<style>
  .el-input-wrap {
    position: relative;
  }
  .el-input-count {
    position: absolute;
    line-height: 1.5;
    color: #909399;
    background: #fff;
    font-size: 12px;
    bottom: 5px;
    right: 10px;
  }
</style>

經過手動的方式添加父元素設置定位屬性,再添加 span 元素設置定位和樣式,來展現動態的長度限制效果,這麼作就能夠實現和新版本的 show-word-limit 如出一轍的效果,然而始終有些許麻煩,每次用到的地方都要這麼來一遍,且不太好抽出來封裝組件,那麼是否是能夠考慮一下使用自定義指令來作呢?vue-cli

思路

最終效果指望是經過一個指令來實現上面的效果,如數組

<el-input 
  v-limit
  type="textarea"    
  :autosize="{ minRows: 4, maxRows: 4 }" 
  v-model="desc"
  maxlength="30">
</el-input>

和以前對比少了些什麼?咱們須要作些什麼?app

1,建立一個父元素 設置定位屬性,這一步是展現文本是否認位在輸入框的右下角的關鍵dom

2,建立一個 span 元素 設置樣式,用於展現效果ecmascript

3,將 el-input 綁定的值的長度填到 span 元素中函數

4,每次 el-input 綁定的值的長度改變時,更新 span 元素的值spa

思路有了,並非很複雜,那麼接下來咱們看看,如何來編寫這個代碼code

實戰代碼

首先建立一個js文件 showWordLimit.js, 而後 export 一個能夠註冊指令的對象,而後在鉤子函數 inserted 時,咱們來實現第一步,建立一個父元素,並設置定位屬性

export default{
  inserted: function () {
    let wrap = document.createElement('div')
    wrap.style.position = 'relative'
  }
}

既然是父元素,咱們還須要將使用了該指令的 el-input 元素插入到這個父元素內,經過鉤子的參數來拿到 dom 元素

export default{
  inserted: function (el) {
    let wrap = document.createElement('div')
    wrap.style.position = 'relative'
    
    el.parentNode.replaceChild(wrap, el)
    wrap.appendChild(el)
  }
}

這樣下來,第一步就算完成了,下面繼續建立一個 span 元素,並設置好樣式,同時還要插入到剛剛建立的父元素內

export default{
  inserted: function (el) {
    let wrap = document.createElement('div')
    wrap.style.position = 'relative'
    
    el.parentNode.replaceChild(wrap, el)
    wrap.appendChild(el)
    
    let oSpan = document.createElement('span')
    oSpan.style.lineHeight = 1.5
    oSpan.style.color = '#909399'
    oSpan.style.position = 'absolute'
    oSpan.style.background = '#FFF'
    oSpan.style.fontSize = '12px'
    oSpan.style.bottom = '5px'
    oSpan.style.right = '10px'
    oSpan.innerText = '0/30'
    
    wrap.appendChild(oSpan)
  }
}

注意到 innerText 的值,應該是個變量,根據 el-inputvalue 值長度來決定的,這裏就須要用到動態指令的作法,在使用指令的時候,將這個值傳過來,例如

<el-input 
  v-limit="desc.length"
  type="textarea"    
  :autosize="{ minRows: 4, maxRows: 4 }" 
  v-model="desc"
  maxlength="30">
</el-input>

回到 showWordLimit.js,怎麼接收這個值呢?一樣也是經過鉤子的參數 binding 能拿到,不清楚的能夠參考Vue官方文檔,自定義指令這一塊,寫得很清楚,拿到了以後,替換掉 span 元素的值

export default{
  inserted: function (el, binding) {
    let wrap = document.createElement('div')
    wrap.style.position = 'relative'
    
    el.parentNode.replaceChild(wrap, el)
    wrap.appendChild(el)
    
    let oSpan = document.createElement('span')
    oSpan.style.lineHeight = 1.5
    oSpan.style.color = '#909399'
    oSpan.style.position = 'absolute'
    oSpan.style.background = '#FFF'
    oSpan.style.fontSize = '12px'
    oSpan.style.bottom = '5px'
    oSpan.style.right = '10px'
    // binding.value 等於使用該指令的 el-input 組件的值的長度, 此處使用的ES6字符串模板拼接
    oSpan.innerText = `${binding.value}/30`
    
    wrap.appendChild(oSpan)
  }
}

OK,到目前爲止,姑且能夠試用一下了,在 man.js 導入並註冊該指令

// main.js    若是沒有使用 vue-cli 腳手架的同窗,須要找到對應的入口文件導入註冊
import showWordLimit from '@/directive/showWordLimit'
Vue.directive('limit', showWordLimit)
<!-- 在須要用到的頁面中 使用指令 -->
<el-input 
  v-limit="desc.length"
  type="textarea"    
  :autosize="{ minRows: 4, maxRows: 4 }" 
  v-model="desc"
  maxlength="30">
</el-input>

這個時候會發現,展現效果的數值,是不會隨着輸入框的內容長度變換而更新的,雖然是作了傳遞文本框內容長度的處理,可是在 inserted 內,它只會執行一次,若是文本框默認值的長度就有10,那麼它顯示的就將是10/30,而後咱們編輯了內容,長度改變了,它依然仍是10/30,若是想要每次編輯內容都更新這個展現效果的數值,那麼久要用到另一個鉤子函數 update

export default{
  inserted: function (el, binding) {
    let wrap = document.createElement('div')
    wrap.style.position = 'relative'
    
    el.parentNode.replaceChild(wrap, el)
    wrap.appendChild(el)
    
    let oSpan = document.createElement('span')
    oSpan.style.lineHeight = 1.5
    oSpan.style.color = '#909399'
    oSpan.style.position = 'absolute'
    oSpan.style.background = '#FFF'
    oSpan.style.fontSize = '12px'
    oSpan.style.bottom = '5px'
    oSpan.style.right = '10px'
    // binding.value 等於使用該指令的 el-input 組件的值的長度, 此處使用的ES6字符串模板拼接
    oSpan.innerText = `${binding.value}/30`
    
    // 爲了在 update 生命週期函數內方便獲取到它,添加個 id 
    oSpan.setAttribute('id', 'oSpan')
    
    wrap.appendChild(oSpan)
  },
  update: function (el, binding) {
    let oSpan = document.getElementById('oSpan')
    oSpan.innerText = `${binding.value}/30`
  }
}

如今展現效果的數值應該已經能夠實現實時更新了,細心的同窗應該會發現,有個地方有遺漏,那就是展現效果的最大字符限制的數字,目前是固定死的30,那若是想限制字符爲其餘數值,就不行了,那要解決這個問題也很是簡單,將傳遞過來的值,改成數組的形式便可

<el-input 
  v-limit="[desc.length, 50]"
  type="textarea"    
  :autosize="{ minRows: 4, maxRows: 4 }" 
  v-model="desc"
  maxlength="30">
</el-input>
export default{
  inserted: function (el, binding) {
    let wrap = document.createElement('div')
    wrap.style.position = 'relative'
    
    el.parentNode.replaceChild(wrap, el)
    wrap.appendChild(el)
    
    let oSpan = document.createElement('span')
    oSpan.style.lineHeight = 1.5
    oSpan.style.color = '#909399'
    oSpan.style.position = 'absolute'
    oSpan.style.background = '#FFF'
    oSpan.style.fontSize = '12px'
    oSpan.style.bottom = '5px'
    oSpan.style.right = '10px'
    // binding.value 等於使用該指令的 el-input 組件的值的長度, 此處使用的ES6字符串模板拼接
    oSpan.innerText = `${binding.value[0]}/${binding.value[1]}`
    
    // 爲了在 update 生命週期函數內方便獲取到它,添加個 id 
    oSpan.setAttribute('id', 'oSpan')
    
    wrap.appendChild(oSpan)
  },
  update: function (el, binding) {
    let oSpan = document.getElementById('oSpan')
    oSpan.innerText = `${binding.value[0]}/${binding.value[1]}`
  }
}

至此,這個自定義指令就算徹底作好了,固然難度不大,這篇文章的目的主要是但願能夠給一些尚未接觸過自定義指令應用的同窗作個引導,講述一個完整的應用流程和思路,但願能幫助到一些人,也留於本身筆記

相關文章
相關標籤/搜索