大前端- react - 基础语法

一:基础回顾

1.react是什么?

react是一个用于构建用户界面的javascript库,它只负责应用的视图层,帮助开发人员构建快速且交互式的web应用程序。
react使用组件的方式构建用户界面。

2.jsx语法

在 React 中使用 JSX 语法描述用户界面,它是一种 JavaScript 语法扩展。

在 React 代码执行之前,Babel 会将 JSX 语法转换为标准的 JavaScript API。

JSX 语法就是一种语法糖,让开发人员使用更加舒服的代码构建用户界面。

jsx 语法很像html语法,但不是html语法。

  • 2.1 在jsx中使用使用表达式:
const user = {
  firstName: 'Harper',
  lastName: 'Perez'
}
function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}
const element = <h1>Hello, {formatName(user)}!</h1>;

JSX 本身其实也是一种表达式,将它赋值给变量,当作参数传入,作为返回值都可以。

function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}
  • 2.2 属性

如果属性值为字符串类型,需要加引号,属性名称推荐采用驼峰式命名法。

const element = <div greeting="hello"></div>;

如果属性值为JavaScript表达式,属性值外面加大括号。

const element = <img src={user.avatarUrl} />;
// 注意大括号外面不能加引号,JSX 会将引号当中的内容识别为字符串而不是表达式
  • 2.3 JSX 单标记必须闭合

如果 JSX 是单标记,必须闭合,否则报错。

const element = <img src={user.avatarUrl} />
const element = <input type="text"/>
  • 2.4 className

为 JSX 标记添加类名需要使用 className,而不是class。

const element = <img src={user.avatarUrl} className="rounded"/>;
  • 2.5 JSX 自动展开数组
    可以对数组自动展开。
const ary = [<p>哈哈</p>, <p>呵呵</p>, <p>嘿嘿</p>];
const element = (
	<div>{ary}</div>
);
// 解析后
/*
	<div>
		<p>哈哈</p>
		<p>呵呵</p>
		<p>嘿嘿</p>
	</div>
*/
  • 2.6 三元运算
{ boolean ? <div>Hello React</div> : null }
{ boolean && <div>Hello React</div> }
  • 2.7 循环

key:元素可以帮助react识别哪些元素修改了,哪些元素没有修改。它可以减少dom操作,进一步做性能优化

const persons = [{
  id: 1,
  name: '张三',
  age: 20
}, {
  id: 2,
  name: '李四',
  age: 15
}, {
  id: 3,
  name: '王五',
  age: 22
}]
<ul>
  { persons.map(person => <li key={person.id}> {person.name} {person.age} </li>) }
</ul>
  • 2.8 事件
{/* 第一个参数即是事件对象 不需传递 */}
<button onClick={this.eventHandler}>按钮</button>
{/* 需要传递事件对象 */}
<button onClick={e=>this.eventHandler('arg',e)}>按钮</button>
{/* 最后一个参数即是事件对象 不需传递 */}
// .bind() 第一个参数:改变 this的指向。第二个开始:可以传递自己定义的参数。
<button onClick={this.eventHandler.bind(null, 'arg')}>按钮</button>
constructor () {
  this.eventHandler = this.eventHandler.bind(this)
}
eventHandler () {}
<button onClick={this.eventHandler}>按钮</button>

on+ 事件名称
如何绑定事件?如何进行参数传递?

  • 2.9 样式
    在react中是以组件的方式开发的。因此要考虑如何添加样式。只应用到组件本身,而不泄漏到外部。
    • 2.9.1 行内样式(遇到数值,可以不加单位,直接写数值)
class App extends Component {
  render() {
    const style = {width: 200, height: 200, backgroundColor: 'red'};
    return <div style={style}></div>
  }
}
  • 2.9.2 外链样式(使用css编写样式)
    注意文件的命名合适:import styles from './Button.module.css'
// Button.js
import styles from './Button.module.css';
class Button extends Component {
  render() {
    return <button className={styles.error}>Error Button</button>;
  }
}
  • 2.9.3 全局样式

通过import直接引入

import './styles.css'
  • 2.10 ref 属性

使用ref可以获取到这个组件/元素的实例对象。

在react中ref有3种使用方式。

  • 2.10.1 createRef
