从Composable访问引用值(第2部分)

2022-04-07 00:00:00 vue.js vuejs3 vue-reactivity

这是我之前问题的后续问题: Accessing ref values from composable

以下是我的应用程序代码:

<template>
  {{ reversed_names }}
</template>

<script>
import { ref, watchEffect } from "@vue/runtime-core";
import getData from "./composables/getData";
import reverseNames from "./composables/reverseNames";

export default {
  name: "App",
  setup() {
    var filenames = ["test1.json", "test2.json"];
    const { names_data, error, load_data } = getData(filenames);
    load_data();

    const reversed_names = ref({});

    watchEffect(() => {
      const reversed_names = reverseNames(names_data.value);
      console.log("revnames inside watchEffect", reversed_names.value);
    });
    console.log("revnames outside watchEffect", reversed_names);

    return { names_data, reversed_names };
  },
};
</script>

下面是reverseNames函数:

import { ref } from "@vue/runtime-core";

const reverseNames = (names_data) => {
  const reverse_names = ref({})
  var filenames = Object.keys(names_data)
  for (let f in filenames) {
    var filename = filenames[f]
    reverse_names.value[filename] = {}
    var members = names_data[filename]["members"]
    for (let n in members) {
      let name = members[n];
      var reverseName = name.split("").reverse().join("");
      reverse_names.value[filename][name] = reverseName
    }
  }
  return reverse_names
};

export default reverseNames
watchEffect存在,因此一旦从文件加载数据(请参阅上面的链接问题),我就可以使用names_data。 在调用reverseNames之前,代码运行得很好。 names_data.value包含以下内容:

{ "test1.json": 
    { "members": 
        { "0": "Alice", 
          "1": "Bob", 
          "2": "Charlie" 
        } 
    }, 
  "test2.json": 
    { "members": 
        { "0": "David", 
          "1": "Elizabeth", 
          "2": "Fred" 
        } 
    } 
}

这里是令人困惑的部分:

watchEffect函数中,控制台显示该函数的正确返回词典。 但是,在watchEffect函数外部,控制台仅显示在watchEffect()调用之前定义的空ref({})

setup()返回并显示在模板中的空词典。

如何才能使setup()watchEffect内部返回reversed_names


解决方案

const reversed_names函数作用域外部和内部watchEffect是不同且无关的变量。reversed_names函数作用域内部隐藏了父作用域中的另一个函数。

引用是一种模式,它利用了在JavaScript中通过引用而不是通过值传递对象的模式。这意味着它是一个引用,而不是一个需要在每个需要保留反应性的地方传递的值,并且要知道它是在Vue应用程序的上下文中使用的。保存引用的变量永远不应重新赋值,而需要重新赋值value属性。

reverseNames不是通用的助手函数,但特定于Vue,因此可以接受ref。reverse_names没有起到很好的作用,因为它不会传递到任何地方,因此不会从使用ref模式中受益。可能是:

const reverseNames = (namesDataRef) => {
  const reverse_names = {} // not a ref
  var filenames = Object.keys(namesDataRef.value)
    reverse_names[filename] = {}
      ...
      reverse_names[filename][name] = reverseName
    }
  }

  namesDataRef.value = reverse_names;
  // doesn't need to return anything because it uses refs
}

const reversed_names = ref({});

watchEffect(() => {
  reverseNames(reversed_names);
  console.log("revnames inside watchEffect", reversed_names.value);
});

这是反模式。如果reverseNames使用的是异步副作用或任何可能触发在reverseNames调用期间无法跟踪的更改的内容,这将是有意义的,类似于another question。但它是同步的,只影响一个裁判,所以它没有理由使用反应性。相反,可以将反应性提升到调用方:

const reverseNames = (namesData) => {
  const reverse_names = {} // not a ref
  ...
  return reverse_names
}

const reversed_names = ref({});

watchEffect(() => {
  reversed_names.value = reverseNames(names_data.value);
  console.log("revnames inside watchEffect", reversed_names.value);
});

没有副作用(console.log),将不需要watchEffect,它可以是计算的ref:

const reversed_names = computed(() => reverseNames(names_data.value));

相关文章