当前位置: 首页 > news >正文

vue3笔记(2)自用

目录

一、作用域插槽

二、pinia的使用

一、Pinia 基本概念与用法

1. 安装与初始化

2. 创建 Store

3. 在组件中使用 Store

4. 高级用法

5、storeToRefs 

二、Pinia 与 Vuex 的主要区别

三、为什么选择 Pinia?

三、定义全局指令

1.封装通用 DOM 操作,复用逻辑

2.增强模板的声明式编程能力

3.解耦组件逻辑,保持组件简洁

4.便于统一维护和扩展

四、指令钩子

1. 钩子函数列表及含义

2. 核心参数说明(以 mounted(el, binding, vnode, prevVnode) 为例)

3. 实际场景示例(用 v-focus 指令理解钩子)

4. 为什么需要指令钩子?

五、图片懒加载

六、路由传参

1、Vue 2 获取路由参数

1. 动态路由参数(路径中的参数,如 /user/:id)

2. 查询参数(URL 中的 ?key=value)

2、Vue 3 获取路由参数

1. 动态路由参数

2. 查询参数

对比

补充说明

总结

七、路由

1、默认请求参数

2、路由缓存问题

解决方法

一、作用域插槽

<template><div class="app"><el-table :data=list><el-table-column label="ID" prop="id"></el-table-column><el-table-column label="姓名" prop="name" width="150"></el-table-column><el-table-column label="籍贯" prop="place"></el-table-column><el-table-column label="操作" width="150"><template #default="{row}">//作用域插槽<el-button type="primary"  @click="updateData(row.id)" link>编辑</el-button><el-button type="danger" @click="deleteData(row.id)">删除</el-button></template></el-table-column></el-table></div>
</template>
  • #default="{row}":这是 作用域插槽(scoped slot),row 代表当前行的数据对象。
  • 每个操作按钮都能通过 row 访问当前行的所有属性(如 row.idrow.name)。

二、pinia的使用

一、Pinia 基本概念与用法

Pinia 是 Vue.js 的新一代状态管理库,由 Vue 核心团队开发,旨在替代 Vuex。它提供了更简洁的 API、更好的 TypeScript 支持和更小的体积。

1. 安装与初始化

npm install pinia
// main.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';const pinia = createPinia();
const app = createApp(App);app.use(pinia);
app.mount('#app');

或者

import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'const pinia = createPinia()
createApp(App).use(pinia).mount('#app')

2. 创建 Store

// stores/counter.js
import { defineStore } from 'pinia';// 使用组合式 API 风格
export const useCounterStore = defineStore('counter', () => {// 状态const count = ref(0);// 计算属性const doubleCount = computed(() => count.value * 2);// 方法const increment = () => {count.value++;};const reset = () => {count.value = 0;};return { count, doubleCount, increment, reset };
});// 或使用选项式 API 风格(类似 Vuex)
export const useUserStore = defineStore({id: 'user',state: () => ({name: '张三',age: 20}),getters: {fullName: (state) => `用户: ${state.name}`},actions: {updateName(newName) {this.name = newName;}}
});

3. 在组件中使用 Store

<template><div><p>Count: {{ counter.count }}</p><p>Double: {{ counter.doubleCount }}</p><button @click="counter.increment">+1</button><button @click="counter.reset">重置</button></div>
</template><script setup>
import { useCounterStore } from '../stores/counter';// 获取 store 实例
const counter = useCounterStore();// 直接访问 state
console.log(counter.count);// 调用 action
counter.increment();
</script>

4. 高级用法

