IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> React第三天 -> 正文阅读

[JavaScript知识库]React第三天

1. 组件列表渲染、条件渲染

组件列表渲染

有时候我们需要批量的去创建一些DOM元素或组件,
我们可以通过数组存储数据,也可以使用数组来循环渲染数据。


class Demo extends React.Component{
    state = {
        arr: [a, b, c],
    }
    render(){
        // 输出 abc
        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比较,比较规则如下:

  1. 旧虚拟DOM 中

    找到了

    与 新虚拟DOM 相同的 key

    • 若虚拟DOM中的内容没变,则直接使用之前的真实DOM
    • 若虚拟DOM中的内容变了,则生成新的真实DOM并进行替换
  2. 旧虚拟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就是根组件,通过我们编写其他的组件如 HeaderAside等组件,都加装在根组件上。

由一个根组件下面挂上许多组件,体现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地址栏# 后面的哈希值

606_2.png

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

604_1.png

以上两种情况都不会触发 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。如RouterRouteSwitch等,但没有提供有关dom操作进行路由跳转的API
  • react-router-dom:提供了BrowserRouterRouteLink等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) 路由严格匹配与模糊匹配

路由不仅仅只有一级,有的时候是有多级嵌套的,比如以下这张:

606_2.png
模糊匹配严格匹配,都是指当前的组件对当前路由的匹配模式:

  • 模糊匹配
    如果当前路由与匹配的路由成相等或包含(注意层级)的情况,则启用该组件
    • 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"/>
  • to: 需要重定向到哪个路由?

Redirect需放在所有Route下面,当上面的 Route都没有匹配到时,则路由将重定向到指定的路由。

d. Switch 路由

你想想,如果你的路由中,出现了 /home/home/abc/home/a/b/c 等这样的路由,当路由为 /home时则会三个路由都同时渲染,但是你又只想要渲染其中的一条,这个时候我们就可以使用 Switch组件。
使用 Switch组件包裹住所有的 RouteRedirect,当出现多个匹配的路由时,只会渲染第一个匹配的组件。

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")
);

有两种路由器组件,分别是HashRouterBrowserRouter,分别对应者两种路由方式。

(3) 路由组件

路由组件与一般组件

  1. 写法不同

    • 一般组件:<Demo></Demo>
    • 路由组件:<Route path="/demo" component={Demo}/>
  2. 存放位置不同

    • 一般组件:components文件夹
    • 路由组件:page 文件夹
  3. 接受到的

    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)=>{
        // push跳转 + 携带 params参数
        this.props.history.push(`/home/message/detail/${id}/${title}`);
        
        // push跳转 + 携带 search参数
        // this.props.history.push(`/home/message/detail?id=${id}&title=${title}`);
        
        // push跳转 + 携带 state参数
        // this.props.history.push(`/home/message/detail`, {id,title});
    }
    replaceShow = (id, title)=>{
        // replace跳转 + 携带 params参数
        this.props.history.replace(`/home/message/detail/${id}/${title}`);
        
        // replace跳转 + 携带 search参数
        // this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`);
        
        // replace跳转 + 携带 state参数
        // 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>
                                &nbsp;<button onClick={() => this.pushShow(item.id, item.title)}>push查看</button>
                                &nbsp;<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>
                &nbsp;
                <button onClick={this.goForward}>goForward</button>
                &nbsp;
                <button onClick={this.go}>go</button>
            </div>
        )
    }
}

总结一下上面的代码:

  1. 编程式路由都是通过 props中的 history对象进行操作(都是该对象身上的方法,调用方式为:this.props.history.xxx
  2. 常用方法:
    • 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 {
    // withRouter后该组件也有了路由组件的功能
    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>
                &nbsp;
                <button onClick={this.goForward}>goForward</button>
                &nbsp;
                <button onClick={this.go}>go</button>
            </div>
        )
    }
}

// withRouter 用于给一般组件添加上路由组件特有的功能,返回一个新组件
export default withRouter(Header);
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-30 12:39:12  更:2021-07-30 12:41:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 18:28:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码