# 前端模块化设计

本方案是一个预研项目的产出,只做出了 DEMO,后因非技术原因,方案没有落地实践。

# 背景

某些页面展示逻辑复杂,比如点评 APP 首页,用户看到的页面结构是动态的,与所选城市、地理位置、APP 版本相关。
即使 web 端发版迅速,没有 Native 类似的兼容问题,如果所有逻辑代码维护成本也会越来越大。

# 目标

  • 设计一套方案,使 web 页面支持根据配置动态化,配置变化无需发布版本。
  • 支持根据用户属性(如城市)或随机,动态展示或隐藏某些模块。

# 定位

一个基于 Vue 生态、轻量、配置简单但灵活受限的页面动态模块化库。

# 工作原理图

# 页面配置管理

  1. 需要一个服务来管理动态配置。
  2. 每个页面对应一个配置,最好开放一个接口聚合所有配置。
  3. 应用启动时获取配置,定时更新配置。(原理图是针对服务端渲染项目,SPA 应用获取配置的过程可能会降低页面性能,如果性能敏感需要考虑其他优化途径。如 service-worker、preload)

# 模块打包、注册

  1. 使用 webpack 提供的 import 函数将所有模块打包成异步的独立 bundle。
  2. 所有异步模块引用都传递给配置解析器(是一个 Vue render function 组件),根据页面配置,实际使用的模块被解析时将被异步获取。vue 异步组件文档 (opens new window)
const ASYNC_COMPS = {
  Star: () => import('./star.vue'),
  Foo: () => import('./Foo.vue'),
};

const Page = Vue.extend({
  // 其他逻辑...

  components: {
    ...ASYNC_COMPS,
  },
});

# 配置解析生成页面

采用 yaml 配置,会被转化成 json

  1. 每个页面配置至少包含datadom两个属性。
  • dom:页面模块结构的组织关系(树形结构),[tag, attrs, children][fn-*, ...args]的递归形式。
  • tag 首字母大写表示业务模块组件,小写表示原生的 html 标签。
  • fn-* 表示自定义函数,将在配置解析阶段由自定义解析器解析。(更像 Lisp 中的函数)
    • 自定义函数的返回值必须为[tag, attrs, children][fn-*, ...args]形式。
  • data:自定义配置信息,作为dom自定义函数执行的作用域。
  1. 根据用户访问的路由获取对应的页面配置,配置解析模块将配置解析成 Vue 需要的 VNodes。解析过程:

  2. 解析dom中的自定义函数,返回完全的[tag, attrs, children]递归形式。

    • 自定义函数中的表达式(如exp = _S.cityIds.includes(_S.cityId))可使用JSnew Function('_S', ${exp})(scope)包装执行。
    • scope(即_S) = userInfo(访问用户的信息) + cfg.data(前文配置中的data属性)。
  3. 自定义Vue render函数,将解析后的dom(递归传递给Vue createElement函数)转化成 VNode。

  4. Vue render函数渲染异步组件时,将自动获取,已在components中注册但未在 render 中渲染的异步组件不会被获取。

const ASYNC_COMPS = {
  Star: () => import('./star.vue'),
  Foo: () => import('./Foo.vue'),
};

const cfg = {
  '/page1': {
    data: {
      /* ... */
    },
    dom: [
      /* ... */
    ],
  },
};

function parseCfg(dom, h) {
  // 递归将dom转换成VNode
}

const Page = Vue.extend({
  // 其他逻辑...
  render(h) {
    return paseCfg(cfg.dom, h);
  },
  components: {
    ...ASYNC_COMPS,
  },
});

至此,已经可以将模块通过配置进行动态组合生成页面了。

# 获取模块依赖的数据

通常业务模块都需要接口数据。同构项目,一般在组件的asyncData函数中获取数据。
采用模块化方案后,各模块(组件)都通过配置进行动态组合,其asyncData可以交给顶层的Page组件来执行。

  1. 执行Page.asyncData时解析配置中的dom得到所有依赖的异步组件的工厂函数(()=> import(/* xxx component */))。
  2. 执行所有工厂函数得到 Vue component。
  3. 执行所有 Vue component 的asyncData函数。
