前端Vue框架面試題大全


談一下你對 MVVM 的認識

https://blog.csdn.net/Dora_5537/article/details/89441144

mvvm :Model-View-ViewModel的縮寫,
model數據層,數據模型,僅僅關注數據本身
View視圖層是用戶操作界面 也可稱為,當ViewModel對Model進行更新的時候,會通過數據綁定更新到view

viewmodel 業務邏輯層 view需要什么數據 ViewModel要提供這個數據,view有哪些操作,ViewModel就要響應哪些操作,所以也可以說 它是Model for View

為什么會出現MVVM
前端開發中暴露出了三個痛點問題:

  • 開發者在代碼中大量調用相同的 DOM API,處理繁瑣 ,操作冗余,使得代碼難以維護。
  • 大量的 DOM 操作使頁面渲染性能降低,加載速度變慢,影響用戶體驗。
  • 當 Model 頻繁發生變化,開發者需要主動更新到 View ;當用戶的操作導致 Model 發生變化,開發者同樣需要將變化的數據同步到 Model 中,這樣的工作不僅繁瑣,而且很難維護復雜多變的數據狀態。其實,早期 jquery 的出現就是為了前端能更簡潔的操作 DOM 而設計的,但它只解決了第一個問題,另外兩個問題始終伴隨着前端一直存在。MVVM 的出現,完美解決了以上三個問題。

總結:MVVM模式簡化了界面與業務的一覽,解決了數據頻繁更新,MVVM在使用當中,利用雙向綁定技術,使得Model變化時,ViewModel會自動更新,而,ViewModel變化時,View也會自動變化。在MVVM的框架下視圖和模型是不能直接通信的,並沒有直接的聯系,它們通過ViewModel來通信。Model 和 ViewModel 之間的交互是雙向的, 因此 View 數據的變化會同步到 Model 中,而 Model 數據的變化也會立即反應到View 上。ViewModel 通過雙向數據綁定把 View 和 Model 連接了起來,而 View 和 Model 之間的同步工作完全是自動的,無需人為干涉,因此開發者只需關注業務邏輯,不需要手動操作DOM, 不需要關注數據狀態的同步問題,復雜的數據狀態維護完全由 MVVM 來統一管理。

MVC

  1. MVC 是 Model-View-Controller 的縮寫,即 模型—視圖—控制器。
    Model:后端傳遞的數據。
    View:所看到的頁面。
    Controller:頁面業務邏輯。
  2. MVC是單向通信。即View和Model,必須通過Controller來承上啟下。
  3. 使用MVC的目的就是將M和V的代碼分離。
  4. MVC 和 MVVM 的區別 並不是VM完全取代了C,ViewModel 存在目的 在於抽離 Controller 中展示的業務邏輯,而不是替代Controller,其它視圖操作業務等還是應該放在 Controller 中實現。也就是說MVVM實現的是業務邏輯組件的重用。

vue 生命周期有哪些?

一共8個階段
1、beforeCreate(創建前)
2、created(創建后)
3、beforeMount(載入前)
4、mounted(載入后)
5、beforeUpdate(更新前)
6、updated(更新后)
7、beforeDestroy(銷毀前)
8、destroyed(銷毀后)

vue生命周期及對應的行為

 
image.png

beforeCreate(創建前) 在數據觀測和初始化事件還未開始
created(創建后) 完成數據觀測,屬性和方法的運算,初始化事件,$el屬性還沒有顯示出來
beforeMount(載入前) 在掛載開始之前被調用,相關的render函數首次被調用。實例已完成以下的配置:編譯模板,把data里面的數據和模板生成html。注意此時還沒有掛載html到頁面上。
mounted(載入后) 在el 被新創建的 vm.$el 替換,並掛載到實例上去之后調用。實例已完成以下的配置:用上面編譯好的html內容替換el屬性指向的DOM對象。完成模板中的html渲染到html頁面中。此過程中進行ajax交互。
beforeUpdate(更新前) 在數據更新之前調用,發生在虛擬DOM重新渲染和打補丁之前。可以在該鈎子中進一步地更改狀態,不會觸發附加的重渲染過程。
updated(更新后) 在由於數據更改導致的虛擬DOM重新渲染和打補丁之后調用。調用時,組件DOM已經更新,所以可以執行依賴於DOM的操作。然而在大多數情況下,應該避免在此期間更改狀態,因為這可能會導致更新無限循環。該鈎子在服務器端渲染期間不被調用。
beforeDestroy(銷毀前) 在實例銷毀之前調用。實例仍然完全可用。
destroyed(銷毀后) 在實例銷毀之后調用。調用后,所有的事件監聽器會被移除,所有的子實例也會被銷毀。該鈎子在服務器端渲染期間不被調用。

vue父子組件生命周期執行順序

https://www.cnblogs.com/yuliangbin/p/9348156.html

加載渲染過程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

子組件更新過程
父beforeUpdate->子beforeUpdate->子updated->父updated

父組件更新過程
父beforeUpdate->父updated

銷毀過程

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

介紹JSX

jsx可以像我們寫HTML文件一樣寫業務代碼,借助於babel,會將jsx語法轉換成render語法,沒有什么副作用。

vue template語法簡單明了,數據操作與視圖分離,開發體驗友好。但是在某些特定場合中,會限制一些功能的擴展,如動態使用過濾器、解析字符串類型的模板文件等。以上功能的實現可以借助vue的render語法,render語法比template更偏底層,允許在HTML中使用js語法,可以極大的擴展HTML的能力。render函數注入了一個參數createElement,用來創建我們所需要的標簽內容,有三個參數:HTML標簽(elementTag),標簽屬性(option),子元素(children);從createElement的參數列表里面可以看出,如果組件內部結構嵌套比較深,render的語法寫起來會比較繁瑣,需要不斷的調用createElement,對於想偷懶的我,還是想想有沒有什么比較簡易的寫法,jsx無疑是一種很好的選擇,區別在於jsx可以像我們寫HTML文件一樣寫業務代碼,借助於babel,會將jsx語法轉換成render語法,沒有什么副作用。

