產品的反饋系統設計

什麼是反饋系統? 

  反饋系統(feedback system)是具備閉環信息通道的系統。前端

  定義: 將系統的後果或輸出信息採集、處理,而後送回輸入端並據此調整系統行爲的系統。因爲信息流通構成閉合環路, 它也稱爲閉環系統。 反饋做用經常用於檢測信號誤差以及對象特性的變化,並以此來控制系統行爲以及消除偏差。 它又被稱爲反饋控制或者按照偏差控制的系統。node

 

爲何須要反饋系統?

  1. 用戶的須要。用戶在使用過程當中會遇到問題,對於此問題他但願可以有渠道去反饋,主要目的有三。
    1. 其一: 提出對於產品的一些我的意見,幫助開發者更好地改進產品。
    2. 其二: 在使用過程當中可能遇到產品的某些bug,須要渠道來宣泄本身的情緒。
    3. 其三: 在使用過程當中遇到了某些問題,如找不到入口、登陸出錯等等,用戶須要得到及時的解答。 
  2. 產品開發人員的須要。開發人員尤爲是創業團隊是更加須要反饋系統的,主要目的有二。
    1. 其一: 及時瞭解產品的問題與不足並改進產品,以此來不斷地優化產品。
    2. 其二: 對於用戶的問題做出及時的解答,提高產品的用戶體驗。 

 

反饋系統的類型有哪些?

  主要能夠分爲自建的反饋系統和使用第三方工具做爲反饋系統:react

  1. 自建的反饋系統。
    1. 郵件形式 --- 即用戶將反饋信息提交以後經過郵件的形式發送到開發人員,而後開發人員進行郵件形式的回覆。 
    2. 對話形式 --- 即用戶的反饋會直接顯示在頁面上, 開發人員的回覆、用戶的追問等以對話的形式顯示出來。
    3. 工單形式 --- 即用戶的反饋會以工單的形式顯示, 其處理的過程包括處理中、處理完畢等具體流程會顯示出來。
    4. 需求池形式 --- 即用戶的反饋會所有顯示出來。 需求池就是對全部需求的集合,可是開發人員須要及時地對需求池進行優化。
  2. 第三方工具做爲反饋系統。
    1. QQ
    2. 微信
    3. 官方微博
    4. 微信公衆號

 

具備反饋系統的網站推薦

郵件形式:webpack

  

  如Tower,咱們在 https://tower.im/help 的底部能夠看到下面的界面:es6

  進入幫助頁面,若是咱們沒有找到答案,就能夠在底部發現這樣的一個反饋系統,郵箱是網站默認填充的, 而後咱們能夠在內容框中填寫文字、圖片等, 開發、客服人員收到郵件以後會經過郵件的方式發送給用戶。 web

  顯然,這種方式是封閉的,只有發問的用戶能夠收到解決郵件,而其餘用戶看不到更多用戶的問題與回答。 mongodb

  幾分鐘以後,我就接受到了這樣的一封反饋郵件,很好地解決了個人問題, 能夠看到,Tower的入口也是很是方便地。typescript

  

 

 

  

對話形式:數據庫

  

  叮噹網站就是這種對話形式的反饋,在右下角有一個留言的圖片,點擊這個圖片以後就會進入一個對話的頁面,這個頁面還可使用一個新的iframe來顯示,以下所示:npm

      

  這種方式最大的好處就是直觀、方便追問、具備及時性, 可是不太方便跟蹤員工處理的進度狀況。 

 

 

 

工單形式:

  

   在阿里雲的網站上,咱們能夠添加建議並提交,提交以後咱們能夠看到顯示以下:

  能夠看到提交以後,就會造成一個工單,實時的反映當前狀況, 即 ‘已提交’、‘預審經過’、‘已採納’、‘已實現’ 幾個步驟。 對於僅僅是提交的建議,咱們還能夠進行再次編輯,另外,全部的建議其餘用戶也都是能夠看到的,全部人(固然包括管理員)能夠進行反饋、評論、投票等功能。 

  在此網站能夠查看:https://connect.aliyun.com/suggestion/5293?spm=5176.8409797.user.1.38c60aa8v2lqt7

  

 

 

 

