Vue2向Vue3过渡,持续记录

我心飞翔 分类:vue

迁移指南:https://v3.cn.vuejs.org/guide/migration/introduction.html

好用的插件:https://vueuse.org/,被遗忘了的几个指令 v-pre、v-once、this.$forceUpdate()、this.nextTick()

编程总结:在setup中应该如何去规划代码?如何模块化?如何才不会一团乱?

vue3 不同构建版本

Vue3中不再构建UMD模块化的方式,因为UMD会让代码有更多的冗余,它要支持多种模块化的方式。

Vue3中将CJS、ESModule和自执行函数的方式分别打包到了不同的文件中。在packages/vue中有Vue3的不同构建版本。

相关说明:https://cn.vuejs.org/v2/guide/installation.html

1.cjs

两个版本都是完整版,包含编译器

vue.cjs.js、vue.cjs.prod.js(开发版,代码进行了压缩)

2.global

这四个版本都可以在浏览器中直接通过scripts标签导入,导入之后会增加一个全局的Vue对象

vue.global.js(完整版,包含编译器和运行时)
vue.global.prod.js(完整版,包含编译器和运行时,这是开发版本,代码进行了压缩)
vue.runtime.global.js
vue.runtime.global.prod.js

3.browser

四个版本都包含esm,浏览器的原生模块化方式,可以直接通可以直接通过<script type="module" />的方式来导入模块

vue.esm-browser.js
vue.esm-browser.prod.js
vue.runtime.esm-browser.js
vue.runtime.esm-browser.prod.js

4.bundler

这两个版本没有打包所有的代码,只会打包使用的代码,需要配合打包工具来使用,会让Vue体积更小

vue.esm-bundler.js
bue.runtime.esm-bundler.js

setup 组件选项

在 setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。

setup 选项是一个接收 props 和 context 的函数,setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。

setup的执行在beforeCreate之前。只执行一次(参数都是包装后的proxy对象)

  1. props,代表给组件传递的参数
  2. context,组件所处的上下文对象(props、emit、slots);

思考

在setup如何高效的、准确的把各种逻辑抽离出来?使用组合式API时,在实践中尝试MVC,尽量不要把主要的业务逻辑写在组件里。setup 只是为 组件载入逻辑 提供了一个入口,而不应该把所有东西都写在里面。

Vue3 Composition API中的提取和重用逻辑:https://blog.csdn.net/duninet/article/details/105716706

<script setup>:https://blog.csdn.net/wu_xianqiang/article/details/120939405

Vue3中的响应式

  • 基本数据类型还是使用get、set的方式(ref函数)。
  • 对象数据类型使用的是Vue3的新函数reactive(基于Es6 Proxy)
  • Proxy知识点总结:Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
  • Proxy语法:https://es6.ruanyifeng.com/#docs/proxy可进行多种多样的数据拦截。
  • ref函数使用的依然是Object的get、set方法实现响应式,而reactive函数式通过Proxy实现的数据劫持,同时使用Reflect反射对象去操作对象的属性。基本数据类型一般使用ref,对象或者数组则使用reactive函数。

增加配置项emits

用于组件指定可以接受的自定义事件。使用未被定义的自定义时间将会报错。

let emits=defineEmits(['refresh']); //定义事件
emits.emit("refresh"); //触发事件

对于setup新增的api函数

响应式相关:https://v3.cn.vuejs.org/api/refs-api.html

1.ref函数

在setup内创建响应式数据,如果将对象分配为 ref 值,则它将被 reactive 函数处理为深层的响应式对象。

const count = ref(0)

2.reactive函数

在setup内定义响应式数据(包括对象所有层次的属性);

const obj = reactive({ count: 0 })

3.readonly函数

接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。

const original = reactive({ count: 0 })

const copy = readonly(original)

watchEffect(() =&gt; {
  /* 用于响应性追踪  */
  console.log(copy.count)
})

/* 变更 original 会触发依赖于副本的侦听器 */
original.count++

/* 变更副本将失败并导致警告 */
copy.count++ /* 警告! */

