2022-06-27 前端vue面試題2

#vue通信傳值

1.props&$emit

1.1 父傳子props

現在我們要從Index頁面給A頁面傳遞一個數組list

// index.vue<template><div><A:list="list"/></div></template><script>importAfrom"./components/A";exportdefault{name:"Index",components:{A},data(){return{list:["html","css","js"],};},},</script>

// A.vue<template><div><h2>PageA</h2><ul v-for="(item, index) in list":key="index"><li>{{item}}</li></ul></div></template><script>exportdefault{name:"A",props:["list"],};</script>

效果圖如下, 此時list數組以自上而下的一種方式從Index頁面傳遞給A組件, 且props只可以從上一級想下一級傳輸, 即所謂的單向數據流

image.png

1.2 子傳父$emit

那A組件想給Index頁面傳送數據應該如何操作? 關于子組件給父組件傳值, 一般都是通過一個事件搭配$emit進行傳輸

// A.vue<template><div><h2>PageA</h2><ul v-for="(item, index) in list":key="index"><!--定義事件--><li @click="onItemClick(item)">{{item}}</li></ul></div></template><script>exportdefault{name:"A",props:["list"],methods:{onItemClick(item){this.$emit("on-item-click",`'${item}' from page A`);},},};</script>

父組件監聽子組件上的事件名on-item-click

// index.vue<template><div><!--監聽--><A:list="list"@on-item-click="handleItemClick"/></div></template><script>importAfrom"./components/A";exportdefault{name:"Index",components:{A,},data(){return{list:["html","css","js"],};},methods:{handleItemClick(value){console.log(`In page Index get ${value}`);},},};</script>

一、為什么要使用vue-bus?

學習vue的開發者都知道,父子組件的直接的通訊直接使用vue提供的props屬性和emit方法。props接受來自父組件的參數,emit將子組件的參數傳遞給父組件。這樣一來父子組件之間的參數傳遞就得到了解決。(之前做項目的時候看到$parent和$children也可以進行父子組件和兄弟之間的參數傳遞,但是不提倡,原因在于如果組件想要替換位置就有問題)。

那么問題來了,兄弟組件如何進行通訊?有哪些方法

1、vuex全局狀態管理

2、bus總線機制/發布訂閱者模式/觀察者模式

兩者相比較,前者適用于大型項目的開發,如果項目業務沒那么復雜,推薦使用bus來進行解決這類問題。

二、vue-bus如何使用?

第一步:使用npm install vue-bus --save

第二步:在main.js進行全局注冊

第三步:在一個頁面引用兩個兄弟組件

第四步:使用emit進行參數傳遞

第五步:在created或mounted生命周期鉤子,執行事件監聽。最后記得將觸發的事件銷毀,不然會出現點擊多次觸發的情況。

三、bus總線機制/發布訂閱者模式/觀察者模式

比如有一個bus對象(中央事件總線),這個對象上有兩個方法,一個是on(監聽,也就是訂閱),一個是emit(觸發,也就是發布),就好比我們訂閱報紙,到報社去付錢,才知道你要訂閱的。

觀察者模式是用來監聽數據變化,改變視圖層。

#vuex

1、vuex 是什么?

集中式存儲管理應用的所有組件的狀態(單獨出來管理共用的狀態,如:token的管理等,相當于一個全局對象),以相應的規則保證狀態以一種可預測的方式發生變化(不能直接修改數據,需要通過特定的方法, this.

store.commit() ),無法持久化存儲。

vuex 和 Cookie,localstorage 的區別

Cookie:采用鍵值對的方式存儲( document.cookie =? 鍵= 值; 鍵= 值),可以定義過期時間(document.cookie ="expires=有效期"),存儲大小4k

localstorage:使用方法進行鍵值對的方式存儲(getItem(鍵,值)),永久存儲,但是可以根據方法清除, 存儲大小5m

方法描述實例

