vue成長之路+實戰(zhàn)+Vue2+VueRouter2+webpack(二)vue組件入門第一節(jié)

推薦我的vue教程:VUE系列教程目錄

上篇講解了vue-router路由入門,現在講講關于vue組件的內容燃逻。
如果你們有使用過element組件的話,他就是以vue組件的形式進行封裝的臂痕,在講解組件之前我們需要知道vue是數據驅動的伯襟,它的一切依賴于數據,我們應該根據數據的不同來進行相關的處理握童,在這一前提下才能形成vue框架的思考模式姆怪。
在了解這一模式的前提下我們來看看vue組件是個什么東西。

什么是VUE組件澡绩?

在github上稽揭,各位請使用git拉一下項目:vuetemplate。不會使用git拉文件的請去GitHub上下載壓縮包肥卡。

/src/page/components下是有關組件的代碼

我們打開vue官網的組件API溪掀,可以簡單瀏覽,對于新手來說這個API的閱讀有時很晦澀步鉴,或者跟實際應用有些許差別揪胃。于是我的講解是建立在對這個API的補充說明與簡單化的,所以你們最好還是看看這個氛琢。

VUE組件可以擴展 HTML 元素喊递,封裝可重用的代碼。在較高層面上阳似,VUE組件是自定義元素骚勘, Vue.js 的編譯器為它添加特殊功能。在有些情況下障般,組件也可以是原生 HTML 元素的形式调鲸,以 is 特性擴展。

注冊組件

在注冊組件時我們有兩種方式挽荡,第一種注冊全局組件藐石,第二種是局部使用組件,對于那些應用多的組件來說全局無疑是最好的選擇定拟,對于變數太大于微,應用不多且在統(tǒng)一目錄下的局部使用是我們想要的。

全局注冊:

要注冊一個全局組件青自,你可以使用 Vue.component(tagName, options)株依。

// 模板
Vue.component('my-component', {
  // 選項
})

一個模板并不能說明什么,實例才能讓人看的更明白:

// html
<my-component></my-component>

// 注冊
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})

組件的簡單使用就是這個樣子延窜,即<my-component></my-component>最后變成了<div>A custom component!</div>恋腕。可是逆瑞,實際操作中我們并不會這么弱智的使用荠藤,組件的復雜度遠遠不是這個樣子的。

局部注冊

要注冊一個局部組件获高,你可以使用 Vue的components屬性哈肖。

<script>
export default {
  components: {},
  data () {
    return {
    }
  }
}
</script>

例子才是真理:

// html
<my-component></my-component>

<script>
var vuecomponent = {
  template: '<div>A custom component!</div>'
}
export default {
  components: {
    'my-component': vuecomponent
  },
  data () {
    return {
    }
  }
}
</script>

DOM渲染的局限

在html中我們知道有些標簽的孩子是固定的比如<ul> ,<ol>念秧,<table> 淤井,<select>限制了能被它包裹的元素,例如ul里面只能包裹li摊趾。同時币狠,一些像 <option> 這樣的元素只能出現在某些其它元素內部。

在自定義組件中使用這些受限制的元素時會導致一些問題砾层,例如:

<table>
  <my-row>...</my-row>
</table>

自定義組件 <my-row> 被認為是無效的內容总寻,因此在渲染的時候會導致錯誤。那我們怎么解決呢梢为?變通的方案是使用特殊的 is 屬性:

<table>
  <tr is="my-row"></tr>
</table>

data 必須是函數

組件中必須是函數渐行,通過Vue構造器傳入的各種選項大多數都可以在組件里用。 data 是一個例外铸董,它必須是函數祟印。

// html
<simple-counter></simple-counter>
<script>
export default {
  components: {
    'simple-counter': {
      template: '<el-button size="small" v-on:click="counter += 1">{{ counter }}</el-button>',
      // 技術上 data 的確是一個函數了,因此 Vue 不會警告粟害,
      // 但是我們返回給每個組件的實例的卻引用了同一個data對象
      data: function () {
        return {
            counter: 0
        }
      }
    }
  },
  data () {
    return {
    }
  }
}
</script>

這里有很多人看蒙了蕴忆,問起初不是這樣寫嗎?悲幅?套鹅?

data: {
    counter: 0
}

這里別問站蝠,我起初也沒看懂,你可以這樣想:把所有關于data的數據引入變?yōu)?code>data () {}就可以了卓鹿。
為何我會這樣說菱魔?我曾經在上一篇文章里說:vue路由的本質是根據url的不同來進行組件的各種切換罷了。而組件的data必須是數據吟孙,所以你看我寫的代碼里關于.vue文件的都使用的是這種結構:

<template>
  <div>
  </div>
</template>
<script>
export default {
  data () {
    return {
    }
  }
}
</script>

如果不明白你可以看上面的<simple-counter>組件的例子澜倦,其最終的本質變成了:

<template>
  <div>
        <el-button size="small" v-on:click="counter += 1">{{ counter }}</el-button>
  </div>
</template>
<script>
export default {
  data () {
    return {
        counter: 0
    }
  }
}
</script>

其實你只要使用vue-router路由你的.vue文件的結構只能變成這樣:

<template>
  <div>
  </div>
</template>
<script>
export default {
  data () {
    return {
        //  這里寫基礎數據
    }
  }
}
</script>

構成組件

組件意味著協(xié)同工作,引用組件的地方叫父組件杰妓,如<simple-counter></simple-counter>藻治,組件的內容則被成為子組件。

通常父子組件會是這樣的關系:組件 A 在它的模版中使用了組件 B 巷挥。它們之間必然需要相互通信:父組件要給子組件傳遞數據桩卵,子組件需要將它內部發(fā)生的事情告知給父組件。然而倍宾,在一個良好定義的接口中盡可能將父子組件解耦是很重要的吸占。這保證了每個組件可以在相對隔離的環(huán)境中書寫和理解,也大幅提高了組件的可維護性和可重用性凿宾。

在 Vue.js 中矾屯,父子組件的關系可以總結為 props down, events up 。父組件通過 props 向下傳遞數據給子組件初厚,子組件通過 events 給父組件發(fā)送消息件蚕〔蹋看看它們是怎么工作的排作?如圖:

父子組件數據交互

父子組件的交互的原理這個具體呢?

父子組件的交互

props的單向流

組件實例的作用域是孤立的。這意味著不能(也不應該)在子組件的模板內直接引用父組件的數據。要讓子組件使用父組件的數據毡惜,我們需要通過子組件的props選項橱乱。

我們在開發(fā)中瞒大,一個單文件組件的.vue文件的基本構成是這樣的:

<template>
  <div>
  </div>
</template>

<script>
export default {
  // el: '',
  props: {}, // 父到子傳參
  components: {}, // 組件接收
  mixins: [], // 混合
  data () { // 基礎數據
    return {
      //
    }
  },
  created () {}, // 創(chuàng)建周期
  watch: {}, // 狀態(tài)過渡
  methods: {}, // 方法存放的地方
  computed: {}, // 計算屬性存放的地方
  filters: {}, // 過濾
  directives: {} // 指令
}
</script>

可是常用的很少,父組件給子組件傳值使用的就是props選項搪桂。

props選項可以接受兩種模式的參數:

第一種固定的屬性:如這樣message="hello!傳一個普通的字符透敌;
第二種動態(tài)屬性:如這樣v-bind:myMessage="this.message"傳一個變量,其可以簡化為:myMessage="this.message"

例子如下:(父子組件在同一目錄下踢械,子組件-child.vue)

// 父組件
// HTML
<child message="HELLO!" :my-message="this.message"></child>

// script

<script>
import child from './child.vue'
export default {
  components: {
    child: child
    }
  },
  data () {
    return {
      message: '你猜'
    }
  }
}
</script>

// 子組件
<template>
  <div>
    <div>{{message}}</div>
    <div v-text="myMessage"></div>
  </div>
</template>

<script>
export default {
  props: {
    message: null,
    myMessage: null
  }, // 父到子傳參
  data () { // 基礎數據
    return {
      //
    }
  },
  created () {}, // 創(chuàng)建周期
  watch: {}, // 狀態(tài)過渡
  methods: {} // 方法存放的地方
}
</script>

prop 是單向綁定的:當父組件的屬性變化時酗电,將傳導給子組件,但是不會反過來内列。這是為了防止子組件無意修改了父組件的狀態(tài)撵术。

另外,每次父組件更新時话瞧,子組件的所有 prop 都會更新為最新值嫩与。這意味著你不應該在子組件內部改變 prop 。如果你這么做了交排,Vue 會在控制臺給出警告蕴纳。

但是有時我們就是需要修改,怎么辦个粱?(老子需求)

一般情況下我們修改通常是這兩種原因:

  1. prop 作為初始值傳入后古毛,子組件想把它當作局部數據來用;
  2. prop 作為初始值傳入,由子組件處理成其它數據輸出稻薇。

對于第一種情況我們可以李代桃僵嫂冻,即用另外一個變量替代它,把它的值賦給那個變量:

data () { // 基礎數據
    return {
      //
      counter: this.message
    }
  }

第二種情況可以定義一個計算屬性塞椎,處理 prop 的值并返回:

computed: {
    messagetoLowerCase: function () {
      return this.message.trim().toLowerCase()
    }
}
注意:使用字面量語法傳遞數值時桨仿,必須使用動態(tài)props,即如這樣`v-bind:number="1"`

