什么破学校还要学jsp,jsp是学不了了,浅学一下VUE3,应付一下期末考试吧

vue3概要

Vue是一套前端框架,用于简化JavaScript中的DOM操作,简化书写。

在之前我们也学习过后端的框架MyBatis,MyBatis是用来简化JDBC代码编写的;而Vue是前端框架,简化JavaScript代码编写的

vue的使用

  1. vue3的使用前提需要下载安装好node.js版本要在15.0以上
  2. 在命令窗中输入 npm init vue@laste 即可创建一个初始的vue文件夹
  3. hello vue
    值得一提的是每个绑定仅支持单一的js表达式,如{ {n+1} },页面会显示11,{ {ok?yes:no} },页面会显示ok
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <template>
    <p>{ {meg} }</p>
    <p>{ {n+1} }</p>
    <p>{ {ok?yes:no } }</p>
    </template>


    <script >
    export default {
    data(){
    return{
    meg:"hello jsp",
    n:10,
    ok:true
    }
    }
    }
    </script>

    v-bind

    v-bind可以简写为 :
    v-bind的作用的:可以为元素绑定vue属性

    基本用法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <template>
    <div>
    <p v-bind:class="meg">测试</p>
    </div>
    </template>


    <script >
    export default {
    data(){
    return{
    meg:"active"

    }
    }
    }
    </script>

    可以在控制台看到 测试的class属性已经绑定成了active

v-if

用于条件判断是否展示元素

基本用法

当meg为true时显示,为false不显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div>
<p v-if="meg">这是meg为true</p>
<p v-else>为false </p>
</div>
</template>


<script >
export default {
data(){
return{
meg:"true"

}
}
}
</script>

多条件语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<p v-if="meg === 'A'">A</p>
<p v-else-if="meg === 'B'">B</p>
<p v-else>D</p>
</template>

<script>
export default{
data(){
return{
meg:"B"
}
}
}
</script>
<style>
html{
font-size:16px;
}
</style>

页面显示B

v-on

事件处理,用来监听DOM事件,并在事件触发时执行对应的JS语句 ,v-on可简写为@

方法事件处理器

每点击一次Add就会触发add()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<button @click="add">Add</button>
<p>{ { count } }</p>
</template>
<script>
export default{
data(){
return{
count:0,
}
},
methods:{
add(){
console.log(this.count)
this.count++
}
}
}
</script>

方法传参

点击谁,会在控制台输出谁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<p @click="name(item,$event)" v-for="(item,index) in names":key="index">{ { item } }</p>

</template>
<script>
export default{
data(){
return{
names:["a","b","c"]
}
},
methods:{
name(meg,e){
console.log(meg)

}
}
}
</script>

数组变化侦测

变更数据 可以直接使页面更新


代码案例:点击添加数据,会给meg数组增加一个数据sakura

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>
<button v-on:click="ADD">添加数据</button>
<ul>
<li v-for="(item,index) in meg">{ { item } }</li>
</ul>
</div>
</template>
<script>
export default{
data(){
return{
meg:["a","b","c"]
}
},
methods:{
ADD(){
this.meg.push("sakura")
}
}
}
</script>

点击前

点击后

替换数组

这个方法不会使前端直接展示

代码案例:点击添加数据,会给meg数组增加一个数据sakura

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<button v-on:click="ADD">添加数据</button>
<ul>
<li v-for="(item,index) in meg">{ { item } }</li>
</ul>
</div>
</template>
<script>
export default{
data(){
return{
meg:["a","b","c"]
}
},
methods:{
ADD(){
this.meg.concat(["sakura"])
console.log(this.meg.concat(["sakura"]))
}
}
}
</script>

点击后 可以看到页面并没有展示出新添加的数据,但是在控制台可以输出可以看到

原因:因为这个方法是新建了一个数组,在原本的数组数据基础上添加新数据,所以将新数据赋值给原本的数组即可
将 this.meg.concat([“sakura”]) 改成 this.meg= this.meg.concat([“sakura”]) 即可

点击后

watch

用来监听响应式数据变化,当监听数据变化时可执行一个函数
代码示例:meg默认值为hello,当点击按钮时meg的值改为world,watch监听到数据变化时会执行将新数据和老数据打印到控制台的函数
注意!! watch里的函数名称必须与响应式数据名称一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<p>{ { meg } }</p>
<button @click="ischange">改变数据</button>
</template>
<script>
export default{
data(){
return{
meg:"hello"
}
},
methods:{
ischange(){
this.meg="world"
}
},
watch:{
meg(newValue,oldValue){
console.log(newValue,oldValue)
}
}
}
</script>

watch 是一个浅度监听,例如watch监听的是一个响应式对象,那么这个对象的属性变化,watch是监听不到的,解决方法有两种,

  1. 在watch的监听方法中指定监听对象的具体属性(下面例子中注释的部分)
  2. 在watch监听方法的末尾添加deep:true,immediate:true;
    • deep:true 是开启深度监听,这样就可以监听到对象的所有属性
    • immediate:true 原本watch只有在数据变化时才会监听,开启这个则会立即监听
1
2
3
4
5
6
7
8
9
10
econst person = ref({name:"张三”
age:23
});
/*
watch(()=>person.value.name,(newValue)=>{
console.log(newValue);
})
*/
watch(person,(newValue)=>{
console.log(newValue); },{deep:true,immediate:true})

v-model

进行表单数据双向绑定
代码实例:输入框中输入的数据会实时显示在下方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<input type="text" v-model="meg">
<p>实时输入的是{ { meg } }</p>
</div>
</template>
<script>
export default{
data(){
return{
meg:"",
a: ""
}
}
}
</script>

v-model.lazy

不加lazy 每输入一个数据都会实时绑定
加上lazy 全部输入完数据才会进行绑定

v-model.number

只有输入的数字才会进行绑定

v-moder.trim

删除数据的前后空格在绑定

引用组件

局部引用组件

全局引入组件

组件传递数据

父传子

vue2 父传子 Props

父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<p>父传子</p>
<Props1 :meg="meg" :num="num" :zu="zu"/>
</template>

<script>
import Props1 from './Props1.vue';
export default{
data(){
return{
meg:"这是Props传的字符串数据",
num: 20,
zu:['a','b','c']
}
},
components:{
Props1
}
}
</script>

子组件接收
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<p>Props1打印传递的数据</p>
<p>{ { meg } }</p>
<p>{ { num } }</p>
<p v-for="item in zu"> { { item } }</p>
</template>

<script>
import Props1 from './Props1.vue';
export default{
data(){
return{
}
},
props:["meg","num","zu"]
}
</script>

结果

这是局部引用组件,被引用的组件是子组件,子组件接收数据要用props;
props可以接收任何形式的数据

vue3 父传子传递数据

defineProps 是 Vue 3 中新引入的函数,用于定义子组件的 props。

  1. 在子组件中定义props
    这是一个子组件(department-index)中定义的props
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const props = defineProps({
    id: {
    type: String,
    required: true
    },
    isdetail:{
    type:Boolean,
    required: false
    }
    })
  2. 父组件中给子组件props传值
    1
    2
    3
    <el-tab-pane label="项目完成单位" name="department">
    <department-index :isdetail="true" :id="props.id" />
    </el-tab-pane>
    总结
    defineProps 是 Vue 3 中一个非常强大的特性,它提供了一种更加明确和类型安全的方式来定义子组件的 props。通过使用 defineProps,我们可以让子父组件之间的数据传递更加清晰和可维护,同时也提高了代码的健壮性

    子接收数据校验

    子组件可以规定接收数据的类型,如果父组件传递的类型不是子组件规定的,会报警告
    利用props中的type

    子传父(传的是函数)

    vue2子传父 this.$emit

    子传父利用this.$emit,其实原理是回调父组件的函数
    先看子组件,子组件利用this.$emit,向父组件传递了一个函数名,和一个新数据

    父组件用v-on绑定了子组件传过来的函数名和数据,并再methods中实现了。

    最后效果

    vue3 defineEmits

    defineEmits 函数返回的是一个函数,而不是一个对象或数组。因此,不能直接修改它返回的事件列表
    你可以使用 defineEmits 来定义多个事件,只需将它们作为字符串数组传递给 defineEmits 即可。例如:defineEmits([‘event1’, ‘event2’])。
    在子组件中触发事件时,你可以传递任意数量的参数给事件监听器。这些参数将在父组件的事件处理函数中作为参数接收。
    如果你不使用 defineEmits 来定义事件,Vue 仍然会正常工作,但使用 defineEmits 可以提供更好的类型检查和代码提示

看一个案例 分别有三个文件 base.vue edit.vue index.vue

  1. 子组件 base.vue
    其中定义了两个个自定义事件refreshData,closeEdit ,然后在执行 submitHandle函数的时候会 触发emit(‘refreshData’)这个事件

    这里的自定义事件可以只是一个名字
    emit(‘refreshData’)可以携带参数例如 emit(‘refreshData’, ‘1’)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //自定义事件
    const emit = defineEmits(['refreshData', 'closeEdit'])
    // 表单提交
    const submitHandle = () => {
    dataFormRef.value!.validate((valid: boolean) => {
    if (!valid) {
    return
    }

    let data = Object.assign({}, dataForm)
    useProjectUpdateApi(data).then(() => {
    ElMessage.success({
    message: '操作成功',
    duration: 500,
    onClose: () => {
    //visible.value = false
    emit('refreshData')
    }
    })
    })
    })
    }
  2. base.vue的父组件 edit.vue
    在edit.vue中 使用了base.vue组件 并用v-on监听了refreshData事件(@refresh-data=”handleRefresh”),所以当refreshData事件被触发时
    edit.vue组件会监听到,然后执行handleRefresh函数,可以看到edit组件自己也自定义了两个事件,并在handleRefresh函数中触发了自己的refreshData事件
    1
    2
    3
    4
    5
    6
    7
     <base-edit v-if="activeName === 'base'" :id="props.id" @refresh-data="handleRefresh" />


    const emit = defineEmits(['refreshData', 'closeEdit'])
    const handleRefresh = () => {
    emit('refreshData')
    }
  3. edit.vue的父组件index.vue
    当edit中的refreshData事件被触发时,index.vue会监听到并执行getDataList函数,
    1
    <edit ref="editRef" :id="editId" @refresh-data="getDataList"></edit>

    动态组件

    多个组件之间来回切换,需要用到
    简单实例:创建3个组件A,B,C;默认在C组件中展示A,当点击按钮后切换成B
    1
    2
    3
    <template>
    <p>组件A</p>
    </template>
    1
    2
    3
    <template>
    <p>组件B</p>
    </template>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <template>
    <component :is="change"></component>
    <button @click="changeHandle">切换组件</button>
    </template>
    <script>
    import A from '@/components/A.vue'
    import B from '@/components/B.vue'
    export default{
    data(){
    return{
    change: "A"
    }
    },
    components:{
    A,
    B
    },
    methods:{
    changeHandle(){
    this.change=this.change== "A"?"B":"A"
    }
    }
    }
    </script>
    点击前

    点击后

组件存活

使用后,切换组件,原来的组件就会被销毁,如果再次切换到原来的组件,只是重新加载了这个组件的生命周期
例如 将A更改一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<p>组件A</p>
<p>{ { meg } }</p>
<button @click="change">更新数据</button>
</template>
<script>
export default{
data(){
return{
meg: "老数据"
}
},
methods:{
change(){
this.meg=this.meg=="老数据"?"新数据":"老数据"
}
}
}
</script>

再来看点击前

先点击更新数据,会展示新数据

然后在点击两次切换组件 让他切换到A

会发现组件A的数据变成了老数据,说明组件A在被切换后被销毁了,再次切换回来只是重新加载了一边生命周期
如果需要组件存活 需要用到

这样就可以了

axios发起请求

axios用于前端发送请求到后端

  • 安装 axios的依赖
    1
    import axios from 'axios';
    baseURL用来记录转发的目的地路径,可以是具体路径192.168.217.168等,这里写的是api因为会发生跨域
    instance = axios.create({baseURL})是创造一个axios的实例,实例会有很多功能如图


    简单使用:我这里使用的request是因为 axios的导入和URL,instance都是在request中的,所以方便一些

跨域问题

跨域指挥发生在浏览器中,浏览器不能将前端5173端口的请求转发到另一个端口上,但是可以又5173前端端口转发到后端
所以axios的URL不建议直接写后端的端口地址,会发生跨域问题

解决:

  • 使用代理模式
    指定URL 例如api,这样前端会把请求发送到前端 5173/api的接口中,然后利用代理,会将地址改变
    target :改变的api前面的路径端口等
    changeOrigin : 是否开启代理
    rewrite : 可以将api替换成空格
    最后结果 http://local host:5173/api 会代理成 http://local host:8080/

路由

路由帮助我们跳转页面 例如点击登录后跳转到主页面

  1. 安装
    执行命令
    1
    npm install vue-router@latest
  2. 创造路由并导入
    可以看到我们新建了router文件夹下面的index.js
  • 首先导入vue-router,和导入组件
  • 需要定义路由关系
  • 创建路由,并把路由关系指定给路由
  • 导出路由
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //导入vue-router
    import { createRouter, createWebHistory } from 'vue-router'
    //导入组件
    import LoginVue from '@/views/Login.vue'
    import LayoutVue from '@/views/Layout.vue'

    //定义路由关系
    const routes = [
    { path: '/login', component: LoginVue },
    { path: '/', component: LayoutVue }
    ]

    //创建路由器
    const router = createRouter({
    history: createWebHistory(),
    routes: routes
    });

    export default router
  1. 将路由配置全局
    在main.js文件中导入路由并挂载
  2. 在根组件中使用路由标签挂载
    1
    <router-view></router-view>
  3. 在需要跳转的函数或按钮中添加路由

子路由

子路由可以控制主页面都一部分的内容改变
例如:点击左侧,中间内容会改变

实现:

  1. 配置路由关系
    在原来的路由关系中,导入组件,并定义子路由关系
  2. 在分类菜单中加入对应的路由
    index是因为路由关系在我的index.js文件夹中
  3. 在需要展示的页面中的中间部份用路由标签挂载

pinia 携带请求头

一般登录成功后后端会传来一个token,用来确定身份,登陆后其他请求也都需要token验证

  1. 安装
    执行命令
    1
    npm install pinia
  2. pinia导入全局
  3. 定义store
  4. 组件中使用store

基础知识补充

==和===

  1. == 号用于比较两边的值是否相等
  2. === 号不仅判断两边的值是否相同还需要判断两边的类型的是否相同

    #default=”scope” 插槽

    实现功能:表格做判断
    使用template中的作用域插槽,它的作用是在外部获取组件内的数据 ,这里是为了获取这一行的数据,我们让slot-scope值为scope,那么由scope.row就可以得到数据了
    1
    2
    3
    4
    5
    6
    7
    <el-table-column label="操作" fixed="right" header-align="center" align="center" width="160">
    <template #default="scope">
    <el-button v-if="scope.row.PId == 0" v-auth="'projectscorekind:add'" type="primary" link @click="addOrUpdateHandle(false, state.dataList, scope.row)">新增</el-button>
    <el-button v-auth="'projectscorekind:edit'" type="primary" link @click="addOrUpdateHandle(true, state.dataList, scope.row)">修改</el-button>
    <el-button v-auth="'projectscorekind:delete'" type="primary" link @click="deleteHandle(scope.row.Id)">删除</el-button>
    </template>
    </el-table-column>
    利用了 v-if=”scope.row.PId == 0” 判断层数,第一层的Pid为0时显示增加按钮