1 介紹
是一套用於構建使用者介面的漸進式框架。與其它大型框架不同的是,Vue 被設計為可以自底向上逐層應用。Vue 的核心庫只關注檢視層,不僅易於上手,還便於與第三方庫或既有專案整合。
2 特點:
- 核心只關注檢視層(view)
- 靈活、輕量、靈活的特點
- 適用於移動端專案
- 漸進式框架
3 什麼是庫,什麼是框架?
- 庫是將程式碼集合成一個產品,庫是我們呼叫庫中的方法實現自己的功能
- 框架則是為解決一類問題而開發的產品,框架是我們在指定的位置編寫程式碼,框架幫我們呼叫。
框架是庫的升級版
4 漸進式
- 宣告式渲染(無需關心如何實現)
- 元件系統
- 客戶端路由(vue-router)
- 大規模狀態管理(vuex)
- 構建工具(vue-cli)
5 Vue的兩個核心點
- 響應的資料變化
- 當資料發生改變->檢視的自動更新
- 組合的檢視元件
- ui頁面對映為元件樹
- 劃分元件可維護、可複用、可測試
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>
複製程式碼
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>
複製程式碼
最後可以實現雙向繫結的雛形
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>
複製程式碼
當你輸入湯時
就會變成這個樣子 =>
但是當你換成了key
可以簡單的這樣理解:加了key(一定要具有唯一性) id的checkbox跟內容進行了一個關聯。是我們想達到的效果
vue和react的虛擬DOM的Diff演算法大致相同,其核心是基於兩個簡單的假設
首先講一下diff演算法的處理方法,對操作前後的dom樹同一層的節點進行對比,一層一層對比,如下圖:
當某一層有很多相同的節點時,也就是列表節點時,Diff演算法的更新過程預設情況下也是遵循以上原則。
比如一下這個情況:
我們希望可以在B和C之間加一個F,Diff演算法預設執行起來是這樣的:
即把C更新成F,D更新成C,E更新成D,最後再插入E,是不是很沒有效率?
所以我們需要使用key來給每個節點做一個唯一標識,Diff演算法就可以正確的識別此節點,找到正確的位置區插入新的節點。
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">
 <div class="outeer" @click="outer">
 <div class="middle" @click="middle">
 <button @click="inner">點選我(^_^)</button>
</div>
</div>
 <p>{{ message }}</p>
</div>
let app = new Vue({
el: '#app',
data () {
 return { message: '測試冒泡事件' }
},
 methods: {
 inner: function () {
this.message = 'inner: 這是最裡面的Button'
 },
 middle: function () {
 this.message = 'middle: 這是中間的Div'
 },
 outer: function () {
 this.message = 'outer: 這是外面的Div'
 }
 }
})
複製程式碼
防止冒泡事件的寫法是:在點選上加上.stop相當於在每個方法中呼叫了等同於event.stopPropagation(),點選子節點不會捕獲到父節點的事件
<!-- HTML -->
<div id="app">
 <div class="outeer" @click.stop="outer">
 <div class="middle" @click.stop="middle">
 <button @click.stop="inner">點選我(^_^)</button>
</div>
</div>
</div>
複製程式碼
1.2 prevent取消預設事件
.prevent
等同於JavaScript的event.preventDefault()
,用於取消預設事件。比如我們頁面的<a href="#">
標籤,當使用者點選時,通常在瀏覽器的網址列出#
:
1.3 .capture 捕獲事件
捕獲事件:巢狀兩三層父子關係,然後所有都有點選事件,點選子節點,就會觸發從外至內 父節點-》子節點的點選事件
<!-- HTML -->
<div id="app">
 <div class="outeer" @click.capture="outer">
 <div class="middle" @click.capture="middle">
 <button @click.capture="inner">點選我(^_^)</button>
</div>
</div>
</div>
複製程式碼
1.4 .self
修飾符.self
只會觸發自己範圍內的事件,不會包含子元素。
<!-- HTML -->
<div id="app">
 <div class="outeer" @click.self="outer">
 <div class="middle" @click.self="middle">
 <button @click.stop="inner">點選我(^_^)</button>
