(學習視訊分享:)
核心實現類:
Observer : 它的作用是給物件的屬性新增 getter 和 setter,用於依賴收集和派發更新
Dep : 用於收集當前響應式物件的依賴關係,每個響應式物件包括子物件都擁有一個 Dep 範例(裡面 subs 是 Watcher 範例陣列),當資料有變更時,會通過 dep.notify()通知各個 watcher。
Watcher : 觀察者物件 , 範例分為渲染 watcher (render watcher),計算屬性 watcher (computed watcher),偵聽器 watcher(user watcher)三種
Watcher 和 Dep 的關係:
watcher 中範例化了 dep 並向 dep.subs 中新增了訂閱者,dep 通過 notify 遍歷了 dep.subs 通知每個 watcher 更新。
依賴收集:
initState 時,對 computed 屬性初始化時,觸發 computed watcher 依賴收集
initState 時,對偵聽屬性初始化時,觸發 user watcher 依賴收集
render()的過程,觸發 render watcher 依賴收集
re-render 時,vm.render()再次執行,會移除所有 subs 中的 watcer 的訂閱,重新賦值。
派發更新:
元件中對響應的資料進行了修改,觸發 setter 的邏輯
呼叫 dep.notify()
遍歷所有的 subs(Watcher 範例),呼叫每一個 watcher 的 update 方法。
原理:
當建立 Vue 範例時,vue 會遍歷 data 選項的屬性,利用 Object.defineProperty 為屬性新增 getter 和 setter 對資料的讀取進行劫持(getter 用來依賴收集,setter 用來派發更新),並且在內部追蹤依賴,在屬性被存取和修改時通知變化。
每個元件範例會有相應的 watcher 範例,會在元件渲染的過程中記錄依賴的所有資料屬性(進行依賴收集,還有 computed watcher,user watcher 範例),之後依賴項被改動時,setter 方法會通知依賴與此 data 的 watcher 範例重新計算(派發更新),從而使它關聯的元件重新渲染。
一句話總結:
vue.js 採用資料劫持結合釋出-訂閱模式,通過 Object.defineproperty 來劫持各個屬性的 setter,getter,在資料變動時釋出訊息給訂閱者,觸發響應的監聽回撥
易用: 簡單,易學,上手快
靈活: (漸進式)不斷繁榮的生態系統,可以在一個庫和一套完整框架之間自如伸縮。
高效: 20kB min+gzip 執行大小;超快虛擬 DOM;最省心的優化
雙向繫結:開發效率高
基於元件的程式碼共用
Web專案工程化,增加可讀性、可維護性
Vue.js 2.0 採用資料劫持(Proxy 模式)結合釋出者-訂閱者模式(PubSub 模式)的方式,通過 Object.defineProperty()來劫持各個屬性的 setter,getter,在資料變動時釋出訊息給訂閱者,觸發相應的監聽回撥。
每個元件範例都有相應的watcher程式範例,它會在元件渲染的過程中把屬性記錄為依賴,之後當依賴項的setter被呼叫時,會通知watcher重新計算,從而致使它關聯的元件得以更新。
Vue.js 3.0, 放棄了Object.defineProperty ,使用更快的ES6原生 Proxy (存取物件攔截器, 也稱代理器)
步驟:
1.需要observe的資料物件進行遞迴遍歷,包括子屬性物件的屬性,都加上setter和getter這樣的話,給這個物件的某個值賦值,就會觸發setter,那麼就能監聽到了資料變化
2.compile解析模板指令,將模板中的變數替換成資料,然後初始化渲染頁面檢視,並將每個指令對應的節點繫結更新函數,新增監聽資料的訂閱者,一旦資料有變動,收到通知,更新檢視
3.Watcher訂閱者是Observer和Compile之間通訊的橋樑,主要做的事情是: ①在自身範例化時往屬性訂閱器(dep)裡面新增自己 ②自身必須有一個update()方法 ③待屬性變動dep.notice()通知時,能呼叫自身的update()方法,並觸發Compile中繫結的回撥,則功成身退。
4.MVVM作為資料繫結的入口,整合Observer、Compile和Watcher三者,通過Observer來監聽自己的model資料變化,通過Compile來解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通訊橋樑,達到資料變化 -> 檢視更新;檢視互動變化(input) -> 資料model變更的雙向繫結效果。
比如現在需要監控data中, obj.a 的變化。Vue中監控物件屬性的變化你可以這樣:
watch: { obj: { handler (newValue, oldValue) { console.log('obj changed') }, deep: true }
deep屬性表示深層遍歷,但是這麼寫會監控obj的所有屬性變化,並不是我們想要的效果,所以做點修改:
watch: { 'obj.a': { handler (newName, oldName) { console.log('obj.a changed') } } }
還有一種方法,可以通過computed 來實現,只需要:
computed: { a1 () { return this.obj.a } }
利用計算屬性的特性來實現,當依賴改變時,便會重新計算一個新值。
Object.defineProperty缺陷
1.監控到陣列下標的變化時,開銷很大。所以Vue.js放棄了下標變化的檢測;
2.Object.defineProperty只能劫持物件的屬性,而Proxy是直接代理物件。3.Object.defineProperty需要遍歷物件的每個屬性,如果屬性值也是物件,則需要深度遍歷。而 Proxy 直接代理物件,不需要遍歷操作。
4.Object.defineProperty對新增屬性需要手動進行Observe。vue2時需要使用 vm.$set 才能保證新增的屬性也是響應式
5.Proxy支援13種攔截操作,這是defineProperty所不具有的
6.Proxy 作為新標準,長遠來看,JS引擎會繼續優化 Proxy,但 getter 和 setter 基本不會再有針對性優化
檢視並未重新整理。這是因為在Vue範例建立時,新屬性並未宣告,因此就沒有被Vue轉換為響應式的屬性,自然就不會觸發檢視的更新,這時就需要使用Vue的全域性 api $set():
this.$set(this.obj, 'new_property', 'new_value')
computed 計算屬性 : 依賴其它屬性值,並且 computed 的值有快取,只有它依賴的 屬性值發生改變,下一次獲取 computed 的值時才會重新計算 computed 的值。
watch 偵聽器 : 更多的是觀察的作用,無快取性,類似於某些資料的監聽回撥,每 當監聽的資料變化時都會執行回撥進行後續操作。
運用場景:
1.當我們需要進行數值計算,並且依賴於其它資料時,應該使用 computed,因為可以利用 computed 的快取特性,避免每次獲取值時,都要重新計算。
2.當我們需要在資料變化時執行非同步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許我們執行非同步操作 ( 存取一個 API ),限制我們執行該操作的頻率, 並在我們得到最終結果前,設定中間狀態。這些都是計算屬性無法做到的。
3.多個因素影響一個顯示,用Computed;一個因素的變化影響多個其他因素、顯示,用Watch;
1.computed: 計算屬性是基於它們的依賴進行快取的,只有在它的相關依賴發生改變時才會重新求值對於 method ,只要發生重新渲染,
2.method 呼叫總會執行該函數
1.讓我們不用直接操作DOM元素,只運算元據便可以重新渲染頁面
2.虛擬dom是為了解決瀏覽器效能問題而被設計出來的
當運算元據時,將改變的dom元素快取起來,都計算完後再通過比較對映到真實的dom樹上
3.diff演演算法比較新舊虛擬dom。如果節點型別相同,則比較資料,修改資料;如果節點不同,直接幹掉節點及所有子節點,插入新的節點;如果給每個節點都設定了唯一的key,就可以準確的找到需要改變的內容,否則就會出現修改一個地方導致其他地方都改變的情況。比如A-B-C-D, 我要插入新節點A-B-M-C-D,實際上改變的了C和D。但是設定了key,就可以準確的找到B C並插入
1.具備跨平臺的優勢
2.操作 DOM 慢,js執行效率高。我們可以將DOM對比操作放在JS層,提高效率。
3.提升渲染效能
在Vue中使用filters來過濾(格式化)資料,filters不會修改資料,而是過濾(格式化)資料,改變使用者看到的輸出(計算屬性 computed ,方法 methods 都是通過修改資料來處理資料格式的輸出顯示。
使用場景: 比如需要處理時間、數位等的的顯示格式;
1)
.stop
:等同於 JavaScript 中的 event.stopPropagation() ,防止事件冒泡;
2).prevent
:等同於 JavaScript 中的 event.preventDefault() ,防止執行預設的行為(如果事件可取消,則取消該事件,而不停止事件的進一步傳播);
3).capture
:當元素髮生冒泡時,先觸發帶有該修飾符的元素。若有多個該修飾符,則由外而內觸發。如 p1中巢狀p2中巢狀p3.capture中巢狀p4,那麼執行順序為:p3=》p4=》p2=》p1
4).self
:只會觸發自己範圍內的事件,不包含子元素;
5).once
:只會觸發一次。
v-show 僅僅控制元素的顯示方式,將 display 屬性在 block 和 none 來回切換;而v-if會控制這個 DOM 節點的存在與否。當我們需要經常切換某個元素的顯示/隱藏時,使用v-show會更加節省效能上的開銷;當只需要一次顯示或隱藏時,使用v-if更加合理。
作用在表單元素上v-model="message"等同於v-bind:value=「message」 v-on:input="message=e v e n t . t a r g e t . v a l u e " 作 用 在 組 件 上 , 本 質 是 一 個 父 子 組 件 通 信 的 語 法 糖 ,通過prop和.emit實現, 等同於:value="message" @input=" $emit('input', $event.target.value)"
JavaScript中的物件是參照型別的資料,當多個範例參照同一個物件時,只要一個範例對這個物件進行操作,其他範例中的資料也會發生變化。
而在Vue中,我們更多的是想要複用元件,那就需要每個元件都有自己的資料,這樣元件之間才不會相互干擾。
所以元件的資料不能寫成物件的形式,而是要寫成函數的形式。資料以函數返回值的形式定義,這樣當我們每次複用元件的時候,就會返回一個新的data,也就是說每個元件都有自己的私有資料空間,它們各自維護自己的資料,不會干擾其他元件的正常執行。
1.呼叫parse方法將template轉化為ast(抽象語法樹, abstract syntax tree)
2.對靜態節點做優化。如果為靜態節點,他們生成的DOM永遠不會改變,這對執行時模板更新起到了極大的優化作用。
3.生成渲染函數. 渲染的返回值是VNode,VNode是Vue的虛擬DOM節點,裡面有(標籤名,子節點,文字等等)
呼叫parse方法將template轉化為ast(抽象語法樹, abstract syntax tree)
對靜態節點做優化。如果為靜態節點,他們生成的DOM永遠不會改變,這對執行時模板更新起到了極大的優化作用。
生成渲染函數. 渲染的返回值是VNode,VNode是Vue的虛擬DOM節點,裡面有(標籤名,子節點,文字等等)
易用、簡潔且高效的http庫, 支援node端和瀏覽器端,支援Promise,支援攔截器等高階設定。
sass是一種CSS預編譯語言安裝和使用步驟如下。
1.用npm安裝載入程式( sass-loader、 css-loader等載入程式)。
2.在 webpack.config.js中設定sass載入程式。
Vue. js提供了一個v-cloak
指令,該指令一直保持在元素上,直到關聯範例結束編譯。當和CSS一起使用時,這個指令可以隱藏未編譯的標籤,直到範例編譯結束。用法如下
在開發業務時,經常會岀現非同步獲取資料的情況,有時資料層次比較深,如以下程式碼: <span 'v-text=「a.b.c.d」>, 可以使用vm.$set手動定義一層資料:
vm.$set("demo",a.b.c.d)
config/ index.js內對 proxyTable項設定代理。
Vue 在修改資料後,檢視不會立刻更新,而是等同一事件迴圈中的所有資料變化完成之後,再統一進行檢視更新。
換句話說,只要觀察到資料變化,就會自動開啟一個佇列,並緩衝在同一個事件迴圈中發生的所以資料改變。在緩衝時會去除重複資料,從而避免不必要的計算和 DOM 操作。
1.vue 用非同步佇列的方式來控制 DOM 更新和 nextTick 回撥先後執行
2.microtask 因為其高優先順序特性,能確保佇列中的微任務在一次事件迴圈前被執行完畢
因為元件是可以複用的,JS 裡物件是參照關係,如果元件 data 是一個物件,那麼子元件中的 data 屬性值會互相汙染。
所以一個元件的 data 選項必須是一個函數,因此每個範例可以維護一份被返回物件的獨立的拷貝。
由於v-for的優先順序比v-if高,所以導致每回圈一次就會去v-if一次,而v-if是通過建立和銷燬dom元素來控制元素的顯示與隱藏,所以就會不停的去建立和銷燬元素,造成頁面卡頓,效能下降。
解決辦法:
1.在v-for的外層或內層包裹一個元素來使用v-if
2.用computed處理
1.v-model 多用於表單元素實現雙向資料繫結(同angular中的ng-model)
2.v-bind 動態繫結 作用: 及時對頁面的資料進行更改
3.v-on:click 給標籤繫結函數,可以縮寫為@,例如繫結一個點選函數 函數必須寫在methods裡面
4.v-for 格式: v-for=「欄位名 in(of) 陣列json」 迴圈陣列或json(同angular中的ng-repeat)
5.v-show 顯示內容 (同angular中的ng-show)
6.v-hide 隱藏內容(同angular中的ng-hide)
7.v-if 顯示與隱藏 (dom元素的刪除新增 同angular中的ng-if 預設值為false)
8.v-else-if 必須和v-if連用
9.v-else 必須和v-if連用 不能單獨使用 否則報錯 模板編譯錯誤
10.v-text 解析文字
11.v-html 解析html標籤
12.v-bind:class 三種系結方法 1、物件型 ‘{red:isred}’ 2、三元型 ‘isred?「red」:「blue」’ 3、陣列型 ‘[{red:「isred」},{blue:「isblue」}]’
13.v-once 進入頁面時 只渲染一次 不在進行渲染
14.v-cloak 防止閃爍
15.v-pre 把標籤內部的元素原位輸出
1.父傳子:子元件通過props[‘xx’] 來接收父元件傳遞的屬性 xx 的值
2.子傳父:子元件通過 this.$emit(‘fnName’,value) 來傳遞,父元件通過接收 fnName 事件方法來接收回撥
3.其他方式:通過建立一個bus,進行傳值
4.使用Vuex
當 Vue.js 用 v-for 正在更新已渲染過的元素列表時,它預設用「就地複用」策略。如果資料項的順序被改變,Vue 將不會移動 DOM 元素來匹配資料項的順序, 而是簡單複用此處每個元素,並且確保它在特定索引下顯示已被渲染過的每個元素。key的作用主要是為了高效的更新虛擬DOM
Object.defineProperty
本身有一定的監控到陣列下標變化的能力,但是在 Vue 中,從效能/體驗的價效比考慮,尤大大就棄用了這個特性(Vue 為什麼不能檢測陣列變動 )。為了解決這個問題,經過 vue 內部處理後可以使用以下幾種方法來監聽陣列
push();
pop();
shift();
unshift();
splice();
sort();
reverse();
由於只針對了以上 7 種方法進行了 hack 處理,所以其他陣列的屬性也是檢測不到的,還是具有一定的侷限性。
Object.defineProperty 只能劫持物件的屬性,因此我們需要對每個物件的每個屬性進行遍歷。Vue 2.x 裡,是通過 遞迴 + 遍歷 data 物件來實現對資料的監控的,如果屬性值也是物件那麼需要深度遍歷,顯然如果能劫持一個完整的物件是才是更好的選擇。
Proxy 可以劫持整個物件,並返回一個新的物件。Proxy 不僅可以代理物件,還可以代理陣列。還可以代理動態增加的屬性。
JS 執行機制
JS 執行是單執行緒的,它是基於事件迴圈的。事件迴圈大致分為以下幾個步驟:
所有同步任務都在主執行緒上執行,形成一個執行棧(execution context stack)。
主執行緒之外,還存在一個"任務佇列"(task queue)。只要非同步任務有了執行結果,就在"任務佇列"之中放置一個事件。
一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務佇列",看看裡面有哪些事件。那些對應的非同步任務,於是結束等待狀態,進入執行棧,開始執行。
主執行緒不斷重複上面的第三步。
主執行緒的執行過程就是一個 tick,而所有的非同步結果都是通過 「任務佇列」 來排程。 訊息佇列中存放的是一個個的任務(task)。 規範中規定 task 分為兩大類,分別是 macro task 和 micro task,並且每個 macro task 結束後,都要清空所有的 micro task。
for (macroTask of macroTaskQueue) { // 1. Handle current MACRO-TASK handleMacroTask(); // 2. Handle all MICRO-TASK for (microTask of microTaskQueue) { handleMicroTask(microTask); }}
在瀏覽器環境中 :
常見的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate
常見的 micro task 有 MutationObsever 和 Promise.then
非同步更新佇列
可能你還沒有注意到,Vue 在更新 DOM 時是非同步執行的。只要偵聽到資料變化,Vue 將開啟一個佇列,並緩衝在同一事件迴圈中發生的所有資料變更。
如果同一個 watcher 被多次觸發,只會被推入到佇列中一次。這種在緩衝時去除重複資料對於避免不必要的計算和 DOM 操作是非常重要的。
然後,在下一個的事件迴圈「tick」中,Vue 重新整理佇列並執行實際 (已去重的) 工作。
Vue 在內部對非同步佇列嘗試使用原生的 Promise.then、MutationObserver 和 setImmediate,如果執行環境不支援,則會採用 setTimeout(fn, 0) 代替。
在 vue2.5 的原始碼中,macrotask 降級的方案依次是:setImmediate、MessageChannel、setTimeout
vue 的 nextTick 方法的實現原理:
vue 用非同步佇列的方式來控制 DOM 更新和 nextTick 回撥先後執行
microtask 因為其高優先順序特性,能確保佇列中的微任務在一次事件迴圈前被執行完畢
考慮相容問題,vue 做了 microtask 向 macrotask 的降級方案
Vue 事件機制 本質上就是 一個 釋出-訂閱 模式的實現。
class Vue { constructor() { // 事件通道排程中心 this._events = Object.create(null); } $on(event, fn) { if (Array.isArray(event)) { event.map(item => { this.$on(item, fn); }); } else { (this._events[event] || (this._events[event] = [])).push(fn); } return this; } $once(event, fn) { function on() { this.$off(event, on); fn.apply(this, arguments); } on.fn = fn; this.$on(event, on); return this; } $off(event, fn) { if (!arguments.length) { this._events = Object.create(null); return this; } if (Array.isArray(event)) { event.map(item => { this.$off(item, fn); }); return this; } const cbs = this._events[event]; if (!cbs) { return this; } if (!fn) { this._events[event] = null; return this; } let cb; let i = cbs.length; while (i--) { cb = cbs[i]; if (cb === fn || cb.fn === fn) { cbs.splice(i, 1); break; } } return this; } $emit(event) { let cbs = this._events[event]; if (cbs) { const args = [].slice.call(arguments, 1); cbs.map(item => { args ? item.apply(this, args) : item.call(this); }); } return this; }}
三種:
一種是全域性導航勾點:router.beforeEach(to,from,next),作用:跳轉前進行判斷攔截。第二種:元件內的勾點;第三種:單獨路由獨享元件
vue框架中狀態管理。在main.js引入store,注入。新建了一個目錄store,…… export 。
場景有:單頁應用中,元件之間的狀態。音樂播放、登入狀態、加入購物車
MVVM和MVC都是一種設計思想,主要就是MVC中的Controller演變成ViewModel,,MVVM主要通過資料來顯示檢視層而不是操作節點,解決了MVC中大量的DOM操作使頁面渲染效能降低,載入速度慢,影響使用者體驗問題。主要用於資料操作比較多的場景。
場景:資料操作比較多的場景,更加便捷
簡而言之,就是先轉化成AST樹,再得到的渲染函數返回VNODE(Vue公司的虛擬DOM節點)
詳情步驟:
首先,通過編譯編譯器把模板編譯成AST語法樹(抽象語法樹即原始碼的抽象語法結構的樹狀表現形式),編譯是createCompiler的返回值,createCompiler是用以建立編譯器的。負責合併選項。
然後,AST會經過生成(將AST語法樹轉化成渲染功能字串的過程)得到渲染函數,渲染的返回值是VNode,VNode是Vue的虛擬DOM節點,裡面有(標籤名,子節點,文字等等)
答:包裹動態元件時,會快取不活動的元件範例,主要用於保留元件狀態或避免重新渲染;
使用:簡單頁面時
快取: < keep-alive include=」元件名」>< /keep-alive>
不快取: < keep-alive exclude=」元件名」>< /keep-alive>
相同點:都鼓勵元件化,都有’props’的概念,都有自己的構建工具,Reat與Vue只有框架的骨架,其他的功能如路由、狀態管理等是框架分離的元件。
不同點:React:資料流單向,語法—JSX,在React中你需要使用setState()方法去更新狀態。Vue:資料雙向繫結,語法–HTML,state物件並不是必須的,資料由data屬性在Vue物件中進行管理。適用於小型應用,但對於對於大型應用而言不太適合。
參照大神文章:vue筆記 - 生命週期第二次學習與理解
beforeCreate
是new Vue()之後觸發的第一個勾點,在當前階段data、methods、computed以及watch上的資料和方法都不能被存取。
created
在範例建立完成後發生,當前階段已經完成了資料觀測,也就是可以使用資料,更改資料,在這裡更改資料不會觸發updated函數。可以做一些初始資料的獲取,在當前階段無法與Dom進行互動,如果非要想,可以通過vm.$nextTick來存取Dom。
beforeMount
發生在掛載之前,在這之前template模板已匯入渲染函數編譯。而當前階段虛擬Dom已經建立完成,即將開始渲染。在此時也可以對資料進行更改,不會觸發updated。
mounted
在掛載完成後發生,在當前階段,真實的Dom掛載完畢,資料完成雙向繫結,可以存取到Dom節點,使用$refs屬性對Dom進行操作。
beforeUpdate發生在更新之前,也就是響應式資料發生更新,虛擬dom重新渲染之前被觸發,你可以在當前階段進行更改資料,不會造成重渲染。
updated
發生在更新完成之後,當前階段元件Dom已完成更新。要注意的是避免在此期間更改資料,因為這可能會導致無限迴圈的更新。
beforeDestroy
發生在範例銷燬之前,在當前階段範例完全可以被使用,我們可以在這時進行善後收尾工作,比如清除計時器。
destroyed
發生在範例銷燬之後,這個時候只剩下了dom空殼。元件已被拆解,資料繫結被卸除,監聽被移出,子範例也統統被銷燬。
簡單來說,diff演演算法有以下過程
1.同級比較,再比較子節點
2.先判斷一方有子節點一方沒有子節點的情況(如果新的children沒有子節點,將舊的子節點移除)
3.比較都有子節點的情況(核心diff)
3.遞迴比較子節點
正常Diff兩個樹的時間複雜度是O(n^3)
,但實際情況下我們很少會進行跨層級的移動DOM,所以Vue將Diff進行了優化,從O(n^3) -> O(n)
,只有當新舊children都為多個子節點時才需要用核心的Diff演演算法進行同層級比較。
Vue2的核心Diff演演算法採用了雙端比較的演演算法,同時從新舊children的兩端開始進行比較,藉助key值找到可複用的節點,再進行相關操作。相比React的Diff演演算法,同樣情況下可以減少移動節點次數,減少不必要的效能損耗,更加的優雅。
Vue3.x借鑑了
ivi演演算法和 inferno演演算法
在建立VNode時就確定其型別,以及在 mount/patch 的過程中採用位運算來判斷一個VNode的型別,在這個基礎之上再配合核心的Diff演演算法,使得效能上較Vue2.x有了提升。(實際的實現可以結合Vue3.x原始碼看。)
該演演算法中還運用了動態規劃的思想求解最長遞迴子序列。
編碼階段
1.儘量減少data中的資料,data中的資料都會增加getter和setter,會收集對應的2.watcher
3.v-if和v-for不能連用
4.如果需要使用v-for給每項元素繫結事件時使用事件代理
5.SPA 頁面採用keep-alive快取元件
6.在更多的情況下,使用v-if替代v-show
7.key保證唯一
8.使用路由懶載入、非同步元件
9.防抖、節流
10.第三方模組按需匯入
11.長列表捲動到可視區域動態載入
12.圖片懶載入
SEO優化
1.預渲染
2.伺服器端渲染SSR
打包優化
1.壓縮程式碼
2.Tree Shaking/Scope Hoisting
3.使用cdn載入第三方模組
4.多執行緒打包happypack
5.splitChunks抽離公共檔案
6.sourceMap優化
使用者體驗
1.骨架屏
2.PWA
還可以使用快取(使用者端快取、伺服器端快取)優化、伺服器端開啟gzip壓縮等。
location.hash
的值實際就是URL中#後面的東西。
history
實際採用了HTML5中提供的API來實現,主要有history.pushState()
和history.replaceState()
。
SPA( single-page application )僅在 Web 頁面初始化時載入相應的 HTML、JavaScript 和 CSS
一旦頁面載入完成,SPA 不會因為使用者的操作而進行頁面的重新載入或跳轉
取而代之的是利用路由機制實現 HTML 內容的變換, UI 與使用者的互動,避免頁面的重新載入
優點:
1、使用者體驗好、快,內容的改變不需要重新載入整個頁面,避免了不必要的跳轉和重複渲染
2、基於上面一點,SPA 相對對伺服器壓力小
3、前後端職責分離,架構清晰,前端進行互動邏輯,後端負責資料處理
缺點:
1、初次載入耗時多:為實現單頁 Web 應用功能及顯示效果,
需要在載入頁面的時候將 JavaScript、CSS 統一載入,部分頁面按需載入
2、前進後退路由管理:由於單頁應用在一個頁面中顯示所有的內容,
所以不能使用瀏覽器的前進後退功能,所有的頁面切換需要自己建立堆疊管理
3、SEO 難度較大:由於所有的內容都在一個頁面中動態替換顯示,所以在 SEO 上其有著天然的弱勢
第一步:在components目錄新建你的元件檔案(indexPage.vue),script一定要
export default {}
第二步:在需要用的頁面(元件)中匯入:import indexPage from ‘@/components/indexPage.vue’
第三步:注入到vue的子元件的components屬性上面,components:{indexPage}
第四步:在template檢視view中使用,
例如有indexPage命名,使用的時候則index-page
webpack中提供了
require.ensure()
來實現按需載入。以前引入路由是通過import 這樣的方式引入,改為const定義的方式進行引入。
不進行頁面按需載入引入方式:import home from '../../common/home.vue'
進行頁面按需載入的引入方式:const home = r => require.ensure( [], () => r (require('../../common/home.vue')))
以元件功能命名
只負責ui的展示和互動動畫,不要在元件裡與伺服器打交道(獲取非同步資料等)
可複用元件不會因元件使用的位置、場景而變化。儘量減少對外部條件的依賴。
在每一個Vue.js元件中都可以定義各自的CSS、 JavaScript程式碼。如果希望元件內寫的CSS只對當前元件起作用,只需要在Style標籤新增Scoped屬性即可
如果需要在元件切換的時候,儲存一些元件的狀態防止多次渲染,就可以使用 keep-alive 元件包裹需要儲存的元件。
兩個重要屬性,include 快取元件名稱,exclude 不需要快取的元件名稱。
對「src」屬性插值將導致404請求錯誤。應使用 v-bind:src (簡寫:src)格式代替。
載入渲染過程:
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子元件更新過程:父beforeUpdate->子beforeUpdate->子updated->父updated
父元件更新過程:父beforeUpdate->父updated
銷燬過程:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
1.state => 基本資料
2.getters => 從基本資料派生的資料
3.mutations => 修改資料,同步
4.actions => 修改資料,非同步 (Action 提交的是 mutation,而不是直接變更狀態)
5.modules => 模組化Vuex
Vuex 是一個專為 Vue.js 應用程式開發的狀態管理器,採用集中式儲存管理應用的所有元件的狀態,主要是為了多頁面、多元件之間的通訊。
Vuex有5個重要的屬性,分別是 State、Getter、Mutation、Action、Module,由 view 層發起一個 Action 給 Mutation,在 Mutation 中修改狀態,返回新的狀態,通過 Getter暴露給 view層的元件或者頁面,頁面監測到狀態改變於是更新頁面。如果你的專案很簡單,最好不要使用 Vuex,對於大型專案,Vuex 能夠更好的幫助我們管理元件外部的狀態,一般可以運用在購物車、登入狀態、播放等場景中。
1.公共的資料部分可以提升至和他們最近的父元件,由父元件派發
2.公共資料可以放到vuex中統一管理,各元件分別獲取
1.如果請求來的資料是不是要被其他元件公用,僅僅在請求的元件內使用,就不需要放入vuex 的state裡。
2.如果被其他地方複用,這個很大機率上是需要的,如果需要,請將請求放入action裡,方便複用,幷包裝成promise返回,在呼叫處用async await處理返回的資料。如果不要複用這個請求,那麼直接寫在vue檔案裡很方便
actions與mutations作用類似,都是可以對狀態進行修改。不同的是actions是非同步操作的。
actions是可以呼叫Mutations裡的方法的。
const actions={ addActions(context){ context.commit('add',10);//呼叫mutations中的方法 setTimeout(()=>{context.commit('reduce')},5000) // setTimeOut(()=>{context.commit('reduce')},3000); console.log('我比reduce提前執行'); }, reduceActions({commit}){ commit('reduce'); }}
Mutation 更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似於事件:每個 mutation 都有一個字串的 事件型別 (type) 和 一個 回撥函數 (handler)。這個回撥函數就是我們實際進行狀態更改的地方,並且它會接受 state 作為第一個引數
const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 變更狀態 state.count++ } }})
Action Action 類似於 mutation,不同在於:
Action 提交的是 mutation,而不是直接變更狀態。
Action 可以包含任意非同步操作
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } }})
使用mapState
輔助函數, 利用物件展開運運算元將state混入computed物件中
import {mapState} from 'vuex' export default{ computed:{ ...mapState(['price','number']) } }
物件是參照型別,複製後改變屬性還是會影響原始資料,這樣會改變state裡面的狀態,是不允許,所以先用深度克隆複製物件,再修改。
1.Hash: 使用 URL 的 hash 值來作為路由。支援所有瀏覽器。 帶#。如:http://localhost:8080/#/pageA。改變hash,瀏覽器本身不會有任何請求伺服器動作的,但是頁面狀態和url已經關聯起來了。
2.History: 以來 HTML5 History API 和伺服器設定。參考官網中 HTML5 History 模式,不帶#, 如:http://localhost:8080/ 正常的而路徑,並沒有#。基於HTML5的 pushState、replaceState實現
3.Abstract: 支援所有 javascript 執行模式。如果發現沒有瀏覽器的 API,路由會自動強制進入這個模式。
通過children 陣列:
const router = new VueRouter({ routes: [ { path: "/parentPage", component: testPage, children: [ { path: "/childrenA", component: childrenComponentA, }, { path: "/childrenB", component: childrenComponentB, }, ], }, { // 其他和parentPage平級的路由 }, ],});
1.全域性導航勾點:
router.beforeEach(to,from,next)
2.元件內的勾點beforeRouteEnter (to, from, next) beforeRouteUpdate (to, from, next) beforeRouteLeave (to, from, next)
3.單獨路由獨享元件beforeEnter: (to, from, next)
引數:有to(去的那個路由)、from(離開的路由)、next(一定要用這個函數才能去到下一個路由,如果不用就攔截)最常用就這幾種
1、$route
是「路由資訊物件」,包括path,params,hash,query,fullPath,matched,name
等路由資訊引數。
1.
$route.path
字串,對應當前路由的路徑,總是解析為絕對路徑如"/foo/bar"。
2.$route.params
一個 key/value 物件,包含了 動態片段 和 全匹配片段, 如果沒有路由引數,就是一個空物件。
3.$route.query
一個 key/value 物件,表示 URL 查詢引數。 例如,對於路徑/foo?user=1
,則有$route.query.user == 1
, 如果沒有查詢引數,則是個空物件
4.$route.hash
當前路由的hash值 (不帶#) ,如果沒有 hash 值,則為空字串
5.$route.fullPath
完成解析後的 URL,包含查詢引數和hash的完整路徑。
6.$route.matched
陣列,包含當前匹配的路徑中所包含的所有片段所對應的設定引數物件。
7.$route.name
當前路徑名字
8.$ route.meta
路由元資訊
2、$router
是「路由範例」物件包括了路由的跳轉方法,勾點函數等
實體方法:
1)、push
1.字串
this.$router.push('home')
2. 物件this.$router.push({path:'home'})
3. 命名的路由this.$router.push({name:'user',params:{userId:123}})
4.帶查詢引數,變成/register?plan=123this.$router.push({path:'register',query:{plan:'123'}})
push方法其實和< router-link :to=「…」>是等同的。
注意:push方法的跳轉會向 history 棧新增一個新的記錄,當我們點選瀏覽器的返回按鈕時可以看到之前的頁面。
2)、go
頁面路由跳轉
前進或者後退this.$router.go(-1)
// 後退
3)、replace
push方法會向 history 棧新增一個新的記錄,而replace方法是替換當前的頁面,
不會向 history 棧新增一個新的記錄
1.宣告式(標籤跳轉)
2.程式設計式( js跳轉)
vue-router 模組 的router-link元件
把不同路由對應的元件分割成不同的程式碼塊,然後當路由被存取時才載入對應的元件即為路由的懶載入,可以加快專案的載入速度,提高效率
const router = new VueRouter({ routes: [ { path: '/home', name: 'Home', component:() = import('../views/home') } ]})
在router目錄下的index.js檔案中,對path屬性加上/:id
使用router物件的params id
面試官:我難道問不倒這小子了?(面試官持續懵逼中) 對大家有幫助的話三連呀~ 持續更新
【相關視訊教學推薦:、】
以上就是【吐血整理】Vue.js面試題彙總及答案解析(快來收藏)的詳細內容,更多請關注TW511.COM其它相關文章!