前言
在 《一篇帶你用 VuePress + Github Pages 搭建部落格》中,我們使用 VuePress 搭建了一個部落格,最終的效果檢視:TypeScript 中文文件。
VuePress 會在每個標題左側都加一個錨點連結:
點選後連結變為:
http://ts.yayujs.com/learn-typescript/handbook/TheBasics.html#降級-downleveling
此時重新整理下頁面,會發現,頁面並不能正確的定位到錨點位置,而且開啟開發者工具,還會看到報錯:
那這個問題該怎麼解決呢?
錯誤定位
線上上由於程式碼都是壓縮混淆後的,不好排查,我們在本地執行專案,然後檢視報錯資訊:
可以看到錯誤是來自於 vuerepss-plugin-smooth-scroll
這個外掛中,我們點選檢視具體的原始碼:
可以看到,報錯是來自於 document.querySelector(to.hash)
這句,為什麼會在這裡報錯呢?
這是因為對於每一個標題,VuePress 都會生成這樣一個 DOM 結構:
可以看到h2
標籤的id
是以 hash 值命名的,如果我們的 hash 是純英文,並沒有什麼問題,但是現在我們的 hash 中有中文,由於中文被編碼,document.querySelector
報錯。
官方方案
這個問題這麼容易復現,且這麼明顯,我想肯定有人提了 issue,檢視 VuePress 的 issue,可以看到在 2020 年 10 月 3 號就有人提了 PR,並且 merge 了。
那為什麼還會報錯呢?
如果我們去掉我們目前在用的 reco
主題,我們就會發現,頁面並不會有報錯,只不過依然不能定位到錨點而已。
實際上,我們的報錯是來自於我們使用的 reco
主題使用的 vuerepss-plugin-smooth-scroll
外掛。
社群方案
那成吧,那搜尋看下其他人是否也遇到過這個問題吧,可以查到這樣一個方案:
新建一個 docs/.vuepress/theme/layouts/Layout.vue
檔案,程式碼寫入:
<script>
export default {
methods: {
scrollTo(selector) {
if (!selector || selector === '#') return
const el = document.querySelector(decodeURIComponent(selector))
if (el && el.offsetTop) {
window.scrollTo(0, el.offsetTop)
}
}
},
mounted() {
this.scrollTo(location.hash)
}
}
</script>
使用後,頁面是不報錯了,但會發現樣式全部丟失了,因為使用這種方式相當於新建了一個主題,vuePress 已經改用我們的自定義主題了。
不過 VuePress 提供了主題繼承的功能,新建一個 docs/.vuepress/theme/index.js
,程式碼寫入:
module.exports = {
extend: '@vuepress/theme-default'
}
樣式是恢復了一些,但是因為我們本來使用的是 reco 主題,現在改成了繼承 vuepress 預設主題,reco 主題帶來的一些功能就全丟失了,那我們能繼承 reco 主題嗎?
答案是不能,這在 VuePress 官方文件的「主題的繼承 」章節裡有講到:
主題繼承暫時不支援高階繼承,也就是說,一個派生主題無法被繼承。
所以為了解決這個方案,就要放棄 reco 主題,而放棄了 reco 主題,本來也不會有報錯了……
那我們換個方案吧。
自己動手
成吧成吧,我自己解決。
梳理下當下的問題,當訪問帶中文錨點的連結時:
- 頁面有報錯,而報錯來自於
reco
主題使用的vuepress-plugin-smooth-scroll
主題。 - 不能正常跳轉到錨點位置
頁面報錯這個問題最簡單粗暴的方式就是修改原始碼了,我們開啟 node_modules/vuepress-plugin-smooth-scroll/lib/enhanceApp.js
,直接修改:
const enhanceApp = ({ Vue, router }) => {
router.options.scrollBehavior = (to, from, savedPosition) => {
// ...
else if (to.hash) {
//...
// 加上 decodeURIComponent
const targetElement = document.querySelector(decodeURIComponent(to.hash));
//...
}
// ...
};
};
因為我們並不會提交原始碼,而是提交編譯後的檔案到伺服器上,所以改了依賴的原始碼也沒有什麼影響。
接下來是頁面載入完跳轉到錨點位置,之前我們在 《VuePress 部落格優化之新增資料統計功能》,在 enhanceApp.js
中監聽路由的改變,同樣我們可以監聽路由的 ready 事件,然後跳轉到直接的位置,我們修改下 .vuepress/enhanceApp.js
中的程式碼:
export default ({ router }) => {
// ...
router.onReady(() => {
const { hash } = document.location;
setTimeout(() => {
if (hash.length > 1) {
const id = decodeURIComponent(hash);
const el = document.querySelector(`.reco-side-${decodeURIComponent(id).substring(1)}`);
el.click();
}
}, 1000);
});
};
在這裡並沒有直接獲取標題 DOM 元素的位置,然後使用 window.scrollTo
跳轉,這是因為在 reco
主題下,隨著使用者不斷的瀏覽,其實路由也在不停的切換,右側的目錄才會隨之改變,這裡直接模擬了右側目錄的點選操作,也是為了將具體跳轉的行為交給 reco
來實現。
系列文章
部落格搭建系列是我至今寫的唯一一個偏實戰的系列教程,預計 20 篇左右,講解如何使用 VuePress 搭建、優化部落格,並部署到 GitHub、Gitee、私有伺服器等平臺。本篇為第 20 篇,全系列文章地址:https://github.com/mqyqingfeng/Blog
微信:「mqyqingfeng」,加冴羽好友,拉你進前端學習交流群。
如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。如果喜歡或者有所啟發,歡迎 star,對作者也是一種鼓勵。