這是我參與8月更文挑戰的第1天,活動詳情查看:8月更文挑戰javascript
本文做爲本人學習總結之用,同時分享給你們,適合入門的react小白
由於我的技術有限,若是有發現錯誤或存在疑問之處,歡迎指出或指點!不勝感謝!html
中文官網: react.docschina.org/vue
英文官網: https://reactjs.org/java
高效緣由:react
1. 使用虛擬(virtual)DOM, 不老是直接操做頁面真實DOM。
ajax
2. DOM Diffing算法, 最小化頁面重繪
算法
<script type="text/javascript" >
//1.建立虛擬DOM
const VDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React'))
//2.渲染虛擬DOM到頁面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
複製代碼
<script type="text/babel" > /* 此處必定要寫babel */
//1.建立虛擬DOM
const VDOM = ( /* 此處必定不要寫引號,由於不是字符串 */
<h1 id="title"> <span>Hello,React</span> </h1>
)
//2.渲染虛擬DOM到頁面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
複製代碼
const VDOM = React.createElement('xx',{id:'xx'},'xx')
建立的就是一個簡單的虛擬DOM對象編程
//1.建立虛擬DOM
const VDOM = (
<h1 id="title"> <span>Hello,React</span> </h1>
)
//2.渲染虛擬DOM到頁面
ReactDOM.render(VDOM,document.getElementById('test'))
const TDOM = document.getElementById('demo')
console.log('虛擬DOM',VDOM); //虛擬DOM Object:{$$typeof: Symbol(react.element), type: "h1", key: null, ref: null, props: {…}, …}
console.log('真實DOM',TDOM); //真實DOM <div id='demo'></div>
debugger;
console.log(typeof VDOM); //object
console.log(VDOM instanceof Object); //true
複製代碼
關於虛擬DOM:
1.本質是Object類型的對象(通常對象)
2.虛擬DOM比較「輕」,真實DOM比較「重」,由於虛擬DOM是React內部在用,無需真實DOM上那麼多的屬性。
3.虛擬DOM最終會被React轉化爲真實DOM,呈如今頁面上。數組
<h1>Hello JSX!</h1>
1.定義虛擬DOM時,不要寫引號。
2.標籤中混入JS表達式時要用{}。
3.樣式的類名指定不要用class,要用className。
4.內聯樣式,要用style={{key:value}}的形式去寫。
5.只有一個根標籤
6.標籤必須閉合
7.標籤首字母瀏覽器
- 小寫字母開頭,則將該標籤轉爲html中同名元素,若html中無該標籤對應的同名元素,則報錯。
- 字母開頭,react就去渲染對應的組件,若組件沒有定義,則報錯。
ReactDOM.render(virtualDOM,containerDOM)
函數組件:
<script type="text/babel">
//1.建立函數式組件
function MyComponent(){
console.log(this); //此處的this是undefined,由於babel編譯後開啓了嚴格模式
return <h2>我是用函數定義的組件(適用於【簡單組件】的定義)</h2>
}
//2.渲染組件到頁面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/* 執行了ReactDOM.render(<MyComponent/>.......以後,發生了什麼? 1.React解析組件標籤,找到了MyComponent組件。 2.發現組件是使用函數定義的,隨後調用該函數,將返回的虛擬DOM轉爲真實DOM,隨後呈如今頁面中。 */
</script>
複製代碼
類式組件:
<script type="text/babel">
//1.建立類式組件
class MyComponent extends React.Component {
render(){
//render是放在哪裏的?—— MyComponent的原型對象上,供實例使用。
//render中的this是誰?—— MyComponent的實例對象 <=> MyComponent組件實例對象。
console.log('render中的this:',this);
//輸出爲 render中的this: MyComponent {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …}
return <h2>我是用類定義的組件(適用於【複雜組件】的定義)</h2>
}
}
//2.渲染組件到頁面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/* 執行了ReactDOM.render(<MyComponent/>.......以後,發生了什麼? 1.React解析組件標籤,找到了MyComponent組件。 2.發現組件是使用類定義的,隨後new出來該類的實例,並經過該實例調用到原型上的render方法。 3.將render返回的虛擬DOM轉爲真實DOM,隨後呈如今頁面中。 */
</script>
複製代碼
//1.建立組件
class Weather extends React.Component{
//構造器調用幾回? ———— 1次
constructor(props){
console.log('constructor');
super(props)
//初始化狀態
this.state = {isHot:false,wind:'微風',stat:{}}
//解決changeWeather中this指向問題
this.changeWeather = this.changeWeather.bind(this)
}
//render調用幾回? ———— 1+n次 1是初始化的那次 n是狀態更新的次數
render(){
console.log('render');
//讀取狀態
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今每天氣很{isHot ? '炎熱' : '涼爽'},{wind}</h1>
}
//changeWeather調用幾回? ———— 點幾回調幾回
changeWeather(){
//changeWeather放在哪裏? ———— Weather的原型對象上,供實例使用
//因爲changeWeather是做爲onClick的回調,因此不是經過實例調用的,是直接調用
//類中的方法默認開啓了局部的嚴格模式,因此changeWeather中的this爲undefined
console.log('changeWeather');
//獲取原來的isHot值
const isHot = this.state.isHot
//嚴重注意:狀態必須經過setState進行更新,且更新是一種合併,不是替換。
this.setState({isHot:!isHot})
console.log(this);
//嚴重注意:狀態(state)不可直接更改,下面這行就是直接更改!!!
//this.state.isHot = !isHot //這是錯誤的寫法
}
}
//2.渲染組件到頁面
ReactDOM.render(<Weather/>,document.getElementById('test'))
複製代碼
state簡寫方式:簡寫只是把constructor去掉 初始化更加方便
class Weather extends React.Component{
//初始化狀態
state = {isHot:false,wind:'微風'}
render(){
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今每天氣很{isHot ? '炎熱' : '涼爽'},{wind}</h1>
}
//自定義方法————要用賦值語句的形式+箭頭函數
changeWeather = ()=>{
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
//2.渲染組件到頁面
ReactDOM.render(<Weather/>,document.getElementById('test'))
複製代碼
1. 每一個組件對象都會有props(properties的簡寫)屬性
2. 組件標籤的全部屬性都保存在props中
1. 經過標籤屬性從組件外向組件內傳遞變化的數據
2. 注意: 組件內部不要修改props數據
this.props.name
//第一種方式(React v15.5 開始已棄用):
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number
}
//第二種方式(新):使用prop-types庫進限制(須要引入prop-types庫):
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.
}
複製代碼
//建立組件
class Person extends React.Component{
//5.組件類的構造函數
constructor(props){
//構造器是否接收props,是否傳遞給super,取決於:是否但願在構造器中經過this訪問props
// console.log(props);
super(props)
console.log('constructor',this.props);
}
//2對標籤屬性進行類型、必要性的限制
static propTypes = {
name:PropTypes.string.isRequired, //限制name必傳,且爲字符串
sex:PropTypes.string,//限制sex爲字符串
age:PropTypes.number,//限制age爲數值
}
//4指定默認標籤屬性值
static defaultProps = {
sex:'男',//sex默認值爲男
age:18 //age默認值爲18
}
render(){
//1內部讀取某個屬性值
const {name,age,sex} = this.props
//props是隻讀的
//this.props.name = 'jack' //此行代碼會報錯,由於props是隻讀的
return (
<ul> <li>姓名:{name}</li> <li>性別:{sex}</li> <li>年齡:{age+1}</li> </ul>
)
}
}
//3擴展屬性
//渲染組件到頁面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
複製代碼
//建立組件
function Person (props){
const {name,age,sex} = props
return (
<ul> <li>姓名:{name}</li> <li>性別:{sex}</li> <li>年齡:{age}</li> </ul>
)
}
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必傳,且爲字符串
sex:PropTypes.string,//限制sex爲字符串
age:PropTypes.number,//限制age爲數值
}
//指定默認標籤屬性值
Person.defaultProps = {
sex:'男',//sex默認值爲男
age:18 //age默認值爲18
}
//渲染組件到頁面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
複製代碼
組件內的標籤能夠定義ref屬性來標識本身不太建議過多使用React準備廢棄
1. 字符串形式的ref
2. 回調形式的ref
3. createRef建立ref容器
//字符串形式的ref
<input ref="input1" type="text" />
const {input1} = this.refs //直接調用
//回調形式的ref
<input ref={c => this.input1 = c } type="text"/>
const {input1} = this //調用賦值
//函數方式調用
<input ref={this.saveInput} type="text"/>
saveInput = (c)=>{
this.input1 = c;
console.log('@',c);
}
//createRef建立ref容器·
//React.createRef調用後能夠返回一個容器,該容器能夠存儲被 ref所標識的節點,該容器是"專人專用"的
myRef = React.createRef()
<input ref={this.myRef} type="text"/>
複製代碼
1. 經過onXxx屬性指定事件處理函數(注意大小寫)\
2. 經過event.target獲得發生事件的DOM元素對象 —————————不要過分使用ref
//事件處理
showData = (event)=>{
console.log(event.target);
alert(this.myRef.current.value);
}
<button onClick={this.showData}>點我提示左側的數據</button>
//在父子組件中使用
export default class SliderList extends Component {
sliderRef = React.createRef();
componentDidMount(){
this.sliderRef.current.doSlider(); //在父組件中調用
}
render() {
return (
<div className = 'slider-container'> <Slider ref={this.sliderRef}/> </div>
);
}
}
//子組件定義方法
export default class Slider extends Component {
doSlider() {
console.log(111111)
}
render() {
return (
<div>Slider</div>
)
}
}
複製代碼
包含表單的組件分類:
class Login extends React.Component{
//初始化狀態
state = {
username:'', //用戶名
password:'' //密碼
}
//保存用戶名到狀態中
saveUsername = (event)=>{
this.setState({username:event.target.value})
}
//保存密碼到狀態中
savePassword = (event)=>{
this.setState({password:event.target.value})
}
//表單提交的回調
handleSubmit = (event)=>{
event.preventDefault() //阻止表單提交
const {username,password} = this.state
alert(`你輸入的用戶名是:${username},你輸入的密碼是:${password}`)
}
render(){
return(
<form onSubmit={this.handleSubmit}> 用戶名:<input onChange={this.saveUsername} type="text" name="username"/> 密碼:<input onChange={this.savePassword} type="password" name="password"/> <button>登陸</button> </form>
)
}
}
複製代碼
class Login extends React.Component{
handleSubmit = (event)=>{
event.preventDefault() //阻止表單提交
const {username,password} = this
alert(`你輸入的用戶名是:${username.value},你輸入的密碼是:${password.value}`)
}
render(){
return(
<form onSubmit={this.handleSubmit}> 用戶名:<input ref={c => this.username = c} type="text" name="username"/> 密碼:<input ref={c => this.password = c} type="password" name="password"/> <button>登陸</button> </form>
)
}
}
複製代碼
1. 組件從建立到死亡它會經歷一些特定的階段。
2. React組件中包含一系列勾子函數(生命週期回調函數), 會在特定的時刻調用。
3. 咱們在定義組件時,會在特定的生命週期回調函數中,作特定的工做。
生命週期回調函數 <=> 生命週期鉤子函數 <=> 生命週期函數 <=> 生命週期鉤子
1.初始化階段:由ReactDOM.render()觸發---初次渲染
2.更新階段:由組件內部this.setSate()或父組件從新render觸發
3.卸載組件:由ReactDOM.unmountComponentAtNode()觸發
1.初始化階段:由ReactDOM.render()觸發---初次渲染
2.更新階段:由組件內部this.setSate()或父組件從新render觸發
3.卸載組件:由ReactDOM.unmountComponentAtNode()觸發
componentWillUnmount()====> 經常使用
通常在這個鉤子中作一些收尾的事,例如:關閉定時器、取消訂閱消息
強制刷新:this.forceUpdate() ————跟vue強制刷新一致
class Count extends React.Component{
//構造器
constructor(props){
console.log('Count---constructor');
super(props)
//初始化狀態
this.state = {count:0}
}
//加1按鈕的回調
add = ()=>{
//獲取原狀態
const {count} = this.state
//更新狀態
this.setState({count:count+1})
}
//卸載組件按鈕的回調
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
//強制更新按鈕的回調
force = ()=>{
this.forceUpdate()
}
//若state的值在任什麼時候候都取決於props,那麼可使用getDerivedStateFromProps
static getDerivedStateFromProps(props,state){
console.log('getDerivedStateFromProps',props,state);
return null
}
//在更新以前獲取快照
getSnapshotBeforeUpdate(){
console.log('getSnapshotBeforeUpdate');
return 'atguigu'
}
//組件掛載完畢的鉤子
componentDidMount(){
console.log('Count---componentDidMount');
}
//組件將要卸載的鉤子
componentWillUnmount(){
console.log('Count---componentWillUnmount');
}
//控制組件更新的「閥門」
shouldComponentUpdate(){
console.log('Count---shouldComponentUpdate');
return true
}
//組件更新完畢的鉤子
componentDidUpdate(preProps,preState,snapshotValue){
console.log('Count---componentDidUpdate',preProps,preState,snapshotValue);
}
render(){
console.log('Count---render');
const {count} = this.state
return(
<div> <h2>當前求和爲:{count}</h2> <button onClick={this.add}>點我+1</button> <button onClick={this.death}>卸載組件</button> <button onClick={this.force}>不更改任何狀態中的數據,強制更新一下</button> </div>
)
}
}
//渲染組件
ReactDOM.render(<Count count={199}/>,document.getElementById('test'))
複製代碼
如今使用會出現警告,下一個大版本須要加上UNSAFE_前綴才能使用,之後可能會被完全廢棄,不建議使用。
關於diff算法的問題:
1). react/vue中的key有什麼做用?(key的內部原理是什麼?)
2). 爲何遍歷列表時,key最好不要用index?
複製代碼
詳細的說:當狀態中的數據發生變化時,react會根據【新數據】生成【新的虛擬DOM】, 隨後React進行【新虛擬DOM】與【舊虛擬DOM】的diff比較,比較規則以下:
a. 舊虛擬DOM中找到了與新虛擬DOM相同的key:
b. 舊虛擬DOM中未找到與新虛擬DOM相同的key根據數據建立新的真實DOM,隨後渲染到到頁面
a.若對數據進行:逆序添加、逆序刪除等破壞順序操做:會產生沒有必要的真實DOM更新 ==> 界面效果沒問題, 但效率低
b.若是結構中還包含輸入類的DOM:會產生錯誤DOM更新 ==> 界面有問題。
c.注意!若是不存在對數據的逆序添加、逆序刪除等破壞順序操做,僅用於渲染列表用於展現,使用index做爲key是沒有問題的。
最好使用每條數據的惟一標識做爲key, 好比id、手機號、身份證號、學號等惟一值。 若是肯定只是簡單的展現數據,用index也是能夠的。