Vue 3.0 function-based API尝鲜(七):This与Refs

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/HermitSun/article/details/99064912

关键词:thiscontext.refs

前面已经提到,setup的参数context替代了this的作用,而setup内部的this直接指向了undefined。这么设计是为了避免一些常见的this的错误用法(其实主要是function的作用域导致的,箭头函数就不会有这些问题)。上文还提到,需要通过context来访问以前通过this访问的内容,比如$refs$store$router

但是this真的不能获取吗?文档里虽然说了setup里不能访问当前实例,但目前就这个插件来看(并非规范),还是有几个地方可以获取到有实际意义的this的(通过function调用,而非箭头函数):

  • 生命周期函数
  • watch的getter
  • computed,但这里的this指向的是一个由插件自己创建的Vue实例。

再次强调,这个并不是规范用法,甚至有可能是插件的“遗漏”,绝对不要基于这些编写代码,尤其不要用到生产环境中。


我个人感觉,把context分离出来有一个额外的好处,这个我在开头就提到过:方便逻辑的组合和复用;这也正是Vue3的设计初衷。分离出来之后,可能会更多地让人思考如何进行逻辑上的抽象和提取,而且从体验上来说比mixin好。按照尤大的原话来说,解决的是这些问题:

  • 模版中的数据来源不清晰。举例来说,当一个组件中使用了多个 mixin 的时候,光看模版会很难分清一个属性到底是来自哪一个 mixin。HOC 也有类似的问题。
  • 命名空间冲突。由不同开发者开发的 mixin 无法保证不会正好用到一样的属性或是方法名。HOC 在注入的 props 中也存在类似问题。
  • 性能。HOC 和 Renderless Components 都需要额外的组件实例嵌套来封装逻辑,导致无谓的性能开销。

比如有这么一个应用场景:几个组件(视图层面的组件)都需要获取鼠标当前位置的逻辑。这时候,把这个监听、返回鼠标当前位置的过程抽取出一个逻辑组件,明显就比mixin好。

到目前为止说的东西看起来跟refs没什么关系。事实上,refs跟2.x里的用法并没有太大区别;refs从一开始设计出来,就是为了在特殊情况下访问子组件的(从体验上来说,像是直接操作DOM):

关于 ref 注册时间的重要说明:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!$refs 也不是响应式的,因此你不应该试图用它在模板中做数据绑定。

这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs

之所以单独把refs拿出来说,是因为在3.0里用的时候有点小坑,可能不那么符合使用的认知。事实上,这也是我不完全理解的地方。我们先看一段jQuery代码:

<script>
  function useClick(el, cb) {
    $(function() {
      // not works!
      el.addEventListener('click', cb);
    });
  }

  var appEl = document.getElementById('app');
  useClick(appEl)
</script>
<div id="app"></div>

这段代码能跑吗?显然是不能的,在useClick执行之前,#app还没有被渲染。其实refs,包括整个setup,也是这么回事。之所以在setup里不能直接访问context.refs,原因和2.x一样,refs指向的目标还没有被渲染。所以,这种用法是不行的:

useElementKeypress("u", context.refs.elementKeypress, () => {
  console.log("clicked!");
});

正确的用法是通过函数传递:

function useElementKeypress(key, getElement, callback) {
  onMounted(() => {
    getElement().addEventListener("keydown", onKeydown);
  })
}

useElementKeypress("keydown", () => context.refs.someElement, () => {
  console.log("clicked!");
});

因为Vue不是React,所以没有useRef,还是要入乡随俗(笑)。但是这个写法确实不是那么方便……

此外,还有一点值得一提。我们可以通过refs操作子组件,是毫无疑问的,想必大家都用过;但是在Vue3里,操作子组件的属性有几点需要注意:

一是只能操作子组件通过setup返回值暴露出来的属性,这个应该没什么疑问;

二是通过refs获得的子组件的属性,不是包装对象,是值本身。

假设我们要访问一个ref="foo"的组件的一个属性,定义为const bar = value('bar');。下面这段代码可以解释上面所说的:

context.refs.foo.bar       // 'bar'
context.refs.foo.bar.value // undefined

并不是获得了子组件,我们就进入了子组件的作用域。子组件定义了但是没暴露出来的变量(类似于私有变量吧),也是获取不到的。

另外,关于refs可以看看dalao们的讨论:Refs are undefined in setup() outside of lifecycle method

目录

Vue 3.0 function-based API尝鲜(一):前言

Vue 3.0 function-based API尝鲜(二):配置与启动

Vue 3.0 function-based API尝鲜(三):包装对象

Vue 3.0 function-based API尝鲜(四):值得一提的watch

Vue 3.0 function-based API尝鲜(五):生命周期

Vue 3.0 function-based API尝鲜(六):组件间通信

猜你喜欢

转载自blog.csdn.net/HermitSun/article/details/99064912