Vue2转Vue3看这一片就够了!

本文详细介绍了Vue3与Vue2的主要语法区别,如setup替代了Vue2的生命周期函数,ref和reactive的响应式使用,以及props、context、watch、computed和provide/inject的更新。还涵盖了组件间通信和生命周期的示例应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Vue3初体验

1.1 Vue3与Vue2的语法区别

​ 由于选项式API——Vue2的代码是按区域去划分代码块的,比如:methods方法区域,computed计算区域,watch观察区域。但是由于代码量增加,Vue2的情况下写的代码数据与逻辑并不能很好的归并到一起,为增加代码的可读性,所以我们使用组合式API——Vue3代替。

二、语法区别

1.1 setup

  • setup()在组件被创建之前执行,这里不需要使用this,this不会指向实例。
  • 在vue3中舍弃了**beforeCreate()created()方法,在这两个方法内写的方法都可以在setup()**中执行。
  • setup()会将内部的参数通过【return】暴露给其他组件。
  • 通过【ref】可以实现响应式变量
  • 通过【reactive】可以实现响应式对象
  • 通过【】解构出来的数据不具备响应式,但是我们可以通过【toRefs】把它变成响应式
<template>
  <div>
    <div>
      {{ msg }}
      <button @click="updateMsg">改变msg</button>
    </div>
    <div>
      <!-- 模板会自动解析value值 -->
      {{ counter }}
      <button @click="updateCounter">改变counter</button>
    </div>
    <div>
      <!-- 模板会自动解析value值 -->
      {{ obj }}
      <button @click="updateObj">改变obj</button>
    </div>
    <div>
      {{ name }}
      {{ age }}
      {{children}}
      <button @click="updateObj">改变obj</button>
    </div>
  </div>
</template>

<script>
// 引入响应式
import {ref, reactive,toRefs} from "vue";

export default {
  name: "App",
  setup() {
    // 定义变量 【非响应式】
    let msg = "MSG"

    // 定义方法
    function updateMsg() {
      msg = "msg"
      console.log("msg", msg)
    }

    // 通过ref定义响应式变量
    const counter = ref(0); // ref()返回的是一个带 value的对象
    function updateCounter() {
      counter.value++;
      console.log("counter", counter);
    }

    // 通过reactive定义引用类型的数据
    const obj = reactive({
      name: "张三",
      age: 18,
      children:{
        name:"张三丰",
        age:0
      }
    })
    function updateObj() {
      obj.name = "李四";
      obj.age = 19;
    }

    // 暴露参数
    return {
      msg,
      updateMsg,
      counter,
      updateCounter,
      obj,
      updateObj,
      ...obj,// 解构出来的变量无响应式
      ...toRefs(obj), // 使用toRefs则有响应式
    }
  }
}
</script>

<style scoped>
</style>
1.1.1 props与context
<template>
  <div>
    <v ref="v" :message="msg" :class="c" @updatePropsMessage="updateMessage" @updateClass="updateClass"></v>
    <button @click="updateMessage('点击了父组件按钮')">父组件</button>
    <button @click="updateCount">父组件修改子组件</button>
  </div>
</template>

<script>
// 引入响应式
import {ref, reactive, onMounted} from "vue";
import V from "@/components/v.vue";

export default {
  name: "App",
  components: {V},
  setup(props,context) {
    const msg = ref("最初的msg");

    // 子组件通过context.emit调用父组件
    function updateMessage(value) {
      if (value) {
        msg.value = value
      }
      console.log("msg", msg.value);
    }

    // 子组件使用context.attrs
    const c = ref("pink")
    function updateClass(value) {
      c.value = value
    }

    // 父组件调用子组件方法方式
    const v=ref(null)
    function updateCount(){
      v.value.updateCount()
    }
    return {
      msg,
      updateMessage,
      c,
      updateClass,
      updateCount,
      v
    }
  }
}
</script>

<style scoped>
.pink {
  background-color: pink;
}

.green {
  background-color: green;
}
</style>

子组件

<template>
  <div>
    我是V组件,我得到的数值是 {{ message }}
    <button @click="updateMessage">子组件</button>
    <span>子组件计数:{{count}}</span>
  </div>
</template>

<script>
import {ref, reactive, toRefs} from "vue";

