文章目录
vue3.0介绍
什么是Vue.js
1、前端开发框架
2、用于构建用户界面的渐进式框架
3、简化复杂的开发过程
4、Vue 也完全有能力为复杂的单页应用程序提供支持
安装
将 Vue.js 添加到项目中有四种主要方法:
1、在页面上将其导入为CDN包
<script src="https://unpkg.com/vue@next"></script>
2、下载 JavaScript 文件并自行托管
如果您想避免使用构建工具但不能在生产中使用 CDN,那么您可以下载相关.js文件并使用您自己的 Web 服务器托管它。然后您可以使用
3、使用npm安装
npm 是使用 Vue 构建大型应用程序时推荐的安装方法
4、使用官方CLI构建项目,该项目为现代前端工作流程(例如,热重载、lint-on-save 等)提供包含电池的构建设置
Vue 提供官方 CLI (打开新窗口)用于快速搭建雄心勃勃的单页应用程序
vite构建工具工具创建vue3项目
使用npm创建
前提:安装Node.js
步骤如下:
$ npm init vite@latest <project-name> 或者 $ npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
使用yarn
$ yarn create vite <project-name> --template vue
$ cd <project-name>
$ yarn
$ yarn dev
vue3项目结构
Vue声明式语法与数据双向绑定
命令式
document.querySelector('h1').innerHTML="小范宝贝"
声明式
export default {
name: "App",
data(){
return{
msg:'小范同学'
}
},
methods:{
changeMsg(){
this.msg = '小范美女'
}
}
};
数据双向绑定
数据变了,页面内容也会变,页面内容变了,数据也随之改变
<h1 @click="changeMsg">{{msg}}</h1>
<input type="text" v-model="msg" />
模板语法
双括号语法
<h1>{{msg}}</h1>
v-once
一次性插值,只渲染一次
<!-- v-once指令,使得内容只渲染一次 -->
<h1 @click="changeMsg" v-once>{{msg}}</h1>
v-html
在内容中插入HTML代码
<!--v-html指令 使得内容插入html的代码 -->
<div>{{content}}</div>
<div v-html="content"></div>
v-bind
绑定属性的内容
<!-- v-bind指令 绑定属性的内容 -->
<div v-bind:id="id"></div>
模板语法中使用JavaScript表达式
<template>
<div>
<!-- JavaScript可以在模板语法中使用 -->
<!-- 双括号语法 -->
<h1>{{ msg }}</h1>
<!-- split()分割msg字符串,得到一个数字 -->
<h1>{{msg.split('')}}</h1>
<!-- reverse() 使得数组内容颠倒 -->
<h1>{{msg.split('').reverse()}}</h1>
<!-- join()使得数组内容合并成字符串 -->
<h1>{{msg.split('').reverse().join('')}}</h1>
<!-- 可以使用三元运算符 -->
<div>{{color=="green"?"开心":"难过"}}</div>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
msg: "Hello,Word!",
color:"green"
};
},
methods: {
},
};
</script>
动态指令
指令对应的属性名称是可以修改的
<template>
<div>
<!-- 动态指令 -->
<div v-bind:[attrbuteName]="d1"></div>
<button @click="toggleColor">点击切换颜色</button>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
attrbuteName:"class",
d1:'d1'
};
},
methods: {
toggleColor() {
this.attrbuteName = "id";
},
},
};
</script>
<style>
#d1{
margin-left: 47%;
width: 100px;
height: 100px;
background-color: yellow;
}
.d1{
margin-left: 47%;
width: 100px;
height: 100px;
background-color: blue;
}
</style>
计算属性
- 简化代码
<template>
<div>
<!-- 原来的msg -->
<h1>{{msg}}</h1>
<!-- 运用JS语句颠倒msg -->
<h1>{{msg.split('').reverse().join('')}}</h1>
<!-- 使用计算属性颠倒msg -->
<h1>{{reverseMsg}}</h1>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
msg: 'Hello World'
};
},
computed:{
reverseMsg(){
return this.msg.split('').reverse().join('')
}
},
methods: {
toggleColor() {
this.attrbuteName = "id";
},
},
};
</script>
watch监听数据的变化
想要监听数据的变化,可以使用watch监听,里面写监听目标变量的函数,里面有两个参数,第一个参数是新值,第二个参数是旧值。
<template>
<div>
<!-- 原来的msg -->
<h1>{{msg}}</h1>
<input type="text" v-model="msg">
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
msg: 'Hello World'
};
},
watch:{
msg(newValue,oldValue){
if(newValue.length<10){
alert("输入的值太少了!!!")
}
}
}
};
</script>
类的多种绑定方式
放置字符串
<template>
<div>
<!-- 类class -->
<!-- 第一种写法,放置字符串 -->
<h1 v-bind:class = "msg">hello</h1>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
msg: 'Hello World'
};
},
};
</script>
放置对象
<template>
<div>
<!-- 类class -->
<!-- 放置对象 -->
<h1 v-bind:class="{active:isActive}">Hello</h1>
<button @click="toggleActive">切换样式</button>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
isActive: true
};
},
methods:{
toggleActive(){
this.isActive=!this.isActive
}
}
};
</script>
<style scoped>
.active{
background-color: yellow;
}
</style>
放置数组
<template>
<div>
<!-- 类class -->
<!-- 放置数组 -->
<h1 :class="arr">Hello</h1>
<button @click="toggleActive">切换类</button>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
arr: ['swiper','active']
};
},
methods:{
toggleActive(){
this.arr.pop()
}
}
};
</script>
数组和对象的结合
<template>
<div>
<!-- 类class -->
<!-- 数组和对象的结合 -->
<h1 :class="[className, {active:isActive}]">Hello</h1>
<button @click="toggleActive">切换类</button>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
className : "abc",
isActive : true
};
},
methods:{
toggleActive(){
this.className= "cba",
this.isActive = !this.isActive
}
}
};
</script>
<style scoped>
.active {
background-color: yellow;
}
</style>
Style样式的多种绑定方式
放置字符串
<template>
<div>
<!-- 类class -->
<!-- 第一种写法:放置字符串 -->
<h1 :style="msg">Hello</h1>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
msg: "background:red;"
};
},
};
</script>
放置对象
<template>
<div>
<!-- 类class -->
<!-- 第一种写法:放置字符串 -->
<h1 :style="{background: 'purple'}">Hello</h1>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
};
},
};
</script>
放置数组
<template>
<div>
<!-- 类class -->
<!-- 第一种写法:放置字符串 -->
<h1 :style="[styleObj,{boxSizing: 'border-box'}]">Hello</h1>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
styleObj:{
background: "pink",
border: '1px solid blue'
}
};
},
};
</script>
v-if 和 v-show
-
v-if…v-else-if…v-else… 可以根据不同的条件显示多个内容,频繁切换的话,较为消耗性能
-
v-show 根据条件确定是否显示单个内容,不显示时,其实是将样式改为display: none 所以性能消耗较小
<template>
<div>
<!-- 设置多个内容切换显示 -->
<h1 v-if = "user == '超级VIP'"> 欢迎金主爸爸</h1>
<h1 v-else>充值使你更强大</h1>
<button @click="toggleUser">切换用户</button>
<!-- 设置一个内容切换显示 -->
<h1 v-show="isShow">只显示我一个</h1>
<button @click="toggleShow">切换</button>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
user:'超级VIP',
isShow: true
};
},
methods:{
toggleUser(){
this.user = "普通用户"
},
toggleShow:function(){
this.isShow = !this.isShow
}
}
};
</script>
v-for循环
- 循环数组
- 循环数组里的对象
- 循环对象
<template>
<div>
<!-- 循环数组 -->
<ol>
<li v-for="(item, i) in news" :key="i">{{ item }}====>{{ i }}</li>
</ol>
<!-- 循环数组里的对象 -->
<ol>
<li v-for="(item, i) in news1" :key="i">
{{ item.title }}==>{{ item.content }}===>{{ item.athor }}====>{{ i }}
</li>
</ol>
<!-- 循环对象 -->
<ol>
<li v-for="(item, i) in athor" :key="i">{{ item }}====>{{ i }}</li>
</ol>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
news: ["11111", "222222", "333333"],
news1: [
{
title: "我是标题1",
content: "我是内容1",
athor: "作者1",
},
{
title: "我是标题2",
content: "我是内容2",
athor: "作者2",
},
],
athor:{
name:'lingLing',
age: '18'
}
}
},
};
</script>
事件与参数、时间修饰符
绑定事件使用 v-on(简写@)
- 绑定事件不需要参数
<h1 @click="addEvent">{{num}}</h1>
- 绑定事件,直接处理表达式
<h1 @click="num += 2">{{num}}</h1>
- 绑定事件,传递参数
<h1 @click=" addEvent1(20)">{{num}}</h1>
- 绑定事件。传递 $event事件对象和参数
<h1 @click=" addEvent1(20,$event)">{{num}}</h1>
- 一个事件绑定多个处理函数
<h1 :style="{background:color}" @click="addEvent1(20,$event),changeColor($event)">{{num}}</h1>
事件修饰符
- 阻止事件冒泡 .stop
- 阻止默认事件 .prevent
- 事件只触发一次 .once
<h1 @click.once=" addEvent1(20,$event)">{{num}}</h1>
按键修饰符
enter/tap.delete/esc/space/up/down/left/right
<input type="text" @keydown.enter="searchEvent">
系统修饰符
.ctrl/.alt/.shift/.meta
鼠标修饰符
.left/.right/.middle
<template>
<div>
<!-- 绑定事件,不需要参数 -->
<h1 @click="addEvent">{{num}}</h1>
<!-- 绑定事件,直接处理表达式 -->
<h1 @click="num += 2">{{num}}</h1>
<!-- 绑定事件,传递参数 -->
<h1 @click=" addEvent1(20)">{{num}}</h1>
<!--绑定事件。传递 $event事件对象和参数 -->
<h1 @click=" addEvent1(20,$event)">{{num}}</h1>
<!-- 一个事件绑定多个处理函数 -->
<h1 :style="{background:color}" @click="addEvent1(20,$event),changeColor($event)">{{num}}</h1>
<!-- 事件修饰符 -->
<!--
阻止事件冒泡 .stop
阻止默认事件 .prevent
事件只触发一次 .once
-->
<h1 @click.once=" addEvent1(20,$event)">{{num}}</h1>
<!-- 按键修饰符
enter/tap.delete/esc/space/up/down/left/right
系统修饰符
.ctrl/.alt/.shift/.meta
鼠标修饰符
.left/.right/.middle
-->
<input type="text" @keydown.enter="searchEvent">
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
num:0,
color: 'red'
}
},
methods:{
addEvent:function(e){
console.log(e)
this.num += 2
},
addEvent1:function(number){
this.num += number
},
addEvent2:function(number,event){
this.num += number
},
changeColor:function(){
this.color = 'pink'
},
searchEvent:function(){
console.log("执行了搜索事件!!!")
}
}
};
</script>
表单输入双向绑定、修饰符
文本框 text
*单行本文框数据的双向绑定
<input v-model="searchKey" type="text" @keydown="searchEvent" />
多行文本框textarea
- 多行文本框的数据绑定
<textarea v-model="lineText" name="textarea" id="" cols="30" rows="10"></textarea>
复选框checkbox
- 复选框的数据绑定,获取的是是否选中的内容
<input type="checkbox" name="likes" v-model="fruits" value="柿子">
<input type="checkbox" name="likes" v-model="fruits" value="香蕉">
<input type="checkbox" name="likes" v-model="fruits" value="葡萄">
<input type="checkbox" name="likes" v-model="fruits" value="苹果">
单选框radio
只能选一个值
<input type="radio" name="sex" value="man" v-model="picked">
<input type="radio" name="sex" value="woman" v-model="picked">
选项框 select
添加multiple属性可以选择多个选项
<select name="citys" v-model="citys" multiple >
<option value="广州"></option>
<option value="上海"></option>
<option value="北京"></option>
<option value="成都"></option>
</select>
数据绑定v-model的修饰符
.lazy
input变为change事件改变值,焦点失焦后才改变
<input v-model.lazy="searchKey1" type="text" @keydown="searchEvent" />
.number
把输入值转换为数字类型
<input v-model.lazy.number="searchKey2" type="text" @keydown="searchEvent" />
.trim
去掉空格
<input v-model.lazy.trim="searchKey3" type="text" @keydown="searchEvent" />
案例代码
<template>
<div>
<!-- 单行本文框数据的双向绑定 -->
<input v-model="searchKey" type="text" @keydown="searchEvent" />
<h1>{{ searchKey }}</h1>
<!-- 多行文本框的数据绑定 -->
<textarea v-model="lineText" name="textarea" id="" cols="30" rows="10"></textarea>
<h1>{{ lineText }}</h1>
<!-- 复选框的数据绑定,获取的是是否选中的内容-->
<input type="checkbox" name="like" v-model="checked" value="123">
<h1>{{checked}}</h1>
<!-- 复选框多个值的情况 -->
<input type="checkbox" name="likes" v-model="fruits" value="柿子">
<input type="checkbox" name="likes" v-model="fruits" value="香蕉">
<input type="checkbox" name="likes" v-model="fruits" value="葡萄">
<input type="checkbox" name="likes" v-model="fruits" value="苹果">
<h1>{{fruits}}</h1>
<!-- 单选框的数据绑定 -->
<input type="radio" name="sex" value="man" v-model="picked">
<input type="radio" name="sex" value="woman" v-model="picked">
<h1>{{picked}}</h1>
<!-- 单选选项框的数据绑定 -->
<select name="city" v-model="city">
<option value="广州"></option>
<option value="上海"></option>
<option value="北京"></option>
<option value="成都"></option>
</select>
<h1>{{city}}</h1>
<!-- 多选选项框的数据绑定 -->
<select name="citys" v-model="citys" multiple >
<option value="广州"></option>
<option value="上海"></option>
<option value="北京"></option>
<option value="成都"></option>
</select>
<h1>{{citys}}</h1>
<!-- 表单获取的是是否选中的内容 -->
<input type="checkbox" name="like" v-model="checked1" true-value="喜欢" false-value="不喜欢">
<h1>{{checked1}}</h1>
<!-- 修饰符 -->
<!-- .lazy/input变为change事件改变值,焦点失焦后才改变 -->
<input v-model.lazy="searchKey1" type="text" @keydown="searchEvent" />
<h1>{{searchKey1}}</h1>
<!-- .number 把输入值转换为数字类型 -->
<input v-model.lazy.number="searchKey2" type="text" @keydown="searchEvent" />
<h1>{{searchKey2}}</h1>、
<input v-model.lazy.trim="searchKey3" type="text" @keydown="searchEvent" />
<h1>{{searchKey3}}</h1>
</div>
</template>
<script>
// 声明式
export default {
name: "App",
data() {
return {
searchKey: "百度一下",
lineText: "",
checked: false,
fruits:[],
picked:'',
city:'',
citys:[],
checked1:'',
searchKey1: "百度一下",
searchKey2:'转换为数字类型',
searchKey3:'去掉空格'
};
},
methods: {
searchEvent: function () {
console.log("执行了搜索事件!!!");
},
},
};
</script>
组件
父组件向子组件传递数据props
父组件数据newContent绑定在content ,子组件通过props接收父组件的数据
父组件 App.vue代码如下:
<template>
<div>
<!-- 父组件数据newContent绑定在content -->
<News :content = "newsContent"></News>
</div>
</template>
<script>
// 声明式
import News from './components/News.vue'
export default {
name: "App",
data() {
return {
newsContent:"23岁女星与男子开房被原配抓包!!!"
};
},
methods: {
},
components:{
News
}
};
</script>
子组件 App.vue代码如下:
<template>
<div>
<h1>新闻内容是{{content}}</h1>
</div>
</template>
<script>
export default {
// 子组件通过props接收父组件的数据
props:["content"]
}
</script>
通过provide和inject 将数据提供给子组件
父组件通过provide()将数据提供出去,子组件通过inject()将数据注入进来
父组件代码:App.vue
<template>
<div>
<Student></Student>
</div>
</template>
<script>
import { reactive,provide} from "vue";
import Student from './components/Student.vue'
export default {
name: "App",
data() {
return {
};
},
setup() {
const student = reactive({
name: "小红",
age: 18,
grade: "初一",
});
// 将数据提供出去
provide("stu", student);
return { };
},
components:{
Student
},
};
</script>
子组件代码:Student.vue
<template>
<div>
<h1>userName:{{ name }}</h1>
<h1>age: {{ age }}</h1>
<h1>年级:{{grade}}</h1>
</div>
</template>
<script>
import { inject } from "vue";
export default {
setup() {
// 将父组件提供的数据注入进来
const student = inject('stu')
return {...student}
},
};
</script>
运行截图如下
子组件向父组件传递数据:自定义事件
子组件触发自定义事件1,父组件监听自定义事件1,并将监听到的值给自定义事件2
App.vue代码如下:
<template>
<div>
<!-- 子组件将数据传递给父组件,监听到sendParentMsg被触发就会运行自定义事件sendChildMsg -->
<Login @sendParentMsg ="getChildMsg" ></Login>
<h1>从子元素获得的值:{{msg}}</h1>
</div>
</template>
<script>
// 声明式
import Login from './components/Login.vue'
export default {
name: "App",
data() {
return {
msg:''
};
},
methods: {
getChildMsg:function(value){
this.msg = value
}
},
components:{
Login
}
};
</script>
Login.vue代码如下:
<template>
<div>
<input type="text" v-model="userName">
<button @click="sendMsg">将数据提交给父组件</button>
</div>
</template>
<script>
export default {
data(){
return{
userName:''
}
},
methods:{
sendMsg:function(){
// 使用$emit(事件名称,发送的事件值)触发自定义事件 sendParentMsg()
this.$emit("sendParentMsg",this.userName)
}
},
}
</script>
Vue3生命周期
beforeCreate()和created()
初始化数据之前和初始化数据之后,初始化页面时调用
beforeMount()和mounted()
挂载渲染之前和挂载渲染之后,初始化完成后开始渲染页面
beforeUpdate()和updated()
页面数据发生更改时调用
beforeUnmount()和unmounted()
页面数据不显示时不渲染,vue2调用的是 beforeDestroy()和destroyed()
合成API:Composition API
-
可以在setup函数中使用Composition AP
-
普通写法和合成API的写法执行顺序,合成API更加快速便捷,且有利于代码的维护
Composition API :单个值ref
导入ref
ref()是一个变量变成响应式的
import {ref} from 'vue'
<template>
<div>
<h1 @click="changeEvent">普通方式计数:{{count}}</h1>
<h1 @click="num+=10">使用合成API计数:{{num}}</h1>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name: "App",
data() {
return {
count: 0
};
},
setup(){
const num = ref(0)
return{num}
},
methods: {
changeEvent() {
this.count++
},
},
};
</script>
Composition API :函数
<template>
<div>
<h1 @click="changeNum">使用合成API定义的函数计数:{{num}}</h1>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name: "App",
data() {
return {
};
},
//合成API
setup(){
//实例化变量 num
const num = ref(0)
function changeNum(){
num.value += 10
}
//返回相关的变量和函数
return{num,changeNum}
},
};
</script>
Composition API :对象 reactive({})
- reactive({})使得一个对象变成响应式的
- …toRefs(对象名)是用于解构对象,调用对象属性时不需要带上对象名作为前缀
<template>
<div>
<!-- 合成API中对象的使用 -->
<h1>姓名:{{user.userName}}</h1>
<!-- 合成API中返回的是解构对象,调用时不用加对象名前缀 -->
<h1>年龄:{{age}}</h1>
<h1 @click="changeType">类型:{{type}}</h1>
</div>
</template>
<script>
import { reactive,toRefs} from "vue";
export default {
name: "App",
data() {
return {
};
},
setup() {
// 定义对象
const user = reactive({
userName: "小米",
age: 18,
type: "小奶狗",
});
// 更改对象数据
function changeType(){
user.type = "超级帅!!!"
}
//...toRefs(user)是解构对象
return { num, changeNum, user, ...toRefs(user),changeType};
},
};
</script>
Composition API :监听数据变化
watchEffect
跟踪所使用到的数据的变化
导入
import { watchEffect} from "vue";
//只跟踪匿名函数里使用到的值type
watchEffect(()=>{
console.log(user.type)
console.log("user.type的值发生改变时触发")
})
watch
跟踪指定属性
导入
import { watch} from "vue";
- 监听单个属性
// 单个监听
watch(num,(newNum,oldNum)=>{
console.log(newNum)
console.log(oldNum)
console.log("num的值发生改变时触发")
})
- 监听多个属性
// 多个监听
watch([num,user],(newNum,oldNum)=>{
console.log(newNum)
console.log(oldNum)
console.log("值发生改变时触发")
})
setup详解
使用setup接收父组件数据
需要依赖props接收数据,父组件先绑定数据,子组件通过props接收,在setup中可以通过props计算响应式内容
- content属性包含了父组件中传过来的内容
子组件的代码:
<template>
<div>
<h1>userName:{{userName}}</h1>
<h1>age: {{age}}</h1>
<!-- 通过props生成一句话 -->
<h1>description: {{description}}</h1>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
setup(props,content){
// 使用setup接收props的数据,通过props计算响应式的内容
const description = ref(props.userName+"年龄是"+props.age)
console.log(content)
return{description}
},
// 接收来自父组件绑定的数据
props:['userName','age']
}
</script>
父组件代码:
<template>
<div>
<User :userName = "userName" :age= "age" class="abc"></User>
</div>
</template>
<script>
import { reactive,toRefs} from "vue";
import User from './components/User.vue'
export default {
name: "App",
data() {
return {
};
},
setup() {
// 定义对象
const user = reactive({
userName: "小米",
age: 18,
type: "小奶狗"
})
//...toRefs(user)是解构对象
return { user, ...toRefs(user)};
},
components:{
User
},
};
</script>
生命周期
- onBeforeMount 挂载渲染之前
- onMounted 挂载渲染之后
- onBeforeUpdate 更新之前
- onBeforeUnmount 更新之后
先导入
import {onBeforeMount,onMounted,onBeforeUpdate,onBeforeUnmount} from 'vue'
再运用
setup(){
// 声明周期函数的应用
onBeforeMount(()=>{
console.log("onBeforeMount")
})
},
Vue3路由:vue-router
路由的基本使用
- 路由:单页面应用根据路径的变化显示不同的内容
- 在项目路径下安装路由
E:\vuejs\vue3Router01> npm install vue-router@next
HTML
App.vue代码:
<template>
<div>
<p>
<router-link to="/">Go to Home</router-link>
<router-link to="/about">Go to About</router-link>
</p>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "App",
};
</script>
JS
index.js
import {createRouter,createWebHashHistory} from 'vue-router'
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
]
const router = createRouter({
history:createWebHashHistory(),
routes,
})
export default router
main.js
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
import router from './router'
const app = createApp(App)
// 使用路由
app.use(router)
app.mount('#app')
动态路由
单页面应用中,根据路径的id显示不一样的内容
index.js
import {createRouter,createWebHashHistory} from 'vue-router'
//导入组件
import News from '../components/News.vue'
import NotFound from '../components/NotFound.vue'
//将组件加进路由里
const routes = [
{path:'/news/:id',component: News},
{path:'/:path(.*)',component: NotFound}
]
const router = createRouter({
history:createWebHashHistory(),
routes,
})
export default router
news.vue
<template>
<div>
<h1>新闻页{{$route.params.id}}</h1>
</div>
</template>
<script>
export default {
mounted(){
console.log(this.$route)
}
}
</script>
NotFound.vue
<template>
<div>
<h1>找不到页面</h1>
</div>
</template>
路由正则与重复参数
- \d+: id必须是数字
- +: id至少有一个
- *: id可有可无可重复可多个
- ? : id可有可无,不可重复,不可多个
index.js
mport News from '../components/News.vue'
import Article from '../components/Article.vue'
import Film from '../components/Film.vue'
import Song from '../components/Song.vue'
import Vidio from '../components/Vidio.vue'
import NotFound from '../components/NotFound.vue'
const routes = [
{path:'/news/:id',component: News},
// \d+: id必须是数字
{path:'/article/:id(\\d+)',component: Article},
// + : id至少有一个
{path:'/film/:id+',component: Film},
// * : id可有可无可重复可多个
{path:'/song/:id*',component: Song},
// ? : id可有可无,不可重复,不可多个
{path:'/vidio/:id?',component: Vidio},
]
嵌套路由
某些应用程序的 UI 由嵌套多级深的组件组成。在这种情况下,一个 URL 的参数(id)不同,对应嵌套组件的结构也不同,主结构相同,嵌套结构不同
index.js关键代码如下:
import User from '../components/User.vue'
import Hengban from '../components/Hengban.vue'
import Shuban from '../components/Shuban.vue'
const routes = [
{path:'/user/',
component: User,
children:[
{path:'hengban',component:Hengban},
{path:'shuban',component:Shuban}
]
},
]
User.vue
<template>
<div>
<h1>user页面</h1>
<router-view></router-view>
</div>
</template>
Hengban.vue
<template>
<div>
<h1>这是横版页面</h1>
</div>
</template>
Shuban.vue
<template>
<div>
<h1>这是竖版页面</h1>
</div>
</template>
竖版运行视图
程序化导航:使用js跳转页面
导航到其他位置
除了<router-link to="url">
用于创建用于声明性导航的锚标记之外,我们还可以使用路由器的实例方法以编程方式完成此操作。
- 参数可以是字符串路径
this.$router.push("/news/:id")
- 参数是具有path的对象
this.$router.push({path: '/news/:id'})
// 携带参数跳转
this.$router.push({path: '/news/123456'})
- 带参数的命名路由,让路由器构建路径
this.$router.push({name:'film',params:{id:123}})
- 带查询querry的路径
this.$router.push({path:'/vidio',query:{search:'弯弯作死'}})
替换当前位置
this.$router.replace({path: '/news/123456'})
替换当前位置
此方法采用单个整数作为参数,指示在历史堆栈中前进或后退的步数,类似于window.history.go(n)。
<button @click="$router.go(1)">前进</button>
<button @click="$router.go(-1)">后退</button>
命名路由与重定向和别名
命名路由
您可以拥有多个router-view
并为每个router-view命名,一个router-view
没有名字的将default作为它的名字。
App.vue关键代码
<router-view name="ShopTop"></router-view>
<router-view></router-view>
<router-view name="ShopFooter"></router-view>
index.js关键代码
import ShopMain from '../components/ShopMain.vue'
import ShopTop from '../components/ShopTop.vue'
import ShopFooter from '../components/ShopFooter.vue'
{
path: '/shop',
components: {
default:ShopMain,
ShopTop:ShopTop,
ShopFooter:ShopFooter
}
},
ShopMain.vue关键代码
<template>
<div>
<h1>ShopMain主要内容</h1>
</div>
</template>
ShopTop.vue关键代码
<template>
<div>
<h1>ShopTop头部组件</h1>
</div>
</template>
ShopFooter.vue关键代码
<template>
<div>
<h1>ShopFooter底部组件</h1>
</div>
</template>
重定向:redirect
- 重定向也在routes配置中完成。从/a到重定向/b:
const routes = [{ path: '/home', redirect: '/' }]
- 重定向还可以针对命名路由:
const routes = [{ path: '/home', redirect: { name: 'homepage' } }]
- 重定向还可以使用一个函数进行动态重定向
就是说输入/mall路由就会跳转到/shop页面
const routes = [
{
path:'/mall',
redirect:(shop)=>{
return {path:'/shop'}
}
},
]
别名:alias
/shop的别名/home意味着当用户访问时/store,URL 仍然存在/store,但它会像用户正在访问一样匹配/shop。通过通过数组取多个别名
{
path: '/shop',
alias: '/store',
components: {
default:ShopMain,
ShopTop:ShopTop,
ShopFooter:ShopFooter
}
},
路由模式
哈希模式
使用哈希模式URL会出现一个 # ,URL 的这部分永远不会发送到服务器,所以它不需要在服务器级别进行任何特殊处理
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
})
HTML5模式
使用HTML5模式,URL 将看起来“正常”,请求会发送到服务器。所以需要配置适当的服务器
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})
导航守卫
Vue router 提供的导航守卫主要用于通过重定向或取消导航来保护导航。有多种方法可以连接到路由导航过程:全局、每个路由或组件内。
router.beforeEach
全局导航前守卫
index.js
router.beforeEach((to, from,next) => {
// ...
// explicitly return false to cancel the navigation
console.log(to)
console.log(from)
next()
})
从store页面跳转到page页
router.beforeResolve
全局保护,当路由跳转完成之后触发
router.beforeResolve(async to => {
if (to.meta.requiresCamera) {
try {
await askForCameraPermission()
} catch (error) {
if (error instanceof NotAllowedError) {
// ... handle the error and then cancel the navigation
return false
} else {
// unexpected error, cancel the navigation and pass the error to the global handler
throw error
}
}
}
})
beforeEnter
单路由保护,进入路由前触发
{
path: '/shop',
alias: '/store',
components: {
default: ShopMain,
ShopTop: ShopTop,
ShopFooter: ShopFooter
}
},
从store页面跳转到page页
组件内防护
写在组件里面的
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
// 组件内导航防护
beforeRouteEnter() {
console.log("路由进入组件");
},
beforeRouteUpdate() {
console.log("路由更新");
},
beforeRouteLeave() {
console.log("路由离开");
},
状态管理
通过reactive响应式对象管理
通过reactive响应式对象,再通过依赖provide和inject注入子组件中
index.js
import { reactive} from 'vue'
// 状态管理仓库
const store = {
state : reactive({
message:"你好"
}),
setMessage(value){
this.state.message = value;
}
}
export default store
父组件:App.vue
import store from './store/index.js'
export default {
provide:{
store
}
}
子组件:HelloWord.vue
inject:['store']
Vuex状态管理
home.vue
调用$store
<template>
<div class="home">
<h1>商品数量:{{ $store.state.count }}</h1>
<h2>商品价格:100</h2>
<h1>商品总价:{{ $store.getters.totalPrice }}</h1>
<button @click="changeEvent">添加数量</button>
<h1>段子</h1>
<p v-for="(item,i) in $store.state.dzList" :key="i">{{item.text}}</p>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from "@/components/HelloWorld.vue";
export default {
name: "Home",
components: {
HelloWorld,
},
methods: {
changeEvent: function () {
// 触发状态的方法
// 触发无参数的方法
// this.$store.commit("setCount");
// 触有参数的方法
this.$store.commit("setCountNum", 3);
},
},
mounted:function(){
this.$store.dispatch('getDz')
}
};
</script>
index.js
使用store
import { createStore } from 'vuex'
// 管理全局所用到的数据
export default createStore({
// 设置全局数据
state: {
count:0,
dzList:[]
},
/**
* 含计算属性的方法
*/
getters:{
totalPrice:function(state){
return state.count*100
}
},
/**
* 同步的方法
* 修改状态的方法
*/
mutations: {
// 无参数
setCount:function(state){
state.count++;
},
// 有参数
setCountNum:function(state,num){
state.count+=num;
},
setDzList:function(state,arr){
state.dzList = arr;
}
},
/**
* 异步的方法
* 使用Ajax请求修改数据
*/
actions: {
getDz:function(context){
// 开放免费的API
let api = "https://api.apiopen.top/getJoke?page=1&count=10&type=text";
// 发出请求
fetch(api)
.then((res) => res.json())
.then((result) => {
console.log(result)
context.commit('setDzList',result.result)
});
}
},
modules: {
}
})
map辅助函数
- mapState
- mapGetters
- mapMutations
- mapActions
map映射可以使得数据调用更加方便,不用加上$store.state,可以直接使用变量名
组件Home.vue
import {mapState, mapGetters,mapMutations,mapActions} from 'vuex'
export default {
name: "Home",
components: {
HelloWorld,
},
/**
* 把状态映射到组件上
* 直接调用数据
*/
computed:{
//映射数据
...mapState(['count']),
...mapState({
productCount:(state)=>state.count
}),
//映射计算属性
...mapGetters(['totalPrice'])
},
methods: {
changeEvent: function () {
this.$store.commit("setCountNum", 3);
},
//映射改变状态的方法
...mapMutations(['setCountNum']),
...mapActions(['getDz'])
},
mounted:function(){
this.$store.dispatch('getDz')
}
};
vue3 实现前后端交互
通过fetch()
关键代码段
// 获取数据显示到页面
setup() {
// 开放免费的API
let api = "https://api.apiopen.top/getJoke?page=1&count=10&type=text";
// 发出请求
fetch(api)
.then((res) => res.json())
.then((result) => {
store.setDzList(result.result);
console.log(result.result);
});
return {
store
};
通过axios()
终端安装axios:npm install axios --save
import axios from 'axios'
// 获取数据显示到页面
setup() {
// 开放免费的API
let api = "https://api.apiopen.top/getJoke?page=1&count=10&type=text";
// 发出请求
axios.get(api).then((result)=>{
console.log(result)
store.setDzList(result.data.result)
// store.setDzList(result);
})
return {
store
};
},
vite 配置跨域请求
跨域配置文件vite.config.js
/**
* 跨域服务器的配置文件
*/
module.exports = {
proxy :{
'/api':{
target: 'https://pvp.qq.com/',
changeOrigin:true, //是否允许跨域
rewrite: path => path.replace(/^\/api/,'')
}
}
}
Mock.js 模拟获取数据
链接: 点击跳转到官网.
可以拦截 Ajax 请求,返回模拟的响应数据
index.js
import Mock from 'mockjs'
Mock.mock(
"/user/userinfo",
"get",
(req,res)=>{
console.log(req,res)
return{
username:"老陈",
type:"帅!"
}
}
)
vue脚手架cli的使用
安装脚手架cli
npm install -g @vue/cli
查看版本
vue --version
创建项目
vue create vueproject01
选择版本
切换到项目路径
cd vueproject01
运行
npm run serve
模块化管理 vuex
在store中导入模块user,user1
import { createStore } from 'vuex'
import user from './user.js'
import user1 from './user1.js'
// 管理全局所用到的数据
export default createStore({
// 设置全局数据
state: {
...
},
getters:{
...
},
mutations: {
...
},
actions: {
...
},
modules: {
user,
user1
}
})
user模块,user.js代码如下:
/**
* 用户模块
*/
const user = {
state: () => ({
username: "老陈",
age: 30
}),
mutations: {
setUsername:function(state){
state.username = "小陈"
},
setAge:function(state){
state.age = 40
}
},
actions: {
asyncSetAge:function(context){
setTimeout(()=>{
context.commit('setAge')
},3000)
}
},
getters: {
description:function(state,getters,rootState){
return state.username + '的年龄是' + state.age+'岁!'
}
}
}
export default user
命名空间
作用:区分每个模块,以防每个模块的getters和actions肯定会出现同名的情况
const user = {
// 命名空间
namespaced: true,
.....
export default user
路由中的渲染User.vue
要加上模块名
<!-- 命名空间的写法 -->
<h1>用户名:{{ $store.state.user1.username }}</h1>
<h1>年龄:{{ $store.state.user1.age }}</h1>
<h1>描述:{{ $store.getters["user1/description"] }}</h1>
<button @click="changeAge1">修改年龄</button>
模块中辅助函数的写法
<template>
<!-- 命名空间:辅助函数的写法 -->
<h1>用户名:{{ user1.username }}</h1>
<h1>年龄:{{ user1.age }}</h1>
<h1>描述:{{ description }}</h1>
<button @click=" setAge">异步修改年龄</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
export default {
computed: {
...mapState(["user1"]),
...mapGetters("user1", ["description"]),
...mapMutations('user1',['setAge'])
},
methods: {
...mapActions("user1", ["asyncSetAge"]),
},
};
</script>