前言
在使用阿里开源的乾坤并用 vue 编写主工程的时候,遇到了一些问题,现在将遇到的问题和需要注意的内容记录一下。 我使用的是 vue-cli4 脚手架创建的工程,在 public 目录下有 index.html 文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="container"></div>
<!-- built files will be auto injected -->
</body>
</html>
这里的 id 需要改变一下,使用 container 或者其他。因为使用 vue-cli 创建的工程这里默认 id 都是 app,这会和子工程的 id 一样。就会产生样式的问题。
主工程部分: 在子工程未渲染完成时,可以显示主工程内容,等到子工程渲染完成,主工程的显示出现问题。
子工程部分:
将主工程里 index.html 的id 改为 container,同时将 new Vue 的 el 改为 ‘#container’后, 主工程和子工程样式显示正常。
主工程
在主工程中需要依赖 qiankun,
yarn add qiankun
下面对主工程的主要文件记录如下:
文件 App.vue
<template>
<div id="main-container">
<Navbar></Navbar>
<!-- 子应用盒子 -->
<div id="root-view" class="app-view-box" v-html="content"/>
</div>
</template>
<script>
import Navbar from '@/layout/Navbar'
import { Loading } from 'element-ui'
export default {
name: 'App',
props: {
loading: Boolean,
content: String
},
components: {
Navbar
},
beforeCreate () {
const loadingInstance = Loading.service({ fullscreen: true })
this.$nextTick(() => { // 以服务的方式调用的 Loading 需要异步关闭
loadingInstance.close()
})
}
}
</script>
文件 main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'
import './styles/element-variables.scss'
import './icons' // icon
import { registerMicroApps, runAfterFirstMounted, setDefaultMountApp, start } from 'qiankun'
Vue.use(ElementUI)
Vue.config.productionTip = false
let app = null
function VueRender (appContent, loading) {
return new Vue({
el: '#container',
router,
store,
data () {
return {
content: appContent,
loading
}
},
render (h) {
return h(App, {
props: {
content: this.content,
loading: this.loading
}
})
}
})
}
function render ({ appContent, loading } = {}) {
if (!app) {
app = VueRender(appContent, loading)
} else {
app.content = appContent
app.loading = loading
}
}
function genActiveRule (routerPrefix) {
return location => location.pathname.startsWith(routerPrefix)
}
/**
* Step1 初始化应用(可选)
*/
render({ appContent: '', loading: true })
/**
* Step2 注册子应用
*/
registerMicroApps(
[
{
name: 'app1',
entry: '//localhost:7101',
render,
activeRule: genActiveRule('/' + name)
}
],
{
beforeLoad: [
app => {
console.log('[LifeCycle] before load %c%s', 'color: green;', app.name)
}
],
beforeMount: [
app => {
console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name)
}
],
afterUnmount: [
app => {
console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name)
}
]
}
)
/**
* Step3 设置默认进入的子应用
*/
setDefaultMountApp('/app1')
/**
* Step4 启动应用
*/
start({
prefetch: true,
jsSandbox: true,
singular: true,
fetch: window.fetch
})
runAfterFirstMounted(() => {
console.log('[MainApp] first app mounted')
})
改造为 ts
在改造为 ts 的过程中,这个 main.ts 文件处理起来麻烦死了,总是有 eslint 的语法检查错误,目前仍是没有解决,所以暂时对该文件做忽略检查的操作。//@ts-nocheck 放在文件开头,表示该文件忽略 ts 检查。当然即使有错误提示,浏览器仍然是可以正常访问的,没有报错。
//@ts-nocheck
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
import { registerMicroApps, runAfterFirstMounted, setDefaultMountApp, start } from 'qiankun'
Vue.config.productionTip = false
let app = null
function VueRender(appContent: string, loading: boolean) {
return new Vue({
el: '#app',
router,
store,
data() {
return {
content: appContent,
loading
}
},
render(h) {
return h(App, {
props: {
content: this.content,
loading: this.loading
}
})
}
})
}
function render(props: { appContent: string; loading: boolean }) {
const { appContent, loading } = props
if (!app) {
app = VueRender(appContent, loading)
} else {
app.content = appContent
app.loading = loading
}
}
function genActiveRule(routerPrefix: string) {
return (location: { pathname: string }) => location.pathname.startsWith(routerPrefix)
}
/**
* Step1 初始化应用(可选)
*/
render({ appContent: '', loading: true })
/**
* Step2 注册子应用
*/
registerMicroApps(
[
{
name: 'app1',
entry: '//localhost:7101',
render,
activeRule: genActiveRule('/' + name)
}
],
{
beforeLoad: [
app => {
console.log('[LifeCycle] before load %c%s', 'color: green;', app.name)
}
],
beforeMount: [
app => {
console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name)
}
],
afterUnmount: [
app => {
console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name)
}
]
}
)
/**
* Step3 设置默认进入的子应用
*/
setDefaultMountApp('/app1')
/**
* Step4 启动应用
*/
start({
prefetch: true,
jsSandbox: true,
singular: true,
fetch: window.fetch
})
runAfterFirstMounted(() => {
console.log('[MainApp] first app mounted')
})
现将几个不知道如何解决的语法错误记录如下
VueRender 方法中的 render 返回的 prop中的 content 和 loading 参数:
27:25 Property 'content' does not exist on type 'CombinedVueInstance<Vue, object, object, object, Readonly<Record<never, any>>>'.
25 | return h(App, {
26 | props: {
> 27 | content: this.content,
| ^
28 | loading: this.loading
29 | }
30 | })
ERROR in F:/workspace/vue/qiankun-demo1/platfrom-demo-ts/src/main.ts(28,25):
28:25 Property 'loading' does not exist on type 'CombinedVueInstance<Vue, object, object, object, Readonly<Record<never, any>>>'.
26 | props: {
27 | content: this.content,
> 28 | loading: this.loading
| ^
29 | }
30 | })
31 | }
registerMicroApps 方法的 第二个生命周期参数:
ERROR in F:/workspace/vue/qiankun-demo1/platfrom-demo-ts/src/main.ts(66,3):
66:3 Argument of type '{ beforeLoad: ((app: RegistrableApp<{}>) => void)[]; beforeMount: ((app: RegistrableApp<{}>) => void)[]; afterUnmount: ((app: RegistrableApp<{}>) => void)[]; }' is not assignable to parameter of type 'LifeCycles<{}>'.
Types of property 'beforeLoad' are incompatible.
Type '((app: RegistrableApp<{}>) => void)[]' is not assignable to type 'Lifecycle<{}> | Lifecycle<{}>[] | undefined'.
Type '((app: RegistrableApp<{}>) => void)[]' is not assignable to type 'Lifecycle<{}>[]'.
Type '(app: RegistrableApp<{}>) => void' is not assignable to type 'Lifecycle<{}>'.
Type 'void' is not assignable to type 'Promise<any>'.
64 | }
65 | ],
> 66 | {
| ^
67 | beforeLoad: [
68 | app => {
69 | console.log('[LifeCycle] before load %c%s', 'color: green;', app.name)