前言
最近公司可能要用react,然而有幾個同事從來沒用過react,然后領導讓我帶他們學習react, 為下一個react項目做基礎。
然后隨手寫了幾個demo,幫助他們了解一下如何去構建配置項目。
現在分享出來,希望可以幫助到需要的人。 本demo 中有些目錄雖然沒有用,但是我還是列了出來,目的是為了展示一個正規項目的目錄骨架結構。
create-react-app 模板文件我也沒有歸類,等了解之后,可以自己歸類,加一個樣式的文件夾。
正文
就目前的大環境而言,在開發react或vue項目的時候,應該沒有幾個人還直接在html直接飲用react的庫文件吧,都是用的前端構建工具webpack + es6 來寫項目。所以直接就推薦facebook官方出品的腳手架create-react-app來做項目。 create-react-app是最適合新手來學習使用的腳手架,全部是簡單的命令。不需要像別的腳手架一樣,先去clone整個項目,然后再安裝依賴。
第一步 (安裝工具,生成項目, 啟動項目)
安裝create-react-app工具
npm install -g create-react-app
生成項目
create-react-app react-demo
生成項目之后自動會下載依賴全部,
進入項目之后,啟動項目
cd react-demo
npm start 或者 yarn start
npm 和 yarn 都是包管理器, 都可以下載依賴,具體這里不做解釋,詳情可以自己搜索。一個項目最好用一種包管理工具,不要混用,下面都用yarn來下載依賴。不做另外說明。
項目啟動之后打開3000端口。
項目啟動成功如下圖:
第二步 (配置react-router4.0,包含路由傳參等)
1、安裝依賴
yarn add react-router-dom --save
2、新建文件、配置路由
src
目錄下 新建 containers
目錄 里面放置主頁面文件
src
目錄下 新建 components
目錄 里面放置高復用模板文件
在 containers
中新建 Home.js
Home.js 代碼
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。這里只講如何去配置redux.
1、安裝依賴
yarn add redux react-redux --save
2、新建文件, 配置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是什么呢?fetch其實是 XMLHttpRequest 的替代品,比起 XMLHttpRequest 這種粗糙的東西,fetch顯然看起來精致多了,最棒的是fetch 是基於 Promise 的,讓我們擺脫了地獄回調的噩夢。下面我們就把fetch 簡單封裝一下。方便在react中使用。
1、安裝依賴
yarn add whatwg-fetch es6-promise --save
2、新建文件 封裝 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 請求,有興趣的大家可以私下去測試。
配置完成如下圖:
第五步 (配置sass)
在高版本的create-react-app中去掉了支持sass的功能,如果我們還想用只能自己配置,需要改動配置文件。
1、安裝依賴
yarn add node-sass sass-loader --save-dev
2、修改配置
在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