前言
- 前端 Vue CLI 组件化开发,所需插件 vuex、axios、router
- 后端 Spring Boot 工程
核心:前端80访问后端9000,后端访问数据库3306
小demo 功能简介:有导航栏可以切换,首页和关于页面是默认有的,备忘录是我新增的,备忘录页面的数据来自后端,后端访问数据库获得的,可以新增备忘录数据,可以删除备忘录中的数据,与后端数据库进行数据交互。
前端编写
1、新建一个 vue 工程,安装所需插件
vue create 项目名
cd 项目名 # 进到工程目录
vue add axios
vue add router
vue add vuex
npm run serve # 启动
工程结构:
2、用 VSCode 打开 vue 工程进行编辑,由于我想用 Bootstrap,所以需要修改一下 public 目录下的 index.html,那么 App.vue 中的 style 就可去掉不用
<!doctype html>
<html lang="zh">
<head>
<title>Vue CLI 单页面程序</title>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
<!-- 挂载点 -->
<div id="app"></div>
</body>
</html>
3、App.vue - 起始视图
加上面包屑导航,切换的视图都是挂载在 router-view 标签的位置
<template>
<div id="app">
<!-- 可组件化,导航栏 -->
<nav class="breadcrumb">
<a class="breadcrumb-item" href="/">首页</a>
<a class="breadcrumb-item" href="/todos">备忘录</a>
<a class="breadcrumb-item" href="/about">关于</a>
</nav>
<div class="container">
<div class="row">
<div class="col-lg-9">
<!-- 路由视图 挂载点,可以有多个 -->
<router-view/>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'app',
components: {
}
}
</script>
4、在 views 目录下多定义一个备忘录视图 Todos.vue,其中输入框和列表是单独的组件,这样模块化程度高,以后可复用
<!--.vue 快速生成模板-->
<template>
<div class="container">
<!-- 巨幕 -->
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h1 class="display-3">{{$store.state.todosTitle}}</h1>
<p class="lead">{{title}}</p>
</div>
</div>
<!-- 输入框 -->
<n-task/>
<!-- 列表 -->
<n-list/>
</div>
</template>
<script>
// 1.导入组件
import NTask from '@/components/Todos/NTask.vue'
import NList from '@/components/Todos/NList.vue'
export default {
components:{
// 2.注册组件
NTask,
NList
},
data() {
return {
title:'组件内的数据'
}
}
}
</script>
<style>
</style>
5、Todos.vue 用到了组件 NList.vue、NTask.vue,在 components 目录下新建
NList.vue
<template>
<div class="list-group">
<a href="#"
v-for="(item,index) in $store.state.todoList" :key="index"
class="list-group-item list-group-item-action">{{item.task}}
<button
@click="remove(item.id)"
type="button"
class="btn btn-outline-danger btn-sm float-right">X</button>
</a>
</div>
</template>
<script>
export default {
methods:{
remove:function(id){
//1、直接访问了 Vuex.Store 中的状态
//this.$store.state.todoList.splice(index,1);
//2、最佳实践:调用 action
this.$store.dispatch('deleteTask', id);
}
},
// 使用vuex的组件中进行分发
// 获取后端的数据
mounted(){
this.$store.dispatch('getList')
}
}
</script>
<style>
</style>
NTask.vue
<template>
<div class="form-group">
<input
@keyup.enter="create()"
v-model="task"
type="text"
class="form-control form-control-lg"
placeholder="今日事今日毕">
</div>
</template>
<script>
export default {
data() {
return {
task:''
}
},
methods:{
create:function(){
// 1、直接访问了 Vuex.Store 中的状态
//this.$store.state.todoList.unshift(this.task);
// 2、最佳实践:调用 action
this.$store.dispatch('createTask', this.task);
this.task='';
},
}
}
</script>
<style>
</style>
6、路由 router 目录下的 index.js ,定义路由规则
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
// 导入
import Todos from '@/views/Todos.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/todos',
component: Todos
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import ( /* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
7、仓库 store 目录下的 index.js,我的后端接口是9000
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
// 使用仓库
Vue.use(Vuex)
// 集中式状态管理
// 全局唯一,单例
// 状态自管理应用
export default new Vuex.Store({
// 驱动应用的数据源
state: {
url: 'http://阿里云公网ip:9000/todo',
todosTitle: '备忘录($store.state)',
todoList: [],
},
// 改变 state 中的状态
mutations: {
getList(state, list) {
state.todoList = list;
}
},
// 响应在view上的用户输入导致的状态变化
// action 提交的是 mutation,而不是直接变更状态
actions: {
// 新增任务
createTask({ dispatch }, task) {
let params = {
task: task
}
axios.post(this.state.url, params)
.then(res => {
console.log(res.status)
if (200 == res.status) {
// 重新加载数据列表
dispatch('getList');
}
})
.catch(err => {
console.error(err);
})
},
// 删除任务
deleteTask({ dispatch }, id) {
let url = this.state.url + "/" + id;
axios.delete(url)
.then(res => {
console.log(res.status, id);
if (200 == res.status) {
dispatch('getList');
}
})
.catch(err => {
console.error(err);
})
},
// 从后端获取数据
// 获取任务列表
getList(context) {
let url = this.state.url;
axios.get(url)
.then(res => {
console.log(res.data);
context.commit("getList", res.data);
})
.catch(err => {
console.error(err);
})
}
},
getters: {},
modules: {},
})
8、打包 vue 工程
先停止运行 vue 工程
ctrl + c
再编译打包,打包之后可在工程目录中的 dist 目录查看
npm run build
后端编写
1、entity
public class Todo {
int id;
String task;
public Todo() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTask() {
return task;
}
public void setTask(String task) {
this.task = task;
}
@Override
public String toString() {
return "Todo [id=" + id + ", task=" + task + "]";
}
}
2、mapper
@Mapper
public interface TodoMapper {
@Insert("insert into todo(task) value(#{task})")
@Options(useGeneratedKeys = true,keyColumn = "id",keyProperty = "id")
void insert(Todo todo);
@Delete("delete from todo where id=#{id}")
void deleteById(int id);
@Select("select id,task from todo")
Todo[] selectAll();
}
3、controller
@RestController
@CrossOrigin
@RequestMapping("/todo")
public class TodoController {
@Autowired
TodoMapper mapper;
@SuppressWarnings({ "rawtypes", "unchecked" })
@GetMapping
public ResponseEntity getTodo() {
Todo[] todoList = mapper.selectAll();
return new ResponseEntity(todoList, HttpStatus.OK);
}
@PostMapping
public void createTask(@RequestBody Todo todo) {
mapper.insert(todo);
}
@DeleteMapping("/{id}")
public void deleteTask(@PathVariable int id) {
mapper.deleteById(id);
}
}
4、配置文件
5、打成 jar 包,Maven install,完成之后在工程目录的 targer 文件夹下
数据库
1、新建数据库 todos,新建一张表 todo:
表结构:
随便放几条数据测试一下
2、导出数据库脚本 Data Export
部署到阿里云
1、上传 sql 脚本 到 阿里云服务器 /opt 目录下
命令:scp sql脚本的路径 root@阿里云公网IP:/opt
2、上传 后端打包好的 jar 包 到 阿里云服务器 /opt 目录下
命令:scp jar包的路径 root@阿里云公网IP:/opt
3、上传 前端编译好的 dist 到 阿里云服务器 /opt 目录下,由于 dist 是目录,需要递归 -r
命令:scp -r dist的路径 root@阿里云公网IP:/opt
4、ssh 安全登录阿里云服务器
命令:ssh root@阿里云公网IP,随后输入登录密码即可
登录之后安装所需软件:
apt update # 更新包列表
apt upgrade # 系统更新
apt install nginx # 安装 nginx
apt install mysql-server # 安装 mysql 服务
java # 按照提示语句安装相应版本的 java,apt install.....
5、配置 mysql
mysql -uroot -p # 第一次不用密码,直接回车两次即可
update user set host='%' where user='root'; # 更新 user 的 host 字段为 %
FLUSH PRIVILEGES; # 刷新一下
alter user 'root'@'%' IDENTIFIED WITH caching_sha2_password BY '你的密码'; # 设置密码
exit # 退回 服务器终端
修改mysql配置文件,去到 /etc/mysql/mysql.conf.d 目录下,编辑 mysqld.conf 文件
cd /etc/mysql/mysql.conf.d
nano /mysqld.conf
修改配置文件中的一句:bind_address=0.0.0.0,保存后退出即可
重启一下 mysql :service mysql restart
6、导入数据库
mysql -uroot -p
create database todos;
source /opt/todo.sql # 执行刚上传到 /opt 的sql脚本
7、修改 nginx 的配置
cd /opt
mv dist www # 刚上传的前端的dist文件夹,重命名为 www,更符合规范
cd /etc/nginx
cp nginx.conf nginx.cong.old # 修改前,先备份一下,以免出错
配置文件中加入黄色框那一段,保存后退出
8、启动 java 程序
cd /opt
mv todo-0.0.1-SNAPSHOT.jar todo.jar # 名字太长,重命名一下
chmod +x todo.jar # 修改权限,可执行
nohup java -jar todo.jar & # 运行,不挂起,关闭终端程序也可继续运行
ps -aux | grep java # 查看进程号
kill 进程号 # 杀死进程可终止程序运行
最终效果
通过 阿里云公网IP 访问,点击导航栏3个页面可来回切换
终…