Open hujiulong opened 6 years ago
@zhenghan2017 JSX就是语法糖,就是用更方便的方法调用createElement
感谢您的回答
明白了,受教了,谢谢
深入浅出,梳理的很不错哦
render函数最后的return container.appendChild( dom )以及ReactDom.render最后的return不是必须的吧,或者这样return有什么好处呢?
render函数最后的return container.appendChild( dom )以及ReactDom.render最后的return不是必须的吧,或者这样return有什么好处呢?
render方法被用来递归,需要返回子节点的内容呀
👍 博主写的太赞了!
这里有个问题问一下
const ReactDOM = {
render: ( vnode, container ) => {
container.innerHTML = '';
return render( vnode, container );
}
}
上面代码的意思是,每次渲染都把 container.innerHTML
值为空字符串,也就是每次都是全量重新渲染,这样是不是有性能问题?而且之前页面某个元素如果有焦点,也会发生失焦的情况
render函数最后的return container.appendChild( dom )以及ReactDom.render最后的return不是必须的吧,或者这样return有什么好处呢?
render方法被用来递归,需要返回子节点的内容呀
render方法没有需要获取子节点的内容,return container.appendChild( dom ) 返回的子节点内容并没有使用啊
讲的很清楚,谢谢UP主。
很有趣
真的很强!
文章写的真不错🍬
后面会出hooks嘛
推荐个 react16源码(Fiber架构) https://www.cnblogs.com/colorful-coco/p/9579402.html
@hufan-akari 看得很仔细啊,这个地方确实有点问题,但是和你说的有点区别
className
是有必要改回class
的,大多数情况下dom.className = value
和dom.setAttribute( 'class', value )
效果是一样的。 但是svg元素比较特殊,svg元素的className
是一个SVGAnimatedString
对象,也就是说给svg元素设置class时要用setAttribute
。有问题的地方在于,我就算改成class了,也会执行
dom[name] = value
,所以这段代码应该这样改- if ( name in dom ) { + if ( name !== 'class' && name in dom ) { dom[ name ] = value || ''; }
其实是一个小问题啦,这个实现我也其实也没太多考虑svg
为什么不直接全部用setAttribute来做?
- if ( name in dom ) {
- dom[ name ] = value || '';
- }
if ( value ) {
dom.setAttribute( name, value );
} else {
dom.removeAttribute( name );
}
卧槽,你后面竟然用了个setinterval,差点看漏了,我说怎么会一直变化,并没有相关代码
It is {new Date().toLocaleTimeString()}.
没看明白这里是咋解析的
抱歉,刚才没理解你的意思。
const element = ( <div> hello{new Date().toLocaleDateString()}</div> );
上面这段代码经过编译后得到的是
var element = _react.default.createElement("div", null, " hello", new Date().toLocaleDateString());
但如果改成{new Date()}
,文章中的代码就无法运行了,所以这里还需要修改对vnode类型的判断逻辑。
赞
if (name in dom) {
dom[name] = value || "";
}
if (value) {
dom.setAttribute(name, value);
} else {
dom.removeAttribute(name, value);
}
关于上述代码,我也查找了相关资料,也做了一些测试,发现去除 name in dom
这一判断,对代码逻辑没有特殊的影响。
if (value) {
dom.setAttribute(name, value);
} else {
dom.removeAttribute(name, value);
}
我现在挺困惑,还请楼主可以解释下,为什么要加这个判断?我看了 react-dom
内部的处理,仅针对 checked
、multiple
,muted
,selected
这四个,会使用 property
方式。
createElement那里的children处理不对吧,如果使用了{this.state.list.map(i => node)}这样的形式就渲染出问题了,应该改成
function createElement(tag, attrs, ...children) { attrs = attrs || {} children = children.flat(1) return new Element(tag, attrs, children, attrs.key) }
才对吧
jsx中引入React是为了调用React.createElement将jsx转换为虚拟dom
您好,您的邮件我已收到。
前言
React是前端最受欢迎的框架之一,解读其源码的文章非常多,但是我想从另一个角度去解读React:从零开始实现一个React,从API层面实现React的大部分功能,在这个过程中去探索为什么有虚拟DOM、diff、为什么setState这样设计等问题。
提起React,总是免不了和Vue做一番对比
Vue的API设计非常简洁,但是其实现方式却让人感觉是“魔法”,开发者虽然能马上上手,但其原理却很难说清楚。
相比之下React的设计哲学非常简单,虽然有很多需要自己处理的细节问题,但它没有引入任何新的概念,相对更加的干净和简单。
关于jsx
在开始之前,我们有必要搞清楚一些概念。
我们来看一下这样一段代码:
这段代码并不是合法的js代码,它是一种被称为jsx的语法扩展,通过它我们就可以很方便的在js代码中书写html片段。
本质上,jsx是语法糖,上面这段代码会被babel转换成如下代码
你可以在babel官网提供的在线转译测试jsx转换后的代码,这里有一个稍微复杂一点的例子
准备工作
为了集中精力编写逻辑,在代码打包工具上选择了最近火热的零配置打包工具parcel,需要先安装parcel:
接下来新建
index.js
和index.html
,在index.html
中引入index.js
。当然,有一个更简单的方法,你可以直接下载这个仓库的代码:
注意一下babel的配置 .babelrc
这个
transform-react-jsx
就是将jsx转换成js的babel插件,它有一个pragma
项,可以定义jsx转换方法的名称,你也可以将它改成h
(这是很多类React框架使用的名称)或别的。准备工作完成后,我们可以用命令
parcel index.html
将它跑起来了,当然,现在它还什么都没有。React.createElement和虚拟DOM
前文提到,jsx片段会被转译成用
React.createElement
方法包裹的代码。所以第一步,我们来实现这个React.createElement
方法从jsx转译结果来看,createElement方法的参数是这样:
第一个参数是DOM节点的标签名,它的值可能是
div
,h1
,span
等等 第二个参数是一个对象,里面包含了所有的属性,可能包含了className
,id
等等 从第三个参数开始,就是它的子节点我们对createElement的实现非常简单,只需要返回一个对象来保存它的信息就行了。
函数的参数
...children
使用了ES6的rest参数,它的作用是将后面child1,child2等参数合并成一个数组children。现在我们来试试调用它
打开调试工具,我们可以看到输出的对象和我们预想的一致
我们的createElement方法返回的对象记录了这个DOM节点所有的信息,换言之,通过它我们就可以生成真正的DOM,这个记录信息的对象我们称之为虚拟DOM。
ReactDOM.render
接下来是ReactDOM.render方法,我们再来看这段代码
经过转换,这段代码变成了这样
所以
render
的第一个参数实际上接受的是createElement返回的对象,也就是虚拟DOM 而第二个参数则是挂载的目标DOM总而言之,render方法的作用就是将虚拟DOM渲染成真实的DOM,下面是它的实现:
设置属性需要考虑一些特殊情况,我们单独将其拿出来作为一个方法setAttribute
这里其实还有个小问题:当多次调用
render
函数时,不会清除原来的内容。所以我们将其附加到ReactDOM对象上时,先清除一下挂载目标DOM的内容:渲染和更新
到这里我们已经实现了React最为基础的功能,可以用它来做一些事了。
我们先在index.html中添加一个根节点
我们先来试试官方文档中的Hello,World
可以看到结果:
试试渲染一段动态的代码,这个例子也来自官方文档
可以看到结果:
后话
这篇文章中,我们实现了React非常基础的功能,也了解了jsx和虚拟DOM,下一篇文章我们将实现非常重要的组件功能。
最后留下一个小问题 在定义React组件或者书写React相关代码,不管代码中有没有用到React这个对象,我们都必须将其import进来,这是为什么?
例如:
不知道答案的同学再仔细看看这篇文章哦
从零开始实现React系列
React是前端最受欢迎的框架之一,解读其源码的文章非常多,但是我想从另一个角度去解读React:从零开始实现一个React,从API层面实现React的大部分功能,在这个过程中去探索为什么有虚拟DOM、diff、为什么setState这样设计等问题。
整个系列大概会有四篇左右,我每周会更新一到两篇,我会第一时间在github上更新,有问题需要探讨也请在github上回复我~
下一篇文章
从零开始实现React(二):组件和生命周期