易盾移动端同构实践,几步改善官网交互体验

发布者:网易易盾
发布于:2021-08-05 17:42

背景


为了首屏渲染更快,让用户更快看到内容,网易易盾开启了移动端官网同构建设,即一套代码两端运行, 客户端代码直接运行在服务端, 无需编写冗余的渲染视图。


改造前,官网项目的技术栈较大众,以 egg.js 为后端框架,Nunjuck 为模板,前端采用 jQuery。


本次则采用Vue 作为前端框架进行了部分改进,团队成员的技术储备也基本上建立在 Vue 的技术体系上,从以下3方面出发,重整技术选型,全面改善易盾的官网生态。


统一技术栈:Nunjuck 模板很强大,但和我们习惯用的 Vue 语法还是有些不同。而 jQuery 属于旧时代的产物,数据驱动视图的思想已经深入人心。


用户体验:同构后的前端能兼顾 SEO 和 SPA 的交互体验。


开发体验:更易和现代的开发构建工具集成,如 webpack、ESLint、SASS 等。



技术选型


方案一:Vue SSR 官方指南 https://ssr.vuejs.org/zh/

项目地址:https://github.com/vuejs/vue-hackernews-2.0/


优缺点分析:

1.自由度最高,不依赖 node 框架和 vue 相关任何生态

2.需要的工作量最大,主要是 vue-ssr 通用逻辑 & 前端构建配置的实现

3.可复用目前基于 eggjs 的后端逻辑


方案二:nuxt.js


优缺点分析:

1.完善的解决方案,提供了许多开箱即用的功能,能大大减少很多配置和通用逻辑实现

2.不依赖 node 框架,主要关注的是应用的 UI 渲染

3.需要完成 egg.js 后端 api 接口的逻辑迁移


方案三:egg.js 和 nuxt.js 结合

以 egg.js 为主,nuxt.js 降级为 egg.js 一个中间件。这种方案可行得益于 nuxt.js 不依赖 node web 框架。


1.复用目前 node 端逻辑,仅对视图做迁移

2.利用 nuxt.js 提供的开箱即用的特性,无需关注 vue-ssr 通用逻辑实现和前端复杂的构建配置

3.工作量之一需要完成 eggjs 和 nuxt.js 的结合,提供一个 eggjs 中间件


方案三既能利用了 nuxt.js 的能力,且无需对目前 node 端逻辑做改动,很好的满足了我们的需求。基于该方案的流程图如下:

图片



落地过程


【egg-nuxt-render 插件的实现】


插件中间件的实现关注点:

图片


  1. 请求未命中 egg 路由时会返回 404,中间件处理 404 请求,修改 status 为 200,后续交于 nuxt.js 处理。如果 nuxt 路由中也未命中,由 nuxt 提供 404 页面。

  2. 传递 egg ctx,使得 nuxt 中能获取到 egg 的上下文。

  3. 调用 nuxt.render,即完成将当次请求交于 nuxt.js 处理。



【项目目录】


至此,我们可以这样设计我们的目录结构。仅需在 eggjs 根目录下新增 client 目录,来放置 nuxt.js 的所需的各种目录(每个目录的含义可参考 nuxt 官方文档)。nuxt.config.js 为 nuxt 的配置文件,会被 egg-nuxt-render 插件读取。


图片



【页面迁移】


到了这一步,我们就可以开始迁移我们的页面了,即 Nunjuck 模板重构为 vue 模板。删除原先 egg router 中的页面路由定义,然后在 client/pages 里添加对应的页面.vue。这一步是体力活,也是本次同构最费时间的工作。看到这里有人可能会疑惑,这种方案需要要求所有的页面一次性重构完成,如果项目很庞大,没办法一次性重构完或者或者一次性重构完风险极高的情况下,是不是就很难实施了?考虑到这种合理的情况,设计了另一个兼容方案,给大家提供一种思路。



【兼容方案】


因为我们团队的项目规模可控,故采用一次性重构完所有页面。


方案流程图如下:

图片

注意:本方案未经线上测试,仅供参考


这种方案的核心想法是先渲染 Nunjuck 模板,然后通过 ctx 将渲染结果传递到 nuxt 的 vue 模板完成最终渲染。主要可以关注以下几点:


1.保留 eggjs 页面路由的定义,调用 htmlRender 将 Nunjuck 模板转化成 html 字符串,并且保存到ctx.__HTML_RENDER__。此时需要将 status 置为 404。 


图片


2.由于 status 为 404,会执行 egg-nuxt-render 中间件,进入 nuxt 路由。但此时 nuxt 中并没有该路由(因为该页面未重构),所以我们将其指到同一个组件,即 NjkWraper.vue。


图片


3.NjkWraper.vue 负责渲染 __HTML_RENDER__ 内容。__HTML_RENDER__ 应仅包含每个页面的主体部分,不包含页面通用部分(如导航、底部等),此部分由 vue 实现。


图片



4.由于所有的路由都会命中 NjkWraper.vue,会导致真实的 404 请求丢失。所以我们还需要做一步判断该请求是不是真实的 404 请求。如果是,则需要重定向到 404 页面。这一步可在 vue router 守卫中实现。


图片


至此,我们只要先将页面通用部分,如导航、底部等,通过 vue 重构,具体页面后面可以慢慢迁移。


兼容方案存在的弊端:


页面间跳转必须刷新页面。因为最终输出到浏览器的页面中会包含 vue 组件 和 Nunjuck 渲染出来的内容,其中 Nunjuck 内容中可能存在 jquery、regular 等各种库,重复引入恐有问题。


所有页面重构完后,兼容代码需要删除。



总结


本次的同构实践本质上是对已有项目的重构,考虑了系统稳定性和重构成本,故未对 egg.js 主框架做调整。当开启一个新的项目时,直接采用 nuxt.js 提供的完整的解决方案会更加纯粹。




声明:该文观点仅代表作者本人,转载请注明来自看雪