Vue 學(xué)習(xí)筆記

[TOC]

簡介

Vue 是一個用于構(gòu)建 Web 用戶界面的漸進式 JavaScript 框架囱怕。其核心庫只關(guān)注視圖層(view layer)耙饰,同時具備良好的第三方支持庫生態(tài)用以應(yīng)付構(gòu)建復(fù)雜大型單頁應(yīng)用(SPA:Single-Page Application)。

MVVM 模型

Vue 雖然沒有完全遵循 MVVM(Model-View-ViewModel)模型塑崖,當其設(shè)計也受到了 MVVM 的啟發(fā),以數(shù)據(jù)驅(qū)動界面惹苗,如下圖所示:

Vue MVVM

Vue 中殿较,充當 ViewModel 的是一個 Vue 實例(new Vue({})),該 Vue實例 作用于某一個 HTML 元素上桩蓉,全權(quán)代理該元素節(jié)點的所有操作淋纲。

Vue實例 內(nèi)部通過 DOM Listeners 可以觀測到頁面上 DOM 元素的變化,從而將該種變化同步更改到 Model 中的對應(yīng)數(shù)據(jù)院究。

同時通過 Data Bindings洽瞬,當 Model 中的數(shù)據(jù)改變時,則會對相應(yīng)視圖上的顯示進行更改业汰,從而實現(xiàn)了 View 和 Model 的數(shù)據(jù)雙向綁定伙窃。

:傳統(tǒng)的 Web 編程模型是 結(jié)構(gòu)驅(qū)動,即要對一個 DOM 節(jié)點進行操作样漆,第一步就是要獲取該 DOM 節(jié)點對象对供,然后再修改數(shù)據(jù)更新到節(jié)點上。
Vue 的中心思想是 數(shù)據(jù)驅(qū)動氛濒,要更改界面产场,其實就是要更改數(shù)據(jù)。
簡而言之舞竿,在 Vue 中京景,不應(yīng)當考慮操作 DOM,而是專注于 操作數(shù)據(jù)骗奖。

安裝

Vue 的安裝有多種方法确徙,這里主要介紹兩種方法:

  1. 通過<script>標簽直接引入:
<!-- 對于制作原型或?qū)W習(xí),你可以這樣使用最新版本 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 或者-->
<!-- 對于生產(chǎn)環(huán)境执桌,我們推薦鏈接到一個明確的版本號和構(gòu)建文件鄙皇,以避免新版本造成的不可預(yù)期的破壞:-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>

:通過<script>標簽引入,Vue會被注冊為一個全局變量

  1. 直接使用官方提供的快速搭建復(fù)雜單頁面應(yīng)用 (SPA) 的腳手架 vue-cli
  • 首先全局安裝該腳手架 vue-cli
npm install -g @vue/cli
vue create <project-name>

:使用 vue-cli 前需確保系統(tǒng)已安裝 nodejs仰挣。

以上兩步操作完成伴逸,我們便創(chuàng)建完成一個 Vue 項目。

在項目的 package.json 中膘壶,可以看到 vue-cli 提供了兩個腳本命令讓我們運行與打包項目:

  • npm run serve:運行項目
  • npm run build:打包項目到 dist 文件夾

組件化

Vue 的兩大特性為 數(shù)據(jù)驅(qū)動組件化错蝴。

通常一個大的頁面可以劃分為許多個小區(qū)塊,這些小區(qū)塊有些結(jié)構(gòu)是相似的颓芭,我們可以將這些相似的區(qū)塊抽象出一個統(tǒng)一的結(jié)構(gòu)顷锰,方便復(fù)用,這種抽象結(jié)構(gòu)的方法即稱為組件化亡问。

在實際項目開發(fā)中官紫,一個大的頁面通常都是由許多個小的組件構(gòu)造而成的,如下圖所示:

組件化

Vue 提供了兩種組件定義的方式:

<body>
    <div id="app">
        <my-component />
    </div>

    <script>
        const myComponent = Vue.component('my-component',{
            data(){
                return {
                    'message': 'Hello Global Component'
                }
            },
            template:`
            <h1>{{message}}</h1>
            `
        });
        const vm = new Vue({
            el: "#app",
        });
    </script>
</body>
  • 局部組件:對于全局組件來說悼吱,即使頁面沒有使用該組件,組件也會被注入到最終的構(gòu)建結(jié)果中良狈,導(dǎo)致了 JavaScript 文件的無謂增加后添。而局部組件可以做到按需加載,需要哪些組件薪丁,按需引入即可遇西,更加靈活高效。
    定義方式:通過一個普通的 JavaScript 對象來定義組件严嗜,然后 Vue實例 按需引入需要的組件即可粱檀。
