<body>
<div id="app">
<form>
<input type="text" v-model="userInfo.name" />
<button type="button" v-click="increment">增加button>
form>
<h3 v-bind="number">h3>
<h4 v-bind="userInfo.name">h4>
div>
body>
window.onload = function () {
var app = new myVue({
el: '#app',
data: {
number: 0,
userInfo: {
name: 0
}
},
methods: {
increment: function () {
console.log(this)
console.log()
this.userInfo.name += 1
console.log(this.userInfo.name)
this.number++
}
},
directives: {}
})
console.log(app._binding, 'bind')
}
并初始化,将配置数据挂载到构造函数的原型对象上
function myVue(options) {
this._init(options)
}
myVue.prototype._init = function (options) {
this.$options = options
// 存储跟节点DOM对象
this.$el = document.querySelector(options.el)
// 存储data数据
this.$data = options.data
// 存储methods所有的方法
this.$methods = options.methods
// 用于存储data中数据绑定到dom的元素
this._binding = {}
// 形式如下
// {
// number: {
// _directives: [watcher1, watcher2]
// },
// 'userInfo.name': {
// _directives: [watcher1, watcher2]
// }
// }
// 绑定发布者,在set属性里面遍历_binding[key]._directives
this._obverse(this.$data)
// 递归根节点,通过识别节点的属性,识别出不同指令,不同指令进行不同操作,
// 如v-bind指令:创建watcher并将其push进_binding
// _binding[key]._directives.push(watcher)
this._complie(this.$el)
}
并在其原型对象上实现update方法,该类将会把DOM中使用了指令的节点与data数据绑定
function Watcher(name, el, vm, exp, attr) {
this.name = name //指令名称,例如文本节点,该值设为"text"
this.el = el //指令对应的DOM元素
this.vm = vm //指令所属myVue实例
this.exp = exp //指令对应的值,本例如"number"
this.attr = attr //绑定的属性值,本例为"innerHTML"
this.update()
}
Watcher.prototype.update = function () {
// let exps = this.exp.split('.')
// let res = this.vm.$data
// exps.forEach(item => (res = res[item]))
// console.log(res, 'res')
this.el[this.attr] = getObjValueByStr(this.vm.$data, this.exp)
}
在data数据的 set属性中实现页面数据更新的方法调用
myVue.prototype._obverse = function (obj, preKey) {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
let curKey
if (preKey && preKey != '') {
curKey = preKey + '.' + key
} else {
curKey = key
}
console.log(curKey)
this._binding[curKey] = {
_directives: []
}
let value = obj[key]
console.log(value, '----')
let binding = this._binding[curKey]
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function () {
console.log(`获取${value}`)
return value
},
set: function (newVal) {
if (value !== newVal) {
console.log(`更新${newVal}`)
value = newVal
console.log(binding, 'binding')
binding._directives.forEach(function (item) {
item.update()
})
}
}
})
if (typeof value === 'object') {
this._obverse(value, curKey)
}
}
}
}
遍历根节点中的所有节点,根据节点中设置的属性(即指令),对不同指令实现具体的功能如属性绑定指令 v-bind ,就创建一个监听实例,并放入myVue的binding中,binding[绑定的data中的数据名称]._directives.push(watcher实例)
myVue.prototype._complie = function (root) {
var _this = this
var nodes = root.children
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i]
if (node.children.length) {
this._complie(node)
}
if (node.hasAttribute('v-click')) {
node.onclick = (function () {
var attrVal = nodes[i].getAttribute('v-click')
return _this.$methods[attrVal].bind(_this.$data)
})()
}
if (node.hasAttribute('v-model') && (node.tagName == 'INPUT' || node.tagName == 'TEXTAREA')) {
node.addEventListener(
'input',
(function (key) {
var attrVal = node.getAttribute('v-model')
_this._binding[attrVal]._directives.push(
new Watcher(
'input',
node,
_this,
attrVal,
'value'
)
)
return function () {
// let arr = attrVal.split('.')
// let resAttr = _this.$data
// for (let j = 0; j < arr.length - 1; j++) {
// resAttr = resAttr[arr[j]]
// }
// resAttr[arr[arr.length - 1]] = nodes[key].value
objValueByStr(_this.$data, attrVal, nodes[key].value)
// _this.$data[attrVal] = nodes[key].value
}
})(i)
)
}
if (node.hasAttribute('v-bind')) {
var attrVal = node.getAttribute('v-bind')
_this._binding[attrVal]._directives.push(
new Watcher(
'text',
node,
_this,
attrVal,
'innerHTML'
)
)
}
}
}
属性名 | 功能说明 |
---|---|
configurable: | 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。 |
enumerable | 表示能否通过for in循环访问属性,默认值为false |
writable: | 表示能否修改属性的值。默认值为false。 |
value: | 包含这个属性的数据值。默认值为undefined。 |
get: | 在读取属性时调用的函数,默认值是undefined |
set: | 在写入属性的时候调用的函数,默认值是undefined |
DOCTYPE html>
<head>
<title>myVuetitle>
head>
<style>
#app {
text-align: center;
}
style>
<body>
<div id="app">
<form>
<input type="text" v-model="userInfo.name" />
<button type="button" v-click="increment">增加button>
form>
<h3 v-bind="number">h3>
<h4 v-bind="userInfo.name">h4>
div>
body>
<script>
function myVue(options) {
this._init(options)
}
myVue.prototype._init = function (options) {
this.$options = options
// 存储跟节点DOM对象
this.$el = document.querySelector(options.el)
// 存储data数据
this.$data = options.data
// 存储methods所有的方法
this.$methods = options.methods
// 用于存储data中数据绑定到dom的元素
this._binding = {}
// 形式如下
// {
// number: {
// _directives: [watcher1, watcher2]
// },
// 'userInfo.name': {
// _directives: [watcher1, watcher2]
// }
// }
// 绑定发布者,在set属性里面遍历_binding[key]._directives
this._obverse(this.$data)
// 递归根节点,通过识别节点的属性,识别出不同指令,不同指令进行不同操作,
// 如v-bind指令:创建watcher并将其push进_binding
// _binding[key]._directives.push(watcher)
this._complie(this.$el)
}
myVue.prototype._obverse = function (obj, preKey) {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
let curKey
if (preKey && preKey != '') {
curKey = preKey + '.' + key
} else {
curKey = key
}
console.log(curKey)
this._binding[curKey] = {
_directives: []
}
let value = obj[key]
console.log(value, '----')
let binding = this._binding[curKey]
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function () {
console.log(`获取${value}`)
return value
},
set: function (newVal) {
if (value !== newVal) {
console.log(`更新${newVal}`)
value = newVal
console.log(binding, 'binding')
binding._directives.forEach(function (item) {
item.update()
})
}
}
})
if (typeof value === 'object') {
this._obverse(value, curKey)
}
}
}
}
myVue.prototype._complie = function (root) {
var _this = this
var nodes = root.children
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i]
if (node.children.length) {
this._complie(node)
}
if (node.hasAttribute('v-click')) {
node.onclick = (function () {
var attrVal = nodes[i].getAttribute('v-click')
return _this.$methods[attrVal].bind(_this.$data)
})()
}
if (node.hasAttribute('v-model') && (node.tagName == 'INPUT' || node.tagName == 'TEXTAREA')) {
node.addEventListener(
'input',
(function (key) {
var attrVal = node.getAttribute('v-model')
_this._binding[attrVal]._directives.push(
new Watcher(
'input',
node,
_this,
attrVal,
'value'
)
)
return function () {
// let arr = attrVal.split('.')
// let resAttr = _this.$data
// for (let j = 0; j < arr.length - 1; j++) {
// resAttr = resAttr[arr[j]]
// }
// resAttr[arr[arr.length - 1]] = nodes[key].value
objValueByStr(_this.$data, attrVal, nodes[key].value)
// _this.$data[attrVal] = nodes[key].value
}
})(i)
)
}
if (node.hasAttribute('v-bind')) {
var attrVal = node.getAttribute('v-bind')
_this._binding[attrVal]._directives.push(
new Watcher(
'text',
node,
_this,
attrVal,
'innerHTML'
)
)
}
}
}
function Watcher(name, el, vm, exp, attr) {
this.name = name //指令名称,例如文本节点,该值设为"text"
this.el = el //指令对应的DOM元素
this.vm = vm //指令所属myVue实例
this.exp = exp //指令对应的值,本例如"number"
this.attr = attr //绑定的属性值,本例为"innerHTML"
this.update()
}
Watcher.prototype.update = function () {
// let exps = this.exp.split('.')
// let res = this.vm.$data
// exps.forEach(item => (res = res[item]))
// console.log(res, 'res')
this.el[this.attr] = getObjValueByStr(this.vm.$data, this.exp)
}
// 通过字符串形式(userInfo.name),将对象中的属性修改为目标值
// target=userInfo: {name: '12'}, objStr = 'userInfo.name', value = '1'
function objValueByStr(targetObj, objStr, value) {
let arr = objStr.split('.')
let resAttr = targetObj
for (let j = 0; j < arr.length - 1; j++) {
resAttr = resAttr[arr[j]]
}
resAttr[arr[arr.length - 1]] = value
}
// 通过字符串形式('userInfo.name'),获取对象中该属性的值
function getObjValueByStr(targetObj, objStr) {
let exps = objStr.split('.')
let res = targetObj
exps.forEach(item => (res = res[item]))
return res
}
window.onload = function () {
var app = new myVue({
el: '#app',
data: {
number: 0,
userInfo: {
name: 0
}
},
methods: {
increment: function () {
console.log(this)
console.log()
this.userInfo.name += 1
console.log(this.userInfo.name)
this.number++
}
},
directives: {}
})
console.log(app._binding, 'bind')
}
script>