以前已經把博客作好了,因爲用了antd
實現,界面感受很不完美,並且也有一些bug,最近在找工做,投簡歷,只獲得了一次的面試機會,面試完感受本身作得挺很差的,因而想着再努力一點,繼續作多點項目,因爲博客以前完成得很很差,因此打算把博客重構一遍。前端
redux
redux-saga
redux
流程: 組件派發action
,reducer
接收 action
改變state
,state
改變視圖更新redux
組成
store
:存儲應用中全部的狀態,在 react
中,一般使用react-redux
提供的提供的Provider
組件用做跟組件,提供應用所須要的store
redux
提供的 createStore
方法建立 store
createStore
方法接收三個參數
reducer
:必選,因爲應用中可能存在多個 reducer
管理不一樣的信息,一般狀況下,createStore
方法中的 reducer
都是由combineReducer
方法建立返回的.preloadedState
:可選,應用中最初的的狀態,能夠預先定義一些不須要經過數據請求就能夠展現使用的信息.enhancer
:可選,顧名思義,用於加強 redux
的能力,使用 redux
提供的compose
方法生成,``compose方法內使用
applyMiddleware`把使用到的中間件用做加強子.action
:把數據從應用傳到 store的載體,store
數據的惟一來源,action
本質是一個 JavaScript
對象,必須包含type
屬性,其他能夠根據實際狀況須要增長,大部分狀況下,其他屬性都命名爲 payload
.由dispatch
派發,格式dispatch({type:any,payload?})
reducer
:接收dispatch
派發過來的 action
,根據action.type
的不一樣肯定如何使用action.payload
來到更新 state
redux-saga
:用於在派發 action
前,從有反作用(effect
)的方法生成純淨的 action
=> 即一個type
和payload
的內容都是肯定對象{type:xxx,payload:xxx}
,全部異步中間件的做用都是如此.
redux-saga
流程:
function* getAsync
call/fork
方法調用已經編寫好的異步請求函數,也能夠不使用call/fork
方法,可是異步獲取數據的業務邏輯就須要寫在getAsync
內部.put
方法派發成功的 action
,並把得到的數據放在action.payload
上put
方法派發請求失敗的 action
watchFetchRequest
,內部使用takeEvery
函數,takeEvery
函數的做用是用於接收 action
,在不使用redux-saga
時,能接收 action
的只有 reducer
takeEvery
函數接收兩個參數
type
:行爲標識=>標明action
的類型type
,只要傳入的type
是相應類型的,就執行該對應方法saga.js
文件存在多個不一樣的異步請求,建立一個生成器函數xxxSaga
,內部使用all
方法,同時執行多個執行異步請求的生成器函數,因爲call
方法會阻塞瀏覽器進程,爲了讓異步請求不阻塞,all
方法內被執行的watchFetchRequest
一類的方法使用fork
方法調用,使其不會阻塞瀏覽器進程takeEvery / all
生成的 xxSaga
函數,傳遞給 sagaMiddleWare
src/store/article/types.ts
1.定義數據的類型import { Action } from 'redux'
// 根據從服務器返回的 article 信息定義 Article 類型
export interface Article {
readonly _id: string
readonly content: string
readonly summary: string
readonly category: Category
readonly viewCount: number
readonly createdAt: string
readonly updatedAt: string
}
// 根據從服務器返回 category 的信息定義 Category
export interface Category {
readonly _id: string
readonly name: string
readonly articleCount: number
readonly createdAt: string
readonly updatedAt: string
}
// 定義 store 中存儲的 Article 中狀態信息
export interface ArticleState {
data: Article[]
loading: boolean
error?: any
}
// 定義行爲標識
export const FETCH_REQUEST = 'FETCH_REQUEST'
export type FETCH_REQUEST = typeof FETCH_REQUEST
export const FETCH_SUCCESS = 'FETCH_SUCCESS'
export type FETCH_SUCCESS = typeof FETCH_SUCCESS
export const FETCH_ERROR = 'FETCH_ERROR'
export type FETCH_ERROR = typeof FETCH_ERROR
// 定義行爲
export interface fetchRequestAction extends Action {
type: FETCH_REQUEST
}
export interface fetchSuccessAction extends Action {
type: FETCH_SUCCESS
payload: Article[]
}
export interface fetchErrorAction extends Action {
type: FETCH_ERROR
message: string
}
// 把上面全部的行爲統一使用建立聯合類型 ArticleAction
export type ArticleAction = fetchRequestAction | fetchSuccessAction | fetchErrorAction
複製代碼
src/store/article/action.ts
actionCreator
import { ArticleAction, FETCH_REQUEST, FETCH_SUCCESS, FETCH_ERROR } from './types'
// 發送請求,向 reducer 傳遞正在發送請求這一消息,把 state設置爲 true 改成 true
export function fetchRequest(): ArticleAction {
return {
type: FETCH_REQUEST
}
}
// 請求成功,向 reducer 傳遞請求成功後返回的數據
export function fetchSuccess(data:any): ArticleAction {
return {
type: FETCH_SUCCESS,
payload: data
}
}
// 請求失敗向 reducer 傳遞失敗的錯誤信息
export function fetchError(message:string): ArticleAction {
return {
type: FETCH_ERROR,
message
}
}
複製代碼
src/store/article/saga.ts
saga
import { call, all, fork, put, takeEvery } from 'redux-saga/effects'
import { FETCH_REQUEST } from './types'
import { fetchArticles } from '../../api/article'
import { fetchError, fetchSuccess } from './action'
// 建立異步邏輯處理函數 getArticles
function* getArticles() {
try {
// 使用 call 方法調用從 api 中傳遞過來的 fetchArticle,根據返回結果使用put 方法派發不一樣的 action
const res = yield call(fetchArticles)
res.error ? yield put(fetchError(res.error)) : yield put(fetchSuccess(res.data))
} catch (e) {
let errorResult = e instanceof Error && e.stack ? e.stack : '不知名錯誤'
yield put(fetchError(errorResult))
}
}
// 使用 takeEvery 方法捕獲從應用派發的action.type,調用相對應的方法 getArticles
function* watchFetchRequest() {
yield takeEvery(FETCH_REQUEST, getArticles)
}
// 爲了方便之後應用的擴展,使用 all方法整合當前文件由 takeEvery 捕獲的全部全部 dispatch 做爲 當前類型的 saga
export default function* saga() {
yield all([fork(watchFetchRequest)])
}
複製代碼
src/store/article/reducer.ts
store
的初始狀態及編寫 reducer
函數import { ArticleAction, ArticleState, FETCH_REQUEST, FETCH_SUCCESS, FETCH_ERROR } from './types'
import { Reducer } from 'redux';
// 初始狀態
const initState: ArticleState = {
data: [],
loading: false,
error:undefined
}
const articleReducer: Reducer<ArticleState, ArticleAction> = (state= initState, action) => {
switch (action.type) {
case FETCH_REQUEST:
// 發送請求,將 loading 狀態設置爲 true
return {...state,loading:true}
case FETCH_SUCCESS:
// 請求成功,將 loading 狀態設置爲 false,同時使用從服務器中返回的數據更新 state
const data = action.payload
return {...state,loading:false,data}
case FETCH_ERROR:
// 請求失敗,將 loading 狀態設置爲 false,同時把錯誤信息更新到 state 上
return {...state,error:action.message,loading:false}
default:
return state
}
}
export default articleReducer
複製代碼
src/store/index.ts
import { connectRouter, RouterState } from 'connected-react-router'
import { combineReducers } from 'redux'
import { History } from 'history'
import {all,fork} from 'redux-saga/effects'
// 導入各個文件的 reducer saga,狀態類型
import articleSaga from './article/saga'
import articleReducer from './article/reducer'
import { ArticleState } from './article/types'
// 定義應用的 store 中的 多個 reducers 的類型
export interface ApplicationState {
article: ArticleState
router: RouterState
}
// 使用 combineReducers 建立 rootReducer
export const createRootReducer = (history: History) =>
combineReducers({
article: articleReducer,
router: connectRouter(history)
})
// 使用 all 方法建立 rootSaga
export function* rootSaga() {
yield all([fork(articleSaga)])
}
複製代碼
redux
進行最後配置import { Store, createStore, applyMiddleware, compose } from 'redux'
import createSagaMiddleware from 'redux-saga'
// 使用 使用 router-middle-ware 把瀏覽器 history 掛載到 redux 容器上
import { routerMiddleware } from 'connected-react-router'
// 指明傳遞給 configureStore 的 history 參數爲 History 類型
import { History } from 'history'
// 導入狀態接口,以及通過聯合後的 reducers/sagas
import { ApplicationState, createRootReducer, rootSaga } from './store'
export default function configureStore(history: History,initialState: ApplicationState): Store<ApplicationState> {
// 建立saga 中間件
const sagaMiddleware = createSagaMiddleware()
// 使用 rootReducer/rootSaga,建立 store, 容器的初始狀態(initialState)會在應用的入口傳入
const store = createStore(
createRootReducer(history),
initialState,
compose(applyMiddleware(routerMiddleware(history), sagaMiddleware))
)
sagaMiddleware.run(rootSaga)
return store
}
複製代碼
src/Main.tsx
import React from 'react';
import {Store} from 'redux';
import { ApplicationState } from './store';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';
import {History} from 'history'
// Main 組件須要傳入的屬性分別爲由 redux 建立的 store,由瀏覽器歷史 history
interface MainProps {
store: Store<ApplicationState>
history:History
}
// Main組件: 提供應所需用 store,和 router 的容器
const Main: React.FC<MainProps> = ({ store, history }) => {
return (
<Provider store={store}>
<ConnectedRouter history={history}>
</ConnectedRouter>
</Provider>
);
}
export default Main;
複製代碼
src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { createBrowserHistory } from 'history'
import * as serviceWorker from './serviceWorker'
import configureStore from './configureStore'
import Main from './Main'
import { History } from 'history'
const initialState = window.INITIAL_REDUX_STATE
const history: History = createBrowserHistory({
basename: '/'
})
const store = configureStore(history, initialState)
ReactDOM.render(<Main store={store} history={history} />, document.getElementById('root'))
serviceWorker.unregister()
複製代碼
以上就是就是博客基礎狀態共享的框架搭建和代碼.依據以上的框架,當須要添加新的狀態時,能夠很輕易的添加到對應的文件中,若是有新的 reducer
須要建立,只須要在對應的步驟中加入新的reducer/saga
便可react
求職中,若是有須要初級前端的歡迎聯繫,電話/微信同號 13416179124, QQ 264589826git