React 與其說是一種框架,倒不如說是一種開發范式。它的核心理念非常簡單:
界面/視圖就是數據結構的可視化表達
UI = f(data)
而界面/視圖由組件組合而來
UI = f1(data) + f2(data) + f3(data) + ...
That's all.
React 扮演的角色就是上述公式中的 f,它完全是函數式的,組件就是函數,給它一個輸入(傳參),它就返回一個輸出(UI)。只有理解了這種開發理念,用 React 才會得心應手。如果只是去學“語法”,僅滿足於“能用”,React 確實會顯得非常怪異而麻煩。
Vue 的核心理念與 React 其實是一致的,但在 API 的實現上 Vue 對於用戶而言更加友好。不像 React,Vue 不會要求用戶“Think in Vue”,因為不需要,Vue 是一個漸進式的框架,它並沒有大幅改變用戶原有的開發方式,它既可以作為插件植入老的 jQuery 項目,也可以作為整個項目的核心框架層。並且 Vue 提供了各種實用的 API,而 React 本身的 API 非常少,許多功能需要自行封裝或引用現成的組件。
前言
使用vue的時候經常會遇到一些問題,其實仔細閱讀查閱官方文檔,就會發現文檔中已提到一些格外需要注意的點; 為了深入的理解官方文檔中對這些問題的解釋,查閱了一些資料,再加上自己的理解,整理了一些常見的問題;如果哪方面解釋的不太合理希望各路大神指出;
文章篇幅較長,但是很實用;
目錄
- 組件里面, data必須是一個函數
- vue中$set的使用場景
- vue生命周期詳解
- vue組件通信
- vue組件之keep-alive
- 生命周期函數/methods/watch里面不應該使用箭頭函數
- methods/computed/watch
1.組件里面, data必須是一個函數
類比引用數據類型
Object是引用數據類型, 每個組件的data 都是內存的同一個地址,一個數據改變了其他也改變了;
那么用什么方法可以使每個組件的data相互獨立,不受影響呢?
當一個組件被定義,data 必須聲明為返回一個初始數據對象的函數,因為組件可能被用來創建多個實例。如果 data 仍然是一個純粹的對象,則所有的實例將共享引用同一個數據對象!通過提供 data 函數,每次創建一個新實例后,我們能夠調用 data 函數,從而返回初始數據的一個全新副本數據對象。
2.vue中$set的使用場景
場景1:
通過數組的下標去修改數組的值,數據已經被修改了,但是不觸發updated函數,視圖不更新,
1 |
export default { |
執行changeItem1, 控制台打印 111 ‘x’, 沒有觸發updated,視圖不更新
執行changeItem1, 控制台打印 222 ‘x’, 數據更新 ‘x’; 觸發updated,視圖更新
場景2: vue中檢測不到對象屬性的添加和刪除
1 |
data() { |
想要給userProfile加一個age屬性
1 |
addProperty () { |
執行addProperty函數時,打印如下
1 |
555 { name: '小明', age: '12'} |
但是沒有觸發updated, 視圖未更新
改成下面這種
1 |
addProperty () { |
再次執行, 數據發生變化, 觸發updated, 視圖更新;
有時你想向已有對象上添加一些屬性,例如使用 Object.assign() 或 _.extend() 方法來添加屬性。但是,添加到對象上的新屬性不會觸發更新。在這種情況下可以創建一個新的對象,讓它包含原對象的屬性和新的屬性:
1 |
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })` |
這是vue中很典型的一個問題,使用的時候一定要注意!
簡單的解釋一下原理:
vue在創建實例的時候把data深度遍歷所有屬性,並使用 Object.defineProperty 把這些屬性全部轉為 getter/setter。讓 Vue 追蹤依賴,在屬性被訪問和修改時通知變化。所以屬性必須在 data 對象上存在才能讓 Vue 轉換它,這樣才能讓它是響應的。
當你在對象上新加了一個屬性 newProperty,當前新加的這個屬性並沒有加入vue檢測數據更新的機制(因為是在初始化之后添加的),vue.$set是能讓vue知道你添加了屬性, 它會給你做處理
3.vue生命周期詳解
1. vue的生命周期
- beforeCreate: 組件實例剛剛被創建,組件屬性計算之前,如data屬性
- created: 組件實例創建完成,屬性已綁定,但是DOM還未完成,$el屬性還不存在
- beforeMount:模板編譯/掛載之前
- mounted: 模板編譯/掛載之后
- beforeUpdate: 組件更新之前
- updated: 組件更新之后
- activated: for
keep-alive
,組件被激活時調用 - deactivated: for
keep-alive
,組件被移除時調用 - beforeDestroy: 組件銷毀前被調用
- destoryed: 組件銷毀后調用
ps:下面代碼可以直接復制出去執行
1 |
<!DOCTYPE html> |
beforeCreated: el和data並未初始化
created: 完成data數據的初始化,el沒有
beforeMount: 完成了el和data初始化
mounted: 完成掛載
1 |
打開命令行在命令行中輸入vm.a = 'change';查看效果 |
4.vue組件通信
1.父組件給子組件傳遞數據
vue中使用props向子組件傳遞數據
1): 子組件在props中創建一個屬性,用於接收父組件傳過來的值
2): 父組件中注冊子組件
3): 在子組件標簽中添加子組件props中創建的屬性
4): 把需要傳給子組件的值賦給該屬性
2.子組件向父組件傳遞數據
子組件主要通過事件傳遞數據給父組件
1), 子組件中需要以某種方式,例如點擊事件的方法來觸發一個自定義事件
2),將需要傳的值作為$emit的第二個參數,該值將作為實參數傳給相應自定義事件的方法
3),在父組件中注冊子組件並在子組件標簽上綁定自定義事件的監聽
3.子組件向子組件傳遞數據
vue找那個沒有直接子組件對子組件傳參的方法,建議將需要傳遞數據的在組件,都合並為一個組件,如果一定需要子組件對子組件傳參,可以先傳到父組件,再傳到子組件,為了方便開發,vue推出了一個狀態管理工具vuex,可以啃方便的實現組件之間的參數傳遞
具體的實例代碼如下:可以自行參考相關代碼在編輯器中嘗試
父組件向子組件傳遞數據
1 |
// 父組件向子組件傳遞數據 |
子組件向父組件傳遞數據// 子組件向父組件傳遞數據<!--
1.@ : 是 v-on的簡寫
2.子組件主要通過事件傳遞數據給父組件 3.當input的值發生變化時,將username傳遞給parent.vue,首先聲明了一個setUser,用change事件來調用setUser 4.在setUser中,使用了$emit來遍歷transferUser事件,並返回this.username,其中transferuser是一個自定義事件,功能類似一個中轉,this.username通過這個事件傳遞給父組件 --> <template> <div> <div>{{msg}}</div> <span>用戶名</span> <input v-model="username" @change='setUser'>向父組件傳值</button> </div> </template> <script> export default { data() { return { username: '測試', }; }, props: { msg: { type: String, }, }, methods: { setUser() { this.$emit('transferuser', this.username); }, }, }; </script>
在父組件
<Child @transferuser='fn'/>
export default {
name: 'parent',
data: function () {
},
components: {
child
},
methods: {
fn: function (msg) { //回調方法,接收子組件傳的參數
this.msg = msg;
}
},
mounted: function () {
});
}
};
5.vue組件之keep-alive
項目中寫vue也沒注意到<keep-alive></keep-alive>
這個組件,最近在深入的研究vue組件的生命周期函數,每一個函數都是干嘛的,然后其中有activated
和deactivated
這兩個函數與<keep-alive></keep-alive>
這個組件有關
activated
: keep-alive組件激活時調用deactivated
: keep-alive組件停用時調用keep-alive用法
<keep-alive>
包裹動態組件時,會緩存不活動的組件實例,而不是銷毀它們<keep-alive>
是一個抽象組件:它自身不會渲染一個DOM元素,也不會出現在父組件鏈中- 當組件在
<keep-alive>
內被切換,它的activated
和deactivated
這兩個生命周期鈎子函數將會被對應執行具體的實例如下
- 是一個簡單的tab切換,可以嘗試把
<keep-alive>
去掉之后,對比一下,然后就會發現它的好處
test.vue
1 |
<template> |
測試結果如下:
注意看一下頁面和控制台輸出的信息,可以更加直觀的注意到<keep-alive>
的作用及activated
和deactivated
這兩個函數什么時候會被觸發
用setTimeout模擬請求后端接口的場景
test1.vue
和test2.vue
的相關代碼如下:
test1.vue
1 |
<template> |
test2.vue
1 |
<template> |
6. 生命周期函數/methods/watch里面不應該使用箭頭函數
es6的箭頭函數的出現,是我們可以用更少的代碼實現功能,但是應該注意箭頭函數和普通函數的最大區別是this的指向問題: 箭頭函數的this指向函數所在的所用域,普通函數的this指向函數的調用者;
官方文檔中特別提醒中已經指出這一點:
vue中生命周期函數, methods, watch 自動綁定 this 上下文到實例中,因此你可以訪問數據,對屬性和方法進行運算。這意味着 你不能使用箭頭函數來定義一個生命周期方法, 這是因為箭頭函數綁定了父上下文,因此 this 與你期待的 Vue 實例不同
7.methods/computed/watch
methods VS computed
我們可以將同一個函數定義為methods或者computed,用這兩種方式,得到的結果是相同的,不同的是computed是基於它們的依賴進行緩存的,計算屬性只有在它相關的依賴發生改變時才重新求值;
適用場景:
重新計算開銷很大的話,選computed; 不希望有緩存的選methods
computed vs watch
watch 有新舊值兩個參數, 計算屬性沒有,但是計算屬性可以從setter獲得新值
關於computed
對於計算屬性要特別說明一點: vue的計算屬性computed默認只有getter,需要使用getter的時候需要自己加一個setter
1 |
export default { |
執行 changeFullName 發現報錯[Vue warn]: Computed property "fullame" was assigned to but it has no setter.
我們需要給計算屬性fullName添加一個setter
1 |
computed: { |