.markdown-body { color: rgba(89, 89, 89, 1); font-size: 15px; font-family: -apple-system, system-ui, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; background-image: linear-gradient(90deg, rgba(60, 10, 30, 0.04) 3%, rgba(0, 0, 0, 0) 0), linear-gradient(1turn, rgba(60, 10, 30, 0.04) 3%, rgba(0, 0, 0, 0) 0); background-size: 20px 20px; background-position: center }
.markdown-body p { color: rgba(89, 89, 89, 1); font-size: 15px; line-height: 2; font-weight: 400 }
.markdown-body p+p { margin-top: 16px }
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { padding: 30px 0; margin: 0; color: rgba(19, 92, 224, 1) }
.markdown-body h1 { position: relative; text-align: center; font-size: 22px; margin: 50px 0 }
.markdown-body h1:before { position: absolute; content: ""; top: -10px; left: 50%; width: 32px; height: 32px; transform: translateX(-50%); background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABfVBMVEX///8Ad/8AgP8AgP8AgP8Aff8AgP8Af/8AgP8AVf8Af/8Af/8AgP8AgP8Af/8Afv8AAP8Afv8Afv8Aef8AgP8AdP8Afv8AgP8AgP8Acf8Ae/8AgP8Af/8AgP8Af/8Af/8AfP8Afv8AgP8Af/8Af/8Afv8Afv8AgP8Afv8AgP8Af/8Af/8AgP8AgP8Afv8AgP8Af/8AgP8AgP8AgP8Ae/8Afv8Af/8AgP8Af/8AgP8Af/8Af/8Aff8Af/8Abf8AgP8Af/8AgP8Af/8Af/8Afv8AgP8AgP8Afv8Afv8AgP8Af/8Aff8AgP8Afv8AgP8Aff8AgP8AfP8AgP8Ae/8AgP8Af/8AgP8AgP8AgP8Afv8AgP8AgP8AgP8Afv8AgP8AgP8AgP8AgP8AgP8Af/8AgP8Af/8Af/8Aev8Af/8AgP8Aff8Afv8AgP8AgP8AgP8Af/8AgP8Af/8Af/8AgP8Afv8AgP8AgP8AgP8AgP8Af/8AeP8Af/8Af/8Af//////rzEHnAAAAfXRSTlMAD7CCAivatxIDx5EMrP19AXdLEwgLR+6iCR/M0yLRzyFF7JupSXn8cw6v60Q0QeqzKtgeG237HMne850/6Qeq7QaZ+WdydHtj+OM3qENCMRYl1B3K2U7wnlWE/mhlirjkODa9FN/BF7/iNV/2kASNZpX1Wlf03C4stRGxgUPclqoAAAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEaBzgZ4yeM3AAAAT9JREFUOMvNUldbwkAQvCAqsSBoABE7asSOBRUVVBQNNuy9996789+9cMFAMHnVebmdm+/bmdtbQv4dOFOW2UjPzgFyLfo6nweKfIMOBYWwFtmMPGz2Yj2pJI0JDq3udJW6VVbmKa9I192VQFV1ktXUAl5NB0cd4KpnORqsEO2ZIRpF9gJfE9Dckqq0KuZt7UAH5+8EPF3spjsRpCeQNO/tA/qDwIDA+OCQbBoKA8NOdjMySgcZGVM6jwcgRuUiSs0nlPFNSrEpJfU0jTLD6llqbvKxei7OzvkFNQohi0vAsj81+MoqsCaoPOQFgus/1LyxichW+hS2JWCHZ7VlF9jb187pIAYcHiViHAMnp5mTjJ8B5xeEXF4B1ze/fTh/C0h398DDI9HB07O8ci+vRBdvdGnfP4gBuM8vw7X/G3wDmFhFZEdxzjMAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTgtMDEtMjZUMDc6NTY6MjUrMDE6MDA67pVWAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTAxLTI2VDA3OjU2OjI1KzAxOjAwS7Mt6gAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAWdEVYdFRpdGxlAGp1ZWppbl9sb2dvIGNvcHlxapmKAAAAV3pUWHRSYXcgcHJvZmlsZSB0eXBlIGlwdGMAAHic4/IMCHFWKCjKT8vMSeVSAAMjCy5jCxMjE0uTFAMTIESANMNkAyOzVCDL2NTIxMzEHMQHy4BIoEouAOoXEXTyQjWVAAAAAElFTkSuQmCC"); opacity: 0.36 }
.markdown-body h2 { position: relative; font-size: 20px; border-left: 4px solid; padding: 0 0 0 10px; margin: 30px 0 }
.markdown-body h3 { font-size: 16px }
.markdown-body ul { list-style: disc outside; margin-left: 2em; margin-top: 1em }
.markdown-body li { line-height: 2; color: rgba(89, 89, 89, 1) }
.markdown-body img.loaded { margin: 0 auto; display: block }
.markdown-body blockquote { background: rgba(255, 249, 249, 1); margin: 2em 0; padding: 2px 20px; border-left: 4px solid rgba(178, 174, 197, 1) }
.markdown-body blockquote p { color: rgba(102, 102, 102, 1); line-height: 2 }
.markdown-body a { color: rgba(3, 106, 202, 1); border-bottom: 1px solid rgba(3, 106, 202, 0.8); font-weight: 400; text-decoration: none }
.markdown-body em strong, .markdown-body strong { color: rgba(3, 106, 202, 1) }
.markdown-body hr { border-top: 1px solid rgba(19, 92, 224, 1) }
.markdown-body pre { overflow: auto }
.markdown-body code, .markdown-body pre { overflow: auto; position: relative; line-height: 1.75; font-family: Menlo, Monaco, Consolas, Courier New, monospace }
.markdown-body pre>code { font-size: 12px; padding: 15px 12px; margin: 0; word-break: normal; display: block; overflow-x: auto; color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.markdown-body code { border-radius: 2px; overflow-x: auto; background-color: rgba(255, 245, 245, 1); color: rgba(255, 80, 44, 1); font-size: 0.87em; padding: 0.065em 0.4em }
.markdown-body table { border-collapse: collapse; margin: 1rem 0; overflow-x: auto }
.markdown-body table td, .markdown-body table th { border: 1px solid rgba(223, 226, 229, 1); padding: 0.6em 1em }
.markdown-body table tr { border-top: 1px solid rgba(223, 226, 229, 1) }
.markdown-body table tr:nth-child(2n) { background-color: rgba(246, 248, 250, 1) }.markdown-body pre, .markdown-body pre>code.hljs { color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.hljs-comment, .hljs-quote { color: rgba(153, 153, 136, 1); font-style: italic }
.hljs-keyword, .hljs-selector-tag, .hljs-subst { color: rgba(51, 51, 51, 1); font-weight: 700 }
.hljs-literal, .hljs-number, .hljs-tag .hljs-attr, .hljs-template-variable, .hljs-variable { color: rgba(0, 128, 128, 1) }
.hljs-doctag, .hljs-string { color: rgba(221, 17, 68, 1) }
.hljs-section, .hljs-selector-id, .hljs-title { color: rgba(153, 0, 0, 1); font-weight: 700 }
.hljs-subst { font-weight: 400 }
.hljs-class .hljs-title, .hljs-type { color: rgba(68, 85, 136, 1); font-weight: 700 }
.hljs-attribute, .hljs-name, .hljs-tag { color: rgba(0, 0, 128, 1); font-weight: 400 }
.hljs-link, .hljs-regexp { color: rgba(0, 153, 38, 1) }
.hljs-bullet, .hljs-symbol { color: rgba(153, 0, 115, 1) }
.hljs-built_in, .hljs-builtin-name { color: rgba(0, 134, 179, 1) }
.hljs-meta { color: rgba(153, 153, 153, 1); font-weight: 700 }
.hljs-deletion { background: rgba(255, 221, 221, 1) }
.hljs-addition { background: rgba(221, 255, 221, 1) }
.hljs-emphasis { font-style: italic }
.hljs-strong { font-weight: 700 }

引言

现在的前端真可谓是百花齐放,百家争鸣,各种框架层出不穷,但是主要目前用的最多的还是要数VueReact、以及Angular,这三种,当然不乏近期新出的一些其他框架,但是她们都有一个显著的特点,那就是使用了MVVM的架构。

首先我们要搞清楚什么是MVVM

MVVM就是Model-View-ViewModel的缩写,MVVM最早由微软提出来,它借鉴了桌面应用程序的MVC思想,在前端页面中,把Model用纯JavaScript对象表示,View负责显示,两者做到了最大限度的分离。把ModelView关联起来的就是ViewModelViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model

改变JavaScript对象的状态,会导致DOM结构作出对应的变化!这让我们的关注点从如何操作DOM变成了如何更新JavaScript对象的状态,而操作JavaScript对象比DOM简单多了。

这就是MVVM的设计思想:关注Model的变化,让MVVM框架去自动更新DOM的状态,从而把开发者从操作DOM的繁琐步骤中解脱出来!

接下来我会带着你们如何去实现一个简易的MVVM架构。

一、构建MVVM构造函数

创建一个MVVM构造函数,用于接收参数,如:datamethodscomputed等:

function MVVM(options) {
this.$options = options;
let data = this._data = this.$options.data;
observe(data);
for (let key in data) {
Object.defineProperty(this, key, {
enumerable: true,
get() {
return this._data[key];
},
set(newVal) {
this._data[key] = newVal;
}
});
};
initComputed.call(this);
new Compile(options.el, this);
options.mounted.call(this);
}

二、构建Observe构造函数

创建一个Observe构造函数,用于监听数据变化:

function Observe(data) {
let dep = new Dep();
for (let key in data) {
let val = data[key];
observe(val);
Object.defineProperty(data, key, {
enumerable: true,
get() {
Dep.target && dep.addSub(Dep.target);
return val;
},
set(newVal) {
if (val === newVal) {
return;
}
val = newVal;
observe(newVal);
dep.notify();
}
});
};
};

三、构建Compile构造函数

创建一个Compile构造函数,用于解析模板指令:

function Compile(el, vm) {
vm.$el = document.querySelector(el);
let fragment = document.createDocumentFragment();
while (child = vm.$el.firstChild) {
fragment.appendChild(child);
}
replace(fragment);
function replace(frag) {
Array.from(frag.childNodes).forEach(node => {
let txt = node.textContent;
let reg = /\{\{(.*?)\}\}/g;
if (node.nodeType === 3 && reg.test(txt)) {
let arr = RegExp.$1.split('.');
let val = vm;
arr.forEach(key => { val = val[key]; });
node.textContent = txt.replace(reg, val).trim();
new Watcher(vm, RegExp.$1, newVal => {
node.textContent = txt.replace(reg, newVal).trim();
});
}
if (node.nodeType === 1) {
let nodeAttr = node.attributes;
Array.from(nodeAttr).forEach(attr => {
let name = attr.name;
let exp = attr.value;
if (name.includes('v-')) {
node.value = vm[exp];
}
new Watcher(vm, exp, newVal => {
node.value = newVal;
});
node.addEventListener('input', e => {
let newVal = e.target.value;
vm[exp] = newVal;
});
});
};
if (node.childNodes && node.childNodes.length) {
replace(node);
}
});
}
vm.$el.appendChild(fragment);
}

四、构建Watcher构造函数

创建一个Watcher构造函数,用于更新视图:

function Watcher(vm, exp, fn) {
this.fn = fn;
this.vm = vm;
this.exp = exp;
Dep.target = this;
let arr = exp.split('.');
let val = vm;
arr.forEach(key => {
val = val[key];
});
Dep.target = null;
} Watcher.prototype.update = function() {
let arr = this.exp.split('.');
let val = this.vm;
arr.forEach(key => {
val = val[key];
});
this.fn(val);
}

五、构建Dep构造函数

创建一个Dep构造函数,用于管理Watcher:

function Dep() {
this.subs = [];
} Dep.prototype.addSub = function(sub) {
this.subs.push(sub);
} Dep.prototype.notify = function() {
this.subs.forEach(sub => {
sub.update();
});
}

六、构建initComputed构造函数

创建一个initComputed构造函数,用于初始化计算属性:

function initComputed() {
let vm = this;
let computed = this.$options.computed;
Object.keys(computed).forEach(key => {
Object.defineProperty(vm, key, {
get: typeof computed[key] === 'function' ? computed[key] : computed[key].get,
set() {}
});
});
}

总结:

至此我们就完成了一个简易的MVVM框架,虽然简易,但是基本的核心思想差不多都已经表达出来了,最后还是希望大家不要丢在收藏文件夹里吃灰,还是要多多动手练习一下,所谓眼过千遍,不如手过一遍。

手把手教你实现MVVM架构的更多相关文章

  1. Delphi - 手把手教你基于D7+Access常用管理系统架构的设计与实现 (更新中)

    前言 从事软件开发工作好多年了,学的越深入越觉得自己无知,所以还是要对知识保持敬畏之心,活到老,学到老! 健身和代码一样都不能少,身体是革命的本钱,特别是我们这种高危工种,所以小伙伴们运动起来!有没有 ...

  2. 庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境

    庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境 一.介绍 说起微服务架构来,有一个环节是少不了的,那就是CI/CD持续集成的环境.当然,搭建CI/CD环境的工具很多, ...

  3. 3、手把手教你Extjs5(三)MVVM特性的简单说明

    下面我们来看一下自动生成的代码中的MVVM架构的关系.Main是一个可视的控件,MainController是这个控件的控制类,MainModel是这个控件的模型类. 在上面的图片中,左边是Main. ...

  4. 手把手教你用动软.NET代码生成器实例教程

    动软实战攻略 手把手教你用动软 文档编号:20110421 版权所有 © 2004-2011 动软 在线帮助:http://help.maticsoft.com 目录   一.        产品介绍 ...

  5. [原创]手把手教你写网络爬虫(4):Scrapy入门

    手把手教你写网络爬虫(4) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 上期我们理性的分析了为什么要学习Scrapy,理由只有一个,那就是免费,一分钱都不用花! 咦?怎么有人扔西红柿 ...

  6. [原创]手把手教你写网络爬虫(5):PhantomJS实战

    手把手教你写网络爬虫(5) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 大家好!从今天开始,我要与大家一起打造一个属于我们自己的分布式爬虫平台,同时也会对涉及到的技术进行详细介绍.大 ...

  7. 【转】手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

    1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理!   我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ...

  8. 用Python手把手教你搭一个Transformer!

    来源商业新知网,原标题:百闻不如一码!手把手教你用Python搭一个Transformer 与基于RNN的方法相比,Transformer 不需要循环,主要是由Attention 机制组成,因而可以充 ...

  9. 手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

    1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理!   我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ...

  10. 手把手教你如何玩转Activiti工作流

    手把手教你如何玩转Activiti工作流 置顶 2018年01月30日 19:51:36 Cs_hnu_scw 阅读数:24023   版权声明:本文为博主原创文章,未经博主允许不得转载. https ...

随机推荐

  1. Ollama模型迁移

    技术背景 在前面的一些文章中,我们介绍过使用Ollama在Linux平台加载DeepSeek蒸馏模型,使用Ollama在Windows平台部署DeepSeek本地模型.除了使用Ollama与模型文件交 ...

  2. Qt 枚举类型 值、字符串、枚举类型互转

    枚举类型 class Enum : public QObject{ Q_OBJECT public: enum Fruit{ APPLE = 0, BANANA, POTATO } Q_ENUM(Fr ...

  3. PERT 图表教程

    (翻译自: PERT Chart Tutorial) PERT 图表 是(程序评估和审查技术)的首字母缩写.PERT 图是一种项目管理工具,用于在项目中安排.组织和协调任务.它基本上是一种分析完成给定 ...

  4. 批量上传序列到Gisaid数据库

    本文演示的是用Gisaid官网自带的Excel宏批量上传毒株信息和序列到Gisaid数据库,需要魔法. 1. 下载GISAID Batch Uploader 找到自己需要上传的病毒类型,我是Flu,在 ...

  5. python py文件名称不能和库名称一样,否则报错module 'requests' has no attribute 'post'

    这个问题自己犯过几次,加深一下记忆

  6. 【由技及道】统一封装API返回结果后String返回报错文件解决原理--Spring 消息转换器的层次图解与规则说明【人工智障AI2077的开发问题日志002】

    ▄▀▄ ▀■■■▀ AI2077的日志片段 ▄■■■■■▄ [ERROR] | 量子通道波动异常! | 检测到StringConverter试图吞噬ApiResult对象 | 启动二向箔防御程序... ...

  7. nginx 部署vue http、https

    nignx配置文件 server { listen 80; server_name your_domain.com; return 301 https://$server_name$request_u ...

  8. composer remove 卸载依赖

    remove 命令 remove 命令用于移除一个包及其依赖(在依赖没有被其他包使用的情况下),如果依赖被其他包使用,则无法移除: $ composer remove monolog/monolog ...

  9. 《机器人SLAM导航核心技术与实战》第1季:第9章_视觉SLAM系统

    <机器人SLAM导航核心技术与实战>第1季:第9章_视觉SLAM系统 视频讲解 [第1季]9.第9章_视觉SLAM系统-视频讲解 [第1季]9.1.第9章_视觉SLAM系统_ORB-SLA ...

  10. java基础之二分查找,可变参运用

    一. public class BinarySeachTest { public static void main(String[] args) { int[] arr = new int[]{22, ...