Vue3计算属性

Posted monana6

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue3计算属性相关的知识,希望对你有一定的参考价值。

计算属性

// 1. 响应式数据
const data = reactive( count: 0 );
// 2. 计算属性
const plusOne = computed(() => data.count + 1);
// 3. 依赖收集
effect(() => console.log(plusOne.value));
// 4. 触发上面的effect重新执行
data.count++;

为什么data.count的改变能间接触发访问了计算属性的 effect 的重新执行呢?

首先看一下简化版本的computed的代码

export function computed(getter) 
  let dirty = true;
  let value: T;

  // 这里还是利用了effect做依赖收集
  const runner = effect(getter, 
    // 这里保证初始化的时候不去执行getter
    lazy: true, //表示effect函数返回的runner并不会立即执行,懒加载
    computed: true, // 表示这是一个computed effect,用于trigger阶段的优先级排序
    // 调度执行顺序的实现
    scheduler: () => 
      // 在触发更新时 只是把dirty置为true
      // 而不去立刻计算值 所以计算属性有lazy的特性
      dirty = true;
    ,
  );
  
  return 
    get value() 
      if (dirty) 
        // 在真正的去获取计算属性的value的时候
        // 依据dirty的值决定去不去重新执行getter 获取最新值
        value = runner();
        dirty = false;
      
      // 这里是关键 后续讲解
      trackChildRun(runner);
      return value;
    ,
    set value(newValue: T) 
      setter(newValue);
    ,
  ;

首先要知道,effect 函数会立即开始执行,再执行之前,先把effect自身变成全局的activeEffect,以供响应式数据收集依赖。

并且activeEffect的记录是用的栈的方式,随着函数的开始进行入栈,随着函数的结束出栈。这样就可以维护嵌套的effect关系。

先起几个别名便于讲解

// 计算effect
const plusOne =computed(() => data.count + 1);
// 日志effect
effect(() => console.log(plusOne.value));

从依赖关系来看,
日志effect读取了计算effect
计算effect读取了响应式属性count
所以更新的顺序也应该是:

count改变` -> `计算effect更新` -> `日志effect更新

解读

当日志effect开始执行的时候,此时的activeEffect是日志effect,此时的 effectStack 是[ 日志 effect ]

执行日志effect读取了plusOne.value,进而触发了

 get value() 
      if (dirty) 
        // 在真正的去获取计算属性的value的时候
        // 依据dirty的值决定去不去重新执行getter 获取最新值
        value = runner()
        dirty = false
      
      // 这里是关键 后续讲解
      trackChildRun(runner)
      return value
,
首先进入了求值过程:value = runner(),runner 其实就是计算effect,它是对于用户传入的 getter 函数的包装
进入runner()函数之后,此时的activeEffect就是计算effect,此时的 `effectStack` 是[ 日志 effect, 计算 effect]

runner 所包裹的() => data.count + 1也就是计算effect会去读取count,因为是由 effect 包裹的函数,所以触发了响应式数据的get拦截:

此时count会收集计算effect作为自己的依赖。

并且计算effect会收集count的依赖集合,保存在自己身上。(通过effect.deps属性)

dep.add(activeEffect);
activeEffect.deps.push(dep);

也就是形成了一个双向收集的关系,于是计算effect有了count的所有依赖,count也存了计算effect的依赖。

然后在 runner 运行结束后,计算effect出栈了,此时activeEffect变成了栈顶的日志effect

此时activeEffect是日志 effect,此时的effectStack是[ 日志 effect ]

接下来进入关键的步骤:`trackChildRun()`

trackChildRun(runner);

function trackChildRun(childRunner: ReactiveEffect) 
  for (let i = 0; i < childRunner.deps.length; i++) 
    const dep = childRunner.deps[i];
    dep.add(activeEffect);
  

这个runner就是计算effect,它的deps上此时挂着count的依赖集合,

trackChildRun中,它把当前的 activeEffect 也就是日志effect也加入到了count的依赖集合中。

此时count的依赖集合是这样的:[ 计算effect, 日志effect ]

这样下次count更新的时候,会把两个 effect 都重新触发,而由于触发的顺序是先触发computed effect 后触发普通effect,因此就完成了

  1. 计算 effect 的 dirty 置为 true,标志着下次读取需要重新求值。
  2. 日志 effect 读取计算 effect 的 value,获得最新的值并打印出来。

Vue_(组件)计算属性

 

 

  Vue计算属性中文文档  传送门

 

技术图片

 

  Vue计算属性:更强大的属性声明方式,可以对定义的属性进行逻辑处理与数据监视;

  注意:模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护

 

  Learn

    一、计算属性的基本使用

    二、计算属性的getter和setter

    三、计算属性与方法的区别【重点】  传送门

 

  项目结构

  技术图片

  【每个demo下方都存有html源码】

 

 

 

一、计算属性的基本使用

  计算属性computed中对<input>组件中msg信息进行事件监听

        data:{
                msg:‘Hello Gary!!‘
            },
            computed : {
                msg1 : function(){
                return this.msg.toUpperCase();
                }
            }    

 

<div>
        <input type="text" v-model="msg" /><br />
        原样文本显示:<h1>{{msg}}</h1><br />
            
        大写文本显示:<h1>{{msg.toUpperCase()}}</h1><br />
        计算属性文本显示:<h1>{{msg1}}</h1><br />
</div>

 

  两种方法使原文本显示数据小写全部转换为大写

技术图片

 

技术图片
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Gary</title>
    <script type="text/javascript"  src="../js/vue.js"></script>
  <script>
    window.onload= ()=>{

     new Vue({
        el:div,
        data:{
                msg:Hello Gary!!
            },
            computed : {
                msg1 : function(){
                return this.msg.toUpperCase();
                }
            }
        });
    }

  </script>

    
