比較redux和reflux以及自己寫個TinyFlux?
看了redux和reflux,感覺redux好複雜啊,看reflux,1個小時就能弄懂,可是看redux,1天下來都迷迷糊糊的。
感覺flux不就應該是view層發消息給store,store根據消息去修改數據,修改完數據後通知涉及到被修改的view組建刷新state嗎。。。然後我看著reflux的使用方式(它的源代碼沒怎麼看,感覺還是多了),自己寫了個tinyFlux。。。二樓附上。。各位看看。。。。可能還太幼稚了。。。歡迎批評啊。。。
謝邀,Flux 相關的輪子已經太多了,實在看不過來,抱歉。
簡單說說 Reflux 和 Redux 的區別吧。
之前我在 如何理解 Facebook 的 flux 應用架構? - 知乎用戶的回答 里提到過,目前市面上很多 Flux 相關的框架就是把 Dispatcher 隱藏了,然後封裝了 Action Creator 和 Component 綁定的細節,簡單地說就是「簡化 Flux 里冗餘的代碼和操作」。
Reflux 在實現上面說到的內容的基礎上,又提供了很多開發者喜歡的功能,比如 View 監聽 Action 這種違背 Flux 概念但是確實在實際應用中很方便的特性。
而 Redux 並不僅僅是「簡化 Flux」,它重新定義了 Flux 中每一個角色的功能、職責及實現方式。其中最大的不同是沒有 Store 的概念(或者說 Store 不再需要用戶去定義),而增加了 Reducer 的概念。Store 會通過我們定義的 Reducer 自動生成,使用 redux.createStore 方法。此外,Redux 定義了一個 middleware 機制,可以讓我們在 Action 中更方便的處理業務邏輯。
總結一下就是,如果你覺得目前 Flux 用著不爽太多冗餘代碼, 那麼你寫的 TinyFlux 很不錯,解決了你的很多問題;然而如果你想擁有更多 redux 帶來的新特性,或者說你喜歡並推崇不可變的數據結構,或者說你想顯得逼格比較高,那麼看懂並會用 redux 絕對是你的不二選擇。
PS. redux 確實不錯,我們已經開始著手在生產環境中使用了。Redux 核心概念
深入淺出Redux http://www.w3ctech.com/topic/1561
//TinyFlux.js
function createStore (store, ...args) {
if (!store) {
throw new Error("請定義store");
};
if (typeof store.init === "function") {
store.init(args);
};
if (typeof store.actions !== "function") {
throw new Error("actions應該是一個返回一個對象的function,對象里的方法都要用箭頭表達式來書寫,利用箭頭表達式的this綁定。");
};
store.actions = store.actions();
if (store.subscriber || store.emit) {
throw new Error("不該存在subscriber和emit");
};
store.subscriber = [];
store.emit = function(argument) {
store.subscriber.forEach(component =&> component.reload());
};
return store;
}
function connect (store, def) {
return {
getInitialState: function(){
var state = {};
for (var i in def) {
try{
state[i] = eval("store."+def[i]);
} catch(err) {
console.log(err);
}
};
return state;
},
reload: function() {
var state = {};
for (var i in def) {
try{
state[i] = eval("store."+def[i]);
} catch(err) {
console.log(err);
}
};
this.setState(state);
},
componentDidMount: function(){
store.subscriber.push(this);
},
componentWillUnmount: function() {
store.subscriber.splice(store.subscriber.indexOf(this),1);
}
};
}
module.exports = {
createStore: createStore,
connect: connect
}
//用戶自己寫的代碼部分 TodoStore.js
import TinyFlux from "./TinyFlux.js";
export default TinyFlux.createStore({
actions: function() {
return {
editItem: (itemKey, newLabel) =&> {
var foundItem = getItemByKey(this.list, itemKey);
if (!foundItem) {
return;
}
foundItem.label = newLabel;
this.updateList(this.list);
},
addItem: (label) =&> {
this.updateList([{
key: generateUUID(),
created: new Date(),
isComplete: false,
label: label
}].concat(this.list));
},
removeItem: (itemKey) =&> {
this.updateList(_.filter(this.list, function(item) {
return item.key !== itemKey;
}));
},
toggleItem: (itemKey) =&> {
var foundItem = getItemByKey(this.list, itemKey);
if (foundItem) {
foundItem.isComplete = !foundItem.isComplete;
this.updateList(this.list);
}
},
toggleAllItems: (checked) =&> {
this.updateList(_.map(this.list, function(item) {
item.isComplete = checked;
return item;
}));
},
clearCompleted: () =&> {
this.updateList(_.filter(this.list, function(item) {
return !item.isComplete;
}));
},
getAllItem: () =&> {
//return JSON.parse(JSON.stringify(this.list));
return this.list;
},
};
},
list: [{
key: generateUUID(),
created: new Date(),
isComplete: false,
label: "Rule the web"
}],//或者使用初始化方法,可以在createStore時附帶init方法的參數
// init: function(list, localStorageKey) {
// localStorageKey = localStorageKey || "TodoStore";
// if (list) {
// localStorage.setItem(localStorageKey, JSON.stringify(list));
// } else {
// list = localStorage.getItem(localStorageKey);
// if (!list || list.length == 0) {
// list = [{
// key: generateUUID(),
// created: new Date(),
// isComplete: false,
// label: "Rule the web"
// }];
// localStorage.setItem(localStorageKey, JSON.stringify(list));
// };
// };
// this.localStorageKey = localStorageKey;
// this.list = list;
// },
updateList: function(list) {
localStorage.setItem(this.localStorageKey, JSON.stringify(list));
// if we used a real database, we would likely do the below in a callback
this.list = list;
this.emit();
//this.trigger(list); // sends the updated list to all listening components (TodoApp)
}
});
function generateUUID(){
var d = new Date().getTime();
var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=="x" ? r : (r0x3|0x8)).toString(16);
});
return uuid;
}
function getItemByKey(list,itemKey){
return _.find(list, function(item) {
return item.key === itemKey;
});
}
//React組建
import TinyFlux from "./TinyFlux.js";
import TodoStore from "./TodoStore.js";
var actions = TodoStore.actions;
export var TodoApp = React.createClass({
mixins: [TinyFlux.connect(TodoStore,{
list: "list" //TodoApp的state的list屬性等於TodoStore的"list"屬性,這第二個list也可以有多層數據,例如{sex: "user.sex"}
})],
render: function() {
return (
&
&
&
&
&
}
});
export var TodoMain = React.createClass({
mixins: [ ReactRouter.State ],
propTypes: {
list: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
},
toggleAll: function(evt) {
//調用actions的方法去改變數據,相當於給store發消息
actions.toggleAllItems(evt.target.checked);
},
render: function() {
var filteredList;
switch(this.getPath()){
case "/completed":
filteredList = _.filter(this.props.list,function(item){ return item.isComplete; });
break;
case "/active":
filteredList = _.filter(this.props.list,function(item){ return !item.isComplete; });
break;
default:
filteredList = this.props.list;
}
var classes = React.addons.classSet({
"hidden": this.props.list.length &< 1
});
return (
&
&
&
-
{ filteredList.map(function(item){
return &
})}
&
}
});
//TodoItem、TodoHeader、TodoFooter的代碼就不貼了,也都是直接調用actions的方法來改變數據。。。
感覺這樣使用已經足夠方便。。。
當然,我自己還覺得還有一些不夠或者不清楚另一種設計是不是更好的地方(上面說了這個TinyFlux可能還很幼稚,所以只是我自己覺得,你們可能就覺得太多地方不夠了),例如:1. 組建是直接調用actions的方法好呢,還是發送一個消息好,發送消息是不是要弄個消息隊列
2. 數據是存在store上,在mixin的時候附到組建的state上好,還是把數據存在createStore的閉包里,actions里分有兩種類型的action,get和post,在mixin的時候運行get類型的action獲取數據附到組建的state上
歡迎拍磚,不過別太狠哈。。。推薦閱讀:
※為什麼瀏覽器不將類似react.js的虛擬DOM在其內部實現呢?
※如何理解虛擬DOM?
※如何用React做一個MVC項目?
※在 componentWillUnmount 中到底應該清除哪些變數?
※store的組織是扁平化好,還是分層級樹狀的好?大型的項目store該怎麼組織?