props驗證

我們可以為組件的 props 指定驗證規(guī)格案狠。如果傳入的數據不符合規(guī)格服傍,Vue 會發(fā)出警告。當組件給其他人使用時骂铁,這很有用吹零。

要指定驗證規(guī)格,需要用對象的形式拉庵,而不能用字符串數組:(修改上面的例子)

// html
<child message="HELLO!" :my-message="this.message" :number="11"></child>

// 子組件props
props: {
    message: String,
    myMessage: {
      type: String,
      required: true
    },
    number: {
      validator: function (value) {
        return value > 10
      }
    }
  }

驗證規(guī)格模板:

props: {
    // 基礎類型檢測 (`null` 意思是任何類型都可以)
    propA: Number,
    // 多種類型
    propB: [String, Number],
    // 必傳且是字符串
    propC: {
      type: String,
      required: true
    },
    // 數字灿椅,有默認值,如果你沒有傳則以默認為準
    propD: {
      type: Number,
      default: 100
    },
    // 數組或對象的默認值應當由一個工廠函數返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定義驗證函數
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }

自定義事件向父組件傳值

事件$on與$emit

我們知道钞支,父組件是使用 props 傳遞數據給子組件茫蛹,但如果子組件要把數據傳遞回去,應該怎樣做烁挟?那就是自定義事件婴洼!

每個 Vue 實例都實現了事件接口(Events interface),即:

使用 $on(eventName) 監(jiān)聽事件
使用 $emit(eventName) 觸發(fā)事件

注意:Vue的事件系統(tǒng)分離自瀏覽器的EventTarget API撼嗓。盡管它們的運行類似柬采,但是$on 和 $emit 不是addEventListener 和 dispatchEvent 的別名。

你們一定很奇怪怎么用事件監(jiān)聽來向父元素傳遞静稻?

其實原理很簡單就是我們在父組件上通過v-on監(jiān)聽子組件的事件警没,而子組件通過$emit(eventName) 觸發(fā)事件。例子如下:

// 父組件
<template>
  <div>
<child  v-on:onchild="inparent"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
  components: {
    child: child
  },
  data () {
    return {
    }
  },
  methods: {
    inparent () {
      alert('父組件響應了')
    }
  }
}
</script>
// 子組件
<template>
  <div>
    <el-button size="small" v-on:click="onparent">父組件響應吧U裢濉I奔!!</el-button>
  </div>
</template>

<script>
export default {
  props: {},
  data () { // 基礎數據
    return {
    }
  },
  methods: {
    onparent () {
      this.$emit('onchild')
    }
  }
}
</script>

這個例子中押搪,子組件給父組件傳值通過$emit('onchild')树酪,觸發(fā)父組件的v-on:onchildv-on:onchild響應后執(zhí)行inparent函數大州。但是就達到我們的目的了续语??厦画?

傳值疮茄,傳值滥朱,傳值,值呢力试?這個API里可沒說徙邻,那怎么辦呢?

很簡單按照程序工程師的思路來想畸裳,值肯定是這種模式:

this.$emit('onchild', 需要的值)
// 多個呢缰犁?
this.$emit('onchild', 需要的值1,需要的值2)

那接值呢?

onparent (需要的值1, 需要的值2) {
}

所以完整的模式應該是這樣的:

// 父組件
<template>
  <div>
<child  v-on:onchild="inparent"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
  components: {
    child: child
  },
  data () {
    return {
    }
  },
  methods: {
   inparent (childs, childrens) {
      alert('父組件響應了')
      console.log(childs)
      console.log(childrens)
    }
  }
}
</script>
// 子組件
<template>
  <div>
    <el-button size="small" v-on:click="onparent">父組件響應吧2篮K荨!</el-button>
  </div>
</template>

<script>
export default {
  props: {},
  data () { // 基礎數據
    return {
        childs: '我是孩子的值',
        childrens: '我是孩子的另一個值'
    }
  },
  methods: {
    onparent () {
      this.$emit('onchild', this.childs, this.childrens)
    }
  }
}
</script>
sync-修飾符

在一些情況下伍伤,我們可能會需要對一個 prop 進行『雙向綁定』并徘。當一個子組件改變了一個 prop 的值時,這個變化也會同步到父組件中所綁定的值嚷缭。這很方便饮亏,但也會導致問題耍贾,因為它破壞了『單向數據流』的假設阅爽。由于子組件改變 prop 的代碼和普通的狀態(tài)改動代碼毫無區(qū)別,當光看子組件的代碼時荐开,你完全不知道它何時悄悄地改變了父組件的狀態(tài)付翁。這在 debug 復雜結構的應用時會帶來很高的維護成本。

