手把手教你写一个简易的微前端框架,零基础学前端开发

import(‘http://localhost:8001/js/app.js’)

},

mount() {

// …

},

unmount() {

// …

},

})

},

activeRule: (location) => location.hash === ‘#/vue’

})

这种方式也不靠谱,每次子应用的入口资源文件变了,主应用的代码也得跟着变。还好,我们有第三种方式,那就是在注册子应用的时候,把子应用的入口 URL 写上,由微前端来负责加载资源文件。

registerApplication({

// 子应用入口 URL

pageEntry: ‘http://localhost:8081’

// …

})

“自动”加载资源文件

现在我们来看一下如何自动加载子应用的入口文件(只在第一次加载子应用时执行):

export default function parseHTMLandLoadSources(app: Application) {

return new Promise(async (resolve, reject) => {

const pageEntry = app.pageEntry

// load html

const html = await loadSourceText(pageEntry)

const domparser = new DOMParser()

const doc = domparser.parseFromString(html, ‘text/html’)

const { scripts, styles } = extractScriptsAndStyles(doc as unknown as Element, app)

// 提取了 script style 后剩下的 body 部分的 html 内容

app.pageBody = doc.body.innerHTML

let isStylesDone = false, isScriptsDone = false

// 加载 style script 的内容

Promise.all(loadStyles(styles))

.then(data => {

isStylesDone = true

// 将 style 样式添加到 document.head 标签

addStyles(data as string[])

if (isScriptsDone && isStylesDone) resolve()

})

.catch(err => reject(err))

Promise.all(loadScripts(scripts))

.then(data => {

isScriptsDone = true

// 执行 script 内容

executeScripts(data as string[])

if (isScriptsDone && isStylesDone) resolve()

})

.catch(err => reject(err))

})

}

上面代码的逻辑:

1.利用 ajax 请求子应用入口 URL 的内容,得到子应用的 HTML2.提取 HTML 中 script style 的内容或 URL,如果是 URL,则再次使用 ajax 拉取内容。最后得到入口页面所有的 script style 的内容3.将所有 style 添加到 document.head 下,script 代码直接执行4.将剩下的 body 部分的 HTML 内容赋值给子应用要挂载的 DOM 下。

下面再详细描述一下这四步是怎么做的。

一、拉取 HTML 内容

export function loadSourceText(url: string) {

return new Promise((resolve, reject) => {

const xhr = new XMLHttpRequest()

xhr.onload = (res: any) => {

resolve(res.target.response)

}

xhr.onerror = reject

xhr.onabort = reject

xhr.open(‘get’, url)

xhr.send()

})

}

代码逻辑很简单,使用 ajax 发起一个请求,得到 HTML 内容。e807c2aa034fba2d7d22641215a10f57.png上图就是一个 vue 子应用的 HTML 内容,箭头所指的是要提取的资源,方框标记的内容要赋值给子应用所挂载的 DOM。

二、解析 HTML 并提取 style script 标签内容

这需要使用一个 API DOMParser[13],它可以直接解析一个 HTML 字符串,并且不需要挂到 document 对象上。

const domparser = new DOMParser()

const doc = domparser.parseFromString(html, ‘text/html’)

提取标签的函数 extractScriptsAndStyles(node: Element, app: Application) 代码比较多,这里就不贴代码了。这个函数主要的功能就是递归遍历上面生成的 DOM 树,提取里面所有的 style script 标签。

三、添加 style 标签,执行 script 脚本内容

这一步比较简单,将所有提取的 style 标签添加到 document.head 下:

export function addStyles(styles: string[] | HTMLStyleElement[]) {

styles.forEach(item => {

if (typeof item === ‘string’) {

const node = createElement(‘style’, {

type: ‘text/css’,

textContent: item,

})

head.appendChild(node)

} else {

head.appendChild(item)

}

})

}

js 脚本代码则直接包在一个匿名函数内执行:

export function executeScripts(scripts: string[]) {

try {

scripts.forEach(code => {

new Function(‘window’, code).call(window, window)

})

} catch (error) {

throw error

}

}

四、将剩下的 body 部分的 HTML 内容赋值给子应用要挂载的 DOM 下

为了保证子应用正常执行,需要将这部分的内容保存起来。然后每次在子应用 mount() 前,赋值到所挂载的 DOM 下。

// 保存 HTML 代码

app.pageBody = doc.body.innerHTML

// 加载子应用前赋值给挂载的 DOM

app.container.innerHTML = app.pageBody

app.mount()

现在我们已经可以非常方便的加载子应用了,但是子应用还有一些东西需要修改一下。

子应用需要做的事情

在 V1 版本里,注册子应用的时候有一个 loadApp() 方法。微前端框架在第一次加载子应用时会执行这个方法,从而拿到子应用暴露的三个方法。现在实现了 pageEntry 功能,我们就不用把这个方法写在主应用里了,因为不再需要在主应用里引入子应用。

但是又得让微前端框架拿到子应用暴露出来的方法,所以我们可以换一种方式暴露子应用的方法:

// 每个子应用都需要这样暴露三个 API,该属性格式为 mini-single-spa-${appName}

window[‘mini-single-spa-vue’] = {

bootstrap,

mount,

unmount

}

这样微前端也能拿到每个子应用暴露的方法,从而实现加载、卸载子应用的功能。

另外,子应用还得做两件事:

1.配置 cors,防止出现跨域问题(由于主应用和子应用的域名不同,会出现跨域问题)2.配置资源发布路径

如果子应用是基于 webpack 进行开发的,可以这样配置:

module.exports = {

devServer: {

port: 8001, // 子应用访问端口

headers: {

‘Access-Control-Allow-Origin’: ‘*’

}

},

publicPath: “//localhost:8001/”,

}

一个完整的示例

示例代码在 examples 目录。

registerApplication({

name: ‘vue’,

pageEntry: ‘http://localhost:8001’,

activeRule: pathPrefix(‘/vue’),

container: $(‘#subapp-viewport’)

})

registerApplication({

name: ‘react’,

pageEntry: ‘http://localhost:8002’,

activeRule:pathPrefix(‘/r

  • 16
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值