Vue.js 2.0 手把手入門筆記

1 介紹

是一套用於構建用戶界面的漸進式框架。與其它大型框架不一樣的是,Vue 被設計爲能夠自底向上逐層應用。Vue 的核心庫只關注視圖層,不只易於上手,還便於與第三方庫或既有項目整合。javascript

2 特色:

  • 核心只關注視圖層(view)
  • 靈活、輕量、靈活的特色
  • 適用於移動端項目
  • 漸進式框架

3 什麼是庫,什麼是框架?

  • 庫是將代碼集合成一個產品,庫是咱們調用庫中的方法實現本身的功能
  • 框架則是爲解決一類問題而開發的產品,框架是咱們在指定的位置編寫代碼,框架幫咱們調用。

框架是庫的升級版css

4 漸進式

  • 聲明式渲染(無需關心如何實現)
  • 組件系統
  • 客戶端路由(vue-router)
  • 大規模狀態管理(vuex)
  • 構建工具(vue-cli)

5 Vue的兩個核心點

  1. 響應的數據變化
  2. 當數據發生改變->視圖的自動更新
  3. 組合的視圖組件
  4. ui頁面映射爲組件樹
  5. 劃分組件可維護、可複用、可測試

6 MVC(backbone,react)

  • model 數據
  • view  視圖
  • controller 控制器

7 MVVM(angular,vue) 雙向

  • model 數據
  • view  視圖
  • viewModel視圖模型

8 Object.defineProperty(es5)沒有替代方案

  • 不支持ie8<=

2 vue基礎指令

2.1 安裝vue

  • cdn方式
  • npm 方式

2.2 簡單的嘗試

這裏使用cdn方便測試html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>

<body>
    <div id="content">
        <!-- moustache 小鬍子語法 表達式 能夠放賦值 取值 三元-->
        {{ msg }}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<!-- 使用官網的vue地址 -->
<script> // 引用vue後會給一個vue構造函數 var vm = new Vue({ // vm === viewModel el: '#content', // 告訴vue管理哪一部分,querySelector "document.querySelector("#content")" data: { // data中的數據會被vm所代理 msg: 'Hello Vue!' // 能夠經過vm.msg獲取對應的呢日用 } })// Object.defineProperty vm.msg = "wjw" // 修改視圖 </script>
</html>
複製代碼

image.png

2.3 模板語法

綜上所屬得出了一套模板語法前端

2.3.1 文本

<span>Message:{{msg}}</span>
複製代碼

2.3.2 表達式

{{number + 1}}
{{ok?'YES':'NO'}}
{{message.split('').reverse().join('')}}
複製代碼

可是vue的表單元素 input checkbox textarea radio select 非文本處理vue

vue的指令 directive 只是dom上的行間屬性,vue給這類屬性賦予了一些意義,來實現特殊功能全部指令都以v-開頭value屬性默認狀況下回vue忽略掉 selected checked 都沒有意義java

2.3.3表單輸入

v-model 會將msg賦予輸入框,輸入框的值改變會影響數據node

<input v-model="msg">
<input type="checkbox" v-model="msg1" value="登山">
複製代碼

2.3.4 原始HTML

<p>Using mustache:<span v-html='rawHtml'></spn></p>
複製代碼

2.3.5 指令

<p v-if='seen'>如今看到我了</p>
複製代碼

2.3.6 特性

<div v-bind:id='dynamicld'></div>
複製代碼

2.4 Object.defineProperty原理

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="content"></div>
    <input type="text" id="input">
</body>
  
<script> let obj = {} let temp = {}; document.getElementById("content").innerHTML = obj.name // 'name' 表明屬性 Object.defineProperty(obj,'name',{ configurable:false, //是否可刪除 // writable:true,// 是否可賦值(若是使用set方法,則不能使用) enumerable:true, // 是否可枚舉,也是就for..in.. // value:1,// 值(若是使用get方法,則不能使用) get(){ // 取obj的name會觸發get方法 return temp['name'] }, set(val){// 給obj賦值會觸發get方法 // console.log(val); temp['name'] = val // 改變temp的結果 input.value = val // 將值賦值給輸入框 } }); input.value = obj.name // 頁面一加載,會將調用get方法 input.addEventListener('input',function(){ // 等待輸入框的變化 obj.name = this.value // 當值變化時會調用set方法 document.getElementById("content").innerHTML = obj.name }) </script>

</html>
複製代碼

image.png

最後能夠實現雙向綁定的雛形react

3 數據響應的變化

vue會循環data中的數據(數據劫持) 依次的增長getter和settergit

let vm = new Vue({
   el:'#content',
   data:{
     a:{}
   }
 })
複製代碼

可是這時候我想添加一個school方法,發現沒有產生getter和settergithub

1.1 方法一 $set

使用變量時 先要初始化,不然新加的屬性不會致使頁面刷新

vm.$set(vm.a,"school",'1')// 此方法能夠給對象添加響應式的變化
複製代碼

1.2 方法二 替換原對象

vm.a = {"school":"heihei",age:8};
複製代碼

1.3 數組問題

去改變數組中的某一項監控不到的,也不能改變數組的長度方法

let vm = new Vue({
  el:'#content',
  data:{
    a:[1,2,3,4,5,6]
  }
})
複製代碼

錯誤方法

vm.a[0] =100
vm.a.length -=2 
複製代碼

變異方法:pop push shift unshit sort reserve splice

vm.a = vm.a.map(item=>item*3) 
複製代碼

4 數組的循環v-for

vue 提供了一個v-for 解決循環問題 更高效 會複用原有結構

4.1 代碼

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="content">
        <!--要循環誰就在誰身上增長v-for屬性,相似於for...in..-->
        <!--默認是value of 數組/ (value,index) of 數組-->
        <li v-for="(todo,index) in todos">
 <!-- 會改變原始數組的方法,爲變異方法 例如push(),pop()等; 非變異方法,不會改變原始數組,可是會返回一個新數組 -->
            {{ todo.text }} {{index+1}}
        </li>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<!-- 使用官網的vue地址 -->
<script> let vm = new Vue({ el:'#content', data:{ todos: [ { text: '學習 JavaScript' }, { text: '學習 Vue' }, { text: '整個牛項目' } ] } }) </script>
</html>
複製代碼

v-for循環數組 當用for來更新已被渲染的元素時,vue的「就地複用」機制 是不會改變數據項的順序的。要想從新排序,需爲每項添加key屬性(也就是每項惟一的id)

想要改變

會改變原始數組的方法,爲變異方法 例如push(),pop()等;  非變異方法,不會改變原始數組,可是會返回一個新數組

4.2 爲何v-for必定要有key

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <div>
            <input type="text" v-model="name">
            <button @click="add">添加</button>
        </div>
        <ul>
            <li v-for="(item, i) in list">
                <input type="checkbox"> {{item.name}}
            </li>
        </ul>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<!-- 使用官網的vue地址 -->
<script> // 建立 Vue 實例,獲得 ViewModel var vm = new Vue({ el: '#app', data: { name: '', newId: 3, list: [ { id: 1, name: '蔬菜' }, { id: 2, name: '奶酪' }, { id: 3, name: '肉' } ] }, methods: { add() { //注意這裏是unshift this.list.unshift({ id: ++this.newId, name: this.name }) this.name = '' } } }); </script>
  </div>

</html>
複製代碼

image.png

