从Vue2的思想去学Vue3

大熙哥 2021年11月04日 129次浏览

不知不觉已经11月份了,时间过得真快,在新的一年希望能够了解一下Vue3的开发思想。(Vue2都还没学透呢 /(ㄒoㄒ)/~~)

如何看待Vue3?

虽然说vue2.x是一个比较稳定的版本,也是很长一段时间我们在使用的版本,社区生态已经十分完善。但是呢?前端的技术迭代更新就是这么快,作为前端领域必不可少的一门技能,能够提前去接触到也是提升自我技术的必经之路。

什么是vite?

刚遇到这个名词一脸懵逼,以为是类似于Vue3这种框架,其实是前端开发与构建工具,也就是我们一直用的webpack,不得不提webpack的水很深,这时又来了一个vite,实在是前端永无止境。那么vite的优势是什么呢?

vite优势

一个字,快!
webpack在我们本地开发的过程中,每一次做细微的修改,都会造成很长时间的重新打包,对于很多大型项目,这样消耗的时间实在太久了,这也是长久以来的一个痛点,于是,vue3携带vite出世了,vite依然是基于node来工作的,原理是利用浏览器现在已经支持es6的import了,遇到import会发送一个http请求去加载文件,vite拦截这些请求,做一些预编译,省去了webpack冗长打包的时间,提升开发体验,让本地开发更为高效。

简单的点名系统

首先写一个简单的Demo,感受vue2和vue3的区别。同时学习setup() 和 ref()。

<template>
  <div>
    <p>点名系统Vue3小测试</p>
    <div v-for="(item, i ) in studentArr">
      <h1 @click="selectStuFn(i)">{{item}}</h1>
    </div>
    <h1>请{{selecPeople}}回答问题。</h1>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref} from 'vue'
export default defineComponent({
  name: 'App',
  setup() {
    const studentArr = ref(['张三', '李四', '老王'])
    const selecPeople = ref("")
    const selectStuFn = (index: number) => {
      selecPeople.value = studentArr.value[index]
    }
    return {studentArr, selecPeople, selectStuFn}
  }
})
</script>

vue2的思路

在vue2中,我们开发上面的点名系统,需要在data中初始化studentArr 以及 selecPeople ,然后在methods 中编写selectStuFn 函数。

vue3的思路

在vue3中,直接全部写在setup函数中就行,定义的两个属性和方法都定义到了setup其中,最后需要return回去。

区别

  1. 在vue2中定义在data里面的值就会自动变成响应式,不需要我们做什么操作,就可以在template里面直接使用。
  2. 在vue3中,我们需要通过ref关键字进行包括,这样的操作才能将它定义为一个响应式的数据。
  3. 在vue3中,我们对其赋值或者取值的时候必须是通过**.value 写法去拿或者取。

深入setup函数和ref函数

  1. setup函数其实是一个生命周期钩子,它对应的其实就是Vue2中的beforeCreate和create,并且他是vue3的composition API的入口函数。
  2. 在vue3中我们通过这个函数来定义vue2中的data,methods,watch,computed属性
  3. setup函数必须有返回值,必须返回一个对象,对象里包含所有在template模板中需要使用到的属性(包含data,methods等)
  4. 在setup里面通过ref生命的响应式数据,去取值或者赋值的时候必须通过**.value的方法去拿,但是template却不需要使用.value**
  5. ref也可以调用原生dom
  6. setup接收第一个参数是props,用于接收props,也就是定义在组件上的属性(同vue2),但是接收的props必须先在props属性中定义,否则是不会被接收到的
  7. setup接收的第二个参数是context,在js里面这个形参代表了上下文,它暴露组件的 property,他就是一个普通的javaScript对象,在这里面我们可以取到attrs, slots,emit等属性。

为什么要这么搞?

此处引用vue3文档的一段话,在我开发WebMaker深有感悟。确实有些组件已经是不能再细分了,写着写着就变成了超大组件,Vue3这种开发方式也很好的多人合作,特别是有了TS的加层,可以面向接口编程了。

使用 (data、computed、methods、watch) 组件选项来组织逻辑通常都很有效。然而,当我们的组件开始变得更大时,逻辑关注点的列表也会增长。尤其对于那些一开始没有编写这些组件的人来说,这会导致组件难以阅读和理解。

