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属性值。
- 结合 React Router 和
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 第三方库),利用浏览器缓存机制。 关键配置:chunks, minSize, minChunks, cacheGroups。
在 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;