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 ...
随机推荐
- mysql 数据库分表小实例
项目开发中,我们的数据库数据越来越大,随之而来的是单个表中数据太多.以至于查询书读变慢,而且由于表的锁机制导致应用操作也搜到严重影响,出现了数据库性能瓶颈. 当出现这种情况时,我们可以考虑分表,即将单 ...
- [转]使用IConfigureNamedOptions和ConfigureAll配置命名选项
这是我上一篇关于在ASP.NET Core 2.x中使用多个强类型设置实例的后续文章.在文章的结尾,我介绍了命名选项的概念,该选项已添加到ASP.NET Core 2.0中.在本文中,我将详细介绍如何 ...
- 用户和登录的ID、Name和SID
SQL Server的安全主体主要分为Login.User和Role,不仅有ID属性,还有Name属性和SID属性,SID是指Security ID.在查看用户和登录的时候,受到模拟上下文的影响.当执 ...
- Linux less grep
第一步,less查看文件 less 日志文件名 1,这时候,使用组合键 shift + g 可以定位到文件末尾. 在文件末尾,使用组合键(从末尾开始根据之后输入的字符串向上检索) shift + ? ...
- Java入门系列之字符串创建方式、判断相等(一)
前言 陆续从0开始学习Java出于多掌握一门语言以后的路也会更宽,.NET和Java兼顾,虽然路还很艰难,但事在人为.由于Java和C#语法相似,所以关于一些很基础的内容不会再重头讲,Java系列中所 ...
- node-sass安装失败处理办法
参考: https://npm.taobao.org/mirrors https://lzw.me/a/node-sass-install-helper.html 设置环境变量安装 SASS_BINA ...
- oracle学习笔记(八)——结果集元数据ResultSetMetaData以及ResultSet转为对应的实体类框架
介绍 可用于获取关于 ResultSet 对象中列的类型和属性信息的对象,在持久框层框架(如:mybatis, hibernate)中被广泛的应用. 常用方法 int getColumnCount() ...
- 【转载】如何在Android中避免创建不必要的对象
在编程开发中,内存的占用是我们经常要面对的现实,通常的内存调优的方向就是尽量减少内存的占用.这其中避免创建不必要的对象是一项重要的方面. Android设备不像PC那样有着足够大的内存,而且单个App ...
- XCode保存问题
1. 确认下证书是不是开发证书,如果是发布证书就会出现这样的提示. 2. 证书失效了,去开发者中心重新生成一个. 3. 包标识符不与描述文件包含的包标识符不一致,按照它的提示换一下就好了,最好不要点 ...
- IDEA下新建Vue项目
1.首先需要安装nodeJS,并配置全局环境变量. 2.在IDEA中新建一个空的project 3.在setting中配置JavaScript的语言版本为6 在file types的html中添加.* ...