react(一)

一、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

上面代碼中,p1p2都是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同時是ColorPointPoint兩個類的實例,這與 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/

  • 第一個簡單的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 節點。

  • JSX(JavaScript eXtension)- 容許 HTML 與 JavaScript 混寫

參考: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 所需的狀態。

  • 組件(Components) 和 屬性(Props)

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')
);

分析:

  1. 當 <Clock /> 被傳入 ReactDOM.render() 時, React 會調用 Clock組件的構造函數。 由於 Clock 要顯示的是當前時間,因此它將使用包含當前時間的對象來初始化 this.state 。咱們稍後會更新此狀態。

  2. 而後 React 調用了 Clock 組件的 render() 方法。 React 從該方法返回內容中獲得要顯示在屏幕上的內容。而後,React 而後更新 DOM 以匹配 Clock 的渲染輸出。

  3. 當 Clock 輸出被插入到 DOM 中時,React 調用 componentDidMount() 生命週期鉤子。在該方法中,Clock 組件請求瀏覽器設置一個定時器來一次調用 tick()

  4. 瀏覽器會每隔一秒調用一次 tick()方法。在該方法中, Clock 組件經過 setState() 方法並傳遞一個包含當前時間的對象來安排一個 UI 的更新。經過 setState(), React 得知了組件 state(狀態)的變化, 隨即再次調用 render() 方法,獲取了當前應該顯示的內容。 此次,render() 方法中的 this.state.date 的值已經發生了改變, 從而,其輸出的內容也隨之改變。React 因而據此對 DOM 進行更新。

  5. 若是經過其餘操做將 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')
);
相關文章
相關標籤/搜索