学学Vue(05)-computed/watch

72 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情 >>

computed

前面我们使用Mustache 来包裹数据并在里面使用表达式进行一些数据的拼接或计算,这样十分不优雅,那么有什么办法解决呢?

首先想到的就是,定义一个方法拼接数据,然后将之返回给Mustache 中,可以先来试试,如下: 我们得到了和Mustache 中书写同样的效果。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <h2>{{firstName+" "+lastName}}</h2>
    <h2>{{getFullName()}}</h2>
    <h2></h2>
  </div>
  <script src="../lib/vue.mini.js"></script>
  <script>
    const app=Vue.createApp({
      data(){
        return{
          firstName:'kobe',
          lastName:'bryant',
          score:80
        }
      },
      methods:{
        getFullName(){
          return this.firstName+" "+this.lastName
        }
      }
    })
    app.mount('#app')
  </script>
</body>
</html>

上诉的调用方法来进行,感觉在Mustache 中调用方法有点奇怪,它不是应该直接拿到静态内容展示才比较好的吗?而且每次就算数据没有改变我们都会调用一下方法,过于浪费性能了。

那我们可以使用computed 计算属性,如下,我们通过计算属性判断成绩的级别,当大于等于九十分时为优秀,然后直接在Mustache 中添加计算属性内的语法糖,这里看着和调用方法一样其实不然,fullNamegetLevel 只是只读情况下的语法糖。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <h2>{{fullName}}</h2>
    <h2>{{getLevel}}</h2>
    <h2></h2>
  </div>
  <script src="../lib/vue.mini.js"></script>
  <script>
    const app=Vue.createApp({
      data(){
        return{
          firstName:'kobe',
          lastName:'bryant',
          score:80
        }
      },
      computed:{
        fullName(){
          return this.firstName+" "+this.lastName
        },
        getLevel(){
          const score=this.score
          if(score>=90){
            return '优秀'
          }else if(score>=80){
            return '良好'
          }else if(score>=60){
            return '及格'
          }
        }
      }
    })
    app.mount('#app')
  </script>
</body>
</html>

computed详解

computed完整的写法如下,但是通常情况下只使用只读的computed 即可:

computed:{
    fullName:{
        get:()=>(),
        set:(value)=>void
    }
}

当对data 中的数据进行修改时,setter 会被调用,之后get就会自动调用

注意: 虽然我们可以将计算属性看作非只读的,但是官方还是建议将计算属性的返回值视为只读的,并且不要在计算函数中做异步请求活着更改DOM

watch

在某些情况下我们需要应对一些状态的变化,并根据这些变化运行某些副作用,这时候就需要watch 了,在每次响应式property发生变化时触发一个函数。如下所示:当一个info发生变化的时候,会触发副作用,但是如果info.name 修改了,默认情况下是不会触发副作用的,因为算是浅层监听。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <h2>{{message}}</h2>
    <button @click="changeMessage">改变message</button>
  </div>
  <script src="../lib/vue.mini.js"></script>
  <script>
    const app=Vue.createApp({
      data(){
        return{
          message:'hello',
          info:{name:'why',age:18}
        }
      },
      methods:{
        changeMessage(){
          this.message='你好啊,李银河',
          this.info={name:'hello',age:10}
        }
      },
      watch:{
        // 1. 默认有两个参数 newValue,oldValue
        message(newValue,oldValue){
          console.log('message发生改变',newValue,oldValue)
        },
        info(newValue,oldValue){
          // 如果是对象 拿到的是代理对象 想要拿到原始对象
          console.log('info改变了',Vue.toRaw(newValue),oldValue)
        }
      } 
    })
    app.mount('#app')
  </script>
</body>
</html>

watch的选项

如下,如果想要深层监听对象的话,就可以使用deep:true ,有时候我们有一个需求,比如页面初始化的时候进行网络请求,那么就可以使用immediate:true 让它第一次渲染时就执行一次监听器。但是即时深层监听,只要没有替换对象本身,虽然触发了副作用,但是这里的newValueoldValue 是相同的。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <h2>{{message}}</h2>
    <button @click="changeMessage">改变message</button>
  </div>
  <script src="../lib/vue.mini.js"></script>
  <script>
    const app=Vue.createApp({
      data(){
        return{
          message:'hello',
          info:{name:'why',age:18}
        }
      },
      methods:{
        changeMessage(){
          this.message='你好啊,李银河',
          this.info.name='hello'
        }
      },
      watch:{
        // 1. 默认有两个参数 newValue,oldValue
        message(newValue,oldValue){
          console.log('message发生改变',newValue,oldValue)
        },

        info:{
          handler(newValue,oldValue){
            console.log('info改变了',Vue.toRaw(newValue),oldValue)
          },
          deep:true,
          // 第一次渲染时 想要执行一次监听器
          immediate:true
        }
      } 
    })
    app.mount('#app')
  </script>
</body>
</html>