src/component.js
component.js 文件中定义了 Preact 中 Component 类。因为我们还没有涉及到 Diff 算法, 所以目前在这里只会介绍文件中的一部分代码, 其余的部分在接下来文章中会得到补充。
constructor
Component 类的定义, Component 类中定义了两个参数, props 以及 context。props 为初始化组件的参数, context 为父组件的上下文。
export function Component(props, context) {
this.props = props
this.context = context
}
setState
setState 方法中会初始化名为 ”_nextState” 的属性。_nextState 属性确保了每一次的 setState 都将在 setState 内部生成一个浅拷贝的对象。
Component.prototype.setState = function(update, callback) {
// 在第一次初始化组件的时, _nextState 为 undefined, 所以将执行或语句的后半句
// this._nextState = assign({}, this.state), this._nextState 将会被初始化
let s = (this._nextState!==this.state && this._nextState) || (this._nextState = assign({}, this.state));
// 在这里同 React 一样 setState 将会接受两种类型的参数, Object 和 Function 类型
// 如果是 Object 类型, 将会执行一层浅拷贝
// 如果是 Function 类型, 则会把现在 state 和 props 传入函数, 返回的对象将作为执行一层浅拷贝
if (typeof update!==’function’ || (update = update(s, this.props))) {
assign(s, update);
}
if (update==null) return;
// 如果有 setState 存在第二个参数, 将会把 callback 保存到_renderCallbacks 的数组中
if (callback) this._renderCallbacks.push(callback);
// 将组件传入处理组件重新渲染 (render) 的队列中处理
enqueueRender(this);
};
q
q 定义了一个渲染 (render) 的队列
let q = []
defer
defer 将会返回一个 Promise.resolve()的回调。
const defer = typeof Promise==’function’ ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout;
let foo = function () {
return ‘Hello World’
}
// Promise {<resolved>: “Hello World”}
defer(foo)
enqueueRender
enqueueRender 函数会将需要 render 的组件 push 到 q 队列中。并根据条件判断是否需要渲染组件。
为什么说 setState 是一个异步的过程, 可以从这里得到答案, defer 将会返回一个 Promise.resolve()对象。
export function enqueueRender(c) {
// 只有当_dirty 为 false 时, 并且队列的长度为 1 的情况下才会处理队列
if (!c._dirty && (c._dirty = true) && q.push(c) === 1) {
(options.debounceRendering || defer)(process);
}
}
process
process 函数中将会遍历 q 队列, 调用 forceUpdate 方法重新渲染组件。
function process() {
let p;
// 迭代将会在 q 队列被清空时结束
while ((p=q.pop())) {
// 只有当_dirty 为 true 时才会更新组件, forceUpdate 方法因为涉及到 diff 算法的内容这里暂不涉及
if (p._dirty) p.forceUpdate(false)
}
}
render
render 函数接受 props 和 state 返回需要更新的 Dom 树。
render 函数默认 Fragment。Fragment 在 create-element.js 中定义为空函数, Fragment 可以用来包裹一组节点。具体作用可以参考 React 中 Fragment。
Component.prototype.render = Fragment;
结语
目前我们简略的概览 component 文件, 从我们目前已知的内容, 可以总结出以下的内容。
forceUpdate 会在以后的文章中介绍。