最近由于各種原因又開始面試了噪服,然后想把這方面關(guān)于vue做一個整理,以便于自己后續(xù)鞏固復習~
若有不足拼弃,歡迎大佬補充并指證~
1略号、vue的生命周期鉤子函數(shù)
1)berforeCreate new vue()觸發(fā)的第一個鉤子,現(xiàn)階段data纵菌、methods斥赋、computed以及watch上的數(shù)據(jù)和方法都不能被訪問。
2)created 實力創(chuàng)建完成后觸發(fā)产艾,此階段完成了數(shù)據(jù)觀測疤剑,可是使用數(shù)據(jù),更改數(shù)據(jù)闷堡。需要注意的是隘膘,在這個階段更改數(shù)據(jù)的話,不會觸發(fā)updated函數(shù)哦~杠览,能獲取初始數(shù)據(jù)弯菊,不能和dom進行交互,如果實在忍不住想訪問dom踱阿,你用vm.$nextTick試試管钳!
3) beforeMount 掛在之前觸發(fā),此階段templete已經(jīng)導入渲染函數(shù)進行編譯软舌,而且此時虛擬dom已經(jīng)創(chuàng)建完成才漆,馬上就開始渲染了.注意哈,此階段更改數(shù)據(jù)的話和上一個一樣佛点,不會觸發(fā)updated的哦
4) mount 掛在完成后觸發(fā)醇滥,此階段真實dom掛載完成了,數(shù)據(jù)也雙向綁定了超营,可以訪問dom節(jié)點了鸳玩,操作dom的話用$refs試試,結(jié)果會讓你滿意的演闭。
5)beforeUpdate 數(shù)據(jù)更新之前(響應式數(shù)據(jù)發(fā)生更新不跟,虛擬dom重新渲染之前)觸發(fā),這個階段可以進行數(shù)據(jù)更改米碰,那會造成重渲染嗎窝革?答案當然是不會啦,放手干吧见间!
6)updated 更新完成聊闯,dom完成更新觸發(fā)。注意注意:避免在這個期間更改數(shù)據(jù)米诉,不然就導致無線循環(huán)的更新啦菱蔬。
7)beforeDestroyed 實例銷毀之前觸發(fā),當前階段可以進行善后收尾工作,比如說清楚計時器之類的拴泌。
8)destroyed 實例銷毀之后觸發(fā)剩一個dom空殼魏身,組件被拆解了,數(shù)據(jù)綁定被卸除蚪腐,監(jiān)聽被移出箭昵,子實例也統(tǒng)統(tǒng)被銷毀。
2 理解一下MVVM回季,和MVC有什么區(qū)別
##哎呀家制,先說說MVC吧,MVC 模式代表 Model-View-Controller(模型-視圖-控制器) 模式泡一,這種模式一般用于應用程序的分層開發(fā)颤殴。
Model(模型) - 模型代表一個存取數(shù)據(jù)的對象或 JAVA POJO。它也可以帶有邏輯鼻忠,在數(shù)據(jù)變化時更新控制器涵但。
View(視圖) - 視圖代表模型包含的數(shù)據(jù)的可視化。Controller(控制器) - 控制器作用于模型和視圖上帖蔓。它控制數(shù)據(jù)流向模型對象矮瘟,并在數(shù)據(jù)變化時更新視圖。它使視圖與模型分離開塑娇。
##再來說說MVVM吧澈侠,
MVVM是Model-View-ViewModel的簡寫,本質(zhì)上就是MVC 的改進版钝吮。
即 MODEL 模型- VIEW 視圖- VIEWMODEL 視圖模型埋涧。
MODEL 指的是后端傳遞的數(shù)據(jù)。
VIEW 指的是所看到的頁面奇瘦。
VIEWMODEL mvvm模式的核心,它是連接view和model的橋梁劲弦。那么你得知道它做了什么耳标,它做了兩件事情,或者說它走了兩個方向邑跪。
1)將 MODEL 轉(zhuǎn)化成 VIEW次坡,即將后端傳遞的數(shù)據(jù)轉(zhuǎn)化成所看到的頁面。實現(xiàn)的方式是:數(shù)據(jù)綁定画畅。俗稱聯(lián)調(diào)接口砸琅,把后端返回的數(shù)據(jù)展示在前端頁面。
2)將 VIEW 轉(zhuǎn)化成 MODEL 轴踱,即將所看到的頁面轉(zhuǎn)化成后端的數(shù)據(jù)症脂。實現(xiàn)的方式是:DOM 事件監(jiān)聽。
上述兩項都完成了,就是我們常說的诱篷,數(shù)據(jù)的雙向綁定壶唤。
2、vue 2.0 如何實現(xiàn)的雙向數(shù)據(jù)綁定(說白了就是考原理棕所,有興趣就去看看源碼吧闸盔。)
先網(wǎng)上扒一張圖過來
看圖,首先就是data琳省,在vue里迎吵,要實現(xiàn)對data的數(shù)據(jù)相應,利用Observer進行數(shù)據(jù)劫持针贬。初始化數(shù)據(jù)之后击费,使用Object.definedProperty重新定義data中的所有屬性,然后進行依賴收集坚踩,也就是圖中的watcher(當前組件的watcher)
注釋:Object.definedProperty(obj, prop, descriptor)
obj:必需荡灾。目標對象
prop:必需。需定義或修改的屬性的名字
descriptor:必需瞬铸。目標屬性所擁有的特性
存取器了解一下批幌,getter、setter
getter:當訪問該屬性時嗓节,該方法會被執(zhí)行荧缘。函數(shù)的返回值會作為該屬性的值返回
setter:當屬性值修改時,該方法會被執(zhí)行拦宣。該方法將接受唯一參數(shù)截粗,即該屬性新的參數(shù)值。
動動爪子試試
附一下代碼:
var obj = {}; //定義一個新對象
var initVal = 'nihao'; //設(shè)置一個初始值
Object.defineProperty(obj,"newVal",{ //重定義數(shù)據(jù),也就是數(shù)據(jù)劫持鸵隧,數(shù)據(jù)代理
get:function (){//當獲取值的時候觸發(fā)的函數(shù)绸罗,也就是obj.newVal 的默認值是 nihao
return initVal;
},
set:function (value){//當設(shè)置值的時候觸發(fā)的函數(shù),設(shè)置的新值通過參數(shù)value拿到,如果設(shè)置新的值豆瘫,obj.newVal就會發(fā)生改變
initVal = value;
}
});
//獲取值
console.log( obj.newVal ); // 打印出來 hello
//設(shè)置值
obj.newVal = 'hao ge der';
console.log( obj.newVal ); //打印出來 hao ge der
打印結(jié)果 不信你自己試試
注意:當使用了getter或setter方法珊蟀,不允許使用writable和value這兩個屬性
注意:不要在getter中再次獲取該屬性值,也不要在setter中再次設(shè)置改屬性外驱,否則會棧溢出
簡單寫個vue 數(shù)據(jù)代理和劫持
文件目錄
簡單示例
在js文件里面寫個簡單的vue 實例 進行數(shù)據(jù)代理 和數(shù)據(jù)劫持
class Vue {
// 構(gòu)造器
constructor(params){
this.$options = params // vm
this._data = params.data //vm.data
this.initData() //初始化
}
initData(){
let data = this._data;
console.log(data) //結(jié)果如下
// {name: "xuxiansheng", message: "this is vue.js"}
// message: "this is vue.js"
// name: "xuxiansheng"
// proto: Object 原型
//收集所有集合中的key 結(jié)果是一個數(shù)組 也就是收集watcher
let keys = Object.keys(data); //結(jié)果如下
// ["name", "message"]
console.log(keys)
// 遍歷數(shù)組 使用objectDefine.property()對數(shù)據(jù)進行代理
for(let i = 0; i < keys.length; i++){
// this指向vue實例
// 注意這里要用關(guān)鍵字let 而不要用var 否則會出現(xiàn)變量名提升 訪問不到data中的數(shù)據(jù)
// Object.definedProperty(obj, prop, descriptor)
// obj:必需育灸。目標對象
// prop:必需。需定義或修改的屬性的名字
// descriptor:必需昵宇。目標屬性所擁有的特性
Object.defineProperty(this, keys[i], {
// 可枚舉磅崭?true or false
// 至于for...in循環(huán)和Object.keys方法的區(qū)別,在于for...in包括對象繼承自原型對象的屬性瓦哎,而Object.keys只包括對象本身的屬性砸喻。
enumerable:true,
// 是否可以刪除目標屬性或是否可以再次修改屬性的特性
configurable:true,
get: function() { //獲取屬性值
return this._data[keys[i]]
},
set: function(value) { //設(shè)置新的值
// 設(shè)置值
data[keys[i]] = value
}
})
}
// 為了實現(xiàn)data 數(shù)據(jù)的響應柔逼,需要對data中的數(shù)據(jù)進行劫持
for(let i = 0;i < keys.length; i++){//遍歷數(shù)組
// 拿到data對象里面 key 為 keys[i]的值
let value = data[keys[i]]
console.log(value) // xuxiansheng this is vue.js
Object.defineProperty(data, keys[i], {
enumerable:true,
configurable:true,
get: function reactiveGetter(){
console.log(`data的屬性${keys[i]}的獲取`);
return value;
},
set: function reactiveSetter(val){
console.log(`data設(shè)置屬性${keys[i]}`)
value = val;
}
})
}
}
}
注意一下,get set函數(shù)里面打印不出來具體的屬性值恩够,所以reactiveGetter() reactiveSetter()這兩個需要手動觸發(fā)一下卒落,才能打印里面的內(nèi)容
你要想知道打印出來的是什么,蜂桶,那就去瀏覽器的控制臺輸入命令 如下
有個東西值得好好注意一下儡毕,那就是沒有進行數(shù)據(jù)劫持 只進行數(shù)據(jù)代理的時候,在瀏覽器控制臺執(zhí)行vm 回車 結(jié)果如下
但是做了數(shù)據(jù)劫持之后 如下圖
vm明明是一個實例扑媚,可結(jié)果不一樣腰湾。
所以數(shù)據(jù)代理劫持的目的就是為了實現(xiàn)vue里面的data數(shù)據(jù)訪問的時候,如this.name 直接拿到的就是message的值疆股,實際上this.name訪問的應該是this.data.message
結(jié)合實際就是這么理解
以上是簡單的數(shù)據(jù)劫持费坊,如果遇到復雜的數(shù)據(jù) 那就進行扁平化處理,比如先判斷當前數(shù)據(jù)的類型是復雜還是簡單
簡單數(shù)據(jù)注釋掉 把它和復雜數(shù)據(jù)一起處理 封裝一個方法
先判斷數(shù)據(jù)類型
進行數(shù)據(jù)劫持
這個就暫時這樣吧.....寫太累了 后續(xù)整體更新吧
3 vue 2.x如何監(jiān)測數(shù)據(jù)變化
那就是上面說的函數(shù)劫持了唄旬痹,重寫了數(shù)組的方法(也就是上面提到的object.key()得到的數(shù)組)附井,將data中的數(shù)據(jù)進行原型鏈的重寫,這樣一來調(diào)用api的時候就可以通知依賴更新了两残。
但是數(shù)組中如果包含引用類型的話永毅,也就是復雜數(shù)據(jù)類型,然后進行遞歸遍歷人弓,實現(xiàn)數(shù)據(jù)監(jiān)測沼死,看不明白就自己動動爪子寫寫,好記性不如爛筆頭
4 nextTick 實現(xiàn)原理是什么
在下一次dom更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)崔赌,這個主要使用了微任務和宏任務意蛀,不通執(zhí)行環(huán)境分別嘗試,promise mutationObserver setImmediate 健芭,如果這三個都不行就用setTimeout 通過異步方法清空任務隊列县钥。
5 說說宏任務和微任務 (上個問題答了 這個問題緊隨而來)
第一件事 先盜個圖來
請自信告訴我,上面答應出來的是什么慈迈?
1魁蒜、2、3吩翻、4? ×
2锥咸、4狭瞎、1、3搏予?×
2熊锭、4、3、1碗殷? ×
2精绎、4、1 锌妻? √ 想想為啥是這個答案
從一開始寫代碼的時候就知道一句話 js代碼是從上往下一行一行執(zhí)行的代乃,所以很多人先入為主覺得輸出應該是1234,可事實并非如此仿粹。
這就涉及到上面的宏任務 微任務了搁吓,結(jié)合上面宏任務微任務的圖來看,同步和異步任務分別進入不同的執(zhí)行"場所"吭历,同步的進入主線程堕仔,異步的進入Event Table并注冊函數(shù)。
當指定的事情完成時晌区,Event Table會將這個函數(shù)移入Event Queue摩骨。主線程內(nèi)的任務執(zhí)行完畢為空,會去Event Queue讀取對應的函數(shù)朗若,進入主線程執(zhí)行恼五。上述過程會不斷重復,也就是面試官經(jīng)常會問你的捡偏,什么是循環(huán)機制唤冈,這個就是Event Loop(事件循環(huán))機制。
再偷個圖
js異步有一個機制银伟,就是遇到宏任務你虹,先執(zhí)行宏任務,將宏任務放入eventqueue彤避,然后在執(zhí)行微任務傅物,將微任務放入eventqueue。
記住這倆規(guī)則:
宏任務一般是:包括整體代碼script琉预,setTimeout董饰,setInterval。
微任務:Promise圆米,process.nextTick卒暂。
所以上述代碼就是,setTimeout是宏任務先執(zhí)行但是特么它是異步的娄帖,所以要往下檢查是不是有微任務也祠,遇到了new Promise立即執(zhí)行之后,打印console.log('2')近速,Promise.then()又被放入了微任務的隊列里诈嘿,所以先去執(zhí)行了宏任務console.log('4')堪旧,執(zhí)行完成之后才執(zhí)行了setTimeout,所有主線程的任務棧執(zhí)行完了才去執(zhí)行了Promise.then()
這塊有點復雜 暫時就理解這么多 后續(xù)補上吧