前端工程化
一:介绍:
前端工程化
是使用软件工程的方法
来单独
解决前端
的开发流程中模块化、组件化、规范化、自动化
的问题,其主要目的为了提高效率和降低成本。
前端工程化实现技术栈
前端工程化实现的技术栈有很多,我们采用ES6+nodejs+npm+Vite+VUE3+router+pinia+axios+Element-plus组合来实现
- ECMAScript6 VUE3中大量使用ES6语法
- Nodejs 前端项目运行环境
- npm 依赖下载工具
- vite 前端项目构建工具
- VUE3 优秀的渐进式前端框架
- router 通过路由实现页面切换
- pinia 通过状态管理实现组件数据传递
- axios ajax异步请求封装技术实现前后端数据交互
- Element-plus 可以提供丰富的快速构建网页的组件仓库
二、ECMA6Script:
2.1.介绍:
ECMAScript 6,简称ES6,是JavaScript 语言的一次重大更新。它于2015 年发布,是原来的ECMAScript标准的第六个版本。ES6带来了大量的新特性,包括箭头函数、模板字符串、let和const关键字、解构、默认参数值、模块系统等等,大大提升了JavaScript的开发体验。由于VUE3中大量使用了ES6的语法,所以ES6成为了学习VUE3的门槛之一
。
2.2.变量与模板字符串:
变量:
-
let 和var的差别
1、let 不能重复声明
2、let有块级作用域,非函数的花括号遇见let会有块级作用域,也就是只能在花括号里面访问。
3、let不会预解析进行变量提升
4、let 定义的全局变量不会作为window的属性
5、let在es6中推荐优先使用
-
const和var的差异
1、新增const和let类似,只是const定义的变量不能修改
2、并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
模板字符串(template string)是增强版的字符串,用反引号(`)标识:
1、字符串中可以出现换行符
2、可以使用 ${xxx} 形式输出变量和拼接变量
html
<script>
// 1 多行普通字符串
let ulStr =
'<ul>'+
'<li>JAVA</li>'+
'<li>html</li>'+
'<li>VUE</li>'+
'</ul>'
console.log(ulStr)
// 2 多行模板字符串
let ulStr2 = `
<ul>
<li>JAVA</li>
<li>html</li>
<li>VUE</li>
</ul>`
console.log(ulStr2)
// 3 普通字符串拼接
let name ='张小明'
let infoStr =name+'被评为本年级优秀学员'
console.log(infoStr)
// 4 模板字符串拼接
let infoStr2 =`${name}被评为本年级优秀学员`
console.log(infoStr2)
</script>
2.3.解构表达式:
ES6 的解构赋值是一种方便的语法,可以快速将数组或对象中的值拆分并赋值给变量。解构赋值的语法使用花括号 {}
表示对象,方括号 []
表示数组。通过解构赋值,函数更方便进行参数接受等。
数组解构赋值
- 可以通过数组解构将数组中的值赋值给变量,语法为:
javascript
let [a, b, c] = [1, 2, 3]; //新增变量名任意合法即可,本质是按照顺序进行初始化变量的值
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
- 该语句将数组 [1, 2, 3] 中的第一个值赋值给 a 变量,第二个值赋值给 b 变量,第三个值赋值给 c 变量。 可以使用默认值为变量提供备选值,在数组中缺失对应位置的值时使用该默认值。例如:
javascript
let [a, b, c, d = 4] = [1, 2, 3];
console.log(d); // 4
对象解构赋值
- 可以通过对象解构将对象中的值赋值给变量,语法为:
javascript
let {a, b} = {a: 1, b: 2};
//新增变量名必须和属性名相同,本质是初始化变量的值为对象中同名属性的值
//等价于 let a = 对象.a let b = 对象.b
console.log(a); // 1
console.log(b); // 2
- 该语句将对象 {a: 1, b: 2} 中的 a 属性值赋值给 a 变量,b 属性值赋值给 b 变量。 可以为标识符分配不同的变量名称,使用 : 操作符指定新的变量名。例如:
javascript
let {a: x, b: y} = {a: 1, b: 2};
console.log(x); // 1
console.log(y); // 2
函数参数解构赋值
- 解构赋值也可以用于函数参数。例如:
javascript
function add([x, y]) {
return x + y;
}
add([1, 2]); // 3
- 该函数接受一个数组作为参数,将其中的第一个值赋给 x,第二个值赋给 y,然后返回它们的和。
- ES6 解构赋值让变量的初始化更加简单和便捷。通过解构赋值,我们可以访问到对象中的属性,并将其赋值给对应的变量,从而提高代码的可读性和可维护性。
2.4 es6的箭头函数
ES6 允许使用"箭头" 义函数。语法类似Java中的Lambda表达式
2.4.1 声明和特点
html
<script>
//ES6 允许使用"箭头"(=>)定义函数。
//1. 函数声明
let fn1 = function(){}
let fn2 = ()=>{} //箭头函数,此处不需要书写function关键字
let fn3 = x =>{} //单参数可以省略(),多参数无参数不可以!
let fn4 = x => console.log(x) //只有一行方法体可以省略{};
let fun5 = x => x + 1 //当函数体只有一句返回值时,可以省略花括号和 return 语句
//2. 使用特点 箭头函数this关键字
// 在 JavaScript 中,this 关键字通常用来引用函数所在的对象,
// 或者在函数本身作为构造函数时,来引用新对象的实例。
// 但是在箭头函数中,this 的含义与常规函数定义中的含义不同,
// 并且是由箭头函数定义时的上下文来决定的,而不是由函数调用时的上下文来决定的。
// 箭头函数没有自己的this,this指向的是外层上下文环境的this
let person ={
name:"张三",
showName:function (){
console.log(this) // 这里的this是person
console.log(this.name)
},
viewName: () =>{
console.log(this) // 这里的this是window
console.log(this.name)
}
}
person.showName()
person.viewName()
//this应用
function Counter() {
this.count = 0;
setInterval(() => {
// 这里的 this 是上一层作用域中的 this,即 Counter实例化对象
this.count++;
console.log(this.count);
}, 1000);
}
let counter = new Counter();
</script>
2.4.2.使用举例:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#xdd{
display: inline-block;
width: 200px;
height: 200px;
background-color: red;
}
</style>
</head>
<body>
<div id="xdd"></div>
<script>
let xdd = document.getElementById("xdd");
// 方案1
xdd.onclick = function(){
console.log(this)
let _this= this; //this 是xdd
//开启定时器
setTimeout(function(){
console.log(this)
//变粉色
_this.style.backgroundColor = 'pink';
},2000);
}
// 方案2
xdd.onclick = function(){
console.log(this)
//开启定时器
setTimeout(()=>{
console.log(this)// 使用setTimeout() 方法所在环境时的this对象
//变粉色
this.style.backgroundColor = 'pink';
},2000);
}
</script>
</body>
</html>
对于方法一来说,setTimeout(function(){},2000);
,该方法的调用者是window对象,所以直接在内部用this设置是是真的window对象的,而我们想要操作的是xdd对象,所以我们可以用方法一的方法获取到外部的this,然后再内部使用。或者用方法二直接就用箭头函数获取到外部的this对象。
2.4.3 rest和spread
rest参数,在形参上使用 和JAVA中的可变参数几乎一样
html
<script>
// 1 参数列表中多个普通参数 普通函数和箭头函数中都支持
let fun1 = function (a,b,c,d=10){console.log(a,b,c,d)}
let fun2 = (a,b,c,d=10) =>{console.log(a,b,c,d)}
fun1(1,2,3)
fun2(1,2,3,4)
// 2 ...作为参数列表,称之为rest参数 普通函数和箭头函数中都支持 ,因为箭头函数中无法使用arguments,rest是一种解决方案
let fun3 = function (...args){console.log(args)}
let fun4 = (...args) =>{console.log(args)}
fun3(1,2,3)
fun4(1,2,3,4)
// rest参数在一个参数列表中的最后一个只,这也就无形之中要求一个参数列表中只能有一个rest参数
//let fun5 = (...args,...args2) =>{} // 这里报错
</script>
spread参数,在实参上使用rest
html
<script>
let arr =[1,2,3]
//let arrSpread = ...arr;// 这样不可以,...arr必须在调用方法时作为实参使用
let fun1 =(a,b,c) =>{
console.log(a,b,c)
}
// 调用方法时,对arr进行转换 转换为1,2,3
fun1(...arr)
//应用场景1 合并数组
let arr2=[4,5,6]
let arr3=[...arr,...arr2]
console.log(arr3)
//应用场景2 合并对象属性
let p1={name:"张三"}
let p2={age:10}
let p3={gender:"boy"}
let person ={...p1,...p2,...p3}
console.log(person)
</script>
2.5 es6的对象创建和拷贝
2.5.1 对象创建的语法糖
ES6中新增了对象创建的语法糖,支持了class extends constructor等关键字,让ES6的语法和面向对象的语法更加接近
javascript
class Person{
// 属性
#n;
age;
get name(){
return this.n;
}
set name(n){
this.n =n;
}
// 实例方法
eat(food){
console.log(this.age+"岁的"+this.n+"用筷子吃"+food)
}
// 静态方法
static sum(a,b){
return a+b;
}
// 构造器
constructor(name,age){
this.n=name;
this.age = age;
}
}
let person =new Person("张三",10);
// 访问对象属性
// 调用对象方法
console.log(person.name)
console.log(person.n)
person.name="小明"
console.log(person.age)
person.eat("火锅")
console.log(Person.sum(1,2))
class Student extends Person{
grade ;
score ;
study(){
}
constructor(name,age ) {
super(name,age);
}
}
let stu =new Student("学生小李",18);
stu.eat("面条")
2.5.2 对象的深拷贝和浅拷贝
对象的拷贝,快速获得一个和已有对象相同的对象的方式
- 浅拷贝
html
<script>
let arr =['java','c','python']
let person ={
name:'张三',
language:arr
}
// 浅拷贝,person2和person指向相同的内存
let person2 = person;
person2.name="小黑"
console.log(person.name)
</script>
- 深拷贝
html
<script>
let arr =['java','c','python']
let person ={
name:'张三',
language:arr
}
// 深拷贝,通过JSON和字符串的转换形成一个新的对象
let person2 = JSON.parse(JSON.stringify(person))
person2.name="小黑"
console.log(person.name)
console.log(person2.name)
</script>
2.6 es6的模块化处理
2.6.1模块化介绍
模块化是一种组织和管理前端代码的方式,将代码拆分成小的模块单元,使得代码更易于维护、扩展和复用。它包括了定义、导出、导入以及管理模块的方法和规范。前端模块化的主要优势如下:
- 提高代码可维护性:通过将代码拆分为小的模块单元,使得代码结构更为清晰,可读性更高,便于开发者阅读和维护。
- 提高代码可复用性:通过将重复使用的代码变成可复用的模块,减少代码重复率,降低开发成本。
- 提高代码可扩展性:通过模块化来实现代码的松耦合,便于更改和替换模块,从而方便地扩展功能。
> 目前,前端模块化有多种规范和实现,包括 CommonJS、AMD 和 ES6 模块化。ES6 模块化是 JavaScript 语言的模块标准,使用 import 和 export 关键字来实现模块的导入和导出。现在,大部分浏览器都已经原生支持 ES6 模块化,因此它成为了最为广泛使用的前端模块化标准. `
- ES6模块化的几种暴露和导入方式
- 分别导出
- 统一导出
- 默认导出
ES6中无论以何种方式导出,导出的都是一个对象,导出的内容都可以理解为是向这个对象中添加属性或者方法
2.6.2 分别导出
- module.js 向外分别暴露成员
javascript
//1.分别暴露
// 模块想对外导出,添加export关键字即可!
// 导出一个变量
export const PI = 3.14
// 导出一个函数
export function sum(a, b) {
return a + b;
}
// 导出一个类
export class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
}
}
- app.js 导入module.js中的成员
javascript
/*
*代表module.js中的所有成员
m1代表所有成员所属的对象
*/
import * as m1 from './module.js'
// 使用暴露的属性
console.log(m1.PI)
// 调用暴露的方法
let result =m1.sum(10,20)
console.log(result)
// 使用暴露的Person类
let person =new m1.Person('张三',10)
person.sayHello()
- index.html作为程序启动的入口 导入 app.js
html
<!-- 导入JS文件 添加type='module' 属性,否则不支持ES6的模块化 -->
<script src="./app.js" type="module" />
2.6.3 统一导出
- module.js向外统一导出成员
javascript
//2.统一暴露
// 模块想对外导出,export统一暴露想暴露的内容!
// 定义一个常量
const PI = 3.14
// 定义一个函数
function sum(a, b) {
return a + b;
}
// 定义一个类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
}
}
// 统一对外导出(暴露)
export {
PI,
sum,
Person
}
- app.js导入module.js中的成员
javascript
/*
{}中导入要使用的来自于module.js中的成员
{}中导入的名称要和module.js中导出的一致,也可以在此处起别名
{}中如果定义了别名,那么在当前模块中就只能使用别名
{}中导入成员的顺序可以不是暴露的顺序
一个模块中可以同时有多个import
多个import可以导入多个不同的模块,也可以是同一个模块
*/
//import {PI ,Person ,sum } from './module.js'
//import {PI as pi,Person as People,sum as add} from './module.js'
import {PI ,Person ,sum,PI as pi,Person as People,sum as add} from './module.js'
// 使用暴露的属性
console.log(PI)
console.log(pi)
// 调用暴露的方法
let result1 =sum(10,20)
console.log(result1)
let result2 =add(10,20)
console.log(result2)
// 使用暴露的Person类
let person1 =new Person('张三',10)
person1.sayHello()
let person2 =new People('李四',11)
person2.sayHello()
2.6.4 默认导出
- modules混合向外导出
javascript
// 3默认和混合暴露
/*
默认暴露语法 export default sum
默认暴露相当于是在暴露的对象中增加了一个名字为default的属性
三种暴露方式可以在一个module中混合使用
*/
export const PI = 3.14
// 导出一个函数
function sum(a, b) {
return a + b;
}
// 导出一个类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
}
}
// 导出默认
export default sum
// 统一导出
export {
Person
}
- app.js 的default和其他导入写法混用
javascript
/*
*代表module.js中的所有成员
m1代表所有成员所属的对象
*/
import * as m1 from './module.js'
import {default as add} from './module.js' // 用的少
import add2 from './module.js' // 等效于 import {default as add2} from './module.js'
// 调用暴露的方法
let result =m1.default(10,20)
console.log(result)
let result2 =add(10,20)
console.log(result2)
let result3 =add2(10,20)
console.log(result3)
// 引入其他方式暴露的内容
import {PI,Person} from './module.js'
// 使用暴露的Person类
let person =new Person('张三',10)
person.sayHello()
// 使用暴露的属性
console.log(PI)
三:Vue3:
Vue是一种流行的JavaScript框架,用于构建用户界面。它是一种渐进式框架,可以用来构建单页面应用程序(SPA)和动态网页。Vue具有简单易学的API,同时也提供了许多强大的功能,例如数据绑定、组件化、路由管理等。Vue的设计目标是使开发者能够更轻松地构建交互式的Web界面。
Vue3快速体验(非工程化方式):
html
<!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>
</head>
<body>
<!-- 这里也可以用浏览器打开连接,然后将获得的文本单独保存进入一个vue.js的文件,导入vue.js文件即可 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">
<!-- 给style属性绑定colorStyle数据 -->
<!-- {{插值表达式 直接将数据放在该位置}} -->
<h1 v-bind:style="colorStyle">{{headline}}</h1>
<!-- v-text设置双标签中的文本 -->
<p v-text="article"></p>
<!-- 给type属性绑定inputType数据 -->
<input v-bind:type ="inputType" value="helloVue3"> <br>
<!-- 给按钮单击事件绑定函数 -->
<button @click="sayHello()">hello</button>
</div>
<script>
//组合api
const app = Vue.createApp({
// 在setup内部自由声明数据和方法即可!最终返回!
setup(){
//定义数据
//在VUE中实现DOM的思路是: 通过修改修数据而影响页面元素
// vue3中,数据默认不是响应式的,需要加ref或者reactive处理,后面会详细讲解
let inputType ='text'
let headline ='hello vue3'
let article ='vue is awesome'
let colorStyle ={'color':'red'}
// 定义函数
let sayHello =()=>{
alert("hello Vue")
}
//在setup函数中,return返回的数据和函数可以在html使用
return {
inputType,
headline,
article,
colorStyle,
sayHello
}
}
});
//挂载到视图
app.mount("#app");
</script>
</body>
</html>
Vite项目搭建:
1.打开VSCode,在终端输入如下信息并起名:
2.选择框架等:
3.然后就创建好了:
4.现在增加依赖,输入cd+项目名进入该项目,输入npm i增加依赖:
出现modules就成功啦。
5.输入npm run dev
开始运行:
鼠标悬浮在url上面,ctrl+点击就启动了。
补充:cls清屏。
Vite+Vue3项目结构目录:
- public/ 目录:用于存放一些公共资源,如 HTML 文件、图像、字体等,这些资源会被直接复制到构建出的目标目录中。
- src/ 目录:存放项目的源代码,包括 JavaScript、CSS、Vue 组件、图像和字体等资源。在开发过程中,这些文件会被 Vite 实时编译和处理,并在浏览器中进行实时预览和调试。以下是src内部划分建议:
assets/
目录:用于存放一些项目中用到的静态资源,如图片、字体、样式文件等。components/
目录:用于存放组件相关的文件。组件是代码复用的一种方式,用于抽象出一个可复用的 UI 部件,方便在不同的场景中进行重复使用。layouts/
目录:用于存放布局组件的文件。布局组件通常负责整个应用程序的整体布局,如头部、底部、导航菜单等。pages/
目录:用于存放页面级别的组件文件,通常是路由对应的组件文件。在这个目录下,可以创建对应的文件夹,用于存储不同的页面组件。plugins/
目录:用于存放 Vite 插件相关的文件,可以按需加载不同的插件来实现不同的功能,如自动化测试、代码压缩等。router/
目录:用于存放 Vue.js 的路由配置文件,负责管理视图和 URL 之间的映射关系,方便实现页面之间的跳转和数据传递。store/
目录:用于存放 Vuex 状态管理相关的文件,负责管理应用程序中的数据和状态,方便统一管理和共享数据,提高开发效率。utils/
目录:用于存放一些通用的工具函数,如日期处理函数、字符串操作函数等。
- vite.config.js 文件:Vite 的配置文件,可以通过该文件配置项目的参数、插件、打包优化等。该文件可以使用 CommonJS 或 ES6 模块的语法进行配置。
- package.json 文件:标准的 Node.js 项目配置文件,包含了项目的基本信息和依赖关系。其中可以通过 scripts 字段定义几个命令,如 dev、build、serve 等,用于启动开发、构建和启动本地服务器等操作。
- Vite 项目的入口为 src/main.js 文件,这是 Vue.js 应用程序的启动文件,也是整个前端应用程序的入口文件。在该文件中,通常会引入 Vue.js 及其相关插件和组件,同时会创建 Vue 实例,挂载到 HTML 页面上指定的 DOM 元素中。
Vite+Vue3项目组件(SFC入门)
什么是VUE的组件?
- 一个页面作为整体,是由多个部分组成的,每个部分在这里就可以理解为一个组件
- 每个.vue文件就可以理解为一个组件,多个.vue文件可以构成一个整体页面
- 组件化给我们带来的另一个好处就是组件的复用和维护非常的方便
什么是.vue文件?
- 传统的页面有.html文件.css文件和.js文件三个文件组成(多文件组件)
- vue将这文件合并成一个.vue文件(Single-File Component,简称 SFC,单文件组件)
- .vue文件对js/css/html统一封装,这是VUE中的概念 该文件由三个部分组成
<script> <template> <style>
- template标签 代表组件的html部分代码 代替传统的.html文件
- script标签 代表组件的js代码 代替传统的.js文件
- style标签 代表组件的css样式代码 代替传统的.css文件
工程化vue项目如何组织这些组件?
- index.html是项目的入口,其中
<div id ='app'></div>
是用于挂载所有组建的元素 - index.html中的script标签引入了一个main.js文件,具体的挂载过程在main.js中执行
- main.js是vue工程中非常重要的文件,他决定这项目使用哪些依赖,导入的第一个组件
- App.vue是vue中的核心组件,所有的其他组件都要通过该组件进行导入,该组件通过路由可以控制页面的切换
在打开网页的时候,展示的是index.html中的消息:
html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
其中src="/src/main.js"
指向了main.js文件:
js
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
createApp(App).mount('#app')
createApp(App).mount('#app')
这行代码是在main.js中用来挂载一个Vue应用到指定的HTML元素上。具体来说,createApp(App)
用来创建一个Vue应用实例,然后调用.mount('#app')
方法将这个实例挂载到id为app
的HTML元素上,从而让Vue应用在该元素中渲染和生效。
注意,它是挂载到index.html文件上面的app的。
它指向了./App.vue
文件并且为它起了一个别名叫App:
vue
<script setup>
import haha from "./components/haha.vue"
</script>
<template>
<h1 class="h1c1">hello</h1>
<haha></haha>
</template>
<style scoped>
.h1c1{
color:red;
}
</style>
它又导入了其他的vue文件,并且使用了:
vue
<script setup>
</script>
<template>
<h1 class="h11">haha</h1>
</template>
<style scoped>
.h11{
color:blue;
}
</style>
这就是一个基本的执行顺序。
CSS导入方式:
- 全局引入main.js:
js
import './style/reset.css' //书写引入的资源的相对路径即可!
- vue文件script代码引入:
js
import './style/reset.css'
3.Vue文件style代码引入:
js
@import './style/reset.css'
Vue响应式入门以及setup函数:
js
<script type="module">
//存储vue页面逻辑js代码
import {ref} from 'vue'
export default{
setup(){
//非响应式数据: 修改后VUE不会更新DOM
//响应式数据: 修改后VUE会更新DOM
//VUE2中数据默认是响应式的
//VUE3中数据要经过ref或者reactive处理后才是响应式的
//ref是VUE3框架提供的一个函数,需要导入
//let counter = 1
//ref处理的响应式数据在js编码修改的时候需要通过.value操作
//ref响应式数据在绑定到html上时不需要.value
let counter = ref(1)
function increase(){
// 通过.value修改响应式数据
counter.value++
}
function decrease(){
counter.value--
}
return {
counter,
increase,
decrease
}
}
}
</script>
<template>
<div>
<button @click="decrease()">-</button>
{{ counter }}
<button @click="increase()">+</button>
</div>
</template>
<style scoped>
button{
border: 1px solid red;
}
</style>
- 非响应式数据: 修改后VUE不会更新DOM
- 响应式数据: 修改后VUE会更新DOM
- VUE2中数据默认是响应式的
- VUE3中数据要经过ref或者reactive处理后才是响应式的
- ref是VUE3框架提供的一个函数,需要导入
- let counter = 1
- ref处理的响应式数据在js编码修改的时候需要通过.value操作
- ref响应式数据在绑定到html上时不需要.value
语法糖:
js
<script type="module" setup>
/* <script type="module" setup> 通过setup关键字
可以省略 export default {setup(){ return{}}}这些冗余的语法结构 */
import {ref} from 'vue'
// 定义响应式数据
let counter = ref(1)
// 定义函数
function increase(){
counter.value++
}
function decrease(){
counter.value--
}
</script>
<template>
<div>
<button @click="decrease()">-</button>
{{ counter }}
<button @click="increase()">+</button>
</div>
</template>
<style scoped>
button{
border: 1px solid red;
}
</style>
插值表达式:
插值表达式:最基本的数据绑定形式是文本插值,它使用的是"Mustache"语法 ,即双大括号{{}}
- 插值表达式是将数据渲染到元素的指定位置的手段之一
- 插值表达式不绝对依赖标签,其位置相对自由
- 插值表达式中支持javascript的运算表达式
- 插值表达式中也支持函数的调用
js
<script setup type="module">
let msg ="hello vue3"
let getMsg= ()=>{
return 'hello vue3 message'
}
let age = 19
let bee = '蜜 蜂'
// 购物车
const carts = [{name:'可乐',price:3,number:10},{name:'薯片',price:6,number:8}];
//计算购物车总金额
function compute(){
let count = 0;
for(let index in carts){
count += carts[index].price*carts[index].number;
}
return count;
}
</script>
<template>
<div>
<h1>{{ msg }}</h1>
msg的值为: {{ msg }} <br>
getMsg返回的值为:{{ getMsg() }} <br>
是否成年: {{ age>=18?'true':'false' }} <br>
反转: {{ bee.split(' ').reverse().join('-') }} <br>
购物车总金额: {{ compute() }} <br/>
购物车总金额: {{carts[0].price*carts[0].number + carts[1].price*carts[1].number}} <br>
</div>
</template>
<style scoped>
</style>
文本渲染:
为了渲染双标中的文本,我们也可以选择使用
v-text
和v-html
命令
- v-*** 这种写法的方式使用的是vue的命令
- v-***的命令必须依赖元素,并且要写在元素的开始标签中
- v-***指令支持ES6中的字符串模板
- 插值表达式中支持javascript的运算表达式
- 插值表达式中也支持函数的调用
- v-text可以将数据渲染成双标签中间的文本,但是不识别html元素结构的文本
- v-html可以将数据渲染成双标签中间的文本,识别html元素结构的文本
js
```html
<script setup type="module">
let msg ='hello vue3'
let getMsg= ()=>{
return msg
}
let age = 19
let bee = '蜜 蜂'
let redMsg ='<font color=\'red\'>msg</font>'
let greenMsg =`<font color=\'green\'>${msg}</font>`
</script>
<template>
<div>
<span v-text='msg'></span> <br>
<span v-text='redMsg'></span> <br>
<span v-text='getMsg()'></span> <br>
<span v-text='age>18?"成年":"未成年"'></span> <br>
<span v-text='bee.split(" ").reverse().join("-")'></span> <br>
<span v-html='msg'></span> <br>
<span v-html='redMsg'></span> <br>
<span v-html='greenMsg'></span> <br>
<span v-html="`<font color='green'>${msg}</font>`"></span> <br>
</div>
</template>
<style scoped>
</style>
属性渲染:
想要渲染一个元素的 attribute,应该使用
v-bind
指令
由于插值表达式不能直接放在标签的属性中,所有要渲染元素的属性就应该使用v-bind
v-bind可以用于渲染任何元素的属性,语法为 v-bind:属性名='数据名'
, 可以简写为:属性名='数据名'
js
<script setup type="module">
const data = {
name:'尚硅谷',
url:"http://www.atguigu.com",
logo:"http://www.atguigu.com/images/index_new/logo.png"
}
</script>
<template>
<div>
<a
v-bind:href='data.url'
target="_self">
<img
:src="data.logo"
:title="data.name">
<br>
<input type="button"
:value="`点击访问${data.name}`">
</a>
</div>
</template>
<style scoped>
</style>
事件的绑定:
我们可以使用
v-on
来监听 DOM 事件,并在事件触发时执行对应的 Vue的JavaScript代码。
- 用法:
v-on:click="handler"
或简写为@click="handler"
- vue中的事件名=原生事件名去掉
on
前缀 如:onClick --> click
- handler的值可以是方法事件处理器,也可以是内联事件处理器
- 绑定事件时,可以通过一些绑定的修饰符,常见的事件修饰符如下
.once:只触发一次事件。[重点]
.prevent:阻止默认事件。[重点]
- .stop:阻止事件冒泡。
- .capture:使用事件捕获模式而不是冒泡模式。
- .self:只在事件发送者自身触发时才触发事件。
js
<script setup type="module">
import {ref} from 'vue'
// 响应式数据 当发生变化时,会自动更新 dom树
let count=ref(0)
let addCount= ()=>{
count.value++
}
let incrCount= (event)=>{
count.value++
// 通过事件对象阻止组件的默认行为
event.preventDefault();
}
</script>
<template>
<div>
<h1>count的值是:{{ count }}</h1>
<!-- 方法事件处理器 -->
<button v-on:click="addCount()">addCount</button> <br>
<!-- 内联事件处理器 -->
<button @click="count++">incrCount</button> <br>
<!-- 事件修饰符 once 只绑定事件一次 -->
<button @click.once="count++">addOnce</button> <br>
<!-- 事件修饰符 prevent 阻止组件的默认行为 -->
<a href="http://www.atguigu.com" target="_blank" @click.prevent="count++">prevent</a> <br>
<!-- 原生js方式阻止组件默认行为 (推荐) -->
<a href="http://www.atguigu.com" target="_blank" @click="incrCount($event)">prevent</a> <br>
</div>
</template>
<style scoped>
</style>
响应式处理:
js
<script type="module" setup>
let counter = 0;
function show(){
alert(counter);
}
</script>
<template>
<div>
<button @click="counter--">-</button>
{{ counter }}
<button @click="counter++">+</button>
<hr>
<!-- 此案例,我们发现counter值,会改变,但是页面不改变! 默认Vue3的数据是非响应式的!-->
<button @click="show()">显示counter值</button>
</div>
</template>
<style scoped>
</style>
1.使用ref关键字:
js
<script type="module" setup>
/* 从vue中引入ref方法 */
import {ref} from 'vue'
let counter = ref(0);
function show(){
alert(counter.value);
}
/* 函数中要操作ref处理过的数据,需要通过.value形式 */
let decr = () =>{
counter.value--;
}
let incr = () =>{
counter.value++;
}
</script>
<template>
<div>
<button @click="counter--">-</button>
<button @click="decr()">-</button>
{{ counter }}
<button @click="counter++">+</button>
<button @click="incr()">+</button>
<hr>
<button @click="show()">显示counter值</button>
</div>
</template>
<style scoped>
</style>
注意,在template中操作不用加value,script要加上.value。
2.使用reaction:
js
<script type="module" setup>
/* 从vue中引入reactive方法 */
import {ref,reactive} from 'vue'
let data = reactive({
counter:0
})
function show(){
alert(data.counter);
}
/* 函数中要操作reactive处理过的数据,需要通过 对象名.属性名的方式 */
let decr = () =>{
data.counter--;
}
let incr = () =>{
data.counter++;
}
</script>
<template>
<div>
<button @click="data.counter--">-</button>
<button @click="decr()">-</button>
{{ data.counter }}
<button @click="data.counter++">+</button>
<button @click="incr()">+</button>
<hr>
<button @click="show()">显示counter值</button>
</div>
</template>
<style scoped>
</style>
对比ref和reactive:
使用 ref
适用于以下开发场景:
包装基本类型数据:ref
主要用于包装基本类型数据(如字符串、数字等),即只有一个值的数据,如果你想监听这个值的变化,用 ref
最为方便。在组件中使用时也很常见。访问方式简单:ref
对象在访问时与普通的基本类型值没有太大区别,只需要通过 .value 访问其实际值即可。
使用 reactive
适用于以下开发场景:
包装复杂对象:reactive
可以将一个普通对象转化为响应式对象,这样在数据变化时会自动更新界面,特别适用于处理复杂对象或者数据结构。
需要递归监听的属性:使用 reactive
可以递归追踪所有响应式对象内部的变化,从而保证界面的自动更新。
扩展响应式关键字toRefs 和 toRef:
toRef基于reactive响应式对象上的一个属性,创建一个对应的 ref响应式数据。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。toRefs将一个响应式对象多个属性转换为一个多个ref数据,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 [toRef()]
js
<script type="module" setup>
/* 从vue中引入reactive方法 */
import {ref,reactive,toRef,toRefs} from 'vue'
let data = reactive({
counter:0,
name:"test"
})
// 将一个reactive响应式对象中的某个属性转换成一个ref响应式对象
let ct =toRef(data,'counter');
// 将一个reactive响应式对象中的多个属性转换成多个ref响应式对象
let {counter,name} = toRefs(data)
function show(){
alert(data.counter);
// 获取ref的响应对象,需要通过.value属性
alert(counter.value);
alert(name.value)
}
/* 函数中要操作ref处理过的数据,需要通过.value形式 */
let decr = () =>{
data.counter--;
}
let incr = () =>{
/* ref响应式数据,要通过.value属性访问 */
counter.value++;
}
</script>
<template>
<div>
<button @click="data.counter--">-</button>
<button @click="decr()">-</button>
{{ data.counter }}
&
{{ ct }}
<button @click="data.counter++">+</button>
<button @click="incr()">+</button>
<hr>
<button @click="show()">显示counter值</button>
</div>
</template>
<style scoped>
</style>
条件渲染:
-
v-if='表达式'
只会在指令的表达式返回真值时才被渲染 -
也可以使用
v-else
为v-if
添加一个"else 区块"。 -
一个
v-else
元素必须跟在一个v-if
元素后面,否则它将不会被识别。
js
<script type="module" setup>
import {ref} from 'vue'
let awesome = ref(true)
</script>
<template>
<div>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
<button @click="awesome = !awesome">Toggle</button>
</div>
</template>
<style scoped>
</style>
v-show
条件渲染扩展:
-
另一个可以用来按条件显示一个元素的指令是
v-show
。其用法基本一样: -
不同之处在于
v-show
会在 DOM 渲染中保留该元素;v-show
仅切换了该元素上名为display
的 CSS 属性。 -
v-show
不支持在<template>
元素上使用,也不能和v-else
搭配使用
js
<script type="module" setup>
import {ref} from 'vue'
let awesome = ref(true)
</script>
<template>
<div>
<h1 id="ha" v-show="awesome">Vue is awesome!</h1>
<h1 id="hb" v-if="awesome">Vue is awesome!</h1>
<h1 id="hc" v-else>Oh no 😢</h1>
<button @click="awesome = !awesome">Toggle</button>
</div>
</template>
<style scoped>
</style>
列表渲染:
我们可以使用 v-for
指令基于一个数组来渲染一个列表。
-
v-for
指令的值需要使用item in items
形式的特殊语法,其中items
是源数据的数组,而item
是迭代项的别名: -
在
v-for
块中可以完整地访问父作用域内的属性和变量。v-for
也支持使用可选的第二个参数表示当前项的位置索引。
js
<script type="module" setup>
import {ref,reactive} from 'vue'
let parentMessage= ref('产品')
let items =reactive([
{
id:'item1',
message:"薯片"
},
{
id:'item2',
message:"可乐"
}
])
</script>
<template>
<div>
<ul>
<!-- :key不写也可以 -->
<li v-for='item in items' :key='item.id'>
{{ item.message }}
</li>
</ul>
<ul>
<!-- index表示索引,当然不是非得使用index这个单词 -->
<li v-for="(item, index) in items" :key="index">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
</div>
</template>
<style scoped>
</style>
当使用v-for="(item, index) in items"
这种格式时,Vue.js会遍历数组items
中的每个元素,并在每次迭代中将当前元素赋值给变量item
,将当前元素的索引赋值给变量index
。这种格式的v-for
指令常用于需要同时访问数组元素和元素索引的情况。
练习:
js
<script type="module" setup>
//引入模块
import { reactive} from 'vue'
//准备购物车数据,设置成响应数据
const carts = reactive([{name:'可乐',price:3,number:10},{name:'薯片',price:6,number:8}])
//计算购物车总金额
function compute(){
let count = 0;
for(let index in carts){
count += carts[index].price*carts[index].number;
}
return count;
}
//删除购物项方法
function removeCart(index){
carts.splice(index,1);
}
</script>
<template>
<div>
<table>
<thead>
<tr>
<th>序号</th>
<th>商品名</th>
<th>价格</th>
<th>数量</th>
<th>小计</th>
<th>操作</th>
</tr>
</thead>
<tbody v-if="carts.length > 0">
<!-- 有数据显示-->
<tr v-for="cart,index in carts" :key="index">
<th>{{ index+1 }}</th>
<th>{{ cart.name }}</th>
<th>{{ cart.price + '元' }}</th>
<th>{{ cart.number }}</th>
<th>{{ cart.price*cart.number + '元'}}</th>
<th> <button @click="removeCart(index)">删除</button> </th>
</tr>
</tbody>
<tbody v-else>
<!-- 没有数据显示-->
<tr>
<td colspan="6">购物车没有数据!</td>
</tr>
</tbody>
</table>
购物车总金额: {{ compute() }} 元
</div>
</template>
<style scoped>
</style>
双向绑定
单项绑定和双向绑定
- 单向绑定: 响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变不会同步更新到响应式数据
- 双向绑定: 响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变会同步更新到响应式数据
js
<script type="module" setup>
//引入模块
import { reactive,ref} from 'vue'
let hbs = ref([]); //装爱好的值
let user = reactive({username:null,password:null,introduce:null,pro:null})
function login(){
alert(hbs.value);
alert(JSON.stringify(user));
}
function clearx(){
//user = {};// 这中写法会将数据变成非响应的,应该是user.username=""
user.username=''
user.password=''
user.introduce=''
user.pro=''
hbs.value.splice(0,hbs.value.length);;
}
</script>
<template>
<div>
账号: <input type="text" placeholder="请输入账号!" v-model="user.username"> <br>
密码: <input type="text" placeholder="请输入密码!" v-model="user.password"> <br>
爱好:
吃 <input type="checkbox" name="hbs" v-model="hbs" value="吃">
喝 <input type="checkbox" name="hbs" v-model="hbs" value="喝">
玩 <input type="checkbox" name="hbs" v-model="hbs" value="玩">
乐 <input type="checkbox" name="hbs" v-model="hbs" value="乐">
<br>
简介:<textarea v-model="user.introduce"></textarea>
<br>
籍贯:
<select v-model="user.pro">
<option value="1">黑</option>
<option value="2">吉</option>
<option value="3">辽</option>
<option value="4">京</option>
<option value="5">津</option>
<option value="6">冀</option>
</select>
<br>
<button @click="login()">登录</button>
<button @click="clearx()">重置</button>
<hr>
显示爱好:{{ hbs }}
<hr>
显示用户信息:{{ user }}
</div>
</template>
<style scoped>
</style>
效果展示:
计算属性:
我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。
js
<script type="module" setup>
//引入模块
import { reactive,computed} from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
console.log("publishedBooksMessage")
return author.books.length > 0 ? 'Yes' : 'No'
})
// 一个函数
let hasBooks = ()=>{
console.log("hasBooks")
return author.books.length > 0?'Yes':'no'
}
</script>
<template>
<div>
<p>{{author.name}} Has published books?:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
<span>{{ hasBooks() }}</span><!-- 调用方法,每个标签都会调用一次 -->
<span>{{ hasBooks() }}</span>
<p>{{author.name}} Has published books?:</p>
<span>{{ publishedBooksMessage }}</span><!-- 属性计算,属性值不变时,多个个标签只会调用一次 -->
<span>{{ publishedBooksMessage }}</span>
</div>
</template>
<style scoped>
</style>
在第一种方式中,使用了三元表达式{``{ author.books.length > 0 ? 'Yes' : 'No' }}
来判断作者是否发布了书籍。每次渲染模板时,都会重新计算author.books.length
,因此多个标签中调用时,每个标签都会执行一次判断逻辑。这可能会导致性能开销,尤其在有大量数据需要计算的情况下。
而在第二种方式中,定义了一个计算属性publishedBooksMessage
来计算消息。计算属性会根据它们的依赖缓存结果,只有当依赖发生变化时才会重新计算。因此,当多个标签中引用同一个计算属性时,只会在第一次调用时计算一次,后续多次引用时会直接使用缓存的结果,不会重复计算。这样可以提高性能,避免不必要的重复计算。
计算属性在Vue组件中的格式通常是在computed
选项下定义一个对象,对象的key是计算属性的名称,value是一个函数,函数用来计算并返回计算属性的值。计算属性的格式如下:
js
computed: {
propertyName: function() {
// 计算逻辑
return result;
}
}
其中,propertyName
是计算属性的名称,function()
是计算属性的计算逻辑,return result
是计算属性的计算结果。
数据监听器:
计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些"副作用":例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。我们可以使用 watch函数在每次响应式状态发生变化时触发回调函数:
watch主要用于以下场景:
- 当数据发生变化时需要执行相应的操作
- 监听数据变化,当满足一定条件时触发相应操作
- 在异步操作前或操作后需要执行相应的操作
监控响应式数据(watch):
js
<script type="module" setup>
//引入模块
import { ref,reactive,watch} from 'vue'
let firstname=ref('')
let lastname=reactive({name:''})
let fullname=ref('')
//监听一个ref响应式数据
watch(firstname,(newValue,oldValue)=>{
console.log(`${oldValue}变为${newValue}`)
fullname.value=firstname.value+lastname.name
})
//监听reactive响应式数据的指定属性
watch(()=>lastname.name,(newValue,oldValue)=>{
console.log(`${oldValue}变为${newValue}`)
fullname.value=firstname.value+lastname.name
})
//监听reactive响应式数据的所有属性(深度监视,一般不推荐)
//deep:true 深度监视
//immediate:true 深度监视在进入页面时立即执行一次
watch(()=>lastname,(newValue,oldValue)=>{
// 此时的newValue和oldValue一样,都是lastname
console.log(newValue)
console.log(oldValue)
fullname.value=firstname.value+lastname.name
},{deep:true,immediate:false})
</script>
<template>
<div>
全名:{{fullname}} <br>
姓氏:<input type="text" v-model="firstname"> <br>
名字:<input type="text" v-model="lastname.name" > <br>
</div>
</template>
<style scoped>
</style>
注意:()->
不可省略。
监控响应式数据(watchEffect):
- watchEffect默认监听所有的响应式数据
js
<script type="module" setup>
//引入模块
import { ref,reactive,watch, watchEffect} from 'vue'
let firstname=ref('')
let lastname=reactive({name:''})
let fullname=ref('')
//监听所有响应式数据
watchEffect(()=>{
//直接在内部使用监听属性即可!不用外部声明
//也不需要,即时回调设置!默认初始化就加载!
console.log(firstname.value)
console.log(lastname.name)
fullname.value=`${firstname.value}${lastname.name}`
})
</script>
<template>
<div>
全名:{{fullname}} <br>
姓氏:<input type="text" v-model="firstname"> <br>
名字:<input type="text" v-model="lastname.name" > <br>
</div>
</template>
<style scoped>
</style>
watch
和 watchEffect
都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
watch
只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch
会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
watchEffect
,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。
生命周期:
- 常见钩子函数
- onMounted() 注册一个回调函数,在组件挂载完成后执行。
- onUpdated() 注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。
- onUnmounted() 注册一个回调函数,在组件实例被卸载之后调用。
- onBeforeMount() 注册一个钩子,在组件被挂载之前被调用。
- onBeforeUpdate() 注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
- onBeforeUnmount() 注册一个钩子,在组件实例被卸载之前调用。
Vue组件:
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。组件就是实现应用中局部功能代码和资源的集合!在实际应用中,组件常常被组织成层层嵌套的树状结构。
案例需求: 创建一个页面,包含头部和菜单以及内容显示区域,每个区域使用独立组建:
分别创建三个vue文件:
Header:
js
<script setup type="module">
</script>
<template>
<div>
欢迎: xx <a href="#">退出登录</a>
</div>
</template>
<style>
</style>
Navigator:
js
<script setup type="module">
</script>
<template>
<!-- 推荐写一个根标签-->
<div>
<ul>
<li>学员管理</li>
<li>图书管理</li>
<li>请假管理</li>
<li>考试管理</li>
<li>讲师管理</li>
</ul>
</div>
</template>
<style>
</style>
Content:
js
<script setup type="module">
</script>
<template>
<div>
展示的主要内容!
</div>
</template>
<style>
</style>
App.vue引入三个组件:
js
<script setup>
import Header from './components/Header.vue'
import Navigator from './components/Navigator.vue'
import Content from './components/Content.vue'
</script>
<template>
<div>
<Header class="header"></Header>
<Navigator class="navigator"></Navigator>
<Content class="content"></Content>
</div>
</template>
<style scoped>
.header{
height: 80px;
border: 1px solid red;
}
.navigator{
width: 15%;
height: 800px;
display: inline-block;
border: 1px blue solid;
float: left;
}
.content{
width: 83%;
height: 800px;
display: inline-block;
border: 1px goldenrod solid;
float: right;
}
</style>
组件传参:
一般在vue框架中,组件的传参是由父传子,子传父,而兄弟传参就可以通过子传父,父传子完成,下面我们来演示一下:
Navigator.vue: 发送数据到App.vue(子传父):
js
<script setup type="module">
import {defineEmits} from 'vue'
const emits = defineEmits(['sendMenu']);
//触发事件,向父容器发送数据
function send(data){
emits('sendMenu',data);
}
</script>
<template>
<!-- 推荐写一个根标签-->
<div>
<ul>
<li @click="send('学员管理')">学员管理</li>
<li @click="send('图书管理')">图书管理</li>
<li @click="send('请假管理')">请假管理</li>
<li @click="send('考试管理')">考试管理</li>
<li @click="send('讲师管理')">讲师管理</li>
</ul>
</div>
</template>
<style>
</style>
App.vue: 发送数据到Content.vue(父传子):
js
<script setup>
import Header from './components/Header.vue'
import Navigator from './components/Navigator.vue'
import Content from './components/Content.vue'
import {ref} from "vue"
//定义接受navigator传递参数
var navigator_menu = ref('ceshi');
const receiver = (data) =>{
navigator_menu.value = data;
}
</script>
<template>
<div>
<hr>
{{ navigator_menu }}
<hr>
<Header class="header"></Header>
<Navigator @sendMenu="receiver" class="navigator"></Navigator>
<!-- 向子组件传递数据-->
<Content class="content" :message="navigator_menu"></Content>
</div>
</template>
<style scoped>
.header{
height: 80px;
border: 1px solid red;
}
.navigator{
width: 15%;
height: 800px;
display: inline-block;
border: 1px blue solid;
float: left;
}
.content{
width: 83%;
height: 800px;
display: inline-block;
border: 1px goldenrod solid;
float: right;
}
</style>
Content.vue接受:
js
<script setup type="module">
defineProps({
message:String
})
</script>
<template>
<div>
展示的主要内容!
<hr>
{{ message }}
</div>
</template>
<style>
</style>
四:Router:
4.1.介绍与安装:
在前端开发中,Router(路由器)是用来管理应用程序中不同页面之间导航的工具。在Vue.js中,Vue Router是官方提供的路由管理器,用于实现单页面应用(SPA)的路由功能。
通过Vue Router,可以定义不同的路由规则,将不同的URL路径映射到不同的组件,实现页面之间的切换和导航。Vue Router可以帮助开发者实现页面的跳转、参数传递、嵌套路由、路由守卫等功能,从而构建出更加复杂和交互丰富的单页面应用。
总的来说,Router在前端开发中扮演着非常重要的角色,帮助开发者管理应用程序的导航和路由,实现页面间的无缝切换和交互。
安装Router:
成功如上图。
4.2.代码应用:
4.2.1.准备组件:
components/Home.vue:
js
<script setup>
</script>
<template>
<div>
<h1>Home页面</h1>
</div>
</template>
<style scoped>
</style>
components/List.vue:
js
<script setup>
</script>
<template>
<div>
<h1>List页面</h1>
</div>
</template>
<style scoped>
</style>
components/Add.vue:
js
<script setup>
</script>
<template>
<div>
<h1>Add页面</h1>
</div>
</template>
<style scoped>
</style>
components/Update.vue:
js
<script setup>
</script>
<template>
<div>
<h1>Update页面</h1>
</div>
</template>
<style scoped>
</style>
效果如图:
4.2.2.准备组件:
src/routers/router.js:
js
// 导入路由创建的相关方法
import {createRouter,createWebHashHistory} from 'vue-router'
// 导入vue组件
import Home from '../components/Home.vue'
import List from '../components/List.vue'
import Add from '../components/Add.vue'
import Update from '../components/Update.vue'
// 创建路由对象,声明路由规则
const router = createRouter({
//createWebHashHistory() 是 Vue.js 基于 hash 模式创建路由的工厂函数。在使用这种模式下,路由信息保存在 URL 的 hash 中,
//使用 createWebHashHistory() 方法,可以创建一个路由历史记录对象,用于管理应用程序的路由。在 Vue.js 应用中,
//通常使用该方法来创建路由的历史记录对象。
//就是路由中缓存历史记录的对象,vue-router提供
history: createWebHashHistory(),
routes:[
{
path:'/',
/*
component指定组件在默认的路由视图位置展示
components:Home
components指定组件在name为某个值的路由视图位置展示
components:{
default:Home,// 默认路由视图位置
homeView:Home// name为homeView的路由视图位置
}
*/
components:{
default:Home,
homeView:Home
}
},
{
path:'/list',
components:{
listView : List
}
},
{
path:'/add',
components:{
addView:Add
}
},
{
path:'/update',
components:{
updateView:Update
}
},
]
})
// 对外暴露路由对象
export default router;
4.2.3. main.js引入router配置:
js
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
//导入router模块
import router from './routers/router.js'
let app = createApp(App)
//绑定路由对象
app.use(router)
//挂在试图
app.mount("#app")
注意,js文件需要对外暴露才可以被使用,vue文件不需要显式的暴露。
4.2.4.启动:
4.3.路由重定向:
重定向的作用:将一个路由重定向到另一个路由上:
修改案例:访问/list和/showAll都定向到List.vue:
router.js:
js
// 导入路由创建的相关方法
import {createRouter,createWebHashHistory} from 'vue-router'
// 导入vue组件
import Home from '../components/Home.vue'
import List from '../components/List.vue'
import Add from '../components/Add.vue'
import Update from '../components/Update.vue'
// 创建路由对象,声明路由规则
const router = createRouter({
history: createWebHashHistory(),
routes:[
{
path:'/',
components:{
default:Home,
homeView:Home
}
},
{
path:'/list',
components:{
listView : List
}
},
{
path:'/showAll',
// 重定向
redirect :'/list'
},
{
path:'/add',
components:{
addView:Add
}
},
{
path:'/update',
components:{
updateView:Update
}
},
]
})
// 对外暴露路由对象
export default router;
App.vue:
vue
<script setup>
</script>
<template>
<div>
<h1>App页面</h1>
<hr/>
<!-- 路由的连接 -->
<router-link to="/">home页</router-link> <br>
<router-link to="/list">list页</router-link> <br>
<router-link to="/showAll">showAll页</router-link> <br>
<router-link to="/add">add页</router-link> <br>
<router-link to="/update">update页</router-link> <br>
<hr/>
<!-- 路由连接对应视图的展示位置 -->
<hr>
默认展示位置:<router-view></router-view>
<hr>
Home视图展示:<router-view name="homeView"></router-view>
<hr>
List视图展示:<router-view name="listView"></router-view>
<hr>
Add视图展示:<router-view name="addView"></router-view>
<hr>
Update视图展示:<router-view name="updateView"></router-view>
</div>
</template>
<style scoped>
</style>
4.4.编程式路由:
以上都是声明式路由,to中的内容目前是固定的,点击后只能切换/list对象组件,面来看看编程式路由:
- 通过useRouter,动态决定向那个组件切换的路由
- 在 Vue 3 和 Vue Router 4 中,你可以使用
useRouter
来实现动态路由(编程式路由) - 这里的
useRouter
方法返回的是一个 router 对象,你可以用它来做如导航到新页面、返回上一页面等操作。
案例需求: 通过普通按钮配合事件绑定实现路由页面跳转,不直接使用router-link标签:
vue
<script setup type="module">
import {useRouter} from 'vue-router'
import {ref} from 'vue'
//创建动态路由对象
let router = useRouter()
let routePath =ref('')
let showList= ()=>{
// 编程式路由
// 直接push一个路径
//router.push('/list')
// push一个带有path属性的对象
router.push({path:'/list'})
}
</script>
<template>
<div>
<h1>App页面</h1>
<hr/>
<!-- 路由的连接 -->
<router-link to="/">home页</router-link> <br>
<router-link to="/list">list页</router-link> <br>
<router-link to="/showAll">showAll页</router-link> <br>
<router-link to="/add">add页</router-link> <br>
<router-link to="/update">update页</router-link> <br>
<!-- 动态输入路径,点击按钮,触发单击事件的函数,在函数中通过编程是路由切换页面 -->
<button @click="showList()">showList</button> <br>
<hr/>
<!-- 路由连接对应视图的展示位置 -->
<hr>
默认展示位置:<router-view></router-view>
<hr>
Home视图展示:<router-view name="homeView"></router-view>
<hr>
List视图展示:<router-view name="listView"></router-view>
<hr>
Add视图展示:<router-view name="addView"></router-view>
<hr>
Update视图展示:<router-view name="updateView"></router-view>
</div>
</template>
<style scoped>
</style>
4.5.路由传参:
4.5.1. 切换到ShowDetail.vue组件时,向该组件通过路由传递参数:
修改App.vue文件:
vue
<script setup type="module">
import {useRouter} from 'vue-router'
//创建动态路由对象
let router = useRouter()
//动态路由路径传参方法
let showDetail= (id,language)=>{
// 尝试使用拼接字符串方式传递路径参数
//router.push(`showDetail/${id}/${languange}`)
/*路径参数,需要使用params */
router.push({name:"showDetail",params:{id:id,language:language}})
}
let showDetail2= (id,language)=>{
/*uri键值对参数,需要使用query */
router.push({path:"/showDetail2",query:{id:id,language:language}})
}
</script>
<template>
<div>
<h1>App页面</h1>
<hr/>
<!-- 路径参数 -->
<router-link to="/showDetail/1/JAVA">showDetail路径传参显示JAVA</router-link>
<button @click="showDetail(1,'JAVA')">showDetail动态路由路径传参显示JAVA</button>
<hr/>
<!-- 键值对参数 -->
<router-link v-bind:to="{path:'/showDetail2',query:{id:1,language:'Java'}}">showDetail2键值对传参显示JAVA</router-link>
<button @click="showDetail2(1,'JAVA')">showDetail2动态路由键值对传参显示JAVA</button>
<hr>
showDetail视图展示:<router-view name="showDetailView"></router-view>
<hr>
showDetail2视图展示:<router-view name="showDetailView2"></router-view>
</div>
</template>
<style scoped>
</style>
修改router.js增加路径参数占位符:
js
// 导入路由创建的相关方法
import {createRouter,createWebHashHistory} from 'vue-router'
// 导入vue组件
import ShowDetail from '../components/ShowDetail.vue'
import ShowDetail2 from '../components/ShowDetail2.vue'
// 创建路由对象,声明路由规则
const router = createRouter({
history: createWebHashHistory(),
routes:[
{
/* 此处:id :language作为路径的占位符 */
path:'/showDetail/:id/:language',
/* 动态路由传参时,根据该名字找到该路由 */
name:'showDetail',
components:{
showDetailView:ShowDetail
}
},
{
path:'/showDetail2',
components:{
showDetailView2:ShowDetail2
}
},
]
})
// 对外暴露路由对象
export default router;
ShowDetail.vue 通过useRoute获取路径参数:
vue
<script setup type="module">
import{useRoute} from 'vue-router'
import { onUpdated,ref } from 'vue';
// 获取当前的route对象
let route =useRoute()
let languageId = ref(0)
let languageName = ref('')
// 借助更新时生命周期,将数据更新进入响应式对象
onUpdated (()=>{
// 获取对象中的参数
languageId.value=route.params.id
languageName.value=route.params.language
console.log(languageId.value)
console.log(languageName.value)
})
</script>
<template>
<div>
<h1>ShowDetail页面</h1>
<h3>编号{{route.params.id}}:{{route.params.language}}是世界上最好的语言</h3>
<h3>编号{{languageId}}:{{languageName}}是世界上最好的语言</h3>
</div>
</template>
<style scoped>
</style>
ShowDetail2.vue通过useRoute获取键值对参数:
vue
<script setup type="module">
import{useRoute} from 'vue-router'
import { onUpdated,ref } from 'vue';
// 获取当前的route对象
let route =useRoute()
let languageId = ref(0)
let languageName = ref('')
// 借助更新时生命周期,将数据更新进入响应式对象
onUpdated (()=>{
// 获取对象中的参数(通过query获取参数,此时参数是key-value形式的)
console.log(route.query)
console.log(languageId.value)
console.log(languageName.value)
languageId.value=route.query.id
languageName.value=route.query.language
})
</script>
<template>
<div>
<h1>ShowDetail2页面</h1>
<h3>编号{{route.query.id}}:{{route.query.language}}是世界上最好的语言</h3>
<h3>编号{{languageId}}:{{languageName}}是世界上最好的语言</h3>
</div>
</template>
<style scoped>
</style>
4.6.路由守卫:
在 Vue 3 中,路由守卫是用于在路由切换期间进行一些特定任务的回调函数。路由守卫可以用于许多任务,例如验证用户是否已登录、在路由切换前提供确认提示、请求数据等。Vue 3 为路由守卫提供了全面的支持,并提供了以下几种类型的路由守卫:
- 全局前置守卫:在路由切换前被调用,可以用于验证用户是否已登录、中断导航、请求数据等。
- 全局后置守卫:在路由切换之后被调用,可以用于处理数据、操作 DOM 、记录日志等。
- 守卫代码的位置: 在router.js中
类似于filter。
js
//全局前置路由守卫
router.beforeEach( (to,from,next) => {
//to 是目标地包装对象 .path属性可以获取地址
//from 是来源地包装对象 .path属性可以获取地址
//next是方法,不调用默认拦截! next() 放行,直接到达目标组件
//next('/地址')可以转发到其他地址,到达目标组件前会再次经过前置路由守卫
console.log(to.path,from.path,next)
//需要判断,注意避免无限重定向
if(to.path == '/index'){
next()
}else{
next('/index')
}
} )
//全局后置路由守卫
router.afterEach((to, from) => {
console.log(`Navigate from ${from.path} to ${to.path}`);
});
登录案例,登录以后才可以进入home,否则必须进入login:
定义Login.vue:
vue
<script setup>
import {ref} from 'vue'
import {useRouter} from 'vue-router'
let username =ref('')
let password =ref('')
let router = useRouter();
let login = () =>{
console.log(username.value,password.value)
if(username.value == 'root' & password.value == '123456'){
router.push({path:'/home',query:{'username':username.value}})
//登录成功利用前端存储机制,存储账号!
localStorage.setItem('username',username.value)
//sessionStorage.setItem('username',username)
}else{
alert('登录失败,账号或者密码错误!');
}
}
</script>
<template>
<div>
账号: <input type="text" v-model="username" placeholder="请输入账号!"><br>
密码: <input type="password" v-model="password" placeholder="请输入密码!"><br>
<button @click="login()">登录</button>
</div>
</template>
<style scoped>
</style>
定义Home.vue:
vue
<script setup>
import {ref} from 'vue'
import {useRoute,useRouter} from 'vue-router'
let route =useRoute()
let router = useRouter()
// 并不是每次进入home页时,都有用户名参数传入
//let username = route.query.username
let username =window.localStorage.getItem('username');
let logout= ()=>{
// 清除localStorge中的username
//window.sessionStorage.removeItem('username')
window.localStorage.removeItem('username')
// 动态路由到登录页
router.push("/login")
}
</script>
<template>
<div>
<h1>Home页面</h1>
<h3>欢迎{{username}}登录</h3>
<button @click="logout">退出登录</button>
</div>
</template>
<style scoped>
</style>
App.vue:
vue
<script setup type="module">
</script>
<template>
<div>
<router-view></router-view>
</div>
</template>
<style scoped>
</style>
定义routers.js:
js
// 导入路由创建的相关方法
import {createRouter,createWebHashHistory} from 'vue-router'
// 导入vue组件
import Home from '../components/Home.vue'
import Login from '../components/login.vue'
// 创建路由对象,声明路由规则
const router = createRouter({
history: createWebHashHistory(),
routes:[
{
path:'/home',
component:Home
},
{
path:'/',
redirect:"/home"
},
{
path:'/login',
component:Login
},
]
})
// 设置路由的全局前置守卫
router.beforeEach((to,from,next)=>{
/*
to 要去那
from 从哪里来
next 放行路由时需要调用的方法,不调用则不放行
*/
console.log(`从哪里来:${from.path},到哪里去:${to.path}`)
if(to.path == '/login'){
//放行路由 注意放行不要形成循环
next()
}else{
//let username =window.sessionStorage.getItem('username');
let username =window.localStorage.getItem('username');
if(null != username){
next()
}else{
next('/login')
}
}
})
// 设置路由的全局后置守卫
router.afterEach((to,from)=>{
console.log(`从哪里来:${from.path},到哪里去:${to.path}`)
})
// 对外暴露路由对象
export default router;
五:axios:
5.1.promise:
回调函数: 一些特殊的函数,表示未来才会执行的一些功能,后续代码不会等待该函数执行完毕就开始执行了:
js
<script>
// 设置一个2000毫秒后会执行一次的定时任务
setTimeout(function (){
console.log("setTimeout invoked")
},2000)
console.log("other code processon")
</script>
前端中的异步编程技术,类似Java中的多线程+线程结果回调!
- 所谓
Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise
对象有以下两个特点。
(1)Promise对象代表一个异步操作,有三种状态:`Pending`(进行中)、`Resolved`(已完成,又称 Fulfilled)和`Rejected`(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是`Promise`这个名字的由来,它的英语意思就是"承诺",表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从`Pending`变为`Resolved`和从`Pending`变为`Rejected`。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。
5.2 Promise 基本用法:
js
<script>
/*
1.实例化promise对象,并且执行(类似Java创建线程对象,并且start)
参数: resolve,reject随意命名,但是一般这么叫!
参数: resolve,reject分别处理成功和失败的两个函数! 成功resolve(结果) 失败reject(结果)
参数: 在function中调用这里两个方法,那么promise会处于两个不同的状态
状态: promise有三个状态
pending 正在运行
resolved 内部调用了resolve方法
rejected 内部调用了reject方法
参数: 在第二步回调函数中就可以获取对应的结果
*/
let promise =new Promise(function(resolve,reject){
console.log("promise do some code ... ...")
//resolve("promise success")
reject("promise fail")
})
console.log('other code1111 invoked')
//2.获取回调函数结果 then在这里会等待promise中的运行结果,但是不会阻塞代码继续运行
promise.then(
function(value){console.log(`promise中执行了resolve:${value}`)},
function(error){console.log(`promise中执行了reject:${error}`)}
)
// 3 其他代码执行
console.log('other code2222 invoked')
</script>
这个value
里面放的是resolve
函数中传入的参数,也就是promise success
。`
Promise.prototype.catch
方法是.then(null, rejection)
的别名,用于指定发生错误时的回调函数。:
html
<script>
let promise =new Promise(function(resolve,reject){
console.log("promise do some code ... ...")
// 故意响应一个异常对象
throw new Error("error message")
})
console.log('other code1111 invoked')
/*
then中的reject()的对应方法可以在产生异常时执行,接收到的就是异常中的提示信息
then中可以只留一个resolve()的对应方法,reject()方法可以用后续的catch替换
then中的reject对应的回调函数被后续的catch替换后,catch中接收的数据是一个异常对象
*/
promise.then(
function(resolveValue){console.log(`promise中执行了resolve:${resolveValue}`)}
//,
//function(rejectValue){console.log(`promise中执行了reject:${rejectValue}`)}
).catch(
function(error){console.log(error)}
)
console.log('other code2222 invoked')
</script>
5.3.async与await:
async 用于标识函数的
- async标识函数后,async函数的返回值会变成一个promise对象
- 如果函数内部返回的数据是一个非promise对象,async函数的结果会返回一个成功状态 promise对象
- 如果函数内部返回的是一个promise对象,则async函数返回的状态与结果由该对象决定
- 如果函数内部抛出的是一个异常,则async函数返回的是一个失败的promise对象
js
<script>
/*
async 用于标识函数的
1. async标识函数后,async函数的返回值会变成一个promise对象
2. 如果函数内部返回的数据是一个非promise对象,async函数的结果会返回一个成功状态 promise对象
3. 如果函数内部返回的是一个promise对象,则async函数返回的状态与结果由该对象决定
4. 如果函数内部抛出的是一个异常,则async函数返回的是一个失败的promise对象
*/
async function fun1(){
//return 10
//throw new Error("something wrong")
let promise = Promise.reject("heihei")
return promise
}
let promise =fun1()
promise.then(
function(value){
console.log("success:"+value)
}
).catch(
function(value){
console.log("fail:"+value)
}
)
</script>
await
- await右侧的表达式一般为一个promise对象,但是也可以是一个其他值
- 如果表达式是promise对象,await返回的是promise成功的值
- await会等右边的promise对象执行结束,然后再获取结果,后续代码也会等待await的执行
- 如果表达式是其他值,则直接返回该值
- await必须在async函数中,但是async函数中可以没有await
- 如果await右边的promise失败了,就会抛出异常,需要通过 try ... catch捕获处理
js
<script>
/*
1. await右侧的表达式一般为一个promise对象,但是也可以是一个其他值
2. 如果表达式是promise对象,await返回的是promise成功的值
3. await会等右边的promise对象执行结束,然后再获取结果,后续代码也会等待await的执行
4. 如果表达式是其他值,则直接返回该值
5. await必须在async函数中,但是async函数中可以没有await
6. 如果await右边的promise失败了,就会抛出异常,可以通过 try ... catch捕获处理
*/
async function fun1(){
return 10
}
async function fun2(){
try{
let res = await fun1()
//let res = await Promise.reject("something wrong")
}catch(e){
console.log("catch got:"+e)
}
console.log("await got:"+res)
}
fun2()
</script>
5.4.axios基本使用:
1 案例需求:请求后台获取随机土味情话
注意,要安装axios。
vue
<script setup type="module">
import axios from 'axios'
import { onMounted,reactive } from 'vue';
let jsonData =reactive({code:1,content:'我努力不是为了你而是因为你'})
let getLoveMessage =()=>{
axios({
method:"post", // 请求方式
url:"https://api.uomg.com/api/rand.qinghua?format=json", // 请求的url
data:{ // 当请求方式为post时,data下的数据以JSON串放入请求体,否则以key=value形式放url后
username:"123456"
}
}).then( function (response){//响应成功时要执行的函数
console.log(response)
Object.assign(jsonData,response.data)
}).catch(function (error){// 响应失败时要执行的函数
console.log(error)
})
}
/* 通过onMounted生命周期,自动加载一次 */
onMounted(()=>{
getLoveMessage()
})
</script>
<template>
<div>
<h1>今日土味情话:{{jsonData.content}}</h1>
<button @click="getLoveMessage">获取今日土味情话</button>
</div>
</template>
<style scoped>
</style>
异步响应的数据结构
json
{
// `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: {}
}
then取值:
js
then(function (response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
通过async和await处理异步请求:
html
<script setup type="module">
import axios from 'axios'
import { onMounted,reactive } from 'vue';
let jsonData =reactive({code:1,content:'我努力不是为了你而是因为你'})
let getLoveWords = async ()=>{
return await axios({
method:"post",
url:"https://api.uomg.com/api/rand.qinghua?format=json",
data:{
username:"123456"
}
})
}
let getLoveMessage =()=>{
let {data} = await getLoveWords()
Object.assign(message,data)
}
/* 通过onMounted生命周期,自动加载一次 */
onMounted(()=>{
getLoveMessage()
})
</script>
<template>
<div>
<h1>今日土味情话:{{jsonData.content}}</h1>
<button @click="getLoveMessage">获取今日土味情话</button>
</div>
</template>
<style scoped>
</style>
axios在发送异步请求时的可选配置:
js
{
// `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 // 默认值
}
Axios get和post方法:
配置添加语法:
js
axios.get(url[, config])
axios.get(url,{
上面指定配置key:配置值,
上面指定配置key:配置值
})
axios.post(url[, data[, config]])
axios.post(url,{key:value //此位置数据,没有空对象即可{}},{
上面指定配置key:配置值,
上面指定配置key:配置值
})
get:
js
<script setup type="module">
import axios from 'axios'
import { onMounted,ref,reactive,toRaw } from 'vue';
let jsonData =reactive({code:1,content:'我努力不是为了你而是因为你'})
let getLoveWords= async ()=>{
try{
return await axios.get(
'https://api.uomg.com/api/rand.qinghua',
{
params:{// 向url后添加的键值对参数
format:'json',
username:'zhangsan',
password:'123456'
},
headers:{// 设置请求头
'Accept' : 'application/json, text/plain, text/html,*/*'
}
}
)
}catch (e){
return await e
}
}
let getLoveMessage =()=>{
let {data} = await getLoveWords()
Object.assign(message,data)
}
/* 通过onMounted生命周期,自动加载一次 */
onMounted(()=>{
getLoveMessage()
})
</script>
<template>
<div>
<h1>今日土味情话:{{jsonData.content}}</h1>
<button @click="getLoveMessage">获取今日土味情话</button>
</div>
</template>
<style scoped>
</style>
Post:
js
<script setup type="module">
import axios from 'axios'
import { onMounted,ref,reactive,toRaw } from 'vue';
let jsonData =reactive({code:1,content:'我努力不是为了你而是因为你'})
let getLoveWords= async ()=>{
try{
return await axios.post(
'https://api.uomg.com/api/rand.qinghua',
{//请求体中的JSON数据
username:'zhangsan',
password:'123456'
},
{// 其他参数
params:{// url上拼接的键值对参数
format:'json',
},
headers:{// 请求头
'Accept' : 'application/json, text/plain, text/html,*/*',
'X-Requested-With': 'XMLHttpRequest'
}
}
)
}catch (e){
return await e
}
}
let getLoveMessage =()=>{
let {data} = await getLoveWords()
Object.assign(message,data)
}
/* 通过onMounted生命周期,自动加载一次 */
onMounted(()=>{
getLoveMessage()
})
</script>
<template>
<div>
<h1>今日土味情话:{{jsonData.content}}</h1>
<button @click="getLoveMessage">获取今日土味情话</button>
</div>
</template>
<style scoped>
</style>
5.5.拦截器:
如果想在axios发送请求之前,或者是数据响应回来在执行then方法之前做一些额外的工作,可以通过拦截器完成:
js
// 添加请求拦截器 请求发送之前
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);
}
);
定义src/axios.js提取拦截器和配置语法:
js
import axios from 'axios'
// 创建instance实例
const instance = axios.create({
baseURL:'https://api.uomg.com',
timeout:10000
})
// 添加请求拦截
instance.interceptors.request.use(
// 设置请求头配置信息
config=>{
//处理指定的请求头
console.log("before request")
config.headers.Accept = 'application/json, text/plain, text/html,*/*'
return config
},
// 设置请求错误处理函数
error=>{
console.log("request error")
return Promise.reject(error)
}
)
// 添加响应拦截器
instance.interceptors.response.use(
// 设置响应正确时的处理函数
response=>{
console.log("after success response")
console.log(response)
return response
},
// 设置响应异常时的处理函数
error=>{
console.log("after fail response")
console.log(error)
return Promise.reject(error)
}
)
// 默认导出
export default instance
App.vue:
vue
<script setup type="module">
// 导入我们自己定义的axios.js文件,而不是导入axios依赖
import axios from './axios.js'
import { onMounted,ref,reactive,toRaw } from 'vue';
let jsonData =reactive({code:1,content:'我努力不是为了你而是因为你'})
let getLoveWords= async ()=>{
try{
return await axios.post(
'api/rand.qinghua',
{
username:'zhangsan',
password:'123456'
},//请求体中的JSON数据
{
params:{
format:'json',
}
}// 其他键值对参数
)
}catch (e){
return await e
}
}
let getLoveMessage =()=>{
// 这里返回的是一个fullfilled状态的promise
getLoveWords().then(
(response) =>{
console.log("after getloveWords")
console.log(response)
Object.assign(jsonData,response.data)
}
)
}
/* 通过onMounted生命周期,自动加载一次 */
onMounted(()=>{
getLoveMessage()
})
</script>
<template>
<div>
<h1>今日土味情话:{{jsonData.content}}</h1>
<button @click="getLoveMessage">获取今日土味情话</button>
</div>
</template>
<style scoped>
</style>