專門做app的網(wǎng)站內(nèi)容營銷策略
一個更好用的文檔
添加鏈接描述
箭頭函數(shù)的簡化
//簡化前
function countIncreAction(data) {return {type:"INCREMENT",data}
}
//簡化后
const countIncreAction =data=>({type:"INCREMENT",data
})
react UI組件庫相關(guān)資料
組件庫連接和推薦
antd組件庫文檔
Material UI
舉例
import React, {Component} from 'react';
import { Button, DatePicker } from 'antd';class App extends Component {render() {return (// 注意此處要用BrowserRouter包括根組件,可以提到index.js里面包裹App標簽<div><ul><Button type="primary">PRESS ME</Button><DatePicker placeholder="select date" /></ul></div>);}
}
export default App;
export和export default區(qū)別
- export 和 export default 都是es6語法中用來導出組件的
- 可以導出的文檔類型有( 數(shù)據(jù)、常量、函數(shù)、js文件、模塊等)
區(qū)別
- 一個文件中如果有多個模塊需要export,則使用export
export class Com extends Component{render() {return (<div ><h1>這是頭部</h1></div>)}
}export const str = '我是要被導出的str'// import 引入方式
import { Com , str } // 引入時需要解構(gòu)出來 可以解構(gòu)多個 用 , 號隔開
- export default 看名字就知道了,一個文件中只有一個模塊被聲明
function fn (){console.log('我是fn函數(shù)')
}export default fn //exprot default 導出時不會去關(guān)注 文件內(nèi)容 只要名稱對應(yīng)即可//export 必須寫在 文件最后;// 引入方式
import fn from '../'
redux
- redux是一個專門用于做狀態(tài)怪的js庫
- 可以用在vue、react、angular項目中,但基本與react配合使用
- 作用:集中式管理react應(yīng)用中的多個組件共享的狀態(tài),可以把組件內(nèi)需要共享的狀態(tài)交給redux管理
應(yīng)用場景
- 狀態(tài)共享:某個組件的狀態(tài),需要讓其他組件可以隨時拿到(共享)
- 組件通信:一個組件需要改變另一個組件的狀態(tài)(通信)
- 總體原則:能不用就不用,如果不用比較吃力才用
流程圖
我們先來看一下redux的工作流程,可以有一個大概的印象,我們看到,redux的核心有三個,一個是action creator, 一個是store,一個是reducers,其中redux的核心是什么?是store,我們調(diào)用redux,其實就是調(diào)用store的api,那么store負責api,誰負責干活呢?reducer,真正負責處理邏輯是在reducer里面。react component 我們自己的組件,負責和store交互
我們可以先來看一個簡單的列子
- store的實現(xiàn),其實store是不用我們實現(xiàn)的,我們只需要引入redux包,create一個store就可以了。
//引入store
import {createStore} from "redux"
import {countReducer} from "./count_reducer"
//引入reducer
export default createStore(countReducer)
- store負責和組件交互,但是需要依賴注入,即store要把任務(wù)交給誰處理,真正處理的這個人,就是reducer,所以我們寫一個reducer的邏輯,reducer就是一個純function,接收兩個參數(shù),一個是基礎(chǔ)數(shù)據(jù),一個是操作方式。
export function countReducer(state = 1, action) {const {type,data}=actionconsole.log(state,action);switch (type) {case 'INCREMENT'://注意,這個return的值就是statereturn state + data*1;case 'DECREMENT':return state - data*1;default:return state;}
}
- 至此 store和reducer我們都編輯完畢了,但是我們在組建中怎么調(diào)用呢?我們接下來編寫組件
import React, {Component, createRef} from 'react';
import store from "../../redux/store"class Count extends Component {sel=createRef();increment=()=>{const {value}=this.sel.current//store去分發(fā)任務(wù)store.dispatch({type: "INCREMENT",data:value})}decrement=()=>{const {value}=this.sel.current//store去分發(fā)任務(wù)store.dispatch({type: "DECREMENT",data:value})}incrementOdd=()=>{const {value}=this.sel.currentconst oldVal=store.getState()//store去分發(fā)任務(wù)if(oldVal%2===0){return}store.dispatch({type: "INCREMENT",data:value})}incrementAsync=()=>{const {value}=this.sel.currentsetTimeout(()=>{//store去分發(fā)任務(wù)store.dispatch({type: "INCREMENT",data:value})},1000)}render() {return (<div>{/*store.getState() 后去state的值*/}<h1>當前求和為:{store.getState()}</h1><select name="" id="" ref={this.sel}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button onClick={this.increment}>+</button><button onClick={this.decrement}>-</button><button onClick={this.incrementOdd}>奇數(shù)加</button><button onClick={this.incrementAsync}>異步加</button></div>);}
}export default Count;
- 我們通過store.dispatch去分發(fā)任務(wù),修改了state,但是redux有一個問題,他只負責存儲和更新state,但是不負責調(diào)render,這就導致我們的state變化之后頁面不能刷新,所以,我們還得調(diào)用一下setstate接口來更新render,那么另一個問題又來了,什么時候去setstate呢?我們需要redux告知我們,這個api就是
store.subscribe
,所以我們在index.js就給監(jiān)聽上
import React from 'react';
import ReactDOM from "react-dom/client";
import App from "./App";
import store from "./redux/store"
// ReactDOM.createRoot(document.getElementById("root")).render(app)
const root=ReactDOM.createRoot(document.getElementById("root"))
root.render(<App />)//注意App標簽一定不要預(yù)先定義,否則會編譯為靜態(tài)標簽而導致無法更新
store.subscribe(()=>{root.render(<App />)
})
- 自此,一個簡單的redux功能實現(xiàn)了。
redux demo實例
- 前面我們用了store和reducer,但是action是什么我并未涉及,接下來我們看怎么創(chuàng)建一個action文件count_action.js
const countIncreAction =data=>({type:"INCREMENT",data
})const countdecreAction = (data) => ({type: "DECREMENT",data
})
- 誒,我們發(fā)現(xiàn)一個現(xiàn)象,這個寫法和我們使用store分發(fā)任務(wù)有點像
decrement=()=>{const {value}=this.sel.current//store去分發(fā)任務(wù)store.dispatch({type: "DECREMENT",data:value})}
- 那我們怎么把我們自己寫的換成action呢,我們來替換一下,我們先引入,再替換
import React, {Component, createRef} from 'react';
import store from "../../redux/store"
import {countIncreAction,countdecreAction} from "../../redux/count_action"class Count extends Component {sel=createRef();increment=()=>{const {value}=this.sel.current//store去分發(fā)任務(wù)store.dispatch(countIncreAction(value))}
}export default Count;
- 關(guān)于一些優(yōu)化,我們知道,在代碼中,如果能用常量或者變量代替字符串,那么就用變量或者常量在代碼中使用,那么我們可以使用一個常量文件來管理這些
異步action
action 可以返回一個對象,交由store去用,但是如果是我們想要一個異步的action怎么辦呢?我們的對象里面也不支持自定義時間啊。redux很明顯不支持,但是有一個組件提供了這種功能,就是redux-thunk,它通過store可以接收第二個參數(shù),以applyMiddleware的形式讓store可以接收function。我們來看一下,首先我們創(chuàng)建一個可以接收function的store
//引入store
import {applyMiddleware, createStore} from "redux"
import {countReducer} from "./count_reducer"
import {thunk} from "redux-thunk"
//引入reducer
export default createStore(countReducer,applyMiddleware(thunk))
- 那么我們在使用的時候就可以簡單這樣使用
incrementAsync=()=>{const {value}=this.sel.currentstore.dispatch(countIncreAsyncAction(value,1000))}
react-redux
react-redux是由官方出的一個組件庫,用來簡化對redux的操作。redux對組件做了區(qū)分,一種是UI組件,負責頁面展示,一種是容器組件負責邏輯處理,既然是這樣,那么和redux能交互的就只有容器組件(container),容器組件拿到數(shù)據(jù)后通過props傳遞給UI組件(component)
我們把count.jsx改成純UI組件,如下
import React, {Component, createRef} from 'react';
class CountUI extends Component {sel=createRef();increment=()=>{const {value}=this.sel.current}decrement=()=>{const {value}=this.sel.current}incrementOdd=()=>{const {value}=this.sel.current}incrementAsync=()=>{const {value}=this.sel.current}render() {return (<div><h1>當前求和為:0</h1><select name="" id="" ref={this.sel}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button onClick={this.increment}>+</button><button onClick={this.decrement}>-</button><button onClick={this.incrementOdd}>奇數(shù)加</button><button onClick={this.incrementAsync}>異步加</button></div>);}
}
export default CountUI;
- 我們創(chuàng)建一個container組件
//這個文件就是容器組件,負責和redux交互并傳遞數(shù)據(jù)給UI組件
//引入UI組件
import {CountUI} from "../../components/count/count"
import {connect} from "react-redux"
//創(chuàng)建一個
//創(chuàng)建一個容器組件并暴露
export default connect()(CountUI)
- 我們說過,容器不僅要傳遞數(shù)據(jù)給UI組件,還需要和redux交互呢,和redux交互其實就是調(diào)用store的接口,那么我們怎么和store聯(lián)系上呢?我們在App里面改一下。
render() {return (<div><Count store={store} /></div>);}
}
connect
具體來說,connect接收兩個參數(shù),
- 一個是mapStateToProps,默認傳入state參數(shù),也就是講state數(shù)據(jù)以props的形式傳遞給子組件
- 一個是mapDispathcToProps ,默認傳入一個dispatch參數(shù),用來指定分發(fā)任務(wù)給哪個action,接下來我們看一下這redux,component,container的交互
//這個文件就是容器組件,負責和redux交互并傳遞數(shù)據(jù)給UI組件
//引入UI組件
import {CountUI} from "../../components/count/count"
import {connect} from "react-redux"
import {countDecreAction, countIncreAction, countIncreAsyncAction} from "../../redux/count_action"
//在此指定數(shù)據(jù),即把那些state賦值給props
function mapStateToProps(state) {return {count: state}
}
//在此指定redux,即讓哪個redux來處理數(shù)據(jù)
function mapDispathcToProps(dispatch) {return {add: (data) =>dispatch(countIncreAction(data)),addOdd: (data) =>dispatch(countIncreAction(data)),addAsync: (data) =>dispatch(countIncreAsyncAction(data,1000)),sub: (data) => dispatch(countDecreAction(data))}
}
//創(chuàng)建一個容器組件并暴露
//參數(shù)解釋:mapStateToProps用來傳遞數(shù)據(jù) mapDispathcToProps用來指定redux ,返回一個函數(shù)后,然后指定要綁定哪個UI組件
export default connect(mapStateToProps, mapDispathcToProps)(CountUI)
- 容器組件寫好之后,我們在UI組件怎么調(diào)用呢,很簡單,從props獲取即可
import React, {Component, createRef} from 'react';
export class CountUI extends Component {sel=createRef();increment=()=>{const {value}=this.sel.currentthis.props.add(value)}decrement=()=>{const {value}=this.sel.currentthis.props.sub(value)}incrementOdd=()=>{const oldValue=this.props.countconst {value}=this.sel.currentif (oldValue%2===0){return}this.props.addOdd(value)}incrementAsync=()=>{const {value}=this.sel.currentthis.props.addAsync(value)}render() {return (<div><h1>當前求和為:{this.props.count}</h1><select name="" id="" ref={this.sel}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button onClick={this.increment}>+</button><button onClick={this.decrement}>-</button><button onClick={this.incrementOdd}>奇數(shù)加</button><button onClick={this.incrementAsync}>異步加</button></div>);}
}
- 簡化版
//這個文件就是容器組件,負責和redux交互并傳遞數(shù)據(jù)給UI組件
//引入UI組件
import {CountUI} from "../../components/count/count"
import {connect} from "react-redux"
import {countDecreAction, countIncreAction, countIncreAsyncAction} from "../../redux/count_action"
export default connect(state => ({count: state}),{add: countIncreAction,addOdd: countIncreAction,addAsync: countIncreAsyncAction,sub: countDecreAction}
)(CountUI)
react-redux的優(yōu)化
- 既然react-redux 是對redux進行了優(yōu)化,那么react-redux會不會在state發(fā)生變化的時候來實現(xiàn)自動render呢?答案是肯定的,我們來試一下
import React from 'react';
import ReactDOM from "react-dom/client";
import App from "./App";
import store from "./redux/store"
// ReactDOM.createRoot(document.getElementById("root")).render(app)
const root=ReactDOM.createRoot(document.getElementById("root"))
root.render(<App />)
// 使用react-redux 注釋掉監(jiān)聽store
// store.subscribe(()=>{
// root.render(<App />)
// })
- provider
我們知道,每個容器組件我們都需要傳遞一個store,像這樣
<Count1 store={store} />
<Count2 store={store} />
<Count3 store={store} />
這樣看起來明顯是不科學的,那么有沒有一種方法,讓我們寫一次,就可以不用寫了呢,有,首先第一點我想到的就是用一個特殊標簽包裹一下這些組件,只要在標簽內(nèi)的,都默認傳遞了store,react真的這么做了,這餓就是provider ,我們來看一下怎么用
import React from 'react';
import ReactDOM from "react-dom/client";
import App from "./App";
import {Provider} from "react-redux"
import store from "./redux/store"
const root=ReactDOM.createRoot(document.getElementById("root"))
root.render(<Provider store={store}><App /></Provider>)
- 我們還可以把UI組件整合到container里面去,組成一個文件
多組件數(shù)據(jù)共享
- 我們先來創(chuàng)建這樣一個項目目錄,在src目錄下,來實現(xiàn)數(shù)據(jù)共享
- 我們先從最簡單的來,先來定義action文件
a-action.js
export const AAction = data=>({data:data,type:'AAction'})
b-action.js
export const BAction = data=>({data:data,type:'BAction'})
- 再來編寫reducer
a-reducer.js
const initUser=[{id:1,name:'小a',age:18}]
export const aReducer = (state = initUser, action) => {
const {type, data} = action
switch (action.type) {
case 'AAction':
return [data,...state]
default:
return state
}
}
b-reducer.js
const initUser=[{id:1,name:'小b',age:118}]
export const bReducer = (state = initUser, action) => {
const {type, data} = action
switch (action.type) {
case 'BAction':
return [data,...state]
default:
return state
}
}
- action reducer都有了,我們開始寫store,我們知道創(chuàng)建store的這行代碼
export default createStore(countReducer,applyMiddleware(thunk))
,里面的參數(shù)接收的是一個reducer,那么如果我有多個reducer怎么辦呢?我們就用到了combineReducers
//引入store
import {combineReducers, createStore} from "redux"
//引入reducer import {aReducer} from "./reducers/a-reducer"
import {bReducer} from "./reducers/b-reducer"
//將多個reducer寫入一個對象 注意是key=>value 格式書寫
const rootReducer = combineReducers({
"areducer":aReducer,
"breducer":bReducer
})
export default createStore(rootReducer)
- 我們在index.js中引入store,使用provider支持對容器的store傳遞
import React from 'react';
import ReactDOM from "react-dom/client";
import App from "./App";
import {Provider} from "react-redux"
import store from "./redux/store"
const root=ReactDOM.createRoot(document.getElementById("root"))
root.render(<Provider store={store}><App /></Provider>)
- 在a容器組件中暴露connect
import React, {Component, createRef} from 'react';
import {nanoid} from "nanoid";
import {AAction} from "../../redux/actions/a-action";
import {connect} from "react-redux"; class AUI extends Component { nameRef=createRef();
ageRef=createRef(); addUser=()=>{
let id=nanoid();
let name=this.nameRef.current.value;
let age=this.ageRef.current.value;
this.props.addAUser({id,name,age});
}
render() {
// console.log(this.props);
const {auser,buser}=this.props
return (
<div>
<h2>我是a組件</h2>
<input type="text" ref={this.nameRef} placeholder="name"/>
<input type="text" ref={this.ageRef} placeholder="age"/>
<button onClick={this.addUser}>添加用戶</button>
<h4>a組件用戶</h4>
<ul>
{auser.map((item=><li key={item.id}>name: {item.name} | age: {item.age}</li>))}
</ul>
<h4>b組件用戶</h4>
<ul>
{buser.map((item=><li key={item.id}>name: {item.name} | age: {item.age}</li>))}
</ul>
</div>
);
}
}
//注意,取state的時候要根據(jù)前面定義的key來取
export default connect(state=>({auser:state.areducer,buser:state.breducer}),{addAUser:AAction})(AUI);
- 在b容器組件中暴露connect
import React, {Component, createRef} from 'react';
import {nanoid} from "nanoid";
import {connect} from "react-redux";
import {BAction} from "../../redux/actions/b-action"; class BUI extends Component {
nameRef=createRef();
ageRef=createRef(); addUser=()=>{
let id=nanoid();
let name=this.nameRef.current.value;
let age=this.ageRef.current.value;
this.props.addBUser({id,name,age});
}
render() {
return (
<div>
<h2>我是組b件</h2>
<input type="text" ref={this.nameRef} placeholder="name"/>
<input type="text" ref={this.ageRef} placeholder="age"/>
<button onClick={this.addUser}>添加用戶</button>
</div>
);
}
} export default connect(state=>({buser:state.breducer}),{addBUser:BAction})(BUI);
- 最終效果如下,b組件添加用戶,會在a組件中展示
-
組件數(shù)據(jù)的交互,關(guān)鍵在于給某個組件state賦值的時候,
{auser:state.areducer,buser:state.breducer}
-
注意數(shù)組和對象的地址引用,這種引用只對比引用地址是否一致,并不會對比地址指向的數(shù)據(jù)是否一致,從而導致頁面不會更新。
redux-devtool
拓展安裝 npm install redux-devtools-extension --legacy-peer-deps
//引入store
import {combineReducers, createStore} from "redux"
//引入reducer import {aReducer} from "./reducers/a-reducer"
import {bReducer} from "./reducers/b-reducer"
//引入devtools
import {composeWithDevTools} from "redux-devtools-extension"
//將多個reducer寫入一個對象 注意是key=>value 格式書寫
const rootReducer = combineReducers({
"areducer":aReducer,
"breducer":bReducer
})
export default createStore(rootReducer,composeWithDevTools())
打包
執(zhí)行打包命令npm run build
開始編譯,編譯完畢,會在項目根目錄生成一個build目錄,我們啟動一個服務(wù)器指定目錄即可執(zhí)行
比如我使用go作為服務(wù)器來執(zhí)行build目錄,即可正常訪問目錄,用nginx,node也是同理
package mainimport ("fmt""net/http""os"
)func main() {// 設(shè)置靜態(tài)文件目錄為React應(yīng)用程序的build文件夾路徑fmt.Println(os.Getwd())fs := http.FileServer(http.Dir("./tmp/build"))// 創(chuàng)建一個處理靜態(tài)文件的handlerhttp.Handle("/", fs)// 啟動服務(wù)器并監(jiān)聽端口err := http.ListenAndServe(":8088", nil)if err != nil {panic(err)}
}
- 我們也可以使用serve來啟動項目
安裝serve$ npm install -g serve
,啟動serve,在build同目錄下,serve build
即可啟動服務(wù)。
一般上線的話需要搭配node 或者nginx來啟動。