Vue3
Vue3 CDN
Vue3 中文文档:
Vue 社区
Vue2 参考,学习笔记
一、 初识 Vue3.0
建议先学习完 Vue2、vue-router、vuex 、vue-cli 再学习 Vue3
1. Vue3 的一大特性函数 ---- setup
1、setup 函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数 也就说在 setup函数中是无法 使用 data 和 methods 中的数据和方法的。
2、setup 函数是 Composition API(组合API)的入口。
3、在setup 函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用。
4、setup 函数只能是同步而不能是异步的。
1. setup(Composition API) 的学习
首先要知道,在 Vue3 里是兼容 Vue2 语法的,也就是说,可以使用 data,methods 等等。虽然使用 Vue2 的语法不会报错,但是,Vue3 就该使用 Vue3 的语法。
在 Vue2 里我们要定义数据和方法都得在 data 和 methods 里,其他定义组件,计算属性,监听数据等等,Vue2 都给我们规定死了。而在 Vue3 中不同,Vue3 中所有的操作都放在了 setup 中,并且定义数据及方法都与 Vue2 不同,其他定义组件,计算属性,监听数据等等在 setup 中变为了按需引入,即使用时需要 import 将其引入才可用。
<template>
// ...
</template>
<script>
export default {
// Vue2 接受父组件数据
props:{
name: String,
age:{
type: Number,
default: 18
}
},
// props 为接受的父组件数据
// ctx 本组件上下执行文
setup(props, ctx){
console.log(props.name)
console.log(ctx)
return {}
}
}
</script>
1. reactive 定义响应式数据
使用方法:
<template>
// 使用
<p>{{ state.id }}</p>
</template>
<script>
// 引入 reactive
import { reactive } from 'vue'
export default {
setup(props, ctx){
// 定义 state 存放 reactive 声明的数据
const state = reactive({
id: 1
}
)
return {
// 最后需要将其返回
state
}
}
}
</script>
页面效果:
2. 定义方法
在 Vue3 里定义方法不需要引入,直接使用变量定义函数的方式即可,但同样需要将定义好的方法返回。
<template>
// 使用
<p>{{ state.id }}</p>
// 添加方法
<button @click='add()'>增加</button>
</template>
<script>
// 引入 reactive
import { reactive } from 'vue'
export default {
setup(props, ctx){
// 定义 state 存放 reactive 声明的数据
const state = reactive({
id: 1
}
)
// 定义方法
const add = () => {
state.id++
}
return {
// 最后需要将其返回
state,
add
}
}
}
</script>
效果:
可能有人问了?我每次在外面使用数据都得state.id吗?那不是很麻烦吗?不不不,这里可以使用ES6的展开运算符,将state里的数据映射出去,就可以直接输入定义的名称使用了。
<template>
// 使用
<p>{{ state.id }}</p>
// 添加方法
<button @click='add()'>增加</button>
</template>
<script>
// 引入 reactive
import { reactive } from 'vue'
export default {
setup(props, ctx){
// 定义 state 存放 reactive 声明的数据
const state = reactive({
id: 1
}
)
// 定义方法
const add = () => {
state.id++
}
return {
// ES6展开运算符返回
...state,
add
}
}
}
</script>
然而这时候。。。你会发现一个BUG!!!那就是我们页面的数据在触发add方法后没反应了,但是我们给add方法添加一个console.log,它却照样能够自增,但是页面上的数据却不会改变?也就是说明数据并没有完成响应式:
为什么?因为在使用展开运算符的时候,它同时会将我们的响应式给取消。
那这时候怎么办呢?
3. toRefs 保护响应式数据特性
<template>
// 使用
<p>{{ state.id }}</p>
// 添加方法
<button @click='add()'>增加</button>
</template>
<script>
// 引入 reactive
import { reactive } from 'vue'
export default {
setup(props, ctx){
// 定义 state 存放 reactive 声明的数据
const state = reactive({
id: 1
}
)
// 定义方法
const add = () => {
state.id++
}
return {
// 使用 toRefs
...toRefs(state),
add
}
}
}
</script>
4. computed 计算属性
<p>{{ id }}----{{ name }}</p>
// 导入
import { reactive, toRefs, computed } from 'vue'
// 定义响应式数据
const state = reactive({
id: 1,
name: computed(() => '你好:' + state.id)
}
)
return {
...toRefs(state)
}
5. watch 监听数据
<p>{{ id }}----{{ name }}----{{ type }}</p>
// 导入
import { reactive, toRefs, computed, watch } from 'vue'
// 定义响应式数据
const state = reactive({
id: 1,
name: computed(() => '你好:' + state.id),
type: '奇数'
}
)
// 添加监听数据
// cur: 监听之后修改的数据,也就是 2
// old:监听之前未修改的数据,也就是 1;之后则是以此类推
watch(() => state.id, (cur, old) => {
console.log(cur, old)
if(cur % 2 == 0){
state.type = '偶数'
}else{
state.type = '奇数'
}
})
return {
...toRefs(state)
}
6. Vue3 的生命周期
先来看看Vue2生命周期在3里的变化:
| Vue2周期 | Vue3 | |
|---|---|---|
| beforeCreate | --------> | setup() |
| created | --------> | setup() |
| beforeMount | --------> | onBeforeMount |
| mounted | --------> | onMounted |
| beforeUpdate | --------> | onBeforeUpdate |
| updated | --------> | onUpdated |
| beforeDestroy | --------> | onBeforeUnmount |
| destroyed | --------> | onUnmounted |
| errorCaptured | --------> | onErrorCaptured |
观看上图可以发现,除了 created 创建周期和 destroyed 周期在 Vue3 里改变了,其他都只是在原本的周期上加了 on ,变化总体来说并不大。
<script setup>
// 导入
import { onMounted, onUnmounted } from 'vue'
// onMounted
onMounted(() => {
state.id++
})
// onUnmounted
onUnmounted(() => {
console.log('你好')
})
</script>
7. 一个页面有多个方法时:
// 如果一个页面方法太多时,可以:
const methods = {
a(){
console.log('a')
},
b(){
console.log('b')
}
}
// onMounted
onMounted(() => {
state.id++
methods.a()
methods.b()
})
// 返回
return {
...methods
}
8.ref 定义单个响应式数据
<script setup>
import { ref } from 'vue'
const count = ref(0)
console.log(count.value)
count.value++
console.log(count.value)
</script>
9.unref 返回目标值
目标是一个响应式数据则返回其内部值,如果只是个普通变量,则返回变量自身。
<script setup>
import { ref, unref } from 'vue'
const count = ref(1)
// 输出:1
console.log(unref(count))
</script>
10.toRef 将 reactive 响应式对象中的某个属性转为 ref 响应式数据
可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。
<script setup>
import { reactive, toref } from 'vue'
const state = reactive({
foo: 1,
bar: 2
})
// 转换为一个新的 ref
const fooRef = toRef(state, 'foo')
fooRef.value++
// 输出:2
console.log(state.foo)
state.foo++
// 输出:3
console.log(fooRef.value)
</script>
11.isRef 检查目标是否是一个 ref 对象
12.watchEffect 执行函数体监听
13. markRaw 标记对象为不可响应式
设置原始数据为不可响应式,即使 ref 和 reactive 将数据包装。
14. shallowReactive 创建一个响应式代理
创建一个响应式代理,但它只代理浅层对象,而不代理深层对象。
const state = shallowReactive({
foo: 1,
nested: {
bar: 2
}
})
// 改变 state 本身的性质是响应式的
state.foo++
// 但是不转换嵌套对象;
isReactive(state.nested)
// 非响应式
state.nested.bar++
15. shallowReadonly 创建一个只读的响应式代理对象
同样只对浅层对象作效,深层对象可以任意修改,但不是响应式对象。
const state = shallowReadonly({
foo: 1,
nested: {
bar: 2
}
})
// 改变 state 本身的 property 将失败
state.foo++
// ...但适用于嵌套对象
isReadonly(state.nested) // false
state.nested.bar++ // 适用
16. readonly 将对象转为只读代理
将一个对象设置为只读对象。
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// 用于响应性追踪
console.log(copy.count)
})
// 变更 original 会触发依赖于副本的侦听器
original.count++
// 变更副本将失败并导致警告
copy.count++
2. Vue3 常用 API
1. getCurrentInstance() 获取当前组件实例
<script>
// 引入 getCurrentInstance 获取组件本身
import { getCurrentInstance } from "vue"
export default {
setup() {
// 获取组件本身
const instance = getCurrentInstance()
console.log(instance)
return {}
},
}
</script>
2. useRoute、useRouter 获取路由信息
- useRoute():返回当前路由地址。等同于 $route。
- useRouter():返回路由实例。等同于 $router。
<script setup>
import { ref } from "vue"
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
const route = useRoute()
console.log(router)
console.log(route)
</script>
3. onBeforeRouteLeave、onBeforeRouteUpdate 导航守卫
- onBeforeRouteLeave():离开路由时触发。
- onBeforeRouteUpdate() :当前路由更新时触发
<script setup>
import { onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router'
//当前组件路由改变后,进行触发
onBeforeRouteUpdate((to, from)=>{
console.log(to, from)
})
// 离开当前的组件,触发
onBeforeRouteLeave((to,from)=>{
console.log(to, from)
})
</script>
4. useStore 获取 vuex 状态管理信息
等同于 this.$store。
- 在 vue3 中访问 store 和 getter。
<script>
// 在 vue3 中必须给予 store 计算属性,
// 才能保持住 store 的响应性。
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
// 使用 store
const count = computed(() => {
store.state.count
})
// 使用 getter
const double = computed(() => {
store.getters.double
})
return {
count,
double
}
}
}
</script>
- 访问 mutation 和 action。
<script>
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
// 使用 mutation
const increment = () =>{
store.commit('increment')
}
// 使用 action
const asyncIncrement = () =>{
store.dispatch('asyncIncrement')
}
return {
increment,
asyncIncrement
}
}
}
</script>
5.app.config.globalProperties.$xxx 原型扩展
代替 vue2 的 Vue.prototype 写法。
import http from 'axios'
// Vue2
Vue.prototy.$http = http
// Vue3
app.config.globalProperties.$http = http
6. keep-alive 新用法
在 vue3 中,keep-avlie 需要被 router-view 所嵌套,并在 keep-alive 中写上component 动态组件,设置 :is 属性。
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component"></component>
</keep-alive>
</router-view>
7. Provid 和 inject 依赖注入进行父、子、孙组件传参
在父组件中通过 Provid 对象传递的属性,无论在其子组件还是孙组件中都可以使用 inject 进行接受。可以无限级的传递下去。
- Provide 传递属性:
Provide: { 属性名称: 属性值 ... }
// 父组件
<template>
// 引入的子组件
<MyMarker />
</template>
<script>
// 从 Vue 中导入 Provide
import { Provide } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
// 导入子组件
MyMarker
},
setup(){
// 传递属性
provide('name', '阿尘s')
provide('user', {
id: 0,
age: 18,
sex: '男'
})
}
}
</script>
- inject 接受属性:
inject: ['接收的属性名1', '接收的属性名2', ...]
// 子组件
<template>
<p>{{ name }}</p>
<p v-for="(item, key) in user" :key="key">{{ item }}</p>
</template>
<script>
// 从 vue 中导入 inject
import { inject } from 'vue'
export default {
setup(){
// 接受父组件传递的属性
const name = inject('name')
const user = inject('user')
return {
name,
user
}
}
}
</script>
二、
基于 setup() 函数的语法糖,在 setup() 的基础上省略了更加多的语法以及代码量,使我们的开发速度增进很多。下面介绍下它的一些特性,以及 API 的使用。
1. 外部组件导入后无需注册就可以直接使用
<template>
<!-- 直接使用 -->
<comp></comp>
</template>
<script setup>
// 外部组件导入后无需注册就可以直接使用
import Comp from "comps/JSXComp.vue";
</script>
2. 定义的数据和方法不用返回即可使用
<template>
// 直接使用 state 中的 sum
<button type="button" @click="state.sum++">sum is: {{ state.sum }}</button>
// 直接绑定方法
<button @click="onMethods.a()"></button>
</template>
<script setup>
// 1. 定义的响应式数据无需 return 出去就可以直接使用
const number = ref("定义的数据无需 return 出去");
const state = reactive({
sum: 0,
count1: "数据1",
count2: "数据2",
});
// 在 DOM 中可以直接使用
console.log(number.value, state.count1, state.count2);
// 2. 定义的方法也无需 return 出去就可以直接使用
const onMethods = {
a() {
console.log("定义的方法也无需 return 出去");
},
};
// 可直接绑定 DOM 元素
onMethods.a();
</script>
3. 省略 setup() 语句。
无需写入 export default 导出关键字,也无需 setup() 函数,就可以直接在 script 标签中写入 Vue3 代码。
<script setup>
// 代码内容...
</script>
4. 使用 defineProps 和 defineEmits 进行数据传递,事件传播。
- defineProps 使用方法:
const props = defineProps({ 数据名称: 数据类型, 数据名称: { type: 数据类型, default: 数据默认值 } })
- defineEmits 使用方法:
const emit = defineEmits(['事件一'], ['事件二'])
<script setup>
// 接收父组件传参
const props = defineProps({
msg: String,
msg2: {
type: String,
defalut: '默认值'
}
});
// 使用 emit 传播事件给父组件
const emit = defineEmits(["myClick"]);
// 定义方法
const onclick = () => {
// 使用 emit 进行传播
emit("myClick", '传参内容');
};
</script>
5. defineExpose 暴露属性
暴露自身属性于父组件中使用。
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// 暴露属性
defineExpose({
a,
b
})
</script>
6. useSlots 和 use Attrs 获取插槽和属性
<script setup>
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>
三、Vue3 新特性
Vue3 做了那些优化?以及它的新特性
四、全局 API
1. defineComponent
代替了 Vue2 的 Vue.extend 构造函数。
其定义方法有两种,第一种是类似以往组件定义,使用 options API。
import { defineComponent } from 'vue'
const MyComponent = defineComponent({
data() {
return { count: 1 }
},
methods: {
increment() {
this.count++
}
}
})
第二种是使用 setup 函数的形式进行定义。
import { defineComponent, ref } from 'vue'
const HelloWorld = defineComponent(function HelloWorld() {
const count = ref(0)
return { count }
})
2. defineAsyncComponent
异步组件定义,完善了 Vue2 动态、异步、懒加载组件的用法。
- 使用 import 单独定义一个异步组件。
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
app.component('async-component', AsyncComp)
- 在页面中局部注册一个组件
import { createApp, defineAsyncComponent } from 'vue'
createApp({
// ...
components: {
AsyncComponent: defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
}
})
- 完整使用,定义一个高自由度的异步罪案
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent({
// 工厂函数
loader: () => import('./Foo.vue'),
// 加载异步组件时要使用的组件
loadingComponent: LoadingComponent,
// 加载失败时要使用的组件
errorComponent: ErrorComponent,
// 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
delay: 200,
// 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件
// 默认值:Infinity(即永不超时,单位 ms)
timeout: 3000,
// 定义组件是否可挂起 | 默认值:true
suspensible: false,
/**
*
* @param {*} error 错误信息对象
* @param {*} retry 一个函数,用于指示当 promise 加载器 reject 时,加载器是否应该重试
* @param {*} fail 一个函数,指示加载程序结束退出
* @param {*} attempts 允许的最大重试次数
*/
onError(error, retry, fail, attempts) {
if (error.message.match(/fetch/) && attempts <= 3) {
// 请求发生错误时重试,最多可尝试 3 次
retry()
} else {
// 注意,retry/fail 就像 promise 的 resolve/reject 一样:
// 必须调用其中一个才能继续错误处理。
fail()
}
}
})
3. defineCustomElement
与 defineComponent 相似,但 defienCustomElement 返回的是一个自定义元素。
<my-vue-element></my-vue-element>
import { defineCustomElement } from 'vue'
const MyVueElement = defineCustomElement({
// 这里是普通的 Vue 组件选项
props: {},
emits: {},
template: `...`,
// 只用于 defineCustomElement:注入到 shadow root 中的 CSS
styles: [`/* inlined css */`]
})
// 注册该自定义元素。
// 注册过后,页面上所有的 `<my-vue-element>` 标记会被升级。
customElements.define('my-vue-element', MyVueElement)
4. nextTick
将回调推迟到下一个 DOM 更新周期之后执行。
import { createApp, nextTick } from 'vue'
const app = createApp({
setup() {
const message = ref('Hello!')
const changeMessage = async newMessage => {
message.value = newMessage
await nextTick()
console.log('Now DOM is updated')
}
}
})
五、实例
1. 多级导航栏(Nav)
使用 vue3 制作一个显示隐藏导航栏,不用设置 css 的 display 属性,下面是部分代码。
写的时候用了阿里的 iconf 图标,这个可以自己选择使用其他图片代替,也可以一样使用阿里的 iconf 图标。
<div class="Nav">
<ul>
<li class="Home">
<a @click="homeShow()">
<i class="iconfont iconshouye1"></i>
subnave 1
</a>
<ul v-show="Home">
<li>
<a>option 1</a>
</li>
</ul>
</li>
<li class="Address">
<a @click="addressShow()">
<i class="iconfont icontongxunlu"></i>
subnave 1
</a>
<ul v-show="Address">
<li>
<a>option 1</a>
</li>
<li>
<a>option 2</a>
</li>
</ul>
</li>
</ul>
</div>
<script>
export default{
setup() {
const nav = reactive({
Home: false,
Address: false
})
// 设置列表的显示隐藏
const methods = {
homeShow(){
nav.Home = !nav.Home
},
addressShow(){
nav.Address = !nav.Address
}
}
// 返回
return {
...toRefs(nav),
...methods
}
}
}
</script>
2. 轮播图(Banner)
轮播图也很简单,先声明 imgArray 保存图片,再声明个 index 控制图片显示第几张。之后再 onMounted 里添加定时器,每 1.5 秒给 index+1 就行。
<div class="Banner">
<img :src="imgArray[index]" alt="图片显示错误" width="1080" height="590">
</div>
<script>
export default{
setup(){
const banner = reactive({
index: 0,
imgArray: [
require('../assets/img/1.png'),
require('../assets/img/2.png'),
require('../assets/img/3.png'),
require('../assets/img/4.jpg')
]
})
onMounted(() => {
setInterval(() => {
if(banner.index >=3){
banner.index = 0
return false
}
banner.index++
},1500)
})
return {
...toRefs(banner)
}
}
}
</script>
3. 天气查询(Weather)
也是很简单的一玩意,就调用 API 就行。引入或安装 axios 后,其单页面引入方式如 Vue2相同,如果是想要全局配置,得使用
app.config.globalProperties.axios = 'axios'
官网地址:vue3js.cn/docs/zh/api…
代码:
<div class="Weather">
<input type="text" v-model="inputData">
<input type="button" value="查询" @click="searchSelect()">
<p>
<a @click="select('上海')">上海</a>
<a @click="select('北京')">北京</a>
<a @click="select('四川')">四川</a>
<a @click="select('武汉')">武汉</a>
</p>
<div v-show="show">
<p>查询城市:{{ city }}</p>
<p>小提醒:{{ ganmao }}</p>
</div>
<ul>
<li v-for="(item, key) in data" :key="key">
<p>日期:{{ item.date }}</p>
<p>天气:{{ item.type }}</p>
<p>温度:{{ item.high }}~{{ item.low }}</p>
</li>
</ul>
</div>
<script>
export default{
setup(){
const weather = reactive({
// 用户输入的城市信息
inputData: '',
// 存储查询的城市
city: '',
// 存储查询到的提示信息
ganmao: '',
// 存储查询的天气信息
data: [],
// 控制详细信息显示隐藏
show: false
)}
// 定义方法
const methods = {
// 点击按钮查询
searchSelect(){
if(weather.inputData == ''){
alert('为空你查什么?')
return false
}
/*
请求地址:http://wthrcdn.etouch.cn/weather_mini
请求方法:get
请求参数:city(城市名)
响应内容:天气信息
*/
axios.get('http://wthrcdn.etouch.cn/weather_mini?city=' + weather.inputData)
.then(res => {
// 因为这个 APi 无论查什么都会成功返回 200,所以我查了下规律
// 发现所有查不到城市天气信息的都会返回 1002
if(res.data.status == 1002){
console.log(res)
alert('查了个什么玩意?')
return false
}
// 获取查找到的天气信息
weather.data = res.data.data.forecast
// 获取目前查找的城市
weather.city = res.data.data.city
// 存储查询到的提示信息
weather.ganmao = res.data.data.ganmao
// 显示详细信息
weather.show = true
})
},
// 固定城市查询
select(city){
axios.get('http://wthrcdn.etouch.cn/weather_mini?city=' + city)
.then(res => {
// 获取查找到的天气信息
weather.data = res.data.data.forecast
// 获取目前查找的城市
weather.city = res.data.data.city
// 存储查询到的提示信息
weather.ganmao = res.data.data.ganmao
// 显示详细信息
weather.show = true
})
}
}
return {
...toRefs(weather),
...methods
}
}
}
</script>
效果演示:不过...有个Bug,无法查询四川,这个是真的不知道为什么就是不能查,使用输入框输入查询都不行,也会报错。
4. 在线音乐(musicPaly)
这个就稍微有点点复杂,使用的是网易云的 Api。并且写的时候还考虑了手机端兼容性,使用了媒体查询,控制了 mv 显示的大小。且设置了切换音乐和 mv 播放时的自动暂停。以及评论区的显示隐藏功能。
主要完成了:
- 获取、播放音乐
- 自动播放下一首
- 获取热门评论
- 获取、播放Mv
效果预览:
pc端mv:
这里先将源代码上传:
vue3.zip
使用 reactive 声明 musicPaly 对象合集,并添加 inputData 获取输入的内容
再给input回车事件和button点击事件都绑定上 searchMusic() 方法,
然后在 musicPaly 对象合集中添加 music 存放获取到的歌曲列表
之后在 searchMusic() 方法中我们使用axios请求歌曲列表,且只获取前十条(因为只是做个小demo,为了方便就只取十条做测试就行)
将 music 对象里的内容遍历出来(打马赛克的内容不用管,现在还用不着)
那么现在输入歌名获取歌曲列表的功能应该就能实现了(播放图标不用管)
再之后实现点击歌名播放音乐和自动播放下一首功能,添加 playMusic(v.id, k) 方法,并将歌曲的 id 和 key 传过去。
添加存放 歌曲播放地址 Url
添加 Html5 音乐标签,并绑定 Url 地址
通过传过来的id查找歌曲 Url
并通过 key 得知现在播放的是第几首,然后通过监听 audio 的 ended(音乐是否已播放结束)
来调用 nextPlay(isTrue) 方法
效果自测,在线音乐就写到这。(我太懒了)
之后功能可以自己下载源码查看并学习,其里面每一行代码我都写了注释的。