4.toRaw

返回 reactive 或 readonly 代理的原始对象。

5.markRaw

标记一个对象,使其永远不会转换为 proxy。返回对象本身。

6.shallowReactive、shallowReadonly

创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (暴露原始值)。只转换对象的自身的属性,不追踪内部属性的对象的属性

7.toRefs

在不丢失响应性的前提下,解构数据对象。

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })
  /*操作 state 的逻辑*/
  /*返回时转换为ref*/
  return toRefs(state)
}
export default {
  setup() {
    /*可以在不失去响应性的情况下解构*/
    const { foo, bar } = useFeatureX()
    return {
      foo,
      bar
    }
  }
}

适合简化在层级较多的响应式对象调用。

说明:ref与toRef的区别

ref复制, 修改响应式数据,不会影响以前的数据,界面会更改。
toRef引用, 修改响应式的数据,会影响以前的数据,界面不会更新。

(1).ref本质是拷贝,修改响应式数据不会影响原始数据;toRef的本质是引用关系,修改响应式数据会影响原始数据
(2).ref数据发生改变,界面会自动更新;toRef当数据发生改变是,界面不会自动更新
(3).toRef传参与ref不同;toRef接收两个参数,第一个参数是哪个对象,第二个参数是对象的哪个属性

8.类型判断相关

  • isReactive、isReadOnly,判断是否为响应式数据对象。
  • isProxy,检查对象是否是由 reactive 或 readonly 创建的 proxy。

9.配置项相关

  • computed,在setup内定义计算属性
  • watch,在setup内定义监视属性
  • watchEffect,用到谁就监视谁。类似computed,但是watchEffect更注重过程,computed注重返回的结果。
  • 各种生命周期的函数和钩子。setup内定义的生命周期钩子在生命周期配置项定义的之前被调用(例如onCreated)。
  • defineProps 和 defineEmits在 <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits ,它们具备完整的类型推断并且在 <script setup> 中是直接可用。

Provide和Inject

父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。

作为配置项使用:https://v3.cn.vuejs.org/guide/component-provide-inject.html

组合式API中使用:https://v3.cn.vuejs.org/guide/composition-api-provide-inject.html

异步组件、Fragment和Teleport

官方文档:https://v3.cn.vuejs.org/guide/component-dynamic-async.html

1.Teleport

提供了一种干净的方法,允许我们控制在 DOM 中哪个父节点下渲染了 HTML,而不必求助于全局状态或将其拆分为两个组件。

<teleport to="body">
    <div v-if="modalOpen" class="modal">
        <div>
          I'm a teleported modal! 
          (My parent is "body")
          <button @click="modalOpen = false">
            Close
          </button>
        </div>
    </div>
</teleport>

2.Fragment

组件有多个根节点时将隐式产生。

script setup

1.在单文件组件中,当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用。

2.当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用。

3.使用 <script setup> 的组件是默认关闭的,也即通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。为了在 <script setup> 组件中明确要暴露出去的属性,使用 defineExpose 编译器宏。

提示

普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行。其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理

官方文档:https://v3.cn.vuejs.org/api/sfc-script-setup.html

v-bind.sync的改变

官方文档:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E4%BD%BF%E7%94%A8-v-bind-sync

在 3.x 中,自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件:

<ChildComponent v-model="pageTitle" />

<!-- 是以下的简写: -->

<ChildComponent
  :modelValue="pageTitle"
  @update:modelValue="pageTitle = $event"
/>

对比

1.setup内可结合vue的各种特性,而且可以结合传统的模块化编程思维,,复用各种代码使逻辑更加清晰。而vue更像是纯粹的新思维(自定义setup中的hook,封装composition API)。

迁移文档:https://v3.cn.vuejs.org/guide/migration/introduction.html

Vue-Router:https://next.router.vuejs.org/zh/guide/migration/index.html

在setup中获取$router,需要使用useRouter方法调用。

Element-plus:https://element-plus.gitee.io/zh-CN/

相关收集

