目录
1.什么是MVC,什么是MVVM?
两种开发模式:
前后端不分离(基于服务端渲染,web网页资源服务器 )
前后端分离(Web接口服务器)
两种软件架构模式:
MVC
MVVM
MVC:全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范,将业务逻辑、数据、界面显示分离组织代码的形式
M:model层,主要处理数据的curd
V:view 视图层,前 端页面
C:controller 控制器,也被称为业务逻辑层,路由也是这里面的,最重要的是conrtol,数据的业务逻辑。例:登陆,注销
MVVM-----前后端分离模式, 可以说是MVC架构模式的改进版
- Model:模型层,在这里表示 JavaScript 对象
- View:视图层,在这里表示 DOM(HTML 操作的元素)
- ViewModel:连接视图和数据的中间件,Vue.js 就是 MVVM 中的 ViewModel 层的实现者
VUE就是一个典型的MVVM模型的框架
2 vue简介
官网参考文档:https://cn.vuejs.org/guide/introduction.html
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。
Vue 有很多特点和Web开发中常见的高级功能
-
- 解耦视图和数据
- 可复用的组件
- 前端路由技术
- 状态管理
- 虚拟DOM
为什么要使用 Vue.js
- 轻量级,体积小是一个重要指标。Vue.js 压缩后有只有 20多kb (Angular 压缩后 56kb+,React 压缩后 44kb+)
- 移动优先。更适合移动端,比如移动端的 Touch 事件
- 易上手,学习曲线平稳,文档齐全
- 吸取了 Angular(模块化)和 React(虚拟 DOM)的长处,并拥有自己独特的功能,如:计算属性
- 开源,社区活跃度高
Vue的两大核心特点
- 数据驱动视图
- 组件化
3 Vue初体验
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入vue.js框架 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<div>{{msg}}====={{number}}</div>
<button @click="addnumber">{{number}}</button>
</div>
<script>
const {createApp,ref} = Vue
createApp({
setup(){
const msg=ref("hello vue")
const number = ref(0)
function addnumber(){
number.value++
}
return {
msg,
number,
addnumber
}
}
}).mount("#app")
</script>
</body>
</html>
4 Vue的使用和安装
前提条件:已安装node 16.0或以上版本
创建一个vue项目:
npm create vue@latest myapp //myapp是项目名称
5 vue基础
1.setup函数
setup函数。是vue的入口函数。
1、setup函数是Vue3.0中的一个新的配置项,值为一个函数,setup是组件的一个入口函数。
2、组件中用到的:数据、方法等等,均要配置在setup中,并返回。
3、若返回一个对象,则对象中的属性、方法在模板中均可以直接使用
1.1 原始setup函数
<script>
//vue3组合式API setup函数的语法规则:
//1、入口函数
//2、模板中用到的数据和方法,需要return以下,才能使用
import { ref } from "vue";
export default {
setup() {
//定义响应式数据
const msg = ref("哈哈哈");
const count = ref(0)
//定义方法
function add(){
count.value++;
}
return {
msg,
count,
add,
};
},
};
</script>
<template>
<header>
<h1>hello vue</h1>
<h1>{{ msg }}</h1>
<div>计数器:{{count}}</div>
<button @click="add">++</button>
</header>
<main></main>
</template>
<style scoped></style>
1.2 setup函数的语法糖
<script setup>
//setup函数的语法糖
//setup函数的语法糖,setup以属性的形式出现在script标签里
//响应式数据和方法无需再return出来,可直接在模板中使用
import {ref} from "vue"
const msg = ref("欢迎来到北游学java")
const count=ref(0)
function add(){
count.value++
}
</script>
<template>
<header>
<h1>hello vue</h1>
<h1>{{ msg }}</h1>
<div>计数器:{{count}}</div>
<button @click="add">++</button>
</header>
<main></main>
</template>
<style scoped></style>
2.ref函数
ref函数 用来定义响应式数据
使用
-
使用之前需要从"vue"中导入
-
可以定义任何类型的响应式数据
-
用ref定义的响应式数据,在js中使用,需要通过.value获取,在视图中使用,不需要.value 可以直接使用
import {ref} from "vue" //定义普通数据 let a = 10; //定义响应式数据 let msg = ref("lalala")
3.reactive函数
reactive用来定义响应式复杂数据(数组和对象)
使用
-
使用前需要从"vue"引入
-
可以用来定义响应书复杂数据类型 在js中不需要通过.value使用
-
推荐用法:如果需要定基本响应式数据和数组类型,使用ref定义,其余一概使用reactive定义
区别
对比一下ref函数和reactive函数区别:
-
ref可以用来定义任何类型的响应式数据,reactive只能用来定一复杂数据类型(对象)
-
ref定义的响应式数据在is中要通过.value使用,reactive定义的数据可以在is中直接使用
-
推荐用法:如果需要定基本响应式数据和数组类型,使用ref定义,其余一概使用reactive定义
4.几个简单指令
补充几个简单的vue指令
指令
1.v-html解析并渲染html标签字符串 类似于innerHtml
2.v-text 渲染文本字符v 类似于innerText
3.v-noce 代表该元素或标签只渲染一次,后续对被v-noce指令定义的标签渲染不再生效
4.{{}} 插值表达式
注意点
1、v-text 标签里不能添加内容 {{}}可以实现部分文本的穿插
2、插值表达式{{}}只能是表达式(例如:变量名,三元表达式)不可以写js语句
<script setup>
import {ref,reactive } from "vue"
let title = ref("halohalo")
let title1 = ref("<h1>我是大傻子</h1>")
let num = ref(10);
function add(){
num.value++
}
</script>
<template>
<div>
<p v-html="title1"></p>
<p v-text="title1"></p>
<p>{{ num }}</p>
<p v-once>{{ num*10 }}</p>
<button @click="add">++</button>
</div>
</template>
<style scoped></style>
5.条件渲染
使用
1.指令 v-if v-else v-else-if 类似于 js中的 if else else-if
根据条件决定是否渲染该元素,实现显示隐藏效果2.v-show条件渲染 条件为true 渲染 false不渲染 隐藏结果
3.三元表达式也可以实现条件渲染
对比v-if和v-show的区别
1.v-if条件为false时 dom节点压根不存在该标签
2.v-show条件为false时 dom节点中有该标签,他是通过添加display:none实现隐身
3.如何取舍
如果显示隐藏切换频繁时,使用v-show
如果只切换一次时,选择v-if
<script setup>
import {ref,reactive } from "vue"
let islogin = ref(false)
let score = ref(90)
function switchlogin() {
islogin.value = !(islogin.value)
}
let boo = ref(true)
</script>
<template>
<div>
<h1 v-if="islogin">登录成功</h1>
<h1 v-else>登录失败</h1>
<button @click="switchlogin">切换登录状态</button>
<div>
您的成绩为{{score}},
<br>
等级为:
<span v-if="score>90">666</span>
<span v-else-if="score>80">66</span>
<span v-else-if="score>70">6</span>
<span v-else>不及格</span>
</div>
<button @click="boo=!boo">切换</button>
<div id="box" v-show="boo"></div>
</div>
</template>
<style scoped>
#box{
width: 300px;
height: 300px;
background-color: red;
}
</style>
6.列表渲染
列表渲染:渲染数组 渲染对象
1.渲染数组
语法:v-for=“(item,index) in arr”
item代表数组元素 必选的
index代表数组索引 可选
2.渲染对象
不常用
语法:v-for=“(value,key,index) in obj”
注意:列表渲染时一定要动态绑定key属性,值取唯一值,一般推荐使用id :key=“item.id” 如果没有id用index
7.v-bind动态绑定
1.使用场景
‘{{}}’ 插值表达式可以实现标签内容的动态控制
但是某些情况下,我们也需要动态控制一些标签属性 例如a标签的href img的src属性
2.使用语法
v-bind:属性名=“变量名”
3.注意
v-bind是可以动态绑定任何属性包括class属性 id等
4.v-bind语法糖(简写形式)
:属性名=“变量名”
v-bind动态控制class属性,进而控制标签样式
<!--
/* v-bind动态绑定属性
1.使用场景:‘{{}}’ 插值表达式可以实现标签内容的动态控制
但是某些情况下,我们也需要动态控制一些标签属性 例如a标签的href img的src属性
2.使用语法 v-bind:属性名="变量名"
3、注意:v-bind是可以动态绑定任何属性包括class属性 id等
4、v-bind语法糖(简写形式) :属性名="变量名"
5.v-bind动态控制class属性,进而控制标签样式
*/ -->
<template>
<div>
<!-- v-bind -->
<a v-bind:href="url" target="_blank">百度一下</a>
<br><br>
<!-- 语法糖 -->
<img :src="imgurl" alt="图片" width="400px">
<br><br>
<button @click="changeImage">切换图片</button>
<!-- v-bind动态控制class属性 -->
<div :class="{'box':boo}" id="a"></div>
<button @click=" boo= !boo">切换颜色</button>
<!-- 点击高亮 -->
<h1>点击高亮</h1>
</div>
</template>
<script setup>
import { ref } from 'vue';
let boo = ref(true)
let list = ref([1,3,,4,5,6,6,7,8,9])
// 使用 ref 创建响应式数据
const url = ref('https://www.baidu.com/');
const imgurl1 = ref('https://ts1.cn.mm.bing.net/th/id/R-C.748160bf925a7acb3ba1c9514bbc60db?rik=AYY%2bJ9WcXYIMgw&riu=http%3a%2f%2fseopic.699pic.com%2fphoto%2f50017%2f0822.jpg_wh1200.jpg&ehk=CMVcdZMU6xxsjVjafO70cFcmJvD62suFC1ytk8UuAUk%3d&risl=&pid=ImgRaw&r=0');
const imgurl2 = ref('https://ts1.cn.mm.bing.net/th/id/R-C.23034dbcaded6ab4169b9514f76f51b5?rik=mSGADwV9o%2fteUA&riu=http%3a%2f%2fpic.bizhi360.com%2fbbpic%2f40%2f9640_1.jpg&ehk=RYei4n5qyNCPVysJmE2a3WhxSOXqGQMGJcvWBmFyfdg%3d&risl=&pid=ImgRaw&r=0');
const imgurl = ref("")
imgurl.value = imgurl1.value
// 定义切换图片的函数
function changeImage() {
// // 根据当前显示的图片路径切换到另一张图片
// if (imgurl1.value === imgurl.value) {
// imgurl.value = imgurl2.value;
// } else {
// imgurl.value = imgurl1.value;
// }
imgurl.value = (imgurl1.value === imgurl.value) ?imgurl2.value : imgurl1.value
}
</script>
<style scoped>
.box{
background-color: red;
}
#a{
width: 400px;
height: 400px;
border: 1px solid black;
}
</style>
8.v-on事件绑定
1.事件绑定
语法:v-on:事件名称=“事件处理函数” 例如:点击事件click
2.事件绑定语法糖
@事件名称=“事件处理函数”
3.事件对象
$event
4.事件传参
看例子
<script setup>
import {ref,reactive } from "vue"
/*
1.事件绑定
语法:v-on:事件名称="事件处理函数" 例如:点击事件click
2.事件绑定语法糖: @事件名称="事件处理函数"
3.事件对象 $event
4.事件传参
常用事件类型:click mouseleave 鼠标离开
mouseenter 鼠标进入
*/
let num = ref(0);
let handclick = (e)=>{
console.log(e)
console.log(e.target)
console.log(e.target.id)
console.log("lalalal");
}
let handclick1 = (e,a)=>{
num.value += a
}
let isred = ref(false);
let bian = function(){
console.log("lalalal");
isred.value = ! isred.value
}
let bian1 = function(){
isred.value = false;
}
</script>
<template>
<button v-on:click="handclick($event)">aaa</button>
<!-- 事件传参 -->
<h1>{{ num }}</h1>
<button @click="handclick1($event,5)">num+5</button>
<!-- 图片变身 -->
<div class="box" @mouseenter="bian" :class="{'red':isred}" @mouseleave="bian1"> </div>
</template>
<!--
scoped属性 表示该样式代码只在当前vue中生效
不加scoped属性 表示该样式代码在所有vue中生效
-->
<style scoped>
.box{
width: 300px;height: 300px;background-color: blue;
}
.red{
background-color: red;
}
</style>
9.事件绑定练习
<script setup>
import {ref,reactive } from "vue"
// 练习:渲染一个学生信息管理表格 实现删除功能
//方法1. arr.splice(beginIndex,count) 从beginIndex开始截取count个元素 修改原数组
//方法2. arr.filter((item,index)=>{retrun index!=ind})
</script>
<template>
</template>
<!--
scoped属性 表示该样式代码只在当前vue中生效
不加scoped属性 表示该样式代码在所有vue中生效
-->
<style scoped></style>
10.事件绑定的修饰符
<script setup>
import {ref,reactive } from "vue"
/*
事件绑定修饰符 @事件名称.事件修饰符 = "事件处理函数"
1. .prevent 会自动调用e.preventDefault() 用来阻止事件默认行为
2. .once 事件只触发一次
*/
let cc= ()=>{
console.log("www")
}
let ccc= ()=>{
console.log("qqq")
}
</script>
<template>
<a href="https://www.baidu.com" @click.prevent="cc">111</a>
<button @click.once="ccc">222</button>
</template>
<style scoped></style>
11.计算属性
1.使用场景
{{}}插值表达式可以将响应式数据直接渲染到标签内容上,但是在某些情况下,我们的数据可能需要转化处理之后,在显示在页面上,这时就需要用到计算属性 computed
2.使用方式
1.需要从 "vue"中引用
2.调用computed()函数,在函数的参数(回调函数)中将转换处理好的数据返回即可
3.注意点
计算属性的值会随着着依赖数据的变化而变化,从而自动更新视图
<script setup>
import {ref,reactive,computed } from "vue"
/*
计算属性
1、使用场景:{{}}插值表达式可以将响应式数据直接渲染到标签内容上,但是在某些情况下,我们的数据可能需要转化处理之后,在显示在页面上,这时就需要用到计算属性 computed
2.使用方式
1.需要从 "vue"中引用
2.调用computed()函数,在函数的参数(回调函数)中将转换处理好的数据返回即可
3.注意点:
1.计算属性的值会随着着依赖数据的变化而变化,从而自动更新视图
*/
//练习1
let firstName = ref("张")
let lastName = ref("三")
//使用计算属性
let fullName = computed(()=>firstName.value+lastName.value)
//练习2 工资1000 显示公积金 社保 医保缴纳金额
let salary = ref(10000)
let gjj = computed(()=>salary.value*0.12)
let sb = computed(()=>salary.value*0.02)
let yb = computed(()=>salary.value*0.04)
let addSal = function(){
salary.value+=1000;
}
</script>
<template>
<div>
<!-- 练习1 -->
<!-- 不使用计算属性 -->
<h1>{{ firstName+lastName }}</h1>
<!-- 使用计算属性 -->
<h1>{{ fullName }}</h1>
<br>
<br>
<!-- 练习二 -->
<h1>工资:{{ salary }}</h1>
<h1>公积金{{ gjj }}</h1>
<h1>社保:{{ sb }}</h1>
<h1>医保:{{ yb }}</h1>
<button @click="addSal">加钱</button>
</div>
</template>
<style scoped></style>
12.v-model双向数据绑定
注意点
1.双向数据绑定仅仅针对于表单标签
2.双向数据绑定指vue实例对象中的响应式数据和表单标签的value属性
3.表单的双向数据绑定本质上是v-bind和v-on的结合
4.v-model实现双向数据绑定
<script setup>
import { ref, reactive } from "vue"
/*
双向数据绑定 v-model
1.双向数据绑定仅仅针对于表单标签
2.双向数据绑定指vue实例对象中的响应式数据和表单标签的value属性
3.表单的双向数据绑定本质上是v-bind和v-on的结合
4.v-model实现双向数据绑定
*/
//想要做到修改value属性,响应式数据也同时更新
let val = ref("333")
let change = function (e) {
console.log(e.target.value)
val.value = e.target.value
}
// <!-- v-model实现双向数据绑定 -->
let keyWord = ref("222")
let lala = (e)=>{
console.log(e.target.value)
}
// <!-- 单选框双向数据绑定 -->
let sex= ref("男")
// <!-- 单独复选框的双向数据绑定 -->
let isAgree = ref(false)
// <!-- 多个复选框的双向数据绑定 -->
let hobby = ref([])
</script>
<template>
<!-- @input 输入事件 input被输入内容时会触发 -->
<input type="text" :value="val" @input="change">
<h1>{{ val }}</h1>
<!-- v-model实现双向数据绑定 -->
<input type="text" v-model="keyWord" @input="lala">
<h1>{{ keyWord }}</h1>
<!-- 单选框双向数据绑定 -->
<label >
<input type="radio" value="男" v-model="sex">男
</label>
<label >
<input type="radio" value="女" v-model="sex">女
</label>
<label >
<input type="radio" value="不详" v-model="sex">不详
</label>
<h1>{{ sex }}</h1>
<!-- 单独复选框的双向数据绑定 -->
<input type="checkbox" v-model="isAgree">同意协议
<br>
<button :disabled="!isAgree">下一步</button>
<h1>{{ isAgree }}</h1>
<!-- 多个复选框的双向数据绑定 -->
爱好:
<label >
<input type="checkbox" v-model="hobby" value="唱">唱歌
</label>
<label >
<input type="checkbox" v-model="hobby" value="跳">跳
</label>
<label >
<input type="checkbox" v-model="hobby" value="rap">rap
</label>
{{ hobby }}
</template>
<style scoped></style>
13.监听器
<script setup>
import {reactive, ref,watch} from "vue"
/*
watch 监听器 用来监听响应式数据的变化 使用之前需要从 "vue"中引入
watch监听响应式数据的几种情况
1.监听一个基本响应式数据的变化,添加immediate:true可以监听初始化变化
2.监听多个基本响应式数据的变化,数组中只要有一个发生变化就会触发监听
3.监听响应式对象
4.监听响应式对象的某一个的属性
5.监听响应式对象的某几个属性
*/
// 1.监听一个基本响应式数据
let num = ref(0)
watch(num,(newval,oldval)=>{
console.log("更新前"+oldval)
console.log("更新后"+newval)
},{immediate:true})
// 2.监听多个基本响应式数据的变化
let msg = ref("lalala")
let change = ()=>{
msg.value = "哈哈"
}
watch([num,msg],(newval,oldval)=>{
console.log("更新前"+oldval)
console.log("更新后"+newval)
})
//3.监听响应式对象
let userInfo = reactive({name:"admin",age:18})
let changUser = ()=>{
if(userInfo.name ==="lweq"){
userInfo.name="admin"
return
}
userInfo.name = "lweq"
}
//注意:回调函数只有一个形参,代表更新之后 的值
watch(userInfo,(val)=>{
console.log(val)
})
//4.监听响应式对象的某一个的属性
let stu = reactive({name:"wang",age:25,sex:"男",high:180,addr:"中国"})
let changeStu = ()=>{
stu.age = 50;
}
//监听age属性
watch(()=>stu.age,(n,o)=>{
console.log("更新前"+o)
console.log("更新后"+n)
})
//5.监听响应式对象的某几个属性
//监听age和sex属性
watch( [()=>stu.age,()=>stu.sex],(n,o)=>{
console.log("更新前"+o)
console.log("更新后"+n)
})
</script>
<template>
<!-- 1.监听一个基本响应式数据 -->
<div>计数器 {{ num }}</div>
<button @click="num++">++</button>
<!-- 2.监听多个基本响应式数据的变化 -->
<h1>{{ msg }}</h1>
<button @click="change">++</button>
<!-- //3.监听响应式对象 -->
<h1 @click="changUser">{{ userInfo.name }}</h1>
<h1>{{ userInfo.age }}</h1>
<!--4.监听响应式对象的某一个的属性 -->
<div v-for="(value,key,index) in stu" :key="index">{{ key }}:{{ value }}:{{ index }}</div>
<button @click="changeStu">++</button>
</template>
<style scoped></style>
14.生命周期钩子函数
概念
组件-实例从被创建到被销毁的整个过程
使用步骤
- 先从vue中导入以
on打头
的生命周期钩子函数 - 在setup函数中调用生命周期函数并传入回调函数
- 生命周期钩子函数可以调用多次
生命周期函数 | 含义 | 执行机制 |
---|---|---|
setup | 入口函数 | 实例对象创建之前自动执行 |
onBeforeMount | 渲染前 | 几乎不用 |
onMounted | 渲染后 | 常用,用来发送ajax请求 操作dom 初始化图表、设置定时器 |
onBeforeUpdate | 更新前 | 几乎不用 可以监听器watch替代 |
onUpdated | 更新后 | 几乎不用 |
onBeforeUnmount | 销毁前 | 常用来进行销毁操作 比如清除定时器 |
onUnmounted | 销毁后 | 几乎不用 |
<script setup>
/*
生命周期钩子函数
1.setup 入口函数 实例对象创建之前自动执行
2.onBeforeMount 渲染前 几乎不用
3.onMounted 渲染后 常用,用来发送ajax请求 操作dom 初始化图表、设置定时器
4.onBeforeUpdate 更新前 几乎不用 可以监听器watch替代
5.onUpdated 更新后 几乎不用
6.onBeforeUnmount 销毁前 常用来进行销毁操作 比如清除定时器
7.onUnmounted 销毁后 几乎不用
使用声明周期钩子函数之前,也需要导入 从"vue"中
*/
import {ref,reactive,onBeforeMount,onMounted,onUpdated,onBeforeUpdate,onBeforeUnmount,onUnmounted} from "vue"
//渲染前
onBeforeMount(()=>{
})
//渲染后 发送ajax请求
onMounted(()=>{
})
let timer = reactive({time:null})
timer.time = setInterval(()=>{
console.log("lalalla")
},3000)
//销毁前
onBeforeUnmount(()=>{
clearInterval(timer); // 停止定时器
})
</script>
<template>
</template>
<style scoped></style>
15.ref获取dom节点
步骤
1.创建ref对象
2.在目标节点中建立联系
3.使用dom节点
<script setup>
import {ref,reactive } from "vue"
/*
ref函数有两大功能
1.定义响应式数据和数组
2.用来获取dom节点 功能类似于原生js中的document.getElementById()
获取dom节点步骤:
1.创建ref对象
2.在目标节点中建立联系
3.使用dom节点
*/
//获取dom节点
//1.创建ref对象
let divRef = ref(null)
let getDiv = ()=>{
//3.使用dom节点
console.log(divRef.value) //获取dom节点
console.log(divRef.value.innerText) //获取节点文本
}
</script>
<template>
<!-- 2.在目标节点中建立 -->
<div ref="divRef">
欢迎!!
</div>
<button @click="getDiv">获取div</button>
</template>
<style scoped></style>
6 自定义组件
1.自定义组件的使用
组件化思想
组件化是 Vue.js 中的重要思想 ,它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
任何的应用都会被抽象成一颗组件树:
有了组件化的思想,我们在之后的开发中就要充分的利用它,尽可能的将页面拆分成一个个小的、可复用的组件,这样让我们的代码更加方便组织和管理,并且扩展性也更强
分类
局部组件:只能在当前引入组件中使用;
全局组件:可以在所有组件中使用
步骤
1.创建组件 .vue文件 在src/components/xxx.vue
2.引入组件
3.使用组件
App.vue组件:
<script setup>
import {ref,reactive } from "vue"
import OneCom from "./components/OneCom.vue"
import TwoCom from "./components/TwoCom.vue";
/*
组件化开发
1.组件的分类:局部组件 全局组件
2.自定义组件的使用步骤
1.创建组件 .vue文件 在src/components/xxx.vue
2.引入组件
3.使用组件
*/
</script>
<template>
<div>
根组件
<OneCom></OneCom>
<TwoCom></TwoCom>
</div>
</template>
<style scoped></style>
全局组件
需要早入口文件main.js中引入,并注册
2.父传子
什么是组件通讯
组件是独立且封闭的单元,默认情况下组件只能使用自己的数据(state)
组件化开发的过程中,完整的功能会拆分多个组件,在这个过程中不可避免的需要互相传递一些数据
为了能让各组件之间可以进行互相沟通,数据传递,这个过程就是组件通信
组件的通信的分类
1、父传子
2、子传父
3、非父子组件通信
父传子的实现步骤
1.父组件提供数据
2.在父组件的子组件标签上添加自定义属性 值就是要传递的数据
3.在子组件中用defineProsp接收,通过props.属性名获取要传递的值
const props = defineProps({
msg:String,
})
APP.vue
~~~vue
<!--1.2.在父组件的子组件标签上添加自定义属性 值就是要传递的数据 -->
~~~
SonCom.vue
<script setup>
import { ref, reactive,defineProps,defineEmits } from "vue"
//1.3在子组件中用defineProsp接收,通过props.属性名获取要传递的值
const props = defineProps({
msg: String,//String是进行类型检测是否为String如果不为String则警告
user: {
name: String,
age: Number
}
})
</script>
<template>
<!-- 父传子 -->
<div>子组件</div>
<div>从父组件接收过来的数据: {{ props.msg }}</div>
<div>
{{ props.user.name+props.user.age }}
</div>
</template>
<style scoped>
div {
background-color: pink;
color: blue;
}
</style>
3.子传父
props是单向数据流,不允许子组件修改父组件的数据,此时需要子组件向父组件传递数据
实现步骤
1.子组件提供数据
2.子组件通过defineEmits注册自定义事件,并发送自定义事件,同时传参
3.父组件的子组件标签上绑定自定义事件,事件处理函数的形参就是要传递过来的数据
SonCom.vue
<script setup>
import { ref, reactive,defineProps,defineEmits } from "vue"
//2.子传父
//1.子组件提供数据
let id = ref(100)
//2.注册自定义事件
const emits = defineEmits(["sentId"])
let handClick = ()=>{
emits('sentId',id.value)//发送自定义事件并传值
}
</script>
<template>
<div>
<!-- 子传父 -->
<button @click="handClick">发送id</button>
</div>
</template>
<style scoped>
div {
background-color: pink;
color: blue;
}
</style>
APP.vue
<script setup>
import {ref,reactive } from "vue"
import SonCom from "./components/SonCom.vue";
/*
2.子传父 因为vue是单项数据流,所以子组件不能直接修改父组件传递过来的数据
步骤:
1.子组件提供数据
2.子组件通过defineEmits注册自定义事件,并发送自定义事件,同时传参
3.父组件的子组件标签上绑定自定义事件,事件处理函数的形参就是要传递过来的数据
*/
// 子传父
let getId = function(id){
console.log(id)
}
</script>
<template>
<div class="box">
根组件
<!-- 在父组件的子组件标签上绑定自定义事件 -->
<SonCom @sentId="getId"></SonCom>
</div>
</template>
<style scoped>
.box{
background-color: red;
height: 400px
}
</style>
4.跨级组件通信 发布订阅
步骤
1、下载pubsub-js npm i pubsub-js
2、订阅者(接收数据的组件)先订阅 PubSub.subscribe(“变量名”,(msg,data){ })
3、发布者(向外传递数据的组件)发布消息 PubSub.publish(“变量名”,value)
App.vue
<script setup>
import { ref, reactive, onMounted } from "vue"
import PubSub from 'pubsub-js'
import Father from "./components/Father.vue";
/*
跨级组件通信 发布订阅 ,可用于任何关系的组件之间
跨级组件通信 发布订阅模式实现数据传递,步骤:
1、下载pubsub-js npm i pubsub-js
2、订阅者(接收数据的组件)先订阅 PubSub.subscribe("变量名",(msg,data){ })
3、发布者(向外传递数据的组件)发布消息 PubSub.publish("变量名",value)
*/
let Num = ref(99)
onMounted(() => {
PubSub.publish("Num", Num.value)
})
</script>
<template>
<div style="background-color: red;height: 800px;width: 600px;margin: 0 auto;">
爷爷组件
爷爷组件的Num:{{ Num }}
<button @click="Num++">++</button>
<Father></Father>
</div>
</template>
<style scoped></style>
Father.vue
<script setup>
import { ref, reactive } from "vue"
import Son from "./Son.vue";
</script>
<template>
<div style="background-color: blue;height: 200px;">父组件
<Son></Son>
</div>
</template>
<style scoped>
</style>
Son.vue
<script setup>
import { ref, reactive, onMounted } from "vue"
import PubSub from 'pubsub-js'
let newNum = ref()
onMounted(() => {
PubSub.subscribe("Num", (msg, date) => {
newNum.value = date
})
})
</script>
<template>
<div style="background-color: chartreuse;">
孙组件
根组件传递过来的Num{{ newNum }}
</div>
</template>
<style scoped></style>
7 插槽
定义
slot 翻译为插槽,插槽的目的是让我们原来的设备具备更多的扩展性
组件的插槽,也是为了让我们封装的组件更加具有扩展性,让使用者可以决定组件内部的一些内容到底展示什么
插槽的三种类型
- 默认插槽 default
- 具名插槽 name
- 作用域插槽 v-slot
在子组件中:
- 插槽用
<slot>
标签来确定渲染的位置,里面放的是父组件没传内容时的后备内容,一个不带 name 的<slot>
出口会带有隐含的名字 default - 具名插槽用 name 属性来表示插槽的名字
- 作用域插槽在作用域上绑定属性来将子组件的信息传给父组件使用
1.默认插槽
顾名思义,插槽没有名字 用slot标签代表插槽内容,
插槽没有命名,或者命名为default 插槽可以重复调用的
语法:
父组件
默认插槽
子组件
2.具名插槽
有名字的插槽,名字不能是default
语法:
定义插槽内容 <template v-solt:插槽名>插槽内容
使用插槽
语法糖:
<template #插槽名>
插槽内容
3.作用域插槽
定义插槽内容(在父组件中定义)的时候,想使用子组件中的数据,这种插槽叫作用域插槽
定义:
{{ slotprops.acg }}
语法糖:
<template #slot3=“slotprops”>
{{ slotprops.acg }}
使用:
<slot name=“slot3” :acg=“acg” …可定义多个>
案例
App.vue
<script setup>
import { ref, reactive, onMounted } from "vue"
import Son from "./components/Son.vue";
import Son2 from "./components/Son2.vue";
import Son3 from "./components/Son3.vue";
</script>
<template>
<div style="background-color: red;height: 800px;width: 600px;margin: 0 auto;">
根组件
<Son>
<!-- 默认插槽 -->
<!-- <h1>QWERUIOP</h1> -->
<!-- 默认插槽的完整形式 -->
<template v-slot:default>
<h1>默认插槽</h1>
</template>
</Son>
<Son2>
<!-- 具名插槽 -->
<template v-slot:GGBond>
<h1>
我是具名插槽
</h1>
</template>
<template #FFQueen>
<p>我是具名插槽语法糖</p>
</template>
</Son2>
<Son3>
<!-- 作用域插槽 -->
<template #slot3="slotprops">
<h1>{{ slotprops.acg }}</h1>
</template>
</Son3>
</div>
</template>
<style scoped></style>
Son.vue
<script setup>
import { ref, reactive, onMounted } from "vue"
</script>
<template>
<div style="background-color: chartreuse;">
子组件
<!-- 默认插槽 -->
<slot></slot>
<slot></slot>
<slot></slot>
</div>
</template>
<style scoped></style>
Son2.vue
<script setup>
import { ref, reactive, onMounted } from "vue"
</script>
<template>
<div style="background-color: blue;">
子组件2
<slot name="GGBond"></slot>
<slot name="GGBond"></slot>
<slot name="GGBond"></slot>
<slot name="FFQueen"></slot>
<slot name="FFQueen"></slot>
<slot name="FFQueen"></slot>
<slot name="FFQueen"></slot>
</div>
</template>
<style scoped></style>
Son3.vue
<script setup>
import { ref, reactive, onMounted } from "vue"
let acg = ref(789)
</script>
<template>
<div style="background-color: pink;">
子组件3
<slot name="slot3" :acg="acg"></slot>
</div>
</template>
<style scoped></style>
8 路由 vue-router
1.路由的基本使用
vue-router 是基于路由和组件的,路由用于设定访问路径,将路径和组件映射起来
前端路由:一个url路径对应一个页面.vue文件
路由实现的步骤
-
下载vue-router
npm i vue-roter
-
创建路由模块.js文件
src/router/index.js(xxx.js)
-
在路由模块里创建路由对象并导出
index.js
/* 路由模块 在该模块创建路由对象并导出 */ import HomeView from "../views/HomeView.vue" import AboutView from "@/views/AboutView.vue"; //创建路由对象的步骤 //1.引入createRouter,createWebHashHistory import { createRouter, createWebHashHistory } from "vue-router"; //2.配置路由表 本质上是一个数组,里面存放路由对象 // .vue文件需要自定义 src/views/xxx.vue const routes = [ //配置默认路由 { path:"/", redirect:"/home",//路由重定向 }, { path: "/home", name: "home", component: HomeView }, { path: "/about", name: "about", component: AboutView }, { path: "/cars", name: "cars", // 路由懒加载 component: import("@/views/CarsView.vue") } ] //3.创建路由对象并导出 const router = createRouter({ // routes:routes, //路由表 简写 routes, === routes:routes, routes, history:createWebHashHistory() //路由模式 history模式 }) export default router;
-
在main.js中引入路由对象 并注册
// import './assets/main.css' import { createApp } from 'vue' import App from './App.vue' //导入路由对象 import router from "./router/index.js" let app = createApp(App) //注册路由 app.use(router) app.mount('#app')
-
使用路由 (声明式路由 和 编程式路由)
-
声明式路由
路由跳转组件:
路由显示组件:
本质上是一个a标签
<script setup> import { ref, reactive } from "vue" import { RouterLink, RouterView } from "vue-router" </script> <template> <div class="Abox"> <div class="Abox1"> <RouterLink to="/home">首页</RouterLink> <RouterLink to="/about">关于</RouterLink> <RouterLink to="/cars">购物车</RouterLink> </div> <div> <!-- 路由组件显示的位置 放在哪在哪显示 --> <RouterView></RouterView> </div> </div> </template> <style scoped> .Abox1 a{ color: black; text-decoration: none; display: inline-block; margin: 30px; height: 30px; line-height: 30px; } /*路由被激活时,自动添加的类名 router-link-active, 可以设置激活样式*/ .Abox1 .router-link-active{ color: red ; background-color :pink; } </style>
-
编程式路由
在js中实现路由跳转
1.首先引用useRouter函数 import { useRouter } from “vue-router”;
2.获取路由实例对象 const router = useRouter() //路由对象
3.调用路由实例对象的方法.push进行跳转
<script setup> import { ref, reactive } from "vue" import { useRouter } from "vue-router"; const router = useRouter() //路由对象 let goHome = ()=>{ console.log(router); //路由实例对象 用来进行路由跳转 // 常用的方法:push("url路径") back()返回上一页 go(1)下一个 go(-1)上一个 和back效果一样 router.push("/home") } </script> <template> <h1>购物车</h1> <button @click="goHome">返回首页</button> <!-- <a href="/home">返回首页</a> --> </template> <style scoped></style>
-
2.路由传参
路由传参:有两种方式,1.query传参;2.params动态传参
1.query传参
传参:把参数以?隔开拼接到url路径
例如:跳转到个人中心页面的同时传递参数id 值100
1) 声明式路由 个人中心
(2)编程式路由 router.push(/profile?id=${id.value}
)
接参:使用useRoute 获取当前路由信息 通过route.query.id获取传递过来的id值
2.params动态传参
例如:跳转到购物车页面同时传递一个参数money=9999
路由注册时: //动态传参 path:'/cars/:money" index.js文件里修改
传参:
1) 声明式路由
购物车
(2)编程式路由
router.push(“/cars/78787”)
接参:使用useRoute获取当前路由信息通过route.params.money获取传递过来的id值
useRouter 和 useRoute的区别
- useRouter获取路由实例对象,用来进行路由跳转
例如: import{useRouter} from ‘vue-router’
const router = useRouter;
router.push(“url路径”) //进行路由跳转
- useRoute 获取当前路由信息 一般用来获取路由传参
例如: import{useRoute} from ‘vue-router’
const route = useRoute;
router.query.id 或者 route.params.id
案例
App.vue 传递参数
<script setup>
import { ref, reactive } from "vue"
import { RouterLink, RouterView } from "vue-router"
</script>
<template>
<div class="Abox">
<div class="Abox1">
<RouterLink to="/home">首页</RouterLink>
<RouterLink to="/about">关于</RouterLink>
<!-- params动态传参 声明式路由 -->
<RouterLink to="/cars/999">购物车</RouterLink>
<RouterLink to="/ProFile?id=100">个人中心</RouterLink>
</div>
<div>
<!-- 路由组件显示的位置 放在哪在哪显示 -->
<RouterView></RouterView>
</div>
</div>
</template>
<style scoped>
.Abox1 a {
color: black;
text-decoration: none;
display: inline-block;
margin: 30px;
height: 30px;
line-height: 30px;
}
/*路由被激活时,自动添加的类名 router-link-active, 可以设置激活样式*/
.Abox1 .router-link-active {
color: red;
background-color: pink;
}
</style>
ProFileView.vue query传参的对象 声明式路由 接收参数
<script setup>
import { ref } from "vue"
import { useRoute } from "vue-router";
const route = useRoute(); //route代表当前路由的实例信息 一般用来获取路由传参 route.query.属性名 就是传递过来属性的值
console.log(route.query.id)
</script>
<template>
<div>个人中心</div>
</template>
<style scoped></style>
HomeView.vue query传参的对象 编程式路由
<script setup>
import { ref, reactive } from "vue"
import { useRouter } from "vue-router";
const router = useRouter() //路由对象
let id = ref(456)
let toProFile = function(){
router.push(`/ProFile?id=${id.value}`) //多个&连接
}
</script>
<template>
<h1>首页</h1>
<button @click="toProFile">跳转个人中心</button>
</template>
<style scoped></style>
params动态传参 在index.js里配置
/* 路由模块
在该模块创建路由对象并导出
*/
import HomeView from "../views/HomeView.vue"
import AboutView from "@/views/AboutView.vue";
import ProFileView from "@/views/ProFileView.vue"
//创建路由对象的步骤
//1.引入createRouter,createWebHashHistory
import { createRouter, createWebHashHistory } from "vue-router";
//2.配置路由表 本质上是一个数组,里面存放路由对象
// .vue文件需要自定义 src/views/xxx.vue
const routes = [
//配置默认路由
{
path:"/",
redirect:"/home",//路由重定向
},
{
path: "/home",
name: "home",
component: HomeView
},
{
path: "/about",
name: "about",
component: AboutView
},
{ //params动态传参 App.vue里直接传入999就是money的值
//<RouterLink to="/cars/999">购物车</RouterLink>
path:'/cars/:money',
name: "cars",
// 路由懒加载
component: import("@/views/CarsView.vue")
},{
path:"/ProFile",
name:"ProFile",
component:ProFileView
}
]
//3.创建路由对象并导出
const router = createRouter({
// routes:routes, //路由表 简写 routes, === routes:routes,
routes,
history:createWebHashHistory() //路由模式 history模式
})
export default router;
CarsView.app 接参 params动态传参
<script setup>
import { ref, reactive } from "vue"
import { useRouter,useRoute } from "vue-router";
const route = useRoute() //当前路由的实力信息
console.log(route.params.money)//输出接收到的参数money=999
</script>
<template>
<h1>购物车</h1>
<button @click="goHome">返回首页</button>
<!-- <a href="/home">返回首页</a> -->
</template>
<style scoped></style>
AboutView.app 传参编程式路由
<script setup>
import { ref, reactive } from "vue"
import { useRouter,useRoute } from "vue-router";
//params动态传参 编程式式路由
const router = useRouter()
let tocars = function(){
router.push("/cars/78787")
}
</script>
<template>
<h1>关于</h1>
<button @click="tocars">购物车页面</button>
</template>
<style scoped></style>
3.路由嵌套
步骤
1.创建二级路由的组件
2.在路由对象里添加二级路由 在要添加二级路由的对象里新增children属性
index.js
/* 路由模块
在该模块创建路由对象并导出
*/
import { name } from "pubsub-js";
import { createRouter, createWebHashHistory } from "vue-router";
const routes = [
{
path: "/",
redirect: "/home"
},
{
path: "/home",
name: "home",
component: import("../views/HomeView.vue"),
children: [
//二级路由的默认路由
{
path: '',
redirect: "/home/usersmanage" //url要写全
},
{
//path后面的路径不要带杠
path: "ordermanage",
name: "ordermanage",
component: import("../views/home/OrderManage.vue")
},
{
path: "usersmanage",
name: "usersmanage",
component: import("../views/home/UsersManage.vue")
},
{
path: "systemmanage",
name: "systemmanage",
component: import("../views/home/SystemManage.vue")
},
{
path: "datacount",
name: "datacount",
component: import("../views/home/DataCount.vue")
}
]
},
{
path: "/about",
name: "about",
component: import("../views/AboutView.vue")
},
{
path: "/set",
name: "set",
component: import("../views/SetView.vue")
},
{
path:"/login",
name:"login",
component:import("../views/Login.vue")
}
]
//3.创建路由对象并导出
const router = createRouter({
// routes:routes, //路由表 简写 routes, === routes:routes,
routes,
history: createWebHashHistory() //路由模式 history模式
})
export default router;
3.在相对应的一级路由的组件里使用二级路由
这里一级路由是HomeView.app
<script setup>
import { ref, reactive } from "vue"
import { useRouter } from "vue-router";
</script>
<template>
<div class="home">
<div class="aside">
<RouterLink to="/home/usersmanage">用户管理</RouterLink>
<RouterLink to="/home/ordermanage">订单管理</RouterLink>
<RouterLink to="/home/systemmanage">系统管理</RouterLink>
<RouterLink to="/home/datacount">数据统计</RouterLink>
</div>
<div class="main">
<RouterView></RouterView>
</div>
</div>
</template>
<style scoped>
.home .aside {
width: 200px;
height: 800px;
background-color: #ccc;
float: left;
padding-top: 30px;
box-sizing: border-box;
}
.aside a {
display: block;
height: 30px;
line-height: 30px;
text-decoration: none;
color: black;
text-align: center;
border-bottom: 1px solid gray;
}
.aside .router-link-active {
background-color: red;
}
.home .main {
width: 800px;
height: 800px;
float: left;
background-color:rgba(9, 181, 212, 0.914);
text-align: center;
}
.home {
position: relative;
height: 800px
}
.login {
position: absolute;
}
</style>
9 多页面状态管理pinia
vue 已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?
多页面状态管理,即多个页面都共用某一个数据,那么状态管理工具用什么呢?
pinia的使用步骤:
1、下载 npm i pinia
2、创建pinia仓库模块 src/store/index.js
3、在main.js中,引入并注册pinia仓库
4、在组件中使用
1.pinia状态管理工具的使用方法一(了解)
1、创建pinia仓库(了解)
import {defineStore} from "pinia"
const useUserStore = defineStore("user",{
// state初始化数据
state:()=>{
return {
num:999,
password:"123456",
user:{
uname:'admin',
age:30,
}
}
},
// 计算属性 ---类似于computed
getters:{
getSum(state){
return state.num *100;
}
},
// 修改状态的方法
// 定义修改状态的方法
actions:{
addnum(n){
this.num=this.num+n
}
}
})
export default useUserStore
2、在main.js中导入(了解)
// import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
//导入路由对象
import router from "./router/index.js"
//导入pinia对象
import { createPinia } from 'pinia'
const pinia = createPinia()
let app = createApp(App)
//注册路由
app.use(router)//注册路由
app.use(pinia) //注册pinia
app.mount('#app')
3、在页面中使用pinia仓库中的数据(了解)
<script setup>
import { ref, reactive } from "vue"
//使用pinia中的数据
import useUserStore from "@/store";
//创建pinia仓库实例对象
const store = useUserStore();
console.log(store.num)
console.log(store.password)
let numadd = function(){
//2.调用store仓库里actions的方法,前提是需要在acyions定义修改数据的方法
store.addnum(5)
}
</script>
<template>
<h1>aaaaa</h1>
<h1>num:{{ store.num }}</h1>
<h1>password:{{ store.password }}</h1>
<!-- 1.直接修改num的值 -->
<button @click="store.num++">num+1</button>
<button @click="numadd">addnum</button>
</template>
<style scoped>
</style>
2.pinia状态管理工具使用方法2(重点掌握)
1.在src/store/index.js中创建pinia仓库,并初始化
import {defineStore} from "pinia"
import {ref,computed} from 'vue'
const useUserStore = defineStore("index",()=>{
//定义响应式数据
const count = ref(9)
//定义计算属性
//const doubleCount = computed(()=>count.value*2)
const doubleCount = computed(()=>{return(count.value)*2})
//定义修改数据的方法
function changeCount(){
count.value--
}
return {count,doubleCount,changeCount}
})
export default useUserStore
2.在main.js中引入并注册,同第一种方法的
3.在组件中使用仓库中的数据
<script setup>
import { ref, reactive } from "vue"
//使用pinia中的数据
import useUserStore from "@/store";
//创建pinia仓库实例对象
const store = useUserStore();
console.log(store.count)
</script>
<template>
<h1>方法2</h1>
<h1>count:{{ store.count }}</h1>
<button @click="store.count++">1count++</button>
<br>
<h1>doubleCount:{{ store.doubleCount }}</h1>
<br>
<button @click="store.changeCount">count--</button>
</template>
<style scoped>
</style>
10 网络请求axios
axios类似于原生的ajax、 jquery中的ajax等,都是用用来向后端发送请求拿去数据的。
**axios(config)**
**axios.request(config)**
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
1、axios发送请求
axios的使用步骤:
1、下载 npm i axios
2、引入 并使用
<script setup>
import { ref } from "vue";
// axios的使用步骤:
// 1、下载 npm i axios
// 2、引入 并使用
import axios from "axios";
import { onMounted } from "vue";
const menus = ref([]);
onMounted(() => {
// 获取首页数据
axios
.get("https://apif.java.crmeb.net/api/front/index")
.then((res) => {
console.log(1);
console.log(res);
console.log(res.data.data.menus);
// 把请求过来的数据,更新到本组件的数据上,就可渲染到页面上了
menus.value = res.data.data.menus;
})
.catch((err) => {
console.log(err);
});
// 获取商品分类数据
axios
.get("https://apif.java.crmeb.net/api/front/category")
.then((res) => {
console.log(res);
console.log(2);
})
// 捕获异常
.catch((err) => {
console.log(err);
});
});
// 处理多个异步操作
// 如果有多个请求,需要同步操作 可以使用async await
onMounted(async ()=>{
const res = await axios.get("https://apif.java.crmeb.net/api/front/category")
console.log(res)
const res2 = await axios.get("https://apif.java.crmeb.net/api/front/index")
console.log(res2)
})
</script>
<template>
<div>购物车页面</div>
<div style="" class="menus">
<div
v-for="(item, index) in menus"
:key="index"
style="width: 80px; height: 100px"
>
<img :src="item.pic" />
<div>{{ item.name }}</div>
</div>
</div>
</template>
<style>
.menus{
display:flex;
flex-wrap:wrap;
justify-content:space-between;
}
</style>
2、axios的封装
1、首次封装 新建axios的网络请求模块 src/utils/http.js
import axios from "axios"
import { inputNumberEmits } from "element-plus"
import {getToken,removeToken} from "./token.js"
import router from "../router/index.js"
axios.defaults.baseURL = "http://vapi.youlai.tech/api/v1/"
// 请求拦截
axios.interceptors.request.use((config)=>{
// 请求成功的拦截 可以做token登录认证
const token = getToken()
if(token){
config.headers.Authoriation = token
}else{
return config
}
return config
},(err)=>{
// 请求失败的拦截
return Promise.reject(err)
})
// 响应成功的拦截
axios.interceptors.response.use((res)=>{
//处理无效token 进行数据过滤
if(res.data.msg=="token无效或已过期"){
// 表示token过期
removeToken() //清空token
// 跳转到登录页面
router.push("/login")
}
return res.data
},(err)=>{
return Promise.reject(err)
})
// 封装请求方法
export const http = (url,method,params)=>{
return new Promise((resolve,reject)=>{
axios({
url:url,
method:method,
params:params, //参数
// params:method==="get"?params:null,
// data:method !=="get"?params:null,
}).then(res=>{
resolve(res)
})
})
}
2、二次封装 src/api/index.js
3、在组组件中测试
不完整接口:
https://docs.apipost.cn/preview/eda898270b2e68d6/013ffbe1cd7bdd41/?target_id=3cb7f592-397c-4e54-8d70-ddfe237726c0 这是丰享荟对应的接口地址,不一定全,可以作为参考使用。
项目参考:
https://java.crmeb.net/static/html/pc.html 丰享荟移动端项目参考地址