Options API

computed computed properties

1. Processing method of complex data

We know that some data in data can be displayed directly through interpolation syntax in the template .

However, in some cases, we may need to perform some conversion on the data before displaying, or combine multiple data for display;

  • For example, we need to perform operations on multiple data data , ternary operators to determine the results , and display the data after some transformation ;
  • It is very convenient to use expressions in templates , but they are designed for simple operations ;
  • Putting too much logic in the template will make the template heavy and difficult to maintain ;
  • And if it is used in multiple places, there will be a lot of repeated code;

Is there any way we can extract the logic out?

  • Yes, one way is to extract the logic into a method and put it in the options of methods ;
  • However, this approach has an intuitive disadvantage, that is, all data usage processes will become a method call ;
  • Another way is to use the computed property computed ;

2. Understand the computed attribute computed

What is a computed property?

  • The official did not give a direct conceptual explanation;
  • Instead, you should use computed properties for any complex logic that includes reactive data ;
  • Computed properties will be mixed into the component instance
    • The this context of all getters and setters is automatically bound to the component instance;

Computed property usage:

  • Options : computed
  • 类型{ [key: string]: Function | { get: Function, set: Function } }

3. Case realization idea

Let's look at three cases:

  • Case 1 : We have two variables: firstName and lastName, and hope that they will be displayed on the interface after splicing;
  • Case 2 : We have a score: score
    • When the score is greater than 60, pass is displayed on the interface;
    • When the score is less than 60, it will be displayed as a failure on the interface;
  • Case 3 : We have a variable message , which records a piece of text: such as Hello World
    • In some cases, we display this text directly;
    • In some cases we need to reverse this text;

We can have three implementation ideas:

  • Idea 1 : Use expressions directly in the template syntax;
  • Idea 2 : Use method to extract logic;
  • Idea 3 : Use the computed property computed ;

3.1. Implementation Idea 1: Template Syntax

Implementation of Idea 1: Template Syntax

  • Disadvantage 1: There are a lot of complex logic in the template, which is not easy to maintain (the original intention of the expression in the template is for simple calculation);
  • Disadvantage 2: When there are multiple times of the same logic, there are duplicate codes;
  • Disadvantage 3: When used multiple times, many operations need to be executed multiple times without caching;

3.2. Implementation idea 2: method implementation 

Implementation of idea 2: method implementation

  • Disadvantage 1: In fact, what we display first is a result, but it all becomes a method call;
  • Disadvantage 2: When the method is used multiple times, there is no cache and multiple calculations are required;

3.3. Implementation of the third idea: Computed implementation 

Implementation of idea 3: computed implementation

  • Note: A computed property looks like a function, but we don’t need to add () when using it , which will be discussed later when we talk about setters and getters;
  • We will find that calculating attributes is a better choice both intuitively and in effect;
  • And the calculated property is cached ;
<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">
        <!-- 插值语法表达式直接进行拼接 -->
        <!-- 1.拼接名字 -->
        <h2>{
   
   { fullname }}</h2>
        <h2>{
   
   { fullname }}</h2>
        <h2>{
   
   { fullname }}</h2>

        <!-- 2.显示分数等级 -->
        <h2>{
   
   { scoreLevel }}</h2>

        <!-- 3.反转单词显示文本 -->
        <h2>{
   
   { reverseMessage }}</h2>
    </div>

    <script src="../lib/vue.js"></script>
    <script>
        // 1.创建app
        const app = Vue.createApp({
            // data: option api
            data() {
                return {
                    // 1.姓名
                    firstName: "kobe",
                    lastName: "bryant",

                    // 2.分数: 及格/不及格
                    score: 80,

                    // 3.一串文本: 对文本中的单词进行反转显示
                    message: "my name is why"
                }
            },
            computed: {
                // 1.计算属性默认对应的是一个函数
                fullname() {
                    return this.firstName + " " + this.lastName
                },

                scoreLevel() {
                    return this.score >= 60 ? "及格" : "不及格"
                },

                reverseMessage() {
                    return this.message.split(" ").reverse().join(" ")
                }
            }
        })

        // 2.挂载app
        app.mount("#app")
    </script>
