Vue3快速上手1.Vue3简介2020年9月18日，Vue.js发布3.0版本，代号：One Piece（海贼王）耗时2年多、2600+次提交、30+个RFC、600+次PR、99位贡献者github上的tags地址：https://github.com/vuejs/vue-next/releases/tag/v3.0.02.Vue3带来了什么1.性能的提升打包大小减少41%初次渲染快55%, 更新渲染快133%内存减少54%......2.源码的升级使用Proxy代替defineProperty实现响应式重写虚拟DOM的实现和Tree-Shaking......3.拥抱TypeScriptVue3可以更好的支持TypeScript4.新的特性Composition API（组合API）setup配置ref与reactivewatch与watchEffectprovide与inject......新的内置组件FragmentTeleportSuspense其他改变新的生命周期钩子data 选项应始终被声明为一个函数移除keyCode支持作为 v-on 的修饰符......一、创建Vue3.0工程1.使用 vue-cli 创建官方文档：https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create## 查看@vue/cli版本，确保@vue/cli版本在4.5.0以上 vue --version ## 安装或者升级你的@vue/cli npm install -g @vue/cli ## 创建 vue create vue_test ## 启动 cd vue_test npm run serve2.使用 vite 创建官方文档：https://v3.cn.vuejs.org/guide/installation.html#vitevite官网：https://vitejs.cn什么是vite？—— 新一代前端构建工具。优势如下：开发环境中，无需打包操作，可快速的冷启动。轻量快速的热重载（HMR）。真正的按需编译，不再等待整个应用编译完成。传统构建 与 vite构建对比图## 创建工程 npm init vite-app <project-name> ## 进入工程目录 cd <project-name> ## 安装依赖 npm install ## 运行 npm run dev二、常用 Composition API官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html1.拉开序幕的setup理解：Vue3.0中一个新的配置项，值为一个函数。setup是所有Composition API（组合API）“ 表演的舞台 ”。组件中所用到的：数据、方法等等，均要配置在setup中。setup函数的两种返回值：若返回一个对象，则对象中的属性、方法, 在模板中均可以直接使用。（重点关注！）若返回一个渲染函数：则可以自定义渲染内容。（了解）注意点：尽量不要与Vue2.x配置混用Vue2.x配置（data、methos、computed...）中可以访问到setup中的属性、方法。但在setup中不能访问到Vue2.x配置（data、methos、computed...）。如果有重名, setup优先。setup不能是一个async函数，因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。（后期也可以返回一个Promise实例，但需要Suspense和异步组件的配合）2.ref函数作用: 定义一个响应式的数据语法: const xxx = ref(initValue)创建一个包含响应式数据的引用对象（reference对象，简称ref对象）。JS中操作数据： xxx.value模板中读取数据: 不需要.value，直接： {{xxx}} 备注：接收的数据可以是：基本类型、也可以是对象类型。基本类型的数据：响应式依然是靠Object.defineProperty()的get与set完成的。对象类型的数据：内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。3.reactive函数作用: 定义一个对象类型的响应式数据（基本类型不要用它，要用ref函数）语法：const 代理对象= reactive(源对象)接收一个对象（或数组），返回一个代理对象（Proxy的实例对象，简称proxy对象）reactive定义的响应式数据是“深层次的”。内部基于 ES6 的 Proxy 实现，通过代理对象操作源对象内部数据进行操作。4.Vue3.0中的响应式原理vue2.x的响应式实现原理：对象类型：通过Object.defineProperty()对属性的读取、修改进行拦截（数据劫持）。数组类型：通过重写更新数组的一系列方法来实现拦截。（对数组的变更方法进行了包裹）。Object.defineProperty(data, 'count', { get () {}, set () {} })存在问题：新增属性、删除属性, 界面不会更新。直接通过下标修改数组, 界面不会自动更新。Vue3.0的响应式实现原理:通过Proxy（代理）: 拦截对象中任意属性的变化, 包括：属性值的读写、属性的添加、属性的删除等。通过Reflect（反射）: 对源对象的属性进行操作。MDN文档中描述的Proxy与Reflect：Proxy：https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ProxyReflect：https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflectnew Proxy(data, { // 拦截读取属性值 get (target, prop) { return Reflect.get(target, prop) }, // 拦截设置属性值或添加新属性 set (target, prop, value) { return Reflect.set(target, prop, value) }, // 拦截删除属性 deleteProperty (target, prop) { return Reflect.deleteProperty(target, prop) } }) proxy.name = 'tom' 5.reactive对比ref从定义数据角度对比：ref用来定义：基本类型数据。reactive用来定义：对象（或数组）类型数据。备注：ref也可以用来定义对象（或数组）类型数据, 它内部会自动通过reactive转为代理对象。从原理角度对比：ref通过Object.defineProperty()的get与set来实现响应式（数据劫持）。reactive通过使用Proxy来实现响应式（数据劫持）, 并通过Reflect操作源对象内部的数据。从使用角度对比：ref定义的数据：操作数据需要.value，读取数据时模板中直接读取不需要.value。reactive定义的数据：操作数据与读取数据：均不需要.value。6.setup的两个注意点setup执行的时机在beforeCreate之前执行一次，this是undefined。setup的参数props：值为对象，包含：组件外部传递过来，且组件内部声明接收了的属性。context：上下文对象attrs: 值为对象，包含：组件外部传递过来，但没有在props配置中声明的属性, 相当于 this.$attrs。slots: 收到的插槽内容, 相当于 this.$slots。emit: 分发自定义事件的函数, 相当于 this.$emit。7.计算属性与监视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配置的回调执行了') })8.生命周期vue2.x的生命周期vue3.0的生命周期1Vue3.0中可以继续使用Vue2.x中的生命周期钩子，但有有两个被更名：beforeDestroy改名为 beforeUnmountdestroyed改名为 unmountedVue3.0也提供了 Composition API 形式的生命周期钩子，与Vue2.x中钩子对应关系如下：beforeCreate===>setup()created=======>setup()beforeMount ===>onBeforeMountmounted=======>onMountedbeforeUpdate===>onBeforeUpdateupdated =======>onUpdatedbeforeUnmount ==>onBeforeUnmountunmounted =====>onUnmounted9.自定义hook函数什么是hook？—— 本质是一个函数，把setup函数中使用的Composition API进行了封装。类似于vue2.x中的mixin。自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。10.toRef作用：创建一个 ref 对象，其value值指向另一个对象中的某个属性。语法：const name = toRef(person,'name')应用: 要将响应式对象中的某个属性单独提供给外部使用时。扩展：toRefs 与toRef功能一致，但可以批量创建多个 ref 对象，语法：toRefs(person)三、其它 Composition API1.shallowReactive 与 shallowRefshallowReactive：只处理对象最外层属性的响应式（浅响应式）。shallowRef：只处理基本数据类型的响应式, 不进行对象的响应式处理。什么时候使用?如果有一个对象数据，结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。如果有一个对象数据，后续功能不会修改该对象中的属性，而是生新的对象来替换 ===> shallowRef。2.readonly 与 shallowReadonlyreadonly: 让一个响应式数据变为只读的（深只读）。shallowReadonly：让一个响应式数据变为只读的（浅只读）。应用场景: 不希望数据被修改时。3.toRaw 与 markRawtoRaw：作用：将一个由reactive生成的响应式对象转为普通对象。使用场景：用于读取响应式对象对应的普通对象，对这个普通对象的所有操作，不会引起页面更新。markRaw：作用：标记一个对象，使其永远不会再成为响应式对象。应用场景:有些值不应被设置为响应式的，例如复杂的第三方类库等。当渲染具有不可变数据源的大列表时，跳过响应式转换可以提高性能。4.customRef作用：创建一个自定义的 ref，并对其依赖项跟踪和更新触发进行显式控制。实现防抖效果： {{keyword}} 5.provide 与 inject作用：实现祖与后代组件间通信套路：父组件有一个 provide 选项来提供数据，后代组件有一个 inject 选项来开始使用这些数据具体写法：祖组件中：setup(){ ...... let car = reactive({name:'奔驰',price:'40万'}) provide('car',car) ...... }后代组件中：setup(props,context){ ...... const car = inject('car') return {car} ...... }6.响应式数据的判断isRef: 检查一个值是否为一个 ref 对象isReactive: 检查一个对象是否是由 reactive 创建的响应式代理isReadonly: 检查一个对象是否是由 readonly 创建的只读代理isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理四、Composition API 的优势1.Options API 存在的问题使用传统OptionsAPI中，新增或者修改一个需求，就需要分别在data，methods，computed里修改 。 2.Composition API 的优势我们可以更加优雅的组织我们的代码，函数。让相关功能的代码更加有序的组织在一起。 五、新的组件1.Fragment在Vue2中: 组件必须有一个根标签在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中好处: 减少标签层级, 减小内存占用2.Teleport什么是Teleport？—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。 我是一个弹窗 关闭弹窗 3.Suspense等待异步组件时渲染一些额外内容，让应用有更好的用户体验使用步骤：异步引入组件import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue'))使用Suspense包裹组件，并配置好default 与 fallback 我是App组件 <Child/> 加载中..... 六、其他1.全局API的转移Vue 2.x 有许多全局 API 和配置。例如：注册全局组件、注册全局指令等。//注册全局组件 Vue.component('MyButton', { data: () => ({ count: 0 }), template: ' Clicked {{ count }} times. ' }) //注册全局指令 Vue.directive('focus', { inserted: el => el.focus() }Vue3.0中对这些API做出了调整：将全局的API，即：Vue.xxx调整到应用实例（app）上2.x 全局 API（Vue）3.x 实例 API (app)Vue.config.xxxxapp.config.xxxxVue.config.productionTip移除Vue.componentapp.componentVue.directiveapp.directiveVue.mixinapp.mixinVue.useapp.useVue.prototypeapp.config.globalProperties2.其他改变data选项应始终被声明为一个函数。过度类名的更改：Vue2.x写法.v-enter, .v-leave-to { opacity: 0; } .v-leave, .v-enter-to { opacity: 1; }Vue3.x写法.v-enter-from, .v-leave-to { opacity: 0; } .v-leave-from, .v-enter-to { opacity: 1; }移除keyCode作为 v-on 的修饰符，同时也不再支持config.keyCodes移除v-on.native修饰符父组件中绑定事件<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />子组件中声明自定义事件 移除过滤器（filter）过滤器虽然这看起来很方便，但它需要一个自定义语法，打破大括号内表达式是 “只是 JavaScript” 的假设，这不仅有学习成本，而且有实现成本！建议用方法调用或计算属性去替换过滤器。......