好一段時間沒寫文章了。嗯,之前欠的nodemcu的文章,我會找時間補(bǔ)上的。剛好項目組推新的固件,加入幾個新的模塊。放心,我一定會更新那個系列的文章的,只有有時間相信未來一段時間都沒有的話。??
這回帶來的是有關(guān)寫Vue組件(.vue)的文章。主要會涉及的內(nèi)容包括props,data, methods等一系列概念。力爭用最簡單的語言讓像我一樣的萌新可以快速上手寫出Vue組件來。跟著本來走一遍可以得到下面的效果。GitHub

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

工具涉及到了幾個組件需要注意一下,包括了vue路由,eslint,測試工具等。這些按需選擇安裝。大寫字母表示是否默認(rèn)安裝。
如果不想因為路由而困惑的話,可以不選擇加入路由。但是,eslint這個對新手來說相當(dāng)自虐的工具,我是強(qiáng)烈推薦安裝的。eslint提供多到可怕的規(guī)范化檢查,可以幫助避免寫出bug才怪。配合vscode的eslint插件,可以得到更好的體驗。
創(chuàng)建完工程后可以得到類似于下圖的工程結(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組件(下)
。