小程序模板網(wǎng)

微信小程序項目重構(gòu)之Redux狀態(tài)管理

發(fā)布時間:2018-01-24 10:49 所屬欄目:小程序開發(fā)教程

1.以往處理狀態(tài)的一些方式第一種:App上掛globalData 這種方式就是直接寫在App里面啦,取值賦值比較方便App.jsApp({globalData: {name: '前端學(xué)者',// ...other globalData},// ...other globalData});// 取值 var ...

 
 
 

1.以往處理狀態(tài)的一些方式

第一種:App上掛globalData 這種方式就是直接寫在App里面啦,取值賦值比較方便

App.js

  1. App({
    globalData: {
    name: '前端學(xué)者',
    // ...other globalData
    },
    // ...other globalData
    });

    // 取值 var name = getApp().globalData.name;
    // 賦值
    getApp().globalData.name = '改個名字';

第二種:自己定義一個或者多個用于存儲數(shù)據(jù)的libData文件,自己造輪子,愛折騰的就直接擼吧

例如libData.js

module.exports = {
  name: '前端學(xué)者',
    // ...other globalData
}
// ...other // 簡單使用 var store = require('./path/to/lib/libData.js');
// 取值 var name = store.name;
// 賦值
store.name = '改個名字';
 

第三種:把要使用的數(shù)據(jù)存在頁面data或者直接存在頁面上,比較分散而且不利于管理和維護,但是對于超小且需要快速實現(xiàn)的項目也足夠了。別笑,我們前端不怕這樣寫,O(∩_∩)O哈哈~

dataInPage.js
Page({
  data: {
    name: '前端學(xué)者'
  },
  name: '前端學(xué)者'
});

// 取值 var name = this.data.name;
var name = this.name;
// 賦值 this.setData('name', '改個名字');
this.name = '改個名字');

2.對于處理狀態(tài)數(shù)據(jù),有時候可能還會混著使用上面的幾種方式,隨著項目復(fù)雜度提升,狀態(tài)將難以管理。

所以呢,我們需要借鑒web端現(xiàn)有的一些優(yōu)秀成熟的方案,今天的主角就是它了,大家都可能很熟悉,歡迎Redux出場!網(wǎng)上各類的資料已經(jīng)很詳細地介紹,不清楚的同學(xué)務(wù)必了解Redux,這里只詳細說明一下怎么在小程序里面使用,和在React中的用法非常相似,因為核心庫mina-redux.js(原名wechat-weapp-redux)的作者就是這么改過來的,這個輪子已經(jīng)非常棒了。下面直接上代碼了,代碼雖然多,但具有實站參考價值,所以直接貼上來了。

不使用任何構(gòu)建工具,把微信開發(fā)者工具中項目設(shè)置的鉤子全部打上,大方地使用import和export吧,現(xiàn)在不需要自己寫構(gòu)建了

  • ES6 轉(zhuǎn) ES5
  • 上傳代碼時樣式自動補全
  • 代碼上傳時自動壓縮
  • 不校驗安全域名、TLS版本以及HTTPS證書

核心還是Redux,和幾個好用的相關(guān)庫,這幾個都是從網(wǎng)上精簡了拿過來的,沒有用到npm等包管理工具

 

lib文件夾下:

  • redux.js(核心文件,必選)
  • redux-logger(core.js, deep-diff.js, defaults.js, diff.js, helpers.js, index.js 這些都是為了打印日志用的,可選)
  • redux-thunk.js(異步action中間處理,推薦)
  • mina-redux.js(核心文件,把redux的dispatch和倉庫數(shù)據(jù)同步到頁面上,必選)
  • remote-redux-devtool.js(遠端調(diào)試使用,可以直觀看到數(shù)據(jù)流向,非常強大,可選)
  • remotedev-server.js(遠程調(diào)試服務(wù),可選,方便調(diào)試)
  • regenerator-runtime.js(facebook的異步庫,與redux沒有任何關(guān)系,可選,極力推薦)

先寫redux相關(guān)的type action reducer

 

app.redux.js (個人喜好把type action reducer寫在一個文件)

/**
 * 處理小程序生命周期和載入的一些數(shù)據(jù)
 */ import regeneratorRuntime from '../lib/regenerator-runtime';
import util from '../lib/util';
import api from '../lib/api';
import { minaScecne } from '../config/config';

const WX_SYSTEM_INFO = 'WX_SYSTEM_INFO';
const WX_USER_INFO = 'WX_USER_INFO';
const WX_ON_LAUNCH = 'WX_ON_LAUNCH';

const WX_GET_CODE = 'WX_GET_CODE';
const WX_API_PROMISIFY = 'WX_API_PROMISIFY';

const LOGIN_BEGIN = 'LOGIN_BEGIN';
const LOGIN_PENDING = 'LOGIN_PENDING';
const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
const LOGIN_FAIL = 'LOGIN_FAIL';

// 用法1 // 不用中間件,一個action每次只能dispatch一個action export const wxApiPromisify = () => ({
  type: WX_API_PROMISIFY,
  data: util.initWxMethod()
})

