關於當前Web前端技術的一些感悟和筆記

最近這些年,隨着前端應用技術日新月異,產生了不少新的前端框架,固然也引入了數不勝數的前端技術概念,前端不在是早期Web Form的拖拉處理方式,也再也不是Ajax+HTML那麼簡單,隨着前端技術的發展,前端的JS愈來愈重要,也愈來愈複雜,而爲了開發的方便,引入了不少能夠對JS+CSS進行編譯的框架,而在發佈的時候按需編譯處理,從而加強了整個前端的開發過程,這些前端的技術包括AngularJS、React、Vue等等,這些前端技術應用框架又囊括了不少相關的技術,包括了MVVM(Model-View-ViewModel)、ES六、Babel、dva、umi、less等技術或概念。前端技術越滾越大,範圍也愈來愈廣,大有突飛猛進的感受。html

一、前端技術的自我回顧和展望

記得在上大學時候,開始玩asp的年代,前端和後端糅合一塊兒的困境;也曾記得WebForm開發的樂趣和無奈,快捷可是很醜很笨重;而如今還在繼續作着Ajax + HTML的這種前端的處理,痛並快樂着。技術老是一步步的推動則,可是眼光一旦聚焦在某個技術範疇,日月如梭,擡頭間很快就會發現世界又多了新的前端技術,從開始的猶豫和不確信的停留這段時間後,發現整個前端的世界也已經漸成格局,包括Angular、React、Vue等技術應用已經日趨成熟,並且擁有着龐大的擁躉羣體,也有着豐富的資源可供學習和了解。前端

下面是Angular、React、Vue幾個技術框架的一些介紹。react

AngularJS誕生於2009年,由Misko Hevery 等人建立,後爲Google所收購。是一款優秀的前端JS框架,已經被用於Google的多款產品當中。AngularJS有着諸多特性,最爲核心的是:MVC(Model–view–controller)、模塊化、自動化雙向數據綁定、語義化標籤、依賴注入等等。Angular開發在全球開發人員中普遍流行,並被谷歌,福布斯,WhatsApp,Instagram,healthcare.gov和許多財富500強公司等大型組織使用。git

React 起源於 Facebook 的內部項目,由於該公司對市場上全部 JavaScript MVC 框架,都不滿意,就決定本身寫一套,用來架設 Instagram 的網站。作出來之後,發現這套東西很好用,就在2013年5月開源了。 因爲 React 的設計思想極其獨特,屬於革命性創新,性能出衆,代碼邏輯卻很是簡單。因此,愈來愈多的人開始關注和使用,認爲它多是未來 Web 開發的主流工具。es6

Vue.js是討論最多且發展最快的JavaScript框架之一。它由前谷歌員工Evan You建立,他在擔任Google員工時曾在Angular工做過。您能夠認爲它是成功的,由於它可以使用HTML,CSS和JavaScript構建有吸引力的UI。github

這些技術各有優勢,很難片面的說明誰優誰劣,它們都各自有本身的生存土壤和大批的擁躉,而我開始選型作前端技術更新的時候,主要看中的是阿里巴巴的Ant-Design開發框架,這個它是使用了React的技術框架,所以也就天然而然的研究學習起React和Ant-Design來,雖然以前對前端的一些技術有所涉獵,可是真正等你想要進入Ant-Design的開發大門的時候,仍是感受本身像進入了一個前端技術的大觀園,一個個新概念接踵而來,一種種代碼的寫法迎面衝擊,教程看了幾遍仍是一頭霧水,真的開始懷疑人生了,不過學習新技術仍是須要不少平靜的心態,調整好,一步一個腳印相信仍是有所斬獲的,偶爾看到阮一峯的大牛介紹在學習研究React的時候,也曾花了幾個月的時候,雖然他的高度難以看齊,可是學習的韌勁和毅力,是值得咱們學習的。學習新的東西,從技術角度,能夠知足好奇心,提升技術水平;從職業角度,有利於求職和晉升,有利於參與潛力大的項目(摘自阮一峯筆記)。web

 

二、React的技術學習