export default {
  props: {
    message: {
      type: String,
      default: "子组件"
    }
  },
  setup(props,context) {
    // 子传父
    function updateMessage(){
      // console.log("slots",context.slots)
      // console.log("emit",context.emit)
      // console.log("expose",context.expose)
      context.emit("updatePropsMessage","点击了子组件按钮")
      if (context.attrs.class === 'pink') {
        context.emit("updateClass", "green");
      } else {
        context.emit("updateClass", "pink");
      }
    }

    const count=ref(0)
    function updateCount(){
      count.value++;
    }
    context.expose({
      updateCount
    })
    return {
      updateMessage,
      count,
      updateCount
    }
  }
}
</script>

<style scoped>

</style>

1.2 watch

  • 需要导入【watch,watchEffect】方法
  • watch】:watch(侦听响应式引用,回调函数),只能监听指定的属性
  • watchEffect】:对象类型深度监听,自动收集方法内需要监听的变量。
<template>
  <div>
    <div>
      {{ count }}
      <button @click="updateCount">改变</button>
    </div>
    <div>
      {{ user }}
      <button @click="updateUser">改变</button>
    </div>
  </div>
</template>

<script>
// 引入响应式
import {ref, reactive, toRefs, watch,watchEffect} from "vue";

export default {
  name: "App",
  setup() {
    /**
     * count start
     */
    const count = ref(0)

    function updateCount() {
      count.value++;
    }
    // watch(侦听响应式引用,回调函数),只能监听指定的属性
    watch(count,(newValue,oldValue)=>{
      console.log("oldValue",oldValue)
      console.log("newValue",newValue)
    })

    /**
     * count end
     */
    /**
     * user start
     */
    const user = reactive({
      name: "张三",
      age: 18
    })

    function updateUser() {
      user.name = "李四"
      user.age = 14
    }
    // 对象类型深度监听,自动收集方法内需要监听的变量。
    watchEffect(()=>{
      console.log("user.name",user.name);
      console.log("user.age",user.age);
      // 可以同时监听多个
      console.log("count",count.value);
    })
    /**
     * user end
     */
    return {
      count,
      updateCount,
      user,
      updateUser
    }
  }
}
</script>

<style scoped>
</style>

1.3 computed

  • computed】会返回一个方法对象
<template>
  <div>
    <div>
      {{ a }}
      <button @click="updateA">改变</button>
    </div>
    <div>
      {{ b }}
      <button @click="updateB">改变</button>
    </div>
    <div>
      {{ add  }}
    </div>
  </div>
</template>

<script>
// 引入响应式
import {ref, reactive,computed} from "vue";

export default {
  name: "App",
  setup() {
    const a=ref(0)
    function updateA(){
      a.value++;
    }
    const b=ref(0)
    function updateB(){
      b.value++;
    }
    // 创建为一个函数
    const add=computed(()=>{
      return a.value+b.value;
    })
    return {
      a,
      updateA,
      b,
      updateB,
      add
    }
  }
}
</script>

<style scoped>
</style>

1.4 provide 与 inject

  • provide 用于传递数据
  • **inject **用于接收数据

父组件

<template>
  <div>
    <v></v>
    <button @click="update">改变</button>
  </div>
</template>

<script>
// 引入响应式
import {provide, ref, reactive} from "vue";
import V from "@/components/v.vue";

export default {
  name: "App",
  components: {V},
  setup(props, context) {
    const name = ref("张三");
    provide('name', name)

    function update() {
      name.value = "王五"
    }

    return {
      update
    }
  }
}
</script>

<style scoped>

</style>

子组件

<template>
  <div>
    {{name}}
  </div>
</template>

<script>
import {inject} from "vue";

export default {
  setup(props,context) {
    // 接收字段,默认值
    const name=inject('name','李四')
    return {
      name
    }
  }
}
</script>

<style scoped>

</style>

三、生命周期

1.1 Vue2 与Vue3生命周期对比

周期Vue2Vue3
实例创建开始beforeCreate没有,setup代替了
实例创建完成created没有,setup代替了
方法创建开始beforeMountonBeforeMount
方法创建完成mountedonMounted
页面更新开始beforeUpdateonBeforeUpdate
页面更新完成updatedonUpdated
组件卸载开始beforeUnmountonBeforeUnmount
组件卸载结束unmountedonUnmounted
错误捕获errorCapturedonErrorCaptured
追踪到响应式依赖renderTrackedonRenderTracked
变更触发了组件渲染renderTriggeredonRenderTriggered
组件被插入到 DOMactivatedonActivated
组件从 DOM 中被移除deactivatedonDeactivated
组件实例在服务器上被渲onServerPrefetch

