TNTWeb – 全称腾讯新闻前端团队,组内小伙伴在 Web 前端、NodeJS 开发、UI 设计、挪动 APP 等大前端畛域都有所实际和积攒。
目前团队次要反对腾讯新闻各业务的前端开发,业务开发之余也积攒积淀了一些前端基础设施,赋能业务提效和产品翻新。
团队提倡开源共建,领有各种技术大牛,团队 Github 地址:https://github.com/tnfe
本文作者 ancai,我的项目地址:https://github.com/ancai/nvm
一、前言
本文是本人在之前的业余时间做过的一个小我的项目的实际总结,次要是介绍对于古代 Web 前端框架的设计实现思路,参考了 Vue2.x 版本的设计思维。
二、MVVM 模式
web 开发的程序员对于 MVC 的设计思维应该并不生疏,网上相干的文章也有很多。M(model)-V(view)-C(Controller) 代表了一种程序组织架构的模式,实现了数据、视图和逻辑的无效解耦。M,示意数据状态,V 代表视图界面,Controller 用于解决数据和视图之间的交互逻辑。后端的 web 框架(如 Java 中的 springMVC、Struts2;nodejs 中的 express、koa 等)仍然沿用此设计模式至今。
不过在前端畛域,波及到用户界面的编程,MVC 的架构模式当初曾经不是支流了。究其原因,次要有几方面:
- 业务逻辑和 UI 混合在一起
- 不利于扩大和复用
- 无奈验证测试
- 减少数据的复杂性
起初微软提出了 MVVM 的架构模式,其中和 MVC 的区别之处是 VM 取代了 C,即 ViewModel(视图模型)替换了控制器。次要利用在用户界面编程的畛域,提供了双向的数据绑定性能。View 的变动会主动反映在 ViewModel 上,通过 ViewModel 来告诉模型,数据模型 Model 变动时同样通过 VM 来关联到视图。MVVM 架构模式的呈现给前端的开发带来了新的活力,使得前端开发更容易编写单元测试。同时借助于 ViewModel 的两头桥梁,开发人员不必再关注一些 DOM 操作的交互细节,程序更简洁和优雅,联合组件化的编程设计思维,前端模块更易复用和拓展。
ViewModel,个别由框架来实现,次要是提供了双向数据绑定性能。
更多对于的比拟举荐两篇文章: 什么是 MVVM 框架?|MVC vs MVVM: Key Differences with Examples
三、流程架构
我的项目外面波及到对模板、视图、数据、指令以及依赖的治理。
从大的方面看,框架次要实现了模板和数据之间的双向绑定。应用数据劫持的相干技术,对数据的变动的进行追踪,同时引入公布订阅模式实现对属性的依赖治理。另一方面则波及模板的解析,提取模板中的指令和表达式,关联到数据,来初始化视图。模板和数据通过 watcher 衔接起来,这样数据的变动间接映射到视图;同时须要监听视图上的输出相干的事件,这样当输出变动时间接映射到数据模型。
从工程解决的角度上看,能够分成四个步骤:初始化、数据监测、模板编译和框架性能。初始化的过程次要是取得调用方传递的配置信息,模型数据和模板的预处理,某些要害属性代理到下层。数据监测的环节,波及对一般 JavaScript 对象的劫持解决,针对数组的加强解决,通过一个公布 - 订阅模式实现对数据属性的依赖注入,同时关联到 watcher。模板编译环节,解析一个相似 HTML 的模板,提取外面的变量表达式和相干指令,通过 watcher 关联到数据模型。
下面给出了我的项目中的外围类图,Observer 实现对数据的劫持,Dep 相当于是一个公布 - 订阅器同时关联到 Watcher 和 Observer,Compile 负责解析模板,进一步提取取得 Directives 类同时关联到 Parser 和 Watcher,Parser 用于解析指令生产视图 UI,Watcher 应用到一个 Batcher 来批量解决更新变动。
我的项目代码的目录构造比较简单,src 目录下总共有 14 个代码文件,总代码行数在千行左右,了解起来不是很艰难。index.js 为入口文件,模板编译相干的解决逻辑均放在 compile 文件夹下,observer.js 文件搁置数据劫持相干的解决逻辑,value.js 外面是字符串类型的属性表达式转化为对象数据的解决逻辑,dep.js 实现了一个简略的公布 - 订阅模式来关联数据状态的变动以及 watcher 实例,watcher.js 外面有具体的正文阐明在哪个环节被调用。另外,整个我的项目应用 webpack 简略构建,打包后的体积也很小。
四、数据劫持实现
利用 ES5 中提供的 object.defineProperty 这一 API,从新定义 get 和 set 属性拜访器实现对数据的劫持。次要是对一般对象和数组的加强解决,监测数据的变动,告诉依赖。在首次拜访属性时注入依赖,通常产生在首次初始化模板时,取得状态属性的表达式,在此时注入状态属性的依赖,这里的依赖就是 watcher 实例;当数据发生变化时,通过 dep 的公布 - 订阅模式告诉 watcher 实例,进而更新视图状态。
五、模板编译实现
模板的编译,没有应用虚构 DOM 结点,间接从原始的 html 模板动手,遍历字符串模板,收集外面的指令。同时解析表达式,取得对模型数据的依赖注入,在首次加载时,会关联到 watcher 上。外面针对不同类型的指令,对应不同的解析处理器,分为一般指令、事件指令、双向绑定指令、if/for 指令等。其中波及到 if/for 的指令,须要创立新的执行上下文,关联子级的环境数据。
六、要害代码实现
下面两张图时对于依赖管理器和 watcher 的简略实现,依赖管理器能够视作一个公布 - 订阅模式的设计,即通过 Dep 类 watcher 订阅了模型数据,当模型数据变动时,watcher 就晓得了。有面图时 watcher 的简略解决逻辑,当模版编译解析出表达式时,就会创立 watcher 的实例,同时拜访模型数据的值时,将该新建的 watcher 实例注入进去。就这样,watcher 实例充当了模版编译和模型数据的桥梁。
七、总结
本我的项目没有应用到 virtual node(虚构 DOM 结点),间接由字符串模板编译成了视图局部。其中略微有点难了解的局部:属性表达式的提取解析、依赖的注入、if 和 for 指令的解决、watcher 的实现。
作为一个能力较弱的程序员,还无奈像前端技术大牛那样发明出一个成熟的前端框架。通过本文的简略论述,心愿能帮忙到有雷同困惑的小伙伴加深对 MVVM 以及前端框架原理的了解,大牛请主动疏忽。本我的项目的残缺源码参见最初局部,给出了具体的示例。
八、代码示例
源码
示例
九、团队
TNTWeb – 腾讯新闻前端团队,TNTWeb 致力于行业前沿技术摸索和团队成员集体能力晋升。为前端开发人员整顿出了小程序以及 web 前端技术畛域的最新优质内容,每周更新✨,欢送 star,github 地址:https://github.com/tnfe/TNT-Weekly