vue3常用API记录

123 阅读11分钟

Vue3优点

  1. 首次渲染更快
  2. diff 算法更快
  3. 内存占用更少
  4. 打包体积更小
  5. 更好的 Typescript 支持
  6. Composition API 组合式 API

vite创建项目并运行

创建项目:npm create vite@latest

项目运行:npm run dev

vue3使用 volar 插件 进行语法提示和代码高亮,需要禁用 vetur插件(和vue2有冲突)

注意:vue3 每个组件中可以有多个根节点

组合式API

  • 通过data、methods、watch 等配置选项组织代码逻辑是选项式API写法
  • 所有逻辑在setup函数中,使用 ref、watch 等函数组织代码是组合式API写法

setup函数

  • 从组件生命周期看,它在 beforeCreate 之前执行
  • 函数中 this 不是组件实例,是 undefined,所以vue3中中几乎用不到 this 
  • 将模板中使用到的数据、函数,定义在 setup 中,并返回
<template>
  <div>
    <h1 @click="say()">{{msg}}</h1>
  </div>
</template>
<script>
export default {
  setup () {
    // 定义数据和函数
    const msg = 'hi vue3'
    const say = () => {
      console.log(msg)
    }
    // 返回给模板使用
    return { msg , say}
  },
}
</script>

setup语法糖

  • 省略 export default,return,将 setup 写在 script 标签内
<script setup>
  const say = () => console.log('hi')
</script>

ref函数

通常使用 ref 定义响应式数据,不限类型(简单or复杂)

使用 ref 创建的数据,在 js文件中需要 .value(因为ref()返回的是一个响应式==对象==,数据放在value属性中) ,template 中可省略

<script>
  import { ref } from 'vue'
  export default {
    setup() {
      const count = ref(0)
      // 该函数每次将count的值加10
      const increment = () => {
        count.value += 10
      }
      return { count, increment}
    }
  }
</script>

修改 ref 声明的响应式数据可以用赋值号

reactive函数

定义复杂类型的响应式数据

<script>
// 1. 导入函数
import { reactive } from "vue"; 
export default {
  setup() {
    // 2. 创建响应式数据对象
    const state = reactive({ name: 'tom', age: 18 })
    // 3. 返回数据
    return { state }
  }
};
</script>

注意:修改reactive声明的响应式数据,不能用赋值号,只能用点语法

import { reactive } from 'vue'
let obj = reactive({
  name: '小小',
  age: 19,
})  
// obj = {
  //   name: '哈哈',
  //   age: 20,
  // }
  obj.name = '哈哈' // 修改reactive定义的数据只能用点语法

computed函数

-------------------------> 定义计算属性

在setup中使用 computed 函数,传入回调函数,该函数返回计算好的数据

<script setup>
    // 导入computed
  import { computed, ref } from "vue";

  const scoreList = ref([80, 100, 90, 70, 60]);
  // 计算属性
  const betterList = computed(() => scoreList.value.filter((item) => item >= 90));
  // 改变数据,计算属性改变
</script>

watch函数

-----------------> 监听数据变化

总括:

监听简单数据类型

一个,直接写变量;多个,变量放到数组里

监听复杂数据类型

watch 写第三个参数,开启深度监听

监听对象中的某个属性(精确监听)

() => 对象.属性

语法:watch(需要监听的数据, 数据改变执行函数, 配置对象)

  1. 监听多个响应式数据,简单数据类型(多个写在数组里)

       watch([num, user], () => {
         console.log('count改变了', num.value)
      })
    
  2. 监听对象数据中的一个属性(简单数据类型) ( 第一个参数为回调函数,监听的数据写在他的返回值里)

      watch(() => user.name, ()=> {
        console.log('name改变了')
      })
    
  3. 监听响应式对象数据中的一个属性(复杂数据类型)。(写法同上,最后一个参数为对象,添加deep,开启深度监听)

    还可以写 immediate: true,组件开启默认执行一次

      watch(() => user, ()=> {
        console.log('user改变了')
      }, {
        deep: true,
        immediate: true
      })
    

    注意:

    用reactive声明的对象,去监听整个对象的变化,不用开启深度监听,可以直接监听到。但是ref不行。

    监听复杂数据类型变化后的值,旧值和新值打印出来的都是新值,指向的是同一块内存。

