從0開始寫Vue組件(上)

好一段時間沒寫文章了。嗯,之前欠的nodemcu的文章,我會找時間補(bǔ)上的。剛好項目組推新的固件,加入幾個新的模塊。放心,我一定會更新那個系列的文章的,只有有時間相信未來一段時間都沒有的話。??

這回帶來的是有關(guān)寫Vue組件(.vue)的文章。主要會涉及的內(nèi)容包括props,datamethods等一系列概念。力爭用最簡單的語言讓像我一樣的萌新可以快速上手寫出Vue組件來。跟著本來走一遍可以得到下面的效果。GitHub

目標(biāo)

創(chuàng)建一個工程

直接使用vue-cli創(chuàng)建一個webpack工程。雖然官方文檔里面建議新手不要使用構(gòu)建工具。不過,個人覺得用這個創(chuàng)建工程學(xué)習(xí)vue也蠻爽的。工具將很多工具打包到工程里面來,提供了一個相當(dāng)不錯的開發(fā)體驗。


vue-cli

工具涉及到了幾個組件需要注意一下,包括了vue路由,eslint,測試工具等。這些按需選擇安裝。大寫字母表示是否默認(rèn)安裝。

如果不想因為路由而困惑的話,可以不選擇加入路由。但是,eslint這個對新手來說相當(dāng)自虐的工具,我是強(qiáng)烈推薦安裝的。eslint提供多到可怕的規(guī)范化檢查,可以幫助避免寫出bug才怪。配合vscode的eslint插件,可以得到更好的體驗。

創(chuàng)建完工程后可以得到類似于下圖的工程結(jié)構(gòu)

工程結(jié)構(gòu)

重點關(guān)注src文件夾里面的內(nèi)容。

main.js里面new了一個vue實例,元素(el)指向了id=‘a(chǎn)pp’的div。app.vue這個文件又引用了pie.vue這個組件,而pie.vue又引用了mdiv.vue這個組件。看起來和洋蔥一樣,一層一層又一層。

寫第一個組件

有了上面的了解后,可以動手寫組件了。先從最里層開始寫起。一個.vue文件,可以認(rèn)為由三部分組成。

<template>
  <div class="mdiv">
    <div class="head" @mousedown="mousedown">{{ title }}</div>
    <div class="menu">
      <div style="float: right; margin: 0 7px" @click="menuClick">
        <span class="fas fa-ellipsis-v" v-show="isedit"></span>
      </div>
      <div style="float: right; margin: 0 3px"  @click="expand">
        <span class="fas" v-bind:class="{ 'fa-expand': !isexpand, 'fa-compress': isexpand }"></span>
      </div>
      <slot name="contextmenu"></slot>
    </div>
    <slot name="container"></slot>
    <div class="resize" @mousedown="rmousedown"></div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      title: ''
    }
  }
}
</script>

<style scoped>
</style>

第一個部分是template,就是HTML了。這部分的內(nèi)容不多,直接貼代碼。
第二部分是script,就是組件的vue實例了。這個實例的el就指向了上面的template。
第三部分是style,樣式。加了scoped后,這個標(biāo)簽里面的樣式就只在這個文件里面有效了。

HTML

在template里面有幾個內(nèi)容需要關(guān)注的。

  • @mousedown="mousedown" 這里的@是v-on的縮寫,用來綁定事件。可以是DOM的原生事件,也可以是自定義事件。當(dāng)mousedown事件發(fā)生時,會調(diào)用綁定的mousedown方法。

  • {{ title }}模板語法。當(dāng)title發(fā)生變化的時候,頁面元素也會跟著變化。

  • v-show="isedit" isedit是個布爾值,當(dāng)值為正(true)的時候,這個標(biāo)簽就會顯示出來,不然就隱藏起來。v-if也可以得到類似的效果,區(qū)別可以看這里v-if vs v-show。

  • v-bind:class="{ 'fa-expand': !isexpand, 'fa-compress': isexpand }" 用來綁定class,實現(xiàn)切換class。isexpand也是布爾值,當(dāng)值為真時,<span class="fas" v-bind:class="{ 'fa-expand': !isexpand, 'fa-compress': isexpand }"></span>將渲染成<span class="fas fa-compress"></span>。

  • <slot name="contextmenu"></slot> slot是插槽,父組件可以將內(nèi)容插到這個槽里面。這個是蠻好玩的特性。下篇文章還是說到。

script

這部分是vue的實例。實例有很多東西,有些是函數(shù),有些事對象。

  • data 是個函數(shù),返回一個對象。可以簡單的將對象的屬性看成組件的變量。
