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
);
}
}
});
}
}
複製代碼