當你輸入湯時

image.png

就會變成這個樣子  =>

image.png

可是當你換成了key

能夠簡單的這樣理解:加了key(必定要具備惟一性) id的checkbox跟內容進行了一個關聯。是咱們想達到的效果

vue和react的虛擬DOM的Diff算法大體相同,其核心是基於兩個簡單的假設
首先講一下diff算法的處理方法,對操做先後的dom樹同一層的節點進行對比,一層一層對比,以下圖:

image.png

當某一層有不少相同的節點時,也就是列表節點時,Diff算法的更新過程默認狀況下也是遵循以上原則。
好比一下這個狀況:
image.png

咱們但願能夠在B和C之間加一個F,Diff算法默認執行起來是這樣的:
image.png

即把C更新成F,D更新成C,E更新成D,最後再插入E,是否是很沒有效率?
因此咱們須要使用key來給每一個節點作一個惟一標識,Diff算法就能夠正確的識別此節點,找到正確的位置區插入新的節點。

image.png

vue中列表循環需加:key="惟一標識" 惟一標識能夠是item裏面id index等,由於vue組件高度複用增長Key能夠標識組件的惟一性,爲了更好地區別各個組件 key的做用主要是爲了高效的更新虛擬DOM

5 事件

5.1 定義&縮寫

事件定義以及縮寫

<div id="app">
	<button @click="msg"></button>
	<button @mousedown="add"></button>
  <!--若是不傳遞參數,則不要寫括號會自動傳入事件源,若是寫括號了,要手動傳入$event屬性-->
</div>

let vm = new Vue({
		el:"#app",
    methods:{
    	msg(){
        console.log(Math.random());
      }
    }
})
複製代碼

methods和data中的數據會所有放在vm上,並且名字不能衝突,衝突會報錯,methods中的this指向的都是實例

5.2 mousedown

當鼠標指針移動到元素上方,並按下鼠標按鍵(左、右鍵都可)時,會發生 mousedown 事件。
與 click 事件不一樣,mousedown 事件僅須要按鍵被按下,而不須要鬆開便可發生。

5.3 mouseup

當在元素上鬆開鼠標按鍵(左、右鍵都可)時,會發生 mouseup 事件。
與 click 事件不一樣,mouseup 事件僅須要鬆開按鈕。當鼠標指針位於元素上方時,放鬆鼠標按鈕就會觸發該事件。

5.4 click

當鼠標指針停留在元素上方,而後按下並鬆開鼠標左鍵時,就會發生一次 click 事件。
注意:觸發click事件的條件是按下並鬆開鼠標左鍵!,按下並鬆開鼠標右鍵並不會觸發click事件。
三個事件的觸發順序

5.5 總結

若在同一個元素上按下並鬆開鼠標左鍵,會依次觸發mousedown、mouseup、click,前一個事件執行完畢纔會執行下一個事件
若在同一個元素上按下並鬆開鼠標右鍵,會依次觸發mousedown、mouseup,前一個事件執行完畢纔會執行下一個事件,不會觸發click事件

6 事件修飾符的使用

1 事件處理

若是須要在內聯語句處理器中訪問原生DOM事件。可使用特殊變量$event,把它傳入到methods中的方法中。
     在Vue中,事件修飾符處理了許多DOM事件的細節,讓咱們再也不須要花大量的時間去處理這些煩惱的事情,而能有更多的精力專一於程序的邏輯處理。在Vue中事件修飾符主要有:

  • .stop:等同於JavaScript中的event.stopPropagation(),防止事件冒泡
  • .prevent:等同於JavaScript中的event.preventDefault(),防止執行預設的行爲(若是事件可取消,則取消該事件,而不中止事件的進一步傳播)
  • .capture:與事件冒泡的方向相反,事件捕獲由外到內
  • .self:只會觸發本身範圍內的事件,不包含子元素
  • .once:只會觸發一次

1.1 stop 防止事件冒泡

冒泡事件:嵌套兩三層父子關係,而後全部都有點擊事件,點擊子節點,就會觸發從內至外  子節點-》父節點的點擊事件

<!-- HTML --> 

<div id="app"> 
 &emsp;<div class="outeer" @click="outer"> 
   &emsp;<div class="middle" @click="middle"> 
     &emsp;<button @click="inner">點擊我(^_^)</button>
     </div>
   </div> 
 &emsp;<p>{{ message }}</p> 
</div>

 let app = new Vue({
	 el: '#app', 
   data () { 
   &emsp;return { message: '測試冒泡事件' } 
   }, 
 &emsp;methods: { 
   &emsp;inner: function () {
       this.message = 'inner: 這是最裏面的Button' 
   &emsp;}, 
   &emsp;middle: function () { 
     &emsp;this.message = 'middle: 這是中間的Div' 
   &emsp;}, 
   &emsp;outer: function () { 
     &emsp;this.message = 'outer: 這是外面的Div' 
   &emsp;} 
 &emsp;} 
})
複製代碼

防止冒泡事件的寫法是:在點擊上加上.stop至關於在每一個方法中調用了等同於event.stopPropagation(),點擊子節點不會捕獲到父節點的事件

<!-- HTML --> 

<div id="app"> 

 &emsp;<div class="outeer" @click.stop="outer"> 

   &emsp;<div class="middle" @click.stop="middle"> 

     &emsp;<button @click.stop="inner">點擊我(^_^)</button>

     </div>

   </div> 

</div>
複製代碼

1.2 prevent取消默認事件

.prevent等同於JavaScript的event.preventDefault(),用於取消默認事件。好比咱們頁面的<a href="#">標籤,當用戶點擊時,一般在瀏覽器的網址列出#

1.3 .capture 捕獲事件

捕獲事件:嵌套兩三層父子關係,而後全部都有點擊事件,點擊子節點,就會觸發從外至內  父節點-》子節點的點擊事件

<!-- HTML --> 
<div id="app"> 
 &emsp;<div class="outeer" @click.capture="outer"> 
   &emsp;<div class="middle" @click.capture="middle"> 
     &emsp;<button @click.capture="inner">點擊我(^_^)</button>
     </div>
   </div> 
</div>
複製代碼


 

1.4 .self

修飾符.self只會觸發本身範圍內的事件,不會包含子元素。

<!-- HTML --> 
<div id="app"> 
 &emsp;<div class="outeer" @click.self="outer"> 
   &emsp;<div class="middle" @click.self="middle"> 
     &emsp;<button @click.stop="inner">點擊我(^_^)</button>
     </div>
   </div> 
</div>
複製代碼

1.5 .once 只執行一次點擊

若是咱們在@click事件上添加.once修飾符,只要點擊按鈕只會執行一次。

2 鍵盤修飾符

在JavaScript事件中除了前面所說的事件,還有鍵盤事件,也常常須要監測常見的鍵值。在Vue中容許v-on在監聽鍵盤事件時添加關鍵修飾符。記住全部的keyCode比較困難,因此Vue爲最經常使用的鍵盤事件提供了別名:

  • .enter:回車鍵
  • .tab:製表鍵
  • .delete:含deletebackspace
  • .esc:返回鍵
  • .space: 空格鍵
  • .up:向上鍵
  • .down:向下鍵
  • .left:向左鍵
  • .right:向右鍵

3 鼠標修飾符