如果能够将同一个逻辑关注点相关代码收集在一起会更好。而这正是组合式 API 使我们能够做到的。

使用reactive函数优化程序

<template>
  <div>
    <p>点名系统Vue3小测试</p>
    <div v-for="(item, i ) in data.studentArr">
      <h1 @click="data.selectStuFn(i)">{{item}}</h1>
    </div>
    <h1>请{{data.selecPeople}}回答问题。</h1>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive} from 'vue'
export default defineComponent({
  name: 'App',
  setup() {

    const data = reactive({
      studentArr : ['张三', '李四', '老王'],
      selecPeople : "",
      selectStuFn : (index: number) => {
        data.selecPeople = data.studentArr[index]
    }
    })

    return {data}
  }
})
</script>
  1. 不再使用ref对属性进行申明响应式,而是和类似vue2的写法,我只需要定义一个data对象即可,想要的属性都放在data,最后统一返回data即可。
  2. 不再使用**.value的方式而改用data.**这样的方式,更方便自己理解,当然同理,template也需要加上
  3. 不需要再return一堆东西了,只需要return一个对象就可以了,写法更加简洁。

toRefs()函数对reavtive()函数解构返回

这时或许我们有疑惑,这还是得在template加上data才能访问啊,此时肯定有聪明哥说解构赋值rerurn {...data}返回所有属性就好啦。但是这样呢会破坏响应式特性。那怎么办?
使用官方提供的 toRefs() 函数即可完成。

<template>
  <div>
    <p>点名系统Vue3小测试</p>
    <div v-for="(item, i ) in studentArr">
      <h1 @click="selectStuFn(i)">{{item}}</h1>
    </div>
    <h1>请{{selecPeople}}回答问题。</h1>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs} from 'vue'
export default defineComponent({
  name: 'App',
  setup() {

    const data = reactive({
      studentArr : ['张三', '李四', '老王'],
      selecPeople : "",
      selectStuFn : (index: number) => {
        data.selecPeople = data.studentArr[index]
    }
    })
    let newData = toRefs(data)
    return {...newData}
  }
})
</script>

这个方法就是为了通过其方法解构后不去破坏他的响应式特性,具体原理还未深入了解,本文只是知道大概使用方法和功能。

vue3的生命周期

vue2vue3差异
beforeCreatesetupsetup() :开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method
createdsetup
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted
activatedonActivated
deactivatedonDeactivated
  1. vue2的beforeCreate和create变成了setup
  2. vue2的destroyed和beforDestroy变成了onBeforeUnmount,onUnmounted

watch的变化和watchEffect

watch就是监听一个变量的改变,在vue2中采用对象的方式传递监听函数,那么在vue3是怎么样的呢?

    let value = ref('')
    watch(value, (newVal, oldVal)=>{
      console.log('值被改变了。')
    })
    value.value = '443'

这里的wacth函数接收两个参数,第一个参数是我们要检测的值,第二个是回调函数,和以前一样拥有新旧两个值。

vue3的watch支持同时监听多个值,以数组的形式,如下:

    let value = ref('')
    let value2 = ref('')
    watch([value, value2], ([newVal, newVal2], [oldVal, oldVal2])=>{
      console.log('值被改变了。')
    })
    value.value = '443'
    value2.value = '443'

特别注意:watch只能监听通过 ref() 方法定义的响应式数据,通过reactive()函数定义的变量将会报错。那怎么办呢?

当你去监听一个对象或者数组的时候,前后对象就是循环引用,会发现newVal和oldVal的值始终一样,所以,才会出现上面这种情况,所以,要想解决上面的问题也很简单,可以通过一个函数直接return回来一个新值,这样就可以解决引用问题。

<template>
  <div>
    <p>点名系统Vue3小测试</p>
    <div v-for="(item, i ) in studentArr">
      <h1 @click="selectStuFn(i)">{{item}}</h1>
    </div>
    <h1>请{{selecPeople}}回答问题。</h1>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRefs, watch} from 'vue'
