Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VuePress 博客优化之中文锚点跳转问题 #259

Open
mqyqingfeng opened this issue Jan 26, 2022 · 4 comments
Open

VuePress 博客优化之中文锚点跳转问题 #259

mqyqingfeng opened this issue Jan 26, 2022 · 4 comments

Comments

@mqyqingfeng
Copy link
Owner

前言

《一篇带你用 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 主题,本来也不会有报错了……

那我们换个方案吧。

自己动手

成吧成吧,我自己解决。

梳理下当下的问题,当访问带中文锚点的链接时:

  1. 页面有报错,而报错来自于 reco主题使用的 vuepress-plugin-smooth-scroll主题。
  2. 不能正常跳转到锚点位置

页面报错这个问题最简单粗暴的方式就是修改源码了,我们打开 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 来实现。

系列文章

博客搭建系列,讲解如何使用 VuePress 搭建、优化博客,并部署到 GitHub、Gitee、私有服务器等平台。系列预计 20 篇左右,本篇为第 20 篇,全系列文章地址:https://github.com/mqyqingfeng/Blog

微信:「mqyqingfeng」,加冴羽好友,进一个神奇的前端学习交流群。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。

@AbsoluteZeroKing
Copy link

排查了我一天,终于发现了,原来是你这个东西导致的,你这个操作在dev环境下可行,但因为调用了document,打包的时候会出现bug没法打包,作者麻烦看一下

@buxianshan
Copy link

确实是vuepress-plugin-smooth-scroll这个插件的bug,但是这个插件源码已经被归档了,没办法提PR修了。很多依赖这个插件的主题都有这个问题。目前只能本地改源码了😂
https://github.com/vuepress/vuepress-plugin-smooth-scroll

@liangpengfei
Copy link

不知道博主遇到过:点击锚点之后,锚点不跳转,只是把锚点添加到链接结尾了。。这是咋回事儿吗?我看console里也没有报错,很是奇怪。

@Elon-188
Copy link

不知道博主遇到过:点击锚点之后,锚点不跳转,只是把锚点添加到链接结尾了。。这是咋回事儿吗?我看console里也没有报错,很是奇怪。

我也有这个问题

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants