使用key來給每一個節點作一個惟一標識
key的做用主要是爲了高效的更新虛擬DOM。另外vue中在使用相同標籤名元素的過渡切換時,也會使用到key屬性,其目的也是爲了讓vue能夠區分它們,
不然vue只會替換其內部屬性而不會觸發過渡效果。html
在 new Vue() 中,data 是能夠做爲一個對象進行操做的,然而在 component 中,data 只能以函數的形式存在,不能直接將對象賦值給它。
當data選項是一個函數的時候,每一個實例能夠維護一份被返回對象的獨立的拷貝,這樣各個實例中的data不會相互影響,是獨立的。前端
1、Vuex就是一個倉庫,倉庫裏面放了不少對象。其中state就是數據源存放地,對應於與通常Vue對象裏面的data
2、state裏面存放的數據是響應式的,Vue組件從store中讀取數據,如果store中的數據發生改變,依賴這個數據的組件也會發生更新
3、它經過mapState把全局的 state 和 getters 映射到當前組件的 computed 計算屬性中vue
Vue爲MVVM框架,當數據模型data變化時,頁面視圖會獲得響應更新,其原理對data的getter/setter方法進行攔截(Object.defineProperty或者Proxy),利用發佈訂閱的設計模式,在getter方法中進行訂閱,在setter方法中發佈通知,讓全部訂閱者完成響應。node
在響應式系統中,Vue會爲數據模型data的每個屬性新建一個訂閱中心做爲發佈者,而監聽器watch、計算屬性computed、視圖渲染template/render三個角色同時做爲訂閱者,對於監聽器watch,會直接訂閱觀察監聽的屬性,對於計算屬性computed和視圖渲染template/render,若是內部執行獲取了data的某個屬性,就會執行該屬性的getter方法,而後自動完成對該屬性的訂閱,當屬性被修改時,就會執行該屬性的setter方法,從而完成該屬性的發佈通知,通知全部訂閱者進行更新。react
計算屬性computed和監聽器watch均可以觀察屬性的變化從而作出響應,不一樣的是:webpack
計算屬性computed更可能是做爲緩存功能的觀察者,它能夠將一個或者多個data的屬性進行復雜的計算生成一個新的值,提供給渲染函數使用,當依賴的屬性變化時,computed不會當即從新計算生成新的值,而是先標記爲髒數據,當下次computed被獲取時候,纔會進行從新計算並返回。ios
而監聽器watch並不具有緩存性,監聽器watch提供一個監聽函數,當監聽的屬性發生變化時,會當即執行該函數。web
beforeCreate:是new Vue()以後觸發的第一個鉤子,在當前階段data、methods、computed以及watch上的數據和方法都不能被訪問。面試
created:在實例建立完成後發生,當前階段已經完成了數據觀測,也就是可使用數據,更改數據,在這裏更改數據不會觸發updated函數。能夠作一些初始數據的獲取,在當前階段沒法與Dom進行交互,若是非要想,能夠經過vm.$nextTick來訪問Dom。ajax
beforeMount:發生在掛載以前,在這以前template模板已導入渲染函數編譯。而當前階段虛擬Dom已經建立完成,即將開始渲染。在此時也能夠對數據進行更改,不會觸發updated。
mounted:在掛載完成後發生,在當前階段,真實的Dom掛載完畢,數據完成雙向綁定,能夠訪問到Dom節點,使用$refs屬性對Dom進行操做。
beforeUpdate:發生在更新以前,也就是響應式數據發生更新,虛擬dom從新渲染以前被觸發,你能夠在當前階段進行更改數據,不會形成重渲染。
updated:發生在更新完成以後,當前階段組件Dom已完成更新。要注意的是避免在此期間更改數據,由於這可能會致使無限循環的更新。
beforeDestroy:發生在實例銷燬以前,在當前階段實例徹底能夠被使用,咱們能夠在這時進行善後收尾工做,好比清除計時器。
destroyed:發生在實例銷燬以後,這個時候只剩下了dom空殼。組件已被拆解,數據綁定被卸除,監聽被移出,子實例也通通被銷燬。
一個組件可能在不少地方使用,也就是會建立不少個實例,若是data是一個對象的話,對象是引用類型,一個實例修改了data會影響到其餘實例,因此data必須使用函數,爲每個實例建立一個屬於本身的data,使其同一個組件的不一樣實例互不影響。
父子組件通訊
父組件 -> 子組件:prop
子組件 -> 父組件:$on/$emit
獲取組件實例:使用$parent/$children,$refs.xxx,獲取到實例後直接獲取屬性數據或調用組件方法
兄弟組件通訊
Event Bus:每個Vue實例都是一個Event Bus,都支持$on/$emit,能夠爲兄弟組件的實例之間new一個Vue實例,做爲Event Bus進行通訊。
Vuex:將狀態和方法提取到Vuex,完成共享
跨級組件通訊
使用provide/inject
Event Bus:同兄弟組件Event Bus通訊
Vuex:將狀態和方法提取到Vuex,完成共享
每個Vue實例都是一個Event Bus,當子組件被建立的時候,父組件將事件傳遞給子組件,子組件初始化的時候是有$on方法將事件註冊到內部,在須要的時候使用$emit觸發函數,而對於原生native事件,使用addEventListener綁定到真實的DOM元素上。
低耦合。視圖(View)能夠獨立於Model變化和修改,一個ViewModel能夠綁定到不一樣的"View"上,當View變化的時候Model能夠不變,當Model變化的時候View也能夠不變。
可重用性。你能夠把一些視圖邏輯放在一個ViewModel裏面,讓不少view重用這段視圖邏輯。
獨立開發。開發人員能夠專一於業務邏輯和數據的開發(ViewModel),設計人員能夠專一於頁面設計。
可測試。界面素來是比較難於測試的,而如今測試能夠針對ViewModel來寫
1.與AngularJS的區別
相同點:
都支持指令:內置指令和自定義指令。
都支持過濾器:內置過濾器和自定義過濾器。
都支持雙向數據綁定。
都不支持低端瀏覽器。
不一樣點:
1.AngularJS的學習成本高,好比增長了Dependency Injection特性,而Vue.js自己提供的API都比較簡單、直觀。
2.在性能上,AngularJS依賴對數據作髒檢查,因此Watcher越多越慢。
Vue.js使用基於依賴追蹤的觀察而且使用異步隊列更新。全部的數據都是獨立觸發的。 對於龐大的應用來講,這個優化差別仍是比較明顯的。
2.與React的區別
相同點:
1.React採用特殊的JSX語法,Vue.js在組件開發中也推崇編寫.vue特殊文件格式,對文件內容都有一些約定,二者都須要編譯後使用。
2.中心思想相同:一切都是組件,組件實例之間能夠嵌套。
3.都提供合理的鉤子函數,可讓開發者定製化地去處理需求。
4.都不內置AJAX,Route等功能到核心包,而是以插件的方式加載。 5.在組件開發中都支持mixins的特性。
不一樣點:
React依賴Virtual DOM,而Vue.js使用的是DOM模板。React採用的Virtual DOM會對渲染出來的結果作髒檢查。
Vue.js在模板中提供了指令,過濾器等,能夠很是方便,快捷地操做DOM。
在模板中放入太多的邏輯會讓模板太重且難以維護,在須要對數據進行復雜處理,且可能屢次使用的狀況下,儘可能採起計算屬性的方式。好處:①使得數據處理結構清晰;②依賴於數據,數據更新,處理結果自動更新;③計算屬性內部this指向vm實例;④在template調用時,直接寫計算屬性名便可;⑤經常使用的是getter方法,獲取數據,也可使用set方法改變數據;⑥相較於methods,無論依賴的數據變不變,methods都會從新計算,可是依賴數據不變的時候computed從緩存中獲取,不會從新計算。
優勢: Vue的目標是經過儘量簡單的 API實現響應的數據綁定和組合的視圖組件,核心是一個響應的數據綁定系統。MVVM、數據驅動、組件化、輕量、簡潔、高效、快速、模塊友好;即第一次就將全部的東西都加載完成,所以,不會致使頁面卡頓。
缺點: 不支持低版本的瀏覽器,最低只支持到IE9;不利於SEO的優化(若是要支持SEO,建議經過服務端來進行渲染組件);第一次加載首頁耗時相對長一些;不可使用瀏覽器的導航按鈕須要自行實現前進、後退。
vue-router模塊的router-link組件的屬性。
嵌套路由:
嵌套路由顧名思義就是路由的多層嵌套。
重構router/index.js的路由配置,須要使用children數組來定義子路由 ,路由定義:
{ path: '/me', name: 'Me', component: Me, children: [ { path: 'collection', name: 'Collection', component: Collection }, { path: 'trace', name: 'Trace', component: Trace } ] }
以「/」開頭的嵌套路徑會被看成根路徑,因此子路由上不用加「/」;
在生成路由時,主路由上的path會被自動添加到子路由以前,因此子路由上的path不用在從新聲明主路由上的path了。
在外層路由組件中,以下寫法。
<template> <div class="me"> <div class="tabs"> <ul> <!--<router-link :to="{name: 'Default'}" tag="li" exact>默認內容</router-link>--> <router-link :to="{name: 'Collection'}" tag="li" >個人收藏</router-link> <router-link :to="{name: 'Trace'}" tag="li">個人足跡</router-link> </ul> </div> <div class="content"> <router-view></router-view> </div> </div> </template>
因爲篇幅有限,只能分享部分面試題,更多面試題及答案能夠【點擊我】閱讀下載哦~無償分享給你們,算是一個感恩回饋吧
漸進式網頁應用,PWA 應該具備一下特性:
漸進式:能確保每一個用戶都能打開網頁響應式:PC,手機,平板,無論哪一種格式,網頁格式都能完美適配
2.離線應用:支持用戶在沒網的條件下也能打開網頁,這裏就須要 Service Worker 的幫助
3.APP 化:可以像 APP 同樣和用戶進行交互
4.常更新:一旦 Web 網頁有什麼改動,都能當即在用戶端體現出來
5.安全:安全第一,給本身的網站加上一把綠鎖–HTTPS
6.可搜索:可以被引擎搜索到
7.推送:作到在不打開網頁的前提下,推送新的消息
8.可安裝:可以將 Web 想 APP 同樣添加到桌面
9.可跳轉:只要經過一個鏈接就能夠跳轉到你的 Web 頁面
slot又名插槽,是Vue的內容分發機制,組件內部的模板引擎使用slot元素做爲承載分發內容的出口。插槽slot是子組件的一個模板標籤元素,而這一個標籤元素是否顯示,以及怎麼顯示是由父組件決定的。
slot又分三類,默認插槽,具名插槽和做用域插槽。
默認插槽:又名匿名查抄,當slot沒有指定name屬性值的時候一個默認顯示插槽,一個組件內只有有一個匿名插槽。
具名插槽:帶有具體名字的插槽,也就是帶有name屬性的slot,一個組件能夠出現多個具名插槽。
做用域插槽:默認插槽、具名插槽的一個變體,能夠是匿名插槽,也能夠是具名插槽,該插槽的不一樣點是在子組件渲染做用域插槽時,能夠將子組件內部的數據傳遞給父組件,讓父組件根據子組件的傳遞過來的數據決定如何渲染該插槽。
實現原理:當子組件vm實例化時,獲取到父組件傳入的slot標籤的內容,存放在vm.$slot中,默認插槽爲vm.$slot.default,具名插槽爲vm.$slot.xxx,xxx 爲插槽名,當組件執行渲染函數時候,遇到slot標籤,使用$slot中的內容進行替換,此時能夠爲插槽傳遞數據,若存在數據,則可稱該插槽爲做用域插槽。
vue中的模板template沒法被瀏覽器解析並渲染,由於這不屬於瀏覽器的標準,不是正確的HTML語法,全部須要將template轉化成一個JavaScript函數,這樣瀏覽器就能夠執行這一個函數並渲染出對應的HTML元素,就可讓視圖跑起來了,這一個轉化的過程,就成爲模板編譯。
模板編譯又分三個階段,解析parse,優化optimize,生成generate,最終生成可執行函數render。
parse階段:使用大量的正則表達式對template字符串進行解析,將標籤、指令、屬性等轉化爲抽象語法樹AST。
optimize階段:遍歷AST,找到其中的一些靜態節點並進行標記,方便在頁面重渲染的時候進行diff比較時,直接跳過這一些靜態節點,優化runtime的性能。
generate階段:將最終的AST轉化爲render函數字符串。
對於 Vue 組件來講,模板編譯只會在組件實例化的時候編譯一次,生成渲染函數以後在也不會進行編譯。所以,編譯對組件的 runtime 是一種性能損耗。
而模板編譯的目的僅僅是將template轉化爲render function,這個過程,正好能夠在項目構建的過程當中完成,這樣可讓實際組件在 runtime 時直接跳過模板渲染,進而提高性能,這個在項目構建的編譯template的過程,就是預編譯。
對於 runtime 來講,只須要保證組件存在 render 函數便可,而咱們有了預編譯以後,咱們只須要保證構建過程當中生成 render 函數就能夠。
在 webpack 中,咱們使用vue-loader編譯.vue文件,內部依賴的vue-template-compiler模塊,在 webpack 構建過程當中,將template預編譯成 render 函數。
與 react 相似,在添加了jsx的語法糖解析器babel-plugin-transform-vue-jsx以後,就能夠直接手寫render函數。
因此,template和jsx的都是render的一種表現形式,不一樣的是:
JSX相對於template而言,具備更高的靈活性,在複雜的組件中,更具備優點,而 template 雖然顯得有些呆滯。可是 template 在代碼結構上更符合視圖與邏輯分離的習慣,更簡單、更直觀、更好維護。
Virtual DOM 是 DOM 節點在 JavaScript 中的一種抽象數據結構,之因此須要虛擬DOM,是由於瀏覽器中操做DOM的代價比較昂貴,頻繁操做DOM會產生性能問題。虛擬DOM的做用是在每一次響應式數據發生變化引發頁面重渲染時,Vue對比更新先後的虛擬DOM,匹配找出儘量少的須要更新的真實DOM,從而達到提高性能的目的。
在新老虛擬DOM對比時
首先,對比節點自己,判斷是否爲同一節點,若是不爲相同節點,則刪除該節點從新建立節點進行替換
若是爲相同節點,進行patchVnode,判斷如何對該節點的子節點進行處理,先判斷一方有子節點一方沒有子節點的狀況(若是新的children沒有子節點,將舊的子節點移除)
比較若是都有子節點,則進行updateChildren,判斷如何對這些新老節點的子節點進行操做(diff核心)。
匹配時,找到相同的子節點,遞歸比較子節點
在diff中,只對同層的子節點進行比較,放棄跨級的節點比較,使得時間複雜從O(n^3)下降值O(n),也就是說,只有當新舊children都爲多個子節點時才須要用核心的Diff算法進行同層級比較。
在對節點進行diff的過程當中,判斷是否爲相同節點的一個很重要的條件是key是否相等,若是是相同節點,則會盡量的複用原有的DOM節點。因此key屬性是提供給框架在diff的時候使用的,而非開發者。
重構響應式系統,使用Proxy替換Object.defineProperty,使用Proxy優點:
可直接監聽數組類型的數據變化
監聽的目標爲對象自己,不須要像Object.defineProperty同樣遍歷每一個屬性,有必定的性能提高
可攔截apply、ownKeys、has等13種方法,而Object.defineProperty不行
直接實現對象屬性的新增/刪除
新增Composition API,更好的邏輯複用和代碼組織
重構 Virtual DOM
模板編譯時的優化,將一些靜態節點編譯成常量
slot優化,將slot編譯爲lazy函數,將slot的渲染的決定權交給子組件
模板中內聯事件的提取並重用(本來每次渲染都從新生成內聯函數)
代碼結構調整,更便於Tree shaking,使得體積更小
使用Typescript替換Flow
Vue2.0中,隨着功能的增長,組件變得愈來愈複雜,愈來愈難維護,而難以維護的根本緣由是Vue的API設計迫使開發者使用watch,computed,methods選項組織代碼,而不是實際的業務邏輯。
另外Vue2.0缺乏一種較爲簡潔的低成本的機制來完成邏輯複用,雖然能夠minxis完成邏輯複用,可是當mixin變多的時候,會使得難以找到對應的data、computed或者method來源於哪一個mixin,使得類型推斷難以進行。
因此Composition API的出現,主要是也是爲了解決Option API帶來的問題,第一個是代碼組織問題,Compostion API可讓開發者根據業務邏輯組織本身的代碼,讓代碼具有更好的可讀性和可擴展性,也就是說當下一個開發者接觸這一段不是他本身寫的代碼時,他能夠更好的利用代碼的組織反推出實際的業務邏輯,或者根據業務邏輯更好的理解代碼。
第二個是實現代碼的邏輯提取與複用,固然mixin也能夠實現邏輯提取與複用,可是像前面所說的,多個mixin做用在同一個組件時,很難看出property是來源於哪一個mixin,來源不清楚,另外,多個mixin的property存在變量命名衝突的風險。而Composition API恰好解決了這兩個問題。
從React Hook的實現角度看,React Hook是根據useState調用的順序來肯定下一次重渲染時的state是來源於哪一個useState,因此出現瞭如下限制
不能在循環、條件、嵌套函數中調用Hook
必須確保老是在你的React函數的頂層調用Hook
useEffect、useMemo等函數必須手動肯定依賴關係
而Composition API是基於Vue的響應式系統實現的,與React Hook的相比
聲明在setup函數內,一次組件實例化只調用一次setup,而React Hook每次重渲染都須要調用Hook,使得React的GC比Vue更有壓力,性能也相對於Vue來講也較慢
Compositon API的調用不須要顧慮調用順序,也能夠在循環、條件、嵌套函數中使用
響應式系統自動實現了依賴收集,進而組件的部分的性能優化由Vue內部本身完成,而React Hook須要手動傳入依賴,並且必須必須保證依賴的順序,讓useEffect、useMemo等函數正確的捕獲依賴變量,不然會因爲依賴不正確使得組件性能降低。
雖然Compositon API看起來比React Hook好用,可是其設計思想也是借鑑React Hook的。
在客戶端請求服務器的時候,服務器到數據庫中獲取到相關的數據,而且在服務器內部將Vue組件渲染成HTML,而且將數據、HTML一併返回給客戶端,這個在服務器將數據和組件轉化爲HTML的過程,叫作服務端渲染SSR。
而當客戶端拿到服務器渲染的HTML和數據以後,因爲數據已經有了,客戶端不須要再一次請求數據,而只須要將數據同步到組件或者Vuex內部便可。除了數據意外,HTML也結構已經有了,客戶端在渲染組件的時候,也只須要將HTML的DOM節點映射到Virtual DOM便可,不須要從新建立DOM節點,這個將數據和HTML同步的過程,又叫作客戶端激活。
使用SSR的好處:
有利於SEO:其實就是有利於爬蟲來爬你的頁面,由於部分頁面爬蟲是不支持執行JavaScript的,這種不支持執行JavaScript的爬蟲抓取到的非SSR的頁面會是一個空的HTML頁面,而有了SSR之後,這些爬蟲就能夠獲取到完整的HTML結構的數據,進而收錄到搜索引擎中。
白屏時間更短:相對於客戶端渲染,服務端渲染在瀏覽器請求URL以後已經獲得了一個帶有數據的HTML文本,瀏覽器只須要解析HTML,直接構建DOM樹就能夠。而客戶端渲染,須要先獲得一個空的HTML頁面,這個時候頁面已經進入白屏,以後還須要通過加載並執行 JavaScript、請求後端服務器獲取數據、JavaScript 渲染頁面幾個過程才能夠看到最後的頁面。特別是在複雜應用中,因爲須要加載 JavaScript 腳本,越是複雜的應用,須要加載的 JavaScript 腳本就越多、越大,這會致使應用的首屏加載時間很是長,進而下降了體驗感。
因爲篇幅有限,只能分享部分面試題,更多面試題及答案能夠【點擊我】閱讀下載哦~無償分享給你們,算是一個感恩回饋吧
ajax
傳統 Ajax 指的是 XMLHttpRequest(XHR), 最先出現的發送後端請求技術,隸屬於原始js中,核心使用XMLHttpRequest對象,多個請求之間若是有前後關係的話,就會出現回調地獄。
juery.ajax $.ajax({ type: 'POST', url: url, data: data, dataType: dataType, success: function () {}, error: function () {} });
JQuery ajax 是對原生XHR的封裝,除此之外還增添了對JSONP的支持,舉出幾個缺點
自己是針對MVC的編程,不符合如今前端MVVM的浪潮
基於原生的XHR開發,XHR自己的架構不清晰。
JQuery整個項目太大,單純使用ajax卻要引入整個JQuery很是的不合理(採起個性化打包的方案又不能享受CDN服務)
不符合關注分離(Separation of Concerns)的原則
配置和調用方式很是混亂,並且基於事件的異步模型不友好
axios axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
axios 是一個基於Promise 用於瀏覽器和 nodejs 的 HTTP 客戶端,本質上也是對原生XHR的封裝,只不過它是Promise的實現版本,符合最新的ES規範,它自己具備如下特徵:
1.從瀏覽器中建立 XMLHttpRequest
2.支持 Promise API
3.客戶端支持防止CSRF
4.提供了一些併發請求的接口(重要,方便了不少的操做)
5.從 node.js 建立 http 請求
6.攔截請求和響應
7.轉換請求和響應數據
8.取消請求
9.自動轉換JSON數據
fetch try { let response = await fetch(url); let data = response.json(); console.log(data); } catch(e) { console.log("Oops, error", e); }
fetch號稱是AJAX的替代品,是在ES6出現的,使用了ES6中的promise對象。Fetch是基於promise設計的。Fetch的代碼結構比起ajax簡單多了,參數有點像jQuery ajax。可是,必定記住fetch不是ajax的進一步封裝,而是原生js,沒有使用XMLHttpRequest對象。
fetch的優勢:
1.符合關注分離,沒有將輸入、輸出和用事件來跟蹤的狀態混雜在一個對象裏
2.更好更方便的寫法
3.語法簡潔,更加語義化
基於標準 Promise 實現,支持 async/await
同構方便,使用 isomorphic-fetch
4.更加底層,提供的API豐富(request, response)
5.脫離了XHR,是ES規範裏新的實現方式
包括幾個步驟:
一、用 JavaScript 對象結構表示 DOM 樹的結構,而後用這個樹構建一個真正的 DOM 樹,插到文檔當中;
二、當狀態變動的時候,從新構造一棵新的對象樹,而後用新的樹和舊的樹進行比較,記錄兩棵樹差別;
三、把 2 所記錄的差別應用到步驟 1 所構建的真正的 DOM 樹上,視圖就更新了。
Virtual DOM 本質上就是在 JS 和 DOM 之間作了一個緩存。能夠類比 CPU 和硬盤,既然硬盤這麼慢,咱們就在它們之間加個緩存:既然 DOM 這麼慢,咱們就在它們 JS 和 DOM 之間加個緩存。CPU(JS)只操做內存(Virtual DOM),最後的時候再把變動寫入硬盤(DOM)。
單向數據流:顧名思義,數據流是單向的。數據流動方向能夠跟蹤,流動單一,追查問題的時候能夠更快捷。缺點就是寫起來不太方便。要使 UI 發生變動就必須建立各類 action 來維護對應的 state。
雙向數據綁定:數據之間是相通的,將數據變動的操做隱藏在框架內部。優勢是在表單交互較多的場景下,會簡化大量與業務無關的代碼。缺點就是沒法追蹤局部狀態的變化,增長了出錯時 debug 的難度。
vue-router 默認使用 hash 模式,因此在路由加載的時候,項目中的 URL 會自帶 「#」。若是不想使用 「#」, 可使用 vue-router 的另外一種模式 history:new Router ({ mode : 'history', routes: [ ]})須要注意的是,當咱們啓用 history 模式的時候,因爲咱們的項目是一個單頁面應用,因此在路由跳轉的時候,就會出現訪問不到靜態資源而出現 「404」 的狀況,這時候就須要服務端增長一個覆蓋全部狀況的候選資源:若是 URL 匹配不到任何靜態資源,則應該返回同一個 「index.html」 頁面。