跳到主要内容

Vue3

Vite构建+配置

使用yarn初始化项目

yarn create vite

Vite配置

修改默认端口

server: {
port: 8081
}

跨域问题

server: {
port: 8081,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
}
}
}

Vue

Ref与Reactive

ref修改需要value,reactive不需要

  • ref 能将任意类型的数据变为【响应式】的,Ref也可以像reactive一样包装对象
  • reactive 只能将对象类型变为【响应式】,对基本类型无效(例如 string,number,boolean)
const msg = ref("hello world")
const user = reactive({
name: "zhangsan",
age: 18
})
function change() {
msg.value = "hello vue3"
user.name = "lisi"
user.age = 20
}

页面中使用:不需要value

<h1>{{ msg }}</h1>
姓名: {{ user.name }}<br>
年龄: {{ user.age }}

属性绑定

  • 【:属性名】用来将标签属性与【响应式】变量绑定

这个是单向绑定,代码中属性变量,页面也变,但是页面变了,代码中不会改变

要双向绑定需要使用v-model

const buttonName=ref("按钮")
<input type="button" :value="buttonName">

事件绑定

  • 使用@click进行绑定
<h2>{{count}}</h2>
<input type="button" value="+" @click="add">
<input type="button" value="-" @click="del">
const count = ref(0)
function add() {
count.value++
}
function del() {
count.value--
}

表单绑定

  • 用 v-model 实现双向绑定,即
    • javascript 数据可以同步到表单标签
    • 反过来用户在表单标签输入的新值也会同步到 javascript 这边
  • 双向绑定只适用于表单这种带【输入】功能的标签,其它标签的数据绑定,单向就足够了
  • 复选框这种标签,双向绑定的 javascript 数据类型一般用数组
const user1 = ref({
name: "zhangsan",
age: 18,
sex: "男",
hobby: ["吃饭", "睡觉", "打豆豆"]
})
<div class="outer">
<div>
<label for="name">姓名:</label>
<input type="text" id="name" v-model="user1.name">
</div>
<div>
<label for="age">年龄:</label>
<input type="text" id="age" v-model="user1.age">
</div>
<div>
<label for="sex">性别:</label>
<input type="radio" value="" v-model="user1.sex">
<input type="radio" value="" v-model="user1.sex">
</div>
<div>
<label for="hobby">爱好:</label>
吃饭 <input type="checkbox" value="吃饭" v-model="user1.hobby">
睡觉 <input type="checkbox" value="睡觉" v-model="user1.hobby">
打豆豆 <input type="checkbox" value="打豆豆" v-model="user1.hobby">
</div>
<div>
<input type="button" value="提交" @click="saveUser">
</div>
</div>

计算属性

  • 有缓存功能
const firstName = ref("张")
const lastName = ref("三")
const fullName = computed(() => {
return firstName.value + lastName.value
})
{{ fullName}}

监听器

const name = ref("张三")
watch(name, (newVal, oldVal) => {
console.log(newVal, oldVal)
})
<input type="text" v-model="name">

组件

子组件如下:

通过defineProps定义属性,使用插值表达式使用,非可选加?

<template>
<div class="container">
<div class="card">
<div>
<p class="name">{{ name }}</p>
<p class="location">{{ country }}</p>
</div>
<img :src="avatar || '/src/assets/vue.svg'"/>
</div>
</div>
</template>
<script setup lang="ts">
defineProps<{ name: string, country: string, avatar?: string }>()
</script>

父组件使用:

<Child country="北京" name="蔡徐坤" />
<Child country="南京" name="蔡徐坤啊" avatar="/vite.svg" />

Vueuse

安装

npm i @vueuse/core

使用

<div>
X:{{ X }} <br>
Y:{{ Y }}
</div>
const {X, Y} = useMouse()

VueRequest

安装

yarn add vue-request

使用

配合计算属性一起用

const {data} = useRequest<AxiosRespList<Student>>(() => axios.get("/api/students"))
const students = computed(() => {
return data?.value?.data.data || []
})
<h3 v-if="students.length===0">暂无数据</h3>
<ul v-else>
<li v-for="item in students" :key="item.id">
{{ item.name }}
</li>
</ul>

Axios

安装

yarn add axios

简单实用

const getStudent = async () => {
// const res = await axios.get("http://localhost:8080/api/students")
const res = await axios.get("/api/students")
console.log(res)
}

onMounted(() => {
getStudent()
})

注意跨域问题:

本地发请求必须是:http://localhost:7070/ ,不可以写为127.0.0.1

环境变量

区分开发环境和生产环境,这件事交给构建工具 vite 来做

默认情况下,vite 支持上面两种环境,分别对应根目录下两个配置文件

  • .env.development - 开发环境
  • .env.production - 生产环境

