主要内容
目的:为了将一个大型项目拆分为多个子项目.通俗来说,就是vue
项目中,web1
为导航栏,web2
为门户,web3
为系统管理,通过微前端
将他们糅合成一个完成的项目.
qiankun.js
介绍
qiankun.js是当前最出色的一款微前端
实现库, 他帮我们实现了css隔离
、js隔离
、项目关联
等功能
# 安装qiankun.js
$ npm i qiankun -S
1. 项目主目录结构
共三个vue
项目,container
属于项目导航部分,web1
为门户部分,web2
为系统管理部分,container
项目中有个subapp
文件夹,用来存在web1
和web2
项目(subapp
文件夹也可以放外层)。
1.1 结构一:
1.2 结构二:
2. 安装qiankun
后配置项目加载规则
2.1 在container
的app.vue
中
<template>
<div id="nav">
<!-- 定义导航栏,且激活地址 -->
<router-link to="/">Home</router-link> |
<router-link to="/w1">web1</router-link> |
<router-link to="/w2">web2</router-link>
<!-- 新增id为box的元素,将web1中的内容插入到box中 子容器 -->
<div id="box"></div>
<!-- container(父容器) -->
<router-view />
</div>
</template>
2.2 修改home.vue中的内容
<template>
<div class="home">我是 container 工程</div>
</template>
此时界面如下:
2.3 配置container
的main.js
// 引入qiankun.js
import { registerMicroApps, start } from 'qiankun';
// 注册子应用
registerMicroApps([
{
name: 'vueApp2',
entry: '//localhost:8083',
container: '#box',
activeRule: '/w2',
},
{
name: 'vueApp1',
entry: '//localhost:8082',
container: '#box',
activeRule: '/w1',
},
]);
// 开启服务
start();
参数解析:
registerMicroApps(apps, lifeCycles?)
apps
-Array
- 微应用的注册信息
name
-String
- 微应用的名称,必须确保唯一 - 【必选】entry
-String
- 微应用的入口 - 【必选】container
-String | HTMLElement
- 微应用的容器节点的选择器或者Element
实例 - 【必选】acticeRule
-String | (location:Location) => boolean | Array | (location:Location) => boolean
- 微应用的激活规则 - 【必选】
3. 在subapp
中新建子项目
3.1 新建子项目
# 跳转到subapp目录下
$ cd subapp
# 初始化web1
$ vue init webpack web1
# 初始化web2
$ vue init webpack web2
3.2 配置web1
项目,web2
同理配置
3.2.1 配置web1
的main.js
在main.js
导出自己的生命周期函数
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
Vue.config.productionTip = false;
let instance = null;
function render() {
instance = new Vue({
router,
render: h => h(App)
}).$mount('#web1') // 框架会拿到完整的dom结构, 所以index.html里面的id也要改一下
}
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log('bootstrap');
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount() {
console.log('bootstrap mount');
render()
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
console.log('bootstrap unmount');
instance.$destroy()
}
3.2.2 web1
中的index.html
中修改
index.html
为打包模版,因为有多个子应用,所以id
不唯一,将div
中id
改为web1
。
3.2.3 web1
的App.vue
修改
<template>
<div id="web1">Welcome Web1 page!</div>
</template>
3.2.4 web1 -> config -> index.js
中修改端口号
这里修改后的端口号和主应用中配置的微服务的端口号对应。
module.exports = {
dev: {
port:'8082' // web2 改为 8083
}
}
4. 运行项目
4.1 单独启动
# contianer,web1,web2顶层目录下启动
$ npm run dev
4.2 一起启动
4.2.1 安装npm-run-all
单独依次启动应用比较麻烦,可以统一启动,安装npm-run-all
$ npm i npm-run-all --save-dev
4.2.2 配置contianer下的package.json
"scripts": {
"serve": "npm-run-all --parallel serve:*",
"serve:box": "vue-cli-service serve",
"serve:web1": "cd subapp/web1 && yarn serve",
"serve:web2": "cd subapp/web2 && yarn serve",
"build": "npm-run-all --parallel build:*",
"build:box": "vue-cli-service build",
"build:web1": "cd subapp/web1 && yarn build",
"build:web2": "cd subapp/web2 && yarn build"
},
运行项目即可
4.3 运行项目后请求子应用跨域
在子应用的build -> webpack.dev.js
中修改
devServer: {
// 由于会产生跨域, 所以加上
headers: {
'Access-Control-Allow-Origin': "*"
},
}
现在的运行结果为:
# 踩坑记录
Q1: webpackJsonp_vue is not defined
A1: 路由中用到异步懒加载引入改为正常引入
// const Home = resolve => require(['@/views/Home.vue'], resolve); // const Login = resolve => require(['@/components/Login.vue'], resolve); import Home from '@/views/Home.vue'; import Login from '@/components/Login.vue';
Q2: Uncaught Error: application 'vueApp1' died in status LOADING_SOURCE_CODE: [qiankun]: You need to export lifecycle functions in vueApp1 entry
A2: 答案是缺少
libraryTarget:'umd'
,需要以包的形式打包, 挂载window
上
Q3:application 'portal-site' died in status LOADING_SOURCE_CODE: Failed to fetch
A3: 检查子应用是否启动以及子应用的端口号是否匹配的上
在这里我的问题是:子应用的配置中的端口号为
8081
,,然而在main.jsd
的apps配置
中出现了8001
的问题,草率了...
Q4: application 'portal-site' died in status LOADING_SOURCE_CODE: [qiankun]: Target container with #portalSite not existed while portal-site loading!
- 目标容器不存在
Target container with #container not existed while xxx loading!
- 目标容container
一开始就不存在Target container with #container not existed while xxx mounting
- 说明刚开始container
容器是存在的,后来因为代码改变了document
,导致container
容器丢失。A4: 当前的子应用挂在容器错误
const apps = [ { name:'portal-site', entry: "//localhost:8081/portal-site", // container: "#portalSite", container: "#app", activeRule: "/portal-site" } ] export default apps;
Q5: Loading chunk {x} failed.
A5: 将路由的异步引入改为同步
【注意】:通过度娘,看到好多人的解释都是路由加载后出现的问题,所以在
router -> index.js
文件中导航守卫
之前加入路由的错误监听,然而并没有
解决上面的问题...
// router/index.js router.onError((error) => { const pattern = /Loading chunk (\d) + failed/g; const isChunkLoadFailed = error.message.match(pattern); const targetPath = router.history.pending.fullPath; if (isChunkLoadFailed) { router.replace(targetPath) } })
遗留问题
- 子应用单独开发
- 打包
- css隔离
- js隔离