需求池形式:

  

  

  https://www.mockplus.cn/

  這是一個作原型的工具,更爲高效、簡單。

 

 

反饋系統基本框架

   

   從反饋系統的基本框架來看,此係統在在前端至少須要兩個頁面(或者說是網站) --- 用戶反饋網站 以及 後臺管理網站。

前端用戶反饋頁面

第一部分:

  即須要在網站的的主頁面或者幫助頁面處提供一個入口,由此進入反饋界面,在反饋界面中的須要提供一個表單用於提交反饋,內容以下:

  • 表單的字段裏須要包括用戶的userName等基本信息用做後臺記錄。
  • 選擇提交反饋的類型
    • 通常問題。如界面UI問題、網站進不了、沒法登錄等等問題。 
    • 產品問題。須要具體制定某個產品,即針對某個產品進行的反饋。若是這個反饋插件只是放在了某一個特定產品的網站上,那麼咱們就不須要選擇產品了,若是反饋插件放在了一個公司(這個公司下有不少產品)網站上,咱們就須要讓用戶選擇特定的產品。
  • 輸入反饋的具體內容。 能夠輸入文字,也能夠輸入圖片。 

  輸入完成以後提示已提交至後臺便可。 

 

  另外,在反饋界面,咱們還能夠列出一些常見的問題和回答,這樣,用戶也許就不須要去提問了。

 

 

第二部分:

  固然,除了一個提交反饋的表單仍是不夠的,還須要一個「個人反饋」界面。

  即在第一部分中,咱們反饋進行了提交,在「個人反饋」界面就須要展現對於個人全部反饋的詳情了。 

  • 這個反饋的詳情是後臺管理員進行回覆的。
  • 用戶能夠在管理員回覆的基礎上進行追問 。

     既然有個人反饋界面,這也就是說每次你進入這個網站的時候仍是須要惟一標識的,那麼就須要使用登陸和註冊功能,這樣才能在你下一次登陸的時候將你的相關的建議展現出來。 

 

 

前端後臺管理界面

  後臺管理界面會稍微複雜一些。 

  • 能夠查看全部產品列表。
  • 能夠建立新的產品。
  • 能夠查看某產品的反饋列表。
  • 能夠關注產品。 由於每個使用後臺管理的人多是不一樣的,有人是爲了關注 maxhub 相關產品,有人是爲了關注 seewo 相關的產品。關注以後,當有新的消息時,就會給出後天相應的提示。 沒有關注的也是能夠看到反饋列表的,只是不會給出提醒。
  • 能夠查看某個反饋的詳情。
  • 能夠回覆單個反饋(採用堆樓模式)。
  • 能夠操做單個反饋,實現狀態的改變(新建、已回覆(回覆後自動確認)、已關閉(關閉後自動修改))。
    • 新建 --- 即一個新的反饋,須要及時的回覆。 
    • 已回覆 --- 即對於新的反饋,已經給予了回覆。
    • 已關閉 --- 即咱們看到已經回覆完成以後,對方也滿意了,就能夠關閉這個反饋了,那麼咱們就不須要再去常常查看了 。 那麼此時應該通知客戶端已經關閉了反饋、而且不可再回復了嗎? 實際上也是能夠的,既然已經解決了問題,就能夠關閉了。
  • 條件搜索。 根據某個條件,搜索到相應的用戶問題。

 

重要: 能夠作成推送平臺的js插件!

  • 在任何地方引入以後,能夠在頁面上添加入口。
  • 點擊入口以後展現一個簡單的反饋界面,能夠提交反饋。
  • 考慮js插件的可配置性(入口位置、顏色、文案等等)。
  • js插件支持移動端。 

 

