SPA(Single Page Application)
單頁面應(yīng)用,就是只有一張Web頁面的應(yīng)用较鼓。單頁應(yīng)用程序 (SPA) 是加載單個HTML 頁面并在用戶與應(yīng)用程序交互時動態(tài)更新該頁面的Web應(yīng)用程序措伐。
單頁面應(yīng)用的優(yōu)點
- 最大的好處是用戶體驗晦雨,對于內(nèi)容的改動不需要加載整個頁面巷嚣。
- 數(shù)據(jù)層和UI的分離克胳,可以重新編寫一個原生的移動設(shè)備應(yīng)用程序而不用大動干戈(同一套后端程序代碼中狂,不用修改就可以用于Web界面掸宛、手機、平板等多種客戶端)待牵。
- 高效其屏。它對服務(wù)器壓力很小,消耗更少的帶寬缨该,能夠與面向服務(wù)的架構(gòu)更好地結(jié)合偎行。
單頁面應(yīng)用的缺點
- 不利于SEO
- 初次加載耗時增多
- 導(dǎo)航不可用;前進贰拿、后退蛤袒、地址欄等,需要程序進行管理膨更;
書簽妙真,需要程序來提供支持;
應(yīng)用場景
那么單頁應(yīng)用的應(yīng)用如何呢荚守?看了一些資料珍德,總覺出來單頁面應(yīng)用有兩個硬傷:
首屏加載慢(大量js導(dǎo)致首屏加載慢)、seo不友好
如何應(yīng)用SPA或者是否應(yīng)用SPA矗漾,大概需要考慮以下這幾點:
- 交互體驗
不同的應(yīng)用面對不同人群锈候,會有不同的交互體驗需求。 - 工程代價
大型網(wǎng)站轉(zhuǎn)spa會有很大的代價敞贡。 - 容錯問題
SPA所有腳本都加進來泵琳,如果出現(xiàn)一個JS錯誤,那很可能整個網(wǎng)站就掛掉了,風(fēng)險很大获列。 - 是否必要
簡單呈現(xiàn)內(nèi)容的網(wǎng)站谷市,沒有必要用spa。增加了開發(fā)和調(diào)試的復(fù)雜性击孩,但是除了效果更酷炫點迫悠,沒有多少實用價值。 - 是否需要兼容低版本的ie瀏覽器
SPA總結(jié)
綜合了解了這種SPA單頁應(yīng)用和傳統(tǒng)的多頁面應(yīng)用溯壶,在以后的開發(fā)中及皂,我可能會采取單頁和多頁相結(jié)合的方式甫男,該跳轉(zhuǎn)的地方還是跳轉(zhuǎn)且改,結(jié)合單頁模式的用戶體驗優(yōu)點,將用戶體驗發(fā)揮到極致板驳,因為我覺得用戶體驗是最重要的東西之一又跛。
項目安裝的模塊解釋
開發(fā)依賴模塊:
- autoprefixer:postcss-loader的一個插件,使用一個數(shù)據(jù)庫根據(jù)當(dāng)前瀏覽器的普及度以及屬性支持自動給你的css添加前綴前綴:詳情點這里
- babel-core:babel轉(zhuǎn)碼的核心,必須安裝bable詳情點這里(阮一峰)
- babel-loader:babel加載器若治,配置babel編譯必備
- babel-plugin-add-module-exports:babel對export default{}支持不好慨蓝,不想寫成module.exports就需要安裝點這里
- babel-plugin-react-transform:代替react-hot-loader的插件,是基于Babel Plugin的端幼。這是一個基本的架子礼烈,要實現(xiàn)熱替換還要安裝其他插件。
- react-transform-hmr:安裝這個才能實現(xiàn)熱替換的功能婆跑。
- babel-preset-es2015:babel轉(zhuǎn)譯預(yù)設(shè)規(guī)則(轉(zhuǎn)es5)
- babel-preset-react:babel轉(zhuǎn)譯預(yù)設(shè)規(guī)則(react的jsx)
- css-loader:允許引入css文件
- style-loader:為了在html中以style的方式嵌入css
- postcss-loader:一個插件平臺此熬,這里只要用其autoprefixer功能
- eslint-loader:代碼規(guī)范檢查點這里
- extract-text-webpack-plugin:分離css文件
- url-loader:圖片與字體加載器,file-loader的上層封裝,依賴file-loader
- file-loader:圖片與字體加載器
- html-webpack-plugin:這樣可以將輸出的文件名自動注入到html中,不用我們自己手寫
- json-loader:處理json文件
- koa:node框架
- koa-router:koa路由
- less:less編譯css
- less-loader:less加載器
- open-browser-webpack-plugin:打包完成自動打開瀏覽器的插件
- webpack:一代神器
- webpack-dev-server:一個小型的Node.js Express服務(wù)器滑进,可實現(xiàn)代碼修改自動看這里
上線依賴模塊:
- es6-promise:使用fetch時為了兼容老版本需要安裝
- immutable:react性能優(yōu)化犀忱,需要學(xué)習(xí)新的APIimmutable
- react:
- react-addons-css-transition-group:實現(xiàn)組件出現(xiàn)與消失的css3過渡動畫官方地址
- react-addons-pure-render-mixin:用以替換shouldComponentUpdate,優(yōu)化性能
- react-dom:
- react-redux:
- react-router:
- react-swipe:輪播圖插件,引入swipe-js-iso,創(chuàng)建reat組件
- swipe-js-iso:基于swipe.js的一個Pull Request
- redux:
- whatwg-fetch:fetch
webpack配置詳解
resolve
定義了解析模塊路徑時的配置扶关,常用的就是extensions;可以用來指定模塊的后綴阴汇,這樣在引入模塊時就不需要寫后綴
resolve:{extensions:['', '.js','.jsx']}
postcss
在加載css/less時,用到postcss,主要使用autoprefixer功能节槐,能自動加css3的瀏覽器前綴搀庶;
postcss:[
require('autoprefixer)//調(diào)用autoprefixer插件,例如display:flex 針對不同品牌及版本的瀏覽器hack前綴
]
html-webpack-plugin
html模板插件
var HtmlWebpackPlugin = require('html-webpack-plugin');
plugins:[
new HtmlWebpackPlugin({template:'./app/index.html'})
]
webpack.HotModuleReplacementPlugin
var webpack = require('webpack');
plugins:[
new webpack.HotModuleReplacementPlugin()
]
open-browser-webpack-plugin
var OpenBrowserPlugin = require('open-browser-webpack-plugin');
plugins:[
new OpenBrowserPlugin({url:'http://localhost:8080'})
]
DefinePlugin
可在業(yè)務(wù)js代碼中使用 DEV 判斷是否是dev模式(dev模式下可以提示錯誤铜异、測試報告等, production模式不提示,在package.json配置的dev腳本命令中定義了NODE_ENV的值哥倔,所以這里可以獲取到,也可以直接寫'true')
var webpack = require('webpack');
plugins:[
new webpack.DefinePlugin({__DEV__:JSON.stringify(JSON.parse((process.env.NODE_ENV == 'dev') || 'false'))})
]
webpack-dev-server 代理
devServer: {
proxy: {
// 凡是 `/api` 開頭的 http 請求熙掺,都會被代理到 localhost:3000 上未斑,由 koa 提供 mock 數(shù)據(jù)。
// koa 代碼在 ./mock 目錄中币绩,啟動命令為 npm run mock
'/api': {
target: 'http://localhost:3000',
secure: false
}
},
contentBase: "./public", //本地服務(wù)器所加載的頁面所在的目錄
colors: true, //終端中輸出結(jié)果為彩色
historyApiFallback: true, //不跳轉(zhuǎn)
inline: true, //實時刷新
hot: true // 使用熱加載插件 HotModuleReplacementPlugin
}
ExtractTextPlugin
webpack.production.config中配置蜡秽,實現(xiàn)上線css與js代碼分離
var ExtractTextPlugin = require('extract-text-webpack-plugin');
loaders: [
{ test: /\.less$/, exclude: /node_modules/, loader: ExtractTextPlugin.extract('style', 'css!postcss!less') },
{ test: /\.css$/, exclude: /node_modules/, loader: ExtractTextPlugin.extract('style', 'css!postcss') }
]
plugins:[
new ExtractTextPlugin('[name].[chunkhash:8].css')
]
vender
entry: {
app: path.resolve(__dirname, 'app/index.jsx'),
// 將 第三方依賴 單獨打包
vendor: [
'react',
'react-dom',
'react-redux',
'react-router',
'redux',
'es6-promise',
'whatwg-fetch',
'immutable'
]
}
output: {
path: __dirname + "/build",
filename: "[name].[chunkhash:8].js",
publicPath: '/'
}
plugins:[
//提供公共代碼
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: '[name].[chunkhash:8].js'
})
]
import React from 'react'引用過程府阀?
npm 安裝的 react 的物理文件是存放在 ./node_modules/react中的,打開./node_modules/react/package.json找到"main": "react.js",芽突,這里的main即指定了入口文件试浙,即./node_modules/react/react.js這個文件。
react開發(fā)中的代碼分離
- page層:按頁面拆分田巴,每個頁面有一個主頁面index.jsx
- subPage層:對于復(fù)雜頁面壹哺,要將一個頁面拆封成多個子頁,不復(fù)雜的頁面只寫在index.jsx里即可
- component層:只用來展示數(shù)據(jù)的組件,對于不同頁面內(nèi)相同的組件箩朴,寫在component層以便復(fù)用
react生命周期
組件在初始化的時候,會觸發(fā)5個鉤子函數(shù):
getDefaultProps()
設(shè)置默認的props埠居,也可以用dufaultProps設(shè)置組件的默認屬性拐格。getInitialState()
在使用es6的class語法時是沒有這個鉤子函數(shù)的捏浊,可以直接在constructor中定義this.state金踪。此時可以訪問this.props。componentWillMount()
組件初始化時只調(diào)用靶瘸,以后組件更新不調(diào)用屋剑,整個生命周期只調(diào)用一次唉匾,此時可以修改state。render()
react最重要的步驟,創(chuàng)建虛擬dom逮诲,進行diff算法裆甩,更新dom樹都在此進行嗤栓。此時就不能更改state了叨叙。componentDidMount()
組件渲染之后調(diào)用,可以通過this.getDOMNode()獲取和操作dom節(jié)點钮呀,只調(diào)用一次。
更新時觸發(fā)的5個鉤子函數(shù):
componentWillReceivePorps(nextProps)
組件初始化時不調(diào)用蚂四,組件接受新的props時調(diào)用田度。shouldComponentUpdate(nextProps, nextState)
react性能優(yōu)化非常重要的一環(huán)。組件接受新的state或者props時調(diào)用奸笤,我們可以設(shè)置在此對比前后兩個props和state是否相同,如果相同則返回false阻止更新,因為相同的屬性狀態(tài)一定會生成相同的dom樹扣癣,這樣就不需要創(chuàng)造新的dom樹和舊的dom樹進行diff算法對比,節(jié)省大量性能,尤其是在dom結(jié)構(gòu)復(fù)雜的時候莱衩。不過調(diào)用this.forceUpdate會跳過此步驟。componentWillUpdate(nextProps, nextState)
組件初始化時不調(diào)用,只有在組件將要更新時才調(diào)用勒极,此時可以修改staterender()
componentDidUpdate()
組件初始化時不調(diào)用,組件更新完成后調(diào)用絮短,此時可以獲取dom節(jié)點。
還有一個卸載鉤子函數(shù)componentWillUnmount()
組件將要卸載時調(diào)用,一些事件監(jiān)聽和定時器需要在此時清除奖磁。
以上可以看出來react總共有10個周期函數(shù)(render重復(fù)一次)稠腊,這個10個函數(shù)可以滿足我們所有對組件操作的需求褐啡,利用的好可以提高開發(fā)效率和組件性能低飒。
常用的生命周期在項目中怎么用到?
- comopentDidMount
組件第一次加載時渲染loading組件,一般在此獲取網(wǎng)絡(luò)數(shù)據(jù)端仰,將數(shù)據(jù)賦值給狀態(tài),改變狀態(tài)重新渲染頁面。實際開始項目開發(fā)時,會經(jīng)常用到苍碟。 - shouldComponentUpdate
主要用于性能優(yōu)化抒钱,React 的性能優(yōu)化也是一個很重要的話題。 - componentDidUpdate
組件更新了之后觸發(fā)的事件,一般用于清空并更新數(shù)據(jù)。實際開始項目開發(fā)時,會經(jīng)常用到。 - componentWillUnmount
組件在銷毀之前觸發(fā)的事件,一般用戶存儲一些特殊信息,以及清理setTimeout事件等趴捅。
react性能優(yōu)化
- 介紹PureComponent
點這里 - 性能檢測,檢測優(yōu)化結(jié)果
npm i react-addons-perf --save
// 性能測試
import Perf from 'react-addons-perf';
if (__DEV__) { window.Perf = Perf }
運行程序猎拨。在操作之前先運行Perf.start()開始檢測国觉,然后進行若干操作,運行Perf.stop停止檢測,然后再運行Perf.printWasted()即可打印出浪費性能的組件列表。在項目開發(fā)過程中蹂风,要經(jīng)常使用檢測工具來看看性能是否正常任内。
- PureRenderMixin 優(yōu)化
React 最基本的優(yōu)化方式
組件中的props和state一旦變化會導(dǎo)致組件重新更新并渲染越除,但是如果props和state沒有變化也的觸發(fā)更新了(這種情況確實存在狼渊,比如調(diào)用setState方法米苹,但狀態(tài)并沒有改變)良瞧,這就導(dǎo)致了無效渲染
import React from 'react' ;
import PureRenderMixin from 'react-addons-pure-render-mixin' ;
class List extends React.Component {
constructor(props, context) {
super(props, context);
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
//...
}
重寫組件的shouldComponentUpdate函數(shù),在每次更新之前判斷props和state亏较,如果有變化則返回true莺褒,無變化則返回false。
因此雪情,我們在開發(fā)過程中遵岩,在每個 React 組件中都盡量使用PureRenderMixin
- Immutable.js 優(yōu)化
React 的終極優(yōu)化是使用 Immutable.js 來處理數(shù)據(jù),Immutable 實現(xiàn)了 js 中不可變數(shù)據(jù)的概念(可以去查一下何為“不可變數(shù)據(jù)”)巡通。
但是也不是所有的場景都適合用它尘执,當(dāng)我們組件的props和state中的數(shù)據(jù)結(jié)構(gòu)層次不深(例如普通的數(shù)組、對象等)的時候宴凉,就沒必要用它誊锭。但是當(dāng)數(shù)據(jù)結(jié)構(gòu)層次很深(例如obj.x.y.a.b = 10這種),你就得考慮使用了弥锄。
之所以不輕易使用是丧靡,Immutable 定義了一種新的操作數(shù)據(jù)的語法,如下籽暇。和我們平時操作 js 數(shù)據(jù)完全不一樣温治,而且每個地方都得這么用,學(xué)習(xí)成本高戒悠、易遺漏熬荆,風(fēng)險很高。
var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set('b', 50);
map1.get('b'); // 2
map2.get('b'); // 50
因此绸狐,建議優(yōu)化還是要從設(shè)計著手卤恳,盡量把數(shù)據(jù)結(jié)構(gòu)設(shè)計的扁平一些累盗,這樣既有助于優(yōu)化系統(tǒng)性能,又減少了開發(fā)復(fù)雜度和開發(fā)成本突琳。
react-router
注意:react-router4.0及以上版本語法有重大改變若债,老語法會報錯
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home}></IndexRoute>
<Route path="city" component={City}></Route>
<Route path='/Login(/:router)' component={Login}/>
<Route path="user" component={User}></Route>
<Route path="search/:category(/:keyword)" component={Search}></Route>
<Route path="detail/:id" component={Detail}></Route>
<Route path="*" component={NotFound}></Route>
</Route>
</Router>
目錄結(jié)構(gòu)
文件結(jié)構(gòu):
- 入口文件,源碼文件夾app目錄下的index.js
需要用redux傳遞信息的組件用Provider包住
const store = configureStore();
render(
<Provider store={store}>
<Hello/>
</Provider>,document.querySelector('#app')
);
- constants 常量文件夾
定義了action的type的常量本今,方便修改復(fù)用
export const USERINFO_LOGIN='USERINFO_LOGIN';
export const UPDATE_CITY='UPDATE_CITY';
- components 木偶組件文件夾
負責(zé)渲染視圖 - store文件夾
創(chuàng)建store的函數(shù)拆座,需要引用reducers目錄內(nèi)的rootReducer - reducers文件夾
reducers目錄下有index.js入口文件,可用combineReducers方法引用多個規(guī)則
import {combineReducers} from 'redux';
import userinfo from './userinfo';
import userinfo2 from './userinfo';
const rootReducer = combineReducers({userinfo,userinfo2});
export default rootReducer;
- actions 文件夾
存放派發(fā)方法
import * as actionTypes from '../constants/userinfo';
export function login(data) {
return {type:actionTypes.USERINFO_LOGIN,data}
}
export function updateCity(data) {
return {type: actionTypes.UPDATE_CITYNAME, data}
}
- containers 頁面文件夾
引入constants冠息,引入connect方法和bindActionCreators方法:
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as userinfoActions from '../actions/userinfo';
connect方法最后需要把組件包裝一下再輸出挪凑,目的是將派發(fā)后的狀態(tài)(userinfo)以及派發(fā)時的actions(userinfoActions)當(dāng)作組件的props傳遞給組件,userinfo是狀態(tài)數(shù)據(jù)逛艰,組件根據(jù)userinfo渲染視圖躏碳,userinfoActions是觸發(fā)狀態(tài)改變的方法,讓組件某事件綁定該方法后就可以有改變狀態(tài)的能力:
function mapStateToProps(state) {//一個自定義函數(shù)散怖,最為connect方法的第一個參數(shù)
console.log(state);
return {userinfo:state.userinfo}; //我的理解:state為rootReducer狀態(tài)菇绵,state.userinfo為rootReducer下的userinfo狀態(tài),rootReducer下可以掛載多個規(guī)則;state打印結(jié)果為一個實例對象镇眷,里面有對應(yīng)的各個規(guī)則以及其當(dāng)前的狀態(tài)
}
function mapDispatchToProps(dispatch) {//一個自定義函數(shù)咬最,最為connect方法的第二個參數(shù)
return {userinfoActions:bindActionCreators(userinfoActions,dispatch)}
}
export default connect(mapStateToProps,mapDispatchToProps)(Hello);
組件渲染完成后,觸發(fā)redux狀態(tài)改變欠动,再重新渲染組件
componentDidMount(){
this.props.userinfoActions.login({
userid:'aaa',
city:'北京'
})
}
}
fetch
- jquery 不考慮兼容永乌,做dom查詢,事件綁定具伍,效果處理
react開發(fā)為了用ajax函數(shù)去引用jq不值當(dāng)翅雏,而且js中ajax有一個詬病:復(fù)雜業(yè)務(wù)下callback的嵌套問題人芽;fetch是一種可替代ajax獲取/提交數(shù)據(jù)的技術(shù)望几,有些高級瀏覽器已經(jīng)可以window.fetch使用,相比與$.ajax更輕量萤厅,且原生支持promise橄抹,更符合現(xiàn)在的編程習(xí)慣 - 解決異步嵌套問題除了promise還有2個方法:
1.es6的generator函數(shù) 2.es7的async,await - fetch的坑
http://blog.csdn.net/whbwhb1/article/details/53322451
options = {
catchs: 異常處理惕味,控制臺拋出的異常是否自己處理:true 是害碾,false 否 由公共方法統(tǒng)一處理優(yōu)化顯示給用戶 默認 false
credentials: 請求帶上cookies,是每次請求保持會話一直
method: 請求使用的方法赦拘,如 GET、POST
headers: 請求的頭信息芬沉,形式為 Headers 對象或 ByteString躺同。
body: 請求的 body 信息:可能是一個 Blob阁猜、BufferSource、FormData蹋艺、URLSearchParams 或者 USVString 對象剃袍。注意 GET 或 HEAD 方法的請求不能包含 body 信息。
mode: 請求的模式捎谨,如 cors民效、no-cors 或者same-origin。是否允許跨域請求
cache: 請求的 cache 模式: default, no-store, reload, no-cache, force-cache, or only-if-cached.
}
es6-promise.js可以使它很好的支持IE9以上的版本涛救,IE8 需要改fetch.js源碼才能支持(見上一網(wǎng)址博客)
前端也需要掌握http
前端涉及到很多的數(shù)據(jù)操作:數(shù)據(jù)的獲取畏邢,數(shù)據(jù)的提交,數(shù)據(jù)的安全性检吆,數(shù)據(jù)性能的優(yōu)化
數(shù)據(jù) Mock
在目前互聯(lián)網(wǎng)行業(yè) web 產(chǎn)品開發(fā)中舒萎,前后端大部分都是分離開發(fā)的,前端開發(fā)過程中無法實時得到后端的數(shù)據(jù)蹭沛。這種情況下臂寝,一般會使用三種方式:
- 模擬靜態(tài)數(shù)據(jù):即按照既定的數(shù)據(jù)格式,自己提供一些靜態(tài)的JSON數(shù)據(jù)摊灭,用相關(guān)工具(如fis3)做接口來獲取這些數(shù)據(jù)咆贬。該形式使用不比較簡單的、只用 get 方法的場景帚呼,該項目不適用掏缎。
- 模擬動態(tài)接口:即自己用一個 web 框架,按照既定的接口和數(shù)據(jù)結(jié)構(gòu)的要求萝挤,自己模擬后端接口的功能御毅,讓前端項目能順利跑起來。該方式適用于新開發(fā)的項目怜珍,后端和前端同時開發(fā)端蛆。
- 轉(zhuǎn)發(fā)線上接口:項目開發(fā)中,所有的接口直接用代理獲取線上的數(shù)據(jù)酥泛,post 的數(shù)據(jù)也都直接提交到線上今豆。該方式適用于成熟項目中。
最外層組件的作用
在路由配置中柔袁,我們有一個最外層組件呆躲,App:
<Router history={this.props.history}>
<Route path='/' component={App}>
<IndexRoute component={Home}/>
<Route path='/city' component={City}/>
<Route path='/User' component={User}/>
<Route path='/search/:type(/:keyword)' component={Search}/>
<Route path='/detail/:id' component={Detail}/>
<Route path='*' component={NotFound}/>
</Route>
</Router>
其作用是:
- 復(fù)用公共的頭部尾部組件
render() {
return (<div>
<Head/>
{this.props.children}
<Footer/>
</div>
)
}
- 加載loading組件
render() {
return (
<div>{this.state.initDone ? this.props.children : <div>正在加載...</div> }
</div>
)
}
module.exports與exports,export與export default之間的關(guān)系和區(qū)別
- CommonJS模塊規(guī)范
為了方便捶索,Node為每個模塊提供一個exports變量插掂,指向module.exports。可以直接在 exports 對象上添加方法辅甥,但是注意酝润,不能直接將exports變量指向一個值,因為這樣等于切斷了exports與module.exports的聯(lián)系璃弄。 - ES6模塊規(guī)范
// 第一組
export default function crc32() { // 輸出
// ...
}
import crc32 from 'crc32'; // 輸入
// 第二組
export function crc32() { // 輸出
// ...
};
import {crc32} from 'crc32'; // 輸入
上面代碼的兩組寫法要销,第一組是使用export default時,對應(yīng)的import語句不需要使用大括號夏块;第二組是不使用export default時疏咐,對應(yīng)的import語句需要使用大括號。
export default命令用于指定模塊的默認輸出脐供。顯然浑塞,一個模塊只能有一個默認輸出,因此export default命令只能使用一次患民。所以缩举,import命令后面才不用加大括號,因為只可能對應(yīng)一個方法匹颤。
本質(zhì)上仅孩,export default就是輸出一個叫做default的變量或方法,然后系統(tǒng)允許你為它取任意名字印蓖。
js:判斷為null或者undefined
if(a==null)//這個判斷即可
開發(fā)時候遇到的問題
在開發(fā)detail的comment時候辽慕,遇到一個問題:
組件結(jié)構(gòu)是 父 => 子: (page)Detail => (subpage)Comment => CommentList => Item
數(shù)據(jù)是在Comment組件中獲取的,fetch獲取到數(shù)據(jù)后赦肃,將數(shù)據(jù)傳遞給CommentList溅蛉,再由CommentList傳遞給Item,由于Item中渲染的虛擬dom中一個參數(shù)寫錯了:
let data = this.props.data
<p>{item.comment}</p> {/*將data寫成item了*/}
結(jié)果導(dǎo)致在Comment組件里他宛,獲取到數(shù)據(jù)data后船侧,this.setState({data})不成功,當(dāng)時是用console.log調(diào)試厅各,在setState之前可以獲取到數(shù)據(jù)镜撩,但是在setState之后console沒有任何反映,而且setState顯示也沒有生效队塘,當(dāng)時一直以為是數(shù)據(jù)的問題袁梗,調(diào)試了半天,數(shù)據(jù)肯定沒問題憔古,就在子組件找問題遮怜,找到后改掉就正常了
debug調(diào)試發(fā)現(xiàn),才錯誤參數(shù)渲染之前獲取到的數(shù)據(jù)都是正常的鸿市,此時setState還沒生效,而控制臺也不會報錯
我的結(jié)論:setState后锯梁,由于state變化而導(dǎo)致的虛擬dom變化即碗,虛擬dom因為參數(shù)錯誤而無法渲染時,setState就一直無法完成;這中錯誤控制臺也不會報錯涝桅,通過再次測試拜姿,將Comment組件render里的一個參數(shù)故意寫錯,確實還是一樣的狀況冯遂,且不會報錯,在開發(fā)中要注意
this.setState()
http://www.tuicool.com/articles/zEfEfua
setState() 不會立刻改變 this.state 谒获,而是創(chuàng)建一個即將處理的 state 轉(zhuǎn)變蛤肌。在調(diào)用該方法之后訪問 this.state 可能會返回現(xiàn)有的值。
this.setState 是在 render 時, state 才會改變調(diào)用的, 也就是說, setState 是異步的. 組件在還沒有渲染之前, this.setState 還沒有被調(diào)用.這么做的目的是為了提升性能, 在批量執(zhí)行 State 轉(zhuǎn)變時讓 DOM 渲染更快.
- setState是異步的
很多開發(fā)剛開始沒有注意到 setState 是異步的批狱。如果你修改一些 state 裸准,然后直接查看它,你會看到之前的 state 蚜迅。這是 setState 中最容易出錯的地方莱坎。 setState 這個詞看起來并不像是異步的陨献,所以如果你不假思索的用它,可能會造成 bugs 权悟。
另外, setState 函數(shù)還可以將一個回調(diào)函數(shù)作為參數(shù), 當(dāng) setState 執(zhí)行完并且組件重新渲染之后. 這個回調(diào)函數(shù)會執(zhí)行, 因此如果想查看通過 setState 改變后的 state, 可以這樣寫:
this.setState({myState: nextState}, ()=>{console.log(this.state.myState)})
- setState會造成不必要的渲染
每次調(diào)用都會造成重新渲染。很多時候推盛,這些重新渲染是不必要的峦阁。不必要的渲染有以下幾個原因:
- 新的 state 其實和之前的是一樣的。這個問題通吃懦桑可以通過 shouldComponentUpdate 來解決榔昔。也可以用 pure render 或者其他的庫賴解決這個問題。
- 通常發(fā)生改變的 state 是和渲染有關(guān)的瘪菌,但是也有例外撒会。比如,有些數(shù)據(jù)是根據(jù)某些狀態(tài)來顯示的师妙。
- 有些 state 和渲染一點關(guān)系都沒有诵肛。有一些 state 可能是和事件、 timer ID 有關(guān)的疆栏。
所以:和渲染無關(guān)的狀態(tài)盡量不要放在 state 中來管理
通常 state 中只來管理和渲染有關(guān)的狀態(tài) 曾掂,從而保證 setState 改變的狀態(tài)都是和渲染有關(guān)的狀態(tài)。這樣子就可以避免不必要的重復(fù)渲染壁顶。其他和渲染無關(guān)的狀態(tài)珠洗,可以直接以屬性的形式保存在組件中,在需要的時候調(diào)用和改變若专,不會造成渲染许蓖。
避免不必要的修改,當(dāng) state 的值沒有發(fā)生改變的時候,盡量不要使用 setState 膊爪。雖然 shouldComponentUpdate 和 PureComponent 可以避免不必要的重復(fù)渲染自阱,但是還是增加了一層 shallowEqual 的調(diào)用,造成多余的浪費米酬。