1 计算属性
1.1 计算属性的引入
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。
<body>
<div id="name-methods">
    姓:<input type="text" v-model="firstName"><br/>
    名:<input type="text" v-model="secondName"><br/>
    全名:<span>{{firstName.slice(0,3)}}-{{secondName}} </span>
</div>
<script type="text/javascript">
    new Vue({
        el: '#name-methods',
        data() {
            return {
                firstName: 'zhao',
                secondName: 'shuai-lc'
            }
        },
        methods: {}
    })
</script>
</body>
</html>
抽象成如下方式更为合适:
<body>
<div id="name-methods">
    姓:<input type="text" v-model="firstName"><br/>
    名:<input type="text" v-model="secondName"><br/>
    全名:<span>{{ fullName() }}</span>
</div>
<script type="text/javascript">
    new Vue({
        el: '#name-methods',
        data() {
            return {
                firstName: 'zhao',
                secondName: 'shuai-lc@inspur.com'
            }
        },
        methods: {
            fullName() {
                return this.firstName + '-' + this.secondName
            }
        }
    })
</script>
</body>
</html>
1.2 计算属性的理解
-  定义:要用的属性不存在,要通过已有的属性计算得来。 
-  原理:底层借助了Object.defineproperty方法提供的getter和setter。 
-  get函数什么时候执行? 
 1)初次读取时会执行一次。
 2)当依赖的数据发生改变时会被再次调用。
-  优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。 
-  备注: 
 1)计算属性最终会出现在vm上,直接读取使用即可。
 2)如果计算属性要被修改,那必须直接写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<body>
<div id="name-calculate">
    姓:<input type="text" v-model="firstName"><br/>
    名:<input type="text" v-model="secondName"><br/>
    全名:<span>{{ fullName }}</span>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
        el: '#name-calculate',
        data: {
            firstName: 'zhao',
            secondName: 'shuai-lc@inspur.com'
        },
        methods: {},
        computed: {
            fullName: {
                // 当有人读取fullName的时候调用get()
                // 1)初次读取时会执行一次。
                // 2)当依赖的数据发生改变时会被再次调用。
                get() {
                    return this.firstName + ' - ' + this.secondName
                },
                // 当有人修改fullName时候调用set()
                set(value) {
                    const list = value.split('-');
                    this.firstName = list[0]
                    this.secondName = list[1]
                }
            }
        }
    })
</script>
</body>
</html>
注意的是 :
- vm._data里面是没有计算属性的
  
- 计算属性中get函数的this,Vue已经维护好了,并把getter中的this指向调成了vm。
- 书写多次fullName但是getter只用了一次,说明Vue有缓存。如果用上集methods方法,则书写几次方法就调用几次,显然有弊端。
1.3 计算属性简写
确定了只读,不可以修改,才能使用简写方式。
<body>
<div id="root">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:<span>{{ fullName }}</span> <br/><br/>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        computed: {
            // 简写
            fullName: function () {
                return this.firstName + '-' + this.lastName
            }
        }
    })
</script>
</body>
</html>
2 监视属性
2.1 监视属性
- 当被监视的属性变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视!
- 监视的两种写法:
 1)new Vue时传入watch配置
 2)通过vm.$watch监视
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
    <h1>今天天气很 - {{ info }}</h1>
    <button @click="changeWeather()">切换天气</button>
</div>
<script type="text/javascript">
    new Vue({
        el: '#weather',
        data: {
            judgeHot: true,
        },
        computed: {
            info() {
                return this.judgeHot ? '炎热' : '凉爽'
            }
        },
        methods: {
            changeWeather() {
                this.judgeHot = !this.judgeHot
            }
        },
        watch: {
            judgeHot: {
                // immediate:true, //初始化时让hander()调用一下, 默认为false
                immediate: false,
                // handler什么时候调用?当judgeHot发生改变时
                handler(newValue, oldValue) {
                    console.log('judgeHot 被修改了 ...', newValue, oldValue)
                }
            },
            info: {
                handler(newValue, oldValue) {
                    console.log('info 被修改了 ...', newValue, oldValue)
                }
            }
        }
    })
</script>
</body>
</html>

