vue3组件笔记

1.父传子

// 父组件
<button-counter 
        title="My journey with Vue"
        isShow="false"
></button-counter>

// 子组件   
// 使用了 <script setup> 的组件
<script setup>
import { ref } from 'vue'
const props = defineProps(['title','isShow'])
console.log(props)
console.log(props.title)

// 没有使用 <script setup> 的组件
<script>
    export default {
        props: ['title'],
        setup(props,{emit}){
            const title = props.title
            return {
                title,
            }
        }
    }
</script>

2.子传父

// 子组件
// 没有使用 <script setup> 的组件
// 第一种:<button @click="$emit('enlarge-text')">Enlarge text</button>
<script>
    export default {
        props: ['title'],
        setup(props,{emit}){
            function enlargeClick() {
                emit('enlarge-text')
            }
            return {
                enlargeClick
            }
        }
    }
</script>

// 第二种:<button @click="enlargeClick">变大按钮</button>
// 使用 <script setup> 的组件
<button @click="enlargeClick">变大按钮</button>

// 通过defineEmits宏来声明需要抛出的事件, 仅可用于<script setup>中
const emit = defineEmits(['enlarge-text'])

function enlargeClick() {
    emit('enlarge-text')
}


// 父组件
 <button-counter 
          title="My journey with Vue"
          isShow="false"
          @enlarge-text="postFontSize+=0.2"
></button-counter>

3.通过插槽来分配内容

// 父组件
 <div>
 	<button-counter>这是通过插槽传递的内容: {{postFontSize}}</button-counter>
 </div>
 
// 子组件
 <div>
   count: {{title}}
   <button @click="enlargeClick">变大按钮</button>
   我们使用 <slot> 作为一个占位符,父组件传递进来的内容就会渲染在这里。
   <slot />
</div>

4.动态组件

<script setup>
import Home from './Home.vue'
import Posts from './Posts.vue'
import Archive from './Archive.vue'
import { ref } from 'vue'
 
const currentTab = ref('Home')

const tabs = {
  Home,
  Posts,
  Archive
}
</script>

<template>
  <div class="demo">
  tab是每一个组件
    <button
       v-for="(_, tab) in tabs"
       :key="tab"
       :class="['tab-button', { active: currentTab === tab }]"
       @click="currentTab = tab"
     >
      {{ tab }}
    </button>
	  <component :is="tabs[currentTab]" class="tab"></component>
  </div>
</template>

5.组件注册

// 全局注册
import MyComponent from './App.vue'
app.component('MyComponent', MyComponent)

// 局部注册   在<script  setup>中引入后直接使用
<script setup>
import ComponentA from './ComponentA.vue'
</script>

<template>
  <ComponentA />
</template>
// 不是在<script  setup>中,需要使用 components 选项来显式注册:
import ComponentA from './ComponentA.js'

export default {
  components: {
    ComponentA
  },
}
/*
全局注册VS局部注册
1.无法在生产打包时被自动移除 (也叫“tree-shaking”). 注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中。
2.全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性。
*/

6.props和单向数据流, props校验

<script setup>
defineProps({
  greetingMessage: String
})
{{ greetingMessage }}
</script>

// 将一个对象的所有属性都当作 props 传入
const post = {
  id: 1,
  title: 'My Journey with Vue'
}
<BlogPost v-bind="post" />
// 等价于
<BlogPost :id="post.id" :title="post.title" />

/*
单向数据流
所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。
这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。
*/

// 1.prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性
const props = defineProps(['initialCounter'])
// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)

// 2.需要对传入的 prop 值做进一步的转换   定义一个计算属性
const props = defineProps(['size'])
// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())

