前端快速工程化

前端 vs 后端

image-20240606103633092

ES6

  • ECMAScript(ES) 是规范、 JavaScript 是 ES 的实现
  • ES6 的第一个版本 在 2015 年 6 月发布,正式名称是《ECMAScript 2015 标准》(简称 ES2015)
  • ES6 指是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等

let

推荐使用let关键字替代 var关键字声明变量,因为 var存在诸多问题,比如:

跨域

1
2
3
4
5
6
{
var a = 1;
let b = 2;
}
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined

只会打印a,而b无法引用,所以a可以跨域,不安全

image-20240606093610335

重复申明

1
2
3
4
5
6
7
8
// var 可以声明多次
// let 只能声明一次
var m = 1
var m = 2
let n = 3
let n = 4
console.log(m) // 2
console.log(n) // Identifier 'n' has already been declared

image-20240606093849111

变量提升

1
2
3
4
5
6
// var 会变量提升,变量在初始化之前即可被引用
// let 不存在变量提升,变量初始化之前引用会报错
console.log(x); // undefined
var x = 10;
console.log(y); //ReferenceError: y is not defined
let y = 20;

image-20240606094050855

const

1
2
3
4
// 1. 声明之后不允许改变
// 2. 一但声明必须初始化,否则会报错
const a = 1;
a = 3; //Uncaught TypeError: Assignment to constant variable.

解构

数组解构

1
2
3
4
5
6
//1.数组解构
let arr = [1, 2, 3];
//以前我们想获取其中的值,只能通过角标。ES6 可以这样:
let [x, y, z] = arr;// x,y,z 将与 arr 中的每个位置对应来取值
// 然后打印
console.log(x, y, z);

对象解构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const person = {
name: "jack",
age: 21,
language: ['java', 'js', 'css']
}
// 解构表达式获取值,将 person 里面每一个属性和左边对应赋值
const {name, age, language} = person;
// 等价于下面
// const name = person.name;
// const age = person.age;
// const language = person.language;
// 可以分别打印
console.log(name);
console.log(age);
console.log(language);
//扩展:如果想要将 name 的值赋值给其他变量,可以如下,nn 是新的变量名
const {name: nn, age, language} = person;
console.log(nn);
console.log(age);
console.log(language);

链判断

如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。

比如,读取message.body.user.firstName这个属性,安全的写法是写成下面这样。

1
2
3
4
5
6
7
8
9
10
let  message = null;
// 错误的写法
const firstName = message.body.user.firstName || 'default';

// 正确的写法
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';
console.log(firstName)

这样的层层判断非常麻烦,因此 ES2020 引入了“链判断运算符”(optional chaining operator)?.,简化上面的写法。

1
const firstName = message?.body?.user?.firstName || 'default';

参数默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//在 ES6 以前,我们无法给一个函数参数设置默认值,只能采用变通写法:
function add(a, b) {
// 判断 b 是否为空,为空就给默认值 1
b = b || 1;
return a + b;
}
// 传一个参数
console.log(add(10));

//现在可以这么写:直接给参数写上默认值,没传就会自动使用默认值
function add2(a, b = 1) {
return a + b;
}

// 传一个参数
console.log(add2(10));

箭头函数

类似于Java中的lambda表达式

1
2
3
4
5
6
7
8
//以前声明一个方法
// var print = function (obj) {
// console.log(obj);
// }
// 可以简写为:
let print = obj => console.log(obj);
// 测试调用
print(100);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 两个参数的情况:
let sum = function (a, b) {
return a + b;
}
// 简写为:
//当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。
let sum2 = (a, b) => a + b;
//测试调用
console.log(sum2(10, 10));//20
// 代码不止一行,可以用`{}`括起来
let sum3 = (a, b) => {
c = a + b;
return c;
};
//测试调用
console.log(sum3(10, 20));//30

模板字符串

1
2
3
4
5
6
let info = "你好,我的名字是:【"+name+"】,年龄是:【"+age+"】,邮箱是:【】"
console.log(info);

# 模板字符串的写法
let info = `你好,我的名字是:${name},年龄是:${person.age},邮箱是:${person.email}`
console.log(info);

Promise

代表 异步对象,类似Java中的 CompletableFuture

promise 是现代 JavaScript 中异步编程的基础,是一个由异步函数返回的可以向我们指示当前操作所处的状态的对象。在 Promise 返回给调用者的时候,操作往往还没有完成,但 Promise 对象可以让我们操作最终完成时对其进行处理(无论成功还是失败)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//fetch:浏览器支持从远程获取数据的一个函数,这个函数返回的就是 Promise 对象
const fetchPromise = fetch(
"https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json"
);

console.log("开始发送请求...")
//响应成功的处理逻辑
fetchPromise.then((response) => {
console.log(`已收到响应:${response.status}`);
response.json().then(data => console.log(data))
});
//响应失败的处理逻辑
fetchPromise.catch(error => console.log(error));
console.log("已发送请求……");

image-20240606100712450