针对以上需求,分别在两个文件中加入

VITE_BACKEND_API_BASE_URL = 'http://localhost:8080'
VITE_BACKEND_API_BASE_URL = 'http://xxx.com'

然后在代码中使用 vite 给我们提供的特殊对象 import.meta.env,就可以获取到 VITE_BACKEND_API_BASE_URL 在不同环境下的值

import.meta.env.VITE_BACKEND_API_BASE_URL

编辑器智能提示

做如下配置:新增文件 src/env.d.ts 并添加如下内容

interface ImportMetaEnv {
readonly VITE_BACKEND_API_BASE_URL: string
// 更多环境变量...
}

interface ImportMeta {
readonly env: ImportMetaEnv
}

封装自己的axios

在/src/api下面新建一个文件request.ts

import axios from "axios";

const _axios = axios.create({
baseURL: import.meta.env.VITE_BACKEND_API_BASE_URL
})

export default _axios

请求与响应拦截

// 请求拦截器
_axios.interceptors.request.use(
(config) => { // 统一添加请求头
config.headers['Authorization'] = 'cxk';
return config
},
(error) => { // 请求出错时的处理
return Promise.reject(error)
}
)

// 响应拦截器
_axios.interceptors.response.use(
(response) => { // 状态码 2xx
// 这里的code是自定义的错误码
if (response.data.code === 200) {
return response
} else if (response.data.code === 401) {
// 情况1
return Promise.resolve({})
}
// ...
},
(error) => { // 状态码 > 2xx, 400,401,403,404,500
console.error(error) // 处理了异常
if (error.response.status === 400) {
// 情况1
} else if (error.response.status === 401) {
// 情况2
}
// ...
return Promise.resolve({})
}
)

请求例子,封装类型interface

interface Student {
id: number,
name: string,
sex: string,
age: number
}
const students = ref<Student[]>([])
const getStudents = async () => {
const res = await axios.get('/api/students');
console.log(res.data);
students.value = res.data.data;
}

使用:

<div class="tbody">
<div v-if="students.length === 0">暂无数据</div>
<template v-else>
<div class="row" v-for="s of students">
<div class="col">{{ s.id }}</div>
<div class="col">{{ s.name }}</div>
<div class="col">{{ s.sex }}</div>
<div class="col">{{ s.age }}</div>
</div>
</template>
</div>

前端封装后端传来的类型

在src/model文件夹下面新建文件Model8080.ts

export interface Student {
id: number,
name: string,
sex: string,
age: number
}

// 如果 spring 错误,返回的对象格式
export interface SpringError {
timestamp: string,
status: number,
error: string,
message: string,
path: string
}

// 如果 spring 成功,返回 list 情况
export interface SpringList<T> {
data: T[],
message?: string,
code: number
}

// 如果 spring 成功,返回 page 情况
export interface SpringPage<T> {
code: number,
data: { list: T[], total: number },
message?: string
}

更改axios范型返回值

const res = await axios.get<SpringList<Student>>('/api/students');

Vue-router

安装

yarn add vue-router@4

创建路由

import {createRouter, createWebHashHistory} from 'vue-router'
import A51 from '../views/A51.vue'
import A52 from '../views/A52.vue'
// 路由 => 路径和组件之间的对应关系
const routes = [
{
path: '/a1',
component: A51
},
{
path: '/a2',
component: A52
}
]

const router = createRouter({
history: createWebHashHistory(), // 路径格式
routes: routes // 路由数组
})

export default router

动态导入(推荐):

{
path: '/a3',
component: () => import('../views/A53.vue')
}

使用

在main.ts中:

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

在父级容器中使用router-view

<template>
<div class="a5">
<router-view> </router-view>
</div>
</template>

嵌套路由

{
path: '/a3',
component: () => import('../views/A53.vue'),
children: [
{
path: 'student',
component: () => import('../views/A531.vue')
},
{
path: 'teacher',
component: () => import('../views/A532.vue')
}
]
}

重定向

访问/a3跳到/a3/student

访问不存在的跳到404页面,即a2

{
path: '/a3',
component: () => import('../views/A53.vue'),
redirect: '/a3/student',// 重定向 访问/a3时,自动跳转到/a3/student
children: [
{
path: 'student',
component: () => import('../views/A531.vue')
},
{
path: 'teacher',
component: () => import('../views/A532.vue')
}
]
},
{
//匹配不到路由时,跳转到404页面
path: '/:pathMatch(.*)*',
redirect: '/a2'
}

Pinia

安装

yarn add pinia

引入

在main.ts中引入pinia

import {createPinia} from "pinia";

createApp(App)
.use(router)
.use(createPinia())
.mount('#app')

Vuex

安装

yarn add vuex@next --save

使用

Ant Design of Vue

安装

yarn add ant-design-vue