【Vue3】整理的一些Vue3知识点和案例(Vue3 获取窗口宽和高)
1- Vue3基础介绍
1.1 Vue3简介
- 2020年9月18日,
Vue.js
发布3.0版本,代号:One Piece(海贼王) - Vue3.0使用了
Typescript
进行了大规模的重构,带来了Composition API RFC
版本,类似React Hook
一样的写Vue
,可以自定义自己的hooks
1.2 与Vue2的不同
1.启动方式不同
在main.js 里
- Vue3
## Vue3
import {
createApp } from "vue";
createApp(App).use(store).use(router).mount('#app')
- Vue2
## Vue2
import Vue from "vue";
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
2.全局挂载方法不同
- Vue3
// Vue3
const app = createApp(App);
//定义vue3 的全局方法,config 配置,global 全局 Properties属性
app.config.globalProperties.$say = function (msg) {
alert(msg);
};
- Vue2
// Vue2
Vue.prototype.$say=function(msg){
alert{
msg}}
3.根节点不同
- vue3 可以有多个根节点
- vue2 只能有一个根节点
4.生命周期里不同
- Vue3
- Vue2
1.3 Vue3特点
- 新增 组合式 API
- 更加接近原生js
- 按需加载
1.4 Vue3带来了什么
1.性能的提升
-
打包大小减少41%
-
初次渲染快55%, 更新渲染快133%
-
内存减少54%
…
2.源码的升级
-
使用
Proxy
代替defineProperty
实现响应式 -
重写
虚拟DOM
的实现和Tree-Shaking
…
3.拥抱TypeScript
- Vue3可以更好的支持
TypeScript
4.新的特性
- Composition API(组合API)
setup
配置ref
与reactive
watch
与watchEffect
provide
与inject
- …
- 新的内置组件
Fragment
Teleport
Suspense
- 其他改变
- 新的生命周期钩子
data 选项
应始终被声明为一个函数- 移除
keyCode
支持作为v-on
的修饰符 - …
2- Vue3基础工作
## 安装 vue-cli
npm install -g @vue/cli
## 创建一个项目
## 使用create 命令行创建
vue create 项目名
3- 常用 Composition API
3.1 setup
-
setup
是Vue3.0
中一个新的配置项,值为一个函数 -
setup是
Composition API(组合API)
的入口 -
组件中所用到的:数据、方法等等,均要配置在
setup
中 -
setup函数
的两种返回值:- 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
- 若返回一个渲染函数:则可以自定义渲染内容。(了解)
-
注意点:
1.尽量不要与
Vue2.x
配置混用
Vue2.x
配置(data,methods,computed...
)中可以访问到setup
中的属性、方法- 但在
setup
中不能访问到Vue2.x
配置(data,methods,computed...
)- 如果有重名,
setup
优先
2.
setup
不能是一个async函数
,因为返回值不再是return
的对象, 而是promise
, 模板看不到return
对象中的属性。(后期也可以返回一个Promise
实例,但需要Suspense
和异步组件的配合)
3.2 ref函数
- 作用:定义一个响应式的数据
- 语法:
##语法
const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
- js 中操作数据:xxx.value
- 模板中读取数据:不需要.value,直接
{ {xxx}}
- 备注
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠
Object.defineProperty()
的get
与set
完成的。- 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数——
reactive
函数。
3.3 reactive函数
- 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用
ref
函数) - 语法
##语法
const 代理对象 = reactive(源对象)
接收一个对象(或数组),返回一个代理对象(
Proxy
的实例对象,简称proxy对象
)
-
reactive 定义的响应式数据是 “深层次的”
-
内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
3.4 reactive对比ref
- 从定义数据角度对比
ref
用来定义 基本类型数据reactive
用来定义 对象(或数组)类型数据- 备注:
ref
也可以用来定义 对象(或数组)类型数据,它内部会自动通过reactive
转为代理对象
- 从原理角度对比:
ref
通过object.defineProperty()
的get
与set
来实现响应式(数据劫持)reactive
通过使用Proxy
来实现响应式(数据劫持),并通过Reflect
操作 源对象内部的数据
- 从使用角度对比
- ref 定义的数据:操作数据 需要
.value
,读取数据时模板中直接读取不需要.value
- reactive 定义的数据:操作数据与读取数据:均不需要
.value
3.5 setup的两个注意点
setup 执行的时机
- 在beforeCreate 之前执行一次,this 是 undefined
setup 的参数
- props :值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
- context:上下文对象
- attrs:值为对象,包含:组外部传递过来,但没有在props配置中声明的属性,相当于 this.$attrs
- slots:收到的插槽内容,相当于 this.$slots
- emit:分发自定义事件的函数,相当于this.$emit
3.6 计算属性与监视
1.computed
- 与Vue2.x中computed配置功能一致
- 写法
import {
computed} from 'vue'
setup(){
...
//计算属性——简写
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
//计算属性——完整
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
}
2.watch函数
- 与Vue2.x中watch配置功能一致
- 两个小“坑”:
- 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
- 监视reactive定义的响应式数据中某个属性时:deep配置有效。
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
},{
immediate:true})
//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
})
/* 情况三:监视reactive定义的响应式数据
若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
*/
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{
immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{
immediate:true,deep:true})
//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{
immediate:true,deep:true})
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{
deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
3.watchEffect函数
- watch的套路是:既要指明监视的属性,也要指明监视的回调。
- watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
- watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
3.7 生命周期
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
beforeDestroy 改名为 beforeUnmount
destroyed 改名为 unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
3.8 自定义hook函数
- 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装
- 类似于vue2.x中的mixin
- 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂
3.9 toRef
-
作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
-
语法:
const name = toRef(person,'name')
-
应用: 要将响应式对象中的某个属性单独提供给外部使用时。
-
扩展:
toRefs
与toRef
功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
3.10 setup语法糖
只需要在 script
标签中添加 setup
,组件只需引入不用注册,属性和方法也不用返回,setup
函数也不需要,甚至 export default
都不用写了,不仅是数据,计算属性和方法,甚至是自定义指令也可以在我们的 template
中自动获得。
setup script
语法糖提供了三个新的API来供我们使用:defineProps
、defineEmit
和useContext
defineProps
用来接收父组件传来的值props。defineEmit
用来声明触发的事件表。useContext
用来获取组件上下文context。
4- 案例
- StepCom.vue
<template>
<div>
<button @click="count--">-</button>
<input type="text" style="width: 80px" v-model="count" />
<button @click="count++">+</button>
</div>
</template>
<script setup>
//defineProps 定义props
import {
defineProps, ref, watch, defineEmits, watchEffect } from "vue";
//定义props 传入参数
const props = defineProps({
//类型为数字或者字符串,默认值为1
value: {
type: [Number, String],
default: 1,
},
});
//定义一个响应式对象初始值为props.value
const count = ref(props.value);
//定义事件发送器
var emits = defineEmits(["input"]);
//监听count的变化
watch(count, () => {
//发送一个input事件,事件的值为count 的value
emits("input", count.value);
});
//在 watchEffect 都会被自动监听,执行回调函数
/* 只要watchEffect中出现了数据,数据发生变化时候,都会执行watchEffect */
watchEffect(() => {
count.value = props.value;
});
</script>
<style>
</style>
- HomeView.vue
<template>
<div>
<button @click="num++">{
{
num }}</button> <br />
<button @click="setNum(num + 5)">加5</button> <br />
<p v-for="item in list" :key="item">{
{
item }}</p>
<input type="text" v-model="temp" @keyup.enter="addList()" />
</div>
</template>
<script>
//1.ref 导入 一个创建响应式 值类型数据的方法
//2,reactive 响应式 引用类型的方法
import {
ref, reactive } from "vue";
export default {
setup() {
//1.导入一个创建响应式,num的默认值是5
const num = ref(5);
//3.定义更新num的方法
function setNum(n) {
//在setup 中num 的值访问 与赋值 要加value 在template 不需要
num.value = n;
}
/* 定义一个响应式列表数据 */
var list = reactive(["Vue", "React", "Angular", "小程序"]);
// 定义temp 临时数据
var temp = ref("");
//定义添加list 的方法
function addList() {
//注意值类型访问要加value
list.push(temp.value);
temp.value = "";
}
//2.返回num
return {
num, setNum, list, temp, addList };
// console.log("setup在组件挂载前执行");
},
beforeUnmount() {
console.log("组件将要卸载");
},
unmounted() {
console.log("组件已经卸载");
},
};
</script>
<style>
</style>
5- Vue3 获取窗口宽和高
- 新建utils.js 文件
import {
ref, onMounted, onBeforeUnmount } from "vue";
//导出获取窗口的宽高
export function useWinSize() {
const size = ref({
width: window.innerWidth, height: window.innerHeight });
//窗口变化时候更新 size
function onResize() {
size.value = {
//用窗口的最新宽高更新 width 与 height
width: window.innerWidth,
height: window.innerHeight,
};
}
//组件 挂载完毕 更新 size
onMounted(() => {
window.addEventListener("resize", onResize);
});
//组件 要卸载的时候移除 事件监听
onBeforeUnmount(() => {
window.removeEventListener("resize", onResize);
});
return size;
}
- 在HomeView.vue 里 使用
<template>
<div>
<h1>setup</h1>
<button @click="num++">{
{
num }}</button>
<step-com :value="num" @input="num = $event" ref="stepper"></step-com>
<p>{
{
size }}</p>
</div>
</template>
<script setup>
// 使用setup 可以简写 ,不用导出组件不用注册了
import StepCom from "@/components/StepCom.vue";
import {
onMounted, ref } from "vue";
import {
useWinSize } from "@/utils/utils";
const num = ref(5);
const stepper = ref();
onMounted(() => {
console.log("组件已经挂载完毕");
//组件挂在完毕引用stepper组件
/* stepper.value 就是对stepper-com组件的引用 */
console.log(stepper.value.count);
});
const size = useWinSize();
</script>
<style>
</style>