setItem()將對應的名字和值傳遞進去存儲localStorage.setItem('users','我是user')

getItem()名稱作為參數,可以獲取對應的值localStorage.getItem('users')

removeItem()名稱作為參數,可以刪除對應的數據localStorage.removeItem('users')

clear()可以刪除所有存儲的數據localStorage.clear()

sessionstorage:跟 localstorage 方法一樣,但是是單頁面回話存儲,跳轉頁面被清除,

vuex:相當于全局變量,刷新頁面清除,想長久保持數據存在,只能使用Cookie和localstorage存儲,然后再賦值回去

如:

getters :{userName: (state,getters,rootState)=>{console.log(state.userId)let a =(state.userId?state.userId:localStorage.getItem('users'))return a}},

2、vue 使用

state 唯一數據源

像我們vue的data

定義conststate={token:'我是token'}conststore=newVuex.Store({state})直接使用this.$store.state.token=='我是token'引入使用import{mapState}from'vuex'computed:{...mapState(['token'])//不取別名 this.token==? '我是token'...mapState({userToken:'token'//取別名,this.userToken ==? '我是token'})},

getter 計算屬性

從 store 中的 state 中派生出一些狀態

定義conststate={token:'我是token'}constgetter={tokenName(state,getters)=>{returnstate.token+'的getter '//在token后面加數據}}conststore=newVuex.Store({state,? ? getter})直接使用this.$store.getter.tokenName=='我是token的getter'引入使用import{mapGetters}from'vuex'computed:{...mapGetters(['tokenName'])//不取別名 this.tokenName == '我是token的getter'...mapGetters({userTokenName:'tokenName'//取別名,this.userTokenName =='我是token的getter'})},

mutation 唯一store修改方法

更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation,mutation 必須同步執行

定義conststate={token:'我是token'}constmutation={setToken(state,val){state.token=val}}conststore=new Vuex.Store({state,mutation})直接使用this.$store.commit('setToken','白日夢')// token ==? '白日夢'引入使用import{mapMutations}from'vuex'methods:{...mapMutations(['setToken'])//不取別名 this.setToken('白日夢')? =>? token ==? '白日夢'...mapMutations({setName:'setToken'//取別名, this.setName('白日夢')? =>? token ==? '白日夢'})},

Action 提交的是 mutation

Action 提交的是 mutation,而不是直接變更狀態。

Action 可以包含任意異步操作。

定義conststate={token:'我是token'}constmutation={setToken(state,val){state.token=val}}constAction={ActionToken({commit},val){commit('setToken',val)}}conststore=new Vuex.Store({state,mutation,Action})直接使用this.$store.dispatch('ActionToken','白日夢')// token ==? '白日夢'引入使用import{mapActions}from'vuex'methods:{...mapActions(['ActionToken'])//不取別名 this.setToken('白日夢')? =>? token ==? '白日夢'...mapActions({setName:'ActionToken'//取別名, this.setName('白日夢')? =>? token ==? '白日夢'})},

modules 模塊化

每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊

當? namespaced: false? ? 默認值為false? ? ,除了state取值需要添加模塊名稱,別的不需要添加

constuser={namespaced:false,? state:()=>({name:'小青'}),getters:{names(state){returnstate.name+'青'}},mutations:{setName(state,val){state.name=val}},actions:{setAcName({commit},val){commit('setName',val)}},}conststore=new Vuex.Store({modules:{user}})使用this.$store.state.user.name=='小青'this.$store.getters.names=='小青青'this.$store.commit('setName','小周')//? ? name == '小周'this.$store.dispatch('setAcName','小周')//? ? name == '小周'

當? namespaced: true ,它的所有 getter、action 及 mutation 都會自動根據模塊注冊的路徑調整命名。例如:

constuser={namespaced:true,? state:()=>({name:'小青'}),getters:{names(state){returnstate.name+'青'}},mutations:{setName(state,val){state.name=val}},actions:{setAcName({commit},val){commit('setName',val)}},}conststore=new Vuex.Store({modules:{user}})使用this.$store.state.user.name=='小青'this.$store.getters['user/names']=='小青青'this.$store.commit('user/setName','小周')//? ? name == '小周'this.$store.dispatch('user/setAcName','小周')//? ? name == '小周'

Vuex 并不限制你的代碼結構。但是,它規定了一些需要遵守的規則:

應用層級的狀態應該集中到單個 store 對象中。

提交 mutation 是更改狀態的唯一方法,并且這個過程是同步的。

異步邏輯都應該封裝到 action 里面。

只要你遵守以上規則,如何組織代碼隨你便

#axios和ajax

? 本文主要針對Ajax,Promise,Axios三者的本質、優缺點,使用實戰做了闡述,抽象了應用辦法,高度橫向做了對比,一起進入學習吧~

一、Ajax

AJAX:異步 JavaScript 和 XML,用來發送異步請求。有了Ajax之后,在無需重新加載整個頁面的情況下,可以與服務器交換數據并更新部分網頁內容。

Ajax是基于現有的Internet標準,聯合使用了XMLHttpRequest,JS/DOM,CSS,XML等技術。

它有個較大的缺陷就是業務邏輯需要在回調函數中執行,如果多個請求存在依賴關系,就會產生回調地獄問題,對讀寫理解困難。

1. 創建XHR

? 由于IE7以下版本不支持XMLHttpRequest對象,使用 ActiveXObject,因此需要做兼容處理:

letrequest;// code for IE7+, Firefox, Chrome, Opera, Safariif(window.XMLHttpRequest){request=newXMLHttpRequest();}else{// code for IE6, IE5request=newActiveXObject("Microsoft.XMLHTTP");// 新建Microsoft.XMLHTTP對象}

2. XHR對象常用方法

(1)XMLHttpRequest.open()

初始化一個請求。已激活的請求再次調用此方法時,相當于調用了abort(),會中斷上一個請求。

當設置了async(第三個參數)true之后,請規定onreadystatechange事件中的就緒狀態執行響應函數。

(2)XMLHttpRequest.send()

向服務器發送http請求。如果open()中定義的是異步請求,則此方法會在請求發起后立刻返回;如果是同步,則在請求返回后才執行。

(3)XMLHttpReqeust.abort()

當請求已經發出,則立刻中斷請求。將readystate設置為0,立刻調用onreadystatechange方法執行回調函數。

(4)XMLHttpRequest.setReqeustHeader()

用于給HTTP請求增加自定義請求頭,在open()之后,send()之前定義。設置多個相同的請求頭,在發起時會進行合并。

(5)XMLHttpRequest.getResponseHeader()

根據名稱獲取HTTP請求頭,如果需要一次請獲取全部,請使用getAllHeetResponseHeader()方法。

3. 封裝一個原生的Ajax請求

? 結合以上的內容,封裝一個兼容Post和Get的Ajax請求如下:

ajaxRequest(method,url,data,callback){letxhr;if(window.XMLHttpRequest){xhr=newXMLHttpRequest();}else{xhr=newActiveXObject("Microsoft.XMLHTTP");}if(method.toLocalCase()==="get"){url=url+this.getParams(data);xhr.open(method,url,true);xhr.send();}else{// postxhr.open(method,url,true);xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");data?xhr.send(data):xhr.send()}xhr.onreadystatechange=function(){if(xhr.readyState===4){if(xhr.state===200){// 約定返回字符串格式,如是xml格式使用responseXMLcallback(xhr.responseText);}else{reject(newError(xhr.statusText))}else{if(xhr.readyStatue===0){alert("請求已取消");}}}}buildGetParams(obj){if(!obj||obj.keys.length===0)return'';letstr=Object.keys(obj).reduce(function(prev,cur,index){if(index===1){prev=prev+'='+obj[prev]}returnprev+(prev?'&':'')+cur.toString()+'='+obj[cur]})}

? 模擬使用post調用

this.ajaxRequest('post','http://demo-domain:8080/test/ajax_post',{name:'test'},function(data){letresult=JSON.parse(data)console.log(result)});

二、Promise

? Promise對象由ES6提供,它表示一個尚未完成且預計在未來完成的異步操作。Promise 僅僅是一種決定異步任務狀態確定時會出現什么結果的技術。它本身并不提供異步請求功能,更不能替代Ajax。

Promise將異步請求的回調操作,改成了同步的鏈式寫法,最直觀的就是使用Promise處理ajax請求時,可以解決回調地獄問題;另外,Promise封裝了統一的接口,使得控制異步操作變得更加簡單。

但它的缺點也比較明顯,發起的Promise無法取消中斷,其次,如果不設捕獲異常的回調函數,Promise內部拋出的錯誤無法反應到外部出來。另外,未執行完成的Promise也無法確定是剛開始還是即將結束(即ajax的readyState在哪個過程)。

1. 創建Promise

Promise自身提供了封裝好的Promise()構造器,使用new關鍵字創建。我們可以自定義一個異步函數,傳入Promise的構造器如:Promise(asyncFn)

letpromise=newPromise((resolve,reject)=>{// 執行結束需要使用resolve或reject將結果返回})

2. Promise的基本API

Promise有三種狀態,分別是pending:執行狀態、fulfilled:已成功、rejected:已失敗。

pending只能改變為fulfilled或rejected其中一種,且狀態一旦改變,將凝固不再改變。即使再次修改,該操作也會被忽略。

(1)Promise.prototype.then(onFulfilled, onRejected)

當Promise的狀態凝固后,就會調用.then方法。該方法接受兩個回調函數,即成功回調函數resolve(),和失敗回調函數reject(),并且返回一個Promise。通常情況下,我們通過一定的條件判斷如res.code為1表示成功,將結果作為參數即resolve(res.data)傳遞出去。否則使用reject(res.message)返回錯誤。

當然,.then()的第二個參數非必選,你也可以通過.catch()API來實現

(2)Promsie.prototype.catch(onRejected)

該API表示Promise從pending狀態改變為rejected狀態時調用,接受一個失敗回調函數,返回一個Promise,使用效果同.then()的第二個參數。

(3)Promise.prototype.all()

該方法接受一個數組,用于將多個Promise包裝成一個Promise來執行,只有所有的請求狀態都凝固后才改變自己的狀態。并且只有在全部請求都返回fulfilled,該函數才返回fulfilled

(4)Promise.prototype.race()

竟速模式,該方法接受一個可遍歷對象,將多個Promise包轉成一個Promise來執行,最先成功(fulfilled)返回的請求當作該Promise的響應返回

......

3. 一種建議的Promise鏈式寫法

雖然.then()方法很強大,但并不建議在其中定義處理異常方法,原因是如果在.then()的onFulfilled回調函數中發生了異常,其內部定義的onRejected是無法捕獲到的。但是在.then()后面使用.catch()則可以捕獲到之前發生的所有異常。一種健康的寫法是:

functiontaskA(){...};functiontaskB(){...};functionfinalTask(){...};varpromise=newPromise();promise.then(taskA).catch(onRejectedA).then(taskB).catch(onRejectedB).then(finalTask)

】不要使用promise.then(taskA).then(taskB).then(finalTask).catch(onRejected),雖然這么定義所有的異常也都會被捕獲到,但如果是taskA發生了異常,那taskB,fianlTask也將不被執行。

4. 使用Promise寫法實現Ajax請求

? 為了簡便以下只以Get請求為例,

getRequest(url){returnnewPromise((resolve,reject)=>{letxhr=newXMLHttpRequest();xhr.open('get',url,true);xhr.send();xhr.onreadystatechange(){if(xhr.readyState===4){if(xhr.status===200){resolve(JSON.parse(xhr.responseText));}else{reject(newError(xhr.statusText))}}}})}// 調用this.getRequest("http://demo-domain:8080?name=test").then(resp=>{console.log(resp)})

三、Axios

Axios是一個基于Promise的http庫,支持node端和瀏覽器端,支持攔截器、自定義請求頭、取消請求等高級配置,支持自動轉換JSON,也支持npm包的使用。總之,優點多多,用就對了...

針對Axios,在本文筆者不想寫簡單通用的功能,如有需要可以去axios官網學習了解。筆者想闡述更高效的用法如下

1. 自定義axios實例

? 使用自定義配置新建一個簡單的axios實例

letrequest=axios.create({baseURL:'http://some-domain.com/api/',timeout:1000,headers:{'X-Custom-Header':'footbar'}})

2. 常用axios的請求配置

以下配置中,只有url是必須的,如果沒有配置method,將默認使用get方法。以下為常用的配置,更多請參見官網。

url: '/user'用戶請求服務器資源的 URL

method: 'post'創建請求時使用的方法

baseURL: 'http://demo-domain:8080/api/'自動加在URL(非絕對路徑時)前的路徑

headers: { 'x-Requested-With': 'XMLHttpRequest' }自定義請求頭

params: { 'ID': '12345' }與請求一起發送的URL參數,當method指定為GET時使用

data: { 'name': 'zhangfs' }請求主體參數,用于PUT,POST,PATCH方法

timeout: 8000請求超時時間,0表示無超時;超過時間請求被中斷

withCredentials: false請求跨域時是否需要使用憑證

auth: { username: 'zhangfs' }用于HTTP基礎驗證,將復寫Authorization頭

proxy: {

host: '127.0.0.1',

port: 9000,

}代理服務器的主機名與端口,將設置Proxy-Authoriation

cancelToken: new CancelToken(function(cancel) { ... })指定取消請求的cancel token

全局默認配置

使用axios.defaults.xxx來配置;注意在實例中配置的優先級高于默認配置優先級

letrequest=newaxios({baseURL:'http://demo-domain.com:8080/api'})request.defaults.header.common['Authorization']=AUTH_TOKENrequest.defaults.header.post['Content-Type']='application/x-www-form-urlencoded'

3. 請求攔截器

在請求或響應被 then 或 catch 處理前攔截它們。如果自定義了axios實例如request,則使用request.interceptors.xxx

// 添加請求攔截器axios.interceptors.request.use(function(config){// 在發送請求之前做些什么returnconfig;},function(error){// 對請求錯誤做些什么returnPromise.reject(error);});// 添加響應攔截器axios.interceptors.response.use(function(response){// 對響應數據做點什么returnresponse;},function(error){// 對響應錯誤做點什么returnPromise.reject(error);});

當然,也可以對攔截器進行移除,此時需要對攔截器顯命名

varmyInterceptor=axios.interceptors.request.use(function(){/*...*/});axios.interceptors.request.eject(myInterceptor);

4. 取消請求

ajax調用請求的abort()通過改變readyState=0來進行中斷請求,axios則通過cancel token取消。

Axios 的 cancel token API 基于cancelable promises proposal,它還處于第一階段。

可以使用CancelToken.source工廠方法創建 cancel token

varCancelToken=axios.CancelToken;varsource=CancelToken.source();axios.get('/user/12345',{cancelToken:source.token}).catch(function(thrown){if(axios.isCancel(thrown)){console.log('Request canceled',thrown.message);}else{// 處理錯誤}});// 取消請求(message 參數是可選的)source.cancel('Operation canceled by the user.');

? 還可以通過傳遞一個 executor 函數到 CancelToken 的構造函數來創建 cancel token

varCancelToken=axios.CancelToken;varcancel;axios.get('/user/12345',{cancelToken:newCancelToken(functionexecutor(c){// executor 函數接收一個 cancel 函數作為參數cancel=c;})});// 取消請求cancel();

值得注意的是,可以使用同一個cancel token取消多個請求。

5. 一個axios實戰實例

? 請求封裝文件service.js

constservice=axios.create({headers:{},baseURL:Vue.prototype.configer.baseURL,timeout:8000,withCredentials:true})service.interceptors.request.use(config=>{if(config.method==='get'){if(config.url.indexOf('?')<0){config.url+='?r='+newDate().getTime()}else{config.url+='&r='+newDate().getTime()}}returnconfig},error=>{returnPromise.reject(error)})service.interceptors.response.use(response=>{// 處理返回體constresult=response.dataconstcode=Number(result.code)/**

? ? * Desc: 接口請求業務異常統一處理

? ? * Desc: 如:50008: 非法Token; 50012: 第三端登錄; 50014: Token已過期;

? ? */if(code===3002||code===50008||code===50012||code===50014||code===302){alert('您已退出登錄')}else{if(result.code&&result.code!=='200'){console.log(result.message)}returnresult}},errorFn// HTTP Code異常統一處理)

? api封裝文件 api.js

importservicefrom'@/plugins/service'importaxiosfrom'axios'constCancelToken=axios.CancelTokenexportfunctionqueryDemoApi(data,_this){returnrequest({url:'/packageRoute/queryDemoApi',method:'post',data,cancelToken:newCancelToken(functionexecutor(c){_this.cancelAjax=c})})}

? 組件內使用 info.vue

data(){return{cancelRequest:null}}method:{pageTriggleSearch(username){if(typeofthis.cancelRequest==='function'){this.cancelRequest()}this.queryInfoApi({name:username},this).then((data)=>{this.cancelRequest=null// do something else})}}

#vue-router

SPA

SPA單頁面應用程序:只有一個頁面(index.html),使用vue-router實現組件的創建與銷毀,在視覺上給人一種“頁面切換”的錯覺。所以,對SPA應用來講,vue-router是核心。

思想:在SPA應用程序中,一切皆組件,所有的組件都直接或間接地與“路由系統”有關。

路由規則是如何配置的?

創建router實例,在main.js掛載

什么是“嵌套路由”?在“路由規則”如何配置“嵌套路由”?

children叫做“嵌套路由”

自定義路由規則,如何理解路由規則?

所謂的路由規則,當你訪問某個url時,路由系統就加載指定的組件進行顯示

兩個全局組件

<router-link>

常用屬性to/tag/active-class/exact-active-class等

表示目標路由的鏈接。當被點擊后,內部會立刻把?to?的值傳到?router.push(),所以這個值可以是一個字符串或者是描述目標位置的對象。

詳細請見官網

<router-view>

<router-view>?組件是一個 functional 組件,渲染路徑匹配到的視圖組件。<router-view>?渲染的組件還可以內嵌自己的?<router-view>,根據嵌套路徑,渲染嵌套組件。

其他屬性 (非 router-view 使用的屬性) 都直接傳給渲染的組件, 很多時候,每個路由的數據都是包含在路由參數中。

因為它也是個組件,所以可以配合?<transition>?和?<keep-alive>?使用。如果兩個結合一起用,要確保在內層使用?<keep-alive>

<transition>

????????<keep-alive>

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

????????</keep-alive>

</transition>

兩個內置API

$route描述是當前url的信息(它是一個響應式變量,可以參與計算屬性,還可以使用watch監聽)

$router是路由API,常用方法有 push()入棧、replace()替換入棧、back()出棧。

this.router.push(′/hot?list=′+c.cate)改變url,接著route變化

兩種路由模式

Hash模式

url上有個#,背后是監聽onhashchange事件來實現的;hash值發生變化,客戶端不會向服務器發起頁面請求;hash模式的SPA應用部署到服務器上時不會出404問題

History模式

url上沒有#,背后是使用history api來實現的;當url路徑發生變化時,客戶端會向服務器發起頁面請求;history模式的SPA應用部署到服務器上時會出現404,怎么辦?(在服務器使用Nginx做重定向處理)

兩種路由跳轉

聲明式路由跳轉,使用實現頁面跳轉,一般用于菜單上

編程式路由跳轉,使用this.$router這個api實現頁面跳轉,一般用于js邏輯中

兩種命名

命名路由

命名路由,就是給“路由規則”添加一個名字

在聲明式導航中使用“命名路由”,<router-link :to='{name:'vip'}'>

在編程式導航中使用“命名路由”,this.push({name:'vip'})

命名視圖

命名視圖,給添加一個名字。那么路由規則這樣配置{path,components:{xxx:'組件'}}。

兩種路由傳參

動態路由傳參

“動態路由”一般用于從列表頁跳轉到詳情頁,“路由規則”配置一般這種風格{path:'/detail/:id'}。那么在詳情頁我們可以使用this.$route.params來接收這個“動態的路由參數”。還在在“路由規則”上開啟props傳參{path:'/detail/:id', props:true},在詳情頁使用props接收這個“動態的路由參數”。

Query傳參

在路由跳轉時使用?a=1&b=2這種查詢字符串,在另一個頁面中使用this.$route.query來接收

兩個優化

重定向

給未定義的url添加默認跳轉,跳轉到指定的路由規則上,像這樣配置{path:'/*',redirect:'/404'}。這是一種用戶體驗層面的優化。

一般放在路由規則的最后一條,并且重定向到一個已定義過的規則

路由懶加載

當頁面太多時,我們要根據url訪問需求按需加載組件。背后技術原理是Vue異步組件和Webpack代碼分割技術。擴展:這種()=>import()代碼是異步的,并且在webpack打包時只要遇到這種語法,會默認將其分割一個獨立的小的.js文件。

兩個高級技巧

導航守衛

我們知道每次發生url變化時,路由系統要根據“路由規則”去尋找對應的組件進行顯示。vue-router把這個匹配的過程抽象成幾個重要的“路由鉤子”——beforeEach()/beforeResolve()/afterEach()。導航守衛經常用于登錄攔截、權限設計等。

路由元信息

一種方便我們給“路由規則”添加自定義屬性的方式,并且可以在$route上進行訪問。在“路由規則”這樣配置 {path,component,meta:{}}。通常路由元信息可以配置導航守衛實現權限設計

其它路由小技巧

路由別名

{path:'/home', alias:'/h' },路由別名的作用是給復雜的path取一個方便記憶的“小名”

過渡行為

在外面包裹一個動畫,讓頁面切換時更加溫和

數據獲取

使用watch監聽route的變化,當route變化成功后調接口

滾動行為

使用 new VueRouter({scrollBehavior}) 這個選項精確地控制頁面的滾動位置

#proxy/reflect


Proxy 與 Reflect 是 ES6 為了操作對象引入的 API 。

Proxy 可以對目標對象的讀取、函數調用等操作進行攔截,然后進行操作處理。

Reflect 可以用于獲取目標對象的行為,它與 Object 類似,但是更易讀,更優雅。它的方法與 Proxy 是對應的。

基本用法

var proxy = new Proxy(target, handler)

target 即目標對象, handler 是一個對象,聲明了代理 target 的指定行為。

lettarget={name:'ZhangSan',age:28}lethandler={get:function(target,key){console.log('get: '+key);returntarget[key];// 不是target.key},set:function(target,key,value){console.log('set: '+key);target[key]=value;}}letproxy=newProxy(target,handler)proxy.name// 實際執行 handler.getproxy.age=25// 實際執行 handler.set

常用方法

// 在判斷代理對象是否擁有某個屬性handler.has()// 在讀取代理對象的某個屬性時觸發handler.get()// 在給代理對象的某個屬性賦值時觸發。handler.set()// 在刪除代理對象的某個屬性時觸發該操作handler.deleteProperty()// 在獲取代理對象的所有屬性鍵時觸發handler.ownKeys()// 在調用一個目標對象為函數的代理對象時觸發handler.apply()

實際運用

私有屬性保護

letproxy=newProxy({},{get(target,key,receiver){if(key.startsWith('_')){thrownewErro(`Invalid attempt to get private? ? "${key}"`);}console.log('Get:'+key);returntarget[key];}});letobj=Object.create(proxy);obj.name// Get: name

參數校驗

letvalidator={set:function(obj,prop,value){if(prop==='age'){if(!Number.isInteger(value)){thrownewTypeError('The age is not an integer');}if(value>200){thrownewRangeError('The age seems invalid');}}obj[prop]=value;}};letproxy=newProxy({},validator)proxy.age='oppps'// 報錯

Reflect

ES6 中將 Object 的一些明顯屬于語言內部的方法移植到了 Reflect 對象上

Reflect 對象對某些方法的返回結果進行了修改,使其更合理。

Reflect 對象使用函數的方式實現了 Object 的命令式操作。

方法

Reflect.get(target,name,receiver)Reflect.set(target,name,value,receiver)Reflect.apply(target,thisArg,args)Reflect.construct(target,args[,newTarget])Reflect.defineProperty(target,name,desc)Reflect.deleteProperty(target,name)Reflect.has(target,name)Reflect.ownKeys(target)Reflect.preventExtensions(target)Reflect.isExtensible(target)Reflect.getOwnPropertyDescriptor(target,name)Reflect.getPrototypeOf(target)Reflect.setPrototypeOf(target,prototype)

eg:

letexam={name:"Tom",age:24,getinfo(){returnthis.name+this.age;}}Reflect.get(exam,'name');// "Tom"http:// 當 target 對象中存在 name 屬性的 getter 方法, getter 方法的 this 會綁定 // receiverletreceiver={name:"Jerry",age:20}Reflect.get(exam,'info',receiver);// Jerry20

與proxy組合使用

Reflect 對象的方法與 Proxy 對象的方法是一一對應的。所以 Proxy 對象的方法可以通過調用 Reflect 對象的方法獲取默認行為,然后進行額外操作。

letexam={name:"Tom",age:24}lethandler={get:function(target,key){console.log("getting "+key);returnReflect.get(target,key);},set:function(target,key,value){console.log("setting "+key+" to "+value)Reflect.set(target,key,value);}}letproxy=newProxy(exam,handler)proxy.name="Jerry"proxy.name// setting name to Jerry// getting name// "Jerry"

運用

實現觀察者模式

// 定義 Set 集合constqueuedObservers=newSet();// 把觀察者函數都放入 Set 集合中constobserve=fn=>queuedObservers.add(fn);// observable 返回原始對象的代理,攔截賦值操作constobservable=obj=>newProxy(obj,{set});functionset(target,key,value,receiver){// 獲取對象的賦值操作constresult=Reflect.set(target,key,value,receiver);// 執行所有觀察者queuedObservers.forEach(observer=>observer());// 執行賦值操作returnresult;}

// 原對象letobj={name:'steve',age:20}// data 是我們要攔截的 原對象// dataProxy 是我們新生成的 攔截對象letdataProxy=newProxy(obj,{get(target,key,receiver){// target 就是我們的原對象,objconsole.log("target >>> ",target===obj);// key 就是操作的健console.log("key >>> ",key);// receiver 就是 dataProxy,攔截對象console.log("receiver >>> ",receiver);console.log(`當前值為:${target[key]}`);returntarget[key];},});// 使用攔截器dataProxy.name;// 打印結果:// -> 當前值為:steve// -> "steve"

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