Skip to content

Commit

Permalink
Merge pull request #3 from liuwu1211/develop
Browse files Browse the repository at this point in the history
增加部分注释
  • Loading branch information
ark-65 authored Sep 24, 2020
2 parents 817b759 + f77c56e commit 2afe804
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 20 deletions.
9 changes: 7 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ <h3>{{person.fav}}</h3>
<li>1</li>
<li>2</li>
<li>3</li>
<li>{{test}}</li>
</ul>
<h3>{{msg}}</h3>
<div z-text="msg"></div>
Expand All @@ -38,18 +39,22 @@ <h3>{{msg}}</h3>
age: 28,
fav: 'computer'
},
test: 'test',
msg: '学习MVVM实现原理',
htmlStr: '<p>双击666</p>',
imgSrc: './imgs/mvvm.jpg'
},
methods: {
handlerClick () {
console.log(this);
this.msg = '学习MVVM';
this.$data.person.name = 'ARK';
// 把data用defineProperty重写get 和set方法后
this.msg = '学习MVVM';
this.person.age = 30;
alert(this.person.name);
}
}
})
</script>
</body>
</html>
</html>
36 changes: 30 additions & 6 deletions js/Compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,36 @@ class Compile {
// 绑定vm
this.vm = vm;
// 1. 获取文档随便对象,放入内存中,在渲染时调用,会减少页面的回流和重绘。文档碎片的对象就是整个的根节点,在文档碎片中替换所有的表达式后return dom
/**
* **真实DOM:**
* 大家都知道DOM API,例如调用documnt.createElement创建一个真实div节点插入到DOM文档流中,
* 这个原生API实际上是通过使用C++编写的浏览器引擎实现的,我们不需要了解C++是如何实现的,
* 只需要调用javascript api就可以创建真实DOM。你可以通过在浏览器打印DOM节点,会发现它包含很多属性。
*
* **虚拟DOM:**
* 在Vue中的虚拟DOM会在每个实例通过this.$createElement返回一个虚拟节点,
* 这个虚拟节点也表示一个div但他是一个纯javascript对象,他和真实DOM差异是非常大的。
* 看到上图虚拟DOM它除了包含当前节点名字和属性,还有children表示节点的子元素,这就构成了一个虚拟DOM树。
*
* 普及一下虚拟DOM和真实的DOM的差异:
* 1、 资源消耗问题
* 使用javascript操作真实DOM是非常消耗资源的,虽然很多浏览器做了优化但是效果不大。你看到虚拟DOM是一个纯javascript对象。
* 假设你有1000个节点,那会相应创建1000个节点,那也是非常节省资源的,但是如果创建1000个DOM节点就不同了。
* 2、执行效率问题
* 如果你要修改一个真实DOM,一般调用innerHTML方法,那浏览器会把旧的节点移除再添加新的节点,但是在虚拟DOM中,只需要修改一个对象的属性,
* 再把虚拟DOM渲染到真实DOM上。很多人会误解虚拟DOM比真实DOM速度快,其实虚拟DOM只是把DOM变更的逻辑提取出来,使用javascript计算差异,
* 减少了操作真实DOM的次数,只在最后一次才操作真实DOM,所以如果你的应用有复杂的DOM变更操作,虚拟DOM会比较快。
* 3、虚拟DOM还有其他好处
* 其实虚拟DOM还可以应用在其他地方,因为他们只是抽象节点,可以把它编译成其他平台,例如android、ios。
* 市面上利用形同架构模式的应用有React Native,Weeks,Native script,就是利用虚拟DOM的特点实现的。
*
*/
const fragment = this.node2Fragment(this.el);
// console.log(fragment)
console.log(fragment)
// 2. 编译模板
this.compile(fragment);
// this.compile(fragment);
// 3. 追加子元素到根元素
this.el.appendChild(fragment);
// this.el.appendChild(fragment);
}

