第一步,定义响应式变量
function defineReactive(obj, key, value) {
Object.defineProperty(obj, key, {
get() {
console.log('get value');
return value;
},
set(newValue) {
if (newValue != value) {
console.log('set value');
value = newValue;
}
}
});
}
const person = { name: 'John', age: 20 };
Object.keys(person).forEach((key) => defineReactive(person, key, person[key]));
person.name = 'Tom';
第二步,定义响应式封装对象
function defineReactive(obj, key, value) {
Object.defineProperty(obj, key, {
get() {
console.log('get value');
return value;
},
set(newValue) {
if (newValue != value) {
console.log('set value');
observe(newValue);
value = newValue;
}
}
});
}
function observe(value) {
if (typeof value != 'object') {
return;
}
Object.keys(value).forEach((key) => {
if (typeof value[key] == 'object') {
observe(value[key]);
}
defineReactive(value, key, value[key]);
});
}
const person = { name: 'John', age: 20, family: { father: 'Tom', mother: 'amy' } };
observe(person);
person.family.father = 'Mike';
person.family = { fater: '', mother: '' };
第三步,创建Vue对象原型
function defineReactive(obj, key, value) {
Object.defineProperty(obj, key, {
get() {
console.log('get value');
return value;
},
set(newValue) {
if (newValue != value) {
console.log('set value');
observe(newValue);
value = newValue;
}
}
});
}
function observe(value) {
if (typeof value != 'object') {
return;
}
Object.keys(value).forEach((key) => {
if (typeof value[key] == 'object') {
observe(value[key]);
}
defineReactive(value, key, value[key]);
});
}
function compile(node, vm) {}
function Vue(options) {
this.$data = options.data;
this.$methods = options.methods;
observe(this.$data);
const rootElement = document.querySelector(options.el);
compile(rootElement, this);
}
new Vue({
el: '#root',
data: {
name: 'John',
age: 20
},
methods: {}
});
第四步,编译vue模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<p>{{name}}</p>
<button @click="onClick">Button</button>
</div>
<!-- <script src="../node_modules/vue/dist/vue.js"></script> -->
<script src="vue_4.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: 'John'
},
methods: {
onClick() {
console.log('clicked');
}
}
});
</script>
</body>
</html>
function defineReactive(obj, key, value) {
Object.defineProperty(obj, key, {
get() {
console.log('get value');
return value;
},
set(newValue) {
if (newValue != value) {
console.log('set value');
observe(newValue);
value = newValue;
}
}
});
}
function observe(value) {
if (typeof value != 'object') {
return;
}
Object.keys(value).forEach((key) => {
if (typeof value[key] == 'object') {
observe(value[key]);
}
defineReactive(value, key, value[key]);
});
}
function compile(rootNode, vm) {
rootNode.childNodes.forEach((node) => {
// 元素节点
if (node.nodeType == 1) {
compileElement(node, vm);
// 文本节点
} else if (node.nodeType === 3) {
compileText(node, vm);
}
// 遍历子节点
if (node.childNodes) {
compile(node, vm);
}
});
}
function compileText(node, vm) {
if (/\{\{\ *([^}]+) *}\}/.test(node.textContent)) {
const key = RegExp.$1;
console.log(key);
node.textContent = node.textContent.replace(new RegExp('\\{\\{ *' + key + ' *\\}\\}', 'g'), vm.$data[key]);
}
}
function compileElement(node, vm) {
let nodeAttrs = node.attributes;
Array.from(nodeAttrs).forEach((attr) => {
if (attr.name.indexOf('@') == 0) {
const eventName = attr.name.substr(1);
node.addEventListener(eventName, function () {
vm.$methods[attr.value]();
});
}
});
}
function Vue(options) {
this.$data = options.data;
this.$methods = options.methods;
observe(this.$data);
const rootElement = document.querySelector(options.el);
compile(rootElement, this);
}
第五步,创建对象data映射
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<p>sss {{name}} sss</p>
<button class="red" @click="onClick">Button</button>
</div>
<!-- <script src="../node_modules/vue/dist/vue.js"></script> -->
<script src="vue_5.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: 'John'
},
methods: {
onClick() {
this.name = 'LiChao';
console.log('123123');
}
}
});
</script>
</body>
</html>
function defineReactive(obj, key, value) {
Object.defineProperty(obj, key, {
get() {
console.log('get value');
return value;
},
set(newValue) {
if (newValue != value) {
console.log('set value');
observe(newValue);
value = newValue;
}
}
});
}
function observe(value) {
if (typeof value != 'object') {
return;
}
Object.keys(value).forEach((key) => {
if (typeof value[key] == 'object') {
observe(value[key]);
}
defineReactive(value, key, value[key]);
});
}
function compile(rootNode, vm) {
rootNode.childNodes.forEach((node) => {
// 元素节点
if (node.nodeType == 1) {
compileElement(node, vm);
// 文本节点
} else if (node.nodeType === 3) {
compileText(node, vm);
}
if (node.childNodes) {
compile(node, vm);
}
});
}
function compileText(node, vm) {
if (/\{\{\ *([^}]+) *}\}/.test(node.textContent)) {
const key = RegExp.$1;
node.textContent = node.textContent.replace(new RegExp('\\{\\{ *' + key + ' *\\}\\}', 'g'), vm.$data[key]);
}
}
function compileElement(node, vm) {
let nodeAttrs = node.attributes;
console.log(nodeAttrs);
Array.from(nodeAttrs).forEach((attr) => {
if (attr.name.indexOf('@') == 0) {
const eventName = attr.name.substr(1);
node.addEventListener(eventName, function () {
vm.$methods[attr.value].call(vm);
});
}
});
}
function proxy(vm) {
Object.keys(vm.$data).forEach((key) => {
Object.defineProperty(vm, key, {
get() {
return vm.$data[key];
},
set(v) {
vm.$data[key] = v;
}
});
});
}
function Vue(options) {
this.$data = options.data;
this.$methods = options.methods;
observe(this.$data);
proxy(this);
const rootElement = document.querySelector(options.el);
compile(rootElement, this);
}
第六步,响应式更新,依赖收集
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<p>Hi, {{name}}</p>
<p>{{gender}}</p>
<p>{{name}}</p>
<p>{{c}}</p>
<button @click="var name='1111'; name='2222';">Button</button>
</div>
<!-- <script src="../node_modules/vue/dist/vue.js"></script> -->
<script src="vue_6.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: 'John',
gender: 'male',
a: 1,
b: 2
},
mounted() {
console.log('mounted');
},
computed: {
c() {
return this.a + this.b;
}
},
methods: {
onClick() {
//this.$data.name = 'LiChao';
this.name = 'LiChao';
// this.gender = 'female';
this.a = 10;
}
}
});
</script>
</body>
</html>
function defineReactive(obj, key, value) {
// 每个key对应一个Dep实例
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
console.log('get value');
Dep.target && dep.addDep(Dep.target);
return value;
},
set(newValue) {
if (newValue != value) {
console.log('set value');
observe(newValue);
value = newValue;
dep.notify();
}
}
});
}
function observe(value) {
if (typeof value != 'object') {
return;
}
Object.keys(value).forEach((key) => {
if (typeof value[key] == 'object') {
observe(value[key]);
}
defineReactive(value, key, value[key]);
});
}
function compile(rootNode, vm) {
rootNode.childNodes.forEach((node) => {
// 元素节点
if (node.nodeType == 1) {
compileElement(node, vm);
// 文本节点
} else if (node.nodeType === 3) {
compileText(node, vm);
}
if (node.childNodes) {
compile(node, vm);
}
});
}
function compileText(node, vm) {
if (/\{\{\ *([^}]+) *}\}/.test(node.textContent)) {
node._textContent = node.textContent;
const key = RegExp.$1;
//初始化数据
updateText(node, key, vm);
//监听数据
new Watcher(vm, key, () => {
updateText(node, key, vm);
});
}
}
function updateText(node, key, vm) {
node.textContent = node._textContent.replace(new RegExp('\\{\\{ *' + key + ' *\\}\\}', 'g'), vm.$data[key]);
}
function compileElement(node, vm) {
let nodeAttrs = node.attributes;
Array.from(nodeAttrs).forEach((attr) => {
if (attr.name.indexOf('@') == 0) {
const eventName = attr.name.substr(1);
node.addEventListener(eventName, function () {
vm.$methods[attr.value].call(vm);
});
}
});
}
function proxy(vm) {
Object.keys(vm.$data).forEach((key) => {
Object.defineProperty(vm, key, {
get() {
return vm.$data[key];
},
set(v) {
vm.$data[key] = v;
}
});
});
}
class Watcher {
constructor(vm, key, updater) {
this.vm = vm;
this.key = key;
this.updaterFn = updater;
// 创建实例时,把当前实例指定到Dep.target静态属性上
Dep.target = this;
// 读一下key,触发get
vm[key];
// 置空
Dep.target = null;
}
// 未来执行dom更新函数,由dep调用的
update() {
this.updaterFn.call(this.vm, this.vm[this.key]);
}
}
class Dep {
constructor() {
this.watchers = [];
}
addDep(watcher) {
this.watchers.push(watcher);
}
notify() {
this.watchers.forEach((watcher) => watcher.update());
}
}
function Vue(options) {
this.$data = options.data;
this.$methods = options.methods;
observe(this.$data);
proxy(this);
const rootElement = document.querySelector(options.el);
compile(rootElement, this);
}