<body>
    <div id="app">
        <component-a></component-a>
        <component-b />
    </div>
    <script>
        const componentA = {
            template: `
            <h1>Component A</h1>
            `
        };
        const componentB = {
            template: `
            <h1>Component B</h1>
            `
        };

        const vm = new Vue({
            el: '#app',
            components: {  // 按需引入需要的組件
                componentA,
                componentB
            }
        });
    </script>
</body>

最佳實踐:在 Vue 中,組件通常都定義到一個單獨的.vue文件中漫玄,其他組件需要時茄蚯,導(dǎo)入相應(yīng)組件的.vue文件即可。

// MyComponent.vue
<template>
  <h1>{{message}}</h1>
</template>

<script>
export default {
  name: "MyCompoent",
  data() {
    return {
      message: "Hello MyComponent!"
    };
  }
};
</script>

<style scoped>
h1 {
  background: red;
}
</style>

可以直接使用以下命令直接運行.vue文件睦优,查看組件展示效果:

vue serve MyComponent.vue --open

也可以在其他組件內(nèi)導(dǎo)入該組件渗常,進行使用:

<template>
  <div id="app">
    <h1>Parent Component</h1>
    <!-- 使用組件 -->
    <my-component />
  </div>
</template>

<script>
// 導(dǎo)入組件
import MyComponent from "./MyComponent.vue";

export default {
  name: "app",
  components: {
    MyComponent // 引入組件
  }
};
</script>

:在 Vue 中,組件實質(zhì)是帶有一個名字的 Vue實例汗盘,其性質(zhì)與 Vue實例 基本一致(遵循 Vue實例 的生命周期等內(nèi)容)皱碘,特點是多了個組件復(fù)用功能。

Vue實例

Vue 實例充當 ViewModel 角色隐孽,負責(zé) View 和 Model 之間的數(shù)據(jù)綁定:

new Vue(Options)

當創(chuàng)建一個 Vue 實例時癌椿,你可以傳入一個選項對象Options,該Options的選項列表有如下可選:

下面列舉一些Options常用選項

  • 選項 / 數(shù)據(jù)data
    描述:Vue 實例的數(shù)據(jù)對象菱阵,用于數(shù)據(jù)的存儲與顯示踢俄。
    類型:Object | Function
<body>
    <div id="app">
        <h1>{{message}}</h1>
    </div>
    <script>
        const vm = new Vue({
            el: "#app",
            data: {
                message: "Hello Vue!"
            }
        });
    </script>
</body>

:Vue 將會遞歸將data的屬性轉(zhuǎn)換為getter/setter,從而讓data的屬性能夠響應(yīng)數(shù)據(jù)變化晴及。比如在控制臺輸入vm.message = 'Hi Vue!!!都办,可以觀察到頁面數(shù)據(jù)發(fā)生了更改。

組件 中的data屬性必須是Fcuntion類型抗俄,其返回一個Object脆丁,原因是組件復(fù)用時世舰,保證每個新組件都有獨一的一份數(shù)據(jù)拷貝动雹。

// MyComponent.vue
<template>
    <h1>{{message}}</h1>
</template>

<script>
export default {
    name: 'MyComponent',
    data(){       // 函數(shù)類型
        return {  // 返回數(shù)據(jù)對象
            'message': 'Hello Vue!'
        }
    }
}
</script>
  • 選項 / 數(shù)據(jù)props
    描述:該屬性用于接收來自父組件的數(shù)據(jù)。
    類型:Array<string> | Object跟压。
    當傳遞的是Object類型時胰蝠,則可以基于對象的語法使用以下選項:
    ?type:指定數(shù)據(jù)類型,該值可以為原生類型(StringNumber茸塞,Boolean躲庄,ArrayObject钾虐,Date噪窘,FunctionSymbol)效扫,自定義構(gòu)造函數(shù)倔监,或上述內(nèi)容組成的數(shù)組。
    ? default:any:為該prop指定一個默認值菌仁。如果該prop沒有被傳入浩习,則使用該默認值。對象或數(shù)組的默認值必須從一個工廠函數(shù)返回济丘。
    ? required: Boolean:定義該prop是否為必填項谱秽。
    ? validator: Function:自定義驗證函數(shù),對該prop進行校驗摹迷。
