生命周期鉤子函式
Vue 實體有一個完整的生命周期,也就是從開始創建、初始化資料、編譯模版、掛載Dom -> 渲染、更新 -> 渲染、卸載等一系列程序,我們稱這是Vue的生命周期
- Vue中組件生命周期呼叫順序
| 生命周期 | 描述 |
|---|---|
| beforeCreate | 組件實體被創建之初,組件的屬性生效之前 |
| created | 組件實體已經完全創建,屬性也系結,但真實dom還沒有生成,$el還不可用 |
| beforeMount | 在掛載開始之前被呼叫:相關的 render 函式首次被呼叫 |
| mounted | el 被新創建的 vm.$el 替換,并掛載到實體上去之后呼叫該鉤子 |
| beforeUpdate | 組件資料更新之前呼叫,發生在虛擬 DOM 打補丁之前 |
| update | 組件資料更新之后 |
| activited | keep-alive專屬,組件被激活時呼叫 |
| deadctivated | keep-alive專屬,組件被銷毀時呼叫 |
| beforeDestory | 組件銷毀前呼叫 |
| destoryed | 組件銷毀后呼叫 |
虛擬dom
- 由于dom操作耗時十分長,且dom物件的體積很大,單個div的dom屬性就有294個之多;
- Virtual DOM 就是用一個原生的 JS 物件去描述一個 DOM 節點,所以它比創建一個 DOM 的代價要小很多,
- VNode 是對真實 DOM 的一種抽象描述,它的核心定義無非就幾個關鍵屬性,標簽名、資料、子節點、鍵值等,其它屬性都是用來擴展 VNode 的靈活性以及實作一些特殊 feature 的,由于 VNode 只是用來映射到真實 DOM 的渲染,不需要包含操作 DOM 的方法,因此它是非常輕量和簡單的,
- Virtual DOM到真實的dom需要經過以下程序:VNode 的 create、diff、patch
v-model雙向資料系結原理
vue 雙向資料系結是通過 資料劫持 結合 發布訂閱模式 的方式來實作的,也就是說資料和視圖同步,資料發生變化,視圖跟著變化,視圖變化,資料也隨之發生改變; 核心:Object.defineProperty() 方法,
v-model本質上是語法糖,v-model在內部為不同的輸入元素使用不同的屬性并拋出不同的事件
- text 和 textarea 元素使用 value 屬性和 input 事件
- checkbox 和 radio 使用 checked 屬性和 change 事件
- select 欄位將 value 作為 prop 并將 change 作為事件
<input v-model="sth" /><!-- 二者等價 -->
<input :value="sth" @input="sth = $event.target.value" />
復制代碼
vue 插槽
設定在自組件內部的插槽像一個盒子,位置由子組件決定,放什么內容由父組件決定,
實作了內容分發,提高了組件自定義的程度,讓組件變的更加靈活
- 默認插槽:
無需name屬性,取子組件肚子里第一個元素節點作為默認插槽,
<!-- 子組件,組件名:child-component -->
<div class="child-page">
<h1>子頁面</h1>
<slot></slot> <!-- 替換為 <p>hello,world!</p> -->
</div>
<!-- 父組件 -->
<div class="parent-page">
<child-component>
<p>hello,world!</p>
</child-component>
</div>
<!-- 渲染結果 -->
<div class="parent-page">
<div class="child-page">
<h1>子頁面</h1>
<p>hello,world!</p>
</div>
</div>
復制代碼
- 具名插槽:
在多個插槽的情況下使用,利用name標識插槽,
<!-- 子組件,組件名:child-component -->
<div class="child-page">
<h1>子頁面</h1>
<slot name="header"></slot>
<slot></slot> <!-- 等價于 <slot name="default"></slot> -->
<slot name="footer"></slot>
</div>
<!-- 父組件 -->
<div class="parent-page">
<child-component>
<template v-slot:header>
<p>頭部</p>
</template>
<template v-slot:footer>
<p>腳部</p>
</template>
<p>身體</p>
</child-component>
</div>
<!-- 渲染結果 -->
<div class="parent-page">
<div class="child-page">
<h1>子頁面</h1>
<p>頭部</p>
<p>身體</p>
<p>腳部</p>
</div>
</div>
復制代碼
- 作用域插槽:
子組件給父組件傳遞資料,
<!-- 子組件,組件名:child-component -->
<div class="child-page">
<h1>子頁面</h1>
<slot name="header" data="data from child-component."></slot>
</div>
<!-- 父組件 -->
<div class="parent-page">
<child-component>
<template v-slot:header="slotProps">
<p>頭部: {{ slotProps.data }}</p>
</template>
</child-component>
</div>
<!-- 渲染結果 -->
<div class="parent-page">
<div class="child-page">
<h1>子頁面</h1>
<p>頭部: data from child-component.</p>
</div>
</div>
復制代碼
Vue與Angular以及React的區別?
Vue與AngularJS的區別
- Angular采用TypeScript開發, 而Vue可以使用javascript也可以使用TypeScript
- AngularJS依賴對資料做臟檢查,所以
Watcher越多越慢;Vue.js使用基于依賴追蹤的觀察并且使用異步佇列更新,所有的資料都是獨立觸發的, - AngularJS社區完善, Vue的學習成本較小
Vue與React的區別
- vue組件分為全域注冊和區域注冊,在react中都是通過import相應組件,然后模版中參考;
props是可以動態變化的,子組件也實時更新,在react中官方建議props要像純函式那樣,輸入輸出一致對應,而且不太建議通過props來更改視圖;- 子組件一般要顯示地呼叫props選項來宣告它期待獲得的資料,而在react中不必需,另兩者都有props校驗機制;
- 每個Vue實體都實作了事件介面,方便父子組件通信,小型專案中不需要引入狀態管理機制,而react必需自己實作;
- vue使用
插槽分發內容,使得可以混合父組件的內容與子組件自己的模板; - vue多了
指令系統,讓模版可以實作更豐富的功能,而React只能使用JSX語法; - Vue增加的語法糖
computed和watch,而在React中需要自己寫一套邏輯來實作; - react的思路是
all in js,通過js來生成html,所以設計了jsx,還有通過js來操作css,社區的styled-component、jss等;而 vue是把html,css,js組合到一起,用各自的處理方式,vue有單檔案組件,可以把html、css、js寫到一個檔案中,html提供了模板引擎來處理, - react做的事情很少,很多都交給社區去做,vue很多東西都是內置的,寫起來確實方便一些,比如
redux的combineReducer就對應vuex的modules, 比如reselect就對應vuex的getter和vue組件的computed, vuex的mutation是直接改變的原始資料,而redux的reducer是回傳一個全新的state,所以redux結合immutable來優化性能,vue不需要, - react是整體的思路的就是函式式,所以推崇純組件,資料不可變,單向資料流,當然需要雙向的地方也可以做到,比如結合
redux-form,組件的橫向拆分一般是通過高階組件,而vue是資料可變的,雙向系結,宣告式的寫法,vue組件的橫向拆分很多情況下用mixin
$route和$router的區別
- $route是
路由資訊物件,包括path,params,hash,query,fullPath,matched,name等路由資訊引數, - 而$router是
路由實體物件包括了路由的跳轉方法,鉤子函式等,
Vue的SPA 如何優化加載速度
- 減少入口檔案體積
- 靜態資源本地快取
- 開啟Gzip壓縮
- 使用SSR,nuxt.js
vue專案中的優化
編碼階段
- 不要在模板里面寫過多運算式
- 盡量減少
data中的資料,data中的資料都會增加getter和setter,會收集對應的watcher v-if和v-for不能連用- 如果需要使用
v-for給每項元素系結事件時使用事件代理 SPA頁面采用keep-alive快取組件- 頻繁切換的使用v-show,不頻繁切換的使用v-if
- 回圈呼叫子組件時添加key,
key保證唯一 - 使用路由懶加載、異步組件
- 防抖、節流
- 第三方模塊按需匯入
- 長串列滾動到可視區域動態加載
- 圖片懶加載
SEO優化
- 預渲染
- 服務端渲染
SSR,nuxt.js
打包優化
- 壓縮代碼
Tree Shaking/Scope Hoisting- 使用
cdn加載第三方模塊 - 多執行緒打包
happypack splitChunks抽離公共檔案sourceMap優化
用戶體驗
- 骨架屏
PWA漸進式Web應用,使用多種技術來增強web app的功能,讓網頁應用呈現和原生應用相似的體驗,
還可以使用快取(客戶端快取、服務端快取)優化、服務端開啟
gzip壓縮等,
vue有了資料回應式,為何還要diff?
核心原因:粒度
React通過setState知道有變化了,但不知道哪里變化了,所以需要通過diff找出變化的地方并更新dom,Vue已經可以通過回應式系統知道哪里發生了變化,但是所有變化都通過回應式會創建大量Watcher,極其消耗性能,因此vue采用的方式是通過回應式系統知道哪個組件發生了變化,然后在組件內部使用diff,這樣的中粒度策略,即不會產生大量的Watcher,也使diff的節點減少了,一舉兩得,
vue模版編譯
編譯的核心是把 template 模板編譯成 render 函式,主要分為如下三個步驟:
- 生成AST樹
- 優化
- codegen
MVVM
MVVM是Model-View-ViewModel縮寫,也就是把MVC中的Controller演變成ViewModel,Model層代表資料模型,View代表UI組件,ViewModel是View和Model層的橋梁,資料會系結到viewModel層并自動將資料渲染到頁面中,視圖變化的時候會通知viewModel層更新資料,
回應式資料原理(Vue2.x & Vue3.0)
Vue2.x在初始化資料時,會使用Object.defineProperty重新定義data中的所有屬性,當頁面使用對應屬性時,首先會進行依賴收集(收集當前組件的watcher),如果屬性發生變化會通知相關依賴進行派發更細(發布訂閱模式),
vue3.0采用es6中的proxy代替Object.defineProperty做資料監聽,
Proxy與Object.defineProperty的優劣對比?
Proxy的優勢如下:
- Proxy可以直接監聽物件而非屬性
- Proxy可以直接監聽陣列的變化
- Proxy有多達13種攔截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具備的
- Proxy回傳的是一個新物件,我們可以只操作新的物件達到目的,而Object.defineProperty只能遍歷物件屬性直接修改
- Proxy作為新標準將受到瀏覽器廠商重點持續的性能優化,也就是傳說中的新標準的性能紅利
Object.defineProperty的優勢如下:
- 兼容性好,支持IE9
vue的$nextTick
在下次 DOM 更新回圈結束之后執行延遲回呼,nextTick主要使用了宏任務和微任務,根據執行環境分別嘗試采用
- Promise
- MutationObserver
- setImmediate
- 如果以上都不行則采用setTimeout
定義了一個異步方法,多次呼叫nextTick會將方法存入佇列中,通過這個異步方法清空當前佇列,
vue組件中data為什么必須是一個函式
復用組件的時候,都會回傳一份新的data,相當于每個組件實體都有自己私有的資料空間,不會共享同一個data物件,
vue中組件通信的方式
- 父傳子:
props - 子傳父:
$emit、ref - 兄弟:
EventBus
vue-router
vue-router是vue的官方插件,主要用來管理前端路由, 對于 Vue 這類漸進式前端開發框架,為了構建 SPA(單頁面應用),需要引入前端路由系統,這也就是 Vue-Router 存在的意義,前端路由的核心,就在于:改變視圖的同時不會向后端發出請求,
功能有:
- 改變URL且不讓瀏覽器向服務器發出請求
- 檢測URL的改變
- 記錄當前頁面的狀態
- 可以使用瀏覽器的前進后退功能
- URL路徑決定頁面如何顯示
history和hash模式的區別
1. 實作原理
hash 模式和 history 模式都屬于瀏覽器自身的特性,Vue-Router 只是利用了這兩個特性(通過呼叫瀏覽器提供的介面)來實作前端路由,
2. 對比表格
| 區別 \ mode | hash | history |
|---|---|---|
| 監聽事件 | hashChange | popstate |
| 缺點 | # 號不好看 | 子路由重繪404、ie9及以下不兼容 |
| push操作 | window.location.assign | window.history.pushState |
| replace操作 | window.location.replace | window.history.replaceState |
| 訪問操作 | window.history.go | window.history.go |
| 后退操作 | window.history.go(-1) | window.history.go(-1) |
| 向前操作 | window.history.go(1) | window.history.go(1) |
3. 關于 popstate 事件監聽路由的局限 history物件的 back(), forward() 和 go() 三個等操作會主動觸發 popstate 事件,但是 pushState 和 replaceState 不會觸發 popstate 事件,這時我們需要手動觸發頁面跳轉(渲染),
4. 關于子路由重繪的解決方式
history模式子路由重繪會404,因此需要后端配合,將未匹配到的路由默認指向html檔案
5. 瀏覽器(環境)兼容處理
history 模式中pushState、replaceState是HTML5的新特性,在 IE9 下會強行降級使用 hash 模式,非瀏覽器環境轉換成abstract 模式,
6. router-link router-link點擊相當于呼叫$router.push方法去修改url
<router-link> 比起寫死的 <a href="..."> 會好一些,理由如下:
- 無論是 HTML5 history 模式還是 hash 模式,它的表現行為一致,所以,當你要切換路由模式,或者在 IE9 降級使用 hash 模式,無須作任何變動,
- 在 HTML5 history 模式下,router-link 會守衛點擊事件,讓瀏覽器不再重新加載頁面,
- 當你在 HTML5 history 模式下使用 base 選項之后,所有的 to 屬性都不需要寫(基路徑)了,
vue-router路由懶加載
像 vue 這種單頁面應用,如果沒有路由懶加載,運用 webpack 打包后的檔案將會很大,造成進入首頁時,需要加載的內容過多,出現較長時間的白屏,運用路由懶加載則可以將頁面進行劃分,需要的時候才加載頁面,可以有效的分擔首頁所承擔的加載壓力,減少首頁加載用時,
vue 路由懶加載有以下三種方式:
- vue 異步組件
- ES6 的 import()
- webpack 的 require.ensure()
1. vue 異步組件 這種方法主要是使用了 resolve 的異步機制,用 require 代替了 import 實作按需加載
export default new Router({
routes: [
{
path: '/home',',
component: (resolve) => require(['@/components/home'], resolve),
},
{
path: '/about',',
component: (resolve) => require(['@/components/about'], resolve),
},
],
})
復制代碼
2. ES6 的 import() vue-router 在官網提供了一種方法,可以理解也是為通過 Promise 的 resolve 機制,因為 Promise 函式回傳的 Promise 為 resolve 組件本身,而我們又可以使用 import 來匯入組件,
export default new Router({
routes: [
{
path: '/home',
component: () => import('@/components/home'),
},
{
path: '/about',
component: () => import('@/components/home'),
},
],
})
復制代碼
1. webpack 的 require.ensure() 這種模式可以通過引數中的 webpackChunkName 將 js 分開打包,
export default new Router({
routes: [
{
path: '/home',
component: (resolve) => require.ensure([], () => resolve(require('@/components/home')), 'home'),
},
{
path: '/about',
component: (resolve) => require.ensure([], () => resolve(require('@/components/about')), 'about'),
},
],
})
復制代碼


篇幅有限,還有沒有發出來的題目都前端面試題資料里,需要完整PDF資料的小伙伴只需要點擊這里就可以免費獲取!
有被問到題目的小伙伴們評論區可以聊聊你是怎么回答的哦,沒有被問到的更要多看熟記,說不定就會碰到啦~這篇文章對你有幫助請評論點贊支持一波,謝謝!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/274423.html
標籤:其他
