vue ref是在组件里唯一吗_立等可取的 Vue + Typescript 函数式组件实战

vue ref是在组件里唯一吗_立等可取的 Vue + Typescript 函数式组件实战_第1张图片

不同于面向对象编程(OOP)中通过抽象出各种对象并注重其间的解耦问题等,函数式编程(FP) 聚焦最小的单项操作,将复杂任务变成一次次 f(x) = y 式的函数运算叠加。函数是 FP 中的一等公民(First-class object),可以被当成函数参数或被函数返回;同时,这些函数应该不依赖或影响外部状态,这意味着对于给定的输入,将产生相同的输出

在 Vue 中,一个函数式组件(FC - functional component)就意味着一个没有实例(没有 this 上下文、没有生命周期方法、不监听任何属性、不管理任何状态)的组件。从外部看,它也可以被视作一个只接受一些 prop 并按预期返回某种渲染结果的 fc(props) => VNode 函数。Vue 中的 FC 有时也被称作无状态组件(stateless component)

❓为何需要函数式(无状态)组件

  • 因为函数式组件忽略了生命周期和监听等实现逻辑,所以渲染开销很低、执行速度快
  • 相比于普通组件中的 v-if 等指令,使用 h 函数或结合 jsx 更容易地实现子组件的条件性渲染
  • 比普通组件中的 + v-if 指令  更容易地实现高阶组件(HOC - higher-order component)模式,即一个封装了某些逻辑并条件性地渲染参数子组件的容器组件

❓函数式组件与真正 FP 有何区别

真正的 FP 函数基于不可变状态(immutable state),但 Vue 中的“函数式”组件没有这么理想化。后者基于可变数据,相比普通组件也只是没有实例概念而已。

同时,与 React Hooks 类似的是,Vue Composition API 也在一定程度上为函数式组件带来了少许响应式特征、onMounted 等生命周期式的概念和管理副作用的方法。

❓TypeScript 对于函数式组件有何意义

无论是 React 还是 Vue,本身都提供了一些验证 props 类型的手段。但这些方法一来配置上都稍显麻烦,二来对于轻巧的函数式组件都有点过“重”了。

TypeScript 作为一种强类型的 JavaScript 超集,可以被用来更精确的定义和检查 props 的类型、使用更简便,在 VSCode 或其他支持 Vetur 的开发工具中的自动提示也更友好

React 中的 FC + TS

在 React 中,可以 使用 FC 来约束一个返回了 jsx 的函数入参:

import React 

也可以直接定义函数的参数类型,这样的好处是可以对 props 的类型再使用泛型

interface IGreeting

而 Vue 中的做法该如何呢?

本文主要基于 vue 2.x 版本,结合 tsx 语法,尝试探讨一种在大多数现有 vue 项目中马上就能用起来的、具有良好 props 类型约束的函数式组件实践

Vue 3 风格的 tsx 函数式组件

RenderContext

RenderContext 类型被用来约束 render 函数的第二个参数,vue 2.x 项目中对渲染上下文的类型定义如下:

// types/options.d.ts 

这很清晰地对应了文档中的相应说明段落:

...组件需要的一切都是通过 context 参数传递,它是一个包括如下字段的对象:

  • props:提供所有 prop 的对象
  • children:VNode 子节点的数组
  • slots:一个函数,返回了包含所有插槽的对象
  • scopedSlots:(2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
  • data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
  • parent:对父组件的引用
  • listeners:(2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
  • injections:(2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的 property。

interface

正如 interface RenderContext 定义的那样,对于函数式组件外部输入的 props,可以使用一个自定义的 TypeScript 接口声明其结构,如:

interface IProps {

而后指定该接口为 RenderContext 的首个泛型:

import Vue, { CreateElement, RenderContext } 

emit

在函数式组件中是没有实例上的 this.$emit 可以用的,要达到同样的效果,可以采用下面的写法:

(h: CreateElement, context: RenderContext) => {

配合上 model: { prop, event } 组件选项,对外依然可以达到 v-model 的效果。

filter

在 jsx 返回结构中,传统模板中的 { title | withColon } 过滤器语法不再奏效。

等效的写法比如:

import filters 

子组件的 v-model

jsx 中 v-model 指令是无法正确的工作的,替代写法为:

 model={{
    value: formdata.iptValue,
    callback: (v: string) => (formdata.iptValue = v)
 }}
 placeholder="请填写"
/>

作用域插槽

传统模板中对于作用域插槽的用法如下:

jsx 中相应写法则是:

  list={attrs}
  scopedSlots={{
    default: (scope: any) => (
              model={{
          value: attrs[scope.scopeIndex].attr,
          callback: (v: string) => {
     //...
          }
        }}
      />
    )
  }}
/>

同时,正如例子中所示,element-ui 等全局注册的组件仍需要使用 kebab-case 形式才能正确被编译。

与 Composition API 结合

虽说目的是简单渲染的函数式组件中不用太多响应式特性,但也并非不可以一起工作,比如:

import {

综合实例

了解过以上这些要点,编写一个类型良好的 tsx 函数式组件就没有什么障碍了。

一个实例如:

你可能感兴趣的:(vue,ref是在组件里唯一吗,vue函数如何调用其他函数?)