一、计算属性

什么是计算属性

计算属性,类似于过滤器,用于处理某个或者几个属性的复杂展示逻辑,但是并不会改变源数据;

示例;

  1. {
  2. ...
  3. computed: {
  4. sum() {
  5. return this.products.filter(item => item.isSelected).reduce((prev, next) => {
  6. return prev + next.productPrice * next.productCount;
  7. }, 0)
  8. }
  9. },
  10. ...
  11. }

使用计算属性

计算属性默认和普通数据一样,直接在 HTML 中使用即可;示例

  1. <td colspan="6">
  2. 总价格:{{sum | toFixed}}
  3. </td>

计算属性的set

  • computed 中的属性都有一个 get 和 set 方法,当获取这个属性时,会执行 get 方法,属性值是 get 方法的返回值;当设置 computed 中的属性时,会触发 set 方法;
{
    ...
    computed: {
        checkAll: {
            get() {
                return this.products.every(i => i.isSelected);
            },
            set(val) {
                this.products.forEach(i => i.isSelected = val);
            }
        }
    },
    ...
}
  • computed 中的属性都会被 vm 所代理,最终都要放到 vm 上;
  • computed 中的属性和 data 中的属性不能同名,也不能和 methods 中的属性同名
  • computed 计算属性还可以是一个函数,相当于只设置 get 的情况,函数的返回值就是计算属性的值,但是不能设置
  • 如果一个属性依赖于其他属性计算而来,那么这个属性最好用 computed

计算属性不能用在异步处理中

  • 计算属性的处理不能写在异步处理程序中,如定时器、ajax等
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <input type="text" v-model="sth"> <br>
    {{msg}}
</div>

<script src="vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            sth: ''
        },
        computed: {
            msg () {
                // 计算属性的处理不能写在异步处理程序中,如定时器、ajax等
                setTimeout(() => {
                    if (this.sth.length > 5) {
                        return '太长了'
                    }
                    if (this.sth.length < 3) {
                        return '太少了'
                    }
                }, 0)
            }
        }
    })
</script>
</body>
</html>

计算属性示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
</head>
<body>
<div id="app">
    <!--caption 只能放在 table 中使用:表头-->
    <!--bootstrap-->
    <div class="container">
        <div class="row">
            <h2 class="text-center text-danger">珠峰购物车</h2>
            <table class="table table-bordered">
                <tr>
                    <td>
                        全选 <input type="checkbox" v-model="checkAll"/>
                    </td>
                    <td>
                        商品
                    </td>
                    <td>
                        单价
                    </td>
                    <td>
                        数量
                    </td>
                    <td>
                        小计
                    </td>
                    <td>
                        操作
                    </td>
                </tr>
                <tr v-for="(product, index) in products" :key="index">
                    <!-- {
                                     "isSelected": true,
                                     "productCover": "https://img10.360buyimg.com/cms/s80x80_jfs/t6094/107/710811867/382815/4d54717/592bf165N755a88f0.jpg",
                                     "productName": "深入浅出Node.js",
                                     "productInfo": "颜色:Node.js学习",
                                     "productPrice": 57.8,
                                     "productCount": 3
                                 }-->
                    <td><input type="checkbox" v-model="product.isSelected"></td>
                    <td>
                        <img :src="product.productCover" :title="product.productName" alt="">
                        {{product.productInfo}}
                    </td>
                    <td>
                        {{product.productPrice}}
                    </td>
                    <td>
                        <input type="number" v-model="product.productCount" min="1"/>
                    </td>
                    <td>
                        {{product.productPrice * product.productCount | toFixed(2)}}
                    </td>
                    <td>
                        <button class="btn btn-danger" @click="remove(product)">删除</button>
                    </td>
                </tr>
                <tr>
                    <td colspan="6">
                        总价格:{{sum | toFixed}}
                    </td>
                </tr>
            </table>
        </div>
    </div>
</div>

