純手工構(gòu)建React+Redux+Webpack腳手架

React作為一個前端核心庫,建立了良好的JS庫生態(tài),出現(xiàn)了大量的第三方JS庫用來支持React構(gòu)建適合自己項目的框架。這篇內(nèi)容,我們將使用redux與wepack來構(gòu)建一個MVVM模式的React腳手架

開發(fā)環(huán)境

  • macOS
  • node
  • npm
  • atom
    注:如連接無法打開請使用代理

構(gòu)建好的目錄

react_cli
├── build 
├── node_modules
├── src
├──├──assets
├──├──components
├──├──config
├──├──containers
├──├──redux
├──├──├──actions
├──├──├──configureStore
├──├──├──constants
├──├──├──reducers
├──├──index.jsx
├── test
├── .babelrc
├── package.json
├── webpack.config.js

npm初始化package.json

? mkdir react-cli       #新建項目文件夾
? cd react-cli          #進(jìn)入
? npm init  #初始化package.json

name: (react_cli) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: Asir
license: (ISC) 

根據(jù)提示輸入內(nèi)容,也可以直接回車,然后在package.json文件中修改

安裝React,基礎(chǔ)框架

? npm install --save react react-dom

├── react@15.4.2
├── react-dom@15.4.2

安裝webpack和webpack-dev-server

? npm install --save webpack webpack-dev-server

├── webpack@2.3.2 #用于將代碼整體打包壓縮輸出的工具
├── webpack-dev-server@2.4.2 #用于開發(fā)模式代碼調(diào)試與熱更新

安裝配置webpack用到的庫

  • babel相關(guān)的庫,用來打包編譯轉(zhuǎn)換為目前主流瀏覽器支持的JS語法
? npm install --save-dev babel babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-3 
├── babel@6.23.0 
├── babel-loader@6.4.1 
├── babel-preset-es2015@6.24.0  #打包編譯ES6轉(zhuǎn)ES5的庫
├── babel-preset-react@6.23.0   #打包編譯jsx語法
├── babel-preset-stage-3@6.22.0 #打包編譯async|await語法

更多babel相關(guān)請查閱官方文檔、中文網(wǎng)

  • style相關(guān)庫,用來打包c(diǎn)ss,編譯less、sass
? npm install --save-dev  style-loader css-loader less-loader sass-loader less node-sass 
├── style-loader@0.16.1 
├── css-loader@0.27.3 
├── less-loader@4.0.2 
├── sass-loader@6.0.3 
├── less@2.7.2
├── node-sass@4.5.1
  • path 用于拼接路徑
? npm install --save-dev path 
├── path@0.12.7
  • html-webpack-plugin 模板插件用于導(dǎo)出html時,可以使用模板
? npm install --save-dev html-webpack-plugin 
├── html-webpack-plugin@2.28.0

創(chuàng)建并配置webpack.config.js

? atom webpack.config.js

var path = require('path');
var webpack = require('webpack');
var HtmlwebpackPlugin = require('html-webpack-plugin');

var ROOT_PATH = path.resolve(__dirname);
var APP_PATH = path.resolve(ROOT_PATH, 'src');
var BUILD_PATH = path.resolve(ROOT_PATH, 'build');

module.exports= {
  entry:  path.resolve(APP_PATH, 'index.jsx'),
  output: {
    path: BUILD_PATH,
    filename: 'bundle.js'
  },
  //babel重要的loader在這里
  module: {
    rules: [
      {
        test: /\.(less|scss|css)$/,
        loaders: ['css-loader', 'sass-loader', 'less-loader']
      },
      {
        test: /\.jsx?$/,
        loader: "babel-loader",
        include: APP_PATH,
      }
    ]
  },

  devtool: 'eval-source-map', //開發(fā)環(huán)境

  devServer: {
   compress: true, // 啟用Gzip壓縮
   historyApiFallback: true, // 為404頁啟用多個路徑
   hot: true, // 模塊熱更新,配置HotModuleReplacementPlugin
   https: false, // 適用于ssl安全證書網(wǎng)站
   noInfo: true, // 只在熱加載錯誤和警告
   // ...
 },

 plugins: [
   new HtmlwebpackPlugin({
    title: '記賬本',
    template: 'build/template/index.html'
  })
 ],
}

更多webpack配置查看http://webpack.github.io/

創(chuàng)建.babelrc,用于babel調(diào)用的插件配置

? atom .babelrc

{
  "presets": ["es2015","react", "stage-3"]
}

更多babel配置查看https://babeljs.io/

安裝Redux及相關(guān)組件

npm install --save react-redux
npm install --save redux redux-thunk redux-logger 
npm install --save redux-immutablejs immutable

關(guān)于immutable的使用請參考http://www.itdecent.cn/p/dec712858b27

定義Action`s type

在目錄src/redux/constants下新建index.js

export const EXAMPLE = 'EXAMPLE'

定義reducer

在目錄src/redux/reducers下新建index.js

import { combineReducers } from 'redux'
import { createReducer } from 'redux-immutablejs'
import { fromJS } from 'immutable'
import {
  EXAMPLE
} from '../constants'

const example = createReducer(fromJS({
  title: "項目構(gòu)建成功"
}),{
  [EXAMPLE]: (state, action) => {
    return state.merge({
          title: action.payload.title
    })
  }
})

const rootReducer = combineReducers({
  example
})

export default rootReducer

生成store對象用于管理react中的state

在目錄src/redux/configureStore下新建index.js

? cd src/redux/configureStore && atom index.js 

index.js內(nèi)容如下

import {
  createStore,
  applyMiddleware
} from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';
import createLogger from 'redux-logger';

const logger = createLogger({
  level: 'info',
  logger: console,
  collapsed: true
})
const createStoreWithMiddleware = process.env.NODE_ENV === 'development' ? applyMiddleware(
  thunk, logger
)(createStore) : applyMiddleware(
  thunk
)(createStore)

export default configureStore(initialState) {
  const store = createStoreWithMiddleware(reducer, initialState)
  return store
}

代碼中使用了redux中的createStore方法用于生成store對象和applyMiddleware方法用于redux中間件,中間件使用了redux-thunk用于actions擴(kuò)展,也就是上一節(jié)我們留下的一個疑問。actions中除了可以寫包含type屬性的對象外,使用thunk中間件后可以在actions寫業(yè)務(wù)數(shù)據(jù)邏輯,

編輯actions

在src/redux/actions目錄下新建example.js

import {
  EXAMPLE
} from '../constants'

function example(val){
  return {
    type: EXAMPLE,
    payload: {
      title: val
    }
  }
}

export function changeTitle(val){
  return (dispatch, getState) => {
    dispatch(example(val))
  }
}

redux框架的設(shè)計原理參考http://www.itdecent.cn/p/ec820d8581cd

安裝react-router

基礎(chǔ)路由庫

npm install --save react-router

使用文檔:https://github.com/ReactTraining/react-router

安裝react-router-redux

npm install --save react-router-redux

使用文檔:https://github.com/reactjs/react-router-redux

Redux 配合 react-router 使用

history + store (redux) → react-router-redux → enhanced history → react-router

src/containers/rootRoutes.jsx

import React,{Component} from 'react'
import {Provider} from 'react-redux'
import { Router, Route, browserHistory} from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'

import Example from './example'
export default class Root extends Component{
    render(){
        const {store} = this.props
        const history = syncHistoryWithStore(browserHistory, store)
        return (
            <Provider store={store}>
              <div>
                <Router history={browserHistory}>
                        <Route path="/" component={Example}/>
                </Router>
              </div>
            </Provider>
        )
    }
}

configureStore/index.js

import {
  createStore,
  applyMiddleware
} from 'redux';
import thunk from 'redux-thunk';
import reducer from '../reducers';
import { createLogger } from 'redux-logger';
import { routerMiddleware} from 'react-router-redux'
import { browserHistory} from 'react-router'

const middleware = routerMiddleware(browserHistory)
const logger = createLogger({
  level: 'info',
  logger: console,
  collapsed: true
})
const createStoreWithMiddleware = process.env.NODE_ENV === 'production' ? applyMiddleware(
  middleware, thunk
)(createStore) : applyMiddleware(
  middleware, thunk, logger
)(createStore)

const configureStore = (initialState) => {
  const store = createStoreWithMiddleware(reducer, initialState)
  return store
}
export default configureStore

更多react-router使用方法查看https://github.com/ReactTraining/react-router

最后我們來寫一個使用了store數(shù)據(jù)的React組件

src/containers/example.jsx

class Example extends Component {
  constructor(props) {
    super(props);
  }
  showModal (e) {
    e.preventDefault(); // 修復(fù) Android 上點(diǎn)擊穿透
    this.props.actions.changeTitle("React世界歡迎您")
  }
  render() {
    return (
      <div>
         <input readOnly onClick={this.showModal.bind(this)} value={this.props.example.title} style={{width:'100%', height:'50px', border:0, background:"#CCCCCC"}}/>
      </div>
    )
  }
}


function mapStateToProps(state) {
  return {
    example: state.example.toJS()
  }
}
function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(Actions, dispatch)
  }
}
export default connect(
  mapStateToProps, mapDispatchToProps
)(Example)

在package.json中添加

"scripts": {
    "start": "webpack-dev-server --hot --inline --port 8080 --host 192.168.31.182",  #添加項
    "test": "echo \"Error: no test specified\" && exit 1"
  },

現(xiàn)在啟動webpack-dev-server, 可以在瀏覽器中看到,輸出的頁面!

? npm start

結(jié)束

至此,我們使用webpack, babel, redux, react-router及相關(guān)庫構(gòu)建了基于React的腳手架。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容