1. 组件列表渲染、条件渲染
组件列表渲染
有时候我们需要批量的去创建一些DOM元素或组件, 我们可以通过数组存储数据,也可以使用数组来循环渲染数据。
class Demo extends React.Component{
state = {
arr: [a, b, c],
}
render(){
return (
<div>
<ul> {stus} </ul>
</div>
)
}
}
我们发现React输出数组会把所有的元素直接循环输出出来,
这里我们可以通过 jsx语法配合ES6语句来实现列表渲染。
class Demo extends React.Component{
state = {
stus: [
{id: "001", name: "小明", age: "28"},
{id: "002", name: "小红", age: "26"},
],
}
render(){
return (
<div>
<ul>
{
this.state.stus.map(item =>{
return <li key={item.id}>{item.name}---{item.age}</li>
})
}
</ul>
</div>
)
}
}
2. DOM的 Diffing 算法
key 的使用
有的人发现了写列表渲染的时候,我都会给每个标签加上一个 key属性,这是为什么呢? 其实 key的作用是给当前的标签添加一个唯一的标识,用于给React进行 diffing算法计算的时候使用
diffing 算法
当状态发生改变时,react会根据【新的状态】生成【新的虚拟DOM】 然后将新旧虚拟DOM进行 diff比较,比较规则如下:
-
旧虚拟DOM 中 找到了 与 新虚拟DOM 相同的 key
- 若虚拟DOM中的内容没变,则直接使用之前的真实DOM
- 若虚拟DOM中的内容变了,则生成新的真实DOM并进行替换
-
旧虚拟DOM 中未找到与 新虚拟DOM 相同的 key,则根据数据创建新的真实DOM,然后渲染到页面
用index作为key可能引发的问题
- 对数据进行 逆序添加、逆序删除 邓破坏顺序的操作 会产生没有必要的真实DOM更新,影响效率
- 对结构中包含输入类的DOM 会产生错误的 DOM更新,同时界面渲染有问题
- 若仅用于展示数据,那用 index作为 key则没有问题
3. React脚手架
1. 安装React脚手架
在自己的命令行窗口中输入(需要现有 node环境):
npm i create-react-app -g
全局安装 create-react-app脚手架
2. 创建React应用
create-create-app 应用名称
- 应用名称不应该出现大写字母和特殊字符
- 使用英文命名而不是中文命名
3. 文件解析
项目文件中比较常用的文件就以下这些:
- node_modules ------ npm包存放的位置
- public ------ 用来存放静态文件
- src ------ 项目的代码存放的位置
- components ------ 用于存放公用组件
- page ------ 用于存放页面
App.js ------ 根组件App.css ------ 根组件样式index.js ------ 项目入口的文件index.css ------ 项目的公用样式 .gitgnore ------ 编写git的配置文件package.json ------ 项目配置文件README.md ------ 项目信息
APP就是根组件,通过我们编写其他的组件如 Header、Aside等组件,都加装在根组件上。
由一个根组件下面挂上许多组件,体现react项目的组件化开发思想.
4. npm 指令
npm start // 使用 webpack-dev-server 启动服务查看应用
npm build // 打包生成生产文件
npm test // 进行软件测试(不常用)
npm eject // 将所有的配置文件暴漏出来(不常用且不建议用)
4. react-router的使用
1. 什么是 react-router
react-router 是为了React编写SPA应用操作前端路由而诞生的。
(1) 前端路由(可能有点直白)
前端路由是通过HTML5的新API History来操作的,其原理就是url地址的地址发生改变,但是并不会触发重新加载,同时javascript可以监听到改变。 有两种类型:
HashRouter: 利用url地址栏的 # 后面的哈希值

BrowserRouter: 利用浏览器的History API,地址栏中不包含 # ,显得更加美观

