和上次说的同样此次带来preact
的解读javascript
preact实际上把它看成是一个精简版react
就行了。html
此次我抄下了preact
,而且改写了代码, 命名为zreact
vue
把以前将事件,props之类的单独放出来,这样这份zreact
。java
能够支持ie8,虽然并无什么用。node
此次代码解读顺序按使用preact的代码顺序。react
这里是第一篇,createElement,也就是vue,react的render所返回的VNode对象。webpack
日常则是使用babel+jsx来生成createElement调用。git
vue经常使用则是template,可是经过webpack会作到预先转换为render。github
对于preact来讲,最多见的就是jsx。
下面是一个最简单的使用preact。web
import {h, render, Component} from "preact"; /** @jsx h */ // 经过上面的注释告诉babel使用什么方法做为VNode的建立函数。 // 若是不使用这个默认会是React.createElement, // 或者经过下面的babel配置来修改 class App extends Component { render(props) { return <h1>App</h1>; } } var test = ""; render( <div className="test"> <span style={test}>测试</span> <App></App> </div> , document.body)
.babelrc
{ "presets": ["es2015"], "plugins": [ ["transform-react-jsx", { "pragma":"h" }] ] }
经过babel转换后会变成
import {h, render} from "preact"; class App extends Component { render() { return h("h1", null, "App"); } } var test = ""; render( h( "div", { className: "test" }, h("span", { style: test }, "测试"), h(App) ), document.body )
因此对于preact最早执行的东西是这个h
函数也就是createElement
对于jsx
标准的createElement
函数签名为
interface IKeyValue { [name: string]: any; } /** * 标准JSX转换函数 * @param {string|Component} nodeName 组件 * @param {IKeyValue} attributes 组件属性 * @param {any[]} childs 这个VNode的子组件 */ function h( nodeName: string | function, attributes: IKeyValue, ...childs: any[] ): VNode; class VNode { public nodeName: string| Component; public children: any[]; public attributes: IKeyValue | undefined; public key: any | undefined; }
因此这里的标准jsx
很是简单。
第一个参数为原生html组件或者Component
类。
第二个参数为该组件的属性,及自定义属性。
第三个参数及后面的全部都是这个组件的子组件。
其中第三个及后面的参数为数组就会被分解放入子组件中。
最后返回一个VNode
实例。
function h(nodeName: string | Component, attributes: IKeyValue, ...args: any[]) { // 初始化子元素列表 const stack: any[] = []; const children: any[] = []; // let i: number; // let child: any; // 是否为原生组件 let simple: boolean; // 上一个子元素是否为原生组件 let lastSimple: boolean = false; // 把剩余的函数参数所有倒序放入stack for (let i = args.length; i--; ) { stack.push(args[i]); } // 把元素上属性的children放入栈 if (attributes && attributes.children != null) { if (!stack.length) { stack.push(attributes.children); } // 删除 delete attributes.children; } // 把stack一次一次取出 while (stack.length) { // 取出最后一个 let child: any = stack.pop(); if (child && child.pop !== undefined) { // 若是是个数组就倒序放入stack for (let i = child.length; i-- ; ) { stack.push(child[i]); } } else { // 清空布尔 if (typeof child === "boolean") { child = null; } // 判断当前组件是否为自定义组件 simple = typeof nodeName !== "function"; if (simple) { // 原生组件的子元素处理 if (child == null) { // null to "" child = ""; } else if (typeof child === "number") { // num to string child = String(child); } else if (typeof child !== "string") { // 不是 null,number,string 的不作处理 // 而且设置标记不是一个字符串 simple = false; } } if (simple && lastSimple) { // 当前为原生组件且子元素为字符串,而且上一个也是。 // 就把当前子元素加到上一次的后面。 children[children.length - 1] += child; } else { // 其它状况直接加入children children.push(child); } /* else if (children === EMPTY_CHILDREN) { children = [child]; } */ // 记录此次的子元素状态 lastSimple = simple; } } const p = new VNode(); // 设置原生组件名字或自定义组件class(function) p.nodeName = nodeName; // 设置子元素 p.children = children; // 设置属性 p.attributes = attributes == null ? undefined : attributes; // 设置key p.key = attributes == null ? undefined : attributes.key; // vnode 钩子 if (options.vnode !== undefined) { options.vnode(p); } return p; }
这个标准jsx的VNode
生成函数很简单,这边要注意的是子组件是连续的字符串。
会被合并成一个,这样能够防止在生成dom时,建立多余的Text
。
import { h } from "./h"; import { VNode } from "./vnode"; import { extend } from "./util"; /** * 经过VNode对象新建一个自定义的props,children的VNode对象 * @param vnode 旧vnode * @param props 新的props * @param children 新的子组件 */ export function cloneElement(vnode: VNode, props: any, ...children: any[]) { const child: any = children.length > 0 ? children : vnode.children; return h( vnode.nodeName, extend({}, vnode.attributes, props), child, ); }
clone-element依赖于createElement
此次的blog感受好短,我已经没有东西写了。
话说回来vue的template,如今看来不如说是一个变异的jsx语法。
感受明明是在读preact源码却对vue的实现更加的理解了。
下一篇应该是Component
了。