一、简介
2020年9月18日,Vue.js发布3.0版本
代号:One Piece(海贼王) https://v3.cn.vuejs.org/
二、快速开始
-
通过 CDN:
-
<script src="https://unpkg.com/vue@next"></script>
-
通过脚手架 Vitenpm init vite hello-vue3 # 或 yarn create vite hello-vue3
// 创建工程
npm init vite-app v3_demo
// 进入工程目录
cd v3_demo
// 安装依赖
npm install (or 'yarn')
// 运行
npm run dev (or 'yarn dev')
-
通过脚手架 vue-cli vue create hello-vue3
// vue-cli 需要v4.5.0 以上
vue -V 5.0.6 /cli
npm install -g /cli ( cli版本安装or升级 )
// 创建工程
vue create v3_demo
// 选择 vue 3 preset
cd v3_demo
npm run serve (or 'yarn serve')
三、vite 与 webpack比较
-
webpack 资源先打包 服务器路由 - 模块 - 打包 - 准备服务器
-
-
vite 冷启动 热重载(HMR) 按需编译准备服务器 - 入口 - 路由 - 模块
-
四、VUE 挂载APP
//引入的不是vue构造函数 而是createApp是一个工厂函数
import { createApp } from 'vue'
import App from './App.vue'
// 创建应用实例对象-app (类似v2中的vm 但 app比vm 更轻)
const app = createApp(App)
//挂载
app.mount('#app')
//并不兼容 new vue v2 语法
const vm = new Vue({
render: (h) => h(app)
})
vm.$mount('app')
app
五、Template Fragment
-
v2 组件必须有一个根标签
-
v3 可以没有根标签了
⭐ 减少标签层级 减少内存占用
<template>
<div>111</div>
<div>222</div>
</template>
通过vue开发者工具可以看出来 是vue内部会将多个标签包含在Fragment的虚拟元素中不参与渲染
六、Composition API (组合式 API 概念)
-
Option API 使用 (data、computed、methods、watch) 组件开始变得更大时 导致组件难以阅读和理解
o
-
Composition API 利用Hook函数组织我们的代码,函数。让相关功能的代码更加有序的组织在一起
o
七、常用的Composition API
1.setup
-
setup 组合式 API 的入口
oVue3.0中一个新的配置项,值为一个函数
o组件中所用到的:数据、方法、计算属性、watch监听、生命周期,均要配置在setup中
osetup 返回值
§一个对象
§一个渲染函数 (了解即可)
-
执行时机
o在beforeCreate之前执行一次,this是undefined。
-
注意点
o尽量不要与Vue2.x配置混用
oVue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。
o但在setup中不能访问到Vue2.x配置(data、methos、computed...)。
o如果有重名, setup优先。
osetup不能是一个async函数,因为返回值不再是对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
<template>
<div>{{ p.name }}</div>
<div>{{ p.age }}</div>
<div @click="getName">点击改变名称</div>
</template>
<script>
//import { h } from "vue";
export default {
name: "App",
setup() {
let p = {
name: "cy",
age: "18",
};
function getName() {
alert(p.name)
}
return { p, getName };
// return ()=> h('div','cyy') 渲染函数
},
};
</script>
2.Props / Emits
-
Vue 3 现在提供一个 emits 选项,和现有的 props 选项类似
-
这个选项可以用来定义一个组件可以向其父组件触发的事件。
<template>
<div>
<p>{{ text }}</p>
<button v-on:click="$emit('accepted')">OK</button>
</div>
</template>
<script>
//2.0
export default {
props: ['text']
}
</script>
-
3.0 和 prop 类似,现在可以通过 emits 选项来定义组件可触发的事件
-
该选项可以接收一个对象,该对象允许定义传入事件参数的验证器,和 props 定义里的验证器类似。
<template>
<div>
<p>{{ text }}</p>
<button v-on:click="$emit('accepted')">OK</button>
</div>
</template>
<script>
//3.0
export default {
props: ['text'],
emits: ['accepted']
}
</script>
3.Ref 函数
-
定义一个响应式的数据
-
const xxx = ref(initValue)
o创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
oJS中操作数据:xxx.value
o模板中读取数据: 不需要.value,直接:{{xxx}}
<template>
<h1>美女的信息</h1>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>职业: {{ job.hobby }}</h2>
<h2>工资:{{ job.address }}</h2>
<button @click="changeInfo">十年后的信息</button>
</template>
<script>
import { ref } from "vue";
export default {
name: "App",
setup() {
let name = ref("cy");
let age = ref(18);
let job = ref({
hobby: "吃",
address: "北京",
});
function changeInfo() {
name.value = "十年后的cy";
age.value += 10;
job.value.hobby = "玩";
job.value.address = "上海";
}
console.log(name, age, job);
return {
name,
age,
job,
changeInfo,
};
},
};
</script>
-
分析输出
o接收的数据可以是:基本类型、也可以是对象类型。
o基本类型的数据:ref会返回一个RefImpl的实例对象,响应式依靠的是类上的getter与setter完成的。
o对象类型的数据:value是一个proxy对象 ;内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。
4.reactive函数
-
定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
-
const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个proxy对象
-
reactive定义的响应式数据是“深层次的”。
-
内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
<template>
<h2>姓名:{{ cy.name }}</h2>
<h2>年龄:{{ cy.age }}</h2>
<h2>爱好:{{ cy.hobby }}</h2>
<h3>测试数据:{{ cy.job.a.b.c }}</h3>
<button @click="changeInfo">修改信息</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
setup() {
// 数据
let cy = reactive({
name: "cy",
age: 8,
hobby: ["吃", "学习", "看书"],
job: {
a: {
b: {
c: 123,
},
},
},
});
function changeInfo() {
cy.name = "cyy";
cy.age = 18;
cy.job.a.b.c = 456;
// 直接通过数组下标修改,可以触发响应式
cy.hobby[0] = "吃吃吃";
}
return {
cy,
changeInfo,
};
},
};
</script>
-
分析输出
o接收的数据 一个对象包裹
o使用的时候 不需要.value;修改下标也可以触发响应式
oreactive 包裹的对象是一个proxy代理对象
5.Reactive 与 Ref 对比
-
定义
oref 基本数据类型
oreactive 对象(数组)数据类型
oref也可以定义复杂数据类型,内部自动通过reactive转为代理对象
-
原理
oref obj.defineproperty() ;通过getter与setter数据劫持
oreactive proxy数据劫持。通过Reflect操作内部数据
-
使用
oref 读取操作需要.value
oreactive 直接读取 不需要.value
6.Vue3.0中的响应式原理
Vue2.x的响应式
-
实现原理
o对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
o数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
-
存在问题
o新增属性、删除属性, 界面不会更新。
o直接通过下标修改数组, 界面不会自动更新
-
解决方案
o使用官方提供的Vue.set、Vue.delete或者vm.$set、vm.$delete API
let p = {
name: 'cy',
age: '18'
}
let p_ = {}
Object.defineProperty(p_, 'name', {
configurable: true, //可配置
get() {
return p.name
},
set(value) {
p.name = value
}
})
Object.defineProperty(p_, 'age', {
configurable: true,
get() {
return p.age
},
set(value) {
p.age = value
}
})
proxy的响应式
-
通过 Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
let p = {
name: 'cy',
age: '18'
}
const p_ = new Proxy(p, {
get(target, prop) {
console.log('get');
return target[prop]
},
//修改、追加
set(target, prop, value) {
console.log('set');
target[prop] = value
},
deleteProperty(target, prop) {
console.log('delete');
return delete target[prop]
},
});
vue3.0的响应式
-
v3 底层 window.Reflect 反射 (set get deleteProperty)
o ECMA ==== >Reflect
let p = {
name: 'cy',
age: '18'
}
const p_ = new Proxy(p, {
get(target, prop) {
console.log('get');
return Reflect.get(target, prop)
},
//修改、追加
set(target, prop, value) {
console.log('set');
return Reflect.set(target, prop)
},
deleteProperty(target, prop) {
console.log('delete');
return Reflect.deleteProperty(target, prop)
},
});
7.Computend 计算属性
-
computed 函数
o与Vue2.x中computed配置功能一致
o
<template>
<input type="text" v-model="person.name" />
<input type="text" v-model="person.age" />
<h2>计算属性:{{ fullName }}</h2>
</template>
<script>
import { reactive, computed } from "vue";
export default {
setup() {
const person = reactive({
name: "cy",
age: "18",
});
//简写
let fullName = computed(() => {
return person.name + "-" + person.age;
});
//完整
// let fullName = computed({
// get() {
// return person.firstName + "-" + person.lastName;
// },
// set(value) {
// const nameArr = value.split("-");
// person.firstName = nameArr[0];
// person.lastName = nameArr[1];
// },
// });
return { person, fullName };
},
};
</script>
8.Watch 监听
-
参数
o ref 一个 {sum}
//监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
},{immediate:true})
//用ref定义了一个对象
watch(person.value,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
})
//ref 监听对象
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{deep: true})
o ref 多个 {[sum,sum1]}
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
})
o reactive 全部 {person}
§若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
§若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
o reactive 单个 {()=>{person.name}}
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
o reactive 多个个 {[()=>{person.name},()=>{person.age}]}
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
o 特殊 深层obj 开启deep监听
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true})
-
目前存在问题
o监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep失效)
o监视reactive定义的响应式数据中某个属性时:deep配置有效。
9.watchEffect函数
-
不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
-
类似computed属性
watchEffect(() => {
console.log("watchEffect");
console.log(p.test);
});
10. watchEffect 与 Computend
-
都是依赖性
-
watchEffect注重过程(函数体),不用写返回值。
-
computed注重计算出来的值(返回值),必须需要返回值
11.生命周期
-
Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
o beforeDestroy改名为 beforeUnmount
o destroyed改名为 unmounted
-
Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
obeforeCreate===>setup()
ocreated=======>setup()
obeforeMount ===>onBeforeMount
omounted=======>onMounted
obeforeUpdate===>onBeforeUpdate
oupdated =======>onUpdated
obeforeUnmount ==>onBeforeUnmount
ounmounted =====>onUnmounted
-
⭐ 组合式API的钩子比配置项早
12.Hook函数
-
hook 是个函数
-
本质一个函数 把setup中的组合api拆分出去
-
类似Vue2.0中的mixin
import { reactive, onMounted, onBeforeUnmount } from "vue";
export default function() {
//实现鼠标“打点”相关的数据
let point = reactive({
x: 0,
y: 0,
});
//实现鼠标“打点”相关的方法
function savePoint(event) {
point.x = event.pageX;
point.y = event.pageY;
console.log(event.pageX, event.pageY);
}
//实现鼠标“打点”相关的生命周期钩子
onMounted(() => {
window.addEventListener("click", savePoint);
});
onBeforeUnmount(() => {
window.removeEventListener("click", savePoint);
});
return point;
}
<template>
<h2>我是HelloWorld组件</h2>
<h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
</template>
<script>
//引入Hook 函数
import usePoint from '../hooks/usePoint'
export default {
name:'HelloWorld',
setup(){
const point = usePoint()
return {point}
}
}
</script>
13.toRef / toRefs
-
创建一个 ref 对象,其value值指向另一个对象中的某个属性。
-
const name = toRef(person,'name')
-
要将响应式对象中的某个属性单独提供给外部使用时。
-
toRefs与toRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
优化一下我们一开始的使用
<template>
<input type="text" v-model="name" />
<input type="text" v-model="age" />
<p></p>
<br />
<span v-for="item in arr" :key="item">{{ item }}</span>
<p>{{ obj.a }}</p>
<p>{{ obj.b.b1 }}</p>
</template>
<script>
import { reactive, toRefs, toRef } from "vue";
export default {
setup() {
const person = reactive({
name: "cy",
age: "18",
arr: ["?", "?", "?", "?"],
obj: {
a: "obj.a",
b: {
b1: "obj.b.b1 ",
},
},
});
//return {
//person,
// name:toRef(person,'name') //引用一个 维持通话
// ...
//}
//更优美的使用数据
return { person, ...toRefs(person) };
},
};
</script>
通过log可以看出来 toRefs 把 对象objectRefTmpl对象
基本类型ref对象、对象类型 proxy代理。
14.全局API变动
15.v-if 与 v-for 的优先级对比
-
2.x 版本中在一个元素上同时使用 v-if 和 v-for 时,v-for 会优先作用。
-
3.x 版本中 v-if 总是优先于 v-for 生效。
八、新的组件
1.Fragment
-
Vue2中: 组件必须有一个根标签
-
Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
-
好处: 减少标签层级, 减小内存占用
2.Teleport
<teleport to="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>
3.Suspense
-
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
-
使用步骤:
o异步引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
-
使用Suspense包裹组件,并配置好default与 fallback
odefault:就是组件要显示的内容
ofallback:就是组件没加载完全的“备胎”
<div class="app">
<h3>父组件</h3>
<Suspense>
<template v-slot:default>
<Child />
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
九、被移除的 API
-
keyCode作为v-on修饰符的支持
-
$on、$off 和 $once 实例方法
-
过滤器 (filter)
-
内联模板 attribute
-
$children实例 property
-
propsData选项
-
$destroy 实例方法。用户不应再手动管理单个 Vue 组件的生命周期。
-
全局函数 set 和 delete 以及实例方法 $set 和 $delete。基于代理的变化检测已经不再需要它们了。
参考
-
Vue3官方文档 https://v3.cn.vuejs.org/
-
尚硅谷Vue3视频 https://www.bilibili.com/video/BV1Zy4y1K7SH
-
掘金 https://juejin.cn/post/7005140118960865317/#heading-40
文章评论