資源:create-react-app、react、react-dom、redux、react-redux、redux-thunk、react-router-dom、antd-mobile/antd、lib(scss庫)、axios/fetch
一、創建項目(首先確保你的電腦中裝有create-react-app這個腳手架,如果沒有,可以通過npm命令安裝,或者使用後續大勳提供的項目源碼)
create-react-app my-react-app
項目創建完畢,我們需要抽離配置文件,以便於可以後期合作開發
二、抽離配置文件
cd my-react-app
cnpm run eject
一定要選擇輸入y
三、項目配置
1、刪除src文件夾下除了registerServiceWorker.js和index.js之外的所有的文件
2、增加components、lib(scss庫)、store、router、tool、api這些文件夾
3、創建App.jsx,修改config/webpack.config.dev.js和webpack.config.prod.js,添加@符號,讓它指向src目錄
App.jsx
import React, {Component} from 'react'
export default class App extends Component {
render () {
return (
<div>app</div>
)
}
}
webpack.config.dev.js的第87行
webpack.config.prod.js的第93行
4、在src/index.js中引入App.jsx組件,測試組件可用性,運行cnpm run start測試
import React from 'react';
import ReactDOM from 'react-dom';
import App from '@/components/App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
5、引入錯誤邊界異常處理,components/ErrorBoundary.jsx
import React, {Component} from 'react'
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true });
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary
6、修改src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from '@/components/App';
import ErrorBoundary from '@/components/ErrorBoundary';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
<ErrorBoundary>
<App />
</ErrorBoundary>
, document.getElementById('root'));
registerServiceWorker();
7、引入scss模塊
cnpm i node-sass sass-loader -D
修改config/webpack.config.dev.js和webpack.config.prod.js,添加scss的配置
webpack.config.dev.js,在191-198行寫入如下代碼
webpack.config.prod.js,在515-222行寫入如下代碼
四、創建必須組件
因爲項目中的組件我們需要將其分爲容器組件和UI組件,所以此處我們先行引入react-redux模塊,又因爲react-redux需要redux配合使用,需要安裝,如果你的項目中的組件需要有異步操作,那麼需要使用到redux-thunk模塊
cnpm i react-redux redux redux-thunk -S
以首頁爲例,index.jsx爲容器組件、ui.jsx爲UI組件、store.js爲該首頁組件的reducer---用來提供狀態
ui.jsx
import React, {Component} from 'react';
export default class UI extends Component {
render () {
return (
<div className = "container">
<div className = "box">頁面內容</div>
<footer>頁面底部</footer>
</div>
)
}
}
store.js,如果首頁中有banner數據和prolist數據的話-----此處一定要記住寫法
const reducer = (state = {banner: [1,2,3], prolist: []}, {type, data}) => {
const {banner, prolist} = state;
switch (type) {
case 'CHANGE_BANNER':
return {banner: data, prolist};
case 'CHANGE_PROLIST':
return {prolist: data, banner};;
default:
return state;
}
}
export default reducer;
在src文件夾下store下的index.js
import {createStore, combineReducers} from 'redux';
import home from '@/components/home/store';
const reducer = combineReducers({home});
const store = createStore(reducer);
export default store;
在入口文件處處理index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import store from '@/store/index';
import App from '@/components/App';
import ErrorBoundary from '@/components/ErrorBoundary';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
<Provider store = {store}>
<ErrorBoundary>
<App />
</ErrorBoundary>
</Provider>
, document.getElementById('root'));
registerServiceWorker();
index.jsx---容器組件
import UI from './ui';
import {connect} from 'react-redux';
const mapStateToProps = (state) => {
return {
banner: state.home.banner,
prolist: state.home.prolist
}
}
const mapDispatchToProps = (dispatch) => {
return {
}
}
const Com = connect(
mapStateToProps,
mapDispatchToProps
)(UI);
export default Com;
如果首頁UI組件內部需要在組件裝載完畢之後請求數據,又不希望在容器組件內部請求數據,需要用到異步請求模塊redux-thunk
UI.jsx
import React, {Component} from 'react';
export default class UI extends Component {
compoentDidMount () {
this.props.getBannerList();
this.props.getProList();
}
render () {
return (
<div className = "container">
<div className = "box">頁面內容</div>
<footer>頁面底部</footer>
</div>
)
}
}
新建action.js
const getData = (url) => {
return new Promise((resolve, reject) => {
fetch(url).then(res => res.json()).then(data => resolve(data)).catch(err => reject(err))
})
}
export default {
getbannerlist (dispatch) {
getData('http://www.daxunxun.com/douban').then(data => {
dispatch({
type: 'CHANGE_BANNER',
data
})
})
},
getprolist (dispatch) {
getData('http://www.daxunxun.com/douban').then(data => {
dispatch({
type: 'CHANGE_PROLIST',
data
})
})
}
}
index.jsx --- 容器組件
import UI from './ui';
import {connect} from 'react-redux';
import action from './action';
import store from '@/store/index';
const mapStateToProps = (state) => {
// console.log(state)
return {
banner: state.home.banner,
prolist: state.home.prolist
}
}
const mapDispatchToProps = (dispatch) => {
return {
getBannerList: () => {
// console.log('1')
store.dispatch(action.getbannerlist)
},
getProList: () => {
store.dispatch(action.getprolist)
}
}
}
const Com = connect(
mapStateToProps,
mapDispatchToProps
)(UI);
export default Com;
最終,home組件結構如下
如法炮製,設置其他的一些組件,比如分類kind、購物車cart、用戶中心user
五、設置路由模塊
1、安裝路由模塊
cnpm i react-router-dom -S
2、創建路由模塊router/index.js
import Home from '@/components/home/index';
import Kind from '@/components/kind/index';
import Cart from '@/components/cart/index';
import User from '@/components/user/index';
const routes = [
{
path: '/home',
component: Home
},
{
path: '/kind',
component: Kind
},
{
path: '/cart',
component: Cart
},
{
path: '/user',
component: User
}
]
export default routes;
3、程序入口地址修改index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import store from '@/store/index';
import App from '@/components/App';
import ErrorBoundary from '@/components/ErrorBoundary';
import registerServiceWorker from './registerServiceWorker';
import {HashRouter as Router, Switch, Route} from 'react-router-dom';
function render(){
ReactDOM.render(
<Provider store = {store}>
<ErrorBoundary>
<Router>
<Switch>
<Route path = '/' component = {App} />
</Switch>
</Router>
</ErrorBoundary>
</Provider>
, document.getElementById('root'));
}
render();
store.subscribe(render);
registerServiceWorker();
4、修改App.jsx
import React, {Component} from 'react';
import routes from '@/router/index';
import {Switch, Route, Redirect} from 'react-router-dom';
export default class App extends Component {
render () {
return (
<div>
<Switch >
{
routes.map((item, index) => {
return (
<Route key={index} path={item.path} component = {item.component} />
)
})
}
<Redirect to={{pathname:'/home'}} />
</Switch>
</div>
)
}
}
5、地址欄輸入相關路由測試可行性
6、路由跳轉
6.1 聲明式跳轉
<Link> ----- 不含有樣式----列表進入詳情
<NavLink> ----- 選中的路由會自帶有一個active的樣式 ---- 底部切換
import {NavLink, Link} from 'react-router-dom';
<NavLink to='/home'>首頁</NavLink><Link to='/detail/123'>詳情</Link>
6.2 編程式跳轉 ---- 一定要注意寫的地方
this.props.history.push('/home')
7、假設你的項目中有一些頁面結構不一致的情況下,比如說詳情頁面有自己的底部,那麼可以在入口文件處和App組件同樣配置,index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import store from '@/store/index';
import App from '@/components/App';
import Detail from '@/components/detail/index';
import ErrorBoundary from '@/components/ErrorBoundary';
import registerServiceWorker from './registerServiceWorker';
import {HashRouter as Router, Switch, Route} from 'react-router-dom';
function render(){
ReactDOM.render(
<Provider store = {store}>
<ErrorBoundary>
<Router>
<Switch>
<Route path = '/detail' component = {Detail} />
<Route path = '/' component = {App} />
</Switch>
</Router>
</ErrorBoundary>
</Provider>
, document.getElementById('root'));
}
render();
store.subscribe(render);
registerServiceWorker();
六、UI庫的配置
pc: cnpm i antd -S
mobile: cnpm i antd-mobile -S
雖然UI組件庫可以全部引入,但是我們還是強烈建議使用按需引入模式
cnpm i babel-plugin-import -D
cnpm i babel-preset-env babel-preset-react -D
創建一個.babelrc文件,寫入如下內容
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"react"
],
"plugins": [
["import", { "libraryName": "antd-mobile", "style": "css" }]
]
}
然後!!!最重要的一步,把package.json中的babel配置給刪掉,尤其是:react-app!!!
假設我們要使用時間選擇器,需要用到模塊 DatePicker
import { DatePicker, List } from 'antd-mobile';
<DatePicker
value={this.state.date}
onChange={date => this.setState({ date })}
>
<List.Item arrow="horizontal">Datetime</List.Item>
</DatePicker>
其餘的UI組件類似
七、css的模塊化
修改配置文件
webpack.config.dev.js
webpack.config.prod.js
組件內部創建style.css
UI組件內部使用
import React, {Component} from 'react';
import style from './style.css';
export default class UI extends Component {
componentDidMount () {
this.props.getBannerList();
this.props.getProList();
}
render () {
console.log('props', this.props)
return (
<div className = "container">
<div className = {style.box}>
<ul>
{
this.props.prolist.map((item,index) => {
return (
<li key = {item.id}>{item.title}</li>
)
})
}
</ul>
</div>
<footer>頁面底部</footer>
</div>
)
}
}