文章目录
点击访问 Dva.js 官网
1、安装创建
- 安装:
npm install dva-cli -g
- 创建:
dva new 项目名称
- 启动:
npm start
- 目录结构:
2、Demo
2.1、改造项目结构
2.2、CODE
· index.js
import dva from 'dva';
import './index.css';
// 1. Initialize
const app = dva();
// 2. Plugins
// app.use({});
// 3. Model
app.model(require('./models/simpleDemo').default);
app.model(require('./models/asyncDemo').default);
// 4. Router
app.router(require('./router').default);
// 5. Start
app.start('#root');
· index.css
html,
body,
:global(#root) {
height: 100%;
}
* {
margin: 0;
padding: 0;
}
· router.js
import React from 'react'
import { Router, Route, Switch, Redirect } from 'dva/router'
import App from './routes/App'
import Film from './routes/Film'
import Cinema from './routes/Cinema';
import Center from './routes/Center';
import Detail from './routes/Detail';
import Login from './routes/Login';
function RouterConfig({ history }) {
return (
<Router history={history}>
<Switch>
<Route path='/login' component={Login} />
<Route path="/" render={() => (
<App>
<Switch>
<Route path="/film" component={Film} />
<Route path="/cinema" component={Cinema} />
<Route path="/center" render={props => !!sessionStorage.getItem('token') ? <Center {...props} /> : <Redirect to='/login' />} />
<Route path="/detail/:id" component={Detail} />
<Redirect from='/' to='film' />
</Switch>
</App>
)} />
</Switch>
</Router>
);
}
export default RouterConfig;
· components/Tabbar.js
import { NavLink } from 'dva/router'
import React from 'react'
import style from './Tabbar.css'
export default function Tabbar() {
return (
<footer>
<ul>
<li>
<NavLink to='/film' activeClassName={style.active}>Film</NavLink>
</li>
<li>
<NavLink to='/cinema' activeClassName={style.active}>Cinema</NavLink>
</li>
<li>
<NavLink to='/center' activeClassName={style.active}>Center</NavLink>
</li>
</ul>
</footer>
)
}
· components/Tabbar.css
footer {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 50px;
line-height: 50px;
text-align: center;
background: #fff;
}
footer ul {
list-style: none;
display: flex;
}
footer ul li {
flex: 1;
}
.active {
color: #f00;
}
· routes/App.js
import React from 'react'
import Tabbar from '../components/Tabbar'
export default function App({ children }) {
return (
<div>
App
{children}
<Tabbar />
</div>
)
}
· routes/Center.js
import { connect } from 'dva'
import React, { useState } from 'react'
function Center({ list, dispatch, history }) {
useState(() => {
dispatch({ type: 'async/getList' })
}, [])
const logout = () => {
sessionStorage.clear()
history.push('/login')
}
return (
<div>
Center<br />
<button onClick={logout}>退出登录</button>
<hr />
<ul>
{
list?.map(i => (
<li key={i.id}>
{i.nm}<br />
<img src={i.img} alt={i.nm} style={{ width: 100 }} />
</li>
))
}
</ul>
</div>
)
}
export default connect((mapStateToProps, mapDispatchToProps, mergeProps, options) => {
return {
list: mapStateToProps.async.list
}
})(Center)
· routes/Cinema.js
import { connect } from 'dva'
import React, { useState } from 'react'
function Cinema({ name, age, list, dispatch }) {
useState(() => {
dispatch({ type: 'demo/getList' })
}, [])
const changeName = () => {
dispatch({
type: 'demo/save',
payload: {
name: name.split('-')[0] + '-' + new Date().getTime()
}
})
}
return (
<div>
<h4>Cinema:</h4>
{`name: ${name} / age: ${age}`}<br />
<button onClick={changeName}>随机修改name</button>
<h4>影院列表:</h4>
<ul>
{
list?.map(c => (<li key={c.cinemaId}>{c.name}</li>))
}
</ul>
</div>
)
}
export default connect((mapStateToProps, mapDispatchToProps, mergeProps, options) => {
console.log(mapStateToProps, mapDispatchToProps, mergeProps, options)
return {
name: mapStateToProps.demo.name,
age: 3.1415926,
list: mapStateToProps.demo.list
}
})(Cinema)
· routes/Detail.js
import React from 'react'
export default function Detail({ match: { params: { id } } }) {
return (
<div>Detail:{id}</div>
)
}
· routes/Film.js
import React, { useEffect, useState } from 'react'
import request from '../utils/request'
export default function Film({ history }) {
const [films, setFilms] = useState([])
useEffect(() => {
request('https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6926685', {
headers: {
'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16722839631329013205237761","bc":"110100"}',
'X-Host': 'mall.film-ticket.film.list'
}
}).then(({ data: { data: { films } } }) => setFilms(films))
}, [])
return (
<>{
films?.map(film => (
<li key={film?.filmId} onClick={() => history.push(`/detail/${film.filmId}`)}>
<span>{film?.name}</span><br />
<img src={film?.poster} alt={film.name} style={{ width: 100 }} />
</li>
))
}</>
)
}
· routes/Login.js
import React from 'react'
export default function Login({ history }) {
const login = () => {
sessionStorage.setItem('token', 'xxx')
history.push('/')
}
return (
<div>
Login<br />
<button onClick={login}>登录</button>
</div>
)
}
· models/asyncDemo.js
import { getShowList } from '../services/demo'
// 跨域model(在 .weboackrc 配置代理)
export default {
namespace: 'async',
state: {
list: []
},
effects: {
*getList({ payload }, { call, put }) {
const { data: { data: { hot } } } = yield call(getShowList, payload)
yield put({
type: 'save',
payload: {
list: hot
}
})
}
},
reducers: {
save: (preState, { payload }) => {
return {
...preState,
...payload
}
}
}
}
· models/simpleDemo.js
import { getCinemaList } from '../services/demo'
export default {
namespace: 'demo',
state: {
name: '嘎嘎嘎',
list: []
},
// 订阅
subscriptions: {
setup({ dispatch, history }) {
console.log('初始化')
console.log('dispatch==>', dispatch)
console.log('history==>', history)
},
},
// 专门负责异步获取数据: redux-saga
effects: {
*getList({ payload }, { call, put }) {
const { data: { data: { cinemas } } } = yield call(getCinemaList)
yield put({
type: 'save',
payload: {
list: cinemas
}
});
},
},
// 专门负责修改state
reducers: {
save(state, action) {
return { ...state, ...action.payload };
},
}
}
· services/demo.js
import request from '../utils/request';
export function getCinemaList() {
return request('https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=9546096', {
headers: {
'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16722839631329013205237761","bc":"110100"}',
'X-Host': 'mall.film-ticket.cinema.list'
}
});
}
export function getShowList() {
return request('/api/mmdb/movie/v3/list/hot.json?ct=%E5%A4%A7%E8%BF%9E&ci=65&channelId=4');
}
· .webpackrc
{
"proxy": {
"/api": {
"target": "https://i.maoyan.com",
"changeOrigin": true
}
}
}