別問我[vue],怎麼自定義組件?

在這裏插入圖片描述
初學vue已近3個周,至此開發技能已滿足。從eggjsvue 再到 scss,這個過程需要總結和分享。深度學習需要從這裏點點滴滴的開始 ~
以上截圖是,剛爲內容分享使用vue所畫的頁面。看效果,與原圖相比逼真度——真假難分

接下來總結方向,vue中如何自定義組件?如何使用自定義組件?
使用自定義組件必然會牽扯到組件間通訊,怎麼實現通訊?

好,上代碼

<!-- @/views/Bank-Acc-Transfer.vue-->
<template>
  <div class="acc-transfer-container">
    <div class="header">
      <div class="img-back">
        <span class="img"></span>
      </div>
      <div class="span">
        <span>賬戶轉賬</span>
      </div>
    </div>
    <div class="transfer-list">
      <div class="line-row">
        <span class="span-left line-height">收款賬戶</span>
        <div class="income">
          <span class="income-word">{{payee}}</span>
          <span class="transfer-img"></span>
        </div>
      </div>

      <div class="line-row line-height">
        <span class="span-left">幣種</span>
        <span id="r-span">{{currency}}</span>
      </div>

      <div class="line-row">
        <span class="span-left">轉賬金額</span>
        <input
          type="text"
          class="transfer-num"
          ref="infocus"
          maxlength="16"
          autofocus="autofocus"
          @blur.prevent="onblur"
          @focus="onFocus"
          v-model="transferNum"
          style="border:1px solid transparent; outline:none; text-align: right"
        />
        <div v-if="transferNum ? true : false" @click="resetInput">
          <i class="ic_closes"></i>
        </div>
        <el-button style="padding: 8px 6px" type="danger">全額轉入</el-button>
      </div>

      <div class="capital-num">
        <span class="ctextw">{{textw}}</span>
        <span class="ctextm">{{textm}}</span>
      </div>

      <div class="line-row pay-acc line-height">
        <span class="span-left">付款賬戶</span>
        <div class="pay">
          <span class="pay-word" ref="payWord">{{payAcc}}</span>
          <span class="transfer-img"></span>
        </div>
      </div>

      <div class="line-row line-height">
        <span id="remain" class="span-left">可用餘額</span>
        <span id="m-remain">{{allBalance}}</span>
      </div>
    </div>

    <el-button type="danger" round size="medium" class="btn_next">下一步</el-button>
  </div>
</template>

<script>
export default {
 ... ...
};
</script>

<style lang='scss'>
... ...
</style>

上面代碼看着很不友好,代碼難以閱讀!接下來對上面代碼進行重構,使用組件化拼接方式來實現相同頁面效果。
因爲使用組件化方式,會更加直觀,效果圖及方案設計如圖 ~
在這裏插入圖片描述
以上截圖的設計結構,編碼如下

<!-- @/views/Bank-Acc-Transfer-c.vue-->
<template>
  <div class="acc-transfer-container">
    <div class="header">
      <div class="img-back">
        <span class="img"></span>
      </div>
      <div class="span">
        <span>賬戶轉賬</span>
      </div>
    </div>
    <div class="transfer-list">
      <li-acc-click mType="收款賬戶" :account="payee" ></li-acc-click>
      <li-show mType="幣種" :allBalance="currency" ></li-show>
      <li-acc-input :callbackInput="callbackInput"></li-acc-input>
      <li-acc-click mType="付款賬戶" :account="payAcc"></li-acc-click>
      <li-show :allBalance="allBalance"></li-show>
    </div>
    
    <el-button type="danger" round size="medium" class="btn_next">下一步</el-button>
  </div>
</template>