fetch api

  • fetch 是浏览器支持从远程获取数据的一个函数,这个函数返回的就是 Promise 对象
  • 通过 fetch() API 得到一个 Response 对象
    • response.status: 读取响应状态码
    • response.json():读取响应体json数据;(这也是个异步对象

Promise状态

Promise 有三种状态:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。这是调用 fetch() 返回 Promise 时的状态,此时请求还在进行中。
  • 已兑现(fulfilled):意味着操作成功完成。当 Promise 完成时,它的 then() 处理函数被调用。
  • 已拒绝(rejected):意味着操作失败。当一个 Promise 失败时,它的 catch() 处理函数被调用。

Promise对象

如何自定义Promise对象?

1
2
3
4
5
6
7
8
const promise = new Promise((resolve, reject) => {
// 执行异步操作
if (/* 异步操作成功 */) {
resolve(value);// 调用 resolve,代表 Promise 将返回成功的结果
} else {
reject(error);// 调用 reject,代表 Promise 会返回失败结果
}
});

自定义Promise对象示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let get = function (url, data) {
return new Promise((resolve, reject) => {
$.ajax({
url: url,
type: "GET",
data: data,
success(result) {
resolve(result);
},
error(error) {
reject(error);
}
});
})
}

Async 函数

async function 声明创建一个绑定到给定名称的新异步函数。函数体内允许使用 await 关键字,这使得我们可以更简洁地编写基于 promise 的异步代码,并且避免了显式地配置 promise 链的需要。

  • async 函数是使用async关键字声明的函数。async 函数是 AsyncFunction 构造函数的实例,并且其中允许使用 await 关键字。
  • async 和 await 关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用 promise。
  • async 函数 返回的还是 Promise对象
1
2
3
4
async function myFunction() {
// 这是一个异步函数

}

在异步函数中,你可以在调用一个返回 Promise 的函数之前使用 await 关键字。这使得代码在该点上等待,直到 Promise 被完成,这时 Promise 的响应被当作返回值,或者被拒绝的响应被作为错误抛出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
async function fetchProducts() {
try {
// 在这一行之后,我们的函数将等待 `fetch()` 调用完成
// 调用 `fetch()` 将返回一个“响应”或抛出一个错误
const response = await fetch(
"https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);
if (!response.ok) {
throw new Error(`HTTP 请求错误:${response.status}`);
}
// 在这一行之后,我们的函数将等待 `response.json()` 的调用完成
// `response.json()` 调用将返回 JSON 对象或抛出一个错误
const json = await response.json();
console.log(json[0].name);
} catch (error) {
console.error(`无法获取产品列表:${error}`);
}
}

fetchProducts();

模块化

实际项目中,不可能将所有的js代码写在某一个单独的文件中,在实际开发中需要模块化,

将 JavaScript 程序拆分为可按需导入的单独模块的机制。Node.js 已经提供这个能力很长时间了,还有很多的 JavaScript 库和框架已经开始了模块的使用

下面演示一个模块化示例,主要包含三个文件:

  • index.html:主页面
  • main.js:主js文件,里面从其他js文件中引入需要的内容
  • user.js:申明一些变量或函数供第三方使用

image-20240606103318267

index.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ES6规范</title>
//必须申明type为模块化 module
<script src="main.js" type="module"></script>
</head>
<body>
<h1>这是ES6模块化测试使用</h1>
</body>
</html>

user.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//在user.js中定义一个对象和一个对应的函数
const user = {
username: "张三",
age: 18
}

const isAdult = (age)=>{
if (age > 18){
console.log("成年人")
}else {
console.log("未成年")
}
}

//需要借助export关键字,向外暴露能被其他地方引用的对象
export {user,isAdult}

main.js

1
2
3
4
5
//从user.js中导入需要的对象
import {user,isAdult} from "./lib/user.js";

console.log(`姓名:${user.username},年龄:${user.age}`);
isAdult(user.age);

npm

npm 是 nodejs 中进行 包管理 的工具

以前引入第三方js,需要手动下载导入,现在只需用npm进行依赖的安装和管理

环境准备

  • 安装nodejs nodejs下载地址

  • 配置npm

    • ```shell
      npm config set registry https://registry.npmmirror.com #设置国内阿里云镜像源
      npm config get registry #查看镜像源
      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



      ### 常用命令

      - npm init: 项目初始化
      - npm init -y:默认一路yes,不用挨个输入信息
      - npm install 包名:安装js包到项目中(仅当前项目有效)。指定 **包名**,或者 **包名@版本号**
      - npm install -g: 全局安装,所有项目都能用
      - 可以去 [npm仓库](https://www.npmjs.com/) 搜索第三方库
      - npm update 包名:升级包到最新版本
      - npm uninstall 包名:卸载包
      - npm run:项目运行



      ### 使用示例

      创建一个文件目录`02-npm`,在其下执行`npm init -y`会初始化一个项目

      ![image-20240606104558876](%E5%89%8D%E7%AB%AF%E5%BF%AB%E9%80%9F%E5%B7%A5%E7%A8%8B%E5%8C%96/image-20240606104558876.png)

      目录中会多出一个`package.json`文件

      - **package.json** 文件一般都在每个项目的根目录下面,定义了这个项目所需要的各种模块,以及项目的配置信息,包括名称、版本、许可证、依赖模块等元数据。格式是严格的JSON格式
      - 当你执行 npm install 的时候,node 会先从 package.json 文件中读取所有 dependencies 信息,然后根据 dependencies 中的信息与 node_modules 中的模块进行对比,没有的直接下载,已有的检查更新。另外,package.json 文件只记录你通过 npm install 方式安装的模块信息,而这些模块所依赖的其他子模块的信息不会记录

      ![image-20240606104627076](%E5%89%8D%E7%AB%AF%E5%BF%AB%E9%80%9F%E5%B7%A5%E7%A8%8B%E5%8C%96/image-20240606104627076.png)

      安装所需依赖,假定我们需要使用jquery,则执行`npm install jquery`

      ![image-20240606104849023](%E5%89%8D%E7%AB%AF%E5%BF%AB%E9%80%9F%E5%B7%A5%E7%A8%8B%E5%8C%96/image-20240606104849023.png)

      ![image-20240606143059363](%E5%89%8D%E7%AB%AF%E5%BF%AB%E9%80%9F%E5%B7%A5%E7%A8%8B%E5%8C%96/image-20240606143059363.png)

      运行,`package.json`文件中的`scripts`中的命令我们可以通过`npm run xxx`来运行

      ![image-20240606143435515](%E5%89%8D%E7%AB%AF%E5%BF%AB%E9%80%9F%E5%B7%A5%E7%A8%8B%E5%8C%96/image-20240606143435515.png)

      我们分享或者从第三下载项目时,由于项目第三方依赖包很大,所以一般会把`node_modules`排除在外,那么我们在本地运行这些项目又需要第三方依赖怎么办?直接在项目中运行`npm install`其会自动根据`package.json`中依赖的第三方库及版本进行下载

      ### 总结:npm使用流程

      <img src="%E5%89%8D%E7%AB%AF%E5%BF%AB%E9%80%9F%E5%B7%A5%E7%A8%8B%E5%8C%96/1712110492836-0f787e3a-4b9c-4236-8250-3912df233a20.webp" alt="image.png" style="zoom:50%;" />

      ## Vite

      ### 概述

      [官网](https://cn.vitejs.dev)

      <img src="%E5%89%8D%E7%AB%AF%E5%BF%AB%E9%80%9F%E5%B7%A5%E7%A8%8B%E5%8C%96/image-20240606144119659.png" alt="image-20240606144119659" style="zoom:50%;" />

      - 快速创建前端项目脚手架
      - 统一的工程化规范:目录结构、代码规范、git提交规范 等
      - 自动化构建和部署:前端脚手架可以自动进行代码打包、压缩、合并、编译等常见的构建工作,可以通过集成自动化部署脚本,自动将代码部署到测试、生产环境等;

      ### 常用命令

      #### 创建项目

      ```shell
      npm create vite #根据向导选择技术栈

安装依赖

1
2
3
4
npm install #安装项目所有依赖

npm install axios #安装指定依赖到当前项目
npm install -g xxx # 全局安装

启动项目

1
npm run dev #启动项目

项目打包

1
npm run build #构建后 生成 dist 文件夹

项目部署

  • 前后分离方式:需要把 dist 文件夹内容部署到如 nginx 之类的服务器上
  • 前后不分离方式:把 dist 文件夹内容复制到 SpringBoot 项目 resources 下面

使用示例

创建项目

image-20240606144716638

安装依赖、启动项目

image-20240606145017250

访问项目

image-20240606145119005

项目打包

image-20240606145317517

image-20240606145411584

Vue3

Vue官方文档

我们可以利用Vite工具链快速创建一个Vue项目

组件化

组件系统是一个抽象的概念:

  • 组件:小型、独立、可复用的单元
  • 组合:通过组件之间的组合、包含关系构建出一个完整应用

几乎任意类型的应用界面都可以抽象为一个组件树

img

SFC

Vue 的单文件组件 (即 .vue 文件,英文 Single-File Component,简称 *SFC) 是一种特殊的文件格式,使我们能够将一个 Vue 组件的模板、逻辑与样式封装在单个文件中

1
2
3
4
5
6
7
8
9
10
11
<script setup>
//编写脚本
</script>

<template>
//编写页面模板
</template>

<style scoped>
//编写样式
</style>

Vue运行原理

运行原理.png

image-20240606151543916

Vue基础语法

插值

页面展示的某个内容来自于某个变量,如何将变量的值显示在页面的正确位置,需要用到插值,直接在html中需要的位置{{变量名}}即可引用变量值

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
<script setup>
//定义基本数据变量
let name="张三";
let age=27;
let scores={
"数学":129,
"英语":136,
"政治":89
}
</script>

<template>
<table>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>数学</th>
<th>英语</th>
<th>政治</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{name}}</td>
<td>{{age}}</td>
<td>{{scores.数学}}</td>
<td>{{scores.英语}}</td>
<td>{{scores.政治}}</td>
</tr>
</tbody>
</table>
</template>

<style scoped>

</style>

image-20240606152744403

指令

vue指令官方文档

v-on指令

可以为元素绑定特定的事件,比如点击按钮出发某个函数等操作

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup>
//顶一个对应的函数
function buy(){
alert("欢迎选购!!!");
}
</script>

<template>
<button v-on:click="buy">点击购买</button>
</template>

<style scoped>
</style>
条件判断v-if

比如根据某个条件,决定页面显示的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script setup>
//定义变量
let person={
name:"章三",
age:27
}
</script>

<template>
<div>
<!--根据age大小显示不同内容-->
<span style="color: red" v-if="person.age>18">成年人</span>
<span style="color: blue" v-else>未成年人</span>
</div>
</template>
<style scoped>
</style>
循环指令v-for

比如循环显示某个列表的内容

1
2
3
4
5
6
7
8
9
10
11
12
<script setup>
//定义变量
let persons=["章三","里斯","王五","赵六"]
</script>

<template>
<div>
<li v-for="person in persons">{{person}}</li>
</div>
</template>
<style scoped>
</style>

image-20240606160128431

属性绑定v-bind

动态地绑定一个或者多个属性

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup>
//定义变量
let url = "https://www.baidu.com"
</script>

<template>
<div>
<!-- 将a标签href属性与变量url动态绑定 -->
<a v-bind:href="url">跳转</a>
</div>
</template>
<style scoped>
</style>

注意:此时如果我们想修改变量的值,属性值并不会跟着修改

响应式

前面个通过指令绑定,数据地动态变化无法反馈到页面元素中

Vue通过ref()reactive()包装数据,将会生成一个数据的代理对象。vue内部的 基于依赖追踪的响应式系统 就会追踪感知数据变化,并触发页面的重新渲染

响应式-ref()
使用方式
  • 使用 ref() 包装原始类型、对象类型数据,生成 代理对象
  • 任何方法、js代码中,使用 代理对象.value 的形式读取和修改值
  • 页面组件中,直接使用 代理对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script setup>
//定义变量
import {ref} from "vue";
//ref生成代理对象
let num = ref(0);
let incr = function (){
num.value++;
}
</script>

<template>
<div>
<span><button v-on:click="incr">增加</button></span>
<!-- 动态显示num的值 -->
<h3>{{num}}</h3>
</div>
</template>
<style scoped>
</style>
响应式-reactive()
使用方式
  • 使用 reactive() 包装对象类型数据,生成 代理对象
  • 任何方法、js代码中,使用 代理对象.属性的形式读取和修改值
  • 页面组件中,直接使用 代理对象.属性
ref() vs reactive()

两者差异比较:

  • ref()可以包装原始类型、对象类型数据
  • reactive()只能包装对象类型数据

  • ref()使用时,需要对象.value.属性,而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
<script setup>
//定义变量
import {reactive, ref} from "vue";
//ref生成代理对象
let car1 = ref({
brand:"比亚迪",
price:10000
})

let car2 = reactive({
brand:"小米",
price:10000
})

//ref包装对象,要使用到值,需要: 对象.value.属性
let incr1 = function (){
car1.value.price+=10000;
}

//reactive包装对象,要使用到值只需: 对象.属性
let incr2 = function (){
car2.price+=1000;
}
</script>

<template>
<div>
<h2>汽车:{{car1.brand}},价格:{{car1.price}}</h2>
<h2>汽车:{{car2.brand}},价格:{{car2.price}}</h2>
<button @click="incr1">比亚迪涨价</button>
<button @click="incr2">小米涨价</button>
</div>
</template>
<style scoped>
</style>

表单绑定

前面我们使用的v-bind只能实现从数据到页面的单向绑定,数据变化可以在页面中体现出来,但是页面变化无法体现在数据变量中

我们可以使用v-model在表单输入元素或者组件上创建双向绑定,即数据改了可以传递到页面,页面改了可以传递到数据变量

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
79
80
81
82
83
84
85
86
<script setup>
import {reactive} from "vue";
//将data的各个属性与表单进行绑定
const data=reactive({
name:"章三",
agree:true,
hobby:[],
gender:"男",
degree:"",
course:[]
});
</script>

<template>
<div style="display: flex;">
<div style="border: 1px solid black;width: 300px">
<form>
<h1>表单绑定</h1>
<p style="background-color: azure">
<label>姓名(文本框):</label>
<input v-model="data.name"/>
</p>
<p style="background-color: azure"><label>同意协议(checkbox):</label>
<input type="checkbox" v-model="data.agree"/>
</p>
<p style="background-color: azure">
<label>兴趣(多选框):</label><br/>
<label><input type="checkbox" value="足球" v-model="data.hobby"/>足球</label>
<label><input type="checkbox" value="篮球" v-model="data.hobby"/>篮球</label>
<label><input type="checkbox" value="羽毛球" v-model="data.hobby"/>羽毛球</label>
<label><input type="checkbox" value="乒乓球" v-model="data.hobby"/>乒乓球</label>
</p>
<p style="background-color: azure">
<label>性别(单选框):</label>
<label><input type="radio" name="sex" value="男" v-model="data.gender">男</label>
<label><input type="radio" name="sex" value="女" v-model="data.gender">女</label>
</p>
<p style="background-color: azure">
<label>学历(单选下拉列表):</label>
<select v-model="data.degree">
<option disabled value="">选择学历</option>
<option>小学</option>
<option>初中</option>
<option>高中</option>
<option>大学</option>
</select>
</p>
<p style="background-color: azure">
<label>课程(多选下拉列表):</label>
<br/>
<select multiple v-model="data.course">
<option disabled value="">选择课程</option>
<option>语文</option>
<option>数学</option>
<option>英语</option>
<option>道法</option>
</select>
</p>
</form>
</div>
<div style="border: 1px solid blue;width: 200px">
<h1>结果预览</h1>
<p style="background-color: azure"><label>姓名:{{data.name}}</label></p>
<p style="background-color: azure"><label>同意协议:{{data.agree}}</label>
</p>
<p style="background-color: azure">
<label>兴趣:
<li v-for="h in data.hobby">{{h}}</li>
</label>
</p>
<p style="background-color: azure">
<label>性别:{{data.gender}}</label>
</p>
<p style="background-color: azure">
<label>学历:{{data.degree}}</label>
</p>
<p style="background-color: azure">
<label>课程:
<li v-for="c in data.course">{{c}}</li>
</label>
</p>
</div>
</div>
</template>
<style scoped>
</style>

image-20240606191540670

计算属性

根据已有的数据计算出新数据

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
<script setup>
import {computed, reactive} from "vue";

const car = reactive({
brand:"小米",
price:1000,
number:10
});
//总价由单价×数目得到
const totalPrice = computed(()=>car.price*car.number);

//涨价函数
let addPrice = function(){
car.price+=100;
};
</script>

<template>
<p>汽车品牌:{{car.brand}}</p>
<p>汽车单价:{{car.price}}</p>
<p>汽车数目:{{car.number}}</p>
<p>总价:{{totalPrice}}</p>
<button @click="addPrice">加价</button>
<button @click="car.number++">加数目</button>
</template>
<style scoped>
</style>

image-20240606192330189

vue进阶使用

监听 watch

vue3使用watch的注意事项

监听某个变量或者属性值的变化,然后执行对应的业务逻辑处理

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
<script setup>
import {computed, reactive, watch} from "vue";

const car = reactive({
brand:"小米",
price:1000,
number:1
});
//总价由单价×数目得到
const totalPrice = computed(()=>car.price*car.number);

//涨价函数
let addPrice = function(){
car.price+=100;
};

//监听数目的变化,如果汽车数目超过了5,则弹出限购提示
//对象的属性必须使用()=>对象.属性这种形式
watch(()=>car.number,()=>{
if(car.number>5){
alert("当前购买数量超出5");
}
})
</script>

<template>
<p>汽车品牌:{{car.brand}}</p>
<p>汽车单价:{{car.price}}</p>
<p>汽车数目:{{car.number}}</p>
<p>总价:{{totalPrice}}</p>
<button @click="addPrice">加价</button>
<button @click="car.number++">加数目</button>
</template>
<style scoped>
</style>

监听 watchEffect

watch只能监听单个ref或者reactive对象,我们可以使用watchEffective同时监听所有的ref或者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
<script setup>
import {computed, reactive, watch, watchEffect} from "vue";

const car = reactive({
brand:"小米",
price:1000,
number:1
});
//总价由单价×数目得到
const totalPrice = computed(()=>car.price*car.number);

//涨价函数
let addPrice = function(){
car.price+=100;
};

//监听数目的变化,如果汽车数目超过了5,则弹出限购提示
//监听价格变化,价格如果超出1500,则弹出太贵了的提示
//watchEffect会自动监听所有的ref或者reactive对象
watchEffect(()=>{
if(car.number>5){
alert("超出限购");
car.number=5;
}
if(car.price>1500){
alert("太贵了");
car.price=1500;
}
})
</script>

<template>
<p>汽车品牌:{{car.brand}}</p>
<p>汽车单价:{{car.price}}</p>
<p>汽车数目:{{car.number}}</p>
<p>总价:{{totalPrice}}</p>
<button @click="addPrice">加价</button>
<button @click="car.number++">加数目</button>
</template>
<style scoped>
</style>

Vue生命周期

官网关于生命周期的介绍

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。

img

生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁,每个阶段都有两个钩子,一前一后。

常用的钩子:

  • onBeforeMount:挂载之前
  • onMounte:挂载完成
  • onBeforeUpdate:更新之前
  • onUpdated:更新完成
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
<script setup>
import {onBeforeMount, onBeforeUpdate, onMounted, onUpdated, ref} from "vue";

const count = ref(0);
// 生命周期钩子
//常见场景:在挂载之前,即onBeforeMount之前向后台发送请求数据

//挂载之前有值,无页面元素
onBeforeMount(()=>{
console.log('挂载之前',count.value,document.getElementById("btn01"))
})
//挂载之后才有页面元素
onMounted(()=>{
console.log('挂载完毕',count.value,document.getElementById("btn01"))
})
//更新之前数据变了,但是页面内容未变动
onBeforeUpdate(()=>{
console.log('更新之前',count.value,document.getElementById("btn01").innerHTML)
})
onUpdated(()=>{
console.log('更新完毕',count.value,document.getElementById("btn01").innerHTML)
})
</script>

<template>
<button id="btn01" @click="count++">点{{count}}</button>
</template>
<style scoped>
</style>

组件传值

项目中每一个vue文件都相当于一个组件, 一个大型项目由很多组件组成,这些组件之间如何进行值的传递?

父传子-Prop

父组件给子组件传递值,单向数据流效果

  • 父组件修改值,子组件发生变化
  • 子组件修改值,父组件发生变化
1
2
3
4
5
6
7
8
9
10
11
12
//父组件给子组件传递数据:使用属性绑定
<Son :books="data.books" :money="data.money"/>

//子组件定义接受父组件的属性
let props = defineProps({
money: {
type: Number,
required: true,
default: 200
},
books: Array
});

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
//在父组件里,给子组件传递数据,使用属性绑定
<script setup>
import Son from "./Son.vue";
let money=1000
</script>

<template>
<h2>Father</h2>
<son :money="money"/>
</template>

<style scoped>
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
//在子组件里,定义接收父组件数据的属性
<script setup>
let props = defineProps(["money"])
</script>

<template>
<h2 style="color: red">son</h2>
<h3>money:{{money}}</h3>
</template>

<style scoped>

</style>
子传父-Emit
1
2
3
4
5
6
7
8
9
10
//子组件定义发生的事件
let emits = defineEmits(['buy']);
function buy(){
// props.money -= 5;
emits('buy',-5);
}

//父组件感知事件和接受事件值
<Son :books="data.books" :money="data.money"
@buy="moneyMinis"/>

插槽-slots

插槽也可以进行组件之间的传值(html元素)的传递

基本使用
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
//父组件
<script setup>

import Son from "./Son.vue";
import {reactive} from "vue";

const car=reactive({
brand:"小米",
price:1000
})

</script>

<template>
<h2>Father</h2>
<h3>汽车品牌:{{car.brand}}</h3>
<h3>汽车价格:{{car.price}}</h3>
<button @click="car.price+=100">涨价</button><br/>
<son :car="car">
哈哈,我是谁??
</son>
</template>

<style scoped>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//子组件
<script setup>
let props = defineProps(["car"])
</script>

<template>
<h2 style="color: red">son</h2>
<h3>汽车品牌:{{props.car.brand}}</h3>
<h3>汽车价格:{{props.car.price}}</h3>
<button @click="props.car.price-=100">降价</button>

<h4>来自于父组件使用slot传递的内容:<slot/></h4>
</template>

<style scoped>

</style>

image-20240607200626824

插槽可以带默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script setup>
let props = defineProps(["car"])
</script>

<template>
<h2 style="color: red">son</h2>
<h3>汽车品牌:{{props.car.brand}}</h3>
<h3>汽车价格:{{props.car.price}}</h3>
<button @click="props.car.price-=100">降价</button>

<h4>来自于父组件使用slot传递的内容:<slot>我是slot默认值</slot></h4>
</template>

<style scoped>

</style>

image-20240607200745242

具名插槽

当子组件中多个地方需要插槽,应当如何进行区分

对于子组件,给插槽取名称<slot name="名称"></slot>

对于父组件,使用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
25
26
27
28
29
30
31
32
//父组件
<script setup>

import Son from "./Son.vue";
import {reactive} from "vue";

const car=reactive({
brand:"小米",
price:1000
})

</script>

<template>
<h2>Father</h2>
<h3>汽车品牌:{{car.brand}}</h3>
<h3>汽车价格:{{car.price}}</h3>
<button @click="car.price+=100">涨价</button><br/>
<son :car="car">
<template v-slot:slot1>
插槽1
</template>
//v-slot可以用 #代替
<template #slot2>
<span style="color: red">插槽2</span>
</template>
</son>
</template>

<style scoped>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//子组件
<script setup>
let props = defineProps(["car"])
</script>

<template>
<h2 style="color: red">son</h2>
<h3>汽车品牌:{{props.car.brand}}</h3>
<h3>汽车价格:{{props.car.price}}</h3>
<button @click="props.car.price-=100">降价</button>

<h4>来自于父组件使用slot传递的内容:<slot>我是slot默认值</slot></h4>

<slot name="slot1"></slot>
<slot name="slot2"></slot>
</template>

<style scoped>

</style>

image-20240607201429542

vu3总结

未命名绘图.png

几个简写:

  • v-on=@

  • v-bind= :

  • v-slot= #

Vue-Router

路由概述

vue router官方文档

前端系统根据页面路径。跳转到指定的组件,展示对应的页面效果

img

路由入门示例

创建项目

1
2
npm create vite
#选择对应的项目和语言

整合vue-router

参照官方文档

1
2
#安装依赖
npm install vue-router@4

官方入门案例

路由配置的一般步骤

  1. 编写router/index.js文件
  2. index.js中配置路由信息
  3. index.js中创建路由器并导出
  4. main.js中使用路由
  5. 在相应的页面使用router-linkrouter-view完成路由功能

实例

router/index.js内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//1.配置路由信息
import home from "../views/home.vue";
import dog from "../views/dog.vue";
import {createMemoryHistory, createRouter} from "vue-router";
//定义路由规则,将路由与对应页面绑定
//有如下两种路由定义的写法
const routes = [
{path: '/',component: home},
{path: '/dog',component: dog},
{path: "/cat",component:()=>import('../views/cat.vue')}
]

//2.定义路由对象
const router = createRouter({
history: createMemoryHistory(),
routes
})

//3.导出路由,供main.js使用
export default router

main.js内容

1
2
3
4
5
6
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from "./router/index.js";
//4.注册路由插件,使用use(xx)
createApp(App).use(router).mount('#app')

App.vue内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script setup>
</script>

<template>
<div>
<!-- 使用组件 RouterLink 来创建链接 -->
<router-link to="/">首页Home</router-link>
<router-link to="/dog">dog页面</router-link>
<router-link to="/cat">cat页面</router-link>
<!-- RouterView 组件可以使 Vue Router 知道你想要在哪里渲染当前 URL 路径对应的路由组件 -->
<router-view/>
</div>

</template>

<style scoped>
</style>

路径参数

使用 :变量名接受动态参数;这个成为 路径参数,类似与Spring MVC中的path variable

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//1.配置路由信息
import home from "../views/home.vue";
import dog from "../views/dog.vue";
import {createMemoryHistory, createRouter} from "vue-router";
//定义路由规则,将路由与对应页面绑定
//有如下两种路由定义的写法
const routes = [
{path: '/',component: home},
{path: '/dog',component: dog},
//这样使用路由 /cat/11或者/cat/jkk或者/cat/xxx均指向的是cat.vue页面组件
{path: "/cat:id",component:()=>import('../views/cat.vue')}
]

//2.定义路由对象
const router = createRouter({
history: createMemoryHistory(),
routes
})

//3.导出路由,供main.js使用
export default router

嵌套路由

多级路由嵌套,比如从父页面跳转到子页面,再从子页面可以到孙页面

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
//路由编写规则
//1.配置路由信息
import home from "../views/home.vue";
import dog from "../views/dog.vue";
import {createMemoryHistory, createRouter} from "vue-router";
import user from "../views/user.vue";
import userEmail from "../views/userEmail.vue";
import userDetail from "../views/userDetail.vue";
//定义路由规则,将路由与对应页面绑定
//有如下两种路由定义的写法
const routes = [
{path: '/',component: home},
{
path: '/user/:id',
component: user,
children: [ //嵌套子页面
{
// 当 /user/:id/email 匹配成功
// userEmail 将被渲染到 User 的 <router-view> 内部
path: 'email',
component: userEmail,
},
{
// 当 /user/:id/detail 匹配成功
// userDetail 将被渲染到 User 的 <router-view> 内部
path: 'detail',
component: userDetail,
},
],
},

]

//2.定义路由对象
const router = createRouter({
history: createMemoryHistory(),
routes
})

//3.导出路由,供main.js使用
export default router

编程式

useRoute:路由数据

路由传参跳转到指定页面后,页面需要取到传递过来的值,可以使用 useRoute方法,拿到当前页路由数据;可以做

  • 获取到当前路径
  • 获取到组件名
  • 获取到参数
  • 获取到查询字符串
1
2
3
4
5
6
import {useRoute} from 'vue-router'
const route = useRoute()
// 打印query参数
console.log(route.query)
// 打印params参数
console.log(route.params)

useRouter:路由器

拿到路由器;可以控制跳转、回退等

1
2
3
4
5
6
7
8
import {useRoute, useRouter} from "vue-router";
const router = useRouter()
//push:跳到指定路由
router.push("/")

//go(1) 相当于浏览器上的下一个页面
//go(-1) 相当于浏览器回滚到上一个页面
router.go(-1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {useRoute, useRouter} from "vue-router";

const router = useRouter();

// 字符串路径
router.push('/users/eduardo')

// 带有路径的对象
router.push({ path: '/users/eduardo' })

// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })

// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })

// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

//注意: `params` 不能与 `path` 一起使用
router.push({ path: '/user', params: { username } }) //错误用法 -> /user

路由传参

params 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 跳转并携带params参数(to的字符串写法) -->
<RouterLink :to="`/news/detail/001/新闻001/内容001`">{{news.title}}</RouterLink>

<!-- 跳转并携带params参数(to的对象写法) -->
<RouterLink
:to="{
name:'xiang', //用name跳转,params情况下,不可用path
params:{
id:news.id,
title:news.title,
content:news.title
}
}"
>
{{news.title}}
</RouterLink>

query 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 跳转并携带query参数(to的字符串写法) -->
<router-link to="/news/detail?a=1&b=2&content=欢迎你">
跳转
</router-link>

<!-- 跳转并携带query参数(to的对象写法) -->
<RouterLink
:to="{
//name:'xiang', //用name也可以跳转
path:'/news/detail',
query:{
id:news.id,
title:news.title,
content:news.content
}
}"
>
{{news.title}}
</RouterLink>

