大型中后台项目一般包括10个以上的子项目,如果维护在一个单页面应用中,项目就会越来越大,而且不利于版本的迭代,微前端就很好的解决了这些问题。
这篇文章主要来体验下蚂蚁的微前端:qiankun,虽然比较成熟了,但在体验过程中还是有一些问题,记录总结下,项目代码
实践项目以react单页面应用为主应用,然后构建了三个微应用:react、vue3、node静态页面
微前端要求多个前端服务,所以我们先准备几个应用,使用不同的技术栈,体验微前端的强大
mirco-front-demo
作为整个服务的根目录,为了便于实践,主应用和微应用将放在一起。
主应用:
my-app
port: 10000
create-react-app
微应用:
micro-reat-1
port: 10100
create-react-app
micro-vue-2
port: 10200
vue3
micro-static-3
port: 10300
node + html
需要提前安装create-react-app:
sudo npx install create-react-app
通过create-react-app
创建主应用my-app
,其他的微应用都会挂载到主应用。
# 在根目录 mkdir mirco-front-demo cd mirco-front-demo # 新建主应用my-app npx create-react-app my-app cd my-app # 通过.env文件修改启动端口 echo "PORT=10000" > .env yarn start 复制代码
cd micro-front-demo npx create-react-app micro-react-1 复制代码
同主应用一样创建一个React应用,命名micro-react-1
,并修改启动端口号(也可以使用.env文件修改)
修改启动端口号:
{ "scripts": { "start": "PORT=10100 react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject" } } 复制代码
使用vue3.0,提前安装vue-cli:
yarn global add @vue/cli
,官方文档
vue create micro-vue-2 cd micro-vue-2 touch vue.config.js 复制代码
除了微应用1和微应用2修改启动端口号的方法,这里也可以对webpack进行覆盖
vue-config.js
const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, devServer: { // 监听端口 port: 10200, // 配置跨域请求头,解决开发环境的跨域问题 headers: { "Access-Control-Allow-Origin": "*", } } } 复制代码
启动微应用yarn serve
# 新建微应用项目3 mkdir micro-static-3 cd micro-static-3 npm init yarn add express cors # 新建项目文件 mkdir static touch index.js cd static touch index.html 复制代码
index.js
const express = require('express') const cors = require('cors') const app = express() const port = 10300 app.use(cors()) app.use(express.static('static')); app.listen(port, () => { console.log(`Example app listening on port ${port}`) }) 复制代码
启动微应用node index.js
cd mirco-front-demo npm init yarn add npm-run-all -D 复制代码
修改当前目录下package.json
{ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "npm-run-all --parallel start:*", "start:main": "cd my-app && yarn start", "start:micro-react": "cd micro-react-1 && yarn start", "start:micro-vue": "cd micro-vue-2 && yarn serve", "start:micro-static": "cd micro-static-3 && node index.js" } } 复制代码
执行命令
cd micro-front-demo yarn start 复制代码
这样就同时启动了四个前端服务
改造下主应用样式,整个顶部导航栏和侧边栏属于主应用,而中间空白的部分可以展示主应用或子应用的页面。
主应用路由安装react-router-dom
,通过history
模式渲染。
在侧边栏点击不同的链接会加载不同的子应用,样式和路由具体可以看示例代码
修改单页面应用渲染根节点root
为main-root
,防止和微应用中渲染节点冲突。
并且增加一个通过id标记的DIV,用来嵌入微应用,接着引入qiankun
,这里id=subApp
用来挂载微应用
yarn add qiankun 复制代码
在src/index.js
中配置
import React from 'react'; import ReactDOM from 'react-dom'; import { registerMicroApps, start } from 'qiankun'; import './index.css'; import App from './App'; function render(){ ReactDOM.render(<App />, document.querySelector('#main-root')); } render({}); registerMicroApps([ { name: 'react', // app name registered entry: '//localhost:10100', container: "#subApp", activeRule: '/react' }, { name: 'vue', // app name registered entry: '//localhost:10200', container: "#subApp", activeRule: '/vue' }, { name: 'static', // app name registered entry: '//localhost:10300', container: "#subApp", activeRule: '/static' } ], { beforeLoad: app => { console.log('before load app.name=====>>>>>', app.name) }, beforeMount: [ app => { console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name) } ], afterMount: [ app => { console.log('[LifeCycle] after mount %c%s', 'color: green;', app.name) } ], afterUnmount: [ app => { console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name) } ] }) start() 复制代码
引入react-router-dom
给微应用配置路由,展示不同的页面,如下:
修改src/index.js
下启动文件
function render(props) { const { container } = props; ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root')); } if (!window.__POWERED_BY_QIANKUN__) { render({}); } export async function bootstrap() { console.log('[react16] react app bootstraped'); } export async function mount(props) { console.log('[react16] props from main framework', props); render(props); } export async function unmount(props) { const { container } = props; ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root')); } 复制代码
给微应用添加导出声明周期函数,微应用挂载成功时,渲染到当前应用的root节点。
当前cra并没有释放webpack配置,所以要通过插件覆盖配置:
yarn add react-app-rewired -D 复制代码
"scripts": { "start": "PORT=10100 react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject" } 复制代码
在当前微应用根目录下touch config-overrides.js
const { name } = require('./package'); module.exports = { webpack: (config, env) => { config.output.library = `${name}-[name]`; config.output.libraryTarget = 'umd'; config.output.globalObject = 'window'; config.output.chunkLoadingGlobal = `webpackJsonp_${name}`; return config; }, devServer: (_) => { const config = _; config.headers = { 'Access-Control-Allow-Origin': '*', }; config.historyApiFallback = true; config.hot = false; config.watchContentBase = false; config.liveReload = false; config.injectClient = false return config; } } 复制代码
启动微应用yarn start
,正常运行
新增 public-path.js
文件,用于修改运行时的 publicPath
src/public-path.js
if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } 复制代码
引入vue路由,设置成history模式,baseRouter设置成vue,导出声明周期函数
import { createApp } from 'vue' import App from './App.vue' import router from './router' import './public-path' const app = createApp(App); function render(props) { const { container } = props; app.use(router) .mount(container ? container.querySelector('#app') : '#app') } export async function bootstrap() { console.log('bootstrap'); } export async function mount(props) { console.log('mount', props); render(props); } export async function unmount() { console.log('unmount'); app.unmount(); } 复制代码
修改webpack配置vue.config.js
module.exports = defineConfig({ configureWebpack: { output: { // 微应用的包名,这里与主应用中注册的微应用名称一致 library: name, // 将你的 library 暴露为所有的模块定义下都可运行的方式 libraryTarget: "umd", // 按需加载相关,设置为 webpackJsonp_微应用名称 即可 chunkLoadingGlobal: `webpackJsonp_${name}`, } } }) 复制代码
启动应用yarn serve
这是一个express服务启动的静态服务
文件入口导出声明周期entry.js
const render = ($) => { $('#app').html('Hello, render html, 一个通过http服务部署的静态网站'); return Promise.resolve(); }; ((global) => { global['static'] = { bootstrap: () => { console.log('purehtml bootstrap'); return Promise.resolve(); }, mount: () => { console.log('purehtml mount'); return render($); }, unmount: () => { console.log('purehtml unmount'); return Promise.resolve(); }, }; })(window); 复制代码
然后在模版文件导入
其实也是挂载在了app节点。
启动当前微服务node index.js
cd micro-front-demo yarn start 复制代码
'__webpack_public_path__' is not defined
Uncaught Error: single-spa minified message #20
覆盖CRA的Webpack配置
const { name } = require('./package'); module.exports = { webpack: (config) => { config.output.library = `${name}-[name]`; config.output.libraryTarget = 'umd'; config.output.jsonpFunction = `webpackJsonp_${name}`; config.output.globalObject = 'window'; return config; }, devServer: (_) => { const config = _; config.headers = { 'Access-Control-Allow-Origin': '*', }; config.historyApiFallback = true; config.hot = false; config.watchContentBase = false; config.liveReload = false; return config; }, }; 复制代码
这里报错,是因为是webpack5.x不兼容config.output.jsonpFunction
的写法,需要替换成config.output.chunkLoadingGlobal
construct.js:17 Uncaught Error: application 'react' died in status LOADING_SOURCE_CODE: [qiankun]: You need to export lifecycle functions in react entry
没有将生命周期暴露出来,需要挨个检查下面的配置
Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema. - configuration.output has an unknown property 'jsonpFunction'. These properties are valid: object { assetModuleFilename?, asyncChunks?, auxiliaryComment?, charset?, chunkFilename?, chunkFormat?, chunkLoadTimeout?, chunkLoading?, chunkLoadingGlobal?, clean?, compareBeforeEmit?, crossOriginLoading?, cssChunkFilename?, cssFilename?, devtoolFallbackModuleFilenameTemplate?, devtoolModuleFilenameTemplate?, devtoolNamespace?, enabledChunkLoadingTypes?, enabledLibraryTypes?, enabledWasmLoadingTypes?, environment?, filename?, globalObject?, hashDigest?, hashDigestLength?, hashFunction?, hashSalt?, hotUpdateChunkFilename?, hotUpdateGlobal?, hotUpdateMainFilename?, iife?, importFunctionName?, importMetaName?, library?, libraryExport?, libraryTarget?, module?, path?, pathinfo?, publicPath?, scriptType?, sourceMapFilename?, sourcePrefix?, strictModuleErrorHandling?, strictModuleExceptionHandling?, trustedTypes?, umdNamedDefine?, uniqueName?, wasmLoading?, webassemblyModuleFilename?, workerChunkLoading?, workerWasmLoading? } -> Options affecting the output of the compilation. `output` options tell webpack how to write the compiled files to disk. Did you mean output.chunkLoadingGlobal (BREAKING CHANGE since webpack 5)?
作者:前端中后台
链接:https://juejin.cn/post/7075607657205710855
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/127964.html
摘要:而从技术实现角度,微前端架构解决方案大概分为两类场景单实例即同一时刻,只有一个子应用被展示,子应用具备一个完整的应用生命周期。为了解决产品研发之间各种耦合的问题,大部分企业也都会有自己的解决方案。 原文链接:https://zhuanlan.zhihu.com/p/... Techniques, strategies and recipes for building a modern ...
摘要:可视化钢琴使用钢琴音色播放文件,并且将按键可视化到钢琴键盘上。如图主界面链接在线 可视化钢琴 使用钢琴音色播放Midi文件,并且将按键可视化到钢琴键盘上。如图: 主界面: showImg(https://segmentfault.com/img/bVbmEzC?w=1920&h=943); github链接 https://github.com/qk44077907...在线demo ...
摘要:近期公司需要针对分享流程进行优化,其中一点就是前端检测是否安装应用,来进行不同的判断下载或直接跳转到中。为回调函数,根据返回来判断是否安装。 近期公司需要针对分享流程进行优化,其中一点就是前端H5检测是否安装应用,来进行不同的判断(下载或直接跳转到app中)。原理很简单:创建一个iframe去打开uri。如果打开app成功网页进入后台,再切换回来时间会超过2.5s。利用时间去检测。下面...
阅读 130091·2024-02-01 10:43
阅读 758·2024-01-31 14:58
阅读 733·2024-01-31 14:54
阅读 82539·2024-01-29 17:11
阅读 2821·2024-01-25 14:55
阅读 1896·2023-06-02 13:36
阅读 2771·2023-05-23 10:26
阅读 835·2023-05-23 10:25