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 则为包含路由 /home http://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);
|