一、React項目
1、項目依賴安裝
腳手架:
解壓兩個文件:在項目的根目錄下執行npm install////npm I 會按照package.json的配置安裝依賴模塊。
安裝完成后,會出現一個node_modules,里面是安裝的所有依賴的模塊。
都是項目的根目錄。
npm install
代理是哪里進去哪里出來。
2、項目整體說明
.babelrc: babel轉譯的配置文件。 (放的是預設值)環境信息,插件等。
.gitignore(忽略的信息)
Index.html(網頁的HTML顯示信息,發布的站點根目錄)
Jsconfig.json(vscode自己所用的)
LICENSE()
.npmrc() 鏡像庫
Package.js(依賴信息)
README.md()
Src(js的插件信息)
App.js
Appstate.js
Index.html
Node_moudles(npm包管理器設置以后所有的依賴文件)
Webpack.config.dev.js 開發時候使用的
Webpack.config.prod.js 生產環境使用的
3、配置文件詳解
1)package.json
Npm 產生的文件,里面記錄項目信息,所有項目依賴。
(1)版本信息:repository:
里面包括的是版本等信息。
(2)項目管理:scripts:
Start:啟動webpack的dev server開發用的web server,提供的靜態文件加載、自動刷新和熱替換HMR(hot module replacement)
HMR可以在應用程序運行中替換,添加和刪除模塊,不需要重新加載模塊,只是變化部分替換掉,不用HMR則自動刷新頁面。
--HOT 啟動HMR
--inline默認模式,使用HMR的時候建議使用inline模式,熱替換時候會有消息顯示在控制台。
Build使用webpack構建打包,對應npm run build
(3)項目依賴devdependencies
開發時候的依賴,不會打包到目標文件中。例如轉譯代碼等。沒必要到生產中去。
Dependencies運行的時候的依賴必須帶到項目中去,
版本號:
指定安裝版本號:
~安裝指定中和最新的版本。
^安裝指定中最高的版本,不低於指定的版本。*******建議常用
Latest:最新版本。
Babel轉譯的。
Css樣式:
Css-loader樣式表相關的模塊。
React-hot-loader熱加載插件。
運行時候的依賴:
Antd ant design基於react實現,后台管理較方便。
Axios 異步請求
Polyfiill解決瀏覽器api不支持的問題,磨平差異化。
React開發的主框架。
React-router路由
React-router-dom dom綁定路由
Mobx狀態管理庫,透明化
Mobx-react mobx-react-devtools mobx和react結合的模塊。
React和mobx是一個強強聯合。
2)babel配置,.babelrc
Babel轉譯的配置文件
3)webpack配置
(1)webpack.config.dev.js
符合commonjs的模塊。
Devtools:’source-map’
調試時候映射處理。
Entry入口:從入口直接找到需要打包的文件。
Output輸出:
Resolve解析
Module模塊
Test匹配條件的。
Exclude排除的,
Use使用模塊的useentries列表中的loader,
Rules中.js結尾的但不在node_modules目錄的文件使用轉譯babel-loader和熱加載loader。
加載器:
style-loader通過<style>標簽吧css添加到dom中。
Css-loader加載css
Less-loader對less的支持
Less:控制h2標記的。
Devserver,開發使用的server
Compress啟動gzip
Port啟動端口3000
Hot啟動HMR
Proxy執行訪問/api開頭路徑都代理到http://127.0.0.0:8080中
4)vscode的配置
Jsconfig.json是vscode的配置文件,覆蓋當前配置。
上面的配置文件,需要更改name,version,description,需要修改repository倉庫地址,需要修改author、license信息。修改完成后,進行開發。
App-xxxxx的文檔必須放在根目錄,對於Windows下的是磁盤的根目錄。
4、啟動項目
npm start
二、react
1、簡介
React是Facebook開發的並開源的前端框架。
2013年開源的,react解決的是前端的MVC框架中的view視圖層的問題。
2、virtual dom***
DOM是什么?
DOM(文檔對象模型document object model)
是HTML和xml文檔,把其解析成為dom對象,展示成為樹的模型。
Js在瀏覽器端多數情況下都是操作dom樹的。
選擇節點問題,通過id等進行查找等。
The document
The dom tree
將網頁所有內容映射到一顆樹形的結構的層級對象模型上,瀏覽器提供對dom支持,用戶可以用腳本調用dom api來動態的修改dom節點,從而達到修改網頁的目的,這種修改在瀏覽器中完成,瀏覽器胡根據dom的修改重繪dom的節點部分。
修改dom重新渲染的代價比較高,前端框架為了提高效率,盡量減少DOM的重繪,提出了virtual DOM,所有的修改都是現在的virtual dom上面完成,通過比較算法,找出瀏覽器和dom之間的差異,使用這個差異操作dom,瀏覽器只是需要渲染這個部分變化就行了。
React實現了dom diff算法可以高效比對vir dom和 dom的差異。
3、jsx的語法。
Jsx是一種Javascript和xml混寫的語法,是Javascript的擴展。
直接插入HTML語句等。
4、測試程序
import React from 'react';
import ReactDOM from 'react-dom';
// import { render } from 'react-dom';
// import { AppContainer } from 'react-hot-loader';
// import AppState from './AppState';
// import App from './App';
// const appState = new AppState();
// render(
// <AppContainer>
// <App appState={appState} />
// </AppContainer>,
// document.getElementById('root')
// );
// if (module.hot) {
// module.hot.accept('./App', () => { render(App) })
// }
class Root extends React.Component{
render(){
return <div>javascript excise</div>;
}
}
ReactDOM.render(<Root/>,document.getElementById('root'));
Return的必須是jsx的語法。單標計必須是封口的。
Render()渲染函數,必須有return值,必須有頂級元素返回,所有元素必須閉合。
單行省略小括號,多好使用小括號。
class Root extends React.Component{
render(){
return <div>javascript <br/> excise</div>;
}
}
<br/>換行符
class Root extends React.Component{
render(){
return React.createElement('div',null,'javascript');
//return <div>javascript <br/> excise</div>;
}
}
//ReactDOM.render(<Root/>,document.getElementById('root'));
ReactDom.render(React.createElement(Root),document.getElementById('root'));
更改屬性的值:大括號等信息。
增加一個子元素:
class SubEle extends React.Component{
render(){
return <div>Sub content</div>
}
}
class Root extends React.Component{
render(){
return (
<div>
<h2>welcome to study</h2>
<br/>
<SubEle/>
</div>);
}
}
ReactDOM.render(<Root/>,document.getElementById('root'))
React組件的render函數return,只能是一個頂級元素。
Jsx語法是XML,要求所有元素必須閉合,<br/> 不能是<br>
5、jsx規范
標簽中首字母小寫就是HTML標記,首字母大寫就是組件。
要求嚴格的標記,要求所有標簽必須閉合,br也應該寫成<br />,/前面留一個空格。
單行省略小括號,多行必須使用小括號。
元素有嵌套的,多行,縮進。
Jsx表達式:使用{}括起來,如果大括號內使用了引號,會當做字符串處理。
6、組件狀態state
每一個react組件都有一個狀態變量state,是一個JavaScript對象,為期定義屬性保存值。
狀態變化了,會出發UI重新渲染,使用setstate方法重新修改state的值。
State是組件內部使用的,是私有屬性。
import React from 'react';
import ReactDOM from 'react-dom';
class Root extends React.Component{
state = {
p1: 'abc',
p2:'.com'
};
render(){
this.state.p1 = 'www.abc'
return (
<div>
<div>welcome to {this.state.p1}{this.state.p2}</div>
<br />
</div>
);
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
//this.state.p1 = 'www.abc'
改為下面的,不允許,不允許對更新中的state進行setState
//this.setState({p1:'www.ab'}) //不允許這樣修改,控制台會報錯,
使用延時函數:setTimeout(里面是一個函數)
setTimeout(()=>this.setState({p1:'www.abdd'}),5000)
class Root extends React.Component{
state = {
p1: 'abc',
p2:'.com'
};
render(){
//this.state.p1 = 'www.abc'
//this.setState({p1:'www.ab'}) //不允許這樣修改,控制台會報錯,
setTimeout(()=>this.setState({p1:'www.abdd'}),5000)
return (
<div>
<div>welcome to {this.state.p1}{this.state.p2}</div>
<br />
</div>
);s
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
7、復雜例子
Div的id是t1,鼠標按下事件就捆綁了一個函數,只要鼠標按下就會觸發調用getEventTrigger函數,瀏覽器就會給其送一個參數event,event是時間對象,當時間觸發的時候,event包含觸發這個時間的對象。
8、HTML DOM的Javascript事件
屬性 |
此事件發生在何時 |
Onabort |
圖像的加載被中斷 |
Onblur |
元素失去焦點 |
Onchange |
域的內容被改變 |
Onclick |
當用戶點擊某個對象時候調用的時間句柄 |
Ondbclick |
當用戶雙擊某個對象時候調用的事件句柄 |
Onerror |
在加載文檔或圖像時候發生錯誤 |
Onfocus |
元素獲得焦點 |
Onkeydown |
某個鍵盤鍵被按下 |
Onkeypress |
某個鍵盤按鍵被按下並松開 |
Onkeyup |
某個按鍵被松開 |
Onload |
一張頁面或一幅圖像完成加載 |
Onmousedown |
鼠標按鈕被按下 |
Onmousemove |
鼠標按鍵被移動 |
Onmouseout |
鼠標從某元素移開 |
Onmouseover |
鼠標移到某個元素之上 |
Onmouseup |
鼠標按鍵被松開 |
Onreset |
重置按鈕被點擊 |
Onresize |
窗口或框架被重新調整大小 |
Onselect |
文本被選中 |
Onsubmit |
確認按鈕被點擊 |
Onunload |
用戶退出頁面 |
import React from 'react';
import ReactDOM from 'react-dom';
class Toggle extends React.Component{
state = {flag:true};
handleClick(event){
console.log(event.target.id)
console.log(event.target === this);
console.log(this)
console.log(this.state)
this.setState({flag:!this.state.flag})
}
render(){
return <div id="t1" onClick={this.handleClick.bind(this)}>
點擊這句話,會觸發一個事件1。{this.state.flag.toString()}
</div>;
}
}
class Root extends React.Component{
state = {
p1: 'abc',
p2:'.com'
};
render(){
//this.state.p1 = 'www.abc'
//this.setState({p1:'www.ab'}) //不允許這樣修改,控制台會報錯,
setTimeout(()=>this.setState({p1:'www.abdd'}),5000)
return (
<div>
<div>welcome to {this.state.p1}{this.state.p2}</div>
<br />
<Toggle />
</div>
);
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
Toggle類
有自己的state屬性,當render完成后,網頁上有一個div標簽捆綁了一個click事件的函數,div標簽內有文本內容。
如果用過點擊左鍵,就觸發了click方法關聯的handleClick函數,在這個函數里將狀態值改變。
狀態值state的改變將引發render重繪。
{this.handleClick.bind(this)},不能外加引號。
This.handleClick.bind(this)一定要綁定this,否則當觸發捆綁的函數時候,this是函數執行的上下文決定的,this已經不是觸發事件的對象了。
Console.log(event.target.id)取回的產生事件的對象的id,不是封裝的組件的對象,所以,console.log(event.target===this)是false,這個this是通過綁定過來的。
React的事件:
使用小駝峰命名。
使用jsx表達式,表達式中要指定事件處理函數。
不能使用return false,如果要阻止事件默認行為,使用event.preventDefault()
Id是唯一的,堅決不能有重復的。
9、props
把react組件當做標簽使用,可以為其增加屬性:
組件之間利用state或者傳parent傳遞參數
修改屬性是不可以的,因為是只讀的this.props.name.
import React from 'react';
import ReactDOM from 'react-dom';
class Toggle extends React.Component{
state = {flag:true};
handleClick(event){
console.log(event.target.id)
console.log(event.target === this);
console.log(this)
console.log(this.state)
this.setState({flag:!this.state.flag})
}
render(){
return <div id="t1" onClick={this.handleClick.bind(this)}>
點擊這句話,會觸發一個事件1。{this.state.flag.toString()}
{this.props.school}{this.props.age}
<br />
{this.props.parent.state.p1}
{this.props.children}
</div>;
}
}
class Toggle1 extends React.Component{
state = {flag:true};
handleClick(event){
console.log(event.target.id)
console.log(event.target === this);
console.log(this)
console.log(this.state)
this.setState({flag:!this.state.flag})
}
render(){
return <div id="t2" onClick={this.handleClick.bind(this)}>
點擊這句話,會觸發一個事件2。{this.state.flag.toString()}
<br />
{this.props.name}
<hr />
{this.props.age}
{this.props.parent.state.p2}
</div>;
}
}
class Root extends React.Component{
state = {
p1: 'abc',
p2:'.com'
};
render(){
//this.state.p1 = 'www.abc'
//this.setState({p1:'www.ab'}) //不允許這樣修改,控制台會報錯,
setTimeout(()=>this.setState({p1:'www.abdd'}),5000)
return (
<div>
<div>welcome to {this.state.p1}{this.state.p2}</div>
<br />
<Toggle school='abc' age= '20' parent={this} >
</Toggle>
<Toggle1 name='tom' age= '10' parent={this}/>
</div>
);
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
注意訪問parent送進去的是this,訪問實例本身的屬性的方式是利用this.props.state.p1的值
10、構造器
利用construc(props)。
Super()調用父類的。
只要extends的,必須利用super()調用。Props作為參數傳入。
import React from 'react';
import ReactDOM from 'react-dom';
class Toggle extends React.Component{
constructor(props){
console.log('Toggle')
super(props)
this.state = {flag:true};
}
handleClick(event){
console.log(event.target.id)
console.log(event.target === this);
console.log(this)
console.log(this.state)
this.setState({flag:!this.state.flag})
}
render(){
return <div id="t1" onClick={this.handleClick.bind(this)}>
點擊這句話,會觸發一個事件1。{this.state.flag.toString()}
{this.props.school}{this.props.age}
<br />
{this.props.parent.state.p1}
{this.props.children}
</div>;
}
}
class Toggle1 extends React.Component{
constructor(props){
console.log('Toggle1')
super(props)
this.state = {flag:true};
}
handleClick(event){
console.log(event.target.id)
console.log(event.target === this);
console.log(this)
console.log(this.state)
this.setState({flag:!this.state.flag})
}
render(){
return <div id="t2" onClick={this.handleClick.bind(this)}>
點擊這句話,會觸發一個事件2。{this.state.flag.toString()}
<br />
{this.props.name}
<hr />
{this.props.age}
{this.props.parent.state.p2}
</div>;
}
}
class Root extends React.Component{
state = {
p1: 'abc',
p2:'.com'
};
render(){
//this.state.p1 = 'www.abc'
//this.setState({p1:'www.ab'}) //不允許這樣修改,控制台會報錯,
setTimeout(()=>this.setState({p1:'www.abdd'}),5000)
return (
<div>
<div>welcome to {this.state.p1}{this.state.p2}</div>
<br />
<Toggle school='abc' age= '20' parent={this} >
</Toggle>
<Toggle1 name='tom' age= '10' parent={this}/>
</div>
);
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
11、組件的生命周期
組件的聲明周期可分為三個狀態。
Mounting:已插入真實dom。
Updating:正在被重新渲染。
Unmounting:已移除真實dom。
方法如下:
(1)裝載組件觸發:
Componentwillmount在渲染前調用,在客戶端也在服務器端,指揮在裝載前調用一次。
Componentdidmount:在第一次渲染后調用,只要是在客戶端,之后的組件已經生成對應的dom結構,可以通過this.getdomnode()來進行訪問, 想與其他的Javascript框架一起使用,在這個方法中調用setTimeout,setinterval或者發送ajax請求等操作,只是在裝載完后調用一次,在render之后。
(2)更新組件觸發,這些方法不會再首次render組件的周期調用。
Componentwillreceiveprops(nextprops)在組件接受到一個新的prop時候被調用,在初始化render時候不會調用。
Shouldcomponentupdae(nextprops,nextstate)返回布爾值,在組件接受到新的Props或者state時候被調用,在初始化或者使用forceupdate時候不被調用。(如果設置為false,就不允許更新組件。)
Componentwillupdate(nextprops,nextstate)在組件接受到新的props或者state還沒render時候被調用,在初始化不會調用。
Componentdidupdate(prevprops,prevstate)在組件完成后更新后立即調用,在初始化時候不會被調用。
(3)卸載組件觸發
ComponentwillUNmount在組件從dom中移除的時候立即被調用
圖中,constructor構造器是最早執行的函數,觸發更新生命周期的函數,需要更新state或者props。
import React from 'react';
import ReactDOM from 'react-dom';
class Sub extends React.Component{
constructor(props){
console.log('sub con')
super(props)
this.state = {count:0}
}
handleClick(event){
this.setState({count:this.state.count + 1});
}
render(){
console.log('sub render')
return <div id='sub'onClick={this.handleClick.bind(this)}>
Sub's count = {this.state.count}
</ div>
}
componentWillMount(){
//第一次render之前
console.log('sub componentwillmount')
}
componentDidMount(){
//第一次render之后
console.log('sub componentDidMount')
}
componentWillUnmount(){
//清理工作
console.log('sub componentWillUnmount ')
}
}
class Root extends React.Component{
constructor(props){
console.log('root con')
super(props)
this.state = {}
}
render(){
return <div>
<Sub />
</ div>
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
順序是constructor =》 componentwillmount =》 render =》 componentdidmount –state或props改變,render。
import React from 'react';
import ReactDOM from 'react-dom';
import { ENGINE_METHOD_DIGESTS } from 'constants';
class Sub extends React.Component{
constructor(props){
console.log('sub con')
super(props)
this.state = {count:0}
}
handleClick(event){
this.setState({count:this.count + 1});
}
render(){
console.log('sub render')
return <div id='sub'onClick={this.handleClick.bind(this)}>
Sub's count = {this.state.count}
<a style={{height:200+'px',color:'red',backgroundColor:'#f0f0f0'}} >
</ a>
</ div>
}
componentWillMount(){
//第一次render之前
console.log('sub componentwillmount')
}
componentDidMount(){
//第一次render之后
console.log('sub componentDidMount')
}
componentWillUnmount(){
//清理工作
console.log('sub componentWillUnmount ')
}
componentWillReceiveProps(nextProps){
//props變更時候,接受新的props,交給shouldComponentUpdate
//props組件內只讀,只能從外部改變
console.log(this.props)
console.log(nextProps)
console.log('sub componentWillReceiveProps',this.state.count);
}
shouldComponentUpdate(nextProps,nextState){
//是否組件更新,props或state方式改變時候,返回布爾值,true才會更新
console.log('sub shouldComponentUpdate',this.state.count,nextState)
return true
}
componentWillUpdate(nextProps,nextState){
//同意更新后,真正更新之前,調用render
console.log('sub componentWillUpdate',this.state.count,nextState)
}
componentDidUpdate(prevProps,prevState){
//同意更新后,真正更新后,在render之后調用。
console.log('sub componentDidUpdate',this.state.count,prevState)
}
}
class Root extends React.Component{
constructor(props){
console.log('root con')
super(props)
this.state = {}
}
render(){
return <div>
<Sub />
</ div>
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
Componentwillmount第一次裝載,在首次render之前,例如控制state、props。
Componentdidmount第一次裝載結束,在首次render之后,控制state,props。
Componentwillreceiveprops在組件內部,props是只讀不可變的,這個函數接受到新的props,可以對props做一些處理,this.props={name:‘root’}這就是偷梁換柱,componentwillreceiveprops觸發,也會走shouldcomponentupdate。
Shouldcomponentupdate判斷是否需要組件更新,是否render,精確的控制渲染,提高性能。
Componentwillupdate除了首次render之外,每次render前執行,componentdidupdate在render之后調用。
componentWillMount(){
//第一次render之前
console.log('sub componentwillmount')
}
componentDidMount(){
//第一次render之后
console.log('sub componentDidMount')
}
componentWillUnmount(){
//清理工作
console.log('sub componentWillUnmount ')
}
componentWillReceiveProps(nextProps){
//props變更時候,接受新的props,交給shouldComponentUpdate
//props組件內只讀,只能從外部改變
console.log(this.props)
console.log(nextProps)
console.log('sub componentWillReceiveProps',this.state.count);
}
shouldComponentUpdate(nextProps,nextState){
//是否組件更新,props或state方式改變時候,返回布爾值,true才會更新
console.log('sub shouldComponentUpdate',this.state.count,nextState)
return true
}
componentWillUpdate(nextProps,nextState){
//同意更新后,真正更新之前,調用render
console.log('sub componentWillUpdate',this.state.count,nextState)
}
componentDidUpdate(prevProps,prevState){
//同意更新后,真正更新后,在render之后調用。
console.log('sub componentDidUpdate',this.state.count,prevState)
}
}
12、無狀態組件
也叫做函數式組件。
許多時候組件簡單不需要state狀態,也不需要聲明周期函數,無條件組件很好的滿足了需要,無狀態組件提供了一個參數props,返回一個react元素,無狀態組件函數本身就是render函數。
import React from 'react';
import ReactDOM from 'react-dom';
//let Root = props =><div>{props.shoolname}</div>;
//ReactDOM.render(<Root shoolname='abc' />,document.getElementById('root'))
function Root(props){
return <div> {props.name}</div>
}
ReactDOM.render(<Root name='bcd'/>,document.getElementById('root'))
三、高階組件
1、柯里化
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = function (Component){
function _wrapper(props){
return (
<div>
{props.name}
<hr />
<Component />
</ div>
)
}
return _wrapper
}
let Root = props =><div>Root</ div>
let NeeComp = wrapper(Root)
ReactDOM.render(<NeeComp name='abc' />,document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = function (Component){
return props=>(
<div>
{props.name}
<hr />
<Component />
</ div>
)
}
let Root = props =><div>Root</ div>
let NeeComp = wrapper(Root)
ReactDOM.render(<NeeComp name='abc' />,document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = Component=> props =>(
<div>
{props.name}
<hr />
<Component />
</ div>
)
let Root = props =><div>Root</ div>
let NeeComp = wrapper(Root)
ReactDOM.render(<NeeComp name='abc' />,document.getElementById('root'))
2、裝飾器
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = Component=>props=>
(<div>
{props.aname}
<hr />
<Component />
</ div>
)
//let Root = props => <div>Root</ div>
@wrapper
class Root extends React.Component{
render(){
return <div> Root</ div>
}
}
let Newarpper = wrapper(Root)
ReactDOM.render(<Newarpper aname='abc' />,document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = Components=>props=>
(<div>
{props.aname}
<hr />
<Components {...props} />
</ div>
)
//let Root = props => <div>Root</ div>
@wrapper
class Root extends React.Component{
render(){
return <div> Root{this.props.aname}</ div>
}
}
//let Newarpper = wrapper(Root)
ReactDOM.render(<Root aname='abc' />,document.getElementById('root'))
Root中也顯示props.name的屬性,使用 <Components {...props} />
增加屬性等。
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = id => Components=>props=>
(<div id = {id}>
{props.aname}
<hr />
<Components {...props} />
</ div>
)
//let Root = props => <div>Root</ div>
@wrapper('wrapper')
class Root extends React.Component{
render(){
return <div> Root{this.props.aname}</ div>
}
}
//let Newarpper = wrapper(Root)
ReactDOM.render(<Root aname='abc' />,document.getElementById('root'))
函數后面不能加括號調用,因為加括號返回的不是組件,而是render函數需要的內容。
要求返回的是組件,組件函數。