React入門:從項目搭建(webpack)到引入路由(react-router)和狀態管理(react-redux/saga)

1、什麼是React

React是什麼?React的官網是這樣介紹的:
React-用於構建用戶界面的 JavaScript 庫。
看起來十分簡潔,React僅僅是想提供一個構建用戶界面的工具,也就是隻關心UI層面,不關心數據層面,數據層面的東西交給專門的人(react-redux)來作。因此有些人會說React就是一個ui框架。
React 認爲渲染邏輯本質上與其餘 UI 邏輯內在耦合,好比,在 UI 中須要綁定處理事件、在某些時刻狀態發生變化時須要通知到 UI,以及須要在 UI 中展現準備好的數據。因此,React提供了jsx語法,也能夠理解爲讓你在ul組件裏寫不少東西。jsx最終是經過babel將組件轉化爲React.createElement()函數來調用。組件化開發的樂趣須要慢慢體會。既然是入門,下面經過搭建項目-小例子-引入路由/狀態管理簡單介紹下react,有關複雜的概念這裏暫不說起。html

const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

ReactDOM.render(
  element,
  document.getElementById('root')
);

2、項目搭建

上面說了,react的組件開發是須要babel進行編譯的,瀏覽器是不能直接解析的,因此react項目通常都是須要作一些簡單的配置的。固然,有不少的配置完善的腳手架可使用,好比官方腳手架creat-react-app或者螞蟻金服的dva框架,使用起來都是很方便上手。可是我們我以爲經過簡單搭建一個項目能夠更好的理解react。下面將會經過webpack一步一步搭建一個可使用的項目,可是具體的優化這裏就先不考慮了。node

1.打開終端、建立項目react

mkdir react-demo && cd react-demo //建立文件夾,並進入文件夾

2.初始化構建webpack

npm init //爲了後面下載npm包和node配置使用
         //一路回車鍵就能夠了!項目中多出來一個package.json文件

3.建立項目入口git

新建app.js文件,引入react和react-dom,新建一個index.html,包含<div id='app'></div>。github

import React from 'react'; // 終端執行 npm i react 下載react
    import ReactDom from 'react-dom' // 終端執行 npm i react-dom 下載react-dom
    
    function App(){ //以函數的方式建立react組件
        return <div>welcom,react-app</div>
    }
    
    ReactDom.render( //將組件App渲染掛載到頁面的根節點app
        <App />,
        document.getElementById('app')//因此須要新建一個html文件提供app節點供組件掛載
    )

3.webpack配置web

建立好入口文件app.js後,須要將jsx語法文件經過babel編譯,因此先引入webpack,並新建js文件webpack.config.js作相關的配置以下:npm

npm i webpack webpack-cli --save //安裝webpack及cli依賴

webpack.config.js文件內容:

const path = require("path");
const {CleanWebpackPlugin} = require('clean-webpack-plugin');//經過 npm 安裝
const HtmlWebpackPlugin = require('html-webpack-plugin'); //經過 npm 安裝
module.exports = {
    entry:{
        app:'./app.js'
    },
    output: {
        path: path.resolve(__dirname, './dist'),
        filename: '[name].bundel.[hash].js'
    },
    module:{
        rules: [{
            test: /\.js$/,
            exclude: /(node_modules|bower_components)/,
            use:[{
                loader: 'babel-loader',
                options: {
                  presets: [
                      '@babel/preset-env',//引入babel
                      '@babel/preset-react' //引入babel-react 
                    ]
                }
            }]
        }]
    },
    plugins: [
        new CleanWebpackPlugin(),//清空dist文件夾
        new HtmlWebpackPlugin({
            template: './index.html'
        }),//添加html模版插件
    ]
}


安裝依賴:
npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env @babel/preset-react --save

npm i html-webpack-plugin clean-webpack-plugin --save

4.配置npm/script啓動項json

打開package.json文件,添加一行script代碼,以下:redux

clipboard.png

此時咱們的代碼結構以下:

clipboard.png

當咱們在終端執行npm run build命令後,能夠看到多出來打包後的dist文件夾,以及打包後的js文件,以下:

clipboard.png

在瀏覽器裏打開dist下的index.html文件,OK,第一行react代碼閃亮出現!

clipboard.png

4.修改npm/script啓動項
上面代碼雖然顯示出來了,可是不能每次更改代碼都執行一次打包吧,太麻煩了?那就搭建一個server吧,在搭配上局部熱更新(hmr)使用起來非常舒暢。須要修改3個地方:

1.在webpack.config.js文件中添加如下代代碼:
devServer: {//開啓一個本地服務
        port: 9000,//端口
        host:"0.0.0.0",//配置後可經過本機ip訪問,方便在手機上調試
        hot:true,//
}
2.修改入口文件app.js:
import React from 'react';
import ReactDOM from 'react-dom'
import {hot} from 'react-hot-loader/root'//添加依賴,經過npm下載

function AppCont(){
    return <div>welcom,react-app hot</div>
}
const App = hot(() =>{//根組件外層經過高階組件包裹
    return <AppCont />
})

ReactDOM.render(
    <App />,
    document.getElementById('app')
)
3.修改package.json文件:增長一個新的script命令dev

"dev": "webpack-dev-server --mode=development --config  webpack.config.js"

執行npm run dev命令,打開http://localhost:9000/,能夠看到頁面內容出現了,在文字後面添加hot後,發現頁面內容更新了,可是沒有刷新!ok到這裏,簡單的項目已經搭建完成,能夠進行開發了。

clipboard.png

3、React的一些概念

這裏經過一個簡單的例子來描述一下react的一些概念。輸入框輸入數字、點擊後面add+數據會改變並求和。

clipboard.png

import React from 'react';

function Item (props){
    const {count,onChange,onAdd} = props;
    return <div>
        <input value={count} onChange={onChange} />
        <span onClick={onAdd}>add+</span>
    </div>
}

class Add extends React.Component{
    constructor(){
        super();
        this.state = {
            addList : [0,0,0,0]
        }
        this.onChange = this.onChange.bind(this)
        this.onAdd = this.onAdd.bind(this)
    }
    onChange(e,index){
        this.state.addList.splice(index,1,e.target.value)
        this.setState({
            addList:this.state.addList
        })
    }
    onAdd(e){
        this.state.addList.splice(e,1,Number(this.state.addList[e]) + 1 )
        this.setState({
            addList:this.state.addList
        })
    }
    render (){
        const { addList } = this.state;
        return <div>
            {
                addList.map((item,index)=>{
                   return <Item key={index} count={item} onChange={(e)=>{this.onChange(e,index)}} onAdd={()=>{this.onAdd(index)}}  />
                })

            }
            <div>{addList[0]*1 + addList[1]*1 + addList[2]*1 + addList[3]*1}</div>
        </div>
    }
}

export default Add;

麻雀雖小...用來入門也是夠用了!

1.react組件的建立的3種方式

1.React.createClass
經常使用的是class繼承和無狀態函數的方式,還有一種React.createClass過於繁瑣並且會致使沒必要要的性能開銷因此基本上算是被廢棄了。
2.無狀態組件
直接經過函數聲明編寫,通常做爲純展現組件使用,內部沒有state(能夠經過hooks使用),不會實例化,也就不須要分配多餘的內存,從而性能獲得必定的提高。可是內部沒法使用this,也沒有生命週期
3.React.Component
React目前極爲推寵的建立有狀態組件的方式。內部可使用this和生命週期。可使用state。
React.Component建立的組件,其成員函數不會自動綁定this,須要手動綁,不然this不能獲取當前組件實例對象。綁定方式有3種:能夠在構造函數中完成綁定(constructor推薦),也能夠在調用時使用method.bind(this)來完成綁定,還可使用arrow function來綁定。

2.參數傳遞

通常就是父向子傳遞參數經過props傳遞。
子向父傳遞則要經過父組件傳遞的函數來傳遞。
跨組件傳遞後面會提到使用redux想怎麼傳就怎麼傳。

3.受控組件/非受控組件

表單元素除了收到state控制,還會受到用戶輸入控制。有兩個來源,使得內容不可預測。React使用state 成爲「惟一數據源」。渲染表單的 React 組件還控制着用戶輸入過程當中表單發生的操做。被 React 以這種方式控制取值的表單輸入元素就叫作「受控組件」。
能夠看到要給受控組件提供onChange函數來保證state數據的統一性。

3、引入redux

上面看到了父子組件間的傳參還很方便的,可是若是要在Add組件外(也就是跨組件傳遞)使用求和後的數據怎麼辦?
固然,可使用增長中間組件、context等方法,這裏講一下大型項目經常使用的redux狀態管理,redux自己的原理也很簡單。
1.提供createStore建立store庫,將數據都存放在一塊兒,單向數據流。
2.數據只讀,修改數據只能經過dispatch方法,傳遞action對象(必須含有type屬性)在純函數reduce裏修改,每一次都是拷貝一個新的數據,不會修改原來的數據源,保證了數據的來源可溯性。