接觸一些新的東西,就必然須要投入精力來學習掌握。對於學習Ant-Desin,雖然這個框架自己提供了不少教程介紹,可是咱們一些技術點,仍是須要更細節的學習,首推仍是阮一峯的技術日誌吧。redux

一、ECMAScript 6 入門後端

二、React 入門實例教程 api

三、Redux 入門教程(一):基本用法

四、Redux 入門教程(二):中間件與異步操做

五、Redux 入門教程(三):React-Redux 的用法

六、Redux 文檔基礎教程

七、DvaJS快速上手

 

下面有些內容在學習的時候,掌握的不是很好,摘錄並做爲一個回顧吧。 

模塊的 Import 和 Export

import 用於引入模塊,export 用於導出模塊。

// 引入所有
import dva from 'dva';

// 引入部分
import { connect } from 'dva';
import { Link, Route } from 'dva/router';

// 引入所有並做爲 github 對象
import * as github from './services/github';

// 導出默認
export default App;
// 部分導出,需 import { App } from './file'; 引入
export class App extend Component {};

析構賦值

析構賦值讓咱們從 Object 或 Array 裏取部分數據存爲變量。

// 對象
const user = { name: 'guanguan', age: 2 };
const { name, age } = user;
console.log(`${name} : ${age}`);  // guanguan : 2

// 數組
const arr = [1, 2];
const [foo, bar] = arr;
console.log(foo);  // 1

咱們也能夠析構傳入的函數參數。

const add = (state, { payload }) => {
  return state.concat(payload);
};

//析構時還能夠配 alias,讓代碼更具備語義
const add = (state, { payload: todo }) => {
  return state.concat(todo);
};

 

對象展開運算符(Object Spread Operator)

//可用於組裝數組。
const todos = ['Learn dva'];
[...todos, 'Learn antd'];  // ['Learn dva', 'Learn antd']

//也可用於獲取數組的部分項。
const arr = ['a', 'b', 'c'];
const [first, ...rest] = arr;
rest;  // ['b', 'c']

// With ignore
const [first, , ...rest] = arr;
rest;  // ['c']

//還可收集函數參數爲數組。
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,
};

內置的 prop type 有:

  • PropTypes.array
  • PropTypes.bool
  • PropTypes.func
  • PropTypes.number
  • PropTypes.object
  • PropTypes.string

 

DVA數據流向

數據的改變發生一般是經過用戶交互行爲或者瀏覽器行爲(如路由跳轉等)觸發的,當此類行爲會改變數據的時候能夠經過 dispatch 發起一個 action,若是是同步行爲會直接經過 Reducers 改變 State ,若是是異步行爲(反作用)會先觸發 Effects 而後流向 Reducers 最終改變 State。

 

Reducer和effects

 reducer 是一個函數,接受 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用於觸發 action,call用於調用異步邏輯,支持 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 }),
});

統一錯誤處理

假如約定後臺返回如下格式時,作統一的錯誤處理。

{
  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 是訂閱,用於訂閱一個數據源,而後根據須要 dispatch 相應的 action。數據源能夠是當前的時間、服務器的 websocket 鏈接、keyboard 輸入、geolocation 變化、history 路由變化等等。格式爲 ({ dispatch, history }) => unsubscribe 。

異步數據初始化

好比:當用戶進入 /users 頁面時,觸發 action users/fetch 加載用戶數據。

app.model({
  subscriptions: {
    setup({ dispatch, history }) {
      history.listen(({ pathname }) => {
        if (pathname === '/users') {
          dispatch({
            type: 'users/fetch',
          });
        }
      });
    },
  },
});
 
react dva 的 connect 與 @connect
 
connect的做用是將組件和models結合在一塊兒。將models中的state綁定到組件的props中。並提供一些額外的功能,譬如dispatch

connect 的使用

connect 方法返回的也是一個 React 組件,一般稱爲容器組件。由於它是原始 UI 組件的容器,即在外面包了一層 State。

connect 方法傳入的第一個參數是 mapStateToProps 函數,該函數須要返回一個對象,用於創建 State 到 Props 的映射關係。

簡而言之,connect接收一個函數,返回一個函數。

第一個函數會注入所有的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 串聯起來
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組件自己。),也是能夠執行的,但要注意@connect必須放在export default class前面:
// 將 model 和 component 串聯起來
@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 中獲取數據)(要將數據綁定到哪一個組件) 
 