</div>
</div>
</div>
複製程式碼
1.5 .once 只執行一次點選
如果我們在@click
事件上新增.once
修飾符,只要點選按鈕只會執行一次。
2 鍵盤修飾符
在JavaScript事件中除了前面所說的事件,還有鍵盤事件,也經常需要監測常見的鍵值。在Vue中允許v-on
在監聽鍵盤事件時新增關鍵修飾符。記住所有的keyCode
比較困難,所以Vue為最常用的鍵盤事件提供了別名:
.enter
:Enter鍵.tab
:製表鍵.delete
:含delete
和backspace
鍵.esc
:返回鍵.space
: 空格鍵.up
:向上鍵.down
:向下鍵.left
:向左鍵.right
:向右鍵
3 滑鼠修飾符
滑鼠修飾符用來限制處理程式監聽特定的滑鼠按鍵。常見的有:
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 函式縮寫
縮寫後
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>
複製程式碼
- 元件名不要帶大寫,多元件使用 -
- 只要元件和定義相同是可以的(首字母可以大寫)
- 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
}
}
})
複製程式碼
點選前
點選後
從圖中可以得知: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.then
和MessageChannel
,如果執行環境不支援,會採用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"]
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 方法
說明:控制檯可以在person 裡找到age 這個屬性,說明新增成功 (響應式)
對比非響應式方法
vm.food="chocolate"
alert(vm.food)
控制檯和網頁上的 {{person}} 都沒有顯示food 這個屬性,說明food 這個屬性沒有被新增 (非響應式)
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中的methods
、computed
或者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>
複製程式碼
當我們看到插入成功的時候,匿名插入就實現了
具名插槽
匿名插槽沒有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
這種寫法,習慣了element-ui的朋友一定就很熟悉了。
總結:
1 . 使用slot可以在自定義元件內插入原生HTML元素,需要搭配使用name和slot屬性,否則多個slot可能會返回重複的HTML元素。
2 . 使用slot-scope可以將slot內部的作用域指向該子元件,否則預設作用域指向呼叫slot的父元件。
新版本的 v-slot
從 vue@2.6.x 開始,Vue 為具名和範圍插槽引入了一個全新的語法,即我們今天要講的主角:
v-slot
指令。目的就是想統一slot
和scope-slot
語法,使程式碼更加規範和清晰。既然有新的語法上位,很明顯,slot
和scope-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>
複製程式碼
這裡的表示式包含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 能夠正確識別出小駝峰和下劃線命名法混用的變數,如這裡的
forChildMsg
和for-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 資料
通常有兩種原因:
-
prop 作為初始值傳入後,子元件想把它當做區域性資料來用
-
prop 作為初始值傳入後,由子元件處理成其他資料輸出
-
定義一個區域性變數,並用 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。
- 定義一個計算屬性,處理 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";
}
}
};
複製程式碼
- 更加妥帖的方式是使用變數儲存 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生命週期簡介
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
瀏覽器裡開啟,F12
看console
就能發現
3 beforecreated
el 和 data 並未初始化
4 created
完成了 data 資料的初始化,el沒有
5 beforeMount
完成了 el 和 data 初始化
6 mounted
完成掛載
7 update
在console控制檯中輸入
app.message= 'hello!!';
複製程式碼
8 destroy
我們在console裡執行下命令對 vue例項進行銷燬。銷燬完成後,我們再重新改變message的值,vue不再對此動作進行響應了。但是原先生成的dom元素還存在,可以這麼理解,執行了destroy操作,後續就不再受vue控制了。
app.$destroy();
複製程式碼
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
的封裝元件,在下列情形中,可以給任何元素和元件新增進入/離開過渡
在進入/離開的過渡中,會有 6 個 class 切換。
v-enter
:定義進入過渡的開始狀態。在元素被插入之前生效,在元素被插入之後的下一幀移除。v-enter-active
:定義進入過渡生效時的狀態。在整個進入過渡的階段中應用,在元素被插入之前生效,在過渡/動畫完成之後移除。這個類可以被用來定義進入過渡的過程時間,延遲和曲線函式。v-enter-to
: 2.1.8版及以上 定義進入過渡的結束狀態。在元素被插入之後下一幀生效 (與此同時v-enter
被移除),在過渡/動畫完成之後移除。v-leave
: 定義離開過渡的開始狀態。在離開過渡被觸發時立刻生效,下一幀被移除。v-leave-active
:定義離開過渡生效時的狀態。在整個離開過渡的階段中應用,在離開過渡被觸發時立刻生效,在過渡/動畫完成之後移除。這個類可以被用來定義離開過渡的過程時間,延遲和曲線函式。v-leave-to
: 2.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>
複製程式碼
出現如圖情況
再來個栗子
<!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>
複製程式碼
可以拖動
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('====================================');
}
})
複製程式碼