在線訪問手冊: hanxueqing.github.io/Web-Front-e… github地址: github.com/Hanxueqing/…javascript
一、無狀態函數式組件css
建立純展現組件,只負責根據傳入的props
來展現,不涉及到要state
狀態的操做,是一個只帶有一個render
方法的組件類。html
二、組件是經過React.createClass建立的(ES5)前端
三、React.Componentvue
在es6中直接經過class關鍵字來建立java
組件其實就是一個構造器,每次使用組件都至關於在實例化組件node
react的組件必須使用render函數來建立組件的虛擬dom結構react
組件須要使用ReactDOM.render方法將其掛載在某一個節點上webpack
組件的首字母必須大寫ios
React.Component
是以ES6的形式來建立react
的組件的,是React目前極爲推薦的建立有狀態組件的方式,相對於 React.createClass
能夠更好實現代碼複用。將上面React.createClass
的形式改成React.Component
形式以下:
class Greeting extends React.Component{
constructor (props) {
super(props);
this.state={
work_list: []
}
this.Enter=this.Enter.bind(this); //綁定this
}
render() {
return (
<div>
<input type="text" ref="myWork" placeholder="What need to be done?" onKeyUp={this.Enter}/>
<ul>
{
this.state.work_list.map(function (textValue) {
return <li key={textValue}>{textValue}</li>;
})
}
</ul>
</div>
);
}
Enter(event) {
var works = this.state.work_list;
var work = this.refs.myWork.value;
if (event.keyCode == 13) {
works.push(work);
this.setState({work_list: works});
this.refs.myWork.value = "";
}
}
}
複製代碼
React.createClass
建立的組件,其每個成員函數的this
都有React
自動綁定,任什麼時候候使用,直接使用this.method
便可,函數中的this
會被正確設置
React.Component
建立的組件,其成員函數不會自動綁定this
,須要手動綁定,不然this
不能獲取當前組件實例對象
1.在構造函數中綁定
constructor(props) {
super(props);
this.Enter = this.Enter.bind(this);
}
複製代碼
2.使用bind綁定
<div onKeyUp={this.Enter.bind(this)}></div>
複製代碼
3.使用arrow function綁定
<div onKeyUp={(event)=>this.Enter(event)}></div>
複製代碼
React.Component
這種es6形式建立組件無狀態組件與有狀態的組件的區別爲?
沒有狀態,沒有生命週期,只是簡單的接受 props 渲染生成 DOM 結構
無狀態組件很是簡單,開銷很低,若是可能的話儘可能使用無狀態組件。
無狀態的函數建立的組件是無狀態組件,它是一種只負責展現的純組件
無狀態組件可使用純函數來實現。
const Slide = (props)=>{return (<div>.....</div>)}這就是無狀態組件(函數方式定義組件) 能夠簡寫爲 const Slide = props =>(<div>......</div>)
複製代碼
(1)this.props
(2)ref鏈
(3)Redux
什麼是高級組件?首先你得先了解請求ES6中的class只是語法糖,本質仍是原型繼承。可以更好的進行說明,咱們將不會修改組件的代碼。而是經過提供一些可以包裹組件的組件, 並經過一些額外的功能來加強組件。這樣的組件咱們稱之爲高階組件(Higher-Order Component)。
高階組件(HOC)是React中對組件邏輯進行重用的高級技術。但高階組件自己並非React API。它只是一種模式,這種模式是由React自身的組合性質必然產生的。
說到高階組件,就先得說到高階函數了,高階函數是至少知足下列條件的函數:
一、接受一個或多個函數做爲輸入 二、輸出一個函數
類比高階函數的定義,高階組件就是接受一個組件做爲參數,在函數中對組件作一系列的處理,隨後返回一個新的組件做爲返回值。
高階組件也有一系列的缺點,首先是被包裹組件的靜態方法會消失,這其實也是很好理解的,咱們將組件當作參數傳入函數中,返回的已經不是原來的組件,而是一個新的組件,原來的靜態方法天然就不存在了。若是須要保留,咱們能夠手動將原組件的方法拷貝給新的組件,或者使用hoist-non-react-statics之類的庫來進行拷貝。
參考:淺談React高階組件
withRouter高階組件,能夠根據傳入的組件生成一個新的組件,而且爲新組件添加上router相關的api。
connect 用於鏈接容器組件與UI組件,connect(mapStateToProps,mapDispatchToProps)(ui組件),當狀態改變的時候,容器組件內部由於經過store.subscribe能夠監聽狀態的改變,給ui組件傳入新的屬性,返回容器組件(智能組件),這個函數返回什麼,ui組件props上就會掛載什麼,ui組件的屬性上就就會有改變狀態的方法了,用的話經過this.props.方法名。
####React高性能的體現:虛擬DOM
在Web開發中咱們總須要將變化的數據實時反應到UI上,這時就須要對DOM進行操做。而複雜或頻繁的DOM操做一般是性能瓶頸產生的緣由(如何進行高性能的複雜DOM操做一般是衡量一個前端開發人員技能的重要指標)。
React爲此引入了虛擬DOM(Virtual DOM)的機制:在瀏覽器端用Javascript實現了一套DOM API。基於React進行開發時全部的DOM構造都是經過虛擬DOM進行,每當數據變化時,React都會從新構建整個DOM樹,而後React將當前整個DOM樹和上一次的DOM樹進行對比,獲得DOM結構的區別,而後僅僅將須要變化的部分進行實際的瀏覽器DOM更新。並且React可以批處理虛擬DOM的刷新,在一個事件循環(Event Loop)內的兩次數據變化會被合併,例如你連續的先將節點內容從A-B,B-A,React會認爲A變成B,而後又從B變成A UI不發生任何變化,而若是經過手動控制,這種邏輯一般是極其複雜的。
儘管每一次都須要構造完整的虛擬DOM樹,可是由於虛擬DOM是內存數據,性能是極高的,部而對實際DOM進行操做的僅僅是Diff分,於是能達到提升性能的目的。這樣,在保證性能的同時,開發者將再也不須要關注某個數據的變化如何更新到一個或多個具體的DOM元素,而只須要關心在任意一個數據狀態下,整個界面是如何Render的。
虛擬DOM就是JavaScript對象,就是在沒有真實渲染DOM以前作的操做。 真實dom的比對變成了虛擬dom的比對(js對象的比對) 虛擬dom裏面比對,涉及到diff算法。 key值 (key值相同dom能夠直接進行復用)
1.DOM結構發生改變-----直接卸載並從新create 2.DOM結構同樣-----不會卸載,可是會update變化的內容 3.全部同一層級的子節點.他們均可以經過key來區分-----同時遵循1.2兩點 (其實這個key的存在與否只會影響diff算法的複雜度,換言之,你不加key的狀況下,diff算法就會以暴力的方式去根據一二的策略更新,可是你加了key,diff算法會引入一些另外的操做)
React會逐個對節點進行更新,轉換到目標節點。而最後插入新的節點,涉及到的DOM操做很是多。diff總共就是移動、刪除、增長三個操做,而若是給每一個節點惟一的標識(key),那麼React優先採用移動的方式,可以找到正確的位置去插入新的節點
diff算法是同步進行更新和比較,必須同步執行完一個操做再進行下一個操做,所耗時間比較長,JavaScript是單線程的,一旦組件開始更新,主線程就一直被React控制,這個時候若是再次執行交互操做,就會卡頓。
React Fiber重構這種方式,渲染過程採用切片的方式,每執行一下子,就歇一下子。若是有優先級更高的任務到來之後呢,就會先去執行,下降頁面發生卡頓的可能性,使得React對動畫等實時性要求較高的場景體驗更好。
keys是什麼幫助 React 跟蹤哪些項目已更改、添加或從列表中刪除。
每一個 keys 在兄弟元素之間是獨一無二的。
keys 使處理列表時更加高效,由於 React 可使用子元素上的 keys 快速知道元素是新的仍是在比較樹時才被移動。
keys 不只使這個過程更有效率,並且沒有 keys ,React 不知道哪一個本地狀態對應於移動中的哪一個項目。
例如:數組循環出來三項,每一項前面有一個多選框,假設第一個多選框勾選了,而後我再動態添加新的元素,會發現新添加的元素就會被勾選了,這就是個問題!設置key值,這樣的話就能夠解決了。
複製代碼
####JSX 語法
在vue中,咱們使用render函數來構建組件的dom結構性能較高,由於省去了查找和編譯模板的過程,可是在render中利用createElement建立結構的時候代碼可讀性較低,較爲複雜,此時能夠利用jsx語法來在render中建立dom,解決這個問題,可是前提是須要使用工具來編譯jsx
JSX是一種語法,全稱:javascript xml
JSX語法不是必須使用的,可是由於使用了JSX語法以後會下降咱們的開發難度,故而這樣的語法又被成爲語法糖。
react.js中有React對象,幫助咱們建立組件等功能
HTML 中全部的信息咱們均可以用 JavaScript 對象來表示,可是用 JavaScript 寫起來太長了,結構看起來又不清晰,用 XML的方式寫起來就方便不少了。
因而 React.js 就把 JavaScript 的語法擴展了一下,讓 JavaScript 語言可以支持這種直接在 JavaScript 代碼裏面編寫相似 XML 標籤結構的語法,這樣寫起來就方便不少了。編譯的過程會把相似 XML 的 JSX 結構轉換成 JavaScript 的對象結構。
在不使用JSX的時候,須要使用React.createElement來建立組件的dom結構,可是這樣的寫法雖然不須要編譯,可是維護和開發的難度很高,且可讀性不好。
所謂的 JSX 其實就是 JavaScript 對象,因此使用 React 和 JSX 的時候必定要通過編譯的過程:
JSX代碼 — > 使用react構造組件,bable進行編譯—> JavaScript對象 —
ReactDOM.render()函數進行渲染
—>真實DOM元素 —>插入頁面
另:
最外層必須有根節點、標籤必須閉合
在vue中,藉助webpack提供的vue-loader來幫助咱們作一些轉化,讓vue代碼能夠在瀏覽器中執行。 react中沒有react-loader來進行代碼的轉化,而是採用babel裏面babel-preset-react來實現的。
constructor(props){
super(props);
this.state = {
age:1
}
}
複製代碼
經過調用this.setState去更新this.state,不能直接操做this.state,請把它當成不可變的。 調用setState更新this.state,他不是立刻就會生效的,他是異步的。因此不要認爲調用完setState後能夠立馬獲取到最新的值。
多個順序執行的setState不是同步的一個接着一個的執行,會加入一個異步隊列,而後最後一塊兒執行,即批處理。
setState是異步的,致使獲取dom可能拿的仍是以前的內容,因此咱們須要在setState第二個參數(回調函數)中獲取更新後的新的內容。
this.setState((prevState)=>({
age:++prevState.age
}),()=>{
console.log(this.state.age) //獲取更新後的最新的值
});
複製代碼
redux中間件
一般狀況下,action只是一個對象,不能包含異步操做
redux-thunk中間件
redux-thunk原理:
-能夠接受一個返回函數的actionCreators,若是這個actionCreators返回的是一個函數,就執行它,若是不是,就按照原來的next(action)執行
-若是不安裝redux-thunk中間件,actionCreators只能返回一個對象
-安裝了redux-thunk中間件以後,actionCreators能夠返回一個函數了,在這個函數裏面能夠寫異步操做的代碼
-redux中間件,建立出來的action在到達reducer之間,加強了dispatch的派發功能
####refs的做用業務場景?
經過ref對dom、組件進行標記,在組件內部經過this.refs獲取到以後,進行操做
<ul ref='content'><li>國內新聞</li></ul>
...
this.refs.content.style.display = this.state.isMenuShow?'block':'none'
複製代碼
ref用於給組件作標記,例如獲取圖片的寬度與高度。
非受控組件,input輸入框,獲取輸入框中的數據,能夠經過ref作標記。
<input ref={el=>this.input = el}/>
複製代碼
避免組件之間數據相互被引用,形成內存泄漏
class Test extends React.Component{
componentDidMount(){
console.log(this.el);
}
render(){
//react在銷燬組件的時候會幫助咱們清空掉ref的相關引用,這樣能夠防止內存泄漏等一系列問題。
return <div ref={el=>this.el=el}></div>
}
}
複製代碼
受控組件與非受控組件是相對於表單而言。
受控組件: 受到數據的控制。組件的變化依靠數據的變化,數據變化了,頁面也會跟着變化了。輸入框受到數據的控制,數據只要不變化,input框輸什麼都不行,一旦使用數據,從狀態中直接獲取。
<input value={this.state.value}/>
複製代碼
非受控組件: 直接操做dom,不作數據的綁定。經過refs來獲取dom上的內容進行相關的操做。
<input ref={el=>this.el=el}/> //不須要react組件作管理
複製代碼
數據驅動是react核心。
class Test extends React.Component{
constructor(props){
super(props);
this.handleWidowScroll = this.handleWidowScroll.bind(this);
}
handleWidowScroll(){
this.setState({
top:document.body.scrollTop
})
}
componentDidMount(){//綁定監聽事件
window.addEventListener("scroll",this.handleWindowScroll);
}
componentWillUnmount(){//移除監聽事件
window.removeEventListener("scroll",this.handleWindowScroll);
}
}
複製代碼
####React中data爲何返回一個函數
爲了防止組件與組件之間的數據共享,讓做用域獨立,data是函數內部返回一個對象,讓每一個組件或者實例能夠維護一份被返回對象的獨立的拷貝。
路由也變成組件了,因此它是很是靈活的(NavLink Route)。 vue中的路由須要單獨的配置 vue-router。
hashHistory # 不須要後端服務器的配置 browserHistory / 須要後端服務器的配置 (後端人員不清楚路由重定向等相關的概念)
經過onhashchange事件監聽路由的改變,一旦路由改變,這個事件就會被執行,就能夠拿到更改後的哈希值,經過更改後的哈希值就可讓咱們的頁面進行一個關聯,一旦路由發生改變了,整個頁面狀態就會發生改變,可是整個頁面是沒有發生任何http請求的,整個頁面處於一種無刷新狀態。
onhashchange
事件,能夠在window
對象上監聽這個事件:window.onhashchange = function(event) {
console.log(event.oldURL, event.newURL);
let hash = loaction.hash //經過location對象來獲取hash地址
console.log(hash) // "#/notebooks/260827/list" 從#號開始
}
複製代碼
由於hash發生變化的url都會被瀏覽器記錄下來,從而你會發現瀏覽器的前進後退均可以用 ,這樣一來,儘管瀏覽器沒有請求服務器,可是頁面狀態和url一一關聯起來,後來人們給它起了一個霸氣的名字叫前端路由,成爲了單頁應用標配。
spa單頁應用:根據頁面地址的不一樣來實現組件之間的切換,整個頁面處於一種無刷新狀態。
隨着history api的到來,前端路由開始進化了,前面的hashchange,你只能改變#後面的url片斷,而history api則給了前端徹底的自由
history api能夠分爲兩大部分:切換和修改 【切換路由/修改路由】
(1)切換歷史狀態
包括括back、forward
、go
三個方法,對應瀏覽器的前進,後退,跳轉操做
history.go(-2);//後退兩次
history.go(2);//前進兩次
history.back(); //後退
hsitory.forward(); //前進
複製代碼
(2)修改歷史狀態
包括 了pushState、replaceState
兩個方法,這兩個方法接收三個參數:stateObj,title,url。
在hash模式下,前端路由修改的是#中的信息,而瀏覽器請求時是不帶它玩的,因此沒有問題。可是在history下,你能夠自由的修改path,當刷新時,若是服務器中沒有相應的響應或者資源,會分分鐘刷出一個404來,須要後端人員去作一個配置。
【初始化階段】:
(1)getDefaultProps:實例化組件以後,組件的getDefaultProps鉤子函數會執行
這個鉤子函數的目的是爲組件的實例掛載默認的屬性
這個鉤子函數只會執行一次,也就是說,只在第一次實例化的時候執行,建立出全部實例共享的默認屬性,後面再實例化的時候,不會執行getDefaultProps,直接使用已有的共享的默認屬性
理論上來講,寫成函數返回對象的方式,是爲了防止實例共享,可是react專門爲了讓實例共享,只能讓這個函數只執行一次
組件間共享默認屬性會減小內存空間的浪費,並且也不須要擔憂某一個實例更改屬性後其餘的實例也會更改的問題,由於組件不能本身更改屬性,並且默認屬性的優先級低。
(2)getInitialState:爲實例掛載初始狀態,且每次實例化都會執行,也就是說,每個組件實例都擁有本身獨立的狀態。
(3)componentWillMount:執行componentWillMount,至關於Vue裏的created+beforeMount,這裏是在渲染以前最後一次更改數據的機會,在這裏更改的話是不會觸發render的從新執行。
(4)render:渲染dom
render()
方法必須是一個純函數,他不該該改變state
,也不能直接和瀏覽器進行交互,應該將事件放在其餘生命週期函數中。 若是shouldComponentUpdate()
返回false
,render()
不會被調用。
(5)componentDidMount:至關於Vue裏的mounted,多用於操做真實dom
【運行中階段】
當組件mount到頁面中以後,就進入了運行中階段,在這裏有5個鉤子函數,可是這5個函數只有在數據(屬性、狀態)發送改變的時候纔會執行
(1)componentWillReceiveProps(nextProps,nextState)
當父組件給子組件傳入的屬性改變的時候,子組件的這個函數纔會執行。初始化props時候不會主動執行
當執行的時候,函數接收的參數是子組件接收到的新參數,這個時候,新參數尚未同步到this.props上,多用於判斷新屬性和原有屬性的變化後更改組件的狀態。
(2)接下來就會執行shouldComponentUpdate(nextProps,nextState),這個函數的做用:當屬性或狀態發生改變後控制組件是否要更新,提升性能,返回true就更新,不然不更新,默認返回true。
接收nextProp、nextState,根據根據新屬性狀態和原屬性狀態做出對比、判斷後控制是否更新
若是shouldComponentUpdate()
返回false
,componentWillUpdate
,render
和componentDidUpdate
不會被調用。
(3)componentWillUpdate,在這裏,組件立刻就要從新render了,多作一些準備工做,千萬千萬,不要在這裏修改狀態,不然會死循環 至關於Vue中的beforeUpdate
(4)render,從新渲染dom
(5)componentDidUpdate,在這裏,新的dom結構已經誕生了,至關於Vue裏的updated
【銷燬階段】
當組件被銷燬以前的一剎那,會觸發componentWillUnmount,臨死前的掙扎
至關於Vue裏的beforeDestroy,因此說通常會作一些善後的事情,例如使定時器無效,取消網絡請求或清理在componentDidMount
中建立的任何監聽。
Vue在調用$destroy方法的時候就會執行beforeDestroy,而後組件被銷燬,這個時候組件的dom結構還存在於頁面結構中,也就說若是想要對殘留的dom結構進行處理必須在destroyed處理,可是react執行完componentWillUnmount以後把事件、數據、dom都所有處理掉了,因此根本不須要其餘的鉤子函數了
AJAX請求應該在componentDidMount生命週期事件中。
將全部的數據存儲在redux中進行管理,既能夠解決該問題。
(1)此鉤子函數在16版本中會被頻繁調用:15.X版本用的是diff算法,不會被頻繁調用,而React下一代調和算法Fiber會經過開始或中止渲染的方式優化應用性能,其會影響到comonentWillMount的觸發次數,對於componentWillMount這個生命週期的調用次數就會變得不肯定。React可能會屢次頻繁調用componentWillMount,若是咱們將ajax請求放到componentWillMount函數中,那麼顯而易見就會被觸發屢次,天然也就不是好的選擇。
(2)componentWillMount()將在React將來版本(官方說法 17.0)中被棄用。爲了不反作用和其餘的訂閱,官方都建議使用componentDidMount()代替。這個方法是用於在服務器渲染上的惟一方法。
初始化父組件第一次將數據傳遞給子組件的時候不會去執行,只有屬性props改變的時候,子組件的鉤子函數纔會觸發執行。
受控組件:受到數據控制,例如表單元素,當輸入框中的內容發生改變的時候,使其更改組件。數據驅動的理念,提倡內部的一些數據最好與組件的狀態進行關聯。
父組件能夠將本身的屬性傳遞給子組件,子組件經過this.props調用。
非受控組件;不會受到數據(state)的控制,由DOM自己進行管理,輸入框的內容發生改變了,直接經過ref進行標記,而後直接獲取使用便可。
把全部的數據放入到redux中管理。(props,state) 項目一旦有問題,能夠直接定位問題點。 組件擴展的時候,後續涉及到傳遞的問題。原本的話,本身使用數據,可是後來公用,還須要考慮如何傳遞。
redux中存儲數據能夠存儲至少5G以上的數據。 目的就是方便數據統一,好管理。
react-redux輔助工具
核心組件:Provider提供者,屬性上經過store將數據派給容器組件,connect用於鏈接容器組件與UI組件。
引入provider,哪一個地方須要用到狀態和屬性,就包裹一下,而且一旦有狀態改變,就會監聽到,而且將最新的狀態返回給UI組件。
ReactDOM.render(
<Provider store = {store}>
<Router>
<App />
</Router>
</Provider>, document.getElementById('root'));
複製代碼
connect()(UI組件) ==》返回一個容器組件
這個方法參數是state=>store.getState()
這個方法返回什麼,UI組件的屬性上就是有什麼
當狀態改變的時候,容器組件就會監聽狀態的變化,而且把更新後的狀態經過屬性的方法傳遞給UI組件
由於容器組件已經幫助咱們實現了store.subscribe方法的訂閱,這時候就不須要constructor函數和監聽函數,容器組件就會自動訂閱狀態的變化,UI組件經過this.props來獲取函數中返回的state,這時候當咱們對state進行操做的時候,狀態就會改變,視圖從新渲染,componentWillReceiveProps這個鉤子函數就會執行,實現了對狀態改變的事實監聽。
connect中有兩個參數,一個是映射狀態到組件屬性(mapStateToProps),一個是映射方法到組件屬性(mapDispatchToProps),最終內部返回一個容器組件幫助咱們作監聽操做,一旦狀態更改,UI組件就會從新渲染。
connect(mapStateToProps,mapDispatchToProps)(ui組件)
容器組件內部幫你作了 store.subscribe() 狀態變化 ==> 容器組件監聽狀態改變了 ==> 經過屬性的方式給ui組件傳遞
把store.getState()
的狀態轉化爲展現組件的props
當咱們須要掛載不少方法的時候咱們能夠將之簡寫
首先咱們引入bindActionCreators
import {bindActionCreators} from "redux"
複製代碼
而後咱們使用bindActionCreators將全部操做狀態的方法所有取出來綁定到UI組件的屬性上,使用的時候直接經過this.props取便可。
//actionCreators很純粹了,須要建立action而後返回action便可!
//ui組件的屬性上就就會有改變狀態的方法了,用的話經過this.props.方法名
const mapDispatchToProps = dispatch=>{
return bindActionCreators(actionsCreators,dispatch)
}
connect(mapStateToProps,mapDispatchToProps)(UI組件)
複製代碼
redux有四個組成部分:
store:用來存儲數據
reducer:真正的來管理數據
actionCreator:建立action,交由reducer處理
view: 用來使用數據,在這裏,通常用react組件來充當
若是你不知道是否須要 Redux,那就是不須要它
只有遇到 React 實在解決不了的問題,你才須要 Redux
簡單說,若是你的UI層很是簡單,沒有不少互動,Redux 就是沒必要要的,用了反而增長複雜性。
須要使用redux的項目:
從組件層面考慮,什麼樣子的須要redux:
redux的設計思想:
Web 應用是一個狀態機,視圖與狀態是一一對應的。
全部的狀態,保存在一個對象裏面(惟一數據源)。
Redux的流程:
建立store:
從redux工具中取出createStore去生成一個store。
建立一個reducer,而後將其傳入到createStore中輔助store的建立。
reducer是一個純函數,接收當前狀態和action,返回一個狀態,返回什麼,store的狀態就是什麼,須要注意的是,不能直接操做當前狀態,而是須要返回一個新的狀態。
想要給store建立默認狀態其實就是給reducer一個參數建立默認值。
組件經過調用store.getState方法來使用store中的state,掛載在了本身的狀態上。
組件產生用戶操做,調用actionCreator的方法建立一個action,利用store.dispatch方法傳遞給reducer
reducer對action上的標示性信息作出判斷後對新狀態進行處理,而後返回新狀態,這個時候store的數據就會發生改變, reducer返回什麼狀態,store.getState就能夠獲取什麼狀態。
咱們能夠在組件中,利用store.subscribe方法去訂閱數據的變化,也就是能夠傳入一個函數,當數據變化的時候,傳入的函數會執行,在這個函數中讓組件去獲取最新的狀態。
reducer是state最終格式的肯定。它是一個純函數,也就是說,只要傳入參數相同,返回計算獲得的下一個 state 就必定相同。沒有特殊狀況、沒有反作用,沒有 API 請求、沒有變量修改,單純執行計算。 reducer對傳入的action進行判斷,而後返回一個經過判斷後的state,這就是reducer的所有職責
Reducer 函數最重要的特徵是,它是一個純函數。也就是說,只要是一樣的輸入,一定獲得一樣的輸出。
純函數是函數式編程的概念,必須遵照如下一些約束。
不得改寫參數
不能調用系統 I/O 的API
不能調用Date.now()或者Math.random()等不純的方法,由於每次會獲得不同的結果
(1)只要是一樣的輸入,一定獲得一個一樣的輸出。
(2)千萬不能更改以前的狀態,必需要返回一個新狀態
(3)裏面不能有不純的操做,例如Math.random(),new Date(),io操做
一般狀況下,action只是一個對象,不能包含異步操做,這致使了不少建立action的邏輯只能寫在組件中,代碼量較多也不便於複用,同時對該部分代碼測試的時候也比較困難,組件的業務邏輯也不清晰,使用中間件了以後,能夠經過actionCreator異步編寫action,這樣代碼就會拆分到actionCreator中,可維護性大大提升,能夠方便於測試、複用,同時actionCreator還集成了異步操做中不一樣的action派發機制,減小編碼過程當中的代碼量。
redux中間件就是指action到達store之間。store.dispatch(action)方法將action派發給了store 而且咱們的action只能是一個對象,需求的時候,就須要考慮到一些異步邏輯放在哪裏去實現? 採用中間件以後,action就能夠是一個函數的形式了,而且會把函數式的action轉成對象,在傳遞給store.
dispatch一個action以後,到達reducer以前,進行一些額外的操做,就須要用到middleware。你能夠利用 Redux middleware 來進行日誌記錄、建立崩潰報告、調用異步接口或者路由等等。
換言之,redux的中間件都是對store.dispatch()的加強
作異步的操做在action裏面去實現!須要安裝redux中間件 redux-thunk redux-saga (基於配置文件 es7 async await) redux-promise
Redux-thunk是一個Redux中間件,位於 Action與 Strore中間,簡單的說,他就是對store.dispatch進行了一次升級,他經過改造store.dispatch,可使得store.dispatch能夠接受函數做爲參數。
能夠看出來redux-thunk最重要的思想,就是能夠接受一個返回函數的action creator。若是這個action creator 返回的是一個函數,就執行它,若是不是,就按照原來的next(action)執行。 正由於這個action creator能夠返回一個函數,那麼就能夠在這個函數中執行一些異步的操做。
用於輪播圖組件的遠程數據已經請求回來了,而且也已經實例化完畢了。發現navbar能滑,可是滑不過去的現象
緣由:由於咱們在ComponentDidMount中請求數據,這個操做是異步操做,不會阻止後續代碼,因此咱們一邊執行請求數據的代碼一邊實例化,數據還在請求中的時候,實例化已經開始執行,等數據回來的時候實例化已經早就結束了。
方法一:放入在componentDidUpdate鉤子函數裏面
問題:當頁面中的無關數據改變的時候一樣會走這個鉤子函數,那就會致使它從新執行。
因此咱們給Swiper的實例化起一個別名
在componentDidUpdate這個函數中if語句判斷它是否存在,若是不存在再去實例化,存在的話就不須要再去執行實例化操做。
//在這個鉤子函數裏面 就能夠獲取到因數據改變致使的虛擬dom從新渲染完成的真實dom結構了
componentDidUpdate(){
if(!this.swiper)this.initSwiper() //數據可能還在請求當中,可是這個實例化操做已經完畢了。等後續數據來了,實例化提早早就結束了。
}
複製代碼
方法二:會發現上面的方案會多寫一個鉤子函數,可不能夠在componentDidmount裏面實現此功能呢?
將實例化操做寫在獲取數據的回調函數裏
componentDidMount(){
//請求數據 更改navs
this.props.getNavs(()=>{
this.initSwiper()
})
}
複製代碼
在store/home/actionCreators文件中讓getNavs接收這個回調函數,在數據請求結束後執行callback回調函數。
import {Get} from "../../modules/axios-utils"
import {GET_NAV_INFO} from "./const"
export default {
getNavs(callback){
return dispatch=>{
Get({
url:"/sk/navs"
}).then(res=>{
let navs = res.data.data.object_list
dispatch({ type: GET_NAV_INFO,navs})
callback && callback()
})
}
}
}
複製代碼
咱們跳轉頁面的時候它會屢次請求數據,因此咱們須要在componentDidMount這個鉤子函數中判斷redux裏面navs是否存在,存在就不須要再發送請求了,這時候從別的頁面再跳轉回首頁就不會重複請求數據,可是數據劃不動,因此咱們須要在函數中再次執行Swiper初始化操做。
let {navs} = this.props;
if(navs){
this.initSwiper()
return false;
}
複製代碼
咱們進行改變狀態操做時,componentWillReceiveProps()這個函數沒有被觸發,說明視圖沒有檢測到狀態的改變。這時候咱們來到reducer.js這個文件中,查看執行添加操做的函數,咱們經過
let new_state = {...prevState}
複製代碼
這句代碼將prevState解構出來賦值給new_stat,,咱們往new_state中的todos數組push一個新內容,並無返回新的狀態,那是由於當咱們對new_state這個數組進行操做的時候,會影響到以前的prevState中的todos,由於todos是個引用類型,它和new_state中的todos指向同一塊內存空間,因此當咱們執行push操做的時候至關於更改了以前的狀態。在redux中規定,千萬不能對以前的狀態進行任何操做,必需要返回一個新狀態,內部以此爲依據來判斷到底有沒有新的狀態產生,根據以前狀態與新狀態的地址比較,更改以後的地址跟以前的地址是同一個的話,就說明沒有產生新狀態。因此即使咱們操做的是new_state中的todos,實際上咱們更改的也是prevState中的todos,因此不會有新的狀態產生。
因此咱們要使用深拷貝,拷貝出來一份同樣的數組,而且這個新數組的引用地址和以前的引用地址徹底不一樣。
ew_state.todos = new_state.todos.slice();
複製代碼
1. 淺拷貝: 將原對象或原數組的引用直接賦給新對象,新數組,新對象/數組只是原對象的一個引用。
2. 深拷貝: 建立一個新的對象和數組,將原對象的各項屬性的「值」(數組的全部元素)拷貝過來,是「值」而不是「引用」
能夠存在,互不干擾。
<div></div>
<div id="react"></div>
<div id="vue"></div>
複製代碼
ReactDOM.render(,document.getElementById("react")); new Vue({el:"#vue",router,store});
後端先調用數據庫,得到數據以後,將數據和頁面元素進行拼裝,組合成完整的html頁面,再直接返回給瀏覽器,以便用戶瀏覽。 例如:www.cnblogs.com/cate/design
數據由瀏覽器經過ajax動態得到,再經過js將數據填充到dom元素上最終展現到網頁中,這樣的過程就叫作客戶端渲染。 例如:m.maizuo.com/v5/#/films/…
客戶端渲染不利於SEO搜索引擎優化 服務端渲染是能夠被爬蟲抓取到的,客戶端異步渲染是很難被爬蟲抓取到的 服務端渲染對SEO友好,通過服務端渲染的頁面,在網絡傳輸的時候,傳輸的是一個真實的頁面,因此爬蟲就會對這個頁面中的關鍵數據進行分析、收錄。 服務端渲染缺點就是 對服務器壓力比較大 客戶端渲染減輕了服務器端的渲染壓力,可以實現先後端分離開發 客戶端渲染缺點就是 對SEO至關的不友好
VUE:Vant、Element、Mint UI、iView
React:Ant Design
移動端:Ant Design Mobile
PC端:Bootstrap、Ant Design
混合開發:MUI
Vue-cli create-react-app wepy-cli
包含基礎的依賴庫,只須要 npm install就能夠安裝,快速搭建項目。
引入:
(1)完整引入
(2)按需引入
(1)添加mate聲明
在編輯器中輸入mate:vp按tab鍵
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
複製代碼
(2)寫移動端必須用怪異盒模型,box-sizing:border-box;
使用彈性盒佈局:display:flex;
(3)根據設計圖設置單位,rem相對於根元素。
Iphone5:寬度640px;
html:font-size:31.25vw;
31.25vw=100px=1rem;
Iphone6:寬度750px;
html:font-size:26.67vw;
26.675vw=100px=1rem;
(4)編寫一屏頁面:
html,body{height:100%;}
ppi/dpi (每英寸所擁有像素點的數量)
dpr > 設備像素比 (物理像素、邏輯像素)
邏輯像素就是css設置的像素
物理像素就是設備顯示的像素
dpr == 物理像素 / 邏輯像素
dpr 通常考慮的值 > 2或者3
若是移動端設計圖的寬度爲750/640 > 選擇的dpr爲2
若是移動端設計圖的寬度爲1080 > 選擇的dpr爲3
例:
若是設計圖爲640px
dpr爲2
若是從ps中量出元素寬度爲300px;
在css裏面設置的爲 300px / dpr(2) == 150px;
**固定佈局:**以像素做爲頁面的基本單位,無論設備屏幕及瀏覽器寬度,只設計一套尺寸;
**可切換的固定佈局:**一樣以像素做爲頁面單位,參考主流設備尺寸,設計幾套不一樣寬度的佈局。經過識別的屏幕尺寸或瀏覽器寬度,選擇最合適的那套寬度佈局;
**彈性佈局(百分比佈局):**以百分比做爲頁面的基本單位,能夠適應必定範圍內全部尺寸的設備屏幕及瀏覽器寬度,並能完美利用有效空間展示最佳效果;
**混合佈局:**同彈性佈局相似,能夠適應必定範圍內全部尺寸的設備屏幕及瀏覽器寬度,並能完美利用有效空間展示最佳效果;只是混合像素、和百分比兩種單位做爲頁面單位。
**佈局響應:**對頁面進行響應式的設計實現,須要對相同內容進行不一樣寬度的佈局設計,有兩種方式:pc優先(從pc端開始向下設計);
移動優先(從移動端向上設計);不管基於那種模式的設計,要兼容全部設備,佈局響應時不可避免地須要對模塊佈局作一些變化(發生佈局改變的臨界點稱之爲斷點)
####什麼是響應式設計?響應式設計的基本原理是什麼
響應式是指根據不一樣設備瀏覽器分辨率或尺寸來展現不一樣頁面結構、行爲、表現的設計方式。
響應式設計的基本原理是經過媒體查詢檢測不一樣的設備屏幕尺寸作處理。
####說說對於移動端頁面部局你都知道哪幾種佈局方案,並說明其各自特色及實現的方法
移動端佈局經常使用的有100%佈局,等比縮放佈局,或是混合佈局。
百分比佈局也稱做流式佈局,通常適用一些流式頁面的佈局;
等比縮放佈局能夠利用rem或vw等方式來實現;
rem(font size of the root element)是指相對於根元素 (html)的字體大小的單位
em是相對於父元素
document.documentElement.style.fontSize = document.documentElement.clientWidth / 3.75 + "px"
window.onresize = function(){
document.documentElement.style.fontSize = document.documentElement.clientWidth / 3.75 + "px"
}
複製代碼
px和em都是長度單位,區別是:px的值是固定的,指定是多少就是多少,計算比較容易。em得值不是固定的,而且em會繼承父級元素的字體大小。
瀏覽器的默認字體高都是16px。因此未經調整的瀏覽器都符合: 1em=16px。那麼12px=0.75em, 10px=0.625em。
####Flex彈性盒佈局
Flex容器:採用 Flex 佈局的元素的父元素;
Flex項目:採用 Flex 佈局的元素的父元素的子元素;
容器默認存在兩根軸:水平的主軸(main axis)和垂直的交叉軸(cross axis)。主軸的開始位置(與邊框的交叉點)叫作main start,結束位置叫作main end;交叉軸的開始位置叫作cross start,結束位置叫作cross end。
項目默認沿主軸排列。單個項目佔據的主軸空間叫作main size,佔據的交叉軸空間叫作cross size。
不兼容全部瀏覽器,不適合寫pc端,主要用於移動端。
彈性元素只能影響子元素,沒法影響全部的後代元素。
彈性佈局屬於css3只兼容高版本瀏覽器
flex:彈性盒
justify-content 項目在主軸上的對齊方式
參數:center 居中/space-between 兩端對齊/space-around均分對齊
align-items 項目在交叉軸上的對齊方式
參數:center 居中
flex-direction 決定主軸的方向
row X軸/column Y軸
參考:Flex 佈局教程:語法篇
www.ruanyifeng.com/blog/2015/0…
媒體查詢 @media screen and (max-width:300px){ …} 最大寬度
Row col col-md-4 col-md-4 col-md-4
在Bootstrap框架當中,每列基本被分爲12格,要使用柵格系統須要在該<div>
標籤當中設置class="container"
,而對於每一行則用<div class="row">
包着,內部因爲有12格,所以能夠結合具體狀況分配比例,舉例:
<div class="container">
<!-- 定義柵格系統 -->
<div class="row">
<!-- 定義一行 -->
<div class="col-md-4">
<!-- 定義了三列,每列佔3格 -->
<img src="timg.jpg" width="300px">
</div>
<div class="col-md-4">
<img src="timg.jpg" width="300px">
</div>
<div class="col-md-4">
<img src="timg.jpg" width="300px">
</div>
</div>
<div class="row">
<!-- 定義了4列,分別佔六、三、二、1格 -->
<div class="col-md-6">
<img src="timg.jpg" width="300px">
</div>
<div class="col-md-3">
<img src="timg.jpg" width="300px">
</div>
<div class="col-md-2">
<img src="timg.jpg" width="300px">
</div>
<div class="col-md-1">
<img src="timg.jpg" width="300px">
</div>
</div>
</div>
複製代碼
網格佈局(Grid)是最強大的 CSS 佈局方案。
它將網頁劃分紅一個個網格,能夠任意組合不一樣的網格,作出各類各樣的佈局。之前,只能經過複雜的 CSS 框架達到的效果,如今瀏覽器內置了。
參考:CSS Grid 網格佈局教程
www.ruanyifeng.com/blog/2019/0…
趨勢:flex和grid使佈局更簡單
咱們知道混合開發的模式如今主要分爲兩種,H5工程師利用某些工具如DCLOUD產品、codorva+phonegap等等來開發一個外嵌native殼子的混合app。 還有就是應用比較普遍的,有native開發工程師和H5工程師一塊兒寫做開發的應用,在native的webview裏嵌入H5頁面,固然只是部分界面這麼作,這樣作的好處就是效率高,開發成本和維護成本都比較低,較爲輕量,可是有一個問題不可避免的會出現,就是js和native的交互。
不少狀況下,一個應用會有PC和移動端兩個版本,而這兩個版本由於差異大,內容多,因此不能用響應式開發可是單獨開發,而域名只有一個,用戶進入域後直接返回對應設備的應用,作法主要有兩種:
1. 前端判斷並跳轉
進入一個應用或者一個空白頁面後,經過navigator.userAgent來判斷用戶訪問的設備類型,進行跳轉
2. 後端判斷並響應對應的應用
用戶地址欄進入域的時候,服務器能接收到請求頭上包含的userAgent信息,判斷以後返回對應
複製代碼
目前小程序開發主要有三種形式:原生、wepy、mpvue,其中wepy是騰訊的開源項目;mpvue是美團開源的一個開發小程序的框架,全稱mini program vue(基於vue.js的小程序),vue開發者使用了這個框架後,開發小程序的效率將獲得很大的提高。
參考:使用mpvue開發微信小程序——原生微信小程序、mpvue、wepy對比
這些文件能夠分爲四類,分別是以js、wxml、wxss和json結尾的文件。
以js結尾的文件,通常狀況下是負責功能的,好比,點擊一個按鈕,按鈕就會變顏色。
以wxml爲後綴的文件,通常狀況下負責佈局,好比,把按鈕放在屏幕的上方,仍是放在屏幕的正中間。
以wxss爲後綴的文件,是負責渲染的功能,好比,按鈕是什麼顏色,是正方形仍是圓形。
以json爲後綴的文件,這裏能夠暫時理解爲更改屏幕上方的標題的,也就是說明頁面的頂部標題。
參考:微信小程序裏面各個部分是幹什麼的
一、儘可能減小 HTTP 請求
二、使用瀏覽器緩存
三、使用壓縮組件
四、圖片、JS的預載入
五、將腳本放在底部
六、將樣式文件放在頁面頂部
七、使用外部的JS和CS
(1) 減小http請求次數:CSS Sprites(雪碧圖), JS、CSS源碼壓縮、圖片大小控制合適;網頁Gzip,CDN託管,data緩存 ,圖片服務器。
(2) 前端模板 JS+數據,減小因爲HTML標籤致使的帶寬浪費,前端用變量保存AJAX請求結果,每次操做本地變量,不用請求,減小請求次數
(3) 用innerHTML代替DOM操做,減小DOM操做次數,優化javascript性能。
(4) 當須要設置的樣式不少時設置className而不是直接操做style。
(5) 少用全局變量、緩存DOM節點查找的結果。減小IO讀取操做。
(6) 避免使用CSS Expression(css表達式)又稱Dynamic properties(動態屬性)。
(7) 圖片預加載,將樣式表放在頂部,將腳本放在底部 加上時間戳。
(8) 避免在頁面的主體佈局中使用table,table要等其中的內容徹底下載以後纔會顯示出來,顯示比div+css佈局慢。
1. 儘量的減小 HTTP 的請求數 | content |
---|---|
2. 使用 CDN(Content Delivery Network) | server |
3. 添加 Expires 頭(或者 Cache-control ) | server |
4. Gzip 組件 | server |
5. 將 CSS 樣式放在頁面的上方 | css |
6. 將腳本移動到底部(包括內聯的) | javascript |
7. 避免使用 CSS 中的 Expressions | css |
8. 將 JavaScript 和 CSS 獨立成外部文件 | javascript css |
9. 減小 DNS 查詢 | content |
10. 壓縮 JavaScript 和 CSS (包括內聯的) | javascript css |
11. 避免重定向 | server |
12. 移除重複的腳本 | javascript |
13. 配置實體標籤(ETags) | css |
14. 使 AJAX 緩存 |
參考:你如何對網站的文件和資源進行優化?
單頁應用實際是把視圖(View)渲染從Server交給瀏覽器,Server只提供JSON格式數據,視圖和內容都是經過本地JavaScript來組織和渲染。而搜索搜索引擎抓取的內容,須要有完整的HTML和內容,單頁應用架構的站點,並不能很好的支持搜索。
參考:單頁應用SEO淺談
一個單頁應用程序SEO友好嗎?
baijiahao.baidu.com/s?id=160547…
實事求是的寫下本身網站的名字,網站的名字要合理,最好包含網站的主要內容。
頁面頭部指的是代碼中部分,具體一點就是中的「Description(描述)」和「Keywords(關鍵字)」兩部分
(1)採用純文本連接,少用,最好是別用Flash動畫設置連接,由於搜索引擎沒法識別Flash上的文字.
(2)按規範書寫超連接,這個title屬性,它既能夠起到提示訪客的做用,也可讓搜索引擎知道它要去哪裏.
(3)最好別使用圖片熱點連接,理由和第一點差很少。
網絡爬蟲對title和alt友好,網絡優化時必須寫。
爲網站製做一個「網站地圖」
PageRank(pr值,友情連接)
靜態頁面與動態頁面
避免大「體積」的頁面
最重要的一點!合理的代碼結構
使用生產環境production版本的react.js
重寫shouldComponentUpdate來避免沒必要要的dom操做,一旦返回true ,組件更新操做;返回false,就不會更新,節省性能。
使用key來幫助React識別列表中全部子組件的最小變化
PureComponent 純組件 ,自帶shouldComponentUpdate,能夠對props進行淺比較,不會比較對象這些東西。 發現後面的props與前面的props同樣,就不會進行render了。
class Test extends React.PureComponent{ <div><Test a={10}/></div>
constructor(props){
super(props);
}
render(){
return <div>hello...{this.props.a}</div>
}
}
複製代碼
像VUE這種單頁面應用,若是沒有應用懶加載,運用webpack打包後的文件將會異常的大,形成進入首頁時,須要加載的內容過多,時間過長,會出現長時間的白屏,即便作了loading也是不利於用戶體驗,而運用懶加載則能夠將頁面進行劃分,須要的時候加載頁面,能夠有效的分擔首頁所承擔的加載壓力,減小首頁加載用時,簡單說就是:進入首頁不用一次加載過多資源形成的用時時長。