如何實現一個指令

什么是虛擬 DOM

虛擬DOM概念隨着react的誕生而誕生,由facebook提出,其卓越的性能很快得到廣大開發者的認可;繼react之后vue2.0也在其核心引入了虛擬DOM的概念

什么是虛擬DOM?
vdom可以看作是一個使用javascript模擬了DOM結構的樹形結構,這個樹結構包含整個DOM結構的信息.。dom 樹對應的虛擬 dom 對象( js 對象),如下圖:

 
image.png

可見左邊的DOM結構,不論是標簽名稱還是標簽的屬性或標簽的子集,都會對應在右邊的樹結構里。

為什么要使用虛擬DOM?

虛擬 dom 是相對於瀏覽器所渲染出來的真實 dom 的,在react,vue等技術出現之前,我們要改變頁面展示的內容只能通過遍歷查詢 dom 樹的方式找到需要修改的 dom 然后修改樣式行為或者結構,來達到更新 ui 的目的。這種方式相當消耗計算資源,因為每次查詢 dom 幾乎都需要遍歷整顆 dom 樹,如果建立一個與 dom 樹對應的虛擬 dom 對象( js 對象),以對象嵌套的方式來表示 dom 樹,那么每次 dom 的更改 就變成了 js 對象的屬性的更改 ,這樣一來就能查找 js 對象的屬性變化要比查詢 dom 樹的 性能開銷小

其實並不是查詢 dom 樹性能開銷大而是 dom 樹的實現模塊和 js 模塊是分開的這些跨模塊的通訊增加了成本,以及 dom 操作引起的瀏覽器的回流和重繪,使得性能開銷巨大,原本在 pc 端是沒有性能問題的,因為 pc 的計算能力強,但是隨着移動端的發展,越來越多的網頁在智能手機上運行,而手機的性能參差不齊,會有性能問題。

他們的思想是每次更新 dom 都盡量避免刷新整個頁面,而是有針對性的 去刷新那被更改的一部分 ,來釋放掉被無效渲染占用的 gpu,cup 性能。

angular
angular 采用的機制是 臟值檢測查機制 所有使用了 ng 指令的 scope data 和 {{}} 語法的 scope data 都會被加入臟檢測的隊列

vue
vue 采用的是虛擬dom通過重寫 setter , getter實現觀察者監聽 data 屬性的變化生成新的虛擬 dom 通過 h 函數創建真實 dom 替換掉dom樹上對應的舊 dom。

react
react 也是通過虛擬 dom 和 setState 更改 data 生成新的虛擬 dom 以及 diff 算法來計算和生成需要替換的 dom 做到局部更新的。

虛擬dom的原理

diff算法 深度優先遍歷
diff的過程就是調用patch函數,就像打補丁一樣修改真實dom

virtual dom有哪些好處

  • 虛擬 DOM 不會立馬進行排版與重繪操作
  • 虛擬 DOM 進行頻繁修改,然后一次性比較並修改真實 DOM 中需要改的部分,最后在真實 DOM 中進行排版與重繪,減少過多DOM節點排版與重繪損耗
  • 虛擬 DOM 有效降低大面積真實 DOM 的重繪與排版,因為最終與真實 DOM 比較差異,可以只渲染局部

虛擬DOM主要做了什么

虛擬DOM本身是什么(JS對象)

為什么虛擬 DOM 的操作比 DOM 更快;

總之,一切為了減弱頻繁的大面積重繪引發的性能問題,不同框架不一定需要虛擬DOM,關鍵看框架是否頻繁會引發大面積的DOM操作

你的知道瀏覽器的虛擬DOM與真實DOM的區別(注意:需不需要虛擬DOM,其實與框架的DOM操作機制有關):
虛擬DOM不會進行排版與重繪操作
虛擬DOM進行頻繁修改,然后一次性比較並修改真實DOM中需要改的部分(注意!),最后並在真實DOM中進行排版與重繪,減少過多DOM節點排版與重繪損耗
真實DOM頻繁排版與重繪的效率是相當低的
虛擬DOM有效降低大面積(真實DOM節點)的重繪與排版,因為最終與真實DOM比較差異,可以只渲染局部(同2)
使用虛擬DOM的損耗計算:
總損耗=虛擬DOM增刪改+(與Diff算法效率有關)真實DOM差異增刪改+(較少的節點)排版與重繪
直接使用真實DOM的損耗計算:
總損耗=真實DOM完全增刪改+(可能較多的節點)排版與重繪
總之,一切為了減弱頻繁的大面積重繪引發的性能問題,不同框架不一定需要虛擬DOM,關鍵看框架是否頻繁會引發大面積的DOM操作

說一下virtual Dom中key的作用

實際的標簽中可能存在兩個一模一樣的兩個節點,但是在virtual Dom中無法體現這個區別,另一方面為了加快diff算法的速度,一個區別節點的變量的需求變得非常必要。virtual Dom中需要給每個節點一個標識,作為判斷是同一個節點的依據。所以這也是 Vue 和 React 中官方推薦列表里的節點使用唯一的 key 來保證性能。其中在diff算法中,大量使用了利用tagName和key組合判斷節點之間差異的邏輯代碼

vue有了響應式,為啥需要虛擬dom

vue的虛擬dom和react虛擬dom有啥區別嘞

vue.nextTick實現原理

https://www.jianshu.com/p/7f9495b1c8ab

Vue 實現響應式並不是數據發生變化之后 DOM 立即變化,而是按一定的策略進行 DOM 的更新。

1.在Vue生命周期的created()鈎子函數進行DOM操作一定要放到Vue.nextTick()的回調函數中。

在created()鈎子函數執行的時候DOM 其實並未進行任何渲染,而此時進行DOM操作無異於徒勞,所以此處一定要將DOM操作的js代碼放進Vue.nextTick()的回調函數中。與之對應的就是mounted()鈎子函數,因為該鈎子函數執行時所有的DOM掛載和渲染都已完成,此時在該鈎子函數中進行任何DOM操作都不會有問題。