</body>
</html>

4. Calculated properties vs methods

  • In the above implementation ideas, we will find that the implementation of computed properties and methods seems to have little difference, and we have mentioned many times that computed properties are cached .
  • Next, let's take a look at the difference between the same calculation used multiple times, calculated properties and methods :
<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">
        <!-- 1.methods -->
        <h2>{
   
   { getFullname() }}</h2>
        <h2>{
   
   { getFullname() }}</h2>
        <h2>{
   
   { getFullname() }}</h2>

        <!-- 2.computed -->
        <h2>{
   
   { fullname }}</h2>
        <h2>{
   
   { fullname }}</h2>
        <h2>{
   
   { fullname }}</h2>

        <!-- 修改name值 -->
        <button @click="changeLastname">修改lastname</button>
    </div>

    <script src="../lib/vue.js"></script>
    <script>
        // 1.创建app
        const app = Vue.createApp({
            // data: option api
            data() {
                return {
                    firstName: "kobe",
                    lastName: "bryant"
                }
            },
            methods: {
                getFullname() {
                    console.log("getFullname-----")
                    return this.firstName + " " + this.lastName
                },
                changeLastname() {
                    this.lastName = "why"
                }
            },
            computed: {
                fullname() {
                    console.log("computed fullname-----")
                    return this.firstName + " " + this.lastName
                }
            }
        })

        // 2.挂载app
        app.mount("#app")
    </script>
</body>
</html>

 What is the reason?

  • This is because computed properties are cached based on their dependencies ;
  • When the data does not change , the calculated attribute does not need to be recalculated ;
  • However, if the dependent data changes , the calculated property will still be recalculated when used ;

5. Setters and getters for computed properties

  • In most cases, computed properties only need a getter method , so we will directly write the computed property as a function .
  • But what if we really want to set the value of a computed property ?
    • At this time, we can also set a setter method for the calculated property ;
<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>

        <button @click="setFullname">设置fullname</button>
    </div>

    <script src="../lib/vue.js"></script>
    <script>
        // 1.创建app
        const app = Vue.createApp({
            // data: option api
            data() {
                return {
                    firstname: "coder",
                    lastname: "why"
                }
            },
            computed: {
                // 语法糖的写法
                // fullname() {
                //   return this.firstname + " " + this.lastname
                // },

                // 完整的写法:(开发中几乎不会使用,而使用它的语法糖方式,了解即可)
                fullname: {
                    get: function () {
                        return this.firstname + " " + this.lastname
                    },

                    // 那么这个set方法中value形参就会接收到我们method中方法为其的赋值
                    set: function (value) {
                        const names = value.split(" ")
                        this.firstname = names[0]
                        this.lastname = names[1]
                    }
                }
            },
            methods: {
                setFullname() {
                    // 给computed对象中的fullname属性设置值
                    this.fullname = "kobe bryant"
                }
            }
        })

        // 2.挂载app
        app.mount("#app")
    </script>
</body>
</html>

How does the source code handle setters and getters? (learn)

        You may find it very strange, how does Vue internally process whether we pass in a getter, or an object containing setters and getters?

        In fact, it is very simple, the Vue source code is just a logical judgment;

 

listener watch

1. Know the listener watch

What is a listener?

  • During development, we define data in the object returned by data , and this data is bound to the template through interpolation syntax and other methods ;
  • When the data changes, the template will be automatically updated to display the latest data;
  • But in some cases, we want to monitor the change of a certain data in the code logic , at this time, we need to use the listener watch to complete;

The usage of the listener is as follows:

  • Option: watch
  • 类型:{ [key: string]: string | Function | Object | Array}