以上两种情况都不会触发 url的跳转功能
(2) SPA 应用
SPA 全称 Single Page web Application,顾名思义是只有一个页面的应用,通过javascript进行实时的渲染来更新页面。 即通过当前的路由来判断页面应该加载什么组件,从而呈现不同的页面与效果。
2. 使用 react-router
(1) 安装与使用
在项目文件夹中打开命令行窗口进行 npm下载
npm i react-router-dom -S
复制代码
注意:我们下载的是 react-router-dom 而不是 react-router 两者区别:
react-router:提供了router的核心 API。如Router、Route、Switch等,但没有提供有关dom操作进行路由跳转的API;react-router-dom:提供了BrowserRouter、Route、Link等api,可以通过dom操作触发事件控制路由。
react-router-dom中包含了react-router,所以我们选择下 react-router-dom。
(2) 常用组件
a. 路由跳转
在多页面应用中,通常都是使用 a标签进行页面跳转
<a href="http://localhost:3000">跳转页面</a>
使用单页面富应用中使用react-router则使用路由跳转组件
import {Link, NavLink} from "react-router";
<Link activeClassName="nav-active" className="nav" to="/about">About</Link>
<NavLink activeClassName="nav-active" className="nav" to="/home">Home</NavLink>
activeClassName: 处于当前路由时,对应的组件会自动添加该类className: 当前组件类名to: 当前组件所对应的路由
Link组件与 NavLink组件都可以进行路由的跳转,区别在于:当前路由对应的NavLink会自动添加class: active,而 Link不会。
b. 注册路由
import {Route} from "react-router";
<Route path="/home" component={Home}></Route>
<Route exact path="/about" component={About}></Route>
path: 所要监听的路由component: 该路由要绑定的组件exact: 可选,不写时为 false,是否选择严格匹配
当当前路由对应上了路由组件所绑定的路由时,则会展示所绑定的组件。
(a) 路由严格匹配与模糊匹配
路由不仅仅只有一级,有的时候是有多级嵌套的,比如以下这张:
 模糊匹配和严格匹配,都是指当前的组件对当前路由的匹配模式:
