1.vue3

1.1 vue3认识

vue3提供了更好的性能,更小的捆绑包体积,更好的TypeScript集成,用于处理大规模用例的新的API

(1)vue3比vue2更快

(2)vue3加入typescript的支持

(3)vue3没有beforecreate,created ,用setup取代

(4)vue3的组合式API和vue2的选项式API同时存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export default{
data(){
return{}
},
methods:{}
computed:{}
watch:{}
}

-----------------------------------------------------------------------------------------
<script setup>
import { ref, onMounted } from 'vue'

// 响应式状态
const count = ref(0)

// 用来修改状态、触发更新的函数
function increment() {
count.value++
}

// 生命周期钩子
onMounted(() => {
console.log(`计数器初始值为 ${count.value}。`)
})
</script>

(5)代码更利于维护 vue2—》vue3

1.2 搭建脚手架工程

如果以前安装过的 先卸载脚手架

npm uninstall vue-cli -g

1
npm install -g @vue/cli

安装好之后,通过

vue -V 查看版本

构建项目

vue create 项目名 回车

cd 项目名

npm run serve 运行项目即可

image-20231022223638891

浏览器访问:

image-20231022223723312

文件夹认识

image-20231022223748412

1.3 vue3的setup函数

setup函数:

(1)setup函数是组合式API的入口

(2)setup函数是启动之后自动运行的函数

修改HelloWorld.vue里面内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="hello">
1222222
</div>
</template>

<script>
export default {
setup(){
console.log('setup运行了')
}
}
</script>


<style scoped>

</style>

image-20231022223819025

(3)定义的变量和常量,方法 都放到setup函数里面去,最后都要return出去,才能在视图层中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<template>
<div class="hello">
{{ msg }}
<div v-for="(item,index) in arr" :key="index">
<h1>{{ item }}</h1>
</div>

<div>
<button @click="test">点击</button>
</div>
</div>
</template>

<script>
export default {
setup(){
console.log('setup运行了')
let msg = '测试1111'
let arr = ['1','2','3']

function test(){
console.log('oooooooooo')
}
return {msg,arr,test}
}
}
</script>


<style scoped>

</style>


1.4 ref函数

使用示例:

1
2
import {ref} from 'vue'
ref('xxx')

作用:通过ref包裹的数据,成为响应式的数据,可以通过 .value 属性获取或修改值。

说明:

当ref里面的值发生变化,视图里面更着变化

ref可以操作基本类型,也可以操作复杂类型,比如数组 对象

操作代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<template>
<div class="hello">
{{ msg }}
<div v-for="(item,index) in arr" :key="index">
<h1>{{ item }}</h1>
</div>

<div>{{ obj.name }}</div>
<div>
<button @click="test">点击</button>
</div>
</div>
</template>

<script setup>
import {ref} from 'vue'
//字符串
let msg = ref('测试1111')
//数组
let arr = ref(['1','2','3'])
//对象
let obj = ref({name:'zs',age:18})

function test(){
msg.value = 'hello ts3' //这个value是vue3通过proxy生成一个代理对象,然后通过value取值的
arr.value[0] = '10'
obj.value.name = 'lisi'
}
</script>


<style scoped>

</style>

建议ref操作基本类型 比如 数字 还有 字符串,操作复杂类型 通过reactive

1.5 reactive函数

1.5.1 reactive使用

reactive函数 也是返回一个响应式对象,reactive操作复杂的数据,比如数组和对象. 他返回响应式对象Proxy

注意: 定义基本普通类型,不能使用reactive,如下代码:

1
2
3
import {reactive} from 'vue'
//字符串
let msg = reactive('测试1111')

image-20231022223848849

测试对象和数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<template>
<div class="hello">

<div v-for="(item,index) in arr" :key="index">
<h1>{{ item }}</h1>
</div>

<div>{{ obj.name }}</div>
<div>
<button @click="test">点击</button>
</div>
</div>
</template>

<script setup>
import {reactive} from 'vue'

//数组
let arr = reactive(['1','2','3'])
//对象
let obj = reactive({name:'zs',age:18})

function test(){

arr[0] = '10'
obj.name = 'lisi'
}
</script>