export default defineComponent({
  name: 'App',
  setup() {
    const data = reactive({
      studentArr : ['张三', '李四', '老王'],
      selecPeople : "",
      selectStuFn : (index: number) => {
          data.selecPeople = data.studentArr[index]
      }
    })
    watch(()=> data.selecPeople, (newVal,oldVal)=>{
      console.log('值被改变了。')
    })
    let newData = toRefs(data)
    return {...newData}
  }
})
</script>

了解完了,那么watchEffect是啥?

    let name = ref('34234')
    watchEffect(() => console.log(name.value))
    name.value = '你好'

他是一个方法,接收一个回调函数,他不需要去指定监听谁,它会去自动收集依赖,只要在回调函数中使用了的属性,在发生变化的时候,都会去触发这个回调函数,只能说太强了。

Vue3模块化

在使用vue2项目中,如果复用某些功能,我们会采用mixin的方式进行混入,而在vue3中,新的模块儿化的重用机制将会更加便捷。

test.ts

import { defineComponent, reactive, ref, toRefs, watch, watchEffect} from 'vue'
const data = reactive({
    studentArr : ['张三', '李四', '老王'],
    selecPeople : "",
    selectStuFn : (index: number) => {
        data.selecPeople = data.studentArr[index]
    }
})
watch(()=> data.selecPeople, (newVal,oldVal)=>{
    console.log('值被改变了。')
})
let name = ref('34234')
watchEffect(() => console.log(name.value))
name.value = '你好'
let newData = toRefs(data)

export default {...newData, name}

App.vue

<template>
  <div>
    <p>点名系统Vue3小测试</p>
    <div v-for="(item, i ) in studentArr">
      <h1 @click="selectStuFn(i)">{{item}}</h1>
    </div>
    <h1>请{{selecPeople}}回答问题。</h1>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRefs, watch, watchEffect} from 'vue'
import test from './test'
export default defineComponent({
  name: 'App',
  setup() {
    return {...test}
  }
})
</script>

vue3的瞬间移动组件Teleport

Teleport翻译过来就是瞬移,vue的入口文件的html只有一个容器就是

,我们称之为入口,同时页面上的所有组件实际都是挂载在这个节点的,打开开发者工具可以看到最外层只有一有个节点就是这个app。

瞬间移动组件适合撰写弹窗组件或者消息提示组件,像这种全局只有一个状态的组件,我们不希望他混入我们的组件中,我们希望他独立于app组件,所以,在大多数优秀的ui框架中,这一类的高级组件,一般会去采用新创建一个节点的方式,抽离出app节点,这样更为容易控制,并且在使用完毕后即时的删除。那么Teleoirt组件实际上就是这样的一个东西,可以独立于app之外的新的节点。

这样的组件在对于一些状态单一的组件创建中,变得更加丝滑了,不会对使用的组件内部造成任何破坏,也不会出现样式污染等各类问题,非常的实用。

Vue3的异步请求组件Suspense

这个异步组件(Suspense)呢是vue3新加的一种组件,再日常的开发中,异步组件是不可缺少的一部分,例如接口请求,图片请求,各种异步操作所导致的请求都属于异步组件,在vue2中这些状态的判断都需要我们自己手动去判断,但是vue3提供这一非常贴心的组件,这个中文翻译过来就是悬念的意思,他提供了两个template 也就是两个插槽,一个是没请求回来的时候显示什么,一个是请求成功显示什么。

使用非常简单,通过Suspense标签包裹的组件就是异步组件,在其中,提供了两个插槽,分别是成功和失败的插槽,会对应显示其中的内容。

其他关于Vue3的知识点

1. 什么是Composition API?

Compositon API不是一个api,而是很多个API组合的一套API,我们统称为这名字,vue2中,我们会在methods,computed,watch,data中等等定义属性和方法,共同处理页面逻辑,我们称这种方式为Options API。这样的方式在项目过大的时候我们发现,一个methods定义数十个方法的时候,还得准备知道每个方法的this,作用,干嘛的就会变得非常麻烦。

2. Vue2和Vue3的生命周期可以混用么?

可以,vue3的优先级更高。

3. Vue3中可以使用vue2的写法么?

可以

前端路漫长,大家互相学习,以下是vue3的思维导图

image.png

本文参考于 九儿的小书屋 的 万字长文带你全面掌握Vue3