// 获取input的dom对象
class Input extends Component {
  constructor() {
    super()
    this.inputRef = React.createRef()
    // 有一个current属性,这个current属性中存放的就是当前的dom对象
    console.log(this.inputRef.current)// 输出dom对象
  }
  render() {
    return (
      <div>
        <input type="text" ref={this.inputRef} />
        <button onClick={() => console.log(this.inputRef.current)}> button </button>
      </div>
    )
  }
}
  • 2.10.2 函数参数

ref:指定为一个函数。这个函数有一个参数,这个参数就是当前元素对应的dom对象。接下来在函数体中通过this,给这个类(Input)添加一个input属性,这个属性的值就是当前元素对应的dom对象。可以通过console.log(this.input)输出dom对象。

class Input extends Component {
  render() {
    return (
      <div>
        <input type="text" ref={input => (this.input = input)} />
        <button onClick={() => console.log(this.input)}>button</button>
      </div>
    )
  }
}
  • 2.10.3 ref 字符串

不推荐使用,在严格模式下报错。

class Input extends Component {
  render() {
    return (
      <div>
        <input type="text" ref="username" />
        <button onClick={() => console.log(this.refs.username)}>button</button>
      </div>
    )
  }
}
  • 2.10.4 案例:获取组件实例

点击按钮让 input 文本框获取焦点。

input 文本框以及让文本框获取焦点的方法定义在 Input 组件中,在 App 组件中引入 Input 组件,按钮定义在 App 组件中。

// Input.js
class Input extends Component {
  constructor() {
    super()
    this.inputRef = React.createRef()
    this.focusInput = this.focusInput.bind(this)
  }
  focusInput() {
    this.inputRef.current.focus()
  }
  render() {
    return (
      <div>
        <input type="text" ref={this.inputRef} />
      </div>
    )
  }
}
// App.js
class App extends Component {
  constructor() {
    super()
    this.InputComponentRef = React.createRef()
  }
  render() {
    return (
      <div className="App">
        <Input ref={this.InputComponentRef} />
        <button onClick={() => this.InputComponentRef.current.focusInput()}>button</button>
      </div>
    )
}
3. 组件
  • 3.1 什么是组件

    React 是基于组件的方式进行用户界面开发的. 组件可以理解为对页面中某一块区域的封装。

    react中有2种类型的组件。
    在这里插入图片描述

  • 3.2 创建组件

    • 3.2.1 创建类组件
  1. App继承Component中的方法
  2. 必须有一个redner函数
  3. 在当前的组件中没有使用react,为什么要引入呢?这是因为:这个jsx代码在执行之前会被转换成React.createElement方法,那么这个代码在执行的时候一定会找React.createElement这个方法,所以在这个文件的顶部要引入react,如果不引入, 在执行的时候是找不到react的, 就会报错。
import React, { Component } from 'react';

class App extends Component {
    render () {
        return <div>Hello, 我是类组件</div>
    }
}
  • 3.2.2 创建函数组件
    普通的函数,用 return直接返回页面当中要呈现的内容
const Person = () => {
     return <div>Hello, 我是函数型组件</div>;
}

注意事项

  1. 组件名称首字母必须大写。 react用以区分当前调用的是组件还是普通html标签。
  2. jsx语法外层必须有一个根元素。因为在jsx当中规定在语法的外面必须要有一个根元素。
3.3 组件 props
3.3.1 props 传递数据
  • 在jsx中如何在组件的外部向内部传递数据?在组件中可以通过 props 对象获取外部传递进来的数据

  • props: 以属性的方式向组件的内部传递数据。

  • 案例

<Person name="乔治" age="20"/>
<Person name="玛丽" age="10"/>

类组件获取数据的的方式

// 类组件
class Person extends Component {
  render() {
    return (
      <div>
        <h3>姓名:{this.props.name}</h3>
        <h4>年龄:{this.props.age}</h4>
      </div>
    );
  }
}

函数组件获取数据的方式:以参数的方式传递。

// 函数组件
const Person = props => {
  return (
    <div>
      <h3>姓名:{props.name}</h3>
      <h4>年龄:{props.age}</h4>
    </div>
  );
}

注意:

  1. props 对象中存储的数据是只读的,不能在组件内部被修改。
  2. 当 props 数据源(传递数据的地方)中的数据被修改后,组件中的接收到的 props 数据会被同步更新,而且页面当中的数据也会跟着同步的,这就是数据驱动dom的原理。( 数据驱动DOM的原理 )
3.3.2 设置 props 默认值

在调用这个组件的时候,传递一些属性,但是这些属性是可以有设置默认值的。如何设置接收到props当中的默认值呢?
类组件设置默认值:

class App extends Component {
    static defaultProps = {}
}

函数组件设置props默认值

function ThemedButton(props) {
}
ThemedButton.defaultProps = {
  theme: "secondary",
  label: "Button Text"
};
3.3.3 组件 children
  • 通过 props.children 属性可以获取到在调用组件时填充到组件标签内部的内容。

