1.什麼是React?
React 是一個用於構建使用者介面的JavaScript庫 核心專注於檢視,目的實現元件化開發
2.元件化的概念
我們可以很直觀的將一個複雜的頁面分割成若干個獨立元件,每個元件包含自己的邏輯和樣式 再將這些獨立元件組合完成一個複雜的頁面。 這樣既減少了邏輯複雜度,又實現了程式碼的重用
- 可組合:一個元件可以和其他的元件一起使用或者可以直接巢狀在另一個元件內部
- 可重用:每個元件都是具有獨立功能的,它可以被使用在多個場景中
- 可維護:每個小的元件僅僅包含自身的邏輯,更容易被理解和維護
3.跑通react開發環境
$ npm install create-react-app -g
$ create-react-app <project-name>
$ cd <project-name> && npm start
複製程式碼
預設會自動安裝React,react由兩部分組成,分別是:
- react.js 是 React 的核心庫
- react-dom.js 是提供與DOM相關的功能,會在window下增加ReactDOM屬性,內部比較重要的方法是render,將react元素或者react元件插入到頁面中。
4.簡介JSX
是一種JS和HTML混合的語法,將元件的結構、資料甚至樣式都聚合在一起定義元件,會編譯成普通的Javascript。 需要注意的是JSX並不是html,在JSX中屬性不能包含關鍵字,像class需要寫成className,for需要寫成htmlFor,並且屬性名需要採用駝峰命名法!
5.createElement
JSX其實只是一種語法糖,最終會通過babel轉譯成createElement語法,以下程式碼等價
ReactDOM.render(<div>姜,<span>帥哥</span></div>);
ReactDOM.render(React.createElement("div",null,"姜,",React.createElement("span",null,"帥哥")));
複製程式碼
我們一般使用React.createElement來建立一個虛擬dom元素。
6.react元素/JSX元素
function ReactElement(type,props) {
this.type = type;
this.props = props;
}
let React = {
createElement(type,props={},...childrens){
childrens.length===1?childrens = childrens[0]:void 0
return new ReactElement(type,{...props,children:childrens})
}
};
複製程式碼
ReactElement就是虛擬dom的概念,具有一個type屬性代表當前的節點型別,還有節點的屬性props
7.模擬render實現
let render = (eleObj,container)=>{
// 先取出第一層 進行建立真實dom
let {type,props} = eleObj;
let elementNode = document.createElement(type); // 建立第一個元素
for(let attr in props){ // 迴圈所有屬性
if(attr === 'children'){ // 如果是children表示有巢狀關係
if(typeof props[attr] == 'object'){ // 看是否是隻有一個文字節點
props[attr].forEach(item=>{ // 多個的話迴圈判斷 如果是物件再次呼叫render方法
if(typeof item === 'object'){
render(item,elementNode)
}else{ //是文字節點 直接建立即可
elementNode.appendChild(document.createTextNode(item));
}
})
}else{ // 只有一個文字節點直接建立即可
elementNode.appendChild(document.createTextNode(props[attr]));
}
}else if(attr === 'className'){ // 是不是class屬性 class 屬性特殊處理
elementNode.setAttribute('class',props[attr]);
}else{
elementNode.setAttribute(attr,props[attr]);
}
}
container.appendChild(elementNode)
};
複製程式碼
8.JSX表示式的用法
- 可以放JS的執行結果 2) 如果換行需要用()包裹jsx程式碼 3) 可以把JSX元素當作函式的返回值 4) <{來判斷是表示式還是js
import React from 'react';
import ReactDOM from 'react-dom';
function toResult({name,age}) {
return <span>今年{name},{age}歲了!</span>
}
let arrs = [{name:'lisi',age:8},,{name:'姜文',age:28}];
ReactDOM.render(<div>
{arrs.map(((item,index)=>(
typeof item==='object'?<li key={index}>{toResult(item)}</li>:null
)))}
</div>,document.getElementById('root'));
複製程式碼
null也是合法元素,迴圈時需要帶key屬性
9.JSX屬性
在JSX中分為普通屬性和特殊屬性,像class要寫成className,for要寫成htmlFor style要採用物件的方式 dangerouslyInnerHTML插入html
10.在JSX中分為普通屬性和特殊屬性,像class要寫成className,for要寫成htmlFor style要採用物件的方式 dangerouslyInnerHTML插入html
react元素是是元件組成的基本單位
首字母必須大寫,目的是為了和JSX元素進行區分 元件定義後可以像JSX元素一樣進行使用 每個元件必須返回唯一的頂級JSX元素 可以通過render方法將元件渲染成真實DOM
11.元件的兩種定義方式
react怎麼區分是元件還是jsx元素?元件名需要開頭大寫,react元件當作jsx來進行使用
第一種方式是函式宣告
function Build(props) {
return <p>{props.name} {props.age}</p>
}
render(<div>
<Build name={school1.name} age={school1.age}/>
<Build {...school2} />
</div>,window.root);
複製程式碼
第二種方式是類宣告
class Build extends Component{
render(){
let {name,age} = this.props;
return <p>{name} {age}</p>
}
}
複製程式碼
類宣告有狀態,this,和宣告週期
12.元件中屬性和狀態的區別
元件的資料來源有兩個地方 props 外界傳遞過來的(預設屬性,屬性校驗) state 狀態是自己的,改變狀態唯一的方式就是setState 屬性和狀態的變化都會影響檢視更新
13.繫結事件
給元素繫結事件,事件繫結方式
class Clock extends Component {
constructor(){
super();
this.state = {date:new Date().toLocaleString()}
}
componentDidMount(){ //元件渲染完成,當渲染後會自動觸發此函式
this.timer = setInterval(()=>{ // 箭頭函式 否則this 指向的是window
this.setState({date:new Date().toLocaleString()})
},1000);
}
componentWillUnmount(){ //元件將要解除安裝,當元件移除時會呼叫
clearInterval(this.timer); //一般在這個方法中 清除定時器和繫結的事件
}
destroy=()=>{ //es7 箭頭函式
// 刪除某個元件
ReactDOM.unmountComponentAtNode(window.root);
}
render(){
// 給react元素繫結事件預設this是undefined,bind方式 在就是箭頭函式
return <h1 onClick={this.destroy}>{this.state.date}</h1>
}
}
複製程式碼
// 執行順序 constructor -> render -> componentDidMount -> setState-> render - onClick-> unmountComponentAtNode -> componentWillUnmount -> clearInterval ReactDOM.render(,window.root); 給jsx元素繫結事件要注意事件中的this指向,事件名採用 on+"開頭大寫事件名"的方式
14.屬性校驗,預設屬性
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types'; //引入屬性校驗的模組
class School extends React.Component{ // 類上的屬性就叫靜態屬性
static propTypes = { // 校驗屬性的型別和是否必填
age:PropTypes.number.isRequired, // 支援的型別可以參考prop-types的readme檔案
};
static defaultProps = { // 先預設呼叫defaultProps
name:'張三',
age:1
}; // 預設屬性
constructor(props){ //如果想在建構函式中拿到屬性需要通過引數的方式
//不能在元件中更改屬性 不能修改屬性*
super();
}
render(){
return <h1>{this.props.name} {this.props.age}</h1>
}
}
propTypes和defaultProps名字不能更改,這是react規定好的名稱
複製程式碼
15.狀態的使用
class Counter extends Component{
constructor(){
super();
this.state = {count:0}
};
handleClick = ()=>{
// setState方法會進行合併 setState有兩種寫法 一種是物件一種是函式
/*this.setState({count:this.state.count+1});
this.setState({count:this.state.count+1});*/
//this.setState((prevState)=>({count:prevState.count+1})); //如果返回的就是一個物件可以用小括號包裹
//this.setState((prevState)=>({count:prevState.count+1}));
// 下一個狀態是依賴於上一個狀態時需要寫成函式的方式
this.setState({count:this.state.count+1},function () {
this.setState({count:this.state.count+1});
}); // 這個寫法等同於 this.setState((prevState)=>({count:prevState.count+1}));
};
render(){
console.log('render');
return (
<p>
{this.state.count}
<button onClick={this.handleClick}>+</button>
</p>
)
}
}
ReactDOM.render(<Counter/>,window.root);
複製程式碼
16.複合元件
複合元件就是將多個元件進行組合,結構非常複雜時可以把元件分離開
父子元件的通訊
class Panel extends Component{
render(){
let {header,body} = this.props;
return (
<div className="container">
<div className="panel-default panel">
<Header head={header}></Header>
<Body b={body}/>
</div>
</div>
)
}
} // react種需要將屬性一層層向下傳遞 單向資料流
class Body extends Component{
render(){return (<div className="panel-body">{this.props.b}</div>)}
}
class Header extends Component{
render(){return (<div className="panel-heading">{this.props.head}</div>)}
}
let data = {header:'我非常帥',body:'長的帥'};
ReactDOM.render(<Panel {...data}/>,window.root);
複製程式碼
子父元件的通訊 通過父親傳遞給兒子一個函式,兒子呼叫父親的函式將值傳遞給父親,父親更新值,重新整理檢視
class Panel extends Component{
constructor(){
super();
this.state = {color:'primary'}
}
changeColor=(color)=>{ //到時候兒子傳遞一個顏色
this.setState({color});
};
render(){
return (
<div className="container">
<div className={"panel-"+this.state.color+" panel"}>
<Header head={this.props.header}
change={this.changeColor}
></Header>
</div>
</div>
)
}
}
class Header extends Component{
handleClick = ()=>{
this.props.change('danger'); //呼叫父親的方法
};
render(){return (
<div className="panel-heading">
{this.props.head} <button className="btn btn-danger" onClick={this.handleClick}>改顏色</button>
</div>)}
}
複製程式碼
17.受控元件和非受控元件
受狀態控制的元件,必須要有onChange方法,否則不能使用 受控元件可以賦予預設值(官方推薦使用 受控元件) 實現雙向資料繫結
class Input extends Component{
constructor(){
super();
this.state = {val:'100'}
}
handleChange = (e) =>{ //e是事件源
let val = e.target.value;
this.setState({val});
};
render(){
return (<div>
<input type="text" value={this.state.val} onChange={this.handleChange}/>
{this.state.val}
</div>)
}
}
複製程式碼
- 受控元件
class Sum extends Component{
constructor(){
super();
this.state = {a:1,b:1}
}
// key表示的就是當前狀態改的是哪一個
// e表示的是事件源
handleChange(key,e){ //處理多個輸入框的值對映到狀態的方法
this.setState({
[key]:parseInt(e.target.value)||0
})
}
render(){
return (
<div>
<input type="number" value={this.state.a} onChange={e=>{this.handleChange('a',e)}}/>
<input type="number" value={this.state.b} onChange={e=>{this.handleChange('b',e)}}/>
{this.state.a+this.state.b}
</div>
)
}
}
複製程式碼
- 非受控元件
class Sum extends Component{
constructor(){
super();
this.state = {result:''}
}
//通過ref設定的屬性 可以通過this.refs獲取到對應的dom元素
handleChange = () =>{
let result = this.refs.a.value + this.b.value;
this.setState({result});
};
render(){
return (
<div onChange={this.handleChange}>
<input type="number" ref="a"/>
{/*x代表的真實的dom,把元素掛載在了當前例項上*/}
<input type="number" ref={(x)=>{
this.b = x;
}}/>
{this.state.result}
</div>
)
}
}
複製程式碼