鼠標修飾符用來限制處理程序監聽特定的滑鼠按鍵。常見的有:

  • .left:鼠標左鍵
  • .middle:鼠標中間滾輪
  • .right:鼠標右鍵

4 修飾鍵

能夠用以下修飾符開啓鼠標或鍵盤事件監聽,使在按鍵按下時發生響應:

  • .ctrl
  • .alt
  • .shift
  • .meta

5 自定義按鍵修飾符別名

在Vue中能夠經過config.keyCodes自定義按鍵修飾符別名。例如,因爲預先定義了keycode 116(即F5)的別名爲f5,所以在文字輸入框中按下F5,會觸發prompt方法,出現alert

<!-- HTML -->

<div id="app">

    <input type="text" v-on:keydown.f5="prompt()">

</div>



Vue.config.keyCodes.f5 = 116;



let app = new Vue({

    el: '#app',

    methods: {

        prompt: function() {

            alert('我是 F5!');

        }

    }

});
複製代碼

6 總結

在Vue中,使用v-on來給元素綁定事件,而爲了更好的處理邏輯方面的事物,Vue提供了一個methods。在methods中定義一些方法,這些方法能夠幫助咱們處理一些邏輯方面的事情。而在這篇文章中,咱們主要介紹了一些事件的修飾符,好比常見的阻止事件冒泡,鍵盤修飾符等。除此以外,還提供了config.keyCodes提供自定義按鍵修飾符別名。

7 縮寫

7.1 指令縮寫

<a v-bind:href='url'></a>
<a :href='url'></a>
<a v-on:click='doSomething'></a>
<a @click='doSomething'></a>
複製代碼

7.2 函數縮寫

image.png

縮寫後

image.png

8 組件化管理

1.組件化開發

咱們能夠很直觀的將一個複雜的頁面分割成若干個獨立組件,每一個組件包含組件的邏輯和樣式,再將這些獨立組件完成一個複雜的頁面。這樣既減小了邏輯複雜度,又實現了代碼的重用。頁面是組件的容器,組件自動組合造成完整的界面,當不須要某個組件時,或者想要替換某個組件時,能夠隨時進行替換和刪除,而不影響整個應用的運行。

二、組件化開發的好處

  • 提升開發效率
  • 方便重複使用
  • 便於協同開發
  • 更容易被管理和維護

在vue中例如div、span均可以看作一個組件

三、全局組件

  • 全局組件:能夠聲明一次在任何地方使用
  • 局部組件:必須告訴這個組件屬於誰

通常寫插件的時候全局組件使用的多一些

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>

<body>
    <div id="app">
        <my-handsom></my-handsom>
        <my-handsom></my-handsom>
        <my-handsom></my-handsom>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script> Vue.component("my-handsom",{ //一個對象能夠當作一個組件 data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }) var vm = new Vue({ el: '#app' }) </script>
</html>
複製代碼

image.png

  • 組件名不要帶大寫,多組件使用 -
  • 只要組件和定義相同是能夠的(首字母能夠大寫)
  • html採用短橫線隔開命名法js中轉駝峯也是能夠的

深刻了解組件

props

組件的參數傳遞

slot

插槽在組件抽象設計中的應用

自定義事件

父子組件的通訊方式

9 全局api- Vue.extend

使用基礎 Vue 構造器,建立一個「子類」。參數是一個包含組件選項的對象。

data 選項是特例,須要注意 - 在 Vue.extend() 中它必須是函數

<div id="mount-point"></div>
複製代碼
// 建立構造器
var demo = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})

// 建立 Profile 實例,並掛載到一個元素上。
new demo().$mount('#mount-point')
複製代碼

10 全局api-nextTick

官方說明

參數

  • {Function} [callback]
  • {Object} [context]

用法

在下次 DOM 更新循環結束以後執行延遲迴調。在修改數據以後當即使用這個方法,獲取更新後的 DOM。

// 修改數據
vm.msg = 'Hello'

// DOM 尚未更新
Vue.nextTick(function () {
  // DOM 更新了
})

// 做爲一個 Promise 使用 (2.1.0 起新增,詳見接下來的提示)
Vue.nextTick()
  .then(function () {
    // DOM 更新了
})
複製代碼

2.1.0 起新增:若是沒有提供回調且在支持 Promise 的環境中,則返回一個 Promise。請注意 Vue 不自帶 Promise 的 polyfill,因此若是你的目標瀏覽器不原生支持 Promise (IE:大家都看我幹嗎),你得本身提供 polyfill。

示例

先來一個示例瞭解下關於Vue中的DOM更新以及nextTick的做用。
模板

<div class="app">
  <div ref="msgDiv">{{msg}}</div>
  <div v-if="msg1">Message got outside $nextTick: {{msg1}}</div>
  <div v-if="msg2">Message got inside $nextTick: {{msg2}}</div>
  <div v-if="msg3">Message got outside $nextTick: {{msg3}}</div>
  <button @click="changeMsg">
    Change the Message
  </button>
</div>
複製代碼

Vue實例
**

new Vue({
  el: '.app',
  data: {
    msg: 'Hello Vue.',
    msg1: '',
    msg2: '',
    msg3: ''
  },
  methods: {
    changeMsg() {
      this.msg = "Hello world."
      this.msg1 = this.$refs.msgDiv.innerHTML
      this.$nextTick(() => {
        this.msg2 = this.$refs.msgDiv.innerHTML
      })
      this.msg3 = this.$refs.msgDiv.innerHTML
    }
  }
})
複製代碼

點擊前

image.png

點擊後

image.png

從圖中能夠得知:msg1和msg3顯示的內容仍是變換以前的,而msg2顯示的內容是變換以後的。其根本緣由是由於Vue中DOM更新是異步的(詳細解釋在後面)。

應用場景

下面瞭解下nextTick的主要應用的場景及緣由。

  • 在Vue生命週期的created()鉤子函數進行的DOM操做必定要放在Vue.nextTick()的回調函數中

created()鉤子函數執行的時候DOM 其實並未進行任何渲染,而此時進行DOM操做無異於徒勞,因此此處必定要將DOM操做的js代碼放進Vue.nextTick()的回調函數中。與之對應的就是mounted()鉤子函數,由於該鉤子函數執行時全部的DOM掛載和渲染都已完成,此時在該鉤子函數中進行任何DOM操做都不會有問題 。

  • 在數據變化後要執行的某個操做,而這個操做須要使用隨數據改變而改變的DOM結構的時候,這個操做都應該放進Vue.nextTick()的回調函數中。

具體緣由在Vue的官方文檔中詳細解釋:

Vue 異步執行 DOM 更新。只要觀察到數據變化,Vue 將開啓一個隊列,並緩衝在同一事件循環中發生的全部數據改變。若是同一個 watcher 被屢次觸發,只會被推入到隊列中一次。這種在緩衝時去除重複數據對於避免沒必要要的計算和 DOM 操做上很是重要。而後,在下一個的事件循環「tick」中,Vue 刷新隊列並執行實際 (已去重的) 工做。Vue 在內部嘗試對異步隊列使用原生的 Promise.thenMessageChannel,若是執行環境不支持,會採用 setTimeout(fn, 0)代替。 例如,當你設置vm.someData = 'new value',該組件不會當即從新渲染。當刷新隊列時,組件會在事件循環隊列清空時的下一個「tick」更新。多數狀況咱們不須要關心這個過程,可是若是你想在 DOM 狀態更新後作點什麼,這就可能會有些棘手。雖然 Vue.js 一般鼓勵開發人員沿着「數據驅動」的方式思考,避免直接接觸 DOM,可是有時咱們確實要這麼作。爲了在數據變化以後等待 Vue 完成更新 DOM ,能夠在數據變化以後當即使用Vue.nextTick(callback) 。這樣回調函數在 DOM 更新完成後就會調用。

