react初學(xué)者——談?wù)凴eact.js的核心入門知識

近來React.js變得越來越流行始锚,本文就來談一談React.js的入門實(shí)踐刽酱,通過分析一些常用的概念,以及提供一些入門的最佳編程編程方式瞧捌,僅供參考棵里。

首先需要搞懂的是,React并不是一個(gè)框架姐呐,React提供了一些新穎的概念殿怜、庫 和編程原則讓你能夠同時(shí)在服務(wù)端和客戶端編寫快速、緊湊曙砂、漂亮的代碼來構(gòu)建你的web應(yīng)用头谜。

如果你使用React,那么可能會(huì)涉及到一些常用的概念或技術(shù)鸠澈,包括:

ES6 React

虛擬DOM(virtual DOM)

組件驅(qū)動(dòng)開發(fā)(component-driven development)

不變性(immutability)

自上而下的渲染(top-down rendering)

渲染路徑和優(yōu)化

打包工具, ES6, 構(gòu)建請求, debugging, 路由等

同構(gòu)React(isomorphic React)

什么是React.js

React.js不是一個(gè)框架

在整個(gè)Web應(yīng)用的MVC架構(gòu)中柱告,你可以將React看作為視圖層截驮,并且是一個(gè)高效 的視圖。React提供了和以往不一樣的方式來看待視圖际度,它以組件開發(fā)為基礎(chǔ)葵袭。 對React應(yīng)用而言,你需要分割你的頁面甲脏,使其成為一個(gè)個(gè)的組件眶熬。也就是說,你的 應(yīng)用是由這些組件組合而成的块请。

你可以通過分割組件的方式去開發(fā)復(fù)雜的頁面或某個(gè)功能區(qū)塊,并且組件是可以 被復(fù)用的拳缠。這個(gè)過程大概類似于用樂高積木去瓶裝不同的物體墩新。我們稱這種編程方式稱為 組件驅(qū)動(dòng)開發(fā)。

React的一大特點(diǎn)是其所擁有的虛擬DOM窟坐,它讓頁面渲染變得非常的高效海渊,并且比直接 操縱DOM變得更為可控。這兩大特點(diǎn)的組合使得React具有強(qiáng)大的自上而下的頁面渲染 能力哲鸳。

好了臣疑,React的有兩個(gè)特點(diǎn):組件化和高效的虛擬DOM,但是為什么它這么被看好呢徙菠? 因?yàn)镽eact更多的是一種概念層面的東西讯沈,而庫是其次的。也有很多其他遵從了這些思想的第三方實(shí)現(xiàn)婿奔。和每一個(gè)編程概念一樣缺狠,React尤其 獨(dú)有的解決方案、工具和工具萍摊。但這里并不會(huì)深入的去討論他們挤茄,而是關(guān)注React本身。

Virtual DOM

為了跟蹤模型層的變化冰木,并且將其應(yīng)用到DOM中(也就是渲染)穷劈,我們需要注意兩個(gè) 重要的事情:

數(shù)據(jù)是什么時(shí)候改變的

哪一個(gè)(些)DOM元素需要被更新

對于(1)而言,React提供了一個(gè)觀察者模型用于替代傳統(tǒng)的臟檢查(dirty checking)踊沸, 也就是持續(xù)的檢查模型的變化歇终。這也就是解釋了為什么React不需要計(jì)算哪些發(fā)生 了改變的原因,因?yàn)樗鼤?huì)立即知道雕沿。這個(gè)過程減少了計(jì)算量练湿,并它應(yīng)用程序變得 更平滑。但這里真正有趣的是审轮,React是如何管理DOM操縱的肥哎。

對于DOM改變(2)而言辽俗,React在內(nèi)存中構(gòu)建了 DOM 的樹形表示,并且計(jì)算出哪個(gè)DOM元素應(yīng)該被改變篡诽。對瀏覽器而言崖飘,DOM操縱是比較耗費(fèi)性能的,因此我們更傾向于 讓其變得最小化杈女。幸運(yùn)的是朱浴,React視圖盡可能少的觸及到DOM元素。給予對象表示而言达椰, 更少的DOM操縱意味著計(jì)算會(huì)更快翰蠢,因此DOM改變也被盡可能的減少。

React在底層實(shí)現(xiàn)了一個(gè)diffing算法啰劲,該算法使用DOM的樹形表示法梁沧,當(dāng)某個(gè)節(jié)點(diǎn)發(fā)生變化(標(biāo)記為dirty)時(shí)它會(huì)重新計(jì)算整個(gè)子樹,你會(huì)注意到你的模型發(fā)生 了改變蝇裤,因?yàn)檎麄€(gè)子樹在之后會(huì)被重新渲染廷支。關(guān)于該算法的詳細(xì)分析可以參考這篇文章