2.在數據變化后要執行的某個操作,而這個操作需要使用隨數據改變而改變的DOM結構的時候,這個操作都應該放進Vue.nextTick()的回調函數中。

Vue 異步執行 DOM 更新。只要觀察到數據變化,Vue 將開啟一個隊列,並緩沖在同一事件循環中發生的所有數據改變。如果同一個 watcher 被多次觸發,只會被推入到隊列中一次。這種在緩沖時去除重復數據對於避免不必要的計算和 DOM 操作上非常重要。然后,在下一個的事件循環“tick”中,Vue 刷新隊列並執行實際 (已去重的) 工作。Vue 在內部嘗試對異步隊列使用原生的Promise.then和MessageChannel,如果執行環境不支持,會采用setTimeout(fn, 0)代替。

例如,當你設置vm.someData = 'new value',該組件不會立即重新渲染。當刷新隊列時,組件會在事件循環隊列清空時的下一個“tick”更新。多數情況我們不需要關心這個過程,但是如果你想在 DOM 狀態更新后做點什么,這就可能會有些棘手。雖然 Vue.js 通常鼓勵開發人員沿着“數據驅動”的方式思考,避免直接接觸 DOM,但是有時我們確實要這么做。為了在數據變化之后等待 Vue 完成更新 DOM ,可以在數據變化之后立即使用Vue.nextTick(callback)。這樣回調函數在 DOM 更新完成后就會調用。

Emit事件怎么發,需要引入什么

子組件: 通過emit this.emit("listenToChildEvent", deliver)
父組件進行綁定事件的,調用方法!
v-on:listenToChildEvent="showMsgFromChild"

渲染出全部的name,在哪個生命周期里寫,其中有幾個name不存在,通過異步接口獲取,如何做

談談eleme框架源碼

https://www.jianshu.com/p/92d0e78c9906

vue和react談談區別和選型考慮

1) React和Vue有許多相似之處,它們都有:

使用 Virtual DOM
提供了響應式(Reactive)和組件化(Composable)的視圖組件。
將注意力集中保持在核心庫,伴隨於此,有配套的路由和負責處理全局狀態管理的庫。

2) 性能:
到目前為止,針對現實情況的測試中,Vue的性能是優於React的

3) 生態圈
Vue.js: ES6+Webpack+unit/e2e+Vue+vue一router+單文件組件+vuex+iVew
React: ES6+Webpack+Enzyme+React+React一router+Redux

4) 什么時候選擇Vue.js
如果你喜歡用(或希望能夠用)模板搭建應用,請使用Vue
如果你喜歡簡單和”能用就行”的東西,請使用Vue
如果你的應用需要盡可能的小和快,請使用Vue
如果你計划構建一個大型應用程序,請使用React
如果你想要一個同時適用於Web端和原生App的框架,請選擇React
如果你想要最大的生態圈,請使用React

vue項目中如何約束rxjs數據的類型(根據項目問)

vue組件間通信x3

組件間通訊方法

import { Button } from 'antd',打包的時候只打包button,分模塊加載,是怎么做到的

 
image.png

使用import時,webpack對node_modules里的依賴會做什么

配置相關路徑

前端怎么控制管理路由

可以通過vue-router實例來配置路由規則列表,指定路徑path與組件component的對應關系。可以通過mode這一參數控制路由的實現模式,默認值是hash,基於hash的實現方式,如果顯示設置為history,則會設為基於history API的實現方式,如果瀏覽器不支持,可以設置fallback來控制是否需要回滾為'hash'模式。另外,如果是非瀏覽器端運行(如nodejs中),會將mode強制設為'abstract'模式。
vue-router支持路由嵌套、動態路由的配置、重定向及別名等,可參看官方文檔

前端路由的實現方式
在HTML5的 history API出現之前,前端路由主要是通過 hash 來實現的,hash能兼容低版本的瀏覽器。下面分別來介紹這2種方式。

方法一:基於hash(location.hash+hashchange事件)
我們知道location.hash的值是url中#后面的內容,如http://www.163.com#netease此網址中,location.hash='#netease'。hash滿足以下幾個特性,才使得其可以實現前端路由:

url中hash值的變化並不會重新加載頁面,因為hash是用來指導瀏覽器行為的,對服務端是無用的,所以不會包括在http請求中。
hash值的改變,都會在瀏覽器的訪問歷史中增加一個記錄,也就是能通過瀏覽器的回退、前進按鈕控制hash的切換
我們可以通過hashchange事件,監聽到hash值的變化,從而響應不同路徑的邏輯處理。
window.addEventListener("hashchange", funcRef, false)
如此一來,我們就可以在hashchange事件里,根據hash值來更新對應的視圖,但不會去重新請求頁面,同時呢,也在history里增加了一條訪問記錄,用戶也仍然可以通過前進后退鍵實現UI的切換。

觸發hash值的變化有2種方法:

一種是通過a標簽,設置href屬性,當標簽點擊之后,地址欄會改變,同時會觸發hashchange事件

<a href="#kaola">to KAOLA</a>
另一種是通過js直接賦值給location.hash,也會改變url,觸發hashchange事件。

location.hash="#kaola"

route是一條路由,是將一個URL路徑和一個處理函數相關聯,是一條url和函數的映射規則,如上面代碼中通過原型上的route可以設置一條路由規則,將一個path和其callback關聯起來。

而router則更像是一個容器,或者說一種機制,它管理了一組route。簡單來說,route只是進行了URL和函數的映射,而在當接收到一個URL之后,去路由映射表中查找相應的函數,這個過程是由router來處理的,如上面代碼,Router管理傳入的route,並且在hash改變的時候,根據當前的url響應其對應的函數。

方法二:基於History新API(history.pushState()+popState事件)
HTML5中history對象上新的API,同樣能實現前端的路由。通過pushState()方法或replaceState()方法可以修改url的地址,並在popstate事件中能監聽地址的改變,不同的是,手動的進行pushState()並不會觸發popstate事件。