11 全局api-set

官網說明

Vue.set( target, propertyName/index, value )

  • 參數
    • {Object | Array} target
    • {string | number} propertyName/index
    • {any} value
  • 返回值:設置的值。
  • 用法
    向響應式對象中添加一個屬性,並確保這個新屬性一樣是響應式的,且觸發視圖更新。它必須用於向響應式對象上添加新屬性,由於 Vue 沒法探測普通的新增屬性 (好比 this.myObject.newProperty = 'hi')

注意對象不能是 Vue 實例,或者 Vue 實例的根數據對象。

示例

<div id="div">  
	<p >{{items}}</p>
</div>
 
<script>
 
var vm = new Vue({
el:"#div",
  data: {
    items: ['a', 'b', 'c']
  }
});
 
Vue.set(vm.items,2,"ling")
 
</script>
複製代碼

1 設置數組元素

Vue.set(vm.items,2,"ling") : 表示 把vm.items  這個數組的下標爲2 的元素,改成"ling"
把數組  ["a","b","c"] 修改 後是 ["a","b","ling"] 

image.png

2 向響應式對象添加屬性

<div id="div">  
	<p>{{person}}</p>
</div>
 
<script>
var vm = new Vue({
el:"#div",
data: {
   person:{
			name:"ling",
			job:"engineer"
   }
},
created:function(){
		alert(this.person.age)
  }
});
 
Vue.set(vm.person,"age","26")
</script>
複製代碼

注意:person 是data 裏面的子對象,因此可使用 Vue.set( ) 方法。data 這個根對象就不能使用 set 方法

image.png

image.png

說明:控制檯能夠在person 裏找到age 這個屬性,說明添加成功 (響應式)

**

對比非響應式方法

vm.food="chocolate"
alert(vm.food)

image.png

控制檯和網頁上的 {{person}} 都沒有顯示food 這個屬性,說明food 這個屬性沒有被添加 (非響應式)


image.png
**

12 全局api-delete

Vue.delete( target, propertyName/index )

  • 參數
    • {Object | Array} target
    • {string | number} propertyName/index

僅在 2.2.0+ 版本中支持 Array + index 用法。

  • 用法
    刪除對象的屬性。若是對象是響應式的,確保刪除能觸發更新視圖。這個方法主要用於避開 Vue 不能檢測到屬性被刪除的限制,可是你應該不多會使用它。

在 2.2.0+ 中一樣支持在數組上工做。

  • 目標對象不能是一個 Vue 實例或 Vue 實例的根數據對象。
data:{
   namelist : {
     id : 1, 
       name : '葉落森'
   }       
}
複製代碼
// 刪除name
delete this.namelist.name;//js方法
Vue.delete(this.namelist,'name');//vue方法
複製代碼

13 全局api-fifer過濾器

8.1 介紹

容許你自定義過濾器,可被用於一些常見的文本格式化。過濾器能夠用在兩個地方:雙花括號插值和 v-bind 表達式 (後者從 2.1.0+ 開始支持)。過濾器應該被添加在 JavaScript 表達式的尾部,由「管道」符號指示

8.2 優點

一、在Vue中使用過濾器(Filters)來渲染數據是一種頗有趣的方式。
二、首先咱們要知道,Vue中的過濾器不能替代Vue中的methodscomputed或者watch
三、過濾器不改變真正的data,而只是改變渲染的結果,並返回過濾後的版本。
四、在不少不一樣的狀況下,過濾器都是有用的,好比儘量保持API響應的乾淨,並在前端處理數據的格式。
五、在你但願避免重複和鏈接的狀況下,它們也能夠有效地封裝成可重用代碼塊背後的全部邏輯。