2.2 深度监视
- Vue中的watch默认不监测对象内部值得改变(一层)。
- 配置deep:true可以监测对象内部值改变(多层)。
备注:
- Vue自身可以监测对象内部值得改变,但Vue提供得watch默认不可以!
- 使用watch时根据数据得具体结构,决定是否采用深度监视。默认不开启是为了提升效率
<body>
<div id="weather">
    <h3>a的值是:{{ numbers.a }}</h3>
    <button @click="autoIncrement()">点我让a+1</button>
</div>
<script type="text/javascript">
    let vm = new Vue({
        el: '#weather',
        data: {
            numbers: {
                a: 1,
                b: 2
            }
        },
        computed: {},
        methods: {
            autoIncrement() {
                return this.numbers.a++
            }
        },
        watch: {
            // 监视多级结构中某个属性的变化
            'numbers.a': {
                handler() {
                    console.log('a has changed ...')
                }
            }
        }
    })
</script>
</body>
</html>

<body>
<div id="weather">
    <h3>a的值是:{{ numbers.a }}</h3>
    <button @click="autoIncrementA()">点我让a+1</button>
    <h3>b的值是:{{ numbers.b }}</h3>
    <button @click="autoIncrementB()">点我让b+1</button>
    <br/><br/>
</div>
<script type="text/javascript">
    let vm = new Vue({
        el: '#weather',
        data: {
            numbers: {
                a: 1,
                b: 2
            }
        },
        computed: {},
        methods: {
            autoIncrementA() {
                return this.numbers.a++
            },
            autoIncrementB() {
                return this.numbers.b++
            }
        },
        watch: {
            // 监视多级结构中所有属性的变化
            numbers: {
                deep: true,
                handler() {
                    console.log('numbers has changed ...')
                }
            }
        }
    })
</script>
</body>
</html>
2.3 监视的简写形式
简写的前提是,如果不需要immediate、deep等的配置项,即配置项中只有handler的时候才可以简写。
<body>
<div id="root111">
    <h2>今天天气很{{ info }}</h2>
    <button @click="changeWeather">切换天气</button>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    const vm = new Vue({
        el: '#root111',
        data: {
            isHot: true
        },
        computed: {
            info() {
                return this.isHot ? '炎热' : '凉爽'
            }
        },
        methods: {
            changeWeather() {
                this.isHot = !this.isHot
            }
        },
        watch: {
            /*isHot:{
                handler(newValue,oldValue){
                    console.log('isHot被修改了',newValue,oldValue)
                }
            }*/
            isHot(newValue, oldValue) {
                console.log('isHot被修改了', newValue, oldValue)
            }
        }
    })
    vm.$watch('isHot', {
        handler(newValue, oldValue) {
            console.log('isHot 被修改了 ...', newValue, oldValue)
        }
    })
    //vm.$watch简写
    vm.$watch('isHot', function (newValue, oldValue) {
        console.log('isHot被修改了', newValue, oldValue)
    })
</script>
</body>
</html>
3 watch对比computed
两者都能实现的时候,选用computed比较简单,需要异步计算等比较复杂实现的时候用watch。computed和watch之间的区别:
- computed能完成的功能,watch都可以完成。
- watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
 两个重要的小原则:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
- 所有不被Vue所管理的函数(定时器的回调函数、Ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象。
<body>
<div id="watch-name">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:<span>{{ fullName }}</span>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    let vm = new Vue({
        el: '#watch-name',
        data: {
            firstName: 'zhao',
            lastName: 'shuai-lc@inspur.com',
            fullName: 'zhao#shuai-lc@inspur.com'
        },
        watch: {
            firstName(newValue, oldValue) {
                this.fullName = newValue + '#' + this.lastName
            },
            lastName(newValue, oldValue) {
                this.fullName = this.firstName + '#' + newValue
            }
        }
    })
</script>
</body>
</html>

<body>
<div id="watch-name">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:<span>{{ fullName }}</span>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    let vm = new Vue({
        el: '#watch-name',
        data: {
            firstName: 'zhao',
            lastName: 'shuai-lc@inspur.com',
            fullName: 'zhao#shuai-lc@inspur.com'
        },
        watch: {
            firstName(val) {
                setTimeout(() => {
                    console.log(this) // vue
                    this.fullName = val + '-' + this.lastName
                }, 1000)
                setTimeout(function () {
                    console.log(this) // window
                    this.fullName = val + '-' + this.lastName
                }, 1000)
            },
            lastName(val) {
                this.fullName = this.firstName + '-' + val
            }
        }
    })
</script>
</body>
</html>



















