Vue3.0插槽、自定义指令等语法变更学习第三篇

377 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Slot具名插槽语法

在 Vue2.x 中, 具名插槽的写法:

<!--  子组件中:-->
<slot name="title"></slot>

在父组件中使用:

<template slot="title">
    <h1>歌曲:《孤勇者》</h1>
<template>

如果我们要在 slot 上面绑定数据,可以使用作用域插槽,实现如下:

// 子组件
<slot name="content" :data="data"></slot>
export default {
    data(){
        return{
            data:["走过来人来人往","不喜欢也得欣赏","陪伴是最长情的告白"]
        }
    }
}

<!-- 父组件中使用 -->
<template slot="content" slot-scope="scoped">
    <div v-for="item in scoped.data">{{item}}</div>
<template>

在 Vue2.x 中具名插槽和作用域插槽分别使用slot和slot-scope来实现, 在 Vue3.0 中将slot和slot-scope进行了合并同意使用。 Vue3.0 中v-slot:

<!-- 父组件中使用 -->
 <template v-slot:content="scoped">
   <div v-for="item in scoped.data">{{item}}</div>
</template>

<!-- 也可以简写成: -->
<template #content="{data}">
    <div v-for="item in data">{{item}}</div>
</template>

组件上v-model用法

在 Vue 2.0 发布后,开发者使用 v-model 指令必须使用为 value 的 prop。如果开发者出于不同的目的需要使用其他的 prop,他们就不得不使用 v-bind.sync。此外,由于v-modelvalue 之间的这种硬编码关系的原因,产生了如何处理原生元素和自定义元素的问题。 在 Vue 2.2 中,我们引入了 model 组件选项,允许组件自定义用于 v-model 的 prop 和事件。但是,这仍然只允许在组件上使用一个 model。 在 Vue 3 中,双向数据绑定的 API 已经标准化,减少了开发者在使用 v-model 指令时的混淆并且在使用 v-model 指令时可以更加灵活。

2.x语法

在 2.x 中,在组件上使用 v-model 相当于绑定 value prop 和 input事件:

<ChildComponent v-model="pageTitle" />

<!-- 简写: -->

<ChildComponent :value="pageTitle" @input="pageTitle = $event" />

如果要将属性或事件名称更改为其他名称,则需要在 ChildComponent 组件中添加 model 选项:

<!-- ParentComponent.vue -->

<ChildComponent v-model="pageTitle" />
// ChildComponent.vue

export default {
  model: {
    prop: 'title',
    event: 'change'
  },
  props: {
    // 这将允许 `value` 属性用于其他用途
    value: String,
    // 使用 `title` 代替 `value` 作为 model 的 prop
    title: {
      type: String,
      default: 'Default title'
    }
  }
}

所以,在这个例子中 v-model 的简写如下:

<ChildComponent :title="pageTitle" @change="pageTitle = $event" />
使用 v-bind.sync

在某些情况下,我们可能需要对某一个 prop 进行“双向绑定”(除了前面用 v-model 绑定 prop 的情况)。为此,我们建议使用 update:myPropName 抛出事件。例如,对于在上一个示例中带有 title prop 的 ChildComponent,我们可以通过下面的方式将分配新 value 的意图传达给父级:

this.$emit('update:title', newValue)

如果需要的话,父级可以监听该事件并更新本地 data property。例如:

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

为了方便起见,我们可以使用 .sync 修饰符来缩写,如下所示:

<ChildComponent :title.sync="pageTitle" />
3.x语法

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

<ChildComponent v-model="pageTitle" />

<!-- 简写: -->

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

渲染函数API改变

  • h现在全局导入,而不是作为参数传递给渲染函数
  • 渲染函数参数更为在有状态组件和函数组件之间更加一致
  • vnode现在是一个有扁平的prop结构

render函数将自动接收h函数(它是createElement的别名)作为参数

//vue2.x
export default{
  render(h){
    return h('div')
  }
}
//vue3 渲染
import { h } from 'vue'

export default {
  render() {
    return h('div')
  }
}

举个例子:

<template>
  <div>
    <RenderComp v-model='title'>
      <template v-slot:default>
        <!-- 默认插槽 -->
        头部
      </template>
      <template v-slot:content>
        <!-- 具名插槽 -->
        内容
      </template>
  </RenderComp>
  </div>
</template>
<script>
  import {
    ref,
    h
  } from "vue";
  export default {
    components: {
      RenderComp: {
        props: {
          modelValue: {
            type: String,
            default: ''
          },
        },
        setup(props,{attrs,slots,emit}) {
          // 以前得通过$scopedSlots获取对应的插槽
          console.log(slots.default()); //获取默认插槽
          console.log(slots.content()); //获取名字为content的插槽
          function changeTitle(newV) {
            emit('update:modelValue','哈哈哈');
          }
          return () => h("div", {}, [h("div", {
            onClick:changeTitle,
          },[
            `渲染函数api:${props.modelValue}`,
            slots.default(),
            slots.content()
          ])]);
        }
      }
    },
    setup(props) {
      const title = ref("双向数据绑定");
      return {
        title
      };
    }
  };
