一、es6的class、箭頭函數css
1)ES6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,做爲對象的模板。經過class
關鍵字,能夠定義類。html
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var p1 = new Point(1, 1); console.log(p1.toString()); // (1, 1) console.log(typeof Point, Point === Point.prototype.constructor); // function true
上面代碼定義了一個「類」,能夠看到裏面有一個constructor
方法,這就是構造方法,而this
關鍵字則表明實例對象。也就是說,ES5 的構造函數Point
,對應 ES6 的Point
類的構造方法。react
Point
類除了構造方法,還定義了一個toString
方法。注意,定義「類」的方法的時候,前面不須要加上function
這個關鍵字,直接把函數定義放進去了就能夠了。另外,方法之間不須要逗號分隔,加了會報錯。es6
上面代碼代表,類的數據類型就是函數,類自己就指向構造函數。使用的時候,也是直接對類使用new
命令,跟構造函數的用法徹底一致。segmentfault
構造函數的prototype
屬性,在 ES6 的「類」上面繼續存在。事實上,類的全部方法都定義在類的prototype
屬性上面。後端
class Point { constructor() { // ... } toString() { // ... } } // 等同於 Point.prototype = { constructor() {}, toString() {} };
類必須使用new
調用,不然會報錯。這是它跟普通構造函數的一個主要區別,後者不用new
也能夠執行。api
var p1 = new Point(2,3); var p2 = new Point(3,2); p1.__proto__ === p2.__proto__; //true
上面代碼中,p1
和p2
都是Point
的實例,它們的原型都是Point.prototype
,因此__proto__
屬性是相等的。跨域
__proto__
並非語言自己的特性,這是各大廠商具體實現時添加的私有屬性,雖然目前不少現代瀏覽器的 JS 引擎中都提供了這個私有屬性,但依舊不建議在生產中使用該屬性,避免對環境產生依賴。生產環境中,咱們可使用 Object.getPrototypeOf
方法來獲取實例對象的原型,而後再來爲原型添加方法/屬性。瀏覽器
2)Class 能夠經過extends
關鍵字實現繼承,這比 ES5 的經過修改原型鏈實現繼承,要清晰和方便不少。服務器
class ColorPoint extends Point {
}
上面代碼定義了一個ColorPoint
類,該類經過extends
關鍵字,繼承了Point
類的全部屬性和方法。可是因爲沒有部署任何代碼,因此這兩個類徹底同樣,等於複製了一個Point
類。
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 調用父類的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 調用父類的toString() } } let cp = new ColorPoint(1,1,'red'); console.log(cp, cp.toString()); // ColorPoint {x: 1, y: 1, color: "red"} "red (1, 1)"
子類必須在constructor
方法中調用super
方法,不然新建實例時會報錯。這是由於子類本身的this
對象,必須先經過父類的構造函數完成塑造,獲得與父類一樣的實例屬性和方法,而後再對其進行加工,加上子類本身的實例屬性和方法。若是不調用super
方法,子類就得不到this
對象。另外一個須要注意的地方是,在子類的構造函數中,只有調用super
以後,纔可使用this
關鍵字,不然會報錯。這是由於子類實例的構建,是基於對父類實例加工,只有super
方法才能返回父類實例。
若是子類沒有定義constructor
方法,這個方法會被默認添加,代碼以下。也就是說,無論有沒有顯式定義,任何一個子類都有constructor
方法。
class ColorPoint extends Point { } // 等同於 class ColorPoint extends Point { constructor(...args) { super(...args); } }
let cp = new ColorPoint(25, 8, 'green'); cp instanceof ColorPoint // true cp instanceof Point // true
上面代碼中,子類ColorPoint實例對象cp
同時是ColorPoint
和Point
兩個類的實例,這與 ES5 的行爲徹底一致。
3)ES6 容許使用「箭頭」(=>
)定義函數
var f = v => v; // 等同於 var f = function (v) { return v; };
若是箭頭函數不須要參數或須要多個參數,就使用一個圓括號表明參數部分。
var f = () => 5; // 等同於 var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等同於 var sum = function(num1, num2) { return num1 + num2; };
若是箭頭函數的代碼塊部分多於一條語句,就要使用大括號將它們括起來,而且使用return
語句返回。
var sum = (num1, num2) => { return num1 + num2; }
因爲大括號被解釋爲代碼塊,因此若是箭頭函數直接返回一個對象,必須在對象外面加上括號,不然會報錯。
// 報錯 let getTempItem = id => { id: id, name: "Temp" }; // 不報錯 let getTempItem = id => ({ id: id, name: "Temp" });
二、react
參考:http://react.css88.com/docs/cdn-links.html
<div id="root"></div> <!-- react.js 是 React 的核心庫,react-dom.js 是提供與 DOM 相關的功能 --> <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <!-- 上面的版本只適合開發環境,不適合生產環境。下面爲壓縮優化版本 <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> --> <!-- babel:將 JSX 語法轉爲 JavaScript 語法,這一步很消耗時間,實際上線的時候,應該將它放到服務器完成 --> <script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script> <!-- <script> Uncaught SyntaxError: Unexpected token <--> <!-- 控制檯有警告:You are using the in-browser Babel transformer. Be sure to precompile your scripts for production 網頁中實時將ES6代碼轉爲ES5,對性能會有影響。生產環境須要加載已經轉碼完成的腳本 React 獨有的 JSX 語法,跟 JavaScript 不兼容。凡是使用 JSX 的地方,都要加上 type="text/babel" --> <script type="text/babel"> const element = <h1>Hello, world</h1>; ReactDOM.render( element, document.getElementById('root') ); </script>
瀏覽器渲染結果:
a)crossorigin(實現更好的錯誤處理體驗)
引入跨域的腳本(好比用了 apis.google.com 上的庫文件),若是這個腳本有錯誤,由於瀏覽器的限制(根本緣由是協議的規定),是拿不到錯誤信息的。當本地嘗試使用 window.onerror
去記錄腳本的錯誤時,跨域腳本的錯誤只會返回 Script error
。但 HTML5 新的規定,是能夠容許本地獲取到跨域腳本的錯誤信息,但有兩個條件:一是跨域腳本的服務器必須經過 Access-Control-Allow-Origin
頭信息容許當前域名能夠獲取錯誤信息,二是當前域名的 script
標籤也必須指明 src
屬性指定的地址是支持跨域的地址,也就是 crossorigin 屬性。(參考:https://www.chrisyue.com/what-the-hell-is-crossorigin-attribute-in-html-script-tag.html)
b)更通用格式 UMD(Universal Module Definition)-但願提供一個先後端跨平臺的解決方案
UMD的實現很簡單,先判斷是否支持NodeJS模塊格式(exports是否存在),存在則使用NodeJS模塊格式。再判斷是否支持AMD(define是否存在),存在則使用AMD方式加載模塊。前兩個都不存在,則將模塊公開的全局(window或global)。
c)ReactDOM.render 是 React 的最基本方法,用於將模板轉爲 HTML 語言,並插入指定的 DOM 節點。
參考:https://segmentfault.com/q/1010000003877594/a-1020000003878406
JavaScript 的一種擴展語法,推薦在 React 中使用這種語法來描述 UI 信息。React爲了代碼的可讀性、更方便地建立虛擬DOM等緣由,加入了一些相似XML的語法擴展。JSX是可選的,對於使用 React 而言不是必須的。
JSX 的基本語法規則:遇到 HTML 標籤(以 <
開頭),就用 HTML 規則解析;遇到代碼塊(以 {
開頭),就用 JavaScript 規則解析。
JSX代碼並不能直接運行,須要將它編譯成正常的JavaScript表達式才能運行,jsxTransformer.js就是這一編譯器的角色。React官方博客在2015年6月發佈了一篇文章,聲明用於JSX語法解析的編譯器JSTransform已通過期,再也不維護,React JS和React Native已經所有采用第三方Babel的JSX編譯器實現。Babel做爲專門的JavaScript語法編譯工具,提供了更爲強大的功能。
function formatName(user) { return user.firstName + ' ' + user.lastName; } const user = { firstName: 'Harper', lastName: 'Perez' }; const title = '我是h1'; const element = ( <div> <h1 title={title}>Hello, {formatName(user)}!</h1> <img className="img" src="http://img2.imgtn.bdimg.com/it/u=3723784612,2573513060&fm=200&gp=0.jpg"/> </div> ); ReactDOM.render( element, document.getElementById('root') );
爲便於閱讀,咱們將 JSX 分割成多行。咱們推薦使用括號將 JSX 包裹起來,雖然這不是必須的,但這樣作能夠避免分號自動插入的陷阱。
在屬性中嵌入 JavaScript 表達式時,不要使用引號來包裹大括號。不然,JSX 將該屬性視爲字符串字面量而不是表達式。對於字符串值你應該使用引號,對於表達式你應該使用大括號,但二者不能同時用於同一屬性。
jsx裏的標籤都應該閉合。
比起 HTML , JSX 更接近於 JavaScript , 因此 React DOM 使用駝峯屬性命名約定, 而不是HTML屬性名稱。例如,class
在JSX中變爲className。
render()函數中返回的全部元素須要包裹在一個"根"元素裏面。
React DOM 會將元素及其子元素與以前版本逐一對比, 並只對有必要更新的 DOM 進行更新, 以達到 DOM 所需的狀態。
Props 是隻讀的:不管你用函數或類的方法來聲明組件, 它都沒法修改其自身 props.
1)函數式組件
全部的React組件都有一個render
函數,它指定了React組件的HTML輸出。
<div id="root"></div>
如下代碼在頁面上渲染 「Hello, Sara」
// 接收一個 props 參數, 並返回一個 React 元素 function Welcome(props) { return <h1>Hello, {props.name}</h1>; } // 用戶定義組件(Welcome 組件)將 JSX 屬性以一個單獨對象的形式傳遞給相應的組件,咱們將其稱爲 「props」 對象 const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById('root') );
組件名稱老是以大寫字母開始,不然會報錯;組件能夠在它們的輸出中引用其它組件。
function Welcome(props) { return <h1>Hello, {props.name}</h1>; } function App() { return ( <div> <Welcome name="Sara" /> <Welcome name="Cahal" /> <Welcome name="Edite" /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') );
2)提取組件
不要懼怕把一個組件分爲多個更小的組件。提取組件可能看起來是一個繁瑣的工做,可是在大型的 Apps 中能夠回報給咱們的是大量的可複用組件。一個好的經驗準則是若是你 UI 的一部分須要用屢次 ,或者自己足夠複雜,最好的作法是使其成爲可複用組件。
props是隻讀的。
全部 React 組件都必須是純函數,並禁止修改其自身 props 。固然,應用 UI 老是動態的,而且隨時有能夠改變。state(狀態)
容許 React 組件在不違反上述規則的狀況下, 根據用戶操做, 網絡響應, 或者其餘, 來動態地改變其輸出。
3)類組件
類組件容許咱們在其中添加本地狀態和生命週期鉤子
class Clock extends React.Component { // 添加一個類構造函數初始化this.state constructor(props) { super(props); this.state = {date: new Date()}; } // 掛載—組件輸出被渲染到 DOM 以後運行(設置定時器) componentDidMount() { // 若是須要存儲一些不用於視覺輸出的內容,則能夠手動向類中添加額外的字段,以下面的timerID // 若是在 render() 方法中沒有被引用, 它不該該出如今 state 中 this.timerID = setInterval( () => this.tick(), 1000 ); } // 卸載—DOM 被銷燬時運行(清除計時器) componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setState({ date: new Date() }); // this.state.date = new Date(); //這樣將不會從新渲染一個組件 } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );
分析:
當 <Clock />
被傳入 ReactDOM.render()
時, React 會調用 Clock
組件的構造函數。 由於 Clock
要顯示的是當前時間,因此它將使用包含當前時間的對象來初始化 this.state
。咱們稍後會更新此狀態。
而後 React 調用了 Clock
組件的 render()
方法。 React 從該方法返回內容中獲得要顯示在屏幕上的內容。而後,React 而後更新 DOM 以匹配 Clock
的渲染輸出。
當 Clock
輸出被插入到 DOM 中時,React 調用 componentDidMount()
生命週期鉤子。在該方法中,Clock
組件請求瀏覽器設置一個定時器來一次調用 tick()
。
瀏覽器會每隔一秒調用一次 tick()
方法。在該方法中, Clock
組件經過 setState()
方法並傳遞一個包含當前時間的對象來安排一個 UI 的更新。經過 setState()
, React 得知了組件 state
(狀態)的變化, 隨即再次調用 render()
方法,獲取了當前應該顯示的內容。 此次,render()
方法中的 this.state.date
的值已經發生了改變, 從而,其輸出的內容也隨之改變。React 因而據此對 DOM 進行更新。
若是經過其餘操做將 Clock
組件從 DOM 中移除了, React 會調用 componentWillUnmount()
生命週期鉤子, 因此計時器也會被中止。
正確地使用 State(狀態)
a)不要直接修改 state。
例如,這樣將不會從新渲染一個組件:this.state.date = new Date();
用 setState()
代替:this.setState({ date: new Date() });
惟一能夠分配 this.state
的地方是構造函數。
b)state更新多是異步的
React 爲了優化性能,有可能會將多個 setState()
調用合併爲一次更新
class Clock extends React.Component { // 添加一個類構造函數初始化this.state constructor(props) {
super(props); this.state = { date: new Date(), counter: 0 }; } // 掛載—組件輸出被渲染到 DOM 以後運行(設置定時器) componentDidMount() { this.timerID = setInterval( () => this.tick(), 1000 ); } // 卸載—DOM 被銷燬時運行(清除計時器) componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setState({ date: new Date() }); this.setState((prevState, props) => ({ counter: prevState.counter + parseInt(props.increment) })); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> <h2>counter:{this.state.counter}</h2> </div> ); } } ReactDOM.render( <Clock increment = '2'/>, document.getElementById('root') );
由於 this.props
和 this.state
多是異步更新的,你不能依賴他們的值計算下一個state。例如, 如下代碼可能致使 counter
(計數器)更新失敗
// 錯誤 this.setState({ counter: this.state.counter + this.props.increment, });
要彌補這個問題,使用另外一種 setState() 的形式,它接受一個函數而不是一個對象。這個函數將接收前一個狀態做爲第一個參數,應用更新時的 props 做爲第二個參數。
// 正確 this.setState((prevState, props) => ({ counter: prevState.counter + props.increment }));
c)state更新會被合併
當你調用 setState()
, React 將合併你提供的對象到當前的狀態中。this.setState({comments})會徹底替換this.state.comments
4)數據向下流動
一個組件能夠選擇將 state(狀態) 向下傳遞,做爲其子組件的 props(屬性)。這一般稱爲一個「從上到下」,或者「單向」的數據流。任何 state始終由某個特定組件全部,而且從該 state導出的任何數據 或 UI 只能影響樹中 「下方」 的組件。
class Clock extends React.Component { // ... render() { return ( <div> <FormattedDate date={this.state.date}/> </div> ); } } function FormattedDate(props) { return <h2>It is {props.date.toLocaleTimeString()}.</h2> } ReactDOM.render( <Clock />, document.getElementById('root') );