ifram和qiankun

208 阅读11分钟

qiankun的原理

1.qiankun 通过 import-html-entry 方式加载子应用。它会请求子应用的 HTML 入口,解析其中的脚本和样式,并动态插入到主应用页面中。对于 JavaScript 隔离,qiankun 默认采用 Proxy 沙箱机制。每次加载子应用时,初始化一个 ProxySandbox,代理 window 的访问和修改操作,使子应用对全局变量的读写限制在独立内存空间,避免污染主应用和其他子应用。在不支持 Proxy 的环境下,会采用快照沙箱机制,通过在子应用挂载前后对 window 快照,卸载时对比还原,间接实现隔离;

2.qiankun 基于 Single-SPA 理念: **single-spa 的核心原理是通过路由规则来控制不同子应用的加载和卸载,实现应用之间的解耦和独立开发、部署。当用户访问的路由匹配某个子应用的活跃规则时,single-spa 会动态加载该子应用的代码。它通过 System.import() 或其他模块加载方式来按需加载子应用.single-spa 定义了一套生命周期函数,用于控制子应用的加载、挂载、卸载等过程: bootstrap:子应用的初始化阶段,执行一些全局配置或准备工作。 mount:子应用挂载到 DOM 中,开始渲染。 unmount:子应用从 DOM 中卸载,清理资源。 single-spa 支持模块共享,允许子应用之间共享某些模块(如 React、Vue 等框架的实例)。通过配置 systemjs-webpack-4 或其他模块共享工具,可以实现代码的复用和减少重复加载。

const moduleShareConfig = {
 shares: {
   react: 'react@^17.0.0',
   'react-dom': 'react-dom@^17.0.0'
 }
};

qiankun利用路由规则激活相应子应用。通过 registerMicroApps 注册子应用信息,包括名称、入口、激活路由和渲染容器等。调用 start 方法启动框架后,qiankun 监听浏览器路由变化,当 URL 匹配子应用的激活路由时,拉取子应用资源,并依次执行其 bootstrap(初始化)、mount(挂载渲染)生命周期函数。路由切走时,执行子应用的 unmount 函数,将其从页面和内存卸载,释放资源;

3.qiankun 提供多种主应用与子应用间的数据通信方式。注册子应用时,可通过 props 字段将数据或方法透传给子应用,子应用在生命周期函数的 props 参数中获取。还提供 initGlobalState 等方法创建全局数据仓库,供主应用和各子应用订阅数据变化并触发更新,实现全局数据共享。此外,也可使用浏览器原生的 window.postMessage 或 Pub/Sub 消息总线进行通信 资源加载上:qiankun支持预加载资源(加载未打开的为应用的资源),支持按需加载资源,减少重复请求,提升响应速度。ifram在加载资源时需要重建浏览器上下文,重复加载资源,内存占用过高,页面卡顿

SPA体验:ifram在浏览器前进后退时可能会导致子应用路由改变(回到首页),qiankun保证应用的单页效果; 弹窗:ifram会限制在ifram范围内,子应用可以挂载到主应用DOM上,弹窗组件可以覆盖全屏; qiankun支持多框架,降低迁移成本; ifram依赖postMessage通信,容易延迟,qiankun使用props、initGlobalState进行通信,效果更好; ifram隔离导致无法共享上下文(如登陆状态),qiankun基于沙箱模式,支持可控的状态共享; qiankun支持动态注入全局样式,ifram完全隔离;

白屏怎么排查:

(1)网络是否连接; (2)开发者工具,看资源是否加载成功,根据状态码判断; (3)打开Console面板,检查是否有js语法错误; (4)url路径是否正确,静态资源路径是否正确(CDN地址) (5)异步加载,使用import加载资源路径错误,可以使用try、catch包裹,增加错误回退; (6)根节点是不是被隐藏了,检查elements看根节点在不在; (7)一些用法,ES6+新语法,promise这些是不是没有polyfill; (8)首屏加载过慢导致用户以为白屏了,对资源进行打包;

首屏加载过慢怎么处理:

1.代码压缩:webpack使用terser cssMinizer,TreeShaking(保证webpack的配置文件中,mode设置为‘production’,启用Tree-Shaking的前置条件,webpack在生产模式自动启用tree-shaking,原理是ES6的静态分析,能更准确的确定哪些代码是未被使用的。在编译时就能确定模块的依赖关系和导出内容,对其进行静态分析,形成依赖图,构建工具从入口开始解析代码的import语句,形成一个模块依赖图。确定哪些不用的,就在打包的时候排除掉); 服务端使用GZIP,减少传输的体积;

// webpack.config.js
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin()],
  },
};

CSSNano 基于postCSS,支持合并重复样式,删除注释;

// webpack.config.js
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
  optimization: {
    minimizer: [new CssMinimizerPlugin()],
  },
};

2.使用webp,但注意兼容性; 使用cwebp命令行工具,使用在线转换工具。使用webp SDK’ 使用picture