先認識下兩個新增的API:history.pushState和 history.replaceState,這兩個API都接收三個參數:

window.history.pushState(null, null, "http://www.163.com");
狀態對象(state object),一個JavaScript對象,與用pushState()方法創建的新歷史記錄條目關聯。無論何時用戶導航到新創建的狀態,會觸發popstate事件,並能在事件中使用該對象。
標題(title) :傳入一個短標題給當前state。現在大多數瀏覽器不支持或者會忽略此參數,最好傳入null代替;
地址(URL):新的歷史記錄條目的地址。瀏覽器不會在調用pushState()方法后加載該地址,但之后,可能會試圖加載,例如用戶重啟瀏覽器。新的URL不一定是絕對路徑;如果是相對路徑,它將以當前URL為基准;傳入的URL與當前URL應該是同源的,否則,pushState()會拋出異常。該參數是可選的;不指定的話則為文檔當前URL。
這兩個API的相同之處是都會操作瀏覽器的歷史記錄,而不會引起頁面的刷新。不同之處在於,pushState會增加一條新的歷史記錄,而replaceState則會替換當前的歷史記錄。這兩個api,加上state改變觸發的popstate事件,提供了單頁應該的另一種路由方式。

當我們在歷史記錄中切換時就會觸發 popstate 事件,可以在事件中還原當前state對應的UI。對於觸發popstate 事件的方式,各瀏覽器實現也有差異,我們可以根據不同瀏覽器做兼容處理。

兩種方式對比,基於Hash的路由,兼容性更好;基於History API的路由,則更正式,可以設置與當前URL同源的任意URL,路徑更直觀。另外,基於Hash的路由不需要對服務器做改動,基於History API的路由需要對服務器做一些改造,配置不同的路由都返回相同的頁面。

使用路由時出現問題如何解決

路由的匹配規則是按照書寫的順序執行的,第一條匹配成功則不去匹配下一條,利用這一特性,可以在所有匹配路由的下面攔截匹配所有路由:

//創建路由對象並配置路由規則 let router = new VueRouter({ routes:[ {path:'/',redirect:{name:"home"}}, // 重定向到主頁 {name:'home',path:'/home',component:Home}, {name:'login',path:'/login',component:Login}, {path:'*',component:NotFound},//全不匹配的情況下,匹配NotFound組件,路由按順序從上到下,依次匹配。最后一個*能匹配全部, ] }); 

原理同方法2,只不過在匹配到*時,重定向到根路徑:

//創建路由對象並配置路由規則 let router = new VueRouter({ routes:[ {path:'/',redirect:{name:"home"}}, // 重定向到主頁 {name:'home',path:'/home',component:Home}, {name:'login',path:'/login',component:Login}, {path:'*',redirect:'/'},//路由按順序從上到下,依次匹配。最后一個*能匹配全部,然后重定向到主頁面 ] }); 

history 路由和 hash 路由的區別, 在瀏覽器有什么影響;

hash前端路由,無刷新
history 會去請求接口

vue-router提供兩種模式的原因:

vue 是漸進式前端開發框架,為了實現 SPA ,需要引入前端路由系統(vue-router)。前端路由的核心是:改變視圖的同時不會向后端發出請求。

為了達到這一目的,瀏覽器提供了 hash 和 history 兩種模式。

  1. hash :hash 雖然出現在 URL 中,但不會被包含在 http 請求中,對后端完全沒有影響,因此改變 hash 不會重新加載頁面。
  2. history :history 利用了 html5 history interface 中新增的 pushState() 和 replaceState() 方法。這兩個方法應用於瀏覽器記錄棧,在當前已有的 back、forward、go 基礎之上,它們提供了對歷史記錄修改的功能。只是當它們執行修改時,雖然改變了當前的 URL ,但瀏覽器不會立即向后端發送請求。

因此可以說, hash 模式和 history 模式都屬於瀏覽器自身的屬性,vue-router 只是利用了這兩個特性(通過調用瀏覽器提供的接口)來實現路由。

實現的原理

  1. hash 模式的原理是 onhashchange 事件,可以在 window 對象上監聽這個事件。
  2. history :hashchange 只能改變 # 后面的代碼片段,history api (pushState、replaceState、go、back、forward) 則給了前端完全的自由,通過在window對象上監聽popState()事件。
pushState()、replaceState() 方法接收三個參數:stateObj、title、url。 // 設置狀態 history.pushState({color: "red"}, "red", "red"); // 監聽狀態window.onpopstate = function(event){ console.log(event.state); if(event.state && event.state.color === "red"){ document.body.style.color = "red"; }} // 改變狀態history.back();history.forward();復制代碼 

應用場景

通過 pushState 把頁面的狀態保存在 state 對象中,當頁面的 url 再變回到這個 url 時,可以通過 event.state 取到這個 state 對象,從而可以對頁面狀態進行還原,如頁面滾動條的位置、閱讀進度、組件的開關等。

調用 history.pushState() 比使用 hash 存在的優勢:

  • pushState 設置的 url 可以是同源下的任意 url ;而 hash 只能修改 # 后面的部分,因此只能設置當前 url 同文檔的 url
  • pushState 設置的新的 url 可以與當前 url 一樣,這樣也會把記錄添加到棧中;hash 設置的新值不能與原來的一樣,一樣的值不會觸發動作將記錄添加到棧中
  • pushState 通過 stateObject 參數可以將任何數據類型添加到記錄中;hash 只能添加短字符串
  • pushState 可以設置額外的 title 屬性供后續使用

劣勢:

  • history 在刷新頁面時,如果服務器中沒有相應的響應或資源,就會出現404。因此,如果 URL 匹配不到任何靜態資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面
  • hash 模式下,僅 # 之前的內容包含在 http 請求中,對后端來說,即使沒有對路由做到全面覆蓋,也不會報 404

