專門(mén)做app的網(wǎng)站內(nèi)容營(yíng)銷策略
一個(gè)更好用的文檔
添加鏈接描述
箭頭函數(shù)的簡(jiǎn)化
//簡(jiǎn)化前
function countIncreAction(data) {return {type:"INCREMENT",data}
}
//簡(jiǎn)化后
const countIncreAction =data=>({type:"INCREMENT",data
})
react UI組件庫(kù)相關(guān)資料
組件庫(kù)連接和推薦
antd組件庫(kù)文檔
Material UI
舉例
import React, {Component} from 'react';
import { Button, DatePicker } from 'antd';class App extends Component {render() {return (// 注意此處要用BrowserRouter包括根組件,可以提到index.js里面包裹App標(biāo)簽<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語(yǔ)法中用來(lái)導(dǎo)出組件的
- 可以導(dǎo)出的文檔類型有( 數(shù)據(jù)、常量、函數(shù)、js文件、模塊等)
區(qū)別
- 一個(gè)文件中如果有多個(gè)模塊需要export,則使用export
export class Com extends Component{render() {return (<div ><h1>這是頭部</h1></div>)}
}export const str = '我是要被導(dǎo)出的str'// import 引入方式
import { Com , str } // 引入時(shí)需要解構(gòu)出來(lái) 可以解構(gòu)多個(gè) 用 , 號(hào)隔開(kāi)
- export default 看名字就知道了,一個(gè)文件中只有一個(gè)模塊被聲明
function fn (){console.log('我是fn函數(shù)')
}export default fn //exprot default 導(dǎo)出時(shí)不會(huì)去關(guān)注 文件內(nèi)容 只要名稱對(duì)應(yīng)即可//export 必須寫(xiě)在 文件最后;// 引入方式
import fn from '../'
redux
- redux是一個(gè)專門(mén)用于做狀態(tài)怪的js庫(kù)
- 可以用在vue、react、angular項(xiàng)目中,但基本與react配合使用
- 作用:集中式管理react應(yīng)用中的多個(gè)組件共享的狀態(tài),可以把組件內(nèi)需要共享的狀態(tài)交給redux管理
應(yīng)用場(chǎng)景
- 狀態(tài)共享:某個(gè)組件的狀態(tài),需要讓其他組件可以隨時(shí)拿到(共享)
- 組件通信:一個(gè)組件需要改變另一個(gè)組件的狀態(tài)(通信)
- 總體原則:能不用就不用,如果不用比較吃力才用
流程圖
我們先來(lái)看一下redux的工作流程,可以有一個(gè)大概的印象,我們看到,redux的核心有三個(gè),一個(gè)是action creator, 一個(gè)是store,一個(gè)是reducers,其中redux的核心是什么?是store,我們調(diào)用redux,其實(shí)就是調(diào)用store的api,那么store負(fù)責(zé)api,誰(shuí)負(fù)責(zé)干活呢?reducer,真正負(fù)責(zé)處理邏輯是在reducer里面。react component 我們自己的組件,負(fù)責(zé)和store交互
我們可以先來(lái)看一個(gè)簡(jiǎn)單的列子
- store的實(shí)現(xiàn),其實(shí)store是不用我們實(shí)現(xiàn)的,我們只需要引入redux包,create一個(gè)store就可以了。
//引入store
import {createStore} from "redux"
import {countReducer} from "./count_reducer"
//引入reducer
export default createStore(countReducer)
- store負(fù)責(zé)和組件交互,但是需要依賴注入,即store要把任務(wù)交給誰(shuí)處理,真正處理的這個(gè)人,就是reducer,所以我們寫(xiě)一個(gè)reducer的邏輯,reducer就是一個(gè)純function,接收兩個(gè)參數(shù),一個(gè)是基礎(chǔ)數(shù)據(jù),一個(gè)是操作方式。
export function countReducer(state = 1, action) {const {type,data}=actionconsole.log(state,action);switch (type) {case 'INCREMENT'://注意,這個(gè)return的值就是statereturn state + data*1;case 'DECREMENT':return state - data*1;default:return state;}
}
- 至此 store和reducer我們都編輯完畢了,但是我們?cè)诮M建中怎么調(diào)用呢?我們接下來(lái)編寫(xiě)組件
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>當(dāng)前求和為:{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;
- 我們通過(guò)store.dispatch去分發(fā)任務(wù),修改了state,但是redux有一個(gè)問(wèn)題,他只負(fù)責(zé)存儲(chǔ)和更新state,但是不負(fù)責(zé)調(diào)render,這就導(dǎo)致我們的state變化之后頁(yè)面不能刷新,所以,我們還得調(diào)用一下setstate接口來(lái)更新render,那么另一個(gè)問(wèn)題又來(lái)了,什么時(shí)候去setstate呢?我們需要redux告知我們,這個(gè)api就是
store.subscribe
,所以我們?cè)趇ndex.js就給監(jiān)聽(tīng)上
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標(biāo)簽一定不要預(yù)先定義,否則會(huì)編譯為靜態(tài)標(biāo)簽而導(dǎo)致無(wú)法更新
store.subscribe(()=>{root.render(<App />)
})
- 自此,一個(gè)簡(jiǎn)單的redux功能實(shí)現(xiàn)了。
redux demo實(shí)例
- 前面我們用了store和reducer,但是action是什么我并未涉及,接下來(lái)我們看怎么創(chuàng)建一個(gè)action文件count_action.js
const countIncreAction =data=>({type:"INCREMENT",data
})const countdecreAction = (data) => ({type: "DECREMENT",data
})
- 誒,我們發(fā)現(xiàn)一個(gè)現(xiàn)象,這個(gè)寫(xiě)法和我們使用store分發(fā)任務(wù)有點(diǎn)像
decrement=()=>{const {value}=this.sel.current//store去分發(fā)任務(wù)store.dispatch({type: "DECREMENT",data:value})}
- 那我們?cè)趺窗盐覀冏约簩?xiě)的換成action呢,我們來(lái)替換一下,我們先引入,再替換
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)化,我們知道,在代碼中,如果能用常量或者變量代替字符串,那么就用變量或者常量在代碼中使用,那么我們可以使用一個(gè)常量文件來(lái)管理這些
異步action
action 可以返回一個(gè)對(duì)象,交由store去用,但是如果是我們想要一個(gè)異步的action怎么辦呢?我們的對(duì)象里面也不支持自定義時(shí)間啊。redux很明顯不支持,但是有一個(gè)組件提供了這種功能,就是redux-thunk,它通過(guò)store可以接收第二個(gè)參數(shù),以applyMiddleware的形式讓store可以接收f(shuō)unction。我們來(lái)看一下,首先我們創(chuàng)建一個(gè)可以接收f(shuō)unction的store
//引入store
import {applyMiddleware, createStore} from "redux"
import {countReducer} from "./count_reducer"
import {thunk} from "redux-thunk"
//引入reducer
export default createStore(countReducer,applyMiddleware(thunk))
- 那么我們?cè)谑褂玫臅r(shí)候就可以簡(jiǎn)單這樣使用
incrementAsync=()=>{const {value}=this.sel.currentstore.dispatch(countIncreAsyncAction(value,1000))}
react-redux
react-redux是由官方出的一個(gè)組件庫(kù),用來(lái)簡(jiǎn)化對(duì)redux的操作。redux對(duì)組件做了區(qū)分,一種是UI組件,負(fù)責(zé)頁(yè)面展示,一種是容器組件負(fù)責(zé)邏輯處理,既然是這樣,那么和redux能交互的就只有容器組件(container),容器組件拿到數(shù)據(jù)后通過(guò)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>當(dāng)前求和為: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)建一個(gè)container組件
//這個(gè)文件就是容器組件,負(fù)責(zé)和redux交互并傳遞數(shù)據(jù)給UI組件
//引入U(xiǎn)I組件
import {CountUI} from "../../components/count/count"
import {connect} from "react-redux"
//創(chuàng)建一個(gè)
//創(chuàng)建一個(gè)容器組件并暴露
export default connect()(CountUI)
- 我們說(shuō)過(guò),容器不僅要傳遞數(shù)據(jù)給UI組件,還需要和redux交互呢,和redux交互其實(shí)就是調(diào)用store的接口,那么我們?cè)趺春蛃tore聯(lián)系上呢?我們?cè)贏pp里面改一下。
render() {return (<div><Count store={store} /></div>);}
}
connect
具體來(lái)說(shuō),connect接收兩個(gè)參數(shù),
- 一個(gè)是mapStateToProps,默認(rèn)傳入state參數(shù),也就是講state數(shù)據(jù)以props的形式傳遞給子組件
- 一個(gè)是mapDispathcToProps ,默認(rèn)傳入一個(gè)dispatch參數(shù),用來(lái)指定分發(fā)任務(wù)給哪個(gè)action,接下來(lái)我們看一下這redux,component,container的交互
//這個(gè)文件就是容器組件,負(fù)責(zé)和redux交互并傳遞數(shù)據(jù)給UI組件
//引入U(xiǎn)I組件
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,即讓哪個(gè)redux來(lái)處理數(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)建一個(gè)容器組件并暴露
//參數(shù)解釋:mapStateToProps用來(lái)傳遞數(shù)據(jù) mapDispathcToProps用來(lái)指定redux ,返回一個(gè)函數(shù)后,然后指定要綁定哪個(gè)UI組件
export default connect(mapStateToProps, mapDispathcToProps)(CountUI)
- 容器組件寫(xiě)好之后,我們?cè)赨I組件怎么調(diào)用呢,很簡(jiǎn)單,從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>當(dāng)前求和為:{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>);}
}
- 簡(jiǎn)化版
//這個(gè)文件就是容器組件,負(fù)責(zé)和redux交互并傳遞數(shù)據(jù)給UI組件
//引入U(xiǎn)I組件
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 是對(duì)redux進(jìn)行了優(yōu)化,那么react-redux會(huì)不會(huì)在state發(fā)生變化的時(shí)候來(lái)實(shí)現(xiàn)自動(dòng)render呢?答案是肯定的,我們來(lái)試一下
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)聽(tīng)store
// store.subscribe(()=>{
// root.render(<App />)
// })
- provider
我們知道,每個(gè)容器組件我們都需要傳遞一個(gè)store,像這樣
<Count1 store={store} />
<Count2 store={store} />
<Count3 store={store} />
這樣看起來(lái)明顯是不科學(xué)的,那么有沒(méi)有一種方法,讓我們寫(xiě)一次,就可以不用寫(xiě)了呢,有,首先第一點(diǎn)我想到的就是用一個(gè)特殊標(biāo)簽包裹一下這些組件,只要在標(biāo)簽內(nèi)的,都默認(rèn)傳遞了store,react真的這么做了,這餓就是provider ,我們來(lái)看一下怎么用
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里面去,組成一個(gè)文件
多組件數(shù)據(jù)共享
- 我們先來(lái)創(chuàng)建這樣一個(gè)項(xiàng)目目錄,在src目錄下,來(lái)實(shí)現(xiàn)數(shù)據(jù)共享
- 我們先從最簡(jiǎn)單的來(lái),先來(lái)定義action文件
a-action.js
export const AAction = data=>({data:data,type:'AAction'})
b-action.js
export const BAction = data=>({data:data,type:'BAction'})
- 再來(lái)編寫(xiě)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都有了,我們開(kāi)始寫(xiě)store,我們知道創(chuàng)建store的這行代碼
export default createStore(countReducer,applyMiddleware(thunk))
,里面的參數(shù)接收的是一個(gè)reducer,那么如果我有多個(gè)reducer怎么辦呢?我們就用到了combineReducers
//引入store
import {combineReducers, createStore} from "redux"
//引入reducer import {aReducer} from "./reducers/a-reducer"
import {bReducer} from "./reducers/b-reducer"
//將多個(gè)reducer寫(xiě)入一個(gè)對(duì)象 注意是key=>value 格式書(shū)寫(xiě)
const rootReducer = combineReducers({
"areducer":aReducer,
"breducer":bReducer
})
export default createStore(rootReducer)
- 我們?cè)趇ndex.js中引入store,使用provider支持對(duì)容器的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的時(shí)候要根據(jù)前面定義的key來(lái)取
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組件添加用戶,會(huì)在a組件中展示
-
組件數(shù)據(jù)的交互,關(guān)鍵在于給某個(gè)組件state賦值的時(shí)候,
{auser:state.areducer,buser:state.breducer}
-
注意數(shù)組和對(duì)象的地址引用,這種引用只對(duì)比引用地址是否一致,并不會(huì)對(duì)比地址指向的數(shù)據(jù)是否一致,從而導(dǎo)致頁(yè)面不會(huì)更新。
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"
//將多個(gè)reducer寫(xiě)入一個(gè)對(duì)象 注意是key=>value 格式書(shū)寫(xiě)
const rootReducer = combineReducers({
"areducer":aReducer,
"breducer":bReducer
})
export default createStore(rootReducer,composeWithDevTools())
打包
執(zhí)行打包命令npm run build
開(kāi)始編譯,編譯完畢,會(huì)在項(xiàng)目根目錄生成一個(gè)build目錄,我們啟動(dòng)一個(gè)服務(wù)器指定目錄即可執(zhí)行
比如我使用go作為服務(wù)器來(lái)執(zhí)行build目錄,即可正常訪問(wèn)目錄,用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)建一個(gè)處理靜態(tài)文件的handlerhttp.Handle("/", fs)// 啟動(dòng)服務(wù)器并監(jiān)聽(tīng)端口err := http.ListenAndServe(":8088", nil)if err != nil {panic(err)}
}
- 我們也可以使用serve來(lái)啟動(dòng)項(xiàng)目
安裝serve$ npm install -g serve
,啟動(dòng)serve,在build同目錄下,serve build
即可啟動(dòng)服務(wù)。
一般上線的話需要搭配node 或者nginx來(lái)啟動(dòng)。