  • 例如:在调用person组件的时候,没有添加属性,而是在组件的内部去添加了一些内容,那么一定要清楚这些内容是要显示在组件内部的。也就是说:我要在组件内部获取到这个内容。
    在组件内部我们要通过props.children来获取到这个内容。

<Person>组件内部的内容</Person>
const Person = (props) => {
    return (
    	<div>{props.children}</div>
    );
}
3.3.4 单向数据流
  1. 在React中, 关于数据流动有一条原则, 就是单向数据流动, 自顶向下, 从父组件到子组件.(不允许从子组件到父组件)

  2. 单向数据流特性要求我们共享数据要放置在上层组件中.

  3. 子组件通过调用父组件传递过来的方法更改数据.(通过props传递的数据是不能修改的,但是在某个组件内需要修改这个数据,那么怎么做呢?答:需要在顶层组件内定一个修改数据的方法,传递给子组件,子组件通过调用这个防方法,来修改数据props。)

  4. 当数据发生更改时, React会重新渲染组件树.(数据变化的时候,会同步到使用到的组件)

  5. 单向数据流使组件之间的数据流动变得可预测. 使得定位程序错误变得简单.(因为数据的流动时有机可循的,因此可以推测。)
    在这里插入图片描述

3.4 类组件状态 state
3.4.1 定义组件状态
  • 类组件除了能够从外部 (props) 接收状态数据以外还可以拥有自己的状态 (state),此状态在组件内部可以被更新,状态更新 DOM 更新。
  • 组件内部的状态数据被存储在组件类中的 state 属性中,state 属性值为对象类型,属性名称固定不可更改。
class App extends Component {
  constructor () {
    super()
    this.state = {
      person: { name: '张三', age: 20 },
    }
  }
  render () {
    return (
      <div>
        {this.state.person.name}
        {this.state.person.age}
      </div>
    );
  }
}
3.4.2 更改组件状态
  • state 状态对象中的数据不可直接更改,如果直接更改 DOM 不会被更新,要更改 state 状态数据需要使用 setState方法。
class App extends Component {
  constructor () {
    this.state = {
      person: { name: '张三', age: 20 },
    }
    this.changePerson = this.changePerson.bind(this)
  }
	changePerson () {
    this.setState({
      person: {
        name: '李四',
        age: 15
      }
    })
  }
  render() {
    return (
      <div>
        {this.state.person.name}
        {this.state.person.age}
        <button onClick={this.changePerson}>按钮</button>
      </div>
    );
  }
}
3.4.3 双向数据绑定
  • 双向数据绑定是指,组件类中更新了状态,DOM 状态同步更新,DOM 更改了状态,组件类中同步更新。组件 <=> 视图。

  • 双向指的是:1.组件类,2.页面中的dom元素。

  • 双向数据绑定指的是:如果组件类当中修改了状态,那么dom也会同步更新状态。如果dom更新了状态,组件类中储存的状态也会同步更新。
    组件类中的状态会影响视图,视图当中把状态更改了,它也会影响到组件类。这就是双向数据绑定。

  • 要实现双向数据绑定需要用到表单元素和 state 状态对象。
    因为在dom元素中只有表单元素的它才可以更改这个数据。

class App extends Component {
  constructor () {
    this.state = {
      name: "张三"
    }
    this.nameChanged = this.nameChanged.bind(this)
  }
  nameChanged (event) {
    this.setState({name: event.target.value});
  }
  render() {
    return (
      <div>
        <div>{this.state.name}</div>
        <Person name={this.state.name} changed={this.nameChanged}/>
      </div>
    )
  }
}
const Person = props => {
	return <input type="text" value={props.name} onChange={props.changed}/>;
}
3.5 类组件生命周期函数

在这里插入图片描述

  • 什么生命周期函数?当程序运行到某个阶段会自动执行的函数,就是生命周期函数。可以利用生命周期函数,在程序运行到某一时刻,去做一些他想要做的事情。
  • 组件的生命周期分为3个阶段:1.挂载阶段,2.组件的数据更新阶段 3.组件的卸载阶段
  1. 挂载阶段

