首页 > 其他分享 >React

React

时间:2022-11-11 10:13:35浏览次数:48  
标签:react DOM React props 组件 路由

基础

React是用于构建用户的JS库(Facebook开源)

使用JS进行操作DOM会进行大量的重绘重排列

React采用组件化模式、声明式编码提高开发效率及组件复用率

React使用虚拟DOM和Diffing算法,减少与真实DOM的交互

Diffing算法:当浏览器进行更新时会和旧虚拟Dom进行匹配,当匹配到相同的虚拟Dom时不进行更新,只更新匹配不到(新的)的虚拟Dom

Recact使用jsx语法声明元素

基本的React使用(后期可用React脚手架进行项目搭建,无需引入这些样式和声明)

1、创建一个容器,并引入react相关库文件

<div id="test"></div>
<!-- 引入react核心库 -->
<script src="./js/react.development.js"></script>
<!-- 引入react-dom操作dom -->
<script src="./js/react-dom.development.js"></script>
<!-- 引入babel,将jsx转换为js -->
<script src="./js/babel.min.js"></script>
<script type="text/babel">//此处一定要写babel,因为是jsx语法
  // h1标签不能写引号,因为不是字符串,创建虚拟dom
    const VDOM = <h1>Hello,React</h1>
    ReactDOM.render(VDOM,document.getElementById('test'))
</script>

虚拟DOM

虚拟dom本质是一个object对象(一般对象)

虚拟DOM比较“轻”,真实dom比较“重”,因为虚拟dom是react内部在使用,无需真实DOM那么多属性,但虚拟DOM会被React使用Diffing算法转换为真实DOM

虚拟DOM中key的作用

1) .简单的说:key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用。

2) .详细的说:当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,

随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下

a. 旧虚拟DOM中找到与新虚拟DOM相同的key:

(1) .若虚拟DOM中内容没变,直接使用之前的真实DOM

(2) .若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM

b. 旧虚拟DOM中未找到与新虚拟DOM相同的key

根据数据创建新的真实DOM,随后渲染到到页面

2.用ndex作为key可能会引发的问题:

1.若对数据进行:逆力添加、逆序删除等破坏顺序操作:

会产生没有必要的真实DOM更新==> 界而效果没问题,但效率低。

2 .如果结构中还包含输入类的DOM:

会产生错误DOM更新==> 界面有问题。

3 .注意:如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,

仅用于演染列表用f展示,使用index作为key是没有问题的。

3.开发中如何选择key?

1 .最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。

2 .如果确定只是简单的展示数据,用index也是可以的。

JSX

全称JavaScript XML,本质上React的语法糖React.createElement(component, props, ...children),主要是用来简化创建虚拟DOM

可以使用大括号引入js表达式(js语句和js表达式不同,一个表达式会产生一个值,可以使用变量接收到,比如a,a+b,function,一个语句则是控制代码走向的,如if、for、swich)

如果需要指定样式要使用className

如果要使用内联样式要使用style={{key:value}}

const name = 'React'

<h1>你好,我叫{name}</h1>   //    <h1>你好,我叫React</h1>

渲染List列表

const software = [
  { id: 1, name: '淘宝' },
  { id: 2, name: '京东' },
  { id: 3, name: '拼多多' }
]

function App() {
  return (
    <div className="App">
      <ul>
        {
          software.map(item => <li>{item.name}</li>)
        }
      </ul>
    </div>
  )
}

export default Appt

条件渲染

const flag = true
function App() {
  return (
    <div className="App">
      {flag ? '条件为真' : '条件为假}
    </div>
  )
}
export default App

脚手架

脚手架是用来帮助程序员快速创建一个基于xxx库的模板项目

react提供了一个用于创建react项目的脚手架库: create-react-app

使用脚手架开发的项目的特点: 模块化, 组件化, 工程化

创建项目(npm)

第一步,全局安装:npm i -g create-react-app

第二步,切换到想创项目的目录,使用命令:create-react-app hello-react

第三步,进入项目文件夹:cd hello-react

第四步,启动项目:npm start/yarn start

创建项目(npx)

第一步,npx create-react-app 是固定命令,create-react-app是React脚手架的名称

第二步,react-basic表示项目名称,可以自定义,保持语义化

第三步,npx 命令会帮助我们临时安装create-react-app包,然后初始化项目完成之后会自自动删掉,所以不需要全局安装create-react-app

组件

React内的组件分为函数式组件和类式组件

函数式组件

1、组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签

2、函数组件必须有返回值,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null

3、组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容

4、使用函数名称作为组件标签名称,可以成对出现也可以自闭合

// 定义函数组件
function HelloFn () {
  return <div>这是我的第一个函数组件!</div>
}

// 定义类组件
function App () {
  return (
    <div className="App">
      {/* 渲染函数组件 可以使用自闭合标签渲染 */}
      <HelloFn />
      <HelloFn></HelloFn>
    </div>
  )
}
export default App

类的基本知识(类组件的前置知识)

class Person{
    //构造器方法
    constructor(name,age){
    //此处的this指向类的实例对象
        this.name=name
        this.age=age
    }
    //自定义的一般方法
    //方法在类的原型对象上,供实例使用
    speak(){
        console.log(`我叫${this.name}`)
    }
}
const p1 = new Person('tom',18)
console.log(p1)
//创建一个student继承自person
class Student extends Person{
//子类如果是继承自父类,那子类的构造器需要用super
    constructor(name,age,grade){
    //用super调用父类的属性
        super(name,age)
        this.grade=grade
    }
    //重写父类的方法
    //方法在类的原型对象上,供实例使用
    speak(){
        console.log(`我叫${this.name},我在${this.grade}`)
        }
}
const s1 = new Stuent('xiao',20,'高一')
console.log(s1)
子类可以调用父类的方法,也可以重写
s1.speak()
  1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,比如添加需要指定属性时

  2. 如果A类继承了B类,且A类中写了构造器,那么A类中的super必须要调用

  3. 类中的所有方法都是放在原型对象上的,供实例使用

类式组件

1、类名称也必须以大写字母开头

2、类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性

3、类组件必须提供 render 方法render 方法必须有返回值,表示该组件的 UI 结构

// 引入React
import React from 'react'

// 定义类组件
class HelloC extends React.Component {
  render () {
    return <div>这是我的第一个类组件!</div>
  }
}

function App () {
  return (
    <div className="App">
      {/* 渲染类组件 */}
      <HelloC />
      <HelloC></HelloC>
    </div>
  )
}
export default App

组件状态

React hook出来之前,函数式组件是没有自己的状态的

类式组件的三大属性--state

通过更新组件的状态来更新页面

class Counter extends React.Component {
  // 初始化状态
  state = {
    count: 0
  }
  render() {
    // 读取状态
    return <button>计数器{this.state.count}</button>
  }
}

注意:state的状态不能直接更改,需要借助API-setState修改

class Counter extends React.Component {
  // 定义数据
  state = {
    count: 0
  }
  // 定义修改数据的方法
  setCount = () => {
    this.setState({
      count: this.state.count + 1
    })
  }
  // 使用数据 并绑定事件
  render () {
    return <button onClick={this.setCount}>{this.state.count}</button>
  }
}

类式组件的三大属性--props(父子传值)

props主要通过标签属性从组件外向组件内传递数据

props是只读的,不能直接修改

父传子

import React from 'react'

// 函数式子组件
function FSon(props) {
  console.log(props)
  return (
    <div>
      子组件1
      {props.msg}
    </div>
  )
}

// 类子组件
class CSon extends React.Component {
  render() {
    return (
      <div>
        子组件2
        {this.props.msg}
      </div>
    )
  }
}
// 父组件
class App extends React.Component {
  state = {
    message: 'this is message'
  }
  render() {
    return (
      <div>
        <div>父组件</div>
        <FSon msg={this.state.message} />
        <CSon msg={this.state.message} />
      </div>
    )
  }
}

export default App

子传父

import React from 'react'

// 子组件
function Son(props) {
  function handleClick() {
    // 调用父组件传递过来的回调函数 并注入参数
    props.changeMsg('this is newMessage')
  }
  return (
    <div>
      {props.msg}
      <button onClick={handleClick}>change</button>
    </div>
  )
}


class App extends React.Component {
  state = {
    message: 'this is message'
  }
  // 提供回调函数
  changeMessage = (newMsg) => {
    console.log('子组件传过来的数据:',newMsg)
    this.setState({
      message: newMsg
    })
  }
  render() {
    return (
      <div>
        <div>父组件</div>
        <Son
          msg={this.state.message}
          // 传递给子组件
          changeMsg={this.changeMessage}
        />
      </div>
    )
  }
}

export default App

使用propTypes可以对属性进行规则限制

import PropTypes from 'prop-types'

const List = props => {
  const arr = props.colors
  const lis = arr.map((item, index) => <li key={index}>{item.name}</li>)
  return <ul>{lis}</ul>
}

List.propTypes = {
  colors: PropTypes.array
}

props和state的区别

props(“properties” 的缩写)和 state 都是普通的 JavaScript 对象。

它们都是用来保存信息的,这些信息可以控制组件的渲染输出。

它们的一个重要的不同点就是:props 是传递给组件的(类似于函数的形参),而 state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)。

类式组件的三大属性--refs

使用React处理表单元素,一般有两种方式:

  1. 受控组件 (推荐使用)
  2. 非受控组件 (了解)

受控组件

import React from 'react'

class InputComponent extends React.Component {
  // 声明组件状态
  state = {
    message: 'this is message',
  }
  // 声明事件回调函数
  changeHandler = (e) => {
    this.setState({ message: e.target.value })
  }
  render () {
    return (
      <div>
        {/* 绑定value 绑定事件*/}
        <input value={this.state.message} onChange={this.changeHandler} />
      </div>
    )
  }
}


function App () {
  return (
    <div className="App">
      <InputComponent />
    </div>
  )
}
export default Appf

非受控组件 -- 通过ref获取dom的值

import React, { createRef } from 'react'

class InputComponent extends React.Component {
  // 使用createRef产生一个存放dom的对象容器
  msgRef = createRef()

  changeHandler = () => {
    console.log(this.msgRef.current.value)
  }

  render() {
    return (
      <div>
        {/* ref绑定 获取真实dom */}
        <input ref={this.msgRef} />
        <button onClick={this.changeHandler}>click</button>
      </div>
    )
  }
}

function App () {
  return (
    <div className="App">
      <InputComponent />
    </div>
  )
}
export default App
ref通过手动操作dom的方式获取文本框的值,文本框的状态不受react组件的state中的状态控制,直接通过原生dom获取输入框的值

不要过度使用ref,如果发生事件的元素是触发事件的元素,即可以使用event.target操作元素

比如是失去焦点获取输入框的值,通过event.target可以得到发生事件的DOM元素对象

生命周期

生命周期的三个阶段(旧)-- 了解即可

初始化阶段由ReactDOM.render()触发---初次渲染

  1. constructor()

  2. componentWillMount() // 不常用

  3. render()

  4. componentDidMount()

更新阶段由组件内部this.setSate()或父组件重新render触发

  1. shouldComponentUpdate() // 不常用

  2. componentWillUpdate() // 不常用

  3. render()

  4. componentDidUpdate()

卸载组件 由ReactDOM.unmountComponentAtNode()触发

  1. componentWillUnmount()

生命周期的三个阶段(新)

初始化阶段: 由ReactDOM.render()触发---初次渲染

1.constructor()

2.getDerivedStateFromProps() // 允许组件从props获取参数并给state赋值

3.render() // 初始化会调用且每次更新都会调用

4.componentDidMount() // 通常用来开启定时器、发送ajax请求,订阅消息等

更新阶段: 由组件内部this.setSate()或父组件重新render触发

1.getDerivedStateFromProps() 

2.shouldComponentUpdate() // 不常用

3.render() // 每次更新都会调用

4.getSnapshotBeforeUpdate() // 获取更新前的属性

5.componentDidUpdate()

卸载组件: 由ReactDOM.unmountComponentAtNode()触发

1.componentWillUnmount() // 通常用来来关闭定时器、取消订阅

即将废弃的钩子

1.componentWillMount

2.componentWillReceiveProps

3.componentWillUpdate

 

Hooks基础

 

ajax

ajax跨域代理

方法一

在package.json中追加如下配置

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

说明:

  1. 优点:配置简单,前端请求资源时可以不加任何前缀。

  2. 缺点:不能配置多个代理。

  3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)

方法二

第一步:创建代理配置文件


在src下创建配置文件:src/setupProxy.js
编写setupProxy.js配置具体代理规则:

const proxy = require('http-proxy-middleware')
​
module.exports = function(app) {
  app.use(
    proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
      target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      /*
        changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
        changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
        changeOrigin默认值为false,但我们一般将changeOrigin值设为true
      */
      pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置),将api1替换为空
    }),
    proxy('/api2', { 
      target: 'http://localhost:5001',
      changeOrigin: true,
      pathRewrite: {'^/api2': ''}
    })
  )
}

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。

  2. 缺点:配置繁琐,前端请求资源时必须加前缀。

fetch-除xhr外的发送ajax请求的方式

jquery和axios都是基于xhr(XMLHttpRequest)发送ajax请求

fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求

注意:老版本浏览器可能不支持

Get请求:

fetch(url).then(function(response) {
    return response.json()
  }).then(function(data) {
    console.log(data)
  }).catch(function(e) {
    console.log(e)
  });

Post请求

fetch(url, {
    method: "POST",
    body: JSON.stringify(data),
  }).then(function(data) {
    console.log(data)
  }).catch(function(e) {
    console.log(e)
  })

路由

基础

一个路由就是一个映射关系(key:value)

key为路径, value可能是function(后端路由,当前端请求时返回函数调用的结果)或component(前端路由)

后端路由

1) 理解: value是function, 用来处理客户端提交的请求。

2) 注册路由: router.get(path, function(req, res))

3) 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据

前端路由

1) 浏览器端路由,value是component,用于展示页面内容。

2) 注册路由: <Route path="/test" component={Test}>

3) 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件

React路由

插件库:react-router-dom

Link

导航区的a标签改为Link标签

<Link to="/home">Home</Link>

Route

展示区的Route进行路径与组件匹配

<Route path='/home' component={Home}/>

BrowserRouter和HashRouter

在index的APP外包裹<BrowserRouter><HashRouter>

区别

1.底层原理不一样:

BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。

HashRouter使用的是URL的哈希值。

2.path表现形式不一样

BrowserRouter的路径中没有#,例如:localhost:3000/demo/test

HashRouter的路径包含#,例如:localhost:3000/#/demo/test

3.刷新后对路由state参数的影响

(1).BrowserRouter没有任何影响,因为state保存在history对象中。

(2).HashRouter刷新后会导致路由state参数的丢失!!!

4.备注:HashRouter可以用于解决一些路径错误相关的问题。

NavLink

NavLink是Link标签的升级版,如果被点击时想添加属性可以使用activeClassName="属性名"

<NavLink activeClassName="backColor" to="/about">About</NavLink>

对NavLink进行封装,标签体的内容会通过props属性的children传递给子组件,所以可以直接结构赋值出所有的props

import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
​
export default class index extends Component {
  render() {
    // 封装NavLink,backColor是自定义的选中样式
    return (
      <NavLink activeClassName='backColor' {...this.props} />)
  }
}

Swich

Swich标签可以进行单一匹配,如果两个路由的地址相同但组件不同,默认会展示两个组件

使用swich可以避免展示两个组件,匹配到第一个就不继续进行匹配

Redirect

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

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

路由样式丢失

在public的index.html中可能会引入外部样式,因为路由的原因导致样式丢失

解决方法:

1.public/index.html 中 引入样式时不写 ./ 写 / (常用)

2.public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)

3.使用HashRouter代替BrowserRouter(少用)

路由模糊匹配与精准匹配

路由的匹配规则默认为模糊匹配

开启精准匹配<Route exact={true} path="/about" component={About}/>

可以简写<Route exact path="/about" component={About}/>

注意:严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

嵌套多级路由

多级路由需要加上父路由的path值

路由传参

1.params参数

路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>

注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>

接收参数:this.props.match.params

2.search参数

路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>

注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

接收参数:this.props.location.search

备注:获取到的search是urlencoded编码字符串,需要借助querystring的parse方法解析

3.state参数

路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>

注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

接收参数:this.props.location.state

备注:刷新也可以保留住参数

编程式路由导航

可以在不借助Link和NavLink的情况下进行导航的跳转

借助this.prosp.history对象上的API对操作路由跳转、前进、后退

this.prosp.history.push(要去往的路由)

this.prosp.history.replace(要取代的路由,不留下历史记录)

this.prosp.history.goBack()后退一步

this.prosp.history.goForward()前进一步

this.prosp.history.go(n)后退或前进n步,n为负值是后退

withRouter

路由组件有自带的三个props属性,而如果是自己用标签注册的组件则没有props属性

  1. history

  2. location

  3. match

如果一般组件也想使用路由组件的特有API可以使用withRouter

导入withRouter,并在导出组件时使用withRouter(组件名)方法导出,返回一个新组件

import {withRouter} from 'react-router-dom'
export default withRouter(组件名)

标签:react,DOM,React,props,组件,路由
From: https://www.cnblogs.com/JC30705/p/16879664.html

相关文章

  • React支持less操作
    React支持less操作1、执行暴漏命令npmruneject2、输入"Y"确认这时候发现config文件夹没暴漏出来,是因为git没有暂存,得执行如下命令:Gitadd.3、接着执......
  • React / Vue 实现 Excel 数据导入
    React1.Excel数据格式化主要是三个步骤:使用input/antd-upload组件上传需要导入的文件——将excel数据处理为json数据——将json数据处理成antd-table组件所需的数据(主要......
  • 一文读透react精髓
    学和使用react有一年多了,最近想在梳理一下react基础知识,夯实基础,激流勇进~关于reacr-router,redux,redux-saga后续都会慢慢输出,希望各位看官老爷持续关注~~要是能给个赞......
  • 从实现一个React到深度理解React框架核心原理
    前言这篇文章循序渐进地介绍实现以下几个概念,遵循本篇文章基本就能搞懂为啥需要fiber,为啥需要commit和phases、reconciliation阶段等原理。本篇文章又不完全和原文一致,这......
  • react高频面试题总结(附答案)
    hooks为什么不能放在条件判断里以setState为例,在react内部,每个组件(Fiber)的hooks都是以链表的形式存在memoizeState属性中update阶段,每次调用setState,链表......
  • 老生常谈React的diff算法原理-面试版
    第一次发文章notonly(虽然)版式可能有点烂butalso(但是)最后赋有手稿研究finally看完他你有收获diff算法:对于update的组件,他会将当前组件与该组件在上次更新是对应的......
  • 一天梳理完react面试高频知识点
    描述事件在React中的处理方式。为了解决跨浏览器兼容性问题,React中的事件处理程序将传递SyntheticEvent的实例,它是跨浏览器事件的包装器。这些SyntheticEvent与你习惯......
  • 第四章、react高级
    目录九、高阶组件和组件补充1、认识高阶组件2、高阶组件应用-增强props3、高阶组件应用-登录鉴权操作4、高阶组件应用-生命周期劫持5、ref的转发6、Portals的使用九、高阶......
  • React组件基础三
    一.props用法组件是封闭的,要接收外部数据应该通过props来实现props的作用:接收传递给组件的数据传递数据:给组件标签添加属性接收数据:函数组件通过参数props接收......
  • react在create-react-app中使用hooks常见注意事项
    useState它的作用就是声明状态变量useState注意点:初始参数只在组件的首次渲染的时候使用,再次更新是会被忽略每次通过setXxx修改状态都会引起组件重新渲染useS......