热搜:fiddler git ip ios 代理
历史搜索

带你温顾Vue2快速学习Vue3

游客2024-11-08 12:03:01
目录文章目录
  1. 为什么要升级 Vue3?
  2. vue2 VS vue3(编码体感)
  3. vue3 比较直观的新特性
  4. option api VS composition api
  5. 指令:vue2 VS vue3
  6. 组件通信:vue2 VS vue3
  7. 插槽 vue2 VS vue3
  8. 代码复用 vue2 VS vue3
  9. script setup 补充
  10. 强大的 style 特性

Vue3 已经发布有一段时间了,同时也得到了各大厂商和社区的支持和众多开发者喜爱,周边生态也正在逐步完善。可谓是一片欣欣向荣的美景。本文意在通过梳理 Vue2 常用 api 通过差异化对比 Vue3,帮助你快速掌握 Vue3

注:本文假设你已经有一定vue2实操经验,不会过多描述api细节。

如果想更系统学习请看Vue3 教程

为什么要升级 Vue3?

两个关键的因素导致了我们考虑重写 Vue 新的主要版本:

  1. 主流浏览器对新的JavaScript语言特性的普遍支持。
  2. 当前Vue代码库随着时间的推移而暴露出来的设计和体系架构问题。

更多细节,我们可以听听祖师爷在知乎的回答尤雨溪亲笔:重头来过的 Vue 3 带来了什么?

vue2 VS vue3(编码体感)

就我个人编码习惯而言,相对于vue2Option api的直观明了,vue3Composition api可以更好的组织代码,支持自定义hooks来达到代码复用,从而取代mixin,代码风格上,也可以把类似的业务逻辑写到一个代码块,从而避免代码分散,走读代码需要上下反复横跳。更友好的支持了TS以及众多新特性的加入,也让vue3写起来更爽,更利于代码维护和拓展。结合vite使用,更是极大提升了开发体验。总体来说,让我们拥抱vue3吧,给vue团队点个赞👍👍👍

vue3 比较直观的新特性

  • 全新的Composition api让我们换一种方式组织代码;
  • <script setup> 语法糖,使得代码更简洁;
  • 可以省去template的根元素包裹标签;
  • 提供了新的内置组件<teleport>,支持了组件可以挂载到任意dom节点下;
  • 提供了在css中使用v-bind来引入script变量,又一个强大的黑魔法小技巧;
  • 使用createApp来创建应用实例;
  • 更友好的TS支持;
  • 使用proxy代理方式来替换掉defineproperty
  • 全局和内部 API 已经被重构为支持 tree-shake

option api VS composition api

composition apivue3的一大特色,vue3对外暴露了大量函数供以我们按需引用,随意组合。

option api中,我们通过实例化Vue并将行为对象作为参数传入:

new Vue({
    data(){
        return {}
    },
    methods:{},
    computed:{},
    created(){},
    ...
})

Composition api 我们可以通过setup作为入口函数,并返回一个对象数据暴露给模板使用:

<template>
  <div @click="hi">{{ msg }}</div>
</template>
<script>
export default {
    setup() {
      const msg = ref('Hello!')
      function hi() {
        console.log(msg)
      }
      // 暴露给模板
      return {
        msg,
        hi
      }
    }
  }
</script>

setup还支持另一种写法,更加简化了代码:

<template>
  <div @click="hi">{{ msg }}</div>
</template>

<script setup>
const msg = ref('Hello!')
function hi() {
  console.log(msg)
}
</script>

当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用。

响应数据的定义

vue2中把响应数据定义到data函数的return中:

data(){
    return {
        ...
    }
}

vue3中我们可以通过refreactive来定义响应数据:

const num = ref(0)
const obj = reactive({name:'molly'})

ref:

  • 在模板中可以直接使用ref定义的数据,在js中则需要使用.value来取值或赋值;
  • 使用geterseter方式实现响应式;
  • 建议使用ref来定义基础数据。

reactive:

  • 可使用toRefs将其解包,在模板中直接使用对应的attribute
  • 使用proxy代理方式实现响应式;
  • 建议使用reactive来定义复杂数据。

生命周期钩子

vue2中提供了 11 个生命周期钩子,我们可在选项对象上直接定义使用。

vue3中把生命周期钩子单独抽离成了一个个对应的hooks函数,以onXXX的形式调用。值得注意的是生命周期钩子注册函数只能在setup期间同步使用,这就意味着beforeCreatecreated等同于setup执行阶段。其他的周期用法基本一致,例如:mounted对应onMounted