以上部份內容摘自 https://blog.csdn.net/zhangrui_web/article/details/79651812

 

二、Ant-Design的框架

這款基於React開發的UI框架,界面很是簡潔美觀,是阿里巴巴旗下螞蟻金融服務集團(旗下擁有支付寶、餘額寶等產品)所設計的一個前端UI組件庫。目前支持了React, 而且有一個對Vue支持的測試版本。

學習和使用Ant-Design,咱們可使用VSCode來對項目代碼進行維護和編輯,這樣能夠在Mac和Window環境一樣的開發體驗和操做模式,很是方便。

 

若是須要掌握Ant-Design框架,咱們須要瞭解model,namespace,connect,dispatch,action,reducer ,effect這些概念。

DVA 的 model 對象有幾個基本的屬性介紹。

  1. namespace:model 的命名空間,只能用字符串。一個大型應用可能包含多個 model,經過namespace區分。
  1. state:當前 model 狀態的初始值,表示當前狀態。
  1. reducers:用於處理同步操做,能夠修改 state,由 action 觸發。reducer 是一個純函數,它接受當前的 state 及一個 action 對象。action 對象裏面能夠包含數據體(payload)做爲入參,須要返回一個新的 state。
  1. effects:用於處理異步操做(例如:與服務端交互)和業務邏輯,也是由 action 觸發。可是,它不能夠修改 state,要經過觸發 action 調用 reducer 實現對 state 的間接操做。
  1. action:action 就是一個普通 JavaScript 對象,是 reducers 及 effects 的觸發器,形如{ type: 'add', payload: todo },經過 type 屬性能夠匹配到具體某個 reducer 或者 effect,payload 屬性則是數據體,用於傳送給 reducer 或 effect。

總體的數據流向見下圖:

 

 

在Reducer裏面,不要修改傳入的 state。 使用 Object.assign() 新建了一個副本。不能這樣使用 Object.assign(state, { visibilityFilter: action.filter }),由於它會改變第一個參數的值。你必須把第一個參數設置爲空對象。

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 參數都不一樣,分別對應它管理的那部分 state 數據。

下面兩種合成 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,減小不少重複代碼好比action reducers 常量等,dva全部的redux操做是放在models目錄下,經過namespace做爲key,標識不一樣的模塊state,能夠給state設置初始數據。

reducers跟傳統的react-redux寫法一致,全部的操做放在reducers對象內

Effect 被稱爲反作用,在咱們的應用中,最多見的就是異步操做,Effects 的最終流向是經過 Reducers 改變 State

其中上面的effects裏面,call, put實際上是saga的寫法,dva集成了saga,能夠參考上圖中的saga內容

 

DVA 首先是一個基於 redux 和 redux-saga 的數據流方案,而後爲了簡化開發體驗,DVA 還額外內置了 react-router 和 fetch,因此也能夠理解爲一個輕量級的應用框架。

DVA 是基於現有應用架構 (redux + react-router + redux-saga 等)的一層輕量封裝,沒有引入任何新概念,所有代碼不到 100 行。

在Ant-Design的Pages/.umi目錄裏面,有一個initDva.js文件,就是用來統一批量處理 DVA 的引入的,以下所示。

在有 DVA 以前,咱們一般會建立 sagas/products.jsreducers/products.js 和 actions/products.js,而後在這些文件之間來回切換。

有了 DVA 後,它最核心的是提供了 app.model 方法,用於把 reducer, initialState, action, saga 封裝到一塊兒,這樣咱們在書寫代碼的時候,把它主要內容,和加載分離出來。若是創建的Model比較多,每次開始的時候須要加入這一句好像也是挺麻煩的,若是能夠自動把這個model批量加入,應該會更好吧,不過不知道是基於什麼考量。

相關文章
相關標籤/搜索