React 全家桶入门教程 01

news/2024/11/7 17:00:07/

React 全家桶入门教程 01

前面是基础课程(难度小,略过),后面是案例

目的

  1. 巩固react基础知识,查漏补缺(熟悉的部分快进)
  2. 学习相关的库的使用

https://study.163.com/course/courseMain.htm?courseId=1210995818 共计126课时,2021年1月出品

全部课程约38小时;7月24日前学了10小时。

第一章 React 基础

01 react简介

React 是构建用户界面的框架。

前端工作包括:发送请求,处理数据,展示数据(使用DOM展现)。发出请求(ajax)、处理数据(算法优化,过滤排序算法)、渲染界面(React 将 JS 数据渲染成 HTML 视图的库)

React 只处理展示数据(把 state 转换成 DOM)。不管前两部分。

原生 JS 缺点:操作 DOM繁琐,性能较差(JS 直接操作 DOM,浏览器进行大量的重排重绘,reflow and repaint);jQuery 比较重,代码量很大;原生JS没有组件化方案,代码复用很低。

React 框架优点:组件化(提高组件复用率)、声明式开发;便于 ReactNative 进行移动端开发;虚拟 DOM 和 Diff 算法高性能。

02 hello-react案例

  • react.js React 核心库
  • React-dom.js React 操作 DOM 的库
  • babel.js 把ES6转换成ES5,把 JSX 转换成 JS

注意

  1. HTML 引入顺序不能错
  2. Script 这个 type (实际上不会这样写)
  3. 内部是 JSX,不需要加引号(不是字符串 dom = '';)
<script type="text/babel">
  let dom = <div></div>;
</script>

03 虚拟DOM的两种创建方式

创建虚拟 DOM,可以使用 JSX 字面量(优先),或者使用 React API 创建。

const VDOM = React.createElement('h1', {id: 'test'}, 'Hi');
ReactDOM.render(VDOM, document.getElementById('root'));const vDOM2 = <div></div>;

04 虚拟DOM与真实DOM

虚拟DOM就是 jsx 语法中创建的虚拟 DOM,真实 DOM 就是原生 JS 创建的 DOM。

let virtualDOM = <div></div>;
let trueDOM = document.getElementById('test');

虚拟 DOM 特点:

  1. 本质是一个对象(Object)
  2. 虚拟DOM属性较少,真实DOM属性较多(例如尺寸和样式相关的API)因为虚拟DOM是 React 内部使用,所以不需要真实DOM上那么多的属性。操作虚拟DOM也更轻量化。
  3. 最终 React 会把虚拟DOM,转换成真实DOM,渲染到浏览器中。

05 jsx语法规则

jsx:javascript+XML 比传统的 HTML 严格,例如标签必须闭合;不能随便写自定义的标签。XML早期用于前后端传递数据的格式,可以使用XML或者JSON(JSON 可以 parse stringify 和字符串进行转换,主要使用后一种进行传输)。

JSX 语法规则

  1. 虚拟DOM直接写标签DIV,不需要加引号(不是字符串)
  2. 标签DIV内的 JS 表达式使用 大括号 {}
  3. 使用 className 代替 class 类名
  4. 样式使用 style = {{padding: 100}}
  5. 只能有一个根标签
  6. 标签必须闭合(自闭合或者成对)
  7. 标签:小写字母表示HTML标签,大写字母开头表示组件(需要引入)如果没有HTML标签或者组件,那么react会报错。

06 jsx小练习

JS 表达式是什么?和 JS 语句(代码)不一样

  • 表达式:等号右侧的是表达式,也就是说这一段代码会输出一个结果(a, a + b, foo(a, b), arr.map(), function test() {} )都是表达式。let a = 表达式;
  • 语句:if for while switch 等语句结构,不会返回结果,这部分不能在 JSX 中使用

所以,三目运算法可以使用,if 判断无法使用

<div>
  {
    arr.map((item) => {
      return (
        <span key={item.id}>{item.value}</span>
      );
    })
  }
</div>

第二章 React 面向组件编程

07 组件与模块

组件:界面中某个功能对应的代码

模块化;组件化;工程化

模块化:JS 封装成不同的部分;大型项目拆分成不同的模块,提高复用性