<script setup>
defineProps({
	propB: [String, Number],//多种可能的类型
  propC: {
    type: String,
    required: true,   //必传
    default: '666'    //默认值return {};
  },
  // 对象类型的默认值
  propE: {
    type: Object,
    // 对象或数组的默认值
    // 必须从一个工厂函数返回。
    // 该函数接收组件所接收到的原始 prop 作为参数。
    default(rawProps) {
      return { message: 'hello' }  // 或者  return {};//或 return []
    }
  },
  // 自定义类型校验函数
  propF: {
    validator(value) {
      // 该值必须匹配这些字符串中的一个
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
})

/*
1.所有 prop 默认都是可选的,除非声明了 required: true。
2.除 Boolean 外的未传递的可选 prop 将会有一个默认值 undefined。
3.Boolean 类型的未传递 prop 将被转换为 false。你应该为它设置一个 default 值来确保行为符合预期。
4.如果声明了 default 值,那么在 prop 的值被解析为 undefined 时,无论 prop 是未被传递还是显式指明的 undefined,都会改为 default 值。
*/

7.v-model绑定在组件上

// 子组件
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>

// 父组件
<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
  
const message = ref('hello')
</script>

<template>
  <CustomInput v-model="message" /> {{ message }}
</template>
/*
1.将内部原生 input 元素的 value attribute 绑定到 modelValue prop
2.输入新的值时在 input 元素上触发 update:modelValue 事件
*/



// 另一种实现,利用计算属性
<!-- CustomInput.vue -->
<script setup>
import { computed } from 'vue'

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

const value = computed({
  get() {
    return props.modelValue
  },
  set(value) {
    emit('update:modelValue', value)
  }
})
</script>

<template>
  <input v-model="value" />
</template>

7.v-model 的参数,多个v-model绑定和单个一样

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

// 父组件
<MyComponent v-model:title="bookTitle" />

// 子组件
// <!-- MyComponent.vue -->
<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>

<template>
  <input
    type="text"
    :value="title"
    @input="$emit('update:title', $event.target.value)"
  />
</template>

9.自定义 v-model 修饰符。

//父组件  自定义修饰符 capitalize,它会自动将 v-model 绑定输入的字符串值第一个字母转为大写:
const myText = ref('')
<MyComponent v-model.capitalize="myText" />

//子组件
<script setup>
const props = defineProps({
  modelValue: String,
  modelModifiers: { default: () => ({}) }  //接收一下,看有没有修饰符
})

const emit = defineEmits(['update:modelValue'])
console.log(props.modelModifiers) // { capitalize: true }

function emitValue(e){   // input事件,判断有没有这个修饰符,有就做一下操作,没有就不做
    let value = e.target.value
    if(props.modelModifiers.capitalize){
        value = value.charAt(0).toUpperCase() + value.slice(1)
    }
    emit('update:modelValue',value)
}

</script>

<template>
  <input
    type="text"
    :value="modelValue"
    @input="emitValue"
  />
</template>

10.透传 attribute

1.透传的 attribute 不会包含 <MyButton> 上声明过的 props 或是针对 emits 
声明事件的 v-on 侦听函数,换句话说,声明过的 props 和侦听函数被 <MyButton>“消费”了。

2.透传的 attribute 若符合声明,也可以作为 props 传入 <BaseButton>。

3.如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置 inheritAttrs: false。

11.插槽

<slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。

简单插槽

// 父组件
<FancyButton>
  Click me! <!-- 插槽内容 -->
</FancyButton>

// 子组件
<button class="fancy-btn">
  <slot></slot> <!-- 插槽出口 -->
  插槽内容可以是任意合法的模板内容,不局限于文本。例如我们可以传入多个元素,甚至是组件:
   <Icon/>
   
   <slot>
    Submit <!-- 默认内容 -->  slot的默认值,如果父组件传内容就用父组件的内容, 如果没有传内容就用默认内容.
   </slot>
   
</button>

具名插槽

<slot> 元素可以有一个特殊的 attribute name,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容:

//子组件
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>  //没有提供 name 的 <slot> 出口会隐式地命名为“default”。
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

//父组件
<BaseLayout>
  <template v-slot:header>  简写: #header, 意思就是“将这部分模板片段传入子组件的 header 插槽中”
    <!-- header 插槽的内容放这里 -->
  </template>
</BaseLayout>

动态插槽

// 动态指令参数在 v-slot 上也是有效的,即可以定义下面这样的动态插槽名:
<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

  <!-- 缩写为 -->
  <template #[dynamicSlotName]>
    ...
  </template>
</base-layout>

作用域插槽

//子组件通过slot向父组件传递数据
<!-- <MySlot> 的模板 -->
const greeting = ref('英美俄日')
<div>
  <slot :text="greeting" :count="1"></slot>
</div>

//默认插槽如何接收props⭐️🌟
<mySlot v-slot="slotProps">   在 v-slot 中使用解构  v-slot="{ text, count }"
  按钮
  {{ slotProps.text }} // {{ slotProps.count }}
</mySlot>

12.provide(提供)和inject(注入)

一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。

要为组件后代提供数据,需要使用到 provide() 函数:

provide() 函数接收两个参数。

第一个参数被称为注入名,可以是一个字符串或是一个 Symbol。后代组件会用注入名来查找期望注入的值。一个组件可以多次调用 provide(),使用不同的注入名,注入不同的依赖值。

第二个参数是提供的值,值可以是任意类型,包括响应式的状态,

<script setup>
import { provide } from 'vue'
        /* 注入名 */, /* 值 */ 
provide('message',  'hello!')
</script>

除了在一个组件中提供依赖,我们还可以在整个应用层面提供依赖:

import { createApp } from 'vue'

const app = createApp({})

app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')

要接收上层组件提供的数据,需使用 inject() 函数:

inject() 函数接收两个参数。第一个参数是接收的注入名, 第二个参数是默认值. 最好设置默认值

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

const message = inject('message')

// 如果没有祖先组件提供 "message",  `value` 会是 "这是默认值"
const value = inject('message', '这是默认值')

// 默认值可能需要通过调用一个函数或初始化一个类来取得。为了避免在用不到默认值的情况下进行不必要的计算或产生副作用,我们可以使用工厂函数来创建默认值:
const value = inject('key', () => new ExpensiveClass())
</script>



// 不使用<script setup>,inject() 需要在 setup() 内同步调用:
import { inject } from 'vue'

export default {
  setup() {
    const message = inject('message')
    return { message }
  }
}

当提供 / 注入响应式的数据时,建议尽可能将任何对响应式状态的变更都保持在供给方组件中。 可以给子组件传递函数

<!-- 在供给方组件内 --> 供给方提供一个数据和方法, 注入方可以通过调用函数传递参数的形式更改数据
<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

function updateLocation() {
  location.value = 'South Pole'
}

provide('location', {
  location,
  updateLocation
})
</script>

<!-- 在注入方组件 -->
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>

<template>
  <button @click="updateLocation">{{ location }}</button>
</template>

13.异步组件

在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent 方法来实现此功能

<script setup>
import { defineAsyncComponent } from 'vue'

const AdminPage = defineAsyncComponent(() => import('./components/AdminPageComponent.vue'))

</script>

<template>
  <AdminPage />
</template>

14.Transition和TransitionGroup

<Transition> 仅支持单个元素或组件作为其插槽内容。如果内容是一个组件,这个组件必须仅有一个根元素。

v-enter-from:进入动画的起始状态。

v-enter-active:进入动画的生效状态。

v-enter-to:进入动画的结束状态。

v-leave-from:离开动画的起始状态。

v-leave-active:离开动画的生效状态。

v-leave-to:离开动画的结束状态。

为过渡效果命名#

我们可以给 <Transition> 组件传一个 name prop 来声明一个过渡效果名:

<Transition name="fade"> ... </Transition>

.fade-enter-active, .fade-leave-active { transition: opacity 0.5s ease; }

15.KeepAlive

<KeepAlive> 是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。

<KeepAlive> 默认会缓存内部的所有组件实例,但我们可以通过 include 和 exclude prop 来定制该行为。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值