方案題:不同前端技術棧的項目,如何實現一套通用組件方案?

vuex, mobx, redux各自的特點和區別

vue-router(hash, HTML5 新增的 pushState)

單頁應用,如何實現其路由功能---路由原理

寫在前面:通常 SPA 中前端路由有2種實現方式:

  1. window.history
  2. location.hash

下面就來介紹下這兩種方式具體怎么實現的

一.history

1.history基本介紹

window.history 對象包含瀏覽器的歷史,window.history 對象在編寫時可不使用 window 這個前綴。history是實現SPA前端路由是一種主流方法,它有幾個原始方法:

  1. history.back() - 與在瀏覽器點擊后退按鈕相同
  2. history.forward() - 與在瀏覽器中點擊按鈕向前相同
  3. history.go(n) - 接受一個整數作為參數,移動到該整數指定的頁面,比如go(1)相當於forward(),go(-1)相當於back(),go(0)相當於刷新當前頁面
  4. 如果移動的位置超出了訪問歷史的邊界,以上三個方法並不報錯,而是靜默失敗

在HTML5,history對象提出了 pushState() 方法和 replaceState() 方法,這兩個方法可以用來向歷史棧中添加數據,就好像 url 變化了一樣(過去只有 url 變化歷史棧才會變化),這樣就可以很好的模擬瀏覽歷史和前進后退了,現在的前端路由也是基於這個原理實現的。

2.history.pushState

pushState(stateObj, title, url) 方法向歷史棧中寫入數據,其第一個參數是要寫入的數據對象(不大於640kB),第二個參數是頁面的 title, 第三個參數是 url (相對路徑)。

  1. stateObj :一個與指定網址相關的狀態對象,popstate事件觸發時,該對象會傳入回調函數。如果不需要這個對象,此處可以填null。
  2. title:新頁面的標題,但是所有瀏覽器目前都忽略這個值,因此這里可以填null。
  3. url:新的網址,必須與當前頁面處在同一個域。瀏覽器的地址欄將顯示這個網址。

關於pushState,有幾個值得注意的地方:

pushState方法不會觸發頁面刷新,只是導致history對象發生變化,地址欄會有反應,只有當觸發前進后退等事件(back()和forward()等)時瀏覽器才會刷新

這里的 url 是受到同源策略限制的,防止惡意腳本模仿其他網站 url 用來欺騙用戶,所以當違背同源策略時將會報錯

3.history.replaceState

replaceState(stateObj, title, url) 和pushState的區別就在於它不是寫入而是替換修改瀏覽歷史中當前紀錄,其余和 pushState一模一樣

4.popstate事件

定義:每當同一個文檔的瀏覽歷史(即history對象)出現變化時,就會觸發popstate事件。

注意:僅僅調用pushState方法或replaceState方法 ,並不會觸發該事件,只有用戶點擊瀏覽器倒退按鈕和前進按鈕,或者使用JavaScript調用back、forward、go方法時才會觸發。另外,該事件只針對同一個文檔,如果瀏覽歷史的切換,導致加載不同的文檔,該事件也不會觸發。

用法:使用的時候,可以為popstate事件指定回調函數。這個回調函數的參數是一個event事件對象,它的state屬性指向pushState和replaceState方法為當前URL所提供的狀態對象(即這兩個方法的第一個參數)。

二.Hash

1.Hash基本介紹

url 中可以帶有一個 hash http://localhost:9000/#/a.html

window 對象中有一個事件是 onhashchange,以下幾種情況都會觸發這個事件:

  1. 直接更改瀏覽器地址,在最后面增加或改變#hash;
  2. 通過改變location.href或location.hash的值;
  3. 通過觸發點擊帶錨點的鏈接;
  4. 瀏覽器前進后退可能導致hash的變化,前提是兩個網頁地址中的hash值不同。

vue-router如何做用戶登錄權限等

router官網中進階部分提供了導航守衛的功能,可以通過設置全局前置守衛或者路由獨享守衛來做用戶登陸權限的判別。

你在項目中怎么實現路由的嵌套

需要在 VueRouter 的參數中使用 children 配置,這樣就可以很好的實現路由嵌套。

vue怎么監聽數組

在將數組處理成響應式數據后,如果使用數組原始方法改變數組時,數組值會發生變化,但是並不會觸發數組的setter來通知所有依賴該數組的地方進行更新,為此,vue通過重寫數組的某些方法來監聽數組變化,重寫后的方法中會手動觸發通知該數組的所有依賴進行更新

我們知道通過Object.defineProperty()劫持數組為其設置getter和setter后,調用的數組的push、splice、pop等方法改變數組元素時並不會觸發數組的setter,這就會造成使用上述方法改變數組后,頁面上並不能及時體現這些變化,也就是數組數據變化不是響應式的(對上述不了解的可以參考這篇文章)。但實際用vue開發時,對於響應式數組,使用push、splice、pop等方法改變數組時,頁面會及時體現這種變化,那么vue中是如何實現的呢?通過vue源碼可以看出,vue重寫了數組的push、splice、pop等方法

因為監聽的數組帶來的代價和一些問題,Vue使用了重寫原型的方案代替。攔截了數組的一些方法,在這個過程中再去做通知變化等操作。

雙向綁定的原理?數據劫持?

https://blog.csdn.net/weixin_38500689/article/details/88227985

如何做到的雙向綁定

原理
vue數據雙向綁定通過‘數據劫持’ + 訂閱發布模式實現

數據劫持
指的是在訪問或者修改對象的某個屬性時,通過一段代碼攔截這個行為,進行額外的操作或者修改返回結果

典型的有
1.Object.defineProperty()
2.es6中Proxy對象

vue2.x使用Object.defineProperty();
vue3.x使用Proxy;

訂閱發布模式
定義:對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都將得到通知
訂閱發布模式中事件統一由處理中心處理,訂閱者發布者互不干擾。
優點:實現更多的控制,做權限處理,節流控制之類,例如:發布了很多消息,但是不是所有訂閱者都要接收

