vue2与vue3的区别

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

vue2的双向数据绑定是利用了es5 的一个API Object.definepropert() 对数据进行劫持 结合发布订阅模式来实现的。

vue3中使用了es6的proxyAPI对数据进行处理。

1. vue2和vue3双向数据绑定原理发生了改变

  1. 相比与vue2,使用proxy API 优势有:
  2. defineProperty只能监听某个属性,不能对全对象进行监听;
  3. 可以省去for in 、闭包等内容来提升效率(直接绑定整个对象即可);
  4. 可以监听数组,不用再去单独的对数组做特异性操作,vue3可以检测到数组内部数据的变化。

2.Vue3支持碎片(Fragments)

就是说可以拥有多个跟节点。

vue2

  1. <template>
  2. <div class='form-element'>
  3. <h2> {{ title }} </h2>
  4. </div>
  5. </template>

vue3

  1. <template>
  2. <div class='form-element'>
  3. </div>
  4. <h2> {{ title }} </h2>
  5. </template>

3. Composition API

Vue2 与vue3 最大的区别是vue2使用选项类型api,对比vue3合成型api。

  1. 旧得选项型api在代码里分割了不同得属性:data,computed,methods等;
  2. 新得合成型api能让我们使用方法来分割,相比于旧的API使用属性来分组,
  3. 这样代码会更加简便和整洁。

vue2

  1. export default {
  2. props: {
  3. title: String
  4. },
  5. data () {
  6. return {
  7. username: '',
  8. password: ''
  9. }
  10. },
  11. methods: {
  12. login () {
  13. // 登陆方法
  14. }
  15. },
  16. components:{
  17. "buttonComponent":btnComponent
  18. },
  19. computed:{
  20. fullName(){
  21. return this.firstName+" "+this.lastName;
  22. }
  23. }
  24. }

vue3

  1. export default {
  2. props: {
  3. title: String
  4. },
  5. setup () {
  6. const state = reactive({ //数据
  7. username: '',
  8. password: '',
  9. lowerCaseUsername: computed(() => state.username.toLowerCase()) //计算属性
  10. })
  11. //方法
  12. const login = () => {
  13. // 登陆方法
  14. }
  15. return {
  16. login,
  17. state
  18. }
  19. }
  20. }

4. 建立数据data

Vue2 - 这里把数据放入data属性中

  1. export default {
  2. props: {
  3. title: String
  4. },
  5. data () {
  6. return {
  7. username: '',
  8. password: ''
  9. }
  10. }
  11. }
vue2是把数据放入data中,vue3就需要使用一个新的setup()方法,此方法在组件初始化构造得时候触发。
使用一下三个步骤建立反应性数据:
1. 从vue引入reactive;
2.使用reactive() 方法来声明数据为响应性数据;
3. 使用setup()方法来返回我们得响应性数据,从而template可以获取这些响应性数据。
  1. import { reactive } from 'vue'
  2. export default {
  3. props: {
  4. title: String
  5. },
  6. setup () {
  7. const state = reactive({
  8. username: '',
  9. password: ''
  10. })
  11. return { state }
  12. }
  13. }

template使用,可以通过state.username和state.password获得数据的值。

  1. <template>
  2. <div>
  3. <h2> {{ state.username }} </h2>
  4. <h2> {{ state.password}} </h2>
  5. </div>
  6. </template>

5. 生命周期

  1. vue2 --------------- vue3
  2. beforeCreate setup()
  3. Created setup()
  4. beforeMount onBeforeMount
  5. mounted onMounted
  6. beforeUpdate onBeforeUpdate
  7. updated onUpdated
  8. beforeDestroyed onBeforeUnmount
  9. destroyed onUnmounted
  10. activated onActivated
  11. deactivated onDeactivated
  1. setup() :开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method
  2. onBeforeMount() : 组件挂载到节点上之前执行的函数。
  3. onMounted() : 组件挂载完成后执行的函数。
  4. onBeforeUpdate(): 组件更新之前执行的函数。
  5. onUpdated(): 组件更新完成之后执行的函数。
  6. onBeforeUnmount(): 组件卸载之前执行的函数。
  7. onUnmounted(): 组件卸载完成后执行的函数
  8. 若组件被<keep-alive>包含,则多出下面两个钩子函数。
  9. onActivated(): 被包含在中的组件,会多出两个生命周期钩子函数。被激活时执行 。
  10. 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、父传子

  1. import { toRefs } from 'vue'
  2. setup(props) {
  3. const { title } = toRefs(props)
  4. console.log(title.value)
  5. onMounted(() => {
  6. console.log('title: ' + props.title)
  7. })
  8. }

 6.2、子传父,事件 - Emitting Events

举例,现在我们想在点击提交按钮时触发一个login的事件。

在 Vue2 中我们会调用到this.$emit然后传入事件名和参数对象。

  1. login () {
  2. this.$emit('login', {
  3. username: this.username,
  4. password: this.password
  5. })
  6. }

 在setup()中的第二个参数content对象中就有emit,这个是和this.$emit是一样的。那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。