生命周期钩子函数

注意:生命周期钩子函数可以调用多次,按照代码从上往下的顺序执行

  1. beforeCreate、created 被 setup替代
  2. beforeDestroyed、destroyed 被 onBeforeUnmount、onUnmounted 替代
  3. 其余的都是在原来vue2的名字前面加 on (例如:onMounted、onActivated……)
  4. vue3组合式API在onMounted中发请求
选项式API下的生命周期函数使用组合式API下的生命周期函数使用
beforeCreate不需要(直接写到setup函数中)
created不需要(直接写到setup函数中)
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyedonBeforeUnmount
destroyedonUnmounted
activatedonActivated
deactivatedonDeactivated

ref获取DOM元素

\1. 创建 ref => const hRef = ref(null) null为固定写法

\2. 模板中建立关联 =>

我是标题

\3. 使用 => hRef.value

<script setup>
  import { ref } from 'vue'
  const hRef = ref(null)
  const clickFn = () => {
    hRef.value.innerText = '我不是标题'
  }
  </script>

ref操作组件-defineExpose

使用 setup函数使得 组件是默认关闭的,组件实例获取不到它内部的数据和函数。所以需要子组件通过 defineExpose() 函数将自身的数据、方法暴露给父组件。

父组件

<template>
     // abc 为子组件名
  <abc ref="formRef"></abc>
  <button @click="fn">按钮</button>
</template>
<script setup>
import { ref } from 'vue'
import abc from './components/form.vue'

  const formRef = ref(null)
  const fn = () => {
    console.log('获取子组件中的num的数据',formRef.value.num)
    formRef.value.validate()
  }
</script>

注:父组件中通过ref获取实例,在js文件中使用里面的数据和方法,要加.value

子组件

<script setup>
  import { ref } from 'vue'
  const num = ref(0)
  const validate = () => {
    console.log('表单校验函数')
  }


  // 要想在父组件中获取子组件的数据和方法,需要在子组件中使用 defineExpose 暴露给子组件实例
  defineExpose({
    num,
    validate
  })
</script>

父传子子传父

父组件通过自定义属性传值

子组件

<script setup>

// defineProps: 接收父组件传递的数据
// 想要在 script 中也操作 props 属性,应该接收返回值
 const props = defineProps({
  money: Number,
  car: String
}) 


// 子组件通过 defineEmits获取 emit 函数
const emit = defineEmits(['changeMoney'])
const change = () => {
// 通知父组件触发自定义事件changeMoney,且传值10
  emit('changeMoney', 10)
}
</script>

跨级组件通讯provide与inject

祖先组件通过 provide 提供后代组件需要依赖的数据或函数

import { provide } from 'vue';

// 要传递的数据
const count = ref(0)
provide('count', count)

// 修改数据的方法 (谁提供的数据,谁负责修改)
const updateCount = (num) => {
  count.value += num
}
provide('updateCount', updateCount)

孙子组件通过 inject 注入祖级provide提供的数据或函数

<script setup>
import { inject } from 'vue';

// inject 注入祖级provide提供的数据或函数
const count = inject('count')
const updateCount = inject('updateCount')
</script>

保持响应式-toRefs函数

在使用 reactive 或者 ref 创建的响应式数据被 展开解构 的时候会失去响应式功能。

对象的多个属性都变成响应式数据,并且要求响应式数据和原始数据相关联,并且更新响应式数据时更新界面,这时候toRefs()就派上用场了,它用于批量设置多个响应式数据。

import { reactive, toRefs } from "vue";
const user = reactive({ name: "tom", age: 18 });
// 对每一个属性做包装
const { name, age } = toRefs(user)

toRef(): 和toRefs功能相似,将对象中的某个属性转换为响应式数据

ref、toRef 和 toRefs的区别

ref

