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));
  };
}, []);

//...
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。