-
模糊匹配
-
如果当前路由与匹配的路由成相等或包含(注意层级)的情况,则启用该组件
http://localhost:3000/home/a/b/c 则为包含路由 /homehttp://localhost:3000/a/b/home/c 则为不包含路由 /home(层级不对) -
严格匹配
-
如果当前路由与匹配的路由相等的话,才启用该组件
http://localhost:3000/home 则为与路由 /home 相等http://localhost:3000/home/a 则为与路由 /home 不相等
c. 重定向路由
你明明设置好了路由 /home,但是有的用户就喜欢对着干,在地址栏输入了 /nothing,而你没有注册这个路由,那该怎么办呢? 这个时候你就可以东涌道重定向路由了,对于没有注册过的路由,都会被跳转到你指定的某个路由去,这就是重定向路由。 经常可以用作一些404,页面丢失等情况的路由跳转方式。
import {Redirect, Route} from "react-router";
<Route ....../>
<Route ....../>
<Redirect to="/home"/>
Redirect需放在所有Route下面,当上面的 Route都没有匹配到时,则路由将重定向到指定的路由。
d. Switch 路由
你想想,如果你的路由中,出现了 /home 与 /home/abc 和 /home/a/b/c 等这样的路由,当路由为 /home时则会三个路由都同时渲染,但是你又只想要渲染其中的一条,这个时候我们就可以使用 Switch组件。 使用 Switch组件包裹住所有的 Route 和 Redirect,当出现多个匹配的路由时,只会渲染第一个匹配的组件。
import {Switch, Route, Redirect} from "react-router";
<Switch>
<Route ..../>
<Route ..../>
<Redirect to="..."/>
</Switch>
e. 路由器
你想要使用路由跳转组件和路由组件,还差一个路由器组件,同时路由器组件必须包裹着这两个组件。
import {HashRouter, BrowserRouter} from "react-router";
一般为了使整个React应用都可以使用到路由组件,所以一般我们都是把路由器包裹在根组件上的。
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.querySelector("#root")
);
有两种路由器组件,分别是HashRouter 与 BrowserRouter,分别对应者两种路由方式。
(3) 路由组件
路由组件与一般组件
-
写法不同
- 一般组件:
<Demo></Demo> - 路由组件:
<Route path="/demo" component={Demo}/> -
存放位置不同
- 一般组件:components文件夹
- 路由组件:page 文件夹
-
接受到的 props
不同
- 一般组件:根据组件标签传递了上面,就收到了什么
- 路由标签:会收到三个固定的属性
{
"history": {
"length": 18,
"action": "PUSH",
"location": {
"pathname": "/home",
"search": "",
"hash": "",
"key": "tvfyve"
}
},
"location": {
"pathname": "/home",
"search": "",
"hash": "",
"key": "tvfyve"
},
"match": {
"path": "/home",
"url": "/home",
"isExact": true,
"params": {}
}
}
3.嵌套路由
假设我们有个路由组件为 Home,在根组件中使用 Link跳转到了该路由,当前路由为 /home,可在组件 Home 中还有两个 Link,分别导向路由 /home/message 与 /home/news 然后在组件中还有其他的路由组件。这就时嵌套路由的使用。 如下面的代码:
class Home extends Component{
render(){
return (
<div>
<Link to="/home/message">Message</Link>
<Link to="/home/news">News</Link>
<hr/>
<Route path="/home/message" component={Message} />
<Route path ="/home/news" component={News} />
</div>
)
}
}
4. 编程式路由
如果说,我们想要做用户点击按钮登陆后,如果他是老师就去老师页面,如果是学生就去学生页面,这个显然单靠 Link 无法完成,我们可以通过 js进行路由的跳转(也是 react-router 基于 History API 编写的)
class Message extends Component {
state = {
messageArr:[
{id:"01", title: "消息1"},
{id:"02", title: "消息2"},
{id:"03", title: "消息3"},
]
}
pushShow = (id, title)=>{
this.props.history.push(`/home/message/detail/${id}/${title}`);
}
replaceShow = (id, title)=>{
this.props.history.replace(`/home/message/detail/${id}/${title}`);
}
goBack = ()=>{
this.props.history.goBack();
}
goForward = ()=>{
this.props.history.goForward();
}
go = ()=>{
this.props.history.go(2);
this.props.history.go(-2);
}
render() {
const {messageArr} = this.state;
return (
<div>
<ul>
{
messageArr.map(item=>{
return (
<li key={item.id}>
<Link to={`/home/message/detail/${item.id}/${item.title}`}>{item.title}</Link>
<button onClick={() => this.pushShow(item.id, item.title)}>push查看</button>
<button onClick={() => this.replaceShow(item.id, item.title)}>replace查看</button>
</li>
)
})
}
</ul>
<hr/>
<Route path="/home/message/detail/:id/:title" component={Detail} />
<button onClick={this.goBack}>goBack</button>
<button onClick={this.goForward}>goForward</button>
<button onClick={this.go}>go</button>
</div>
)
}
}
总结一下上面的代码:
- 编程式路由都是通过
props中的 history对象进行操作(都是该对象身上的方法,调用方式为:this.props.history.xxx) - 常用方法:
push(route[, state]): 跳转到指定路由(带有历史记录)replace(route[, state]): 跳转到指定路由(不带有历史记录)goBack(): 后退一个goForward(): 前进一个go(num): 前往指定步数,当 num为正数时,为前进,当 num为负数时则为后退。
5. withRouter 组件
有的时候,我们想要在其他组件中也使用路由组件的功能,比如导航栏,应该属于公用组件,但是里面的导航链接的功能却是路由组件的功能,我们应该怎么解决呢? 在 react-router 中,提供了这么一种方法,可以让一般组件具有路由组件的功能,则就是 withRouter() 方法。 看看演示:
import {withRouter} from "react-router-dom";
class Header extends Component {
goBack = ()=>{
this.props.history.goBack();
}
go = ()=>{
this.props.history.go(2);
}
goForward = ()=>{
this.props.history.goForward();
}
render() {
return (
<div>
<h1>This is a React-router-dom Test!</h1>
<button onClick={this.goBack}>goBack</button>
<button onClick={this.goForward}>goForward</button>
<button onClick={this.go}>go</button>
</div>
)
}
}
export default withRouter(Header);
|