接收一个参数,返回一个响应式数据

toRef

接收两个参数,第一个参数是哪个对象,第二个参数是对象的哪个属性。浅拷贝响应式数据里的一个属性,并保留响应式。用于为对象中的一个属性添加响应式。

toRefs

接收一个对象,遍历对象所有属性,挨个调用toRef。用于批量设置多个响应式数据。

toRefs 和 toRef

toRef 和 toRefs 可以用来复制 reactive 里面的属性然后转成 ref,而且它既保留了响应式,也保留了引用,也就是你从 reactive 复制过来的属性进行修改后,除了视图会更新,原有 ractive 里面对应的值也会跟着更新,如果你知道 浅拷贝 的话那么这个引用就很好理解了,它复制的其实就是引用 + 响应式 ref

小结

toRef/toRefs创建的数据改变都会使原数据改变 ;

ref、toRef、toRefs在js中取值需要加 ‘.value’

补充

获取DOM元素类型

浏览器console 输入document.querySelector('元素名').__proto__

获取到元素的原型对象,名字就是该元素的类型。

获取proxy对象的原数据

vue3使用proxy代替vue2的object.defineProperty,相当于在对象前设置了“拦截” ,所以当我们打印一些值的时候是proxy代理之后的是Proxy 对象,Proxy对象里边的[[Target]]才是真实的对象。

解决方法:

  1. 使用序列化获取

对原数据的深拷贝

JSON.parse(JSON.stringify(Proxy)

  1. 使用vue3中的 toRaw() 方法获取

    注:该方法返回的数据任会保持原数据的响应和引用

    import { toRaw } from 'vue'
    const res = toRaw(Proxy)
    

pinia解构失去响应式

pinia仓库做解构拿到的数据会失去响应式,要使用storeToRefs来保持响应式,这只针对简单数据类型来说。复杂数据类型解构出任保持响应式。

import { storeToRefs } from 'pinia'
const { age } = storeToRefs(store)

axios使用泛型

当使用配置写法axios()时,可以换成axios.request()。axios.request 是axios插件提供的一个支持泛型的方法,可以在request后面传入泛型,指定 data 返回值中的类型。

当使用方法别名写法axios.get()时,默认支持泛型。

路由的使用

创建路由对象

import { createRouter, createWebHistory } from 'vue-router'

// createRouter 创建路由实例,===> new VueRouter()
// history 是路由模式,hash模式,history模式
// createWebHistory() 是开启history模块   http://xxx/user
// createWebHashHistory() 是开启hash模式    http://xxx/#/user

// vite 的配置 import.meta.env.BASE_URL 是路由的基准地址,默认是 ’/‘
// https://vitejs.dev/guide/build.html#public-base-path
// 如果将来你部署的域名路径是:http://xxx/my-path/user
// vite.config.ts  添加配置  base: my-path,路由这就会加上 my-path 前缀了

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: []
})

export default router

小结:

  • 如何创建实例的方式?
    • createRouter()
  • 如何设置路由模式?
    • createWebHistory() 或者 createWebHashHistory()
  • import.meta.env.BASE_URL 值来自哪里?
    • vite.config.tsbase 属性的值
  • base 作用是什么?
    • 项目的基础路径前缀,默认是 /

路由跳转

跳转

import { useRouter } from 'vue-router'
const router = useRouter()

// 跳转传参
router.push({ name: 'user', params: { userId: '123' }})
router.push({ path: 'register', query: { userId: '123' }})

接收参数

import { useRoute } from 'vue-router'
const route = useRoute()
//query
let userId=route.query.userId;
 
//params
let userId=route.params.userId;

路由守卫权限控制

vue3中该回调函数中只有两个参数to , from。没有next。不返回或者返回true,就是放行。返回一个路径,就是强制跳转到该页面

router.beforeEach((to, from) => {
  const store = useUserStore()
  const whiteList = ['/login']
  if (!store.user?.token && !whiteList.includes(to.path)) return '/login'
})

TS中的Omit、Pick

