最近,咱们团队开源了一套沉淀了2年的前端SPA架构框架,主要是用来解决动态路由的问题。咱们的思路来源于后端,采用中间件的设计模式来架构整个框架。咱们的原则是让你们快速开发一个SPA单页应用,只关心业务逻辑,其余的行为均可以帮助处理掉。javascript
其实咱们的开源比较匆忙,从不少方面看仍是有些不标准的,可是以后咱们会严格按照标准的规范和流程走下去,给你们一份稳定的架构。对于如今开始关注咱们团队的小伙伴,我表示很是的感谢。有你们的支持,咱们会作的更好。html
这个问题从咱们设计的初衷来讲,就是解决全部路由的问题。在业界,其实你们都广泛承认一对一的路由模式,从而生产出了不少路由体系,好比vue-router
和react-router
。他们都是很是不错的架构,从某种意义上来讲,引领了前端路由的前进。不少小伙伴都是直接使用他们的全家桶来开发项目,也获得了很好的效果。前端
我不肯定你们是否考虑过一个问题,当一个用户从登录页面A登录后进入B,再回到A页面的状况。一对一的静态路由其实理论上最终会将A页面显示为未登陆,这是彻底正确的,可是逻辑上它应该是一个登陆的页面。静态路由没法区分环境变量对页面选择的影响,只能经过hook等手段来将页面内容替换掉。理论上,咱们借助后端的写法应该是这样的逻辑:vue
route.get('/a', ctx => {
if (global.logined) {
ctx.render(webveiwA);
} else {
ctx.render(webviewB);
}
})复制代码
咱们须要根据周边的环境变量去自动选择该渲染哪一个页面,如此的逻辑才是咱们想要的。因此咱们会根据这样的思路来设计动态路由。在nodejs
的世界中,这种模式已经很是常见,好比express-router
与koa-router
,都是采用这种设计思路来实现动态话的路由体系。我观察了前端的发展,都没有提出在前端实现这样的逻辑。因而,咱们便开始研究如何将这种思路架设在前端使用,来得到更理想的逻辑体验。java
为何这么说呢?缘由很是简单。在公司里面,咱们大概有90%的H5业务都是采用Miox来实现的。咱们的技术栈实际上是Vue,由于Miox对Vue的结合太过深刻,因此天然有部分小伙伴认为Miox是基于Vue来开发的,也就是说Miox是依赖着Vue?其实不是,Miox并不依赖任何框架实现。我来举个列子:node
咱们的电脑,若是换了一个显卡,那么必需要装显卡驱动。根据不一样的显卡驱动,表现也不一样。若是咱们将Vue看成一个显卡,而Miox看成咱们的电脑,那么咱们须要一份显卡驱动来让整台电脑接受这块显卡。react
因此我提出一个概念,就是渲染驱动的概念。Vue仅仅是咱们Miox的一个渲染引擎,用来渲染页面的,能够理解为模板。咱们还须要一份驱动告诉Miox,来讲明Vue的渲染是如何在Miox实现的。这部分能够从这里看明白。固然,不只仅是Vue,咱们还可以将React接入到Miox中。理论上,只要能提供对应的渲染驱动,均可以将任意的渲染引擎接入到Miox中来使用。天然的,你们的书写都将会变成那种引擎的书写方式。这就是咱们的插拔式设计。在公司里面使用的时候,咱们没必要在乎使用何种渲染引擎,Miox均可以支持,同时帮您管理好整个路由体系。git
在前端,若是咱们可以用中间件来拦截整个逻辑过程的话,对开发是至关有利的,不只仅在代码层面可以提升可读性,同时能够在具体业务层面提升效率。咱们用后端路由逻辑来举个例子:github
当咱们遇到一些API都是须要通过登陆验证的时候,咱们能够将
/authorize
的路由前缀都使用中间件统一处理。其余的都不走验证逻辑。web
const Authorize = require('./auth');
const AuthorizeApi = require('./auth/routes');
route.use(
'/authorize',
Authorize.connect(), // Authorize.connect是统一的验证逻辑代码
AuthorizeApi.routes(),
AuthorizeApi.allowedMethods()
);复制代码
如此当经过/authorize
的路由都须要通过Authorize.connect()
来验证是否具备权限。这使得代码很是简洁易懂。
Miox的设计即是如此,经过这种中间件的架构,使得咱们在前端得到了统一拦截处理的能力。在实际生产中,咱们不少地方都用到了中间件来处理统一的校验逻辑,使得代码维护性获得很大地提高。
那么若是不使用呢?咱们须要在进入页面的时候,每一个页面中都要嵌入一段代码来处理权限问题,不只仅代码量增长,并且对于以后的维护,可能会产生漏改的问题。
具体想了解中间件的小伙伴,我推荐去看下koa-router。
说到缓存,这个话题过于庞大,对于Miox的缓存机制,我只能简单介绍一下,有兴趣的小伙伴能够看下源码。
在开发过程当中,特别是对于开发移动端页面,咱们须要保存前一个页面滚动位置,那么咱们在切换到另外一个页面的时候是不能将前一个页面销毁的,缘由是咱们但愿回到前一个页面的时候仍是停留在以前滚动的位置。那么咱们须要缓存这个页面来确保位置的不改动。Miox模拟了history的部分API,同时增长了一层页面堆栈。咱们须要维护这层页面堆栈来确保页面的可溯性。每次咱们经过一种算法来动态比较路由与页面的关系,从而从这个堆栈中选出咱们想要的缓存页面。固然,没有这个对应关系的时候,咱们天然是要建立的。咱们基于尽最大可能限度复用页面的宗旨,来缓存这些页面与路由的关系。
可能有人要问,若是缓存过多,对于页面的切换会有性能上的影响吧?是的,过多的页面缓存也是阻碍性能的关键。这里咱们进行了缓存个数的优化。在启动miox服务的时候,咱们有一个配置的参数max
,通常默认为一个,固然,你们也能够本身自由设置最大个数,来保障缓存的性能问题。
在History中,咱们都认可没法判断出当前浏览器行为的方向性,因此没法给出咱们想要的方向来自动作页面切换的效果。为了解决这个问题,咱们搜集了业界的解决方案,采用sessionStorage
来模拟history堆栈,从而解决这个问题(新history的API中已经增长了history.index
动态属性来告诉咱们如今位于堆栈中的位置)。咱们将此方案整合到了Miox中,而且对其增强,来告诉动画引擎此次行为在浏览器中是如何表现的。天然,动画引擎就可以根据这个来自动切换页面的动画,达到自动处理的效果。
官方提供了一个简单的模块来支持动画,固然小伙伴想要自定义动画也是很是简单的,具体见这里。
在传统的MVX框架中提供了组件的生命周期,咱们在某种意义上也认为是页面的生命周期,可是咱们对比原生IOS的周期行为,仍是有所欠缺的。好比说active
生命周期。这个是什么意思呢?我来举例说明:
当一个页面被推入后台,又被唤起的时候,咱们根本不知道它是否是再一次被激活,咱们只能知道页面又一次被进入,第二次进入的概念是须要不少代码来辅助完成的。而在传统框架中,很难触发再一次的
mounted
生命周期,由于页面已经被mounted
过了。Miox提供了这样的生命周期的定义。
// use vue.js
export default Vue.extend({
mounted() {
this.$on('webview:active', this.activeLife);
},
methods: {
activeLife() {
console.log('我被唤起了');
}
}
})复制代码
固然,咱们也能够将这些生命周期直接抛到全局去,用于全局的监控。
app.on('webview:mounted', webview => {
console.log(webview);
})复制代码
对于前置的生命周期,咱们一样提供了如下的生命周期来辅助:
这些周期可以让你很好地掌控整个过程,对于自动埋点什么的功能很是实用。
目前主流的架构都支持了服务端渲染来加强SEO的能力,那么对于Miox而言,也须要支持他们的服务端渲染。考虑到Miox自身会给渲染出来的内容包裹一些代码,因此,咱们须要本身实现SSR。固然,渲染引擎的SSR实现是交给本身来完成的。也就是说咱们须要给他包裹一层SSR渲染。
Miox暂时支持Vue的SSR渲染,后续会逐步添加对于React的SSR渲染。还有好比百度的san.js
其实也能够接入进来实现SSR渲染。服务端渲染并非太麻烦,若是你们可以掌握Miox的运行原理的话。
对于开源,咱们团队内部作了不少努力,也咨询了不少大牛,但愿可以给你们创造出一份简易开发的架构来帮助你们完成业务。目前,团队后续计划以下:
但愿你们看到这篇文章后能够支持咱们,给咱们多提供一些意见和建议,让咱们共同将Miox完善下去。喜欢的小伙伴,帮忙点个Star。
项目开源在 GitHub: 51nb/Miox