日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

手把手教你实现MVVM架构

發(fā)布時間:2025/5/22 编程问答 31 如意码农
生活随笔 收集整理的這篇文章主要介紹了 手把手教你实现MVVM架构 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
.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 }

引言

現(xiàn)在的前端真可謂是百花齊放,百家爭鳴,各種框架層出不窮,但是主要目前用的最多的還是要數(shù)VueReact、以及Angular,這三種,當(dāng)然不乏近期新出的一些其他框架,但是她們都有一個顯著的特點(diǎn),那就是使用了MVVM的架構(gòu)。

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

MVVM就是Model-View-ViewModel的縮寫,MVVM最早由微軟提出來,它借鑒了桌面應(yīng)用程序的MVC思想,在前端頁面中,把Model用純JavaScript對象表示,View負(fù)責(zé)顯示,兩者做到了最大限度的分離。把ModelView關(guān)聯(lián)起來的就是ViewModelViewModel負(fù)責(zé)把Model的數(shù)據(jù)同步到View顯示出來,還負(fù)責(zé)把View的修改同步回Model

改變JavaScript對象的狀態(tài),會導(dǎo)致DOM結(jié)構(gòu)作出對應(yīng)的變化!這讓我們的關(guān)注點(diǎn)從如何操作DOM變成了如何更新JavaScript對象的狀態(tài),而操作JavaScript對象比DOM簡單多了。

這就是MVVM的設(shè)計思想:關(guān)注Model的變化,讓MVVM框架去自動更新DOM的狀態(tài),從而把開發(fā)者從操作DOM的繁瑣步驟中解脫出來!

接下來我會帶著你們?nèi)绾稳?shí)現(xiàn)一個簡易的MVVM架構(gòu)。

一、構(gòu)建MVVM構(gòu)造函數(shù)

創(chuàng)建一個MVVM構(gòu)造函數(shù),用于接收參數(shù),如: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);
}

二、構(gòu)建Observe構(gòu)造函數(shù)

創(chuàng)建一個Observe構(gòu)造函數(shù),用于監(jiān)聽數(shù)據(jù)變化:

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();
}
});
};
};

三、構(gòu)建Compile構(gòu)造函數(shù)

創(chuàng)建一個Compile構(gòu)造函數(shù),用于解析模板指令:

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);
}

四、構(gòu)建Watcher構(gòu)造函數(shù)

創(chuàng)建一個Watcher構(gòu)造函數(shù),用于更新視圖:

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);
}

五、構(gòu)建Dep構(gòu)造函數(shù)

創(chuàng)建一個Dep構(gòu)造函數(shù),用于管理Watcher:

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

六、構(gòu)建initComputed構(gòu)造函數(shù)

創(chuàng)建一個initComputed構(gòu)造函數(shù),用于初始化計算屬性:

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() {}
});
});
}

總結(jié):

至此我們就完成了一個簡易的MVVM框架,雖然簡易,但是基本的核心思想差不多都已經(jīng)表達(dá)出來了,最后還是希望大家不要丟在收藏文件夾里吃灰,還是要多多動手練習(xí)一下,所謂眼過千遍,不如手過一遍。

總結(jié)

以上是生活随笔為你收集整理的手把手教你实现MVVM架构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。