如何在服務(wù)端渲染

因?yàn)镽eact在DOM表示時(shí)使用了一個(gè)虛擬(假的)DOM栓辜,因此借助于這種方式使得在服務(wù)端 渲染輸出HTML稱為可能(不借助于JSDom, PhantomJS等)恋拍。React還能智能的識別出 服務(wù)端渲染出來的頁面標(biāo)記,并在客戶端只為這些標(biāo)記添加事件處理器藕甩,這對構(gòu)建 同構(gòu)web app非常有用施敢。

有意思的是,React渲染出來的HTML標(biāo)記都包含了data-reactid屬性辛萍,這有助于 React中追蹤DOM節(jié)點(diǎn)悯姊。

一些閱讀資料

React’s diff algorithm

The Secrets of React’s virtual DOM

Why is React’s concept of virtual DOM said to be more performant than dirty model checking?

virtual-dom

組件驅(qū)動(dòng)開發(fā)

對于component-driven development而言,你在一個(gè)模板中是看不到整個(gè)網(wǎng)站的贩毕。 雖然在一開始你可能會(huì)遇到一些困難悯许,但是如果進(jìn)一步的使用這種思路,你會(huì)發(fā)現(xiàn) 它易于理解辉阶,易于維護(hù)先壕,并且容易測試。

如何使用React的方式來思考組件開發(fā)

下面我們來看如何實(shí)現(xiàn)組件驅(qū)動(dòng)開發(fā)這一理念谆甜。我們看一個(gè)例子垃僚,這個(gè)例子來源于?thinking in react?這篇文章。對于構(gòu)建一個(gè)可過濾的產(chǎn)品列表而言规辱,通常其包括如下的組件結(jié)構(gòu):

FilterableProductTable

SearchBar

ProductTable

ProductCategoryRow

ProductRow

一個(gè)組件應(yīng)該包含什么

首先谆棺,理想的,我們應(yīng)該遵守單一責(zé)任原則來設(shè)計(jì)你的組件罕袋。當(dāng)你發(fā)下你的組件應(yīng)該做的更多的時(shí)候改淑,你可以考慮將其分割為 更小的組件集合碍岔。

因?yàn)槲覀冊谟懻摻M件層級,因此在你的組件中也會(huì)使用到其他組件朵夏。我們首先看下 在ES5中組件代碼是什么樣子的:

var HelloComponent = React.createClass({?

? ? render: function() {? ? ? ? return <div>Hello {this.props.name}</div>;? ? }});

如果使用ES6蔼啦,你的組件代碼可以這樣寫:

class HelloComponent extends React.Component {?

? render() {? ? return <div>Hello {this.props.name}</div>;? }}

JS和JSX

正如你說看到的,我們的組件是JS和HTML代碼的混合仰猖,你可能會(huì)覺得這很糟糕捏肢,因?yàn)?MVC一直在教我們盡可能的隔離視圖和控制邏輯。但另一方饥侵,這種混合獲得另一個(gè)層面的 單一責(zé)任鸵赫,他使得組件更加的靈活和可重用。

當(dāng)然爆捞,在React中你也可以使用純JS來編寫你的組件:

render () {? ? return React.createElement("div", null, "Hello ",? ? ? ? this.props.name);}

是的奉瘤,你會(huì)發(fā)現(xiàn)這很麻煩,沒有使用HTML來得直觀煮甥。因此React提供了JSX (JavaScript eXtension)語法讓你能夠在JS中書寫HTML代碼。

render () {? ? return <div>Hello {this.props.name}</div>;}

什么是JSX

JSX在ECMAScript的基礎(chǔ)上提供了類似于XML的擴(kuò)展藕赞。 JSX和HTML有點(diǎn)像成肘,但也有不一樣的地方。例如斧蜕,HTML中的class屬性在JSX中 為className双霍。其他不一樣的地方,你可以參考FB的HTML Tags vs. React Components?這篇文章批销。

但是由于瀏覽器原生并不支持JSX洒闸,因此我們需要將其編譯為JS,有很多方法能夠 完成這個(gè)任務(wù)均芽,后面我們會(huì)提到這些方法丘逸。此外,Babel也能夠講JSX編譯為JS掀宋。

一些參考資料:

JSX in depth

Online JSX compiler

Babel: How to use the react transformer

組件還應(yīng)該包括什么