export const wxSystemInfo = (data) => ({
  type: WX_SYSTEM_INFO,
  data: data
})

export const wxUserInfo = (data) => ({
  type: WX_USER_INFO,
  data: data
})

export const wxOnLaunch = (data = {}) => {
  const { scene, query, path } = data;
  Object.keys(minaScecne).map((item => {
    if (scene.toString() === item) {
      data.sceneText = minaScecne[item]
    }
  }));
  return {
    type: WX_ON_LAUNCH,
    data: data
  }
}

// 用法2 // app上以store.dispatch(actionMethodName)的方式調(diào)用,可以組合action export const login = async dispatch => {
  const wxLoginResult = await wx.async.login();
  await dispatch({
    type: WX_GET_CODE,
    data: wxLoginResult
  })
  await dispatch({
    type: LOGIN_BEGIN,
    data: {
      isFetching: false
    }
  })
  await dispatch({
    type: LOGIN_PENDING,
    data: {
      isFetching: true
    }
  })
  if(wxLoginResult.code){
    try {
      const apiLoginResult = await api.user.login({ code: wxLoginResult.code });
      await dispatch({
        type: LOGIN_SUCCESS,
        data: apiLoginResult
      })
      wx.setStorageSync('token', apiLoginResult.data.token)
    } catch (error) {
      await dispatch({
        type: LOGIN_FAIL,
        data: error
      })
    }
  } else {
    await dispatch({
      type: LOGIN_FAIL,
      data: wxLoginResult.errMsg
    })
  }
}

// 用法3 // 使用thunk中間件可以對一個action進行細化出多個狀態(tài),比如請求,可以做用于處理ui loading // TODO 寫個request action helper 封裝一下,不用每次寫那么多... export const thunkExampleAction = () => (
  async dispatch => {
    const wxLoginResult = await wx.async.login();
    await dispatch({
      type: WX_GET_CODE,
      data: wxLoginResult
    })
    await dispatch({
      type: LOGIN_BEGIN,
      data: {
        isFetching: false
      }
    })
    await dispatch({
      type: LOGIN_PENDING,
      data: {
        isFetching: true
      }
    })
    if(wxLoginResult.code){
      try {
        const apiLoginResult = await api.user.login({ code: wxLoginResult.code });
        await dispatch({
          type: LOGIN_SUCCESS,
          data: apiLoginResult
        })
        wx.setStorageSync('token', apiLoginResult.data.token)
      } catch (error) {
        await dispatch({
          type: LOGIN_FAIL,
          data: error
        })
      }
    } else {
      await dispatch({
        type: LOGIN_FAIL,
        data: wxLoginResult.errMsg
      })
    }
  }
);

// 倉庫數(shù)據(jù)結(jié)構(gòu)預(yù)定義 const initState = {
  wxApiPromisify: {
    isFinish: false
  },
  wxSystemInfo: {},
  wxUserInfo: {},
  wxOnLaunch: {},
  wxLogin: {
    code: null,
    token: null,
    userId: null,
    phone: null,
    errMsg: null
  },
  userLogin: {
    isFetching: false,
    data: null
  }
};

export const appReducer = (state = initState, action) => {
  switch (action.type) {
    case WX_API_PROMISIFY:
      return Object.assign({}, state, { wxApiPromisify: { isFinish: action.data } });
    case WX_GET_CODE:
      return Object.assign({}, state, { wxLogin: action.data });
    case WX_SYSTEM_INFO:
      return Object.assign({}, state, { wxSystemInfo: action.data});
    case WX_USER_INFO:
      return Object.assign({}, state, { wxUserInfo: action.data});
    case WX_ON_LAUNCH:
      return Object.assign({}, state, { wxOnLaunch: action.data});
    case LOGIN_BEGIN:
      var userLogin = action.data;
      userLogin.isFetching = false;
      return Object.assign({}, state, { userLogin });
    case LOGIN_PENDING:
      var userLogin = action.data;
      userLogin.isFetching = true;
      return Object.assign({}, state, { userLogin });
    case LOGIN_SUCCESS:
      var userLogin = action.data;
      userLogin.isFetching = false;
      return Object.assign({}, state, { userLogin });
    case LOGIN_FAIL:
      var userLogin = action.data;
      userLogin.isFetching = false;
      return Object.assign({}, state, { userLogin });
    default:
      return state;
  }
};

app.js 小程序啟動入口

import regeneratorRuntime from './lib/regenerator-runtime';
import util from './lib/util';  // 自定義的工具庫 import { createStore, applyMiddleware, combineReducers, compose } from './lib/redux';
import thunk from './lib/redux-thunk';
import { Provider } from './lib/mina-redux';