1.Vue项目模板Vitesse:https://github.com/antfu/vitesse/blob/main/README.zh-CN.md

选项API变化

无法在 <script setup> 声明的选项,例如 inheritAttrs 或通过插件启用的自定义的选项。

<script>
// 普通 <script>, 在模块范围下执行(只执行一次)
runSideEffectOnce()

// 声明额外的选项
export default {
  inheritAttrs: false,
  customOptions: {}
}
</script>

1.inheritAttrs

默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而通过实例 property $attrs 可以让这些 attribute 生效,且可以通过 v-bind 显性的绑定到非根元素上。

该 property 包括组件 props 和 emits property 中未包含的所有属性 (例如,class、style、v-on 监听器等)。

与单个根节点组件不同,具有多个根节点的组件不具有自动 attribute fallthrough (隐式贯穿) 行为。如果未显式绑定 $attrs,将发出运行时警告。

2.compilerOptions 

compilerOptions 用于设置编译模板的时候的一些配置

const Foo = {
  // ...
  compilerOptions: {
    delimiters: ['${', '}'],
    comments: true
  }
}

组件中的style样式 

1.深度选择器:deep()

处于 scoped 样式中的选择器如果想要做更“深度”的选择,也即:影响到子组件,可以使用 :deep() 这个伪类;

<style scoped>
.a :deep(.b) {
  /* ... */
}
</style>

2.插槽选择器:slotted()

默认情况下,作用域样式不会影响到 渲染出来的内容,因为它们被认为是父组件所持有并传递进来的。使用 :slotted 伪类以确切地将插槽内容作为选择器的目标:

<style scoped>
:slotted(div) {
  color: red;
}
</style>

3.全局选择器:global(.red)

如果想让其中一个样式规则应用到全局,比起另外创建一个 <style>,可以使用 :global 伪类来实现

<style scoped>
:global(.red) {
  color: red;
}
</style>

4.css module

<style module> 标签会被编译为 CSS Modules 并且将生成的 CSS 类作为 $style 对象的键暴露给组件

5.状态驱动的动态 CSS

单文件组件的 <style> 标签可以通过 v-bind 这一 CSS 函数将 CSS 的值关联到动态的组件状态上

<script setup>
const theme = {
  color: 'red'
}
</script>

<template>
  <p>hello</p>
</template>

<style scoped>
p {
  color: v-bind('theme.color');
}
</style>

实际的值会被编译成 hash 的 CSS 自定义 property,CSS 本身仍然是静态的。自定义 property 会通过内联样式的方式应用到组件的根元素上,并且在源值变更的时候响应式更新。

低级静态组件与 v-once 

在 Vue 中渲染纯 HTML 元素的速度非常快,但有时你可能有一个包含很多静态内容的组件。在这些情况下,可以通过向根元素添加 v-once 指令来确保只对其求值一次,然后进行缓存,如下所示:

app.component('terms-of-service', {
  template: `
    <div v-once>
      <h1>Terms of Service</h1>
      ... a lot of static content ...
    </div>
  `
})

提示

再次提醒,不要过度使用这种模式。虽然在需要渲染大量静态内容的极少数情况下使用这种模式会很方便,但除非你注意到先前的渲染速度很慢,否则就没有必要这样做——另外,过度使用这种模式可能会在以后引起很多混乱。例如,假设另一个开发人员不熟悉 v-once 或者没有在模板中发现它,他们可能会花上几个小时来弄清楚为什么模板没有正确更新。

异步组件

在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了实现这个效果,Vue 有一个 defineAsyncComponent 方法

官方文档:https://v3.cn.vuejs.org/guide/component-dynamic-async.html#%E5%BC%82%E6%AD%A5%E7%BB%84%E4%BB%B6

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/AsyncComponent.vue')
)

app.component('async-component', AsyncComp)

异步组件可以搭配vite的import.meta.glob,批量引入某个目录下的所有组件作为异步组件。

问题总结