// 订阅 state 变化
const unsubscribe = counter.$subscribe((mutation, state) => {console.log('State changed:', mutation.type, state);
});// 重置 state
counter.$reset();// 批量修改 state
counter.$patch({count: 100,// 其他属性
});// 插件示例
const loggerPlugin = (context) => {console.log('Store initialized:', context.store.$id);return {// 扩展 storehello: 'world'};
};pinia.use(loggerPlugin);

5、storeToRefs 

storeToRefs 是 Pinia 提供的一个辅助函数,用于从 store 中提取状态并保持其响应式特性。它解决了在解构 store 时丢失响应式的问题,让你可以更优雅地在组件中使用 store 状态。

  • 问题:直接解构 store 对象(如 const { count } = store)会丢失响应式。
  • 解决方案storeToRefs 将 store 中的状态转换为响应式引用(refs),保持响应式更新。
// ❌ 错误:解构后丢失响应式
const { count } = store;
// ✅ 转换为响应式引用
const { count } = storeToRefs(store);

 storeToRefs用于响应式数据、coumputed。对于方法,直接解构即可。

二、Pinia 与 Vuex 的主要区别

特性PiniaVuex (4.x)
API 风格组合式 API 为主,支持选项式仅支持选项式 API
模块化方式自动模块,无嵌套结构需要手动定义模块
TypeScript 支持一流支持,无需额外配置需要额外配置,类型定义复杂
体积更小(~1kb)较大(~2kb)
Mutation无 Mutation,直接修改 state必须通过 Mutation 修改 state
代码结构更简洁,更少样板代码更多样板代码(state/getters/mutations/actions)
插件系统更灵活相对固定
DevTools 支持更现代,支持时间旅行调试支持时间旅行调试
Vue 版本支持Vue 2 和 Vue 3Vue 2 和 Vue 3

三、为什么选择 Pinia?

  1. 更简洁的 API

    • 无需 mapStatemapActions 等辅助函数。
    • 直接通过 store.counter 访问状态,store.increment() 调用方法。
  2. 更好的 TypeScript 支持

    • 自动类型推导,无需手动定义接口。
    • 组合式 API 天然支持类型安全。
  3. 无 Mutation

    • 移除了 Vuex 中冗余的 Mutation 概念,直接在 Action 中修改 state。

三、定义全局指令

Vue 本身有像 v-ifv-showv-model 这类内置指令,方便开发者操作 DOM、处理数据渲染等。而全局指令,是开发者借助 Vue 提供的机制,自己定义的、可在整个 Vue 应用里通用的指令。

它基于 Vue 的指令系统拓展而来,注册后,在所有组件的模板中,都能用 v-指令名 的形式(比如定义了全局指令 v-focus,就可以在任意组件 <input v-focus /> 这样用 )去调用,来实现特定功能。

1.封装通用 DOM 操作,复用逻辑

Vue 组件主要聚焦数据和界面的映射,但有些频繁的 DOM 底层操作(如元素自动聚焦、自定义滚动行为、表单输入格式化等 ),若在每个组件里重复写,既冗余又难维护。全局指令可以把这类操作封装起来,一处定义,处处使用。
比如定义一个 v-focus 全局指令,实现元素挂载后自动获取焦点:

// Vue2 中全局指令注册(main.js 里)
Vue.directive('focus', {inserted: function (el) { el.focus() }
})// Vue3 中全局指令注册(main.js 里)
const app = createApp(App)
app.directive('focus', {mounted(el) {el.focus()}
})

之后在任意组件的输入框上用 <input v-focus />,就能自动聚焦,不用在每个组件里写获取焦点的逻辑。

2.增强模板的声明式编程能力

全局指令能让模板更简洁、语义化。
比如定义 v-permission 指令控制按钮权限:

// 假设根据用户权限决定元素是否显示
app.directive('permission', {mounted(el, binding) {const hasPerm = checkPermission(binding.value) if (!hasPerm) {el.parentNode && el.parentNode.removeChild(el) }}
})

模板中用 <button v-permission="'delete'">删除</button>,语义清晰,一看就知道和权限控制有关,无需在组件里写一堆权限判断的 JS 逻辑。

3.解耦组件逻辑,保持组件简洁

把和 DOM 操作强相关的代码(比如复杂的动画初始化、第三方库初始化 )放到指令里,组件就不用关心这些细节,专注业务数据和基本渲染。
比如用指令初始化一个图表库:

app.directive('chart', {mounted(el, binding) {const chart = new Chart(el, binding.value.config) // 后续更新等逻辑也可在指令钩子处理}
})

组件里只需 <div v-chart="{ config: { /* 图表配置 */ } }"></div>,组件代码更简洁,职责更单一。

4.便于统一维护和扩展

当需要修改指令功能(如调整权限判断逻辑、优化聚焦的时机 ),只需改全局指令的定义,所有用到该指令的组件都会生效,不用逐个组件修改,大大提升了代码的可维护性。

    四、指令钩子

    在 Vue 中,指令钩子(Directive Hooks) 是指令定义对象里的一组生命周期函数,用于在指令绑定的 DOM 元素的不同生命周期阶段执行自定义逻辑。它们像 “钩子”,能让你在元素从创建到销毁的各个关键节点介入,做一些 DOM 操作、数据处理或逻辑控制。

    1. 钩子函数列表及含义

    钩子函数执行时机(Vue 3 场景)典型用途
    created指令绑定到元素后,DOM 渲染前调用(元素还没插入 DOM 树)可初始化一些与元素关联的 “纯数据逻辑”,比如根据指令参数准备数据,或添加事件监听(但注意此时 DOM 可能不可操作)
    beforeMount元素即将插入 DOM 树前调用(元素已存在于虚拟 DOM,但未真实渲染到页面)做一些 DOM 插入前的准备,比如根据指令动态修改元素属性(如 el.setAttribute ),但元素还没在页面可见
    mounted元素插入 DOM 树后调用(真实 DOM 已渲染,可操作)最常用!比如操作 DOM(聚焦输入框 el.focus() )、初始化第三方库(基于 DOM 渲染图表、地图 )
    beforeUpdate元素所在组件更新前调用(组件数据变化,虚拟 DOM 准备对比更新,真实 DOM 还未改变)可在更新前记录元素状态(比如旧的滚动位置、样式 ),用于更新后恢复或对比
    updated元素所在组件更新后调用(虚拟 DOM 对比完成,真实 DOM 已更新)基于新的 DOM 状态做调整(比如重新计算元素尺寸、更新第三方库的配置 )
    beforeUnmount元素所在组件卸载前调用(组件即将销毁,DOM 还未移除)清除定时器、移除自定义事件监听(避免内存泄漏),比如 el.removeEventListener
    unmounted元素所在组件卸载后调用(DOM 已从页面移除)收尾工作,比如释放第三方库占用的资源、彻底清理与该元素相关的全局状态

    2. 核心参数说明(以 mounted(el, binding, vnode, prevVnode) 为例)

    • el指令绑定的真实 DOM 元素,可直接操作(如 el.style.color = 'red' )。
    • binding:指令的绑定信息,包含:
      • binding.value:指令的参数值(如 v-my-directive="100" 里的 100 )。
      • binding.arg:指令的参数(如 v-my-directive:foo 里的 foo )。
      • binding.modifiers:指令的修饰符(如 v-my-directive.bar 里的 { bar: true } )。
    • vnode:Vue 生成的虚拟 DOM 节点(描述当前元素的虚拟结构,一般用于高级场景,如对比新旧虚拟节点差异 )。
    • prevVnode:上一次的虚拟 DOM 节点(仅在更新 / 卸载阶段存在,用于对比变化 )。

    3. 实际场景示例(用 v-focus 指令理解钩子)

    需求:定义一个 v-focus 指令,让输入框插入 DOM 后自动聚焦。

    <script setup>
    import { createApp } from 'vue'const app = createApp(App)// 注册全局指令
    app.directive('focus', {// 元素插入 DOM 后执行(此时可操作真实 DOM)mounted(el) {el.focus() // 让输入框自动聚焦}
    })
    </script><template><!-- 使用指令 --><input v-focus placeholder="自动聚焦的输入框" />
    </template>
    

    如果需要在组件更新后,根据条件重新聚焦,就可以用 updated 钩子:

    app.directive('focus', {mounted(el) {el.focus()},updated(el, binding) {// 如果指令参数为 true,更新后重新聚焦if (binding.value) {el.focus()}}
    })
    

    在模板中动态控制:

    <input v-focus="shouldFocus" placeholder="动态聚焦" />
    

    4. 为什么需要指令钩子?

    • 精准控制 DOM 生命周期:Vue 是数据驱动的框架,直接操作 DOM 容易和响应式逻辑冲突。指令钩子让你在 “安全” 的时机操作 DOM(比如 mounted 确保元素已渲染 )。
    • 复用 DOM 操作逻辑:把聚焦、权限控制、第三方库初始化等逻辑封装到指令,避免在组件里重复写(比如 10 个输入框都要自动聚焦,用 v-focus 一行解决 )。
    • 解耦组件与 DOM 操作:组件只关心业务数据,DOM 操作细节交给指令,代码更简洁、职责更清晰。

    总结:指令钩子是 Vue 指令在 DOM 不同生命周期阶段的 “切入点”,让我们能优雅地操作 DOM、复用逻辑,同时避开直接操作 DOM 带来的风险,是 Vue 拓展 DOM 能力的核心机制之一。

    五、图片懒加载

    核心原理:图片进入视口区才发送资源请求

    思路:定义一个全局自定义指令;利用vueUse的useIntersectionObserver判断元素是否进入视口;进入视口后再给img标签的src添加url地址

    //main.jsimport { useIntersectionObserver } from '@vueuse/core'const app = createApp(App)app.use(createPinia())
    app.use(router)app.mount('#app')// 定义全局指令
    app.directive('pic-lazy',{mounted(el,binding){//el:指令绑定的元素//binding:指令对象console.log(el,binding)// 监听元素是否进入视口useIntersectionObserver(el,// 回调函数,isIntersecting 表示是否进入视口([{ isIntersecting }]) => {if (isIntersecting) {// console.log('元素进入视口啦!')// 这里可以写进入视口后的逻辑,比如加载图片、统计曝光等// stop() // 如果只想监听一次,进入视口后可以停止监听el.src = binding.valueconsole.log(isIntersecting)}},// 可选配置:设置进入视口的“占比”阈值(0 ~ 1),默认 0.1(即 10% 进入视口就算){ threshold: 0.5 })}
    })
    

    对于图片标签,把原来的<img :src="……" alt="" /> 改为 <img v-pic-lazy="……" alt="" />

    六、路由传参

    1、Vue 2 获取路由参数

    在 Vue 2 中,路由参数主要通过 $route 对象获取,支持动态路由参数查询参数

    1. 动态路由参数(路径中的参数,如 /user/:id

    // 路由配置
    {path: '/user/:id',component: User
    }// 在组件中获取
    export default {computed: {userId() {return this.$route.params.id; // 获取路径中的 :id 参数}},created() {console.log(this.$route.params.id); // 直接访问}
    }
    

    2. 查询参数(URL 中的 ?key=value

    // URL: /user?name=john&age=20
    export default {computed: {userName() {return this.$route.query.name; // 获取查询参数 name},userAge() {return this.$route.query.age;  // 获取查询参数 age}}
    }
    

    2、Vue 3 获取路由参数

    Vue 3 的组合式 API(Composition API)改变了获取路由参数的方式,需要通过 useRoute 函数引入路由实例。

    1. 动态路由参数

    import { useRoute } from 'vue-router';export default {setup() {const route = useRoute();// 方式一:直接在 setup 中使用console.log(route.params.id);// 方式二:定义为响应式数据(推荐)const userId = computed(() => route.params.id);return {userId};}
    }
    

    2. 查询参数

    import { useRoute } from 'vue-router';export default {setup() {const route = useRoute();// 获取查询参数const userName = computed(() => route.query.name);const userAge = computed(() => route.query.age);return {userName,userAge};}
    }
    

    对比

    场景Vue 2.xVue 3.x
    动态路由参数this.$route.params.idconst route = useRoute();
    route.params.id
    查询参数this.$route.query.nameconst route = useRoute();
    route.query.name
    响应式处理通过 watch 监听 $route使用 computed 或 watch 监听 route

    补充说明

    1. Vue 3 中仍可使用 $route
      在 Vue 3 的选项式 API(Options API)中,仍可通过 this.$route 访问路由参数,但组合式 API 推荐使用 useRoute

    2. 响应式处理

      • 在 Vue 3 中,路由参数变化时,需要使用 computed 或 watch 确保数据响应式更新:
        import { useRoute, watch } from 'vue-router';setup() {const route = useRoute();// 监听路由变化watch(() => route.params.id, (newId, oldId) => {console.log('ID 变化:', newId);});
        }
        
    3. 路由配置示例
      无论是 Vue 2 还是 Vue 3,路由配置中定义参数的方式相同:

      // 路由配置(Vue 2/Vue 3 通用)
      {path: '/user/:id', // 动态路由参数name: 'User',component: User
      }
      

    总结

    • Vue 2:依赖 this.$route 对象,在 createdcomputed 等选项中使用。
    • Vue 3:通过 useRoute() 函数获取路由实例,在组合式 API 的 setup 函数中使用,更灵活且类型安全。

    七、路由

    1、默认请求参数

    下面这个接口默认传的值为1

    function getNewthingAPI({params={}}){const {distributionSite='1'}=paramsreturn httpInstance({url:'/home/new',params:{distributionSite}})
    }

    调用时可以对这个值进行指定

    getBannerAPI( {params: { distributionSite: '2' }})

    如果不指定,就使用默认值1

    getBannerAPI()

    2、路由缓存问题

    路由缓存问题通常指在单页应用( Vue、React)中,切换路由后组件状态未重置或数据未更新,主要原因包括:

    • 组件复用机制:路由切换时,若新旧路由使用同一组件(如动态路由 /:id),框架可能复用该组件实例,导致组件内的数据、生命周期钩子(如 createdmounted)不重新执行,状态保留旧值。
    • 缓存策略设置:主动使用缓存配置(如 Vue 的 <keep-alive>)时,未正确配置缓存范围,导致不需要缓存的组件被缓存,数据未刷新。
    • 数据依赖未更新:组件数据依赖路由参数,但未监听路由参数变化,参数改变后未触发数据重新获取(如 Vue 中未使用 watch 或 onBeforeRouteUpdate 监听 $route.params)。
    • 异步数据加载时机:数据请求放在 mounted 等只执行一次的钩子中,路由切换后组件复用,钩子不再触发,导致新数据未加载。

    解决方法

    1. 禁用不必要的缓存
      若无需缓存组件,移除 <keep-alive> 或通过 exclude 排除特定组件:

      <!-- Vue中排除不需要缓存的组件 -->
      <keep-alive exclude="DetailPage"><router-view />
      </keep-alive>
      
    2. 监听路由参数变化
      在复用组件中,监听路由参数变化并重新加载数据:

      // 方法1:使用watch监听$route
      watch: {$route(to, from) {if (to.params.id !== from.params.id) {this.loadData(to.params.id); // 重新加载数据}}
      }// 方法2:使用导航守卫
      onBeforeRouteUpdate((to) => {this.loadData(to.params.id);
      });
      
    3. 强制组件重新渲染
      通过 key 使组件实例重新创建:

      <!-- Vue中给router-view添加key,确保路由变化时重新渲染 -->
      <router-view :key="$route.fullPath" />
      

      $route.fullPath 包含完整路由信息,参数变化时 key 改变,触发组件重新挂载。

    4. 调整数据加载时机
      将数据请求放在每次路由进入时都执行的钩子中,onMounted 结合watch监听。

    5. 清除缓存数据
      在组件离开时手动重置状态(如 Vue 的 onBeforeUnmount 或 React 的 useEffect 清理函数):

      // Vue中离开组件时重置数据
      onBeforeUnmount() {this.data = null;
      }
      

    http://www.lryc.cn/news/597093.html

    相关文章:

  • 微软2025教育AI报告:教育群体采用AI的比例显著提升
  • 技术速递|使用 Semantic Kernel 与 A2A 协议构建多智能体解决方案
  • Qt 样式表(QSS):打造个性化界面
  • 【前端】【Vue DevTools】Vue DevTools 进阶:用 Trae / Cursor 替换 VSCode 打开文件(跳转行列无误)
  • 论文略读:Knowledge is a Region in Weight Space for Finetuned Language Models
  • iOS上使用WebRTC推拉流的案例
  • 想曰加密工具好用吗?本地安全、支持多算法的加密方案详解
  • ZLMediaKit流媒体服务器WebRTC页面显示:使用docker部署
  • 基于Matlab传统图像处理技术的车辆车型识别与分类方法研究
  • 【第三章自定义检视面板_创建自定义编辑器_如何创建自定义PropertyDrawer(9/9)】
  • 第六章 W55MH32 UDP Multicast示例
  • 在离线 Ubuntu 22.04机器上运行 ddkj_portainer-cn 镜像 其他相关操作也可以复刻 docker
  • CCD工业相机系统设计——基于FPGA设计
  • 【后端】FastAPI的Pydantic 模型
  • 【Linux-云原生-笔记】keepalived相关
  • 蒙牛社交电商的升级路径研究:基于开源链动2+1模式、AI智能名片与S2B2C商城小程序源码的融合创新
  • 轻量化RTSP视频通路实践:采集即服务、播放即模块的工程解读
  • 【Redis】在Ubentu环境下安装Redis
  • RCE随笔-奇技淫巧(2)
  • 【Linux-云原生-笔记】Haproxy相关
  • ros0基础-day18
  • OCP NIC 3.0 Ethernet的multiroot complex和multi host complex的区别
  • Android多开实现方案深度分析
  • 【硬件】Fan in和Fan out
  • RAG深入理解和简易实现
  • 海信IP501H-IP502h_GK6323处理器-原机安卓9专用-优盘卡刷固件包
  • springcloud环境和工程搭建
  • 中国多媒体与网络教学学报编辑部中国多媒体与网络教学学报杂志社2025年第6期目录
  • 论文略读:Mitigating Catastrophic Forgetting in Language Transfer via Model Merging
  • 旋变调零技术介绍与方法