组件化:React组件拆分成不同的部分(根据功能拆分),拆分 JSX css JS 等

08 开发者工具的安装

react 开发调试工具(Component 可以查看组件层级和 state props 等属性,Profiers 查看页面性能)

profiler 记录网站的性能(加载时间)

09 函数式组件

function Demo() {
  console.log(this);
  // 在函数组件中,经过babel编译后,是严格模式,所以 this 是 undefined
  return <span></span>;
}
ReactDOM.render(<Demo/>, document.getElementById('root'));
// ReactDOM.render 执行过程
// 1. 解析第一个参数,找到组件定义 Demo(函数组件)
// 2. 调用 Demo 函数,将返回值 虚拟 DOM 转换成真实 DOM
// 3. 解析第二个参数,将真实DOM渲染到页面上

10 ES6 类

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  speak = () => {
    console.log(this.name + String(this.age));
  }
}class Student extends Person {
  constructor(name, age, grade) {
    super(name, age);
    this.grade = grade;
  }
  speak = () => {
    console.log(this.name + this.age + this.grade);
  }
}const p1 = new Person('Bing', 10);
p1.speak();
const s1 = new Strudent("Andy", 20, 3);
s2.speak();

11 类式组件

class MyComponent extends React.Component {
  render() {
    return (
      <span>test</span>
    );
  }
}// <MyComponent />

遇到标签后,React 解析到是一个类,执行 new MyComponent 创建类的实例对象,调用 render 方法返回虚拟 DOM,然后渲染到页面上作为真实DOM节点。

2.2 state

12-14 state 入门

state 是状态机,通过更新状态驱动页面更新

class Weather extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isHot: false,
    };
  }
  render() {
    return (<span></span>);
  }
}

原生事件绑定

let dom = document.getElementById('btn1');
dom.addEventListener('click', () => {
  console.log('click btn1');
});let dom2 = document.getElementById('btn2');
dom2.onClick = () => {
  console.log('click btn2');
}

React 中通常不直接获取DOM

let dom = (<div onClick={this.onClick}></div>);onClick = (e) => {
  console.log(1);
}

15 类中方法中的this

16 解决类中this指向问题

17 setState的使用

react 中的函数需要绑定this(bind 或者 箭头函数),改变state 需要通过 setState 改变。

18 state的简写方式

19 总结state

18-19 state 中绑定 this 可以通过 bind 或者 箭头函数实现。因为界面调用的是原型对象上的函数,state 状态在实例对象上,所以需要通过改变 this 的方法,获取到实例对象上的属性。state 的简写:可以不放在构造器中,直接放在类中。简单组件可以这样使用,复杂组件为了明确状态和属性,最好放在 constructor 构造器中实现。

21使用展开运算符批量传递 props

展开运算符使用

  • 可以直接展开数组(复制数组)
  • 不能直接展开对象;如果是 React 组件中传 props 可以使用展开运算符
  • 在函数中可以表示不确定的参数,实现循环或者递归运算
function sum(...numbers) {
  return numbers.reduce((pre, curr) => {
    return pre + curr;
  }, 0);
}sum(1,2,3,4);
let obj = { name: 'a', age: 19 };// props 较多可以这样写,大括号并不是对象,是 jsx 的语法糖
<MyComponent {...obj}/>// console.log(...obj); 不能直接展开对象打印到原生JS中

22 限制 props 类型

使用 PropTypes 和 defaultProps 限制数据类型和默认值

23 props 简写形式

24 构造器

constructor 不是必须的,主要用于声明状态和绑定函数(bind)

constructor (props) {
  super(props); // 实现继承
  this.state = {
    isFinished: false,
  };
  this.isChecked = React.createRef();
  this.fn = this.fn.bind(this); // 通常使用箭头函数
}

25 函数式组件使用 props

函数式组件可以使用 props,hook 中可以使用 setEffect 设置状态,也可以限制数据类型。

function Persion(props) {
  const { name, age } = props;
  return <span>{name}</span>;
}Person.defaultProps = {
  age: 20,
};<Person name={'Tom'} />

2.4 refs

27字符串形式的ref

ref 在 react 中,就类似原生 JS 中的 ID,通过 ref 可以直接获取到对应的 DOMS。组件内的元素可以通过 ref 来标识自己。ref 创建有三种形式:字符串形式创建,回调函数创建,createRef 创建三种。最新 react 推荐使用第三种创建 refs。

使用字符串形式创建:创建使用 ref="test",获取属性使用 this.refs 复数形式(一个组件内部有很多 refs)

this.refs.input1
this.refs.input2
this.refs.input3

28 回调形式的ref

使用字符串形式创建 ref,react 官方不推荐这种写法(后期会废弃)性能比较差

推荐使用 createRefs 或者 回调函数创建 refs

<input ref={(node) => this.input1 = node} />
<input ref={c => this.input2 = c}/>

29 回调ref中调用次数的问题

如果回调函数直接写在 JSX 中,每次 render 都会调用两次。所以,回调函数可以放在类的方法中。React 官方说这两种方法的性能差异不大。

<input ref={this.setRef}/>setRef = (node) => {
  this.input2 = node;
}

30createRef的使用

this.inputRef = React.createRef();let a = this.inputRef.current.value;<input ref={this.inputRef} />

31 总结ref

三种创建 ref 的方法对比,官方推荐第三种,工作中推荐后两种,实际哪种方便用哪种。

32 react中的事件处理

react 中通过 onClick 绑定事件(事件处理函数):使用合成事件,不是原生事件,为了更好的兼容性。事件通过时间委托方式处理(委托给组件最外层的元素)——为了更高效(如果再底层监听事件,那么需要很多事件监听函数,在上层只需要一个事件监听函数)。可以通过 event.target 获取到触发事件的对象

33 非受控组件

表单通过 ref 获取输入内容,就是非受控组件(用户输入过程中,react不会获取到输入的内容)切勿过度使用refs

使用状态提升可以处理 ref 较多的情况

下面是传统表单,点击按钮后,会自动获取 username 和 password,然后提交到 action 网址中。

<form action="www.baidu.com">username: <input type="text" name="username"/>password: <input type="password" name="password"/><button>login</button>
</form>

34 受控组件

<input onChange={this.save("username")}/>
<input onChange={this.save("password")}/>save = (type) => {
  // 这里返回一个函数,就是高阶函数
  // 首次加载时,返回这个函数。当 onChange 触发时,执行内部回调函数
  return (event) => {
    // console.log(event.target.value, type);
    this.setState({
      [type]: event.target.value
    });
  }
}

35 高阶函数-函数柯里化

高阶函数:参数是一个函数,或者返回值是一个函数的函数,就是高阶函数(Promsise, setTimeout, reduce)

函数 carry-function 柯里化:函数返回值是函数,可以继续链式调用。多次接收参数,最后统一调用。

function sum(a) {
  return (b) => {
    return (c) => {
      return a + b + c;
    }
  }
}const result = sum(1)(2)(3);

36 不用柯里化的写法

<input onChange={e => this.save('username', e.target.value)}/>save = (type, value) => {
  this.setState({
    [type]: value,
  });
}

37 引出生命周期

从节点卸载组件

ReactDOM.unmountComponentAtNode(document.getElementById('test'));

38 生命周期(旧)-组件挂载流程

39 生命周期(旧)-setState流程

40 生命周期(旧)-forceUpdate流程

41 生命周期(旧)-父组件render流程

42 总结生命周期(旧)

很简单

43 对比新旧生命周期

新版本不推荐使用的三个生命周期函数(ComponnentWillMount, ComponentWillReceiveProps, ComponentWillUpdate);以及新加入的两个生命周期函数(getDerivedStateFromProps, getSnapshotBeforeUpdate)

为什么不推荐使用?过时的三个生命周期函数,经常造成误解滥用,可能造成bug。在 17版本中,可以使用,界面上提示警告。在 18 版本中异步渲染,不能直接使用这三个生命周期函数,必须加上 UNSAVE 前缀(React.lazy, React.Suspend)。

44 getDerivedStateFromProps

这个生命周期函数是 componentWillReceiveProps 的替代,用于 props 变化造成组件 state 更新的情况。

这是一个静态方法,在组件render前,或者组件更新后都调用。

static getDerivedStateFromProps(nextProps, prevState) {
  // 对比 nextProps 和 prevState 等,返回新的 state 或者 null
  // 这是静态方法,无法直接访问到实例对象的属性和状态(this)
  if (nextProps.name !== prevState.name) {
    prevState.name = nextProps.name;
  }
  return prevState;
}

派生状态会造成代码冗余,尽量避免使用。ComponentWillReceiveProps 只在父组件重新渲染时触发,getDerivedStateFromProps 在每次渲染前调用(包括内部setState)。

45 getSnapshotBeforeUpdate

在渲染之前调用,可以获得 DOM 信息(例如元素滚动位置),返回值传给 componentDidUpdate 函数,用于处理滚动位置和动画。通常不使用。

getSnapshotBeforeUpdate(prevProps, prevState) {
  if (prevProps.list.length < this.props.list.length) {
    const list = this.listRef.current;
    return list.scrollHeight - list.scrollTop;
  }
  return null;
}// 当有新消息加入时,界面滚动位置不变
componentDidUpdate(prevProps, prevState, snapshot) {
  if (snapshot) {
    const list = this.listRef.current;
    list.scrollTop = list.scrollHeight - snapshot;
  }
}

46 getSnapshotBeforeUpdate 举例

简化的案例

getSnapshotBeforeUpdate() {
  return this.refs.list.scrollHeight;
}componentDidUpdate(preProps, preState, height) {
  this.refs.list.scrollTop += this.refs.list.scrollHeight - height;
}

47 总结生命周期(新)

初始化阶段:getDerivedStateFromProps render componentDidMount

更新阶段:getDerivedStateFromProps shouldComponentUpdate render getSnapshotBeforeUpdate componentDidUpdate

卸载阶段:componentWillUnmount

注:getDerivedStateFromProps 在首次加载和再次更新时,都会触发。

48 DOM的diffing算法

对应面试题:React 中 key 的作用是什么?用来作为唯一标识,在 渲染过程中 diff 算法中,判断前后节点是否发生变化。如果Key变化了,那么直接渲染这个节点。如果 key 不变,那么递归比较这个节点内部的子节点。

使用 index 作为 key 可能存在问题,所以最好使用 id 作为 Key。如果在大型列表中,列表项越多,这个性能问题就越大(重新加载一个节点,还是重新加载全部的节点)

如果某些列表是静态的,为了方便,也可以使用 index 作为 key。

第三章 react-cli

49 初始化react脚手架

React-create-app 使用和配置

50 脚手架文件介绍-public

favicon

index.html

logo.png

Manifest.json 应用加壳配置文件

Robots.txt 爬虫协议文件

// 这个表示 public 文件夹的路径<link ref="icon" href="%PUBLIC_URL%/favicon.ico"/>// 理想视口,用于移动端网页适配
<meta name="viewport" content="width=device-width, initial-scale=1">// 安卓移动端 TAB 背景色(兼容性不好)
<meta name="theme-color" content="#000"><meta name="description" content="this is test" />// 苹果手机桌面快捷应用图标
<link rel="apple-touch-icon" href="%PUBLIC%/logo192.png" />// 应用加壳规则
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">

51 脚手架文件介绍-src

reportWebVitals.js 页面性能分析工具

React.StrictMode 这个标签并不代表严格模式,而是处理 React 语法中的提示(例如使用了过时的 ref="test" 等)这个包裹在顶层标签下面即可。

52 组件化编码流程

组件化拆分流程

1、拆分组件:根据设计稿,思考拆分成多少部分

2、静态组件拆分:设置不同静态组件,同时 mock 数据

3、动态数据更新(状态属性,界面交互等)

从一个旧项目重构成 raect 项目,或者新项目,最好先从细节组件,公共组件做起。

拆分组件的原则:组件在界面上有明确的分界,组件有明确的名称(对应组件的功能或者位置)如果某个组件名称不合适,那么这个组件的功能和位置不合适,需要进一步拆分处理重构。

在 VScode 中加入插件,可以用快捷键搭建基本的架构

ID 的库

uuid 库比较大,可以使用 nanoid 产生随机的 ID,在性能要求不高的情况下可以用

defaultChecked

defaultChecked 可以初始化勾选状态,但是不支持后续 state 更改,实际工程中避免使用这个属性。实际中使用 checked = 处理选择或者不选择,还有一个半选择的图标(回去查一下)

checkbox 如果获取 event.target.value 可以获取到 on 始终是这个值,无意义

window.confirm 原生的弹出框,函数的返回值是用户的选择,用于简单的交互。这个会阻挡JS的执行,在不同浏览器中可能不美观,实际项目中没有使用。

后面有一个案例(53-64),很简单,不详细说了

第四章 react ajax

65 脚手架配置代理-方法1

react 主要处理状态管理和界面渲染,没有包括网络请求功能。通常使用 axios 做网络请求(promise return )。

axios.get(url).then(res => {
  console.log(res.data);
}, err => {
  console.log(err);
});

如果本地服务端在 5000 端口,浏览器界面是 3000 端口,那么根据浏览器同源策略,会出现跨域的问题。

需要一个代理服务器,把5000返回的响应,转发到3000端口上面。

通常在 package.json 中设置代理,这样就能处理5000端口返回的数据了。这里表示先请求3000端口,如果没有的话找5000端口

"proxy": "http://localhost:5000"

66 脚手架配置代理-方法2

实际开发中,可能有不同的服务器提供多种服务,但是前端只有一个端口,所以也需要在服务端处理跨域的问题。

这个在 react 脚手架下可以这样配置,一个项目配置一次即可,不需要多次配置

// setProxy.js 需要脚手架环境,不能改名字// 这个库已经在 cra 中包含,不需要单独安装
const proxy = require('http-proxy-middleware'); module.exports = function(app) {
  app.use(
    // 这是中间件,表示判断api1的请求,发送到 5000 端口,然后改变源,并且替换到 api1
    proxy('/api1', {
      target: 'http://localhost:5000',
      changeOrigin: true, // 服务器收到请求头中 Host 字段的值(可选)
      pathRewrite: {'^api1': ''},
    }),
    // 另一个中间件,处理另一个跨域的情况
    proxy('/api2', {
      target: 'http://localhost:5001',
      changeOrigin: true,
      pathRewrite: {'^api2': ''},
    })
  )
}
// 跨域也可以在服务端使用 cors 技术实现

67 github搜索案例-静态组件

常用的固定的css最好放在 public 中,然后在 HTML 模板中插入

组件单独的 css 可以放在 src/css 路径下,在不同组件中引入

68 github搜索案例-axios发送请求

// 这是原来的写法,代码可读性较差
axios.get(url).then(res => {
  console.log(res.data);
  this.setState({ data: res.data });
}, err => {
  console.log(err);
});// 这样回调函数更清晰,推荐这种写法
axios.get(url).then(
  res => {
    console.log(res.data);
    this.setState({
      data: res.data
    });
  },
  err => {
    console.log(err);
  },
);

69 github搜索案例-展示数据

70 github搜索案例

三目运算法可以嵌套使用

<div>
  {
    isFirst ? <div>Welcome</div> :
    isLoading ? <Loading/> :
    isError ? <Error> :
    <List/>
  }
</div>

71 消息订阅与发布-pubsub

Pubsub-js 是一个第三方库,publish-subscribe

npm install pubsub-js --save
import PubSub from 'pubsub-js';componentDidMount() {
  this.token = PubSub.subscribe('delete', function(message, data){
    console.log(message, data); 
  });
}componentWillUnmount() {
  PubSub.unscribe(this.token);
}// another component
PubSub.publish('delete', data);

72 fetch发送请求

JS 底层通过 xhr 或者 fetch 发送请求。jQuery 和 axios 都是基于 xhr 发送请求。

问题:老版本浏览器不支持;实际上使用不多(原生XHR书写麻烦,axios 已经封装)。

fetch 关注分离:第一步先判断服务器是否连接,第二步再获取返回的数据

fetch('/api1/search/users?q=test').then(
  response => {
    console.log('连接服务器成功');
    // 这里的返回值是 Promise 所以可以使用 then 处理
    return response.join();
  },
  error => {
    console.log('连接服务器失败');
    return new Promise(() => {});
  },
).then(
  response => {
    console.log('获取数据成功')
  },
  error => {
    console.log('获取数据失败')
  },
);

上面这样写,需要使用两个 error 处理函数,实际可以使用一个替代

fetch(api).then(
  response => {
    console.log('连接服务器成功');
    return response.join();
  },
).then(
  response => {
    console.log('获取数据成功')
  }
).catch(
  err => {
    console.log(err);
  }
);

或者使用 await 优化

try {
  const res = await fetch(api);
  const data = await res.json();
  console.log(data);
} catch (err) {
  console.log(err);
}

技术层面,XHR(axios, ajax) 和 fetch 是独立的技术

73 总结github搜索案例

相关知识点:

1、设计状态要全面:例如一个列表,需要考虑初始的状况(欢迎词),网络请求中的状态(loading)数据加载后的状态(列表)数据为空的状态(空列表,还是渲染没有数据)。如果涉及网络请求,要考虑成功后的回调函数,失败后的回调函数,界面的提示等等。

2、ES6 解构赋值的重命名

let obj = {a: {b : 1}};
const { a } = obj;
const { a: { b } } = obj; // 多重对象解构赋值
const { a: time } = obj; // 解构赋值变量重命名

3、消息订阅和发布

先订阅后发布;适合任何组件传参通信;需要在卸载时取消订阅

这里介绍的是第三方的订阅,实际项目中自定义的消息订阅,需要逐层传参,使用不变

4、fetch 发送请求(关注分离思想)

fetch 实际使用不多,了解即可,大部分情况使用 axios

axios 设计基于 XHR 原理,直接返回请求的数据。fetch 是浏览器原生的,不基于 XHR,先判断是否连通,然后获取数据。

try {
  const res = await fetch(url); // 先判断服务器是否连通
  const data = await response.json(); // 然后从返回的 Promise 中拿到数据
  console.log(data);
} catch (e) {
  console.log(e);
}

第五章 react-router

74 对SPA应用的理解

单页面应用;整个应用只有一个完整的页面;点击页面内部的连接,页面不会刷新,内部内容更新(使用 axios 请求获取数据,前端异步展示)

75 对路由的理解

路由:映射关系(key-value)满足某个路由,就显示某个页面

后端路由:根据 URL 中的参数,查找到匹配的路由,处理请求,返回响应数据

前端路由:根据 url 中的参数,界面展示不同的组件;当 URL 变化后,前端界面更新

<Route path="/test" component={Test}>

76 前端路由原理

前端路由(react-router,history),是基于 History 对象,进行 pop push replace 栈操作

// 引入第三方的库
let history = History.createBrowserHistory(); // 基于 History 对象
// let history = History.createHashHistory(); // 基于 Hash function push(path) {
  history.push(path);
  return false;
}function replace(path) {
  history.replace(path);
}function back() {
  history.goBack();
}function forward() {
  history.goForward();
}history.listen((location) => {
  console.log('request route path is changed', location);
})

77 路由的基本使用

react-router 分成 web native 版本,我们使用 web 前端版本(前端路由)

path 对应的 value 是组件 component,对应界面展示的内容

npm install react-router-dom
import { Link, BrowserRouter } from 'react-router-dom';
import { About, Home } from './component/';render () {
  return (
    // 路由链接实现切换组件(这里最好放在顶层组件)
    <BrowserRouter>
      <Link to="/about">关于我们</Link>
      <Link to="/home">主页</Link>
    </BrowserRouter>    // 注册路由
    <Route path="/about" component={About} />
    <Route path="/home" component={Home} />
  );
}

改进后的顶层组件

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } form 'react-router-dom';
import App from './App';ReactDOM.render(
  <BrowserRouter>
    <App/>
  </BrowserRouter>,
  document.getElementById('root');
)

总结:

  • APP 外部统一包裹一个 BrowserRouter 和 HashRouter
  • 界面导航区,使用 Link 标签
  • 界面内容区,使用 Route 进行路径匹配

78 路由组件与一般组件

路由组件内部,可以收到特殊的 props

history(history 的一个属性就是 location)
  go
  goback
  goForward
  push
  replace
location
  pathname
  search
  state
match
  params
  path
  url
<NavLink activeClassName to="/about">About Us</NavLink>

navlink 可以实现路由链接高亮,通过 activeClassName 指定样式名称

标签体内容是特殊的标签属性(通过 this.props.children 获取标签体内容)

import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';export default class MyLink extends Component {
  render() {
    const { to } = this.props;
    return (
      <NavLink
        activeClassName="test"
        className="list-group-item"
        to={to}
        {...this.props}
      >
        {this.props.children}
      </NavLink>
    );
  }
}

81 Switch的使用

通常情况下,path 和 component 是一一对应的,如果有相同的 path 后面会覆盖前面

如果是 switch 嵌套的路由,那么优先匹配,找到满足的就不继续匹配了,提高效率

<Switch>
  <Route path="/to" component={About}/>
  <Route path="/to" component={My}/>
</Switch>

实际项目中,通常不会写两个相同的 path

82 解决样式丢失问题

开发中,可能公共样式丢失(bootstrap 样式引入无效)或者其他静态资源引入不正常

检查引入的路径

<link rel='icon' href="%PUBLIC_URL%/favicon.ico" />
<link rel="stylesheet" href="./css/bootstrap.css" />// 使用项目路径代替相对路径
<link rel="stylesheet" href="%PUBLIC_URL%/bootstrap.css" />

不要写 ./ 相对路径,可以写 / 从根目录中引入,或者 %PUBLIC_URL% 表示公共路径

83 路由的模糊匹配与严格匹配

默认路由是模糊匹配(例如组件 /about 可以匹配路由链接 /about/us/)顺序需要一致

严格匹配: exact={true} 必须严格匹配,通常不需要(可能造成二级路由失效)

<Route exact={true} path="/about" component={About} />

84 Redirect的使用

redirect 表示重定向,通常用于 Switch 的无效匹配时,默认跳转的页面

写在路由注册最下方,当所有路由都无法匹配是,跳转到 redirect 指定的路由

<Switch>
  <Route path="/about" componnet={About} />
  <Route path="/home" component={Home} />
  <Redirect to="/about" />
</Switch>

85 嵌套路由

多级导航-多级路由嵌套

<div><ul className="nav nav-tabs"><li><MyNavLink to="/home/news">新闻</MyNavLink></li><li><MyNavLink to="/home/message">消息</MyNavLink></li></ul><Switch><Route path="/home/news" component={News} /><Route path="/home/message" component={Message} /><Redirect to="/home/news" /></Switch>
</div>

注册子路由,要协商父路由的 path

路由的匹配是按照注册路由的顺序进行的

86 向路由组件传递params参数

<Link to={`home/message/datail/${id}/${name}`} >{title}</Link>// 声明接收 params 参数
<Route path="/home/message/detail/:id/:name" component={Detail} />

在子路由中接收参数(这种可读性强,使用最多)

const { id, name } = this.props.match.params;
<Link to={`/home/message/detail/?id=${id}&name=${name}`}>{title}</Link>

子组件中接收到字符串,需要解析成对象

import qs from 'querystring';qs.stringify({name: 'Amy', age: 20});
qs.parse('name=amy&age=20');const { search } = this.props.location;
const { id, name } = qs.parse(search.slice(1));

88 向路由组件传递state参数

这样界面的路由中,用户看不到传递的参数。

<Link to={{pathname: '/home/message/detail', state: {id: id, name: name}}}>{title}</Link>

子组件获取参数

const { id, name } = this.props.location.state;

界面刷新后,可以保留参数(界面强制清空缓存,这个连接就无效了,实际技术还是 history 对象)

89 总结路由参数

  • Params:在 URL 中携带参数,注册路由使用 :name 接收。参数 this.props.match.params 使用最多
  • Search: 在 ?key=value 中传参。参数:this.props.location.search(string)
  • state 参数 刷新也可以保留参数,this.props.location.state
<Link to={`/home/${id}/${title}`}></Link><Link to={`/home/?id=${id}&title=${title}`}></Link><Link replace={true} to={{pathname: '/home/message/detail', state; {id: id, title: title}}}></Link>

90 push与repalce

通过 JS 更改 history 即可

91 编程式路由导航

之前的案例都是用户点击按钮,触发路由,改变页面内容。如果需要使用 JS 触发路由,就需要使用相关的 API 进行操作(例如,点击按钮后,界面停留3s,再跳转)。

原理就是操作 BOM 的 history 对象

history.go()
history.goBack()
history.goForward()history.push()
history.replace()

具体使用