导航守卫

具体使用细节参照导航守卫官方文档

类似于拦截器,再跳转到指定页面之前进行相应的逻辑处理

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
import {createRouter, createWebHistory} from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
},
{
path: '/user/:name',
name: 'User',
component: () => import('@/views/user/UserInfo.vue'),
children: [
{
path: 'profile',
component: () => import('@/views/user/Profile.vue')
},
{
path: 'posts',
component: () => import('@/views/user/Posts.vue')
}
]
}
]
})

router.beforeEach(async (to, from) => {
console.log("守卫:to:", to)
console.log("守卫:from:", from)
if (to.fullPath === '/about') {
return "/"
}
})

export default router

vue-router总结

未命名绘图001.png

Axios

概述

Axios官方文档

Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js,主要用于前端向后端请求数据

1
2
//安装
npm install axios
1
2
3
4
//基本用法
import axios from "axios"
axios.get('/user')
.then(res => console.log(resp.data))

请求

get请求

1
2
3
4
5
6
7
8
9
10
11
12
13
// 向给定ID的用户发起请求
axios.get('/user?ID=12345')
.then(function (response) {
// 处理成功情况
console.log(response);
})
.catch(function (error) {
// 处理错误情况
console.log(error);
})
.finally(function () {
// 总是会执行
});

