Vue组件 | 青训营笔记

127 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第5天

props

props 的大小写 (camelCase vs kebab-case)

HTML中的attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase(驼峰命名法)的props名需要使用其等价的 kebab-case (短横线分隔命名) 命名。

props 类型

通常希望每个props 都有指定的值类型。这时,可以以对象形式列出props,这些属性的名称和值分别是 props各自的名称和类型:

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}

单向数据流

所有的props都使得其父子props之间形成了一个单向下行绑定:父级props的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的props都将会刷新为最新的值。这意味着不应该在一个子组件内部改变props。如果这样做了,Vue会在浏览器的控制台中发出警告。

props 验证

如果必须让组件的使用者为其传递属性的值,可通过以下代码来设置必填项:

export default {
 props {
  propA: {
   type: String,
   required: true
   },
  },
}

自定义事件

事件名

不同于组件和 prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。举个例子,如果触发一个 camelCase 名字的事件
自定义事件实现:

sendHandle(){
    this.$emit("myEvent",this.childMsg);
}
<HeaderChild @myEvent="getMsgHandle" />

.sync 修饰符

在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。
这也是为什么推荐以 update:myPropName 的模式触发事件取而代之。举个例子,在一个包含 title prop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图:

this.$emit('update:title', newTitle)
<text-document
     v-bind:title="doc.title"
     v-on:update:title="doc.title = $event"
></text-document>

语法糖

Less其实就是css的一个新的语法糖,用less写css会更加简单
async 函数是什么?一句话,它就是 Generator 函数的语法糖。

插槽

插槽内容

Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将<slot>元素作为承载分发内容的出口。

<SlotDemo>
     <h3>我是插槽数据</h3>      
</SlotDemo>
<slot></slot>

编译作用域

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

后备内容

有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。

具名插槽

有时我们需要多个插槽。对于这样的情况,<slot> 元素有一个特殊的 attribute:name。这个attribute可以用来定义额外的插槽:

<div class="container">
    <header>
        <slot name="header"></slot>
    </header>
    <main>
        <slot></slot>
    </main>
    <footer>
        <slot name="footer"></slot>
    </footer>
</div>

一个不带 name 的 <slot> 出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:

<base-layout>
    <template v-slot:header>
        <h1>Here might be a page title</h1>
    </template>

    <p>A paragraph for the main content.</p>
    <p>And another one.</p>

    <template v-slot:footer>
        <p>Here's some contact info</p>
    </template>
</base-layout>

注意 v-slot 只能添加在 <template> 上 (只有一种例外情况),这一点和已经废弃的 slot attribute不同。

作用域插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的

<slot name="footer" v-bind:msg="msg"></slot>
<template v-slot:footer="data">
    <h3>我是底部:{{ data.msg }}</h3>
</template>

注意:数据回传回来会多一层对象关系

动态插槽名

<template v-slot:[ft]="data">
     <h3>我是底部:{{ data.msg }}</h3>
</template>

具名插槽的缩写

v-onv-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header

动态组件 & 异步组件

动态组件

重新创建动态组件的行为通常是非常有用的,但是在这个案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 <keep-alive> 元素将其动态组件包裹起来。

<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

异步组件

在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

const AsyncComponent = () => ({
  // 需要加载的组件 (应该是一个 `Promise` 对象)
  component: import('./MyComponent.vue'),
  // 异步组件加载时使用的组件
  loading: LoadingComponent,
  // 加载失败时使用的组件
  error: ErrorComponent,
  // 展示加载时组件的延时时间。默认值是 200 (毫秒)
  delay: 200,
  // 如果提供了超时时间且组件加载也超时了,
  // 则使用加载失败时使用的组件。默认值是:`Infinity`
  timeout: 3000
})

处理边界情况

在绝大多数情况下,我们最好不要触达另一个组件实例内部或手动操作 DOM 元素。不过也确实在一些情况下做这些事情是合适的。

访问根实例

在每个new Vue实例的子组件中,其根实例可以通过 $root 属性进行访问。

// 获取根组件的数据
this.$root.foo

// 写入根组件的数据
this.$root.foo = 2

// 访问根组件的计算属性
this.$root.bar

// 调用根组件的方法
this.$root.baz()

全局对象

Vue.prototype.$hello = 'hello world!';
<p>{{ $hello }}</p>

访问父级组件实例

$root 类似,$parent属性可以用来从一个子组件访问父组件的实例。它提供了一种机会,可以在后期随时触达父级组件,以替代将数据以 prop 的方式传入子组件的方式。

<p>{{ $parent.pMsg }}</p>
<p>{{ $parent.getMsg() }}</p>

访问子组件实例或子元素(读取DOM)

<p ref="pDOM">我是一个要被读取的DOM</p>
this.$refs.pDOM.innerHTML = '我是动态设置的新内容'