Vue.component('my-component', {
  props: {
    // 基礎(chǔ)的類型檢查 (`null` 和 `undefined` 會通過任何類型驗證)
    propA: Number,
    // 多個可能的類型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 帶有默認值的數(shù)字
    propD: {
      type: Number,
      default: 100
    },
    // 帶有默認值的對象
    propE: {
      type: Object,
      // 對象或數(shù)組默認值必須從一個工廠函數(shù)獲取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定義驗證函數(shù)
    propF: {
      validator: function (value) {
        // 這個值必須匹配下列字符串中的一個
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})
var Comp = Vue.extend({
  props: ['msg'],
  template: '<div>{{ msg }}</div>'
})

var vm = new Comp({
  propsData: {
    msg: 'hello'
  }
})

propsData屬性只能用于new創(chuàng)建的實例中峡碉。

  • 選項 / 數(shù)據(jù)methods
    描述:方法定義听绳,Vue實例 可以直接訪問這些方法,或在指令表達式中直接調(diào)用這些方法异赫。
    類型:{ [key: string]: Function }
var vm = new Vue({
  data: { a: 1 },
  methods: {
    plus: function () {
      this.a++
    }
  }
})
vm.plus()
vm.a // 2

methods中的this自動綁定到當前 Vue實例椅挣。

  • 選項 / 數(shù)據(jù)computed
    描述:計算屬性,主要用于對data數(shù)據(jù)進行計算轉(zhuǎn)換塔拳。
    類型:{ [key: string]: Function | { get: Function, set: Function } }
<template>
  <div>
    <h1>獲取數(shù)據(jù): {{computedData}}</h1>
    <h1>設(shè)置數(shù)據(jù):{{setData = 3}}</h1>
    <h1>獲取數(shù)據(jù): {{setData}}</h1>
  </div>
</template>

<script>
export default {
  name: "Computed",
  data() {
    return {
      count: 1
    };
  },
  computed: {
    // 只讀取 data
    computedData() {
      return this.count + 10;
    },
    // 讀取和設(shè)置 data
    setData: {
      get() {
        return this.count + 1;
      },
      set(value) {
        this.count = value;
      }
    }
  }
};
</script>

computed類型為Object鼠证,其具有如下特點:

  1. computed內(nèi)部定義的屬性為訪問器屬性,即具備gettersetter靠抑,且其內(nèi)部this自動綁定到當前 Vue實例量九。
  2. computed會自動 緩存 計算結(jié)果,只有當依賴的響應(yīng)式屬性變化時颂碧,computed才會重新進行計算荠列。
    緩存computedmethods的最大區(qū)別之處,methods每次調(diào)用一定會運行函數(shù)载城,而computed則不一定肌似。
  • 選項 / 數(shù)據(jù)watch
    描述:偵聽屬性,用于監(jiān)控datacomputed的數(shù)據(jù)诉瓦,當數(shù)據(jù)變更時進行回調(diào)通知川队。
    類型:{ [key: string]: string | Function | Object | Array }
    watch類型為Object力细,其內(nèi)部屬性的類型有多種:string | Function | Object | Array,這里簡單介紹 3 種:
  1. string:字符串表示回調(diào)函數(shù)名固额,當數(shù)據(jù)改變時眠蚂,回調(diào)該函數(shù):
const vm = new Vue({
    el: '#app',
    data: {
        a: 1
    },
    methods: {
        aChanged(value, oldValue) {
            console.log(`a changed: new:${value} --> old:${oldValue}`);
        }
    },
    watch: {
        a: 'aChanged'
    }
});
  1. Function:當數(shù)據(jù)改變時,直接回調(diào)該函數(shù):
const vm = new Vue({
    el: '#app',
    data: {
        a: 1
    },
    watch: {
        a(value, oldValue) {
            console.log(`a changed: new:${value} --> old:${oldValue}`);
        }
    }
});
  1. Object:對監(jiān)控的屬性為對象時斗躏,Vue 默認只能監(jiān)控到對象重新被賦值的變化逝慧,而如果需要監(jiān)聽對象內(nèi)部屬性的變化,則可使用該選項啄糙,其中:
    handler代表回調(diào)函數(shù)馋艺。
    deep用來控制監(jiān)聽對象屬性的層級,deep=true時只要對象內(nèi)部 property 改變(不管嵌套有多深)迈套,都會監(jiān)聽到捐祠。
    immediate用來設(shè)置是否立即產(chǎn)生回調(diào)。當immediate=true時桑李,回調(diào)函數(shù)會立即被調(diào)用踱蛀,傳遞的是屬性當前的值。
const vm = new Vue({
    el: '#app',
    data: {
        a: {
            aa: {
                aaa: 3
            }
        }
    },
    watch: {
        a: {
            handler(value, oldValue) {
                console.log(`a changed: new:${value} --> old:${oldValue}`);
            },
            deep: true // 被監(jiān)聽對象的 property 改變時被調(diào)用贵白,無論嵌套的有多深
        }
    }
});

:大多數(shù)情況下率拒,觀察和響應(yīng)數(shù)據(jù)變更使用計算屬性(computed)便足夠了,但是當在數(shù)據(jù)變化時需要執(zhí)行異步或開銷較大的操作時禁荒,則此時使用偵聽屬性(watch)會更加適合猬膨。

  • 選項 / DOMel
    描述:設(shè)置 Vue實例 的掛載目標節(jié)點
    類型:string | Element
new Vue({
    el: '#app'
});

:如果在實例化時存在這個選項,實例將立即進入編譯過程呛伴,否則勃痴,需要顯式調(diào)用vm.$mount()手動開啟編譯。

  • 選項 / DOMtemplate
    描述:字符串模板热康,模板會 替換 掛載的元素沛申。
    類型:string
<div id="app"></div>

const vm = new Vue({
    el: '#app',
    template: '<h1>template</h1>' // <div> 會被 <h1> 完全覆蓋
})
  • 選項 / DOMrender
    描述:字符串模板的代替方案,允許你發(fā)揮 JavaScript 最大的編程能力姐军。該渲染函數(shù)接收一個createElement方法作為第一個參數(shù)用來創(chuàng)建VNode铁材。
    類型:(createElement: () => VNode) => VNode
<div id="app"></div>

new Vue({
    render(createElement) {
        return createElement('div', {
            class: 'rendered'
        },
            [
                createElement('h1', {
                    domProps: {
                        innerHTML: 'div>h1 rendered by vue'
                    }
                })
            ]
        );
    }}).$mount('#app');

Vue 推薦在絕大多數(shù)情況下使用模板來創(chuàng)建你的 HTML,只有在一些特殊場景下奕锌,比如模板冗長且具備重復(fù)元素著觉,則此時使用渲染函數(shù)render通過編寫 JavaScript 代碼來渲染出頁面會更加方便簡潔。

生命周期

每個 Vue實例 在掛載到頁面時惊暴,都會經(jīng)歷一系列的初始化過程饼丘,例如,需要設(shè)置數(shù)據(jù)監(jiān)聽缴守、編譯模板葬毫、將實例掛載到 DOM 并在數(shù)據(jù)變化時更新 DOM 等镇辉。在創(chuàng)建 Vue實例 的這整個過程中屡穗,Vue 為我們預(yù)留出了一些 Hook 點贴捡,方便我們在 Vue實例 創(chuàng)建過程的某個生命周期中進行一些操作。如下圖所示:

vue lifecycle

:圖片來源于網(wǎng)上村砂,侵刪烂斋。

這些預(yù)留的生命周期鉤子函數(shù)總共有如下幾個:

  • beforeCreate:Vue實例 初始化之后,此時datamethods中的數(shù)據(jù)還未進行初始化础废,因此無法獲取汛骂。

  • created:表示 Vue實例 創(chuàng)建完成,但還未掛載到頁面上评腺,此時datamethods都已經(jīng)初始化成功帘瞭,可以對其進行調(diào)用獲取,而掛載階段未開始蒿讥,所以$el屬性目前不可見蝶念。

  • beforeMount:在掛載開始之前被調(diào)用,此時模板已在內(nèi)存中被編譯完成芋绸,只是尚未掛載到頁面上媒殉,因此,此時頁面上顯示的還是未渲染的結(jié)構(gòu)摔敛。

  • mounted:掛載完成廷蓉,此時頁面會顯示我們渲染的視圖。如果想要操作頁面上的 DOM 節(jié)點马昙,最早的時間就是該處桃犬。
    mounted不會 承諾所有的子組件也都一起被掛載。如果你希望等到整個視圖都渲染完畢行楞,可以用vm.$nextTick替換掉mounted

mounted: function () {
  this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been rendered
  })
}
  • beforeUpdate:數(shù)據(jù)更新時調(diào)用疫萤,此時內(nèi)存中data數(shù)據(jù)已更新,但頁面中顯示的數(shù)據(jù)還未更新敢伸,數(shù)據(jù)與頁面不同步扯饶。這里適合在更新之前訪問現(xiàn)有的 DOM,比如手動移除已添加的事件監(jiān)聽器池颈。

  • updated:新數(shù)據(jù)成功渲染到頁面尾序,此時數(shù)據(jù)與頁面處于同步狀態(tài)。
    updated不會 承諾所有的子組件也都一起被掛載躯砰。如果你希望等到整個視圖都渲染完畢每币,可以用vm.$nextTick替換掉mounted

  • activated:激活狀態(tài),表示當前組件處于前臺頁面琢歇,用戶可與該組件進行交互兰怠。
    :只有組件被內(nèi)置組件keep-alive包裹時梦鉴,該鉤子才有可能被調(diào)用。

<template>
    <keep-alive>
        <my-component />
    </keep-alive>
</template>
  • deactivated:停用狀態(tài)揭保,表示當前組件處于后臺頁面肥橙,用戶不能與之交互。
    :只有組件被內(nèi)置組件keep-alive包裹時秸侣,該鉤子才有可能被調(diào)用存筏。

  • beforeDestroy:實例銷毀之前調(diào)用。在這一步味榛,實例仍然完全可用椭坚,即此時實例的datamethods等所有數(shù)據(jù)完全可用搏色。

  • destroyed:Vue 實例銷毀后調(diào)用善茎。此時 Vue實例 指向的所有東西都會解綁定,所有的事件監(jiān)聽器會被移除频轿,所有的子實例也會被銷毀垂涯。

  • errorCaptured:當捕獲一個來自子孫組件的錯誤時被調(diào)用。此鉤子會收到三個參數(shù):錯誤對象略吨、發(fā)生錯誤的組件實例以及一個包含錯誤來源信息的字符串集币。此鉤子可以返回false以阻止該錯誤繼續(xù)向上傳播。

指令

指令 (Directives) 是帶有v-前綴的特殊屬性翠忠。

Vue 提供了以下內(nèi)置的指令:

  • v-text:更新元素的textContent鞠苟,該指令與使用{{ Mustache }}插值效果一樣。
    類型:string
<span v-text="msg"></span>
<!-- 和下面的一樣 -->
<span>{{msg}}</span>
  • v-html:更新元素的innerHTML秽之。
    類型:string
<template>
  <div id="app">
    <div v-html="html"></div>
  </div>
</template>

<script>
export default {
  name: "app",
  data() {
    return {
      html: '<a  >baidu</a>'
    };
  },
};
</script>

v-html的內(nèi)容只會按普通 HTML 插入当娱,不會作為 Vue 模板進行編譯。
:在單文件組件里考榨,scoped 的樣式不會應(yīng)用在 v-html 內(nèi)部跨细,因為那部分 HTML 沒有被 Vue 的模板編譯器處理。
:在網(wǎng)站上動態(tài)渲染任意 HTML 是非常危險的河质,因為容易導(dǎo)致 XSS 攻擊冀惭。只在可信內(nèi)容上使用v-html,永不用在用戶提交的內(nèi)容上掀鹅。

  • v-show:條件渲染散休,根據(jù)表達式之真假值,切換元素的displayCSS 屬性乐尊。
    類型:any
<h1 v-show="ok">Hello!</h1>

v-show不支持<template>元素戚丸,也不支持v-else

  • v-if:條件渲染扔嵌,根據(jù)表達式的值的真假條件渲染元素限府。在切換時元素及它的數(shù)據(jù)綁定 / 組件被銷毀并重建夺颤。如果元素是<template>,將提出它的內(nèi)容作為條件塊。
    類型:any
<h1 v-if="awesome">Vue is awesome!</h1>

:當和v-if一起使用時,v-for的優(yōu)先級比v-if更高。
v-show = false時只是把元素設(shè)置為:display:none,元素還留著 DOM 樹上必搞。
v-if = false時,元素會被整個移除没佑,其上綁定的數(shù)據(jù)/組件都會被銷毀原叮。

  • v-elsev-ifv-if-else的分支。
    類型:無
<div v-if="Math.random() > 0.5">
  Now you see me
</div>
<div v-else>
  Now you don't
</div>
  • v-else-ifv-if的分支咱台。
    類型:any
<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>
  • v-for:遍歷源數(shù)據(jù),渲染元素列表回溺。
    類型:Array | Object | number | string | Iterable
<div v-for="item in items">
  {{ item.text }}
</div>
<!-- 另外也可以為數(shù)組索引指定別名 (或者用于對象的鍵):-->
<div v-for="(item, index) in items"></div>
<div v-for="(val, key) in object"></div>
<div v-for="(val, name, index) in object"></div>

v-for渲染元素時春贸,默認使用“就地更新”策略,即當列表數(shù)據(jù)改變時遗遵,Vue 不會移動當前 DOM 元素來重新匹配數(shù)據(jù)項萍恕,而是根據(jù)索引位置重新渲染數(shù)據(jù)。比如:

現(xiàn)在我們有數(shù)據(jù)項:

data() {
    return {
        datas: [
            { id: 1, name: "one" },
            { id: 2, name: "two" },
            { id: 3, name: "three" }
        ]
    }

將這些數(shù)據(jù)項渲染到頁面上:

<template>
  <div>
    <ul>
      <li v-for="(item,index) in datas">
        <input type="checkbox" />
        {{item.name}}
      </li>
    </ul>
</template>

我們使用v-for將每條數(shù)據(jù)項渲染到一個<li>上车要,此時顯示效果如下:

如果此時我們勾選第一個<li>checkbox允粤,即one勾選上,然后再往數(shù)據(jù)列表前面添加一個數(shù)據(jù):this.datas.unshift({ id: 4, name: "four" })翼岁,則可以看到顯示效果如下:

unshift

可以看到类垫,我們想要的是one被勾選了,但是效果是數(shù)據(jù)列表首位被勾選琅坡。出現(xiàn)這種現(xiàn)象的原因就是v-for默認采用的“就地更新”策略:它會復(fù)用已渲染完成的 DOM 元素悉患,然后只對變化的數(shù)據(jù)進行修改,比如這里復(fù)用了第一條<li><checkbox>one</li>榆俺,添加數(shù)據(jù)項售躁,對第一條<li>來說,他的數(shù)據(jù)改變了茴晋,但是<checkbox>不包含在數(shù)據(jù)項里陪捷,因此只會修改數(shù)據(jù),將one修改為four晃跺,而checkbox仍保持勾選狀態(tài)揩局。

因此,“就地更新”策略是高效的掀虎,但是 只適用于不依賴子組件狀態(tài)或臨時 DOM 狀態(tài)(例如:表單輸入值) 的列表渲染輸出凌盯。

而要解決上述問題付枫,只需為v-for提供一個key屬性(key必須是唯一的),這樣 Vue 就可以識別出數(shù)據(jù)項對應(yīng)的渲染條目驰怎,從而重用和重新排序現(xiàn)有元素:

<template>
  <div>
    <ul>
      <li v-for="(item,index) in datas" :key="item.id">
        <input type="checkbox" />
        {{item.name}}
      </li>
    </ul>
</template>

由于新添加的數(shù)據(jù)id=4阐滩,當前已存在的<li>沒有與之對應(yīng)的標識key,因此 Vue 會重新渲染一個新的<li>县忌,并將其與id=4對應(yīng)起來掂榔,結(jié)果如下圖所示:

v-for key

:“就地更新”策略其實就是使用索引作為節(jié)點標識,即:key=index症杏。

  • v-on:綁定事件監(jiān)聽器装获。
    縮寫:@
    類型:Function | Inline Statement | Object
    參數(shù):event
    修飾符:
    ? .stop:調(diào)用event.stopPropagation(),停止事件分發(fā)厉颤。
    ? .prevent:調(diào)用event.preventDefault()穴豫,取消事件的默認動作。
    ? .capture:添加事件偵聽器時使用capture(捕獲)模式逼友。
    ? .self:只有當事件是從偵聽器綁定的元素本身觸發(fā)時才觸發(fā)回調(diào)精肃。
    ? .{keyCode | keyAlias}:只有當事件是從特定鍵觸發(fā)時才觸發(fā)回調(diào)。
    ? .native:監(jiān)聽組件根元素的原生事件帜乞。
    ? .once:只觸發(fā)一次回調(diào)司抱。
    ? .left: 只有當點擊鼠標左鍵時觸發(fā)。
    ? .right: 只有當點擊鼠標右鍵時觸發(fā)黎烈。
    ? .middle: 只有當點擊鼠標中鍵時觸發(fā)习柠。
    ? .passive:以{ passive: true }模式添加偵聽器
<!-- 方法處理器 -->
<button v-on:click="doThis"></button>

<!-- 動態(tài)事件 (2.6.0+) -->
<button v-on:[event]="doThis"></button>

<!-- 內(nèi)聯(lián)語句 -->
<button v-on:click="doThat('hello', $event)"></button>

<!-- 縮寫 -->
<button @click="doThis"></button>

<!-- 動態(tài)事件縮寫 (2.6.0+) -->
<button @[event]="doThis"></button>

<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>

<!-- 阻止默認行為 -->
<button @click.prevent="doThis"></button>

<!-- 阻止默認行為,沒有表達式 -->
<form @submit.prevent></form>

<!--  串聯(lián)修飾符 -->
<button @click.stop.prevent="doThis"></button>

<!-- 鍵修飾符怨喘,鍵別名 -->
<input @keyup.enter="onEnter">

<!-- 鍵修飾符津畸,鍵代碼 -->
<input @keyup.13="onEnter">

<!-- 點擊回調(diào)只會觸發(fā)一次 -->
<button v-on:click.once="doThis"></button>

<!-- 對象語法 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>

v-on用在普通元素上時,只能監(jiān)聽 原生 DOM 事件必怜。用在自定義組件上時肉拓,也可以監(jiān)聽子組件觸發(fā)的自定義事件

<my-component @my-event="handleThis"></my-component>

<!-- 內(nèi)聯(lián)語句 -->
<my-component @my-event="handleThis(123, $event)"></my-component>

<!-- 組件中的原生事件 -->
<my-component @click.native="onClick"></my-component>
  • v-bind:動態(tài)綁定
    縮寫::
    類型:any (with argument) | Object (without argument)
    參數(shù):attrOrProp (optional)
    修飾符
    ? .prop:被用于綁定 DOM 屬性 (property)
    ? .camel:將 kebab-case 特性名轉(zhuǎn)換為 camelCase(駝峰式)
    ? .sync:會擴展成一個更新父組件綁定值的 v-on 偵聽器
<!-- 綁定一個屬性 -->
<img :src="imageSrc">

<!-- 動態(tài)特性名 (2.6.0+) -->
<button :[key]="value"></button>

<!-- class 綁定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]">

<!-- style 綁定 -->
<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>

<!-- 綁定一個有屬性的對象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>

<!-- 通過 prop 修飾符綁定 DOM 屬性 -->
<div v-bind:text-content.prop="text"></div>

<!-- prop 綁定梳庆∨荆“prop”必須在 my-component 中聲明。-->
<my-component :prop="someThing"></my-component>

<!-- 通過 $props 將父組件的 props 一起傳給子組件 -->
<child-component v-bind="$props"></child-component>

<!-- 支持綁定駝峰命名屬性 -->
<svg :view-box.camel="viewBox"></svg>
  • v-model:表單控件與數(shù)據(jù)屬性的雙向綁定膏执。
    修飾符
    ? .lazy:使用<input>change事件進行同步驻售。
    ? .number:自動將字符串轉(zhuǎn)為數(shù)字。
    ? .trim:輸入首尾空格過濾更米。
<input type="text" v-model="message" />

<script>
export default {
  name: 'VModel',
  data() {
    return {
      message: ''
    };
  }
};
</script>
  • v-slot:插槽欺栗。
    縮寫:#
    當我們定義組件的時候,有些內(nèi)容可能需要由父組件傳入,因此迟几,此時可以使用插槽消请,預(yù)留出位置給到父組件進行自定義內(nèi)容傳入:
// 子組件:預(yù)留插槽
<template>
  <div>
    <h1>Son Component</h1>
    <!-- 預(yù)留插槽 -->
    <slot></slot>
  </div>
</template>

// 父組件:傳入插槽內(nèi)容
<template>
  <div>
    <h1>Parent Component</h1>
    <son-component>
        <h2>slot: content from Parent Component</h2>
    </son-component>
  </div>
</template>

? 后備內(nèi)容:可以通過為<slot>內(nèi)部提供默認內(nèi)容,只有當父組件顯示傳入內(nèi)容時类腮,才會覆蓋默認內(nèi)容:

<slot>
    <h1>Default Content</h1>
</slot>

? 具名插槽:我們可以給插槽進行命名(使用name屬性)臊泰,這樣父組件就可指定名字(使用v-slot指令)對特定的插槽進行覆蓋:

// 子組件模板
<template>
  <div>
    <h1>Son Component</h1>
    <!-- 預(yù)留命名插槽 -->
    <slot name="header"></slot>
    <main>
      <!-- name="default" -->
      <slot></slot>
    </main>
    <slot name="footer"></slot>
  </div>
</template>

// 父組件
<template>
  <div>
    <h1>Parent Component</h1>
    <son-component>
      <template v-slot:header>
        <h2>替換 header 插槽</h2>
      </template>
      <h3>替換默認插槽</h3>
      <template #footer>
        <h2>替換 footer 插槽</h2>
      </template>
    </son-component>
  </div>
</template>

v-slot只能添加在一個<template>或 組件 上。
:默認插槽其實也是一個具名插槽蚜枢,其名稱為:default缸逃。

? 插槽 prop:使用 插槽 prop 可以傳遞子組件的數(shù)據(jù)給到父組件,使父組件可以在覆蓋插槽的內(nèi)容上使用子組件的數(shù)據(jù):

// 子組件
<slot :msg="message"></slot>

<script>
export default {
  name: 'SonComponent',
  data() {
    return {
      message: 'Hello from Son Component!'
    };
  }
};
</script>

// 父組件:slotProps 接收子組件的 插槽props
<son-component #default="slotProps">
    {{slotProps.msg}}
</son-component>
  • v-pre:跳過該元素及其子元素的編譯過程厂抽⌒杵担可以用來顯示原始 Mustache 標簽。
    類型:無
<span v-pre>{{ this will not be compiled }}</span>
  • v-cloak:這個指令保持在元素上直到關(guān)聯(lián)實例結(jié)束編譯修肠。通常結(jié)合 CSS 規(guī)則來達到隱藏未編譯的 Mustache 標簽直到實例準備完畢贺辰。
    類型:無
[v-cloak] {
  display: none;
}

<div v-cloak>
  {{ message }}
</div>
  • v-once:只渲染元素和組件 一次户盯。隨后的重新渲染嵌施,元素/組件及其所有的子節(jié)點將被視為靜態(tài)內(nèi)容并跳過。這可以用于優(yōu)化更新性能莽鸭。
<span v-once>This will never change: {{msg}}</span>
  • 自定義指令:在 Vue2.0 中吗伤,代碼復(fù)用和抽象的主要形式是組件。然而硫眨,有的情況下足淆,你仍然需要對普通 DOM 元素進行底層操作,這時候就會用到自定義指令礁阁。

Vue 提供了兩種自定義指令的方式:

  1. 全局指令:使用Vue.directive
// 注冊一個全局自定義指令 `v-focus`
Vue.directive('focus', {...})
  1. 局部指令:組件中定義一個directives屬性:
// 注冊一個局部自定義指令 `v-focus`
directives: {
  focus: {...}
}
  • 鉤子函數(shù):一個指令定義對象可以提供如下幾個鉤子函數(shù) (均為可選):
    ? bind:指令第一次綁定到元素時調(diào)用巧号。改鉤子只會被調(diào)用一次,可在此做一些初始化設(shè)置姥闭。
    ? inserted:被綁定元素插入父節(jié)點時調(diào)用 (僅保證父節(jié)點存在丹鸿,但不一定已被插入文檔中)。
    ? update:所在組件的 VNode 更新時調(diào)用棚品,但是可能發(fā)生在其子 VNode 更新之前靠欢。
    ? componentUpdated:指令所在組件的 VNode 及其子 VNode 全部更新后調(diào)用。
    ? unbind:指令與元素解綁時調(diào)用铜跑。改鉤子只會被調(diào)用一次门怪,可在此做一些資源釋放操作。

示例:使用自定義指令v-customtext模擬v-text

<template>
  <h1 v-customtext="msg"></h1>
</template>

<script>
export default {
  name: 'customeDirective',
  data() {
    return {
      msg: 'Hello Custom Directives!'
    };
  },
  directives: {
    customtext: {
      inserted(el, binding, vnode, oldVnode) {
        el.innerText = binding.value;
      }
    }
  }
};
</script>

其他

  • 組件間通信:

? 父傳子:子組件通過props屬性可接收父組件傳遞過來的變量:

// ParentComponent.vue
<template>
  <son-component :msg="message" />
</template>

<script>
import SonComponent from './SonComponent';

export default {
  name: 'ParentComponent',
  components: {
    SonComponent
  },
  data() {
    return {
      message: 'data from Parent Component'
    };
  }
};
</script>

// SonComponent.vue
<template>
  <h1>{{msg}}</h1>
</template>

<script>
export default {
  name: 'SonComponent',
  props: {
    msg: {
      type: String,
      required: true
    }
  }
};
</script>

? 子傳父:子組件可以通過$emit發(fā)送自定義事件向父組件傳值锅纺,父組件直接注冊接收該事件即可:

// SonComponent.vue
<template>
  <button @click="sendEvent">點擊發(fā)送事件</button>
</template>

<script>
export default {
  name: 'SonComponent',
  methods: {
    sendEvent() {
      // 發(fā)送自定義事件
      this.$emit('eventFromChild', 'data from Son Component!!');
    }
  }
};
</script>

// ParentComponent.vue
<template>
  <div>
    <!-- 接收事件 -->
    <son-component @eventFromChild="recvChildEvent" />
    <h1>{{data}}</h1>
  </div>
</template>

<script>
import SonComponent from './SonComponent';

export default {
  name: 'ParentComponent',
  components: {
    SonComponent
  },
  data() {
    return {
      data: 'hhhh'
    };
  },
  methods: {
    recvChildEvent(data) {
      this.data = data;
    }
  }
};
</script>

? 父傳子孫:父組件/祖先組件通過provide提供變量掷空,子孫組件通過inject來接收該變量:

// ParentComponent
import SonComponent from './SonComponent.vue'
export default {
    name: 'ParentComponent',
    components: {
        SonComponent
    },
    provide: {
        message: 'data from Parent Component'
    }
}
// SonComponent
<template>
    <h1>{{message}}</h1>
</template>>

<script>
export default {
    name: 'SonComponent',
    inject: ['message']
}
</script>>

更多組件間通信方式,請參考:Vue組件間通信6種方式

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疼电,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子减拭,更是在濱河造成了極大的恐慌蔽豺,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拧粪,死亡現(xiàn)場離奇詭異修陡,居然都是意外死亡,警方通過查閱死者的電腦和手機可霎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門魄鸦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人癣朗,你說我怎么就攤上這事拾因。” “怎么了旷余?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵绢记,是天一觀的道長。 經(jīng)常有香客問我正卧,道長蠢熄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任炉旷,我火速辦了婚禮签孔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘窘行。我一直安慰自己饥追,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布罐盔。 她就那樣靜靜地躺著但绕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪翘骂。 梳的紋絲不亂的頭發(fā)上壁熄,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音碳竟,去河邊找鬼草丧。 笑死,一個胖子當著我的面吹牛莹桅,可吹牛的內(nèi)容都是我干的昌执。 我是一名探鬼主播烛亦,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼懂拾!你這毒婦竟也來了煤禽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤岖赋,失蹤者是張志新(化名)和其女友劉穎檬果,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唐断,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡选脊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了脸甘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恳啥。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丹诀,靈堂內(nèi)的尸體忽然破棺而出钝的,到底是詐尸還是另有隱情,我是刑警寧澤铆遭,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布硝桩,位于F島的核電站,受9級特大地震影響疚脐,放射性物質(zhì)發(fā)生泄漏亿柑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一棍弄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疟游,春花似錦呼畸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至另绩,卻和暖如春儒陨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背笋籽。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工蹦漠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人车海。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓笛园,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子研铆,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容