jeremygo

jeremygo

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

サイト内でのVuePressの埋め込み体験実践

最近、内部監視プラットフォームサイト(React + antd)の構築に取り組んでいます。ドキュメントには現在主流の静的コンテンツサイトジェネレーター「VuePress」を選択しました。さらに、ユーザーに一元的なアクセス体験を提供するため、ドキュメントコンテンツを直接サイトシステムに埋め込むことを検討しています。そのために、iframeを使用してサイトに組み込みましたが、2 つの問題が発生しましたので、ここに記録します。

ドキュメントが完全に表示されない#

具体的な現象は、ナビゲーションバーのドキュメントセンタータブをクリックすると、コンテンツが完全に表示されず、上部の一部しか表示されないことです。下部は大きな空白です。これは、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」が具体的なドキュメントにアクセスしたアドレスを知り、外部ルート(history.replaceState)を変更するために通知する必要があります。

最終的に選択した解決策は次のとおりです。「VuePress」はクリックイベントを監視し、postMessageを使用してサイトにジャンプするための href を変更します(外部ルートと 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));
  };
}, []);

//...
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。