vue2与vue3的区别

 vue2vue3双向数据绑定原理发生了改变

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()函数特性

  1. setup()函数接收两个参数props、context(包含attrs、slots、emit)
  2. setup函数是处于生命周期beforeCreated和created俩个钩子函数之前
  3. 执行setup时,组件实例尚未被创建(在setup()内部,this不会是该活跃实例得引用,即不指向vue实例,Vue为了避免我们错误得使用,直接将setup函数中得this修改成了undefined
  4. 与模板一起使用时,需要返回一个对象(在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用)
  5. 因为setup函数中,props是响应式得,当传入新的prop时,它将会被更新,所以不能使用es6解构,因为它会消除prop的响应性,如需解构prop,可以通过使用setup函数中得toRefs来完成此操作。
  6. 父传子,用props,子传父用事件 Emitting Events。在vue2中,会调用this$emit然后传入事件名和对象;在vue3中得setup()中得第二个参数content对象中就有emit,那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。父传子,props
  7. 在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-bindv-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>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勒布朗-前端

请多多支持,留点爱心早餐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值