本文章是学习B站全栈之巅系列课程的学习笔记,利用笔记来吸收升华学到的知识。
学习感受是,如果有一定的
nodejs+mongoDB+vue
的基础,加做过一两个相关的练手项目,再学习这个课程,你会发现原来搭建一个完整有基础功能的网站,是如此的简单,会有很大的收获。
参考源码:https://github.com/morningClock/herohoner
视频学习地址:点击这里
完整的网站项目中,一般会拥有前台网站,后台管理网站,以及服务器端处理程序。
前台网站:提供给用户访问的网站。
后台管理网站:对网站数据进行可视化管理的网站。
服务端处理程序:处理网站请求。
所以我们在项目下,建立三个子目录为
/web
使用vue
搭建的前台网站。
/admin
使用vue
搭建的后台网站
/server
提供接口处理。
根据开发的简易程度,进行开发顺序的确认。首先网站的前台项目是项目中最复杂的部分,因为一开始没有数据难以呈现效果,而且涉及到大量细节问题,所以要到最后开发。
我们在开发网站时需要拥有数据,那么我们就需要从后台管理系统,以及后端接口处理,进行着手开发。
项目中,使用了Element
的UI库,没接触过UI库,其实也不影响使用,因为UI库是为了简化开发的,所以基本上可以拿来就用。而且后端是面向管理员的,所以对界面的细节要求不高。
Element安装有两种方法:
使用vue add element
使用npm install element-ui
需要手动在main.js
中引入Element。
// 引入element-ui
import ElementUI from 'element-ui'
// 引入样式库
import 'element-ui/lib/theme-chalk/index.css'
// 挂载Element
Vue.use(ElementUI)
当然,ElementUI
还提供了局部组件引用的功能,详细见Element 按需引入。
我们还需要用到vue-router
,进行组件路由。
我们直接使用vue add router
,进行默认的配置。项目中会生成views目录,并且拥有三个组件,此时,我们只需要简单的配置。
配置router.js
import Vue from 'vue'
import Router from 'vue-router'
import Main from './views/Main.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'main',
component: Main,
}
]
})
配置App.vue
主页面Main.vue
简单的配置并应用Element的布局容器章节中的示例:实例
其中需要修改的是,将组件的高度,设置为100vh,撑开容器的高度。
此时,就已经完成了最简单的后台界面了。接下来就是按照需求,进行对应的修改。
项目网站中,分类是最基础的功能,比如网站的新闻,活动。这些都是分类。当然分类中,还会有许多子分类。我们要实现一个分类的管理,那么我们要对分类进行增删改查操作,我们先从新增开始。
现在我们的后台网站,只有一个主页面,并且显示的内容还是模板中的内容。我们先初始化后台的基础页面,保留一些我们需要展示的内容。
后台管理页面的前端修改
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zvxm29HX-1573006263218)(F:\Github\myrepositories\learning-notes\全栈之巅学习笔记\lesson1\assets\image-20191104143432627.png)]
我们需要这样的列表,那么我们把其他清空了,只保留一项菜单,新增选项分类列表
与新建分类
。
由于我们需要点击选项时,进行路由。使用element
提供的router
选项,就可以点击跳转到index
属性所指向的组件了,可配置选项点击这里查看。
中,现在是写死的表格,我们需要点击选项切换,所以我们要抽离成两个组件(CategoryList.vue 与 CategoryEdit.vue
),并配置子路由。
CategoryList
展示分类列表、CategoryEdit
用于分类的新建与编辑。
我们预设路由是/categories/list
与/categories/create
。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Wb3jzmv-1573006263220)(F:\Github\myrepositories\learning-notes\全栈之巅学习笔记\lesson1\assets\image-20191104145518009.png)]
Main.vue
的tempalte
部分
内容管理
分类
分类列表
新建分类
查看
新增
删除
王小虎
CategoryList.vue
新建分类页面
新建分类
保存
export default {
data () {
return {
model: {}
}
},
methods: {
save () {
// 保存新建的分类
}
}
}
router.js
路由配置
import Vue from 'vue'
import Router from 'vue-router'
import Main from './views/Main.vue'
import CategoryEdit from './views/CategoryEdit.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'main',
component: Main,
children: [
{ path: '/categories/create', component: CategoryEdit },
]
}
]
})
新建分类后端接口的实现
后端接口的实现其实非常简单,此时我们进入server目录,进行后端接口的实现。
此项目中我们使用了express、mongoose
以及跨域处理插件cors
。
npm install express mongoose cors
开启监听端口服务index.js
。
const express = require('express')
const app = express()
// 解决跨域问题
app.use(require('cors')())
app.listen(3000, () => {
console.log('http://localhost:3000')
})
此时我们就可以run起来,看见提示语句。
为了让层次更分明,我们需要将接口写入admin/api
,方便管理。
const express = require('express')
const app = express()
app.use(require('cors')())
app.use(express.json())
// 引入admin接口,传入express对象
require('./routes/admin')(app)
app.listen(3000, () => {
console.log('http://localhost:3000')
})
然后在admin/api
新建一个index.js`的接口处理文件,管理admin中相关的接口。
module.exports = app => {
const express = require('express')
const router = express.Router()
// 接口
router.post('/categories', async(req, res) => {
res.send(‘請求成功’)
})
// 输出router对象,挂载在全局express上
app.use('/admin/api', router)
}
此时我们可以配合前端界面,进行接口的测试。
前端安装axios
插件,并单独新建一个http.js
进行请求方法的管理。
import axios from 'axios'
const http = axios.create({
baseURL: 'http://localhost:3000/admin/api'
})
export default http
main.js
中引用该配置文件,并挂载到Vue
对象上
// 引入axios
import http from './http'
Vue.prototype.$http = http
修改CategoryEdit.vue
中的save方法,发起请求,测试是否成功
async save () {
// 保存新建的分类
res = await this.$http.post('categories', this.model)
// 输出`请求成功`
console.log(res.data)
}
此时我们的接口已经正常运作,我们需要配置我们的数据库,用作数据的存储。
当然数据库,我们也是希望用单独的文件来管理,防止index.js中内容过多,不容易后期的管理。
新建plugins
文件夹,并创建db.js
module.exports = app => {
// 连接mongoDB
const mongoose = require('mongoose')
// herohoner,可以自定义
mongoose.connect('mongodb://127.0.0.1:27017/herohoner', {
useNewUrlParser: true
})
}
然后再index.js
入口文件中,引用配置
// 引入db
require('./plugins/db')(app)
接入数据库的操作就完成,接下来就是将数据写入数据库中,mongoDB
中,我们要写入数据,必需要对数据进行规范,写Schema约束,然后使用Schema创建model模型,最后再将数据写入model模型中。
每一个表都有自己的Schema,所以我们可以单独管理各个Schema创建的model模型。
新建文件夹models
用于存放不同的模型,在下面创建我们要使用的model管理文件Category.js
。
const mongoose = require('mongoose')
// 创建约束
const schema = new mongoose.Schema({
name: { type: String }
})
// 输出具备约束的model模型
module.exports = mongoose.model('Category', schema)
万事俱备,只欠东风,我们的数据就可以写入数据库了。回到/routes/admin/api/index.js
中
写入接口
// 引入model模型
const Category = require('../../models/Category')
/**
* 新建分类
*/
router.post('/categories', async(req, res) => {
// 创建该模型,并填入post中传递过来的数据。
const model = await Category.create(req.body)
// 将创建后的模型数据返回到前端
res.send(model)
})
前端接受到后端的响应,进行对应的处理
async save () {
let res
if (this.id) {
// 编辑
res = await this.$http.put(`categories/${this.id}`, this.model)
} else {
// 新建
res = await this.$http.post('categories', this.model)
}
this.$router.push('/categories/list')
this.$message({
type: 'success',
message: '保存成功'
})
},
不要害怕,完成新增分类完成后,基本上已经完成了大部分的后端接口功能了,接下来操作都几乎差不多。
CategoryList.vue
我们照样从官网,搬一些基础的模板过来,查询时,我们只需要在页面初始化阶段,发送一个GET请求,请求查询接口就可以完成查询了。
分类列表
编辑
删除
后端接口的修改非常简单,只需要在admin/api/index.js
中新增接口处理
/**
* 获取分类列表
*/
router.get('/categories', async(req, res) => {
// 这里限制了10条,后期可以使用limit做分页处理
const items = await Category.find().limit(10)
res.send(items)
})
查询也完成了,是不是十分简单,只需要熟悉一下mongoose
中的数据操作方法就可以了。
编辑分类也完全是一样的套路,修改前端,请求接口,新增接口处理,编辑数据库中对应的数据。
当然这次不一样的是,我们需要知道我们在编辑哪个数据,这时候我们要使用到唯一标识_id
。
在前端请求编辑接口时,在url
上添加查询出来的_id
就可以了。非常简单。
其中scope.row
代表了表格中,当前行的数据。
编辑
当然编辑页我们也需要,但是编辑页和新建页可以复用,所以我们可以修改成如下
{{id? '编辑': '新建'}}分类
保存
还需要新增一个路由。
router.js
import Vue from 'vue'
import Router from 'vue-router'
import Main from './views/Main.vue'
import CategoryEdit from './views/CategoryEdit.vue'
import CategoryList from './views/CategoryList.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'main',
component: Main,
children: [
{ path: '/categories/create', component: CategoryEdit },
// 技巧:注入url参数进入该组件,
// 组件使用路由参数需要使用this.$router.params可实现同样效果
{ path: '/categories/edit/:id', component: CategoryEdit, props: true },
{ path: '/categories/list', component: CategoryList }
]
}
]
})
前端大功告成,到后端新增一个获取详情接口以及更新分类的接口就完事了。
/**
* 编辑分类
*/
router.put('/categories/:id', async(req, res) => {
const model = await Category.findByIdAndUpdate(req.params.id, req.body)
res.send(model)
})
/**
* 获取分类详情
*/
router.get('/categories/:id', async(req, res) => {
const model = await Category.findById(req.params.id)
res.send(model)
})
同样的套路,不过我们要保证体验,需要提供一个提示确认窗口
categoryList.vue
中添加
删除
async remove (row) {
this.$confirm(`是否确定删除分类${row.name}?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
const res = await this.$http.delete(`categories/${row._id}`)
this.fetch();
this.$message({
type: 'success',
message: '删除成功!'
});
})
}
后端接口新增删除接口
/**
* 删除分类
*/
router.delete('/categories/:id', async(req, res) => {
await Category.findByIdAndDelete(req.params.id, req.body)
res.send({
success: true
})
})
分类通常会有一定的关联,所以我们增加在每一个分类中添加上级分类的字段,通过该字段可以查询到上级分类的相关数据。
前端List页和Edit页面增加上级分类的信息。
CategoryList.vue
在table中新增一列,展示获取到分类的parent选项对应的name名称。
CategoryEdit.vue
增加一个下拉选项框,选择已有的分类作为父级分类。
// 默认选择已有的parent
// 分类列表
我们还需要获取到所有列表的信息。
async fetchParents () {
const res = await this.$http.get(`categories`)
this.parents = res.data
}
这样前端部分就完成了。
后端相对简单,只需要在模型中增加一个字段,并用moongoose.SchemaTypes.Object
,并用ref属性关联模型。然后在查询时,使用populate('字段名称'
)方法查询出关联模型的数据,就可以了。
Category.js
const mongoose = require('mongoose')
const schema = new mongoose.Schema({
name: { type: String },
// type为mongoose的唯一id标识mongoose.SchemaTypes.ObjectId
// ref为需要关联的model
parent: { type: mongoose.SchemaTypes.ObjectId, ref: 'Category'}
})
module.exports = mongoose.model('Category', schema)
index.js
/**
* 获取分类列表
*/
router.get('/categories', async(req, res) => {
// populate关联字段
// populate指定的字段会继续找到指定的字段它的model对象
const items = await Category.find().populate('parent').limit(10)
res.send(items)
})
眼尖的你,可能已经发现了,此时,新建后,跳转回列表页,发现侧边栏的高亮,竟然还是新建列表。一阵苦恼怎么做。查看一下ElementUI
的文档最下面选项配置,或者百度一下,都可以轻松解决这个问题。
解决方法:使用default-active
,每次更新组件页面时,动态改变其值。说白了,就是让它值跟随你的url
的$router.path
来变化。
内容管理
分类
分类列表
新建分类
到此你就完成了一个拥有简单分类功能的后台了,接下来我们把接口做成通用的增删改查接口。