Pick: 从类型对象中取出指定的属性类型 Omit: 从类型对象中排出指定的属性类型,得到剩余的

type OmitUser = Omit<User, 'token'>

TS中的Partial 、Required

 Required: 转换为全部必须

Partial: 转换为全部可选

type Image = {
  id: string
  url: string
}
type changeImage = Partial<Image>

TS中的InstanceType

获取组件实例的类型

InstanceType<typeof 组件>

history路由对象

是全局的路由对象

// 查看当前可以回退的历史记录
console.log(history.state.back)

场景:顶部导航栏,左侧返回按钮添加返回功能时,判断当前是否有可以回退的历史记录

组件类型校验无法生效问题

当我们使用插件自动导入组件时,会发现组件内定义的类型检查没有生效。

新建 components.d.ts文件,如下配置

// 核心代码
// 1. 导入组件实例
import NavBar from './NavBar.vue'
// 2. 声明 vue 类型模块
declare module 'vue' {
    // 3. 给 vue  添加全局组件类型,interface 和之前的合并
    interface GlobalComponents {
        // 4. 定义具体组件类型,typeof 获取到组件实例类型
        // typeof 作用是得到对应的TS类型
        VanNavBar: typeof NavBar;
    }
}

v-model

vue2 => :value="show" + @input="show = $event"

vue3,支持写多个,去掉了sync修饰符。

<!-- vue3父组件中 -->
<!-- 前一个是默认的,后一个是自定义的,类似sync写法 -->
<son-dialog v-model="show" v-model:my="show"></son-dialog>
// vue3子组件中

defineProps<{
  // 属性名固定 modelValue
  modelValue: boolean,
  my: boolean
}>()

const emit = defineEmits<{
  // 事件名固定写成 update:modelValue
  (e: 'update:modelValue', data: boolean): void,
  (e: 'update:my', data: boolean): void
}>()

组件路由守卫

onBeforeRouteLeave 组件内的路由离开守卫

// onBeforeRouteLeave vue-router组合式API,组件内的离开守卫,可以通过返回false 来取消这次页面跳转。
onBeforeRouteLeave(() => {
  // 业务需要:已经创建订单了,就不能回到上一页了
  if (orderId.value) return false
})

详情

WebSocket

原生js建立连接

// 创建ws实例,建立连接
var ws = new WebSocket("wss://javascript.info/article/websocket/demo/hello");

// 连接成功事件
ws.onopen = function(evt) { 
  console.log("Connection open ...");
  // 发送消息
  ws.send("Hello WebSockets!");
};
// 接受消息事件
ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  // 关闭连接  
  ws.close();
};
// 关闭连接事件
ws.onclose = function(evt) {
  console.log("Connection closed.");
};      

项目中常用 socket.io-client 来实现客户端通讯。

socket.io建立连接

如何使用客户端js库?

pnpm add socket.io-client

如何建立连接?浏览器和服务器建立双向通信(类似打电话)

import { io } from 'socket.io-client'
// 参数1:不传默认是当前服务域名,开发中传入服务器地址
// 参数2:配置参数,根据需要再来介绍
const socket = io(url, options)

确定连接的状态

  socket.on('connect', () => {
      // 建立连接成功会执行该回调函数
      console.log('连接成功!')
  })

  socket.on('error', (event) => {
    // 错误异常消息
    console.log('error')
  })

  socket.on('disconnect', () => {
    // 已经断开连接
    console.log('disconnect')
  })

如何发送消息?浏览器向服务器发消息

// chat message 发送消息事件,由后台约定,可变
socket.emit('chat message', '消息内容')

如何接收消息?接收服务器发送的消息

// chat message 接收消息事件,由后台约定,可变
socket.on('chat message', (ev) => {
  // ev 是服务器发送的消息
})

如何关闭连接?

// 离开组件需要使用
socket.close()

小结:

  • sockt.io 在前端使用的js库需要知道哪些内容?
    • 如何建立链接 io('地址')
    • 连接成功的事件 connect
    • 如何发消息 emit + 事件
    • 如何收消息 on + 事件
    • 如果关闭连接 close()