每個(gè)組件都應(yīng)該包括一些內(nèi)部狀態(tài)深纲,處理邏輯,和事件處理器(例如按鈕點(diǎn)擊劲妙、輸入改變)湃鹊, 當(dāng)然也包括一些內(nèi)部的樣式。

你會(huì)遇到{this.props.name}這樣的代碼片段镣奋,這意味著你可以通過屬性的方式 先組件內(nèi)傳遞數(shù)據(jù)币呵,例如<MyComp name='weiwei sun' />。這讓組件變得可重用侨颈, 并且能夠自上而下的向嵌套的組件傳遞數(shù)據(jù)余赢。

示例代碼如下:

class UserName extends React.Component {?

? render() {? ? return <div>name: {this.props.name}</div>;? }}class User extends React.Component {?

? render() {? ? return <div>? ? ? ? <h1>City: {this.props.user.city}</h1>? ? ? ? <UserName name={this.props.user.name} />? ? ? </div>;

? }

}

var user = { name: 'John', city: 'San Francisco' };?

React.render(<User user={user} />, document.body);

React擁抱ES6

在React中嘗試編寫ES6是個(gè)非常不錯(cuò)的開始芯义,React并不是一開始就支持ES6的, 而是從v0.13.0開始支持的没佑。你會(huì)經(jīng)常用到的ES6特性包括類毕贼、箭頭函數(shù)、consts 和模塊蛤奢。例如鬼癣,我們會(huì)經(jīng)常從繼承React.Component類開始編寫我們的組件。

還有一點(diǎn)需要注意的是啤贩,并不是每個(gè)瀏覽器都支持ES6待秃,因此目前情況下,我們需要 使用一些工具將我們編寫的ES6代碼轉(zhuǎn)換為ES5代碼痹屹,我推薦使用Babel章郁。

一些參考資料:

Babel: Learn ES6

React ES6 announcement

組件生命周期

每個(gè)React組件在加載時(shí)都有特定的生命周期,在此期間不同的方法會(huì)被執(zhí)行志衍。 下面簡單介紹React組件的生命周期:

componentWillMount

該方法會(huì)在組件render之前執(zhí)行暖庄,并且永遠(yuǎn)只執(zhí)行一次。

componentDidMount

該方法會(huì)在組件加載完畢之后立即執(zhí)行楼肪。此時(shí)培廓,組件已經(jīng)完成了DOM結(jié)構(gòu)的渲染, 并可以通過this.getDOMNode()方法來訪問春叫。

componentWillReceiveProps

組件接收到一個(gè)新的prop時(shí)會(huì)被執(zhí)行肩钠,且該方法在初始render時(shí)不會(huì)被調(diào)用。

shouldComponentUpdate

在組件接收到新的props或state時(shí)被執(zhí)行暂殖。

componentWillUpdate

在組件接收到新的props或者state但還沒有render時(shí)被執(zhí)行价匠。 在初始化時(shí)不會(huì)被執(zhí)行。

componentDidUpdate

在組件完成更新后立即執(zhí)行呛每。在初始化時(shí)不會(huì)被執(zhí)行踩窖。 一般會(huì)在組件完成更新后被使用。

componentWillUnMount

在組件從DOM中unmount后立即執(zhí)行莉给。該方法主要用來執(zhí)行一些必要的清理任務(wù)毙石。

關(guān)于生命周期的具體內(nèi)容,你可以參考官方文檔颓遏。

在打包時(shí)使用Webpack和Babel

我們會(huì)經(jīng)常用到一些工具徐矩,首先一個(gè)是node.js的模塊系統(tǒng)和它的包管理工具npm。 我們會(huì)編寫node風(fēng)格的代碼來require我們需要的東西叁幢。并且react本身也是一個(gè)獨(dú)立的?npm包滤灯。

通常你有兩種選擇,commonJS或者ES6:

var React = require('react/addon');var MyComponent = React.createClass({? ? // do something});module.exports = MyComponent;

或者

import React from 'react/addons';class MyComponent extends React.Component {? ? // do something use es6}export default MyComponent;

例如,我們會(huì)使用debug模塊來調(diào)試鳞骤, 使用superagent模塊來編寫請求窒百。

現(xiàn)在,我們有了Node的依賴管理系統(tǒng)豫尽,并且使用npm來提供模塊篙梢。下面我們需要做的 事:選擇一個(gè)合適的庫來打包我們的代碼,并且能夠讓其運(yùn)行在瀏覽器上美旧。

因此我們需要一個(gè)打包器渤滞。目前最流行的解決方案包括兩個(gè),分別是Browserify和?Webpack榴嗅。我們選擇使用Webpack妄呕,因?yàn)閃ebpack 更適合于React社區(qū)。

Webpack是如何工作的

Webpack用于打包我們的代碼嗽测,并且包含進(jìn)我們需要的包绪励,然后輸出為瀏覽器可運(yùn)行的 文件。因?yàn)槲覀兪褂肑SX和ES6唠粥,因此我們需要相應(yīng)的工具來將其轉(zhuǎn)換為ES5疏魏。事實(shí)上, Babel能夠同時(shí)做這兩件事。使用Webpack能夠很輕松的完成這些任務(wù)晤愧,因?yàn)閃ebpack 是面向配置的蠢护。

