试试这么读preact源码(一)- createElement
之前阅读的是
preact
的v8
版本,官方已经升级到v10
的测试版了,接下来我们主要对照两个版本之间的差异来学习源码,这样就能清楚的知道作者为什么要这么改,这么改有哪些好处了!
createElement/h 函数
V10
的 preact
学乖了,总算在源码阶段就更贴近 react
了,看看这样方法:
function createElement(type, props, children) { //...}
哈哈,仔细看 createElement
方法的三个参数,类 react
,就要从变量名开始!
形参的不同
我们来看下上个版本的h方法:
function createElement(nodeName, attributes){ // ...}
新版本中新添加了一个形参 children
(nodeName
,attributes
对应 type
,props
),这个变量的作用就是存储子组件的,先看下新版本对这个形参的解决:
if (arguments.length>3) { children = [children]; for (let i=3; i<arguments.length; i++) { children.push(arguments[i]); }}
传入 createElement
方法的参数数目,假如大于3个,则第三个参数 children
重新定义位为一个数组,原值是这个数组的第一个元素,而后遍历这个方法第3位之后的所有参数,并作为元素压入 children
这个数组中。
上个版本的 createElement
方法也是通过遍历 arguments
来收集子组件的,不过它在函数外定义了一个全局对象和一个初始子组件的空数组:
// 将从第3位起的参数收集起来(其实就是一堆 h 方法的执行函数)const stack = [];// 定义一个初始子组件空数组const EMPTY_CHILDREN = [];function h(nodeName, attributes) { // ... // 遍历h函数的参数,从第三个参数开始就是子组件 for (i = arguments.length; i-- > 2; ) { // 将从第3位起的参数收集起来(其实就是一堆 h方法的执行函数) stack.push(arguments[i]); } // ...}
最终实现的效果是一样的,新版本用一个形参代替了函数外的两个变量,这两个变量尽管不是全局,但对于这个板块来说是共用的,这就引出了一个话题:函数的反作用。
旧版本中,h
函数内部改变了函数体外部的一个变量,在这个函数执行完成之后,这个对象不会被销毁,而且所指向的值也发生了改变,而这种改变就是 h
函数的反作用。
假如这个板块中另一个函数也用到了这个变量,那即可能会造成不可预估的bug,所以,这个反作用是可以避免的。
新版本的 h
方法用一个形参重新赋值为一个数组的操作都是在函数体内部做的,与外部没有任何联络,函数执行完成后,函数体内部的变量都会销毁,这样的优化是非常值得我们学习的。
移除了对子组件的遍历及类型判断
在旧版本中,函数内部会对 stack
这个收集子组件的数组元素做类型判断:
boolean
,将元素重新赋值为null
number
,转化成string
- 值为 null ,则重新赋值为空字符
新版本去掉这样的操作,主要是由于它将子组件的操作放在了 props
中
if (children != null) { props.children = children;}
后面会讲到新版本对 props
专门做了一个板块及方法 diff/props.js
中的 diffProps
函数。
新添加了 createVNode
方法,创立 vnode
对象不在用 new
关键字
旧版本的虚拟 dom
是通过实例话 Vnode
类来实现的
const VNode = function VNode() {};let p = new VNode();p.nodeName = nodeName;p.children = children;p.attributes = attributes == null ? undefined : attributes;p.key = attributes == null ? undefined : attributes.key;
创立一个虚拟 dom
,就要 new
一个对象,学过js的人都听过,new
是很耗性能的,具体可以参考这篇文章:prototype, constructor and new。
新版本中没有在 createElement
这个函数内部直接做创立,而是调用了一个 createVNode
函数:
export function createVNode(type, props, key, ref) { const vnode = { type, props, key, ref, _children: null, _dom: null, _lastDomChild: null, _component: null }; vnode._self = vnode; if (options.vnode) options.vnode(vnode); return vnode;}
这个函数的职责就是创立一个虚拟dom
,首先在函数内部定义了一个字面量 vnode
,它有若干的属性,其中要注意的是以 _
开头的都是系统内部会使用到的,这里只是做了一个初始值:
type
虚拟dom的元素类型,如 div span ,一个文本类型,或者者是一个function
props
通过jsx传入的属性key
唯一键值ref
返回虚拟dom的真是dom_children
子组件集合_dom
真实dom_lastDomChild
子组件中的最后一个dom_component
指向的子组件_self
缓存了虚拟dom本身信息
新版本把创立虚拟 dom
独立成一个小方法供其余板块复用这个逻辑,尤其是在 diff
子组件的时候会递归调用。
createElement
函数讲完了,其实我觉得这个函数可以说是 preact
真正的入口函数,在下一章会讲到 render
的时候就会发现,虚拟 dom
是一切的开始!
原文地址
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 试试这么读preact源码(一)- createElement