import { onMounted } from 'vue'
setup() {
    onMounted(() => {
        console.log('mounted!')
    })
}

computed

两个版本的computed基本保持一致,不同的是vue3computed抽离成一个hooks函数使用:

// vue2
computed:{
    num:()=>this.num *2
}

// vue3
const numVal = computed(() => num.value *2)

watch

vue2:

watch监听一个特定数据源的结果变化,回调函数得到的参数为新值和旧值。除了监听data中的数据
还可以监听props$route$emitcomputed。可通过选项参数deep来深度监听,指定 immediate: true 将立即触发回调:

watch:{
    obj:(val,old)=>{
        deep: true,
        immediate: true
    }
}

vue3

vue3的计算属性得到了很大的加强,支持监听多个数据源和执行副作用:

// 监听单个数据源
const count = ref(0)
watch(count, (val, old) => {
  /* ... */
})
// 假设 count 是一个对象,也支持监听整个对象,而无需指定 deep 属性
// 监听多个数据源
const count = ref(0)
const obj = reactive({name:'molly'})
watch([() => obj.name, count], ([newName, newCount], [oldName, oldCount]) => {
    /* ... */
})
// 监听多个数据源时,watch 函数的第一个参数可传入数据源数组,第二个回调函数的参数也是一个数组

vue3还新增了watchEffect,表示立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

watch相比,watchEffect不需要传入指定监听的数据源,它会自动收集依赖。也没有新值旧值的概念,只要依赖发生变化,就会重新执行函数:

const count = ref(0)
watchEffect(() => console.log(count.value))
setTimeout(() => {
  count.value++
}, 100)

watchEffect 会返回一个用于停止这个监听的函数。

watchEffect在组件的setup函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。

const stop = watchEffect(() => {
  /* ... */
})
stop()

filters

vue2中我们可以很方便的使用 filters 过滤器来处理一些文本格式转换,对数据进行加工。

filters: {
  sum(num1,num2) {
    return num1+num2
  }
}

请注意: 在vue3中,将移除过滤器,且不再支持。官方更推荐我们使用方法调用或计算属性来替换filters

至于为啥要移除这个api官方的解释是:虽然这看起来很方便,但它需要一个自定义语法,打破了大括号内的表达式“只是 JavaScript”的假设,这不仅有学习成本,而且有实现成本。

我的理解是:api功能设计重复,filters能干的事儿,计算属性和函数调用也能干,且干的更好。所以尤大大含泪移除filters,可怜的filters只能被抛弃。

人生亦是如此,职场中优胜劣汰,希望我们永远都不会是那个被优化掉的filters😭😭😭。

components

vue2中我们需要通过选项components进行组件注册,而在vue3中,我们可以直接使用组件:

//vue2 
import A from './a.vue'
components:{
    A
}
//vue3
<template>
  <A/>
</template>
<script setup>
    import A from './a.vue'
</script>

指令:vue2 VS vue3

两个版本的指令用法基本相同,这里我只列出vue3差异部分。

v-model

vue2中我们实现一个自定义v-model可以这样写:

// vue2
props:{
    title:{
        type:String,
        default: 'molly'
    }
},
model: {
    prop: 'title',
    event: 'change',
},
methods:{
    change(val){
        this.$emit('input',val)
    }
},

vue3中可以定义v-model参数名,同时还支持设置多个v-model,简直美滋滋~

// vue3
props:{
    title:{
        type:String
    },
    num:{
        type:Number
    },
},
setup(props,{emit}){
    function change1(val){
        emit('update:title',val)
    }
    function change2(val){
        emit('update:num',val)
    }
    return {
        change1,
        change2
    }
}

// 在父组件中使用
<Son v-model:title="molly" v-model:num="18" />

新增指令

v-memo,该指令记住一个模板的子树。元素和组件上都可以使用。该指令接收一个固定长度的数组作为依赖值进行记忆比对。如果数组中的每个值都和上次渲染的时候相同,则整个该子树的更新会被跳过。相当于内存换时间,相同渲染内容则从内存读取,这对于长列表场景很有用。

其他变更

  • 对于 v-if/v-else/v-else-if 的各分支项 key 将不再是必须的,因为现在 vue3 会自动生成唯一的 key
  • <template v-for> 的 key 应该设置在 <template> 标签上 (而不是设置在它的子节点上);
  • 作用于同一个元素上时,v-if 会拥有比 v-for 更高的优先级;
  • v-on 的 .native 修饰符已被移除;
  • v-for 中的 ref 不再注册ref数组;
  • 在使用v-bind="object"与组件独立属性重名时,那么绑定的声明顺序将决定它们如何被合并,以后者为标准。
