今天带大家简单的实现MVVM模式,Object.defineProperty代理(proxy)数据
 
MVVM的实现方式:
  • 模板编译(Compile)
  • 数据劫持(Observer) Object.defineProperty
  • 发布的订阅(Dep)
  • 观察者(Watcher)
 
 
MVVM:
 
  • 数据就是简单的javascript对象,需要将数据绑定到模板上
  • 监听视图的变化,视图变化后通知数据更新,数据更新会再次导致视图的变化!
 
下面是实现方法:
---------------------------------------demo-start--
这是我打的demo:
 

 {{message}}

  {{message}}

{{info}}

 ---------------------------------------demo-end--
 
 
demo图例:
 
简单的mock Vue MVVM:
 
html内容:
<body>
<div id="app">
<!-- 测试data数据:实现双向绑定 -->
<input type="text" id="input" />
<div>
&nbsp;{{message}}
<div>
&nbsp;&nbsp;{{message}}
</div>
</div>
{{info}}
</div>
<!-- 简单实现 Vue MVVM模式 -->
<script src="ziChin_mock_vue.js"></script>
<script>
let message = '子卿的初始message'
// 实例MVVM:
var vm = new Vue({
el: '#app',
data: {
message,
info:'初始info'
}
})
// 利用oninput输入框测试双向绑定:
let input = document.querySelector('#input')
input.value = message
input.oninput = function (e) {
vm.$data.message = e.target.value
}
</script>
</body>

 
ziChin_mock_vue.js文件:
// 构建一个MVVM实例(ES6实现)
class Vue {
constructor(options) {
// 初始化变量
this.$options = options
this.$el = options.el
this.$data = options.data
// 1.监听数据
this.observer(this.$data)
// 2.编译模版
this.compile(this.$el)
}
compile(el) {
// ...
}
observer(data) {
// ...
}
}
// 观察者模式
class Dep {
// ...
}
class Watcher {
// 订阅信息
// ...
}
 
 
observer 监听数据以便更新视图(数据劫持):
observer(data) {
Object.keys(data).forEach(key => {
let dep = new Dep()
let value = data[key]
// 数据劫持的核心方法:
Object.defineProperty(data, key, {
configurable: true,
enumerable: true,
get() {
if (Dep.target) {
dep.addSub(Dep.target) // 把订阅信息缓存起来
}
return value
},
set(newValue) {
dep.notify(newValue, value)
value = newValue
}
})
})
}
 
 
compile 编译模版(这里我没有用虚拟Node):
compile(el) {
let element = document.querySelector(el)
let childNodes = element.childNodes
const compileNodes = childNodes => { // 递归
Array.from(childNodes).forEach(node => {
if (node.nodeType === 3) { // 文本节点
let reg = /\{\{\s*(\S*)\s*\}\}/
let dataKey = null
node.textContent = node.textContent.replace(reg, ($0, $1) => {
dataKey = $1
return this.$data[dataKey]
})
if (dataKey !== null) { // 监听(视图与数据一一对应)
new Watcher(this, dataKey, (newValue, value) => {
node.textContent = node.textContent.replace(value, newValue)
})
}
} else if (node.nodeType === 1) { // 标签节点
compileNodes(node.childNodes)
}
})
}
compileNodes(childNodes)
}
 
Dep、Watcher 观察者模式:
// 观察者模式
class Dep {
constructor() {
this.subs = []
}
addSub(sub) { // 缓存订阅内容
this.subs.push(sub)
}
notify(newValue, value) { // 发布信息
this.subs.forEach(item => item.update(newValue, value))
}
}
class Watcher {
constructor(vm, dataKey, cb) {
Dep.target = this
vm.$data[dataKey] // 触发Object中get函数的 --> addSub,缓存订阅内容
Dep.target = null
this.cb = cb
}
update(newValue, value) {
this.cb(newValue, value) // 由notify触发
}
}

所有代码:

html:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ziChin_mock_vue</title>
</head>
<body>
<div id="app">
<!-- 测试data数据:实现双向绑定 -->
<input type="text" id="input" />
<div>
&nbsp;{{message}}
<div>
&nbsp;&nbsp;{{message}}
</div>
</div>
{{info}}
</div>
<!-- 简单实现 Vue MVVM模式 -->
<script src="ziChin_mock_vue.js"></script>
<script>
let message = '子卿的初始message'
// 实例MVVM:
var vm = new Vue({
el: '#app',
data: {
message,
info:'初始info'
}
})
// 利用oninput输入框测试双向绑定:
let input = document.querySelector('#input')
input.value = message
input.oninput = function (e) {
vm.$data.message = e.target.value
}
</script>
</body>
</html>

js:

