最近這些年宠能,隨著前端應(yīng)用技術(shù)突飛猛進此再,產(chǎn)生了很多新的前端框架牢贸,當然也引入了數(shù)不勝數(shù)的前端技術(shù)概念,前端不在是早期Web Form的拖拉處理方式拍嵌,也不再是Ajax+HTML那么簡單遭赂,隨著前端技術(shù)的發(fā)展,前端的JS越來越重要横辆,也越來越復(fù)雜撇他,而為了開發(fā)的方便,引入了很多可以對JS+CSS進行編譯的框架狈蚤,而在發(fā)布的時候按需編譯處理困肩,從而增強了整個前端的開發(fā)過程,這些前端的技術(shù)包括AngularJS、React、Vue等等叉跛,這些前端技術(shù)應(yīng)用框架又囊括了很多相關(guān)的技術(shù),包括了MVVM(Model-View-ViewModel)潭枣、ES6、Babel幻捏、dva盆犁、umi、less等技術(shù)或概念粘咖。前端技術(shù)越滾越大蚣抗,范圍也越來越廣,大有日新月異的感覺瓮下。
1翰铡、前端技術(shù)的自我回顧和展望
記得在上大學(xué)時候,開始玩asp的年代讽坏,前端和后端糅合一起的困境锭魔;也曾記得WebForm開發(fā)的樂趣和無奈,快捷但是很丑很笨重路呜;而現(xiàn)在還在繼續(xù)做著Ajax + HTML的這種前端的處理迷捧,痛并快樂著织咧。技術(shù)總是一步步的推進則,但是眼光一旦聚焦在某個技術(shù)范疇漠秋,日月如梭笙蒙,抬頭間很快就會發(fā)現(xiàn)世界又多了新的前端技術(shù),從開始的猶豫和不確信的停留這段時間后庆锦,發(fā)現(xiàn)整個前端的世界也已經(jīng)漸成格局捅位,包括Angular、React搂抒、Vue等技術(shù)應(yīng)用已經(jīng)日趨成熟艇搀,而且擁有著龐大的擁躉群體,也有著豐富的資源可供學(xué)習和了解求晶。
下面是Angular焰雕、React、Vue幾個技術(shù)框架的一些介紹芳杏。
AngularJS誕生于2009年矩屁,由Misko Hevery 等人創(chuàng)建,后為Google所收購爵赵。是一款優(yōu)秀的前端JS框架档插,已經(jīng)被用于Google的多款產(chǎn)品當中。AngularJS有著諸多特性亚再,最為核心的是:MVC(Model–view–controller)、模塊化晨抡、自動化雙向數(shù)據(jù)綁定氛悬、語義化標簽、依賴注入等等耘柱。Angular開發(fā)在全球開發(fā)人員中廣泛流行如捅,并被谷歌,福布斯调煎,WhatsApp镜遣,Instagram,healthcare.gov和許多財富500強公司等大型組織使用士袄。
React 起源于 Facebook 的內(nèi)部項目悲关,因為該公司對市場上所有 JavaScript MVC 框架,都不滿意娄柳,就決定自己寫一套寓辱,用來架設(shè) Instagram 的網(wǎng)站。做出來以后赤拒,發(fā)現(xiàn)這套東西很好用秫筏,就在2013年5月開源了诱鞠。 由于 React 的設(shè)計思想極其獨特,屬于革命性創(chuàng)新这敬,性能出眾航夺,代碼邏輯卻非常簡單。所以崔涂,越來越多的人開始關(guān)注和使用阳掐,認為它可能是將來 Web 開發(fā)的主流工具。
Vue.js是討論最多且發(fā)展最快的JavaScript框架之一堪伍。它由前谷歌員工Evan You創(chuàng)建锚烦,他在擔任Google員工時曾在Angular工作過。您可以認為它是成功的帝雇,因為它能夠使用HTML涮俄,CSS和JavaScript構(gòu)建有吸引力的UI。
這些技術(shù)各有優(yōu)點尸闸,很難片面的說明誰優(yōu)誰劣彻亲,它們都各自有自己的生存土壤和大批的擁躉,而我開始選型做前端技術(shù)更新的時候吮廉,主要看中的是阿里巴巴的Ant-Design開發(fā)框架苞尝,這個它是使用了React的技術(shù)框架,因此也就自然而然的研究學(xué)習起React和Ant-Design來宦芦,雖然之前對前端的一些技術(shù)有所涉獵宙址,但是真正等你想要進入Ant-Design的開發(fā)大門的時候,還是感覺自己像進入了一個前端技術(shù)的大觀園调卑,一個個新概念接踵而來抡砂,一種種代碼的寫法迎面沖擊,教程看了幾遍還是一頭霧水恬涧,真的開始懷疑人生了注益,不過學(xué)習新技術(shù)還是需要很多平靜的心態(tài),調(diào)整好溯捆,一步一個腳印相信還是有所斬獲的丑搔,偶爾看到阮一峰的大牛介紹在學(xué)習研究React的時候,也曾花了幾個月的時候提揍,雖然他的高度難以看齊啤月,但是學(xué)習的韌勁和毅力,是值得我們學(xué)習的碳锈。學(xué)習新的東西顽冶,從技術(shù)角度,可以滿足好奇心售碳,提高技術(shù)水平强重;從職業(yè)角度绞呈,有利于求職和晉升,有利于參與潛力大的項目(摘自阮一峰筆記)间景。
2佃声、React的技術(shù)學(xué)習
接觸一些新的東西,就必然需要投入精力來學(xué)習掌握倘要。對于學(xué)習Ant-Desin圾亏,雖然這個框架本身提供了很多教程介紹,但是我們一些技術(shù)點封拧,還是需要更細節(jié)的學(xué)習志鹃,首推還是阮一峰的技術(shù)日志吧。
5陕见、Redux 入門教程(三):React-Redux 的用法
下面有些內(nèi)容在學(xué)習的時候评甜,掌握的不是很好,摘錄并作為一個回顧吧仔涩。
模塊的 Import 和 Export
import
用于引入模塊忍坷,export
用于導(dǎo)出模塊。
// 引入全部
import dva from 'dva';
// 引入部分
import { connect } from 'dva';
import { Link, Route } from 'dva/router';
// 引入全部并作為 github 對象
import * as github from './services/github';
// 導(dǎo)出默認
export default App;
// 部分導(dǎo)出熔脂,需 import { App } from './file'; 引入
export class App extend Component {};
析構(gòu)賦值
析構(gòu)賦值讓我們從 Object 或 Array 里取部分數(shù)據(jù)存為變量承匣。
// 對象
const user = { name: 'guanguan', age: 2 };
const { name, age } = user;
console.log(`${name} : ${age}`); // guanguan : 2
// 數(shù)組
const arr = [1, 2];
const [foo, bar] = arr;
console.log(foo); // 1
我們也可以析構(gòu)傳入的函數(shù)參數(shù)。
const add = (state, { payload }) => {
return state.concat(payload);
};
//析構(gòu)時還可以配 alias锤悄,讓代碼更具有語義
const add = (state, { payload: todo }) => {
return state.concat(todo);
};
對象展開運算符(Object Spread Operator)
//可用于組裝數(shù)組。
const todos = ['Learn dva'];
[...todos, 'Learn antd']; // ['Learn dva', 'Learn antd']
//也可用于獲取數(shù)組的部分項嘉抒。
const arr = ['a', 'b', 'c'];
const [first, ...rest] = arr;
rest; // ['b', 'c']
// With ignore
const [first, , ...rest] = arr;
rest; // ['c']
//還可收集函數(shù)參數(shù)為數(shù)組零聚。
function directions(first, ...rest) {
console.log(rest);
}
directions('a', 'b', 'c'); // ['b', 'c'];
//代替 apply。
function foo(x, y, z) {}
const args = [1,2,3];
// 下面兩句效果相同
foo.apply(null, args);
foo(...args);
//對于 Object 而言些侍,用于組合成新的 Object
const foo = {
a: 1,
b: 2,
};
const bar = {
b: 3,
c: 2,
};
const d = 4;
const ret = { ...foo, ...bar, d }; // { a:1, b:3, c:2, d:4 }
propTypes
JavaScript 是弱類型語言隶症,所以請盡量聲明 propTypes 對 props 進行校驗,以減少不必要的問題岗宣。
function App(props) {
return <div>{props.name}</div>;
}
App.propTypes = {
name: React.PropTypes.string.isRequired,
};
內(nèi)置的 prop type 有:
PropTypes.array
PropTypes.bool
PropTypes.func
PropTypes.number
PropTypes.object
PropTypes.string
DVA數(shù)據(jù)流向
數(shù)據(jù)的改變發(fā)生通常是通過用戶交互行為或者瀏覽器行為(如路由跳轉(zhuǎn)等)觸發(fā)的蚂会,當此類行為會改變數(shù)據(jù)的時候可以通過 dispatch 發(fā)起一個 action,如果是同步行為會直接通過 Reducers 改變 State 耗式,如果是異步行為(副作用)會先觸發(fā) Effects 然后流向 Reducers 最終改變 State胁住。
Reducer和effects
reducer 是一個函數(shù)趁猴,接受 state 和 action,返回老的或新的 state 彪见。即:(state, action) => state
app.model({
namespace: 'todos',
state: [],
reducers: {
add(state, { payload: todo }) {
return state.concat(todo);
},
remove(state, { payload: id }) {
return state.filter(todo => todo.id !== id);
},
update(state, { payload: updatedTodo }) {
return state.map(todo => {
if (todo.id === updatedTodo.id) {
return { ...todo, ...updatedTodo };
} else {
return todo;
}
});
},
},
};
建議最多一層嵌套儡司,以保持 state 的扁平化,深層嵌套會讓 reducer 很難寫和難以維護余指。
app.model({
namespace: 'app',
state: {
todos: [],
loading: false,
},
reducers: {
add(state, { payload: todo }) {
const todos = state.todos.concat(todo);
return { ...state, todos };
},
},
});
effects示例
app.model({
namespace: 'todos',
effects: {
*addRemote({ payload: todo }, { put, call }) {
yield call(addTodo, todo);
yield put({ type: 'add', payload: todo });
},
},
});
Put用于觸發(fā) action捕犬,call用于調(diào)用異步邏輯,支持 promise酵镜。
異步請求
異步請求基于 whatwg-fetch碉碉,API 詳見:https://github.com/github/fetch
GET 和 POST
import request from '../util/request';
// GET
request('/api/todos');
// POST
request('/api/todos', {
method: 'POST',
body: JSON.stringify({ a: 1 }),
});
統(tǒng)一錯誤處理
假如約定后臺返回以下格式時,做統(tǒng)一的錯誤處理淮韭。
{
status: 'error',
message: '',
}
編輯 utils/request.js垢粮,加入以下中間件:
function parseErrorMessage({ data }) {
const { status, message } = data;
if (status === 'error') {
throw new Error(message);
}
return { data };
}
然后,這類錯誤就會走到 onError hook 里缸濒。
Subscription
subscriptions 是訂閱足丢,用于訂閱一個數(shù)據(jù)源,然后根據(jù)需要 dispatch 相應(yīng)的 action庇配。數(shù)據(jù)源可以是當前的時間斩跌、服務(wù)器的 websocket 連接、keyboard 輸入捞慌、geolocation 變化耀鸦、history 路由變化等等。格式為 ({ dispatch, history }) => unsubscribe 啸澡。
異步數(shù)據(jù)初始化
比如:當用戶進入 /users 頁面時袖订,觸發(fā) action users/fetch 加載用戶數(shù)據(jù)。
app.model({
subscriptions: {
setup({ dispatch, history }) {
history.listen(({ pathname }) => {
if (pathname === '/users') {
dispatch({
type: 'users/fetch',
});
}
});
},
},
});
react dva 的 connect 與 @connect
connect的作用是將組件和models結(jié)合在一起嗅虏。將models中的state綁定到組件的props中洛姑。并提供一些額外的功能,譬如dispatch
connect 的使用
connect 方法返回的也是一個 React 組件皮服,通常稱為容器組件楞艾。因為它是原始 UI 組件的容器,即在外面包了一層 State龄广。
connect 方法傳入的第一個參數(shù)是 mapStateToProps 函數(shù)硫眯,該函數(shù)需要返回一個對象,用于建立 State 到 Props 的映射關(guān)系择同。
簡而言之两入,connect接收一個函數(shù),返回一個函數(shù)敲才。
第一個函數(shù)會注入全部的models裹纳,你需要返回一個新的對象择葡,挑選該組件所需要的models。
export default connect(({ user, login, global = {}, loading }) => ({
currentUser: user.currentUser,
collapsed: global.collapsed,
fetchingNotices: loading.effects['global/fetchNotices'],
notices: global.notices
}))(BasicLayout);
// 簡化版
export default connect(
({ user, login, global = {}, loading }) => {
return {
currentUser: user.currentUser,
collapsed: global.collapsed,
fetchingNotices: loading.effects['global/fetchNotices'],
notices: global.notices
}
}
)(BasicLayout);
@connect的使用
其實只是connect的裝飾器痊夭、語法糖罷了刁岸。
// 將 model 和 component 串聯(lián)起來
export default connect(({ user, login, global = {}, loading }) => ({
currentUser: user.currentUser,
collapsed: global.collapsed,
fetchingNotices: loading.effects['global/fetchNotices'],
notices: global.notices,
menuData: login.menuData,
redirectData: login.redirectData,
}))(BasicLayout);
// 改為這樣(export 的不再是connect,而是class組件本身她我。)虹曙,也是可以執(zhí)行的,但要注意@connect必須放在export default class前面:
// 將 model 和 component 串聯(lián)起來
@connect(({ user, login, global = {}, loading }) => ({
currentUser: user.currentUser,
collapsed: global.collapsed,
fetchingNotices: loading.effects['global/fetchNotices'],
notices: global.notices,
menuData: login.menuData,
redirectData: login.redirectData,
}))
export default class BasicLayout extends React.PureComponent {
// ...
}
export default connect(從 model 的 state 中獲取數(shù)據(jù))(要將數(shù)據(jù)綁定到哪個組件)
以上部分內(nèi)容摘自 https://blog.csdn.net/zhangrui_web/article/details/79651812
2番舆、Ant-Design的框架
這款基于React開發(fā)的UI框架酝碳,界面非常簡潔美觀,是阿里巴巴旗下螞蟻金融服務(wù)集團(旗下?lián)碛兄Ц秾毢薇贰⒂囝~寶等產(chǎn)品)所設(shè)計的一個前端UI組件庫疏哗。目前支持了React, 并且有一個對Vue支持的測試版本禾怠。
學(xué)習和使用Ant-Design返奉,我們可以使用VSCode來對項目代碼進行維護和編輯,這樣可以在Mac和Window環(huán)境同樣的開發(fā)體驗和操作模式吗氏,非常方便芽偏。
如果需要掌握Ant-Design框架,我們需要了解model弦讽,namespace污尉,connect,dispatch往产,action被碗,reducer ,effect這些概念仿村。
DVA 的 model 對象有幾個基本的屬性介紹锐朴。
namespace
:model 的命名空間,只能用字符串蔼囊。一個大型應(yīng)用可能包含多個 model包颁,通過namespace
區(qū)分。state
:當前 model 狀態(tài)的初始值压真,表示當前狀態(tài)。reducers
:用于處理同步操作蘑险,可以修改state
滴肿,由action
觸發(fā)。reducer 是一個純函數(shù)佃迄,它接受當前的 state 及一個 action 對象泼差。action 對象里面可以包含數(shù)據(jù)體(payload)作為入?yún)⒐笊伲枰祷匾粋€新的 state。effects
:用于處理異步操作(例如:與服務(wù)端交互)和業(yè)務(wù)邏輯堆缘,也是由 action 觸發(fā)滔灶。但是,它不可以修改 state吼肥,要通過觸發(fā) action 調(diào)用 reducer 實現(xiàn)對 state 的間接操作录平。action
:action 就是一個普通 JavaScript 對象,是 reducers 及 effects 的觸發(fā)器缀皱,形如{ type: 'add', payload: todo }
斗这,通過 type 屬性可以匹配到具體某個 reducer 或者 effect,payload 屬性則是數(shù)據(jù)體啤斗,用于傳送給 reducer 或 effect表箭。
整體的數(shù)據(jù)流向見下圖:
在Reducer里面,不要修改傳入的 state
钮莲。 使用 Object.assign()
新建了一個副本免钻。不能這樣使用 Object.assign(state, { visibilityFilter: action.filter })
,因為它會改變第一個參數(shù)的值崔拥。你必須把第一個參數(shù)設(shè)置為空對象极舔。
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
default:
return state
}
}
或者使用使用對象展開運算符(Object Spread Operator)來處理,從而使用 { ...state, ...newState } 達到相同的目的握童。
reducers: {
save(state, action) {
return {
...state,
...action.payload,
};
},
},
在 default 情況下返回舊的 state姆怪。遇到未知的 action 時,一定要返回舊的 state澡绩。
每個 reducer 只負責管理全局 state 中它負責的一部分稽揭。每個 reducer 的 state 參數(shù)都不同,分別對應(yīng)它管理的那部分 state 數(shù)據(jù)肥卡。
下面兩種合成 reducer 方法完全等價:
const reducer = combineReducers({
a: doSomethingWithA,
b: processB,
c: c
})
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state.a, action),
b: processB(state.b, action),
c: c(state.c, action)
}
}
dva封裝了redux溪掀,減少很多重復(fù)代碼比如action reducers 常量等,dva所有的redux操作是放在models目錄下步鉴,通過namespace作為key揪胃,標識不同的模塊state,可以給state設(shè)置初始數(shù)據(jù)氛琢。
reducers跟傳統(tǒng)的react-redux寫法一致喊递,所有的操作放在reducers對象內(nèi)
Effect 被稱為副作用,在我們的應(yīng)用中阳似,最常見的就是異步操作骚勘,Effects
的最終流向是通過 Reducers
改變 State
。
其中上面的effects里面,call, put其實是saga的寫法俏讹,dva集成了saga当宴,可以參考上圖中的saga內(nèi)容
DVA 首先是一個基于 redux 和 redux-saga 的數(shù)據(jù)流方案,然后為了簡化開發(fā)體驗泽疆,DVA 還額外內(nèi)置了 react-router 和 fetch户矢,所以也可以理解為一個輕量級的應(yīng)用框架。
DVA 是基于現(xiàn)有應(yīng)用架構(gòu) (redux + react-router + redux-saga 等)的一層輕量封裝殉疼,沒有引入任何新概念梯浪,全部代碼不到 100 行。
在Ant-Design的Pages/.umi目錄里面株依,有一個initDva.js文件驱证,就是用來統(tǒng)一批量處理 DVA 的引入的,如下所示恋腕。
在有 DVA 之前抹锄,我們通常會創(chuàng)建 sagas/products.js
, reducers/products.js
和 actions/products.js
,然后在這些文件之間來回切換荠藤。
有了 DVA 后伙单,它最核心的是提供了 app.model
方法,用于把 reducer, initialState, action, saga 封裝到一起哈肖,這樣我們在書寫代碼的時候吻育,把它主要內(nèi)容,和加載分離出來淤井。如果建立的Model比較多布疼,每次開始的時候需要加入這一句好像也是挺麻煩的,如果可以自動把這個model批量加入币狠,應(yīng)該會更好吧游两,不過不知道是基于什么考量。