心之所向, 勇往直前!
记录开发过程中的那些小事, 给自己加点经验值.
前言
作为一个. Net 后端开发, 在竞争愈加激烈的当下, 掌握点前端配菜好像已经是家常便饭了.
刚好在工作的第 5 个年头, 辞去小主管职务的我要再次踏上面试之路, 为了要避免被面试官吊打, 除了复习《吊打面试官》相关的题目, 当然也要对自己掌握的技能温故知新.
项目使用了 vue cli3.0 作为基础架构, 这个版本和 2.0 的有一些不同. 具体参考:
1. 《vue cli3.0 快速搭建项目详解》
2. 《vue-cli2.0 与 vue-cli3.0》
环境
技术栈
上面是项目的一些基本情况, 至于实际开发用到的组件这个每个人的项目都有可能不同, 这里就不贴出来了; 而且这个系列只是对一些关键点进行记录和说明, 其他的在网上都可以找到资料的内容就不再重复.
架构
微服务这个词可以说是大火特火, 现在很多应用都在逐步朝着这方面转移.
这个架构的好处, 我想是不言而喻的. 浅显点理解就是独立运行, 灵活, 扩展性强.
在调整后端架构的同时, 我就想前端能不能也实现这种模式? 在查找了几天资料 (主要参考《滴滴 webapp 5.0 Vue 2.0 重构经验分享 https://github.com/DDFE/DDFE-blog/issues/13 》) 理清思路后, 就抽出空余的时间之后就搞出这一套架构. 不过距离真正的微前端还是有些差距. 毕竟现在前端的框架那么多(Vue,React,Angular 等等, 如果要兼容每个框架, 那么可能会出现一些预加载组件出现冗余, 导致主页加载缓慢.)
常见方案
ifreame: 简单易实现, 但冗余 html 而且对 SEO 不友好
WebComponents: 基本能实现功能, 但兼容性不太行而且只对高版本浏览器有效(这是废话, 用了 Vue 已经放弃 IE)
在这里框架中我采用的以 Vue 为核心实现模块化加载.
核心思路
主要通过一个中央处理器(可以理解为浏览器或者 iframe)
处理器主要用于解析后端返回的模块 Url, 根据地址发起 Http 请求拿到子模块的 index.HTML. 这个文件的容量很小, 但是里面记录了该模块需要用到的 CSS 和 JS 文件相对路径. 然后通过正则表达式解析出 script 标签, style 标签. 最后将标签加载到主页的最底部(利用浏览器自动加载文件的特性), 完成了子模块的 Async 加载.
子模块拥有自己独立的领域逻辑, 组件, API 接口文件(为了防止冲突, 对命名有所规范). 各个模块之间相互独立, 一般不会出现引用相同的插件的情况, 造成项目冗余.
如图:
代码:
- reLoadWebsite (host, HTML) {
- // 解析内容页中的 CSS/JS 引用, 并插入父页面文档底部
- let temp = []
- let text = HTML
- const page = { content: HTML, scripts: [], CSS: [] }
- const regScript = /<script[\s]+(?:[^>]+=[\s]*[^>]+)*(?:src[\s]*=[\s]*['|"]?([^>]+(?:\.js))['|"]?)><\/script>/i
- while ((temp = regScript.exec(text)) != null) {
- text = text.replace(temp[0], '')
- if (temp[1] && temp[1].length > 0) page.scripts.push(host + temp[1])
- }
- const regCss = /<link[^>]+(?:href=['|"]?([^>]+(?:\.css))['|"]?)[^>]*>/i
- temp = []
- text = HTML
- while ((temp = regCss.exec(text)) != null) {
- text = text.replace(temp[0], '')
- if (temp[1] && temp[1].length > 0) page.CSS.push(host + temp[1])
- }
- this.loadCss(page.CSS)
- this.loadScripts(page.scripts)
- },
- loadCss (CSS) {
- var HTML = $('html').HTML()
- for (var i = 0; i < CSS.length; i++) {
- if (HTML.indexOf(CSS[i]) < 0) {
- var link = document.createElement('link')
- link.type = 'text/css'
- link.rel = 'stylesheet'
- link.href = CSS[i]
- document.body.appendChild(link)
- }
- }
- },
- loadScripts (scripts) {
- var HTML = $('html').HTML()
- for (var i = 0; i < scripts.length; i++) {
- if (HTML.indexOf(scripts[i]) < 0) {
- var script = document.createElement('script')
- script.type = 'text/javascript'
- script.src = scripts[i]
- document.body.appendChild(script)
- }
- }
- },
路由装载
主模块中加载 Vue-Router, 先把一级路由创建出来.
然后在 main.JS 中将 Vue 等公共对象暴露到 Windows 对象中, 同时暴露一个 registerChildRoutes 方法, 让子模块可以把独立的路由注册到主路由中. 这样就可是实现模块化装载的功能了. 基本上到了这步, 已经是简单版的微前端框架. 当然如果想要架构更加完整和坚固, 还需要做更多的处理.
- // 全局
- const router = Router
- const store = Store
- Windows.Vue = Vue
- Windows.AppData = {
- Router,
- Store,
- Error,
- registerChildRoutes: (routes) => {
- const index = router.options.routes.find(w => w.name === INDEX.name)
- if (index) {
- routes.forEach(e => {
- if (index.children.findIndex(w => w.name === e.name) < 0) {
- index.children.push(e)
- }
- })
- }
- const newRouter = new VueRouter(router.options)
- router.matcher = newRouter.matcher
- }
- }
结语
本篇到此结束, 如果有任何疑问或者指正, 请发表在评论区.
下一篇将讲述《自动构建路由》, 以及子模块的接入
来源: https://www.cnblogs.com/AssertionBird/p/12408061.html