data () {
    return {
      isexpand: false,
      // 空間位置相關(guān)
      pos: {
        left: this.outline[0],
        top: this.outline[1],
        height: this.outline[2],
        width: this.outline[3]
      },
      posNew: {
        left: this.outline[0],
        top: this.outline[1],
        height: this.outline[2],
        width: this.outline[3]
      },
      // DIV相關(guān)臨時變量
      down: null,
      room: null
    }
  }
  • props 是一個對象,聲明了組件接受父組件的傳入的信息。!type是類型,如果寫成字符串(‘string’)則會報錯。更具體的看Prop 驗證。
  props: {
    name: {
      type: String, // 類型
      required: true // 是否必須
    },
    // [X, Y, W, H]
    outline: {
      type: Array,
      required: true
    },
    isedit: {
      type: Boolean,
      default: false // 默認(rèn)值
    }
  }
  • mounted 是一個實例生命周期鉤子,當(dāng)el掛載后會調(diào)用這個函數(shù)。當(dāng)然,還有其他生命周期鉤子可以用。
  mounted () {
    this.$nextTick(() => {
      this.$el.style.left = this.outline[0] + 'px' // x
      this.$el.style.top = this.outline[1] + 'px' // y
      this.$el.style.width = this.outline[2] + 'px' // w
      this.$el.style.height = this.outline[3] + 'px' // h
    })
  }
  • computed 計算屬性,一個相當(dāng)好用的特性。title樣子上像是個函數(shù),在template被使用。當(dāng)name發(fā)生變化后,title會重新調(diào)用。
  computed: {
    title () {
      return this.name
    }
  }
  • watch 偵聽器,行為上和computed很像。但是,outline對應(yīng)了屬性。當(dāng)outline發(fā)生變化時,會執(zhí)行這個函數(shù)。
  watch: {
    outline () {
      this.outlineUpdate()
      this.pos = {
        left: this.outline[0],
        top: this.outline[1],
        height: this.outline[2],
        width: this.outline[3]
      }
      this.$emit('mdiv', 'resize')
    }
  }
  • emit 用于發(fā)送一個事件。第一個參數(shù)是事件名稱。vm.$emit
  • methods 一些方法??梢栽诮M件中使用。
// 這個方法與一個div的click事件綁定,
// 當(dāng)事件發(fā)發(fā)生時,調(diào)用這個函數(shù)
// 用來顯示“全屏”效果
    expand () {
      this.isexpand = !this.isexpand
      if (this.isexpand) { // isexpand === true 變?nèi)?        this.$el.style.left = '1px'
        this.$el.style.top = '1px'
        this.$el.style.height = 'calc(100% - 2px)'
        this.$el.style.width = 'calc(100% - 2px)'
      } else {
        this.$el.style.left = this.pos.left + 'px'
        this.$el.style.top = this.pos.top + 'px'
        this.$el.style.width = this.pos.width + 'px'
        this.$el.style.height = this.pos.height + 'px'
      }
    }

下面這些代碼的主要功能時,鼠標(biāo)可以通過右下角拖拽縮放div。

    // 縮放DIV
    rmousedown (down) {
      this.down = down
      this.room = this.$el.parentElement.getBoundingClientRect()
      this.room.width = this.room.width - this.pos.width - this.pos.left - 4
      this.room.height = this.room.height - this.pos.height - this.pos.top - 4
      console.log(this.room)
      document.onmouseup = this.rmouseup
      document.onmousemove = this.rmousemove
    },
    rmousemove (move) {
      let diff = {x: move.clientX - this.down.clientX, y: move.clientY - this.down.clientY}
      let x = diff.x < this.room.width ? diff.x : this.room.width
      let y = diff.y < this.room.height ? diff.y : this.room.height
      this.posNew.width = this.pos.width + x
      this.posNew.height = this.pos.height + y
      this.$el.style.width = this.posNew.width + 'px'
      this.$el.style.height = this.posNew.height + 'px'
    },
    rmouseup () {
      document.onmousemove = null
      document.onmouseup = null
      // 更新大小
      Object.assign(this.pos, this.posNew)
      this.$emit('mdiv', 'resize')
    }

內(nèi)容比較多,其他內(nèi)容到GitHub上面看。

style

樣式部分,沒什么好說的了。

第一個組件到這里就算實現(xiàn)了。然后就可以在其他組件里面使用,有點繼承的意思。使用上類似這樣。

<template>
  <mdiv></mdiv>
</template>

當(dāng)然,這樣是用不了的,還差點東西。下一篇講從0開始寫Vue組件(下)
。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容