面試題-Vue

Vue八個生命周期

beforeCreate【創建前】????created【創建后】

beforeMount【載入前】? ? mounted【載入后】

beforeUpdate【更新前】? ? updated【更新后】

beforeDestroy【銷毀前】? ? destroyed【銷毀后】

vue生命周期的作用:

它的生命周期有多個事件鉤子,讓我們控制Vue實例的過程時形成更好的邏輯


Vue、React、Angular之間的區別【題目根據此文章內容整理】

MVX框架模式:MVC+MVP+MVVM

1、MVC:Model【模型】+View【視圖】+Controller【控制器】,只要基于分層的目的,讓彼此的指責分開

View通過Controller和Model聯系,Controller是View和Model的協調者,View和Model不直接聯系,基本聯系都是單向的;


MVC模式

用戶通過控制器Controller來操作模版Model從而達到視圖View的變化。

2、MVP是從MVC模式演變出來的,都是通過Controller/Presenter負責邏輯的處理+Model提供數據+View負責顯示

在MVP中,Presenter完全把View和Model進行了分離,主要的程序邏輯在Presenter中實現。

MVP模式

3、MVVM

MVVM是把MVC里面的Controller和MVP里的Presenter改成了ViewModel。Model+View+ViewModel

View變化會自動更新到ViewModel,ViewModel的變化也會自動同步到View顯示。

這種自動同步是因為ViewModel的屬性實現了Observer,當屬性變更時都能出發對應的操作。

MVVM模式



Vue實現數據雙向綁定的原理

采用數據劫持結合發布者+訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter、getter,在數據變動時發布消息給訂閱者,觸發相應監聽回調。當把一個普通JS對象傳給Vue實例,來作為它的data選項時,Vue將遍歷它的屬性,用Object.defineProperty()將他們轉化為getter/setter。用戶看不到getter/setter,但是在內部他們讓Vue追蹤依賴,在屬性被訪問和修改時通知變化。

vue的數據雙向綁定,將MVVM作為數據綁定的入口,整合Observer、Compile和Watcher三者,通過Observer來監聽自己的model數據變化,通過Compile來解析編譯模版指令(Vue是用來解析{{}}),最終利用Watcher搭起Observer和Compile之間的通信橋梁,達到數據變化=>視圖更新;視圖監護變化(input)=>數據model變更雙向綁定效果。


Vue組件間的參數傳遞

1、父組件與子組件傳值

父組件=>子組件:子組件通過props方法接受數據;

子組件=>父組件:$emit方法傳遞參數;

2、兄弟組件傳值

eventBus,就是創建一個事件中心,相當于中轉站,可以用它來傳遞事件和接受事件。【項目比較小時用這個比較合適,Vuex適用于中大型項目】


Vue解決跨域問題


Vue虛擬DOM傳送門

瀏覽器渲染引擎工作流程

創建DOM樹=>創建StyleRules=>創建Render樹=>布局Layout=>繪制Painting

1、用html分析器,分析html元素,構建一顆DOM樹

2、用CSS分析器,分析css文件和元素上的inline樣式,生成頁面的樣式表

3、將DOM樹和樣式表關聯起來,構建一顆Render樹每個DOM節點都有attach方法,接受樣式信息,返回一個render對象,這些render對象最終會被構建成一顆Render樹;

4、有了Render樹,瀏覽器開始布局,為每個Render的節點確定一個在顯示屏上出現的精確坐標;

5、Render樹和顯示坐標節點都有了,就調用每個節點paint方法。把他們繪制出來。

JS操作真實DOM的代價

用我們傳統模式開發,原生js或者jq操作dom時,瀏覽器會從構建dom樹開始從頭到尾執行一次,在第一次操作中,我們需要更新10個DOM節點,瀏覽器收到第一個DOM請求后會立即執行,并不知道之后還有九次,最終執行10次,因此js直接操作真實DOM節點會付出很大的代價。

為什么需要虛擬DOM

web界面由DOM樹來構建,當其中一部分發生變化時,其實就是對應某個DOM節點發生了變化。

虛擬DOM就是為了解決瀏覽器性能問題而被設計出來的;

創建DOM樹=>創建StyleRules=>創建Render樹【10次DOM操作=>虛擬DOM=>將十次更新的diff內容存儲到本地的js對象上=>將這個js對象一次性attch到DOM樹上】=>布局Layout=>繪制Painting


Vue異步更新隊列

獲取DOM內部內容

運行后會拋出錯誤:Cannot read property 'innnerHTML of null,意思就是獲取不到div元素。這里就涉及到一個vue重要的概念:異步更新隊列

Vue會根據當前瀏覽器環境優先使用原生的Promise.then和MutationObserver,如果都不支持,就會采用setTimeout代替。【Vue異步更新DOM

Vue在觀察數據變化時,并不是直接更新DOM,而是開啟一個隊列,并緩沖同一個事件循環中發生的所有數據變化。在緩沖過程中會去除所有重復數據,從而避免不必要的計算和DOM操作。

$nextTick知道什么時候DOM更新完成

vue-router鏈接