<!-- 模板 -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- 结果 -->
<div id="blue"></div>

<!-- 模板 -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- 结果 -->
<div id="red"></div>

组件通信:vue2 VS vue3

vue2中提供了多种api供以我们进行组件通信:

  • props / $emit / $on
  • $attrs / $listeners
  • provide / inject
  • $parent / $children / ref

vue3中依然支持大部分api,并做了适当调整

  • 移除了$on$off$once实例方法
  • 移除了$children实例property
  • $attrs 现在包含了所有传递给组件的 attribute,包括 class 和 style
  • 在 <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits ,它们具备完整的类型推断并且在 <script setup> 中是直接可用的

插槽 vue2 VS vue3

在插槽这一块两个版本基本保持一致,没有太多的改动,还是保留原来的使用方式。vue3做了一点点小更新

  • this.$slots 现在将插槽作为函数公开;
  • 移除 this.$scopedSlots

代码复用 vue2 VS vue3

vue2中代码复用的手段很多,主要有以下几种方式

  • 组件抽离
  • 自定义指令
  • 实例全局挂载
  • 插件封装
  • mixin
  • extend

vue3中同样涵盖以上手段,并做了相应优化与更新。

  • 实例全局挂载这一手段在vue2中,我们一般是简单粗暴的通过prototype将行为对象挂载到vue原型上,这种方式虽然简单明了,但是也存在全局污染的问题。
    Vue.prototype.$xxx = {name:'molly'}

    对应到 vue3 中,则不允许我们这样子干,取而代之的是 app.config.globalProperties

  • mixin 也是代码复用的一大利器,不过相应的也暴露出一些问题。当 mixin 被滥用或大量使用时,会导致依赖关系不明确,不易维护。在 vue3 中更推荐我们使用自定义 hooks 的方式来复用代码。

实现一个自定义 hooks

我们可以把一个功能相关的数据和逻辑都抽离出来放到一起维护,例如实现一个累加器:

import {ref, onUnmounted} from 'vue'
export function useAccumulator(){
    const count = ref(0)
    let timer = null
    timer = setInterval(()=>{
        count.value ++
    },1000)
    onUnmounted(()=>{
        clearInterval(timer)
    })
    return {count}
}

我们定义了一个累加器的hooks函数,在组件入口就可以像普通函数一样使用useAccumulator()

import {useAccumulator} from '../utils'

let {count} = useAccumulator()

script setup 补充

贴上官网的描述

<script setup> 是在单文件组件 (SFC) 中使用组合式 API的编译时语法糖。相比于普通的 <script> 语法,它具有更多优势:

  • 更少的样板内容,更简洁的代码。
  • 能够使用纯 Typescript 声明 props 和抛出事件。
  • 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
  • 更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。

<script setup> 为我们带来了极大的便利,优势如下:

  • 所有顶层绑定变量内容都能在模板中直接使用。
  • <script setup> 范围里的值也能被直接作为自定义组件的标签名使用,相当于我们可以不用通过components 注册组件。
  • 支持使用:is来绑定动态组件。
  • 支持顶层await,先比JavaScript一步实现这个特性,就是爽。

强大的 style 特性

强大的style特性是vue3的又一个神兵利器。

深度选择器

为了使组件之间样式互不影响,我们可以这样写<style scoped>,当处于scoped下想做深度选择时,可以使用:deep()伪类:

<style scoped>
.a :deep(.b) {
  ...
}
</style>

插槽选择器

默认情况下,作用域样式不会影响到<slot/>内容,可以使用:slotted伪类来选择到插槽:

:slotted(div) {
  color: red;
}

全局选择器

可通过:global伪类来实现全局样式:

:global(.red) {
  color: red;
}

css module 的支持

可通过<style module>方式将 css 类作为$style对象暴露出来给组件使用,同时也支持自定义名称:<style module=“molly” >

<template>
  <p :>red</p>
</template>

<style module="molly">
.red {
  color: red;
}
</style>

状态驱动的动态 css

这是我认为最方便的一个特性,可以让我们少写很多代码,非常爽~

style中允许我们使用v-bind来将 css 值动态关联到组件上:

<template>
  <div >hello</div>
</template>

<script>
export default {
  data() {
    return {
      color: 'red'
    }
  }
}
</script>

<style>
// v-bind 可以直接引入 script 中的响应数据作为值
.text {
  color: v-bind(color);
}
</style>

总结至此应该可以轻松上手 vue3 项目了,感谢阅读。

标签:setup()