事實上晃听,這正是 Vue 1.x 中的 .sync修飾符所提供的功能百侧。但是VUE在 2.0 中移除了 .sync。后來在 2.3 VUE又重新引入了 .sync 修飾符能扒。

// 父組件
<template>
  <div>
<child  :foo.sync="bar"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
  components: {
    child: child
  },
  data () {
    return {
          bar: 1
    }
  },
  watch: {
    bar: function () {
      console.log(this.bar)
    }
  }
}
</script>
// 子組件
<template>
  <div>
    <div>{{foo}}</div>
    <el-button size="small" v-on:click="onsync">改變foo</el-button>
  </div>
</template>

<script>
export default {
  props: {
      foo: null
  },
  data () { // 基礎數據
    return {}
  },
  methods: {
      onsync () {
         this.$emit('update:foo', this.foo + 1)
    }
  }
}
</script>

其實本質上VUE做到的只是:需要做的只是讓子組件改變父組件狀態(tài)的代碼更容易被區(qū)分佣渴。

即把<comp :foo="bar" @update:foo="val => bar = val"></comp>簡寫為<child :foo.sync="bar"></child>,不讓使用者在父元素上進行事件監(jiān)聽了而已其他都是一樣的初斑,它通過子組件傳值改變父組件辛润,依賴props傳值把修改的父組件元素再傳回子組件而已。

小結:

父組件向子組件傳值通過props见秤;子組件向父組件傳值砂竖,我們在父組件上通過v-on監(jiān)聽子組件的事件,而子組件通過$emit(eventName) 觸發(fā)事件鹃答。

至此組件的基本知識就結束了乎澄,高深的組件有關的,下一節(jié)再說测摔。

提示:在最近幾天我會慢慢附上VUE系列教程的其他后續(xù)篇幅置济,后面還有精彩敬請期待,請大家關注我的專題:web前端。如有意見可以進行評論浙于,每一條評論我都會認真對待修噪。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市路媚,隨后出現的幾起案子黄琼,更是在濱河造成了極大的恐慌,老刑警劉巖整慎,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脏款,死亡現場離奇詭異,居然都是意外死亡裤园,警方通過查閱死者的電腦和手機撤师,發(fā)現死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拧揽,“玉大人剃盾,你說我怎么就攤上這事∮偻啵” “怎么了痒谴?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铡羡。 經常有香客問我积蔚,道長,這世上最難降的妖魔是什么烦周? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任尽爆,我火速辦了婚禮,結果婚禮上读慎,老公的妹妹穿的比我還像新娘漱贱。我一直安慰自己,他們只是感情好夭委,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布幅狮。 她就那樣靜靜地躺著,像睡著了一般闰靴。 火紅的嫁衣襯著肌膚如雪彪笼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天蚂且,我揣著相機與錄音配猫,去河邊找鬼。 笑死杏死,一個胖子當著我的面吹牛泵肄,可吹牛的內容都是我干的捆交。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼腐巢,長吁一口氣:“原來是場噩夢啊……” “哼品追!你這毒婦竟也來了?” 一聲冷哼從身側響起冯丙,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤肉瓦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胃惜,有當地人在樹林里發(fā)現了一具尸體泞莉,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年船殉,在試婚紗的時候發(fā)現自己被綠了鲫趁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡利虫,死狀恐怖挨厚,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情糠惫,我是刑警寧澤疫剃,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站寞钥,受9級特大地震影響慌申,放射性物質發(fā)生泄漏陌选。R本人自食惡果不足惜理郑,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咨油。 院中可真熱鬧您炉,春花似錦、人聲如沸役电。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽法瑟。三九已至冀膝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霎挟,已是汗流浹背窝剖。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留酥夭,地道東北人赐纱。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓脊奋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親疙描。 傳聞我的和親對象是個殘疾皇子诚隙,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容

  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內容,還有我對于 Vue 1.0 印象不深的內容起胰。關于...
    云之外閱讀 5,045評論 0 29
  • 組件是vue最強大的功能之一久又,組件可以擴展 HTML 元素,封裝可重用的代碼效五。在較高層面上籽孙,組件是自定義元素, V...
    __越過山丘__閱讀 506評論 0 2
  • 此文基于官方文檔火俄,里面部分例子有改動犯建,加上了一些自己的理解 什么是組件? 組件(Component)是 Vue.j...
    陸志均閱讀 3,805評論 5 14
  • 下載安裝搭建環(huán)境 可以選npm安裝瓜客,或者簡單下載一個開發(fā)版的vue.js文件 瀏覽器打開加載有vue的文檔時适瓦,控制...
    冥冥2017閱讀 6,029評論 0 42
  • 三月暢想 三月,是一個幸福的季節(jié)谱仪。 從今天起玻熙,我決定做一個幸福...
    甘草子的簡書閱讀 312評論 0 0