type
status
date
slug
summary
tags
category
icon
password

父子组件数据传递

父传子

父组件:
:将数据作为一个属性给传出去
子组件:
defineProps: vue的一个全局函数
用props来接受,用toRefs将其变为响应式的:

子传父

用emit来实现
父组件:
  • 写一个方法,这个方法接收参数,这个参数就可以是子组件传过来的值
子组件:
  • emit:子组件可以通过在事件上使用$emit()方法来触发自定义事件,并将数据传递回父组件。当子组件触发自定义事件时,父组件可以使用v-on指令或者简写语法@来监听该事件,并在事件触发时调用指定的方法
  • 当子组件触发自定义事件时,Vue实际上是通过事件总线的方式来实现将事件传递回父组件的。在Vue中,每个组件实例都有一个相应的事件总线,该事件总线充当了组件内部事件的中心点。当子组件触发自定义事件时,Vue实际上是在该组件实例的事件总线中触发该事件,并将任何传递的数据作为参数传递给该事件

基于Vite创建

以前Vue2项目基本上是通过vue-cli(是一款脚手架,用webpack创建vue项目)来创建的,现在Vue3多是使用vite来创建项目。
  • 支持ts开箱即用
  • 按需编译
💡
Vite为什么比webpack快?
  • Webpack:先进行打包,然后引入main.js后才把东西渲染到浏览器
  • Vite:
    • 不需要先打包,直接用module,没有经过打包的过程
      • 现在的浏览器支持Es module,Vite可以直接用Es module的语法导出一个模块,然后在index.html引入模块,然后浏览器自己加载模块。
    • 构建项目的时候,Vite会按需构建项目,如果还没有来到某个页面,就不会构建那个页面的代码。而Webpack是直接把整个项目都构建出来
    • vite预构建与按需编译的过程,都是使用esbuild完成的,esbuild是用go语言编写的,可以充分利用多核CPU的优势,所以vite开发环境下的预构建与按需编译速度,都是非常快的
  • create-vue 是 Vue3 的专用脚手架,使用 vite 创建 Vue3 的项目,也可以选择安装需要的各种插件,使用更简单
  • 用npm引入vite
  • 输入项目名称
  • 选择vue项目
  • 选择vue-ts(或者是选择语言,选TypeScript)
  • 此时已经创建完毕

基于Rsbuild创建

Rsbuild 是由 Rspack 驱动的高性能构建工具,它默认包含了一套精心设计的构建配置,提供开箱即用的开发体验,并能够充分发挥出 Rspack 的性能优势。
Rspack:
  • 是一个基于Rust编写的高性能JavaScript打包工具,它提供对webpack 生态良好的兼容性,能够无缝替换 webpack,并提供闪电般的构建速度
  • 提供 5 ~ 10 倍的构建性能
    • notion image