作成插件以後,咱們就能夠在多個產品的主頁上使用了。 這種場景每每是用於一家較大的公司,公司旗下有不少的產品,每一個產品可能都會單獨建站,在每個網站上,咱們能夠添加一個特定產品的js插件,然後臺管理頁面是保持基本不變的。 

 

 

 

 

後臺接口

  登陸、註冊、請求反饋界面、請求後臺管理界面、添加產品、請求全部產品、請求某個產品下的全部反饋(每條反饋是一個文檔,每一個文檔中包含了反饋的具體信息,題目,信息,評論(評論須要包括評論人、評論時間等),狀態、所屬用戶等等,鍵值對不是簡單的string,設計的有規律一些便可。), 請求某個用戶的全部建議(在某個產品下去查找便可,那麼保存用戶信息時還須要保存用戶所使用到的產品類別,這樣在搜索產品時會比較快), 

 

  

最終的項目架構

這個項目分紅了兩個文件來作,一個做爲主服務器,另外一個做爲輔助服務器,那個輔助服務器在發送請求時是代理到主要的服務器上的。前端採用基本的分層方式,後端採用的是MVC的架構方式。 

build是服務器相關文件。 model是數據庫的相關文件。 node_modules是存放的一些包。 router是存放的路由文件。 src是react的相關文件(src中components存放一些基本的組件,pages存放整個的頁面, redux存放的是項目狀態管理的相關文件,index.tsx是react項目的渲染頁面)。 www是服務器上存放的靜態文件。 最後就是基本的 package.json、settings.js(數據庫配置)、tsconfig.json(typescript的相關配置文件)、webpack.config.js(webpack相關文件)。 

 

 

 

 

遇到的問題

一、應該創建幾個文件,即開啓幾個服務器(項目)? 

第一種方式: 開啓一個服務器。

  即認爲只有一個服務器,在用戶點擊按鈕時,觸發一個請求,從後端請求到反饋系統的頁面,這樣就能夠進行簡單的操做了。 

  對於管理員,也能夠請求到服務器端的一個管理員的頁面。 

  這樣在技術上是能夠作到的,可是在前端處理上會出現問題。 好比使用webpack打包的時候,就會把全部的js打包,可是實際上在用戶和管理員處所使用的js並非全部的,這樣就會形成浪費。 

  

第二種方式: 開啓兩個服務器

  這樣的方式最簡單、清楚、明瞭。 

 

結果: 實際上,咱們應該當作兩個項目來作,也就是說,須要同時進行兩個項目來作,一個項目用於寫後天管理系統(較多主要是數據庫的處理),另外一個項目主要是寫前端反饋系統。 

 

二、 這個任務的實際使用場景是這樣的嗎?

  即管理員的界面始終只有一個,而用戶反饋界面的界面是會隨着不一樣產品的改變而改變的,會有不一樣的網站來進行請求。 

 

 結果

  • 反饋平臺是比較核心的,而且這個能夠看作是固定的,由管理員來使用,而且反饋平臺管理員端的後臺代碼也是比較比較重要的,經過其爲反饋系統提供API。 

 

三、 使用react、 ant.design能夠嗎? 

 結果: 能夠嘗試使用 ant.design , 由於 ant.design 是螞蟻的一套前端框架,適合react項目的使用,可能會遇到一些坑,可是仍是能夠嘗試考慮使用的。

 

 

四、 這裏所說的插件究竟是什麼?怎麼去理解? 可配置是說添加一個配置文件、配置對象嗎? 

   插件實際上就是一段js代碼,經過這個js代碼,咱們能夠將這個js插件暴露出來的方法進行 init 而後適用在某個產品上。 

 

 

五、什麼是堆樓模式? 這裏所使用的反饋系統是工單形式的嗎?

   即評論是一層一層的,而不是縮進的形式。 反饋系統能夠看做是工單形式的。  

 

 

