1:什麼是React?javascript
用來構建Ui的 javascript庫 React不是一個MVC框架,僅僅是視圖層的庫。
2:特色?html
使用JSX語法,建立組件,實現組件化開發,爲函數式的Ui編程打開大門。 性能: 經過diff算法和虛擬DOM實現視圖的高效更新。 html僅僅是個開始。
3: React的核心?java
虛擬DOM【React將DOM抽象爲虛擬DOM,虛擬DOM其實就是用一個對象來描述DOM,經過對比先後兩個對象的差別,最終只把變化的的部分從新 渲染,提升渲染的效率】 diff算法(虛擬DOM的加速器,提高React性能的法寶)【使用React的時候,在某個時間點render()函數建立了一顆React元素樹, 在下一個 state或者props更新的時候,render()函數會建立一顆新的React元素樹,React將對比這兩棵樹的不一樣之處,計算出如何高效的的更 新UI】
4:diff算法說明?node
4.1:【兩棵樹的根元素類型不一樣,React會先銷燬舊樹,建立新樹】
// 舊樹
<div>react
<Counter/>
</div>webpack
// 新樹
<span>git
<Counter />
</span>es6
執行過程 destoey Counter => insert Countergithub
4.2: [類型相同的React DOM元素,React會對比二者的屬性是否相同,只更新不一樣的屬性] [當處理完這個DOM節點的時候,React會遞歸處理子節點]
// 舊
<div className="before" title="stuff" />
// 新
<div className="after" title="stuff" />
只更新:className 屬性web
// 舊
<div style={{color: 'red', fontWeight: 'bold'}} />
// 新
<div style={{color: 'green', fontWeight: 'bold'}} />
只更新:color屬性
4.3: 4.3.1: [當在子節點的後邊添加一個節點,這時候兩棵樹的轉化工做執行的就很好]
// 舊
<ul>
<li>first</li>
<li>second</li>
</ul>
// 新
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
執行過程:
React會匹配新舊兩個<li>first</li>,匹配兩個<li>second</li>,而後添加 <li>third</li> tree
4.3.2:[在開始的位置添加一個元素]
// 舊
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
// 新
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
在沒有key屬性時執行過程:
React將改變每個子刪除從新建立,而非保持 <li>Duke</li> 和 <li>Villanova</li> 不變
4.3.3:[key屬性]
// 舊
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
// 新
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
執行過程:
如今React知道帶有key 「2014」的元素是新的,對於「2015」、「2016」僅僅移動位置便可 說明: key屬性在React內部使用,但不會傳遞給你的組件。 在遍歷屬性的時候,推薦在組件中使用key屬性: <li key={ item.id }>{ item.name }</li> key只須要保值和他的兄弟節點惟一便可,不須要全局惟一 儘量的減小數組index做爲key,數組中插入元素等操做的時候,會使得效率低下
5:React的基本使用?
npm install -S react react-dom [react 是React庫的入口點] [react-dom 提供了針對DOM的方法,好比把建立的虛擬DOM,渲染到頁面上去]
// 引入
import React from 'react'
import ReactDOM from 'react-dom'
// 建立虛擬DOM
// ['元素名稱', ‘元素屬性對象’, ‘當前元素得子元素string || createElement()返回值’]
const divVd = React.createElement('div', {
title: 'hello world'
}, 'Hello React!!!')
// 渲染
// ['虛擬DOM對象', ‘dom對象表示渲染到哪一個元素上內’, 回調函數]
ReactDOM.render(divVd, document.getElementById('app'))
說明: createElement()方式,代碼編寫不友好,太過於複雜
6:JSX的基本使用?
JSX最終會被編譯爲createElement()方法 JSX語法炫耀經過babel-preset-react 編譯後,才能被解析執行 npm install -D babel-preset-react (依賴與:babel-core/babel-loader)
/ 1 在 .babelrc 開啓babel對 JSX 的轉換 /
{
"presets": [
"env", "react"
]
}
/ 2 webpack.config.js /
module: [
rules: [
{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ },
]
]
/ 3 在 js 文件中 使用 JSX /
const dv = (
<div title="標題" className="cls container">Hello JSX!</div>
)
/ 4 渲染 JSX 到頁面中 /
ReactDOM.render(dv, document.getElementById('app'))
6.1:JSX的注意點?
[jsx元素添加類名 className 代替 class] [label的for屬性 使用htmlFor進行替換] [使用{}寫js代碼] [只能使用表達式,不能使用語句]
7:React組件?
React組件可讓你把UI分割爲獨立的可複用的,並將每一片斷 概念上講,組件就像js的函數,它們接受用戶傳入的prop,而且返回一個React對象,用來展描述展現在頁面中的內容 7.1: React建立組件的兩種方式? 經過JS函數建立(無狀態組件) 經過 class 建立(有狀態組件)
分類使用:
組件單純爲了展現數據,可使用哈數組件 組件有必定的業務邏輯,須要操做數據,可使用calss組件,阿里進行更改state裏面的值 7.1.1:函數建立? [函數名必須爲大寫字母開頭,React經過這個特色來判斷師是不是一個組件] [函數必須有返回值,返回值可使JSX對象或者null] [返回的JSX,必須有一個根元素] [組件的返回值使用()進行包裹,避免換行]
function Welcome(props) {
return ( <div className="shopping-list"> <h1>Shopping List for {props.name}</h1> <ul> <li>Instagram</li> <li>WhatsApp</li> </ul> </div> )
}
ReactDOM.render(<Welcome name="wcj" />, document.getElementById('app'))
7.1.2: class建立 在es6中class僅僅是一個語法糖,不是真正的類,本質上仍是構造+原型 實現繼承
class Person {
constructor(age) { this.age = age }
// 在class中定義方法 此處爲實例方法 經過實例打點調用
sayHello () {
console.log('你們好,我今年' + this.age + '了');
}
// 靜態方法 經過構造函數打點調用 Person.doudou()
static doudou () {
console.log('我是小明,我新get了一個技能,會暖牀');
}
}
// 添加靜態屬性 Person.staticName = '靜態屬性'
const p = new Person(19)
// 實現繼承的方式
class America extends Person{
constructor() { // 必須調用super(), super表示父類的構造函數 super() this.skin = 'white' this.eyeColor = 'white' }
}
// 建立React獨對象
// React對象繼承的字眼是React.Component
class Shooping List extends React.Component{
constructor(props) { super(props) } // class建立組件的時候,必須有render方法,且顯示return一個react對象或者null render() { return ( <div className="shopping-list"> <h1>Shopping List for {this.props.name}</h1> <ul> <li>Instagram</li> <li>WhatsApp</li> </ul> </div> ) }
}
7.1.3: 組件單獨封裝
import React from 'react'
function Hello(props) {
return ( <div> <div>這是Hello2組件</div> <h1>這是大大的H1標籤,我大,我驕傲!!!</h1> <h6>這是小小的h6標籤,我小,我傲嬌!!!</h6> </div> )
}
export default Hello
//其餘地方引用
import Hello from './components/Hello'
8: props 與 state?
8.1: props [給組件傳遞數據,通常用在父子組件之間] [React把傳遞給組件的屬性轉化爲一個對象交給props] [props是隻讀的,沒法給props添加或者修改屬性] 8.2: state [用來給組件提供組件內部使用的數據] [經過class建立的組件才具備狀態] [狀態是私有的,徹底由組件來控制] [不要在state裏面添加render()方法中不須要的數據,會影響渲染性能] [不要在render方法中調用setState()方法來修改state的值]
class Hello extentds React.Component{
constructor(props) { super() this,state = { gender: 'male' } } render() { return ( <div>性別: { this.state.gender }</div> ) }
}
9: 組件的生命週期?
組件的生命後期包含三個部階段 建立階段(Mounting)、運行和交互階段(Updating)、卸載階段(Unmounting) 9.1: 建立階段(Mounting) [constructor()] [componentWillMount()] [render()] [componentDidMount()] 9.2: 運行和交互階段(Updating) [componentWillReceiveProps()] [shouldComponentUpdate()] [componentWillUpdate()] [render()] [componentDidUpdate()] 9.3: 卸載階段(Unmounting) [componentWillUnmount()] 9.4:詳述? 9.4.1: constructor()
class Greeting extends React.Component{
constructor(props) { // 獲取props super(props) // 初始化 this.state = { count: props.initCount } }
}
// 初始化 props
// 經過靜態屬性defaultProps 來設置
Greeting.defaultProps = {
initCount : 0
}
9.4.2: componentWillMount() [組件被掛在在頁面以前調用] [沒法獲取頁面中的dom對象] [可使用setState()改變狀態值]
componentWillMount() {
console.warn(document.getElementById('btn')) // null
this.setState({
count: this.state.count + 1
})
}
9.4.3: render() [渲染組件到頁面中,沒法獲取頁面中的dom對象] [不要在render方法中調用setState()方法,不然會遞歸渲染] [緣由說明: 狀態改變會從新調用render()函數,render()又從新改變狀態]
render() {
console.warn(document.getElementById('btn')) // null
return (
<div> <button id="btn" onClick={this.handleAdd}>打豆豆一次</button> { this.state.count === 4 ? null : <CounterChild initCount={this.state.count}></CounterChild> } </div>
)
}
9.4.4: componentDidMount() [組件已經掛在到頁面中了] [能夠進行dom操做] [能夠經過setState()修改狀態的值] [在這裏會修改狀態會從新渲染]
componentDidMount() {
// 此時,就能夠獲取到組件內部的DOM對象
console.warn('componentDidMount', document.getElementById('btn'))
}
9.4.5: compomentWillReceiveProps() [組件接收到新的props錢出發這個事件] [能夠經過this.props接收之前傳遞的值,進行比較] [若你須要響應屬性的改變,能夠經過對比this.props和nextProps並在該方法中使用this.setState()處理狀態改變] [修改state不會觸發該方法]
componentWillReceiveProps(nextProps) {
console.warn('componentWillReceiveProps', nextProps)
}
9.4.6: shouldComponentUpdate() [根據這個返回值肯定是否進行渲染組件,返回true則進行從新渲染,不然不渲染] [經過某個條件進行渲染組件,下降組件渲染頻率,提高組件性能] [這個方法必須返回布爾值] [若是返回值是false,那麼後續的render()將不會執行]
// - 參數:
// - 第一個參數:最新屬性對象
// - 第二個參數:最新狀態對象
shouldComponentUpdate(nextProps, nextState) {
console.warn('shouldComponentUpdate', nextProps, nextState)
return nextState.count % 2 === 0
}
9.4.7: componentWillUpdate() [組件將要更新] [最新的屬性和狀態對象] [不能修改狀態,不然會重複渲染]
componentWillUpdate(nextProps, nextState) {
console.warn('componentWillUpdate', nextProps, nextState)
}
9.4.8: render() [從新渲染組件] [這個函數可以屢次執行,只要組件的屬性或者狀態改變了,這個方法就會從新執行] 9.4.8:componentDidUpdate() [組件已經被更新] [舊的屬性和狀態對象]
componentDidUpdate(prevProps, prevState) {
console.warn('componentDidUpdate', prevProps, prevState)
}
9.4.9: componentWillUnmount() [清除定時器] [組件一生只能執行依次]
10: state 和 setState
[使用setState()方法修改,狀態改變後,React會從新渲染組件] [不要直接修改state的值,這樣不會從新渲染組件]
constructor(props) {
super(props)
// 正確姿式!!!
// -------------- 初始化 state --------------
this.state = {
count: props.initCount
}
}
componentWillMount() {
// -------------- 修改 state 的值 --------------
// 方式一:
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
}, function(){
// 因爲 setState() 是異步操做,因此,若是想當即獲取修改後的state // 須要在回調函數中獲取 // https://doc.react-china.org/docs/react-component.html#setstate
});
// 方式二:
this.setState(function(prevState, props) {
return { counter: prevState.counter + props.increment }
})
// 或者 - 注意: => 後面須要帶有小括號,由於返回的是一個對象
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}))
}
11:組件綁定事件
[ref獲取dom對象] [onClick駝峯命名事件綁定]
<input type="button" value="觸發單擊事件"
onClick={this.handleCountAdd}
onMouseEnter={this.handleMouseEnter}
/>
11.1:事件綁定? [經過bind綁定] [經過箭頭函數綁定] 【bind可以調用函數,改變函數內部的this指向,並返回一個新的函數】 【bind第一個參數表示返回函數中的this的指向,後面的參數表示傳遞給函數的參數】
// 1:自定義方法:
handleBtnClick(arg1, arg2) {
this.setState({
msg: '點擊事件修改state的值' + arg1 + arg2
})
}
render() {
return (
<div> <button onClick={ // 無參數 // this.handleBtnClick.bind(this) // 有參數 this.handleBtnClick.bind(this, 'abc', [1, 2]) }>事件中this的處理</button> <h1>{this.state.msg}</h1> </div>
)
}
//2:構造函數中綁定
constructor() {
super()
this.handleBtnClick = this.handleBtnClick.bind(this)
}
// render() 方法中:
<button onClick={ this.handleBtnClick }>事件中this的處理</button>
11.2: props校驗? npm install -s prop-types
// 引入模塊
import PropTypes from 'prop-types'
// 使用
static propTypes = { initCount: PropTypes.number, // 規定屬性的類型 initAge: PropTypes.number.required // 規定屬性的類型,且規定爲必傳字段
}
12: React 單項數據流?
[數據流方向: 自上而下,也就是隻能從父組件傳遞到子組件] [數據都是從父組件提供的,子組件想要使用數據,必須從父組件中去獲取] [若是多個組件都要使用某個數據,那麼最好將這這部數據共享是狀態提高父集當中進行管理] 12.1: 組件通信
父 ---》 子 props
子 ---》 父組件經過prop傳遞迴調函數給子組件,子組件調用函數將數據參數傳遞給父組件。
兄弟組件 React是單項數據流,所以須要藉助於父組件進行傳遞,經過父組件的回調進行修改兄弟組件的props.
class Component {
constructor(props){
super(props);
}
render(){
return ( <div title={this.props.title}></div> )
}
}
<Component title="test"/>//調用title就傳進去了
父子傳值
class Child extends React.Component{
constructor(props){
super(props); this.state = {}
}
render(){
return ( <div> {this.props.text} <br /> <button onClick={this.props.refreshParent}> 更新父組件 </button> </div> )
}
}
class Parent extends React.Component{
constructor(props){
super(props); this.state = {}
}
refreshChild(){
return (e)=>{ this.setState({ childText: "父組件溝通子組件成功", }) }
}
refreshParent(){
this.setState({ parentText: "子組件溝通父組件成功", })
}
render(){
return ( <div> <h1>父子組件溝通</h1> <button onClick={this.refreshChild()} > 更新子組件 </button> <Child text={this.state.childText || "子組件未更新"} refreshParent={this.refreshParent.bind(this)} /> {this.state.parentText || "父組件未更新"} </div> )
}
}
兄弟傳值(context上下文的方式進行傳值)
class Brother1 extends React.Component{
constructor(props){
super(props); this.state = {}
}
render(){
return ( <div> <button onClick={this.context.refresh}> 更新兄弟組件 </button> </div> )
}
}
Brother1.contextTypes = {
refresh: React.PropTypes.any
}
class Brother2 extends React.Component{
constructor(props){
super(props); this.state = {}
}
render(){
return ( <div> {this.context.text || "兄弟組件未更新"} </div> )
}
}
Brother2.contextTypes = {
text: React.PropTypes.any
}
class Parent extends React.Component{
constructor(props){
super(props); this.state = {}
}
getChildContext(){
return { refresh: this.refresh(), text: this.state.text, } }
refresh(){
return (e)=>{ this.setState({ text: "兄弟組件溝通成功", }) }
}
render(){
return ( <div> <h2>兄弟組件溝通</h2> <Brother1 /> <Brother2 text={this.state.text}/> </div> )
}
}
Parent.childContextTypes = {
refresh: React.PropTypes.any,
text: React.PropTypes.any,
}
跨級傳值
Context 做用屬性:跨級傳遞數據,避免向下每層手動的傳遞props.須要配合PropTypes
class Grandfather extends React.Component {
// 類型限制(必須),靜態屬性名稱固定
static childContextTypes = {
color: PropTypes.string.isRequired
}
// 傳遞給孫子組件的數據
getChildContext() {
return { color: 'red' }
}
render() {
return ( <Father></Father> )
}
}
class Child extends React.Component {
// 類型限制,靜態屬性名字固定
static contextTypes = {
color: PropTypes.string
}
render() {
return ( // 從上下文對象中獲取爺爺組件傳遞過來的數據 <h1 style={{ color: this.context.color }}>爺爺告訴文字是紅色的</h1> )
}
}
class Father extends React.Component {
render() {
return ( <Child></Child> )
}
}
13: 路由管理React-router
npm install react-router-dom -Save 13.1: 使用? [引入路由組件] [使用 <Router></Router> 做爲根容器,包裹整個應用(JSX)] [使用 <Link to="/movie"></Link> 做爲連接地址,並指定to屬性] [使用 <Route path="/" compoent={Movie}></Route> 展現路由內容] 【<Router></Router>做爲整個組件的根組件,是路由容器,只能是惟一的子元素】 【<Link></Link> 相似與Vue中的<router-link></router-link>, to屬性指定路由地址】 【<Route></Route>相似於Vue中的<router-view></router-view>,指定路由內容(組件)展現的位置】
1: 引入組件
import { HashRouter as Router, Link, Route } from "react-router-dom"
2: 使用<Router>
<Router> // 3 設置 Link <Menu.Item key="1"><Link to="/">首頁</Link></Menu.Item> <Menu.Item key="2"><Link to="/movie">電影</Link></Menu.Item> <Menu.Item key="3"><Link to="/about">關於</Link></Menu.Item> // 4 設置 Route // exact 表示:絕對匹配(徹底匹配,只匹配:/) <Route exact path="/" component={HomeContainer}></Route> <Route path="/movie" component={MovieContainer}></Route> <Route path="/about" component={AboutContainer}></Route> </Router> 13.2: 路由傳參?
// 配置路由參數
<Route path="/movie/:movieType"></Route>
// 獲取路由參數
const type = this.props.match.params.movieType
13.3: 路由跳轉?
this.props.history.push('/movie/movieDetail/' + movieId)
history.go(-1) 用來實現頁面的前進(1)和後退(-1)
history.push() 方法用於在JS中實現頁面跳轉
14: fetch?
[fetch 是一個現代的概念, 等同於XMLHttpRequest, 它提供了許多與XMLHttpRequest相同的功能,但被設計成更具備可擴展性和高效性]
/*
經過fetch請求回來的數據,是一個Promise對象.
調用then()方法,經過參數response,獲取到響應對象
調用 response.json() 方法,解析服務器響應數據
再次調用then()方法,經過參數data,就獲取到數據了
*/
fetch('/api/movie/' + this.state.movieType)
// response.json() 讀取response對象,並返回一個被解析爲JSON格式的promise對象
.then((response) => response.json())
// 經過 data 獲取到數據
.then((data) => {
console.log(data); this.setState({ movieList: data.subjects, loaing: false })
})
/*
*/
fetch(url, {
method: 'POST', header: { "Content-Type": "application/x-www-form-urlencoded" } body: "key1=value1&key2=value2"
}).then(res => {
console.log(res)
}, err => {
console.log(err)
}).catch(e => {
console.log(e)
})
15: 跨域獲取數據的三種方式?
JSONP
代理
CORS
npm install -s fetch-jsonp [利用JSONP實現跨域獲取數據的時候,只能獲取get請求] 【限制: 1 只能發送GET請求 2 須要服務器支持JSONP請求】
JSONP
fetchJsonp('https://api.douban.com/v2/mov...')
.then(rep => rep.json())
.then(data => { console.log(data) })
代理
【webpack-dev-server 代理配置以下】 【問題:webpack-dev-server 是開發期間使用的工具,項目上線了就再也不使用 webpack-dev-server】 【解決:項目上線後的代碼,也是會部署到一個服務器中,這個服務器配置了代理功能便可(要求兩個服務器中配置的代理規則相同】
// webpack-dev-server的配置
devServer: {
// https://webpack.js.org/config...
// https://github.com/chimurai/h...
// http://www.jianshu.com/p/3bdf...
proxy: {
// 使用:/api/movie/in_theaters // 訪問 ‘/api/movie/in_theaters’ ==> 'https://api.douban.com/v2/movie/in_theaters' '/api': { // 代理的目標服務器地址 target: 'https://api.douban.com/v2', // https請求須要該設置 secure: false, // 必須設置該項 changeOrigin: true, // '/api/movie/in_theaters' 路徑重寫爲:'/movie/in_theaters' pathRewrite: {"^/api" : ""} }
}
}
/ movielist.js /
fetch('/api/movie/in_theaters')
.then(function(data) {
// 將服務器返回的數據轉化爲 json 格式 return data.json()
})
.then(function(rep) {
// 獲取上面格式化後的數據 console.log(rep);
})
CORS-服務器端跨域
// 經過Express的中間件來處理全部請求
app.use('*', function (req, res, next) {
// 設置請求頭爲容許跨域
res.header('Access-Control-Allow-Origin', '*');
// 設置服務器支持的全部頭信息字段
res.header('Access-Control-Allow-Headers', 'Content-Type,Content-Length, Authorization,Accept,X-Requested-With');
// 設置服務器支持的全部跨域請求的方法
res.header('Access-Control-Allow-Methods', 'POST,GET');
// next()方法表示進入下一個路由
next();
});
16: redux 狀態管理工具,用來管理應用中的數據
16.1: 核心? Store: [Redux應該只有一個store] [getState() 獲取state] [dispatch(action) 用來修改state]
/ action/
【 在redux中, action就是一個對象。】
【action必須提供一個: type屬性,表示當前動做的標識】
【其餘參數,標識這個動做須要用到的數據】
{ type: ‘ADD_TOTD’,name: '要添加的任務名稱'}
{type: 'TOGGLE_TOTD', id: 1}
/reducer/
第一個參數:表示狀態(數據),咱們須要給初始狀態設置默認值
第二個參數:表示 action 行爲
function todo(state = [], action) {
switch(action.type) {
case 'ADD_TODO': state.push({ id: Math.random(), name: action.name, completed: false }) return state case 'TOGGLE_TODO': for(var i = 0; i < state.length; i++) { if (state[i].id === action.id) { state[i].completed = !state[i].completed break } } return state default: return state
}
}
// 要執行 ADD_TODO 這個動做:
dispatch( { type: 'ADD_TODO', name: '要添加的任務名稱' } )
// 內部會調用 reducertodo(undefined, { type: 'ADD_TODO', name: '要添加的任務名稱' })