簡介
React 是一個用於構建用戶界面的 JAVASCRIPT 庫。
React主要用於構建UI,很多人認爲 React 是 MVC 中的 V(視圖)。
React 起源於 Facebook 的內部項目,用來架設 Instagram 的網站,並於 2013 年 5 月開源。
React 擁有較高的性能,代碼邏輯非常簡單,越來越多的人已開始關注和使用它。
官網:https://www.reactjscn.com/
下載
首先你要配置好 webpack
,這裏就不再多說了,可以看我以前寫的 http://www.zhaosirlaile.com/#/article/00020
-
下載
react-dom
和react
這兩個包npm i react react-dom -S
-
在
index.html
頁面中創建一個容器<!-- 使用 React 創建的虛擬DOM元素,都會被渲染到這個指定的容器中 --> <div id="app"></div>
-
導入包
import React from 'react' import ReactDOM from 'react-dom'
-
使用
ReactDOM.render
進行渲染:ReactDOM.render(<h1></h1>, document.getElementById('app'))
JSX
什麼是JSX語法:就是符合 xml 規範的 JS 語法;(語法格式相對來說,要比HTML嚴謹很多)
let element = <h1>Hello, world!</h1>;
官方解釋:它被稱爲 JSX, 一種 JavaScript 的語法擴展。 我們推薦在 React 中使用 JSX 來描述用戶界面。JSX 乍看起來可能比較像是模版語言,但事實上它完全是在 JavaScript 內部實現的
簡單來說:就是使用 jsx
更方便的在js文件中書寫html元素
注意
: jsx 語法的本質:並不是直接把 jsx 渲染到頁面上,而是內部先轉換成了 createElement 形式,再渲染的;
在 jsx 中寫入 js 表達式 ({})
在 jsx 語法中,要把 JS代碼寫到 { }
中
- 渲染數字
- 渲染字符串
- 渲染布爾值
- 爲屬性綁定值
- 渲染jsx元素
- 渲染jsx元素數組
- 將普通字符串數組,轉爲jsx數組並渲染到頁面上
- 一種是在外面寫好了在使用
{}
,插入到對應的位置 - 另一種就是在jsx中對應的位置使用數組的map函數也是可以的(推薦)
- 一種是在外面寫好了在使用
jsx 中註釋
一種:
const app = <div>
{
// 這是註釋
// 這是註釋
}
</div>
另一種:
const app = <div>
{
/* 這是註釋
這是註釋 */
}
</div>
jsx 中的元素添加class類名,label添加for
- 需要使用
className
來替代class
htmlFor
替換label元素的for
屬性
jsx 創建 DOM 的時候,注意
- 在JSX創建DOM的時候,所有的節點,必須有唯一的根元素進行包裹;
- 在 jsx 語法中,標籤必須 成對出現,如果是單標籤,則必須自閉和!
當編譯引擎,在編譯JSX代碼的時候,如果遇到了
<
那麼就把它當作 HTML代碼去編譯,如果遇到了{}
就把 花括號內部的代碼當作 普通JS代碼去編譯;
組件
第一種
第一種方式其實就是一個構造函數,
如果在一個組件中return一個null,則表示此組件是空的,什麼都不會渲染;或者return一個合法的JSX創建的虛擬DOM。如果函數中沒有return就會報錯
創建組件
function Pry () {
return <p>Hello Wolrd 組件</p>
}
使用組件
function Pry () {
return <p>Hello Wolrd 組件</p>
}
const myh1 = <div>
<Pry></Pry>
</div>
注意:不管是在創建組件,還是在使用組件,組件名稱的第一個首字母必須大寫
傳參(props)
// 使用組件並 爲組件傳遞 props 數據
function Pry (props) {
return <p>我的名字是:{props.name},我的性別是:{props.sex}</p>
}
const dom = <div>
<Pry name='小明' sex='男' ></Pry>
</div>
// 注意:不論是 Vue 還是 React,組件中的 props 永遠都是隻讀的;不能被重新賦值;
ReactDOM.render(dom,document.getElementById('app'));
使用 .jsx 文件創建組件
這是文件目錄:
我們在 index.js
文件中導入 Hello.jsx
文件
以下是 index.js 的代碼:
import React from 'react';
import ReactDOM from 'react-dom';
// 組件
import Hello from './componnets/Hello';
let dog = {
name:'小航',
sex : "madom",
}
const myh1 = <div>
<Hello {...dog} ></Hello>
</div>
ReactDOM.render(myh1,document.getElementById('app'));
以下是 Hello.js 的代碼:
import React from 'react'; // 必須導入,否則報錯
export default function Hello (props) {
return <p>我的名字是:{props.name},我的性別是:{props.sex}</p>
}
第二種
使用 es6 的 class關鍵字
創建對象,其實 class關鍵字
僅僅只是 new關鍵字的語法糖而已,使用 new 和 class 創建的效果是一樣的,只是 class 關鍵字創建起對象更方便,看着更舒服,但是創建組件就有區別了。
語法
class Animal {
constructor(name,sex,age) { //構造函數
this.name = name;
this.age = age;
this.sex = sex;
}
static info = 'eee'; // 靜態變量
static active () { // 靜態方法
console.log('動起來了')
}
say(){ // 實例方法
console.log('嗚嗚嗚~~~');
}
}
注意:
- 在 class 的
{ }
區間中,只能寫構造器、靜態方法和靜態屬性、實例方法
繼承
class Person {
constructor(name,age,languge){
this.name = name
this.age = age
this.languge = languge;
}
}
class Chinese extends Person { // 子類繼承父類使用 extends
// 自定義構造器時必須使用supter()
// 省略構造器按照繼承的父類的構造器驚喜構造
constructor(name,age,languge,IDNumber){
super(name,age,languge);
this.IDNumber = IDNumber;
}
say(){
console.log('吃飯沒?')
}
}
class American extends Person{
constructor(name,age,languge){
super(name,age,languge);
}
say(){
console.log('hello');
}
}
const c1 = new Chinese('小明',18,'漢語','510180454545744');
console.log(c1);
const a1 = new American('Tom',22,'English');
console.log(a1);
基於 class 關鍵字創建組件
基本語法
// 如果要使用 class 定義組件,必須 讓自己的組件,繼承自 React.Component
class 組件名稱 extends React.Component {
// 在 組件內部,必須有 render 函數,作用:渲染當前組件對應的 虛擬DOM結構
render(){
// render 函數中,必須 返回合法的 JSX 虛擬DOM結構
return <div>這是 class 創建的組件</div>
}
}
注意:
- 在 class 關鍵字創建的組件中,如果想使用外界傳遞過來的 props 參數,不需要接收,直接通過
this.props.xxx
即可 props
是隻讀的,不能進行賦值
私有數據
class Hello extends React.Component{
constructor(){
super(); // 必須加上super(),才能使用this關鍵字,否則報錯
this.state = {} // 相當於 Vue 中的 data(){ return {} }
}
render(h) {
return <h1>你好!{this.props.name},你的年齡{this.props.sex}</h1>
}
}
注意
:this.state 是可讀可寫的
兩種創建方式的對比
構造函數 | class關鍵字 | |
---|---|---|
props | 有 | 有 |
私有數據 | 沒有 | 有 |
生命週期函數 | 沒有 | 有 |
因此:
- 用構造函數創建出來的組件:叫做“無狀態組件”【無狀態組件今後用的不多】
- 用class關鍵字創建出來的組件:叫做“有狀態組件”【今後用的最多】
- 什麼情況下使用有狀態組件?什麼情況下使用無狀態組件?
- 如果一個組件需要有自己的私有數據,則推薦使用:class創建的有狀態組件;
- 如果一個組件不需要有私有的數據,則推薦使用:無狀態組件;
- React官方說:無狀態組件,由於沒有自己的state和生命週期函數,所以運行效率會比 有狀態組件稍微高一些;
有狀態組件和無狀態組件之間的本質區別就是:有無state屬性、和 有無生命週期函數;
- 組件中的
props
和state/data
之間的區別
- props 中的數據都是外界傳遞過來的;
- state/data 中的數據,都是組件私有的;(通過 Ajax 獲取回來的數據,一般都是私有數據);
- props 中的數據都是隻讀的;不能重新賦值;
- state/data 中的數據,都是可讀可寫的;
如何使用 css
注意:webpack中默認是不能解析css的需要先配置
配置 css
先安裝 style-loader
,css-loader
這兩個包
npm i style-loader css-loader -D
打開 webpack.config.js
文件,在 module 對象中的rules數組加入一項
{
test:/\.css/,
use: ['style-loader','css-loader'] //處理 css 文件
}
注意:我的 webpack 是 2.x.x
碼爲以下樣子:
module.exports = {
mode: 'development',
module: {
rules:[
{
test: /\.js|jsx$/, // 處理 es6 的語法及jsx
use: 'babel-loader',
exclude: /node_modules/ // 一定要加這一句
},
{
test:/\.css/,
use: ['style-loader','css-loader'] //處理 css 文件
}
]
},
}
行內樣式
export default class Hello extends React.Component{
constructor(){
super();
this.state = {}
}
render(h) {
return <div>
<p style={{color: 'red',fontWeight:'600px'}}>hello world</p>
</div>
}
}
注意:
- 在jsx中使用style屬性的時候,不要嚮往常那樣寫
style="color:red"
這樣寫是會報錯的 - 應該這樣寫
style={{color:'red',font-size:'15px'}}
纔對,那這兩個括號什麼意思呢?- 第一個括號表示:這裏要寫js的代碼
- 第二個括號表示:這裏是個對象
- jsx 中 style 是個對象
- 如果style的樣式規則中,屬性值的單位是px,可以省略不寫,直接寫個數值
相信大家想到了,既然 style 是個對象,那麼我可以這樣寫
const myStyle = {
style1: {
color: 'red',
fontWeight: 200,
},
style2: {
color: 'black',
}
}
export default class Hello extends React.Component{
constructor(){
super();
this.state = {}
}
render(h) {
return <div>
<p style={myStyle.style1}>hello world</p>
</div>
}
}
外部樣式
以下是我的文件路徑截圖:
css的代碼:
.title {
font-size: 12px;
color: #f0f0f0;
background-color: #fff;
}
jsx的代碼:
import React from 'react';
import './CommentList.css';
export default class Hello extends React.Component{
constructor(){
super();
this.state = {}
}
render(h) {
return <div>
<p className='title'>hello world</p>
</div>
}
}
這樣的導入樣式其實全局的:任何組件引用這個組件都可能造成樣式覆蓋,這樣我就可以藉助css的模塊化(私有,像vue中的scoped)
啓用 css 模塊化
打開webpack.config.js
文件,在處理 css 那一項中改爲use: ['style-loader','css-loader?modules']
也就是加了一個 ?modules
,在重新啓用 webpack,就生效了,不過…
module.exports = {
mode: 'development',
module: {
rules:[
{
test: /\.js|jsx$/,
use: 'babel-loader',
exclude: /node_modules/ // 一定要加這一句
},
{
test:/\.css/,
use: ['style-loader','css-loader?modules'] //處理 css 文件
}
]
},
}
在我們的jsx文件中最好還是使用導入css文件就必須把import './CommentList.css';
改爲import listSyle from './CommentList.css';
,其中listStyle
是自己定義的,並且在jsx文件中把listStyle
當成對象一樣使用,打印 listStyle
,返回的是一個對象
export default class Hello extends React.Component{
constructor(){
super();
this.state = {}
}
render(h) {
return <div>
<p className={listSyle.title}>hello world</p>
</div>
}
}
webpack中的css-loader 啓用了css的模塊化,就是通過對象來使用這些樣式
注意:css-loader只對類選擇器和id選擇器其中用,其它的選擇器都會無效
生命週期
什麼是生命週期
在組件創建、到加載到頁面上運行、以及組件被銷燬的過程中,總是伴隨在某些個特定的事件,而這些事件統稱爲生命週期函數(生命週期鉤子)。
如下圖所示:
組件的生命週期分爲三個階段:
-
組件創建
階段:一個組件的一生只執行一次(就像母親生一個孩子,對孩子而言只有一次生孩子的動作)-
static defaultProps
:該對象用來設置組件中的默認屬性值,並且會在state
之前被運行,一般用於這用於這種情況:父組件沒有傳值過來,這時候就需要這個值,怎麼辦呢?那就可以設置設個屬性,給他一個默認值來初始化我們的數據。import React from 'react'; export default class Counter extends React.Component{ constructor(props) { super(props); } static defaultProps = { initData: 0 } render(){ return <div> <h3>這是靜態變量defaultProps的值:{this.props.initData}</h3> </div> } }
static propTypes
這個對象可以把父組件傳過來的值進行數據類型檢查,如果要爲傳遞過來的數據驚喜類型檢驗,必須安裝 React 提供的第三方包prop-types
,其實這個包在 reactv15
及其之前就在react中,並沒有被抽離出來,在v15
這個版本之後,它就被 react 官方抽離爲一個prop-types
的一個包。要想使用安裝
prop-types
這個包:npm i prop-types -s
import React from 'react'; import ReactTypes from 'prop-types'; export default class Counter extends React.Component{ constructor(props) { super(props); } static propTypes = { componentInitNumber: ReactTypes.number, } render(){ return <div> <h3>當前數量是:{this.props.componentInitNumber}</h3> </div> } }
-
state
對象:初始化組件中的私有變量,也就是這個對象,注意:該對象是可讀可寫的哦!import React from 'react'; import ReactTypes from 'prop-types'; export default class Counter extends React.Component{ constructor(props) { super(props); this.state = { data : 'hello world' } } render(){ return <div> <h3>當前數量是:{this.state.data}</h3> </div> } }
-
compoentWillMount
生命週期函數:組件將要被掛載了,此時還沒有開始渲染虛擬 DOM,該函數等同於vue中的created
,適合寫一些ajax,獲取數據的請求。但該函數是不可以對頁面上的dom元素進行操作的,因爲不管是虛擬DOM還是真實的DOM都未被渲染出來,但是在該函數中,我們可以使用我們自定義的方法。import React from 'react'; import ReactTypes from 'prop-types'; export default class Counter extends React.Component{ constructor(props) { super(props); this.state = { data : 'hello world' } } componentWillMount(){ this.state.data = '你好,世界'; } render(){ return <div> {this.state.data} </div> } }
-
render
生命週期函數:首次渲染虛擬DOM,當render執行完畢時就表示虛擬DOM已經渲染好了,但是虛擬DOM還在內存中,還沒有掛載到頁面上,所以我們不可以訪問我們渲染出來的DOM -
componentDidMount
生命週期函數:虛擬DOM完成了掛載,此時,組件已經顯示到了頁面上,此時,我們可以操作渲染出的DOM元素
,當這個函數執行完成時表示將要進入組件運行
階段了import React from 'react'; import ReactTypes from 'prop-types'; export default class Counter extends React.Component{ constructor(props) { super(props); this.state = { data : 'hello world' } } componentWillMount(){ this.state.data = '你好,世界'; } render(){ return <div id='hello'> {this.state.data} </div> } componentDidMount(){ console.log(document.getElementById('hello')) } }
-
-
組件運行
階段:當state
和props
改變時該生命走起函數就會被觸發,所以該生命週期函數就會被觸發 0 次或多次-
componentWillReceiveProps
生命週期函數:組件將要接收的屬性改變了,此時,該函數會被觸發 -
shouldComponentUpdate
生命週期函數:組件將要被更新,此時,組件還沒有被更新,但是,state 和 props 會被更新成最新的值import React from 'react'; import ReactTypes from 'prop-types'; export default class Counter extends React.Component{ constructor(props) { super(props); this.state = { data : 1 } } render(){ return <div id='hello'> {this.state.data} <br/> <button onClick={ this.increaseOne }>+1</button> </div> } increaseOne = ()=>{ this.setState({ data : this.state.data + 1 }) } componentDidMount(){ console.log(document.getElementById('hello')) } shouldComponentUpdate(newProp,newState){ console.log(this.state.data) console.log(newState) return true } }
注意:在
shouldComponentUpdate
函數中有兩個參數,這兩個參數表示prop
當前的最新值,state
當前的最新值,若果你想要通過this.state.data
獲取數據會有問題的,這個會拿到你上一次的數據,反正不是最新的數據 -
componentWillUpdate
生命週期函數:此時,組件尚未更新,內存中的虛擬DOM數還是舊的
該函數要求必須返回ture
或false
表示,看上面的圖知道,ture 就執行 render 重新渲染頁面,false 表示不執行 render ,直接返回到運行中 -
render
生命週期函數:再次渲染虛擬DOM,並根據 state 和 props 重新渲染,當render渲染完畢時,替換內存中的舊的DOM樹,該render
函數和組件創建
階段的render
函數是一致的,如果我們想要在更新數據的時候操作東西,而不是在第一次使用的話,可以使用&&
來實現這個功能,this.refs.num && console.log(this.refs.num)
-
componentDidUpdate
生命週期函數:此時,頁面根據新的DOM樹重新渲染頁面,所有的東西都是最新的,我們可以放心的使用 DOM元素,state,props 等
-
-
組件銷燬
階段:也是一個組件的一生只執行一次(就像一個人一生只會有一個死的動作)componentWillMount
生命週期函數:組件將要被銷燬是觸發
事件
React事件綁定屬性的命名採用駝峯式寫法。如果採用 JSX 的語法你需要傳入一個函數作爲事件處理函數,而不是一個字符串(DOM元素的寫法)
<button οnclick={increaseOne}>
+1
</button>
注意:你在構造函數中定義事件時一定要向這樣定義函數:
方法一
increaseOne = ()=>{
this.setState({
data : this.state.data + 1
})
}
如果不使用箭頭函數,會報錯,因爲這時候的箭頭函數將this指向改成了當前實例對象,否則就會指向爲是 undefined
方法二
相信大家已經注意到了,只好改變increaseOne
的 this 指向 就可以解決這個問題,其實也不用箭頭函數也可以,我們可以使用 bind
,那爲什麼我們不使用apply
和call
呢?
因爲js的引擎看到appply
和 call
時就會立即執行,這有違我們初衷,我們僅僅只是想改一下它們的this指向就可以了,而bind
完全符合我們的要求,他會改變返回的函數this指向並返回一個新的函數
<button οnclick={increaseOne.bind(this)}>
+1
</button>
方法三 (推薦)
import React from 'react';
import ReactTypes from 'prop-types';
export default class Counter extends React.Component{
constructor(props) {
super(props);
this.state = {
data : 1
}
}
render(){
return <div id='hello'>
{this.state.data}
<br/>
<button onClick={ ()=>this.increaseOne() }>+1</button>
</div>
}
increaseOne(){
this.setState({
data : this.state.data + 1
})
}
}
在 <button onClick={ ()=>this.increaseOne() }>+1</button>
在onclick後面的{}
中直接寫上一個箭頭函數,並在箭頭函數中調用你要完成的函數,這種方式用的是最多的
雙向數據綁定
React
默認不支持雙向數據綁定(不像vue中的v-model
),只支持單向的數據綁定,但是我們可以配合 onchange事件
來實現雙向數據綁定
import React from 'react';
import ReactTypes from 'prop-types';
export default class Counter extends React.Component{
constructor(props) {
super(props);
this.state = {
data : 1
}
}
render(){
return <div id='hello'>
{this.state.data}
<br/>
<button onClick={ ()=>this.increaseOne() }>+1</button>
<input type='text' value={this.state.data} onChange={this.myChange}></input>
</div>
}
myChange = (e) =>{
this.setState({
data: e.target.value
})
}
increaseOne(){
this.setState({
data : this.state.data + 1
})
}
componentDidMount(){
console.log(document.getElementById('hello'))
}
}
context
在父組件中,定義一個名字爲getChildContext
的函數名稱,這個函數的名稱是固定的,而這個函數返回一個對象,我們就可以在這個父組件以下的所有子組件中使用這個我們共享的對象,通過this.context.XXX
進行訪問數據。
import React from 'react';
import ReactTypes from 'prop-types';
export default class Context extends React.Component {
constructor(props){
super(props);
this.state = {
say: `hello world`
}
}
getChildContext () {
return {
say: this.state.say,
}
}
static childContextTypes = { // 注意必須使用這個對象校驗
say: ReactTypes.string,
}
render(){
return <div>
<h1>這是context父組件</h1>
<Com1></Com1>
</div>
}
}
class Com1 extends React.Component {
render(){
return <div>
<h2>這是com1子組件</h2>
<Com2></Com2>
</div>
}
}
class Com2 extends React.Component {
render(){
return <div>
<h3>這是com2子組件</h3>
<Com3></Com3>
</div>
}
}
class Com3 extends React.Component {
static contextTypes = { // 在要使用的子組件位置也要使用這個對象校驗
say: ReactTypes.string
}
render(){
return <div>
<h4>這是com3子組件</h4>
<p>說了:{this.context.say}</p>
</div>
}
}
注意:要使用 context
都要在子組件和父組件中進行類型校驗,纔可以在子組件中進行使用
- 父組件使用:
static childContextTypes
- 子組件使用:
static contextTypes
路由
下載
npm i react-router-dom -s
使用
按需導入 react-router-dom
import {HashRouter,Route,Link} from 'react-router-dom';
其中:
-
HashRouter 組件表示:一個路由的根容器,所以與路由相關的東西,都要包裹在
HashRouter
裏面,而且一個網站項目中只需使用一次HashRouter 就可以了。import React from 'react' import {HashRouter,Route,Link} from 'react-router-dom'; export default class App extends React.Component { constructor (props) { super(props); this.state = {}; } render () { return <HashRouter> <h1>這是網址的APP根組件</h1> </HashRouter> } }
當使用
HashRouter
把根組件的元素包裹起來之後,網站就啓動路由了注意:在一個
HashRouter
元素中只能有唯一一個根元素,並且在一個網站中,只需要使用唯一一次<HasRouter></HashRouter>
就行了 -
Route 組件表示:路由規則,在 Route 中,有兩個比較重要的屬性:
path
和component
import React from 'react' import {HashRouter,Route,Link} from 'react-router-dom'; // 導入組件 import Home from './components/Home'; import About from './components/About'; import Movie from './components/Movie'; export default class App extends React.Component { constructor (props) { super(props); this.state = {}; } render () { return <HashRouter> <div> <Route path='/home' component={Home}></Route> <Route path='/movie' component={Movie}></Route> <Route path='/about' component={About}></Route> </div> </HashRouter> } }
-
Link 組件表示:一個路由的鏈接,就像 vue 中的
router-link 標籤
import React from 'react' import {HashRouter,Route,Link} from 'react-router-dom'; // 導入組件 import Home from './components/Home'; import About from './components/About'; import Movie from './components/Movie'; export default class App extends React.Component { constructor (props) { super(props); this.state = {}; } render () { return <HashRouter> <div> <h1>這是網址的APP根組件</h1> <hr/> <Link to='/home'>首頁</Link> <Link to='/movie'>電影</Link> <Link to='/about'>關於</Link> <hr/> <Route path='/home' component={Home}></Route> <Route path='/movie' component={Movie}></Route> <Route path='/about' component={About}></Route> </div> </HashRouter> } }