Skip to main content

Vue2源码学习

· 5 min read
LIU

Vue.js 是一个渐进式JavaScript框架,其源码设计精巧,包含了响应式系统、虚拟DOM、模板编译等核心功能。本文将从源码角度深入分析Vue2的核心实现原理。

1. 响应式系统

Vue2的响应式系统基于Object.defineProperty实现,通过数据劫持和发布订阅模式来实现数据的响应式更新。

1.1 Observer类

Observer类是响应式系统的核心,负责将普通对象转换为响应式对象:

class Observer {
constructor(value) {
this.value = value;
this.dep = new Dep();
def(value, '__ob__', this);
if (Array.isArray(value)) {
// 处理数组
this.observeArray(value);
} else {
// 处理对象
this.walk(value);
}
}

walk(obj) {
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]);
}
}
}

1.2 defineReactive函数

defineReactive函数是响应式系统的核心实现:

function defineReactive(obj, key, val) {
const dep = new Dep();

Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
if (Dep.target) {
dep.depend();
}
return val;
},
set: function reactiveSetter(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify();
}
});
}

2. 虚拟DOM

Vue2使用虚拟DOM来提高渲染性能,通过比较新旧虚拟DOM的差异,最小化DOM操作。

2.1 VNode类

VNode是虚拟DOM的基本单位:

class VNode {
constructor(tag, data, children, text, elm) {
this.tag = tag;
this.data = data;
this.children = children;
this.text = text;
this.elm = elm;
}
}

2.2 patch函数

patch函数负责比较新旧VNode并更新DOM:

function patch(oldVnode, vnode) {
if (sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode);
} else {
const oldElm = oldVnode.elm;
const parentElm = oldElm.parentNode;
createElm(vnode);
parentElm.insertBefore(vnode.elm, oldElm);
parentElm.removeChild(oldElm);
}
return vnode.elm;
}

3. 模板编译

Vue2的模板编译过程分为三个主要步骤:解析、优化和生成。

3.1 解析器

解析器将模板字符串转换为AST(抽象语法树):

function parse(template) {
let root;
let currentParent;
let stack = [];

parseHTML(template, {
start(tag, attrs) {
let element = createASTElement(tag, attrs);
if (!root) {
root = element;
}
currentParent = element;
stack.push(element);
},
end() {
const element = stack.pop();
currentParent = stack[stack.length - 1];
if (currentParent) {
element.parent = currentParent;
currentParent.children.push(element);
}
},
chars(text) {
if (text.trim()) {
currentParent.children.push({
type: 3,
text
});
}
}
});

return root;
}

3.2 优化器

优化器标记静态节点,提高渲染性能:

function optimize(root) {
if (!root) return;
markStatic(root);
markStaticRoots(root);
}

function markStatic(node) {
node.static = isStatic(node);
if (node.type === 1) {
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
markStatic(child);
if (!child.static) {
node.static = false;
}
}
}
}

3.3 代码生成器

代码生成器将AST转换为渲染函数:

function generate(ast) {
const code = ast ? genElement(ast) : '_c("div")';
return {
render: `with(this){return ${code}}`,
staticRenderFns: state.staticRenderFns
};
}

function genElement(el) {
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el);
} else if (el.once && !el.onceProcessed) {
return genOnce(el);
} else if (el.for && !el.forProcessed) {
return genFor(el);
} else if (el.if && !el.ifProcessed) {
return genIf(el);
} else if (el.tag === 'template' && !el.slotTarget) {
return genChildren(el) || 'void 0';
} else if (el.tag === 'slot') {
return genSlot(el);
} else {
return genData(el);
}
}

4. 总结

Vue2的源码设计体现了以下几个核心思想:

  1. 响应式系统:通过Object.defineProperty实现数据劫持,结合发布订阅模式实现数据响应式更新。

  2. 虚拟DOM:通过虚拟DOM提高渲染性能,最小化DOM操作。

  3. 模板编译:将模板编译为渲染函数,提高运行时的性能。

  4. 组件系统:基于虚拟DOM和响应式系统,实现了组件化开发。

通过深入理解Vue2的源码实现,我们可以更好地使用Vue框架,也能在遇到问题时快速定位和解决问题。