[TOC]
這是一份我給公司後端同事培訓Vue的培訓第一節課技術文件,旨在帶領他們入門Vue的開發世界,釋放更多的前端資源。現開放出來以求業界各位的技術指點或補充,感謝~
以下文件配合程式碼一起演示和講解效果更佳。程式碼地址:Vue-coe-test程式碼地址
建立第一個vue例項
使用以下命令列建立一個vue專案
$ npm install -g vue-cli
$ vue init webpack vue-coe-test
$ cd vue-coe-test
$ npm install
$ npm run dev
複製程式碼
掛載點,模板,例項
### // App.vue
<div id="app"></div>
//main.js
new Vue({
el: '#app',
router,
components: { App },
template: '<h1>{{ msg }}</h1>',
data () {
return {
msg: 'Hello,my first Vue.js App'
}
}
})
複製程式碼
- 掛載點就是Vue例項裡面el屬性對應的dom節點。
- 模板就是掛載點裡面的內容,也可以寫在例項的template中。
- 例項就是new的時候指定一個dom節點,一個模板內容,一個data屬性,vue會自動生成一段動態的資料放在掛載點中。
基本的資料表示式:
- {{ data }}
- v-text
- v-html
<h1>{{ msg }}</h1>
<div v-text="text"></div>
<div v-html="text"></div>
複製程式碼
data () {
return {
msg: 'Welcome to Your Vue.js App',
text: '<div style="color: red">I am red</div>'
}
},
複製程式碼
Vue中的屬性繫結和雙向資料繫結
<div v-bind:title="title" v-bind:class="[{ active: isActive }, 'title']">阿彌陀佛,麼麼噠</div>
data () {
return {
title: '大冰的暢銷書'
}
}
複製程式碼
現在演示的都是單向繫結,那雙向繫結是什麼呢?
資料雙向繫結是指資料可以決定頁面的展示,頁面的操作也可以改變資料。
<input type="text" v-model="title">
data () {
return {
title: '大冰的暢銷書'
}
},
複製程式碼
vue中常用指令
指令 (Directives) 是帶有 v- 字首的特殊特性。指令特性的值預期是單個 JavaScript 表示式 (v-for 是例外情況,稍後我們再討論)。指令的職責是,當表示式的值改變時,將其產生的連帶影響,響應式地作用於 DOM。
v-bind
動態地繫結一個或多個特性,或一個元件 prop 到表示式。
<a v-bind:href="url">...</a>
<!-- 縮寫 -->
<a :href="url">...</a>
複製程式碼
在這裡 href 是引數,告知 v-bind 指令將該元素的 href 特性與表示式 url 的值繫結。
<!-- class 繫結 -->
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
複製程式碼
data: {
isActive: true,
errorClass: 'text-danger'
}
複製程式碼
這裡的class是響應的,是根據data裡的值來顯示對應class的,當然你也可以加上指定的class。
v-show, v-if, v-for
v-if 是“真正”的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子元件適當地被銷燬和重建。 v-if 也是惰性的:如果在初始渲染時條件為假,則什麼也不做——直到條件第一次變為真時,才會開始渲染條件塊。 相比之下,v-show 就簡單得多——不管初始條件是什麼,元素總是會被渲染,並且只是簡單地基於 CSS 進行切換。 一般來說,v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷。因此,如果需要非常頻繁地切換,則使用 v-show 較好;如果在執行時條件很少改變,則使用 v-if 較好。
- 示例(更多用法參考官方文件)
HTML
<!-- vue -->
<div>
<h2>問:猩猩最討厭什麼線?</h2>
<div>
<button :class="[{active: item.active}, 'js-button btn-answer']" v-for="item, index in btns" @click="clickHandle(index)">{{item.text}}</button>
</div>
<div class="js-answer"></div>
<div v-show="show">
<div v-if="answer == 0">
bingo!平行線沒有相交(香蕉)
</div>
<div v-else-if="answer == 1">
你怕是個假猩猩吧
</div>
<div v-else>
你怕是個假猩猩吧
</div>
</div>
</div>
<!-- twig -->
{% set items = ['A、平行線', 'B、線段', 'C、交叉線'] %}
{% for item in items %}
<button class="js-button btn-answer"
{{ item }}
</button>
{% endfor %}
<div class="js-answer"></div>
複製程式碼
data
data () {
return {
show: false,
answer: 0,
btns: [
{
key: 0,
text: 'A、平行線',
active: false
},
{
key: 1,
text: 'B、線段',
active: false
},
{
key: 2,
text: 'C、交叉線',
active: false
}]
}
},
複製程式碼
js
<!-- jquery -->
mounted() {
$('.js-button').click(function(e) {
let index = $(e.target).index();
let answerText = '';
index ? (answerText = '你怕是個假猩猩吧') : (answerText = 'bingo!平行線沒有相交(香蕉)')
$('.js-answer').text(answerText);
$(this).addClass('active').siblings().removeClass('active');
})
}
<!-- vue -->
methods: {
clickHandle(index) {
this.show = true;
this.answer = index;
this.btns.forEach( item=> {
if (item.key == index) {
item.active = true;
} else {
item.active = false;
}
})
}
}
複製程式碼
v-on
繫結事件監聽器。事件型別由引數指定。 表示式可以是一個方法的名字或一個內聯語句,如果沒有修飾符也可以省略。
<!-- 方法處理器 -->
<button v-on:click="doThis"></button>
<!-- 縮寫 -->
<button @click="doThis"></button>
<!-- 內聯語句 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 在子元件上監聽自定義事件 (當子元件觸發“my-event”時將呼叫事件處理器) -->
<my-component @my-event="handleThis"></my-component>
複製程式碼
vue例項中方法和事件
資料傳輸方法
$emit
與$on
來進行元件之間的資料傳輸
-
1.父元件到子元件通訊
-
2.子元件到父元件的通訊
-
3.兄弟元件之間的通訊
vm.$on( event, callback )
監聽當前例項上的自定義事件。事件可以由vm.$emit觸發。回撥函式會接收所有傳入事件觸發函式的額外引數。
vm.$emit( eventName, […args] )
觸發當前例項上的事件。附加引數都會傳給監聽器回撥。
vm.$emit('test', 'hi')
vm.$on('test', function (msg) {
console.log(msg) // hi
})
複製程式碼
父子通訊,子父通訊,兄弟通訊
程式碼演示
- 父子通訊
第一種:父元件給子元件傳子,使用props
<!-- 父元件 -->
<child :sonMsg="msg"></child>
// js
import child from './children.vue';
export default {
components: {
child
}
}
複製程式碼
<!-- 子元件 -->
// html
<div class="children-text">{{sonMsg}}</div>
// js
export default {
props: {
sonMsg: {
type: String,
default: '父元件資料的預設值'
}
}
}
複製程式碼
第二種:使用$refs
傳輸資料
<!-- 父元件 -->
// html
<input type="button" @click="parentEnvet" value="父元件觸發" />
<child ref="childcomp"></child>
// js
import child from './children.vue';
export default {
components: {
child
},
methods: {
parentEnvet() {
// 注意:這裡的childFn是子元件裡已存在的方法
this.$refs.childcomp.childFn();
// 或者這樣寫:this.$refs['childcomp'].childFn();
}
}
}
複製程式碼
<!-- 子元件 -->
// js
methods: {
childFn() {
// do somethings
}
}
複製程式碼
第三種:使用$emit 和 $on
傳輸資料(包括子父通訊)
<!-- 父元件 -->
// html
<div class="parent-box">
<div class="mb20" v-text="vals"></div>
</div>
<div class="children-box">
<child @showMsg="msgFromChild" :sonMsg="msg">
</child>
</div>
// js
<script>
import child from './children.vue';
export default {
components: {
item
},
data () {
return {
vals: '我會展示子元件傳給我的資料',
msg: '我是父元件的資料,將傳給子元件~'
}
},
methods: {
// 接受子元件的資料
msgFromChild(data) {
this.vals = data;
}
}
}
</script>
複製程式碼
<!-- 子元件 -->
// html
<div>
<div class="children-text">{{parentMsg}}</div>
<button @click="send">子元件觸發</button>
</div>
// js
<script>
export default {
props: {
parentMsg: {
type: String,
default: '父元件資料的預設值'
}
},
data () {
return {
msg: '我真的是太帥了'
}
},
methods: {
send() {
// 給父元件傳遞資料
this.$emit('showMsg', this.msg)
}
}
}
</script>
複製程式碼
- 非父子元件進行通訊
思考:瞭解了父子通訊後,你認為非父子元件之間的通訊應該是怎樣的呢?
非父子元件之間傳值,需要定義個公共的例項檔案bus.js,作為中間倉庫來傳值,不然路由元件之間達不到傳值的效果。
1.建立公共的例項檔案,內容如下:
// src/util/bus.js
import Vue from 'vue'
export default new Vue();
複製程式碼
2.分別在非父子元件內引入該公共例項檔案(假如命名為Bus)
A元件在方法裡使用Bus.$emit('fnName', data)
傳送資料
// 元件A
<template>
<div>
<h3>我現在要跟B元件打個招呼</h3>
<input type="text" @change="saiHi" v-model="text"/>
</div>
</template>
<script>
import Bus from '../util/bus.js';
export default {
data () {
return {
text: '你好,我是小豬佩奇A'
}
},
methods: {
saiHi() {
Bus.$emit('saiHi', this.text);
}
}
}
</script>
複製程式碼
B元件(常在created,mounted生命週期裡)使用Bus.$on('fnName', (data) => {xxx})
接收資料
// 元件B
<template>
<div>
<div class="message-text" v-for="msg in msgs" v-text="msg.text"></div>
</div>
</template>
<script>
import Bus from '../util/bus.js';
export default {
data () {
return {
msgs: [{
text: 'hello'
}],
}
},
mounted() {
Bus.$on('saiHi', (data) => {
this.msgs.push({text: data});
})
},
}
</script>
複製程式碼
Vue中計算屬性computed和監聽屬性函式watch
定義
**計算屬性:**基於它們的依賴進行快取的。只在相關依賴發生改變時它們才會重新求值。 **偵聽屬性:**當需要在資料變化時執行非同步或開銷較大的操作時,這個方式是最有用的。通常更好的做法是使用計算屬性而不是命令式的 watch 回撥
computed和watch比較
<div id="demo">{{ fullName }}</div>
複製程式碼
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
複製程式碼
fullName屬性依賴於firstName和lastName,這裡有個缺點就是,無論firstName或lastName其中的任何一個發生改變時,都要呼叫不同的監聽函式來更新fullName屬性。但是當我們使用計算屬性時,程式碼就會變得更加簡潔。
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
複製程式碼
這時,我們只要監聽fullName屬性就行,至於firstName或lastName屬性的任何改變,我們都可以通過fullName的getter()方法得到最終的fullName值。
另外需要注意的是,計算屬性預設只有 getter ,不能通過this.xxx = 'xxx'
這種方式去賦值,不過在需要時你也可以提供一個 setter,允許同時設定getter()、setter()方法。如下:
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
複製程式碼
-
相同點: 首先它們都是以Vue的依賴追蹤機制為基礎的,它們的共同點是:都是希望在依賴資料發生改變的時候,被依賴的資料根據預先定義好的函式,發生“自動”的變化。
-
不同點: watch和computed各自處理的資料關係場景不同 (1) watch擅長處理的場景:一個資料影響多個資料。適合監控場景,某【一個】變數改變時需要做什麼操作;類似於onchange,適合耗時操作,如網路請求等。 (2) computed擅長處理的場景:一個資料受多個資料影響。某【一些】變數發生變化時,影響的【單個】結果對應地發生改變。
生命週期
-
建立前/後**(beforeCreated/created)**: 在beforeCreated階段,vue例項的掛載元素
$el
和資料物件data都為undefined,還未初始化。在created階段,vue例項的資料物件data有了,$el
還沒有。 -
載入前/後**(beforeMount/mounted)**:在beforeMount階段,vue例項的$el和data都初始化了,但還是掛載之前為虛擬的dom節點,data.message還未替換。在mounted階段,vue例項掛載完成,data.message成功渲染。
-
更新前/後**(beforeUpdate/updated)**:當data變化時,會觸發beforeUpdate和updated方法。
-
銷燬前/後**(beforeDestory/destroyed)**:在執行destroy方法後,對data的改變不會再觸發周期函式,說明此時vue例項已經解除了事件監聽以及和dom的繫結,但是dom結構依然存在