new Vue()
实例化{{ prop }}
绑定值v-model
双向绑定值<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue</title>
</head>
<body>
<div id="app">
<input v-model="text" />
{{text}}
<span>{{text}}</span>
</div>
<script src="./vue.js"></script>
<script> var app = new Vue({ el: "#app", data: { text: "hello world", }, }); </script>
</body>
</html>
复制代码
class Vue
// 这里继承 EventTarget 提供 Vue 能够接收事件、而且能够建立侦听器的功能
class Vue extends EventTarget {
constructor(options) {
this.options = options;
this.$el = document.querySelector(options.$el);
// 数据双向绑定
this.data = this.observerData(options.data);
// 数据模板渲染
this.compileTemplate(this.$el);
}
}
复制代码
{{**}}
特征数据值,绑定data
中的值;v-model
属性的对该属性值进行数据data
绑定;compileTemplate(node) {
// 子节点
const children = node.childNodes;
children.forEach((it) => {
if (it.nodeType === 3) {
// text 文本节点
// 正则匹配 {{}} 特征的绑定值
const regexp = /\{\{\s*([^\s\{\}]+)\s*\}\}/gi;
const textContent = it.textContent;
if (textContent.match(regexp)) {
const prop = RegExp.$1;
it.textContent = textContent.replace(regexp, this.data[prop]);
// 节点事件响应监听
// 用于接收属性 set 后的事件响应
this.addEventListener(
prop,
function (event) {
it.textContent = textContent.replace(regexp, event.detail);
},
false
);
}
} else if (it.nodeType === 1) {
// node 元素节点
this.compileTemplate(it);
// check v-model
const attrs = it.attributes;
if (attrs.hasOwnProperty("v-model")) {
const _this = this;
const prop = attrs["v-model"].nodeValue;
it.value = this.data[prop];
// 监听输入 change
it.addEventListener(
"input",
function (event) {
// TODO 入口须要作XSS校验
_this.data[prop] = event.target.value;
},
false
);
}
}
});
}
复制代码
// 双向绑定
observerData(data) {
const _this = this;
return new Proxy(data, {
set: function (target, prop, newValue) {
// 建立 set 属性事件
const event = new CustomEvent(prop, { detail: newValue });
// 广播该 set 属性事件
_this.dispatchEvent(event);
return Reflect.set(...arguments);
},
});
}
复制代码
EventTarget 是一个 DOM 接口,由能够接收事件、而且能够建立侦听器的对象实现。javascript
Reflect 是一个内置的对象,它提供拦截 JavaScript 操做的方法。这些方法与 proxy handlers 的方法相同。Reflect 不是一个函数对象,所以它是不可构造的。
与大多数全局对象不一样,Reflect 不是一个构造函数。你不能将其与一个 new 运算符一块儿使用,或者将 Reflect 对象做为一个函数来调用。Reflect 的全部属性和方法都是静态的(就像 Math 对象)。html
Reflect.get()
: 获取对象身上某个属性的值,相似于 target[name]。Reflect.set()
: 将值分配给属性的函数。返回一个 Boolean,若是更新成功,则返回 true。Proxy 对象用于定义基本操做的自定义行为(如属性查找、赋值、枚举、函数调用等)。vue
target
:要使用 Proxy 包装的目标对象(能够是任何类型的对象,包括原生数组,函数,甚至另外一个代理)。handler
:一个一般以函数做为属性的对象,各属性中的函数分别定义了在执行各类操做时代理 p 的行为。
该方法会拦截目标对象的如下操做:java
- 访问属性:
proxy[foo]
和proxy.bar
- 访问原型链上的属性:
Object.create(proxy)[foo]
- Reflect.get():Reflect.get()方法与从 对象 (target[propertyKey]) 中读取属性相似,但它是经过一个函数执行来操做的。
该方法会拦截目标对象的如下操做:node
- 指定属性值:
proxy[foo] = bar
和proxy.foo = bar
- 指定继承者的属性值:
Object.create(proxy)[foo] = bar
- Reflect.set():静态方法 Reflect.set() 工做方式就像在一个对象上设置一个属性。
const handler = {
get: function(target, prop, receiver){
// 拦截读取
return Reflect.get(...arguments);
},
set: function(target, prop, newValue, receiver){
// 拦截设置
return Reflect.set(...arguments);
}
};
const p = new Proxy(target, handler);
复制代码
CustomEvent 事件是由程序建立的,能够有任意自定义功能的事件。数组
CustomEvent.detail
: 只读,任什么时候间初始化时传入的数据class Vue extends EventTarget {
constructor(options) {
super();
this.options = options;
this.$el = document.querySelector(options.el);
this.data = this.observerData(options.data);
this.compileTemplate(this.$el);
}
// 双向绑定
observerData(data) {
const _this = this;
return new Proxy(data, {
set: function (target, prop, newValue) {
// 事件发布
const event = new CustomEvent(prop, { detail: newValue });
_this.dispatchEvent(event);
return Reflect.set(...arguments);
},
});
}
// 模板编译
compileTemplate(node) {
const children = node.childNodes;
children.forEach((it) => {
if (it.nodeType === 3) {
// text 文本节点
const regexp = /\{\{\s*([^\s\{\}]+)\s*\}\}/gi;
const textContent = it.textContent;
if (textContent.match(regexp)) {
const prop = RegExp.$1;
it.textContent = textContent.replace(regexp, this.data[prop]);
// 事件接收
this.addEventListener(
prop,
function (event) {
it.textContent = textContent.replace(regexp, event.detail);
},
false
);
}
} else if (it.nodeType === 1) {
// node 元素节点
this.compileTemplate(it);
// check v-model
const attrs = it.attributes;
if (attrs.hasOwnProperty("v-model")) {
const _this = this;
const prop = attrs["v-model"].nodeValue;
it.value = this.data[prop];
it.addEventListener(
"input",
function (event) {
// TODO 入口须要作XSS校验
_this.data[prop] = event.target.value;
},
false
);
}
}
});
}
}
复制代码