//同构框架会执行页面组件的Page.asyncData
Page.asyncData = function ({ store, route }) {
  let cfg = store.state.modularization.PAGES_CFG[routePath];

  if (!cfg || !cfg.dom) {
    return Promise.resolve();
  }

  // 解析自定义函数后,可以得到所有依赖的异步组件
  cfg.dom = parseFn(
    cfg,
    // 执行自定义函数的作用域 除了配置的cfg.data 还包括rootState
    // rootState中包含一些基础信息,如何用户所选城市,用户信息等
    Object.assign({}, store.state.rootState, cfg.data)
  );

  return Promise.all(
    pickCustomCompTags(cfg.dom).map((tag) => {
      const c = ASYNC_COMPS[tag];
      if (!c) return Promise.resolve();

      return c().then((comp) => {
        if (typeof comp.default.asyncData === 'function') {
          return comp.default.asyncData({ store, route });
        }
        return Promise.resolve();
      });
    })
  );
};

# 异步组件构建优化

由于所有异步模块(Vue 组件)将单独获取,可能产生较多的请求,对前端性能有一定影响。
可以配合 service worker 与缓存 + http2 能最大限度降低影响。

💗 博主正处于裸辞待业状态,欢迎 商务合作 💗

相关文章

从 React 看前端 UI 代码范式革命

alt text 前言 本来打算写的主题是“我为什么讨厌 React Hooks API”,展开聊聊“小甜甜”是如何变成“牛夫人”的,没想到越写越严肃:) React 是两次前端范式革命的引领者,至今仍有繁荣的社区和旺盛的创造力; React 多次天才又激进的创新,一些想法被借鉴改良、一些引发广泛质疑,大部分是被认同和接受的; ...

WebCodecs 性能表现及优化思路

笔者开源 WebAV 已经一年半,还写了系列文章帮助初学者入门 Web 音视频。 之前一直隐隐担心在 Web 平台处理音视频与 Native APP 会有明显性能差距,因为 WebCodecs API 毕竟被浏览器代理了一层,且一些数据处理需要 js 配合,不确定有多大的性能损耗。 相信刚接触 WebCodecs 的读者也非常关心它的性能表现如何。 ...

Web 网页自集成 HTTP 代理方案

前言 大部分程序员,想必都有会一个常用的抓包代理工具; 但在座的各位,可曾见过这样一款集成在 Web 应用中的代理工具? 它是明显区别于传统代理工具的,有以下特性: 零安装,零配置,Web 点击即用、APP 扫码即用;_(不 ...

Web 文件系统(OPFS 及工具)介绍

文件系统是往往是构建大型软件的基石之一,很长一段时间 Web 平台因缺失成熟的文件系统成为构建大型软件的阻碍,如今 OPFS 可弥补这一缺憾。 本文介绍 OPFS 背景和基本使用方法、使用过程中的注意事项,及如何配合笔者开源的 opfs-tools、opfs-tools-explorer 两个项目,充分发挥 OPFS 的性能与开发效率。 Web 存储 A ...

opfs-tools (文件系统 API) 项目介绍

文件系统是许多领域程序的基石,所有通用编程语言都会内置完备的文件系统 API。 Web 很长一段时间没有提供完善的访问文件系统的规范,使得需要高频读写文件、大文件处理软件在 Web 端都会受到一些限制,比如音视频剪辑、游戏、数据库等等。 之前在浏览器中实现视频裁剪、截帧等相关功能时,发现缺少基本的操作文件的 API,比如读写、移动、复制文件。 而 [OPFS] ...

Web 终极拦截技巧(全是骚操作)

拦截的价值 > 计算机科学领域的任何问题都可以通过增加一个中间层来解决。 —— Butler Lampson 如果系统的控制权、代码完全被掌控,很容易添加中间层; 现实情况我们往往无法控制系统的所有环节,所以需要使用一些 “非常规”(拦截) 手段来增加中间层。 拦截的方法 拦截/覆写 浏览器 API 最常见的场景有通过拦截 console ...

Web 音视频(六)图像素材处理

Web 音视频目录 前序章节介绍了如何在浏览器中解析、创建视频,以及给视频添加一些自定义素材(图片、音频、文字...); 本章介绍如何给图像素材加特效、加动画,实现转场、移动水印、图像滤镜美化等功能。 你可以跳过原理介绍,直接查看 WebAV 示例 素材动画 在视频制作中实现动画跟其他场景略有不同,因为视频 ...