Vue路由的實現(xiàn)原理
路由這個概念最初是由后端提出來的,在我們沒有SPA單頁面應(yīng)用之前杨伙,使用的一直都是后端路由淘衙,根據(jù)不同的路由返回不同的頁面,后來隨著單頁面應(yīng)用的誕生剔桨,開始有了前端路由屉更,實現(xiàn)不刷新但是更新頁面的效果
vue-router是專為Vue打造的路由管理工具
vue-router提供三種路由模式
-
hash模式
- 默認(rèn)模式,通過路徑中的hash值來控制路由跳轉(zhuǎn)洒缀,不存在兼容問題
-
history模式
- H5新增的 history API瑰谜,相對hash而言,不會顯示#號树绩,但是需要服務(wù)器端配置
-
abstract模式
- 支持javascript的所有運行環(huán)境萨脑,常指Node.js服務(wù)器環(huán)境
hash模式實現(xiàn)原理
示例:http://www.haochenguang.cn/#/home
在正常路徑后跟一個 # 號,匹配 # 后邊的路徑為前端路由饺饭,通過window.onhashchange方法來操控路由改變的時候切換內(nèi)容
-
onhashchange
方法的觸發(fā)時機- 直接更改瀏覽器地址渤早,在最后面增加或改變#hash;
- 通過改變location.href或location.hash的值瘫俊;
- 通過觸發(fā)點擊帶錨點的鏈接蛛芥;
- 瀏覽器前進后退可能導(dǎo)致hash的變化,前提是兩個網(wǎng)頁地址中的hash值不同军援。
-
示例代碼
<body> <ul> <li> <a href="#/home">home</a> </li> <li> <a href="#/list">list</a> </li> <li> <a href="#/detail">detail</a> </li> </ul> </body> <script> window.onhashchange = function() { // 做頁面切換渲染等 console.log(location.href); console.log(location.hash); } </script>
- 通過點擊a標(biāo)簽仅淑,傳遞一個hash值,然后通過
window.onhashchange
方法來監(jiān)聽hash的變化胸哥,然后在這個事件觸發(fā)的時候涯竟,根據(jù)location.hash
來動態(tài)的修改單頁面應(yīng)用的內(nèi)容即可
history模式實現(xiàn)原理
示例:http://www.haochenguang.cn/home
看起來與后端路由沒有任何區(qū)別,在window.history
這個對象中,包含瀏覽器的歷史庐船,而在HTML5中银酬,新增了pushState
和replaceState
,通過這兩個API可以改變url地址且不會發(fā)送請求筐钟,同時還有popstate
事件揩瞪,實現(xiàn)原理與hash相似,只不過因為沒有了 # 號篓冲,所以刷新頁面還是會向服務(wù)器發(fā)送請求李破,而后端沒有對應(yīng)的處理措施的話,會返回404壹将,所以需要后端配合
-
popstate
方法的觸發(fā)時機- 僅僅調(diào)用pushState方法或replaceState方法 嗤攻,并不會觸發(fā)該事件;
- 只有用戶點擊瀏覽器倒退按鈕和前進按鈕诽俯,或者使用JavaScript調(diào)用back妇菱、forward、go方法時才會觸發(fā)暴区。
- 另外闯团,該事件只針對同一個文檔,如果瀏覽歷史的切換仙粱,導(dǎo)致加載不同的文檔偷俭,該事件也不會觸發(fā)。
-
示例代碼
-
<body> <ul> <li> <a href="/home">home</a> </li> <li> <a href="/list">list</a> </li> <li> <a href="/detail">detail</a> </li> </ul> </body> <script> document.querySelectorAll('a').forEach(item => { item.onclick = function (e) { e.preventDefault(); let link = item.getAttribute('href'); window.history.pushState({link}, link, link); }; }); window.addEventListener('popstate', function (e) { console.log(e.state); console.log(location.href); }) </script>
- 當(dāng)點擊瀏覽器的后退或前進按鈕缰盏,或者調(diào)用history上的go、back方法時淹遵,就會觸發(fā)事件口猜,打印出對應(yīng)的數(shù)據(jù),e.state 中就存放著pushState方法中的state對象
- 通過在修改路由信息的同時執(zhí)行切換DOM的操作透揣,來實現(xiàn)了路由切換
-
abstract模式實現(xiàn)原理
- abstract模式是不依賴于瀏覽器環(huán)境济炎,所以沒有使用hash或者history等瀏覽器才會提供的API,而是VueRouter內(nèi)部使用數(shù)組進行模擬了路由管理辐真,在node環(huán)境须尚,或者原生App環(huán)境下,都會默認(rèn)使用abstract模式侍咱,VueRouter內(nèi)部會根據(jù)所處的環(huán)境自行判斷耐床,默認(rèn)使用hash模式,如果檢測到?jīng)]有瀏覽器API的時候楔脯,就會使用abstract模式撩轰。
SPA 路由history模式上線后刷新404
- 出現(xiàn)問題:在使用history模式的時候,由于瀏覽器路徑與后端路徑類似,所以當(dāng)刷新頁面的時候堪嫂,瀏覽器還是會向服務(wù)器發(fā)送請求偎箫,但是由于服務(wù)器并沒有對應(yīng)的路由頁面,所以會導(dǎo)致404空白
- 解決方案:在服務(wù)端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態(tài)資源皆串,則應(yīng)該返回同一個 index.html 頁面淹办,這個頁面就是你 app 依賴的頁面。同時這么做以后恶复,服務(wù)器就不再返回 404 錯誤頁面怜森,因為對于所有路徑都會返回 index.html 文件。為了避免這種情況寂玲,在 Vue 應(yīng)用里面覆蓋所有的路由情況塔插,然后在給出一個 404 頁面⊥赜矗或者想许,如果是用 Node.js 作后臺,可以使用服務(wù)端的路由來匹配 URL断序,當(dāng)沒有匹配到路由的時候返回 404流纹,從而實現(xiàn)后退。
-
node服務(wù)器
- 簡單粗暴的一種方法违诗,如果沒有讀取到靜態(tài)資源就直接返回html頁面漱凝,可以用于前端調(diào)試
const http = require('http'); const fs = require('fs'); http.createServer((req,res) => { fs.readFile(__dirname + req.url, (err, data) => { if(!err) { res.end(data); }else { fs.readFile('index.html', (e, html) => { if(!e) { res.end(html); } }) } }) }).listen(8080, err => { !err&&(console.log('http://localhost:8080') })
-
nginx靜態(tài)服務(wù)器
- 常用方式,直接使用80端口
-
server { listen 80; server_name localhost; location / { root html; index index.html; try_files $uri $uri/ /index.html; } }
- 在nginx安裝目錄下诸迟,將打包后的文件放置在安裝目錄下的 html 目錄中茸炒,接著打開conf目錄中的
nginx.conf
文件,修改server部分如上阵苇,然后在nginx根目錄使用nginx -t
測試配置文件修改后的語法是否正確(如果有問題會報錯)壁公,如果沒有錯誤的話,使用命令nginx -s reload
命令重啟讓配置文件生效绅项,打開瀏覽器紊册,輸入localhost
即可看到項目部署完成
- 在nginx安裝目錄下诸迟,將打包后的文件放置在安裝目錄下的 html 目錄中茸炒,接著打開conf目錄中的
- 常用方式,同時運行多個程序時快耿,80端口被占用時
-
server { listen 8000; server_name localhost; location / { root html/project; index index.html; try_files $uri $uri/ /index.html; } }
-
在html目錄下新建文件夾囊陡,例如
project
,將打包后的文件放置在project
文件夾下掀亥,在conf/nginx.conf
文件中撞反,原80端口的下方新加入上述代碼,接著重復(fù)nginx -t
與nginx -s reload
再次打開瀏覽器搪花,輸入localhost:8000
就可以看到項目部署完成
-
在html目錄下新建文件夾囊陡,例如
-
node服務(wù)器
$route和$router的區(qū)別
-
$route是路由信息對象痢畜,包括path垛膝、params、hash丁稀、query吼拥、fullPath、meta线衫、name等路由信息參數(shù)凿可,可以通過$route來獲取當(dāng)前路由的各種參數(shù)
-
path
String
保存當(dāng)前絕對路徑信息 -
params
Object
保存當(dāng)前的params參數(shù)對象 -
hash
String
保存當(dāng)前的hash信息,不帶#號授账,如果沒有hash為空字符串 -
query
Object
保存當(dāng)前的query參數(shù)對象 -
meta
Object
保存當(dāng)前路由的源信息 -
fullPath
String
保存完成解析后的路徑信息枯跑,包含hash、query等參數(shù) -
name
String
保存路由name值
-
path
-
$router是路由實例對象白热,包含的是VueRouter實例上的方法以及配置屬性敛助,常用于編程式導(dǎo)航;
-
push
Function(Object|String)
this.$router.push('/home?tab=all')
this.$router.push({path: '/home', query:{tab:'all'}})
-
replace
Function(Object|String)
this.$router.replace('/home?tab=all')
this.$router.replace({path: '/home', query:{tab:'all'}})
-
go
Function(Number)
-
this.$router.go(-1)
操作history的API進行后退 -
this.$router.go(1)
操作history的API進行前進
-
-
······
-
push
自定義過濾器詳解
在Vue1.x的版本中屋确,是有內(nèi)置過濾器的纳击,而到了2.0以后,廢除了內(nèi)置過濾器攻臀,允許我們自定義過濾器
-
自定義過濾器的使用方法
- 在插值表達式
{{}}
以及v-bind:
指令綁定的屬性的冒號內(nèi)焕数,可以使用自定義過濾器 - 使用時,在js表達式的結(jié)尾使用管道符來指示過濾器
- 示例:
{{date| formatDate}}
或者<input v-bind:value="date | formatDate" />
- 在插值表達式
-
自定義過濾器的使用場景
- 在我們前端拿到后臺的數(shù)據(jù)時刨啸,經(jīng)常有些數(shù)據(jù)是不能直接展示出來的堡赔,需要經(jīng)過處理之后再展示,在Vue中设联,我們可以通過methods來做善已,也可以通過computed計算屬性來做,但是最好還是通過過濾器filters來做离例,因為它是在原數(shù)據(jù)基礎(chǔ)上進行過濾换团,不會修改原有數(shù)據(jù),使用computed以及methods屬于大材小用了
-
過濾器的定義
- 在Vue中分為全局過濾器和局部過濾器
// 全局過濾器 Vue.filter(過濾器名稱, function(value) { return value; // 處理value粘招,vlaue就是需要過濾的數(shù)據(jù) }) // 局部過濾器 new Vue({ el: '#app', filters: { '過濾器名稱'(value) { return value; // 處理value,vlaue就是需要過濾的數(shù)據(jù) } } })
-
實現(xiàn)自定義過濾器
- 無參數(shù)過濾器
Vue.filter('formatDate', function(value) { if(!value) return; return new Date(value).toLocaleString(); }) // html <div>{{ 1562731217574 | formatDate }}</div> // 2019/7/10 下午12:00:17
- 有參數(shù)過濾器
Vue.filter('formatPrice', function(value, num) { if(!value) return; return value.toFixed(num); }) // html <div>{{ 15.6264 | formatPrice(2)}}</div> // 15.63
- 多個參數(shù)的過濾器
Vue.filter('formatText', function(value, start, end) { if(!value) return; return start + '-' + value + '-' + end; }) // html <div>{{ '中間' | formatText('開始','結(jié)束')}}</div> // 開始-中間-結(jié)束
- 過濾器串聯(lián)偎球,可以多個串聯(lián)
<div>{{ '中間' | formatText('開始','結(jié)束') | formatText('串聯(lián)start','串聯(lián)end') }}</div>
- 無參數(shù)過濾器
自定義指令詳解
assets和static/public的區(qū)別
在vue-cli工具中洒扎,有兩個存放靜態(tài)資源的目錄,一個是src目錄下的assets衰絮,另一個在vue-cli2下是static目錄袍冷,在vue-cli3下是public目錄,那這兩個目錄的區(qū)別是什么呢猫牡?
首先需要知道:在*.vue組件中胡诗,所有的templates和css都會被vue-html-loader 和 css-loader解析,尋找資源的URL。
例如: <img src="./assets/logo.png">
或者 background: url('./assets/logo.png')
煌恢,這兩個url路徑都會被webpack解析成資源模塊依賴骇陈,而由于這些靜態(tài)文件大都不是js文件,而vue-cli工具利用各種loader幫助我們解析了這些資源
資源處理規(guī)則
- 相對路徑,
./assets/logo.png
將會被解析成一個模塊依賴瑰抵。 - 沒有前綴的路徑,
assets/logo.png
將會被看成./assets/logo.png
進行解析模塊依賴 - 前綴帶~的URL 會被當(dāng)成模塊請求, 例如:
<img src="~assets/logo.png">
- 相對根目錄的URL
/assets/logo.png
是不會被處理的
javascript中使用靜態(tài)資源
- 例如:
imgUrl: require('./assets/logo.png')
assets和static/public的區(qū)別
- 在assets目錄下的靜態(tài)資源會被webpack解析成資源模塊依賴你雌,在項目打包的時候會隨著項目一起打包
- 而在static/public目錄下的靜態(tài)資源不會被解析,是真正的靜態(tài)資源
本文 CSDN 地址: 前端常見面試題(六)@郝晨光