vue-router是vue官方的路由管理器。他和vue的核心深度集成讓構建單頁面應用變得易如反掌。

使用vue,我們通過組合組件來組成應用程序,當把vue-router添加進來,需要將組件映射到路由,然后告訴vue-router哪里渲染他們。

通過注入路由器,我們可以在任何組件內通過this.$router訪問路由器。

this.$router和router使用起來完全不一樣,我們使用this.$router的原因是我們并不想在每個獨立需要封裝路由的組件中都導入路由。

router:

//使用router-link組件來導航,通過to屬性指定鏈接

<router-link to="/foo"></router-link>? ? //默認渲染為a標簽

//路由出口? ????路由匹配到的組件將在這里渲染

<router-view></router-view>??

1、定義組件

var Foo = {template: '<div>foo</div>'};

2、定義路由

每個路由應該映射一個組件

通過vue.extend()創建組件構造器

const routes = [{"foo", component: Foo}]

3、創建router實例,然后傳routes配置

const router = new VueRouter({

? ? routes

})

4、創建和掛載根實例

通過router配置參數注入路由,從而讓整個應用都有路由功能

const app = new Vue({

? ? router

}).$mount("#app")

動態路徑參數

const Foo = {template: '<div></div>'};

const router = new VueRouter({

? ? routes: [

? ? ? ? {path: '/user/:id', component: Foo}

????]

})

一個路徑參數使用冒號:標記。當匹配到一個路由時,參數值會被設置到this.$router.params

響應路由參數的變化

當使用路由參數時,原來的組件實例會被復用,不過這也意味著組件的生命周期鉤子不會再被調用

復用組件時,相對路由參數的變化作出響應的話,可以watch $route對象

const Foo = {

? ? template: '...',

? ? watch:{

? ? ? ? '$route'(to, from){

? ? ? ? ? ? 對路由變化作出相應

????????}

????}

}

或者使用2.2中引入的beforeRouteUpdate導航守衛

const Foo = {

? ? template: '...',

? ? beforeRouteUpdate(to,from,next){


????}

}

捕獲所有路由以及404路由

常規參數只會匹配被/分隔url片段中的字符。如果想匹配任意字符,我們可以使用通配符*。

當使用通配符路由時,請確保路由的順序是正確的,也就是含有通配符的路由應該放到最后。路由{path:'*'}通常用于客戶端404錯誤,。如果你使用了history模式,請確保正確配置你的服務器。

當使用通配符時,$route.params會自動添加一個名為pathMatch的參數。它包含了通配符匹配的部分。

高級匹配模式

匹配優先級【誰先定義的,誰的優先級就最高】

嵌套路由

<div id="app"><router-link></router-link></div>

const User = {

? ??template: '<div>{{$router.params.id}}<router-view></vouter-view/></div>'

}

const router = new VueRouter({

? ? routes:[

? ? ? ? {path: '/user/:id', component: User,

? ? ? ? children:[{

? ? ? ? ? ? 當路由匹配到‘/user/:id/post’時

? ? ? ? ? ? path:''post",

? ? ? ? ? ? component:post

????????}]

? ? ? ? }

????]

})

要注意,以/開頭的嵌套路由會被當做根路徑。這讓你充分的使用全套組件而無需設置嵌套的路徑。

編程式導航

在vue實例內部,你可以通過$router訪問到路由實例。因此你可以調用this.$router.push。

想要導航到不同的url,則可以使用router.push方法。這個方法會向history棧添加一個新的記錄,所以當用戶點擊瀏覽器后退按鈕時,則回到之前的url。

點擊<router-link to=""></router-link>相當于調用router.psuh(...)

聲明式/編程式
編程式路由

同樣的規則也適用于router-link組件的to屬性。

在2.2.0+,可選的在router.push或router.replace中提供onComplete和onAbort回調作為第二個和第三個參數。這些回調將會在導航完成(所有的異步鉤子被解析之后)或終止(導航到相同的路由、或在當前導航完成以前導航到另一個不同的路由)的時候進行響應的調用。

router.replace(location,onComplete,onAbout)

跟router.push很像,唯一的不同就是他不會向history添加新記錄,跟他的方法命一樣,替換掉當前的history記錄。

router.go()

操作history

router.push()/router.replace()/router.go()與window.history.pushState()/window.history.replaceState()/window.history.go相似,實際上他們確實消防了window.historyAPI.

命名路由

通過一個名稱來標示路由,連接一個路由或執行跳轉的時候。可以在創建Router實例的時候,在routes配置中給某個路由設置名稱。

const router = new VueRouter({

? ? routes:[{

? ? ? ? path: '/user/:id',

? ? ? ? name: 'user',

? ? ? ? component: User

? ? }]

})

要鏈接一個命名路由,可以給router-link的to屬性傳一個對象:

<router-link :to="{name:'user',params:{userId123}}"></router-link>

這跟代碼調用router.push()一樣

router.push({name:"user",params:{userId:123}})? ? //? ? ?'/user/123'

命名視圖

