温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何使用proxy实现一个MVVM库的方法

发布时间:2021-02-02 15:02:23 来源:亿速云 阅读:263 作者:小新 栏目:web开发

这篇文章主要介绍如何使用proxy实现一个MVVM库的方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

前言

MVVM 是当前时代前端日常业务开发中的必备模式(相关框架如reactvueangular 等), 使用 MVVM 可以将开发者的精力更专注于业务上的逻辑,而不需要关心如何操作 dom。虽然现在都 9012 年了,mvvm 相关原理的介绍已经烂大街了,但出于学习基础知识的目的(使用 proxy 实现的 vue3.0 还在开发中), 在参考了之前 vue.js 的整体思路之后,自己动手实现了一个简易的通过 proxy 实现的 mvvm

本项目代码已经开源在github,项目正在持续完善中,欢迎交流学习,喜欢请点个 star 吧!

最终效果

<html>  <body>   <div id="app">    <div>{{title}}</div>   </div>  </body> </html>
import MVVM from '@fe_korey/mvvm'; new MVVM({  view: document.getElementById('app'),  model: {   title: 'hello mvvm!'  },  mounted() {   console.log('主程编译完成,欢迎使用MVVM!');  } });

结构概览

  • Complier 模块实现解析、收集指令,并初始化视图

  • Observer 模块实现了数据的监听,包括添加订阅者和通知订阅者

  • Parser 模块实现解析指令,提供该指令的更新视图的更新方法

  • Watcher 模块实现建立指令与数据的关联

  • Dep 模块实现一个订阅中心,负责收集,触发数据模型各值的订阅列表

流程为:Complier收集编译好指令后,根据指令不同选择不同的Parser,根据ParserWatcher中订阅数据的变化并更新初始视图。Observer监听数据变化然后通知给 WatcherWatcher 再将变化结果通知给对应Parser里的 update 刷新函数进行视图的刷新。

如何使用proxy实现一个MVVM库的方法

模块详解

Complier

将整个数据模型 data 传入Observer模块进行数据监听

this.$data = new Observer(option.model).getData();

循环遍历整个 dom,对每个 dom 元素的所有指令进行扫描提取

function collectDir(element) {  const children = element.childNodes;  const childrenLen = children.length;  for (let i = 0; i < childrenLen; i++) {   const node = children[i];   const nodeType = node.nodeType;   if (nodeType !== 1 && nodeType !== 3) {    continue;   }   if (hasDirective(node)) {    this.$queue.push(node);   }   if (node.hasChildNodes() && !hasLateCompileChilds(node)) {    collectDir(element);   }  } }

对每个指令进行编译,选择对应的解析器Parser

const parser = this.selectParsers({ node, dirName, dirValue, cs: this });

将得到的解析器Parser传入Watcher,并初始化该 dom 节点的视图

const watcher = new Watcher(parser); parser.update({ newVal: watcher.value });

所有指令解析完毕后,触发 MVVM 编译完成回调$mounted()

this.$mounted();

使用文档碎片document.createDocumentFragment()来代替真实 dom 节点片段,待所有指令编译完成后,再将文档碎片追加回真实 dom 节点

let child; const fragment = document.createDocumentFragment(); while ((child = this.$element.firstChild)) {  fragment.appendChild(child); } //解析完后 this.$element.appendChild(fragment); delete $fragment;

Parser

Complier模块编译后的指令,选择不同听解析器解析,目前包括ClassParser,DisplayParser,ForParser,IfParser,StyleParser,TextParser,ModelParser,OnParser,OtherParser等解析模块。

switch (name) {  case 'text':   parser = new TextParser({ node, dirValue, cs });   break;  case 'style':   parser = new StyleParser({ node, dirValue, cs });   break;  case 'class':   parser = new ClassParser({ node, dirValue, cs });   break;  case 'for':   parser = new ForParser({ node, dirValue, cs });   break;  case 'on':   parser = new OnParser({ node, dirName, dirValue, cs });   break;  case 'display':   parser = new DisplayParser({ node, dirName, dirValue, cs });   break;  case 'if':   parser = new IfParser({ node, dirValue, cs });   break;  case 'model':   parser = new ModelParser({ node, dirValue, cs });   break;  default:   parser = new OtherParser({ node, dirName, dirValue, cs }); }

不同的解析器提供不同的视图刷新函数update(),通过update更新dom视图

//text.js function update(newVal) {  this.el.textContent = _toString(newVal); }

OnParser 解析事件绑定,与数据模型中的 methods字段对应

//详见 https://github.com/zhaoky/mvvm/blob/master/src/core/parser/on.ts el.addEventListener(handlerType, e => {  handlerFn(scope, e); });

ForParser 解析数组

详见 https://github.com/zhaoky/mvvm/blob/master/src/core/parser/for.ts

ModelParser 解析双向绑定,目前支持input[text/password] & textarea,input[radio],input[checkbox],select四种情况的双向绑定,双绑原理:

数据变化更新表单:跟其他指令更新视图一样,通过update方法触发更新表单的value

function update({ newVal }) {  this.model.el.value = _toString(newVal); }

表单变化更新数据:监听表单变化事件如input,change,在回调里set数据模型

this.model.el.addEventListener('input', e => {  model.watcher.set(e.target.value); });

Observer

MVVM 模型中的核心,一般通过 Object.definePropertygetset 方法进行数据的监听,在 get 里添加订阅者,set 里通知订阅者更新视图。在本项目采用 Proxy 来实现数据监听,好处有三:

Proxy 可以直接监听对象而非属性

Proxy 可以直接监听数组的变化

Proxy 有多达 13 种拦截方法,查阅

而劣势是兼容性问题,且无法通过 polyfill 磨平。查阅兼容性

注意 Proxy 只会监听自身的每一个属性,如果属性是对象,则该对象不会被监听,所以需要递归监听

设置监听后,返回一个 Proxy 替代原数据对象

var proxy = new Proxy(data, {  get: function(target, key, receiver) {   //如果满足条件则添加订阅者   dep.addDep(curWatcher);   return Reflect.get(target, key, receiver);  },  set: function(target, key, value, receiver) {   //如果满足条件则通知订阅者   dep.notfiy();   return Reflect.set(target, key, value, receiver);  } });

Watcher

Complier 模块里对每一个解析后的 Parser 进行指令与数据模型直接的绑定,并触发 Observerget 监听,添加订阅者(Watcher

this._getter(this.parser.dirValue)(this.scope || this.parser.cs.$data);

当数据模型变化时,就会触发 -> Observerset 监听 -> Depnotfiy 方法(通知订阅者的所有订阅列表) -> 执行订阅列表所有 Watcherupdate 方法 -> 执行对应 Parserupdate -> 完成更新视图

Watcher 里的 set 方法用于设置双向绑定值,注意访问层级

Dep

  • MVVM 的订阅中心,在这里收集数据模型的每个属性的订阅列表

  • 包含添加订阅者,通知订阅者等方法

  • 本质是一种发布/订阅模式

class Dep {  constructor() {   this.dependList = [];  }  addDep() {   this.dependList.push(dep);  }  notfiy() {   this.dependList.forEach(item => {    item.update();   });  } }

以上是“如何使用proxy实现一个MVVM库的方法”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注亿速云行业资讯频道!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI