1. React 介紹
<img src="./images/1.png" width="70%"/>
React 是一個(gè)用于構(gòu)建用戶界面的 JavaScript 庫,它只負(fù)責(zé)應(yīng)用的視圖層,幫助開發(fā)人員構(gòu)建快速且交互式的 web 應(yīng)用程序。
React 使用組件的方式構(gòu)建用戶界面。
2. JSX 語法
在 React 中使用 JSX 語法描述用戶界面,它是一種 JavaScript 語法擴(kuò)展。
在 React 代碼執(zhí)行之前,Babel 會(huì)將 JSX 語法轉(zhuǎn)換為標(biāo)準(zhǔn)的 JavaScript API。
JSX 語法就是一種語法糖,讓開發(fā)人員使用更加舒服的代碼構(gòu)建用戶界面。
2.1 在 JSX 中使用表達(dá)式
const user = {
firstName: 'Harper',
lastName: 'Perez'
}
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const element = <h1>Hello, {formatName(user)}!</h1>;
JSX 本身其實(shí)也是一種表達(dá)式,將它賦值給變量,當(dāng)作參數(shù)傳入,作為返回值都可以。
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
2.2 屬性
如果屬性值為字符串類型,需要加引號(hào),屬性名稱推薦采用駝峰式命名法。
const element = <div greeting="hello"></div>;
如果屬性值為JavaScript表達(dá)式,屬性值外面加大括號(hào)。
const element = <img src={user.avatarUrl} />;
// 注意大括號(hào)外面不能加引號(hào),JSX 會(huì)將引號(hào)當(dāng)中的內(nèi)容識(shí)別為字符串而不是表達(dá)式
2.3 JSX 單標(biāo)記必須閉合
如果 JSX 是單標(biāo)記,必須閉合,否則報(bào)錯(cuò)。
const element = <img src={user.avatarUrl} />
const element = <input type="text"/>
2.4 className
為 JSX 標(biāo)記添加類名需要使用 className,而不是class。
const element = <img src={user.avatarUrl} className="rounded"/>;
2.5 JSX 自動(dòng)展開數(shù)組
const ary = [<p>哈哈</p>, <p>呵呵</p>, <p>嘿嘿</p>];
const element = (
<div>{ary}</div>
);
// 解析后
/*
<div>
<p>哈哈</p>
<p>呵呵</p>
<p>嘿嘿</p>
</div>
*/
2.6 三元運(yùn)算
{ boolean ? <div>Hello React</div> : null }
{ boolean && <div>Hello React</div> }
2.7 循環(huán)
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 事件
{/* 第一個(gè)參數(shù)即是事件對象 不需傳遞 */}
<button onClick={this.eventHandler}>按鈕</button>
{/* 需要傳遞事件對象 */}
<button onClick={e=>this.eventHandler('arg',e)}>按鈕</button>
{/* 最后一個(gè)參數(shù)即是事件對象 不需傳遞 */}
<button onClick={this.eventHandler.bind(null, 'arg')}>按鈕</button>
constructor () {
this.eventHandler = this.eventHandler.bind(this)
}
eventHandler () {}
<button onClick={this.eventHandler}>按鈕</button>
2.9 樣式
2.9.1 行內(nèi)樣式
class App extends Component {
render() {
const style = {width: 200, height: 200, backgroundColor: 'red'};
return <div style={style}></div>
}
}
2.9.2 外鏈樣式
// 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 './styles.css'
2.10 ref 屬性
2.10.1 createRef
class Input extends Component {
constructor() {
super()
this.inputRef = React.createRef()
}
render() {
return (
<div>
<input type="text" ref={this.inputRef} />
<button onClick={() => console.log(this.inputRef.current)}> button </button>
</div>
)
}
}
2.10.2 函數(shù)參數(shù)
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 字符串
不推薦使用,在嚴(yán)格模式下報(bào)錯(cuò)。
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 獲取組件實(shí)例
點(diǎn)擊按鈕讓 input 文本框獲取焦點(diǎn)。
input 文本框以及讓文本框獲取焦點(diǎn)的方法定義在 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>
)
}
<img src="./images/5.gif" />
3. 組件
3.1 什么是組件
React 是基于組件的方式進(jìn)行用戶界面開發(fā)的. 組件可以理解為對頁面中某一塊區(qū)域的封裝。
<img src="./images/2.png" width="70%"/>
3.2 創(chuàng)建組件
3.2.1 創(chuàng)建類組件
import React, { Component } from 'react';
class App extends Component {
render () {
return <div>Hello, 我是類組件</div>
}
}
3.2.2 創(chuàng)建函數(shù)組件
const Person = () => {
return <div>Hello, 我是函數(shù)型組件</div>;
}
注意事項(xiàng)
- 組件名稱首字母必須大寫,用以區(qū)分組件和普通標(biāo)簽。
- jsx語法外層必須有一個(gè)根元素
3.3 組件 props
3.3.1 props 傳遞數(shù)據(jù)
在調(diào)用組件時(shí)可以向組件內(nèi)部傳遞數(shù)據(jù),在組件中可以通過 props 對象獲取外部傳遞進(jìn)來的數(shù)據(jù)。
<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>
);
}
}
// 函數(shù)組件
const Person = props => {
return (
<div>
<h3>姓名:{props.name}</h3>
<h4>年齡:{props.age}</h4>
</div>
);
}
注意:
- props 對象中存儲(chǔ)的數(shù)據(jù)是只讀的,不能在組件內(nèi)部被修改。
- 當(dāng) props 數(shù)據(jù)源中的數(shù)據(jù)被修改后,組件中的接收到的 props 數(shù)據(jù)會(huì)被同步更新。( 數(shù)據(jù)驅(qū)動(dòng)DOM )
3.3.2 設(shè)置 props 默認(rèn)值
class App extends Component {
static defaultProps = {}
}
function ThemedButton(props) {
}
ThemedButton.defaultProps = {
theme: "secondary",
label: "Button Text"
};
3.3.3 組件 children
通過 props.children 屬性可以獲取到在調(diào)用組件時(shí)填充到組件標(biāo)簽內(nèi)部的內(nèi)容。
<Person>組件內(nèi)部的內(nèi)容</Person>
const Person = (props) => {
return (
<div>{props.children}</div>
);
}
3.3.4 單向數(shù)據(jù)流
在React中, 關(guān)于數(shù)據(jù)流動(dòng)有一條原則, 就是單向數(shù)據(jù)流動(dòng), 自頂向下, 從父組件到子組件.
單向數(shù)據(jù)流特性要求我們共享數(shù)據(jù)要放置在上層組件中.
子組件通過調(diào)用父組件傳遞過來的方法更改數(shù)據(jù).
當(dāng)數(shù)據(jù)發(fā)生更改時(shí), React會(huì)重新渲染組件樹.
單向數(shù)據(jù)流使組件之間的數(shù)據(jù)流動(dòng)變得可預(yù)測. 使得定位程序錯(cuò)誤變得簡單.
<img src="./images/3.png" />
3.4 類組件狀態(tài) state
3.4.1 定義組件狀態(tài)
類組件除了能夠從外部 (props) 接收狀態(tài)數(shù)據(jù)以外還可以擁有自己的狀態(tài) (state),此狀態(tài)在組件內(nèi)部可以被更新,狀態(tài)更新 DOM 更新。
組件內(nèi)部的狀態(tài)數(shù)據(jù)被存儲(chǔ)在組件類中的 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 更改組件狀態(tài)
state 狀態(tài)對象中的數(shù)據(jù)不可直接更改,如果直接更改 DOM 不會(huì)被更新,要更改 state 狀態(tài)數(shù)據(jù)需要使用 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 雙向數(shù)據(jù)綁定
雙向數(shù)據(jù)綁定是指,組件類中更新了狀態(tài),DOM 狀態(tài)同步更新,DOM 更改了狀態(tài),組件類中同步更新。組件 <=> 視圖。
要實(shí)現(xiàn)雙向數(shù)據(jù)綁定需要用到表單元素和 state 狀態(tài)對象。
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 類組件生命周期函數(shù)
<img src="./images/4.jpg"/>
在組件完成更新之前需要做某種邏輯或者計(jì)算,就需要用到快照
componentDidUpdate(prevProps, prevState, snapshot) {}
getSnapshotBeforeUpdate 方法會(huì)在組件完成更新之前執(zhí)行,用于執(zhí)行某種邏輯或計(jì)算,返回值可以在 componentDidUpdate 方法中的第三個(gè)參數(shù)中獲取,就是說在組件更新之后可以拿到這個(gè)值再去做其他事情。
getSnapshotBeforeUpdate(prevProps, prevState) {
return 'snapshot'
}
3.6 Context
通過 Context 可以跨層級傳遞數(shù)據(jù)
<img src="./images/6.png" width="70%"/>
// userContext.js
import React from "react"
const userContext = React.createContext("default value")
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>
)
}
}
// 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對象中存儲(chǔ)的值和表單控件中的值時(shí)同步狀態(tài)的
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地址與組件之間的對應(yīng)關(guān)系,訪問不同的url地址顯示不同的組件。
下載:npm install react-router-dom
5.1.1 路由基本使用
// App.js
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 路由嵌套
function News(props) {
return (
<div>
<div>
<Link to={`${props.match.url}/company`}>公司新聞</Link>
<Link to={`${props.match.url}/industry`}>行業(yè)新聞</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>行業(yè)新聞</div>
}
5.1.3 路由傳參
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="/"/>
}
}
}