<style scoped>

</style>

reactive可以操作更深层的对象,操作对象都建议使用reactive操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<template>
<div class="hello">

<div v-for="(item,index) in arr" :key="index">
<h1>{{ item }}</h1>
</div>

<div>{{ obj.name }}</div>
<div>{{ obj.pros.a.arr[0] }}</div>
<div>
<button @click="test">点击</button>
</div>
</div>
</template>

<script setup>
import {reactive} from 'vue'

//数组
let arr = reactive(['1','2','3'])
//对象
let obj = reactive(
{name:'zs',
age:18,
pros:{
a:{
arr:['我是深层次的数据']
}
}})

function test(){

arr[0] = '10'
obj.name = 'lisi'
obj.pros.a.arr[0] = '数据变化了'
}
</script>


<style scoped>

</style>

1.5.2 ref和reactive区别

ref 是可以操作基本数据类型和数组,对象类型,reactive只能操作数组 对象 这种复杂类型

reactive可以操作深层次对象,ref也可以定义数组和对象,ref会自动通过reactive转为代理proxy对象

ref操作数据需要.value,reactive不需要。

1.6 toRef函数

toRef:它也可以创建一个响应式的数据

ref的是一种数据拷贝,修改响应式数据是不会影响原始数据

toRef是本质是引用,和原始数据交换,修改响应式数据会影响原始数据.

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<template>
<div class="hello">

<div>ref里面的值:{{ num1 }}</div>
<div>toRef里面的值:{{ obj.name }}</div>

<div>
<button @click="test">点击</button>
</div>
</div>
</template>

<script setup>
import {ref,toRef} from 'vue'

let num = 0
let num1 = ref(num)
let obj = {name:'zhangsan'}
//第一个参数是对象或者数组,第二参数是对象里面的属性
let refv= toRef(obj,'name')


function test(){
num1.value ++
console.log('ref元数据值:'+num) //不会修改元数据
refv.value = 'zs' //会修改元数据
console.log('toRef元数据值:'+obj.name)
}
</script>


<style scoped>

</style>

1.7 toRefs函数

可以批量设置多个数据为响应式数据

toRefs还可以与其他响应式函数交互,更加方便处理视图层数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div class="hello">
<div>---------reactive-------</div>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<div>---------toRefs-------</div>
<div>{{ name }}</div>
<div>{{ age }}</div>

</div>
</template>

<script setup>
import {reactive,toRefs} from 'vue'
let obj = reactive({name:'zs',age:10})
//toRefs结合reactive使用
let {name,age} =toRefs(reactive({name:'zhangsan',age:18}))


</script>

<style scoped>

</style>

1.8 计算属性

computed计算属性,和vue2变化不大,都是用来监听数据的变化

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 class="hello">


<div>{{ result }}</div>
</div>
</template>

<script setup>
import {ref,computed} from 'vue'
let str1 = ref('hello')
let str2 = ref('world')

let result = computed(()=>{
return str1.value + '-' +str2.value
})

</script>

<style scoped>

</style>

computed 的 getter 和 setter

当 computed 有 getter 和 setter 时,需要传入一个对象而不是一个函数作为 computed 的参数,然后在 computed 中实现 get 和 set 两个属性方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<div class="hello">

<div><input type='text' v-model="result"/></div>
</div>
</template>

<script setup>
import {ref,computed} from 'vue'
let str1 = ref(10)
let str2 = ref(2)


let result = computed({
set(value){
console.log(value)
},
get(){
return str1.value + str2.value
}
})

</script>

<style scoped>

</style>


1.9 watch监听器

用来监听数据的变化

1
2
import {watch} from vue

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div class="hello">

<div><input type='text' v-model="p"/></div>
</div>
</template>

<script setup>
import {ref,watch} from 'vue'
let p = ref(0)

watch(p,(newVal,oldVal)=>{
console.log(newVal,oldVal)
})

</script>

<style scoped>

</style>

1.10 watchEffect

watchEffect监听的效果,在组件初始化的时候,会执行

watch可以监听新值和老值,而watchEffect拿不到

watchEffect不需要监听属性,在里面可以获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<h1 v-text='num'></h1>
<button @click="countFun">叠加num</button>
<button @click='stopAll'>停止监听</button>
</template>

<script setup>
import { ref, watchEffect } from 'vue'
let num = ref(0)

//监听
let wfn =watchEffect(()=>{
console.log('---执行监听', num.value)
})


//停掉监听
const stopAll = () => {
wfn()
}

let countFun= ()=>{
num.value ++
}
</script>

1.11 vue3的生命周期

`Vue3的组合式API提供了一套新的生命周期钩子,与Vue2中的选项式生命周期钩子有着对应关系。在Composition API中,组合式生命周期钩子有:

onBeforeMount:对应Vue2中的beforeMount钩子,Vue实例挂载之前调用。

onMounted:对应Vue2中的mounted钩子,Vue实例挂载完成后调用。

onBeforeUpdate:对应Vue2中的beforeUpdate钩子,数据更新时调用,但在DOM更新前。

onUpdated:对应Vue2中的updated钩子,数据更新后在DOM更新后调用。

onBeforeUnmount:对应Vue2中的beforeDestroy钩子,Vue实例销毁前调用。

onUnmounted:对应Vue2中的destroyed钩子,Vue实例销毁后调用。

生命周期4个主要事件

创建—在组件创建时候执行

挂载-DOM被挂载时执行

更新-当响应数据被修改时候执行

销毁-在元素被销毁之前立即执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
HelloWorld.vue代码
<template>
<h1 v-text='num'></h1>
<button @click="countFun">叠加num</button>
</template>

<script setup>
import { ref, onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted } from 'vue'
let num = ref(0)

let countFun = ()=>{
num.value ++
}
onBeforeMount(()=>{
console.log('onBeforeMount','在挂载开始之前调用')
})
onMounted(()=>{
//数据请求放到这里面
console.log('onMounted','在挂载时调用')
})

onBeforeUpdate(()=>{
console.log('onBeforeUpdate','在更新之前调用')
})
onUpdated(()=>{
console.log('onUpdated','在更新调用')
})
onBeforeUnmount(()=>{
console.log('onBeforeUnmount','在销毁前调用')
})
onUnmounted(()=>{
console.log('onUnmounted','在销毁时候调用')
})

</script>

销毁测试: 在App.vue页面 设置隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>

<HelloWorld v-if="isShow"/>
<button @click="isShow = !isShow">隐藏</button>
</template>

<script >
import HelloWorld from './components/HelloWorld.vue'
import {ref} from 'vue'
export default {
name: 'App',
components: {
HelloWorld
},
setup(){
let isShow = ref(true)
return {isShow}
}
}
</script>

<style>

</style>

1.12 组件传值

1.12.1 父传子

父组件:

1
2
const p1 = reactive({name:'zs',age:18})
provide('p1',p1) //传

子组件:

1
const obj = inject('p1')

完整代码:

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div>我是父组件:{{p1 }}</div>
<HelloWorld />

</template>

<script setup>
import { provide, reactive } from 'vue';
import HelloWorld from './components/HelloWorld.vue'
let p1 = reactive({name:'zs',age:18})
provide("p1",p1) //传

</script>

<style>

</style>

HelloWorld.vue子组件

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>
<div>我是子组件:{{ obj }}</div>
</div>
</template>

<script setup>
import { inject } from 'vue'
let obj = inject('p1')


</script>

结果:

image-20231022223943126

1.12.2 子传父

通过defineEmits

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<div>我是子组件:{{ obj }}</div>
<button @click="toParent">点击我传到父组件</button>
</div>
</template>

<script setup>
import { inject,defineEmits } from 'vue'

let obj = inject('p1')
//接受回调函数
let emit = defineEmits(['toParent'])
//使用回调函数传值
const toParent=()=>{
emit('toParent','我是子数据')
}


</script>

父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>我是父组件:{{p1 }}</div>
<HelloWorld @to-parent="getSon"/>

</template>

<script setup>
import { provide, reactive } from 'vue';
import HelloWorld from './components/HelloWorld.vue'
let p1 = reactive({name:'zs',age:18})
provide("p1",p1) //传

const getSon = (sonValue)=>{
console.log("父组件获取子组件的Value:"+sonValue)
}

</script>

<style>

</style>




1.Element Plus认识

基于 Vue 3,面向设计师和开发者的组件库,提供很多组件,可以在项目中使用

官网地址:

https://element-plus.org/zh-CN/

2.搭建vue3+ts脚手架

(1)创建项目文件夹 ,比如 vue3_02

(2) vue create 项目名

(3) 选择最后 手动安装

image-20231022224559557

(4) 选择对应的组件

image-20231022224615896

光标到对应行,然后按空格即可选中需要的配置

image-20231022224638413

选中之后 回车:

image-20231022224702774

注意:下面的lint 先默认选择第一个 回车 选择 Lint on save

image-20231022224717647

配置好的目录结构:

image-20231022224735335

启动访问的效果:

image-20231022224750494

3.ElementPlus安装和配置

3.1 安装elementplus

1
npm install element-plus --save

3.2 配置elementplus

在main.ts里面配置

1
2
3
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
app.use(ElementPlus)

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

createApp(App)
.use(store)
.use(router)
.use(ElementPlus)
.mount('#app')

修改HelloWorld.vue里面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div class="hello">
<el-button>Default</el-button>
<el-button type="primary">Primary</el-button>
<el-button type="success">Success</el-button>
<el-button type="info">Info</el-button>
<el-button type="warning">Warning</el-button>
<el-button type="danger">Danger</el-button>

</div>
</template>

<script lang="ts" setup>


</script>

浏览器访问:

image-20231022224814165

4.ElementPlus组件使用

4.1 Button按钮组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<template>

<el-row>
<el-button>Default</el-button>
<el-button type="primary">Primary</el-button>
<el-button type="success">Success</el-button>
<el-button type="info">Info</el-button>
<el-button type="warning">Warning</el-button>
<el-button type="danger">Danger</el-button>
</el-row>

<el-row class="mb-4">
<el-button plain>Plain</el-button>
<el-button type="primary" plain>Primary</el-button>
<el-button type="success" plain>Success</el-button>
<el-button type="info" plain>Info</el-button>
<el-button type="warning" plain>Warning</el-button>
<el-button type="danger" plain>Danger</el-button>
</el-row>

<el-row class="mb-4">
<el-button round>Round</el-button>
<el-button type="primary" round>Primary</el-button>
<el-button type="success" round>Success</el-button>
<el-button type="info" round>Info</el-button>
<el-button type="warning" round>Warning</el-button>
<el-button type="danger" round>Danger</el-button>
</el-row>

<el-row>
<el-button :icon="Search" circle />
<el-button type="primary" :icon="Edit" circle />
<el-button type="success" :icon="Check" circle />
<el-button type="info" :icon="Message" circle />
<el-button type="warning" :icon="Star" circle />
<el-button type="danger" :icon="Delete" circle />
</el-row>
</template>

<script lang="ts" setup>
import {
Check,
Delete,
Edit,
Message,
Search,
Star,
} from '@element-plus/icons-vue'

</script>

效果:

image-20231022224840700

4.2 布局layout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<template>

<el-row>
<el-col :span="24"><div class="grid-content ep-bg-purple-dark" ></div></el-col>
</el-row>
<el-row>
<el-col :span="12"><div class="grid-content ep-bg-purple" ></div></el-col>
<el-col :span="12"><div class="grid-content ep-bg-purple-light" ></div></el-col>
</el-row>
<el-row>
<el-col :span="8"><div class="grid-content ep-bg-purple" ></div></el-col>
<el-col :span="8"><div class="grid-content ep-bg-purple-light" ></div></el-col>
<el-col :span="8"><div class="grid-content ep-bg-purple" ></div></el-col>
</el-row>
<el-row>
<el-col :span="6"><div class="grid-content ep-bg-purple" ></div></el-col>
<el-col :span="6"><div class="grid-content ep-bg-purple-light" ></div></el-col>
<el-col :span="6"><div class="grid-content ep-bg-purple" ></div></el-col>
<el-col :span="6"><div class="grid-content ep-bg-purple-light" ></div></el-col>
</el-row>
<el-row>
<el-col :span="4"><div class="grid-content ep-bg-purple" ></div></el-col>
<el-col :span="4"><div class="grid-content ep-bg-purple-light" ></div></el-col>
<el-col :span="4"><div class="grid-content ep-bg-purple" ></div></el-col>
<el-col :span="4"><div class="grid-content ep-bg-purple-light" ></div></el-col>
<el-col :span="4"><div class="grid-content ep-bg-purple" ></div></el-col>
<el-col :span="4"><div class="grid-content ep-bg-purple-light" ></div></el-col>
</el-row>
</template>

<script lang="ts" setup>


</script>

<style lang="scss">
.el-row {
margin-bottom: 20px;
}
.el-row:last-child {
margin-bottom: 0;
}
.el-col {
border-radius: 4px;
}

.grid-content {
border-radius: 4px;
min-height: 36px;
background-color: antiquewhite;

}
.ep-bg-purple{
background-color: black;
}
.ep-bg-purple-light{
background-color: aquamarine;
}
</style>

效果:

image-20231022224904644

4.3 Container 布局容器

用于布局的容器组件,方便快速搭建页面的基本结构:

<el-container>:外层容器。 当子元素中包含 <el-header><el-footer> 时,全部子元素会垂直上下排列, 否则会水平左右排列。

<el-header>:顶栏容器。

<el-aside>:侧边栏容器。

<el-main>:主要区域容器。

<el-footer>:底栏容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<template>
<el-container>
<el-header>Header</el-header>
<el-container style="height: 500px;">
<el-aside width="200px" >Aside</el-aside>
<el-container>
<el-main>Main</el-main>
<el-footer>Footer</el-footer>
</el-container>
</el-container>
</el-container>
</template>

<script lang="ts" setup>


</script>

<style lang="scss">
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}

.el-aside {
background-color: #D3DCE6;
color: #333;
text-align: center;
line-height: 200px;
}

.el-main {
background-color: #E9EEF3;
color: #333;
text-align: center;
line-height: 160px;
}

body > .el-container {
margin-bottom: 40px;
}

.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}

.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
</style>

4.4 Icon 图标

main.ts引入所有图标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}


app.use(store)
.use(router)
.use(ElementPlus)
.mount('#app')

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<el-icon :size="30" :color="color">
<Edit />
</el-icon>
</template>

<script lang="ts" setup>
import {ref} from 'vue'

let color = ref('#ff0011')

</script>

效果:

image-20231022224941344

参考页面:

https://element-plus.org/zh-CN/component/icon.html

4.5 link链接

用法:

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>
<el-link href="https://element-plus.org" target="_blank">default</el-link>
<el-link type="primary" :underline="false">去下划线链接</el-link>
<el-link type="success" disabled>禁用链接</el-link>
<el-link type="warning">warning</el-link>
<el-link type="danger">danger</el-link>
<el-link type="info">info</el-link>
</div>
</template>

<script lang="ts" setup>

</script>

<style scoped>
.el-link {
margin-right: 8px;
}
.el-link .el-icon--right.el-icon {
vertical-align: text-bottom;
}
</style>

效果:

image-20231022225025240

4.6 Text文本

4.6.1 基本文本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<el-text class="mx-1" style="margin-right: 10px;">Default</el-text>
<el-text class="mx-1" type="primary" style="margin-right: 10px;">Primary</el-text>
<el-text class="mx-1" type="success" style="margin-right: 10px;">Success</el-text>
<el-text class="mx-1" type="info" style="margin-right: 10px;">Info</el-text>
<el-text class="mx-1" type="warning" style="margin-right: 10px;">Warning</el-text>
<el-text class="mx-1" type="danger" style="margin-right: 10px;">Danger</el-text>
</template>

<script lang="ts" setup>

</script>

效果:

image-20231022225050771

4.6.2 文本省略

通过 truncated 属性,在文本超过视图或最大宽度设置时展示省略符。

1
2
3
4
5
<el-row>
<el-col :span="4">
<el-text truncated>Squeezed by parent element111111111111111111</el-text>
</el-col>
</el-row>

效果:

image-20231022225110544

4.6.3 覆盖

使用属性 tag 覆盖元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<el-space direction="vertical">
<el-text>span</el-text>
<el-text tag="p">This is a paragraph.</el-text>
<el-text tag="b">Bold</el-text>
<el-text tag="i">Italic</el-text>
<el-text>
This is
<el-text tag="sub" size="small">subscript</el-text>
</el-text>
<el-text>
This is
<el-text tag="sup" size="small">superscript</el-text>
</el-text>
<el-text tag="ins">Inserted</el-text>
<el-text tag="del">Deleted</el-text>
<el-text tag="mark">Marked</el-text>
</el-space>

效果:

image-20231022225134437

4.7 form表单组件

4.7.1 常用表单组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<template>
<el-form :model="form" label-width="120px" >
<el-form-item label="Activity name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="Activity zone">
<el-select v-model="form.region" placeholder="please select your zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="Activity time">
<el-col :span="11">
<el-date-picker
v-model="form.date1"
type="date"
placeholder="Pick a date"
style="width: 100%"
/>
</el-col>
<el-col :span="2" class="text-center">
<span class="text-gray-500">-</span>
</el-col>
<el-col :span="11">
<el-time-picker
v-model="form.date2"
placeholder="Pick a time"
style="width: 100%"
/>
</el-col>
</el-form-item>
<el-form-item label="Instant delivery">
<el-switch v-model="form.delivery" />
</el-form-item>
<el-form-item label="Activity type">
<el-checkbox-group v-model="form.type">
<el-checkbox label="Online activities" name="type" />
<el-checkbox label="Promotion activities" name="type" />
<el-checkbox label="Offline activities" name="type" />
<el-checkbox label="Simple brand exposure" name="type" />
</el-checkbox-group>
</el-form-item>
<el-form-item label="Resources">
<el-radio-group v-model="form.resource">
<el-radio label="Sponsor" />
<el-radio label="Venue" />
</el-radio-group>
</el-form-item>
<el-form-item label="Activity form">
<el-input v-model="form.desc" type="textarea" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">Create</el-button>
<el-button>Cancel</el-button>
</el-form-item>
</el-form>
</template>

<script lang="ts" setup>
import { reactive } from 'vue'

// do not use same name with ref
const form = reactive({
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: '',
})

const onSubmit = () => {
console.log('submit!')
}
</script>

4.7.2 input数组输入框

要使用它,只需要在 <el-input-number> 元素中使用 v-model 绑定变量即可,变量的初始值即为默认值。

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<el-input-number v-model="num" :min="1" :max="10" @change="handleChange" />
</template>

<script lang="ts" setup>
import { ref } from 'vue'

const num = ref(1)
const handleChange = (value: number) => {
console.log(value)
}
</script>

效果:

image-20231022225530731

4.10 导航组件

菜单,使用还是比较多的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
@select="handleSelect"
>
<el-menu-item index="1">Processing Center</el-menu-item>
<el-sub-menu index="2">
<template #title>Workspace</template>
<el-menu-item index="2-1">item one</el-menu-item>
<el-menu-item index="2-2">item two</el-menu-item>
<el-menu-item index="2-3">item three</el-menu-item>
<el-sub-menu index="2-4">
<template #title>item four</template>
<el-menu-item index="2-4-1">item one</el-menu-item>
<el-menu-item index="2-4-2">item two</el-menu-item>
<el-menu-item index="2-4-3">item three</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<el-menu-item index="3" disabled>Info</el-menu-item>
<el-menu-item index="4">Orders</el-menu-item>
</el-menu>
<div class="h-6" />
<el-menu
:default-active="activeIndex2"
class="el-menu-demo"
mode="horizontal"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b"
@select="handleSelect"
>
<el-menu-item index="1">Processing Center</el-menu-item>
<el-sub-menu index="2">
<template #title>Workspace</template>
<el-menu-item index="2-1">item one</el-menu-item>
<el-menu-item index="2-2">item two</el-menu-item>
<el-menu-item index="2-3">item three</el-menu-item>
<el-sub-menu index="2-4">
<template #title>item four</template>
<el-menu-item index="2-4-1">item one</el-menu-item>
<el-menu-item index="2-4-2">item two</el-menu-item>
<el-menu-item index="2-4-3">item three</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<el-menu-item index="3" disabled>Info</el-menu-item>
<el-menu-item index="4">Orders</el-menu-item>
</el-menu>
</template>

<script lang="ts" setup>
import { ref } from 'vue'

const activeIndex = ref('1')
const activeIndex2 = ref('1')
const handleSelect = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
}
</script>

效果:

image-20231022225602366

4.11 Tabs 标签页

abs 组件提供了选项卡功能, 默认选中第一个标签页,你也可以通过 value 属性来指定当前选中的标签页。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane label="User" name="first">User</el-tab-pane>
<el-tab-pane label="Config" name="second">Config</el-tab-pane>
<el-tab-pane label="Role" name="third">Role</el-tab-pane>
<el-tab-pane label="Task" name="fourth">Task</el-tab-pane>
</el-tabs>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import type { TabsPaneContext } from 'element-plus'

const activeName = ref('first')

const handleClick = (tab: TabsPaneContext, event: Event) => {
console.log(tab, event)
}
</script>
<style>
.demo-tabs > .el-tabs__content {
padding: 32px;
color: #6b778c;
font-size: 32px;
font-weight: 600;
}
</style>

image-20231022225622590

4.12 反馈组件

4.12.1 dialog对话框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<template>
<el-button text @click="dialogVisible = true">
click to open the Dialog
</el-button>

<el-dialog
v-model="dialogVisible"
title="Tips"
width="30%"
:before-close="handleClose"
>
<span>This is a message</span>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogVisible = false">
Confirm
</el-button>
</span>
</template>
</el-dialog>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessageBox } from 'element-plus'

const dialogVisible = ref(false)

const handleClose = (done: () => void) => {
ElMessageBox.confirm('Are you sure to close this dialog?')
.then(() => {
done()
})
.catch(() => {
// catch error
})
}
</script>
<style scoped>
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>

效果:

image-20231022225643373

4.13 表格组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<template>
<el-table
border
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="日期"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>
</template>

<script lang="ts" setup>
import {reactive} from 'vue'

let tableData = reactive([{
date: '2024-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2024-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
date: '2024-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}, {
date: '2024-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}])

</script>

效果:

image-20231022225716076




1.nodejs和express后端服务

1.1 创建项目名

(1)在指定目录创建一个文件夹比如vue3_03

1.2 安装express-generator

(2)安装express-generator -g(快速生成express框架的结构)

1
npm install express-generator -g

1.3 创建express项目工程

1
2
3
4
5
6
7
8
express -e 项目名
比如 express -e backp

cd backp

npm install


1.4 项目热启动nodemon

1
npm install nodemon -g

修改package.json里面

image-20231022230031618

1.5 启动项目

npm start

image-20231022230050433

1.6 启动mongodb服务

image-20231022230115445

通过navicat连接mongodb 创建一个数据库

创建集合

插入数据

image-20231022230130605

1.7 node连接mongodb

安装一些 mongoose

1
npm install  mongoose@6.8.0

创建文件夹db/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//引入mongoose
const mongoose = require('mongoose')
//禁用严格查询
mongoose.set("strictQuery", false);
/**
*
* @param {链接成功回调} success
* @param {链接失败回调} error
*/
module.exports = function(success,error){

if(typeof error !== 'function'){
error = ()=>{
console.log('链接失败')
}
}


//链接数据库
mongoose.connect('mongodb://127.0.0.1:27017/abc')

//设置回调
mongoose.connection.on('open',()=>{
success()
})

mongoose.connection.on('error',()=>{
error()
})

mongoose.connection.on('close',()=>{
console.log('链接关闭')
})

}


在启动bin/www 下面链接数据库

image-20231022230150756

新创建一个models文件夹,然后在下面创建一个UserModel.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var mongoose = require('mongoose')

//创建文档结构对象,设置集合中 文档的属性和属性值类型
let userSchema = new mongoose.Schema({
name:String,
age:Number,
email:String,
tel:String

})
//创建模型对象--这个对象可以完成 增删改查操作 第一个参数是集合名称
let UserModel = mongoose.model('users',userSchema)

module.exports = UserModel

在路由文件里面引入 routers/users.js引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var express = require('express');
var router = express.Router();

const UserModel = require('./../models/UserModel')



/* GET users listing. */
router.get('/', function(req, res, next) {
UserModel.find((err,data)=>{
//判断
if(err){
console.log('读取失败')
return
}
//输出data
console.log(data)
})
res.send('respond with a resource');

});

module.exports = router;

启动项目测试:

npm start

2.vue3+ts+elementplus前端服务

2.1 创建项目文件夹

​ 比如 vue3_03/frontp

2.2 vue create 项目名

​ vue create 项目名

选择最后 手动安装

image-20231022230230235

2.3 选择对应的组件

image-20231022230248539

光标到对应行,然后按空格即可选中需要的配置

image-20231022230317038

选中之后 回车:

image-20231022230336369

image-20231022230350197

配置好的目录结构:

image-20231022230414534

启动访问的效果:

image-20231022230429888

2.4 安装elmentplus

npm install element-plus —save

在main.ts里面配置

1
2
3
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
app.use(ElementPlus)

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

createApp(App)
.use(store)
.use(router)
.use(ElementPlus)
.mount('#app')

修改HelloWorld.vue里面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div class="hello">
<el-button>Default</el-button>
<el-button type="primary">Primary</el-button>
<el-button type="success">Success</el-button>
<el-button type="info">Info</el-button>
<el-button type="warning">Warning</el-button>
<el-button type="danger">Danger</el-button>
</div>
</template>

<script lang="ts" setup>


</script>

浏览器访问:

image-20231022230447929

2.5 引入table组件

在HelloWorld里面引入table组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<template>

<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="日期"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>

</template>

<script lang="ts" setup>
import {reactive} from 'vue'
let tableData = reactive([{
date: '2024-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2024-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
date: '2024-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}, {
date: '2024-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}])



</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

3 前后端联调

3.1 前端axios

安装axios

1
npm i axios

引入axios,创建axios实例

请求拦截

响应拦截

暴露实例

在src创建api文件夹,在创建request.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import axios from 'axios'
const api = axios.create({
baseURL: 'http://localhost:3000',
timeout: 5000,
headers: {
"Content-type": 'application/json;charset:utf-8'
}
}
)
//请求拦截
api.interceptors.request.use((config: any) => {
if (localStorage.getItem('token')) {
config.headers.token = localStorage.getItem('token') || ''
}
return config
})
//响应拦截
api.interceptors.response.use((res: any) => {
const msg = res.data.msg || ''
const result = res.data.data || {}
if (msg === 'success') {
return result
} else {
return Promise.reject(res.data)
}
}, (err) => {
console.error(err)
})
export default api;

在HelloWorld.vue引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<template>

<el-table
:data="tableData.usersData"
style="width: 100%">

<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="email"
label="邮箱">
</el-table-column>
<el-table-column
prop="tel"
label="电话号码">
</el-table-column>
<el-table-column
prop="age"
label="年龄">
</el-table-column>
</el-table>

</template>

<script lang="ts" setup>
import {reactive,onMounted} from 'vue'
import api from '@/api/request'
let tableData = reactive({
usersData:[],
total:100
})

let getUserData= async ()=>{
try{
let result:[] = await api({ url:'/users', method:'get'});
tableData.usersData = result
}catch(err:any){
console.log(err);
} }


onMounted( ()=>{
getUserData()
})

</script>



3.2 后端解决跨域

在app.js里面引入:

1
2
3
4
5
6
7
8
9
10
//设置跨域请求
app.all('*', function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By", ' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
next();
});

在users.js查询返回数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var express = require('express');
var router = express.Router();

const UserModel = require('./../models/UserModel')

/* GET users listing. */
router.get('/', function(req, res, next) {
UserModel.find((err,data)=>{
//判断
if(err){
console.log('读取失败')
return
}
//输出data

res.json({msg:'success',data:data});
})


});

module.exports = router;

最终效果:

image-20231022230523392