Vue.js 2.0 手把手入門筆記

前端泥瓦匠發表於2019-06-18

1 介紹

是一套用於構建使用者介面的漸進式框架。與其它大型框架不同的是,Vue 被設計為可以自底向上逐層應用。Vue 的核心庫只關注檢視層,不僅易於上手,還便於與第三方庫或既有專案整合。

2 特點:

  • 核心只關注檢視層(view)
  • 靈活、輕量、靈活的特點
  • 適用於移動端專案
  • 漸進式框架

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

  • 庫是將程式碼集合成一個產品,庫是我們呼叫庫中的方法實現自己的功能
  • 框架則是為解決一類問題而開發的產品,框架是我們在指定的位置編寫程式碼,框架幫我們呼叫。

框架是庫的升級版

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方便測試

<!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的指令 directive 只是dom上的行間屬性,vue給這類屬性賦予了一些意義,來實現特殊功能所有指令都以v-開頭value屬性預設情況下回vue忽略掉 selected checked 都沒有意義

2.3.3表單輸入

v-model 會將msg賦予輸入框,輸入框的值改變會影響資料

<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

最後可以實現雙向繫結的雛形

3 資料響應的變化

vue會迴圈data中的資料(資料劫持) 依次的增加getter和setter

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

但是這時候我想新增一個school方法,發現沒有產生getter和setter

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>
複製程式碼

Vue.js 2.0 手把手入門筆記

 

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>
複製程式碼

Vue.js 2.0 手把手入門筆記

1.5 .once 只執行一次點選

如果我們在@click事件上新增.once修飾符,只要點選按鈕只會執行一次。

2 鍵盤修飾符

在JavaScript事件中除了前面所說的事件,還有鍵盤事件,也經常需要監測常見的鍵值。在Vue中允許v-on在監聽鍵盤事件時新增關鍵修飾符。記住所有的keyCode比較困難,所以Vue為最常用的鍵盤事件提供了別名:

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

Vue.js 2.0 手把手入門筆記

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.元件化開發

我們可以很直觀的將一個複雜的頁面分割成若干個獨立元件,每個元件包含元件的邏輯和樣式,再將這些獨立元件完成一個複雜的頁面。這樣既減少了邏輯複雜度,又實現了程式碼的重用。頁面是元件的容器,元件自動組合形成完整的介面,當不需要某個元件時,或者想要替換某個元件時,可以隨時進行替換和刪除,而不影響整個應用的執行。

2、元件化開發的好處

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

在vue中例如div、span都可以看做一個元件

3、全域性元件

  • 全域性元件:可以宣告一次在任何地方使用
  • 區域性元件:必須告訴這個元件屬於誰

一般寫外掛的時候全域性元件使用的多一些

<!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 優勢

1、在Vue中使用過濾器(Filters)來渲染資料是一種很有趣的方式。
2、首先我們要知道,Vue中的過濾器不能替代Vue中的methodscomputed或者watch
3、過濾器不改變真正的data,而只是改變渲染的結果,並返回過濾後的版本。
4、在很多不同的情況下,過濾器都是有用的,比如儘可能保持API響應的乾淨,並在前端處理資料的格式。
5、在你希望避免重複和連線的情況下,它們也可以有效地封裝成可重用程式碼塊背後的所有邏輯。

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 鉤子中可用。

Vue.js 2.0 手把手入門筆記

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('====================================');
      }
    })
複製程式碼

相關文章