1.let,const,var的區別:
var聲明的變量一般是屬於全局的變量。let和const是ES 6中新增的兩個關鍵字,用來聲明變量,let和const都是塊級作用域。let聲明的變量只在let命令所在的代碼塊內有效。const聲明一個只讀的常量,一旦聲明,常量的值就不能改變。
{ let a = 100 console.log(a);//100 } console.log(a);//Uncaught ReferenceError: a is not defined { const a = 100 console.log(a);//100 a=1000;//Assignment to constant variable. } { var a = 100 console.log(a);//100 } console.log(a);//100
2.箭頭函數:
ES 6允許使用“箭頭”(=>)定義函數。這種方式創建的函數不需要function 關鍵字,並且還可以省略return關鍵字。同時,箭頭函數內的this指向函數定義時所在的上下文對象,而不是函數執行時的上下文對象。
注意事項: 1.當我們的函數的參數爲空時我們可以寫成 var demo=()=>{}。 2.當我們函數處理的邏輯代碼只用一行時函數體的"{}"可以省略不寫。 3.函數的參數只有一個時我們可以寫成var demo =m=>{}
//原始定義的函數: function add(num1,num2){ return num1+num2; } add(111,222); //箭頭函數的改寫: var amount=(num1,num2)=>num1+num2; amount(1001,1002); //空參數的箭頭函數: var demo =()=>{ console.log("hello"); } demo();//hello //函數的邏輯代碼只有一行時: var f=(a,b)=>a+b; f(100,200); 300 var f=()=>"Hello world!"; f();//Hello world! //一個參數的箭頭函數: var demo =m=>{ console.log(m); } demo("hello world!");//hello world!
3.數據的結構賦值:
ES 6允許按照一定模式從數組和對象中提取值,對變量進行賦值,這被稱爲解構賦值。
//對象的結構賦值1 let name = 'Lily'; let age = 4; let person = {name, age}; person; // Object {name: "Lily", age: 4} //對象解構賦值2 let person = {name: 'Lily', age: 4}; let {name, age} = person; person;// //數組的結構賦值: let [a,b,c] = [1, 2, 3]; a //1 b //2 c //3 //對象參數解構 function sum ({x, y}) { return x + y; } sum({x:1, y:2}); // 3
4.Rest參數:
ES 6引入rest參數(形式爲...變量名)用於獲取函數的多餘參數,以代替arguments對象的使用。rest參數是一個數組,數組中的元素是多餘的參數。注意,rest參數之後不能再有其他參數。
function languages(lang, ...types){ console.log(types); } languages('JavaScript', 'Java', 'Python'); //["Java", "Python"]
5.React組件:
有狀態組件,無狀態組件
如果一個組件的內部狀態是不變的,當然就用不到state,這樣的組件稱之爲無狀態組件。反之,一個組件的內部狀態會發生變化,就需要使用state來保存變化,這樣的組件稱之爲有狀態組件。
//有狀態組件 import React, { Component } from "react"; class PostItem extends Component { //初始化數據的構造函數 //組件通過props接收外部傳入的數據(包括方法) //state是組件對內的接口,組件的內部的變化通過state來反映 //state是可變的,props是隻讀的在組件的內部不能修改props的數據值 constructor(props) { super(props); //初始化組件的狀態 this.state = { vote: 0 }; } //處理點讚的業務邏輯 handleClick() { let vote = this.state.vote; vote++; this.setState({ vote: vote }); } render() { const { title, author, date } = this.props; return ( <li> <div> {title} </div> <div> 創建人:<span>{author}</span> </div> <div> 創建時間:<span>{date}</span> </div> <div> <button onClick={() => { this.handleClick(); }} > 點贊 </button> <span> {this.state.vote} </span> </div> </li> ); } } export default PostItem; //無狀態組件 import React, { Component } from "react"; import PostItem from "./PostItem"; const data =[ { title: "大家一起來討論React吧", author: "張三", date: "2017-09-01 10:00" }, { title: "前端框架,你最愛哪一個", author: "李四", date: "2017-09-01 12:00" }, { title: "Web App的時代已經到來", author: "王五", date: "2017-09-01 14:00" } ]; class PostList extends Component { render() { const {title,author,date} = this.props; return ( <div> 帖子列表: <ul> { data.map(item=>( <PostItem title={item.title} author={item.author} date={item.date} /> ) ) } </ul> </div> ); } } export default PostList; 使用函數對組件的狀態進行重構: //有狀態組件 import React, { Component } from "react"; import PostItem from "./PostItem"; //getDefaultProps、getInitialState、componentWillMount、render 和 componentDidMount。 const data = [ { title: "大家一起來討論React吧", author: "張三", date: "2017-09-01 10:00" }, { title: "前端框架,你最愛哪一個", author: "李四", date: "2017-09-01 12:00" }, { title: "Web App的時代已經到來", author: "王五", date: "2017-09-01 14:00" }, ]; class PostList extends Component { constructor(props) { super(props); this.state = { posts: [] }; this.timer = null;//定時器 this.handleVote = this.handleVote.bind(this);//ES 6class中,必須手動綁定方法this的指向 } //生命週期的方法 componentDidMount() { //// 用setTimeout模擬異步從服務器端獲取數據,然後調用setState更新組件狀態。 this.timer = setTimeout(() => { this.setState({ posts: [ { id: 1, title: "大家一起來討論React吧", author: "張三", date: "2017-09-01 10:00", vote: 0 }, { id: 2, title: "前端框架,你最愛哪一個", author: "李四", date: "2017-09-01 12:00", vote: 0 }, { id: 3, title: "Web App的時代已經到來", author: "王五", date: "2017-09-01 14:00", vote: 0 } ] }); }, 1000); } componentWillUnmount() { if (this.timer) { clearTimeout(this.timer); // 清除定時器 } } handleVote(id) { //根據帖子id進行過濾,找到待修改vote屬性的帖子,返回新的posts對象 const posts = this.state.posts.map(item => { const newItem = item.id === id ? { ...item, vote: ++item.vote }:item; return newItem; }); // 使用新的posts對象設置state this.setState({ posts }); } render() { return ( <div> 帖子列表: <ul> { this.state.posts.map(item => <PostItem post = {item} onVote = {this.handleVote} /> ) } </ul> </div> ); } } export default PostList; //使用函數的無狀態組件 import React, { Component } from "react"; function PostItem(props) { const handleClick = () => { props.onVote(props.post.id); }; const { post } = props; return ( <li> <div> {post.title} </div> <div> 創建人:<span>{post.author}</span> </div> <div> 創建時間:<span>{post.date}</span> </div> <div> <button onClick={handleClick}>點贊</button> <span>{post.vote}</span> </div> </li> ); } export default PostItem;
6.屬性的校驗:
React提供了PropTypes這個對象,用於校驗組件屬性的類型。PropTypes包含組件屬性所有可能的類型,我們通過定義一個對象(對象的key是組件的屬性名,value是對應屬性的類型)實現組件屬性類型的校驗。
PropTypes可以校驗的組件屬性類型。
類型 PropTypes對應屬性String PropTypes.stringNumber PropTypes.numberBoolean PropTypes.boolFunction PropTypes.funcObject PropTypes.objectArray PropTypes.arraySymbol PropTypes.symbolElement(React元素) PropTypes.elementNode(可被渲染的節點:數字、字符串、React元素或由這些類型的數據組成的數組) PropTypes.node
示例如下:class PostItem extends React.Component {}PostItem.propTypes = {post: PropTypes.object,onVote: PropTypes.func};
當使用PropTypes.object或PropTypes.array校驗屬性類型時,我們只知道這個屬性是一個對象或一個數組,至於對象的結構或數組元素的類型是什麼樣的,依然無從得知。這種情況下,更好的做法是使用PropTypes.shape PropTypes.arrayOf。
例如: style: PropTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number }), sequence: PropTypes.arrayOf(PropTypes.number) PostItem.propTypes = { post: PropTypes.shape({ id: PropTypes.number, title: PropTypes.string, author: PropTypes.string, date: PropTypes.string, vote: PropTypes.number }).isRequired, onVote: PropTypes.func.isRequired }
defaultProps:React還提供了爲組件屬性指定默認值的特性,這個特性通過組件的defaultProps實現。當組件屬性未被賦值時,組件會使用defaultProps定義的默認屬性。例如:function Welcome(props) {return <h1 className='foo'>Hello, {props.name}</h1>;}Welcome.defaultProps = {name: 'Stranger'};
7.組件的樣式:
我們爲組件添加樣式的方式有兩種:外部CSS樣式和內聯的樣式.
外部的CSS樣式:就是通過編寫想要的css文件,在需要的地方進行引入即可。(兩種引入的方式)1.<link rel="stylesheet" type="text/css" href="style.css">2.import './style.css'; //要保證相對路徑設置正確
內聯的樣式:直接將我們需要的樣式寫入相應需要的地方即可。
function Welcome(props) { return ( <h1 style={{ width: "100%", height: "50px", backgroundColor: "blue", fontSize: "20px" }} Hello, {props.name} </h1> ); }
8.組件的生命週期:
組件從被創建到被銷燬的過程稱爲組件的生命週期。組件的生命週期可以分爲三個階段:掛載階段,更新階段,卸載階段。
注意:
只有類組件才具有生命週期方法,函數組件是沒有生命週期方法的,因此永遠不要在函數組件中使用生命週期方法。
8.1掛載階段:
這個階段組件被創建,執行初始化,並被掛載到DOM中,完成組件的第一次渲染。依次調用的生命週期方法有:(1)constructor:創建組件時我們首先會調用構造函數去接收一個props參數,props一般是從父組件中傳入的屬性對象,如果父組件中沒有傳入屬性數據而自身定義了屬性的數據,則這個props值得就是組件的默認的屬性的數據。我們要在這個方法中調用super(props)才能保證props被傳入組件中。constructor通常用於初始化組件的state以及綁定事件處理方法等工作。(2)componentWillMount:這個方法在組件被掛載到DOM前調用,且只會被調用一次。。在這個方法中調this.setState不會引起組件的重新渲染。(3)render:這個是定義組件的唯一必要的方法(組件的其他的生命週期可以省略)。在這個方法中,根據組件的props和state返回一個React元素,用於描述組件的UI,通常React元素使用JSX語法定義。需要注意的是,render並不負責組件的實際渲染工作,它只是返回一個UI的描述,真正的渲染出頁面DOM的工作由React自身負責。render是一個純函數,在這個方法中不能執行任何有副作用的操作,所以不能在render中調用this.setState,這會改變組件的狀態。(4)componentDidMount:在組件被掛載到DOM後調用,且只會被調用一次。這時候已經可以獲取到DOM結構,因此依賴DOM節點的操作可以放到這個方法中。這個方法通常還會用於向服務器端請求數據。在這個方法中調用this.setState會引起組件的重新渲染。
8.2組件的更新:
組件的更新實質上是因爲組件的props或者state的數據變化引起的組更新,props引起的組件的更新實質上是由父組件引起的,就是父組件的Render方法被調用時組件會發生更新。state引起的組件的更新是通過調用this.setState修改組件的state來進行觸發的。組件更新調用的方法是:
(1)componentWillReceiveProps(nextProps);這個方法只在props引起的組件更新過程中,纔會被調用。State引起的組件更新並不會觸發該方法的執行。方法的參數nextProps是父組件傳遞給當前組件的新的props。但如上文所述,父組件render方法的調用並不能保證傳遞給子組件的props發生變化,也就是說nextProps的值可能和子組件當前props的值相等,因此往往需要比較nextProps和this.props來決定是否執行props發生變化後的邏輯,比如根據新的props調用this.setState觸發組件的重新渲染。(2)shouldComponentUpdate(nextProps,nextState):這個方法決定組件是否繼續執行更新過程。當方法返回true時(true也是這個方法的默認返回值),組件會繼續更新過程;當方法返回false時,組件的更新過程停止,後續的componentWillUpdate、render、componentDidUpdate也不會再被調用。一般通過比較nextProps、nextState和組件當前的props、state決定這個方法的返回結果。這個方法可以用來減少組件不必要的渲染,從而優化組件的性能。(3)componentWillUpdate(nextProps,nextState):這個方法在組件render調用前執行,可以作爲組件更新發生前執行某些工作的地方,一般也很少用到。(4)render(5)componentDidUpdate(prevProps,prevState);組件更新後被調用,可以作爲操作更新後的DOM的地方。這個方法的兩個參數prevProps、prevState代表組件更新前的props和state。
8.3組件的卸載工作
組件的卸載的過程只會調用一個生命週期的方法componentWillUnmount,這個方法可以執行一些組件的清理工作,比如清除組件中的定時器避免內存泄漏。
9.列表和Keys
組件中的列表:組件中的列表我們可以將它理解爲數據的集合。
組件的keys: React使用key屬性來標記列表中的每個元素,當列表數據發生變化時,React就可以通過key知道哪些元素髮生了變化,從而只重新渲染髮生變化的元素,提高渲染效率。
10.事件的處理
注意的事項:1.事件的命名採用駝峯的命名方式:onclick要寫成onClick,onchange要寫成onChange等。2.處理事件的響應函數要以對象的形式賦值給事件的屬性。
//DOM中的事件綁定: <button onclick="clickButton()"> Click </button> //而在React元素中綁定一個點擊事件變成這種形式: <button onclick={clickButton}> //clickButton是一個函數 Click </button>
1.在DOM事件中,可以通過處理函數返回false來阻止事件的默認行爲,但在React事件中,必須顯式地調用事件對象的preventDefault方法來阻止事件的默認行爲。除了這一點外,DOM事件和React事件在使用上並無差別。2.如果在某些場景下必須使用DOM提供的原生事件,可以通過React事件對象的nativeEvent屬性獲取。
//1.使用箭頭函數 //每次調用render方法時,都會重新的創建一個新的事件的處理函數,系統的性能降低 import React, { Component } from "react"; class MyContent extends React.Component { constructor(props) { super(props); this.state = { number: 0 } } // 每點擊一次Button,state中的number增加1 handleClick(event) { const number = ++this.state.number; this.setState({ number: number }); } render() { return ( <div> <div>{this.state.number}</div> <button onClick={(event) => { this.handleClick(event); }}> Click </button> </div> ); } } export default MyContent; //使用組件的方法01 import React, { Component } from "react"; class MyContent extends React.Component { constructor(props) { super(props); this.state = { number: 0 } //將組件的方法賦值給元素的事件屬性,同時在類的構造函數中,將這個方法的this綁定到當前對象 this.handleClick = this.handleClick.bind(this); } // 每點擊一次Button,state中的number增加1 handleClick(event) { const number = ++this.state.number; this.setState({ number: number }); } render() { return ( <div> <div>{this.state.number}</div> <button onClick={this.handleClick}> Click </button> </div> ); } } export default MyContent; //使用組件的方法02 import React, { Component } from "react"; class MyContent extends React.Component { constructor(props) { super(props); this.state = { number: 0 } } // 每點擊一次Button,state中的number增加1 handleClick(event) { const number = ++this.state.number; this.setState({ number: number }); } render() { return ( <div> <div>{this.state.number}</div> {/* 事件屬性賦值和this同時綁定*/} <button onClick={this.handleClick.bind(this)}> Click </button> </div> ); } } export default MyContent; //使用ES 7的property initializers會自動爲class中定義的方法綁定this。 //但是目前還處於實驗階段,默認是不支持的 import React, { Component } from "react"; class MyContent extends React.Component { constructor(props) { super(props); this.state = { number: 0 }; } // ES7的屬性初始化語法,實際上也是使用了箭頭函數 handleClick = (event) => { const number = ++this.state.number; this.setState({ number: number }); } render() { return ( <div> <div>{this.state.number}</div> <button onClick={this.handleClick}> Click </button> </div> ); } } export default MyContent;
11.表單
受控組件:
表單中元素的狀態變化是受到React控制的,狀態的修改是通過組件的state進行的。
文本框的受控原理:
文本框包含類型爲text的input元素和textarea元素。它們受控的主要原理是,通過表單元素的value屬性設置表單元素的值,通過表單元素的onChange事件監聽值的變化,並將變化同步到React組件的state中
import React, { Component } from "react"; class LoginForm extends React.Component { constructor(props) { super(props); this.state = { name: '', password: '' }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } //對用戶名和密碼兩個input進行監聽 handleChange(event) { const target = event.target; this.setState({ [target.name]: target.value }); console.log(this.state); } //表單提交的響應的函數 handleSubmit(event) { console.log('login successfuly'); event.preventDefault(); console.log("handleSubmit觸發執行"+":"+this.state); } render() { return ( <form onSubmit={this.handleSubmit}> <label> 用戶名: <input type="text" name="name" value={this.state.name} onChange={this.handleChange}/> </label> <label> 密碼: <input type="password" name="password" value={this.state.password} onChange={this.handleChange}/> </label> <input type="submit" value="登錄"/> </form> ) } } export default LoginForm
列表:
import React, { Component } from "react"; class ReactStackForm extends React.Component { constructor(props) { super(props); this.state = { value: 'mobx' }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } //監聽下列表的變化 handleChange(event) { this.setState({ value: event.target.value }); } //表單提交的響應的函數 handleSubmit(event) { alert('You picked ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> Pick one library: {/* select的value屬性定義當前哪個option元素處於選中狀態 */} <select value={this.state.value} onChange={this.handleChange}> <option value="react">React</option> <option value="redux">Redux</option> <option value="mobx">MobX</option> </select> </label> <input type="submit" value="Submit" /> </form> ) } } export default ReactStackForm;
複選和單選:
import React, { Component } from "react"; class ReactSelectForm extends React.Component { constructor(props) { super(props); this.state = { react: false, redux: false, mobx: false }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } //監聽複選框變化,設置複選框的checked狀態 handleChange(event) { this.setState({ [event.target.name]: event.target.checked }); console.log(this.state); console.log(this.state.react); } //表單提交的響應的函數 handleSubmit(event) { //顯式地調用事件對象的preventDefault方法來阻止事件的默認行爲 event.preventDefault(); console.log(this.state); } render() { return ( <form onSubmit={this.handleSubmit}> {/* 設置3個複選框 */} <label>React <input type="checkbox" name="react" value="react" checked={this.state.react} onChange={this.handleChange} /> </label> <label>Redux <input type="checkbox" name="redux" value="redux" checked={this.state.redux} onChange={this.handleChange} /> </label> <label>MobX <input type="checkbox" name="mobx" value="mobx" checked={this.state.mobx} onChange={this.handleChange} /> </label> <input type="submit" value="Submit" /> </form> ) } } export default ReactSelectForm;
非受控組件:
其原理有悖於受控組件,其狀態的變化是不受組件的state控制的。非受控組件指表單元素的狀態依然由表單元素自己管理,而不是交給React組件管理。使用非受控組件需要有一種方式可以獲取到表單元素的值,React中提供了一個特殊的屬性ref,用來引用React組件或DOM元素的實例,因此我們可以通過爲表單元素定義ref屬性獲取元素的值。
import React, { Component } from "react"; class SimpleForm extends Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(event) { // 通過this.input 獲取到input元素的值 alert('The title you submitted was ' + this.input.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> title: {/* this.input 指向當前input元素 */} <input type="text" ref={(input) => this.input = input} /> </label> <input type="submit" value="Submit" /> </form> ); } } export default SimpleForm