然后我们在login方法中编写登陆事件
另外:context 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构

  1. setup (props, { attrs, slots, emit }) {
  2. // ...
  3. const login = () => {
  4. emit('login', {
  5. username: state.username,
  6. password: state.password
  7. })
  8. }
  9. // ...
  10. }

6.3、attrs和listeners

子组件使用$attrs可以获得父组件除了props传递的属性和特性绑定属性 (class和 style)之外的所有属性。子组件使用$listeners可以获得父组件(不含.native修饰器的)所有v-on事件监听器,在Vue3中已经不再使用;但是Vue3中的attrs不仅可以获得父组件传来的属性也可以获得父组件v-on事件监听器

 Vue 2

我们可以使用$attrs$listeners来传递父组件的属性和事件到子组件中。例如,我们有一个名为ChildComponent的子组件,它的模板如下:

  1. <template>
  2. <div>
  3. <h1>{{ title }}</h1>
  4. <button @click="onClick">{{ buttonText }}</button>
  5. </div>
  6. </template>

我们可以在父组件中使用v-bindv-on来传递属性和事件:

  1. <template>
  2. <div>
  3. <ChildComponent v-bind="$attrs" v-on="$listeners" />
  4. </div>
  5. </template>

Vue 3

  1. <template>
  2. <div>
  3. <h1>{{ title }}</h1>
  4. <button @click="onClick">{{ buttonText }}</button>
  5. </div>
  6. </template>

 $attrs$listeners已经被移除了,如果需要使用它们,需要在子组件中手动声明它们。例如,在ChildComponent中,我们可以使用以下方式声明$attrs$listeners

  1. import { defineComponent } from 'vue'
  2. export default defineComponent({
  3. inheritAttrs: false,
  4. props: {
  5. title: String,
  6. buttonText: String
  7. },
  8. methods: {
  9. onClick() {
  10. this.$emit('click')
  11. }
  12. },
  13. render() {
  14. return (
  15. <div>
  16. <h1>{this.title}</h1>
  17. <button onClick={this.onClick}>{this.buttonText}</button>
  18. {this.$slots.default}
  19. </div>
  20. )
  21. }
  22. })

inheritAttrs: false表示不继承父组件的属性,需要手动声明$attrs$listeners。在模板中,我们可以使用v-bind="$attrs"v-on="$listeners"来传递属性和事件: 

  1. <template>
  2. <div>
  3. <ChildComponent title="Hello World" buttonText="Click Me" v-bind="$attrs" v-on="$listeners" />
  4. </div>
  5. </template>

 6.4、 setup()内使用响应式数据时,需要通过.value获取

  1. import { ref } from 'vue'
  2. const count = ref(0)
  3. console.log(count.value)复制

6.5、 从setup() 中返回得对象上得property 返回并可以在模板中被访问时,它将自动展开为内部值。不需要在模板中追加.value。

例如,如果您在setup()中返回了一个名为person的对象,并且该对象具有一个名为name的属性,您可以在模板中使用{{ person.name }}来访问该属性的值,而无需使用

{{ person.name.value }}

6.5、setup函数只能是同步的不能是异步的。

如果setup()函数是异步的,则无法保证在开始运行应用程序之前完成初始化。如果您需要执行异步操作,请在setup()函数之外执行它们

  1. import { reactive } from 'vue'
  2. export default {
  3. setup() {
  4. const state = reactive({
  5. data: null
  6. })
  7. async function loadData() {
  8. // 从服务器加载数据的异步操作
  9. const response = await fetch('/data')
  10. const data = await response.json()
  11. // 将数据存储在组件状态中
  12. state.data = data
  13. }
  14. loadData()
  15. return {
  16. state
  17. }
  18. }
  19. }

或者 

  1. import { ref, onMounted } from 'vue'
  2. export default {
  3. setup() {
  4. const data = ref(null)
  5. onMounted(async () => {
  6. const response = await fetch('https://api.example.com/data')
  7. data.value = await response.json()
  8. })
  9. return {
  10. data
  11. }
  12. }
  13. }

我们在组件的 created 函数中使用异步函数来获取数据,并将数据保存到组件的状态中。然后,在 setup 函数中使用这个状态来显示数据。

注意,我们使用了 onMounted 函数来在组件挂载后执行异步操作。这是因为在组件挂载之前,组件的状态还没有被设置,无法使用组件的状态来保存异步操作的结果。

在 setup 函数中,我们可以使用异步函数,例如:

  1. import { ref } from 'vue'
  2. export default {
  3. setup() {
  4. const message = ref('Hello, world!')
  5. setTimeout(() => {
  6. message.value = 'Hello, Vue!'
  7. }, 1000)
  8. return {
  9. message
  10. }
  11. }
  12. }

 虽然 setup 函数本身是同步的,但是我们可以在其中使用异步函数来进行一些异步操作。只要这些操作在组件实例创建之前就已经完成,就不会影响组件的正常运行。

7.vue3 Teleport瞬移组件

Teleport一般被翻译成瞬间移动组件,我把他理解成"独立组件", 他可以拿你写的组件挂载到任何你想挂载的DOM上,所以是很自由很独立的,