<script>
import LiShow from '@/components/LiShow';
import LiAccClick from '@/components/LiAccClick';
import LiAccInput from '@/components/LiAccInput';
export default {
  data() {
    return {
      allBalance: "9.98",
      payAcc: "62284806221890098",
      currency: "人民幣",
      mType: "幣種",
      payee: "請選擇"
    };
  },
  mounted() {},
  methods: {
    handleChange(value) {},
    callbackInput(trans_acc = '0.0'){
      console.log('實時打印回調-子組件動態輸入的內容>>>', trans_acc)
    }
    
  },
  components: {// 局部註冊自定義組件
    LiShow,LiAccClick,LiAccInput
  }
};
</script>

<style lang='scss'>
... ...
</style>

自定義組件 步驟很簡單,其實我們看到的VUE頁面,本身也是一個組件。這個和react-native[react]的組件化思想基本一致。
自定義組件,與vue頁面組件結構一致,同樣分爲三塊:<template> 、 <script> 、 <style scoped lang="scss">
只是在編寫自定義組件時,組件所需大多數用來展示的動態值,均從所植入的頁面組件或組件中獲得!這就涉及到了組件間的通訊~

以此爲例,@/views/Bank-Acc-Transfer-c.vue 中第15行植入的子組件 <li-acc-input :callbackInput="callbackInput"></li-acc-input>
其中<template>模塊組織編碼顯示組件框架

<template>
  <div class="li-acc-input">
    <div class="line-row">
      <span class="title">{{mType}}</span>
      <input
        type="text"
        ref="infocus"
        v-model="transferNum"
        maxlength="16"
        autofocus="autofocus"
        @blur.prevent="onblur"
        @focus="onFocus"
      />
      <div v-if="transferNum ? true : false" @click="resetInput">
        <i class="ic_closes"></i>
      </div>
      <el-button style="padding: 8px 6px" type="danger" @click="transferAll">全額轉入</el-button>
    </div>
    <div class="capital-num">
      <span class="ctextw">{{textw}}</span>
      <span class="ctextm">{{textm}}</span>
    </div>
  </div>
</template>

代碼中可看到插值 {{mType}} 、{{allBalance}} 以及方法{{callbackInput}},他們三個值和方法回調是由外界傳入的,是props中的字段,是外界[外部組件]與該組件進行通訊的媒介。想要改變他們,讓外界傳入就好。且他們很特殊,組件通訊的字段是在 props: {…} 中做了聲明。組件其餘普通變量transferNum、clickReset、textw和textm,會在data(){…} 中做定義。而區別就是定義的位置 props:{} 和 data(){} 決定了他們的性質 !

<script>模塊定義該自定義組件的props[用來進行組件間通訊的屬性],和交互邏輯

<script>
export default {
  name: "li-acc-input",
  data() {
    return {
      transferNum: "",
      clickReset: false,
      textw: "",
      textm: ""
    };
  },
  watch: {
    transferNum(nInput, oInput) {
      this.callbackInput(nInput)
      if (nInput.indexOf(",") > 0) {
        return;
      }
    }
  },
  methods: {
    transferAll() {
      this.transferNum = "100";
    },
    onblur() {
      let thisRef = this;
    },
    onFocus() {},
    resetInput() {
      this.clickReset = true;
      this.transferNum = "";
      this.$refs.infocus.focus();
    }
  },
  props: {
    allBalance: {
      type: String,
      default: "0.00",
      required: true
    },
    mType: {
      type: String,
      default: "轉賬金額",
      required: true
    },
    callbackInput: {
      type: Function
    }
  }
};
</script>

從上面業務邏輯來看,當用戶輸入轉賬金額時,由於Input標籤使用了雙向綁定 v-model=“transferNum” ,轉賬金額的變量transferNum就會實時發生變化。清除金額的按鈕圖標則會根據表達式 v-if=“transferNum ? true : false” 進行顯示和隱藏。在輸入金額的同時通過 watchtransferNum 的監聽,則會實時調用方法 this.callbackInput(nInput),並將輸入的金額通過該方法回調到外組件[父組件]。爲我們頁面進行接口請求,做足數據準備。

<style scoped lang="scss">模塊則組織該組件如何顯示的樣式

