Contents
19. 前端页面开发¶
本章涉及的知识点如下:
使用Vue.js开发一个实际项目;
使用Vue.js与后端接口进行交互,并显示相关数据;
切分一个项目的页面,主要是组件的划分和复用;-
保证前后端数据的统一。
19.1. 1.项目前期准备¶
本节将会创建一个新的Vue.js项目,完成项目所需全部依赖库的安装,并且为Vue.js项目开发安装一些必备的库和模块,例如,符合风格的UI库,以及项目与后端数据接口进行交互的请求库。
19.1.1. 1.1 创建新项目¶
$ vue create app
安装多个依赖包,包括Babel、router、Vuex和ESLint
使用如下命令进入项目,并且启动该项目的开发环境。
$ cd app
$ npm run serve
编译完成后即可在本机查看,默认地址为http://localhost:8080/。
19.2. 2. 选择UI库¶
本例选择iView作为UI库。在最新版本中iView已经更名为view-design,使用如下命令安装:
$ npm install view-ui-plus --save
以上命令自动将iView的所有依赖包下载至本地,并且添加进package.json的依赖配置中。
依赖包除了手动安装外,还可以在vue-cli中安装。无论采用何种安装方式,对于UI库的使用来说没有任何区别。
安装完成的iView不能直接使用,需要在项目中引入,需要在Webpack中指定项目入口进行配置。如果使用vue-cli,则入口文件是main.js。在文件中修改代码如下:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ViewUIPlus from 'view-ui-plus'
import 'view-ui-plus/dist/styles/viewuiplus.css'
const app = createApp(App)
app.use(store)
.use(router)
.use(ViewUIPlus)
.mount('#app')
如果要在项目中使用iView提供的UI组件,则需要修改项目自带的Home.vue文件。代码如下:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<Space wrap>
<Button type="info">Info</Button>
<Button type="success">Success</Button>
<Button type="warning">Warning</Button>
<Button type="error">Error</Button>
</Space>
</div>
</template>
这样就可以在项目的所有页面中使用iView提供的UI组件库了。这种方式采用全局引用,意味着所有iView中的组件无论是否使用均被引入,造成了资源浪费。
为了解决这个问题,iView提供了按需载入功能,借助插件babel-plugin-import实现。首先安装Babel,然后配置.babelrc(或vue-cli自动生成的babel.config.js),代码如下:
npm install babel-plugin-import --save-dev
// .babelrc or babel-loader
{
"plugins": [
[
"import",
{
"libraryName": "view-ui-plus",
"libraryDirectory": "src/components"
},
"view-ui-plus"
]
]
}
然后这样按需引入组件,就可以减小体积了:
// 按需导入
import { Button, Table } from 'view-ui-plus';
app.component('Button', Button);
app.component('Table', Table);
目前版本的iView,无论是否采用按需引用方式,都需要导入iView.css样式文件。
19.3. 3. 安装HTTP请求库¶
一个Vue.js项目,除了编辑页面的样式外,使用最多的一个功能就是对后端API的请求。本例使用Axios访问API。Axios是一个强大的HTTP库,用在浏览器或Node.js中。
使用Axios的好处是,开发者不必确定当前的应用环境。也就是说,在第8章中使用Express进行后端开发的场景中,使用Axios请求API也是可以的,并且两者的写法没有任何区别,由Axios自动判断。
不仅如此,Axios还支持Promise API,并且提供了自动转换JSON数据和XSRF防御的功能。使用如下命令安装Axios:
$ npm install axios - save
在请求中有时需要更改请求的头部,如增加Token,或者需要对请求进行统一处理,这就需要封装Axios。
在项目中新建utils文件夹,用来存放一些应用类的JavaScript文件,这里新建api.js文件用来封装Axios。
// get请求
api.get = async (url, params) => {
return await apiAxios('GET', url, params)
}
// post请求
api.post = async (url, params) => {
return await apiAxios('POST', url, params)
}
module.exports = api
在暴露给外部的两个方法对象中,调用apiAxios()方法制作统一的请求,该方法实例化了Axios进行请求,并且针对不同的请求方法添加参数,如果用户已经登录(会话存储为Token键值),其代码如下:
const axios = require('axios')
const baseUrl = 'http://localhost:3000/'
const api = {}
const apiAxios = async (method, url, params) => {
// 项目既定fapp
const headers = { fapp: 'book', 'Content-Type': 'application/json' }
// 读取存储在sessionStorage中的token
if (sessionStorage.getItem('token')) {
headers.token = sessionStorage.getItem('token')
}
return await new Promise(resolve => {
axios({
// 如果缓存里有token则所有请求都包含其
headers: headers,
method: method,
url: baseUrl + url,
// 数据内容
data:
method === 'POST' ? params : null,
params:
method === 'GET' ? params : null
}).then((res) => {
console.log(res.data)
resolve(res.data)
}).catch(e => {
console.log(e)
})
})
}
接下来在main.js文件中引入封装的API请求,并且将其挂载在Vue.js的全局对象中,这样可以在所有的场景中使用。修改后的main.js文件代码如下:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ViewUIPlus from 'view-ui-plus'
import 'view-ui-plus/dist/styles/viewuiplus.css'
import api from './utils/api'
const app = createApp(App)
app.config.globalProperties.$api = api
app.use(store)
.use(router)
.use(ViewUIPlus)
.mount('#app')
需要注意的是,Axios在Vue.js中运行时采用类似于AJAX的方式请求服务器,如果根域名或端口不同,就会产生跨域问题,浏览器默认会阻止发送此类请求。
19.3.1. 3.1 跨域问题解决办法¶
此类问题无法避免,一般采用以下3种解决办法:
设计反向代理,解决跨域问题;
使用JSONP,允许用户传递一个callback参数给服务器端;
在服务器端设置res的头部信息,允许所有请求或部分指定来源(确定的IP或者IP段)的请求。
本例选择第3种方案,修改server编写的服务器端代码,为其指定一个全局路由中间件,将所有的路由都设置为允许跨域。修改app.js文件代码如下:
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var {checkAPP, checkUser, checkAdmin} = require('./util/middleware')
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
//增加管理员路由
var adminRouter = require('./routes/admin');
var app = express();
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
//设置允许跨域访问该服务.
//设置跨域访问
app.all('*', function(req, res, next){
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "*");
next();
});
app.use('/', checkAPP, indexRouter);
app.use('/users', checkAPP, usersRouter);
app.use('/admin', [checkAPP, checkUser, checkAdmin], adminRouter);
module.exports = app;
这样跨域请求就不会产生错误了,也可以成功获取需要的数据。