Rsbuild:
  • 是目前webpack和Rspack生态中最快的工具,可以较为快速地把项目从Vite或vue-cli迁移到Rsbuild
  • 从Vite迁移:
    • 移除Vite,添加Rsbuild
      • 把package.json中的scripts更新为Rsbuild的cli命令
        • 创建配置文件rsbuild.config.ts,并添加:
          • 构建入口
            • Rsbuild 与 Vite 默认的构建入口不同,Vite 使用 index.html 作为默认入口,而 Rsbuild 使用 src/index.js
              首先删除index.html中的<script>标签,然后在rsbuild.config.ts中添加如下配置:
              这样Rsbuild就会在构建时自动注入<script>标签到生成的HTML文档中

        工程结构

        env.d.ts
        • /// <reference types="vite/client" />
          • 让ts能够识别.ts以外的文件
        index.html
        • 入口文件
        main.ts
        • import { createApp } from 'vue’ 引入创建应用的工具
        • import App from './App.vue' 引入根组件
        • app.mount(’#id’) 将应用挂载在index.html中,id为id的div标签里

        OptionAPI & CompositionAPI

        • Vue2是选项式API,而Vue3是组合式API
          • Vue2数据、方法、计算属性是分散在data、methods、computed中的。如果要新增一个需求,就需要分别修改这几个选项,不便于维护和复用
          • 组合式API可以让功能相关的代码更有序地组织在一起

        setup

        • 是Vue3中一个新的配置项,值是一个函数,compositionAPI所用到的数据、方法、计算属性、监视等均配置在setup中
        • OptionalAPI和setup配置项是可以同时存在的
          • setup里面的数据比data里面的数据先配置,所以data里面可以使用setup里面的变量(用this.xxx引用)
          • 但是setup 里面不能使用data里面的数据

        setup语法糖

        在标签里面写setup,就不用写setup() {return xxx}了

        响应式数据

        ref

        定义响应式变量
        • 返回值:一个RefImpl的实例对象,即ref对象或ref,ref的value属性是响应式的
        • 用xxx.value = yyy来修改它的值
        定义对象类型的响应式数据
        • RefImpl对象的value属性此时是一个Proxy对象
        • 实际上ref是在reactive的基础上处理对象类型的数据的

        reactive

        • 借助proxy实现
        • 从原对象生成响应式对象
        • 直接用xxx.属性修改变量的属性,而不用通过value

        ref和reactive对比

        • ref创建的对象必须使用.value来访问其值
        • reactive重新分配一个对象,会失去响应式(可以使用Object.assign去整体替换
        • 但是ref就可以直接重新分配对象且不会失去响应式
        总结:
        • 基本类型的响应式数据:ref
        • 层级不深的响应式对象:ref和reactive都可以
        • 层级较深的响应式对象:reactive

        toRefs和toRef

        解构响应式对象的属性,并且解构出来的变量也具有响应式
        如果直接解构响应式的对象,结构出来的变量将丢失响应式!!!!

        toRefs

        • 把对象结构为ref类型的响应式变量,这样结构出来的变量也是响应式的ref类型
        • 可以通过修改name来修改person.name,name有点像指针的作用

        toRef

        • 只取响应式对象的一个属性

        computed计算属性

        • 有缓存,一样的数据只会计算一次;普通的方法来实现计算的话调用几次就会计算几次
        • 只有在计算属性依赖的属性发生变化时,才会重新计算
        • 返回的是一个只读的值,不能直接修改res
          • 使用getter和setter来修改

        watch监视

        监视以下四种数据的变化:
        • ref定义的数据
          • 回调函数应有两个参数
          • 基本类型数据
            • 监视的是value是否变化,但监视的是refImpl类型的数据
            • vue3的watch终止监听,只需要将watch赋值给一个变量,当达到条件调用watch赋值的那个变量就可以终止监听了。
          • 对象类型数据
            • 监视的是对象的地址值(数据整个被替换),如果想监听对象内部的数据,要手动开启深度监视
            • watch的第三个参数
        • reactive定义的数据
          • 默认开启了深度监视
          • newValue和oldValue是相同的,因为reactive创建的变量就算值变了地址也相同
        • 函数返回一个值(getter函数)
          • 监视响应式对象中的某个属性,且该属性是基本数据类型的,要写成函数式
            • 监视响应式对象中的某个属性,并且该属性是对象时,要写函数式+开启深度监视(开启后才可以监视该属性的属性的变化)
          • 一个包含上述内容的数组

          watchEffect

          • 立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数
            • 不用写明要监视的变量或对象
            • 回调函数在一开始就会执行一次
            • 回调函数用到的变量发生变化时会执行一次

          标签的ref属性

          之前习惯用id来获取一个html标签然后用js改它的值,但是vue里面组件太多可能会出现多个组件里面的不同标签不小心就被命名了同一个id,然后getElementById的时候就会出问题,就可以用ref来解决这个问题。
          之前写的积分商城数据大屏举例:
          就可以实现类似于局部变量的东西
          局部样式则可以这样实现:这样定义的样式就不会选中其他组件里面的标签
          • 如果在组件标签上面写ref,得到的就是组件实例

          生命周期、钩子/生命周期函数

          Vue中实例从创建到销毁的过程就是生命周期,即指从创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程。
          💡
          DOM DOM(Document Object Model)即文档对象模型,是W3C制定的标准接口规范,是一种处理HTML和XML文件的标准API。 DOM提供了对整个文档的访问模型,将文档作为一个树形结构,树的每个结点表示了一个HTML标签或标签内的文本项。 DOM模型不仅描述了文档的结构,还定义了结点对象的行为,利用对象的方法和属性,可以方便地访问、修改、添加和删除DOM树的结点和内容。 DOM的作用就是为了让JavaScript可以对文档中的标签、属性、内容等进行 访增删改 操作。

          Vue2

          创建:
          • 创建前:beforeCreate
          • 创建完毕:created
          挂载:
          • 挂载前:beforeMount
          • 挂载完毕:mounted
          更新:
          • 更新前:beforeUpdate
          • 更新完毕:updated
          销毁:
          • 销毁前:beforeDestroy
          • 销毁完毕:destroyed

          Vue3

          创建:
          创建前和创建完毕合并为setup函数
          挂载:
          生命周期函数需要import引入,挂载前(onBeforeMount)和挂载完毕(onMounted)都是在Vue2的基础上加个on
          挂载完毕后,会调用onMounted指定的回调函数,而不是调用onMounted
          更新:
          生命周期函数跟挂载一样,也是前面加一个on
          卸载:
          • 卸载前:onBeforeUnmount
          • 卸载完毕:onUnmounted

          父子组件的生命周期

          按照Vue的解析流程来理解,有点像深度搜索:
          先看index.html → 发现App.vue → 发现里面引入了子组件 → 解析子组件 → 继续解析父组件
          父:beforeCreate 首先初始化父组件 父:created 父组件挂载数据 父:beforeMounte 父组件开始挂载dom,tpl里遇到子组件 子:beforeCeate 子组件开始挂载数据 子:created 子组件数据挂载成功 子:beforeMount 子组件开始挂载dom 子:mounted 子组件dom挂载结束 父:mounted 父组件dom挂载结束 父:beforeUpdate 下面开始类似于dom事件流 子:beforeUpdate 子:updated 父:updated 父:beforeDestory 子:beforeDestory 子:destroyed 父:destoryed

          自定义Hooks

          💡
          自定义Hooks是一种函数,它接受一些参数,并返回一个或多个响应式的数据和方法。这些Hooks可以包含任意逻辑,如数据获取、状态管理、副作用处理等,而且可以在多个组件中进行共享和复用。通过使用自定义Hooks,我们可以将组件中的共享逻辑提取出来,使得代码更加清晰、可维护性更高。
          就比如说,实现一个功能需要好几个function,但是如果将这些function都混在scripts里面,就跟vue2没有区别了(mixin),因此需要将这好几个function直接封装以实现特定功能——hoos
          hooks实际上就是一个js或者ts文件,但是命名一般叫useXXX
          调用钩子:
          常用的hooks:
          这个钩子需要一个参数url,因此使用它的时候也要传参

          路由

          route就是一条路由,它将一个URL路径和一个函数进行映射,例如:
          这就是两条路由,当访问 /users 的时候,会执行 getAllUsers() 函数;当访问 /users/count 的时候,会执行 getUsersCount() 函数。

          实现路由环境

          • 首先要从vue-router引入createRouter
          • 然后用createRouter创建路由。配置项:
            • history:
              • history 定义了路由使用的历史记录模式。
              • createWebHistory 使应用使用 HTML5 的 History API 来管理路由,使 URL 看起来干净、没有 # 符号。使用这种模式时,页面路径会显示成普通的 URL(例如:/home/about),而不会带有 #(如:/home 而不是 /#/home)。
              • 使用 createWebHistory 时,服务器需要进行配置,以防止直接访问页面时出现 404 错误。
            • routes:
              • 一个数组,即路由器(router)管理的路由(route)
              • 必须包含path、component
          • 在main.ts引入router,这样路由器才可以被整个程序使用
            • 规定不同路由对应的组件放在什么位置
              • 在components.d.ts中显式地定义了RouterView是一个全局组件 这段代码确保了 TypeScript 对 RouterView 这个全局组件有正确的类型支持,尽管在实际开发中,你不需要手动导入 RouterView
                • router-view 自动全局注册: 当你在应用中注册了 vue-router 后,vue-router 自动将 router-view 组件注册为全局组件。因此,在你的任何 Vue 组件中(例如 App.vue),你可以直接使用 <router-view /> 而不需要手动引入它。
              • 然后在App.vue中使用占位符,让Vue Router将对应的页面(组件)渲染到正确的位置

              实现页面的跳转

              • 用router-link来实现
                • 这就表示点击这个按钮时,跳转到name为create的路由对应的页面
                • 并且这个按钮的样式和文字可以在里面规定(不要那个span也可以,这里有是因为有图标要展示)
              • 激活按钮时的样式
                • .router-link-active 的自动添加
                  • 当使用 Vue Router 的 router-link 组件时,Vue 会自动为与当前路由匹配的链接添加 .router-link-active 类。因此,不需要手动管理激活状态,Vue Router 会根据用户点击的路由自动处理。
                • 然后,CSS 中针对 .router-link-active 类定义的样式就会生效,改变按钮的背景色、文本颜色等,使得按钮的样式发生变化,提供用户点击后的视觉反馈。

              路由器工作模式

              • history模式
                • URL更美观,不带#
              • hash模式
                • 兼容性好,不需要服务器端处理路径

              参数

              普通的组件是可以用标签来传参的,而靠路由规则展示出来的组件的标签没有机会被写出来,所以需要特殊的方法来给这些组件传参

              query参数

              可以直接写在router-link的 :to 属性里面
              • 这样写了之后参数就会直接体现在url中
              • 接收参数
                • 在要接受参数的组件里面从vue-router引入useRoute
                  route里面就会有传进来的query参数,并且将其整理为了一个对象,用route.query.参数名来访问
              • 将js的变量值作为参数,加反引号将里面的内容做成模板字符串,然后用${}嵌入js
                • 此时route里面的query就有个属性是a,值为vara.id并且是动态的
              第二种写法,更简洁易懂
              • 需要使用参数时将query属性给解构出来

                params参数

                也是直接采用query参数的第二种方式
                • 但是不能直接写path,只能写name,否则会被浏览器忽略!!!并且路由配置文件里面的path要写占位符
                  • route/index.ts:
                • 在占位符后面加问号表示参数的必要性:id可传可不传

                  路由的props配置

                  • 将路由收到的所有params参数全部作为props传给路由组件
                    • 只需要在路由配置里面加一行props:true,允许将 路由参数(params)自动作为 props 传递给组件
                      缺点是只能传params类型的参数不能传query类型的参数
                      Scan组件:
                  • 第二种写法:
                    • 可以自己决定将什么作为props传给路由组件,可以传query也可以传params
                      Scan组件跟第一种没有区别

                  路由的replace属性

                  push:往浏览器历史记录的栈里push一个(默认模式)
                  replace:直接将历史记录里面的上一个给替换掉(按那个←的时候不会返回上一个页面)
                  • router-link标签里面添加replace属性可以将路由变为replace模式

                  编程式路由导航

                  脱离<RouterLink>标签实现路由跳转
                  • 在ts里面实现
                    • 在html里面实现,点击按钮就跳转页面
                      • 实现翻页:
                    • 当然也可以@click之后调用一个函数,然后在ts里面跳转路由

                    路由守卫

                    Vue的路由导航守卫是通过 Vue Router 提供的生命周期钩子来控制路由跳转的。它的实现原理基于 Vue Router 的内部机制,当路由发生变化时,Vue Router 会通过导航守卫来拦截并控制路由跳转。这些守卫允许我们在路由跳转前、跳转后、或者路由守卫内部的其他生命周期点进行逻辑控制,例如验证用户是否已登录、权限检查、数据预加载等。
                    • 全局守卫
                      • beforeEach:路由跳转前触发,适用于全局的跳转控制。
                      • afterEach:路由跳转后触发,适用于全局的跳转后处理。
                    • 路由独享守卫
                      • beforeEnter:仅在路由配置上定义,专门控制某个路由的进入行为。
                    • 组件内守卫
                      • beforeRouteEnter:当路由进入该组件之前调用。
                      • beforeRouteUpdate:当路由发生变化,且目标路由与当前路由属于同一个组件时调用。
                      • beforeRouteLeave:当路由离开当前组件时调用。

                    Pinia

                    集中式状态管理:vue2用的vuex,vue3就可以用pinia了。多个组件共享数据有时候很方便
                    • Pinia 是 Vuex4 的升级版,也就是 Vuex5
                    • Pinia 极大的简化了Vuex的使用,是 Vue3的新的状态管理工具
                    • Pinia 对 ts的支持更好,性能更优, 体积更小,无 mutations,可用于 Vue2 和 Vue3
                    • Pinia支持Vue Devtools、 模块热更新和服务端渲染
                    • 用npm安装pinia
                    App.vue引入并创建:
                    此时pinia环境已经搭建好了
                    拿之前的todolist为例:
                    store:从本地存储里面拿数据出来存到lists
                    要用lists的地方:

                    Pinia与响应式

                    • Pinia 与 Vue 的响应式系统: Pinia 使用 Vue 的 reactive API(在 Vue 2 中,它依赖于 Vue.observable)将状态对象变成响应式的。这意味着当你修改 Pinia store 中的数据时,Pinia 会检测到数据的变化并自动触发更新,正如 Vue 处理组件 data 的响应式更新一样。
                    • 直接修改 Pinia 的 store 状态: 在 Vue 2 中,Pinia 的 store 本质上是一个响应式对象,所有通过 state() 函数返回的数据都会自动转化为响应式数据。因此,你可以直接修改 store 中的属性,视图会自动更新,因为 Vue 响应式系统会跟踪这些变化。

                    修改数据

                    方法一:
                    • 定义数据时使用响应式
                    方法二:$patch,数据很多时批量修改
                    • $patch
                      方法三:actions
                      • 是一个对象,里面放着很多动作函数
                      • 写在store目录下的defineStore里面

                        插槽 slot

                        跟组件的复用有关系,有时候需要组件的样式一样但内容不一样,有了插槽就不用新写一个组件了

                        默认插槽

                        现在是想在action-button这个组件里面插入一个图标,插槽的作用就是告诉action-button组件,这个图标具体应该插入在什么位置
                        ActionButton.vue里面有这么一行,就可以告诉组件把它中间夹着的那些内容插入到哪里:

                        具名插槽

                        就是一个槽不够用,需要好几个槽——给槽命名:v-slot,只能放在template或者组件标签上
                        ActionButton.vue里面就有这么一行:

                        作用域插槽

                        就是子组件通过插槽将数据传给父组件
                        假设子组件那里有一个数组,叫games,父组件需要用这个数组
                        • 这样就通过插槽将games数组传出去了
                        父组件接收数据:
                        • 父组件接收到的params是一个对象,里面包含games和a,因此其实可以直接解构param里面的属性
                          • 这样就可以在父组件遍历子组件的数据了
                          • 实现数据子传父的就是作用域插槽

                          vue3的双向数据绑定原理和响应式原理以及和vue2响应式的区别

                          Vue3响应式原理

                          1. 响应式:基于ES6的Proxy对象实现
                            1. 属性被读取时,会触发getter函数,Vue会将当前的watcher对象添加到依赖列表中,当属性被修改时,会触发setter函数,Vue会通知依赖列表中的每个watcher对象进行更新操作,从而保持数据和视图的同步
                            2. 使用Proxy可以监听对象的所有属性变化,包括新增和删除,解决了Vue 2中无法监听数组和新增属性的问题
                            3. 直接监听对象,不需要递归遍历对象的所有属性,提高了效率。
                          1. 虚拟dom:Vue3使用虚拟dom来高效地更新视图。在初始化时,Vue会将模板编译为虚拟DOM树,并将其与真实DOM进行比较,找出差异并更新。当数据改变时,Vue会重新生成虚拟DOM树,并与之前的虚拟DOM树进行比较,找出差异并更新。通过虚拟DOM的比较和更新,可以实现对DOM的局部更新,从而提高渲染性能。
                          1. Proxy的使用:
                            1. Proxy对象可以拦截并修改某些操作的默认行为,如属性查找、赋值、枚举、函数调用等。就很像路由拦截器,Proxy会为响应式数据创建一个代理对象,并在其上定义了一些拦截行为。
                            2. Proxy可以代理针对目标对象的访问,但它不是目标对象的透明代理。简单理解就是在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。
                            3. 用法
                              1. target:要使用Proxy包装的目标对象
                              2. handler:通常是一个函数,各属性中的函数分别定义了在执行各种操作时代理p的行为

                          Vue2响应式原理

                          Vue2的响应式原理主要依赖于Object.defineProperty()方法
                          • 通过递归将Object.defineProperty()应用于对象的所有属性
                          • 通过 Object.defineProperty() 来将传入的属性转换为setter, getter
                          • 在数据发生变动时通知Vue实例,触发相应的getter和setter回调函数,实现了响应式数据系统
                          缺点:
                          • 当对象属性过多时,需要给每个属性都绑定Object.defineProperty(),效率不高。
                          • 不支持数组的监听,以及对象新增属性的监听。需要 Vue.set()this.$set() 手动添加新的属性来保持响应式
                          • 对低版本浏览器支持可能存在问题。

                          TS相关

                          接口 interface

                          • JavaScript: 使用原型链来实现对象的继承,没有类和接口的概念。
                          • TypeScript: 引入了类和接口,支持面向对象的编程风格。
                          定义一个接口的例子:

                          泛型

                          大写,规定变量是个啥,要写具体的内容。这里Array是个泛型

                          自定义类型

                          Rank就是一个数组类型,数组元素都是rank接口规定的样子。让代码更干净清晰
                          HTML/CSS/TS/JS链表
                          • Giscus