修改app.js
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux'; //添加redux依賴後,引入建立store的方法createStore
import {Provider} from 'react-redux';//添加react-redux依賴,引入改節組件
import reduce from './model/reduce';//createStore方法第一個參數是reduce
import {hot} from 'react-hot-loader/root';
import AddItem from './component/addItem';


const store = createStore(reduce)

function AppCont(){
    return <Provider store={store}><AddItem /></Provider>//根組件外面包裹高階組件
}
const App = hot(() =>{
    return <AppCont />
})

ReactDOM.render(
    <App />,
    document.getElementById('app')
)

新建reduce文件,處理reduce函數。因爲後面項目龐大後,reduce函都寫在一塊兒確定太臃腫,redux提供了拆分的方法,能夠經過業務進行拆分,最後經過combineReducers來合併。

import {combineReducers} from 'redux';
const initState = {
    addList:[0,0,0,0]
}

function addSum(state=initState,action){
    switch (action.type){
        case 'add' : 
            return {
                ...state,
                addList:[...action.payload]
            }
        default : return {...state}
    }
    
}
export default combineReducers({
    addSum
})

addItem文件須要作一些修改,將原來寫在state的數據,經過store來存取。修改方法跟下面新增長Sum組件同樣:經過connect函數來鏈接組件和store。

import React from 'react';
import {connect} from 'react-redux';
function Sum(props){
    const {addList} = props
    return <div>{
        Number(addList[0]) + Number(addList[1]) + Number(addList[2]) + Number(addList[3])
    
    }</div>
}
export default connect(data=>data.addSum)(Sum);

4、引入react-router

react單頁面模式下,路由顯得很重要了,react將router單獨剔除,造成一個單獨的組件,使用起來更加的方便,代碼的耦合程度也下降很多。
提供HashRouter、BrowserRouter兩種根路由方式,重要的區別是HashRouter是經過hash路徑訪問,在瀏覽器直接輸入地址能夠訪問,BrowserRouter經過h5的history api訪問,輸入地址時會像服務器查詢,由於是單頁面,沒有歷史因此查詢不到,不過經過咱們使用webpack的devserver配置historyApiFallback: true,能夠訪問,服務端渲染也能夠訪問。

npm i react-router-dom --save
 <Router>
    <Route path="/" component={AddItem}></Route>
    <Link to="/tab1/1">tab1</Link>
    <Link to="/tab2/2">tab2</Link>
    <Link to="/tab3/3">tab3</Link>
    <Route path="/tab1/:num" component={PageItem}></Route>
    <Route path="/tab2/:num" component={PageItem}></Route>
    <Route path="/tab3/:num" component={PageItem}></Route>
 </Router>

OK,到這裏一個簡單的react開發框架搭建完畢,看到這裏應該入門了吧。

5、引入反作用處理saga

咱們說reduce要寫成純函數,寫成沒有反作用的函數,那麼有反作用的事件怎麼辦?最多見的就是接口的調用。不要慌!redux提供了不少中間件供咱們處理反作用函數,也能夠本身寫,原理就是在dispath(action)前面進行反作用的操做,拿到數據後再進行dispatch。下面引入一個比較好用的處理接口的中間件saga:

給store添加第二個參數:
import { watchIncrementAsync } from './saga/index'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(addSum, applyMiddleware(sagaMiddleware));

saga/index.js
import { put, takeEvery,delay } from 'redux-saga/effects'

export function* incrementAsync() {
  console.log(delay)
   yield delay(2000)
   yield put({ type: 'add',payload:[0,0,0,0] })
}

export function* watchIncrementAsync() {
  yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}

須要注意的是,引入redux-saga依賴,由於saga是基於generator語法編寫的異步處理方案,因此須要對babel進行配置,npm i @babel/plugin-transform-runtime --save引入runtime依賴。並作如下配置修改:

options:{
    "plugins": ["@babel/plugin-transform-runtime"]
}

對store等文件作了一些修改,完整代碼,放到git上了代碼地址

6、提交git

以前都是先在git端新建項目,而後clone到本地在開發,此次是直接在本地建立的項目,而後在提交的,中間除了一點小問題,就當在這記錄一下吧。
1.githab新建倉庫,獲取地址。

clipboard.png

clipboard.png

2.打開終端,進入項目目錄下,git init //初始化

3.git add * //添加全部更改

4.git commit -m "提交備註"

5.git remote add origin https://github.com/wyczmy/rea...
//連接遠程倉庫

6.git pull --rebase origin master //遠程若是建立時有README.md文件,須要執行

7.git push -u origin master //推送到遠程 -f 強制推送

若有不妥,歡迎指正!
相關文章
相關標籤/搜索