webpack+react+redux+es6开发模式
一、預備知識
node, npm, react, redux, es6, webpack
二、學習資源
ECMAScript 6入門
React和Redux的連接react-redux
Redux 入門教程? ?redux middleware 詳解? ?Redux研究
React 入門實例教程
webpack學習demo
NPM 使用介紹
三、工程搭建
之前有寫過?webpack+react+es6開發模式?,文章里介紹了一些簡單的配置,歡迎訪問。后續文章請參考?webpack+react+redux+es6開發模式---續。
1.可以npm init, 創建一個新的工程。創建package.json文件,定義需要的dependency,scripts,version等等。
2.新增webpack.config.json文件,定義插件項配置,頁面入口文件,文件輸出,加載器的配置,其他解決方案配置等。下面提供了簡單配置的demo,更詳細的講解,請參考??webpack 入門指南: w2bc.com/Article/50764。
var webpack = require('webpack'); var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');module.exports = {//插件項 plugins: [commonsPlugin],//頁面入口文件配置 entry: {bundle: './index.js'},//入口文件輸出配置 output: {path: './build/',filename: '[name].js'},module: {//加載器配置 loaders: [{ test: /\.css$/, loader: 'style-loader!css-loader' },{ test: /\.js$/, loader: 'jsx-loader?harmony' },{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}]},//其它解決方案配置 resolve: {root: '******', //絕對路徑extensions: ['', '.js', '.json', '.scss'],alias: {AppStore : 'js/stores/AppStores.js',ActionType : 'js/actions/ActionType.js',AppAction : 'js/actions/AppAction.js'}} }; View Code3.編寫如果文件 main.js。這里創建了provider,store,history,router。實現頁面的路由以及react組件以及組件間的state交互。關于react-redux內容請參考?react-redux概念理解,關于react-router內容請參考?React Router 使用教程?。
var React = require('react'); var ReactDOM = require('react-dom'); var { Provider } = require('react-redux'); import { Router } from 'react-router'; import routes from 'routes'; import { createHashHistory, useBasename } from 'history'; import { syncReduxAndRouter } from 'redux-simple-router'; import { routeReducer } from 'redux-simple-router'; var configureStore = require('./stores/configureStore');// Run our app under the /base URL. const history = useBasename(createHashHistory)({basename: '/', }); const store = configureStore(window.__INITIAL_STATE__);syncReduxAndRouter(history, store);ReactDOM.render (<Provider store={store}><Router history={history}>{routes}</Router></Provider>,document.getElementById('root') ); View Code4.創建工程的各個模塊
|--demo1|--src //源碼|--actions // 存放當前觸發Redux的動作行為|--components // 存放工程內部的公共組件|--modules // 存放工程各模塊代碼|--constants ? // action動作常量|--reducers // 存放reducer函數,用來修改store狀態|--routes // 放置頁面路由 react router|--stores // 放置stores配置文件|--main.js // 入口js|--index.html // 工程入口文件html|--node_modules // 存放依賴的第三方模塊庫,使用命令 npm install|--build //打包文件存放的目錄|--webpack.config.js|--package.json四、功能開發
1.做一個簡單的Home頁面
(1).在modules文件夾新建Home.js,?使用antd 的Menu組件, 展示我們要演示的功能。
import React from 'react'; import 'less/home.less'; import { Scrollbars } from 'react-custom-scrollbars'; import {Menu} from 'antd';//首頁 export class Home extends React.Component{constructor(props) {super(props);this.changeRoute = this.changeRoute.bind(this);}componentDidMount() {}changeRoute(e) {this.context.history.pushState({}, e.key);}render() {return (<div className='home'><Scrollbars style={{ height: 600 }}><Menu className='menu' onClick={this.changeRoute}><Menu.Item key='showSelfMsg'>頁面渲染展示信息</Menu.Item><Menu.Item key='frontAndRearInteractive'>模擬前后臺交互</Menu.Item><Menu.Item key='pageExchange'>頁面切換</Menu.Item><Menu.Item key='extend'>子組件擴展</Menu.Item></Menu></Scrollbars></div> );} } Home.contextTypes = {history: React.PropTypes.object.isRequired, }; module.exports = Home; View Code(2).注冊Home頁面的路由,對應routes/index.js加入如下代碼。
<Route path="/" component={ModuleRouters}><IndexRoute component={Home} /> </Route>(3).啟動工程, npm run dev, 瀏覽器中輸入 http://localhost:8000/demo1,即可預覽我們的Home頁面。
?
2.單頁面渲染,完成數據的展示和隱藏
(1).在component目錄下新建ShowSelfMsg.js, 通過改變state狀態,重新渲染頁面.
import React from 'react'; import {connect} from 'react-redux'; import {Button} from 'antd'; import 'less/common.less'; var mapStateToProps = function(state){};class ShowSelfMsg extends React.Component{constructor(props){super(props);this.state = {showContent: false};this.showContent = this.showContent.bind(this);}showContent() {this.setState({showContent: !this.state.showContent});}componentDidMount() {const { dispatch} = this.props;//加載該頁面的數據 }componentWillReceiveProps(nextProps) {}render() {let showContent = this.state.showContent;return (<div className='main'><div className='content'><Button type="ghost" onClick={this.showContent}>{!this.state.showContent ? '單擊顯示內容' : '單擊隱藏內容'}</Button> {showContent ? (<div><span>大家好,我是hjzgg</span></div>) : (null)}<div className='back'><Button type="ghost" onClick={()=>this.context.history.pushState({}, '/')}>返回</Button></div></div></div> );} }ShowSelfMsg.contextTypes = {history: React.PropTypes.object.isRequired, }; module.exports = connect(mapStateToProps)(ShowSelfMsg); View Code(2).注冊路由,在routes/index.js中加入如下代碼。
<Route path="/showSelfMsg" component={ShowSelfMsg} />(3).在Home頁面中點擊 ‘頁面渲染展示信息’,即可進入這個頁面。
?
3.模擬前后臺交互
(1).代碼編寫如下。
(I).在constants新建ActoinTypesjs,定動作類型;
(II).在actions目錄中新建simulationRquest.js, 定義要分發的動作;
(III)在reducers目錄新建simulationRquest.js,存放reducer函數,用來修改store狀態,然后將該函數放入到reducers/index.js中的combineReducers函數中,最終會合并成一個新的reducer;
(IV)components目錄中新建FrontAndRearInteractive.js, dispatch 自定義的動作,實現模擬前后臺交互功能。
ActionType.js
export const SIMULATION_REQUEST_SUCCESS = 'SIMULATION_REQUEST_SUCCESS'; export const SIMULATION_REQUEST_FAIL = 'SIMULATION_REQUEST_FAIL'; export const INIT_EXTEND_DATA_SUCCESS = 'INIT_EXTEND_DATA_SUCCESS'; export const INIT_EXTEND_DATA_FAIL = 'INIT_EXTEND_DATA_FAIL'; export const SAVE_EXTEND_DATA_SUCCESS = 'SAVE_EXTEND_DATA_SUCCESS'; View CodeFrontAndRearInteractive.js
import React from 'react'; import {connect} from 'react-redux'; import {Button} from 'antd'; import {simulationRquestAction} from 'actions/simulationRequest'; var mapStateToProps = function(state){return {myRequest: state.myRequest,} };class FrontAndRearInteractive extends React.Component{constructor(props){super(props);this.state = {showContent: false};this.simulationRequest = this.simulationRequest.bind(this);}simulationRequest() {const {dispatch} = this.props;console.log('props>>>dispath:' + dispatch);dispatch(simulationRquestAction());}componentDidMount() {const { dispatch} = this.props;//加載該頁面的數據 }componentWillReceiveProps(nextProps) {const { myRequest } = nextProps;if(myRequest.code && myRequest.msg)alert('請求結果:code=' + myRequest.code + ', msg=' + myRequest.msg);}render() {const { myRequest } = this.props;return (<div className='main'><div className='content'><Button type="ghost" onClick={this.simulationRequest}>模擬請求</Button> {myRequest && myRequest.data ? (<div><span>{myRequest.data}</span></div>) : (null)}<div className='back'><Button type="ghost" onClick={()=>this.context.history.pushState({}, '/')}>返回</Button></div></div></div> );} } FrontAndRearInteractive.contextTypes = {history: React.PropTypes.object.isRequired, }; module.exports = connect(mapStateToProps)(FrontAndRearInteractive); View Codeactions/simulationRquest.js
import {ajax} from 'utils/ajax'; import url from 'utils/Url'; import {SIMULATION_REQUEST_SUCCESS, SIMULATION_REQUEST_FAIL,} from 'constants/ActionTypes';function simulationRquestSuccess(data, msg){return {type: SIMULATION_REQUEST_SUCCESS,data,msg,} }function simulationRquestFail(msg){return {type: SIMULATION_REQUEST_FAIL,msg,} }export function simulationRquestAction(args){return function (dispatch) {console.log('actions>>>dispath:' + dispatch);/*//真是請求ajax({method : 'GET',url : url.QUERY_ALL_USER,query : {'args': args},type : 'json',success : function(data) {return dispatch(simulationRquestSuccess(data));},error : function(data) {return dispatch(simulationRquestFail('request fail'));} });*///假設請求成功return dispatch(simulationRquestSuccess('我是后臺返回數據:hjzgg!!!', '獲取數據成功'));}; } View Codereducers/simulationRquest.js
import {SIMULATION_REQUEST_SUCCESS, SIMULATION_REQUEST_FAIL,} from 'constants/ActionTypes'; import assign from 'lodash/assign';function myRequest(state = {data: null,msg: null,code: null,}, action) {console.log('reducer action屬性>>>>>' + JSON.stringify(action));switch(action.type) {case SIMULATION_REQUEST_SUCCESS:return assign({}, state, {msg: action.msg,data: action.data,code: 'success',});case SIMULATION_REQUEST_FAIL:return assign({}, state, {msg: action.msg,data: null,code: 'fail',});default:return state;}}module.exports = myRequest; View Code(2).路由注冊,在routes/index.js增加如下代碼。
<Route path="/frontAndRearInteractive" component={FrontAndRearInteractive} />(3).在Home頁面中點擊 ‘模擬前后臺交互’,即可進入頁面。
4.頁面切換
(1).在components目錄新建PageExchange.js 和 Childpage.js,分別為父頁面和子頁面。注意,這里父頁面的變量信息 是通過路由的方式傳遞過去的,當然也可以通過state方式傳遞過去。
PageExchange.js
import React from 'react'; import {connect} from 'react-redux'; import {Button} from 'antd'; import 'less/common.less'; var mapStateToProps = function(state){};class PageExchange extends React.Component{constructor(props){super(props);this.state = {showContent: false};this.gotoChildPage = this.gotoChildPage.bind(this);}gotoChildPage() {console.log('this.context.history>>>>>>' + JSON.stringify(this.context.history));this.context.history.pushState({}, 'childDemoPage/' + '我是父頁面信息');}componentDidMount() {const { dispatch} = this.props;//加載該頁面的數據 }componentWillReceiveProps(nextProps) {}render() {let showContent = this.state.showContent;return (<div className='main'><div className='content'><Button type="ghost" onClick={this.gotoChildPage}>進入子頁面</Button><div className='back'><Button type="ghost" onClick={()=>this.context.history.pushState({}, '/')}>返回</Button></div></div></div> );} }PageExchange.contextTypes = {history: React.PropTypes.object.isRequired, }; module.exports = connect(mapStateToProps)(PageExchange); View CodeChildpage.js
import React from 'react'; import {connect} from 'react-redux'; import {Button} from 'antd'; import 'less/common.less'; var mapStateToProps = function(state){return {} };class ChildPage extends React.Component{constructor(props){super(props);this.returnParentPage = this.returnParentPage.bind(this);}componentDidMount() {const { dispatch} = this.props;//加載該頁面的數據 }componentWillReceiveProps(nextProps) {}returnParentPage() {this.context.history.pushState(null, 'pageExchange');}render() {const parentPageMsg = this.props.params.parentPageMsg;return (<div className='main'><div className='content'><Button type="ghost" onClick={this.returnParentPage}>返回父頁面</Button> {parentPageMsg ? (<div><span>{parentPageMsg}</span></div>) : (null)}</div></div> );} }ChildPage.contextTypes = {history: React.PropTypes.object.isRequired, }; module.exports = connect(mapStateToProps)(ChildPage); View Code(2).注冊路由,在routes/index.js中加入如下代碼。
<Route path="/pageExchange" component={PageExchange} /> <Route path="/childDemoPage(/:parentPageMsg)" component={ChildPage}/>(3).在Home頁面中點擊‘頁面切換’,即可進入頁面。
5.自定義擴展組件
? (1).先說一下應用場景:多個頁面可能需要類似的擴展功能,通過自定義擴展組件,完成對信息的加載。主頁面信息保存時,通知擴展組件要保存信息了,擴展組件將最新修改的信息告知主頁面,主頁面獲取到全部信息后,一起將數據傳給后臺,完成主頁面信息和擴展信息的保存。
(2).在components目錄下新建Page.js和ExtendPage.js,分別為主頁面和自定義擴展組件。
Page.js
import React from 'react'; import {connect} from 'react-redux'; import {Button, Input, Form} from 'antd'; import ExtendPage from 'components/ExtendPage'; import 'less/common.less'; const FormItem = Form.Item; var mapStateToProps = function(state){return {extendStore: state.extendStore} };class Page extends React.Component{constructor(props){super(props);this.state = {childState: false,}this.handleSubmit = this.handleSubmit.bind(this);this.onSaveExtendPage = this.onSaveExtendPage.bind(this);}componentDidMount() {const { dispatch} = this.props;//加載該頁面的數據 }componentWillReceiveProps(nextProps) {}//通知擴展組件,準備保存了 onSaveExtendPage() {if(this.state.childState) {this.setState({childState: false,});}}save(values) {//打印父級和子級文本 alert(JSON.stringify(values));}handleSubmit() {var self = this;this.props.form.validateFields((err, values) => {if (!err) {//表單符合標準//values 為當前父頁面的數據,接下來獲取子頁面的數據this.setState({childState: true}, function() {const { extendStore } = self.props;values.extendData = extendStore && extendStore.data || extendStore;self.save(values);});}});}render() {const { getFieldProps } = this.props.form;const inputProps = getFieldProps('inputText', {initialValue: '',rules: [{required: true, message: 'the input is required' },],validateTrigger: "onBlur"});return (<div style={{marginTop: 50, width: 600, marginLeft: 'auto', marginRight: 'auto'}}><Form onSubmit={this.handleSubmit}><FormItem {...{labelCol: { span: 6 }, wrapperCol: { span: 14 }}} label="父級文本: "><Input {...inputProps} id='inputText' type='text'/></FormItem><FormItem wrapperCol={{ span: 12, offset: 6 }}><Button type="primary" htmlType="submit">提交</Button></FormItem></Form><ExtendPagechildState={this.state.childState}callBack={this.onSaveExtendPage}/><div style={{float: 'right'}}><Button type="ghost" onClick={()=>this.context.history.pushState({}, '/')}>返回</Button></div></div> );} } Page.contextTypes = {history: React.PropTypes.object.isRequired, }; Page = Form.create()(Page); module.exports = connect(mapStateToProps)(Page); View CodeExtendPage.js
import React from 'react'; import {connect} from 'react-redux'; import {Button, Form, Input, message} from 'antd'; const FormItem = Form.Item; import {initExtendData, saveExtendDataAction} from 'actions/extendPage'; var mapStateToProps = function(state){return {extendStore: state.extendStore} };class ExtendPage extends React.Component{constructor(props){super(props);this.state = {}this.saveExtendData = this.saveExtendData.bind(this);this.checkText = this.checkText.bind(this);}checkText(rule, value, callBack) {if(/\s+/.test(value)) {callBack("不能有空白字符");} else {callBack();}}saveExtendData() {this.props.callBack();//保存成功后,更改父頁面的childState的狀態this.props.form.validateFields((err, values) => {if (!err) {//表單符合標準console.log('save ExtendPage values: ' + JSON.stringify(values));const {dispatch} = this.props;dispatch(saveExtendDataAction(values));}});}componentDidMount() {const { dispatch} = this.props;//初始化擴展頁的數據 dispatch(initExtendData());}componentWillReceiveProps(nextProps) {const { extendStore, childState } = nextProps;if(extendStore && extendStore.msg) {message.info(extendStore.msg, 5);extendStore.msg = null;}if(childState) {//父頁面 改變 子頁面的狀態this.saveExtendData();}}render() {const { getFieldProps } = this.props.form;const { extendStore } = this.props;const inputValue = extendStore && extendStore.data && extendStore.data.extendInputText || null;const inputProps = getFieldProps('extendInputText', {initialValue: inputValue,rules: [{required: true, message: 'the input is required' },{validator: this.checkText}],validateTrigger: "onBlur"});return (<div><Form><FormItem {...{labelCol: { span: 6 }, wrapperCol: { span: 14 }}} label="擴展本文: "><Input {...inputProps} type="text" id="extendInputText"/></FormItem></Form></div> );} } ExtendPage = Form.create()(ExtendPage); module.exports = connect(mapStateToProps)(ExtendPage); View Code(3).說一下組件的擴展機制
(I).擴展組件自身會維護更新自己state狀態,在觸發擴展組件保存時,擴展組件將自身數據通過dispatch進行分發,最后通過對應的reducer(這個reducer會通過combineReducers函數合并成一個新的reducer)進行處理,根據邏輯生成新的state。
>>定義動作類型
? >>分發動作
>>reducer處理動作,返回新的state
>>自定義的reducer函數通過combineReducers函數進行合并
? (II).父級組件如何獲取擴展組件的狀態?
也就是store中的狀態樹變化的時候,組件可以通過?mapStateToProps 函數從狀態樹中獲取最新的state。
(III).父級組件如何通知擴展組件 準備保存數據了?
>>擴展組件接收父級組件兩個參數:childState, 通知擴展組件狀態發生變化; callBack, 修改childState狀態,擴張組件通知父級組件更新完成。
?
>>父級組件保存數據時,首先獲取到自己的數據,然后通過setState()方法改變childState的值,通知擴展組件。最后通過setState方法傳入的回調函數(該函數在組件更新完成之后調用)獲取到擴展組件的最新state。
>>擴展組件接收到父級組件的通知,刷新store中的state。這樣父級組件和擴展組件自身都可以通過mapStateToProps方法獲取到最新的state。
?? (4).注冊路由,在routes/index.js中加入如下代碼。
(5).在Home頁面中點擊‘頁面切換’,即可進入頁面。
?
?
?五、問題解惑
?1.module.filename、__filename、__dirname、process.cwd(): ?http://www.tuicool.com/articles/bQre2a
?2.node.js之path模塊: http://www.jianshu.com/p/fe41ee02efc8
? 3.react-router: http://www.ruanyifeng.com/blog/2016/05/react_router.html?utm_source=tool.lu
? ? ?4.出現如下錯誤:Cannot sync router: route state does not exist. Did you install the routing reducer,參考:
?http://stackoverflow.com/questions/34039619/redux-simple-router-react-router-error-route-state-does-not-exist
5.module.exprots, export, export default區別:
export default variationimport variation from 'js file'export variationimport {variation} from 'js file'module.exports=variationimport variation from 'js file'參考:
http://www.2cto.com/kf/201412/360211.html
http://www.jb51.net/article/33269.htm
http://blog.csdn.net/zhou_xiao_cheng/article/details/52759632
http://blog.csdn.net/zhou_xiao_cheng/article/details/52759632
六、dispath疑問
react-router相關API
react-redux 之 connect 方法詳解
驗證一下 redux store.dispatch ?和 react組件 props中的dispath,的確是一樣的。
dispath:function (action) {return typeof action === 'function' ? action(dispatch, getState) : next(action); }
七、演示地址
http://study.hujunzheng.cn:8000/DEMO_FRONT/
八、完整項目下載
https://github.com/hjzgg/webpack-react-redux
轉載于:https://www.cnblogs.com/hujunzheng/p/6133648.html
總結
以上是生活随笔為你收集整理的webpack+react+redux+es6开发模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 宝马730变速箱会自动学习吗
- 下一篇: 五菱之光6400款面包车有点烟器功能吗