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 ...
随机推荐
- mongodb重点知识总结
Mongodb总结 一.NoSQL型数据库介绍 NoSQL,泛指非关系型的数据库.NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题.NoSQL(NoSQL ...
- 时间time()和$_SERVER['REQUEST_TIME']
文件ab1.php <?phpforeach($i=0;$i<1000;$i++){ echo $time();} 结果: 文件ab2.php <?php foreach($i=0; ...
- PHP面试题大全(值得收藏)
PHP进阶.面试:文档.视频资源点击免费获取 一 .PHP基础部分 1.PHP语言的一大优势是跨平台,什么是跨平台? PHP的运行环境最优搭配为Apache+MySQL+PHP,此运行环境可以在不同操 ...
- js的事件循环(Event Loop)
(本文从掘金小册整理) 首先介绍一下几个概念 进程与线程 相信大家经常会听到 JS 是单线程执行的,但是你是否疑惑过什么是线程? 讲到线程,那么肯定也得说一下进程.本质上来说,两个名词都是 CPU 工 ...
- Saiku ui-settings接口404错误避免(二十九)
Saiku ui-settings接口404错误避免 自己手动编译的saiku ,不知道为什么前端总是报错 /saiku/rest/saiku/info/ui-settings 404NotFo ...
- IT兄弟连 HTML5教程 HTML5和HTML的关系
HTML5开发现在很火爆,是一门技术,更是一个概念.可以让我们的工作模式.交互模式以及对应用和游戏的体验有了翻天覆地的变化,很多人都知道HTML5这门技术,也常把HTML5读作H5(简称).其实一些外 ...
- 【Java线程与内存分析工具】VisualVM与MAT简明教程
目录 前言 VisualVM 安装与配置 本地使用 远程监控 MAT 使用场景 安装与配置 获得堆转储文件 分析堆转储文件 窥探对象内存值 堆转储文件对比分析 总结 前言 本文将简要介绍Java线程与 ...
- oracle查找某个字符在字符串中的个数的技巧
Oracle没有提供查找某个字符在字符串中出现次数的函数,当遇到这样的需求的时候,我们只能使用另外的方法去实现. 简单的思路就是,假设有个字符串str,然后里面有n个[a]字符,当把这n个[a]字符去 ...
- vs2017 发布工具 Installer 发包遇到的问题处理
一. 遇到的问题. ERROR: 要在“系统必备”对话框中启用“从与我的应用程序相同的位置下载系统必备组件”,必须将“Microsoft .NET Framework 4.7.2 (x86 和 x64 ...
- NetCore 下使用 DataTable 以及可视化工具
DtatTable 在命名空间System.Data下,NetCore2.0及以上支持.但是2017DataTable没有可视化工具,我也没有深研究直接下载的VS2019.然后在网上早了个SQLHel ...