// 實現一個處理中心 let event = { clientList: {}, // 訂閱事件列表 // 訂閱 on(key, fn){ // 如果這個事件沒有被訂閱,那么創建一個列表用來存放事件 if(!this.clientList[key]) { this.clientList[key] = [] } // 將事件放入已有的事件列表中 this.clientList[key].push(fn); }, // 發布 trigger(type, args){ let fns = this.clientList[type] // 拿到這個事件的所有監聽 if(!fns || fns.length === 0){ // 如果沒有這條消息的訂閱者 return false } // 如果存在這個事件的訂閱,那么遍歷事件列表,觸發對應監聽 fns.forEach(fn => { // 可以在此處添加過濾等處理 fn(args) }) } } 

vue中如何實現
利用Object.defineProperty();把內部解耦為三部分
Observer: 遞歸的監聽對象上的所有屬性,當屬性改變時觸發對應的watcher
watcher(觀察者):當蔣婷的數據值修改時,執行相應的回調函數,更新模板內容
dep:鏈接observer和watcher,每一個observer對應一個dep,內部維護一個數組,保存與該observer相關的watcher

proxy實現觀察者模式
觀察者模式(Observer mode)指的是函數自動觀察數據對象,一旦對象有變化,函數就會自動執行

const person = observable({ name: '張三', age: 20 }); function print() { console.log(`${person.name}, ${person.age}`) } observe(print); person.name = '李四'; // 輸出 // 李四, 20 

代碼中。對象person是觀察目標,函數print是觀察者。一旦數據發生變化,print就會自動執行

使用proxy實現一個最簡單觀察者模式,即實現observable和observe這兩個函數。
思路是observable函數返回一個原始對象的proxy代理,攔截復制操作。觸發充當觀察者的各個函數

const queue = new Set(); const observe = fn => queue.add(fn); const observable = obj => new Proxy(obj, {set}); function set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); queue.forEach(observer => observer()); return result; } 

上面代碼中,先定義了一個Set集合,所有觀察者函數都放進這個集合,然后,observable函數返回原始對象的代理,攔截賦值操作。
攔截函數set中,自動執行所有觀察者

談一下你對 Vue 的認識,以及 Vue 底層實現的機制;

Vue 3.0有沒有過了解

關於Vue 3.0有幸看過尤大的關於3.0版本的RFC Vue Function-based API RFC。 大致說了三個點,第一個是關於提出的新API setup()函數,第二個說了對於Typescript的支持,最后說了關於替換Object.defineProperty為Proxy的支持。
詳細說了下關於Proxy代替帶來的性能上的提升,因為傳統的原型鏈攔截的方法,無法檢測對象及數組的一些更新操作,但使用Proxy又帶來了瀏覽器兼容問題。

1、速度方面:

通過更新 runtime-core 與 runtime-dom ,支持了包括 Fragments、Portals與Suspense w/ async setup() 等(似乎有點越來越像 react),支持 Composition API 、Options API 和 typings,配合 Proxy 的引入,極大程度上提高了響應式的能力。

Composition API 與 Proxy 的運用使得組件化更加靈活,邏輯業務組件的編寫與UI組件的多樣化能夠更好的實現。

2、體積方面:

runtime-core 體積壓縮成了 約10kb

3、維護性:

代碼遷移成 TypeScript (還沒學的抓緊啦)

編譯器(Compiler)優化,以下借用尤大大的特性更新圖翻譯一下:

使用模塊化架構

優化 "Block tree"

更激進的 static tree hoisting 功能

支持 Source map

內置標識符前綴(又名 "stripWith")

內置整齊打印功能

移除 source map 和標識符前綴功能后,使用 Brotli 壓縮的瀏覽器版本精簡了大約 10KB

可見,編譯器更多從數據結構入手,優化架構層級,降低使用成本。

4、更接近原生開發

5、讓你更輕松開發

其次是 GitHub 上的源碼目錄:

reactivity 目錄:數據響應式系統,主要使用 Proxy。
runtime-core 目錄:支持 v-dom 與 Vue 原生的各種 API 在瀏覽器上的調用,調用 Vue 特性的相關代碼。
runtime-dom 目錄: 調用各類瀏覽器原生 DOM 事件的相關代碼。
runtime-test 目錄: 測試用的runtime的相關代碼。
server-renderer 目錄: SSR 相關代碼。
compiler-core 目錄: 支持 Vue 原生編寫的 編譯器特性,以及開發者編寫的編譯器特性的相關代碼。
compiler-dom 目錄: 調用瀏覽器的編譯器的相關代碼。
shared 目錄: 無內置API。
vue 目錄: 實現 vue 構建與編譯的相關代碼。

Vue3 proxy解決了哪些問題?

Vue3 proxy的優劣

Vue響應式原理

在官方文檔中,有簡單介紹一點,是基於 Object.defineProperty 去實現的;但是有一個要求,就是在 實例化 Vue 構造函數前,所有熬進行雙向綁定的數據,都是要在 data 里面去做初始化的,哪怕是一個空值;因為在每次實例化的時候,Vue 會去檢測 data ,查看並把存在屬性用 Object.defineProperty 進行監聽;在每一個需要判斷或者展示當前響應式監聽的數據時,例如:

<div>{{name}}</div> 復制代碼 
new Vue({ data(){ return { name:'zhangsan' } } }) 復制代碼 

初始化的時候,我綁定了一個 name 屬性,它在一個 div 里面做了展示;當我在 div 里面添加 name 展示的時候,其實在模板編譯的時候,獲取了一下 name 屬性;因為前面有提到,我給當前的屬性綁定了 Object.defineProperty ,所以在獲取的時候,我會調用到 get 方法;

在這之前,我有實例化一個 dep 隊列,把每次獲取 name 屬性的地方,做一個 push

當我接下來要做數據修改的時候,比如把 zhangsan 變成了 lisi ,那么在 set 方法里,把我當前屬性的隊列有監聽當前屬性的位置,全部更新一遍;最后把 data 對象里面的屬性值做修改;

