借助webpack,我们实现了ToyReact,一个塑料react🐶
1. JSX 语法解析
JSX 是 JavaScript 与 XML 相结合的一种格式。React发明了JSX,实现了利用HTML 语法来创建虚拟DOM。遇到<时,JSX将起作为HTML解析,遇到 {JSX将其作为JavaScript解析。
JSX语法并不是直接把JSX渲染到页面,而是在React内部先将JSX转换成createElement形式,再去渲染的,同时JSX在编译成JavaScript代码的时候进行了一定的优化,保证了React的更高执行效率。
在ToyReact中,我们通过设置webpack的 ** @babel/plugin-transform-react-jsx** 插件,重新定义了jsx生成dom的函数(并没有实现jsx语法本身)
↓↓↓ webpack 配置如下 ↓↓↓
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
// 配置babel-loader
options: {
presets: ['@babel/preset-env'],
// 这样打包后的‘React.createElement就会变成‘createElement’
// 我们定义的 createElement 函数将会覆盖 React.createElement 方法
plugins: [['@babel/plugin-transform-react-jsx', {pragma: 'createElement'}]]
}
}
}
]
}
}
2. ToyReact生命周期
在Component class里有 RENDER_TO_DOM 和 update() 两个函数,对应React生命周期,就是在挂载前需要的操作和更新时需要的操作。
在挂载之前:通过setAttribute添加自定义的属性,addEventListener添加事件;然后就会执行一次render
如果有更新操作,就会在update()内会通过对比对更新的元素进行替换;再次render
React生命周期函数
-
组件将要挂载时触发的函数:componentWillMount -
组件挂载完成时触发的函数:componentDidMount
-
是否要更新数据时触发的函数:shouldComponentUpdate
-
将要更新数据时触发的函数:componentWillUpdate -
数据更新完成时触发的函数:componentDidUpdate
-
组件将要销毁时触发的函数:componentWillUnmount
-
父组件中改变了props传值时触发的函数:
componentWillReceiveProps
React16废弃生命周期
React16废弃了三个生命周期:componentWillMount、componentWillReceiveProps、componentWillUpdate
废弃原因:在React16的Fiber架构中,调和过程会多次执行will周期,不再是一次执行,所以这些周期失去了原有的意义。此外,由于周期会多次执行, 在周期中设置setState或dom操作,会触发多次重绘,影响性能,也会导致数据错乱
componentWillMount和componentWillUpdate在每一个组件render之前都会去调用componentWillMount(),可以在服务端调用也可以在浏览器端调用,如果有异步请求,不推荐在此时请求数据,因为在render前并不会返回数据。
componentWillUpdate()在组件将要更新数据的时候都会触发一次,执行更新操作。
React16新增2个生命周期:
-
getDerivedStateFromProps:16.3是在props变化时触发,16.4则改为在每次组件渲染器调用
-
getSnapshotBeforeUpdate:在render之后,更新dom之前,state已更新
getDerivedStateFromProps周期有些难用:
- 触发时机频繁,16.3是在props变化时触发,16.4则改为在每次组件渲染器调用, 即无论props变化,组件自己setState,父组件render 都会触发
- 静态方法,本意是隔离访问组件实例,却会造成访问组件的数据和方法十分不便,难以进行数据比较
- 不能setState,而是返回一个对象来更新state,使用不便,也可能触发多次更新状态
getSnapshotBeforeUpdate周期在Fiber架构中,只会调用一次,实现了类似willMount的效果。可以用来读取dom,强制用户只能在mount阶段读取dom。
3. ToyReact虚拟dom
React将DOM抽象为虚拟DOM,用JavaScript模拟一颗DOM树,放在浏览器内存中。当变更时,虚拟DOM使用DIFF算法进行新旧虚拟DOM的比较,将变更放到变更队列中,最终只把变化的部分重新渲染,从而提高渲染效率。
在需要频繁微改动DOM时,直接修改DOM会引起页面的多次渲染,影响性能。而使用虚拟DOM的时候,先对比差异,再修改JS对象(生成虚拟DOM),最后把生成的DOM结构插入到页面中,从而减少渲染次数,提升整个页面的渲染效率。
Range
ToyReact的虚拟DOM实现,借助了Range对象
Range对象表示文档的连续范围区域,相当于高亮选区。一个Range的开始点和结束点可以是任意的,开始点和结束点也可以是一样的(空Range)
Range的应用常见于富文本编辑器的相关操作
使用Range时,首先会创建一个range对象(createRange),将指定节点的终点位置指定为Range对象所代表区域的起点位置(setStartAfter);紧接着将指定的节点插入到某个Range对象所代表的区域中,插入位置为Range对象所代表区域的起点位置,如果该节点已经存在于页面中,该节点将被移动到Range对象代表的区域的起点处(insertNode)。
Range对象API官网:
developer.mozilla.org/en-US/docs/…
Range的使用博客: