别问我[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;
      }
    }
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章