从零实现一个 Mini Vue
前置
前两篇已经实现的功能:
h
创建 VNodemount
挂载 VNodeunmount
移除 VNodepatch
替换 VNodeobserve
将一个对象变成响应式
接下来就是将这些组合在一起。
使用场景
先来看看需求好了,我们的目标是实现一个计数器。
HTML 是这样子的:
<div id="counter"></div>
<button id="inc">inc</button>
#counter
显示当前数字。#inc
按钮被点击时当前数字要加一。
响应式状态
首先我们需要一个状态来存储当前数字。
const counter = {
count: 1,
};
observe(counter);
#inc
按钮被点击时 count++
,这没什么好说的。
const incBtn = $('#inc');
incBtn.addEventListener('click', () => {
counter.count++;
});
页面更新
重要的是,counter.count
更新的时候,页面上的数字也要更新。
首先,就像在 Vue 中的一样,我们需要一个组件来显示计数器的数字。
const counterComponent = {
render(state) {
return h('h1', {}, String(state.count));
},
};
跟 Vue 中的 render 方法不同的是,我们这里的 render 方法接收一个响应式对象作为参数。
在 Vue 中,如果你使用的是模板写法,模板最终也是被编译成 render 方法的。
有了组件,接下来就是需要定义状态改变时要做的事情了。
autorun(function () {
// counter.count 改变了
});
这里有两种情况:
初始状态时
状态发生改变时
1. 初始状态时
在初始状态的时候,直接调用组件的 render
方法生成 VNode,然后挂载到 DOM 上就行:
const node = counterComponent.render(counter);
mount(node, counterContainer);
2. 状态发生改变时
状态发生改变时,需要再一次调用组件的 render
方法生成新的 VNode,然后与旧的 VNode 对比,看哪些元素需要更新。
const newNode = counterComponent.render(counter);
patch(oldNode, newNode);
组合起来就是这样:
let oldNode = null;
autorun(function () {
if (oldNode) {
const newNode = counterComponent.render(counter);
patch(oldNode, newNode);
oldNode = newNode;
} else {
oldNode = counterComponent.render(counter);
mount(oldNode, counterContainer);
}
});
完整代码
Last updated
Was this helpful?