8.3 過濾器例子

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <div>{{ message | capitalize }}</div>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm= new Vue({ el: '#app', data: { message: 'world' }, filters: { // 能夠有好多的自定義過濾器 capitalize(value) { // 這裏的this指向的window if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } } }); </script>
</html>
複製代碼

8.4 過濾器串連

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        {{ message | filterA | filterB }}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm= new Vue({ el: '#app', data: { message: 'world' }, filters: { // 能夠有好多的自定義過濾器 filterA(value){ return value.split('').reverse().join(''); }, filterB(value){ return value.charAt(0).toUpperCase() + value.slice(1) } } }); </script>
</html>
複製代碼

8.5 過濾器傳參

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        {{ message | filterA('hello',hi) }}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm= new Vue({ el: '#app', data: { hi:'!', message: 'world' }, filters: { // 能夠有好多的自定義過濾器 filterA(value1,value2,value3){ return `${value2} ${value1} ${value3}`; } } }); </script>
</html>
複製代碼

這裏,filterA 被定義爲接收三個參數的過濾器函數。其中 message 的值做爲第一個參數,普通字符串 'hello' 做爲第二個參數,表達式 hi 的值做爲第三個參數。

14 插槽-slot

老版本vue

模板中只能有一個根元素

HTML內容模板(template)元素是一種用於保存客戶端內容機制,該內容在加載頁面時不會呈現,但隨後能夠在運行時使用JavaScript實例化。

<div id="app">
   <modal></modal> 
</div>

<template id="modal">
   <div>
     <h1>是否刪除</h1>
  </div>
</template>
複製代碼
let modal = {
 template:"#modal"
}

const app = new Vue({
 el:'#app',
 components:{
   modal
 },
 data:{
 }
})
複製代碼

咱們一般是想把h1的值動態放入,因此就要用到插槽

單個插槽 | 默認插槽 | 匿名插槽

首先是單個插槽,單個插槽是vue的官方叫法,可是其實也能夠叫它默認插槽,或者與具名插槽相對,咱們能夠叫它匿名插槽。由於它不用設置name屬性。 單個插槽能夠放置在組件的任意位置,可是就像它的名字同樣,一個組件中只能有一個該類插槽。相對應的,具名插槽就能夠有不少個,只要名字(name屬性)不一樣就能夠了。

<div id="app">
  <modal>
    <h1>插入成功</h1>
  </modal>
</div>

<template id="modal">
  <div>
    <slot></slot>
  </div>
</template>
複製代碼

image.png

當咱們看到插入成功的時候,匿名插入就實現了

具名插槽

匿名插槽沒有name屬性,因此是匿名插槽,那麼,插槽加了name屬性,就變成了具名插槽。具名插槽能夠在一個組件中出現N次,出如今不一樣的位置。下面的例子,就是一個有兩個具名插槽單個插槽的組件,這三個插槽被父組件用同一套css樣式顯示了出來,不一樣的是內容上略有區別。

簡單的來講,就是,咱們可能遇到一個問題 咱們想插入不一樣的插槽內的內容不同

在 2.6.0+ 中已棄用

<div id="app">
    <modal>
        <h1>插入成功</h1>
        <h2 slot="title">標題</h2>
        <h2 slot="content">內容</h2>
    </modal>
</div>

<template id="modal">
  <div>
    <slot name="default"></slot>
    <slot name="title"></slot>
    <slot name="content"></slot>
  </div>
</template>
複製代碼

咱們能夠發現沒有name的狀況下,默認就是default

做用域插槽 | 帶數據的插槽

最後,就是咱們的做用域插槽。這個稍微難理解一點。官方叫它做用域插槽,實際上,對比前面兩種插槽,咱們能夠叫它帶數據的插槽。什麼意思呢,就是前面兩種,都是在組件的template裏面寫

在 2.6.0+ 中已棄用


```html Vue做用域插槽
```

這種寫法,習慣了element-ui的朋友必定就很熟悉了。

總結: 
1 . 使用slot能夠在自定義組件內插入原生HTML元素,須要搭配使用name和slot屬性,不然多個slot可能會返回重複的HTML元素。
2 . 使用slot-scope能夠將slot內部的做用域指向該子組件,不然默認做用域指向調用slot的父組件。

新版本的 v-slot

vue@2.6.x 開始,Vue 爲具名和範圍插槽引入了一個全新的語法,即咱們今天要講的主角:v-slot 指令。目的就是想統一 slotscope-slot 語法,使代碼更加規範和清晰。既然有新的語法上位,很明顯,slotscope-slot 也將會在 vue@3.0.x 中完全的跟咱們說拜拜了。而從 vue@2.6.0 開始,官方推薦咱們使用 v-slot 來替代後二者。


#### 具名插槽 > 實例化一個vue
// 組件
Vue.component('lv-hello', {
  template: ` <div> <slot name="header"></slot> <h1>個人天呀</h1> </div>`
})

new Vue({
  el: '#app1',
  data: {

  }
});
複製代碼

老版本

<div id="app1">
  <!-- 老版本使用具名插槽 -->
  <lv-hello>
    <p slot="header">我是頭部</p>
  </lv-hello>
</div>
複製代碼

新版本的變化

<!-- 新版本使用具名插槽 -->
  <lv-hello>
    <!-- 注意:這塊的 v-slot 指令只能寫在 template 標籤上面,而不能放置到 p 標籤上 -->
    <template v-slot:header>
      <p>我是頭部</p>
    </template>
  </lv-hello>
</div>
複製代碼

具名插槽的縮寫

v-slot: 替換成 #

<div id="app">
  <lv-hello>
    <template #header>
      <p>我是頭部</p>
    </template>
    <!-- 注意: #號後面必須有參數,不然會報錯。即使是默認插槽,也須要寫成 #default -->
    <template #default>
      <p>我是默認插槽</p>
    </template>
  </lv-hello>
</div>
複製代碼

做用域插槽

所謂做用域插槽,就是讓插槽的內容可以訪問子組件中才有的數據。

Vue.component('lv-hello', {
  data: function () {
    return {
      firstName: '張',
      lastName: '三'
    }
  },

  template: ` <div> <slot name="header" :firstName="firstName" :lastName="lastName"></slot> <h1>個人天呀</h1> </div> `
})
複製代碼
<div id="app">
  <!-- 老版本使用具名插槽 -->
  <lv-hello>
  	<p slot="header" slot-scope="hh">我是頭部 {{ hh.firstName }} {{ hh.lastName }}</p>
	</lv-hello>
<!-- 新版本使用具名插槽 -->
    <lv-hello>
      <!-- 注意:這塊的 v-slot 指令只能寫在 template 標籤上面,而不能放置到 p 標籤上 -->
      <template v-slot:header="hh">
         <p>我是頭部 {{ hh.firstName }} {{ hh.lastName }}</p>
      </template>
  	</lv-hello>
</div>
複製代碼

15 動態綁定樣式-v-bind

13.1 對象語法

:class 綁定的樣式和class綁定的不衝突

13.1.1 直接綁定一個data

<div v-bind:class="{ active: isActive }"></div>
複製代碼

active 這個 class 存在與否將取決於數據屬性 isActive 的 布爾值

<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
複製代碼

13.1.2 data中使用一個對象綁定

data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}
複製代碼

13.1.3 計算屬性中綁定

data: {
  isActive: true,
  error: null
},
computed: {
  classObject: function () {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}
複製代碼

13.2 數組語法

<div v-bind:class="[activeClass, errorClass]"></div>
複製代碼

13.2.1 直接動態綁定一個class

data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}
複製代碼

13.2.2 三元表達式

<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
複製代碼

不過,當有多個條件 class 時這樣寫有些繁瑣。因此在數組語法中也可使用對象語法:

<div v-bind:class="[{ active: isActive }, errorClass]"></div>
複製代碼

16 數據-計算屬性(computed)

1 什麼是計算屬性

模板內的表達式很是便利,可是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板太重且難以維護。例如:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
       <div id="example">
           {{ message.split('').reverse().join('') }}
       </div>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el: '#app', data: { message: 'Hello' } }); </script>
  </div>

</html>
複製代碼

image.png

這裏的表達式包含3個操做,並非很清晰,因此遇到複雜邏輯時應該使用Vue特帶的計算屬性computed來進行處理。

2 計算屬性的用法

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
       <div id="example">
           {{getMessage}}
       </div>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el: '#app', data: { message: 'Hello' }, computed: { // 放在computed中最後也會放在vm上,不能和methods與data重名 getMessage() { return this.message.split('').reverse().join('') } } }); </script>
  </div>

</html>
複製代碼

3 計算屬性使用技巧

計算屬性能夠依賴其餘計算屬性
計算屬性不只能夠依賴當前Vue 實例的數據,還能夠依賴其餘實例的數據

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app1"></div>
    <div id="app2">
         {{getMessage}}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm= new Vue({ el: '#app1', data: { message: 'World' } }); var vm2 = new Vue({ el: '#app2', data: { message: 'Hello' }, computed: { getMessage() { return `${this.message} ${vm.message}` } } }); </script>
  </div>
</html>
複製代碼

4 getter和setter

每個計算屬性都包含一個getter 和一個setter ,咱們上面的兩個示例都是計算屬性的默認用法, 只是利用了getter 來讀取。
在你須要時,也能夠提供一個setter 函數, 當手動修改計算屬性的值就像修改一個普通數據那樣時,就會觸發setter 函數,執行一些自定義的操做

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="getMessage"> <--模擬修改--!>
        {{getMessage}}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm= new Vue({ el: '#app', data: { hi:'Hello', message: 'World' }, computed:{ getMessage:{ //get,set方法 // getter get(){ return this.hi + ' ' + this.message }, // setter set(newValue){ console.log('===================================='); console.log(newValue); console.log('===================================='); var names = newValue.split(' '); this.hi = names[0]; this.message = names[names.length - 1]; } } } }); </script>
</html>
複製代碼


絕大多數狀況下,咱們只會用默認的getter 方法來讀取一個計算屬性,在業務中不多用到setter,因此在聲明一個計算屬性時,能夠直接使用默認的寫法,沒必要將getter 和setter 都聲明。

5 質疑什麼不直接用methods

咱們能夠將同一函數定義爲一個方法而不是一個計算屬性,兩種方式的最終結果確實是徹底相同的。只是一個使用getMessage()取值,一個使用getMessage取值。
然而,不一樣的是計算屬性是基於它們的依賴進行緩存的。計算屬性只有在它的相關依賴發生改變時纔會從新求值。
這就意味着只要 hi尚未發生改變,屢次訪問 getMessage計算屬性會當即返回以前的計算結果,而沒必要再次執行函數。

<!DOCTYPE html>
<html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div>{{getMessage}}</div> <div> {{getMessage1()}}</div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm= new Vue({ el: '#app', data: { hi:'Hello', message: 'World' }, computed:{ getMessage(){ //get,set方法 return this.hi + ' ' + this.message //而使用計算屬性,只要title沒變,頁面渲染是不會從新進這裏來計算的,而是使用了緩存。 } }, methods:{ getMessage1(){ return this.hi + ' ' + this.message //進這個方法,再次計算。不是刷新,而是隻要頁面渲染,就會進方法裏從新計算。 } } }); </script> </html> 複製代碼

17 數據-觀察(watch)

一個對象,鍵是須要觀察的表達式,值是對應回調函數。值也能夠是方法名,或者包含選項的對象。Vue 實例將會在實例化時調用 $watch(),遍歷 watch 對象的每個屬性。

爲何必定要有watch,不用能夠嗎?咱們已經有了computed,能不能不去使用?

1 watch的出現

作一個實驗

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="a">
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el:'#app', data:{ a:"1" }, computed: { a(){ setTimeout(() => { this.a=1; }, 500); } } }) </script>
</html>
複製代碼

不難發如今_異步的狀況下就很差使用了_

2 代碼實現

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="a">
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script>
    var vm = new Vue({
        el:'#app',
        data:{
            a:""
        },
        watch: { // 只有值變化的時候纔會觸發 支持異步了,其餘狀況咱們更善於使用
            a(newVal,oldVal){ // watch的屬性名字要和觀察的人的名字一致
                console.log(newVal);
                console.log(oldVal);
            }
        },
    })
</script>
</html>
複製代碼

3 computed與watch的區別

Vue 提供了一種更通用的方式來觀察和響應 Vue 實例上的數據變更:偵聽屬性。當你有一些數據須要隨着其它數據變更而變更時,你很容易濫用 watch

<!DOCTYPE html>
<html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> {{ fullName }} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </html> 複製代碼
var vm = new Vue({
    el: '#app',
        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
            }
        }
    })
複製代碼

上面代碼是命令式且重複的。將它與計算屬性的版本進行比較:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})
複製代碼

是否是感受優雅不少

4 偵聽器

雖然計算屬性在大多數狀況下更合適,但有時也須要一個自定義的偵聽器。這就是爲何 Vue 經過 watch 選項提供了一個更通用的方法,來響應數據的變化。當須要在數據變化時執行異步或開銷較大的操做時,這個方式是最有用的。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
       <input type="text" v-model="something">
       {{somethingShow}}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el: '#app', data: { something: '', somethingShow:'' }, watch: { something(val){ this.somethingShow = "loading" this.getSomething() } }, methods:{ getSomething(){ setTimeout(() => { this.somethingShow = "hello" }, 1000);// 咱們使用延遲模擬一個網絡請求 } } }) </script>
</html>
複製代碼

5 vm.$watch

vm.$watch( expOrFn, callback, [options] )

觀察 Vue 實例變化的一個表達式或計算屬性函數。回調函數獲得的參數爲新值和舊值。表達式只接受監督的鍵路徑。對於更復雜的表達式,用一個函數取代。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
       <input type="text" v-model="something">
       {{somethingShow}}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el: '#app', data: { something: '', somethingShow:'' } }) vm.$watch('something',(newVal,oldVal)=>{// watch的屬性名要和觀察的人名字一致 vm.somethingShow = "loading" console.log('===================================='); console.log(newVal); console.log('===================================='); vm.somethingShow = newVal }) </script>
</html>
複製代碼

18 數據-屬性(props)

組件接受的選項之一 props 是 Vue 中很是重要的一個選項。父子組件的關係能夠總結爲: props down, events up 父組件經過 props 向下傳遞數據給子組件;子組件經過 events 給父組件發送消息。

父子級組件

好比咱們須要建立兩個組件 parent 和 child。須要保證每一個組件能夠在相對隔離的環境中書寫,這樣也能提升組件的可維護性。
這裏咱們先定義父子兩個組件和一個 Vue 對象

var childNode = {
  template: `
        <div>childNode</div>
        `
};
var parentNode = {
  template: `
        <div>
          <child></child>
          <child></child>
        </div>
        `,
  components: {
    child: childNode
  }
};
new Vue({
  el: "#example",
  components: {
    parent: parentNode
  }
});
複製代碼
<div id="example">
  <parent></parent>
</div>
複製代碼

這裏的 childNode 定義的 template 是一個 div,而且內容是"childNode"字符串。 而在 parentNode 的 template 中定義了 div 的 class 名叫 parent 而且包含了兩個 child 組件。

靜態 props

組件實例的做用域是孤立的。這意味着不能(也不該該)在子組件的模板中直接引用父組件的數據。要讓子組件使用父組件的數據,須要經過子組件的 props 選項。 父組件向子組件傳遞數據分爲兩種方式:動態和靜態,這裏先介紹靜態方式。 子組件要顯示的用 props 聲明它指望得到的數據 修改上例中的代碼,給 childNode 添加一個 props 選項和須要的forChildMsg數據; 而後在父組件中的佔位符添加特性的方式來傳遞數據。

var childNode = {
  template: ` <div> {{forChildMsg}} </div> `,
  props: ["for-child-msg"] // 直接把參數做爲數組放進去
};
var parentNode = {
  template: ` <div> <p>parentNode</p> <child for-child-msg="aaa"></child> <child for-child-msg="bbb"></child> </div> `,
  components: {
    child: childNode
  }
};
複製代碼

命名規範
**

對於 props 聲明的屬性,在父組件的 template 模板中,屬性名須要使用中劃線寫法; 子組件 props 屬性聲明時,使用小駝峯或者中劃線寫法均可以;而子組件的模板使用從父組件傳來的變量時,須要使用對應的小駝峯寫法。別擔憂,Vue 可以正確識別出小駝峯和下劃線命名法混用的變量,如這裏的forChildMsgfor-child-msg是同一值。

動態props

原則上很簡單,for-child-msg做爲一個變量

var parentNode = {
  template: ` <div> <p>parentNode</p> <child :for-child-msg="childMsg1"></child> <child :for-child-msg="childMsg2"></child> </div> `,
  components: {
    child: childNode
  },
  data: function() {
    return {
      childMsg1: "child-1",
      childMsg2: "child-2"
    };
  }
};
複製代碼

在父組件的 data 的 return 數據中的 childMsg1 和 childMsg2 會被傳入子組件中

props 驗證

驗證傳入的 props 參數的數據規格,若是不符合數據規格,Vue 會發出警告。

能判斷的全部種類(也就是 type 值)有: String, Number, Boolean, Function, Object, Array, Symbol

Vue.component("example", {
  props: {
    // 基礎類型檢測, null意味着任何類型都行
    propA: Number,
    // 多種類型
    propB: [String, Number],
    // 必傳且是String
    propC: {
      type: String,
      required: true
    },
    // 數字有默認值
    propD: {
      type: Number,
      default: 101
    },
    // 數組、默認值是一個工廠函數返回對象
    propE: {
      type: Object,
      default: function() {
        console.log("propE default invoked.");
        return { message: "I am from propE." };
      }
    },
    // 自定義驗證函數
    propF: {
      isValid: function(value) {
        return value > 100;
      }
    }
  }
});

let childNode = {
  template: "<div>{{forChildMsg}}</div>",
  props: {
    "for-child-msg": Number
  }
};

let parentNode = {
  template: ` <div class="parent"> <child :for-child-msg="msg"></child> </div>`,
  components: {
    child: childNode
  },
  data() {
    return {
      // 當這裏是字符串 "123456"時會報錯
      msg: 123456
    };
  }
};
複製代碼

還能夠在 props 定義的數據中加入自定義驗證函數,當函數返回 false 時,輸出警告。 好比咱們把上述例子中的 childNode 的for-child-msg修改爲一個對象,幷包含一個名叫validator的函數,該命名是規定叫validator的,自定義函數名不會生效

let childNode = {
  template: "<div>{{forChildMsg}}</div>",
  props: {
    "for-child-msg": {
      validator: function(value) {
        return value > 100;
      }
    }
  }
};
複製代碼

在這裏咱們給for-child-msg變量設置了validator函數,而且要求傳入的值必須大於 100,不然報出警告。

單向數據流

props 是單向綁定的:當父組件的屬性變化時,將傳導給子組件,可是不會反過來。這是爲了防止子組件五一修改父組件的狀態。
因此不該該在子組件中修改 props 中的值,Vue 會報出警告。

let childNode = {
  template: `<div class="child"> <div> <span>子組件數據</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> </div>`,
  props: {
    "for-child-msg": String
  }
};

let parentNode = {
  template: ` <div class="parent"> <div> <span>父組件數據</span> <input v-model="msg"/> </div> <p>{{msg}}</p> <child :for-child-msg="msg"></child> </div>`,
  components: {
    child: childNode
  },
  data() {
    return {
      msg: "default string."
    };
  }
};
複製代碼

傳遞的過程將短橫分割命名,轉成駝峯命名法便可

這裏咱們給父組件和子組件都有一個輸入框,而且顯示出父組件數據和子組件的數據。當咱們在父組件的輸入框輸入新數據時,同步的子組件數據也被修改了;這就是 props 的向子組件傳遞數據。而當咱們修改子組件的輸入框時,瀏覽器的控制檯則報出錯誤警告

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "forChildMsg"

修改 props 數據

一般有兩種緣由:

  1. prop 做爲初始值傳入後,子組件想把它當作局部數據來用

  2. prop 做爲初始值傳入後,由子組件處理成其餘數據輸出

  3. 定義一個局部變量,並用 prop 的值初始化它

可是因爲定義的 ownChildMsg 只能接受 forChildMsg 的初始值,當父組件要傳遞的值變化發生時,ownChildMsg 沒法收到更新。

let childNode = {
  template: ` <div class="child"> <div> <span>子組件數據</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`,
  props: {
    "for-child-msg": String
  },
  data() {
    return { ownChildMsg: this.forChildMsg };
  }
};
複製代碼

這裏咱們加了一個

用於查看 ownChildMsg 數據是否變化,結果發現只有默認值傳遞給了 ownChildMsg,父組件改變只會變化到 forChildMsg,不會修改 ownChildMsg。


  1. 定義一個計算屬性,處理 prop 的值並返回

因爲是計算屬性,因此只能顯示值,不能設置值。咱們這裏設置的是一旦從父組件修改了 forChildMsg 數據,咱們就把 forChildMsg 加上一個字符串"---ownChildMsg",而後顯示在屏幕上。這時是能夠每當父組件修改了新數據,都會更新 ownChildMsg 數據的。

let childNode = {
  template: ` <div class="child"> <div> <span>子組件數據</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`,
  props: {
    "for-child-msg": String
  },
  computed: {
    ownChildMsg() {
      return this.forChildMsg + "---ownChildMsg";
    }
  }
};
複製代碼
  1. 更加妥帖的方式是使用變量存儲 prop 的初始值,並用 watch 來觀察 prop 值得變化。發生變化時,更新變量的值。
let childNode = {
  template: ` <div class="child"> <div> <span>子組件數據</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`,
  props: {
    "for-child-msg": String
  },
  data() {
    return {
      ownChildMsg: this.forChildMsg
    };
  },
  watch: {
    forChildMsg() {
      this.ownChildMsg = this.forChildMsg;
    }
  }
};
複製代碼

19 生命週期

1 vue生命週期簡介

image.png

image.png

2 生命週期探究

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">{{message}}</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var app = new Vue({ el: '#app', data: { message: "hello is world" }, beforeCreate() { console.group('beforeCreate 建立前狀態===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //undefined console.log("%c%s", "color:red", "data : " + this.$data); //undefined  console.log("%c%s", "color:red", "message: " + this.message) }, created() { console.group('created 建立完畢狀態===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //undefined console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化  console.log("%c%s", "color:red", "message: " + this.message); //已被初始化 }, beforeMount() { console.group('beforeMount 掛載前狀態===============》'); console.log("%c%s", "color:red", "el : " + (this.$el)); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化  console.log("%c%s", "color:red", "message: " + this.message); //已被初始化  }, mounted() { console.group('mounted 掛載結束狀態===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化 console.log("%c%s", "color:red", "message: " + this.message); //已被初始化  }, beforeUpdate() { console.group('beforeUpdate 更新前狀態===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, updated() { console.group('updated 更新完成狀態===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, beforeDestroy() { console.group('beforeDestroy 銷燬前狀態===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, destroyed() { console.group('destroyed 銷燬完成狀態===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message) } }) </script>
</html>
複製代碼

chrome瀏覽器裏打開,F12console就能發現

image.png

3 beforecreated

el 和 data 並未初始化

4 created

完成了 data 數據的初始化,el沒有

5 beforeMount

完成了 el 和 data 初始化

6 mounted

完成掛載

7 update

在console控制檯中輸入

app.message= 'hello!!';
複製代碼

image.png

8 destroy

咱們在console裏執行下命令對 vue實例進行銷燬。銷燬完成後,咱們再從新改變message的值,vue再也不對此動做進行響應了。可是原先生成的dom元素還存在,能夠這麼理解,執行了destroy操做,後續就再也不受vue控制了。

app.$destroy();
複製代碼

image.png

9 生命週期總結

9.1 beforecreate

能夠在這加個loading事件,加載的動畫

9.2 created

在這結束loading,還作一些初始化,實現函數自執行

9.3 mounted

在這發起後端請求,拿回數據,配合路由鉤子作一些事情

9.4 beforeDestroy

你確認刪除XX嗎? destroyed :當前組件已被刪除,清空相關內容

20 指令-條件判斷(v-if&v-show)

1 v-if&v-show

  • 條件渲染 (使用 v-if)
  • 條件展現 (使用 v-show)

if操做的是dom show 操做的樣式 若是頻繁切換dom使用v-show,當數據一開時就肯定下來使用v-if更好一些,若是if經過內部指令不會執行了 只有dom從顯示到隱藏 或者隱藏到顯示 才能使用vue的動畫

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <span v-if="flag">你看的見我</span>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el:'#app', data:{ flag:true } }) </script>
</html>
複製代碼

2 區別總結

  • v-show:操做的是元素的display屬性
  • v-if:操做的是元素的建立和插入
  • 相比較而言v-show的性能要高

21 內置組件-動畫(transition)

1 組件的過渡

Vue 提供了 transition 的封裝組件,在下列情形中,能夠給任何元素和組件添加進入/離開過渡

image.png

在進入/離開的過渡中,會有 6 個 class 切換。

  1. v-enter:定義進入過渡的開始狀態。在元素被插入以前生效,在元素被插入以後的下一幀移除。
  2. v-enter-active:定義進入過渡生效時的狀態。在整個進入過渡的階段中應用,在元素被插入以前生效,在過渡/動畫完成以後移除。這個類能夠被用來定義進入過渡的過程時間,延遲和曲線函數。
  3. v-enter-to2.1.8版及以上 定義進入過渡的結束狀態。在元素被插入以後下一幀生效 (與此同時 v-enter 被移除),在過渡/動畫完成以後移除。
  4. v-leave: 定義離開過渡的開始狀態。在離開過渡被觸發時馬上生效,下一幀被移除。
  5. v-leave-active:定義離開過渡生效時的狀態。在整個離開過渡的階段中應用,在離開過渡被觸發時馬上生效,在過渡/動畫完成以後移除。這個類能夠被用來定義離開過渡的過程時間,延遲和曲線函數。
  6. v-leave-to2.1.8版及以上 定義離開過渡的結束狀態。在離開過渡被觸發以後下一幀生效 (與此同時 v-leave 被刪除),在過渡/動畫完成以後移除。

1.1 初步代碼實現

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<style> div>div{ width:100px;height: 100px;background: red; } .v-enter{ opacity: 1; } /* 激活的時候 */ .v-enter-avtive{ opacity: 0; transition: 1s linear; } /* 離開 */ .v-leave-active{ opacity: 0; background: black; transition: 1s linear; } </style>
<body>
    <div id="app">
        <button @click="flag=!flag">切換</button>
        <!-- vue自定義的組件 -->
        <transition>
            <div v-show="flag"></div>
        </transition>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el:'#app', data:{ flag:true } }) </script>
</html>
複製代碼

1.2 多個transition

趕上了多個transition的時候,同一個class確定是會衝突的,那麼如何處理呢

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<style> div>div{ width:100px;height: 100px;background: red; } .jw-enter-active { transition: all .3s ease; } .jw-leave-active { transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0); } .jw-enter, .jw-leave-to { transform: translateX(10px); opacity: 0; } </style>
<body>
    <div id="app">
        <button @click="flag=!flag">切換</button>

        <!-- vue自定義的組件 -->
        <transition name="jw">
            <div v-show="flag"></div>
        </transition>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el:'#app', data:{ flag:true } }) </script>
</html>
複製代碼

簡單的理解就是就 transition有一個name屬性
在css中name-狀態便可調用

22 自定義指令-directives

1 介紹

Vue 也容許註冊自定義指令。注意,在 Vue2.0 中,代碼複用和抽象的主要形式是組件。然而,有的狀況下,你仍然須要對普通 DOM 元素進行底層操做,這時候就會用到自定義指令。

舉一個栗子:

<!DOCTYPE html>
<html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div v-color='flag'>123</div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ directives:{ color(el,bindings){ //el值指代的是button按鈕 console.log(arguments); el.style.background = bindings.value; } }, el: '#app', data: { flag: 'red' }, methods:{ getSomething(){ return "hello" } } }) </script> </html> 複製代碼

出現如圖狀況

image.png

image.png

再來個栗子

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<style> .a{ position: absolute;width: 100px;height: 100px;background: red; } </style>
<body>
    <div id="app">
       <div class="a" v-drag></div>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ directives:{ drag(el){ el.onmousedown = function (e) { var disx = e.pageX - el.offsetLeft; var disy = e.pageY - el.offsetTop; document.onmousemove = function (e) { el.style.left = e.pageX - disx +'px'; el.style.top = e.pageX - disy + 'px'; } document.onmouseup = function (e) { document.onmousemove = document.onmousemove = null; } e.preventDefault(); } } }, el: '#app', data: { flag: 'red' }, methods:{ getSomething(){ return "hello" } } }) </script>
</html>
複製代碼

image.png

能夠拖動

2 鉤子函數

一個指令定義對象能夠提供以下幾個鉤子函數 (均爲可選):
bind:只調用一次,指令第一次綁定到元素時調用。在這裏能夠進行一次性的初始化設置。

inserted:被綁定元素插入父節點時調用 (僅保證父節點存在,但不必定已被插入文檔中)。

update:所在組件的 VNode 更新時調用,可是可能發生在其子 VNode 更新以前。指令的值可能發生了改變,也可能沒有。可是你能夠經過比較更新先後的值來忽略沒必要要的模板更新 (詳細的鉤子函數參數見下)。

3 鉤子函數參數

  • el:指令所綁定的元素,能夠用來直接操做 DOM 。
  • binding:一個對象,包含如下屬性:
    • name:指令名,不包括 v- 前綴。
    • value:指令的綁定值,例如:v-my-directive="1 + 1" 中,綁定值爲 2
    • oldValue:指令綁定的前一個值,僅在 update 和 componentUpdated 鉤子中可用。不管值是否改變均可用。
    • expression:字符串形式的指令表達式。例如 v-my-directive="1 + 1"中,表達式爲 "1 + 1"
    • arg:傳給指令的參數,可選。例如 v-my-directive:foo 中,參數爲 "foo"
    • modifiers:一個包含修飾符的對象。例如:v-my-directive.foo.bar 中,修飾符對象爲 { foo: true, bar: true }
  • oldVnode:上一個虛擬節點,僅在 update 和 componentUpdated 鉤子中可用。

23 實例屬性-$ref

官網針對-ref的解釋

  • 預期string
    ref 被用來給元素或子組件註冊引用信息。引用信息將會註冊在父組件的 $refs 對象上。若是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;若是用在子組件上,引用就指向組件實例:
<!-- `vm.$refs.p` will be the DOM node -->
<p ref="p">hello</p>
<!-- `vm.$refs.child` will be the child component instance -->
<child-component ref="child"></child-component>
複製代碼
  • 當 v-for 用於元素或組件的時候,引用信息將是包含 DOM 節點或組件實例的數組。
    關於 ref 註冊時間的重要說明:由於 ref 自己是做爲渲染結果被建立的,在初始渲染的時候你不能訪問它們 - 它們還不存在!$refs 也不是響應式的,所以你不該該試圖用它在模板中作數據綁定。

操做dom

若是咱們用jQuery的話,通常性均可以操做dom

$("#id").text('xxx')   // 使用Jquery
document.getElementById("id")  // 使用原生Dom
複製代碼

如今咱們牛逼了,咱們用vue。那vue中,若是我要獲取Dom,該怎麼作?
這就進入本文的主題ref, $refs,官網解釋:

<div id="app">
   <div>{{msg}}</div>
</div>
複製代碼

在JavaScript中咱們習慣了使用document.getElementsByTagName


```javascript var vm = new Vue({ el: '#app', data:{ msg:'hello' }, mounted() { console.log(document.getElementsByTagName("div")[0].innerHTML); } }) ```

vue操做dom

那麼咱們在vue中呢

<div id="app">
   <div ref="msg">{{msg}}</div>
</div>
複製代碼
var vm = new Vue({
      el: '#app',
      data:{
        msg:'hello'
      },
      mounted() {
        // console.log(document.getElementsByTagName("div")[0].innerHTML);
        console.log('====================================');
        console.log(this.$refs.msg);
        console.log('====================================');
      }
    })
複製代碼
相關文章
相關標籤/搜索