Frontend Development 12 min read

Developing Cross‑Framework UI Components with Native JavaScript and Iframe Containers

This article explains how to design and implement reusable UI components that work across jQuery, React, and Vue by using a native‑JS container, iframe isolation, and communication techniques such as domain sharing, postMessage alternatives, and Nginx proxying, while providing code examples and practical tips.

政采云技术
政采云技术
政采云技术
Developing Cross‑Framework UI Components with Native JavaScript and Iframe Containers

Background

The author’s backend team needs to supply UI components to multiple business lines that use different frameworks (jQuery, React, Vue). Maintaining separate implementations for each framework leads to duplicated effort and costly upgrades.

Each framework requires a separate development cycle, consuming time and resources.

Component upgrades force all downstream projects to release new versions, increasing communication overhead and slowing iteration.

Ideal Component

Cross‑framework: Write once, run everywhere

Minimal upgrades: the component should require little or no changes on the consumer side after an update.

Implementation Plan

The simplest idea is to write the component in pure JavaScript, avoiding any framework dependencies.

Native Implementation

Advantages:

Cross‑framework: no reliance on a specific library.

Lightweight: small bundle size.

Disadvantages:

Low ROI: building a full set of UI utilities from scratch is time‑consuming.

Compatibility pitfalls: extensive browser‑compatibility testing is required.

Limited for complex interactions.

Suitable scenarios include simple UI such as static headers or side menus.

Native Container Component + Iframe Loading Business Logic

The solution splits a component into two parts:

Container component – a native‑JS wrapper that collects parameters, registers global callbacks, mounts the component, and loads an iframe.

Business‑logic component – loaded inside the iframe, can be built with any framework because the iframe provides sandboxing.

Benefits: the container handles stable logic while the iframe isolates ever‑changing business code, reducing the need for consumer upgrades.

How to Implement

The overall flow is illustrated in a diagram (omitted). The container component performs the following steps:

Initialization

function getTopLevelDomain(host) {
  let data = host || window.location.host;
  return data.split('.').slice(-2).join('.');
}
function setDomainToTopLevelDomain() {
  try {
    window.document.domain = getTopLevelDomain();
  } catch (error) {
    console.error("设置domain失败");
  }
}

Render

class Vanilla {
  // 获取配置信息
  constructor(config) {
    const options = { ...defaultConfig, ...config };
    this.options = options;
    this.elCls = options.elCls;
  }
  // 生成容器 div
  render() {
    const div = document.createElement('div');
    this.el = div;
    const { width, height } = this.options;
    div.className = `${prefixCls}-wrap ${prefixCls}-wrap-loading ${this.elCls || ''}`;
    const maskNode = getMaskNode(prefixCls);
    const iframeNode = getIframeNode(prefixCls, width, height);
    div.innerHTML = maskNode + iframeNode;
    document.body.appendChild(div);
    this.fn();
  }
  init() {
    setDomainToTopLevelDomain();
    this.render();
    this.initCallbacks();
  }
  ...
}

Register Callbacks

class Vanilla {
  ...
  initCallbacks() {
    const self = this;
    const options = this.options;
    window[paramsName] = options;
    window.onSuccess = function onSuccess(data, res) {
      options.onSuccess && options.onSuccess(data, res);
      setTimeout(() => { self.removeNode(); }, 1500);
      self.resetCallbacks && self.resetCallbacks();
    };
    window.onCancel = function onCancel() {
      options.onCancel && options.onCancel();
      self.removeNode();
      self.resetCallbacks && self.resetCallbacks();
    };
    window.onError = function onError(data) {
      options.onError && options.onError(data);
    };
  }
}

Load Iframe

let timer = function timer() {};
class Vanilla {
  ...
  fn() {
    const { width, height, isAutoSize } = this.options;
    const el = this.el;
    const url = getContentUrl('你的iframe地址');
    const iframeEle = el.querySelector('.J_CreditIframe');
    const modalNode = el.querySelector(`.${prefixCls}`);
    if (!isAutoSize && (iframeWidth !== width || iframeHeight !== height)) {
      this.setNodeSizeAndPostion(modalNode, iframeEle, iframeWidth, iframeHeight);
    }
    iframeEle.setAttribute('src', url);
    addEvent(iframeEle, 'load', () => {
      el.className = `${prefixCls}-wrap ${this.elCls || ''}`;
      // auto‑size logic omitted for brevity
    });
  }
  setNodeSizeAndPostion(container, iframe, width, height) {
    container.style.cssText = `width:${width}px;height:${height}px;margin-left:-${width/2}px;margin-top:-${height/2}px;`;
    iframe.style.cssText = `width:${width}px;height:${height}px;`;
  }
  removeNode() {
    timer && clearInterval(timer);
    if (this.el) {
      document.body.removeChild(this.el);
    }
  }
  ...
}

Communication between the container and the business logic component is achieved via global callbacks. When the domains differ, three strategies are discussed:

postMessage – not used because IE9 support is required.

Document.domain + iframe – works when both sides share the same top‑level domain.

Nginx proxy – reverse‑proxy the iframe URL to a unified domain, enabling cross‑origin access.

// Nginx static page location
location ~ ^/your-project/ {
  root /opt/front/your-project/;
  try_files $uri $uri/ /index.html =404;
  access_log off;
}
// Reverse proxy for API
location ~ ^/api/service/(.*)$ {
  proxy_pass http://your-ip;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $http_host;
  proxy_set_header requestId $request_id;
  proxy_http_version 1.1;
  proxy_set_header Connection "";
  expires 30d;
  access_log off;
}

Key Points to Note

Handle non‑white rounded corners by letting the iframe set its own background.

Version control: small patches stay backward compatible; major releases can be switched via dynamic iframe URLs.

Conclusion

Repeated component development across multiple frameworks stems from historical technical debt. The most effective long‑term solution is to unify the tech stack or adopt micro‑frontend architecture. The presented approach offers a practical way to serve heterogeneous front‑ends today while keeping upgrade friction low.

frontendcomponentweb developmentiframecross-frameworkVanilla JS
政采云技术
Written by

政采云技术

ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.