React 遠(yuǎn)程動(dòng)態(tài)組件實(shí)踐示例詳解
背景
想象有這樣一個(gè)場(chǎng)景:A 團(tuán)隊(duì)開發(fā)了一套組件庫(kù),B 和 C 團(tuán)隊(duì)都在各自的業(yè)務(wù)項(xiàng)目中使用了該組件庫(kù)。現(xiàn)在 A 團(tuán)隊(duì)需要對(duì)某個(gè)組件進(jìn)行更新(比如修改顏色),按照以往的做法,A 團(tuán)隊(duì)需要先發(fā)布一個(gè)新的版本,然后其他兩個(gè)團(tuán)隊(duì)各自更新業(yè)務(wù)項(xiàng)目中所依賴的組件庫(kù)的版本后發(fā)布上線。
有沒有更快速的方法呢?比如能否做到只更新組件庫(kù),其他依賴它的項(xiàng)目可以自動(dòng)獲取到其最新的版本,即實(shí)現(xiàn)遠(yuǎn)程動(dòng)態(tài)組件。答案是有的,Webpack 5 新增的 Module Federation 就可以實(shí)現(xiàn)這個(gè)需求,但是今天我們要討論的是另外一種方法。
遠(yuǎn)程動(dòng)態(tài)組件實(shí)現(xiàn)
遠(yuǎn)程動(dòng)態(tài)組件庫(kù)
遠(yuǎn)程動(dòng)態(tài)組件庫(kù)項(xiàng)目結(jié)構(gòu)如下所示:
. ├── babel.config.js ├── package.json ├── rollup.config.js └── src ├── Button.js ├── Text.js
我們簡(jiǎn)單實(shí)現(xiàn)了兩個(gè)組件 Button
和 Text
:
import React from 'react' export default ({children}) => { return <button style={{color: 'blue'}}>{children}</button> }
import React from 'react' export default ({children}) => { return <span style={{color: 'blue'}}>{children}</span> }
我們使用 rollup 對(duì)其進(jìn)行打包,之所以用 rollup 是因?yàn)槠浯虬鰜淼拇a非常簡(jiǎn)潔,方便我們查看,rollup 配置為:
import babel from 'rollup-plugin-babel' import fs from 'fs' const components = fs.readdirSync('./src') export default components.map((filename) => { return { input: `./src/${filename}`, output: { file: `dist/${filename}`, format: 'cjs', }, plugins: [babel()], } })
打包后的結(jié)果如下所示:
. ├── dist │ └── Button.js │ └── Text.js
其中 Button.js
如下所示:
'use strict' var React = require('react') function _interopDefaultLegacy(e) { return e && typeof e === 'object' && 'default' in e ? e : {default: e} } var React__default = /*#__PURE__*/ _interopDefaultLegacy(React) var Button = function (_ref) { var children = _ref.children return /*#__PURE__*/ React__default['default'].createElement( 'button', { style: { color: 'blue', }, }, children ) } module.exports = Button
然后我們使用 http-server 在 dist
目錄下開啟一個(gè)靜態(tài)文件服務(wù),則可以通過 http://localhost:8080/Button.js
獲取到打包后的代碼。
遠(yuǎn)程組件庫(kù)介紹完畢,接下來介紹業(yè)務(wù)項(xiàng)目中如何使用。
接入遠(yuǎn)程組件庫(kù)
我們使用 create-react-app
創(chuàng)建一個(gè) React 應(yīng)用,并新增一個(gè) DynamicComponent
組件:
const DynamicComponent = ({name, children, ...props}) => { const Component = useMemo(() => { return React.lazy(async () => fetchComponent(name)) }, [name]) return ( <Suspense fallback={ <div style={{alignItems: 'center', justifyContent: 'center', flex: 1}}> <span style={{fontSize: 50}}>Loading...</span> </div> }> <Component {...props}>{children}</Component> </Suspense> ) } export default React.memo(DynamicComponent)
這里使用到了 React 中的 Suspense
組件和 React.lazy
方法,關(guān)于他們的用法這里不做過多解釋,整個(gè) DynamicComponent
組件的含義是遠(yuǎn)程加載目標(biāo)組件,這個(gè)過程該組件會(huì)渲染傳入 Suspense
參數(shù) fallback
之中的內(nèi)容,最后會(huì)使用加載成功的組件進(jìn)行替換。接下來看看 fetchComponent
是如何實(shí)現(xiàn)的:
const fetchComponent = async (name) => { const text = await fetch( `http://127.0.0.1:8080/${name}.js?ts=${Date.now()}` ).then((a) => { if (!a.ok) { throw new Error('Network response was not ok') } return a.text() }) const module = getParsedModule(text, name) return {default: module.exports} }
該方法會(huì)發(fā)起網(wǎng)絡(luò)請(qǐng)求得到組件的代碼,并交給 getParsedModule
去解析,最后得到模塊返回。我們來看一下 getParsedModule
是怎么實(shí)現(xiàn)的:
const packages = { react: React, } const getParsedModule = (code) => { let module = { exports: {}, } const require = (name) => { return packages[name] } Function('require, exports, module', code)(require, module.exports, module) return module }
這里使用 Function
來運(yùn)行傳入的代碼,因?yàn)榇虬h(yuǎn)程組件的時(shí)候并沒有將 react
庫(kù)打包進(jìn)去,所以這里需要實(shí)現(xiàn) require
這個(gè)方法。
我們結(jié)合之前打包得到的 Button.js
來看這段代碼,它其實(shí)同下面這個(gè)代碼是等價(jià)的:
const packages = { react: React, } const getParsedModule = (code, moduleName) => { let module = { exports: {}, } const require = (name) => { return packages[name] } ;((require, exports, module) => { 'use strict' var React = require('react') function _interopDefaultLegacy(e) { return e && typeof e === 'object' && 'default' in e ? e : {default: e} } var React__default = /*#__PURE__*/ _interopDefaultLegacy(React) var Button = function (_ref) { var children = _ref.children return /*#__PURE__*/ React__default['default'].createElement( 'button', { style: { color: 'blue', }, }, children ) } module.exports = Button })(require, module.exports, module) return module }
最后我們可以按照如下方式來使用 DynamicComponent
組件:
import DynamicComponent from './DynamicComponent' function App() { return ( <div> <DynamicComponent name='Button'>Click Me</DynamicComponent> <DynamicComponent name='Text'>Remote Component</DynamicComponent> </div> ) } export default App
現(xiàn)在我們嘗試修改遠(yuǎn)程動(dòng)態(tài)組件庫(kù)中的組件,重新打包后就可以馬上看到修改后的效果了。
總結(jié)
本文介紹了一種實(shí)現(xiàn)遠(yuǎn)程動(dòng)態(tài)組件的方式,不過比較簡(jiǎn)陋,事實(shí)上我們還有很多優(yōu)化的空間。比如按照現(xiàn)在的實(shí)現(xiàn)方式,如果頁(yè)面上面使用了兩個(gè) Button
,會(huì)發(fā)起兩次請(qǐng)求,這顯然不合理。針對(duì)這個(gè)問題,我們可以通過提前加載以及模塊緩存的方式來解決。
以上就是React 遠(yuǎn)程動(dòng)態(tài)組件實(shí)踐示例詳解的詳細(xì)內(nèi)容,更多關(guān)于React 遠(yuǎn)程動(dòng)態(tài)組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 解決React報(bào)錯(cuò)Expected an assignment or function call and instead saw an expression
- React報(bào)錯(cuò)信息之Expected?an?assignment?or?function?call?and?instead?saw?an?expression
- VueJs中的shallowRef與shallowReactive函數(shù)使用比較
- Project?Reactor?響應(yīng)式范式編程
- react?定位組件源碼解析
- 通過示例源碼解讀React首次渲染流程
- react?express實(shí)現(xiàn)webssh?demo解析
相關(guān)文章
React?狀態(tài)管理工具優(yōu)劣勢(shì)示例分析
這篇文章主要為大家介紹了React?狀態(tài)管理工具優(yōu)劣勢(shì)示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01React父組件調(diào)用子組件中的方法實(shí)例詳解
最近做一個(gè)React項(xiàng)目,所有組件都使用了函數(shù)式組件,遇到一個(gè)父組件調(diào)用子組件方法的問題,下面這篇文章主要給大家介紹了關(guān)于React父組件調(diào)用子組件中方法的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07React中的useState和setState的執(zhí)行機(jī)制詳解
這篇文章主要介紹了React中的useState和setState的執(zhí)行機(jī)制,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03使用React+SpringBoot開發(fā)一個(gè)協(xié)同編輯的表格文檔實(shí)現(xiàn)步驟
隨著云計(jì)算和團(tuán)隊(duì)協(xié)作的興起,協(xié)同編輯成為了許多企業(yè)和組織中必不可少的需求,本文小編就將為大家介紹如何使用React+SpringBoot簡(jiǎn)單的開發(fā)一個(gè)協(xié)同編輯的表格文檔,感興趣的朋友一起看看吧2023-11-11ReactNative實(shí)現(xiàn)弧形拖動(dòng)條的代碼案例
本文介紹了ReactNative實(shí)現(xiàn)弧形拖動(dòng)條,本組件使用到了react-native-svg和PanResponder,結(jié)合示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-02-02