Vue中使用props 创建计算属性,当props发生变化时 计算属性没有发生变化?

//homeTag2.js
//使用es module进行import
import { createApp, ref, computed, reactive } from "vue"

const HomeTag = {
    props:['id','title'],
    setup(props,context) {

        //将Props中的属性 保存为私有属性
        const homeTagId= ref(props.id)
        //组件自身属性
        let homeTagName = ref("homeTagName")
        //组件使用props创建的计算属性
       const normalizedId= computed(()=>{
        console.log(props.id*1000)
        return Math.floor(Math.random() * 10*props.id) + 1
        })
        return {
            homeTagName,
            homeTagId,
            normalizedId
        }
    },
    template: `<div> propsId:{{id}},{{title}},homeTagId:{{homeTagId}},normalizedId:{{normalizedId}}</div>  `
};
//使用es module 进行你export
export default HomeTag
//index.html
<!DOCTYPE html>
<html lang="en">

<body>
    <div id="app">
        <p>{{name}}</p>
        <home-tag v-bind="post"></home-tag>

        <button @click="btnClick"> click</button>

    </div>

</body>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<script type="importmap">
    {"imports":{
        "vue":"https://unpkg.com/vue@3/dist/vue.esm-browser.js"
    }}
</script>

<script type="module" src="./homeTag2.js">
</script>

<script type="module">

    import HomeTag from "./homeTag2.js"
    const { createApp, ref, reactive } = Vue

    console.log("createApp", createApp)

    //应用实例
    var appVueInstance = createApp({
        setup() {
           
            let name = ref("abc")
            let aritcle = { id: 1, title: "My Journey with vue" };
            const post = ref(aritcle)
            console.log("post",post)
            console.log("post.value",post.value)
            console.log("article",aritcle)
            function btnClick() {
                // aritcle.id=3;
                post.value.id=Math.random()
                console.log("post.value",post.value)
            }
            return {
                name,
                post,
                btnClick
            }
        }
    })
    //注册组件
    appVueInstance.component("home-tag", HomeTag)

    appVueInstance.mount("#app")

</script>

</html>

index.html 中通过点击按钮每次 随机更新 post的id属性值。 而post对象被 v-bind 到 子组件home-tag的 props中的id和title属性中。

home-tag子组件中存在计算属性normalizedId 依赖于props的id属性。

因此 理论上每次 点击 post的id的更新会导致normalizedId 的值一起变化。

问题: 实际运行 normalizedId 的值没有变化。

阅读 4.2k
3 个回答

问题基本已经可以锁定在两种模块加载方式混用上了。

题主这里是 ESM + 普通 JS 模块混用的,所以出现了这种奇怪的现象。两种改法可以解决此问题。

第一种见原题评论区,把 index.html 改造成完全的 ESM 形式。

第二种是把 index.htmlhomeTag2.js 都改造成完全的普通 JS 形式:

<!-- index.html -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="./homeTag2.js"></script>
<script>
    const { createApp, ref, reactive } = Vue;
    const HomeTag = window.HomeTag;
    // 略
</script>
const { ref, computed, reactive } = Vue;
const HomeTag = {
  // 略
};
window.HomeTag = HomeTag;

只要像原题中 index.html 里用的是全局变量 Vue、homeTag2.js 用的是模块 vue 这样的混用,就会出现 computed 不触发的问题。

具体原因未知,可能跟依赖收集的作用域有关,混用的时候两者不在同一个作用域下。这个具体咋回事儿得看下源码了,感兴趣的话题主可以自己尝试跟踪一下,我已经给缩小范围了。

看了一下项目
你用watch:

import { watch, computed } from 'vue';

props: ['id'],
setup(props) {
  const normalizedId = ref(0);

  watch(
    () => props.id,
    (newId, oldId) => {
      normalizedId.value = Math.floor(Math.random() * 10 * newId) + 1;
    },
    { immediate: true }
  );

  // ...
}

或者不用v-bind解构对象:

在父组件里:

<home-tag :post="post"></home-tag>

在子组件里:

props: ['post'],
setup(props) {
  const normalizedId = computed(() => {
    return Math.floor(Math.random() * 10 * props.post.id) + 1;
  });
  // ...
}:
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题