    当一个新的组件被创建的时候,首先会执行constructor函数,可以在constructor中初始化状态对象,改变函数内部的this指向。当constructor这个构造函数执行完成之后,就去执行getDerivedStateFromProps周期函数,getDerivedStateFromProps是什么意思呢?当前组件的状态如果取决于父组件的状态,那么这个时候就可以使用到getDerivedStateFromProps生命周期函数,在getDerivedStateFromProps中有2个参数,一个是;通过props接收到的父组件传递过来的state,另一个是当前这个组件的state,我们可以通过这2个参数,来决定当前要不要更新当前组件的state,如果不需要更新就返回null,如果需要更新,返回全新的状态对象,但是一定要返回,不返回时是不被允许的。当这个生命周期函数执行完成之后,会去执行redner方法,在render方法中会挂载dom对象,当render方法执行完成之后,会去执行componentDidMount这个周期函数,预示着当前的组件已经挂载完成。

在挂载阶段需要注意:1.在constructor函数中不要引起一些副作用,例如:发起请求,获取数据,这样的事情放在componentDidMount函数中执行

  • 2.组件的更新阶段

当组件当中的数据发生更新之后,首先会执行getDerivedStateFromProps,查看状态有没有必要发生更新,替换了之前的compoentwillreciveprops周期函数,当getDerivedStateFromProps运行完成之后,调用shouldComponentUpdate函数,在这个生命周期函数中返回true/false,如果返回了true,继续走下面的周期函数,如果返回false,那就就停止更新组件。如果返回了true,
接下来重新运行render函数重新渲染这个组件,当render方法执行完之后,会去调用getSnapshotBeforeUpdates周期函数,这个用的较少。

getSnapshotBeforeUpdate是什么意思?getSnapshotBeforeUpdate在render方法执行之后去执行,在这个方法中可以执行某种逻辑或计算,这个方法最终要返回snapshot(快照),这个返回值会传递到可以componentDidUpdate,在componentDidUpdate方法中的第三个参数中获取。这2个生命周期是配合使用的。如果只定义了getSnapshotBeforeUpdate这个函数,而没有定义componentDidUpdate程序会报错。

getSnapshotBeforeUpdate这个函数用于组件更新之前完成一些计算逻辑。然后把结果返回给componentDidUpdate。在componentDidUpdate我们可以处理结果。

在组件完成更新之前需要做某种逻辑或者计算,就需要用到快照。

componentDidUpdate(prevProps, prevState, snapshot) {}

getSnapshotBeforeUpdate 方法会在组件完成更新之前执行,用于执行某种逻辑或计算,返回值可以在 componentDidUpdate 方法中的第三个参数中获取,就是说在组件更新之后可以拿到这个值再去做其他事情。

getSnapshotBeforeUpdate(prevProps, prevState) {
  return 'snapshot'
}
  • 3.组件的卸载阶段
    在组件卸载之前,会调用componentWiiUnmount这个周期函数,预示着组件将要被卸载。
    在这个周期函数中做一些清理工作。例如:清理通过ref绑定的dom对象,清理定时器…
3.6 Context
  • 通过 Context 可以跨层级传递数据
  • Context:的作用是什么?
    组件的pros是从上到下依次传递,如果组件层级比较深,那就比较麻烦,因为中间的其他组件没有用到数据的也要传递数据,这个时候,就要用Context,跨层级传递数据。
  • 如下图:
    A,D,F要用到根组件的username,如果用props,就是根组件->b, 根组件->c->e-> f. 这个过程太复杂,而且这个过程中b,c,e根本就没有用到username,但是参与了数据的传递,这样不好。
    那么这个时候用Context跨级传递就再好不过了。a, d, f可以直接拿到数据,不用经过中间的组件,忽略中间的b,c,e组件。
    在这里插入图片描述
// userContext.js
import React from "react"
// 创建上下文对象
const userContext = React.createContext("default value")
// Provider: 用来向下传递数据
// Consumer: 用来从上面取数据
const UserProvider = userContext.Provider
const UserConsumer = userContext.Consumer

export { UserProvider, UserConsumer }

根组件

// App.js
import { UserProvider } from "./userContext"
class App extends Component {
  render() {
    return (
      <UserProvider value="Hello React Context">
        <A />
      </UserProvider>
    )
  }
}

需要用到数据的地方(a,d,f直接用到数据的地方)

// C.js
import { UserConsumer } from "./userContext"

export class C extends Component {
  render() {
    return (
      <div>
        <UserConsumer>
          {username => {
            return <div>{username}</div>
          }}
        </UserConsumer>
      </div>
    )
  }
}

context 的另一种用法

// userContext.js
export default userContext
// C.js
import userContext from "./userContext"

export class C extends Component {
  static contextType = userContext
  render() {
    return (
      <div>
        {this.context}
      </div>
    )
  }
}

4. 表单

4.1 受控表单

表单控件中的值由组件的 state 对象来管理,state对象中存储的值和表单控件中的值时同步状态的。
这个同步状态是如和实现的?是通过数据的双向绑定来实现的。

class App extends Component {
  constructor () {
    this.state = { username: "" }
    this.nameChanged = this.nameChanged.bind(this)
  }
  
