[译] Hooks 对 Vue 而言意味着什么

不要把 Hooks 和 Vue 的生命周期钩子(Lifecycle Hooks) 弄混了,Hooks 是 React 在 V16.7.0-alpha 版本中引入的,并且几天后 Vue 发布了其概念验证版本。虽然 Hooks 是由 React 提出的,它是一个对各 JavaScript 框架生态系统都有价值的、重要的组合机制,所以咱们今天会花一点时间讨论 Hooks 意味着什么。css

Hooks 主要是对模式的复用提供了一种更明确的思路 —— 避免重写组件自己,并容许有状态逻辑的不一样部分能无缝地进行协同工做。html

最初的问题

就 React 而言,问题在于:在表达状态的概念时,类是最多见的组件形式。无状态函数式组件也很是受欢迎,但因为它们只能单纯地渲染,因此它们的用途仅限于展现任务。前端

类自己存在一些问题。例如,随着 React 变得愈来愈流行,类的问题也广泛成为新手的阻碍。开发者为了理解 React,也必须理解类。绑定使得代码冗长且可读性差,而且须要理解 JavaScript 中的 this这里还讨论了使用类所带来的一些优化障碍。vue

在逻辑复用方面,咱们一般使用 render props 和高阶组件等模式。但使用这些模式后会发现本身处于相似的“厄运金字塔”中 —— 样式实现地狱,即过分使用嵌套可能会致使组件难以维护。这致使我想对 Dan Abramov 像喝醉了同样大吼大叫,没有人想要那样。react

Hooks 容许咱们使用函数调用来定义组件的有状态逻辑,从而解决这些问题。这些函数调用变得更具备组合性、可复用性,而且容许咱们在使用函数式组件的同时可以访问和维护状态。React 发布 Hooks 时,人们很兴奋 —— 下面你能够看到 Hooks 展现的一些优点,关于它们如何减小代码和重复:android

@dan_abramov 的代码(来自 #ReactConf2018)可视化,你能看到 React Hooks 为咱们带来的好处。pic.twitter.com/dKyOQsG0Gdios

— Pavel Prichodko (@prchdk) 2018 年 10 月 29 日git

在维护方面,简单性是关键,Hooks 提供了一种单一的、函数式的方式来实现逻辑共享,而且可能代码量更小。github

为何 Vue 中须要 Hooks?

读到这里你确定想知道 Hooks 在 Vue 中必须提供什么。这彷佛是一个不须要解决的问题。毕竟,类并非 Vue 主要使用的模式。Vue 提供无状态函数式组件(若是须要它们),但为何咱们须要在函数式组件中携带状态呢?咱们有 mixins 用于组合能够在多个组件复用的相同逻辑。问题解决了。vue-cli

我想到了一样的事情,但在与 Evan You 交谈后,他指出了我忽略的一个主要用例:mixins 不能相互消费和使用状态,但 Hooks 能够。这意味着若是咱们须要链式封装逻辑,可使用 Hooks。

Hooks 实现了 mixins 的功能,但避免了 mixins 带来的两个主要问题:

  • 容许相互传递状态。
  • 明确指出逻辑来自哪里。

若是使用多个 mixins,咱们不清楚哪一个属性是由哪一个 mixins 提供的。使用 Hooks,函数的返回值会记录消费的值。

那么,这在 Vue 中如何运行呢?咱们以前提到过,在使用 Hooks 时,逻辑在函数调用时表达从而可复用。在 Vue 中,这意味着咱们能够将数据调用、方法调用或计算属性调用封装到另外一个自定义函数中,并使它们能够自由组合。数据、方法和计算属性如今可用于函数式组件了。

例子

让咱们来看一个很是简单的 hook,以便咱们在继续学习 Hooks 中的组合例子以前理解构建块。

useWat?

好的,Vue Hooks 和 React Hooks 之间存在交叉部分。使用 use 做为前缀是 React 的约定,因此若是你在 React 中查找 Hooks,你会发现 Hooks 的名称都会像 useStateuseEffect 等。更多信息能够查看这里。

Evan 的在线 demo 里,你能够看到他在何处访问 useStateuseEffect 并用于 render 函数。

若是你不熟悉 Vue 中的 render 函数,那么看一看官网文档可能会有所帮助。

可是当咱们使用 Vue 风格的 Hooks 时,咱们会如何命名呢 —— 你猜对了 —— 好比:useDatauseComputed等。

所以,为了让咱们看看如何在 Vue 中使用 Hooks,我建立了一个示例应用程序供咱们探索。

详见视频演示:css-tricks.com/wp-content/…

演示网站

GitHub 仓库

在 src/hooks 文件夹中,我建立了一个 hook,它在 useMounted hook 上阻止了滚动,并在 useDestroyed 上从新启用滚动。这有助于我在打开查看内容的对话框时暂停页面滚动,并在查看对话框结束时再次容许滚动。这是一个好的抽象功能,由于它在整个应用程序中可能会屡次使用。

import { useDestroyed, useMounted } from "vue-hooks";

export function preventscroll() {
  const preventDefault = (e) => {
    e = e || window.event;
    if (e.preventDefault)
      e.preventDefault();
    e.returnValue = false;
  }

  // keycodes for left, up, right, down
  const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };

  const preventDefaultForScrollKeys = (e) => {
    if (keys[e.keyCode]) {
      preventDefault(e);
      return false;
    }
  }

  useMounted(() => {
    if (window.addEventListener) // older FF
      window.addEventListener('DOMMouseScroll', preventDefault, false);
    window.onwheel = preventDefault; // modern standard
    window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
    window.touchmove = preventDefault; // mobile
    window.touchstart = preventDefault; // mobile
    document.onkeydown = preventDefaultForScrollKeys;
  });

  useDestroyed(() => {
    if (window.removeEventListener)
      window.removeEventListener('DOMMouseScroll', preventDefault, false);

    //firefox
    window.addEventListener('DOMMouseScroll', (e) => {
      e.stopPropagation();
    }, true);

    window.onmousewheel = document.onmousewheel = null;
    window.onwheel = null;
    window.touchmove = null;
    window.touchstart = null;
    document.onkeydown = null;
  });
} 
复制代码