六、總體的思路應該是怎樣的? 

  對於後臺管理界面是直接使用node做爲後臺,而後使用react來寫前臺管理界面,這個是通用的。 另外,這個後臺除了提供管理界面以外,也應該提供反饋平臺所須要的接口。 另外,還須要一個通用的普通端,這裏能夠本身選產品的類型等。 

 

 

七、數據庫的設計應該是怎麼樣的?

   在數據庫中,咱們須要存儲的信息包括管理員信息、用戶信息、建議信息、評論信息、產品信息等等, 對於這些信息,咱們應當儘可能採用扁平化的風格進行存儲。使用鏈接的方式。 

  (1)產品存儲

  • 產品名稱 (name) --- 用於在用戶獲取到以後在前端顯示出來。
  • 產品ID (productId) --- 用於惟一的標識產品。 
  • 產品被建立的時間 (savedTime) --- 存儲了時間以後,在顯示產品時咱們就能夠採用產品建立時間的前後順序來獲取,這樣就是有序的了。
  • 關注該產品的人(relatedPersonId) --- 即若是某用戶關注了該產品, 咱們就將這我的的ID添加進來,這樣用戶在獲取 我關注的產品 時,就能夠經過查詢數據庫查詢到相應的產品。 而且做爲管理員,應該不是不少,因此說將關注該產品的管理員加入也是能夠的。
  • 建立該產品的人 (createdPersonId)--- 只有建立了這個產品的人才可以刪除這個產品。

  (2)建議存儲

  • 建議的ID(suggestionId)--- 惟一的標識一條建議。
  • 提建議的人(person) --- 用於在展現產品時展現出提建議的人。
  • 提建議的人的Id --- 用於建立該建議的用戶進行查找。
  • 建議建立的時間 (suggestionTime) --- 用於在獲取產品時,針對時間進行排序;展現。
  • 相關的產品ID(productId) --- 在列舉產品的建議時,能夠根據建議中的這個字段獲取到全部的相關建議。
  • 建議的類型(type) --- 在列舉某個產品的建議時,咱們能夠針對功能建議、產品缺陷、產品需求三個方面來分別展現相關的建議。
  • 建議內容(content)--- 這個固然是不可少的,由於咱們須要展現出來。
  • 建議狀態(status)--- 用戶發出一個建議以後,須要管理員來回復處理,好比已提交、被拒絕、已接納、實現建議的內容等等。 

  (3)評論存儲

  • 此條評論所屬的建議的ID(suggestionId),這樣,在列舉建議的評論時,咱們就能夠查找全部suggestionId的建議。
  • 評論時間(time) --- 進行展現的排序。
  • 評論人(person) --- 用於在前臺進行展現。
  • 評論內容(content) --- 存儲評論的內容。
  • 評論ID --- 惟一的標識這一條評論。
  • 評論類型(type) ---  管理員的type爲一、而用戶的type爲2。

  

      (4)管理員存儲

  • 管理員名稱 ---(用於顯示)能夠是手機號。
  • 管理員Id(id)--- 用於尋找全部的本身關注的產品。
  • 管理員類型 --- 若是是1,表示這是一個管理員,若是是2,表示這是一個用戶。
  • 管理員建立的產品Id(產品Id是在建立的時候分配的) --- 只有建立了這個產品的人才能對這個產品進行操做。

  (5)用戶存儲

  • 用戶名稱 --- 用於顯示。
  • 用戶Id --- 用於查找本身的全部的建議。
  • 用戶類型 --- 若是是1,表示這是一個管理員,若是是2,表示這是一個用戶。

 

  即在數據庫中須要創建5個集合,這樣咱們就能夠進行簡單的系統操做了。能夠看到,上面的數據庫的設計儘可能是扁平化的,而沒有進行扎推。 

  而且咱們應當提升可重用性,不要在不一樣的表中創建了太多相同的內容,這是不合適的。

 

 

八、 ant.design究竟應該怎麼用? 

  咱們在使用的時候應該儘量多地去考慮其源碼,這樣,咱們才能更理解,也能有所學習。

  

 

 

