第二天
watcher
一.创建渲染1.初始化渲染Watcher
import {mountComponent} from './lifecycle'
Vue.prototype.$mount = function (el) {
const vm = this;
const options = vm.$options;
el = document.querySelector(el);
// 如果没有render方法
if (!options.render) {
let template = options.template;
// 如果没有模板但是有el
if (!template && el) {
template = el.outerHTML;
}
const render= compileToFunctions(template);
options.render = render;
}
mountComponent(vm,el);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
lifecycle.js
export function lifecycleMixin() {
Vue.prototype._update = function (vnode) {}
}
export function mountComponent(vm, el) {
vm.$el = el;
let updateComponent = () => {
// 将虚拟节点 渲染到页面上
vm._update(vm._render());
}
new Watcher(vm, updateComponent, () => {}, true);
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
render.js
export function renderMixin(Vue){
Vue.prototype._render = function () {}
}
1
2
3
2
3
watcher.js
let id = 0;
class Watcher {
constructor(vm, exprOrFn, cb, options) {
this.vm = vm;
this.exprOrFn = exprOrFn;
if (typeof exprOrFn == 'function') {
this.getter = exprOrFn;
}
this.cb = cb;
this.options = options;
this.id = id++;
this.get();
}
get() {
this.getter();
}
}
export default Watcher;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
先调用_render
方法生成虚拟dom
,通过_update
方法将虚拟dom
创建成真实的dom
dom
2.生成虚拟import {createTextNode,createElement} from './vdom/create-element'
export function renderMixin(Vue){
Vue.prototype._v = function (text) { // 创建文本
return createTextNode(text);
}
Vue.prototype._c = function () { // 创建元素
return createElement(...arguments);
}
Vue.prototype._s = function (val) {
return val == null? '' : (typeof val === 'object'?JSON.stringify(val):val);
}
Vue.prototype._render = function () {
const vm = this;
const {render} = vm.$options;
let vnode = render.call(vm);
return vnode;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
创建虚拟节点
export function createTextNode(text) {
return vnode(undefined,undefined,undefined,undefined,text)
}
export function createElement(tag,data={},...children){
let key = data.key;
if(key){
delete data.key;
}
return vnode(tag,data,key,children);
}
function vnode(tag,data,key,children,text){
return {
tag,
data,
key,
children,
text
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DOM
元素
3.生成真实将虚拟节点渲染成真实节点
import {patch} './observer/patch'
export function lifecycleMixin(Vue){
Vue.prototype._update = function (vnode) {
const vm = this;
vm.$el = patch(vm.$el,vnode);
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
export function patch(oldVnode,vnode){
const isRealElement = oldVnode.nodeType;
if(isRealElement){
const oldElm = oldVnode;
const parentElm = oldElm.parentNode;
let el = createElm(vnode);
parentElm.insertBefore(el,oldElm.nextSibling);
parentElm.removeChild(oldVnode)
return el;
}
}
function createElm(vnode){
let {tag,children,key,data,text} = vnode;
if(typeof tag === 'string'){
vnode.el = document.createElement(tag);
updateProperties(vnode);
children.forEach(child => {
return vnode.el.appendChild(createElm(child));
});
}else{
vnode.el = document.createTextNode(text);
}
return vnode.el
}
function updateProperties(vnode){
let newProps = vnode.data || {}; // 获取当前老节点中的属性
let el = vnode.el; // 当前的真实节点
for(let key in newProps){
if(key === 'style'){
for(let styleName in newProps.style){
el.style[styleName] = newProps.style[styleName]
}
}else if(key === 'class'){
el.className= newProps.class
}else{ // 给这个元素添加属性 值就是对应的值
el.setAttribute(key,newProps[key]);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
二.生命周期的合并
1.Mixin原理
import {mergeOptions} from '../util/index.js'
export function initGlobalAPI(Vue){
Vue.options = {};
Vue.mixin = function (mixin) {
// 将属性合并到Vue.options上
this.options = mergeOptions(this.options,mixin);
return this;
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
2.合并生命周期
export const LIFECYCLE_HOOKS = [
'beforeCreate',
'created',
'beforeMount',
'mounted',
'beforeUpdate',
'updated',
'beforeDestroy',
'destroyed',
]
const strats = {};
function mergeHook(parentVal, childValue) {
if (childValue) {
if (parentVal) {
return parentVal.concat(childValue);
} else {
return [childValue]
}
} else {
return parentVal;
}
}
LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
})
export function mergeOptions(parent, child) {
const options = {}
for (let key in parent) {
mergeField(key)
}
for (let key in child) {
if (!parent.hasOwnProperty(key)) {
mergeField(key);
}
}
function mergeField(key) {
if (strats[key]) {
options[key] = strats[key](parent[key], child[key]);
} else {
if (typeof parent[key] == 'object' && typeof child[key] == 'object') {
options[key] = {
...parent[key],
...child[key]
}
}else{
options[key] = child[key];
}
}
}
return options
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
3.调用生命周期
export function callHook(vm, hook) {
const handlers = vm.$options[hook];
if (handlers) {
for (let i = 0; i < handlers.length; i++) {
handlers[i].call(vm);
}
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
4.初始化流程中调用生命周期
Vue.prototype._init = function (options) {
const vm = this;
vm.$options = mergeOptions(vm.constructor.options,options);
// 初始化状态
callHook(vm,'beforeCreate');
initState(vm);
callHook(vm,'created');
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
三.依赖收集
每个属性都要有一个dep
,每个dep
中存放着watcher
,同一个watcher
会被多个dep
所记录。
1.在渲染时存储watcher
class Watcher{
// ...
get(){
pushTarget(this);
this.getter();
popTarget();
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
let id = 0;
class Dep{
constructor(){
this.id = id++;
}
}
let stack = [];
export function pushTarget(watcher){
Dep.target = watcher;
stack.push(watcher);
}
export function popTarget(){
stack.pop();
Dep.target = stack[stack.length-1];
}
export default Dep;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2.对象依赖收集
let dep = new Dep();
Object.defineProperty(data, key, {
get() {
if(Dep.target){ // 如果取值时有watcher
dep.depend(); // 让watcher保存dep,并且让dep 保存watcher
}
return value
},
set(newValue) {
if (newValue == value) return;
observe(newValue);
value = newValue;
dep.notify(); // 通知渲染watcher去更新
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Dep实现
class Dep{
constructor(){
this.id = id++;
this.subs = [];
}
depend(){
if(Dep.target){
Dep.target.addDep(this);// 让watcher,去存放dep
}
}
notify(){
this.subs.forEach(watcher=>watcher.update());
}
addSub(watcher){
this.subs.push(watcher);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
watcher
constructor(){
this.deps = [];
this.depsId = new Set();
}
addDep(dep){
let id = dep.id;
if(!this.depsId.has(id)){
this.depsId.add(id);
this.deps.push(dep);
dep.addSub(this);
}
}
update(){
this.get();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3.数组的依赖收集
this.dep = new Dep(); // 专门为数组设计的
if (Array.isArray(value)) {
value.__proto__ = arrayMethods;
this.observeArray(value);
} else {
this.walk(value);
}
function defineReactive(data, key, value) {
let childOb = observe(value);
let dep = new Dep();
Object.defineProperty(data, key, {
get() {
if(Dep.target){
dep.depend();
if(childOb){
childOb.dep.depend(); // 收集数组依赖
}
}
return value
},
set(newValue) {
if (newValue == value) return;
observe(newValue);
value = newValue;
dep.notify();
}
})
}
arrayMethods[method] = function (...args) {
// ...
ob.dep.notify()
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
递归收集数组依赖
if(Dep.target){
dep.depend();
if(childOb){
childOb.dep.depend(); // 收集数组依赖
if(Array.isArray(value)){ // 如果内部还是数组
dependArray(value);// 不停的进行依赖收集
}
}
}
function dependArray(value) {
for (let i = 0; i < value.length; i++) {
let current = value[i];
current.__ob__ && current.__ob__.dep.depend();
if (Array.isArray(current)) {
dependArray(current)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
四.实现Vue异步更新之nextTick
1.实现队列机制
update(){
queueWatcher(this);
}
1
2
3
2
3
scheduler
import {
nextTick
} from '../util/next-tick'
let has = {};
let queue = [];
function flushSchedulerQueue() {
for (let i = 0; i < queue.length; i++) {
let watcher = queue[i];
watcher.run()
}
queue = [];
has = {}
}
let pending = false
export function queueWatcher(watcher) {
const id = watcher.id;
if (has[id] == null) {
has[id] = true;
queue.push(watcher);
if(!pending){
nextTick(flushSchedulerQueue)
pending = true;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2.nextTick实现原理
util/next-tick.js
let callbacks = [];
function flushCallbacks() {
callbacks.forEach(cb => cb());
}
let timerFunc;
if (Promise) { // then方法是异步的
timerFunc = () => {
Promise.resolve().then(flushCallbacks)
}
}else if (MutationObserver) { // MutationObserver 也是一个异步方法
let observe = new MutationObserver(flushCallbacks); // H5的api
let textNode = document.createTextNode(1);
observe.observe(textNode, {
characterData: true
});
timerFunc = () => {
textNode.textContent = 2;
}
}else if (setImmediate) {
timerFunc = () => {
setImmediate(flushCallbacks)
}
}else{
timerFunc = () => {
setTimeout(flushCallbacks, 0);
}
}
export function nextTick(cb) {
callbacks.push(cb);
timerFunc();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31