滴水能把石穿透,萬事功到天然成——zZ先森css
$ npm install -g create-react-app
//或者
$ yarn add -g create-react-app
複製代碼
$ create-react-app xxx
//xxx:項目名稱遵循npm包規範,使用小寫字母、數字、橫槓組合方式
複製代碼
若是電腦上安裝了yarn,默認會基於yarn安裝html
要求npm的版本號在5.2以上才能夠,而且一步到位安裝到局部vue
$ npx create-react-app my-app
複製代碼
在index.html中引入的公共資源文件即public中與index.html同級的資源,在導入的時候,前綴加上
<%PUBLIC_URL%>(當前目錄)
,在webpack打包編譯的時候會加以處理。vue中是<%BASE_URL%>
node
<head>
...
<link rel="icon" href="`<%PUBLIC_URL%>/favicon.ico"/>
...
</head>
<body>
//把最後編譯完成的html放到id爲root的盒子裏
<div id="root"></div>
</body>
複製代碼
src
和link
調入html中,這樣的話webpack對此不作處理。存放一些不支持CommonJs規範、ES6Module規範,在逼不得已使用的時候,在這裏直接導入。react
index.jsx
在新建組件的時候一搬爲.jsx文件,webpack中支持.jsx文件的解析和編譯,vscode也就能夠識別babel-preset-react-app 解析JSX語法的webpack
scriptsgit
$ yarn start
的入口文件$ yarn build
的入口文件若是執行yarn start/build 提示少模塊,咱們則少了誰就安裝誰github
@babel/plugin-transform-react-jsxweb
@babel/plugin-transform-react-jsx-sourcenpm
@babel/plugin-transform-react-jsx-self
config |- 這裏存儲的就是webpack的配置項
配置端口號和本地域名和HTTPS協議
"scripts": {
"startMac": "PORT=8081 node scripts/start.js",
"start": "set PORT=8081&&set HOST=127.0.0.1&&set HTTPS=true&&node scripts/start.js",
"build": "node scripts/build.js"
},
複製代碼
先安裝less以及less-loader $ yarn add less less-loader
配置config/webpack.config.js
const cssRegex = /\.(css|less)$/;
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
}, "less-loader"),
// Don't consider CSS imports dead code even if the // containing package claims to have no side effects. // Remove this when webpack adds a warning or an error for this. // See https://github.com/webpack/webpack/issues/6571 sideEffects: true, }, 複製代碼
JavaScript XML(HTML)
基礎語法JSX編寫出來的就是虛擬DOM
每個組件視圖只能有一個根元素節點 :有必要的話,能夠添加一個Fragment空文檔標記標籤<></>
ReactDOM.render([JSX],[CONTAINER],[CALLBACK])
[CONTAINER]
不建議是body或者是html而且指定一個元素容器[CALLBACK]
將虛擬DOM渲染到頁面上,而後出發回調函數,通常不用JSX語法中基於大括號來綁定動態數據值和JS表達式
null
和undefined
表明的是空元素{}
中不能寫對象和函數等其餘引用數據類型(除數組外),寫數組的話會將其轉換爲字符串。{}
給JSX設置類名和樣式
className
{}
裏放的是JS表達式,那麼返回結果能夠是新的JSX元素或者元素值動態綁定數據
key
,由於key
是DOM DIFF中重要的憑證,key
值通常不設置爲循環的索引,而是設置惟一不變的值let name = "zZ",
styObj = {color:'blue'},
data=[
{
id:1,
name:'shu'
},
{
id:2,
name:'gang'
}
];
ReactDOM.render(<div className="box" style={styObj}>
<ul>
data.map(item=>{
return <li key={item.id}>
<span>{item.id}</span>
<span>{item.name}</span>
</li>
})
</ul>
</div>)
複製代碼
此處的索引不能用索引的,主要目的是爲了數據變更的時候,DIFF渲染的時候,渲染速度更快,且不容易產生組件的錯誤。
Vue中經過VUE-LOADER解析template模板
ReactDOM.render(<div className="box" style={styObj}>
zZ是個好男孩
<span>優秀</span>
</div>)
複製代碼
經過解析解析爲如下格式:
React.createElement("div",{
className:"box",
style:{color:"blue"}
},"zZ\u662f\u4e2a\u597d\u7537\u5b69",React.creatElement("span",null,"\u4f18\u79c0"))
複製代碼
返回的的是一個對象:它就是一個虛擬DOM
$$typeof
:Symbol(react.element),key
:null,ref
:null,type
:標籤名/組件名props
:給元素標籤上設置的屬性,除key和ref
children
:若是元素有子節點,纔有children屬性,而且值會根據子節點的類型而定。
render函數把執行React.createElement()返回的對象,變爲真實的DOM,最後渲染到指定容器中,呈現到頁面上。
模擬虛擬DOM如何到真實DOM,封裝以下代碼:
let React = {},
ReactDOM={};
//直接操做私有屬性 將屬性名和屬性值暴露給回調函數
function each(obj,callback){
Obiect.keys(obj).forEach(item=>{
let val = obj[item];
callback && callback(val,item)
})
}
React.creatElement = function createElement(type,props,...children){
let virtualDOM = {
type,
props:{},
key:null,
ref:null
};
//處理props
if(props){
each(props,(value,item)=>{
if(item === "key"){
virtualDOM.key = value;
return
}
if(item === "ref"){
virtualDOM.ref = value;
return
}
})
delete props["key"];
delete props["ref"];
virtualDOM.props = {...props}
}
//處理children
if(children.length>0){
virtualDOM.props.children = children.length===1 ? children[0] : children
}
}
//把虛擬DOM轉換爲真實DOM
ReactDOM.render = function render(virtualDOM,container,callback){
let {
type,
props
}=virtualDOM;
let element = document.createElement(type);
each(props,(val,item)=>{
if(item==="className"){
element.className = val;
return;
}
if(item==="style"){
each(item,(value,key)=>{
element.["style"]["key"] = value;
})
return;
}
if(item === "children"){
let children = val;
//統一做爲數組
children = Array,isArray(children) ? children : [children];
each(children,(val,item)=>{
if(typeof val === "string"){
element.appendChild(document.creatTextNode(val));
return
}
render(val,element)
})
return
}
//將其餘屬性直接掛載到元素上
element.setAttribute(item,val)
})
//放到指定的容器中
container.appendChild(element);
if(typeof callback === "function"){
callback();
}
}
export default {React,ReactDOM}
複製代碼
React中的組件:每個組件(類組件)都是一個單獨的個體(數據私有,有完整的生命週期函數,有本身的視圖)
組件命名能夠以.jsx爲後綴名,在create-react-app腳手架建立項目中,包含了對.JSX文件的處理
HOOKS
將其動態化React.Component/PureComponent
,且必需要有一個render函數做視圖的渲染,在函數中返回JSXHOOKS
,根據業務的需求,將函數靜態組件加以動態化,實現動態組件的狀態、生命週期以及經過refs修改DOM元素。【組件調用】在JSX語法中,ReactDOM.render進行處理組件的時候,當發現type不是標籤字符串,則把當前的組件執行,若是是函數組件,則直接執行,若是是類組件,則進行new執行,並建立一個實例,並把父組件調用子組件傳遞進去的props都傳遞給函數或者實例來進行渲染。注意倆點:與vue不一樣的是單閉合和雙閉合標籤皆可。props是隻讀的不能修改
【函數組件】相對比vue插槽來講,若是在父組件調用子組件的時候,將屬性、標籤、其餘組件經過props屬性傳遞給子組件,也就是函數接收的參數爲props。在React.createElement方法造成虛擬DOM的時候,將存儲在JSX對象的props中,標籤和組件都將存儲在props屬性名爲children中,想要控制在不一樣的位置渲染的時候,有倆種方法第一種: React提供了一個遍歷children的方法集合對象=>Children,Children中有①count/②forEach③map④only ⑤toArray五個方法。第二種: 利用索引來獲取children數組中的每一項,放到不一樣位置。
【類組件】
【繼承】:1. 原型繼承 2.call繼承 3.寄生組合式 繼承 4.ES6中基於
class
實現繼承 關鍵字extends
【super】相似於call繼承,會把父類看成函數執行,讓函數中的this是子類的實例。當前類中必須有constructor。且在調用組件的時候傳遞進來的屬性,傳遞給constructor。在REACT中,在構建類組件的時候,類組件繼承了
React.Component
,則在super執行的時候,至關於把React.Component
看成普通函數執行,讓方法中的THIS是當前實例。this=>{props:xxx,content:xxx,refs:{},updater:{...}}
即爲了能讓傳遞進來的屬性掛載到實例上,則會給super傳遞props。
當REACT-DOM.RENDER
渲染的時候,若是發現虛擬DOM中的TEPE是一個類組件,會建立這個類實例,並把它解析出來的props傳遞這個類,constructor
就能夠接收進來props。執行constructor
以後才建立一個當前類的實例。雖然已將props傳遞給constructor
,可是實例上並未掛載這些屬性,基於this.props
不能獲取到值,可是能夠直接使用形參中的props。當constructor
執行完,REACT會幫咱們繼續處理,經過render
方法把PROPS/CONTEXT...
掛載到實例上,後期的生命週期函數均可以基於THIS.PROPS
來獲取和應用傳遞進來的屬性值。 必需要有render
函數,它返回的是當前組件要渲染的視圖。
Component&PureComponent
類組件進行繼承的時候,React提供了倆種:① Component ② PureComponent。 倆個父類區別在於:PureComponent相對於Component,會默認建立一個鉤子函數shouldComponentUpdate
,若是咱們手動添加shouldComponentUpdate
則以咱們添加的爲主,shouldComponentUpdate
運行原理是淺比較,給這個鉤子函數傳遞進來的就是更新的新數據,經過判斷類決定返回true仍是false。
注意在修改的狀態是數組或者其餘引用數據類型的時候,注意其地址若是沒有更新,其存儲的值修改的話,照樣是不渲染的。基於解構到新數組來給予新的地址加以區分,而後返回true,視圖纔會渲染。
import React from 'react';
export default class Vote extends React.PureComponent {
state = {
arr: [10, 20]
};
render() {
return <div className="voteBox">
{this.state.arr.map((item, index) => {
return <span key={index}>
{item}
</span>;
})}
<button onClick={this.handle}>按鈕</button>
</div>;
}
handle = () => {
let arr = this.state.arr;
arr.push(30);
this.setState({
arr: [...arr]
});
}
複製代碼
咱們把基於狀態或者屬性的更新來驅動視圖渲染,叫受控組件。屬性不能修改,但能夠經過設置默認值、讓父組件從新調用傳遞不一樣的屬性、或者把屬性值賦值給組件的狀態,經過借刀殺人的方式修改屬性。非受控組件: 不受狀態管控,而是直接操做DOM。
給屬性設置規則須要第三方插件【prop-types】 設置的規則不會阻礙內容的渲染,不符合規則的在控制檯報錯。
$ npm install --save prop-types
//&
$yarn add prop-types
複製代碼
import PropTypes from "prop-types";
//&
var PropTypes = require("prop-types");
複製代碼
static defaultProps = {
title:"zZ很懶"
}
複製代碼
PropTypes.isRequired
必須傳遞 PropTypes.string/bool/number/func/object/symbol/node(元素節點)
element
(JSX元素)
instanceOf(Xxx)
(必須是某個類的實例)
oneOf(["News","photos"])
(多箇中的一個)
oneOfType([PropTypes.string,PropTypes.number,PropTypes.instanceOf(Message)])
多個類型中的一個
static propTypes = {
title"PropTypes.string.isRequired } 複製代碼
1.在constructor構造函數中初始化,要求後期再組件中使用的狀態都要在這裏初始化一下
2.經過setState方法來通知視圖從新渲染,setState(partialState,callback)
,在某些狀況下是異步的,也能夠理解在鉤子函數中是局部異步的。須要等待周期函數執行完成,再去修改狀態,保證了周期函數的穩定性,以及總體執行邏輯的完整性。有些狀況是同步的,在設置定時器或者在綁定事件中執行setState,這個時候就是同步的,走修改狀態的流程。
異步狀況:
componentWillMount
中設置setState,爲了提升性能,React爲了不沒必要要的從新渲染,執行完componentWillMount
,就當即修改了狀態。實現了局部異步性。componentDidMount
中設置setState,由於已經執行完render了,因此此時的異步的等到生命週期函數搜執行完再去修改狀態。同步狀況:
setState【修改狀態】過程=> shouldComponentUpdate -> WillUpdate -> render(從新渲染:從新構建虛擬的DOM對象->DOM DIFF(補丁包)->差別渲染) -> DidUpdate
【partialState】 第一個參數部分狀態對象【partialState】,修改初始化中的哪一個狀態,在setState中改誰便可,React在處理的時候把以前的狀態和傳遞的partialState進行合併並替換,使用原生Object.assign(this.state,partialState)
【callback】 在視圖從新渲染完成以後執行。
3.批量更新狀態
基於setState修改狀態時的同步異步操做,React把當前異步修改狀態的操做任務放到隊列裏,若是有來個任務修改同一個狀態,則會以最後一個任務爲主。和瀏覽器渲染更新機制一個道理。爲了不沒必要要的渲染,提升性能。
4.this.forceUpdate(): 強制更新,執行該方法就不會再走shouldComponentUpdate
,直接修改狀態
5.數據驅動機制比較
this.state = {
time: new Date().toLocaleString()
};
render(){
return <div>
<p>{this.state.time}</p>
</div>
}
componentDidMount(){
//第一次加載組件渲染完畢 等價於 VUE中的MOUNT
//這種狀況不會通知組件從新渲染
//this.state.time = "1970年1月1日 0點0分0秒"
//每一次修改狀態基於:setState方法
setInterval(()=>{
this.setState({
time: new Date().toLocaleString();
},()=>{console.log("疫情很快結束!")})
},1000);
}
複製代碼
6.refs :是一個對象,對象裏面存儲着有屬性爲ref的元素DOM,從而得到DOM來進行操做。 有倆種寫法:① 設置屬性ref=xxXxx
,基於this.refs.xxXxx獲取DOM元素。② 經過函數的模式,傳遞的參數是當前的DOM元素,而後將DOM元素直接掛載到實例上,後續經過設置的屬性名來獲取相應的DOM元素,進行操做DOM。
<p ref="timeBox">{new Date().toLoacaleString}</p>
//&
<p ref=DOM=>{
this.timeBox = DOM
}>{new Date().toLoacaleString}</p>
複製代碼
React中的事件綁定
refs
或者經過直接掛載到實例上DOM元素在componentDidMount
鉤子函數中綁定事件onXxx
React爲了處理事件的兼容以及移動端事件的處理,全部的事件都是合成的(事件對象也是本身合成的)=》原理:事件委託,把每一種事件類型都在document上委託一下。
getDefaultProps
獲取屬性的默認值和校驗傳遞屬性的類型constructor
getInitialState
在執行getInitialState
以前,若是當前類組件有構造函數,則先執行該構造函數,從而初始化狀態,把屬性等信息掛載到實例上componentWillMount
第一次渲染以前,能夠在這裏提早從服務器獲取數據,進而把獲取的數據重賦值給狀態或者存放到redux中。render
渲染組件componentDidMount()
第一次組件渲染完成,而後處於運行當中。能夠獲取DOM元素了。【state改變】
shouldComponentUpdate
狀態改變觸發該鉤子函數,此處能夠判斷該更新是否有必要,避免沒必要要的更新,從而獲得優化,返回true
或者false
來控制是否執行下一個鉤子函數,可是不論返回的哪一個,狀態都已經改了componentWillUpdate
在上一個鉤子函數返回true
執行,作一系列的更新前準備render
再次渲染,渲染狀態改變的部分componentDidUpdate
狀態更新完成後,到再次監聽屬性或者狀態的改變。【props改變】
componentWillReceiveProps
屬性值改變觸發該鉤子函數,獲取到某一個具體屬性的改變,而後觸發shouldComponentUpdate
,看看究竟是否能夠更新。【卸載】
componentWillUnmount
組件卸載以前,作最後一步的收尾工做,而後組件的整個生命週期結束。代碼示意以及各個周期函數的參數
class Clock extends React.Component {
static defaultProps = {
time:"1970年1月1日 0點0分0秒"
}
static propTypes = {
time:"instanceOf(Date)"
}
constructor(props){
super(props);
this.state = {t:0}
}
componentWillMount(){
console.log("--- 執行componentWillMount---")
}
render(){
console.log("--- 執行render---")
return <div>
<h2>回溯到{this.state.time}</h2>
<button Onclick={()=>{
this.setState({t:this.state.t+1})
}}>{this.state.t}</button>
</div>
}
componentDidMount(){
console.log("--- 執行componentDidMount---")
}
shouldComponentUpdate (nextProps,nextState) {
console.log("--- 執行shouldComponentUpdate ---",nextProps,nextState)
}
componentWillUpdate(){
console.log("--- 執行componentWillUpdate---")
}
//render從新渲染
componentDidUpdate(){
console.log("--- 執行componentDidUpdate---")
}
componentWillUnmount(){
console.log("--- 執行componentWillUnmount---")
}
}
React.render(<section>
<Clock></Clock>
</section>,document.getElementById("root"),_=>{alert("奧利給")})
複製代碼