一、Vue介绍
作者:尤雨溪
官网:https://cn.vuejs.org/
Vue的作者来自中国,叫尤雨溪,大学专业并非是计算机专业,在大学期间他学习专业是室内艺术和艺术史,后来
读了美术设计和技术的硕士,正是在读硕士期间,他偶然接触到了JavaScript ,从此被这门编程语言深深吸引,
开启了自己的前端生涯。 于2014年2月,开发了一个前端开发库Vue.js。(引用自百度百科)
-
问题1:什么是Vue:
Vue是一个用于构建用户界面的渐进式框架(自由组合,灵活复用),vue的核心库只关注视图层,不仅易于上手,还便于与第三方库(vue-router:跳转,axios:通信,vuex:状态管理)整合。
-
问题2:相比其它框架有什么优势:
https://cn.vuejs.org/v2/guide/comparison.html
Angular:Google收购的前端框架,其特点是将后台的MVC模式搬到了前端并增加了模块化开发的理念
React:Facebook出品,一款高性能的JS前端框架;特点是提出了新概念[虚拟DOM]用于
减少真实DOM操作,在内存中模拟DOM操作,有效的提升了前端渲染效率Vue:综合了Angular (模块化)和React (虚拟DOM)的优点,且易用、灵活、高效
-
问题3:Vue的核心特点:
-
响应式的数据绑定
-
可组合的视图组件
-
虚拟DOM
-
MVVM模式
MVVM 是一种设计思想
DOM Listeners和Data Bindings看作两个工具,它们是实现双向绑定的关键。
从View侧看,ViewModel中的DOM Listeners工具会帮我们监测页面上DOM元素的变化,如果有变化,则更改Model中的数据;从Model侧看,当我们更新Model中的数据时,Data Bindings工具会帮我们更新页面中的DOM元素。
-
二、Element介绍
vue2.0——https://element.eleme.io/#/zh-CN
vue3.0——https://element-plus.gitee.io/zh-CN/
网站快速成型工具,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库
三、开发环境
brew、node.js、npm、vue-cli 相关概念;
- brew:是一个软件包管理工具,类似于centos下的yum或者ubuntu下的apt-get,非常方便,免去了自己手动编译安装的不便
- node.js:运行在服务端的 JavaScript
- npm:是随同node.js一起安装的包管理工具,是 JavaScript 世界的包管理工具。通过 npm 可以安装、共享、分发代码,管理项目依赖关系(新版的nodejs已经集成了npm)
- vue-cli:官方提供的一个脚手架,用于快速生成一个 vue 的项目模板;预先定义好的目录结构及基础代码,就好比咱们在创建 Maven 项目时可以选择创建一个骨架项目,这个骨架项目就是脚手架,我们的开发更加的快速;
1、安装 Node.js
安装教程:https://www.runoob.com/nodejs/nodejs-install-setup.html
官网下载: http://nodejs.cn/download/
brew下载:brew install -g node
设置淘宝镜像加速器
npm install cnpm -g
npm install --registry=https://registry.npm.taobao.org
当后面npm安装失败时,可以使用cnpm替换试试
2、安装VUE-CLI
vue-cli:
在命令台输入, -g表示全局安装
💡 npm install vue-cli -g
查看是否安装成功
💡 vue list
3、创建一个vue工程
通过vue-cli脚手架创建工程:
vue init webpack vue_test
运行项目:
4、工程结构
5、编辑器环境准备
推荐:vsCode、webStorm、idea(推荐程度按顺序递减)
idea:安装vue.js插件(正版需要破解,社区版搜不到插件)https://blog.csdn.net/m0_47333020/article/details/108182429
webStorm:不需要安装插件(正版需要破解)
vsCode:安装相应的插件(比较方便)https://blog.csdn.net/yujing1314/article/details/90340647
四、基于Vue实现增删改查
效果图
1、安装路由、element-ui、axios通信工具
npm install vue-router
npm i element-ui -S
npm install axios
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FVRRZpXg-1640781611641)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f0f72c1b-7bf7-48fc-ba14-15481a80631e/Untitled.png)]
2、在main.js中导入组件:根目录——>main.js
import router from ‘./router’
import ElementUI from ‘element-ui’
import ‘element-ui/lib/theme-chalk/index.css’
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = false
Vue.use(ElementUI);
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
配置代理(解决跨域问题):根目录——>config——>index.js
proxyTable: {
'/': {
// 后端接口请求地址,用于本地执行访问
target: 'http://114.116.255.239:8081',
// secure: false, // 如果是https接口,需要配置这个参数
changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
pathRewrite: {
'^/': ''
}
}
}
万能请求工具封装:src——utils——api.js
import axios from 'axios';
//请求封装
let base = "";
//post请求封装,Key、Value形式
export const postKeyValueRequest=(url,params)=>{
return axios({
method: 'post',
url: `${base}${url}`,
data: params,
transformRequest: [function (data) {
let ret = '';
for(let i in data) {
ret += encodeURIComponent(i)+'='+encodeURIComponent(data[i])+'&';
}
console.log(ret);
return ret;
}],
headers: {
'Content-Type':'application/x-www-form-urlencoded'
}
})
};
//post请求封装,json形式
export const postRequest = (url, params) => {
return axios({
method: 'post',
url: `${base}${url}`,
data: params
})
}
//put请求封装
export const putRequest = (url, params) => {
return axios({
method: 'put',
url: `${base}${url}`,
data: params
})
}
//get请求封装
export const getRequest = (url, params) => {
return axios({
method: 'get',
url: `${base}${url}`,
params: params
})
}
//delete请求封装
export const deleteRequest = (url, params) => {
return axios({
method: 'delete',
url: `${base}${url}`,
params: params
})
}
路由配置(页面访问与跳转):src——router——index.js
import Vue from 'vue'
import Router from 'vue-router'
import {postRequest} from "../utils/api";
import {getRequest} from "../utils/api";
import {postKeyValueRequest} from "../utils/api";
import {putRequest} from "../utils/api";
import {deleteRequest} from "../utils/api";
import Emp from "../views/Emp"
//设置为vue的全局变量,方便在组件中引用
Vue.prototype.postRequest = postRequest;
Vue.prototype.getRequest = getRequest;
Vue.prototype.postKeyValueRequest = postKeyValueRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.deleteRequest = deleteRequest;
Vue.use(Router)
Vue.config.productionTip = false;
//定义路由规则
export default new Router({
routes: [
{
path: '/',
name: '职工列表',
component: Emp,
hidden : true
}
]
})
Emp.vue内容:
<template>
<div style="margin: 50px">
<!--增加-->
<el-button size="small" type="success" style="margin-bottom:10px" @click="dialogVisible = true">添加</el-button>
<el-input size="small" v-model="employee.name" placeholder="请输入姓名"
style="width:200px; margin-left:30px">
</el-input>
<el-select v-model="employee.gender" placeholder="性别" size="small">
<el-option label="All" value=""></el-option>
<el-option label="男" value="1"></el-option>
<el-option label="女" value="0"></el-option>
</el-select>
<!--搜索-->
<el-button size="small" type="primary" style="margin-bottom:10px" @click="initData()">搜索</el-button>
<!-- 列表数据 -->
<el-table
:data="empList"
stripe
style="width: 100%">
<el-table-column
type="selection"
width="50">
</el-table-column>
<el-table-column
prop="id"
label="id"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
label="姓别"
width="180">
<template #default="scope">
<el-tag v-if="scope.row.gender === '1'">男</el-tag>
<el-tag v-else type="warning">女</el-tag>
</template>
</el-table-column>
<el-table-column
prop="address"
label="地址"
width="180">
</el-table-column>
<el-table-column
prop="email"
label="邮箱"
width="180">
</el-table-column>
<el-table-column
prop="phone"
label="电话"
width="180">
</el-table-column>
<el-table-column
prop="department"
label="部门"
width="180">
</el-table-column>
<el-table-column
label="操作">
<template #default="scope">
<el-link type="warning" @click="update(scope.row)">修改</el-link>
<el-link type="danger" @click="del(scope.row.id)">删除</el-link>
</template>
</el-table-column>
</el-table>
<!-- 添加弹框 -->
<el-dialog
title="添加"
:visible.sync="dialogVisible"
width="30%">
<el-form label-position="left" label-width="50px" :model="employee">
<el-form-item label="id">
<el-input v-model="employee.id"></el-input>
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="employee.name"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="employee.address"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-radio v-model="employee.gender" label="1">男</el-radio>
<el-radio v-model="employee.gender" label="0">女</el-radio>
</el-form-item>
<el-form-item label="部门">
<el-select v-model="employee.department" placeholder="请选择">
<el-option label="ABC" value="ABC"></el-option>
<el-option label="AIDC" value="AIDC"></el-option>
</el-select>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="add()">确 定</el-button>
</span>
</el-dialog>
<!-- 修改弹框 -->
<el-dialog
title="修改"
:visible.sync="updateDialogVisible"
width="30%">
<el-form label-position="left" label-width="50px" :model="employee">
<el-form-item label="id">
<el-input v-model="employee.id"></el-input>
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="employee.name"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="employee.address"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-radio v-model="employee.gender" label="1">男</el-radio>
<el-radio v-model="employee.gender" label="0">女</el-radio>
</el-form-item>
<el-form-item label="部门">
<el-select v-model="employee.department" placeholder="请选择">
<el-option label="ABC" value="ABC"></el-option>
<el-option label="AIDC" value="AIDC"></el-option>
</el-select>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="updateDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="updateData()">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script type="text/javascript">
export default {
name: "Task",
data() {
return {
employee: {
id: "",
name: "",
gender: "",
address: "",
email: "",
phone: "",
department: ""
},
empList:[],
dialogVisible: false,
updateDialogVisible: false
}
},
mounted() {
this.initData();
},
methods: {
clearDara() {
this.employee = {
id: "",
name: "",
gender: "",
address: "",
email: "",
phone: "",
department: ""
}
},
// 初始化
initData() {
this.getRequest("employee/query", this.employee).then(res=>{
if(res.data.code == 0) {
this.empList = res.data.info;
}
})
},
// 增加
add() {
this.postRequest("employee/add", this.employee).then(res=>{
if(res.data.code == 0) {
this.$message.success("添加成功!");
this.initData();
} else {
this.$message.error("添加失败!" + res.data.msg);
}
})
this.dialogVisible = false;
this.clearDara();
},
// 更新
update(emp) {
this.employee = emp;
this.updateDialogVisible = true;
},
updateData() {
this.putRequest("employee/update", this.employee).then(res=>{
if(res.data.code == 0) {
this.$message.success("更新成功!");
this.initData();
}else {
this.$message.error("更新失败!" + res.data.msg);
}
})
this.updateDialogVisible = false;
this.clearDara();
},
//删除
del(id) {
this.$alert('是否删除该记录', '提示', {
confirmButtonText: '确定',
callback: action => {
this.deleteRequest("employee/delete/" + id).then(res=>{
if(res.data.code == 0) {
this.$message.success("删除成功!");
this.initData();
}else {
this.$message.error("删除失败!" + res.data.msg);
}
})
}
});
}
}
}
</script>
<style scoped>
</style>
后端主要逻辑:
package com.controller;
import com.pojo.BaseResponse;
import com.pojo.Employee;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author 10018318
* 提供接口
*/
@RestController
@RequestMapping("/employee")
public class EmployeeController {
private static final Map<Integer, Employee> employeeData = new LinkedHashMap<>();
static {
for (int num = 1001; num <= 1010; num++) {
employeeData.put(num, new Employee(num, "李杰贵", "1", "lijiegui1@shein.com", "15874423919", "南京", "ABC"));
}
}
/**
* 查询员工信息
*/
@GetMapping("/query")
public BaseResponse getEmployeeList(Employee employee) {
List<Employee> employeeList = employeeData.entrySet().stream()
.filter(employeeEntry -> (StringUtils.isEmpty(employee.getName()) ? true : employeeEntry.getValue().getName().contains(employee.getName())) &&
(StringUtils.isEmpty(employee.getGender()) ? true : employeeEntry.getValue().getGender().equals(employee.getGender())))
.map(Map.Entry::getValue).collect(Collectors.toList());
return BaseResponse.ok(employeeList);
}
/**
* 添加职工
* @param employee
* @return
*/
@PostMapping("/add")
public BaseResponse addEmployee(@RequestBody Employee employee) {
if (!employeeData.containsKey(employee.getId())) {
employeeData.put(employee.getId(), employee);
return BaseResponse.ok("添加成功!");
}
return BaseResponse.fail("添加失败,id已重复!");
}
/**
* 删除职工
* @param id
* @return
*/
@DeleteMapping("/delete/{id}")
public BaseResponse delEmployeeById(@PathVariable Integer id) {
employeeData.remove(id);
if(!employeeData.containsKey(id)) {
return BaseResponse.ok("删除成功!");
}
return BaseResponse.fail("删除失败!");
}
/**
* 更新职工
* @param employee
* @return
*/
@PutMapping("/update")
public BaseResponse updateEmployee(@RequestBody Employee employee) {
employeeData.put(employee.getId(), employee);
return BaseResponse.ok("更新成功!");
}
}
实体:
@Data
@AllArgsConstructor
public class Employee {
private Integer id;
private String name;
private String gender;
private String email;
private String phone;
private String address;
private String department;
}
五、如何部署
npm run build
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PC7olzSj-1640781611641)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/eefefaa1-bbbf-4d20-908a-da9ad27980c1/Untitled.png)]
-
部署方式:
1、将dist文件下的内容复制到springboot工程下的resource下的static文件下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kc4M2PJ0-1640781611642)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9dff1a66-fb61-464c-9b41-f4004aa14528/Untitled.png)]
2、通过nginx代理实现,
#user nobody; worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name duxing.online; root /home/duxing/front/dist/; index index.html; try_files $uri $uri/ /index.html last; # gzip config gzip on; gzip_min_length 1k; gzip_comp_level 9; gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; gzip_vary on; gzip_disable "MSIE [1-6]\."; location ^~ /prod-api/ { add_header 'Access-Control-Allow-Origin' *; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS'; proxy_pass http://127.0.0.1:8888/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } server { listen 8085; server_name www.duxing.online; root /home/duxing/back/dist/; index index.html; try_files $uri $uri/ /index.html last; # gzip config gzip on; gzip_min_length 1k; gzip_comp_level 9; gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; gzip_vary on; gzip_disable "MSIE [1-6]\."; location ^~ /prod-api/ { add_header 'Access-Control-Allow-Origin' *; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS'; proxy_pass http://127.0.0.1:8888/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } }
参考:
https://cn.vuejs.org
https://juejin.cn/post/6844903709055401991
https://cn.vuejs.org/v2/guide/comparison.html