:這是一個面試的回答,但這個不夠詳細,如果想很詳細的去了解,具體都做了什么,請跳轉:Vue 源碼解析(實例化前) - 響應式數據的實現原理

Proxy 相比於 defineProperty 的優勢

https://www.jianshu.com/p/2df6dcddb0d7

Proxy的優勢:

可以直接監聽對象而非屬性

  • 可以直接監聽數組的變化
  • 攔截方式較多
  • Proxy返回一個新對象,可以只操作新對象達到目的,而Object.defineProperty只能遍歷對象屬性直接修改
  • Proxy作為新標准將受到瀏覽器廠商重點持續的性能優化

Object.defineProperty的優勢如下:

  • 兼容性好,支持IE9

key的作用

vuex數據流動過程

1、vuex是什么
   公共狀態管理
   解決多個非父子組件傳值問題。
   多個頁面都能用Vuex中store公共的數據
a、並不是所有的數據都要放在Vuex中,只有各個組件公用的一些數據會放在Vuex當中
 b、Vuex是一個公共狀態管理模式 並不是數據庫 所以不可能持久保存一些數據,當用戶刷新瀏覽器的時候那么數據就有可能消失
 c、Vuex主要應用在大型的單頁面開發中
2、vuex的特點
 1、遵循單向數據流
 2、Vuex中的數據是響應式的
3、vuex的流程圖:

 
image.png

總結下vuex數據傳輸流程

1、通過new Vuex.Store()創建一個倉庫 state是公共的狀態,state—>components渲染頁面
    2、在組件內部通過this.$store.state.屬性 來調用公共狀態中的state,進行頁面的渲染。
    3、當組件需要修改數據的時候,必須遵循單向數據流。組件里在methods中擴展mapActions,調用store里的actions里的方法。
    4、actions中的每個方法都會接受一個對象 這個對象里面有一個commit方法,用來觸發mutations里面的方法
    5、mutations里面的方法用來修改state中的數據 mutations里面的方法都會接收到2個參數 一個是store中的state, 另外一個是需要傳遞到參數
    6、當mutations中的方法執行完畢后state會發生改變,因為vuex的數據是響應式的 所以組件的狀態也會發生改變

vuex的理解

vuex vue-router原理等

一、vue-router——路由管理

經過自己的學習加項目總結,針對路由這塊內容說下自己的理解:

路由配置主要有三個關鍵字:route routes router

route(單數)表示一條路由,它是一個對象由兩部分構成(path——路徑;component——組件)

例如:homeBtn——homeContent(點擊homeBtn按鈕頁面切換到homeContent組件);sideBtn——sideContent;這是兩條路由(route)

如果路由多,這樣一條一條表示顯然不合理。

由此出現routes(復數)表示一組路由(路由的數組),上面的兩條路由就可以這樣表示:

 
image

具體寫法:const routes = [{path:'/home',component:homeContent},{path:'/sidebar',component:sidebarContent}]

router表示路由機制(即路由的管理者),實現原理:點擊homeBtn按鈕——到routes里去查找到‘/home’對應得組件homeContent,並顯示頁面。

寫法:const router = new VueRouter({routes:routes}),簡寫:const router = new VueRouter({routes})

最后將router實例注入到vue根實例中,便可以使用路由

配置好路由再定義好路由的出口router-view就可以使用路由實現頁面跳轉了。

ps:router-link相當於a標簽。

二、vuex——數據狀態管理

簡單說下vuex的作用:主要用於組件直接通訊。比如A和B和C為兄弟組件,組件中都用到name這個字段,A組件中如果對name字段進行更改,那該如何通知B和C組件name字段已經發生變化了呢?這個時候就可以使用Vuex來進行通訊了。先將name存起來(state管理變量初始狀態的),A組件中通過觸發mutation來通知state中的name發生改變。那么B和C組件就可以從vuex獲取state中的name。大概就是這么個意思。

另外說一下:vuex中的幾個關鍵要素:state、mutation、action以及vuex中自帶的函數:mapState、mapMutation、mapAction、mapGetter(都是個人的理解,如有不當請指出。)

state主要定義一些初始化的變量;

mutation定義修改State的方法,有兩個參數(state、value)

action定義觸發mutation行為的方法;語法:方法名(context)、通過context.commit來觸發mutation的方法;

getter定義獲取state的方法,參數state;

上面內容主要說如何定義這些變量,下面說下如果在組件中使用這些方法:

第一種情況:設置/修改變量的兩種方式:

a、在計算屬性中或方法中調用mutation方法:this.$store.commit(‘方法名’,參數值)

b、使用action觸發mutation方法:this.$store.dispatch(mutaion的方法)

官方不建議使用a,建議使用b。

第二種情況:獲取參數

兩種方式:1、this.store.state.參數;2、this.store.getter.方法名

使用this.$store.state等這些書寫方式太長。所以可以通過vuex的輔助函數:mapState等來簡化書寫

輔助函數的使用方法:

1、mapGetters/apState放在computed屬性中。

書寫方式:a、...mapGetters(['Getter定義的方法名'])
b、參數如果是count:...mapState({state=>state.count})
2、mapMutation和mapAction,一般放在method中寫:...mapMutation(['mutation定義的方法名'])

有錯誤歡迎指正!

如何實現一個自定義組件,不同組件之間如何通信的?

https://blog.csdn.net/brave_haha/article/details/81000633

如何設計一個組件

http://www.fly63.com/article/detial/996

對無狀態組件的理解,使用過程中遇到的問題,如何解決的

watch computed區別

1、methods,computed的區別

例子:

