博客重构记录 一 数据框架搭建

159 阅读7分钟

前言

之前已经把博客做好了,由于用了antd实现,界面感觉很不完美,而且也有一些bug,最近在找工作,投简历,只得到了一次的面试机会,面试完感觉自己做得挺不好的,于是想着再努力一点,继续做多点项目,由于博客之前完成得很不好,所以打算把博客重构一遍。

  • 目标
    • 使用全新的 UI 界面,界面参考
    • 中前台分离
      • 我备案的域名在审核,这几天应该就下来,下来之后部署好 HTTPS,前台 80,443 端口只负责静态页面的展示,不再涉及登录操作,博客内容管理统一放在中台管理
    • 前台添加 添加 github 登录接口
    • 增加评论、点赞功能。
    • 暂时需要实现这三个功能,再多的功能以后想到再添加

博客项目数据操作框架搭建框架搭建

  • 项目使用的库
    • 数据管理 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=> 即一个typepayload的内容都是确定对象{type:xxx,payload:xxx},所有异步中间件的作用都是如此.
      • redux-saga流程:
        1. 创建一个异步发送异步请求的生成器函数 function* getAsync
          • 在函数内部使用 call/fork方法调用已经编写好的异步请求函数,也可以不使用call/fork方法,但是异步获取数据的业务逻辑就需要写在getAsync内部.
          • 异步请求成功,使用put方法派发成功的 action,并把获得的数据放在action.payload
          • 异步请求失败,使用put方法派发请求失败的 action
        2. 创建一个生成器函数watchFetchRequest,内部使用takeEvery函数,takeEvery函数的作用是用于接收 action,在不使用redux-saga时,能接收 action的只有 reducer
          • takeEvery函数接收两个参数
            • type:行为标识=>标明action的类型
          • 执行函数:通过第一个参数的标明的type,只要传入的type是相应类型的,就执行该对应方法
        3. 可选:saga.js文件存在多个不同的异步请求,创建一个生成器函数xxxSaga,内部使用all方法,同时执行多个执行异步请求的生成器函数,由于call方法会阻塞浏览器进程,为了让异步请求不阻塞,all方法内被执行的watchFetchRequest一类的方法使用fork方法调用,使其不会阻塞浏览器进程
      • 最后,导出由使用takeEvery / all生成的 xxSaga函数,传递给 sagaMiddleWare
    • 流程都梳理好了,那就开始创建文件,书写代码
      1. 创建行为标识以及数据类型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
        
      2. src/store/article/action.ts
        1. 定义 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
            }
          }
        
      3. src/store/article/saga.ts
        1. 创建 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)])
         }
        
      4. src/store/article/reducer.ts
        1. 定义 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
        
      5. 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)])
        }
        
        
      6. ```/src/configureStore.ts`
        1. 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
         }
        
      7. 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;
        
      8. src/index.tsx
        1. 应用程序主入口
         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即可

求职中,如果有需要初级前端的欢迎联系,电话/微信同号 13416179124, QQ 264589826