九、  管理系統和用戶端對於建議的狀態管理應該是怎麼樣的? 

用戶端

  • 用戶提交建議,用戶端顯示爲已提交

  沒錯,用戶的權限很小,用戶只能提交以後,看到其答案已經提交了,後續的工做就徹底取決於管理員了。

管理端

  • 用戶提交了建議以後,管理端顯示爲新建議。 
  • 這時管理員應該及時的將狀態修改成審覈中,這樣用戶就能夠直接看到建議的最新狀態,接着和團隊、小組成員進行討論建議的可實施性,也許會持續一段事件,並做出及時地回覆。
  • 討論結束以後,管理員能夠決定審查未經過,但必定要給予用戶進行充分的解釋,那麼這個問題就能夠關閉了或者是決定已採納。及時的將信息顯示在用戶端界面。 
  • 若是是 已採納,那麼管理端就須要及時解決,而後將信息及時通知給客戶端 。 

 

因此說,對於建議的管理,主動權徹底在管理端,這樣纔會比較好控制數據,不至於混亂,就像redux的單向數據流的方式是同樣的。

 

 

十、登陸、註冊這部分應該怎麼作?  管理員、用戶、產品、建議的ID怎麼設置能夠保證沒有大的問題? 怎麼保證不會重複? 表的設計是否有問題。

  一、 對於惟一ID,咱們可使用 uuid  (在項目中咱們直接 npm install uuid --dev 便可),這個工具能夠幫助咱們快速解決問題,而且ID是不會重複的。

  二、 對於登陸、註冊的問題,咱們能夠把這個反饋的網站看作一個黑盒子,而後只須要對之有肯定的輸入, 就能保證其輸出,因此,咱們能夠將其i做爲插件來想, 對於登陸、註冊的事情由不一樣的網站來解決便可。 而咱們須要作的就是在後臺處理好便可。  而在管理端仍是比較容易理解的,就是必須登陸註冊才能查看產品等等。

 

 

十一、 uuid介紹。

參考文章: https://www.npmjs.com/package/uuid
           http://www.jianshu.com/p/d553318498ad

 

  UUID是128位的全局惟一標識符,一般由32字節的字符串表示。它能夠保證時間和空間的惟一性,也稱爲GUID,全稱爲:UUID ―― Universally Unique IDentifier,Python 中叫 UUID。

  

 

十二、 如何實現代理服務器呢?

  很簡單,咱們打開了兩個項目,可是咱們只但願在一個項目的服務器上處理各類服務器、數據庫的數據, 這樣,咱們直接將次服務器直接代理到主要的服務器便可。以下所示:

第一步:

npm install  http-proxy-middleware --save-dev

 

第二步:

在dev-server.js中,須要引入 http-proxy-middleware,而後:

var proxyTable = {
        '/api': {
              target: 'http://127.0.0.1:8000/', // 本地node服務器
              changeOrigin: true,
              pathRewrite: {
                   '^/api': '/'
              }
          }
      };
        

上面就是咱們的基本設置,固然,在proxyTable中能夠代理多個服務器,但這裏咱們只須要一個。 思路就是對api代理,而後發出的時候再重寫。

 

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(options.filter || context, options))
})

ok! 就是這麼簡單了,這樣就能夠完成服務器的代理了。 

 

 

下面咱們就能夠發出一個請求了:

  

    componentWillMount () {
        fetch("/api/getAllProduct", {
            method: "GET"
        }).then(function(res) {
            console.log('進入');
            return res.json();
        }).then(function (data) {
            console.log(data);
            if (data.code == 200) {
              console.log('獲取到全部產品' ,data.data);
            } else {
              console.log(data.message);
            }
        })
    }

 

即: 這裏咱們在 localhost: 3000 的服務器下發出了指向 localhost:8000 服務器的資源。

 

 

1三、屢次看到服務器崩潰! 爲何呢?

 

