首先,我先給出我學習react框架的參考教程,以下內容都是我依據此教程的個人總結。react教程
React與JSX:
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
React是一個聲明式,高效且靈活的用於構建用戶界面的 JavaScript 庫。使用 React 可以將一些簡短、獨立的代碼片段組合成複雜的 UI 界面,這些代碼片段被稱作“組件”。JSX是JavaScript 的語法擴展,所以其也具有JavaScript 的全部功能,並且建議在React框架中配合使用 JSX,JSX 可以很好地描述 UI 應該呈現出它應有交互的本質形式。
上述給出的一個奇怪的函數代碼就是一個函數組件,它就是採用React框架的JSX語法,其主要體現在兩點表現:一個是代碼裏直接嵌入HTML標籤,另一個是{}裏面可以嵌入表達式。這兩點語法都是原生JavaScript語法所不具備的特性。
注意:jsx語法既可以寫在jsx文件裏也可以直接寫在js文件下
其實簡單看來,React框架主要功能體現在前端UI頁面的渲染,包括性能優化以及操作簡化等等方面。站在mvc框架的角度來看,React操作view層的功能實現。
搭建React環境:
方式一:在網站中添加React
添加一個 DOM 容器到 HTML並添加 相應的Script 標籤:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>react-dom</title>
</head>
<body>
<div id="like_button_container"></div>
<!-- 加載 React。-->
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<!-- 加載我們的 React 組件。-->
<script src="like_button.js"></script>
</body>
</html>
創建一個 React 組件:
'use strict';
const e = React.createElement;
class LikeButton extends React.Component {
constructor(props) {
super(props);
this.state = { liked: false };
}
render() {
if (this.state.liked) {
return 'You liked this.';
}
return e(
'button',
{ onClick: () => this.setState({ liked: true }) },
'Like'
);
}
}
const domContainer = document.querySelector('#like_button_container');
ReactDOM.render(e(LikeButton), domContainer);
直接運行打開html頁面即可運行。
方式二:Create React App
此方式是通過IDE命令行的方式創建一個本地的React應用項目,也是最佳的創建方式,所以首先得在本地安裝node(Node >= 8.10 和 npm >= 5.6)。
一切準備就緒後,只需一行代碼即可搭建本地React框架:
npx create-react-app my-app
搭建ts版本的react框架(tsx):
npx create-react-app my-app --template typescript
這是搭建好的目錄結構
jsx:
tsx:
項目框架搭建好後在src目錄下添加我們自己的代碼文件。
進入項目根目錄並運行項目:
cd my-app
npm start
jsx:
tsx:
注:有關ts的具體詳細內容我這裏先暫不做講解,如果大家有感覺到突兀可以先參看我給出的ts教程或者參看我下期編寫的ts學習總結。
組件與Props:
組件是React框架中最重要的概念之一,在博客最開始的時候也提到這個詞,現在來詳細的此概念。
組件的概念:組件,從概念上類似於 JavaScript 函數。它接受任意的入參(即 “props”),並返回用於描述頁面展示內容的 React 元素。
在React框架裏有兩種組件:函數組件和類組件
這是函數組件:
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
這是類組件:
class Board extends React.Component {
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
);
}
render() {
return (
<div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
由上面的代碼對比不難發現,函數組件適用於較爲簡單的功能組件,而類組件就適用於較爲複雜的功能組件,類組件將在下一部分state和生命週期中具體講解。
注意:爲了更有效的區分普通函數與函數組件,所有的組件名稱必須以大寫字母開頭。
Props:這是由React內部包裝設置的一個用於傳遞數據的參數,Props最大的特點就是read only,即Props只能被讀取使用但是不能被修改。
State和(類組件)生命週期:
什麼是State:Props只能讀取不能修改,顯然肯定滿足不了用戶的需求。爲了滿足React 組件隨用戶操作、網絡響應或者其他變化而動態更改輸出內容的功能,State出現了,它可以用戶自定義。State可以被修改,但是不能直接修改,只能通過setState()函數來修改State裏面的值,這也是State和Props最大的區別。
組件的生命週期:由於函數組件過於簡單而這裏的生命週期是指一些聲明週期方法,所以這裏的生命週期對於類組件而言。
組件的生命週期分爲三個週期:掛載,更新和卸載。
這裏的生命週期方法我不做具體講解,大家可以參看詳細的官方教程。
掛載,當組件實例被創建並插入 DOM 中時,其生命週期調用順序如下:
- constructor()
- static getDerivedStateFromProps()
- render()
- componentDidMount()
注意:render()方法是類組件中唯一必須實現的方法。因爲最簡單的函數組件裏面的業務邏輯也包含reader()方法的業務邏輯。如果不初始化 state 或不進行方法綁定,則不需要爲 React 組件實現構造函數。
更新,當組件的 props 或 state 發生變化時會觸發更新。組件更新的生命週期調用順序如下:
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
注意:此處說的props改變與上述說的props不可修改並不矛盾,上述說的props不可修改是指props在當前組件中不能被修改,而此處說的props改變是指在父組件因爲傳遞參數的內容改變而導致子組件props改變。
卸載,當組件從 DOM 中移除時會調用如下方法:
- componentWillUnmount()
事件處理:
React元素的事件處理和 DOM 元素的很相似,但是有一點語法上的不同:
- React 事件的命名採用小駝峯式(camelCase),而不是純小寫。
- 使用 JSX 語法時你需要傳入一個函數作爲事件處理函數,而不是一個字符串。
React元素的事件處理中this綁定的三種方式:
es5中的bind方法:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
public class fields 語法:
class LoggingButton extends React.Component {
// 此語法確保 `handleClick` 內的 `this` 已被綁定。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
箭頭函數:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此語法確保 `handleClick` 內的 `this` 已被綁定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
一些其它的核心概念:
元素渲染: ReactDOM.render()是最頂層的渲染方法,而組件裏面的render方法可以看成它的子成員,ReactDOM.render是唯一一個直接與DOM交互的方法,而其字render方法只改變ReactDOM元素,最終都是由ReactDOM.render來更替UI的DOM元素,所以此方法建議只調用一次。
條件渲染:React 中的條件渲染和 JavaScript 中的一樣,使用 JavaScript 運算符 if 或者條件運算符(包括三目運算符)去創建元素來表現當前的狀態,然後讓 React 根據它們來更新 UI。
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
// 運算符 if
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
// 條件運算符
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
// 三目運算符
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}
狀態提升:多個組件需要反映相同的變化數據,這時我們將共享狀態提升到最近的共同父組件中去。狀態提升體現的是種代碼設計思想,此思想既能降低代碼的耦合性還能提高代碼的擴展性。
React有十分強大的組合模式。我們推薦使用組合而非繼承來實現組件間的代碼重用。
一個完整的較爲全面覆蓋的simple:
這是官網教程給出的一個井字棋遊戲simple,幾乎涵蓋了我上面列出的所有知識點,甚至還有沒提到的細節知識點。比較React是個很大的框架,我所列出的這些才只是冰山一角。
這個是遊戲實現了四大功能:
- tic-tac-toe(三連棋)遊戲的所有功能
- 能夠判定玩家何時獲勝
- 能夠記錄遊戲進程
- 允許玩家查看遊戲的歷史記錄,也可以查看任意一個歷史版本的遊戲棋盤狀態
css代碼:
body {
font: 14px "Century Gothic", Futura, sans-serif;
margin: 20px;
}
ol, ul {
padding-left: 30px;
}
.board-row:after {
clear: both;
content: "";
display: table;
}
.status {
margin-bottom: 10px;
}
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}
.square:focus {
outline: none;
}
.kbd-navigation .square:focus {
background: #ddd;
}
.game {
display: flex;
flex-direction: row;
}
.game-info {
margin-left: 20px;
}
js(jsx)代碼:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
class Board extends React.Component {
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
);
}
render() {
return (
<div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
history: [
{
squares: Array(9).fill(null)
}
],
stepNumber: 0,
xIsNext: true
};
}
handleClick(i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? "X" : "O";
this.setState({
history: history.concat([
{
squares: squares
}
]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext
});
}
jumpTo(step) {
this.setState({
stepNumber: step,
xIsNext: (step % 2) === 0
});
}
render() {
const history = this.state.history;
const current = history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li key={move}>
<button onClick={() => this.jumpTo(move)}>{desc}</button>
</li>
);
});
let status;
if (winner) {
status = "Winner: " + winner;
} else {
status = "Next player: " + (this.state.xIsNext ? "X" : "O");
}
return (
<div className="game">
<div className="game-board">
<Board
squares={current.squares}
onClick={i => this.handleClick(i)}
/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
);
}
}
// ========================================
ReactDOM.render(<Game />, document.getElementById("root"));
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
最後,由於我也只是個React初學者,此篇博客裏面含有很多我自己的理解成分,難免會有錯誤理解,如果有發現錯誤的朋友歡迎評論區指出。路漫漫其修遠,此篇博客的內容才只是React框架的冰山一角,希望能對大家有所幫助。