<picture>
  <source srcset="image.webp" type="image/webp">
  <source srcset="image.jpg" type="image/jpeg">
  <img src="image.jpg" alt="Example Image">
</picture>

懒加载;

// 懒加载Home组件
const Home = React.lazy(() => import('./Home'));

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>//fallback指定加载状态的UI
        <Home />
      </Suspense>
    </div>
  );
}

路由懒加载(适用于 React Router)

  • 基本实现步骤

    • 结合 React Router 和 React.lazy,对不同路由对应的组件进行懒加载。
    • 在路由配置中,使用 React.lazy 包裹的组件作为路由的 component 属性值。
import { BrowserRouter as Router, Route, Switch, Suspense } from 'react-router-dom';

const Home = React.lazy(() => import('./Home'));
const About = React.lazy(() => import('./About'));

function App() {
 return (
   <Router>
     <Suspense fallback={<div>Loading...</div>}>
       <Switch>
         <Route exact path="/" component={Home} />
         <Route path="/about" component={About} />
       </Switch>
     </Suspense>
   </Router>
 );
}

这样,当用户访问不同的路由时,对应的组件才会被加载,从而实现路由级的懒加载,减少应用的初始加载时间。

CDN加速;

CDN 基本原理

  • 核心功能:通过全球分布的边缘节点缓存静态资源(图片、CSS、JS、字体等),使用户就近获取内容,降低延迟。
  • 缓存机制:首次访问从源服务器拉取资源并缓存,后续请求直接由边缘节点响应。
  • 动态内容加速:部分 CDN 支持智能路由优化(如 TCP 加速),减少动 态请求(API)的延迟。

原理

利用DNS重定向技术:用户输入域名进行域名解析(找出对应的IP地址)->路由选择,根据用户所在地理位置,选取用户最近的CDN节点进行访问->缓存判断,CDN节点会检查所请求的内容是否已经缓存在本地或者相邻节点,有就直接返回->没有就是回源请求,向原始服务器进行请求->内容传输,原始服务器将请求内容传输给CDN节点,并由CDN节点进行存储和转发->CDN节点响应用户请求,发送所需内容 DDOS攻击是:利用利用分布式网络的大量设备向目标服务器发起高频请求,通过消耗资源或阻塞网络。发送大量的HTTP请求或者TCP请求导致服务器过载。

CDN的原理是通过将网站的内容复制到离用户最近的CDN服务器上,使用户访问时更快地获取网站内容。

CDN的加速原理主要通过负载均衡、缓存机制、数据传输优化、动态加速技术和安全保障等方面来实现网络加速,提升用户的网络访问体验:

负载均衡:CDN通过将用户请求分发到不同的节点,避免单一节点过载,从而保证用户请求响应速度。

缓存机制:CDN将静态资源缓存在靠近用户的节点上,当用户请求这些资源时,可直接从缓存中获取,避免了从源站获取资源的耗时。

数据传输优化:CDN采用自动智能路由技术,选择最优的传输路径,避免网络拥塞,从而优化数据传输过程。

动态加速技术:CDN可以使用动态加速技术,对动态内容进行加速,例如应用程序、交互式内容和数据库查询等。

安全保障:CDN可以提供一些常用的安全防护机制,如DDoS攻击防护、源站防护等,保障网站的安全。

3.代码分割,使用SplitChunks;

引入 SplitChunksPlugin: 在 Webpack 4 及以上版本中,SplitChunksPlugin 默认已经包含在配置中,因此你不需要手动引入。但在 Webpack 3 及以下版本中,需要手动引入。 将多个模块共享的公共依赖(如第三方库、公共组件)提取到独立文件,避免重复打包。动态导入(import())的模块会自动拆分为独立文件,实现按需加载,减少首屏体积。避免过度拆分导致过多小文件(增加 HTTP 请求),也避免文件过大(影响加载速度)。将高频更新与低频更新的代码分离(如业务代码 vs 第三方库),利用浏览器缓存机制。 关键配置chunksminSizeminChunkscacheGroups。 在 webpack.config.js 中通过 optimization.splitChunks 配置:

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',       // 处理范围:'all'(所有模块)、'async'(动态导入)、'initial'(同步导入)
      minSize: 20000,      // 模块最小体积(字节),小于此值不拆分
      minChunks: 1,       // 模块被引用次数,达到此值才拆分
      maxAsyncRequests: 30, // 最大异步并行请求数(防止过度拆分)
      maxInitialRequests: 30, // 入口点最大并行请求数
      automaticNameDelimiter: '~', // 生成文件名分隔符
      cacheGroups: {       // 缓存组:自定义拆分规则
        vendors: {
          test: /[\/]node_modules[\/]/, // 匹配 node_modules 中的模块
          priority: -10,   // 优先级(数值越大优先级越高)
          filename: 'vendors.js', // 输出文件名(可含哈希)
        },
        default: {
          minChunks: 2,    // 至少被引用 2 次的模块
          priority: -20,
          reuseExistingChunk: true, // 重用已有 chunk
        },
      },
    },
  },
};

