如何让 SPA 导航与使用 innerHTML 内容的外部框架一起工作

How to get SPA navigation working with external framework that uses innerHTML for content

在我的 Vue.js 应用程序中,我使用基于 bootstrap 的框架为我的 header and a menu nav with links 生成 html,然后通过将 innerHTML 分配给挂载点。

但是当我使用生成的内容进行导航时,整个页面会重新加载,因为链接没有使用 <router-link>

一次修复尝试:
在 Vue 应用程序中,我在 window 对象上分配了一个名为 goto 的方法,该方法将执行程序化路由器导航。 然后我能够将 javascript:window.goto("myPageName"); 作为 href 属性传递,但这会带来许多不良副作用。

如何在不重新加载页面的情况下使链接导航干净利落?

(框架需要 jQuery 作为依赖项,以便能够在解决方案中使用。)

我能够使用 MutationObserver that watches for subtree changes 并在检测到通过 .innerHTML 添加的 link 时添加自定义点击处理程序。

使用此方法,我将 vue-goto:myPageName 指定为 href 属性,然后处理程序将负责使其成为 SPA link。

import { router } from "@/router";
import { store } from "@/store";

export const attrib = "vue-goto";
export const prefix = attrib + ":";

function onChange() {
    // find all links matching our custom prefix to which we have not yet added our custom handler
    const links = window.$(`a[href^='${prefix}']`).not(`[${attrib}]`);

    // add custom attribute for us to grab later
    links.attr(attrib, function() {
        // jQuery doesn't like arrow functions
        return window
            .$(this)
            .attr("href")
            .substr(prefix.length)
            .trim();
    });

    // Update href on the link to one that makes sense
    links.attr("href", function() {
        return router.resolve({
            name: window.$(this).attr(attrib), // grab attribute we saved earlier
            params: { lang: store.state.language }, // in our case, our pages are qualified by a language parameter
        }).href;
    });

    // Override default click navigation behaviour to use vue-router programmatic navigation
    links.click(function(e) {
        e.preventDefault(); // prevent default click
        const routeName = window.$(this).attr(attrib);
        const goto = {
            name: routeName,
            lang: store.state.language,
        };
        router.push(goto).catch(ex => {
            // add catch here so navigation promise errors aren't lost to the void causing headaches later
            // eslint-disable-next-line no-console
            console.error(
                `Error occurred during navigation from injected [${prefix}${routeName}] link`,
                "\n",
                ex,
            );
        });
    });
}

let observer;
export function init() {
    if (observer) observer.unobserve(document.body);
    observer = new MutationObserver(onChange);
    observer.observe(document.body, {
        characterData: false,
        childList: true,
        subtree: true, // important, we want to see all changes not just at toplevel
        attributes: false,
    });
}
init();