  nameChanged (e) {
    this.setState({username: e.target.value})
  }
  render() {
    return (
      <form>
        <p>{this.state.username}</p>
        <input type="text" value={this.state.username} onChange={this.nameChanged}/>
      </form>
    )
  }
}
4.2 非受控表单

表单元素的值由 DOM 元素本身管理。

class App extends Component {
  constructor () {
    this.onSubmit = this.onSubmit.bind(this)
  }
  onSubmit(e) {
    console.log(this.username.value)
    e.preventDefault();
  }
  render(
    <form onSubmit={this.onSubmit}>
      <input type="text" ref={username => this.username = username}/>
    </form>
  )
}

5. 路由

什么是路由?
路由就是:url地址与组件之间的对应关系,访问不同的url地址显示不同的组件。

下载:npm install react-router-dom

5.1.1 路由基本使用
// App.js

//BrowserRouter:表示浏览器路由。Router放在最外层,这是规则,要求。
//Route:设置,匹配路由规则。path:资格组件的地址。component: 组件。
// Link: 设置链接用的。to属性:就是链接地址。
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
function Index() {
	return <div>首页</div>;
}
function News() {
	return <div>新闻</div>;
}
function App() {
  return (
    <Router>
      <div>
        <Link to="/index">首页</Link>
        <Link to="/news">新闻</Link>
      </div>
      <div>
        <Route path="/index" component={Index}/>
        <Route path="/news" component={News}/>
      </div>
    </Router>
  );
}
5.1.2 路由嵌套

路由嵌套什么时候使用?
在页面中出现2级链接,3级链接,这个时候需要使用路由前套。

${props.match.url}: 上一级的访问地址
${props.match.url}/company: 当前的访问地址

props.match.url: 获取link里面的to地址

props.match.path: 获取到的是Route里面的path

function News(props) {
  return (
    <div>
      <div>
        <Link to={`${props.match.url}/company`}>公司新闻</Link>
        <Link to={`${props.match.url}/industry`}>行业新闻</Link>
      </div>
      <div>
        <Route path={`${props.match.path}/company`} component={CompanyNews} />
        <Route path={`${props.match.path}/industry`} component={IndustryNews}/>  
      </div>	
    </div>
  );
}

function CompanyNews() {
	return <div>公司新闻</div>
}
function IndustryNews() {
	return <div>行业新闻</div>
}
5.1.3 路由传参

url: 用来解析地址的字符串,转成对象格式的数据。

import url from 'url';
class News extends Component {
  constructor(props) {
    super(props);
    this.state = {
      list: [{
        id: 1,
        title: '新闻1'
      }, {
        id: 2,
        title: '新闻2'
      }]
    }
  }
    
  render() {
    return (
      <div>
        <div>新闻列表组件</div>
        <ul>
          this.state.list.map((item, index) => {
            return (
              <li key={index}>
                <Link to={`/detail?id=${item.id}`}>{item.title}</Link>
              </li>
            );
          })
        </ul>
      </div>
    );
  }
}
class Detail extends Component {
  constructor(props) {
    super(props);
  }
	const { query } = url.parse(this.props.location.search, true);
	console.log(query); // {id: 1}
  render() {
    return <div>新闻详情</div>
  }
}
5.1.4 路由重定向
import { Redirect } from 'react-router-dom';

class Login extends Component {
  render() {
    if (this.state.isLogin) {
      return <Redirect to="/"/>
    }
  }
}
  • class 声明的类组件中 render 函数的 this 指向组件实例对象
  • function 定义的函数组件中的 this 是undefined
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值