VUE3
Ajax
仅对原生ajax作了解。
<script>
function test(){
// 实例化XMLHttpRequest
var request = new XMLHttpRequest();
// 设置回调函数处理响应结果
request.onreadystatechange = function() {
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
// 响应成功的逻辑
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
// 设置请求方式和请求的资源路径
request.open("GET","/try/ajax/ajax_info.txt",true);
// 发送请求
request.send();
}
</script>
ES6
ECMAScript 6,JavaScript语言的版本。
let和const
let
和var
的差别:
let不能重复声明;
let有块级作用域,只能在花括号里面访问;
let不会预解析进行变量提升(必须先声明,再使用);
let 定义的全局变量不会作为window的属性。
const
就是常量,变量的值不能修改,对于数组和对象来说指向的地址不能修改(比如给数组添加元素是可以的)。
所以推荐使用let
和const
。
模板字符串
类似python的f""
,里面可以换行,变量用${变量名}
。
let str =`${name}被评为本年级优秀学员`;
解构表达式
数组解构赋值
let [a, b, c] = [1, 2, 3]; let [a, b, c, d = 4] = [1, 2, 3]; // 赋默认值
对象解构赋值
let {a, b} = {a: 1, b: 2}; // 新增变量名必须和属性名相同 let {a: x, b: y} = {a: 1, b: 2}; // 使用:操作符指定新的变量名 console.log(x, y);
函数参数解构赋值
function add([x, y]) { return x + y; } add([1, 2]); // 3
箭头函数
函数声明
let fn1 = function(){} // 常规
let fn2 = ()=>{} // 箭头函数,此处不需要书写function关键字
let fn3 = x =>{} // 单参数可以省略(),多参数/无参数不可以!
let fn4 = x => console.log(x) // 只有一行方法体可以省略{};
let fun5 = x => x + 1 // 当函数体只有一句返回值时,可以省略花括号和 return 语句
this关键字
- 常规函数中
this
表示该函数所在的对象; - 箭头函数中
this
表示外层上下文环境的this(即所在对象的所在对象)。
let person ={
name:"张三",
showName:function (){
console.log(this) // 这里的this是person
},
viewName: () =>{
console.log(this) // 这里的this是window
}
}
rest和spread
rest
相当于可变参数。
let fun1 = function (...args){console.log(args)}
let fun2 = (...args) =>{console.log(args)}
// args会被当成数组
fun1(1,2,3)
fun2(1,2,3,4)
spread,类似python中的*
,解包。
// 在实参上使用
let arr = [1,2,3]
let fun1 = (a,b,c) => {
console.log(a,b,c)
}
fun1(...arr) // 调用方法时,对arr进行转换 转换为1,2,3
// 合并数组
let arr2 = [4,5,6]
let arr3 = [...arr,...arr2]
console.log(arr3)
// 合并对象属性
let p1 = {name:"张三"}
let p2 = {age:10}
let p3 = {gender:"boy"}
let person = {...p1,...p2,...p3}
console.log(person)
类与对象
class Person {
// 属性
#n; // 带#表示私有,注意这个变量就叫#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.eat("火锅")
console.log(Person.sum(1,2))
class Student extends Person{
score;
study() {
}
constructor(name,age,score) {
super(name,age);
this.score = score;
}
}
let stu =new Student("学生小李",18,99)
stu.eat("面条")
深拷贝和浅拷贝
let arr =['java','c','python']
let person ={
name:'张三',
language:arr
}
// 浅拷贝,person2和person指向相同的内存
let arr2 = arr
let person2 = person;
// 深拷贝,通过json转换/用解构表达式
let arr2 = [...arr]
let person2 = JSON.parse(JSON.stringify(person))
let person3 = {...person}
模块化
类似java的import。
分别导出
// 模块想对外导出,添加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.`); } }
/* *代表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()
<!-- 导入JS文件 添加type='module' 属性,否则不支持ES6的模块化 --> <script src="./app.js" type="module" />
统一导出
// 模块想对外导出,export统一暴露想暴露的内容 // 定义一个常量 const PI = 3.14 // 定义一个函数 function sum(a, b) { return a + b; } // 定义一个类 class Person { ...... } // 统一对外导出(暴露) export {PI, sum, Person}
/* {}中导入要使用的来自于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) let result2 =add(10,20) // 使用暴露的Person类 let person1 =new Person('张三',10) person1.sayHello() let person2 =new People('李四',11) person2.sayHello()
默认导出
/* 默认暴露语法 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}
/* *代表module.js中的所有成员 m1代表所有成员所属的对象 */ import * as m1 from './module.js' import {default as add} from './module.js' // 用的少 import 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)
npm
项目初始化
npm init # 创建package.json文件,包含项目基本信息 npm init -y # 使用默认信息创建
安装依赖 :查看所有依赖地址https://www.npmjs.com
npm install 包名 # 安装最新版本到当前项目 npm install 包名@版本号 # 安装指定版本到当前项目 npm install -g 包名 # 安装全局依赖包 npm install # 安装package.json中的所有记录的依赖
卸载依赖
npm uninstall 包名
升级依赖
npm update 包名 # 将依赖升级到最新版本
查看依赖
npm ls # 查看当前项目依赖 npm list -g # 查看全局依赖
运行命令
运行
package.json
中scripts
下的命令。// package.json { ...... "scripts": { "start": "node index.js", "test": "jest", "build": "webpack" }, ...... }
npm run test npm run build
Vite
创建、运行
npm create vite@latest # 创建项目(或省略@latest)
npm create vite
npm install # 安装依赖
npm run dev # 启动
目录结构
public/
:用于存放一些公共资源,如 HTML 文件、图像、字体等,这些资源会被直接复制到构建出的目标目录中。src/
:存放项目的源代码,包括 JavaScript、CSS、Vue 组件、图像和字体等资源。在开发过程中,这些文件会被 Vite 实时编译和处理,并在浏览器中进行实时预览和调试。以下是src内部划分建议:assets/
:用于存放一些项目中用到的静态资源,如图片、字体、样式文件等。components/
:用于存放组件相关的文件。组件是代码复用的一种方式,用于抽象出一个可复用的 UI 部件,方便在不同的场景中进行重复使用。layouts/
:用于存放布局组件的文件。布局组件通常负责整个应用程序的整体布局,如头部、底部、导航菜单等。pages/
:用于存放页面级别的组件文件,通常是路由对应的组件文件。在这个目录下,可以创建对应的文件夹,用于存储不同的页面组件。plugins/
:用于存放 Vite 插件相关的文件,可以按需加载不同的插件来实现不同的功能,如自动化测试、代码压缩等。router/
:用于存放 Vue.js 的路由配置文件,负责管理视图和 URL 之间的映射关系,方便实现页面之间的跳转和数据传递。store/
:用于存放 Vuex 状态管理相关的文件,负责管理应用程序中的数据和状态,方便统一管理和共享数据,提高开发效率。utils/
:用于存放一些通用的工具函数,如日期处理函数、字符串操作函数等。main.js
:这是 Vue.js 应用程序的启动文件,也是整个前端应用程序的入口文件。在该文件中,通常会引入 Vue.js 及其相关插件和组件,同时会创建 Vue 实例,挂载到 HTML 页面上指定的 DOM 元素中。
vite.config.js
:Vite 的配置文件,可以通过该文件配置项目的参数、插件、打包优化等。该文件可以使用 CommonJS 或 ES6 模块的语法进行配置。package.json
:标准的 Node.js 项目配置文件,包含了项目的基本信息和依赖关系。
项目组件
传统的页面由html、css和js三个文件组成(多文件组件),vue将这文件合并成一个.vue文件(Single-File Component,简称SFC,单文件组件)。
<template>
:代表组件的html部分代码,代替传统的.html文件;<script>
:代表组件的js代码,代替传统的.js文件;<style>
:代表组件的css样式代码,代替传统的.css文件。
样式的导入方式
只有当前vue文件使用的样式直接写在style标签中。
全局引入main.js
import './style/reset.css'
vue文件script代码引入
import './style/reset.css'
vue文件style代码引入
@import './style/reset.css'
响应式和setup函数
VUE3中数据要经过ref
或者reactive
处理后才是响应式的。ref是VUE3框架提供的一个函数,需要导入。ref处理的响应式数据:
- 在js编码修改的时候需要
.value
; - 在绑定到html上时不需要
.value
。
setup:原始的写法,需要export default
和return
。
<script type="module">
import {ref} from 'vue'
export default {
setup(){
let counter = ref(1)
function increase(){
counter.value++
}
function decrease(){
counter.value--
}
return {
counter,
increase,
decrease
}
}
}
</script>
在<script>
中添加setup
可省略上述内容。
<script type="module" setup>
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>
VUE3视图渲染
模版语法
文本渲染
插值表达式
插值表达式能实现文本插值,使用的是“Mustache”语法,即双大括号{{}}
。
<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>
购物车总金额: {{ compute() }} <br/>
<!-- 使用运算符 -->
是否成年: {{ age>=18?'true':'false' }} <br>
<!-- 支持对象的API -->
反转: {{ bee.split(' ').reverse().join('-') }} <br>
</div>
</template>
v-text和v-html
v-text
将数据渲染成双标签中间的文本,但是不识别html元素结构的文本;v-html
将数据渲染成双标签中间的文本,识别html元素结构的文本。
<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="`<font color='green'>${msg}</font>`"></span> <br>
</div>
</template>
属性渲染
v-bind
可以用于渲染任何元素的属性,语法为
v-bind:属性名='数据名'
, 可以简写为
:属性名='数据名'
。
<script setup type="module">
const data = {
name:'尚硅谷',
url:"http://www.atguigu.com",
logo:"http://xxx/logo.png"
}
</script>
<template>
<div>
<a v-bind:href='data.url'>
<img :src="data.logo" :title="data.name">
<input type="button" :value="`点击访问${data.name}`">
</a>
</div>
</template>
事件绑定
使用
v-on:click="handler"
,简写为@click="handler"
;vue中的事件名=原生事件名去掉
on
前缀,如:onClick --> click
;handler的值可以是方法事件处理器,也可以是内联事件处理器(直接写处理事件的代码);
绑定事件时,可以使用修饰符,常见的事件修饰符如下:
.once
:只触发一次事件;.prevent
:阻止默认事件。
<script setup type="module">
import {ref} from 'vue'
let count=ref(0)
let addCount= ()=>{
count.value++
}
let fun= (event)=>{
let flag = confirm("确定访问吗")
if (!flag) {
// 通过事件对象阻止组件的默认行为
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="fun($event)">prevent</a> <br>
</div>
</template>
响应式基础
ref
:更适合单个变量- 在
<script>
中操作需要.value
; - 在
<template>
中无需.value
。
- 在
reactive
:更适合对象,都无需.value
。
toRef
:将reactive响应式数据的某个属性转化为ref响应式数据;toRefs
:将reactive响应式数据的多个属性转化为ref响应式数据;
<script type="module" setup>
import {ref,reactive,toRef,toRefs} from 'vue'
let counter = ref(0);
let data = reactive({
name: "test",
counter:0
})
function show(){
alert(counter.value);
}
let incr = () =>{
counter.value++;
}
let incrData = () =>{
data.counter++;
}
let ct =toRef(data,'counter');
let {counter,name} = toRefs(data);
</script>
<template>
<div>
<button @click="counter++">+</button>
<button @click="incr()">+</button>
<button @click="data.counter++">+</button>
<button @click="incrData()">+</button>
</div>
</template>
条件渲染
v-if='表达式'
只会在指令的表达式返回真值时才被渲染,数据为false时,元素不在DOM树中;- 使用
v-else
为v-if
添加一个“else 区块”,一个v-else
元素必须跟在一个v-if
元素后面,否则它将不会被识别。 v-show
类似v-if
。数据为false时,元素仍在DOM树中,只是将CSS属性display
改为none;不能和v-else
搭配使用。
<template>
<div>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
<h1 id="ha" v-show="awesome">Vue is awesome!</h1>
</div>
</template>
总结:v-if
有更高的切换开销,而
v-show
有更高的初始渲染开销。因此,如果需要频繁切换,则使用
v-show
较好;如果在运行时绑定条件很少改变,则
v-if
会更合适。
列表渲染
v-for
指令的值需要使用 item in items
形式的特殊语法,其中 items
是源数据的数组,而
item
是迭代项的别名。
<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>
双向绑定
- 响应式数据的变化会更新dom树,dom树上用户的操作造成的数据改变也会同步更新到响应式数据;
- 一般用于表单标签;
- 语法:
v-model=''
。
<script type="module" setup>
import { reactive,ref } from 'vue'
let hbs = ref([]); //爱好
let user = reactive({
username:null,
password:null,
introduce:null,
pro:null
})
function clearx(){
//user = {};// 这种写法会将数据变成非响应的
user.username=''
user.password=''
user.introduce=''
user.pro=''
hbs.value.splice(0,hbs.value.length);
}
</script>
<template>
<div>
账号: <input type="text" v-model="user.username"> <br>
密码: <input type="text" 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="玩">
<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>
</select>
<br>
<button @click="clearx()">重置</button>
<hr>
显示爱好:{{ hbs }}
<hr>
显示用户信息:{{ user }}
</div>
</template>
计算属性
- 计算属性:直接当成属性调用(不加括号)。如果数据没有变化,则使用之前的数据。
- 普通函数:调用有括号。每使用一次就执行一次。
<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'
]
})
// 计算属性
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>
<!-- 调用函数,每个标签都会调用一次,所以console.log两次 -->
<span>{{ hasBooks() }}</span>
<span>{{ hasBooks() }}</span>
<!-- 属性计算,属性值不变时,多个标签只会调用一次,所以console.log一次 -->
<span>{{ publishedBooksMessage }}</span>
<span>{{ publishedBooksMessage }}</span>
</div>
</template>
数据监听器
watch
:过于复杂。watchEffect
:监听回调函数里出现的响应式数据。
<script type="module" setup>
import { ref,reactive,watchEffect } from 'vue'
let firstname=ref('')
let lastname=reactive({name:''})
let fullname=ref('')
watchEffect(() => {
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>
VUE生命周期
import {ref,onUpdated,onMounted,onBeforeUpdate} from 'vue'
let message =ref('hello')
// 挂载完毕生命周期
onMounted(()=>{
console.log('-----------onMounted---------')
let span1 =document.getElementById("span1")
console.log(span1.innerText)
})
// 更新前生命周期
onBeforeUpdate(()=>{
console.log('-----------onBeforeUpdate---------')
let span1 =document.getElementById("span1")
console.log(span1.innerText)
})
// 更新完成生命周期
onUpdated(()=>{
console.log('-----------onUpdated---------')
let span1 =document.getElementById("span1")
console.log(span1.innerText)
})
VUE组件
拼接页面
为实现上图效果,在components/
下创建Header.vue
、Navigator.vue
、Content.vue
并编写相应代码,在App.vue
中编写如下代码。
<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>
传递参数
分为:父传子、子传父、兄弟传参(子传父+父传子)
以兄弟传参为例:
Navigator.vue
代码:
<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>
App.vue
代码:
<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>
Content.vue
代码:
<script setup type="module">
import {defineProps} from "vue"
defineProps({
message:String
})
</script>
<template>
<div>
展示的主要内容!
<hr>
{{ message }}
</div>
</template>
VUE3路由Router
npm install vue-router@4 //安装vue-router 4版本
路由入门
src/routers/router.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, // 对应默认的<router-view>
homeView:Home // 对应name为homeView的<router-view>
}
// redirect: "/list" // 重定向
},
{
path:'/list',
components:List
},
{
path:'/add',
components:Add
},
{
path:'/update',
components:Update
}
]
})
// 对外暴露路由对象
export default router;
main.js
import { createApp } from 'vue'
import App from './App.vue'
// 导入router模块
import router from './routers/router.js'
let app = createApp(App)
// 绑定路由对象
app.use(router)
// 挂载
app.mount("#app")
App.vue
:<router-view>
会被替换为组件。
<template>
<div>
<!-- 路由的连接 -->
<router-link to="/">home页</router-link> <br>
<router-link to="/list">list页</router-link> <br>
<router-link to="/add">add页</router-link> <br>
<router-link to="/update">update页</router-link> <br>
<!-- 路由连接对应视图的展示位置 -->
<!-- 默认展示位置(一般用这个就够了) -->
<router-view></router-view>
<!-- 单独展示某个组件,在router.js中对应 -->
<router-view name="homeView"></router-view>
</div>
</template>
编程式路由
上面的<router-link>
是写死的,下面通过代码来跳转。
App.vue
:用的是useRouter
,和下一节区分。
<script setup type="module">
import {useRouter} from 'vue-router'
//创建动态路由对象
let router = useRouter()
let showList = () => {
// 方式一:直接push一个路径
router.push('/list')
// 方式二:push一个带有path属性的对象
router.push({path:'/list'})
}
</script>
<template>
<div>
<h1>App页面</h1>
<!-- 通过编程实现路由切换 -->
<button @click="showList()">showList</button> <br>
<hr/>
<!-- 路由连接对应视图的展示位置 -->
<router-view></router-view>
</div>
</template>
路由传参
- 路径参数:如
/showDetail/1/java
,中的1
和java
就是参数; - 键值对参数:即
/showDetail?id=1&language=java
。
router.js
:
const router = createRouter({
history: createWebHashHistory(),
routes:[
{
// 路径参数写法
path:'/showDetail/:id/:language',
components:ShowDetail
},
{
// 键值对参数写法(和之前一样)
path:'/showDetail2',
components:ShowDetail2
},
]
})
App.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)=>{
// 方式一:使用拼接字符串方式传递
router.push(`/showDetail?id=${id}&language=${language}`)
// 方式二:传对象,使用query
router.push({path:"/showDetail2",query:{id:id,language:language}})
}
</script>
<template>
<div>
<!-- 路径参数 -->
<router-link to="/showDetail/1/JAVA">普通路由</router-link>
<button @click="showDetail(1,'JAVA')">编程路由</button>
<!-- 键值对参数 -->
<router-link to="/showDetail?id=1&language=JAVA">普通路由1</router-link>
<router-link v-bind:to="{path:'/showDetail2',query:{id:1,language:'Java'}}">普通路由2,同上</router-link>
<button @click="showDetail2(1,'JAVA')">编程式路由</button>
<hr>
<router-view></router-view>
</div>
</template>
ShowDetail.vue
:接收参数。注意是useRoute
。
<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 (()=>{
// 路径参数用params
languageId.value=route.params.id
languageName.value=route.params.language
// 键值对参数用query
languageId.value=route.query.id
languageName.value=route.query.language
})
</script>
路由守卫
- 全局前置守卫:在路由切换前被调用,可以用于验证用户是否已登录、中断导航、请求数据等。
- 全局后置守卫:在路由切换之后被调用,可以用于处理数据、操作 DOM 、记录日志等。
router.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}`);
});
VUE3数据交互Axios
Promise
- Promise对象代表一个异步操作,有三种状态:
Pending
(进行中)、Resolved
(已完成,又称 Fulfilled)和Rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从
Pending
变为Resolved
和从Pending
变为Rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。
基本用法
resolve
和reject
是两个函数,如果调用resolve
,状态会由pending转换为resolved;如果调用reject
,状态会由pending转换为reject。可以写参数,then
和catch
里的回调函数可以接收到这些参数。then
中的函数:状态转换为resolved时会执行;catch
中的函数:状态转换为reject时或异常时会执行。
let =new Promise(function(resolve,reject){
console.log("promise do some code ... ...")
resolve("promise success")
// reject("promise fail")
// throw new Error("error message")
})
promise.then(
function(resolveValue){
console.log(`promise中执行了resolve:${resolveValue}`)
}
).catch(
function(error){
console.log(error)
}
)
async和await
async
标识函数后,async
函数的返回值会变成一个promise对象;如果返回的是一个非promise对象(如整数、字符串等),async函数的结果会返回一个成功状态的promise对象;
如果返回的是一个promise对象,则async函数返回的状态与结果由该对象决定(该promise对象可以resolve或reject);
如果函数抛出的是一个异常,则async函数返回的是一个失败的promise对象。
async function fun1(){
// return 10 // 返回成功的promise对象
// throw new Error("something wrong") // 返回失败的promise对象
let promise = Promise.reject("oh on") // 返回失败的promise对象
let promise = Promise.resolve("heihei") // 返回成功的promise对象
return promise
}
let promise = fun1()
promise.then(
function(value){
console.log("success:"+value)
}
).catch(
function(value){
console.log("fail:"+value)
}
)
await
是帮助获取promise成功状态返回值的关键字;await
右侧如果是普通值则直接返回;如果是promise对象则返回promise成功状态的结果;如果是失败状态的promise则抛异常;await
必须在async函数中,但是async函数中可以没有await;await
后边的代码会等待await执行完毕后再继续执行。
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()
Axios入门
npm install axios
axios
返回一个promise对象。
<script setup type="module">
import axios from 'axios'
import { reactive } from 'vue';
let jsonData =reactive({
code:1,
content:"xxx"
})
function getLoveMessage() {
axios({
// 请求方式
method:"post",
// 请求的url
url:"https://api.uomg.com/api/rand.qinghua?format=json",
// 请求方式为post时,data下的数据会以JSON串放入请求体
data:{
username:"123456"
},
// params中的数据会以key=value形式放在url后
params: {
format: 'json'
}
}).then( // 响应成功时要执行的函数
function (response){
console.log(response);
Object.assign(jsonData,response.data);
}
).catch( // 响应失败时要执行的函数
function (error){
console.log(error);
}
)
}
</script>
<template>
<div>
<h1>今日土味情话:{{jsonData.content}}</h1>
<button @click="getLoveMessage()">获取今日土味情话</button>
</div>
</template>
响应的数据:
{
// `data` 由服务器提供的响应
data: {},
// `status` 来自服务器响应的 HTTP 状态码
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 是服务器响应头
// 所有的 header 名称都是小写,而且可以使用方括号语法访问
// 例如: `response.headers['content-type']`
headers: {},
// `config` 是 `axios` 请求的配置信息
config: {},
// `request` 是生成此响应的请求,在浏览器中是 XMLHttpRequest 实例
request: {}
}
get和post方法
axios.get(url[, config])
function getLoveWords() {
return axios.get(
'https://api.uomg.com/api/rand.qinghua',
{
params:{ // 向url后添加的键值对参数
format:'json',
username:'zhangsan'
},
headers:{ // 设置请求头
'Accept' : 'application/json'
}
}
)
}
async function getLoveMessage() {
let {data} = await getLoveWords()
Object.assign(message,data)
}
axios.post(url[, data[, config]])
return axios.post(
'https://api.uomg.com/api/rand.qinghua',
{ //请求体中的JSON数据
username:'zhangsan',
password:'123456'
},
{ // 其他参数
params:{ // url上拼接的键值对参数
format:'json',
},
headers:{ // 请求头
'Accept' : 'application/json'
}
}
)
拦截器
src/axios.js
中配置拦截器。
import axios from 'axios'
// 创建instance实例,发送请求都用这个实例
const instance = axios.create({
baseURL:'https://api.uomg.com',
timeout:10000
})
// 添加请求拦截器,参数为两个函数
instance.interceptors.request.use(
// 函数1:发送请求之前拦截(比如可以处理请求头)
config => {
console.log("before request")
config.headers.Accept = 'application/json'
return config // 一定要return
},
// 函数2:请求错误都处理函数
error => {
console.log("request error")
return Promise.reject(error) // 一定要返回错误
}
)
// 添加响应拦截器,参数为两个函数
instance.interceptors.response.use(
// 函数1:设置响应正确时(返回200)的处理函数
response => {
console.log("after success response")
console.log(response)
return response // 记得return
},
// 函数2:设置响应异常时的处理函数(如404)
error => {
console.log("after fail response")
console.log(error)
return Promise.reject(error) // 返回错误
}
)
// 默认导出
export default instance
其他js中使用上面定义的instance:
import axios from './axios.js'
function getLoveWords() {
return axios.get('https://api.uomg.com/api/rand.qinghua')
}
VUE3状态管理Pinia
npm install pinia
src/store/store.js
:定义共享数据。
import { defineStore } from 'pinia'
export const definedPerson = defineStore(
{
// 数据id,必须全局唯一
id: 'personPinia',
// 状态,其实就是响应式数据,用函数返回
state:()=>{
return {
username:'张三',
age:0,
hobbies:['唱歌','跳舞']
}
},
// 定义一些通过计算而得到结果的一些方法,不修改数据
getters:{
// getters中的方法可以当做属性值方式使用!
getHobbiesCount(){
return this.hobbies.length
},
// 推荐用箭头函数
getAge: (state) => {
return state.age
}
},
// 定义一些对数据修改的方法,可以是异步的
actions:{
doubleAge(){
this.age=this.age*2
}
}
}
)
id
也可以放前面
export const useStore = defineStore('main', { state: () => ({ count: 0, }), getters: { doubleCount: (state) => state.count * 2, }, })
main.js
:app中开启pinia功能。
import { createApp } from 'vue'
import App from './App.vue'
import router from './routers/router.js'
// 导入pinia
import { createPinia } from 'pinia'
let pinia = createPinia() // 创建pinia对象
let app =createApp(App)
app.use(router)
app.use(pinia) // app中使用pinia功能
app.mount('#app')
conponents/Operate.vue
:操作pinia数据。
<script setup type="module">
import { definedPerson} from '../store/store';
// 读取存储的数据
let person= definedPerson()
</script>
<template>
<div>
<h1>operate视图,用户操作Pinia中的数据</h1>
请输入姓名:<input type="text" v-model="person.username"> <br>
请输入年龄:<input type="text" v-model="person.age"> <br>
请增加爱好:
<input type="checkbox" value="吃饭" v-model="person.hobbies"> 吃饭
<input type="checkbox" value="睡觉" v-model="person.hobbies"> 睡觉
<input type="checkbox" value="打豆豆" v-model="person.hobbies"> 打豆豆 <br>
<!-- 事件中调用person的doubleAge()方法 -->
<button @click="person.doubleAge()">年龄加倍</button> <br>
<!-- 事件中调用pinia提供的$reset()方法恢复数据的默认值 -->
<button @click="person.$reset()">恢复默认值</button> <br>
<!-- 事件中调用$patch方法一次性修改多个属性值 -->
<button @click="person.$patch({username:'奥特曼',age:100,hobbies:['晒太阳','打怪兽']})">变身奥特曼</button> <br>
显示pinia中的person数据:{{person}}
</div>
</template>
conponents/List.vue
:展示pinia数据。
<script setup type="module">
import { definedPerson} from '../store/store';
// 读取存储的数据
let person= definedPerson()
</script>
<template>
<div>
<h1>List页面,展示Pinia中的数据</h1>
读取姓名:{{person.username}} <br>
读取年龄:{{person.age}} <br>
通过get年龄:{{person.getAge}} <br>
爱好数量:{{person.getHobbiesCount}} <br>
</div>
</template>