携带请求参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 上述请求也可以按以下方式完成(可选)
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.finally(function () {
// 总是会执行
});

post请求

默认post请求体中的数据将会以json方式提交

1
2
3
4
5
6
7
8
9
10
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

响应

响应的数据结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
// `data` 由服务器提供的响应
data: {},

// `status` 来自服务器响应的 HTTP 状态码
status: 200,

// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',

// `headers` 是服务器响应头
// 所有的 header 名称都是小写,而且可以使用方括号语法访问
// 例如: `response.headers['content-type']`
headers: {},

// `config` 是 `axios` 请求的配置信息
config: {},

// `request` 是生成此响应的请求
// 在node.js中它是最后一个ClientRequest实例 (in redirects),
// 在浏览器中则是 XMLHttpRequest 实例
request: {}
}

配置

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});

//可用的配置项如下:
{
// `url` 是用于请求的服务器 URL
url: '/user',

// `method` 是创建请求时使用的方法
method: 'get', // 默认值

// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',

// `transformRequest` 允许在向服务器发送前,修改请求数据
// 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream
// 你可以修改请求头。
transformRequest: [function (data, headers) {
// 对发送的 data 进行任意转换处理

return data;
}],

// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
transformResponse: [function (data) {
// 对接收的 data 进行任意转换处理

return data;
}],

// 自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},

// `params` 是与请求一起发送的 URL 参数
// 必须是一个简单对象或 URLSearchParams 对象
params: {
ID: 12345
},

// `paramsSerializer`是可选方法,主要用于序列化`params`
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function (params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},

// `data` 是作为请求体被发送的数据
// 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法
// 在没有设置 `transformRequest` 时,则必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属: FormData, File, Blob
// - Node 专属: Stream, Buffer
data: {
firstName: 'Fred'
},

// 发送请求体数据的可选语法
// 请求方式 post
// 只有 value 会被发送,key 则不会
data: 'Country=Brasil&City=Belo Horizonte',

// `timeout` 指定请求超时的毫秒数。
// 如果请求时间超过 `timeout` 的值,则请求会被中断
timeout: 1000, // 默认值是 `0` (永不超时)

// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // default

// `adapter` 允许自定义处理请求,这使测试更加容易。
// 返回一个 promise 并提供一个有效的响应 (参见 lib/adapters/README.md)。
adapter: function (config) {
/* ... */
},

// `auth` HTTP Basic Auth
auth: {
username: 'janedoe',
password: 's00pers3cret'
},

// `responseType` 表示浏览器将要响应的数据类型
// 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'
// 浏览器专属:'blob'
responseType: 'json', // 默认值

// `responseEncoding` 表示用于解码响应的编码 (Node.js 专属)
// 注意:忽略 `responseType` 的值为 'stream',或者是客户端请求
// Note: Ignored for `responseType` of 'stream' or client-side requests
responseEncoding: 'utf8', // 默认值

// `xsrfCookieName` 是 xsrf token 的值,被用作 cookie 的名称
xsrfCookieName: 'XSRF-TOKEN', // 默认值

// `xsrfHeaderName` 是带有 xsrf token 值的http 请求头名称
xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值

// `onUploadProgress` 允许为上传处理进度事件
// 浏览器专属
onUploadProgress: function (progressEvent) {
// 处理原生进度事件
},

// `onDownloadProgress` 允许为下载处理进度事件
// 浏览器专属
onDownloadProgress: function (progressEvent) {
// 处理原生进度事件
},

// `maxContentLength` 定义了node.js中允许的HTTP响应内容的最大字节数
maxContentLength: 2000,

// `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数
maxBodyLength: 2000,

// `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。
// 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),
// 则promise 将会 resolved,否则是 rejected。
validateStatus: function (status) {
return status >= 200 && status < 300; // 默认值
},

// `maxRedirects` 定义了在node.js中要遵循的最大重定向数。
// 如果设置为0,则不会进行重定向
maxRedirects: 5, // 默认值

// `socketPath` 定义了在node.js中使用的UNIX套接字。
// e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程。
// 只能指定 `socketPath` 或 `proxy` 。
// 若都指定,这使用 `socketPath` 。
socketPath: null, // default

// `httpAgent` and `httpsAgent` define a custom agent to be used when performing http
// and https requests, respectively, in node.js. This allows options to be added like
// `keepAlive` that are not enabled by default.
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),