使用如下命令開始:

npm init

npm install webpack --save-dev

npm install babel --save-dev

npm install babel-loader --save-dev

然后創(chuàng)建webpack.config.js文件,我們需要使用ES5來編寫該文件养涮,因?yàn)樗?webpack的配置文件。一個(gè)典型的配置方式如下:

var path = require('path');module.exports = {?

? entry: path.resolve(__dirname, '../src/client/scripts/client.js'),? output: {? ? path: path.resolve(__dirname, '../dist'),? ? filename: 'bundle.js'? },? module: {? ? loaders: [? ? ? {? ? ? ? test: /src\/.+.js$/,? ? ? ? exclude: /node_modules/,? ? ? ? loader: 'babel'? ? ? }? ? ]? }};

運(yùn)行webpack命令你可以執(zhí)行打包流程眉抬。這之后你可以只在頁面中包含bundle.js即可贯吓。 如下:

<script src='bundle.js'></script>

(提示:你可以使用node-static來存放你的靜態(tài)資源文件,使用npm install -g node-static?來安裝蜀变,并使用static .來啟動(dòng))悄谐。

項(xiàng)目結(jié)構(gòu)

一個(gè)典型的項(xiàng)目結(jié)構(gòu)你可以參考這個(gè)倉庫

config/?

? ? app.js

? ? webpack.js (js config over json -> flexible)src/?

? app/ (the React app: runs on server and client too)? ? components/? ? ? __tests__ (Jest test folder)? ? ? AppRoot.jsx

? ? ? Cart.jsx

? ? ? Item.jsx

? ? index.js (just to export app)? ? app.js

? client/? (only browser: attach app to DOM)? ? styles/? ? scripts/? ? ? client.js

? ? index.html

? server/? ? index.js

? ? server.js.gitignore.jshintrcpackage.json?

README.md

如何測試React組件

對于React組件的測試库北,這里推薦使用Jest, Jest也是由Facebook提供的測試框架爬舰,并且有很多強(qiáng)大的特性,但這里并會(huì)詳細(xì)的 介紹它們寒瓦。

關(guān)于Jest情屹,我推薦你閱讀和嘗試來自Facebook的Tutorial

對于ES6代碼的測試杂腰,你可以參考?React ES6 Testing垃你。

小結(jié)

本文簡單介紹了React的基礎(chǔ)原理,一些相關(guān)的編程技術(shù)。后續(xù)還會(huì)整合一些資料 談一談Flux和同構(gòu)惜颇。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末皆刺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子凌摄,更是在濱河造成了極大的恐慌羡蛾,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锨亏,死亡現(xiàn)場離奇詭異痴怨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)屯伞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門腿箩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人劣摇,你說我怎么就攤上這事珠移。” “怎么了末融?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵钧惧,是天一觀的道長。 經(jīng)常有香客問我勾习,道長浓瞪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任巧婶,我火速辦了婚禮乾颁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艺栈。我一直安慰自己英岭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布湿右。 她就那樣靜靜地躺著诅妹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪毅人。 梳的紋絲不亂的頭發(fā)上吭狡,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音丈莺,去河邊找鬼划煮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛场刑,可吹牛的內(nèi)容都是我干的般此。 我是一名探鬼主播蚪战,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼铐懊!你這毒婦竟也來了邀桑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤科乎,失蹤者是張志新(化名)和其女友劉穎壁畸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茅茂,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捏萍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了空闲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片令杈。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖碴倾,靈堂內(nèi)的尸體忽然破棺而出逗噩,到底是詐尸還是另有隱情,我是刑警寧澤跌榔,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布异雁,位于F島的核電站,受9級特大地震影響僧须,放射性物質(zhì)發(fā)生泄漏纲刀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一担平、第九天 我趴在偏房一處隱蔽的房頂上張望示绊。 院中可真熱鬧,春花似錦暂论、人聲如沸耻台。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蹋砚,卻和暖如春扼菠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坝咐。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工循榆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人墨坚。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓秧饮,卻偏偏與公主長得像映挂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子盗尸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容