Fork me on GitHub

koa-前后端交互-6

PS: 本节几乎都是前端内容,熟悉前端的可以不用看。

构建基本前端目录

现在根目录下新建一个src 文件夹,里面用来放前端的源代码。

这里由于主要介绍 koa2 是怎么使用起来的,所以这里重点还是放在koa2上面,前端就只是粗略的介绍。

src 目录下分别放有assets,components,layouts,lib,views

然后目录下放两个文件:index.htmlindex.js

因为是个单页应用,所以只需要一个html文件即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>预告片网站</title>
</head>
<body>
<div id="app"></div>
<script src='./index.js'></script>
</body>
</html>

然后引入对sass的支持

1
npm i cssnext node-sass autoprefixer -D

然后新建一个配置文件将支持引入进来:

1
2
3
4
5
6
module.exports = {
plugins: [
require('autoprefix'),
require('cssnext')
]
}

修改一下打包的命令:

1
2
3
4
"scripts": {
"start": "nodemon server/index.js",
"build": "rm -rf parcel/dist && parcel build src/index.html --no-cache -d dist --public-url /dist/"
},

然后放入一个index.js

1
import './assets/common.sass'

assets里面新建一个common.sass 的文件,里面放上这样的代码:

1
2
3
4
#app
width: 100%
height: 100%
overflow:hidden

然后使用npm run build 成功打包的话,就代表我们已经基本搭建好了。

接下来我们来添加一个parcel打包工具来构建一个打包案例。

server/index.js里面新增一个中间件文件:

1
2
// some codes ...
const MIDDLEWARES = ['router','parcel']

然后在midllewares 下面新建一个叫做 parcel 的目录,里面先放一个index.js 的文件:

1
2
3
4
// 使用env来获取到当前进程上的env
const env = process.env.NODE_ENV === 'production' ? 'prod' : 'dev'
module.exports= require(`./${env}.js`);

然后新建一个dev.jsprod.js

prod.js的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 在开发环境下对前端资源进行打包
// prod 只提供静态的访问功能
// dev 提供了动态的修改功能而已
const views = require('koa-views');
const serve = require('koa-static');
const {
resolve
} = require('path');
const r = path => resolve(__dirname, path)
export const prod = async app => {
// 使用中间件
app.use(serve(r('../../../dist')));
// 提供视图层
app.use(views(r('../../../dist')), {
extension: 'html'
})
}

相比与prod.js,在dev.js 里面新增了一个监听文件更新的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 在开发环境下对前端资源进行打包
const Bundler = require('parcel-bundler');
const views = require('koa-views');
const serve = require('koa-static');
const { resolve } = require('path');
const r = path => resolve(__dirname,path)
const bundler = new Bundler(r('../../../src/index.html'),{
publicUrl:'/',
watch:true,
})
export const dev = async app => {
await bundler.bundle();
// 使用中间件
app.use(serve(r('../../../dist')));
// 提供视图层
app.use(views(r('../../../dist')),{
extension: 'html'
})
app.use(async (ctx)=>{
await ctx.render('index.html')
})
}

上面我们就配置好了 parcel 中间件的使用。

然后去修改一波启动脚本的命令:

1
2
3
4
"scripts": {
"start": "rm -rf dist && rm -rf .cache && NODE_ENV=development nodemon start.js",
"build": "rm -rf parcel/dist && parcel build src/index.html --no-cache -d dist --public-url /dist/"
},

然后记得在开发环境安装一下nodemon

1
npm install nodemon -D

然后配置一下nodemon.json 在根目录下面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"restartable":"rs",
"igonre": [
".git",
"node_modules/**/node_modules"
],
"verbose":true,
"execMap":{
"js": "node --harmony"
},
"watch":[
"server",
"src/views",
"views"
],
"ext": "js json"
}

这样启动脚本都运行好之后,接下来我们就开始使用 React 来完成对项目的构建。

我们在src根目录下面添加一个index.js

1
2
3
4
5
6
7
8
9
10
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './app'
render(<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('app')
)

然后app.js为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';
import { Route,Switch } from 'react-router-dom';
import routes from './routes';
import 'antd/dist/antd.css';
// 引入公共的样式
import './assets/common.sass';
export default () => (
<Switch>
{
routes.map(({ name,path,exact = true,component })=>(
<Route path = {path} exact={exact} component = {component} key={name}/>
))
}
</Switch>
)

