项目结构
npx create-react-app test-dynamic --template=typescript
cd test-dynamic
mkdir src/Components utils
touch src/Components/index.tsx src/utils/dynamic.ts
code . && exit
文件内容
Components/index.tsx
const DDD = () => <div>你好!</div>
export default DDD;
utils/dynamic.ts
import React from "react";
import ReactDOM from "react-dom";
type IDDD = {
current: null | undefined | any;
};
const DDDRef: IDDD = {
current: undefined,
};
export const initialDDD = async () => {
if (DDDRef?.current) return DDDRef?.current;
const module = await import( **/* webpackChunkName: "DDD" */** "../Components") as unknown as any;
console.log("module: ", module); // Module {__esModule: true, Symbol(Symbol.toStringTag): 'Module'}
DDDRef.current = module;
return DDDRef.current;
};
**//** **不注释** **就是全局加载,** **注释** **就是动态加载**
// initialDDD();
export async function dynamicImportInner(config: {
type: string;
params?: { id: string };
}) {
const type = config?.type;
const params = config?.params;
const id = params?.id;
const module = await initialDDD();
console.log("module2: ", module); // Module {__esModule: true, Symbol(Symbol.toStringTag): 'Module'}
const Def = module.default;
console.log("def: ", Def); // const DDD = () => <div>你好!</div>
switch (type) {
case "click":
// 此处的def的本质就是一个函数,就是const DDD = () => <div>你好!</div>本身!
return Def;
case "method":
if (id) {
const instance = React.createElement(Def, {});
ReactDOM.render(instance, document.getElementById(id));
// 由于babel.js的原因,这里直接写成<Def></Def>或者<Def />都是可以的;
// ReactDOM.render(<Def></Def>, document.getElementById(id));
return () =>
ReactDOM.unmountComponentAtNode(document.getElementById(id)!);
}
}
}
(window as any).dynamicImport = (id: string) => dynamicImportInner({type: 'method', params: {id}}); // **方案0**
src/App.tsx
import React from 'react';
import { dynamicImportInner } from './utils/dynamic';
function App() {
// 方案一和方案二使用**useState**方法
const [count, setCount] = React.useState(0);
const DynamicComponent = React.useRef<any>(null);
// 方案三和方案四使用**useRef**方法
// const [DynamicComponent2, setDynamicComponent2] = React.useState(null);
return (
<div className="App">
<button
onClick = { () => {
(async _ => {
const def = await dynamicImportInner({type: 'click'});
**// 方案1:**
// {
// const element = React.createElement(def);
// DynamicComponent.current = element;
// setCount(count+1)
// }
**// 方案2:**
{
DynamicComponent.current = def;
setCount(count+1)
}
**// 方案3**
// {
// setDynamicComponent2(def());
// }
**// 方案4:** 虽然def没有执行,按理来说应该是函数而不是执行之后的虚拟dom,但是会自动变成虚拟dom,非常的神奇;useRef就没有这样的能力
// {
// setDynamicComponent2(def);
// }
// setDynamicComponent(def());
})();
} }
>点我动态加载</button>
<div id="container"></div>
{/* 总而言之,render这里必须放一个**虚拟dom** */}
{/* **在这个地方使用/混用babel之前的jsx语法和babel之后的js代码**是完全ok的 */}
{/* **方案一的渲染方式** */}
{/* {DynamicComponent.current} */}
{/* **方案二的渲染方式** */}
{React.createElement(DynamicComponent.current || (()=><></>))}
{/* **方案三的渲染方式** */}
{/* {DynamicComponent2} */}
{/* **方案四的渲染方式** */}
{/* {DynamicComponent2} */}
</div>
);
}
export default App;
测试
yarn start
// 方案 0
控制台运行.效果为渲染之后两秒钟卸载
dynamicImport('container').then(_=>setTimeout(_, 2000))
// 方案1-4
点击按钮
// 观察devtool网络面板是否重复请求