<script src="axios.js"></script>
<script src="vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            products: []
        },
        filters: {
            toFixed(val, num = 2) {
                return '¥' + val.toFixed(num)
            }
        },
        created() {
            this.getData();
        },
        computed: {
            sum() {
                return this.products.filter(item => item.isSelected).reduce((prev, next) => {
                    return prev + next.productPrice * next.productCount;
                }, 0)
            },
            checkAll: {
                get() {
                    return this.products.every(i => i.isSelected);
                },
                set(val) {
                    this.products.forEach(i => i.isSelected = val);
                }
            }
        },
        methods: {
            getData() {
                axios.get('carts.json').then(({data}) => {
                    this.products = data;
                })
            },
            remove(val) {
                // val 点击时删除的 product
                this.products = this.products.filter(item => item !== val);
            },
            changeAll() {
                // 如果全选为 true,即 this.checkAll 为 true,下面的商品的 checkbox 都要选中,如果是全选是 false,那么商品的 checkbox 也是 false
                this.products.forEach(item => item.isSelected = this.checkAll)
            },
            changeOne() {
                // 点击每一个 input 的复选框时,去校验是否有是所有的 product 中的 isSelected 都为 true,如果都是 true,那么 checkAll 结果也是 true,如果有一个是 false,那么 checkAll 就是 false,使用 every 方法

                this.checkAll = this.products.every(item => item.isSelected);
            }
        }
    })
</script>
</body>
</html>

二、侦听器 watch

什么是侦听器属性

watch: 侦听器属性,用于监听某个属性改变,如果发生改变就会触发对应的函数

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <input type="text" v-model="sth"> <br>
    {{msg}}
</div>

<script src="vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            sth: '',
            msg: '',
        },
        watch: {
            // computed: 计算属性
            // watch: 侦听器属性,用于监听某个属性改变,如果发生改变就会触发对应的函数
             sth(newVal, oldVal) {
                 // 监听属性 sth,当 sth 属性发生变化时就会触发这个函数
                 // 这个函数有两个参数,第一个是 sth 的最新值,第二个是 sth 的上一个值
                 console.log(newVal);
                 console.log(oldVal);
                 setTimeout(() => {
                     if (newVal.length > 5) {
                         this.msg = '太长了'
                     }
                     if (newVal.length < 3) {
                         this.msg = '太少了'
                     }
                 }, 0)
            }
        }
    });

    // 在工作中能使用 computed 尽量使用 computed 而不要使用 watch;
</script>
</body>
</html>

三、computed 和 watch

computed 计算属性

  1. 页面加载时就求值;支持缓存,如果依赖的数据发生改变,才会重新计算;
  2. 不支持异步
  3. 如果一个属性是由其他属性计算而来,这个属性依赖其他属性,依赖发生变化时自动求取最新的计算结果

watch 计算属性

  1. 页面加载时不求值,依赖值发生改变时才求值
  2. watch 支持异步
  3. watch 只能监听一个属性的变化,如果有属性依赖这个结果,那么需要手动去重新计算这个结果;

示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <input type="text" v-model="firstName" />
    <input type="text" v-model="lastName" />
    {{fullName}}
</div>


<script src="vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            firstName: '宾',
            lastName: '马',
            fullName: ''
        },
        computed: {
            /*fullName () {
                return this.lastName + this.firstName;
            }*/
        },
        watch: {
            firstName(newVal, oldVal) {
                this.fullName = this.lastName + newVal;
            },
            lastName(nawVal, oldVal) {
                this.fullName = newVal + this.firstName;
            }
        }
    });

    // computed vs watch
    // computed
    // 1. 页面加载时就求值;支持缓存,如果依赖的数据发生改变,才会重新计算;
    // 2. 不支持异步
    // 3. 如果一个属性是由其他属性计算而来,这个属性依赖其他属性,依赖发生变化时自动求取最新的计算结果

    // watch
    // 1. 页面加载时不求值,依赖值发生改变时才求值
    // 2. watch 支持异步
    // 3. watch 只能监听一个属性的变化,如果有属性依赖这个结果,那么需要手动去重新计算这个结果;
</script>
</body>
</html>

四、v-bind:class

什么是v-bind:class

  • v-bind: 绑定动态属性,v-bind 可以简写成 :
  • v-bind:class 动态绑定类名,属性值有以下常用情况:;
    • 如果是对象,对象的属性名是 class 名,属性值为 true 时类名生效,为 false 时不生效
    • 如果是三元运算符,三元运算符表达式的返回结果是生效的 class
  • :class 和原有的 class 并不会冲突,如果不同,最终会合并,如果相同会覆盖原有的;

代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .x {
            font-size: 30px;
        }
        .y {
            color: red;
        }
        .z {
            background: lightgreen;
        }
    </style>
</head>
<body>
<div id="app">
    <div class="x" :class="{y: true, z: flag}">A</div>
    <div :class="1 ? 'y' : 'z'">C</div>
</div>

<script src="vue.js"></script>
<script>
    // v-bind: 绑定动态属性,v-bind 可以简写成 :
    // v-bind:class 属性值有以下常用情况:;
    // 如果是对象,对象的属性名是 class 名,属性值为 true 时类名生效,为 false 时不生效
    // 如果是三元运算符,三元运算符表达式的返回结果是生效的 class
    // :class 和原有的 class 并不会冲突,如果不同,最终会合并,如果相同会覆盖原有的;
    let vm = new Vue({
        el: '#app',
        data: {
            class1: 'y',
            flag: true
        }
    })
</script>
</body>
</html>

五、v-bind:style

什么是v-bind:style?

v-bind:style 用来绑定元素的动态样式;

:style的常见值

v-bind:style 属性值是一个对象或者数组,如果是对象,里面可以存储具体样式;数组中可以放多个样式对象;

代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <div :style="{color: 'red',width: '100px'}">A</div>
    <div :style="[style1, style2]">B</div>
</div>

<script src="vue.js"></script>
<script>
    // v-bind:style 属性值是一个对象或者数组,如果是对象,里面可以存储具体样式;数组中可以放多个样式对象;
    let vm = new Vue({
        el: '#app',
        data: {
            style1: {background: 'pink'},
            style2: {color: '#000'}
        }
    })
</script>
</body>
</html>

六、自定义指令

  • 自定义指令可以给元素增加一些特殊的功能;以拖拽为例;

代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        #app div {
            position: absolute;
            width: 100px;
            height: 100px;
            background: red;
            -webkit-user-select: none;
        }
        #app div:nth-child(2) {
            top: 200px
        }
    </style>
</head>
<body>
<div id="app">
    <div v-drag>1</div>
    <div v-drag>2</div>
</div>

<script src="vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        directives: {
            // 对象中存储的自定义指令
            drag: {
                inserted(el) {
                    // 这个函数定义了指令的具体行为 第一个参数是使用这个元素的元素
                    el.onmousedown = function (e) {
                        this.startX = e.pageX - this.offsetLeft;
                        this.startY = e.pageY - this.offsetTop;
                        document.onmousemove = function (e) {
                            el.style.left = e.pageX - el.startX + 'px';
                            el.style.top = e.pageY - el.startY + 'px';
                        };
                        document.onmouseup = function () {
                            document.onmousemove = document.onmouseup = null;
                        }
                    }
                }
            }
        }
    })
</script>
</body>
</html>

七、todoList

代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .del {
            text-decoration: line-through;
            color: #ccc;
        }
    </style>
</head>
<body>
<div id="app">
    <div class="container">
        <div class="row">
            <div class="col-md-8">
                <div class="panel panel-success">
                    <div class="panel-heading bg-warning">
                        <h2 class="text-danger">亲~^_^,你有2件事要完成</h2>
                        <input type="text" class="form-control" @keyup.enter="add" v-model="title">
                    </div>
                    <div class="panel-body">
                        <ul class="list-group">
                            <li class="list-group-item" v-for="(todo, index) in filterTodo" :key="index">
                            <span :class="{del: todo.isSelected}">
                                <input type="checkbox" v-model="todo.isSelected"> {{todo.title}}
                            </span>
                                <button class="btn btn-danger pull-right btn-xs" @click="remove(todo)">删除</button>
                            </li>
                        </ul>
                    </div>
                    <div class="panel-footer">
                        <a href="#all">全部任务</a>
                        <a href="#finish">已完成</a>
                        <a href="#unfinish">未完成</a>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>


<script src="vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            todos: [
                {
                    isSelected: false,
                    title: '睡觉'
                },
                {
                    isSelected: false,
                    title: '吃饭'
                }
            ],
            title: '',
            hash: '#all'
        },
        directives: {
            focus(el) {
                el.focus()
            }
        },
        created() {
            window.addEventListener('hashchange', () => {
                // 一旦页面中的 hash 值发生变化,对 data 中的 hash 重新赋值
                this.hash = window.location.hash;
            })
        },
        methods: {
            add () {
                this.todos.push({
                    isSelected: false,
                    title: this.title
                });
                this.title = '';
            },
            remove(val) {
                this.todos = this.todos.filter(item => item !== val);
            }
        },
        computed: {
            filterTodo() {
                switch (this.hash) {
                    case '#all':
                        return this.todos;
                    case '#finish':
                        return this.todos.filter(item => item.isSelected);
                    case '#unfinish':
                        return this.todos.filter(i => !i.isSelected);
                }
            }
        }
    })
</script>
</body>
</html>