使用了一段时间React,从最开始的排斥到习惯,然后再到逐渐喜欢上它,在此做一个总结。我在之前使用过一段时间的Vue,并通过Vue把公司的风控项目重构成了一个前后端分离项目。由于参与另一个项目便开始使用React。说实话,在使用Vue后再使用React,很不习惯,无论是语法上还是设计思想上,Vue和React区别都挺大。Vue支持数据的双向绑定,而React是单向的,它的组件是一个单向数据流,数据改变会导致视图改变,但视图改变不会导致数据改变。因此在做一些输入框,下拉框时不得不写很多 change 事件来主动改变数据,这是我觉得React不好用的地方之一,还有就是JSX语法一开始我也不习惯,因为在Vue中,模板语法就是Html语法,而JSX则不是增加了一些学习成本,好在JSX语法并不复杂。虽然有诸多槽点,不过在使用一段时间React后,我就改变了我的想法,这些一开始我吐槽的槽点,我反倒觉得设计有它的合理性。
由于我本身是一个Java开发,而React中使用了Typescript使得JS支持继承,因此在React的组件中,自定义的组件都需要继承React的Component组件,模型层的数据变量像极了JavaBean中的私有属性,而页面的事件方法可以和Java类中的方法对应上。至于render()
方法,它是组件类里面的一个特殊方法,用于返回模板,render()
是父类 Component 中的方法。因此当你写完一个React组件时,怎么看它都像一个Java类。和Java中的封装继承非常类似。前面说到了数据单向流也好理解了,为了不破坏组件的封装特性,就像Java中的私有变量,对别人不可见一样。所以我用着用着就开始喜欢上React了,因为它组件的设计思想和Java的封装有相通性,对我来说有种熟悉的感觉。
React的起源
学一样新东西就从它的历史开始吧,了解它的背景,才知道来龙去脉。React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。大厂就是很任性啊,不爽就自己搞一套,结果一不小心还成了行业标准了。准确来说,React不是一个完整的MVC框架,而是一个轻量级的视图层库。最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开发模式。React 构建页面 UI 的库。可以简单地理解为,React 将将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,就成了我们的页面,似乎Vue也是这样。
react 的特点
- 虚拟DOM: React是以数据驱动的,每次数据变化React都会扫描整个虚拟DOM树,自动计算与上次虚拟DOM的差异变化,然后针对需要变化的部分进行实际的浏览器DOM更新。
- 组件化: react最核心的思想是将页面中任何一个区域或者元素都可以看做一个组件 component,那么什么是组件呢?组件指的就是同时包含了Html、Css、Js元素的聚合体。使用react开发的核心就是将页面拆分成若干个组件,并且react一个组件中同时耦合了Css、Js。可以理解为封装。这种模式整个颠覆了过去的传统的方式
- 单项数据流:React只有单向数据流动-从父节点传递到子节点,其实reactjs的核心内容就是数据绑定,所谓数据绑定指的是只要将一些服务端的数据和前端页面绑定好,开发者只关注实现业务就行了
- JSX 语法:在React中,我们使用render函数来构建组件的dom结构性能较高,因为省去了查找和编译模板的过程,但是在render中利用createElement创建结构的时候代码可读性较低,较为复杂,此时可以利用jsx语法来在render中创建dom,解决这个问题,但是前提是需要使用工具来编译jsx
React的生态
React不仅仅只是在PC端开发网页,在移动端也有React的身影,React Native可以像开发网页一样开发APP,由于它的跨平台性还挺火的。但当我去扒一扒它的生态圈时,会发现React远比我想象的强大
Web端
: React-dom + React-routerApp
: React-native + React-navigation,让你像开发网页一样,开发App,这对于前端开发来说是好事,意味着一个写网页的开发,只需要很低的学习成本就能开发App了VR
: React-360,它有什么用,React 360是一个创建3D和VR用户交互的框架.构建在React的基础之上,React是一个简化复杂UI创建的库,React 360可以让你用相似的工具和概念在网页上创建沉浸式的360度的内容。多端统一解决方案
: Taro,这又是个啥呢?Taro 遵循 React 语法规范,它采用与 React 一致的组件化思想,组件生命周期与 React 保持一致,同时支持使用 JSX 语法,让代码具有更丰富的表现力,使用 Taro 进行开发可以获得和 React 一致的开发体验。taro官网状态管理
: redux/mobx…UI框架
: antd/material,Antd是蚂蚁金服开源的基于React的UI框架相关脚手架
: create-react-app/react-native-cli/umi…调试工具
: react-devtools
JSX组件模板
JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但通常建议使用它,下面是一个常规的使用JSX定义的React组件
import React from 'react';
class Test extends React.Component{
// 类构造方法
constructor(props) {
super(props);
this.state = {
test:'',
strTest:"测试" //初始化state中strTest值
}
}
//点击事件方法,修改state域中的值,要使用setState方法
handleClick = () => {
const prestrTest = this.state.strTest;
this.setState({
test:prestrTest
})
}
// render方法返回模板
render(){
return(
<h1 onClick = {this.handleClick}>
{this.state.strTest}
</h1>)
}
}
// 导出当前组件
export default Test;
React组件定义方式有两种,上面举例的组件定义方式叫类组件,还有一种定义方式叫函数组件,顾名思义就是通过函数还定义声明一个组件,定义组件有两个原则:
- 组件名称必须以大写字母开头
- 组件的返回值只能有一个根元素
无论是函数组件还是类组件都遵循这个原则,那么函数组件和类组件有什么区别呢
函数组件
function ProfilePage(props) {
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>Follow</button>
);
}
类组件
class ProfilePage extends React.Component {
showMessage = () => {
alert('Followed ' + this.props.user);
};
handleClick = () => {
setTimeout(this.showMessage, 3000);
};
render() {
return <button onClick={this.handleClick}>Follow</button>;
}
}
组件的特点
- 无论是使用函数或是类来声明一个组件,它决不能修改它自己的
props
。 - 所有 React 组件都必须是纯函数,并禁止修改其自身
props
。 - React是单项数据流,父组件改变了属性,那么子组件视图会更新。
- 属性
props
是外界传递过来的,状态state
是组件本身的,状态可以在组件中任意修改 - 组件的属性和状态改变都会更新视图。
两种组件区别
区别 | 函数组件 | 类组件 |
---|---|---|
是否有 this |
没有 | 有 |
是否有生命周期 | 没有 | 有 |
是否有状态 state |
没有 | 有 |
性能 | 高 | 低 |
函数组件的性能比类组件的性能要高, 因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可
导入外部样式
在使用组件过程中,难免需要个性化一些开源组件的样式,这时就需要用到外部样式了,在React组件中怎么像Html引入<link rel="stylesheet" href="">
标签那样引入外部样式呢,我们定义的组件如下
import React, { Component } from "react";
import TestChild from "./TestChild";
import moduleCss from "./test.css";
class Test extends Component {
constructor(props, context) {
super(props);
}
render() {
return (
<div>
<div className={moduleCss.normal}>321321</div>
<TestChild></TestChild>
</div>
);
}
}
export default Test;
test.css内容如下
:global(.btn) {
…
}
// more than one syle in gloabl
:global {
.normal{
width:'200px'
}
.danger{
...
}
}
JSX中使用条件判断
在react中用jsx渲染dom的时候经常会遇到if条件判断,然而在jsx中是不允许 if 条件判断的。以下有几种判断方式,可以根据自己的应用场景,挑选适合的。
方式一
在 render 定义一个变量,然后写具体的逻辑,再把Html模板赋值给这个变量。最后在 return 方法中引用这个变量
class HelloMessage extends React.Component {
render (){
let userMessage;
if (this.props.loggedIn) {
userMessage = (
<span>
<h2>{ `Welcome Back ${ this.props.name }` }</h2>
<p>You can visit settings to reset your password</p>
</span>
)
} else {
userMessage = (
<h2>Hey man! Sign in to see this section</h2>
)
}
return(
<div>
<h1>My Super React App</h1>
{userMessage}
</div>
)
}
}
方式二
在render 定义一个函数,在函数中写具体逻辑,在 return 方法中调用刚刚定义的函数
class HelloMessage extends React.Component {
renderUserMessage(){
if (this.props.loggedIn) {
return (
<span>
<h2>{ `Welcome Back ${ this.props.name }` }</h2>
<p>You can visit settings to reset your password</p>
</span>
);
} else {
return (
<h2>Hey man! Log in to see this section</h2>
);
}
}
render (){
return(
<div>
<h1>My Super React App</h1>
{ this.renderUserMessage() }
</div>
)
}
}
方案三
JSX中虽然不能直接用 if 条件,但可以使用三元运算符,对于一些非黑即白的逻辑可以直接用三元运算符实现
class HelloMessage extends React.Component {
render (){
return(
<div>
<h1>
{ this.props.loggedIn ? 'You are logged In' : 'You are not logged In' }
</h1>
</div>
)
}
方案四
JSX中三元运算符不仅可以返回字符串,也可以返回Html模板
class HelloMessage extends React.Component {
render (){
return(
<div>
<h1>My Super React App</h1>
{ this.props.loggedIn ?
<span>
<h2>{ `Welcome Back ${ this.props.name }` }</h2>
<p>You can visit settings to reset your password</p>
</span>
:
<h2>Hey man! Log in to see this section</h2>
}
</div>
)
}
}
方案五
使用do表达式,关于do表达式的文档
const Component = props =>
<div className='myComponent'>
{do {
if(color === 'blue') { <BlueComponent/>; }
else if(color === 'red') { <RedComponent/>; }
else if(color === 'green') { <GreenComponent/>; }
}}
</div>
;
JSX中使用循环
JSX中使用循环生成标签,比如列表数据,自定义卡片等,循环有两种方式。需要注意的是,主流循环写法是 map,JSX里面不能用for循环,因为for循环不是表达式
。
forEach / for方式
import React,{Component} from 'react';
let list=[
{
name:"百度",
address:"http://www.baidu.com"
},
{
name:"google",
address:"http://www.google.cn"
},
{
name:"firefox",
address:"https://home.firefoxchina.cn"
}
];
class ForEach extends Component{
render(){
//定义数组,将数据存入数组
const elements1=[];
const elements2=[];
// forEach循环
list.forEach((item)=>{
elements1.push(
<div>
<p>网站:{item.name}</p>
<p>网站:{item.address}</p>
</div>
)
});
// for循环
for(let i=0;i<list.length;i++){
elements1.push(
<div key={i}>
<p>网站:{list[i].name}</p>
<p>网站:{list[i].address}</p>
</div>
)
};
return(
<div>
{elements1}
{elements2}
</div>
)
}
}
export default ForEach;
map方式
// 定义一个数组
var bookArr=[
{
siteName:"百度",
siteUrl:"http://www.baidu.com"
},
{
siteName:"阿里巴巴",
siteUrl:"http://www.alibaba.com"
},
{
siteName:"腾讯",
siteUrl:"http://www.qq.com"
}
]
class Book extends React.Component{
render(){
return (
<div>
{
bookArr.map((item,index)=>{
return(
<div key={index}>
<p>网站:{item.siteName}</p>
<p>网站:{item.siteUrl}</p>
<hr/>
</div>
)
})
}
</div>
)
}
}
export default Book;