1.直接在浏览器内通过script引入Vue3,需要注意以下几点

  1. setup选项内,不能像在Cli内一样使用Vue的APi(如ref、reactive等),需要使用 Vue.ref 的形式才能调用。
  2. 闭包指的是在函数内定义的函数,所以它能直接使用上一个函数内的所有数据对象,而普通函数被调用时,是无法使用上一个执行的函数的局部变量的。在浏览器环境下需要将ref、reactive等API注册为全局变量。从而实现在setup内的模块化。
  3. 在浏览器环境下,组件标签必须正常闭合,否则会导致模板解析错误。
  4. 使用Vant库时,例如loading这些API,因为无法使用this调用vue实例,所以在setup内需要通过vant对象去调用。(CLI下通过Use引入入的Toast对象)
  5. v-slot:slotName,指定插槽名(只能在template标签上使用,只有一种特殊情况),v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。

2.setup异步请求

在开发 vue3 中,因为通过接口数据为异步函数获取,导致最后数据无法成功赋值进 return 中的数据。所以需要setup函数异步转同步,后设置了async 后异步转同步,结果导致页面空白不显示。在Vue3中,如果当前组件的setup使用了async/await,那么其调用组件的父组件(父组件中引用defineAsyncComponent定义异步组件)的外层需要嵌套一个suspense标签

异步组件不需要作为 的直接子节点。它可以出现在组件树任意深度的位置,且不需要出现在和 自身相同的模板中。只有所有的后代组件都准备就绪,该内容才会被认为解析完毕。

3.Vue.use 必须在 Vue.mount之前

Vue.use 必须在 Vue.mount之前,否则会报错。use is not a fucntion.

4.vant自动引入样式出错时

将安装的vite-plugin-style-import 插件1.2.0版本改为 1.4.1版本即可

npm install vite-plugin-style-import@1.4.1

5.在setup中操作dom节点

业务场景:echarts在指定的div上画图。

/*示例模板*/
<div ref="dom"></div>
/*定义一个ref*/
let dom=ref(null)

/*这里打印的就是节点*/
onMounted(()=>{
  console.log(dom.value);
})

6.async setup中注册生命周期钩子时,必须写在第一个await之前;以下来自源代码的提示:

onMounted is called when there is no active component instance to be associated with. 
Lifecycle injection APIs can only be used during execution of setup(). 
If you are using async setup(), make sure to register lifecycle hooks before the first await statement. 

7.setup 自定义事件注册与触发

定义可接收的自定义事件:https://v3.cn.vuejs.org/api/sfc-script-setup.html#defineprops-%E5%92%8C-defineemits

<script setup>
     const emit = defineEmits(['change', 'delete'])
     /* setup code  */
</script>

使用setup配置项时,通过emits配置项定义。

触发自定义事件:

  1. 配置项setup中通过setup 函数的参数context.emit去触发。
  2. 语法糖script setup中通过difineEmit返回的对象去触发
/*传递context,用于触发自定义事件*/
onMounted(()=>{
   mounted(context);
}); //挂载生命周期

8.传递的props不是响应式的

传递的props不建议去修改,基础类型和对象的引用修改时都会报错,传递的props值是一个对象时,属性值是可以修改的。

provide/inject可以看作是“长距离的 prop”,默认情况下,provide/inject 绑定并不是响应式的。我们可以通过传递一个 ref property 或 reactive 对象给 provide 来改变这种行为。

9.object

object["prop"] 和 object.prop 是通用的。

10.script setup内接受传参和自定义事件

/*定义注册自定义事件,设置模块的显示*/
let emit=defineEmits(["set"])

/*定义接受模块的设置*/
let props=defineProps([
     "modules"
])

11.transition和transition-group

transition:用于给单独一个组件定义动画

transition-group:用于给多个组件同时定义动画

12.动态组件

<component :name="item.name" :is="item.component"></component>

13.通过props传递一个响应式数据

传递的props属性,对于基础类型和对象的引用修改时都会报错,但是修改对象的值是可以的,并且父组件会保持对这个属性的响应。

14.整个对象的替换,保持响应式

