最近开始在学习React,在此之前只对vue有一定研究,虽然说框架的思路是相近的,但是实际上还是有一些差异的,所以在这记录一下。
既然要学习React,那么首先是打开它的官网看文档咯。打开React的文档之后,首先看到的是熟悉jsx的写法。想之前刚刚工作第一次接触vue的时候,看到jsx的时候就封蒙——为啥这样的东西还能在浏览器中执行?vue虽然平时都是通过模板,但是同样可以使用render
函数,实际上在vue的项目中会把模板语法编译成render
函数,并且在render
中通过Babel
插件可以实现jsx的写法。这个jsx实际上并不是js的东西,只不过会在打包过程中被相关插件(目前普遍用的Babel
)转换成浏览器可以自行的代码,类似于createElement这种方式。而在浏览器中之所以能调试并且看到jsx的写法运行,那也是在source map
的帮助下找到了执行语句被编译之前的样子。以前无论是js
还是css
等文件,在网络传输的时候,都可以删掉空格和换行符来减少体积,到后来甚至直接将长长的变量名字替换成只有一个字符的名字。但是这样一来,这份js代码虽然执行起来没有任何区别,甚至可能有更高的执行效率,但是对人来说已经完全不具备可读性了,这就如同被编译成二进制的C语言程序一样。庆幸的是出现了source map
,通过它可以将这份被压缩的代码与原始的代码联系起来,这样就可以调试了。虽然不是所有浏览器都支持,但是万幸的是,这个标准是不影响在浏览器中执行的情况的,仅仅是在调试的时候才会需要这个东西,并且如今哪个开发人员会没有谷歌浏览器呢?随着前端技术的迅速发展,打包技术的出现,这一切几乎是翻天覆地的变化。这甚至意味着,可以甚至可以通过c的语法写一段代码,并将其打包成一段实现相同功能的js代码,然后浏览器执行的还是js代码,但是通过source map
可以看到c的那段程序在执行,对付一个小小jsx当然也不是什么难事。
接下来的部分似乎没有啥问题,一直往下走,组件这里,可以看到React的组件可以是函数声明,也可以是类声明。函数声明的方式非常简洁,虽然vue没有,但是这并不难理解。类的声明方式是ES6语法,并且类从React.Component
继承,使用构造函数的时候,记得调用父类构造函数。相比之下,vue的组件是一个对象,里面分类存储着参数信息(props
)、数据(data
)、方法(methods
)和生命周期钩子等,当生成组件的时候,vue会对应处理各个部分,比如根据参数信息读取参数或设置默认值、方法使用bind(this)
进行绑定,并添加到this
上,用的时候都是通过this访问。而在React中参数会被作为类的参数传入,由React.Component
的构造函数处理到this.props
上,而自身状态可以根据实际情况放在this
或者this.state
上。但是vue中更新自身状态直接设置就好了,而React则需要调用setState
才能追踪数据变化。原因在于vue使用了Object.defineProperty
做数据劫持(vue3中将会使用反射,这更方便,不过其实我有点担心反射的性能),给data中字段的数据设置值的时候,会调用对应的set方法实现检测,同时,也正是如此,vue可以做到更加精细的更新。
接下来就是声明周期了,vue的生命周期钩子特别多,创建时会依次调用beforeCreate
、created
、beforeMount
、mounted
四个钩子,分别在组件创建和挂载前后触发。销毁时则有beforeDestroy
和destroyed
的两个钩子,更新时beforeUpdate
和updated
两个钩子,这些都是在几个关键的事件前后的钩子。此外,还有几个其他类型的钩子。而在React中,构造函数本身是一个很好的声明周期钩子,他对应着vue的beforeCreate
和created
。一般在组件挂在前的事情可以在组件创建做完,所以React中只有componentDidMount
与vue的mounted
对应(实际上是存在componentWillMount
的,但是大概是没啥用所以准备取消了)。同样,销毁完的组件也没有任何可以访问的数据,所以React也只提供了componentWillUnmount
与vue的beforeDestroy
对应。在组件更新时,钩子有shouldComponentUpdate
和componentDidUpdate
,其中shouldComponentUpdate
主要用于判断组件是否需要更新(虽然也有componentWillUpdate
,但是同样即将过时。。。)。
然后再说说插槽,React支持插槽,但是在使用的时候还是有些区别的:不像vue插槽有专门的$solt
,React中的插槽会被作为参数children
传入,使用时使用this.props.children
访问。匿名插槽在传入子组件的时候和vue一样处理,对于需要使用具名插槽的时候,则需要以js对象的格式写在子元素内部,同时用对象的方式访问this.props.children
。
ref引用在React中也有很大区别。首先,ref引用需要React.createRef()
来创建,同时,他是给原生dom组件用的。如果要访问子组件,React通常使用参数传入一个方法,然后子组件通过这个方法传入自身或相关方法给父组件保存后才可使用了。
好了,文档里面看到需要注意的基本都看了,接下来就是看实际项目了,不难发现ui用的是antd,然后查下它的文档,当然这些ui库都是差不多的,没有特别需要留意的地方,不过表格跟之前用的库还是有些区别,但是也不难。
然后看到了另一个东西——mobx,这个初一看是个状态管理,那就类似vuex。当然它确实是个状态管理,但是由于React本身是没有做数据劫持,mobx却是可以实现这一点,所以,在这里面可以实现诸如计算属性之类的操作。当然,,mobx通常配合装饰器语法使用,这又是个新概念,但是这显然是在java中的注解语法中获得了灵感(之前碰到的箭头函数,虽然有点像PHP中数组键值对的写法,但是显然灵感来自lambda表达式,格式和C#的lambda表达式完全一致,且java中也引入了非常相似的lambda表达式语法)。当然,有看到mobx做了一些顺序上的优化和其他处理,使得mobx性能更好,但是可能会带来一些问题,目前暂时没看明白具体有啥影响,但是碰到问题的话,这可能会是一个排查的点。
除了mobx,React还有个状态管理叫做Redux,因为暂时用不上没细看是否是有做数据劫持,不过这个状态管理的理念上,存储的状态是个快照,所以不应该直接改变状态,如果需要改变状态,就需要action
。这类似于vuex的Mutation
操作state
,所有操作全部对应一个方法,通过参数进行区分。当然这是一个大致的使用,实际使用是可能会发现更多问题并需要注意更多细节。
参考链接
- React文档:https://zh-hans.reactjs.org/docs/
- 具名插槽(React):https://blog.csdn.net/u013611033/article/details/105566201