vue3
vue3作为前端重要的框架,学会vue可以让你更加了解前端。本博客致力于让你一站学会vue3的全部内容,从小白到高手。
全是干货,准备好了吗?
文章目录
创建工程
使用vite创建
vite
是新一代前端构建工具,官网地址:https://vitejs.cn
## 1.创建命令
npm create vue@latest
## 2.具体配置
## 配置项目名称
√ Project name: vue3_test
## 是否添加TypeScript支持
√ Add TypeScript? Yes
## 是否添加JSX支持
√ Add JSX Support? No
## 是否添加路由环境
√ Add Vue Router for Single Page Application development? No
## 是否添加pinia环境
√ Add Pinia for state management? No
## 是否添加单元测试
√ Add Vitest for Unit Testing? No
## 是否添加端到端测试方案
√ Add an End-to-End Testing Solution? » No
## 是否添加ESLint语法检查
√ Add ESLint for code quality? Yes
## 是否添加Prettiert代码格式化
√ Add Prettier for code formatting? No
文档结构
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── pages:存放路由组件
│ │── router:存放路由配置
│ │ └── index.js:默认路由入口文件
│ │── utils:封装自己或者第三方的工具
│ │── store:使用pinia插件时,存储全局数据
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件
├── vite.config.js: 脚手架配置
核心语法
模板语法
插值语法
文本插入
使用{{data_attribute}}插入数据
原始HTML插入(可能会出现安全问题)
使用v-html指令
<template>
<div v-html="rawhtml"></div>//rawhtml中的内容会被插入到div标签里
</template>
指令语法
无参指令
控制元素的显示与隐藏
<template>
<div v-if="divif"></div>//divif=true展示
<div v-show="divshow"></div>//divshow=true展示
//v-if是和dom元素的有无有关,v-show和dom元素的display属性有关
</template>
有参指令
单向数据绑定v-bind (简写:)
<template>
<div v-bind:id="dynamicId"></div>
//简写
<div :id="dynamicId"></div>
//同名简写:id=id
<div :id></div>
//绑定多组数据
<div v-bind="objectOfAttrs"></div>
</template>
双向数据绑定
<input v-modle:value="x"/>
//简写
<input v-modle="x"/>
事件监听
<div v-on:click="dosomething"/>
//简写
<div @click="dosomething"/>
//在dosomething中默认第一个参数会传入event事件对象。
事件对象$event
: 是包含事件相关信息的对象(pageX
、pageY
、target
、keyCode
)
事件修饰符
prevent:阻止事件默认行为
stop:阻止事件冒泡行为
<div @click.prevent="dosomething"/>
循环指令
<div v-for="(value ,key)in items" :key="key">
{{ value }}
</div>
//v-for所在的元素及其子元素内都能使用遍历的内容。
//结果就是v-for所在元素及其子元素都生成了遍历次数的真实dom元素
其他指令
v-if : 如果为 true, 当前标签才会输出到页面
v-else: 如果为 false, 当前标签才会输出到页面
v-cloak : 防止闪现, 与 css 配合: [v-cloak] { display:none}
自定义指令
参考:https://cn.vuejs.org/guide/reusability/custom-directives.html
setup
setup函数
组件中所用到的:数据、方法、计算属性、监视…等都写在里面,但只有返回之后才能在模板里直接使用。setup函数中访问this是undefined。
返回值
·返回一个对象中的:属性、方法等,都可以在模板中直接使用。
·返回一个函数,则可以自定义渲染页面。
setup(){
return () => "hello world!" //组件所渲染的部分展示hello world!
}
setup语法糖
<script lang="ts">
export default {
name:'Name',
}
</script>
<!-- 下面的写法是setup语法糖 -->
<script setup>
console.log(this) //undefined
let name = 'wuhu'
</script>
组件的name属性需要在script标签中单独定义,显得不是那么灵活。
用vite中的插件可以简化代码
(插件的使用
第一步 安装插件 npm i vite-plugin-vue-setup-extend -D
第二步 在vite.config.js
中配置插件
import { defineConfig } from 'vite'
import VUeSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({
plugins:[
VueSetupExtend()
]
})
配置好后代码可简化为只写一个setup
标签
<script setup name="Name">
</script>
响应式数据
ref
定义基本类型的响应式数据
语法:let xxx = ref(初始值
)
返回值:返回一个RefImpl的实例对象,ref对象的value属性是响应式的。
使用数据:
·js这种操作数据需要:xxx.value,但模板中直接使用即可。
·xxx不是响应式的,xxx.value是响应式的。
定义对象类型
语法:let xxx = ref(源对象
)
使用数据:
·在js中,xxx.value.属性
·在模板中,直接使用即可
reactive
只能定义对象类型的响应式数据
语法:let xxx = reactive(源对象
)
返回值:一个Proxy的实例对象,简称:响应式对象。
使用数据:直接使用即可
注意:
·rective重新分配一个新的对象,会失去响应式(可以使用Object.assign整体替换)
·若需要一个层次较深的响应式对象,推荐使用reactive
toRef & toRefs
将响应式对象中的属性,转换为ref
对象。
<script setup name="Person">
import {ref,reactive,toRefs,toRef} from 'vue'
// 数据
let person = reactive({name:'wuhu', age:18, gender:'男'})
// 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
let age = toRef(person,'age')
// 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
let {name,gender} = toRefs(person)
</script>
计算属性computed
通过响应式数据计算得到新的属性。当原响应式对象的值发生改变时,会重新计算并更新。
语法:
只读: let xxx = computed( ()=>{dosomething and return } )
读并且修改: let xxx = computed({get(){},set(){} })
<script setup lang="ts" name="App">
import {ref,computed} from 'vue'
let firstName = ref('zhang')
let lastName = ref('san')
// 计算属性——只读取,不修改
/* let fullName = computed(()=>{
return firstName.value + '-' + lastName.value
}) */
// 计算属性——既读取又修改
let fullName = computed({
// 读取
get(){
return firstName.value + '-' + lastName.value
},
// 修改
set(val){
console.log('有人修改了fullName',val)
firstName.value = val.split('-')[0]
lastName.value = val.split('-')[1]
}
})
</script>
侦听器
watch
作用:监视数据的变化。
返回值:watch()函数返回值是一个函数,调用该函数可以停止监视。
1、监视ref定义的数据类型
监视基本类型,直接写数据名,监视其value值的改变。
监视对象类型,直接写数据名,监视的是对象的地址值
,若想监视对象内部的数据,要手动开启深度监视。
注意:
若修改的是ref
定义的对象中的属性,newValue
和 oldValue
都是新值,因为它们是同一个对象。
若修改整个ref
定义的对象,newValue
是新值, oldValue
是旧值,因为不是同一个对象了。
2、监视reactive定义的响应式对象
<script setup>
import { ref , watch } from 'vue'
let sum = ref(0)
let a = {name : "wuhu", age : 20}
//监视【ref】定义的【基本类型】数据
const stopwatch = watch( sum (newvalue,oldvalue)=>{
console.log('sum变化了',newValue,oldValue)
if(newValue >= 10){
stopWatch()
}
})
/*
监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
watch的第一个参数是:被监视的数据
watch的第二个参数是:监视的回调
watch的第三个参数是:配置对象(deep、immediate、once等等.....)
*/
watch(a,(newValue,oldValue)=>{
console.log('a变化了',newValue,oldValue)
},{deep:true})
// 监视,情况三:监视【reactive】定义的【对象类型】数据,且默认是开启深度监视的
watch(a,(newValue,oldValue)=>{
console.log('a变化了',newValue,oldValue)
})
</script>
3、监视对象中的某个属性
1、若该属性值不是对象类型
,需要写成函数形式。
2、若该属性仍是对象类型
,可直接编写,但推荐使用函数形式。
注意:
若是对象监视的是地址值,若想监视内部的值,需要手动开启深度监视。
<script setup>
import { reactive, watch } from 'vue'
let person = reactive({
name : "wuhu",
age : 20,
hobby : {
book : "love",
game : "pices"
}
})
//监视响应式对象中的基本类型的属性
watch(()=>person.age,(newvalue,oldvalue)=>{
console.log("person.age改变了",newvalue,oldvalue)
})
//监视响应式对象中的对象类型的属性
watch(()=>person.hobby,(newvalue,oldvalue)=>{
console.log("person改变了",newvalue,oldvalue)
},{deep:true})
</script>
4、监视多种数据
watch函数的第一个参数传入一个数组,数组中可以包含上述类型。
watch([person,()=>person.name],(newvalue,oldvalue)=>{dosomething},{deep:true})
watchEffect
立即执行这个函数,同时响应式的追踪其依赖,并在依赖更改时重新执行该函数。
不用明确指出监视的数据,函数中用到哪些属性,就监视哪些属性。
<script>
import {ref,watchEffect} from 'vue'
let name = ref("wuhu")
let age = ref(20)
let hobby = ref({
book : "love",
game : "pices"
})
const stopwatch = watch(()=>{
dosomething use those attribute
//停止监听
stopwatch()
})
</script>
模板引用(元素的ref属性)
ref
是一个特殊的attribute,它允许我们在一个特定的DOM元素或子组件实例被挂载后,获得对它的直接引用。
用在普通
DOM
标签上,获取的是DOM
节点
用在普通DOM节点上
<template>
<input ref="myinput" />
</template>
<script>
import { useTemplateRef, onMonted } from 'vue'
//第一个参数必须与模板中的ref值匹配
const myinput = useTemplateRef('myinput')
onMonted(()=>{
myinput.value.focus()
})
</script>
在含有v-for指令上的节点上,返回的是一个数组!!!
用在组件标签上
用在组件标签上,获取的是组件实例对象
//父组件
<template>
<Child ref="child" />
</template>
<script setup>
import { useTemplateRef, onMounted } from 'vue'
import Child from './Child.vue'
const childRef = useTemplateRef('child')
onMounted(() => {
// childRef.value 将持有 <Child /> 的实例
})
</script>
使用了<script setup>
的组件是默认私有的,父组件是无法访问到一个使用<script setup>
的子组件中的任何东西,除非子组件在其中通过defineExpose
宏显示暴露。
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// 像 defineExpose 这样的编译器宏不需要导入
defineExpose({
a,
b
})
</script>
当父组件通过模板引用获取到了该组件的实例时,得到的实例类型为
{ a: number, b: number }
(ref 都会自动解包,和一般的实例一样)。
hooks(组合式函数)
把setup
函数中使用的Component API
进行封装,提升代码的复用率。
创建hooks文件夹,在文件夹中创建useXxx.js并向外暴露。
// useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'
// 按照惯例,组合式函数名以“use”开头
export default function useMouse() {
// 被组合式函数封装和管理的状态
const x = ref(0)
const y = ref(0)
// 组合式函数可以随时更改其状态。
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
// 一个组合式函数也可以挂靠在所属组件的生命周期上
// 来启动和卸载副作用
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
// 通过返回值暴露所管理的状态
return { x, y }
}
在组件中使用
<template>Mouse position is at: {{ x }}, {{ y }}</template>
<script setup>
import { useMouse } from './hooks/useMouse.js'
const { x, y } = useMouse()
</script>
组件
推荐使用单文件组件。组件名建议使用PascalCase
式。
注册组件
组件在模板中使用之前需要先进行注册。
全局注册
在main.js文件中,调用Vue实例对象的.component()方法,让组件在当前Vue应用中全局可用。
import {createApp} from 'vue'
const app = createApp({})
//方法一
app.component(
//注册名字
'MyComponent',
//组件的实现
{/**/}
)
//方法二:先引入再使用
import MyComponent from './MyComponent.vue'
app.component('MyComponent',MyComponent)
//方法三:使用时引入
app.component('MyComponent',()=>import('./MyComponent.vue'))
.component()方法可以被链式调用:
app
.component('ComponentA', ComponentA)
.component('ComponentB', ComponentB)
.component('ComponentC', ComponentC)
局部注册
局部注册的组件需要在父组件中显式导入,并且只能在父组件中使用。
在setup
中,导入的组件就可以直接使用了,无需注册
<template>
<ComponentA />
</template>
<script setup>
import ComponentA from './ComponentA.vue'
</script>
生命周期
创建阶段:
setup
挂载阶段:
onBeforeMount
、onMounted
更新阶段:
onBeforeUpdate
、onUpdated
卸载阶段:
onBeforeUnmount
、onUnmounted
Props
命名规则:
传递props时建议使用camelCase式定义变量。props是只读的!
Props声明
组件需要显式的声明它所接受的props,这样才能在组件中使用。props使用steup中的defineProps()
宏来声明。
<script setup name = 'son'>
//父组件中<son :foo="data",age=1/>
//字符串数组接收
const props = defineProps(['foo'])
//对象形式接收
defineProps({
foo:String,
age:Number
})
</script>
对于对象形式声明每个属性,key是props的名称,值是该props预期类型的构造函数。
Props校验
defineProps({
// 基础类型检查
// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
propA: Number,
// 多种可能的类型
propB: [String, Number],
// 必传,且为 String 类型
propC: {
type: String,
required: true
},
// 必传但可为 null 的字符串
propD: {
type: [String, null],
required: true
},
// Number 类型的默认值
propE: {
type: Number,
default: 100
},
// 对象类型的默认值
propF: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
}
})
组件事件
父组件通过v-on(缩写@)来监听事件:
<template>
<MYComponent @some-event="callback"/>
//当MyComponent组件中触发someEvent时,父组件会执行callback函数,函数也能接收参数
</template>
组件的事件监视器也支持事件修饰符,eg:.once
子组件
1、声明触发的事件:
使用defineEmits()
宏来声明要触发的事件:
函数参数有两种
-
数组
<script setup> const emit = defineEmits(['someEvent']) </script>
-
对象
<script setup> const emit = defineEmits({ // 没有校验 click: null, // 校验 submit 事件 submit: ({ email, password }) => { if (email && password) { return true } else { console.warn('Invalid submit event payload!') return false } } }) function submitForm(email, password) { emit('submit', { email, password }) } </script>
2、返回值
defineEmits
函数的返回值是一个函数,函数的第一个参数是要触发事件的名称,后面的参数是触发事件传递的值。
所有传入
emit()
的额外参数都会被直接传向监听器。举例来说,emit('foo', 1, 2, 3)
触发后,监听器函数将会收到这三个参数值。
组件v-modle
在组件上使用可以实现双向数据绑定。
父组件通过v-modle
传递。
子组件通过defineModel()
宏,声明传递的数据,返回值是一个ref
,能够访问和修改。
<script setup>
let modle = defineModle()//父组件未传参
//父组件传参,可以传递多个参数
let age = defineModle('age')
</script>
透传Attributes
透传Attributes
指的是传递给一个组件的attribute,却没有被该组件声明为props或emits的attribute或者v-on
事件监听器的attribute。
在模板中使用
若在子组件中是以单元素为根,则会默认继承到该根元素上。元素也可以是组件元素。
若想禁止透传可以在setup中使用defineOpinion({inheritAttrs: false})
禁用。
在模板中可以通过$attrs
直接访问到透传的attributes,可以在子组件中在模板的DOM元素或组件元素使用v-bind = "$attrs"
来绑定要透传到哪个元素上。若有多个根节点,一定要绑定,不然会报错。
//子组件
<template>
<header>...</header>
<main v-bind="$attrs">...</main>//孙组件
<footer>...</footer>
</template>
在javaScript中访问
在<script setup>
中使用useAttrs()
API来访问一个组件的所有透传attribute
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
</script>
插槽
父组件提供插槽内容,并在子组件中将这部分模板内容渲染出来。模板内容中可以使用父组件中的的data。
默认插槽
在子组件中直接使用<slot>
标签,当组件标签没有传入模板内容时,将展现<slot>
标签中的内容。
具名标签
在子组件中的slot
标签设置name
属性,父组件中可以使用<template>
(该标签不会被渲染出来),并设置v-slot
命令的参数指向需放置的solt标签的name属性值。
//子组件BaseLayout.vue
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
//父组件
<BaseLayout>
<template v-slot:header>
<!-- header 插槽的内容放这里 -->
</template>
</BaseLayout>
默认插槽相当于设置:v-slot:default
,简写为#default
。
条件插槽
在子模版中,可以使用$slot
访问到传过来的模板要放到插槽的name值,联和条件指令可以很好的控制未传来插槽内容的插槽要不要展示。
<template>
<div class="card">
<div v-if="$slots.header" class="card-header">
<slot name="header" />
</div>
<div v-if="$slots.default" class="card-content">
<slot />
</div>
<div v-if="$slots.footer" class="card-footer">
<slot name="footer" />
</div>
</div>
</template>
作用域插槽
在子组件的<slot>
上传递attribute,父组件使用v-slot="xxx"
来接收,并且可以在相应的含有v-slot
命令的元素及其子元素上使用。
//子组件
<MyComponent>
<template #header="headerProps">
{{ headerProps }}
</template>
<template #default="defaultProps">
{{ defaultProps }}
</template>
<template #footer="footerProps">
{{ footerProps }}
</template>
</MyComponent>
//父组件
<slot name="header" message="hello"></slot>
组件拓展
pinia
实现通用的数据仓库
第一步:安装并引入pinia
npm install pinia
在src/main.js
引入
import { createApp } from 'vue'
import App from './App.vue'
/* 引入createPinia,用于创建pinia */
import { createPinia } from 'pinia'
/* 创建pinia */
const pinia = createPinia()
const app = createApp(App)
/* 使用插件 */{}
app.use(pinia)
app.mount('#app')
**第二步:**向仓库中写数据
在src路径下创建一个大仓库store,其下又能创建多个小仓库。eg:src/store/xxx.js
。
它有三个概念:
state
、getter
、action
,相当于组件中的:data
、computed
和methods
。
选项式写法:
//文件名为:count.js
//引入defineStore创建store
import {defineStore} from 'pinia'
//定义并暴露store
export const useCountStore = defineStore('count',{
//状态
state(){
return {
sum:6
}
},
//getters,actions可以通过this访问到整个state
//计算
getters:{
bigSum : (state)=> state.sum *2,
moreSum : ()=>this.sum*10
},
//动作
actions:{
addSum(value){
this.sum += 1
}
}
})
第三步:在组件中使用
<script setup>
import {useCountStore} from './src/store/count.js'
//得到对应的store
const countStore = useCountStore()
</script>
使用数据
-
读取数据
store中的数据都能读取到,在setup中state中的数据要用
.value
访问,在模板中直接使用即可。也可使用storeToRef
来进行解构state并保持响应式。
import {storeToRefs} from 'pinia'
const {sum} = storeToRefs(countStore)
- 修改数据
//直接修改
countStore.sum=2
//批量修改
countStore.$patch({
sum:2
})
//借助action修改
countStore.addSum(1)
- 监视state
通过store的$subscribe()
方法侦听state
及其变化
countState.$subscribe((mutate,state)=>{
console.log('countState',mutate,state)
})
组合式写法
import {defineStore} from 'pinia'
import axios from 'axios'
import {nanoid} from 'nanoid'
import {reactive} from 'vue'
export const useTalkStore = defineStore('talk',()=>{
// talkList就是state
const talkList = reactive(
JSON.parse(localStorage.getItem('talkList') as string) || []
)
// getATalk函数相当于action
async function getATalk(){
// 发请求,下面这行的写法是:连续解构赋值+重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = {id:nanoid(),title}
// 放到数组中
talkList.unshift(obj)
}
return {talkList,getATalk}
})
mitt
类似于消息订阅与发布的功能。
第一步:安装mitt
npm run mitt
第二布:新建文件(相当于容器)
src\utils\emitter.js
//引入mitt
import mitt from "mitt";
//创建emitter
const emitter = mitt()
//可以初始化事件,也可以用空容器
/*
// 绑定事件
emitter.on('abc',(value)=>{
console.log('abc事件被触发',value)
})
emitter.on('xyz',(value)=>{
console.log('xyz事件被触发',value)
})
setInterval(() => {
// 触发事件
emitter.emit('abc',666)
emitter.emit('xyz',777)
}, 1000);
setTimeout(() => {
// 清理事件
emitter.all.clear()
}, 3000);
*/
//暴露mitt
export default emitter
第三步:绑定事件,订阅消息
<script setup>
import emitter from "@/utils/emitter";
import { onUnmounted } from "vue";
// 绑定事件
emitter.on('send-toy',(value)=>{
console.log('send-toy事件被触发',value)
})
onUnmounted(()=>{
// 解绑事件
emitter.off('send-toy')
})
</script>
第四步:触发事件,发布消息
import emitter from "@/utils/emitter";
function sendToy(){
// 触发事件
emitter.emit('send-toy',toy.value)
}
路由
实现单页面应用
安装并配置:
npm install vue-router
路由组件通常存放在pages
或 views
文件夹,一般组件通常存放在components
文件夹。
在src下创建一个router文件夹存放路由器
// src/router/index.js
import { createMemoryHistory, createRouter } from 'vue-router'
import HomeView from './HomeView.vue'
import AboutView from './AboutView.vue'
const routes = [
{ path: '/', component: HomeView },
{ path: '/about', component: AboutView },
]
const router = createRouter({
history: createMemoryHistory(),
routes,
})
在main.js中要引入router
import router from './router/index'
app.use(router)
app.mount('#app')
这步的作用包括:
- 全局注册
RouterView
和RouterLink
组件。 - 添加全局
$router
和$route
属性。 - 启用
useRouter()
和useRoute()
组合式函数。 - 触发路由器解析初始路由。
路由的工作模式
-
history
模式优点:
URL
更加美观,不带有#
,更接近传统的网站URL
。缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有
404
错误。const router = createRouter({ history:createWebHistory(), //history模式 /******/ })
-
hash
模式优点:兼容性更好,因为不需要服务器端处理路径。
缺点:
URL
带有#
不太美观,且在SEO
优化方面相对较差。const router = createRouter({ history:createWebHashHistory(), //hash模式 /******/ })
嵌套路由
路由中还可以配置子路由。在routes数组元素对象中使用children
对当前路由命名。
const routes = [
{
path :'/home',
component:Home
children:[
{
path:'news',
component:News
},
{
path:'games',
component:Games
}
]
}
]
跳转路由
<router-link to="/home/news">xxx</router-link>
<!--或-->
<router-link :to="{path:'/home/news'}">xxx</router-link>
命名路由
可以简化路由跳转和传参。
在routes数组元素对象中使用name
对当前路由命名。
routes:[
{
name:'zhuye',
path:'/home',
component:Home,
children:[
{
name:'xinwen',
path:'news',
component:News
}
]
}]
在跳转路由时可以简化写法
<!--简化前:需要写完整的路径(to的字符串写法) -->
<router-link to="/home/news">跳转</router-link>
<!--简化后:直接通过名字跳转(to的对象写法配合name属性) -->
<router-link :to="{name:'xinwen'}">跳转</router-link>
路由视图
使用<router-view>
来说明跳转的路由要放在哪里。
<template>
<!-- 导航区 -->
<div>
<RouterLink to="/home" active-class="active">首页</RouterLink>
<RouterLink to="/news" active-class="active">新闻</RouterLink>
<RouterLink to="/about" active-class="active">关于</RouterLink>
</div>
<!-- 展示区 -->
<div>
<RouterView></RouterView>
</div>
</template>
<script lang="ts" setup name="App">
import {RouterLink,RouterView} from 'vue-router'
</script>
命名视图
同时展示多个同级路由视图。多个视图也就意味着有多个组件,因此在路由配置中的routes数组元素对象中的component
要改为使用components
配置项。
<template>
<router-view name = "news"></router-view>
<router-view name = "games"></router-view>
<router-view></router-view>//没有名字就默认default
</template>
配置项
const routes = [
{
path:'/home',
component:Home,
children:[
{
path:"all",
components:{
default:News,
//Games:Games的缩写
Games,
//与router-view上的name属性匹配
}
}
]
}
]
replace属性
-
作用:控制路由跳转时操作浏览器历史记录的模式。
-
浏览器的历史记录有两种写入方式:分别为
push
和replace
:push
是追加历史记录(默认值)。replace
是替换当前记录。
-
开启
replace
模式:<RouterLink replace .......>News</RouterLink>
路由组件传参
两个重要API,useRouter
和useRoute
在script代码段中使用获取路由器和当前路由。在<template>
中使用$router
和$route
同样能获得。
query参数
1、传递参数
<!-- 跳转并携带query参数(to的字符串写法) -->
<router-link to="/news/detail?a=1&b=2&content=欢迎你">
跳转
</router-link>
<!-- 跳转并携带query参数(to的对象写法) -->
<RouterLink
:to="{
//name:'xiang', //用name也可以跳转
path:'/news/detail',
query:{
id:news.id,
title:news.title,
content:news.content
}
}"
>
{{news.title}}
</RouterLink>
2、接收参数
//Detail.vue
<script setup>
import {useRoute} from 'vue-router'
const route = useRoute()
// 打印query参数
console.log(route.query)
</script>
params参数
1、规则占位
const routes=[
{
path:'news',
component:News,
children:[
{
name:'xiang',
path:'detail/:id/:title/:content',
component:Detail
}
]
}
]
2、传递参数
<!-- 跳转并携带params参数(to的字符串写法) -->
<RouterLink :to="`/news/detail/001/新闻001/内容001`">{{news.title}}</RouterLink>
<!-- 跳转并携带params参数(to的对象写法) -->
<RouterLink
:to="{
name:'xiang', //必须用name跳转
params:{
id:news.id,
title:news.title,
content:news.title
}
}"
>
{{news.title}}
</RouterLink>
3、接收参数
//News.vue
import {useRoute} from 'vue-router'
const route = useRoute()
// 打印params参数
console.log(route.params)
备注1:传递
params
参数时,若使用to
的对象写法,必须使用name
配置项,不能用path
。备注2:传递
params
参数时,需要提前在规则中占位。
路由的props配置
让路由组件更方便的收到参数(可以将路由参数作为props
传给路由组件)
//方法一
// props的布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件
{
name:'xiang',
path:'detail/:id/:title/:content',
component:Detail,
props:true
//底层在调用此规则时相当于<Detail id=" " title=" " content=" ">
}
//方法二
// props的函数写法,作用:把返回的对象中每一组key-value作为props传给Detail组件
{
name:'xiang',
path:'detail/:id/:title/:content',
component:Detail,
props(route){
return route.query
}
}
//方法三
// props的对象写法,作用:把对象中的每一组key-value作为props传给Detail组件
{
name:'xiang',
path:'detail/:id/:title/:content',
component:Detail,
props:{a:1,b:2,c:3}//此时的props是静态不变的
}
在路由组件中使用
<script setup>
defineProps(['id','title','content'])
</script>
编程式路由导航
除了借助<router-link>
创建a标签来定义导航链接,还可以借助router实例的方法,编写代码来实现跳转。
router.push
该方法会向 history 栈添加一个新的记录,当用户点击浏览器后退按钮时,会回到之前的 URL。
<script setup>
import {useRouter} from 'vue-router'
// 字符串路径
router.push('/users/eduardo')
// 带有路径的对象
router.push({ path: '/users/eduardo' })
// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })
// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })
// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })
</script>
注意:当使用对象写法时,若使用
path
属性,则params
会被忽略。
router.replace
该方法与router.push( )用法近乎一致,只不过不会向history栈添加一个记录,而是取代原有的记录。
router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })
router.go
该方法采用一个整数作为参数,表示在历史堆栈中前进或后退多少步。
// 向前移动一条记录,与 router.forward() 相同
router.go(1)
// 返回一条记录,与 router.back() 相同
router.go(-1)
重定向和别名
重定向
将特定的路径,重新定向到已有路由。重定向是通过router
配置来完成的。
重定向完URL会被改变的。例如:访问 /home
时,URL 会被 /
替换,然后匹配成 /
。
//从 /home 重定向到 /:
const routes=[{path:'/home',redirect:'/'}]
//重定向的目标也可以是一个命名的路由
const routes = [{ path: '/home', redirect: { name: 'homepage' } }]
//也可以是一个方法,动态返回重定向目标
const routes = [
{
// /search/screens -> /search?q=screens
path: '/search/:searchText',
redirect: to => {
// 方法接收目标路由作为参数
// return 重定向的字符串路径/路径对象
return { path: '/search', query: { q: to.params.searchText } }
},
},
{
path: '/search',
// ...
},
]
也可以重定向到相对位置
const routes = [
{
// 将总是把/users/123/posts重定向到/users/123/profile。
path: '/users/:id/posts',
redirect: to => {
// 该函数接收目标路由作为参数
// 相对位置不以`/`开头
// 或 { path: 'profile'}
return 'profile'
},
},
]
别名
将 /
别名为 /home
,意味着当用户访问 /home
时,URL 仍然是 /home
,但会被匹配为用户正在访问 /
。
const routes = [{ path: '/', component: Homepage, alias: '/home' }]
有参数的在别名中同样要设置。
const routes = [
{
path: '/users/:id',
component: UsersByIdLayout,
children: [
// 为这 3 个 URL 呈现 UserDetails
// - /users/24
// - /users/24/profile
// - /24
{ path: 'profile', component: UserDetails, alias: ['/:id', ''] },
],
},
]
内置组件
Transition
<Transition>
可以将进入和离开动画通过默认插槽传递给他的元素或组件上。v-if
和v-show
均可触发。
<button @click="show = !show">Toggle</button>
<Transition>
<p v-if="show">hello</p>
</Transition>
/* 下面我们会解释这些 class 是做什么的 */
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
也可以为过渡效果命名
给<Transition>
组件传入name
props来声明一个过度效果名。
<Transition name="fade">
...
</Transition>
//注意!!!改名
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
TransitionGroup
<TransitionGroup>
是一个内置组件,用于对 v-for
列表中的元素或组件的插入、移除和顺序改变添加动画效果。
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item">
{{ item }}
</li>
</TransitionGroup>
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* 确保将离开的元素从布局流中删除
以便能够正确地计算移动的动画。 */
.list-leave-active {
position: absolute;
}
- 默认情况下,它不会渲染一个容器元素。但你可以通过传入
tag
prop 来指定一个元素作为容器元素来渲染。- 列表中的每个元素都必须有一个独一无二的
key
attribute。- CSS 过渡 class 会被应用在列表内的元素上,而不是容器元素上。
Teleport
<Teleport>
可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。
<teleport to='body' >
<div class="modal" v-show="isShow">
<h2>我是一个弹窗</h2>
<p>我是弹窗中的一些内容</p>
<button @click="isShow = false">关闭弹窗</button>
</div>
</teleport>
Suspense
- 等待异步组件时渲染一些额外内容,让应用有更好的用户体验
- 使用步骤:
- 异步引入组件
- 使用
Suspense
包裹组件,并配置好default
与fallback
import { defineAsyncComponent,Suspense } from "vue";
const Child = defineAsyncComponent(()=>import('./Child.vue'))
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.......</h3>
</template>
</Suspense>
</div>
</template>
API
全局API
应用实例API
createApp
创建一个应用实例
类型:function createApp(rootComponent[,rootProps])
第一个参数是根组件。第二个是要传给根组件的props。
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount()
将应用实例挂载在一个容器元素中。
app.mount(rootContainer)
参数可以是一个实际的DOM元素,也可以是CSS选择器(使用第一个匹配到的元素)。返回根组件的实例。
import { createApp } from 'vue'
const app = createApp(/* ... */)
app.mount('#app')
//app.mount(document.body.firstChild)
app.component()
如果同时传递一个字符串及其定义,则注册一个全局组件。
如果只传递一个名字,则会返回用该名字注册的组件(如果存在的话)。
import { createApp } from 'vue'
const app = createApp({})
// 注册一个选项对象
app.component('my-component', {
/* ... */
})
// 得到一个已注册的组件
const MyComponent = app.component('my-component')
app.use()
安装一个插件
import { createApp } from 'vue'
import MyPlugin from './plugins/MyPlugin'
const app = createApp({
/* ... */
})
app.use(MyPlugin)
app.provide()
全局提供一个值,可在应用实例的所有后代中注入(inject)
使用。
第一个参数是注入的key,第二个参数是提供的值。
import { createApp } from 'vue'
const app = createApp(/* ... */)
app.provide('message', 'hello')
app.directive()
若传入两个参数则是全局注册自定义指令。若传入一个参数则返回用该名称定义的指令。
import { createApp } from 'vue'
const app = createApp({
/* ... */
})
// 注册(对象形式的指令)
app.directive('my-directive', {
/* 自定义指令钩子 */
})
// 注册(函数形式的指令)
app.directive('my-directive', () => {
/* ... */
})
// 得到一个已注册的指令
const myDirective = app.directive('my-directive')
app.config
每个应用实例都会暴露一个config
对象,其中包含了对这个应用的配置设定。可以在挂载前更改这些属性。
import { createApp } from 'vue'
const app = createApp(/* ... */)
console.log(app.config)
app.config.globalProperties
用于注册能够被应用内所有组件实例访问到的全局属性的对象。
用法:app.config.globalProperties.attributeName = value/function
之后在任意组件都可用。
通用
version
暴露当前所使用的Vue版本
import { version } from 'vue'
console.log(version)
nextTick()
等待下一次DOM更新刷新的工具方法。
<template>
<button id="counter" @click="increment">{{ count }}</button>
</template>
<script setup>
import { ref, nextTick } from 'vue'
const count = ref(0)
async function increment() {
count.value++
// DOM 还未更新
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM 此时已经更新
console.log(document.getElementById('counter').textContent) // 1
}
</script>
UI组件库
移动端常用 UI 组件库
- Vant https://youzan.github.io/vant
- Cube UI https://didi.github.io/cube-ui
- Mint UI http://mint-ui.github.io
PC 端常用 UI 组件库
- Element UI https://element.eleme.cn
- IView UI https://www.iviewui.co
使用组件库
以Element UI为例
1、安装
npm install element-ui
2、在main.js入口文件使用element-ui插件
import {createApp} from 'vue'
// 引入element-ui
import ElementUI from 'element-ui'
// element-ui的css样式要单独引入
import 'element-ui/lib/theme-chalk/index.css'
import App from './App.vue'
const app = createApp()
// 这种方式引入了ElementUI中所有的组件
app.use(ElementUI)
app.mount('#app')
3、就可以使用element-ui中的组件了。
若有不足之处请指出。谢谢!
标签:vue,const,app,入门教程,vue3,组件,import,路由 From: https://blog.csdn.net/wawu_moment/article/details/143213220