即如上所示,乍一看彷佛並無什麼解決的辦法,都是不知道的文件,可是,若是咱們仔細看,仍是能夠發現致使問題的地方的, 好比這裏咱們能夠看到 router 下的 index.js 問題, 接着鎖定 product.js  的問題, 再去追究,能夠發現,咱們在出錯的時候,沒有及時關閉mongodb數據庫,這樣,就會報錯,修復了這個問題以後,就能夠正常獲取數據了。

 

 

14、在使用redux的過程當中,遇到了一個問題。 即在一個問題列表頁,點擊每一條鏈接以後,能夠進入這個列表的詳情頁,那麼如何在詳情頁獲取到數據呢? 目前的方法是這樣的, 即在列表頁的Link進行路由跳轉的時候,將這條列表的suggestionId傳遞到詳情頁中去,而後在 componentWillMount() 這個鉤子函數中使用下面的方法:

    componentWillMount () {
        const suggestionId = this.props.location.query.suggestionId;
        
        this.props.filterSug(suggestionId);

        console.log(suggestionId);
    }

 

即獲取到當前的 suggestionId, 而後經過一個 action 篩選 store 中的這條建議。

 

function handleSuggestions (state = {allSuggestions: [], filteredSug: []}, action) {
    switch (action.type) {
        case 'ADD_ALL_SUGGESTIONS': 
            const newSug = Object.assign([], action.data);
            return {
                allSuggestions: newSug
            }
        case 'FILTER_SUGGESTION': 
            return {
                allSuggestions: state.allSuggestions,
                filteredSug: state.allSuggestions.filter(function (item, index) {
                    return item.suggestionId == action.id;
                })
            }
        default: 
            return state;
    }
}

 

 

  接着,咱們在再從store中獲取這條建議。以下:

function mapDispatchToProps (dispatch) {
    return {
        filterSug: (id) => dispatch(
            filterSuggestion(id)
        )
    }
}

function mapStateToProps (state) {
    return {
        filteredSug: state.handleSuggestions.filteredSug
    }
}

  

  接着,咱們就能夠在render函數中使用這個 prop 了,即:

const {filteredSug} = this.props;

  可是這樣致使的一個問題就是: 經過 filteredSug.map 調用時,發現會報錯,即 沒法讀取 map 所在的值,他是 undefined 的。   

  而若是咱們使用下面的方法:

const {filteredSug = [] } = this.props;

 

  即便用es6中的默認值的方法,這樣就不會報錯了,而且能夠正常顯示這條建議。 這是爲何? 我以前的想法是: 由於在 store 中存儲時,就會有一個默認值,因此,就算是直接獲取應該也是一個空的數組,而不是undefined啊,爲何這裏還須要提供一個默認值呢? 

     

  首先能夠肯定的是,在store中的state發生變化的時候,就會及時的經過頁面進行最新的渲染,這樣頁面就會及時的變化,即最開始 filteredSug 是 [], 而後當reducer處理完了 action 以後,就會改變state,這樣 filteredSug 就成了有一個對象元素的數組了,這樣咱們就能夠進行map了。

   

  之因此頁面會隨着數據發生變化,是由於頁面對數據有了一個訂閱,來監聽變化。getState函數能夠獲取當前的state。 

  

  下面,咱們須要測試的就是在 const {} = this.props;和後面的 mapStateToProps 獲取的速度問題(即誰先誰後),測試以下:

在 componentWillMount中添加下面的語句:

        console.log('在componentWillMount中的時間', new Date().getTime());

 

在render函數下添加下面的語句

        const {filteredSug} = this.props;

        console.log('render函數時間', new Date().getTime());

 

在 mapStateToProps中添加下面的語句

function mapStateToProps (state) {
    console.log('獲取store中的state的時間', new Date().getTime(), state);
    return {
        filteredSug: state.handleSuggestions.filteredSug
    }
}

 

 

