1、vue3完全支持typescript,提高项目的可维护性
2、Fragment:模板更简单。
- vue3用到了一个虚拟的父级,可以多个根节点dom
- vue2只能有一个父级节点
3、创建Vue实例时,vue2用 new Vue(); vue3用createApp();
-
vue2是通过
new Vue()
,构造函数的方式来创建实例Vue2的组件系统设计中,所有Vue实例是共享一个Vue构造函数对象的,包括全局指令/全局组件,无法做到相互隔离。
也就是说我们整个项目中,只有一个根Vue实例,其他的单文件组件创建的 Vue实例都会成为它的子实例。
import Vue from 'Vue' import APP from './APP.vue' const vm = new Vue({ render:h => h(APP) }) vm.$mount('#app')
-
vue3通过
createApp
工厂函数来创建实例而Vue3通过createApp方法可以返回一个提供应用上下文的应用实例。不同实例注册的组件无法在不同的实例下使用。Vue3更适合在大型的开发环境中使用,不同开发人员可以完全独立地使用不同的实例。
import {createApp} from 'vue' import APP from './APP.vue' const app = createApp(APP) app.mount('#app')
4、全局挂载
-
在vue2项目中,在入口文件main.js中通过Vue.prototype挂载全局方法对象。
import Vue from 'vue' import Axios from 'axios import utils from '@/utils' import qs from 'qs' ... //挂载全局对象 Vue.prototype.$http = Axios; Vue.prototype.$qs = qs ....
-
vue3中取消了Vue.prototype,需要使用官方提供的globalPropertiesAPI在全局挂载方法和属性。
import { createApp } from 'vue' import App from './App.vue' import router from './router/index' import axios from './utils/axios' import qs from 'qs' const app = createApp(App) //全局挂载 app.config.globalProperties.$axios = axios; app.config.globalProperties.$qs = qs; ...
-
使用全局
-
vue2中使用
this.$http , this.$qs <script> export default { data() { return { dataList: [] } }, mounted() { this.getList() }, methods: { getList() { this.$http({ url: 'url地址' }).then(res=>{ let { data } = res.data this.dataList = data }) }, }, } </script>
-
vue3中使用, 在vue3的setup中是使用getCurrentInstanceAPI获取全局对象
... setup(){ const currentInstance = getCurrentInstance() const {$axios,$route} = currentInstance.appContext.config.globalProperties function submit(){ $axios.post('/adminUser/login',{ userName:state.ruleForm.username || '', passwordMd5:md5(state.ruleForm.password) }).then(res=>{ console.log(res) }) } }
5、双向绑定及原理
-
把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新。在单向绑定的基础上,用户更新了View,Model的数据也自动被更新了,这种情况就是双向绑定
-
vue2.x采用的是object.defineProperty
-
1、vue2.x的双向数据绑定是通过数据劫持结合发布者订阅者模式的方式来实现的,通过object.defineProperty来劫持各个属性的setter,getter,在数据变化时发布消息给订阅者,触发相应的监听回调来渲染视图。也就是说数据和视图同步,数据发生变化,视图变化,视图变化,数据也随之发生变化
-
2、Obejct.defineproperty(obj,prop,descriptor)方法,接收三个参数,分别是obj(定义其上属性的对象),prop(定义或者修改的属性),description(具体的改动方法),就是用这个方法来定义一个值,当使用时我们调用他里面的get方法,当我们给这个属性赋值时,用到他的set方法。
-
具体步骤
第一步: 需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给对象的某个值赋值,就会触发setter,那么就可以监听到数据的变化。 第二步: complie解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令的界定绑定到更新函数,添加监听数据的订阅者,一旦数据发生变动,收到通知,更新视图。 第三步: Watcher订阅者是Obejct和compile之间的通信桥梁,主要做的事情就是: 1、在自身实例化的往属性订阅器dep里面添加自己。 2、自身必须有个update方法。 3、待属性变动dep.notice通知时,能调用自身的update方法,并触发compile中绑定的回调。 第四步: MVVM作为数据绑定的入口,整合Observe、Compile和Watcher三者,通过Observe来监听自己的model数据变化,通过compile来解析编译模板指令,最终利用watcher搭起observe和Compile之间的通信桥梁,到达数据变化视图更新,视图变化数据更新的效果。
-
vue3 中使用了 es6 的 ProxyAPI 对数据代理。
-
proxy:对象用户定义基本操作的自定义行为(比如:属性的查找,复制,函数调用等等)。proxy就像一个拦截器一样,他可以在读取对象的属性,修改对象的属性,获取对象属性列表。他可以阻止对象上的默认行为,然后我们自己去定义这些行为。比如我们通过拦截默认的set方法,在自己定一个set方法添加回调函数。
-
proxy的语法
-
target: 要兼容的对象,可以是一个对象,数组,函数等等 handler: 是一个对象,里面包含了可以监听这个对象的行为函数,比如里面的
get
与set
const proxy = new Proxy(target,handler) -
1、handler.get
当通过proxy去读取对象里面的属性的时候,会进入到get钩子函数里面
2、handler.set
当通过proxy去为对象设置修改属性的时候,会进入到set钩子函数里面
3、handler.deleteProperty当使用delete去删除对象里面的属性的时候,会进入deleteProperty`钩子函数
4、handler.apply
当proxy监听的是一个函数的时候,当调用这个函数时,会进入apply钩子函数
5、handle.ownKeys
当通过Object.getOwnPropertyNames, Object.getownPropertySymbols, Object.keys, Reflect.ownKeys去获取对象的信息的时候,就会进入ownKeys这个钩子函数
6、handler.construct
当使用new操作符的时候,会进入construct这个钩子函数
详细介绍:
get 当通过 proxy 去读取对象里面的属性的时候,会进入到get钩子函数里面 target: 目标对象,即通过proxy代理的对象 key: 要访问的属性名称 receiver: receiver相当于是我们要读取的属性的this,一般情况下他就是proxy对象本身,关于receiver的作用,后文将具体讲解 handle.get(target,key, receiver) set 当为对象里面的属性赋值的时候,会触发set target: 目标对象,即通过proxy代理的对象 key: 要赋值的属性名称 value: 目标属性要赋的新值 receiver: 与 get的receiver 基本一致 handle.set(target,key,value, receiver)
6、 Vue2选项式API和Vue3组合式API的区别
-
Vue2使用选项类型API(Options API)对比Vue3合成型API(Composition API)
-
响应式处理
Vue2采用的是Object.defineProperty进行响应式处理,通过劫持数据的getter和setter来监听数据变化。Vue2的响应式处理有很多局限性,例如添加和删除属性,数组下标变更等都无法有效监听。Vue3采用的是ES6的Proxy来处理响应式,不仅支持上述的所有情况,而且有更好的性能表现。在Vue3中,每个数据都使用Proxy进行了代理拦截,这使得Vue3的响应式处理更加方便、灵活和高效。
-
组件生命周期
Vue2的生命周期主要分为以下几个阶段: beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeDestroy, destroyed。Vue3的生命周期相比较Vue2而言,更加易于使用,分为三个阶段:setup, created, mounted。 其中setup阶段被引入作为原来Vue2中的beforeCreate、created阶段的替代品,它负责组件的初始化和响应式数据的创建。
vue2 --------------------- vue3
beforeCreate -> setup() created -> setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted activated -> onActivated deactivated -> onDeactivated
-
组件通讯
Vue2中组件通讯主要通过 e m i t 和 emit和 emit和on进行实现。 e m i t 用于子组件向父组件传递数据,而 emit用于子组件向父组件传递数据,而emit用于子组件向父组件传递数据,而on则用于父组件接收子组件传递过来的数据。Vue3中通过provide和inject来实现组件通讯,但是与Vue2不同的是,它是基于新的响应式API来实现的,它们之间可以多层嵌套传递数据,且避免了命名冲突。
-
其他差异
编译器的变化
Vue2中的编译器是一个单独的项目,它将template转化成render函数进行渲染。而在Vue3中,编译器被合并到了核心代码库中,便于维护。TypeScript
Vue3完全支持TypeScript,这意味着开发者可以更加轻松、安全地编写Vue的组件。静态类型提升
Vue3支持静态类型,将在编译期间进行类型检查,从而提高代码的健壮性和效率。
7、建立数据 data
-
Vue2 - 这里把数据放入data属性中
export default { data() { return { //声明数据变量 } }, methods: { } };
-
Vue3 setup()方法里。
(1)使用reactive 代替Object,Array,Map,Set
(2)使用ref 代替String,Number,Boolean
import {reactive,ref} from ‘vue’; export default { setup(){ const state = reactive({count:0}); const num = ref(0); } }
8、父子传参不同
-
在vue2中,子组件向父组件传值通过this.$emit的形式实现
-
父组件
<!-- 父组件 --> <template> <div> <children :title="title" @getChildren="getChildren"></children> <div>子组件说:{{childrenAsk }}</div> </div> </template> <script> import children from "./children.vue" export default{ data(){ return { title:"我是父组件传过来的值", childrenAsk: "" } }, methods:{ getChildren(val) { this.childrenAsk = val } } } </script>
-
子组件
<!-- 子组件 --> <template> <div> <div>父组件传过来的值: {{ title }}</div> <button @click="askToFather">点击发送给父组件</button> </div> </template> <script> export default { props: { title: { type: String } }, data() { return { askMsg: "这是我给父组件说的话" } }, methods: { askToFather() { this.$emit("getChildren", this.askMsg) } } } </script>
-
vue3中,是不存在this的,vue3中将数据和函数都封装在了setup中,
-
vue3中的setup接收两个参数,第一个参数是props,即父组件向子组件传递的props值,第二个值为context,这个值代表了当前的上下文对象,知道这个东西以后现在来实现vue3的父子组件传值
vue3中父传子和vue2中的父传子一样,再次不做过多阐述,下面重点关注的是vue3的子传父 -
父组件
<template> <div style="color: aqua">父组件</div> <div>子组件对我说:{{ children_msg }}</div> <children :title="msg" @listen="listenToChildren"></children> {{ value }} </template> <script lang="ts"> import children from "@/views/component_emit/children.vue" import { ref } from "vue" export default ({ components: { children, }, name: "father", setup() { let msg = "我是父组件" let children_msg = ref("") // ref的作用是实现响应式, 如果没有ref则不能实现响应式(引用数据类型用reactive) let listenToChildren = (val) => { children_msg.value = val // 使用ref包裹的数据,需要通过.value的形式访问他的值 } return { msg, children_msg, listenToChildren, } }, }) </script> <style></style>
-
子组件
<template> <div style="color: brown">子组件</div> <!-- 父传子使用方法和vue2相同 --> <div>父组件传过来的值为:{{ title }}</div> <button @click="sayToFather">向父组件说话</button> </template> <script lang="ts"> import { defineComponent } from "vue" export default defineComponent({ name: "children", props: { title: { type: String, }, }, setup(props, context) { // context作用是获取上下文对象, // 如果setup写法为setup(props, { emit })的方式的话,下面的context可以省略 const sayToFather = () => { const ask = "我是子组件,我对父组件说话" context.emit("listen", ask) } return { sayToFather, } }, }) </script> <style></style>
9、vue3多了Teleport组件(瞬移组件) 和Suspense组件 (异步组件)
-
Vue.js 3.0
的新功能之一是Teleport
。作用是将组件的一部分渲染到 DOM 树中的一个不同位置,而不是作为组件的直接子节点渲染。<teleport to="body"> <div class="modal"> <!-- 具体内容 --> </div> </teleport>
'body'
是要传送组件的目标元素的选择器。你可以用任何有效的CSS
选择器替换它。 -
Suspense 是
Vue 3
中的一个新特性,它能够处理组件中的异步依赖并在等待过程中提供备用内容。 -
Suspense
组件有两个插槽:default
插槽和fallback
插槽。default
插槽用于放置那些可能含有异步依赖的组件,而fallback
插槽则用于展示加载状态。<template> <Suspense> <template v-slot:default> <Child /> </template> <template v-slot:fallback> <div>Loading...</div> </template> </Suspense> </template> <script> // import Child from './components/Child'//静态引入 import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child')) //异步引入 export default { name:'App', components:{Child}, } </script>
-
在以上代码中,Child 是一个异步组件。当这个组件正在加载时,Suspense 会自动切换到fallback插槽,并显示 “Loading…”。当组件加载完成后,Suspense 则会切换回default插槽,显示加载完成的组件。
-
Suspense 能让我们更方便地控制异步组件的加载状态,让代码看起来更加清晰,使我们能够将更多的精力集中在处理业务逻辑上。
10、v-if和v-for的优先级
- 在vue2中, 当v-if和v-for同时使用时,v-for的优先级高于v-if(因此我们通常需要计算属性先对数据进行加工处理,以达到性能优化的目的)
- 在vue3中,v-if的优先级高于v-for。
- 在vue中,永远不要把v-if和v-for同时用在同一个元素上,会带来性能方面的浪费(每次渲染都会先循环再进行条件判断);想要避免出现这种情况,可在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环。
11、.sync修饰符
-
vue2中 .sync 修饰符的作用就是实现父子组件数据的双向绑定,简化功能逻辑代码 。当然,v-model 也是可以实现数据的双向绑定,但是,一个组件只有一个 v-model,所以 , v-model 只能针对一个变量进行数据绑定,而 .sync 修饰符可以实现多个参数的数据双向绑定。
-
在对一个 prop 进行“双向绑定,单向修改”的场景下,因为子组件不能直接修改父组件,.sync 在2.3版本引入,作为一个事件绑定语法糖,利用EventBus,当子组件触发事件时,父组件会响应事件并实现数据更新,避免了子组件直接修改父组件传过来的内容。
-
在父组件中传入数据到子组件中后,然后在子组件中处理了后,需要改变父组件数组的时候,我们一般会使用
$emit
传递一个事件更新给父组件,父组件再对原来传入的数据进行更新修改。// 父组件 <template> <div id="app"> <button @click="openModal">打开模态框</button> <!-- 以下三种方法都可,我一般用的第一种 --> <!-- 使用方法事件处理器 --> <MyModal :isShow="isShow" @closeModal="closeModal" /> <!-- 使用$event变量 --> <MyModal :isShow="isShow" @closeModal="isShow = $event" /> <!-- 使用内联事件处理器 --> <MyModal :isShow="isShow" @closeModal="(val) => isShow = val" /> </div> </template> <script> import MyModal from './components/MyModal.vue' export default { name: 'App', components: { MyModal }, data() { return { isShow: false } }, methods: { openModal() { this.isShow = true }, closeModal(val) { this.isShow = val } } } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style> // 模态框组件 <template> <div v-if="isShow"> <h1>我是模态框</h1> <button @click="closeModal">关闭模态框</button> </div> </template> <script> export default { props: ['isShow'], methods: { closeModal() { this.$emit('closeModal', false) } } } </script>
-
.sync对上述代码优化,将父组件 :isShow=“isShow” 加上 .sync ,变成 :isShow.sync=“isShow”,.sync 如果用一句话来说就是同步更新了子组件的数据变化,而从实现的角度来说就是 .sync 就是@update:isShow=“closeModal” 的语法糖,是一种简写的形式。
// 父组件 <template> <div id="app"> <button @click="openModal">打开模态框</button> <!-- 使用方法事件处理器 --> <!-- <MyModal :isShow="isShow" @closeModal="closeModal" /> --> <!-- 使用$event变量 --> <!-- <MyModal :isShow="isShow" @closeModal="isShow = $event" /> --> <!-- 使用内联事件处理器 --> <!-- <MyModal :isShow="isShow" @closeModal="(val) => isShow = val" /> --> <!-- <MyModal :isShow="isShow" @update:isShow="(val) => isShow = val" /> <MyModal :isShow="isShow" @update:isShow="isShow = $event" /> <MyModal :isShow="isShow" @update:isShow="closeModal" /> --> // 改的就这一句,其他都一样,模态框组件代码不用变 <MyModal :isShow.sync="isShow" /> </div> </template> ...
-
vue3中移除了
.sync
使用v-mode:xxx
代替了 -
注意不写v-model后的xx属性是不生效的,毕竟这并不是真正的v-model
// 父组件 <template> <!-- .sync修饰符在vue3.0版本已弃用,应该使用v-model:xxx --> <MyDemo v-model:msg="msg" /> </template> <script setup name="App"> import { ref } from 'vue' import MyDemo from './components/MyDemo' const msg = ref('hello') </script> <style> </style> // 子组件 <script setup> defineProps(['msg']) defineEmits(['update:msg']) </script> <template> <h1>{{ msg }}</h1> <button @click="emit('update:msg', '数据改变了')">changeMsg</button> </template> <style></style>
12、key在template和v-if上的使用
- vue2中:在使用v-if、vi-else、v-else-if时,为了保证dom节点渲染的正确性,通常需要在对应的节点添加不同的key,以确保vue在进行虚拟dom对比时是准确的;vue2中template在v-for循环时是不能设置key的,否则会产生警告(需要给子节点设置key)。
- vue3中:在使用v-if、vi-else、v-else-if时,不用提供唯一的key对dom节点进行区分,因为vue内部会自动生成唯一的key,如果你提供了key,那你就要保证它的唯一性;vue3中template在v-for循环时,key应该设置在template标签上
13、vue3 $listeners深层监听被移除
-
vue2中:使用 a t t r s 访问传递给组件的属性,使用 attrs访问传递给组件的属性,使用 attrs访问传递给组件的属性,使用listeners访问传递给组件的事件 ,在 Vue 2.x 中,我们可以通过
this.$attrs
和this.$listeners
向组件传递属性和监听器。再结合使用inheritAttrs: false
,甚至可以将这些属性和监听器绑定到其他元素上而不根元素:<template> <label> <input type="text" v-bind="$attrs" v-on="$listeners" /> </label> </template> <script> export default { inheritAttrs: false } </script>
-
vue3中: 在 Vue 3.x 的虚拟 DOM 中,事件监听器只是以
on
为前缀的属性。因此,监听器被归纳为$attrs
的一部分,从而移除了$listerners
。<template> <label> <input type="text" v-bind="$attrs" /> </label> </template> <script> export default { inheritAttrs: false } </script>
-
如果这个组件接收到一个
id
属性和一个v-on: close
监听器,$attrs
对象现在看起来如下:{ id: 'my-input', onClose: () => console.log('close Event triggered') }