不管是vue2还是3,对于响应式对象的替换和修改都只能在对象内部进行,而不能直接去替换这一整个响应式对象,例如vue2中data返回的对象,直接替换之后就成为一个普通对象了,vue根本没机会去执行Object.defineProperty,对于Proxy对象,同样是如此。所有要想替换一整个对象,只能用Proxy对象的一个属性去进行操作。

15.vue绑定事件时传递事件对象

有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法

<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>

16.setup内的异步操作

provide、inject、生命周期钩子都需要在异步操作之前,不然会导致获取不到值或者无法正常执行。

17.script setup内使用动态组件

不同于之前的直接使用字符串的组件名,在script setup中使用动态组件 is必须是一个代表引入组件的变量名,假如使用record组件(通过import引入),作为动态组件时必须把组件变量作为is的属性值。

/*通时通次*/
import record from './workspace/record.vue'
/*将引入的组件作为变量*/
let component={record,add,achievement,enter,sign,effective};
/*is值为引入的组件配置对象*/
<component :is="record"/>

18.异步数据加载(suspense)

  1. 使用suspense和await。await等待期间显示suspense的加载效果。
  2. 通过一个加载状态的标志,异步请求结束后变更为加载完成,显示主内容,未加载完时显示一个加载效果。
  3. 主要是要搞清楚,如果必须要同步那就await,不需要的话就可以使用加载标志。
  4. 什么时候需要使用await操作,那就是有多个异步行为的时候,后一个异步依赖于前一个异步的结果,可以避免大量的回调操作
/*获取各种排名数据*/
let rank=ranks();

场景举例

如上,ranks内有一个异步请求,按照js的运行逻辑,不会等待请求完毕,而是继续往下运行,所以最终rank为undefine;那么该如何解决,一是使用await同步执行,而是返回一个响应式的变量,让异步更新时,同步数据值。 使用异步组件时,同样适用于此。

19.keep-alive理解

当组件在 <keep-alive> 内被切换时,它的 mounted 和 unmounted 生命周期钩子不会被调用,取而代之的是 activated 和 deactivated。(这会运用在 <keep-alive> 的直接子节点及其所有子孙节点)。配合动态组件时,组件实例能够被在它们第一次被创建的时候缓存下来。

  • avtived和deactived,在keeplive内任意一个组件注册时,路由组件从缓存中被激活、隐藏时触发。
  • vue-router是基于动态组件实现的。
  • keep-alive不支持多级路由缓存,对于一个单独的层级可以单独定义keep-alive。
  • include - string | RegExp | Array。只有名称匹配的组件会被缓存。
  • exclude - string | RegExp | Array。任何名称匹配的组件都不会被缓存。
  • max - number | string。最多可以缓存多少组件实例。

官方文档:https://v3.cn.vuejs.org/guide/component-dynamic-async.html

18.模板相关知识

模板不仅可以使用data等响应式数据,也可以直接使用$route等api;

19.vue记录一次监视属性

通过watch监视一整个对象,对象和表单双向绑定。对象是通过axios从后端请求过来的,后赋值到reactive对象的属性(注意:此后这个数据对象、watch返回的new、old值都是这个对象的引用)。因为存在null值,绑定到表单的时候null会被转换为空字符串,导致对象属性发送改变,触发一次watch。在模板渲染和监视之前将null批量转换为空字符串,从而避免触发watch

null2str(data) {
      if (typeof data != 'object'|| data === null|| data ==='null') {
            data = '';
            return data;
       }else{
            for (let x in data) {
                if (data[x] === null || data[x] === 'null') { /*如果是null 把直接内容转为 ''*/
                    data[x] = '';
                } else {
                    if (Array.isArray(data[x])) { /*是数组遍历数组 递归继续处理*/
                        data[x] = data[x].map(z => {
                            console.log(z)
                            return null2str(z);
                        });
                    }
                    if(typeof(data[x]) === 'object'){ /*是json 递归继续处理*/
                        data[x] = null2str(data[x])
                    }
                }
            }
            return data;
        }
 }

