Vue 组件 props 改变时,如何阻止多余的 render

2021-08-20 12:17:06 +08:00
 fanck0605

这是一个 Vue3 编写的异步加载的博文列表的组件,通过传入一个 postLang 来筛选不同语言的博文。

当 postLang 发生变化时,这个组件会被渲染两次,第一次时 postLang 发生变化时立即渲染,第二次是异步请求完成时刷新博文列表。

这个组件 render 时并没有直接引用 props 里的变量,所以第一次渲染时我的组件中没有任何需要更新的内容, 这次渲染显然是多余的,那么我们可以阻止它进行吗

Lang: Vue
BlogPost: create
BlogPost: render // 显示正在加载...
BlogPost: render // 显示 Lang 为 Vue 的博文
Lang: Vuex       
BlogPost: render // 无用的 render
BlogPost: render // 显示 Lang 为 Vuex 的博文
export const BlogPost = defineComponent({
    name: 'BlogPost',
    props: {
        postLang: {
            type: String,
            default: 'Vue'
        }
    },
    setup(props) {
        console.log(BlogPost.name + ': create');

        const {postLang} = toRefs(props);

        const postList = ref<{ title: string }[]>([]);

        onMounted(async () => postList.value = await fetchPostList(postLang.value));
        watch(postLang, async () => postList.value = await fetchPostList(postLang.value));

        return () => {
            console.log(BlogPost.name + ': render');

            const renderContent = () => {
                if (postList.value.length === 0) {
                    return <div>正在加载中...</div>;
                } else {
                    return postList.value.map(post => <BlogPostItem title={post.title}/>);
                }
            };

            return (
                <div>
                    {renderContent()}
                </div>
            );
        };
    }
});

下面来看一个更加清晰的例子,这一次我们在 setup 函数中完全不用 props,显然组件的内部状态完全不受 props 干扰,但是当入参 props 改变时,我们查看日志可以发现,组件仍然会重新 render 。而如果把 props 换成是 Vue Ref (非组合式 API 的 data ),如果只要 render 函数中没有调用这个 ref,那么组件就不会因为 ref 的改变而被重新 render,那么对于 props,Vue 是否可以有同样的优化,来消除这些多余的 render 呢。

Lang: Vue
BlogPostNotUseProps: create
BlogPostNotUseProps: render // 显示正在加载...
BlogPostNotUseProps: render // 显示 Lang 为 Vue 的博文(写死了)
Lang: Vuex       
BlogPostNotUseProps: render // 无用的 render
export const BlogPostNotUseProps = defineComponent({
    name: 'BlogPostNotUseProps',
    props: {
        postLang: {
            type: String,
            default: 'Vue'
        }
    },
    setup(props) {
        console.log(BlogPostNotUseProps.name + ': create');

        // const {postLang} = toRefs(props);

        const postList = ref<{ title: string }[]>([]);

        onMounted(async () => postList.value = await fetchPostList('Vue'));
        // watch(postLang, async () => postList.value = await fetchPostList(postLang.value));

        return () => {
            console.log(BlogPostNotUseProps.name + ': render');

            const renderContent = () => {
                if (postList.value.length === 0) {
                    return <div>正在加载中...</div>;
                } else {
                    return postList.value.map(post => <BlogPostItem title={post.title}/>);
                }
            };

            return (
                <div>
                    {renderContent()}
                </div>
            );
        };
    }
});
1471 次点击
所在节点    Vue.js
2 条回复
fanck0605
2021-08-20 12:29:25 +08:00
调用它们的父组件
export default defineComponent({
components: {},
data() {
return {
lang: 'Vue'
};
},
render() {
console.log('Lang: ' + this.lang);
return (
<div>
<input v-model={this.lang}/>
<div>Lang: {this.lang}</div>
<BlogPost postLang={this.lang}/>
<BlogPostNotUseProps postLang={this.lang}/>
</div>
);
}
});
banricho
2021-08-26 09:38:20 +08:00
试试用 toRef 代替 toRefs

const postLang = toRef(props, 'postLang')

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/796983

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX