You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fast 3kb React alternative with the same ES6 API. Components & Virtual DOM.
我们用Preact编写代码就雷同于React,比如举个例子:
import{Component,h}from'preact'exportdefaultclassTodoListextendsComponent{state={todos: [],text: ''};setText=e=>{this.setState({text: e.target.value});};addTodo=()=>{let{ todos, text }=this.state;todos=todos.concat({ text });this.setState({ todos,text: ''});};render({},{ todos, text }){return(<formonSubmit={this.addTodo}action="javascript:"><inputvalue={text}onInput={this.setText}/><buttontype="submit">Add</button><ul>{todos.map(todo=>(<li>{todo.text}</li>))}</ul></form>);}}
首先欢迎大家关注我的掘金账号和Github博客,也算是对我的一点鼓励,毕竟写东西没法获得变现,能坚持下去也是靠的是自己的热情和大家的鼓励。
之前分享过几篇关于React的文章:
其实我在阅读React源码的时候,真的非常痛苦。React的代码及其复杂、庞大,阅读起来挑战非常大,但是这却又挡不住我们的React的原理的好奇。前段时间有人就安利过Preact,千行代码就基本实现了React的绝大部分功能,相比于React动辄几万行的代码,Preact显得别样的简洁,这也就为了我们学习React开辟了另一条路。本系列文章将重点分析类似于React的这类框架是如何实现的,欢迎大家关注和讨论。如有不准确的地方,欢迎大家指正。
关于Preact,官网是这么介绍的:
我们用Preact编写代码就雷同于React,比如举个例子:
上面就是用Preact编写TodoList的例子,掌握React的你是不是感觉再熟悉不过了,上面的例子和React不太相同的地方是
render
函数有参数传入,分别是render(props,state,context)
,其目的是为了你解构赋值方便,当然你仍然可以render
函数中通过this
来引用props
、state
和context
。语法方面我们不再多做赘述,现在正式开始我们的内容。本人还是非常推崇React这一套机制的,React这套机制提我们完成了数据和视图的绑定,使得开发人员只需要关注数据和数据流的改变,从而极大的降低的开发的关注度,使得我们能够集中精力于数据本身。而且React引入了虚拟DOM(virtual-dom)的机制,从而提升渲染性能。在开始接触React时,觉得虚拟DOM机制十分的高大上,但经过一段时间的学习,开始对虚拟DOM有了进一步的认识。虚拟DOM从本质上将就是将复杂的DOM转化成轻量级的JavaScript对象,不同的渲染中会生成同的虚拟DOM对象,然后通过高效优化过的Diff算法,比较前后的虚拟DOM对象,以最小的变化去更新真实DOM。
正如上面的图,其实类React的框架的代码都基本可以分为两部分,组件到虚拟DOM的转化、以及虚拟DOM到真实DOM的映射。当然细节性的东西还有非常多,比如生命周期、事件机制(代理)、批量刷新等等。其实Preact精简了React中的很多部分,比如React中采用的是事件代理机制,Preact就没这么做。这篇文章将着重于叙述Preact的JSX与组件相关的部分代码。
最开始学习React的时候,以为JSX是React的所独有的,现在其实明白了JSX语法并不是某个库所独有的,而是一种JavaScript函数调用的语法糖。我们举个例子,假如有下面的代码:
请问可以执行吗?事实上是不能只能的,浏览器会告诉你:
如果你不了解JSX你就会感觉奇怪,因为没有地方显式地调用React,但是事实上上面的代码确实用到了React模块,奥秘就在于JSX。JSX其实相当于JavaScript + HTML(也被称为hyperscript,即hyper + script,hyper是HyperText超文本的简写,而script是JavaScript的简写)。JSX并不属于新的语法,其目的也只是为了在JavaScript脚本中更方便的构建UI视图,相比于其他的模板语言更加的易于上手,提升开发效率。上面的实例如果经过Babel转化其实会得到下面结果:
我们可以看到,之前的JSX语法都被转换成函数
React.createElement
的调用方式。这就是为什么在React中有JSX的地方都需要显式地引入React的原因,也是为什么说JSX只是JavaScript的语法糖。但是按照上面的说法,所有的JSX语法都会被转化成React.createElement
,那岂不是JSX只是React所独有的?当然不是,比如下面代码:我们通过为JSX添加注释
@jsx
(这也被成为Pragma,即编译注释),可以使得Babel在转化JSX代码时,将其装换成函数h
的调用,转化结果成为:当然在每个JSX上都设置Pragma是没有必要的,我们可以在工程全局进行配置,比如我们可以在Babel6中的
.babelrc
文件中设置:这样工程中所有用到JSX的地方都是被Babel转化成使用
h
函数的调用。说了这么多,我们开始了解一下Preact是怎么构造
h
函数的(关于为什么Preact将其称为h
函数,是因为作为hyperscript
的缩写去命名的),Preact对外提供两个接口:h
与createElement
,都是指向函数h
:函数
h
接受两个参数节点名nodeName
,与属性attributes
。然后将除了前两个之外的参数都压如栈stack。这种写法挺令人吐槽的,写成h(nodeName, attributes, ...children)
不是一目了然吗?因为h
的参数是不限的,从第三个参数起的所有参数都是节点的子元素,所以栈存储的是当前元素的子元素。然后会再排除一下第二个参数(其实就是props
)中是否含有children
属性,有的话也将其压如栈中,并且从attributes
中删除。然后循环遍历栈中的每一个子元素:pop
去判别是否是一个数组,如果子元素是一个数组,就将其全部压入栈中。为什么这么做呢?因为子元素有可能是数组,比如:因为子元素是不支持布尔类型的,因此将其置为:
null
。 如果传入的节点不是函数的话,分别判断如果是null
,则置为空字符,如果是数字的话,将其转化成字符串类型。变量simple
用来记录节点是否是简单类型,比如dom
名称或者函数就不属于,如果是字符串或者是数字,就会被认为是简单类型然后代码
其实做的就是一个字符串拼接,lastSimple是用来记录上次的节点是否是简单类型。之所以这么做,是因为某些编译器会将下面代码
转化为:
这是时候
h
函数就会将后两个参数拼接成一个字符串。children
中,现在传入children
中的节点有三种类型: 纯字符串、代表dom
节点的字符串以及代表组件的函数(或者是类)函数结束循环遍历之后,创建了一个
VNODE
,并将nodeName
、children
、attributes
、key
都赋值到节点中。需要注意的是,VNODE
只是一个普通的构造函数:说了这么多,我们看几个转化之后的例子:
上面JSX元素转化成的JavaScript对象就是DOM在内存中的表现。在Preact中不同的数据会生成不同的虚拟DOM节点,通过比较前后的虚拟DOM节点,Preact会找出一种最简单的方式去更新真实DOM,以使其匹配当前的虚拟DOM节点,当然这会在后面的系列文章讲到,我们会将源码和概念分割成一块块内容,方便大家理解,这篇文章着重讲述了Preact的元素创建与JSX,之后的文章会继续围绕Preact类似于diff、组件设计等概念展开,欢迎大家关注我的账号获得最新的文章动态。
The text was updated successfully, but these errors were encountered: