摘要:更多相關(guān)介紹請看這特點僅僅只是虛擬最大限度減少與的交互類似于使用操作單向數(shù)據(jù)流很大程度減少了重復(fù)代碼的使用組件化可組合一個組件易于和其它組件一起使用,或者嵌套在另一個組件內(nèi)部。在使用后,就變得很容易維護,而且數(shù)據(jù)流非常清晰,容易解決遇到的。
歡迎移步我的博客閱讀:《React 入門實踐》
在寫這篇文章之前,我已經(jīng)接觸 React 有大半年了。在初步學(xué)習(xí) React 之后就正式應(yīng)用到項目中,當時就想把自己的一些想法寫出來分享一下,無奈不太會寫文章,再則時間不是很充裕,所以也就擱下了。
本篇文章比較基礎(chǔ),沒有深入的分析,大神們輕看。廢話就不多說了,那么讓我們來進入正題。
簡介首先想要介紹的是 React,看到這篇文章的朋友想必都有一些關(guān)于 React 的了解了,但對于剛接觸的新人而言,在這就要簡要地介紹一下了。然后就是關(guān)于使用 React 構(gòu)建一個簡單單頁應(yīng)用(下文用 SPA 代替,Single Page Application)的一些介紹和講解。
關(guān)于 ReactReact 起源于 Facebook 的內(nèi)部項目,因為該公司對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設(shè)Instagram 的網(wǎng)站。做出來以后,發(fā)現(xiàn)這套東西很好用,就在2013年5月開源了。(更多相關(guān)介紹請看這)
特點:
僅僅只是 UI
虛擬 DOM:最大限度減少與 DOM 的交互(類似于使用 jQuery 操作 DOM)
單向數(shù)據(jù)流:很大程度減少了重復(fù)代碼的使用
組件化:
可組合(Composeable):一個組件易于和其它組件一起使用,或者嵌套在另一個組件內(nèi)部。如果一個組件內(nèi)部創(chuàng)建了另一個組件,那么說父組件擁有(own)它創(chuàng)建的子組件,通過這個特性,一個復(fù)雜的UI可以拆分成多個簡單的UI組件
可重用(Reusable):每個組件都是具有獨立功能的,它可以被使用在多個UI場景
可維護(Maintainable):每個小的組件僅僅包含自身的邏輯,更容易被理解和維護
生命周期:
Mounting:已插入真實 DOM
Updating:正在被重新渲染
Unmounting:已移出真實 DOM
React 為每個狀態(tài)都提供了兩種處理函數(shù),will 函數(shù)在進入狀態(tài)之前調(diào)用,did 函數(shù)在進入狀態(tài)之后調(diào)用,三種狀態(tài)共計五種處理函數(shù)。
componentWillMount()
componentDidMount()
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
componentWillUnmount()
此外,React 還提供兩種特殊狀態(tài)的處理函數(shù)。
componentWillReceiveProps(object nextProps):已加載組件收到新的參數(shù)時調(diào)用
shouldComponentUpdate(object nextProps, object nextState):組件判斷是否重新渲染時調(diào)用
正題那么進入正題,花了點時間去寫一個簡單的 SPA,也算是一個比較完整 React 骨架,但不包括測試(測試的教程可以看這個),相關(guān)源碼可以查看 react-start-kit。
接下來看看我們這個項目的構(gòu)建需要用到些什么:
react
redux
webpack
react-router
ant design
babel
...
還有一些沒有列舉出來,具體可以看倉庫源碼的 package.json。其中的詳細介紹會在文尾列出一些我所看過的文章或是官方介紹。
配置項 Webpack說到 React 項目的構(gòu)建就不得不提 Webpack 這個神器。構(gòu)建工具有很多,例如 Grunt,Gulp,Brunch 等,相比這些構(gòu)建工具,Webpack 感覺就是和 React 不謀而合,尤其是 react-hot-loader 這樣的神器(熱加載),讓 Webpack 成為最主流的 React 構(gòu)建工具。
關(guān)于 Webpack 的特性以及介紹這里就不贅述了,我們可以從下圖看出 Webpack 的作用:
接著我們從項目代碼中來看 Webpack。
entry: { app: [__dirname + "/src/index"], }, output: { path: __dirname + "/_dist", filename: "[name]_[hash].js", }
這部分主要是指定入口和出口文件。entry 作為項目的入口文件;output 作為文件編譯后的出口,其中 path 代表輸出的路徑,filename 代表文件名稱,而 [name]_[hash] 保證了瀏覽器不會存在緩存(即修改文件后效果不生效)。
module: { loaders: [{ test: /.js$/, loaders: ["babel"], exclude: /node_modules/, }, { test: /.css$/, loaders: ["style", "css"], include: /components/, }, { test: /.(jpe?g|png|gif|svg|ico)/i, loader: "file", }, { test: /.(ttf|eot|svg|woff|woff2)/, loader: "file", }, { test: /.(pdf)/, loader: "file", }, { test: /.(swf|xap)/, loader: "file", }], }
而這部分會幫助我們?nèi)ヌ幚聿煌愋偷奈募?,其?test 就是文件的后綴,loaders 是“轉(zhuǎn)譯器”,include 是指定文件的目錄,exclude 是排除某個目錄。我們可以看出,所有的 .js 文件都會通過 babel 去轉(zhuǎn)譯,也就是我們在項目中使用 ES6+ 語法會通過 babel 轉(zhuǎn)譯成瀏覽器可以識別的 ES5 代碼。
最后配置好的 config 是這樣的:
var HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: { app: [__dirname + "/src/index"], }, output: { path: __dirname + "/_dist", filename: "[name]_[hash].js", }, resolve: { root: [ __dirname + "/src", __dirname + "/node_modules", __dirname, ], extensions: ["", ".js"], }, module: { loaders: [{ test: /.js$/, loaders: ["babel"], exclude: /node_modules/, }, { test: /.css$/, loaders: ["style", "css"], include: /components/, }, { test: /.(jpe?g|png|gif|svg|ico)/i, loader: "file", }, { test: /.(ttf|eot|svg|woff|woff2)/, loader: "file", }, { test: /.(pdf)/, loader: "file", }, { test: /.(swf|xap)/, loader: "file", }], }, plugins: [ new HtmlWebpackPlugin({ template: __dirname + "/src/index.html", favicon: __dirname + "/src/favicon.ico", inject: false, }), ], };Express 服務(wù)器啟動
Node.js web 應(yīng)用開發(fā)框架 Express 作為項目的 web 服務(wù)器,有 Node.js 開發(fā)經(jīng)驗的同學(xué)應(yīng)該挺熟悉的了,這里也不多做贅述。
最終的啟動代碼是這樣的:
var express = require("express"); var webpack = require("webpack"); var webpackConfig = require("./webpack.development"); var app = express(); var compiler = webpack(webpackConfig); app.use(require("webpack-dev-middleware")(compiler, { stats: { colors: true, }, })); app.use(require("webpack-hot-middleware")(compiler)); //熱加載 app.listen(process.env.PORT, function(err) { //在沒有端口的情況下,會自動給出一個隨機端口 if (err) { console.log(err); } });
為了方便我們的訪問,項目使用了 minihost 進行啟動,方便快捷。值得一提的是,使用 h -- npm start 命令啟動時,訪問的是項目文件夾的名稱作為鏈接,例如項目叫 myproject,那么此時可以訪問 myproject.t.t。
Redux對于復(fù)雜的 SPA,狀態(tài)(state)管理非常重要。state 可能包括:服務(wù)端的響應(yīng)數(shù)據(jù)、本地對響應(yīng)數(shù)據(jù)的緩存、本地創(chuàng)建的數(shù)據(jù)(比如,表單數(shù)據(jù))以及一些 UI 的狀態(tài)信息(比如,路由、選中的 tab、是否顯示下拉列表、頁碼控制等等)。如果 state 變化不可預(yù)測,就會難于調(diào)試(state 不易重現(xiàn),很難復(fù)現(xiàn)一些 bug)和不易于擴展(比如,優(yōu)化更新渲染、服務(wù)端渲染、路由切換時獲取數(shù)據(jù)等等)。
state 為單一對象,使得 Redux 只需要維護一棵狀態(tài)樹,服務(wù)端很容易初始化狀態(tài),易于服務(wù)器渲染。state 只能通過 dispatch(action) 來觸發(fā)更新,更新邏輯由 reducer 來執(zhí)行。
在使用 Redux 后,state 就變得很容易維護,而且數(shù)據(jù)流非常清晰,容易解決遇到的 BUG。
我們可以看下圖來簡要地理解 Redux:
我們可以在項目中看到的結(jié)構(gòu)是:
├─store ├─actions ├─reducers ├─constants ├─helpers ├─components ├─app.js ├─favicon.ico ├─index.html ├─index.js └─routes.js
最終我們的 store 是:
import {createStore, applyMiddleware, combineReducers, compose} from "redux"; import thunk from "redux-thunk"; import {reduxReactRouter} from "redux-router"; import createHistory from "history/lib/createHashHistory"; import routes from "../routes"; import * as reducers from "../reducers"; let middlewares = [thunk]; if (process.env.NODE_ENV === "development") { //在開發(fā)環(huán)境下可以看到 state 的 log const logger = require("redux-logger"); middlewares = [...middlewares, logger]; } const finalCreateStore = compose( //組合多個函數(shù) applyMiddleware(...middlewares), reduxReactRouter({routes, createHistory}), )(createStore); //創(chuàng)建 store 來管理所有的 state export default function configureStore(initialState) { const reducer = combineReducers(reducers); //把一個由多個不同 reducer 函數(shù)作為 value 的 object,合并成一個最終的 reducer 函數(shù) const store = finalCreateStore(reducer, initialState); if (process.env.NODE_ENV === "development" && module.hot) { //開發(fā)環(huán)境下的熱加載 module.hot.accept("../reducers", () => { const nextReducers = require("../reducers"); const nextReducer = combineReducers(nextReducers); store.replaceReducer(nextReducer); }); } return store; }
獲取 state 需要在組件中調(diào)用 connect 函數(shù),可以自行定義需要獲取的 state。(這用于區(qū)分展示型和容器型組件)
... @connect( state => ({ data: state.data }) ) export default class ComponentOne extends Component { ... }
注意:connect 必須緊跟 component 的定義,不然會報錯。
Router為項目添加路由系統(tǒng),使用了 react-router 來管理路由。在開發(fā)項目的時候,比較推薦的做法是使用路由去跳轉(zhuǎn)頁面,并且創(chuàng)建 store 的同時我們就把 router 加入其中,然后我們根據(jù)路由的變化去更新視圖。
我們可以看看路由的源碼:
import React from "react"; import Route from "react-router/lib/Route"; //import {Route} from "react-router"; import Base from "components/base/Base"; import Home from "components/home/Home"; export default ();
path 是跳轉(zhuǎn)路徑,component 是與路徑相匹配的組件。
Ant Design由螞蟻金服技術(shù)部出品的一個 UI 設(shè)計語言,也是項目中所用到的 UI 組件庫。
特性:
Designed as Ant Design,提煉和服務(wù)企業(yè)級中后臺產(chǎn)品的交互語言和視覺風(fēng)格
React Component 上精心封裝的高質(zhì)量 UI 庫
基于 npm + webpack + babel 的工作流,支持 ES2015
選擇理由:
有很好的技術(shù)支持
簡潔的樣式
基本涵蓋常用組件
...
組件作為 React 渲染的一個基本組成,我們通常把它們分為兩類,容器型和展示型。相較于容器型,展示型是通過容器型傳遞 props 來獲取數(shù)據(jù),而容器型可以直接從 store 中獲取,處理并傳遞給下級組件。
在實際應(yīng)用中會發(fā)現(xiàn),定義一個容器型組件負責(zé)處理數(shù)據(jù),然后分發(fā)給下級展示型組件,當需要更新數(shù)據(jù)時,那么容器型組件發(fā)生變化會引起下級展示型組件的變化,這樣就對我們業(yè)務(wù)上造成了一定的困擾(在不需要更新的部分組件上也發(fā)生了更新)。因此,我們選擇在需要獲取數(shù)據(jù)的組件中使用 connect,這樣則會方便很多(感覺有些違反規(guī)則)。
在項目中我們會這么定義組件:
import React, {Component} from "react"; import {connect} from "react-redux"; import Presentational from "components/common/Presentational"; @connect( state => ({ data: state.data }) ) export default class Container extends Component { render() { const {data} = this.props; return () } }
上面是可以從 store 獲取數(shù)據(jù)的組件,并嵌套另一個組件,將數(shù)據(jù)傳遞給它。
import React, {Component, PropTypes} from "react"; export default class Presentational extends Component { static propTypes = { data: PropTypes.string, } render() { const {data} = this.props; return ({data}) } }
獲取上一個組件傳遞過來的數(shù)據(jù),并展示出來。
總結(jié)這是一篇科普文(哈哈~囧),并沒有深入去分析各項技術(shù)的具體內(nèi)容,希望能幫助剛?cè)胧?React 的新手們。實踐項目的源碼可以在 react-start-kit 看到,你可以下載這個項目進行自己的一些探索和開發(fā)。還在努力探索中,文中有措辭不當或是疏漏,歡迎提出意見和建議。
參考react 官網(wǎng)
Babel 官網(wǎng)
redux 介紹
redux 中文文檔
Ant design 官網(wǎng)
React 入門實例教程
react-router 中文文檔
Webpack 傻瓜式指南(一)
CSS Modules 詳解及 React 中實踐
一看就懂的 ReactJs 入門教程(精華版)
深入淺出React(二):React開發(fā)神器Webpack
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/78831.html
摘要:特意對前端學(xué)習(xí)資源做一個匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進步。 特意對前端學(xué)習(xí)資源做一個匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會及時更新,平時業(yè)務(wù)工作時也會不定期更...
摘要:一團隊組織網(wǎng)站說明騰訊團隊騰訊前端團隊,代表作品,致力于前端技術(shù)的研究騰訊社交用戶體驗設(shè)計,簡稱,騰訊設(shè)計團隊網(wǎng)站騰訊用戶研究與體驗設(shè)計部百度前端研發(fā)部出品淘寶前端團隊用技術(shù)為體驗提供無限可能凹凸實驗室京東用戶體驗設(shè)計部出品奇舞團奇虎旗下前 一、團隊組織 網(wǎng)站 說明 騰訊 AlloyTeam 團隊 騰訊Web前端團隊,代表作品WebQQ,致力于前端技術(shù)的研究 ISUX 騰...
摘要:一團隊組織網(wǎng)站說明騰訊團隊騰訊前端團隊,代表作品,致力于前端技術(shù)的研究騰訊社交用戶體驗設(shè)計,簡稱,騰訊設(shè)計團隊網(wǎng)站騰訊用戶研究與體驗設(shè)計部百度前端研發(fā)部出品淘寶前端團隊用技術(shù)為體驗提供無限可能凹凸實驗室京東用戶體驗設(shè)計部出品奇舞團奇虎旗下前 一、團隊組織 網(wǎng)站 說明 騰訊 AlloyTeam 團隊 騰訊Web前端團隊,代表作品WebQQ,致力于前端技術(shù)的研究 ISUX 騰...
摘要:一團隊組織網(wǎng)站說明騰訊團隊騰訊前端團隊,代表作品,致力于前端技術(shù)的研究騰訊社交用戶體驗設(shè)計,簡稱,騰訊設(shè)計團隊網(wǎng)站騰訊用戶研究與體驗設(shè)計部百度前端研發(fā)部出品淘寶前端團隊用技術(shù)為體驗提供無限可能凹凸實驗室京東用戶體驗設(shè)計部出品奇舞團奇虎旗下前 一、團隊組織 網(wǎng)站 說明 騰訊 AlloyTeam 團隊 騰訊Web前端團隊,代表作品WebQQ,致力于前端技術(shù)的研究 ISUX 騰...
摘要:編程書籍的整理和收集最近一直在學(xué)習(xí)深度學(xué)習(xí)和機器學(xué)習(xí)的東西,發(fā)現(xiàn)深入地去學(xué)習(xí)就需要不斷的去提高自己算法和高數(shù)的能力然后也找了很多的書和文章,隨著不斷的學(xué)習(xí),也整理了下自己的學(xué)習(xí)筆記準備分享出來給大家后續(xù)的文章和總結(jié)會繼續(xù)分享,先分享一部分的 編程書籍的整理和收集 最近一直在學(xué)習(xí)deep learning深度學(xué)習(xí)和機器學(xué)習(xí)的東西,發(fā)現(xiàn)深入地去學(xué)習(xí)就需要不斷的去提高自己算法和高數(shù)的能力然后...
閱讀 1470·2021-10-19 11:42
閱讀 751·2021-09-22 16:04
閱讀 1901·2021-09-10 11:23
閱讀 1904·2021-07-29 14:48
閱讀 1282·2021-07-26 23:38
閱讀 2842·2019-08-30 15:54
閱讀 1054·2019-08-30 11:25
閱讀 1729·2019-08-29 17:23