node, npm, react, redux, es6, webpackcss
ECMAScript 6入門html
Redux 入門教程 redux middleware 詳解 Redux研究react
React 入門實例教程webpack
NPM 使用介紹es6
以前有寫過 webpack+react+es6開發模式 ,文章裏介紹了一些簡單的配置,歡迎訪問。後續文章請參考 webpack+react+redux+es6開發模式---續。github
1.能夠npm init, 建立一個新的工程。建立package.json文件,定義須要的dependency,scripts,version等等。web
2.新增webpack.config.json文件,定義插件項配置,頁面入口文件,文件輸出,加載器的配置,其餘解決方案配置等。下面提供了簡單配置的demo,更詳細的講解,請參考 webpack 入門指南: w2bc.com/Article/50764。ajax
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' } } };
3.編寫若是文件 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') );
4.建立工程的各個模塊
|--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).在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;
(2).註冊Home頁面的路由,對應routes/index.js加入以下代碼。
<Route path="/" component={ModuleRouters}> <IndexRoute component={Home} /> </Route>
(3).啓動工程, npm run dev, 瀏覽器中輸入 http://localhost:8000/demo1,便可預覽咱們的Home頁面。
(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);
(2).註冊路由,在routes/index.js中加入以下代碼。
<Route path="/showSelfMsg" component={ShowSelfMsg} />
(3).在Home頁面中點擊 ‘頁面渲染展現信息’,便可進入這個頁面。
(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';
FrontAndRearInteractive.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);
actions/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!!!', '獲取數據成功')); }; }
reducers/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;
(2).路由註冊,在routes/index.js增長以下代碼。
<Route path="/frontAndRearInteractive" component={FrontAndRearInteractive} />
(3).在Home頁面中點擊 ‘模擬先後臺交互’,便可進入頁面。
(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);
Childpage.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);
(2).註冊路由,在routes/index.js中加入以下代碼。
<Route path="/pageExchange" component={PageExchange} /> <Route path="/childDemoPage(/:parentPageMsg)" component={ChildPage}/>
(3).在Home頁面中點擊‘頁面切換’,便可進入頁面。
(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> <ExtendPage childState={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);
ExtendPage.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);
(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,參考:
5.module.exprots, export, export default區別:
export default variation import variation from 'js file' export variation import {variation} from 'js file' module.exports=variation import 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
驗證一下 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/