<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.js"></script>
  <script>
    // Proxy -> Reflect
    // 1.创建app
    const app = Vue.createApp({
      // data: option api
      data() {
        return {
          message: "Hello Vue",
          info: { name: "why", age: 18 }
        }
      },
      methods: {
        changeMessage() {
          this.message = "你好啊, 李银河!"
          this.info = { name: "kobe" }
        }
      },
      watch: {
        // ======函数名为侦听的数据名=====

        // 1.默认有两个参数: newValue/oldValue
        message(newValue, oldValue) {
          console.log("message数据发生了变化:", newValue, oldValue)
        },
        info(newValue, oldValue) {
          // 2.如果是对象类型, 那么拿到的是代理对象
          // console.log("info数据发生了变化:", newValue, oldValue)
          // console.log(newValue.name, oldValue.name)

          // 3.获取原生对象
          // console.log({ ...newValue })
          console.log(Vue.toRaw(newValue))
        }
      }
    })

    // 2.挂载app
    app.mount("#app")
  </script>
</body>
</html>

2. Configuration options for the listener watch

Let's look at an example first:

  • When we click the button, the value of info.name will be modified ;
  • At this time, we use watch to listen to info , can we listen to it ? The answer is no .

​​​​​​​​This is because by default, watch is only listening to info reference changes , and will not respond to changes in internal properties :

  • At this time we can use an option deep for deeper listening;
  • Note that we said earlier that the listening attribute in the watch can also be an Object;

There is another attribute , which is expected to be executed immediately at the beginning :

  • At this time we use the immediate option ;
  • At this time, no matter whether there is any change in the subsequent data, the listening function will be executed once in a limited time;
<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>{
   
   { info.name }}</h2>
        <button @click="changeInfo">修改info</button>
    </div>

    <script src="../lib/vue.js"></script>
    <script>
        // 1.创建app
        const app = Vue.createApp({
            // data: option api
            data() {
                return {
                    info: {name: "why", age: 18}
                }
            },
            methods: {
                changeInfo() {
                    // 1.创建一个新对象, 赋值给info
                    // this.info = { name: "kobe" }

                    // 2.直接修改原对象某一个属性
                    this.info.name = "kobe"
                }
            },
            watch: {
                // 默认watch监听不会进行深度监听
                // info(newValue, oldValue) {
                //   console.log("侦听到info改变:", newValue, oldValue)
                // }

                // 进行深度监听
                info: {
                    handler(newValue, oldValue) {
                        // 改变对象的属性,这个时候newValue与oldValue其实是同一个对象
                        console.log("侦听到info改变:", newValue, oldValue)
                        // 下面这个代码第一次运行打印为false,因为设置了immediate这个属性
                        console.log(newValue === oldValue)
                    },
                    // 监听器选项:
                    // info进行深度监听
                    deep: true,
                    // 第一次渲染直接执行一次监听器
                    immediate: true
                },
                "info.name": function (newValue, oldValue) {
                    console.log("name发生改变:", newValue, oldValue)
                }
            }
        })

        // 2.挂载app
        app.mount("#app")
    </script>
</body>
</html>

In fact, the listener also has this configuration method (not commonly used)

 

3. Other ways of listener watch 

The other one is not mentioned in the Vue3 documentation, but it is mentioned in the Vue2 documentation that it listens to the properties of the object:

Another way is to use the $watch API:

We can use this.$watches to listen in the created life cycle (will be discussed later);

  • The first parameter is the source to listen to;
  • The second parameter is the listened callback function callback;
  • The third parameter is additional other options, such as deep , immediate ;
<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.js"></script>
    <script>
        // 1.创建app
        const app = Vue.createApp({
            // data: option api
            data() {
                return {
                    message: "Hello Vue"
                }
            },
            methods: {
                changeMessage() {
                    this.message = "你好啊, 李银河!"
                }
            },
            // 生命周期回调函数: 当前的组件被创建时自动执行
            // 一般在该函数中, 会进行网络请求
            created() {
                // ajax/fetch/axios
                console.log("created")

                this.$watch("message", (newValue, oldValue) => {
                    console.log("message数据变化:", newValue, oldValue)
                }, {deep: true})
            }
        })

        // 2.挂载app
        app.mount("#app")
    </script>
</body>
</html>

Guess you like

Origin blog.csdn.net/weixin_52851967/article/details/128717884