產品的反饋系統設計


什么是反饋系統? 

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

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

 

為什么需要反饋系統?

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

 

反饋系統的類型有哪些?

  主要可以分為自建的反饋系統和使用第三方工具作為反饋系統:

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

 

具有反饋系統的網站推薦

郵件形式:

  

  如Tower,我們在 https://tower.im/help 的底部可以看到下面的界面:

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

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

  幾分鍾之后,我就接受到了這樣的一封反饋郵件,很好地解決了我的問題, 可以看到,Tower的入口也是非常方便地。

  

 

 

  

對話形式:

  

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

      

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

 

 

 

工單形式:

  

   在阿里雲的網站上,我們可以添加建議並提交,提交之后我們可以看到顯示如下:

  可以看到提交之后,就會形成一個工單,實時的反映當前情況, 即 ‘已提交’、‘預審通過’、‘已采納’、‘已實現’ 幾個步驟。 對於僅僅是提交的建議,我們還可以進行再次編輯,另外,所有的建議其他用戶也都是可以看到的,所有人(當然包括管理員)可以進行反饋、評論、投票等功能。 

  在此網站可以查看: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相關文件)。 

 

 

 

 

遇到的問題

1、應該建立幾個文件,即開啟幾個服務器(項目)? 

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

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

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

  這樣在技術上是可以做到的,但是在前端處理上會出現問題。 比如使用webpack打包的時候,就會把所有的js打包,但是實際上在用戶和管理員處所使用的js並不是所有的,這樣就會造成浪費。 

  

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

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

 

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

 

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

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

 

 結果

  • 反饋平台是比較核心的,並且這個可以看做是固定的,由管理員來使用,並且反饋平台管理員端的后台代碼也是比較比較重要的,通過其為反饋系統提供API。 

 

3、 使用react、 ant.design可以嗎? 

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

 

 

4、 這里所說的插件到底是什么?怎么去理解? 可配置是說添加一個配置文件、配置對象嗎? 

   插件實際上就是一段js代碼,通過這個js代碼,我們可以將這個js插件暴露出來的方法進行 init 然后適用在某個產品上。 

 

 

5、什么是堆樓模式? 這里所使用的反饋系統是工單形式的嗎?

   即評論是一層一層的,而不是縮進的形式。 反饋系統可以看作是工單形式的。  

 

 

6、整體的思路應該是怎樣的? 

  對於后台管理界面是直接使用node作為后台,然后使用react來寫前台管理界面,這個是通用的。 另外,這個后台除了提供管理界面之外,也應該提供反饋平台所需要的接口。 另外,還需要一個通用的普通端,這里可以自己選產品的類型等。 

 

 

7、數據庫的設計應該是怎么樣的?

   在數據庫中,我們需要存儲的信息包括管理員信息、用戶信息、建議信息、評論信息、產品信息等等, 對於這些信息,我們應當盡量采用扁平化的風格進行存儲。使用連接的方式。 

  (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為1、而用戶的type為2。

  

      (4)管理員存儲

  • 管理員名稱 ---(用於顯示)可以是手機號。
  • 管理員Id(id)--- 用於尋找所有的自己關注的產品。
  • 管理員類型 --- 如果是1,表示這是一個管理員,如果是2,表示這是一個用戶。
  • 管理員創建的產品Id(產品Id是在創建的時候分配的) --- 只有創建了這個產品的人才能對這個產品進行操作。

  (5)用戶存儲

  • 用戶名稱 --- 用於顯示。
  • 用戶Id --- 用於查找自己的所有的建議。
  • 用戶類型 --- 如果是1,表示這是一個管理員,如果是2,表示這是一個用戶。

 

  即在數據庫中需要建立5個集合,這樣我們就可以進行簡單的系統操作了。可以看到,上面的數據庫的設計盡量是扁平化的,而沒有進行扎推。 

  並且我們應當提高可重用性,不要在不同的表中建立了太多相同的內容,這是不合適的。

 

 

8、 ant.design究竟應該怎么用? 

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

  

 

 

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

用戶端

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

  沒錯,用戶的權限很小,用戶只能提交之后,看到其答案已經提交了,后續的工作就完全取決於管理員了。

管理端

  • 用戶提交了建議之后,管理端顯示為新建議。 
  • 這時管理員應該及時的將狀態修改為審核中,這樣用戶就可以直接看到建議的最新狀態,接着和團隊、小組成員進行討論建議的可實施性,也許會持續一段事件,並作出及時地回復。
  • 討論結束之后,管理員可以決定審查未通過,但一定要給予用戶進行充分的解釋,那么這個問題就可以關閉了或者是決定已采納。及時的將信息顯示在用戶端界面。 
  • 如果是 已采納,那么管理端就需要及時解決,然后將信息及時通知給客戶端 。 

 

所以說,對於建議的管理,主動權完全在管理端,這樣才會比較好控制數據,不至於混亂,就像redux的單向數據流的方式是一樣的。

 

 

10、登錄、注冊這部分應該怎么做?  管理員、用戶、產品、建議的ID怎么設置可以保證沒有大的問題? 怎么保證不會重復? 表的設計是否有問題。

  1、 對於唯一ID,我們可以使用 uuid  (在項目中我們直接 npm install uuid --dev 即可),這個工具可以幫助我們快速解決問題,並且ID是不會重復的。

  2、 對於登錄、注冊的問題,我們可以把這個反饋的網站看做一個黑盒子,然后只需要對之有確定的輸入, 就能保證其輸出,所以,我們可以將其i作為插件來想, 對於登錄、注冊的事情由不同的網站來解決即可。 而我們需要做的就是在后台處理好即可。  而在管理端還是比較容易理解的,就是必須登錄注冊才能查看產品等等。

 

 

11、 uuid介紹。

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

 

  UUID是128位的全局唯一標識符,通常由32字節的字符串表示。它可以保證時間和空間的唯一性,也稱為GUID,全稱為:UUID ―― Universally Unique IDentifier,Python 中叫 UUID。

  

 

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

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

第一步:

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 服務器的資源。

 

 

13、多次看到服務器崩潰! 為什么呢?

 

即如上所示,乍一看似乎並沒有什么解決的辦法,都是不知道的文件,但是,如果我們仔細看,還是可以發現導致問題的地方的, 比如這里我們可以看到 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傳遞進來的時間的。

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM