总结一下Vue3常用API,实现Vue2到3的平滑过渡

项目搭建

尤大又一力作Vite不知不觉间升级到2.0,作为风头正盛号称下一代前端构建工具,有什么理由不去体验一下。

Vite项目初始化

官方提供npm、yarn两个指令。
npm: npm init @vitejs/app
yarn: yarn create @vitejs/app

Vite项目初始配置

这里使用了npm的方式,新建 HelloWorld 文件夹并使用vscode打开。

  • 在终端命令行输入 npm init @vitejs/app

  • 配置项目名称

    image.png

  • 配置项目框架,这里我选用了vue

    image.png

  • 配置项目语言类型,这里我选用了vue

    image.png

  • 项目搭建完成,进入项目目录并安装依赖

    image.png

    • 进入项目目录 cd vite-project
    • 安装项目依赖 npm install
  • 运行项目 npm run dev

项目目录调整

新建组件A src/components/ComA.vue,在App.vue中导入ComA.vue并挂载到模板中。
我们在ComA中进行Vue3相关练习

image.png

Compostion-API

相比较Vue2来说,Vue3的升级主要是包含 使用proxy重写了响应式逻辑新API风格Compostion-API 等…,这里只对 Compostion-API 进行讲解。

为什么要使用Compostion-API

它是为了实现基于函数的逻辑复用机制而产生的。以往的 options-API ,在大型项目中可能会出现一下几个痛点。

  • 代码分散、可读性差

    在大型组件中,实现某个功能的代码可能会分散在多个选项中(data、methods、computed…),不便于我们查找选项之间依赖关系、降低代码的可读性。

  • 因代码复用导致 依赖来源不明确

    options-API 我们可以使用混入、Vue原型挂载等方式,为Vue实例挂载依赖数据。例如 在某组件莫名其妙出现了几个未在该组件定义的data数据,这些数据可能某地方挂载到Vue原型上 也可能在混入的示例上等 这些混乱的依赖关系势必会增加开发的心智负担。

setup

setup是Vue3新增选项,它是使用Compostion-API的入口函数,Compostion-API相关代码都要放入到setup函数中。

// components/ComA.vue
<template>
  <div></div>
</template>

<script>
export default {
  // composition-API 入口函数
  setup(props,context) {
    // 书写Composition-API
    ...
  },
  // 其他Vue选项
  data() {...},
  methods:{...}
}
</script>
复制代码

setup参数

  • props:对象,保存父组件向子组件传递的props数据
  • context
    对象,保存上下文数据 可以使用 ES6解构setup(props,{attrs,slots,emit}){...}

    • attrs:对象,保存了组件标签所有attrs属性
    • slots:对象,保存了组件插槽传入的数据
    • emit: 方法,用来触发组件标签上注册的自定义事件,相当于this.$emit

props与attrs

父组件传入了props数据,如果子组件没有在props选项中声明要接收props字段。则该字段会保存在attrs中。

// App.vue  
<template>
  <ComA :msg="msg" text="这是ComA组件"></ComA>
</template>

<script >
import ComA from "./components/ComA.vue";\
export default {
  setup() {
    const msg = 11
    return {msg}
  }
}
</script>

// ComA.vue  
<script>
export default {
  // 在props 声明接收msg
  // props:["msg"],
  // composition-API 入口函数
  setup(props,{attrs}) {
    console.log("props",props);
    console.log("attrs",attrs);
  }
}
</script>
复制代码

image.png

image.png

注意:不允许对props进行解构,props为proxy对象,对其解构会让其失去响应式
例如:setup({name,age}){......} 这种用法是错误的

如果需要解构props,可以使用let { msg } = toRefs(props),这样就需要msg.value进行读写所以最好还是用props.msg的形式

emit

// App.vue  根组件  
<template>
  <ComA :msg="msg" @update="update"></ComA>
</template>

<script setup>
import ComA from "./components/ComA.vue";
const msg = 11
function update(value) {
  console.log("update被触发",value)
}
</script>

// ComA.vue  子组件
<template>
  <button @click="hClickBTN">更新</button>
</template>

<script>
import { onMounted, ref } from '@vue/runtime-core';
export default {
  setup(props,{emit}) {
    function hClickBTN () {
      //  通过emit触发自定义事件 "update",进行子传父通讯
      emit("update","张三")
    }
    return {
      hClickBTN
    }
  }
}
</script>
复制代码

image.png

setup返回值

setup的返回值必须为对象,setup调用后会将返回对象的所有属性和方法挂载Vue实例上。这样模板中就能直接使用。
如果setup没有将属性放到对象中返回出来 模板中就无法使用该属性

// ComA.vue
<template>
  <div>msg:{{msg}}</div>
</template>

<script>
import { onMounted, ref } from '@vue/runtime-core';
export default {
  setup() {
   const msg = 111
  //  return {msg}
  }
}
</script>
复制代码

image.png

image.png

setup注意事项

  • setup只会执行一次,并且setup执行时,组件实例并未被创建,所以setup的this指向undefined
  • setup返回的对象,如果对象中的属性和方法与 options的datamethods返回的字段有冲突时,setup具有更高的优先级它会覆盖掉其他选项中重复的字段。
  • 可以在options的methods等选项中,通过this实例获取 setup返回出的数据。但是setup的this为undefinedsetup中无法访问datamethods等.

script setup

script setup 是 setup入口函数的语法糖写法。这种写法不用再配置setup选项,也不需要手动将声明的变量或者函数return出去,script setup会帮你完成这些工作

<template>
  <div>姓名:{{name}},年龄:{{age}}</div>
</template>

// 普通写法
<script>
export default {
  setup() {
    const name = "张三"
    const age = 18
    return { name,age }  
  }
} 
</script>

// script setup 写法
<script setup>
  const name = "张三"
  const age = 18
</script>
复制代码

在 script setup 中使用 props、emit、nextTick

// App.vue
<template>
  // 传入count、注册自定义事件update
  <ComA :count="count" @update="update"></ComA>
</template>

<script setup>
import { ref } from "vue";
import ComA from "./components/ComA.vue";
// 声明响应式ref数据
const count = ref(10)
function update(value) {
  // 更改ref数据
  count.value = value
  console.log("根组件-count设置新值",count.value);
}
</script>

// ComA.vue  
<script setup>
import { defineEmit, defineProps, onMounted,nextTick } from "vue";
// 声明props方式1:
// const props = defineProps({
//   num: {
//     type: Number,
//     default: 1
//   }
// })
// 声明props方式2:
const props = defineProps(["count"])
// 声明 emit
const emit = defineEmit(["update"])
// 页面DOM挂载完成
onMounted(() => {
  const num = props.count
  console.log("ComA-props.count初始值",props.count);
  // 触发自定义事件update、传入新值
  emit("update", num+1)
  // Vue响应式数据更新是异步的,更改props后需要在nextTick获取最新值
  nextTick().then(()=>{
    console.log("nextTick-ComA-props.count修改后",props.count);
  })
})
</script>
复制代码

image.png

在 script setup 中使用 attrs、slots

// App.vue  
<template>
  <ComA parent="app.vue"></ComA>
</template>

// ComA.vue  
<script setup>
import {  onMounted, useContext } from "vue";
// 使用useContext 注册attrs、slots
const { attrs, slots } = useContext()
console.log("ComA-attrs.parent",attrs.parent);
</script>
复制代码

image.png

响应式数据

ref

ref 主要是用来声明简单数据类型的响应式数据
setup 中, ref数据必须通过 变量名.value 的形式进行读写,setup 在将ref数据返回时会进行解包,所以在模板可以直接使用 ref 数据而不是 变量名.value的形式。

<template>
  <!-- 模板中使用ref数据 -->
  <div>数量:{{amount}}</div>
  <div>
    <!-- 模板中修改ref数据 -->
    <button @click="amount++">增加</button>
  </div>
  <div>
    <button @click="hClickAdd">setup增加</button>
  </div>
</template>

<script >
import { ref } from "vue";
  export default{
    setup(){
      const amount = ref(0)
      // 在setup中访问ref变量的值 必须用 变量名.value 的形式
      function hClickAdd() {
        // 在setup中修改ref变量的值 必须用 变量名.value 的形式
        amount.value++
      }
      return {amount,hClickAdd}
    }
  }
</script>
复制代码

reactive

reactive 主要是用来声明复杂数据类型的响应式数据,他也可以引用 ref 数据

<template>
 <div>
   <p>年龄:{{person.age}}</p>
   <p>姓名:{{person.name}}</p>
   <p><button @click="age++">增加年龄</button></p>
   <p>
     <input type="text" v-model="msg">
     <button @click="updateName">修改姓名</button>
   </p>
   <p></p>
 </div>
</template>

<script setup>
import { reactive, ref } from "vue";
const age = ref(0)
const msg = ref("")
const person = reactive({
  name:"张三",
  age
})
const updateName = ()=>{
  person.name = msg.value
}
</script>
复制代码

image.png

toRefs

他可以将一个响应式对象转换成普通对象,该普通对象的每个属性都是一个ref数据

应用场景

某些响应式数据不能直接使用解构,否则会丢失响应式。我们可以通过toRefs来解决这个问题

对props进行解构

// App.vue  
<template>
  <ComA :parent="parent"></ComA>
</template>

<script setup>
import { onMounted, reactive } from "vue";
import ComA from "./components/ComA.vue";
const parent = reactive({
  name:"App.vue"
})
onMounted(()=>{
  setTimeout(()=>{
    // 更改了name的值
    console.log("parent.name is change");
    parent.name = "newValue"
  },1000)
})
</script>  

// ComA.vue  
<template>
   <div class="teleport_content">props.parent.name:{{parent.name}}</div>
</template>

<script setup>
import { defineProps, reactive, toRefs } from "vue";
  const props = defineProps(["parent"])
  // toRefs 会将所有props的属性变为ref数据
  const {parent} = toRefs(props)
</script>
复制代码

image.png

对reactive数据进行解构

// ComA.vue  
<template>
  <div>name1:{{name1}}</div>
  <div>name2:{{name2}}</div>
</template>

<script setup>
import {  reactive, toRefs } from "vue";
 const person = reactive({name:"张三"})
  // 直接解构 数据丢失响应式
 const {name:name1} =  person
 const {name:name2} =  toRefs(person)
 // 修改数据
 person.name = '李四'
</script>
复制代码

image.png

Compsition-API 生命周期

Vue3提供了一系列onXXX函数 来在setup中注册生命周期钩子函数

options-API与Compsition-API 生命周期的映射关系

beforeCreate、created ==> setup()
beforeMount ==> onBeforeMount
mounted ==> onMounted
beforeUpdate ==> onBeforeUpadte
updated ==> onUpdated
…….

生命周期的使用

<template>
 <div>
   <p>年龄:{{person.age}}</p>
   <p>姓名:{{person.name}}</p>
   <p><button @click="age++">增加年龄</button></p>
   <p>
     <input type="text" v-model="msg">
     <button @click="updateName">修改姓名</button>
   </p>
   <p></p>
 </div>
</template>

<script setup>
import { ref,reactive,onBeforeMount,onMounted,onUpdated } from "vue";
const age = ref(0)
const msg = ref("")
const person = reactive({
  name:"张三",
  age
})
const updateName = ()=>{
  person.name = msg.value
}
console.log("setup run...");
onBeforeMount(()=>{
  console.log("onBeforeMount run...");
})
onMounted(()=>{
  console.log("onMounted run...");
})
onUpdated(()=>{
  console.log("onUpdated run...");
})
...
</script>
复制代码

image.png

Computed

Vue3的Composition-API中的Computed使用方式有两种,一种是只读的computed,另外一种是可读写的computed

只读computed

const refData = computed(()=>{...})

computed传入一个 getter 函数,并返回一个不可修改的ref对象

<template>
   <p v-for="(e,i) in persons" :key="i">
     {{e.name}}的年龄为:{{e.age}}
     <p>
       <input type="number" v-model="e.editAge"/> 
       <button @click="e.age=e.editAge">确定更改</button>
      </p>
   </p>
   <p>所有人年龄总和:{{totalAge}}</p>
</template>

<script setup>
import { ref,onMounted,reactive,computed } from "vue";
const persons = reactive([
  {
    name:"张三",
    age:10,
    editAge:0
  },
  {
    name:"李四",
    age:20,
    editAge:0
  },
  {
    name:"王五",
    age:25,
    editAge:0
  },
])
// 创建一个只读的computed
const totalAge = computed(()=>{
 return  persons.reduce((total,cur,i)=>{
   const totalNum = i===1?total.age:total
   const curNum = cur.age?Number(cur.age):0
    return Number(totalNum)+curNum
  })
})
</script>
复制代码

image.png

image.png

可读写的 computed

computed通过传入一个 具有 getset 函数的对象来创建可写的 ref 对象

<script setup>
import { ref,onMounted,reactive,computed } from "vue";
const zsAge = ref(0)
const lsAge = ref(5)
// 创建一个可读写的computed
const totalAge =  computed({
  set(value) {
    zsAge.value = value
  },
  get() {
    return zsAge.value+lsAge.value
  }
})
console.log("computed初始",totalAge.value);
zsAge.value = 5
console.log("computed依赖被修改后",totalAge.value);
totalAge.value = 10
console.log("computed被修改后",totalAge.value);
</script>
复制代码

image.png

watch

侦听单个数据源

<script setup>
import { ref,watch, reactive,onMounted } from "vue";
const count = ref(0)
const perosn = reactive({
  age:0
})
// 直接侦听 ref数据源
watch(count,(value,preValue)=>{
  console.log("ref-count发生变化",value,preValue);
})
// 侦听一个 reactive数据源的getter
watch(()=>perosn.age,(value,preValue)=>{
  console.log("reactive-person.age发生变化",value,preValue);
})
onMounted(()=>{
  // 修改ref、reactive
  count.value++
  perosn.age++
})
</script>
复制代码

image.png

侦听多个数据源

<script setup>
import {  onMounted, watch,ref } from "vue";
const a = ref(0)
const b = ref(1)
// 使用 注册attrs、slots
watch([a,b],([a,b],[preA,preB])=>{
  console.log("a或b发生变化","a当前值:"+a,"a改变前的值:"+preA,"b当前值:"+b,"b改变前的值:"+preB);
})
onMounted(()=>{
  a.value++
})
</script>
复制代码

image.png

watchEffect

watchEffectwtach 有些类似,它不用主动声明观测哪个数据。函数中使用了哪些响应数据,当这些数据发生变化时该函数就会响应式的执行,该函数初始化时会先执行一次。

<script setup>
import {  onMounted,ref, watchEffect } from "vue";
const a = ref(0)
const b = ref(1)
watchEffect(()=>{
  b.value = a.value+10
  console.log("b的值发生变化", b.value);
})
onMounted(()=>{
  // 修改a的值
  a.value=100
})
</script>
复制代码

getCurrentInstance

getCurrentInstance 可以在 setup 中,获取组件实例。

<script setup>
import {  onMounted, ref,getCurrentInstance,  } from "vue";
const a = ref(0)
const b = ref(1)
onMounted( ()=>{
  // 获取组件实例 
  const instance = getCurrentInstance()
  console.log("instance",instance);
})
</script>
复制代码

image.png

新特性

teleport

teleport(传送门)是Vue3新增内置组件,它可以将组件模板节点内容插入到 组件内外的任意元素节点中。

属性

  • to:字符串,值为DOM选择器(有效选择器)。代表teleport的内容将要移动到哪个元素节点中。
  • disabled:布尔值,代表是否禁用传送门,注意该属性只决定了teleport内容的渲染位置,当disabled为false时,teleport内容会在父组件节点的位置渲染。

举个栗子

// ComA.vue
<template>
 <teleport to="body">
   <!-- 将teleport的内容移动到<body>中-->
   <div class="teleport_content">这是teleport的内容</div>
 </teleport>
</template>
复制代码

image.png

动态改变渲染位置

// App.vue  
<template>
  <ComA></ComA>
</template>

<script setup>
import ComA from "./components/ComA.vue";
s</script>

// ComA.vue  
<template>
  <!-- 向<body>中,插入内容 -->
 <teleport to="body" :disabled="isDEisabled">
   <div class="teleport_content">这是teleport的内容</div>
 </teleport>
</template>

<script setup>
import {  ref  } from "vue";
// 是否禁用传送门
const isDEisabled = ref(true)
</script>
复制代码

image.png

teleport选中多个传送目标节点

teleport的to属性指定了传送的目标节点,当有多个目标节点时会使用第一个。

// App.vue
<template>
  <div class="container"></div>
  <div class="container"></div>
  <ComA></ComA>
</template>

<script setup>
import ComA from "./components/ComA.vue";
</script>

// ComA.vue 
<template>
 <teleport to=".container">
   <div class="teleport_content">这是teleport的内容</div>
 </teleport>
</template>
复制代码

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享