/**
Expand Down Expand Up @@ -89,11 +113,11 @@ class Compile {
// 其中 text是文本节点(空格之类的)
if (this.isElementNode(child)) {
// 元素节点 ,并编译
// console.log('元素节点', child);
console.log('元素节点', child);
this.compileElement(child);
} else {
// 文本节点,并编译
// console.log('文本节点', child);
console.log('文本节点', child);
this.compileText(child);
}
// 如果当前节点还存在子节点,并且有length属性,递归遍历
Expand Down Expand Up @@ -300,4 +324,4 @@ const compileUtil = {
},
// 更新的函数
updater: new Updater()
}
}
17 changes: 10 additions & 7 deletions js/Dep.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
/**
* 为了收集所有的watcher
* 依赖跟踪类
* dep是可以有多个指令订阅的可观察对象
*/
class Dep {
constructor() {
this.subs = [];
// 订阅任务队列,方式有相同的任务,用Set数据结构简单处理
this.subscribers = [];
}
// 收集观察者
// 收集观察者,用于收集依赖项,在vue源码中使用depend来收集依赖项,由于这是简化的,所以我们直接用addSub
addSub(watcher) {
this.subs.push(watcher);
this.subscribers.push(watcher);
}
// 通知观察者去更新
// 用于发布消息,触发依赖项重新执行,通知观察者去更新
notify() {
console.log('通知了观察者', this.subs);
this.subs.forEach(w => w.update());
console.log('通知了观察者', this.subscribers);
this.subscribers.forEach(w => w.update());
}
}
}
12 changes: 8 additions & 4 deletions js/Observer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Observer {
}
*/
if (data && typeof data === 'object') {
// console.log(Object.keys(data));
console.log(`observe----->`, Object.keys(data));
Object.keys(data).forEach(key => {
this.defineReactive(data, key, data[key]);
})
Expand All @@ -38,23 +38,27 @@ class Observer {
// 递归遍历
this.observe(value);
const dep = new Dep();
console.log(dep);
// 劫持data中的属性
/**
* object (必须有 操作的对象本身 这个很容易理解不传它操作谁?)
* propertyname (必须有 属性名 添加修改属性得有属性名)
* descriptor 必须有 大概是属性描述配置
* |- value 设置属性的值
* |- writable 是否可操作属性值 默认true
* |- configurable 是否可修改配置 默认 true
* |- enumerable 是否可枚举 默认 false
* |- configurable 是否可修改配置 默认 true,可以防止被重新定义
* |- enumerable 是否可枚举 默认 false,如果我们将某个属性的enumerable声明为false,当你迭代读取他们的key值时,你是没有办法读取的,同样它不会显示在对象键中
*/
// 执行后可看到 data 下的每一个属性都有个set,get方法
Object.defineProperty(data, key, {
enumerable: true,
configurable: false,
get () {
// 如果Dep.target 存在(在watcher中创建了),就在dep.中插入观察者
// console.log(data, key, value);
Dep.target && dep.addSub(Dep.target);
// 这里Dep.target 是null 因为在watcher中挂载后就立即销毁了,防止重复挂载
// console.log(dep);
// 订阅数据变化时,往Dep中添加观察者
return value;
},
Expand All @@ -70,4 +74,4 @@ class Observer {
}
})
}
}
}
2 changes: 1 addition & 1 deletion js/Watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ class Watcher {
Dep.target = null;
return oldVle;
}
}
}
54 changes: 54 additions & 0 deletions vue.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h2>{{person.name}} -- {{person.age}}</h2>
<h3>{{person.fav}}</h3>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>{{test}}</li>
</ul>
<h3>{{msg}}</h3>
<div v-text="msg"></div>
<div v-text="person.name"></div>
<div v-html="htmlStr"></div>
<input type="text" v-model="msg">
<button v-on:click="handlerClick">双击on-666</button>
<button @click="handlerClick">双击@-888</button>
<img v-bind:src="imgSrc"/>
</div>
<script src="./js/source/Vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
person: {
name: 'ark',
age: 28,
fav: 'computer'
},
test: 'test',
msg: '学习MVVM实现原理',
htmlStr: '<p>双击666</p>',
imgSrc: './imgs/mvvm.jpg'
},
methods: {
handlerClick () {
console.log(this);
this.$data.person.name = 'ARK';
// 把data用defineProperty重写get 和set方法后
this.msg = '学习MVVM';
this.person.age = 30;
alert(this.person.name);
}
}
})
</script>
</body>
</html>

0 comments on commit 2afe804

Please sign in to comment.