nodejs实战开发,前后端分离项目框架搭建vue2+koa2+element+mysql
系统:Windows10 64位
版本对照
node v16.14.0
@vue/cli@5.0.4
cnpm@7.1.1
koa-generator@1.1.17
“axios”: “^0.26.1”,
“element-ui”: “^2.15.6”,
“vue”: “^2.6.14”,
“vue-router”: “^3.2.0”
“koa”: “^2.7.0”
“koa-cors”: “^0.0.16”,
“koa-router”: “^5.4.0”,
“mysql”: “^2.18.1”
一、准备工作
1.安装Node.js
官网下载地址:https://nodejs.org/en/download/
菜鸟安装教程:https://www.runoob.com/nodejs/nodejs-install-setup.html
以下命令在已配置node环境变量的 win+R cmd 窗口中运行
2.查看node版本(v16.14.0)
node -v
3.常用命令
//查看全局安装的插件
npm list -g --depth 0
//更新
npm -g xxx
//删除
npm uninstall -g xxx
//安装
npm install -g xxx
//查看版本
npm view XXXX versions --json
4.安装淘宝镜像
npm install -g cnpm -registry=https://registry.npm.taobao.org
此项目并没有使用cnpm安装
二、vue-cli的安装使用
1.安装vue-cli
//3.0以下版本(默认安装最新版)
npm install vue-cli -g
//3.0以上版本(默认安装最新版)
npm install -g @vue/cli
//以下是安装指定版本
//3.0以下版本
npm install -g vue-cli@版本号
//3.0以上版本
npm install -g @vue/cli@版本号
2.查看vue-cli版本(@vue/cli 5.0.4)
vue -V
3.项目目录搭建
vue create 项目名
3.1 选择Y/N
Your connection to the default npm registry seems to be slow.
Use https://registry.npmmirror.com for faster installation? No
3.2 选择vue版本(此项选择了Vue 2)
Vue CLI v5.0.4
? Please pick a preset:
Default ([Vue 3] babel, eslint)
> Default ([Vue 2] babel, eslint)
Manually select features
4.运行项目(切换到项目根目录)
cd demo
npm run serve
在浏览器中输入 http://localhost:8080/
INFO Starting development server...
DONE Compiled successfully in 6047ms 21:14:15
App running at:
- Local: http://localhost:8080/
- Network: http://192.168.1.3:8080/
如下图:项目创建目录创建完成
三、Element安装与使用
1.安装element (^2.15.6)
官方使用教程 : https://element.eleme.cn/#/zh-CN/component/installation
npm i element-ui -S
2.在 main.js 中写入以下内容:
import Vue from 'vue';
/***引入element-ui*****/
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import 'element-ui/lib/theme-chalk/display.css';
Vue.use(ElementUI)
/*******************/
new Vue({
el: '#app',
render: h => h(App)
});
3.在vue组件使用ui
<template>
<el-container>
<el-header></el-header>
<el-main></el-main>
<el-footer></el-footer>
</el-container>
</template>
四、vue-router的安装与使用
1.安装vue-router
npm install vue-router@3.2.0
注意当前项目使用的是vue2,和最新的vue-router不匹配,所以安装了vue-router@3.2.0
2.router的使用
2.1在项目src目录下新建router\index.js文件
import VueRouter from 'vue-router'
import Vue from 'vue'
import Index from '@/components/index.vue'
import Me from '@/components/navigation/components/me.vue'
import About from '@/components/navigation/components/about.vue'
import Login from '@/components/login.vue'
import Navigation from '@/components/navigation/navigation.vue'
const list = [{
path: '/',
component: Index,
meta: {
title: '首页'
}
}, {
path: '/index', //首页
name: 'index',
component: Index,
meta: {
title: '首页'
}
},
{
path: '/login', //登录
name: 'login',
component: Login,
meta: {
title: '登录'
}
},
{
path: '/navigation', //导航栏
name: 'navigation',
component: Navigation,
children: [ {
path: '/about',
name: 'about',
component: About,
meta: {
title: '关于我们'
}
}, {
path: '/me',
name: '/me',
component: Me,
meta: {
title: '个人中心'
},
]
}],
},
]
Vue.use(VueRouter)
const Router = new VueRouter({
mode: 'history',
routes: list
})
//修改动态网页标题 beforeEach 导航钩子,路由改变前触发
Router.beforeEach((to, from, next) => {
window.document.title = to.meta.title;
next();
})
// 路由跳转自动跳转到顶部
Router.afterEach((to, from, next) => {
window.scrollTo(0, 0);
})
export default Router
2.2 在main.js中引入router
import Vue from 'vue'
import App from './App.vue'
//引入vue-router
import router from '@/router'
new Vue({
render: h => h(App),
router,
}).$mount('#app')
注意,在new Vue中注册router
3.路由的跳转
3.1在组件 <el-link>
中 href 参数中写上path的值
<el-link href="/index" target="_blank">首页</el-link>
3.2在组件 <router-link>
中to参数中写上path的值
<router-link to="/index">首页</router-link>
4.渲染路径
<router-view>
组件是一个 functional 组件,渲染路径匹配到的视图组件
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
五、使用koa-generator生成后端项目框架
Koa2生成器的使用:https://www.itying.com/koa/start-generator.html
通过应用koa脚手架生成工具 可以快速创建一个基于koa2的应用的骨架。
1. 安装koa-generator(@1.1.17)
npm install koa-generator -g
2. 查看koa-generator版本
koa -V
3. 创建一个命名为 koa_demo 的应用。
koa koa_demo
或者
koa2 koa_demo
koa和koa2中使用koa-router中间件的方式不一样,这里选择koa2
4. 运行koa_demo 应用
cd koa_demo
npm install
npm start
通过 koa 应用生成器创建的应用一般都有如下目录结构:
7 directories, 9 files
├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.jade
├── index.jade
└── layout.jade
在浏览器中输入 http://localhost:3000
如下图:应用创建完成
六、Node.js 连接 MySQL
1.安装MySQL
cnpm install mysql
2.封装mysql
2.1在项目根目录下创建mysql\config.js
用来存放数据库的相关参数
const app = {
host : '121.**.***.***', // 数据库地址
user : 'root', // 数据库用户
password : '******' , // 数据库密码
database : 'baseName' // 选中数据库
}
module.exports = app;
2.2 在项目根目录下创建 mysql\mysql.js
// sql查询等方法封装
const mysql = require('mysql')
const config = require("./config")
let pool = '' //这个应该也封装在类里面,奈何不会
class Db {
/**
* 构造函数,创建数据池
*/
constructor() {
pool = mysql.createPool({
host: config.host,
user: config.user,
password: config.password,
database: config.database
})
}
//在数据池中进行会话操作
connect(sql, params) {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) {
reject(err)
} else {
connection.query(sql, params, (error, result) => {
if (error) {
reject(error)
} else {
resolve(result)
}
// 结束会话
connection.release();
})
}
})
})
}
//将参数连接成字符串
paramsToStringAnd(params) {
let str = '', temp = '', len = Object.keys(params).length
if (len > 0) {
Object.keys(params).forEach((paramsKey, index) => {
if (len === 1 || index === len - 1) {
temp = paramsKey + '=' + params[paramsKey]
} else {
temp = paramsKey + '=' + params[paramsKey] + ' and '
}
str = str + temp
})
}
return str;
}
//将参数解封成,key,val,“?” 参考了菜鸟教程的调用方法
paramsToStringSplit(params) {
let str = '', tempKey = '', tempVal = "", placeholder = '', paramsArr = [], len = Object.keys(params).length
Object.keys(params).forEach((paramsKey, index) => {
if (len === 1 || index === len - 1) {
tempKey = tempKey + paramsKey
placeholder = placeholder + '?'
tempVal = tempVal + params[paramsKey]
} else {
tempKey = tempKey + paramsKey + ','
placeholder = placeholder + '?,'
tempVal = tempVal + params[paramsKey] + ','
}
paramsArr.push(params[paramsKey])
})
console.log(tempKey);
console.log(tempVal);
console.log(placeholder);
let res = {
tempKey, tempVal, placeholder,paramsArr
}
return res;
}
//这个封装中其实有着一个方法就可以了,
query(sql, params) {
console.log("【"+sql+"】");
console.log("params==",params)
return new Promise((resolve, reject) => {
this.connect(sql, params).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
// 常用查询方法
find(tableName, params) {
let sql = '';
console.log(Object.keys(params));
if (Object.keys(params).length === 0) {
sql = "select * from " + tableName
} else {
sql = "select * from " + tableName + " where " + this.paramsToStringAnd(params)
}
console.log("查询 sql:【" + sql + "】");
return new Promise((resolve, reject) => {
this.connect(sql).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
//常用数据更新方法
update() {
let str = '', len = Object.keys(params).length
const p =this.paramsToStringSplit(params)
if (len > 0) {
console.log(this.paramsToStringSplit(params));
str = 'INSERT INTO ' + tableName + '(' + p.tempKey + ') VALUES(' + p.placeholder + ')'
console.log(str);
console.log(p.paramsArr);
}
return new Promise((resolve, reject) => {
this.connect(str, p.paramsArr).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
//常用数据增加方法
insert(tableName, params) {
let str = '', len = Object.keys(params).length
const p =this.paramsToStringSplit(params)
if (len > 0) {
console.log(this.paramsToStringSplit(params));
str = 'INSERT INTO ' + tableName + '(' + p.tempKey + ') VALUES(' + p.placeholder + ')'
console.log(str);
console.log(p.paramsArr);
}
return new Promise((resolve, reject) => {
this.connect(str, p.paramsArr).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
//常用数据删除方法,我一般不删除数据
}
module.exports = Db;
2.3 在routes目录下创建api.js
var router = require('koa-router')();
var Db = require('../mysql/mysql')
const myDb = new Db;
router.prefix('/api');
router.get('/', async (ctx, next) => {
ctx.body = "this API"
})
//koa中使用koa-router
// router.get('/queryScenic', function* (next) {
// console.log(this.query)
// yield myDb.find("scenic", this.query).then((res) => {
// this.body = {
// code: 1,
// data: res
// }
// }).catch(err => {
// this.body = {
// code: -1,
// errMsg: err
// }
// })
// });
//
//koa2中这样写
router.get('/queryScenic', async (ctx, next) => {
await myDb.find("scenic", ctx.query).then((res) => {
console.log(typeof res);
if (Object.keys(res).length > 0) {
ctx.body = {
code: 1,
data: res
}
} else {
ctx.body = {
code: 0,
data: "The data is empty"
}
}
}).catch(err => {
ctx.body = {
code: -1,
errMsg: err
}
})
})
/**
* 获取地区列表
* */
router.get('/queryCityList', async (ctx, next) => {
let myDb = new Db;
let pData = [], chData = [], data = [], m = [], N
await myDb.query('select * from administrative', {}).then(rs => {
if (Object.keys(rs).length > 0) {
for (let i = 0; i < Object.keys(rs).length; i++) {
console.log(rs[i], i);
for (let j = 0; j < Object.keys(rs).length; j++) {
if (rs[i].id == rs[j].parent_id) {
rs[i]['children'] = rs.filter(item => {
if (rs[i].id == item.parent_id) {
return item
}
})
data.push(rs[i])
break;
}
}
}
ctx.body = {
code: 1,
data: data
}
}else{
ctx.body = {
code: 0,
data: "The data is empty"
}
}
}).catch(err => {
ctx.body = {
code: -1,
errMsg: err
}
})
})
/**
* 获取兄弟城市*/
router.get("/querySiblingCity",async (ctx,next)=>{
console.log(typeof ctx.query);
console.log( ctx.query);
let obj=ctx.query
console.log(obj)
console.log(obj.parent_id)
if(!ctx.query){
ctx.body={
code:-1,
data:ctx.query
}
}
await myDb.query("select b.* from administrative a inner join administrative b on a.id=b.parent_id where b.parent_id="+obj.parent_id ).then((res) =>{
if (Object.keys(res).length > 0) {
ctx.body = {
code: 1,
data: res
}
} else {
ctx.body = {
code: 0,
data: "The data is empty"
}
}
})
})
module.exports = router;
3.在app.js文件中引入api.js
const api = require('./routes/api');
app.use(api.routes(), api.allowedMethods())
4.调用接口
在浏览器中输入 :
http://localhost:3000/api/querySiblingCity?parent_id=620000
http://localhost:3000/api/queryScenic?id=68
七、前端项目中axios的安装与使用
1.安装axios
npm install axios
2.封装axios请求
2.1在src目录新建axios\http.js
/**
* axios封装
* 请求拦截、响应拦截、错误统一处理
*/
import axios from 'axios';
import router from '../router';
/**
* 请求失败后的错误统一处理
* @param {Number} status 请求失败的状态码
*/
const errorHandle = (status, other) => {
// 状态码判断
switch (status) {
case 200:
console.log("成功");
break;
case 404:
console.log("请求的资源不存在");
break;
default:
console.log("other");
}
}
// 创建axios实例
var instance = axios.create({
timeout: 1000 * 12
});
// 设置post请求头
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
/**
* 请求拦截器
* 每次请求前,如果存在token则在请求头中携带token
*/
instance.interceptors.request.use(
config => {
// 登录流程控制中,根据本地是否存在token判断用户的登录情况
// 但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token
// 后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码
// 而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。
// const token = store.state.token;
// token && (config.headers.Authorization = token);
return config;
},
error => Promise.error(error))
// 响应拦截器
instance.interceptors.response.use(
// 请求成功
res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res),
// 请求失败
error => {
const {
response
} = error;
alert("请求错误:"+error)
console.error(error)
if (response) {
// 请求已发出,但是不在2xx的范围
errorHandle(response.status, response.data.message);
return Promise.reject(response);
} else {
// 处理断网的情况
// eg:请求超时或断网时,更新state的network状态
//。。。。。自己结合vueX实现
}
});
export default instance;
2.2在axios目录下创建api\base.js
/**
* 接口域名的管理
*/
// http://localhost:3000/api/queryScenic?id=35
const base = {
public: 'http://121.84.22.267:3000/api', //公网地址
local: 'http://localhost:3000/api' //本地地址
}
const baseUrl=base.local //不同的环境切换使用
export default baseUrl;
2.3在axios目录下创建api\index.js
用来引起其他api接口文件,这样在多人开发的项目中就可以单独使用某一模块,提供统一的接入口。
/**
* api接口的统一出口
*/
// 景点模块接口
import scenic from './scenic.js';
import city from './city.js'
// 其他模块的接口……
// 导出接口
export default {
scenic,
city
}
2.4 创建具体的api文件, src/axios/api/scenic.js
import baseUrl from './base'; // 导入接口域名列表
import axios from '../http.js'; // 导入http中创建的axios实例
const scenic = {
// 景点列表
scenicList(params) {
let url = `${baseUrl}/queryScenic`
console.log(url);
console.log(params);
return axios.get(url, {params})
},
// 其他接口…………
}
3. 在main.js中引入封装
import api from './axios/api' // 导入api接口
Vue.prototype.$api = api; // 将api挂载到vue的原型上
4.在组件中调用接口
created() {
this.$api.scenic.scenicList({id: 38 })
.then(res => {
console.log(res);
if (res.data.code == 1) {
this.scienc_1 = res.data.data[0]
}
})
this.$api.scenic.scenicList().then(res => {
if (res.data.code == 1) {
this.sciencList = res.data.data
}
})
},
八、解决跨域问题
1.安装koa2-cors( ^2.0.6),
npm install koa2-cors
koa项目安装koa-cors
2. 在后端项目app.js文件中
const cors = require('koa2-cors');
app.use(cors());
至此,一个本地可访问的前后端项目框架搭建完成,剩下的就是搬砖了,谢谢大家,一起学习交流,部分代码需要优化,还请指导