// `proxy` 定义了代理服务器的主机名,端口和协议。
// 您可以使用常规的`http_proxy` 和 `https_proxy` 环境变量。
// 使用 `false` 可以禁用代理功能,同时环境变量也会被忽略。
// `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据。
// 这将设置一个 `Proxy-Authorization` 请求头,它会覆盖 `headers` 中已存在的自定义 `Proxy-Authorization` 请求头。
// 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https`
proxy: {
protocol: 'https',
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},

// see https://axios-http.com/zh/docs/cancellation
cancelToken: new CancelToken(function (cancel) {
}),

// `decompress` indicates whether or not the response body should be decompressed
// automatically. If set to `true` will also remove the 'content-encoding' header
// from the responses objects of all decompressed responses
// - Node only (XHR cannot turn off decompression)
decompress: true // 默认值

}

拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});

pina

概述

官方文档

Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态
背景:

对于一个web页面,用户登录之后,其他各个组件需要使用用户的信息,这个时候我们可以使用前面讲过的vue-router路由传参或者vue组件传值的方式来进行数据传输,但是这两个方式比较繁琐,所以需要引入pinia,来进行库组件数据共享。

image-20240611225430746

Pinia 三个核心概念:

  • State:表示 Pinia Store 内部保存的数据(data)
  • Getter:可以认为是 Store 里面数据的计算属性(computed)
  • Actions:是暴露修改数据的几种方式。

