jeremygo

jeremygo

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

Embedded VuePress Experience Practice on the Website

Recently, we are building an internal monitoring platform site (React + antd). For documentation, we have chosen the popular static content site generator, VuePress. We want to provide users with a seamless access experience by embedding the documentation content directly into the site system. However, we encountered two problems when integrating it using an iframe, and we would like to record them here.

Incomplete display of documentation#

The specific issue is that when clicking on the "Documentation Center" tab in the navigation bar, the content is not fully displayed. Only the top part is shown, and there is a large blank space below, indicating that the default height of the iframe is not enough.

By modifying the parent element to 100vh + 100%, the height can be easily adjusted automatically:

// 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>
  );
};

Retaining the state of document access#

When a user accesses a specific part of the documentation, the corresponding anchor link needs to be updated. This allows direct access to the specific part of the documentation even when refreshing the page or sharing the link.

The key to implementation is solving the communication issue between the site system and VuePress. The site system needs to know the specific address of the documentation accessed in VuePress and update it to the link route address.

The final solution is as follows: In VuePress, listen for click events and use postMessage to notify the site system to change the external route (history.replaceState) and dynamically change the iframe src:

// .vuepress/config.js
module.exports = {
  // https://vuepress.vuejs.org/zh/config/#head Injected into the HTML tags of the page
  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,
);

Finally, in the embedded iframe component, we also need to listen for the message event:

// ...
useEffect(() => {
  const handleSetSrc = (e: any) => {
    if (e.data.type?.includes('monitor_docs')) {
      const links = e.data.link?.split('/monitor');
      // Change the address, system routes directly link to the external source
      window.history.replaceState(null, '', `/docs${links[1] || links[0]}`);
    }
  };
  window.addEventListener('message', (e) => handleSetSrc(e));

  //...

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

//...
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.