Vue中MVVM模式的双向绑定原理 和 代码的实现
- 模板编译(Compile)
- 数据劫持(Observer) Object.defineProperty
- 发布的订阅(Dep)
- 观察者(Watcher)
- 数据就是简单的javascript对象,需要将数据绑定到模板上
- 监听视图的变化,视图变化后通知数据更新,数据更新会再次导致视图的变化!
{{info}}
<body>
<div id="app">
<!-- 测试data数据:实现双向绑定 -->
<input type="text" id="input" />
<div>
{{message}}
<div>
{{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>
// 构建一个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(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(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)
}
// 观察者模式
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>
{{message}}
<div>
{{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:
// 构建一个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模式的双向绑定原理 和 代码的实现的更多相关文章
- 前端笔记之微信小程序(二){{}}插值和MVVM模式&数据双向绑定&指令&API
一.双花括号{{}}插值和MVVM模式 1.1 体会{{}}插值 index.wxml的标签不是html的那些标签,这里的view就是div. {{}}这样的插值写法,叫做mustache语法.mus ...
- 解决Vue中文本输入框v-model双向绑定后数据不显示的问题
前言 项目中遇到一个问题就是在Vue中双向绑定对象属性时,手动赋值属性后输入框的数据不实时更新的问题. <FormItem label="地址" prop="eve ...
- vue中v-model的数据双向绑定(重要)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Vue双向绑定原理,教你一步一步实现双向绑定
当今前端天下以 Angular.React.vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋. 所以我们要时刻保持好奇心,拥抱变化,只有在不断的变 ...
- vue的双向绑定原理及实现
前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图 ...
- vue双向绑定原理及实现
vue双向绑定原理及实现 一.总结 一句话总结:vue中的双向绑定主要是通过发布者-订阅者模式来实现的 发布 订阅 1.单向绑定和双向绑定的区别是什么? model view 更新 单向绑定:mode ...
- 通俗易懂了解Vue双向绑定原理及实现
看到一篇文章,觉得写得挺好的,拿过来给大家分享一下,刚好解答了一些困扰我的一些疑惑!!! 1. 前言 每当被问到Vue数据双向绑定原理的时候,大家可能都会脱口而出:Vue内部通过Object.defi ...
- vue的双向绑定原理浅析与简单实现
很久之前看过vue的一些原理,对其中的双向绑定原理也有一定程度上的了解,只是最近才在项目上使用vue,这才决定好好了解下vue的实现原理,因此这里对vue的双向绑定原理进行浅析,并做一个简单的实现. ...
- vue双向绑定原理分析
当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理. 简易vue源码地址:https://github.com/jiangzhenf ...
随机推荐
- js判断浏览器是否安装或启用了flash的方法总结
目录 # js判断浏览器是否安装或启用了flash的方法 # chrome浏览器启用flash插件的方法 # 参考 # js判断浏览器是否安装或启用了flash的方法 在传统浏览器,可以使用windo ...
- php 压缩字符串
压缩字符串: base64_encode(gzcompress(serialize($data))) 解压字符串: unserialize(gzuncompress(base64_decode($se ...
- 【shell脚本语法】判断、流程控制语句
目录 判断用户参数 流程控制语句 一.判断用户参数 1.1 文件判断参数 PS:$?代表上一个命令的返回值,为0表示正确执行,非0表示错误执行.详情可参考我另一篇博客:https://www.cnbl ...
- PHP 扩展与 ZEND 引擎的整合
PHP 扩展是对 PHP 功能的一个补充,编写完 PHP 扩展以后, ZEND 引擎需要获取到 PHP 扩展的信息,比如 phpinfo() 函数是如何列出 PHP 扩展的信息,PHP 扩展中的函数如 ...
- 【计算机网络】UDP基础知识总结
1. UDP概念相关 [!NOTE] UDP(User Datagram Protocol),又叫用户数据报协议. UDP是一个无连接的.不可靠.基于数据报的传输协议.UDP只是报文(报文可以理解为一 ...
- [转]探索ASP.NET Core 3.0 系列
这是该系列的第一篇文章:探索ASP.NET Core 3.0. 第1部分-探索新的项目文件Program.cs和通用主机(本文) 第2部分-比较ASP.NET Core 3.0模板之间的Startup ...
- [debug] 解决在C++编写过程中的“找到一个或多个多重定义的符号”
如下图: 其在 common.h 中定义了一个变量a ,然后在两个 cpp 文件中都是用它. 在这种情况下,链接时就会出现 “找到一个或多个多重定义的符号”. 解决方案: 在某个cpp文件中定义,然后 ...
- WPF MVVM,Prism,Command Binding
1.添加引用Microsoft.Practices.Prism.Mvvm.dll,Microsoft.Practices.Prism.SharedInterfaces.dll: 2.新建文件夹,Vie ...
- python IPy库
Website: https://github.com/haypo/python-ipy/ 安装: easy_install IPy >>> from IPy import IP ...
- 反射DataTable转实体类
using System; using System.Collections.Generic; using System.Data; using System.Reflection; namespac ...