jeremygo

jeremygo

我是把下一颗珍珠串在绳子上的人

站点内嵌 VuePress 体验实践

最近在建設內部監控平台站點(React + antd),文檔方面選擇的是目前主流的靜態內容站點生成器 VuePress,進一步考慮希望提供給用戶一站式的訪問體驗打算將文檔內容直接內嵌至站點系統中,短平快地通過 iframe 接入後遇到了兩個問題,特此記錄。

文檔無法完全顯示#

具體現象是點擊導航欄的文檔中心 Tab 後內容展示不全,只能展示上部一截,下面是大段的空白,很明顯是 iframe 默認的高度不夠。

通過修改父層元素為 100vh + 100% 可簡單實現高度自適應:

// pages/docs/*.tsx
import React, { useState, useEffect } from 'react';

const docs_url =
  process.env.NODE_ENV === 'production'
    ? 'http://xxx.com/monitor'
    : 'http://localhost:8088/monitor';

const frameRef = React.createRef<any>();
const DocsView = () => {
  const [src, setSrc] = useState(
    docs_url + window.location.href.split('/docs')[1],
  );
  useEffect(() => {
    const pageMain = document.getElementsByClassName('page-main')[0];
    pageMain.setAttribute('style', 'height: 100vh');
    const antRow = pageMain.getElementsByClassName(
      'ant-row ant-row-center ant-row-middle',
    )[0];
    antRow.setAttribute('style', 'height: 100%');

    return () => {
      pageMain.removeAttribute('style');
      antRow.removeAttribute('style');
    };
  }, []);

  return (
    <iframe
      ref={frameRef}
      src={src}
      style={{
        width: '100%',
        height: '100%',
        border: 0,
      }}
    ></iframe>
  );
};

文檔訪問狀態留存#

當用戶訪問文檔其中某個部分後需要更新對應的鏈接錨點,這樣不管是刷新頁面或是分享鏈接出去都可以直接訪問。

實現關鍵在於解決站點系統與 VuePress 之間的通信問題,即站點系統需要知道 VuePress 內部訪問了具體文檔的地址同時更新至鏈接路由地址上。

最終選用的方案如下:VuePress 中監聽點擊事件通過 postMessage 通知站點跳轉 href 來修改外部路由(history.replaceState)實現路由以及 iframe src 動態改變:

// .vuepress/config.js
module.exports = {
  // https://vuepress.vuejs.org/zh/config/#head 注入到頁面 HTML 中的標籤
  head: [
    ['script', { src: '/postMessage.js' }],
  ],
  //...
}
// .vuepress/public/postMessage.js
var topUrl = document.referrer;

document.addEventListener(
  'click',
  function(e) {
    var target = e.target;
    if (target.tagName !== 'A' && target.parentNode.tagName === 'A') {
      target = target.parentNode;
    }
    var href = target.getAttribute('href');
    if (href && href.indexOf('/') === 0) {
      top.postMessage(
        {
          type: 'monitor_docs_msg',
          link: href,
        },
        topUrl,
      );
    }
  },
  false,
);

最後在內嵌 iframe 組件中還需要監聽 message 事件:

// ...
useEffect(() => {
  const handleSetSrc = (e: any) => {
    if (e.data.type?.includes('monitor_docs')) {
      const links = e.data.link?.split('/monitor');
      // 變更地址,系統路由直接外鏈寫死
      window.history.replaceState(null, '', `/docs${links[1] || links[0]}`);
    }
  };
  window.addEventListener('message', (e) => handleSetSrc(e));

  //...

  return () => {
    // ...
    window.removeEventListener('message', (e) => handleSetSrc(e));
  };
}, []);

//...
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。