// replace 没有历史记录
this.props.history.repalce('/home/message');// push 有历史记录
this.props.history.push('/home/message');// 如果需要传参,对应三种参数形式(接收也需要三种形式)
this.props.history.push(`/home/message/${id}`);
this.props.history.push(`/home/message?id=${id}`);
this.props.history.push('/home/message', {id});this.props.history.goBack();
this.props.history.goForward();
this.props.history.go(-2);
this.props.history.go(2);

92 withRouter 使用

如果想在普通组件中使用 history 的 API,那么需要使用 withRouter 包裹一层,这样就可以使用路由组件的 API 了。

import { withRouter } from 'raect-router-dom';class Demo extends React.Component {
}export default withRouter(Demo);

93 BrowserRouter 和 HashRouter 比较

  • 技术不一样:Browser 使用 H5 的 history API,不兼容老版本;Hash 使用 URL 哈希值,兼容 IE 6
  • Path 显示:Browser 没有#,Hash 必须加 #
  • 刷新浏览器?Browser 不会丢失 state 参数,Hash 会丢失 state 参数
  • Hash 可能解决一些路径错误的问题(例如样式引入)
  • 实际使用:BrowserRouter 使用较多

http://www.ppmy.cn/news/730918.html

相关文章

iOS 定位原理

iOS 定位原理 iOS定位有三种方式<iBeacon 不再本次研究范围内>, 手机基站定位, wifi定位, GPS(AGPS辅助定位系统, 比GPS更优), apple会根据当前信号情况和网络环境, 动态的去调整三种方法组合起来的调用次数和顺序, 这个开发者无法干预, 只能在CLLocation中直接拿到计算…

2021年最新UI/UE设计软件全家桶

2021年最新最全的UI/UE设计软件全家桶新鲜出炉了。知识体系完备&#xff0c;从小白到大神各阶段读者均能学有所获。生动形象&#xff0c;化繁为简&#xff0c;讲解通俗易懂。结合工作实践及分析应用&#xff0c;培养解决实际问题的能力。学习资源充足&#xff0c;多种资料配合后…

Vue全家桶入门精细讲解

Vue入门精细讲解 感谢coderwhy老师的精心讲解&#xff0c;本笔记全部内容源于coderwhy老师的课堂笔记&#xff1b; 一. Hello Vuejs 1.1. 认识Vuejs 为什么学习Vuejs 可能你的公司正要将原有的项目使用Vue进行重构。 也可能是你的公司新项目决定使用Vue的技术栈。 当然&…

KVO全家桶

KVO KVO(Key-Value-Observer)也就是观察者模式,是苹果提供的一套事件通知机制。允许对象监听另一个对象特定属性的改变,并在改变时接收到事件。 1、KVO使用 监听对象的某个属性- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyV…

为什么flyme不支持谷歌服务器,大家的flyme能正常使用谷歌全家桶吗

满意答案 sxkjf 2017.12.05 采纳率:56% 等级:9 已帮助:367人 从初代的Flyme 1.0,到如今的Flyme5.0,Flyme凝聚了魅族多年来对智能手机用户体验的深度发掘和在其历代操作系统上演进优化的经验和技术实力,将「化繁为简,纯简绝俗」的设计理念发挥到了极致。Flyme作为安卓…

最全总结 | 聊聊 Python 数据处理全家桶(配置篇)

1.前言 在实际项目中&#xff0c;经常会接触到各种各样的配置文件&#xff0c;它可以增强项目的可维护性 常用配件文件的处理方式&#xff0c;包含&#xff1a;JSON、ini / config、YAML、XML 等 本篇文章&#xff0c;我们将聊聊 Python 数据处理全家桶之配置文件大总结 2.…

苹果HomeKit与谷歌 Home对比:谁是最佳选择?

没有所谓最好的产品&#xff0c;只有最适合的选择&#xff01;最适合您的选择主要取决于你与家人正在使用的设备。 如果长期使用iPhone、iPad 或其他Apple 设备&#xff0c;甚至有苹果全家桶&#xff0c;那么选择HomeKit&#xff0c;因为支持HomeKit 认证的产品加上苹果设备&a…

安装vue全家桶(mac)

下载node npm -v sudo npm install webpack -g sudo npm install -g cnpm --registryhttps://registry.npm.taobao.org sudo npm install -g vue/cli vue create 1113(回车默认选择) 进入新建文件夹根目录&#xff0c;cd 1113 npm run serve