ziChin_mock_vue.js
// 构建一个MVVM实例(ES6实现)
class Vue {
constructor(options) {
// 初始化变量
this.$options = options
this.$el = options.el
this.$data = options.data
// 1.监听数据以便更新视图(数据劫持)
this.observer(this.$data)
// 2.编译模版(这里我没有用虚拟Node)
this.compile(this.$el)
}
compile(el) {
let element = document.querySelector(el)
let childNodes = element.childNodes
const compileNodes = childNodes => {
Array.from(childNodes).forEach(node => {
if (node.nodeType === 3) { // 文本节点
let reg = /\{\{\s*(\S*)\s*\}\}/
let dataKey = null
node.textContent = node.textContent.replace(reg, ($0, $1) => {
dataKey = $1
return this.$data[dataKey]
})
if (dataKey !== null) { // 监听(视图与数据一一对应)
new Watcher(this, dataKey, (newValue, value) => {
node.textContent = node.textContent.replace(value, newValue)
})
}
} else if (node.nodeType === 1) { // 标签节点
compileNodes(node.childNodes)
}
})
}
compileNodes(childNodes)
}
observer(data) {
Object.keys(data).forEach(key => {
let dep = new Dep()
let value = data[key]
Object.defineProperty(data, key, {
configurable: true,
enumerable: true,
get() {
if (Dep.target) {
dep.addSub(Dep.target)
}
return value
},
set(newValue) {
dep.notify(newValue, value)
value = newValue
}
})
})
}
}
// 观察者模式
class Dep {
constructor() {
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
notify(newValue, value) {
this.subs.forEach(item => item.update(newValue, value))
}
}
class Watcher {
constructor(vm, dataKey, cb) {
Dep.target = this
vm.$data[dataKey]
Dep.target = null
this.cb = cb
}
update(newValue, value) {
this.cb(newValue, value)
}
}
 
 
 
 
 
 
 
 
 

Vue中MVVM模式的双向绑定原理 和 代码的实现的更多相关文章

  1. 前端笔记之微信小程序(二){{}}插值和MVVM模式&数据双向绑定&指令&API

    一.双花括号{{}}插值和MVVM模式 1.1 体会{{}}插值 index.wxml的标签不是html的那些标签,这里的view就是div. {{}}这样的插值写法,叫做mustache语法.mus ...

  2. 解决Vue中文本输入框v-model双向绑定后数据不显示的问题

    前言 项目中遇到一个问题就是在Vue中双向绑定对象属性时,手动赋值属性后输入框的数据不实时更新的问题. <FormItem label="地址" prop="eve ...

  3. vue中v-model的数据双向绑定(重要)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. Vue双向绑定原理,教你一步一步实现双向绑定

    当今前端天下以 Angular.React.vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋. 所以我们要时刻保持好奇心,拥抱变化,只有在不断的变 ...

  5. vue的双向绑定原理及实现

    前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图 ...

  6. vue双向绑定原理及实现

    vue双向绑定原理及实现 一.总结 一句话总结:vue中的双向绑定主要是通过发布者-订阅者模式来实现的 发布 订阅 1.单向绑定和双向绑定的区别是什么? model view 更新 单向绑定:mode ...

  7. 通俗易懂了解Vue双向绑定原理及实现

    看到一篇文章,觉得写得挺好的,拿过来给大家分享一下,刚好解答了一些困扰我的一些疑惑!!! 1. 前言 每当被问到Vue数据双向绑定原理的时候,大家可能都会脱口而出:Vue内部通过Object.defi ...

  8. vue的双向绑定原理浅析与简单实现

    很久之前看过vue的一些原理,对其中的双向绑定原理也有一定程度上的了解,只是最近才在项目上使用vue,这才决定好好了解下vue的实现原理,因此这里对vue的双向绑定原理进行浅析,并做一个简单的实现. ...

  9. vue双向绑定原理分析

    当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理. 简易vue源码地址:https://github.com/jiangzhenf ...

随机推荐

  1. Noip2015Day2T3 运输计划

    题目链接 problem 一棵n个点带边权的树,有m个条路径.选择一条边,将其权值变为0,使得长度最长的路径长度最小.求该长度最小为多少. solution 其实仔细一想并不难. 删除一条边会导致所有 ...

  2. 使用canal增量同步mysql数据库信息到ElasticSearch

    本文介绍如何使用canal增量同步mysql数据库信息到ElasticSearch.(注意:是增量!!!) 1.简介 1.1 canal介绍 Canal是一个基于MySQL二进制日志的高性能数据同步系 ...

  3. 修改本地的host文件

    在C:\Windows\System32\drivers\etc下有一个host文件, 在里面可以修改本地的域名,比如我文件里添加一行: 10.0.33.79    devsuite.easthope ...

  4. 转载-Java中LinkedList的一些方法—addFirst addFirst getFirst geLast removeFirst removeLast

    Java中LinkedList的一些方法—addFirst addFirst getFirst geLast removeFirst removeLast 版权声明:本文为博主原创文章,遵循CC 4. ...

  5. Java反射方法总结

    1.得到构造器的方法 Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数, Constructor[] getConstr ...

  6. git 创建分支 提交到远程分支

    git 创建分支 并 提交到远程分支 git branch 0.可以通过git branch -r 命令查看远端库的分支情况 1,从已有的分支创建新的分支(如从master分支),创建一个dev分支 ...

  7. idea使用lombok不生效的解决办法

    file-->setting-->plugins点击下方的 browse repositories. 搜索lombok plugin. 安装后,重启. file-->setting- ...

  8. vue-基本动画

    不使用动画 <div id="app"> <input type="button" value="toggle" @cli ...

  9. Vue中computed和watch的区别

    在vue中computed和watch的真正区别是:computed产生于它的依赖,而watch产生于它的依赖的变化.只要依赖存在,我们就能访问到其对应的computed属性:但只有依赖发生了改变,我 ...

  10. find命令通过排序只保留最新的文件目录

    find /usr/local/canal/logs/example -type d -name "*-*" | sort -nr | awk '{if (NR>=2){pr ...