20. v-model默认属性和事件的变化

官方文档:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E6%A6%82%E8%A7%88

用于自定义组件时,v-model prop 和事件默认名称已更改:

  • prop:value -> modelValue(model-value);
  • 事件:input -> update:modelValue;

21.父组件操作子组件

在父组件中可以通过子组件的实例对象,调用子组件内的方法。

使用 <script setup> 的组件是默认关闭的,也即通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在<script> 中声明的绑定。

为了在 script setup组件中明确要暴露出去的属性,使用 defineExpose 编译器宏.

当父组件通过模板 ref 的方式获取到当前组件的实例,获取到的实例会像这样 { a: number, b: number } (ref 会和在普通实例中一样被自动解包)

22.组合式api中的computed

接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。

23.Activated和deactivated

activated 和 deactivated。(这会运用在的直接子节点及其所有子孙节点。)

24.测试加载顺序。

从main.js开始,依次开始初始化状态管理器、路由对象,然后挂载Vue对象。

开始渲染App.vue,setup部分首先开始运行,然后开始加载路由守卫,之后依次加载组件。

25.组件间通信总结

props(父传子)、emit(子传父)、inject/provide(父子孙)、状态管理器(全局)

如果子组件需要共同操作和使用一项数据,这想数据应属于父组件的数据。子组件不应该直接修改父组件的数据,而是由父组件提供修改的方法,通过自定义事件传递给子组件,Vue通过inject响应式数据,实现所有子组件共同响应一项数据。同样的provide也可以直接传递方法。

26.provide和inject使用记录

当在setup语法糖内使用provide和inject时,如果代码在非阻塞的异步代码之后,控制台会输出异常警告(将初始的异步请求放在onMounted等生命周期内),但是在setup()函数内是没问题的,猜测是语法糖编译中的区别导致的。并且不只是provide、inject存在这个问题,其他的组合式api也可能出现这种情况。如果是阻塞式的方法(await),生命周期等函数必须在它之前运行

27.Vue组件拆分的新理解

1. 什么时候拆分路由?

很多功能相互独立、没有关联的时候拆分路由

2. 什么时候拆分组件?

按照功能的布局、功能的细化进行拆分,例如一个企业微信会话记录功能,可拆分为:左侧列表(列表可拆分上部分的用户信息、下部分的会话列表)、右侧聊天记录(上部分标题和搜索、下部分聊天记录框),也就是 1:2:4 

3. 什么时候使用父子组件共享数据?

例如上面的2,就应该进行父子组件共享。选择的会话改变时,其他组件通过监视属性,触发数据更新。假如a、b都是c的子组件,a、b的共享数据应该定义在c,不应是c的父组件。父组件的父组件定义的应该是所有子组件用的,共享数据的层次感。。。!

28.v-for循环动态生成表单的时候,绑定循环的临时变量会保持响应式吗?

今天发现别人绑定是通过数组索引去绑定的,所以突然想到这个问题,事实上我一直是直接绑定的循环变量,响应式还是有的。也对,这么明显的问题,vue不可能考虑不到😂😂,正经解释一下:

循环一个元素是对象的数组,既然是对象那就是引用,然后对象是响应式的,然后基于vue的响应式原理。

31.外部JS模块

Vue组合式API内的引入的外部JS模块,应当使用使用函数初始化,而不是直接在js文件就开始初始化模板。如果是函数就只会在调用时运行,直接写在js文件,在导入的时候就会运行可执行代码。

32.循环时添加ref

<div v-for="item in list" :ref="setItemRef"></div>

import { onBeforeUpdate, onUpdated } from 'vue'

export default {
  setup() {
    let itemRefs = []
    const setItemRef = el => {
      if (el) {
        itemRefs.push(el)
      }
    }
    return {
      setItemRef
    }
  }
}

33.jsx和渲染函数

jsx指的是一种语法,渲染函数就是把这个语法的对象转换成具体的组件。

jsx插件是把.jsx文件(渲染函数的那个对象)直接转换成组件。