我们的路由放在一个routes 文件夹下面,里面放一个index.js即可(这里也可以使用react-loadable这个库将他们加载成动态的路由组件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React from 'react'
import Loadable from 'react-loadable';
function Loading({ error }) {
if(error){
return 'Opps Error!!';
} else {
return <h3>页面正在加载中,不要慌张</h3>
}
}
const Home = Loadable({
loader: () => import('./views/home'),
loading: Loading
})
const Detail = Loadable({
loader: () => import('./views/movie/detail'),
loading: Loading
})
export default [
{
name:'首页',
icon:'home',
path:'/',
component: Home
},{
name:'详情页',
path:'/detail/:id',
component: Detail
},
]

这里引入的组件库是:home/index.jsmovie/index.js

初期的代码很简单:

1
2
3
4
5
6
7
8
9
10
11
12
// home/index.js
import React from 'react';
export default class Home extends React.Component{
render(){
return(
<div>
首页
</div>
)
}
}

1
2
3
4
5
6
7
8
9
10
11
12
// movie/detail.js
import React from 'react';
export default class Detail extends React.Component{
render(){
return(
<div>
详情页
</div>
)
}
}

然后使用npm run start 启动一次,如果能启动,则说明没啥问题了。

搭建网站的导航条和布局部分

src/layout目录下面新建一个default.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import React ,{ Component } from 'react';
import { Menu , Spin } from 'antd';
import { Link } from 'react-router-dom';
import navRoutes from './../nav';
const MenuItem = Menu.Item;
const getMenuContent = ({ path,name }) => (
<a href={path? path : '/'} style = {{color: '#fff2e8'}}>
{ name }
</a>
)
export default class LayoutDefault extends Component{
constructor(props){
super(props);
this.state = {
loading:false,
tip:'再等一下'
}
}
componentDidMount(){
window._LOADING_ = this.toggeleLoading;
}
componentWillUnmount(){
window._LOADING_ = null;
}
matchRouteName = this.props.match ? navRoutes.find(e=> e.name === this.props.match.params.type) ? navRoutes.find(e=> e.name === this.props.params.type).name : '全部' : navRoutes[0].name
toggeleLoading = (state = false, tip = '再等一下下~') => {
this.setState({
tip,
})
}
render(){
const { children } = this.props;
const { loading ,tip } = this.state;
return(
<div className = "flex-column" style ={{width:'100%',height:'100%'}}>
<Menu
style = {{ fontSize:'13.5px',backgroundColor:'#000' }}
mode="horizontal"
defaultSelectedKeys={[this.matchRouteName]}
>
<MenuItem
style ={{
marginLeft: '24px',
marginRight: '30px',
fontSize:'18px',
textAlign:'center',
color:'#fff !important',
float:'left'
}}
>
<a href={'/'} className='hover-scale logo-text' style={{ color:'#fff2e8' }}>KOA2学习构造</a>
</MenuItem>
{
navRoutes.map((item,key)=>(
<MenuItem key={item.name}>
{
getMenuContent({...item})
}
</MenuItem>
))
}
</Menu>
<Spin
spinning = {loading}
tip = {tip}
wrapperClassName = 'content-spin full'
>
{ children }
</Spin>
</div>
)
}
}

里面的路由是在nav.js里面,里面的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
export default [
{
name:'全部',
path:'/'
},{
name:'科幻',
path:`/list/${encodeURIComponent('科幻')}`
},{
name:'剧情',
path:`/list/${encodeURIComponent('剧情')}`
},{
name:'动作',
path:`/list/${encodeURIComponent('动作')}`
},{
name:'犯罪',
path:`/list/${encodeURIComponent('犯罪')}`
},{
name:'动画',
path:`/list/${encodeURIComponent('动画')}`
},{
name:'奇幻',
path:`/list/${encodeURIComponent('奇幻')}`
},{
name:'冒险',
path:`/list/${encodeURIComponent('冒险')}`
},{
name:'喜剧',
path:`/list/${encodeURIComponent('喜剧')}`
},{
name: '灾难',
path: `/list/${encodeURIComponent('灾难')}`
},
{
name: '恐怖',
path: `/list/${encodeURIComponent('恐怖')}`
},
{
name: '战争',
path: `/list/${encodeURIComponent('战争')}`
},{
name: '喜剧',
path: `/list/${encodeURIComponent('喜剧')}`
},
{
name: '音乐',
path: `/list/${encodeURIComponent('音乐')}`
},
{
name: '文艺',
path: `/list/${encodeURIComponent('文艺')}`
},
{
name: '励志',
path: `/list/${encodeURIComponent('励志')}`
}
]

这样我们的导航条和布局就构建完成了,这里是一些很基础的 React 前端代码。

-------------本文结束感谢您的阅读-------------