// 所有的reducer 主要分為小程序相關(guān)和業(yè)務(wù)相關(guān)兩類 import { appReducer } from './redux/app.redux';
import { channelReducer } from './redux/channel.redux';
import { practiceReducer } from './redux/practice.redux';

// 小程序項目常用的操作相關(guān)action,如處理小程序啟動option,小程序api Promise化,系統(tǒng)信息,小程序用戶信息,api登錄等 import { wxOnLaunch, wxApiPromisify, wxSystemInfo, wxUserInfo, login } from './redux/app.redux';

import composeWithDevTools from './lib/remote-redux-devtool';
import logger from './lib/redux-logger/index';

// 創(chuàng)建倉庫 export const store = createStore(combineReducers({
  appReducer,
  channelReducer,
  practiceReducer,
}), compose(
  applyMiddleware(thunk, logger),
  composeWithDevTools({ hostname: '127.0.0.1', port: 8080, secure: false })
));


App(Provider(store)({
  async onLaunch(option) {
    // 處理小程序啟動option
    store.dispatch(wxOnLaunch(option));
    
    // 小程序api Promise化
    store.dispatch(wxApiPromisify());

    // 獲取小程序系統(tǒng)信息
    store.dispatch(wxSystemInfo(wx.getSystemInfoSync()));

    // 獲取小程序用戶信息
    store.dispatch(wxUserInfo(await wx.async.getUserInfo()));

    // 用戶登錄(小程序登錄 + api登錄)
    login(store.dispatch);
  }
}));

home.js 頁面

import regeneratorRuntime from '../../lib/regenerator-runtime';
import util from '../../lib/util';
import { connect } from '../../lib/mina-redux';
import { getChannelList } from '../../redux/channel.redux';

const pageConfig = {
  data: {
    userLogin: {
      data: null,
    },
    channelList: {
      data: [],
      isFetching: false,
    }
  },
  async onLoad(option) {
    try {
      console.log(`? ${getCurrentPages().slice(-1)[0]['__route__']} onLoad`);
      util.initPageOnLoad.call(this, option);

      wx.showLoading({
        title: '加載中...',
        mask: true,
      });

      // 首先一切后端一切api調(diào)用必須在登錄后,這里首先等待app生命周期里面拿到登錄權(quán)限token await util.waitfor(this.data, ['userLogin', 'data', 'token'], 0, null, 50);

      // 之后就可以愉快地進行各種api數(shù)據(jù)流了, await this.getChannelList();
      wx.hideLoading();
    } catch (error) {
      console.error(`? `, error);
      // TODO
    }
  }
}

// 定義頁面上的data,這個是自動注入到page data上的,需要什么就用什么,據(jù)說小程序有page data的性能瓶頸,按需注入吧 const mapStateToPage = state => ({
  ...state.appReducer,
  ...state.channelReducer,
});

// 定義頁面上的方法,直接this.actionName(param)就可以了調(diào)用了 const mapDispatchToPage = dispatch => ({
  getChannelList: async () => {
    await dispatch(getChannelList());
  }
});

// 串起來 const nextConfig = connect(mapStateToPage, mapDispatchToPage)(pageConfig);

Page(nextConfig);

3.雖然代碼很長,看起來可能很不爽,但是個人覺得已經(jīng)比之前優(yōu)雅多了,稍微熟悉一下就好,最后看下最終效果(^__^) 嘻嘻……

先開啟調(diào)試,然后瀏覽器訪問http://localhost:8080即可,或者使用Redux DevTools調(diào)試插件

$ npm install -g remotedev-server 
$ remotedev --hostname=localhost --port=8080

[Busy] Launching SocketCluster
   [Active] SocketCluster started
            Version: 6.8.0
            Environment: dev
            WebSocket engine: uws
            Port: 8080
            Master PID: 14961
            Worker count: 1
            Broker count: 1 

微信開發(fā)者工具 AppData pages/home/home 數(shù)據(jù)截圖

微信開發(fā)者工具 Console 日志截圖

 

Redux RemoteDev 工具 state數(shù)據(jù)結(jié)構(gòu)Chart圖

Redux RemoteDev 工具 action時間軸及Tree結(jié)構(gòu)

 

Redux RemoteDev 工具 Log monitor

 

Redux DevTools具有很多強大的功能,如傳說中的“時光穿梭”等,可以實現(xiàn)很多很好玩的功能

 


易優(yōu)小程序(企業(yè)版)+靈活api+前后代碼開源 碼云倉庫:starfork
本文地址:http://m.u-renovate.com/wxmini/doc/course/19610.html 復(fù)制鏈接 如需定制請聯(lián)系易優(yōu)客服咨詢:800182392 點擊咨詢
QQ在線咨詢
AI智能客服 ×