1. 提取第三方库(vendors)

  • 配置示例:将 node_modules 中的代码单独打包:
    cacheGroups: {
      vendors: {
        test: /[\/]node_modules[\/]/,
        name: 'vendors',
        chunks: 'all',
      },
    }
    

2. 提取公共业务代码

  • 配置示例:将业务代码中被多次引用的模块提取为 common.js
    cacheGroups: {
      common: {
        name: 'common',
        chunks: 'all',
        minChunks: 2,     // 至少被引用 2 
        priority: 0,
      },
    }
    

4.请求合并,小型CSS/JS文件合并; 5.缓存策略,强缓存和协商缓存 6.预渲染 7.骨架屏; 8.异步加载非关键脚本(async、defer) 9.使用Web Worker### 预加载资源(适用于复杂计算和资源预加载) Web Workers 允许在后台线程中运行脚本,不会阻塞主线程。可以在 Web Worker 中预加载资源,然后将加载好的资源通过消息传递给主线程。 10.资源预加载

使用 <link rel="preload">(适用于静态资源)

  • 原理<link rel="preload"> 是 HTML5 提供的预加载机制,通过在文档头部添加 <link> 标签来指定需要预加载的资源,浏览器会解析该标签并提前加载指定的资源。

  • 示例

    • index.html 文件中,添加 <link rel="preload"> 标签来预加载图片、字体、CSS 文件等资源。
    • 这样在页面加载过程中,浏览器会在解析 HTML 时提前加载这些资源,减少资源加载时间。

React-router

(1)特点:基于 HTML5 History API,URL 无 #,更美观;需要服务器配置支持(如配置重定向规则)

import { BrowserRouter as Router, Route } from 'react-router-dom';
<Router>
  <Route path="/home" component={Home} />
</Router>

(2)- 通过 URL 的哈希部分(如 #/home)实现路由,无需服务器配置,兼容性更好36。

  • 使用场景:静态服务器部署或需兼容旧版浏览器。
import { HashRouter as Router, Route } from 'react-router-dom';
<Router>
  <Route path="/home" component={Home} />
</Router>

(3)特点:路由状态存储在内存中,不依赖浏览器 URL,适用于无浏览器环境(如测试、React Native)

import { MemoryRouter as Router, Route } from 'react-router-dom';
<Router>
  <Route path="/home" component={Home} />
</Router>

主要特性:

  • 声明式路由:通过定义组件的方式来配置路由。
  • 嵌套路由:支持在父路由中嵌套定义子路由。
  • 动态路由:支持在 URL 中使用参数,从而实现动态路由匹配。
  • 程序化导航:允许通过代码控制路由跳转。
  • 浏览器历史记录:与浏览器的历史记录 API 集成,支持前进和后退操作。

Link

Link 组件用于创建导航链接,用户点击链接后,浏览器的 URL 会更新,同时应用渲染相应的组件。它类似于 HTML 中的 <a> 标签,但不会导致页面刷新。 NavLink跳转时可以激活样式

import { Link } from 'react-router-dom';

function Navigation() {
  return (
    <nav>
      <ul>
        <li><Link to="/">Home</Link></li>
        <li><Link to="/about">About</Link></li>
        
        <NavLink to="/about" activeClassName="active">About</NavLink>

      </ul>
    </nav>
  );
}

export default Navigation;


#### Switch

Switch 组件用于包裹一组 Route 组件,一次仅渲染匹配的第一个路由。它确保了路由匹配的独占性,避免渲染多个路由组件


import { Switch, Route } from 'react-router-dom';
import Home from './Home';
import About from './About';

function App() {
  return (
    <Switch>
      <Route exact path="/" component={Home} />
      <Route path="/about" component={About} />
    </Switch>
  );
}

export default App;

React Router 支持在 URL 中使用参数,从而实现动态路由匹配。通过 :param 语法,可以在路由路径中定义参数。

import React from 'react';
import { BrowserRouter, Switch, Route, useParams } from 'react-router-dom';

function User() {
  let { id } = useParams();
  return <h2>User ID: {id}</h2>;
}

function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/user/:id" component={User} />
      </Switch>
    </BrowserRouter>
  );
}

export default App;

程序化导航

除了使用 Link 组件创建导航链接外,React Router 还支持通过代码控制路由跳转。使用 useHistory 钩子可以实现程序化导航

import React from 'react';
import { useHistory } from 'react-router-dom';

function Home() {
  let history = useHistory();

  function handleClick() {
    history.push('/about');
  }

  return (
    <div>
      <h2>Home Page</h2>
      <button onClick={handleClick}>Go to About</button>
    </div>
  );
}

function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
      </Switch>
    </BrowserRouter>
  );
}

export default App;