</script>


同时,演示了scopedSlotsproperty已删除,所有插槽都通过scopedSlotsproperty已删除,所有插槽都通过slots作为函数暴露

使用普通函数创建功能组件

  • 在 3.x 中,功能性组件 2.x 的性能提升可以忽略不计,因此我们建议只使用有状态的组件
  • 功能组件只能使用接收 propscontext 的普通函数创建 (即:slots,attrs,emit)。
  • 重大变更:functional attribute 在单文件组件 (SFC) <template> 已被移除
  • 重大变更:{ functional: true } 选项在通过函数创建组件已被移除

在vue2.0中,功能组件有两个主要用途:

  • 性能优化提高,因为它们的初始化速度比有状态组件快
  • 可以返回多个根节点

然而,在 Vue 3 中,有状态组件的性能已经提高到可以忽略不计的程度。此外,有状态组件现在还包括返回多个根节点的能力。 因此,功能组件剩下的唯一用例就是简单组件,比如创建动态标题的组件。否则,建议你像平常一样使用有状态组件。

总结:非特殊情况下,官网还是建议我们使用有状态的组件

Functional.vue

import { h } from 'vue'

const DynamicHeading = (props, context) => {
  return h(`h${props.level}`, context.attrs, context.slots)
}

DynamicHeading.props = ['level']

export default DynamicHeading

<Functional level='3'>动态标题</Functional>

可以传入不同的level 定制不同的h系列标题。

异步组件的更改
  • 新defineAsyncComponent助手方法,它显示定义异步组件
  • componnet选项命名为loader
  • 加载程序函数被本身不接受resolve和reject参数,必须返回一个Promise
2.x

以前,异步组件是通过将组件定义为返回 promise 的函数来创建的,例如:

const asyncPage = () => import('./NextPage.vue')

对于带有选项的更高阶组件语法:

const asyncPage = {
  component: () => import('./NextPage.vue'),
  delay: 200,
  timeout: 3000,
  error: ErrorComponent,
  loading: LoadingComponent
}
3.x

在vue3中,由于功能组件被定义为纯函数,因为需要通过将异步组件定义包装在新的defineAsyncComponent助手来显式定义组件

import { defineAsyncComponent } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'

// 不带选项的异步组件
const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))

// 带选项的异步组件
const asyncPageWithOptions = defineAsyncComponent({
  loader: () => import('./NextPage.vue'),
  delay: 200,
  timeout: 3000,
  errorComponent: ErrorComponent,
  loadingComponent: LoadingComponent
})

自定义指令

API 已重命名,以便更好地与组件生命周期保持一致

  • bind → beforeMount
  • inserted → mounted
  • beforeUpdate:新的!这是在元素本身更新之前调用的,很像组件生命周期钩子
  • update → 移除!有太多的相似之处要更新,所以这是多余的,请改用 updated
  • componentUpdated → updated
  • **beforeUnmount ** 新的与组件生命周期钩子类似,它将在卸载元素之前调用。
  • unbind -> unmounted

举个例子: main.js

const app = createApp(App);
// 创建自定义指令
app.directive('highlight',{
  // 指令 也拥有一组生命周期钩子
  // 1.在绑定元素的父组件挂载之前调用
  beforeMount(el,binding,vnode){
    el.style.background = binding.value;
  },
})

App.vue

<p v-highlight="'red'">自定义指令</p>

动画transion改变

  • v-enter->v-enter-from
  • v-leave->v-leave-from

vue2.x版本中

在这里插入图片描述

移除API
  • keyCode 支持作为 v-on 的修饰符
  • on,on,off 和 $once 实例方法 过滤

2.x,支持keyCodes作为修改v-on方法的方法

<!-- 键码版本 -->
<input v-on:keyup.13="submit" />

<!-- 别名版本 -->
<input v-on:keyup.enter="submit" />

vue3.x

在建议对任何要用作修饰符的键使用 kebab-cased (短横线) 大小写名称。

<!-- Vue 3 在 v-on 上使用 按键修饰符 -->
<input v-on:keyup.delete="confirmDelete" />

因此,这意味着 config.keyCodes 现在也已弃用,不再受支持。 $on$off$once 实例方法已被移除,应用实例不再实现事件触发接口。 Filters 已从 Vue 3.0 中删除,不再受支持。相反,我们建议用方法调用或计算属性替换它们。