一、简介
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 /cli 5.0.6npm install -g /cli ( cli版本安装or升级 )// 创建工程vue create v3_demo// 选择 vue 3 presetcd v3_demonpm 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.0export 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.0export 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


文章评论