var vm = new Vue({
el: '#app',
data: {
msg: 'nihao',
text: 'byebye'
},
computed: {
getmsg: function () {
return this.msg
}
},
methods:{
gettext:function () {
return this.msg
}
}
})
<p>msg:{{getmsg}}</p>
<p>text:{{gettext()}}</p>
在這個例子中,只要msg值發生變化,getmsg方法就會觸發,而text的值發生變化,只要沒有調用gettext方法,顯示的值不會動態改變。就是說,methods中就是普通的方法,通過調用執行,computed中的方法會監聽自己的依賴,依賴發生變化方法會自動執行。

  1. computed 和methods 達到的效果是相同的

  2. 計算屬性computed和methods都不應該使用箭頭函數來定義計算屬性 因為箭頭函數綁定了父級作用域的上下文,所以 this 將不會按照期望指向Vue
    不同點是:

  3. 計算屬性compute是基於他們的依賴(如果是實例范疇之外的依賴,比如非響應式的not reactive是不會觸發屬性更新的)進行緩存(計算屬性的結果會被緩存),只有相關依賴會發生改變時才會重新求值,未改變只會返回只之前的結果,不在執行函數 .原值與新值一樣不會觸發,函數的返回值就是最終獲取的值.

  4. computed是響應式的,methods並非響應式。

  5. 調用方式不一樣,computed定義的成員像屬性一樣訪問,methods定義的成員必須以函數形式調用。

  6. computed是帶緩存的,只有其引用的響應式屬性(屬性綁定)發生改變時才會重新計算(如果引用的屬性沒有改變,則調用上一次緩存值),而methods里的函數在每次調用時都要執行

  7. computed中的成員可以只定義一個函數作為只讀屬性,也可以定義get/set變成可讀寫屬性,這點是methods中的成員做不到的。 computed 計算屬性的方法在用屬性時不用加(),而methods 方法在使用時要像方法一樣去用,必須必須要加(){{ mes() }}

默認加載的時候先computed再watch,不執行methods;等觸發某一事件后,則是:先methods再watch。

watch適合處理的場景是,偵聽一個數的變化,當該數據變化,來處理其他與之相關數據的變化(該數據影響別的多個數據)==當數據發生變化時,執行異步操作或較大開銷操作的情況。如圖所示:


 
image.png

computed適合處理的場景是,一個數據屬性在它所依賴的屬性發生變化時,也要發生變化,這種情況下,我們最好使用計算屬性。如圖所示:


 
image.png

vue和react的區別

react fiber架構的理解
https://juejin.im/post/5c92f499f265da612647b754#heading-3

restful接口架構的優缺點?

RESTful架構優點:

  1. 前后端分離,減少流量
  2. 安全問題集中在接口上,由於接受json格式,防止了注入型等安全問題
  3. 前端無關化,后端只負責數據處理,前端表現方式可以是任何前端語言(android,ios,html5)
  4. 前端和后端人員更加專注於各自開發,只需接口文檔便可完成前后端交互,無需過多相互了解
  5. 服務器性能優化:由於前端是靜態頁面,通過nginx便可獲取,服務器主要壓力放在了接口上

RESTful架構設計原則(不同公司具體細節可能不同):

  1. 在接口命名時應該用名詞,不應該用動詞,因為通過接口操作到是資源。

  2. 在url中加入版本號,利於版本迭代管理更加直觀

    <pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">https://www.rgc.com/v1/</pre>

  3. 對於資源的操作類型應該是通過http動詞表示。

    • GET /zoos:列出所有動物園
    • POST /zoos:新建一個動物園
    • GET /zoos/ID:獲取某個指定動物園的信息
    • PUT /zoos/ID:更新某個指定動物園的信息(提供該動物園的全部信息)
    • DELETE /zoos/ID:刪除某個動物園
    • GET /zoos/ID/animals:列出某個指定動物園的所有動物
    • DELETE /zoos/ID/animals/ID:刪除某個指定動物園的指定動物
  4. 排序規則:默認時升序,‘-’為降序;多個排序規則時以逗號間隔組合。使用sort查詢參數限制

    <pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">GET /tickets?sort=-time,created_at
    優先以time倒序顯示,其次以created_at正序顯示</pre>

  5. 限制返回值的字段域:明確指定輸出字段列表,用於控制網絡帶寬和速度。使用fields查詢參數來限制。

    <pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">GET /tickets?fileds=id,subject,customer_name,time&sort=-time
    返回參數列表為id,subject,customer_name,time,並且以time字段倒序顯</pre>

  6. HTTP Method分別對於資源的CURD操作
    POST(CREATE):在服務器新建一個資源。
    PUT(UPDATE):在服務器更新資源(客戶端提供改變后的完整資源)。
    PATCH(UPDATE):在服務器更新資源(客戶端提供改變的屬性)。
    DELETE(DELETE):從服務器刪除資源。</pre>

    保證 POST,PUT,DELETE,PATCH,GET 操作冪等性

  7. 使用SSL(Secure Sockets Layer 安全套接層)

  8. 參數和url采用蛇行命名方式。如:updated_time

  9. 服務器請求和返回的數據格式,應該盡量使用JSON,避免使用XML。在 request中的Accept和Response中的Content-Type:application/json

 
image.png

總結:優秀的RESTful接口設計,能夠根據請求的路徑及請求方法就能看出這個接口主要是對具體某個資源進行什么方法的操作以及返回數據的規則等等。

restful接口架構會導致什么安全問題,具體怎么解決?

https://www.jianshu.com/p/b5966899c435

重新渲染render會做些什么

JS是什么范式語言(面向對象還是函數式編程)

js 是更傾向於函數式編程,js是弱類型語言,也可以叫解釋型語言,開始創建js時傾向於函數式編程。隨着js的發展,加入了面向對象的東西,但它是函數式編程。
https://www.cnblogs.com/Darlietoothpaste/p/10633550.html

資深前端

會vue基礎使用不值錢
組件化 + element-ui源碼

做一些更高端和通用的事情,組件化,框架,框架源碼里,有大量的最佳實踐。vue源碼里,大量的工程化,設計模式,代碼規范的最佳實踐。除了經驗豐富外,視野更高,解決詭異的bug,架構的設計



作者:Aniugel
鏈接:https://www.jianshu.com/p/4db4b191de06
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM