最近公司可能要用react,然而有幾個同事歷來沒用過react,而後領導讓我帶他們學習react, 爲下一個react項目作基礎。
而後隨手寫了幾個demo,幫助他們瞭解一下如何去構建配置項目。
如今分享出來,但願能夠幫助到須要的人。 本demo 中有些目錄雖然沒有用,可是我仍是列了出來,目的是爲了展現一個正規項目的目錄骨架結構。
create-react-app 模板文件我也沒有歸類,等了解以後,能夠本身歸類,加一個樣式的文件夾。css
就目前的大環境而言,在開發react或vue項目的時候,應該沒有幾我的還直接在html直接飲用react的庫文件吧,都是用的前端構建工具webpack + es6 來寫項目。因此直接就推薦facebook官方出品的腳手架create-react-app來作項目。 create-react-app是最適合新手來學習使用的腳手架,所有是簡單的命令。不須要像別的腳手架同樣,先去clone整個項目,而後再安裝依賴。html
安裝create-react-app工具前端
npm install -g create-react-app
生成項目vue
create-react-app react-demo
生成項目以後自動會下載依賴所有,
進入項目以後,啓動項目node
cd react-demo npm start 或者 yarn start
npm 和 yarn 都是包管理器, 均可如下載依賴,具體這裏不作解釋,詳情能夠本身搜索。一個項目最好用一種包管理工具,不要混用,下面都用yarn來下載依賴。不作另外說明。react
項目啓動以後打開3000端口。
項目啓動成功以下圖:webpack
一、安裝依賴git
yarn add react-router-dom --save
二、新建文件、配置路由
src
目錄下 新建 containers
目錄 裏面放置主頁面文件
src
目錄下 新建 components
目錄 裏面放置高複用模板文件
在 containers
中新建 Home.js
es6
Home.js 代碼github
import React, { Component } from 'react'; class Home extends Component { render() { return ( <div> Home </div> ); } } export default Home;
在 containers
中新建 List.js
List.js 代碼
import React, { Component } from 'react'; class List extends Component { render() { return ( <div> List </div> ); } } export default List;
在 containers
中新建 Mine.js
Mine.js 代碼 (二級路由,路由傳參示例)
import React, {Component} from 'react'; import {BrowserRouter as Router, Route, Link} from 'react-router-dom'; import Mine1 from './Mine1' import Mine2 from './Mine2' class Mine extends Component { constructor(props) { super(props); this.state = { param: '' }; } changeParam(event) { this.setState({ param: event.target.value }) } render() { return ( <Router> <div> Mine 傳參示例:<input type="text" placeholder='請輸入要傳遞到mine2的參數' value={this.state.param} onChange={this.changeParam.bind(this)}/> <div className="mine-nav"> {/*編寫導航*/} <ul> <li><Link to={`${this.props.match.url}/mine1`}>Mine1</Link></li> <li><Link to={`${this.props.match.url}/mine2/${this.state.param? this.state.param : "default-param"}`}>Mine2</Link></li> {/*傳參示例,若是沒有參數,傳默認參數*/} </ul> {/*路由匹配*/} <div> <Route exact path={`${this.props.match.url}/mine1`} component={Mine1}/> <Route path={`${this.props.match.url}/mine2/:param`} component={Mine2}/> </div> </div> </div> </Router> ); } } export default Mine;
在 containers
中新建 Mine1.js
Mine1.js 代碼
import React, { Component } from 'react'; class Mine1 extends Component { render() { return ( <div> Mine1 </div> ); } } export default Mine1;
在 containers
中新建 Mine2.js
Mine2.js 代碼 (路由傳參示例)
import React, { Component } from 'react'; class Mine2 extends Component { render() { return ( <div> 2222222222222 傳遞的參數:{this.props.match.params.param} </div> ); } } export default Mine2;
修改入口App.js 文件,修改成以下所示:
import React, { Component } from 'react'; import {BrowserRouter as Router, Route, Link} from 'react-router-dom'; import List from './containers/List.js' import Home from './containers/Home.js' import Mine from './containers/Mine.js' import './App.css' import logo from './logo.svg' class App extends Component { render() { return ( <div className="App"> <div className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2 className='App-title'>Welcome to React Plan</h2> </div> <div className="App-content"> {/*路由配置*/} <Router> <div className="content-box"> {/*編寫導航*/} <ul className="nav"> <li><Link to="/">首頁</Link></li> <li><Link to="/list">列表頁</Link></li> <li><Link to="/mine/mine1">個人頁面二級路由</Link></li> {/*link指向二級路由的默認頁面*/} </ul> {/*路由匹配*/} <div className="content"> <Route exact path="/" component={Home}/> <Route path="/list" component={List}/> <Route path="/mine" component={Mine}/> </div> </div> </Router> </div> </div> ); } } export default App
修改App.css 文件,讓稍微有一點樣式:
.App { text-align: center; } .App-logo { animation: App-logo-spin infinite 20s linear; height: 80px; } .App-header { background-color: #222; height: 150px; padding: 20px; color: white; } .App-title { font-size: 1.5em; } .App-intro { font-size: large; } @keyframes App-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } /*下面是新添加的樣式*/ * { margin: 0; padding: 0; list-style: none; } .content-box{ overflow: hidden; } .nav { padding: 20px; float: left; width: 160px; background-color: #fcc; } .nav li{ line-height: 30px; } .content{ padding: 20px 0 0 200px; background-color: #ccf; height: 110px; } .mine-nav{ background-color: #cfc; margin-top: 20px; height: 70px; } .mine-nav ul{ overflow: hidden; } .mine-nav ul li{ float: left; width: 50%; } .mine-nav>div{ margin-top: 20px; }
大功告成,點一點試試吧。配置完畢,頁面以下圖:
redux具體是什麼,這裏就很少提,想詳細瞭解最好看一下阮一峯老師的博客 Redux 入門教程 來了解什麼是redux, 或者你的項目到底需不須要用redux。這裏只講如何去配置redux.
一、安裝依賴
yarn add redux react-redux --save
二、新建文件, 配置redux
src
目錄下 新建 store
目錄裏面放置 store配置文件
src
目錄下 新建 reducers
目錄裏面放置 規則性文件
src
目錄下 新建 constants
目錄裏面放置 數據
src
目錄下 新建 actions
目錄裏面放置 觸發更新文件
在 store
中新建 configStore.js
store > configStore.js 文件
import { createStore } from 'redux' //引入規則文件 import rootReducer from '../reducers/index.js' export default function configStore(initState){ // 建立store const store = createStore( rootReducer, initState, // 若是安裝了redux插件可按照版本打開下面的註釋 // window.devToolsExtension ? window.devToolsExtension() : undefined // window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : undefined ) return store }
在 reducers
中新建 index.js
在 reducers
中新建 number.js
reducers > index.js 文件
// 規則性文件 import { combineReducers } from 'redux' import number from './number' // 若是有多個規則,則引入多個文件 //import XXXX from './XXXX' // 建立規則 export default combineReducers({ // XXXX, number })
reducers > number.js 文件
import * as actionTypes from '../constants/number.js' import { combineReducers } from 'redux' const text = function (state = actionTypes.TEXT, action){ switch(action.type) { case actionTypes.TEXT : return action.data.text default: return state } } const count = function (state = actionTypes.COUNT, action){ switch(action.type) { case actionTypes.COUNT : return action.data.count default: return state } } export default combineReducers({ text, count })
在 constants
中新建 number.js
constants > number.js 文件
export const COUNT = 100; export const TEXT = 'number大類下的數據';
修改 containers
中的 Home.js
(稍微把home組件改爲使用redux的形式)
containers > Home.js 文件
import React, { Component } from 'react'; import {connect} from 'react-redux'; class Home extends Component { render() { return ( <div> Home <br/> {this.props.text} ==> {this.props.count} </div> ); } } // 若是隻是讀取數據,那麼只須要這一個方法便可。(這裏兩個寫出來只是爲了方便理解) function mapStateToProps(state){ return { count: state.number.count, text: state.number.text } } // 同理,若是隻是設置數據,那麼只須要這一個方法便可。 function mapDispatchToProps(dispatch) { return { } } export default connect( mapStateToProps, mapDispatchToProps )(Home)
修改 src
中的 index.js
(給之前看的本博客的小夥伴們說聲道歉,漏了這一步,如今補上。這一步是給store增長入口)
src > index.js 文件
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; import {Provider} from 'react-redux' import configStore from './store/configStore.js' const store = configStore(); ReactDOM.render( <Provider store={store}> <App/> </Provider>, document.getElementById('root')); registerServiceWorker();
作到這裏,應該已經實現了第一步,能夠讀取數據了。 如圖:
讀取數據實現了,接下來確定要實現設置數據。
在 containers
中的新建 TestRedux.js
containers > TestRedux.js 文件
import React, { Component } from 'react'; import {bindActionCreators} from 'redux' import {connect} from 'react-redux' import * as handlerNumberActions from '../actions/number.js' class TestRedux extends Component { textChange(event){ this.props.handlerNumberActions.changeText({ text: event.target.value }) } numberAdd(){ this.props.handlerNumberActions.addNumber({ count: this.props.count }) } numberSubtract(){ this.props.handlerNumberActions.subtractNumber({ count: this.props.count }) } render() { return ( <div style={{height:'100px',background: '#ffc',padding: '10px'}}> <ul> <li>修改redux數據</li> <li>修改文本: <input type="text" style={{padding: '5px 10px'}} value={this.props.text} onChange={this.textChange.bind(this)} /></li> <li>操做數值: <button style={{padding: '5px 10px'}} onClick={this.numberAdd.bind(this)}>+</button> ------------------- <button style={{padding: '5px 10px'}} onClick={this.numberSubtract.bind(this)}>-</button> </li> </ul> </div> ); } } function mapStateToProps(state){ return { count: state.number.count, text: state.number.text } } function mapDispatchToProps(dispatch) { return { handlerNumberActions: bindActionCreators(handlerNumberActions, dispatch) } } export default connect( mapStateToProps, mapDispatchToProps )(TestRedux)
在 actions
中的新建 number.js
actions > number.js 文件
import * as actionTypes from '../constants/number.js' export function addNumber(data){ data.count ++; return { type: actionTypes.COUNT, data } } export function subtractNumber(data){ data.count --; return { type: actionTypes.COUNT, data } } export function changeText(data){ return { type: actionTypes.TEXT, data } }
修改 src
中的 App.js
(把剛寫好的TestRedux組件添加進去)
src > App.js 文件
import React, { Component } from 'react'; import {BrowserRouter as Router, Route, Link} from 'react-router-dom'; import List from './containers/List.js' import Home from './containers/Home.js' import Mine from './containers/Mine.js' import TestRedux from "./containers/TestRedux" import './App.css' import logo from './logo.svg' class App extends Component { render() { return ( <div className="App"> <div className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2 className='App-title'>Welcome to React Plan</h2> </div> <div className="App-content"> {/*路由配置*/} <Router> <div className="content-box"> {/*編寫導航*/} <ul className="nav"> <li><Link to="/">首頁</Link></li> <li><Link to="/list">列表頁</Link></li> <li><Link to="/mine/mine1">個人頁面二級路由</Link></li> {/*link指向二級路由的默認頁面*/} </ul> {/*路由匹配*/} <div className="content"> <Route exact path="/" component={Home}/> <Route path="/list" component={List}/> <Route path="/mine" component={Mine}/> </div> </div> </Router> </div> <TestRedux /> </div> ); } } export default App
至此,完整的redux就完全的配置完畢了。點一點,或者修改一下文本試試吧。
目錄結構和效果以下圖:
fetch是什麼呢?fetch實際上是 XMLHttpRequest 的替代品,比起 XMLHttpRequest 這種粗糙的東西,fetch顯然看起來精緻多了,最棒的是fetch 是基於 Promise 的,讓咱們擺脫了地獄回調的噩夢。下面咱們就把fetch 簡單封裝一下。方便在react中使用。
一、安裝依賴
yarn add whatwg-fetch es6-promise --save
二、新建文件 封裝 fetch 方法
在 src
目錄下的新建 fetch
目錄, 在 fetch
文件下新建 index.js
封裝 fetch
fetch > index.js 文件
import 'whatwg-fetch' import 'es6-promise' export function get(url) { let result = fetch(url, { credentials: 'include', headers: { 'Access-Control-Allow-Origin': '*', 'Accept': 'application/json, text/plain, */*' }, // 設置容許cors跨域 mode: 'cors' }); return result; } // 將對象拼接成 key1=val1&key2=val2&key3=val3 的字符串形式 function obj2params(obj) { let result = ''; let item; for (item in obj) { result += '&' + item + '=' + encodeURIComponent(obj[item]); } if (result) { result = result.slice(1); } return result; } // 發送 post 請求 export function post(url, paramsObj) { let result = fetch(url, { method: 'POST', credentials: 'include', headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/x-www-form-urlencoded' }, body: obj2params(paramsObj) }); return result; }
在public
目錄下 新建 mock
目錄 放置 模擬數據。
在mock
目錄下 新建 list.json
文件。
public > mock > list.json
{ "errorNo": 0, "message": "success", "data": [ { "name": "Kelli", "age": 12 }, { "name": "Vivien", "age": 17 }, { "name": "Jacklyn", "age": 19 }, { "name": "Bobbie", "age": 32 } ] }
修改 src
> containers
下的 List.js
文件。
src > containers > List.js
import React, {Component} from 'react'; import {get} from '../fetch/index'; class List extends Component { constructor(props) { super(props); this.state = {} } componentWillMount() { this.getListData() } getListData() { get("./mock/list.json").then((res) => { return res.json(); }).then((json)=>{ this.setState({ dataList: json.data }) }).catch(function (err) { console.log(err); }) } render() { let _this = this; function createListDom() { return { __html: _this.state.dataList && _this.state.dataList.map( item => { return '<li>name: '+ item.name + ',age: '+ item.age +'</li>' }).join('') }; } return ( <div> List <br/> <ul dangerouslySetInnerHTML={createListDom()} /> </div> ); } } export default List;
這是隻是簡單的測試了get 方法,至於post 請求,有興趣的你們能夠私下去測試。
配置完成以下圖:
在高版本的create-react-app中去掉了支持sass的功能,若是咱們還想用只能本身配置,須要改動配置文件。
一、安裝依賴
yarn add node-sass sass-loader --save-dev
二、修改配置
在node_modules/react-scripts/config
下找到 webpack.config.dev.js
文件,
先在 exclude
中按照規律在後面添加 /.scss$/
,
而後再在loaders
中配置 sass文件規則。
{ test: /\.scss$/, loaders: ['style-loader', 'css-loader', 'sass-loader'] }
改兩個地方的配置以後,sass就配置好了,如圖:
webpack.config.dev.js
是開發環境的配置文件,若是想在生產環境也生效,那麼在webpack.config.prod.js
作一樣配置的修改。
到這裏,全部的配置就所有完成了。項目親測能夠運行。
做者 HoChine
2018 年 04月 27日
項目演示: http://hochine.cn/demo/react-demo
GitHub地址: https://github.com/HoChine/react-demo