虽然外部也可以直接读写Pinia Store 中保存的data,但是我们建议使用Actions暴露的方法操作数据更加安全

img

实例

安装pinia
1
npm install npm
main.js
1
2
3
4
5
6
7
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import {createPinia} from "pinia";
//创建一个 pinia 实例 (根 store) 并将其传递给应用
const pina = createPinia()
createApp(App).use(pina).mount('#app')
定义存储单元

store/money.js中编写如下内容,定义存储单元

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {defineStore} from "pinia";

export const moneyStore = defineStore("money",{
state:()=>({"money":100}),
getters:{
//人名币
rmb:(state)=>state["money"],
//美元
usb:(state)=>state["money"]*6,
//欧元
er:(state)=>state["money"]*11
},
actions:{
win(args){
this["money"]+=args
},
pay(args){
this["money"]-=args
}
}
})
组件中使用存储单元

showMoney.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<h2>账户余额</h2>
<h3>人名币:{{money.rmb}}</h3>
<h3>美元:{{money.usb}}</h3>
<h3>欧元:{{money.er}}</h3>
</div>
</template>

<script setup>
import {moneyStore} from "../store/money.js";

let money = moneyStore()
</script>

<style scoped>
div{
color: #888888;
}
</style>

actionMoney.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<button @click="guaguale">刮刮乐</button>
<button @click="shop">购物</button>
</template>

<script setup>
import {moneyStore} from "../store/money.js";
let money = moneyStore()
function guaguale(){
money.win(100);
}
function shop(){
money.pay(10);
}
</script>

<style scoped>

</style>

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
<script setup>
import ActionMoney from "./components/actionMoney.vue";
import ShoMoney from "./components/shoMoney.vue";
</script>

<template>
<sho-money/>
<action-money/>
</template>

<style scoped>
</style>

image-20240611232311652

Setup Store

也存在另一种定义 store 的可用语法。与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。

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
import {defineStore} from "pinia";
import {computed, ref} from "vue";

// export const moneyStore = defineStore("money",{
// state:()=>({"money":100}),
// getters:{
// //人名币
// rmb:(state)=>state["money"],
// //美元
// usb:(state)=>state["money"]*6,
// //欧元
// er:(state)=>state["money"]*11
// },
// actions:{
// win(args){
// this["money"]+=args
// },
// pay(args){
// this["money"]-=args
// }
// }
// })


export const moneyStore = defineStore("money",()=>{
const money=ref(100);
const rmb = computed(()=>money.value);
const usb = computed(()=>money.value*6);
const er = computed(()=>money.value*11);
const win = (arg)=>{
money.value+=arg;
}

function pay(arg){
money.value-=arg;
}
return {money,rmb,usb,er,win,pay}
})

脚手架

1
2
3
npm create vite  # 选择 使用 create-vue 自定义项目
npm create vue@latest # 直接使用create-vue 创建项目,会自动整合好vue-router和pinia、Axios等工具链,无需额外下载相关雨来
vue-cli #已经过时

Ant Design Vue

官方文档

一个第三方UI组件库,提供了许多漂亮的UI组件,可以直接引用,快速进行相关页面的开发

使用 npm create vue@latest 创建出项目脚手架,然后整合ant design vue

整合ant design vue

安装依赖

1
npm i --save ant-design-vue@4.x

全局注册: 编写main.js

1
2
3
4
5
6
7
8
import { createApp } from 'vue';
import Antd from 'ant-design-vue';
import App from './App';
import 'ant-design-vue/dist/reset.css';

const app = createApp(App);

app.use(Antd).mount('#app');

组件的使用

各个组件的详细用法,参考官方文档示例

参考资料

参考资料1

参考资料2