而后咱们能够在像 AppDetails.vue 同样的 Vue 组件中调用它:

<script>
import { preventscroll } from "./../hooks/preventscroll.js";
...

export default {
  ...
  hooks() {
    preventscroll();
  }
}
</script>
复制代码

咱们不只能够在该组件中使用它,还能够在整个应用程序中使用相同的功能!

可以相互理解的两个 Hooks

咱们以前提到过,Hooks 和 mixins 之间的主要区别之一是 Hooks 实际上能够互相传值。让咱们看一下这个简单但有点不天然的例子。

在咱们的应用程序中,咱们须要在一个可复用的 hook 中进行计算,还有一些须要使用该计算结果的东西。在咱们的例子中,咱们有一个 hook,它获取窗口宽度并将其传递给动画,让它知道只有当咱们在更大的屏幕上时才会触发。

详见视频演示:css-tricks.com/wp-content/…

第一个 hook:

import { useData, useMounted } from 'vue-hooks';

export function windowwidth() {
  const data = useData({
    width: 0
  })

  useMounted(() => {
    data.width = window.innerWidth
  })

  // this is something we can consume with the other hook
  return {
    data
  }
}
复制代码

而后,在第二个 hook 中,咱们使用它来建立一个触发动画逻辑的条件:

// the data comes from the other hook
export function logolettering(data) {
  useMounted(function () {
    // this is the width that we stored in data from the previous hook
    if (data.data.width > 1200) {
      // we can use refs if they are called in the useMounted hook
      const logoname = this.$refs.logoname;
      Splitting({ target: logoname, by: "chars" });

      TweenMax.staggerFromTo(".char", 5,
        {
          opacity: 0,
          transformOrigin: "50% 50% -30px",
          cycle: {
            color: ["red", "purple", "teal"],
            rotationY(i) {
              return i * 50
            }
          }
        },
        ...
复制代码

而后,在组件内部,咱们将一个 hook 做为参数传递给另外一个 hook:

<script>
import { logolettering } from "./../hooks/logolettering.js";
import { windowwidth } from "./../hooks/windowwidth.js";

export default {
  hooks() {
    logolettering(windowwidth());
  }
};
</script>
复制代码

如今咱们能够在整个应用程序中使用 Hooks 来编写逻辑!再提一下,这是一个用于演示目的不太天然的例子,但你能够看到这对于大型应用程序,将逻辑保存在较小的、可复用的函数中是有效的。

将来的计划

Vue Hooks 如今已经能够与 Vue 2.x 一块儿使用了,但仍然是实验性的。咱们计划将 Hooks 集成到 Vue 3 中,但在咱们本身的实现中可能会偏离 React 的 API。咱们发现 React Hooks 很是鼓舞人心,正在考虑如何向 Vue 开发人员介绍其优点。咱们想以一种符合 Vue 习惯用法的方式来作,因此还有不少实验要作。

你能够查看这个仓库做为起步。Hooks 可能会成为 mixins 的替代品,因此虽然这个功能还处于早期阶段,可是一个在此期间探索其概念是有好处的。

(真诚地感谢 Evan You 和 Dan Abramov 为本文审阅。)

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索