以一个例子来看: 编写一个弹窗组件

  1. <template>
  2. <teleport to="#modal">
  3. <div id="center" v-if="isOpen">
  4. <h2><slot>this is a modal</slot></h2>
  5. <button @click="buttonClick">Close</button>
  6. </div>
  7. </teleport>
  8. </template>
  9. <script lang="ts">
  10. export default {
  11. props: {
  12. isOpen: Boolean,
  13. },
  14. emits: {
  15. 'close-modal': null
  16. },
  17. setup(props, context) {
  18. const buttonClick = () => {
  19. context.emit('close-modal')
  20. }
  21. return {
  22. buttonClick
  23. }
  24. }
  25. }
  26. </script>
  27. <style>
  28. #center {
  29. width: 200px;
  30. height: 200px;
  31. border: 2px solid black;
  32. background: white;
  33. position: fixed;
  34. left: 50%;
  35. top: 50%;
  36. margin-left: -100px;
  37. margin-top: -100px;
  38. }
  39. </style>

 在app.vue中使用的时候跟普通组件调用是一样的

  1. <template>
  2. <div id="app">
  3. <img alt="Vue logo" src="./assets/logo.png">
  4. <HelloWorld msg="Welcome to Your Vue.js App"/>
  5. <HooksDemo></HooksDemo>
  6. <button @click="openModal">Open Modal</button><br/>
  7. <modal :isOpen="modalIsOpen" @close-modal="onModalClose"> My Modal !!!!</modal>
  8. </div>
  9. </template>
  10. <script>
  11. import HelloWorld from './components/HelloWorld.vue'
  12. import HooksDemo from './components/HooksDemo.vue'
  13. import Modal from './components/Modal.vue'
  14. import{ref} from 'vue'
  15. export default {
  16. name: 'App',
  17. components: {
  18. HelloWorld,
  19. HooksDemo,
  20. Modal
  21. },
  22. setup() {
  23. const modalIsOpen = ref(false)
  24. const openModal = () => {
  25. modalIsOpen.value = true
  26. }
  27. const onModalClose = () => {
  28. modalIsOpen.value = false
  29. }
  30. return {
  31. modalIsOpen,
  32. openModal,
  33. onModalClose
  34. }
  35. }
  36. }
  37. </script>
  38. <style>
  39. #app {
  40. font-family: Avenir, Helvetica, Arial, sans-serif;
  41. -webkit-font-smoothing: antialiased;
  42. -moz-osx-font-smoothing: grayscale;
  43. text-align: center;
  44. color: #2c3e50;
  45. margin-top: 60px;
  46. }
  47. </style>

8、路由

vue3和vue2路由常用功能只是写法上有些区别:

vue3的beforeRouteEnter作为路由守卫的示例是因为它在setup语法糖中是无法使用的;大家都知道setup中组件实例已经创建,是能够获取到组件实例的。而beforeRouteEnter是再进入路由前触发的,此时组件还未创建,所以是无法用在setup中的;如果想在setup语法糖中使用则需要再写一个script 如下:

  1. <script>
  2. export default {
  3. beforeRouteEnter(to, from, next) {
  4. // 在渲染该组件的对应路由被 confirm 前调用
  5. next()
  6. },
  7. };
  8. </script>

vue2

  1. <script>
  2. export default {
  3. beforeRouteEnter (to, from, next) {
  4. // 在渲染该组件的对应路由被 confirm 前调用
  5. next()
  6. },
  7. beforeRouteEnter (to, from, next) {
  8. // 在渲染该组件的对应路由被 confirm 前调用
  9. next()
  10. },
  11. beforeRouteLeave ((to, from, next)=>{//离开当前的组件,触发
  12. next()
  13. }),
  14. beforeRouteLeave((to, from, next)=>{//离开当前的组件,触发
  15. next()
  16. }),
  17. methods:{
  18. toPage(){
  19. //路由跳转
  20. this.$router.push(xxx)
  21. }
  22. },
  23. created(){
  24. //获取params
  25. this.$route.params
  26. //获取query
  27. this.$route.query
  28. }
  29. }
  30. </script>

vue3 路由写法

  1. <script>
  2. import { defineComponent } from 'vue'
  3. import { useRoute, useRouter } from 'vue-router'
  4. export default defineComponent({
  5. beforeRouteEnter (to, from, next) {
  6. // 在渲染该组件的对应路由被 confirm 前调用
  7. next()
  8. },
  9. beforeRouteLeave ((to, from, next)=>{//离开当前的组件,触发
  10. next()
  11. }),
  12. beforeRouteLeave((to, from, next)=>{//离开当前的组件,触发
  13. next()
  14. }),
  15. setup() {
  16. const router = useRouter()
  17. const route = useRoute()
  18. const toPage = () => {
  19. router.push(xxx)
  20. }
  21. //获取params 注意是route
  22. route.params
  23. //获取query
  24. route.query
  25. return {
  26. toPage
  27. }
  28. },
  29. });
  30. </script>

posted @ 2024-03-15 09:39  mounter爱学习  阅读(36)  评论(0编辑  收藏  举报