同時【同級】展示多個視圖,而不是嵌套展示。例如創建一個布局,有sidebar(側導航)和main(主內容)兩個視圖,這個時候命名視圖就派上用場了。可以在界面中擁有多個單獨命名的視圖,而不是只有一個單獨的出口。如果router-view沒有設置名字,那么默認default。

<router-view name="a"></router-view>

<router-view name="b"></router-view>

<router-view></router-view>

一個視圖使用一個組件渲染,因此對于同個路由多個視圖就需要多個路由。

const router = new VueRouter({

? ? routes: [{

? ? ? ? path: '/user',

? ? ? ? components: {

? ? ? ? ? ? default: Foo,

? ? ? ? ? ? a:Bar,

? ? ? ? ? ? b: Baz

????????}

????}]

})

嵌套命名路由

const router? = new VueRouter({

? ? routes:[{

? ? ? ? path: '/',

? ? ? ? component: Foo,

? ? ? ? children: [{

? ? ? ? ? ? path: '/',

? ? ? ? ? ? component: User

????????},{

? ? ? ? ? ? path: '/',

? ? ? ? ? ? components:{

? ? ? ? ? ? ? ? a: A,

? ? ? ? ? ? ? ? b: B,

? ? ? ? ? ? ? ? default: C

????????????}

????????}]

????}]

})

重定向

重定向也是通過routes配置完成的:

const router = new VueRouter({

? ? routes: [

? ? ? ? {path: '/a',redirect: '/b'}

????]

})

重定向的目標也可以是一個命名的路由:

const router = new VueRouter({

? ? routes: [

? ? ? ? {path: '/a',redirect: {name: 'foo'}}

????]

})

或者一個動態方法,動態返回重定向目標:

const router = new VueRouter({

? ? routes: [

? ? ? ? {path: '/a', redirect: to=> {

? ? ? ? ? ? 方法接收目標路由作為參數

? ? ? ? ? ? return 重定向的 字符串路徑路徑對象

????????}}

????]

})

路由組件傳參

const User = {

? ? props: ['id'],

? ? template: '<div>{id}</div>'

}

const router = new VueRouter({

? ? routes: [

? ? ? ? {path: '/user/:id', component: User, props: true}

????]

})

hash模式與history模式區別:【vue-router核心:改變視圖的同時不會想后端發送請求】【單頁面應用】

聲明式導航=>hash模式、編程式導航=>history模式

1、hash-即地址欄url中的#符號

特點:hash雖然出現在url中,但不會被包括在http請求中對后端完全沒有影響,因此改變hash不會重新加載頁面

2、history-利用了h5 history interface 中新增的window.history.pushState()以及window.history.replaceState()方法

因此hash模式以及history模式都是屬于瀏覽器的特性,vue-router只是利用了這兩種特性來實現前端路由。

優缺點:

1??history模式設置的新的url可以是與當前url同源的任意url,而hash模式只能修改#后面的部分;

2??history模式可以設置與當前url相同,也可以把記錄添加到棧,而hish模式設置的url必需與原來url不一樣,才能把記錄添加到棧;

3??history模式通過stateObject參數可以添加任意類型的數據到記錄中,而hash模式只可以添加短字符;

4??history模式可以額外設置title屬性提供后續使用;

5??hash模式,只有hash之前的url被包含在請求中,所以對后端來說,即使沒有做到對路由的全覆蓋,也不會出現404的情況;history模式下,前端的url必需和后端的url一致。如:'http:www.baidu.com/book/id'如果后端缺少對'/book/id'路由的處理,則會出現404情況。

導航守衛

全局前置守衛:router.beforeEach

const router = new VueRouter({})

router.beforeEach((to, form, next)=>{

? ? //to即將進入的路由目標對象

? ? //from當前導航正要離開的路由

? ? //next【function】一定要調用該方法來resolve這個鉤子。執行效果依賴next方法的調用參數。

})

全局后置守衛:router.afterEach((to, from) => {})

路由獨享的守衛:beforeEnter

組件內的守衛:beforeRouterEnter/beforeRouterUpdate/beforeRouterLeave

完整的導航解析流程:

1、導航被觸發;

2、在失活的組件里調用離開守衛;

3、調用全局的beforeEach守衛;

4、在重用的組件里調用beforeRouterUpdate守衛;

5、在路由配置里靠用befroeEnter;

6、解析異步路由組件;

7、再被激活的組件里調用beforeRouterEnter;

8、調用全局的beforResolve守衛;

9、導航被確認;

10、觸發dom刷新;

11、用創建好的實例調用beforeRouterEnter傳給next回調函數;

路由的懶加載

當打包構建應用時,js包會變得非常大,影響頁面加載。如果我們能把不同路由對應的組件分割成不同的代碼塊,然后當路由被訪問的時候才加載對應組件,這樣就更加高效了。

結合vue的異步組件webpack的代碼分割功能輕松實現路由組件的懶加載。

1、將異步組件定義為返回一個Promise的工廠函數

const Foo = () => Promise.resolve({組件定義對象})

2、在webpack2中,我們可以使用動態import語法來定義代碼分塊點

import('./Foo.vue')

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容