而後,咱們開始測試,加載這個頁面,結果以下:

  這裏的總體思路很是簡單,就是首先進入頁面,而後第一步就獲取到當前的state, 這樣的好處在於,第一步獲取到以後就能夠在後面的各個鉤子函數、render函數中使用了,可是咱們能夠發現一個問題,就是在handleSuggestions這個reducer裏只有allSuggestions可是並無filteredSug。 第二步進入了componentWillMount鉤子函數中,這樣就能夠直接出發filter咱們想要的suggestion的action了。 第三步就是開始render。 因爲在第一步的過程當中就沒有獲取到filterSug,因此在render的時候,就能夠發現map的是一個undefined值。 第四步就比較有意思了,就是在咱們以前觸發了一個action,因此又在render以後,從新接收到了新的store,這樣,就又會從新渲染出來新的render。 咱們能夠發現,這個 fiterdSug 是存在的,可是以前若是沒有賦默認值,那麼就在前面報錯了,也就沒有後面的步驟了。


     

問題緣由:

  其實如今就比較好理解了,問題就是處在 reducer 那裏,咱們在獲取到全部的建議的時候,並無把當前 state 的全部值返回到一個新的state了,因此就致使了在進入 detail 頁面的時候,接收不到 filteredSug,解決問題的方式很簡單,以下:

function handleSuggestions (state = {allSuggestions: [], filteredSug: []}, action) {
    switch (action.type) {
        case 'ADD_ALL_SUGGESTIONS': 
            const newSug = Object.assign([], action.data);
            return {
                allSuggestions: newSug,
                filteredSug: []
            }
        case 'FILTER_SUGGESTION': 
            return {
                allSuggestions: state.allSuggestions,
                filteredSug: state.allSuggestions.filter(function (item, index) {
                    return item.suggestionId == action.id;
                })
            }
        default: 
            return state;
    }
}

這樣,咱們就能夠在render中使用filterSug的時候不須要使用默認值,而後就map了,由於開始map的時候,什麼都沒有,因此就不會渲染,而後store接收到action以後,觸發了新的state,這樣就可使得咱們的filterSug成爲了一個新的值,頁面就會渲染出來了。

  

 

那麼 connect 的這個源碼是怎樣的呢? 爲何在進入頁面的時候,能夠保證在 componentWillMount 鉤子函數以前就能夠已經獲取到了 store 中的state呢? 

 

猜測一: 因爲在react中的組件裏,constrctor鉤子函數式最先被調用的,因此這裏獲取store的步驟多是在 constructor 時調用的。 由於connect是react-redux的方法, 而react-redux是另一個庫,因此只能利用react的現有的api。

測試驗證

  方法: 在constructor鉤子函數中打印一下時間,再在 connect 的相關函數中打印一下時間, 若是說 connect 中的時間在後,那麼就是對的。

   這個結果很意外,爲何能夠首先得到 state 呢? 不是應該首先得到conscructor的嗎? 而後利用這個鉤子函數獲取到state? 

 

猜測二: 既然state是最早獲取到的, 那麼就是說它在原來的組件的基礎上包裝了一層。 

  這個確實很簡單了,在最開始學習redux的過程當中,就已經學習到了下面的概念:組件包括UI組件和容器組件,前者的做用徹底是用於展現的, 而容器組件的做用就是使用狀態管理工具如redux來命名的,即在使用redux時,就須要先將全部的數據以props的形式傳遞到各個容器組件中(實際上並非這樣的,在react中,有一個context的概念,就是傳遞數據,不須要使用props層層傳遞,而只須要使用一個 context 就能夠以最快的速度把想要傳遞的數據給到各個子組件中了), 而後容器組件再將之做爲props傳遞給UI組件, 而且咱們在使用redux時,不管是發送dispatch,仍是接受store中的數據,都須要經過這一層。 

  到這裏這個問題就很清楚了,就是首先,connect的容器組件首先將獲取到的數據傳進來放在props中,而後再開始建立內部的UI組件,而後內部須要相應的props時,直接從這個中間層來獲取就能夠了。因此constructor的創造時間是晚於state傳遞進來的時間的。

相關文章
相關標籤/搜索