最近在开发过程中,有个需求是在自己开发的应用中(以后简称:主应用)挂载第三方的页面应用(以后简称三方应用),大多数情况下,自己工程引入第三方工程页面用的是iframe嵌套,简单快捷,但是上下文处理、事件交互等方面并不是特别方便,于是就尝试另外的解决方案,当我看到vue的路由守卫,就知道问题该如何解决,大致思路如下:
利用路由独享的守卫beforeEnter,在跳转到第三方相关页面前,必定经过beforeEnter路由函数,然后我们在这个路由函数写我们逻辑,区别这个url是我们需要的第三方url还是错误的url,如果是我们需要url地址则会加载我们需要的第三方的js,加载三方应用js的时候,调用主应用中我们声明的接口(一个全局函数),然后把第三方的路由加载到我们主应用上,这里的两个应用都是webpack打包后,让我们实战吧。
1、主应用路由代码,位置:@/src/router/index.js,这里有个知识点,这个地方路由规则中'*'的优先级要低于其它路由规则,比如说,要访问index/coma这个url,因为已经匹配到第一个路由规则,所以不会进入beforeEnter
......
import { handleRoute } from '../utils/route_combine'
import Layout from '@/layout/index.vue'
export const THIRD_PARTY_THOKEN = 'third'
const routes = [
{
path: '/index/coma',
component: (resolve) => require(['@/views/components/com-a/index'], resolve)
},
....
{
path: '404',
component: (resolve) => require(['@/views/error-page/404'], resolve)
},
{
path: '/third',
name: THIRD_PARTY_THOKEN,
component: Layout,
children: []
},
{
path: '*',
async beforeEnter(to, from, next) {
// 第三方业务线拦截
const isService = await handleRoute(to, next, asyncRouter)
// 非第三方业务线页面,走找不到
if (!isService) {
next('/404')
}
}
}
]
export default new Router({
routes
})
2、handleRoute函数代码,位置:@/src/utils/route_combine.js,声明一个三方应用配置,如果我们在handleRoute过程中,发现url属于三方应用的配置,则去加载三方应用的js
// let config = null;
const config = {
thirdparty: {
src: [
'third/third-02/app.bundle.js' // 这个就是打包后的三方应用的js
],
loaded: false
}
}
// const getConfig = () => {
// axios({
// url: 'your-url',
// method: 'get',
// params: 'parameter'
// }).then(data => {
// config = data;
// })
// }
// 加载第三方js
const loadScript = (src) => {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = src
script.async = true
script.onerror = () => {
reject()
}
script.onload = () => {
resolve(true)
};
(document.head || document.body).appendChild(script)
})
}
// 整合第三方路由
export const handleRoute = async(to, next, asyncRouter) => {
const path = to.path || ''
const paths = path.split('/')
const serviceName = paths[1]
// if (!config) {
// await getConfig()
// }
const cfg = config[serviceName]
// 非业务线路由
if (!cfg) {
next()
return false
}
if (cfg.loaded) {
next()
return true
}
for (let i = 0; i < cfg.src.length; i++) {
await loadScript(cfg.src[i])
}
cfg.loaded = true
next({ ...to, replace: true }) // 继续请求页面
return true
}
3、主应用中声明的接口,位置:@/src/main.js,为了方便,直接在main.js中声明了
import router, { THIRD_PARTY_THOKEN } from './router'
......
const router = new Router({
routes: constRouter
})
const registerApp = (routes) => {
if (routes) {
routes.forEach(route => {
// 添加一条新的路由记录作为现有路由的子路由
router.addRoute(THIRD_PARTY_THOKEN, route)
})
}
}
// 声明注册路由的全局接口
window.mainApp = Object.assign(window.mainApp || {}, {
Vue,
router,
ElementUI: Element,
utils: {
registerApp
}
})
4、三方应用调用主应用声明的接口,位置:@/src/main.js
let { Vue, router, ElementUI } = window.dandelionApp;
import { routes, APP_NAME } from './router/index'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets
import '@/styles/index.scss' // global css
// Vue.use(ElementUI)
window.mainApp && window.mainApp.utils.registerApp(routes)
export default Vue;
console.log(Vue, router, APP_NAME, ElementUI)