setup语法糖是不可以使用render的,所以只有用setup选项才可以。

官方文档:https://cn.vuejs.org/guide/extras/render-function.html

//组合式API
export default {
  props: ['message'],
  setup(props, { slots }) {
    return () => [
      // 默认插槽:
      // <div><slot /></div>
      h('div', slots.default()),

      // 具名插槽:
      // <div><slot name="footer" :text="message" /></div>
      h(
        'div',
        slots.footer({
          text: props.message
        })
      )
    ]
  }
}

//选项式API
export default {
  props: ['message'],
  render() {
    return [
      // <div><slot /></div>
      h('div', this.$slots.default()),

      // <div><slot name="footer" :text="message" /></div>
      h(
        'div',
        this.$slots.footer({
          text: this.message
        })
      )
    ]
  }
}

提示

vue 渲染函数,对于子元素,每一个非纯字符串的子元素都应该用函数返回(返回值可以是vNode、Vnode数组、插槽对象表示的vNode),需要注意的是如果渲染普通的html标签不能返回对象格式(会导致无法渲染,并且不报错);

34.具名插槽

给具名插槽的插槽内容的组件传递属性时,跟是正常的属性传递是一致的;

35.计算属性什么时候触发set?

vue计算属性返回的是一个对象、或者数组的时候,修改这个对象的属性的时候不会触发set;如果是基础数据类型(返回的是数组或对象的基本数据类型的属性),才会触发set;

36.使用异步组件?

Vue 将一个组件(以及其所有依赖)改为异步加载,所需要的只是把:import Foo from './Foo.vue' 改成 const Foo = (); =>mport('./Foo.vue')

提示

vue简单的小组件就别用异步组件了,会导致加载闪烁(网页显示,然后等待网络加载,才显示)

37.关于vue3中的v-model

在原生html元素上使用v-model时,编译后会被展开为:

<input
  :value="searchText"
  @input="searchText = $event.target.value"
/>

在自定义组件上使用v-model时,编译后会被展开为:

<CustomInput
  :modelValue="searchText"
  @update:modelValue="newValue => searchText = newValue"
/>

自定义组件需要进行如下实现,才能响应v-model:

  1. 将内部原生 input 元素的 value attribute 绑定到 modelValue prop
  2. 输入新的值时在 input 元素上触发 update:modelValue 事件
  3. 另一种在组件内实现 v-model 的方式是使用一个可写的,同时具有 getter 和 setter 的计算属性。get 方法需返回 modelValue prop,而 set 方法需触发相应的事件

默认情况下,v-model 在组件上都是使用 modelValue 作为 prop,并以 update:modelValue 作为对应的事件。可以通过给 v-model 指定一个参数来更改这些名字 ,如v-model:value;

同时可以绑定多个v-model:

<UserName
  v-model:first-name="first"
  v-model:last-name="last"
/>

<script setup>
defineProps({
  firstName: String,
  lastName: String
})

defineEmits(['update:firstName', 'update:lastName'])
</script>

37.transition

是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。

  • 由 v-if 所触发的切换
  • 由 v-show 所触发的切换
  • 由特殊元素 切换的动态组件

触发过程:

  1. v-enter-from:元素插入或显示之前添加,插入或显示后的下一帧移除;
  2. v-enter-active:元素插入或显示之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。
  3. v-enter-to:在元素插入完成后的下一帧被添加 (也就是 v-enter-from 被移除的同时),在过渡或动画完成之后移除。
  4. v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。
  5. v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。
  6. v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是 v-leave-from 被移除的同时),在过渡或动画完成之后移除。

初始状态(enter-from) ->  定义动画或过渡的属性(v-enter-active)-> 触发动画或过渡(v-enter-to)-> 全部移除

38. css v-bind不生效

在style标签内进行v-bind绑定时,遇到了绑定不生效的问题,研究了之后发现通过v-bind绑定的属性是作为组件根节点上style的属性值进行绑定的,所有只能给组件内部或者子组件使用。

回复

我来回复
  • 暂无回复内容