1.2生命周期使用示例

  • 钩子使用需要引入。
  • 可以分多个区域,多次执行生命周期函数。
<template>
  <div>
    <div>
      {{ a }}
      <button @click="updateA">改变</button>
    </div>
    <div>
      {{ b }}
      <button @click="updateB">改变</button>
    </div>
    <div>
      {{ add  }}
    </div>
  </div>
</template>

<script>
// 引入响应式
import {ref, reactive,onMounted} from "vue";

export default {
  name: "App",
  setup() {
    const a=ref(0)
    function updateA(){
      a.value++;
    }
    onMounted(()=>{
      updateA();
    })
    const b=ref(0)
    function updateB(){
      b.value++;
    }
    onMounted(()=>{
      updateB();
    })
    onMounted(()=>{
      updateA();
      updateB();
    })
    return {
      a,
      updateA,
      b,
      updateB
    }
  }
}
</script>

<style scoped>
</style>

四、Vu3语法糖

在setup里面写代码需要引入并暴露变量、方法,使用起来比较麻烦,这里我们使用****标记,代码可以简介很多。

示例:

父组件

<template>
  <div>
    <v></v>
    <button @click="update">改变</button>
  </div>
</template>

<script setup>
import {provide,ref} from "vue";
import V from "@/components/v.vue";
const name = ref("张三");
provide('name', name)

function update() {
  name.value = "王五"
}

</script>

<style scoped>

</style>

子组件

<template>
  <div>
    {{name}}
  </div>
</template>

<script setup>
import {inject} from "vue";
const name=inject('name','李四')
</script>

<style scoped>

</style>
### Vue3 和 TypeScript 中代码标红问题的原因分析 在使用 Vue3 和 TypeScript 的过程中,如果遇到代码标红的情况,通常是因为以下几个原因: 1. **缺少必要的插件或扩展支持** 如果未正确配置 VSCode 或其他编辑器中的插件,可能会导致语法高亮错误或类型检查失败。例如,在 VSCode 中需要安装 `Vue Language Features (Volar)` 插件来提供对 Vue3 的全面支持[^1]。 2. **TypeScript 配置不完善** 可能是由于项目的 `tsconfig.json` 文件配置不当引起的。确保该文件中包含了正确的模块解析选项以及针对 Vue3 的特定设置。例如,需添加 `"moduleResolution": "node"` 和 `"types": ["vite/client"]` 等字段以适配现代构建工具链的要求[^5]。 3. **模板内的表达式缺乏类型推断能力** 在 Vue 组件的 `<template>` 部分,默认情况下可能无法很好地理解 TypeScript 类型定义。这可以通过启用 `defineComponent` API 并显式声明 prop 类型等方式改善。对于组合式 API (`<script setup>`) 场景,则应利用 SFC 脚本标签内部导出的内容完成更精确的静态分析[^4]。 4. **第三方库缺失类型声明文件** 当依赖某些外部包而它们本身并未附带完整的 `.d.ts` 定义时,也会引发类似的红色警告现象。此时可以尝试手动创建对应的全局声明或者寻找社区维护好的 DefinitelyTyped 版本替代品。 以下是具体的解决方案示例代码片段: ```typescript // tsconfig.json 示例配置 { "compilerOptions": { "target": "ESNext", "module": "ESNext", "strict": true, "jsx": "preserve", "importHelpers": true, "moduleResolution": "node", // 关键项之一 "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "sourceMap": true, "baseUrl": ".", "paths": { "@/*": ["src/*"] }, "lib": ["esnext", "dom"], "types": ["vite/client"] // 支持 Vite 开发环境下的增强功能 } } ``` 另外需要注意的是,在定义组件属性时避免采用箭头函数形式绑定生命周期钩子方法或其他成员变量操作逻辑,因为这样会改变 this 上下文指向从而破坏原有机制设计初衷[^5]。 #### 解决方案总结 综上所述,要彻底消除因 TypeScript 导致的代码标红状况可以从以上四个方面逐一排查并调整相应参数直至恢复正常为止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值