<style scoped lang="scss">
body {
  padding: 0 0;
  margin: 0 0;
}
.li-acc-input {
  background-color: white;
  display: block;
  border-top: 1px #e3e3e4 solid;
  padding: 0 16px;
  white-space: nowrap;
  overflow: hidden;
}
... ...
</style>

**使用,怎麼在頁面中使用?有兩種方式**
方式一局部組件註冊,只要三步,

  1. 如上源代碼 @/views/Bank-Acc-Transfer-c.vue 第27行<script>中引入該自定義組件;
  2. 如上源代碼 @/views/Bank-Acc-Transfer-c.vue 第46-47行<script>中註冊該自定義組件;
  3. 如上源代碼 @/views/Bank-Acc-Transfer-c.vue 第15行<templet>中植入該自定義組件;

方式二全局組件註冊,只要三步,

  1. 定義一個js文件,並使用vue方法install進行註冊
/**單個組件的全局註冊  */
// @components/index.js
import LiAccInput from '@/components/LiAccInput.vue'

const components = {
  // install 方法是vue中默認的一個方法
  install: function(Vue) {
    Vue.component('li-acc-input', LiAccInput)
  }
}
export default components;
/**多個組件的全局註冊  */
// @components/index.js
import LiAccInput from '@/components/LiAccInput.vue'
import LiAccClick from '@/components/LiAccClick.vue'
import LiShow from '@/components/LiShow.vue'

const components = {
  // install 方法是vue中默認的一個方法
  install: function(Vue) {
    Vue.component('li-acc-input', LiAccInput)
    Vue.component('li-acc-click', LiAccClick)
    Vue.component('li-show', LiShow)
  }
}
export default components;
  1. main.js中引入並使用Vue.use(),將自定義組件全局代入
/**單個組件的全局註冊  */
// main.js
import Vue from 'vue'
... ...
import LiAccInput from './components/index';
Vue.use(LiAccInput)
... ...
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
/**多個組件的全局註冊,註冊方式不會變  */
// main.js
import Vue from 'vue'
... ...
import Elemnts from './components/index';
Vue.use(Elemnts)
... ...
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
  1. <templet> 模板中直接植入。結果與方式 一,一模一樣。且是全局性質~
/** 已註冊的全局單個組件,植入 */
<li-acc-input :callbackInput="callbackInput"></li-acc-input>
/** 已註冊的全局多個組件,植入 */
<div class="transfer-list">
      <li-acc-click mType="收款賬戶" :account="payee" ></li-acc-click>
      <li-show mType="幣種" :allBalance="currency" ></li-show>
      <li-acc-input :callbackInput="callbackInput"></li-acc-input>
      <li-acc-click mType="付款賬戶" :account="payAcc"></li-acc-click>
      <li-show :allBalance="allBalance"></li-show>
    </div>

上截圖中用到了組件間的通訊:**父組件與子組件通訊**
父組件:Bank-Acc-Transfer-c.vue
子組件:LiAccInput.vue
**交互一:**子組件顯示內容,如字段mType,是組件左邊的標籤span展示插值字段。可由外界的父組件動態傳入,

<li-show mType="幣種" :allBalance="currency" ></li-show>

也可以是使用本身設置的默認 props:{…default:‘xx’…} 值;
**交互二:** 子組件的綁定屬性callbackInput,是父組件傳入子組件的方法。

 <li-acc-input :callbackInput="callbackInput"></li-acc-input>

在父組件實時回調,並打印子組件傳進來的值trans_acc

methods: {
    handleChange(value) {},
    callbackInput(trans_acc = '0.0'){ // 這裏會有方法的回調
      console.log('實時打印回調-子組件動態輸入的內容>>>', trans_acc)
    }
  }

父組件能夠實時回調,全憑子組件對標籤Input雙向綁定變量transferNum的監聽

watch: {
    transferNum(nInput, oInput) {
      this.callbackInput(nInput) // 就是這裏,執行調用父組件的方法
      if (nInput.indexOf(",") > 0) {
        return;
      }
    }
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章