vue2与vue3的区别
vue2和vue3双向数据绑定原理发生了改变
vue2的双向数据绑定是利用了es5 的一个API Object.definepropert() 对数据进行劫持 结合发布订阅模式来实现的。
vue3中使用了es6的proxyAPI对数据进行处理。
1. vue2和vue3双向数据绑定原理发生了改变
相比与vue2,使用proxy API 优势有:
defineProperty只能监听某个属性,不能对全对象进行监听;
可以省去for in 、闭包等内容来提升效率(直接绑定整个对象即可);
可以监听数组,不用再去单独的对数组做特异性操作,vue3可以检测到数组内部数据的变化。
2.Vue3支持碎片(Fragments)
就是说可以拥有多个跟节点。
vue2
<template>
<div class='form-element'>
<h2> {{ title }} </h2>
</div>
</template>
vue3
<template>
<div class='form-element'>
</div>
<h2> {{ title }} </h2>
</template>
3. Composition API
Vue2 与vue3 最大的区别是vue2使用选项类型api,对比vue3合成型api。
旧得选项型api在代码里分割了不同得属性:data,computed,methods等;
新得合成型api能让我们使用方法来分割,相比于旧的API使用属性来分组,
这样代码会更加简便和整洁。
vue2
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
},
methods: {
login () {
// 登陆方法
}
},
components:{
"buttonComponent":btnComponent
},
computed:{
fullName(){
return this.firstName+" "+this.lastName;
}
}
}
vue3
export default {
props: {
title: String
},
setup () {
const state = reactive({ //数据
username: '',
password: '',
lowerCaseUsername: computed(() => state.username.toLowerCase()) //计算属性
})
//方法
const login = () => {
// 登陆方法
}
return {
login,
state
}
}
}
4. 建立数据data
Vue2 - 这里把数据放入data属性中
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
}
}
vue2是把数据放入data中,vue3就需要使用一个新的setup()方法,此方法在组件初始化构造得时候触发。
使用一下三个步骤建立反应性数据:
1. 从vue引入reactive;
2.使用reactive() 方法来声明数据为响应性数据;
3. 使用setup()方法来返回我们得响应性数据,从而template可以获取这些响应性数据。
import { reactive } from 'vue'
export default {
props: {
title: String
},
setup () {
const state = reactive({
username: '',
password: ''
})
return { state }
}
}
template使用,可以通过state.username和state.password获得数据的值。
<template>
<div>
<h2> {{ state.username }} </h2>
<h2> {{ state.password}} </h2>
</div>
</template>
5. 生命周期
vue2 --------------- vue3
beforeCreate setup()
Created setup()
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroyed onBeforeUnmount
destroyed onUnmounted
activated onActivated
deactivated onDeactivated
setup() :开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method
onBeforeMount() : 组件挂载到节点上之前执行的函数。
onMounted() : 组件挂载完成后执行的函数。
onBeforeUpdate(): 组件更新之前执行的函数。
onUpdated(): 组件更新完成之后执行的函数。
onBeforeUnmount(): 组件卸载之前执行的函数。
onUnmounted(): 组件卸载完成后执行的函数
若组件被<keep-alive>包含,则多出下面两个钩子函数。
onActivated(): 被包含在中的组件,会多出两个生命周期钩子函数。被激活时执行 。
onDeactivated(): 比如从 A组件,切换到 B 组件,A 组件消失时执行。
6. 父子传参不同,setup()函数特性
- setup()函数接收两个参数:props、context(包含attrs、slots、emit)
- setup函数是处于生命周期beforeCreated和created俩个钩子函数之前
- 执行setup时,组件实例尚未被创建(在setup()内部,this不会是该活跃实例得引用,即不指向vue实例,Vue为了避免我们错误得使用,直接将setup函数中得this修改成了undefined)
- 与模板一起使用时,需要返回一个对象(在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用)
- 因为setup函数中,props是响应式得,当传入新的prop时,它将会被更新,所以不能使用es6解构,因为它会消除prop的响应性,如需解构prop,可以通过使用setup函数中得toRefs来完成此操作。
- 父传子,用props,子传父用事件 Emitting Events。在vue2中,会调用this$emit然后传入事件名和对象;在vue3中得setup()中得第二个参数content对象中就有emit,那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。父传子,props
- 在setup()内使用响应式数据时,需要通过 .value 获取
6.1、父传子
import { toRefs } from 'vue'
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
onMounted(() => {
console.log('title: ' + props.title)
})
}
6.2、子传父,事件 - Emitting Events
举例,现在我们想在点击提交按钮时触发一个login的事件。
在 Vue2 中我们会调用到this.$emit然后传入事件名和参数对象。
login () {
this.$emit('login', {
username: this.username,
password: this.password
})
}
在setup()中的第二个参数content对象中就有emit,这个是和this.$emit是一样的。那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。
然后我们在login方法中编写登陆事件
另外:context 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构
setup (props, { attrs, slots, emit }) {
// ...
const login = () => {
emit('login', {
username: state.username,
password: state.password
})
}
// ...
}
6.3、attrs和listeners
子组件使用$attrs可以获得父组件除了props传递的属性和特性绑定属性 (class和 style)之外的所有属性。子组件使用$listeners可以获得父组件(不含.native修饰器的)所有v-on事件监听器,在Vue3中已经不再使用;但是Vue3中的attrs不仅可以获得父组件传来的属性也可以获得父组件v-on事件监听器
Vue 2
我们可以使用$attrs
和$listeners
来传递父组件的属性和事件到子组件中。例如,我们有一个名为ChildComponent
的子组件,它的模板如下:
<template>
<div>
<h1>{{ title }}</h1>
<button @click="onClick">{{ buttonText }}</button>
</div>
</template>
我们可以在父组件中使用v-bind
和v-on
来传递属性和事件:
<template>
<div>
<ChildComponent v-bind="$attrs" v-on="$listeners" />
</div>
</template>
Vue 3
<template>
<div>
<h1>{{ title }}</h1>
<button @click="onClick">{{ buttonText }}</button>
</div>
</template>
$attrs
和$listeners
已经被移除了,如果需要使用它们,需要在子组件中手动声明它们。例如,在ChildComponent
中,我们可以使用以下方式声明$attrs
和$listeners
:
import { defineComponent } from 'vue'
export default defineComponent({
inheritAttrs: false,
props: {
title: String,
buttonText: String
},
methods: {
onClick() {
this.$emit('click')
}
},
render() {
return (
<div>
<h1>{this.title}</h1>
<button onClick={this.onClick}>{this.buttonText}</button>
{this.$slots.default}
</div>
)
}
})
inheritAttrs: false
表示不继承父组件的属性,需要手动声明$attrs
和$listeners
。在模板中,我们可以使用v-bind="$attrs"
和v-on="$listeners"
来传递属性和事件:
<template>
<div>
<ChildComponent title="Hello World" buttonText="Click Me" v-bind="$attrs" v-on="$listeners" />
</div>
</template>
6.4、 setup()内使用响应式数据时,需要通过.value获取
import { ref } from 'vue'
const count = ref(0)
console.log(count.value)复制
6.5、 从setup() 中返回得对象上得property 返回并可以在模板中被访问时,它将自动展开为内部值。不需要在模板中追加.value。
例如,如果您在setup()
中返回了一个名为person
的对象,并且该对象具有一个名为name
的属性,您可以在模板中使用{{ person.name }}
来访问该属性的值,而无需使用
{{ person.name.value }}
。
6.5、setup函数只能是同步的不能是异步的。
如果setup()
函数是异步的,则无法保证在开始运行应用程序之前完成初始化。如果您需要执行异步操作,请在setup()
函数之外执行它们
import { reactive } from 'vue'
export default {
setup() {
const state = reactive({
data: null
})
async function loadData() {
// 从服务器加载数据的异步操作
const response = await fetch('/data')
const data = await response.json()
// 将数据存储在组件状态中
state.data = data
}
loadData()
return {
state
}
}
}
或者
import { ref, onMounted } from 'vue'
export default {
setup() {
const data = ref(null)
onMounted(async () => {
const response = await fetch('https://api.example.com/data')
data.value = await response.json()
})
return {
data
}
}
}
我们在组件的 created 函数中使用异步函数来获取数据,并将数据保存到组件的状态中。然后,在 setup 函数中使用这个状态来显示数据。
注意,我们使用了 onMounted 函数来在组件挂载后执行异步操作。这是因为在组件挂载之前,组件的状态还没有被设置,无法使用组件的状态来保存异步操作的结果。
在 setup 函数中,我们可以使用异步函数,例如:
import { ref } from 'vue'
export default {
setup() {
const message = ref('Hello, world!')
setTimeout(() => {
message.value = 'Hello, Vue!'
}, 1000)
return {
message
}
}
}
虽然 setup 函数本身是同步的,但是我们可以在其中使用异步函数来进行一些异步操作。只要这些操作在组件实例创建之前就已经完成,就不会影响组件的正常运行。
7.vue3 Teleport瞬移组件
Teleport一般被翻译成瞬间移动组件,我把他理解成"独立组件", 他可以拿你写的组件挂载到任何你想挂载的DOM上,所以是很自由很独立的,
以一个例子来看: 编写一个弹窗组件
<template>
<teleport to="#modal">
<div id="center" v-if="isOpen">
<h2><slot>this is a modal</slot></h2>
<button @click="buttonClick">Close</button>
</div>
</teleport>
</template>
<script lang="ts">
export default {
props: {
isOpen: Boolean,
},
emits: {
'close-modal': null
},
setup(props, context) {
const buttonClick = () => {
context.emit('close-modal')
}
return {
buttonClick
}
}
}
</script>
<style>
#center {
width: 200px;
height: 200px;
border: 2px solid black;
background: white;
position: fixed;
left: 50%;
top: 50%;
margin-left: -100px;
margin-top: -100px;
}
</style>
在app.vue中使用的时候跟普通组件调用是一样的
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<HooksDemo></HooksDemo>
<button @click="openModal">Open Modal</button><br/>
<modal :isOpen="modalIsOpen" @close-modal="onModalClose"> My Modal !!!!</modal>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import HooksDemo from './components/HooksDemo.vue'
import Modal from './components/Modal.vue'
import{ref} from 'vue'
export default {
name: 'App',
components: {
HelloWorld,
HooksDemo,
Modal
},
setup() {
const modalIsOpen = ref(false)
const openModal = () => {
modalIsOpen.value = true
}
const onModalClose = () => {
modalIsOpen.value = false
}
return {
modalIsOpen,
openModal,
onModalClose
}
}
}
</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>
8、路由
vue3和vue2路由常用功能只是写法上有些区别:
vue3的beforeRouteEnter
作为路由守卫的示例是因为它在setup
语法糖中是无法使用的;大家都知道setup
中组件实例已经创建,是能够获取到组件实例的。而beforeRouteEnter
是再进入路由前触发的,此时组件还未创建,所以是无法用在setup
中的;如果想在setup语法糖中使用则需要再写一个script
如下:
<script>
export default {
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
next()
},
};
</script>
vue2
<script>
export default {
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
next()
},
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
next()
},
beforeRouteLeave ((to, from, next)=>{//离开当前的组件,触发
next()
}),
beforeRouteLeave((to, from, next)=>{//离开当前的组件,触发
next()
}),
methods:{
toPage(){
//路由跳转
this.$router.push(xxx)
}
},
created(){
//获取params
this.$route.params
//获取query
this.$route.query
}
}
</script>
vue3 路由写法
<script>
import { defineComponent } from 'vue'
import { useRoute, useRouter } from 'vue-router'
export default defineComponent({
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
next()
},
beforeRouteLeave ((to, from, next)=>{//离开当前的组件,触发
next()
}),
beforeRouteLeave((to, from, next)=>{//离开当前的组件,触发
next()
}),
setup() {
const router = useRouter()
const route = useRoute()
const toPage = () => {
router.push(xxx)
}
//获取params 注意是route
route.params
//获取query
route.query
return {
toPage
}
},
});
</script>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~