react項(xiàng)目學(xué)習(xí)筆記-總結(jié)-感悟

SPA(Single Page Application)

單頁(yè)面應(yīng)用,就是只有一張Web頁(yè)面的應(yīng)用。單頁(yè)應(yīng)用程序 (SPA) 是加載單個(gè)HTML 頁(yè)面并在用戶與應(yīng)用程序交互時(shí)動(dòng)態(tài)更新該頁(yè)面的Web應(yīng)用程序。

單頁(yè)面應(yīng)用的優(yōu)點(diǎn)

  1. 最大的好處是用戶體驗(yàn),對(duì)于內(nèi)容的改動(dòng)不需要加載整個(gè)頁(yè)面。
  2. 數(shù)據(jù)層和UI的分離,可以重新編寫(xiě)一個(gè)原生的移動(dòng)設(shè)備應(yīng)用程序而不用大動(dòng)干戈(同一套后端程序代碼,不用修改就可以用于Web界面、手機(jī)、平板等多種客戶端)。
  3. 高效。它對(duì)服務(wù)器壓力很小,消耗更少的帶寬,能夠與面向服務(wù)的架構(gòu)更好地結(jié)合。

單頁(yè)面應(yīng)用的缺點(diǎn)

  1. 不利于SEO
  2. 初次加載耗時(shí)增多
  3. 導(dǎo)航不可用;前進(jìn)、后退、地址欄等,需要程序進(jìn)行管理;
    書(shū)簽,需要程序來(lái)提供支持;

應(yīng)用場(chǎng)景

那么單頁(yè)應(yīng)用的應(yīng)用如何呢?看了一些資料,總覺(jué)出來(lái)單頁(yè)面應(yīng)用有兩個(gè)硬傷:
首屏加載慢(大量js導(dǎo)致首屏加載慢)、seo不友好
如何應(yīng)用SPA或者是否應(yīng)用SPA,大概需要考慮以下這幾點(diǎn):

  1. 交互體驗(yàn)
    不同的應(yīng)用面對(duì)不同人群,會(huì)有不同的交互體驗(yàn)需求。
  2. 工程代價(jià)
    大型網(wǎng)站轉(zhuǎn)spa會(huì)有很大的代價(jià)。
  3. 容錯(cuò)問(wèn)題
    SPA所有腳本都加進(jìn)來(lái),如果出現(xiàn)一個(gè)JS錯(cuò)誤,那很可能整個(gè)網(wǎng)站就掛掉了,風(fēng)險(xiǎn)很大。
  4. 是否必要
    簡(jiǎn)單呈現(xiàn)內(nèi)容的網(wǎng)站,沒(méi)有必要用spa。增加了開(kāi)發(fā)和調(diào)試的復(fù)雜性,但是除了效果更酷炫點(diǎn),沒(méi)有多少實(shí)用價(jià)值。
  5. 是否需要兼容低版本的ie瀏覽器

SPA總結(jié)

綜合了解了這種SPA單頁(yè)應(yīng)用和傳統(tǒng)的多頁(yè)面應(yīng)用,在以后的開(kāi)發(fā)中,我可能會(huì)采取單頁(yè)和多頁(yè)相結(jié)合的方式,該跳轉(zhuǎn)的地方還是跳轉(zhuǎn),結(jié)合單頁(yè)模式的用戶體驗(yàn)優(yōu)點(diǎn),將用戶體驗(yàn)發(fā)揮到極致,因?yàn)槲矣X(jué)得用戶體驗(yàn)是最重要的東西之一。

項(xiàng)目安裝的模塊解釋

開(kāi)發(fā)依賴模塊:

  • autoprefixer:postcss-loader的一個(gè)插件,使用一個(gè)數(shù)據(jù)庫(kù)根據(jù)當(dāng)前瀏覽器的普及度以及屬性支持自動(dòng)給你的css添加前綴前綴:詳情點(diǎn)這里
  • babel-core:babel轉(zhuǎn)碼的核心,必須安裝bable詳情點(diǎn)這里(阮一峰)
  • babel-loader:babel加載器,配置babel編譯必備
  • babel-plugin-add-module-exports:babel對(duì)export default{}支持不好,不想寫(xiě)成module.exports就需要安裝點(diǎn)這里
  • babel-plugin-react-transform:代替react-hot-loader的插件,是基于Babel Plugin的。這是一個(gè)基本的架子,要實(shí)現(xiàn)熱替換還要安裝其他插件。
  • react-transform-hmr:安裝這個(gè)才能實(shí)現(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:一個(gè)插件平臺(tái),這里只要用其autoprefixer功能
  • eslint-loader:代碼規(guī)范檢查點(diǎn)這里
  • extract-text-webpack-plugin:分離css文件
  • url-loader:圖片與字體加載器,file-loader的上層封裝,依賴file-loader
  • file-loader:圖片與字體加載器
  • html-webpack-plugin:這樣可以將輸出的文件名自動(dòng)注入到html中,不用我們自己手寫(xiě)
  • json-loader:處理json文件
  • koa:node框架
  • koa-router:koa路由
  • less:less編譯css
  • less-loader:less加載器
  • open-browser-webpack-plugin:打包完成自動(dòng)打開(kāi)瀏覽器的插件
  • webpack:一代神器
  • webpack-dev-server:一個(gè)小型的Node.js Express服務(wù)器,可實(shí)現(xiàn)代碼修改自動(dòng)看這里

上線依賴模塊:

  • es6-promise:使用fetch時(shí)為了兼容老版本需要安裝
  • immutable:react性能優(yōu)化,需要學(xué)習(xí)新的APIimmutable
  • react:
  • react-addons-css-transition-group:實(shí)現(xiàn)組件出現(xiàn)與消失的css3過(guò)渡動(dòng)畫(huà)官方地址
  • 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的一個(gè)Pull Request
  • redux:
  • whatwg-fetch:fetch

webpack配置詳解

resolve

定義了解析模塊路徑時(shí)的配置,常用的就是extensions;可以用來(lái)指定模塊的后綴,這樣在引入模塊時(shí)就不需要寫(xiě)后綴

resolve:{extensions:['', '.js','.jsx']}

postcss

在加載css/less時(shí),用到postcss,主要使用autoprefixer功能,能自動(dòng)加css3的瀏覽器前綴;

postcss:[
    require('autoprefixer)//調(diào)用autoprefixer插件,例如display:flex 針對(duì)不同品牌及版本的瀏覽器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模式下可以提示錯(cuò)誤、測(cè)試報(bào)告等, production模式不提示,在package.json配置的dev腳本命令中定義了NODE_ENV的值,所以這里可以獲取到,也可以直接寫(xiě)'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` 開(kāi)頭的 http 請(qǐng)求,都會(huì)被代理到 localhost:3000 上,由 koa 提供 mock 數(shù)據(jù)。
          // koa 代碼在 ./mock 目錄中,啟動(dòng)命令為 npm run mock
          '/api': {
            target: 'http://localhost:3000',
            secure: false
          }
        },
        contentBase: "./public", //本地服務(wù)器所加載的頁(yè)面所在的目錄
        colors: true, //終端中輸出結(jié)果為彩色
        historyApiFallback: true, //不跳轉(zhuǎn)
        inline: true, //實(shí)時(shí)刷新
        hot: true  // 使用熱加載插件 HotModuleReplacementPlugin
    }

ExtractTextPlugin

webpack.production.config中配置,實(shí)現(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'),
    // 將 第三方依賴 單獨(dú)打包
    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'引用過(guò)程?

npm 安裝的 react 的物理文件是存放在 ./node_modules/react中的,打開(kāi)./node_modules/react/package.json找到"main": "react.js",,這里的main即指定了入口文件,即./node_modules/react/react.js這個(gè)文件。

react開(kāi)發(fā)中的代碼分離

  • page層:按頁(yè)面拆分,每個(gè)頁(yè)面有一個(gè)主頁(yè)面index.jsx
  • subPage層:對(duì)于復(fù)雜頁(yè)面,要將一個(gè)頁(yè)面拆封成多個(gè)子頁(yè),不復(fù)雜的頁(yè)面只寫(xiě)在index.jsx里即可
  • component層:只用來(lái)展示數(shù)據(jù)的組件,對(duì)于不同頁(yè)面內(nèi)相同的組件,寫(xiě)在component層以便復(fù)用

react生命周期

組件在初始化的時(shí)候,會(huì)觸發(fā)5個(gè)鉤子函數(shù):

  1. getDefaultProps()
    設(shè)置默認(rèn)的props,也可以用dufaultProps設(shè)置組件的默認(rèn)屬性。

  2. getInitialState()
    在使用es6的class語(yǔ)法時(shí)是沒(méi)有這個(gè)鉤子函數(shù)的,可以直接在constructor中定義this.state。此時(shí)可以訪問(wèn)this.props。

  3. componentWillMount()
    組件初始化時(shí)只調(diào)用,以后組件更新不調(diào)用,整個(gè)生命周期只調(diào)用一次,此時(shí)可以修改state。

  4. render()
    react最重要的步驟,創(chuàng)建虛擬dom,進(jìn)行diff算法,更新dom樹(shù)都在此進(jìn)行。此時(shí)就不能更改state了。

  5. componentDidMount()
    組件渲染之后調(diào)用,可以通過(guò)this.getDOMNode()獲取和操作dom節(jié)點(diǎn),只調(diào)用一次。

更新時(shí)觸發(fā)的5個(gè)鉤子函數(shù):

  1. componentWillReceivePorps(nextProps)
    組件初始化時(shí)不調(diào)用,組件接受新的props時(shí)調(diào)用。

  2. shouldComponentUpdate(nextProps, nextState)
    react性能優(yōu)化非常重要的一環(huán)。組件接受新的state或者props時(shí)調(diào)用,我們可以設(shè)置在此對(duì)比前后兩個(gè)props和state是否相同,如果相同則返回false阻止更新,因?yàn)橄嗤膶傩誀顟B(tài)一定會(huì)生成相同的dom樹(shù),這樣就不需要?jiǎng)?chuàng)造新的dom樹(shù)和舊的dom樹(shù)進(jìn)行diff算法對(duì)比,節(jié)省大量性能,尤其是在dom結(jié)構(gòu)復(fù)雜的時(shí)候。不過(guò)調(diào)用this.forceUpdate會(huì)跳過(guò)此步驟。

  3. componentWillUpdate(nextProps, nextState)
    組件初始化時(shí)不調(diào)用,只有在組件將要更新時(shí)才調(diào)用,此時(shí)可以修改state

  4. render()

  5. componentDidUpdate()
    組件初始化時(shí)不調(diào)用,組件更新完成后調(diào)用,此時(shí)可以獲取dom節(jié)點(diǎn)。
    還有一個(gè)卸載鉤子函數(shù)

  6. componentWillUnmount()
    組件將要卸載時(shí)調(diào)用,一些事件監(jiān)聽(tīng)和定時(shí)器需要在此時(shí)清除。

以上可以看出來(lái)react總共有10個(gè)周期函數(shù)(render重復(fù)一次),這個(gè)10個(gè)函數(shù)可以滿足我們所有對(duì)組件操作的需求,利用的好可以提高開(kāi)發(fā)效率和組件性能。

常用的生命周期在項(xiàng)目中怎么用到?

  • comopentDidMount
    組件第一次加載時(shí)渲染loading組件,一般在此獲取網(wǎng)絡(luò)數(shù)據(jù),將數(shù)據(jù)賦值給狀態(tài),改變狀態(tài)重新渲染頁(yè)面。實(shí)際開(kāi)始項(xiàng)目開(kāi)發(fā)時(shí),會(huì)經(jīng)常用到。
  • shouldComponentUpdate
    主要用于性能優(yōu)化,React 的性能優(yōu)化也是一個(gè)很重要的話題。
  • componentDidUpdate
    組件更新了之后觸發(fā)的事件,一般用于清空并更新數(shù)據(jù)。實(shí)際開(kāi)始項(xiàng)目開(kāi)發(fā)時(shí),會(huì)經(jīng)常用到。
  • componentWillUnmount
    組件在銷毀之前觸發(fā)的事件,一般用戶存儲(chǔ)一些特殊信息,以及清理setTimeout事件等。

react性能優(yōu)化

  • 介紹PureComponent
    點(diǎn)這里
  • 性能檢測(cè),檢測(cè)優(yōu)化結(jié)果
    npm i react-addons-perf --save
// 性能測(cè)試 
import Perf from 'react-addons-perf'; 
if (__DEV__) { window.Perf = Perf }

運(yùn)行程序。在操作之前先運(yùn)行Perf.start()開(kāi)始檢測(cè),然后進(jìn)行若干操作,運(yùn)行Perf.stop停止檢測(cè),然后再運(yùn)行Perf.printWasted()即可打印出浪費(fèi)性能的組件列表。在項(xiàng)目開(kāi)發(fā)過(guò)程中,要經(jīng)常使用檢測(cè)工具來(lái)看看性能是否正常。

  • PureRenderMixin 優(yōu)化
    React 最基本的優(yōu)化方式
    組件中的props和state一旦變化會(huì)導(dǎo)致組件重新更新并渲染,但是如果props和state沒(méi)有變化也的觸發(fā)更新了(這種情況確實(shí)存在,比如調(diào)用setState方法,但狀態(tài)并沒(méi)有改變),這就導(dǎo)致了無(wú)效渲染
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); 
    } 
    //...
}

重寫(xiě)組件的shouldComponentUpdate函數(shù),在每次更新之前判斷props和state,如果有變化則返回true,無(wú)變化則返回false。
因此,我們?cè)陂_(kāi)發(fā)過(guò)程中,在每個(gè) React 組件中都盡量使用PureRenderMixin

  • Immutable.js 優(yōu)化
    React 的終極優(yōu)化是使用 Immutable.js 來(lái)處理數(shù)據(jù),Immutable 實(shí)現(xiàn)了 js 中不可變數(shù)據(jù)的概念(可以去查一下何為“不可變數(shù)據(jù)”)。
    但是也不是所有的場(chǎng)景都適合用它,當(dāng)我們組件的props和state中的數(shù)據(jù)結(jié)構(gòu)層次不深(例如普通的數(shù)組、對(duì)象等)的時(shí)候,就沒(méi)必要用它。但是當(dāng)數(shù)據(jù)結(jié)構(gòu)層次很深(例如obj.x.y.a.b = 10這種),你就得考慮使用了。
    之所以不輕易使用是,Immutable 定義了一種新的操作數(shù)據(jù)的語(yǔ)法,如下。和我們平時(shí)操作 js 數(shù)據(jù)完全不一樣,而且每個(gè)地方都得這么用,學(xué)習(xí)成本高、易遺漏,風(fēng)險(xiǎn)很高。
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è)計(jì)著手,盡量把數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)的扁平一些,這樣既有助于優(yōu)化系統(tǒng)性能,又減少了開(kāi)發(fā)復(fù)雜度和開(kāi)發(fā)成本。

react-router

注意:react-router4.0及以上版本語(yǔ)法有重大改變,老語(yǔ)法會(huì)報(bào)錯(cuò)

 <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 木偶組件文件夾
    負(fù)責(zé)渲染視圖
  • store文件夾
    創(chuàng)建store的函數(shù),需要引用reducers目錄內(nèi)的rootReducer
  • reducers文件夾
    reducers目錄下有index.js入口文件,可用combineReducers方法引用多個(gè)規(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 頁(yè)面文件夾
    引入constants,引入connect方法和bindActionCreators方法:
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as userinfoActions from '../actions/userinfo';

connect方法最后需要把組件包裝一下再輸出,目的是將派發(fā)后的狀態(tài)(userinfo)以及派發(fā)時(shí)的actions(userinfoActions)當(dāng)作組件的props傳遞給組件,userinfo是狀態(tài)數(shù)據(jù),組件根據(jù)userinfo渲染視圖,userinfoActions是觸發(fā)狀態(tài)改變的方法,讓組件某事件綁定該方法后就可以有改變狀態(tài)的能力:

function mapStateToProps(state) {//一個(gè)自定義函數(shù),最為connect方法的第一個(gè)參數(shù)
  console.log(state);
  return {userinfo:state.userinfo}; //我的理解:state為rootReducer狀態(tài),state.userinfo為rootReducer下的userinfo狀態(tài),rootReducer下可以掛載多個(gè)規(guī)則;state打印結(jié)果為一個(gè)實(shí)例對(duì)象,里面有對(duì)應(yīng)的各個(gè)規(guī)則以及其當(dāng)前的狀態(tài)
}
function mapDispatchToProps(dispatch) {//一個(gè)自定義函數(shù),最為connect方法的第二個(gè)參數(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開(kāi)發(fā)為了用ajax函數(shù)去引用jq不值當(dāng),而且js中ajax有一個(gè)詬病:復(fù)雜業(yè)務(wù)下callback的嵌套問(wèn)題;fetch是一種可替代ajax獲取/提交數(shù)據(jù)的技術(shù),有些高級(jí)瀏覽器已經(jīng)可以window.fetch使用,相比與$.ajax更輕量,且原生支持promise,更符合現(xiàn)在的編程習(xí)慣
  • 解決異步嵌套問(wèn)題除了promise還有2個(gè)方法:
    1.es6的generator函數(shù) 2.es7的async,await
  • fetch的坑
    http://blog.csdn.net/whbwhb1/article/details/53322451
options = { 
   catchs: 異常處理,控制臺(tái)拋出的異常是否自己處理:true 是,false 否 由公共方法統(tǒng)一處理優(yōu)化顯示給用戶 默認(rèn) false 
   credentials: 請(qǐng)求帶上cookies,是每次請(qǐng)求保持會(huì)話一直 
   method: 請(qǐng)求使用的方法,如 GET、POST 
   headers: 請(qǐng)求的頭信息,形式為 Headers 對(duì)象或 ByteString。 
   body: 請(qǐng)求的 body 信息:可能是一個(gè) Blob、BufferSource、FormData、URLSearchParams 或者 USVString 對(duì)象。注意 GET 或 HEAD 方法的請(qǐng)求不能包含 body 信息。 
   mode: 請(qǐng)求的模式,如 cors、no-cors 或者same-origin。是否允許跨域請(qǐng)求 
   cache:  請(qǐng)求的 cache 模式: default, no-store, reload, no-cache, force-cache, or only-if-cached. 
 } 

es6-promise.js可以使它很好的支持IE9以上的版本,IE8 需要改fetch.js源碼才能支持(見(jiàn)上一網(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)品開(kāi)發(fā)中,前后端大部分都是分離開(kāi)發(fā)的,前端開(kāi)發(fā)過(guò)程中無(wú)法實(shí)時(shí)得到后端的數(shù)據(jù)。這種情況下,一般會(huì)使用三種方式:

  1. 模擬靜態(tài)數(shù)據(jù):即按照既定的數(shù)據(jù)格式,自己提供一些靜態(tài)的JSON數(shù)據(jù),用相關(guān)工具(如fis3)做接口來(lái)獲取這些數(shù)據(jù)。該形式使用不比較簡(jiǎn)單的、只用 get 方法的場(chǎng)景,該項(xiàng)目不適用。
  2. 模擬動(dòng)態(tài)接口:即自己用一個(gè) web 框架,按照既定的接口和數(shù)據(jù)結(jié)構(gòu)的要求,自己模擬后端接口的功能,讓前端項(xiàng)目能順利跑起來(lái)。該方式適用于新開(kāi)發(fā)的項(xiàng)目,后端和前端同時(shí)開(kāi)發(fā)。
  3. 轉(zhuǎn)發(fā)線上接口:項(xiàng)目開(kāi)發(fā)中,所有的接口直接用代理獲取線上的數(shù)據(jù),post 的數(shù)據(jù)也都直接提交到線上。該方式適用于成熟項(xiàng)目中。

最外層組件的作用

在路由配置中,我們有一個(gè)最外層組件,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為每個(gè)模塊提供一個(gè)exports變量,指向module.exports。可以直接在 exports 對(duì)象上添加方法,但是注意,不能直接將exports變量指向一個(gè)值,因?yàn)檫@樣等于切斷了exports與module.exports的聯(lián)系。
  • ES6模塊規(guī)范
// 第一組
export default function crc32() { // 輸出
  // ...
}
import crc32 from 'crc32'; // 輸入

// 第二組
export function crc32() { // 輸出
  // ...
};
import {crc32} from 'crc32'; // 輸入

上面代碼的兩組寫(xiě)法,第一組是使用export default時(shí),對(duì)應(yīng)的import語(yǔ)句不需要使用大括號(hào);第二組是不使用export default時(shí),對(duì)應(yīng)的import語(yǔ)句需要使用大括號(hào)。

export default命令用于指定模塊的默認(rèn)輸出。顯然,一個(gè)模塊只能有一個(gè)默認(rèn)輸出,因此export default命令只能使用一次。所以,import命令后面才不用加大括號(hào),因?yàn)橹豢赡軐?duì)應(yīng)一個(gè)方法。

本質(zhì)上,export default就是輸出一個(gè)叫做default的變量或方法,然后系統(tǒng)允許你為它取任意名字。

js:判斷為null或者undefined

if(a==null)//這個(gè)判斷即可

開(kāi)發(fā)時(shí)候遇到的問(wèn)題

在開(kāi)發(fā)detail的comment時(shí)候,遇到一個(gè)問(wèn)題:
組件結(jié)構(gòu)是 父 => 子: (page)Detail => (subpage)Comment => CommentList => Item
數(shù)據(jù)是在Comment組件中獲取的,fetch獲取到數(shù)據(jù)后,將數(shù)據(jù)傳遞給CommentList,再由CommentList傳遞給Item,由于Item中渲染的虛擬dom中一個(gè)參數(shù)寫(xiě)錯(cuò)了:

let data = this.props.data
<p>{item.comment}</p> {/*將data寫(xiě)成item了*/}

結(jié)果導(dǎo)致在Comment組件里,獲取到數(shù)據(jù)data后,this.setState({data})不成功,當(dāng)時(shí)是用console.log調(diào)試,在setState之前可以獲取到數(shù)據(jù),但是在setState之后console沒(méi)有任何反映,而且setState顯示也沒(méi)有生效,當(dāng)時(shí)一直以為是數(shù)據(jù)的問(wèn)題,調(diào)試了半天,數(shù)據(jù)肯定沒(méi)問(wèn)題,就在子組件找問(wèn)題,找到后改掉就正常了

debug調(diào)試發(fā)現(xiàn),才錯(cuò)誤參數(shù)渲染之前獲取到的數(shù)據(jù)都是正常的,此時(shí)setState還沒(méi)生效,而控制臺(tái)也不會(huì)報(bào)錯(cuò)
我的結(jié)論:setState后,由于state變化而導(dǎo)致的虛擬dom變化,虛擬dom因?yàn)閰?shù)錯(cuò)誤而無(wú)法渲染時(shí),setState就一直無(wú)法完成;這中錯(cuò)誤控制臺(tái)也不會(huì)報(bào)錯(cuò),通過(guò)再次測(cè)試,將Comment組件render里的一個(gè)參數(shù)故意寫(xiě)錯(cuò),確實(shí)還是一樣的狀況,且不會(huì)報(bào)錯(cuò),在開(kāi)發(fā)中要注意

this.setState()

http://www.tuicool.com/articles/zEfEfua
setState() 不會(huì)立刻改變 this.state ,而是創(chuàng)建一個(gè)即將處理的 state 轉(zhuǎn)變。在調(diào)用該方法之后訪問(wèn) this.state 可能會(huì)返回現(xiàn)有的值。

this.setState 是在 render 時(shí), state 才會(huì)改變調(diào)用的, 也就是說(shuō), setState 是異步的. 組件在還沒(méi)有渲染之前, this.setState 還沒(méi)有被調(diào)用.這么做的目的是為了提升性能, 在批量執(zhí)行 State 轉(zhuǎn)變時(shí)讓 DOM 渲染更快.

  • setState是異步的
    很多開(kāi)發(fā)剛開(kāi)始沒(méi)有注意到 setState 是異步的。如果你修改一些 state ,然后直接查看它,你會(huì)看到之前的 state 。這是 setState 中最容易出錯(cuò)的地方。 setState 這個(gè)詞看起來(lái)并不像是異步的,所以如果你不假思索的用它,可能會(huì)造成 bugs 。

另外, setState 函數(shù)還可以將一個(gè)回調(diào)函數(shù)作為參數(shù), 當(dāng) setState 執(zhí)行完并且組件重新渲染之后. 這個(gè)回調(diào)函數(shù)會(huì)執(zhí)行, 因此如果想查看通過(guò) setState 改變后的 state, 可以這樣寫(xiě):

this.setState({myState: nextState}, ()=>{console.log(this.state.myState)})
  • setState會(huì)造成不必要的渲染
    每次調(diào)用都會(huì)造成重新渲染。很多時(shí)候,這些重新渲染是不必要的。不必要的渲染有以下幾個(gè)原因:
  1. 新的 state 其實(shí)和之前的是一樣的。這個(gè)問(wèn)題通常可以通過(guò) shouldComponentUpdate 來(lái)解決。也可以用 pure render 或者其他的庫(kù)賴解決這個(gè)問(wèn)題。
  2. 通常發(fā)生改變的 state 是和渲染有關(guān)的,但是也有例外。比如,有些數(shù)據(jù)是根據(jù)某些狀態(tài)來(lái)顯示的。
  3. 有些 state 和渲染一點(diǎn)關(guān)系都沒(méi)有。有一些 state 可能是和事件、 timer ID 有關(guān)的。

所以:和渲染無(wú)關(guān)的狀態(tài)盡量不要放在 state 中來(lái)管理

通常 state 中只來(lái)管理和渲染有關(guān)的狀態(tài) ,從而保證 setState 改變的狀態(tài)都是和渲染有關(guān)的狀態(tài)。這樣子就可以避免不必要的重復(fù)渲染。其他和渲染無(wú)關(guān)的狀態(tài),可以直接以屬性的形式保存在組件中,在需要的時(shí)候調(diào)用和改變,不會(huì)造成渲染。
避免不必要的修改,當(dāng) state 的值沒(méi)有發(fā)生改變的時(shí)候,盡量不要使用 setState 。雖然 shouldComponentUpdate 和 PureComponent 可以避免不必要的重復(fù)渲染,但是還是增加了一層 shallowEqual 的調(diào)用,造成多余的浪費(fèi)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 項(xiàng)目地址 從頭開(kāi)始建立一個(gè)React App - 項(xiàng)目基本配置 npm init 生成 package.json ...
    瘦人假嚕嚕閱讀 89,587評(píng)論 33 78
  • 最近看了一本關(guān)于學(xué)習(xí)方法論的書(shū),強(qiáng)調(diào)了記筆記和堅(jiān)持的重要性。這幾天也剛好在學(xué)習(xí)React,所以我打算每天堅(jiān)持一篇R...
    gaoer1938閱讀 1,719評(píng)論 0 5
  • 深入JSX date:20170412筆記原文其實(shí)JSX是React.createElement(componen...
    gaoer1938閱讀 8,104評(píng)論 2 35
  • 原教程內(nèi)容詳見(jiàn)精益 React 學(xué)習(xí)指南,這只是我在學(xué)習(xí)過(guò)程中的一些閱讀筆記,個(gè)人覺(jué)得該教程講解深入淺出,比目前大...
    leonaxiong閱讀 2,860評(píng)論 1 18
  • 早上起來(lái),總能看到特別的東西。醒目的警示牌,非常難的。
    10086好閱讀 304評(píng)論 0 0