</head>
<body>
<div>
        <input type="text" v-model="msg" /><br />
        原样文本显示:<h1>{{msg}}</h1><br />
            
        大写文本显示:<h1>{{msg.toUpperCase()}}</h1><br />
        计算属性文本显示:<h1>{{msg1}}</h1><br />
</div>
    
</body>
</html>
Gary_computed.html

 

 

二、计算属性的getter和setter

  需求:需要计算属性文本要比原样文本数字大10

  计算属性computed中对<input>组件中对num1数字信息进行事件监听

        data:{
                num:0
            },
            computed : {
                num1: function(){
                    return parseInt(this.num) + 10;
                }
            }    

 

        <div>
            <input type="text" v-model="num" /><br />
            原样文本显示:<h1>{{num}}</h1><br />
            <input type="text" v-model="num1" /><br />
            计算属性文本显示:<h1>{{num1}}</h1><br />
        </div>

 

  发现只能正向从原样显示去修改计算属性中num1的数值,如果要从计算属性去修改原样文本显示,需要设置计算属性的get和set方法

技术图片

 

技术图片
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Gary</title>
    <script type="text/javascript"  src="../js/vue.js"></script>
  <script>
    window.onload= ()=>{

     new Vue({
        el:div,
        data:{
                num:0
            },
            computed : {
                num1: function(){
                    return parseInt(this.num) + 10;
                }
            }
        });
    }

  </script>

    
</head>
<body>
        <div>
            <input type="text" v-model="num" /><br />
            原样文本显示:<h1>{{num}}</h1><br />
            <input type="text" v-model="num1" /><br />
            计算属性文本显示:<h1>{{num1}}</h1><br />
        </div>
</body>
</html>
Gary_computed-02.html

 

  给计算属性显示设置get和set方法

    data:{
                num:0
            },
            computed : {
                num1 : {
                          get : function(){
                              return parseInt(this.num) + 10;
                          },
                          set : function(value){
                              this.num = value;
                          }
                    }
            }

 

  注意:set方法中是this.num=value,num1的值去监听num值的变化

技术图片

 

技术图片
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Gary</title>
    <script type="text/javascript"  src="../js/vue.js"></script>
  <script>
    window.onload= ()=>{

     new Vue({
        el:div,
        data:{
                num:0
            },
            computed : {
                num1 : {
                            get : function(){
                                return parseInt(this.num) + 10;
                            },
                            set : function(value){
                                this.num = value;
                            }
                    }
            }
        });
    }

  </script>

    
</head>
<body>
        <div>
            <input type="text" v-model="num" /><br />
            原样文本显示:<h1>{{num}}</h1><br />
            <input type="text" v-model="num1" /><br />
            计算属性文本显示:<h1>{{num1}}</h1><br />
        </div>
</body>
</html>
Gary_computed-02.html

 

 

三、计算属性与方法的区别

  计算属性有缓存机制,方法没有

  只要计算属性内相关依赖的值不发生改变,多次调用计算属性可以从缓存中获取值,不必重复计算

  方法每次调用都要重新执行一遍

 

  小写转化成大写计算属性写法

          data : {
                        msg : ‘Gary‘
                    },
                    computed : {
                        msg1 : function(){
                            console.log("执行计算属性computed");
                            return this.msg.toUpperCase();
                        }
                    

 

  小写转化成大写方法写法

      data : {
                        msg : ‘Gary‘
                    },
                    methods : {
                        upperCase(){
                            console.log("执行方法methods");
                            return this.msg.toUpperCase();
                        },

 

  添加个<button>组件去展示输入文本中转化成大写后的内容,用来查看计算属性与方法区别

        <div>
            <input type="text" v-model="msg" /><br />
            原样显示:<h1>{{msg}}</h1><br />
            
            计算属性显示:<h1>{{msg1}}</h1><br />
            方法显示:<h1>{{upperCase()}}</h1><br />
            <button @click="show">show</button>
        </div>

 

                        show(){
                            console.log("计算属性展示文本中的内容 :" + this.msg1);
                            console.log("方法调用展示文本中的内容 :" + this.upperCase());
                        }

 

技术图片

 

技术图片
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Gary</title>
        <script type="text/javascript" src="../js/vue.js" ></script>
        <script>
            window.onload = () => {
                new Vue({
                    el : div,
                    data : {
                        msg : Gary
                    },
                    computed : {
                        msg1 : function(){
                            console.log("执行计算属性computed");
                            return this.msg.toUpperCase();
                        }
                    },
                    methods : {
                        upperCase(){
                            console.log("执行方法methods");
                            return this.msg.toUpperCase();
                        },
                        show(){
                            console.log("计算属性展示文本中的内容 :" + this.msg1);
                            console.log("方法调用展示文本中的内容 :" + this.upperCase());
                        }
                    }
                });
            }
        </script>
    </head>
    <body>
        <div>
            <input type="text" v-model="msg" /><br />
            原样显示:<h1>{{msg}}</h1><br />
            
            计算属性显示:<h1>{{msg1}}</h1><br />
            方法显示:<h1>{{upperCase()}}</h1><br />
            <button @click="show">show</button>
        </div>
    </body>
</html>
Gary_computed-03.html

 

以上是关于Vue3计算属性的主要内容,如果未能解决你的问题,请参考以下文章

总结Vue3 的一些知识点:Vue3 计算属性

vue3中computed计算属性函数

vue3 setup 计算属性 computed

VUE3 之 计算属性与侦听器

Vue3.x computed函数----计算属性

Vue3计算属性computed