【前端 Vue】02 - 计算属性 + 事件绑定 + v-for + v-if + v-show

1. 计算属性

1.1 什么是计算属性 ?

  1. 我们知道,在模板中可以直接通过插值语法显示一些data中 的数据。但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示。比如我们有firstNamelastName 两个变量,我们需要显示完整的名称。但是如果多个地方都需要显示完整的名称,我们就需要写多个{{firstName}} {{lastName}}

  2. 我们可以将上面的代码换成计算属性:我们发现计算属性是写在实例的 computed 选项中的。

<div id="app">

  <h2>{{firstName + ' ' + lastName }}</h2>
  <h2>{{firstName}} {{lastName }}</h2>
  <h2>{{getFullName()}}</h2>
  <!-- 计算属性在使用的时候不需要加上括号  -->
  <h2>{{fullName}}</h2>

</div>

<script src="../../js/vue.js"></script>
const app = new Vue({
       el: '#app',
       data: {
           firstName: 'Lebron',
           lastName: 'James'
       },
       methods: {
           getFullName() {
               return this.firstName + '  ' + this.lastName;
           }
       },
       /* 计算属性 */
       computed: {
           // 计算属性一般不加动词 get 类似这些
     fullName() {
       return this.firstName + '  ' + this.lastName;
     }
   }
});
三种渲染方式

1.2 使用计算属性计算图书总价格

  1. 在计算图书总价的时候尽量使用计算属性的方式,计算属性有缓存功能,在值不改变的时候他只会计算一次。

  2. 而使用方法返回值的方式,如果其中出现循环的时候就会降低计算效率。

<div id="app">
  <h2>总价格:{{totalPrice}}</h2>
</div>

<!-- 计算属性在多次调用的时候只会调用一次 -->
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            books: [
                {id: '110', name: 'Unix编程艺术', price: 120},
                {id: '111', name: '代码大全', price: 120},
                {id: '112', name: '深入理解计算机原理', price: 120},
                {id: '112', name: 'Unix编程艺术', price: 120},
                {id: '112', name: '现代操作系统', price: 120}
            ]
        },
        computed: {
            totalPrice() {
                let result = 0;
                for (let item of this.books) {
                    result += item.price;
                }
                return result;
            }
        }
    });

1.3 计算属性的 setter 和 getter

  1. 每个计算属性都包含一个 getter 和一个 setter ,在获取值时都是使用计算属性的getter方法读取,在某些情况下,你可以提供一个setter方法(不常用)。
<div id="app">
  <h2>{{fullName}}</h2>
  <h2>{{fullNameSetAndGet}}</h2>
</div>

<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            firstName: 'Kobe',
            lastName: 'Bryant'
        },
        computed: {
            fullName() {
                return this.firstName + '  ' + this.lastName;
            },

            fullNameSetAndGet: {
                // set 方法一般是被删除的
                /**
                 *
                 * @param newVal
                 */
                set(newVal) {
                    console.log('set方法');
                    console.log(newVal);
                    const names = newVal.split(' ');
                    this.firstName = names[0];
                    this.lastName = names[1];
                },
                get() {
                    return this.firstName + '  ' + this.lastName;
                }
            }

        }
    });

1.4 计算属性的缓存

  1. 计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。如果值发生改变才进行重新计算。
<div id="app">
  <!-- 1. 直接拼接 -->
  <h2>{{firstName}}  {{lastName}}</h2>

  <!-- 2. 通过定义methods-->
  <!--<h2>{{getFullName()}}</h2>-->
  <!--<h2>{{getFullName()}}</h2>-->
  <!--<h2>{{getFullName()}}</h2>-->
  <!--<h2>{{getFullName()}}</h2>-->

  <!-- 3. 通过computed -->
  <h2>{{fullName}}</h2>
  <h2>{{fullName}}</h2>
  <h2>{{fullName}}</h2>
  <h2>{{fullName}}</h2>
</div>

<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            message: '您好啊!',
            firstName: 'Lebron',
            lastName: 'James'
        },
        methods: {
            getFullName() {
                console.log('getFullName');
                return this.firstName + this.lastName;
      }
    },
        /* 计算属性 */
        computed: {
            fullName() {
                console.log('fullName');
                return this.firstName + this.lastName;
             }
    }
    });

2. ES6语法补充

  1. ES6中 ,新出现 letconst 关键字,用于代替 var,使用let 声明变量,使用const 声明一个常量。

  2. let 关键字声明的变量有块级作用域,var声明的变量没有块级作用域。

  3. 在ES6之前 if 和 for代码块是没有块级作用域的。在Es6 之前因为iffor都没有块级作用域所以在很多时候,我们都必须借助与function的作用域来解决应用外边变量的问题。

2.1 let 和 var 的块级作用域

  1. ES6中 使用 let 关键字替换var关键字声明变量,var 关键字声明变量没有块级作用域,容易引发一些问题。
// ==========================================================
    // 块级作用域
    {

        var name = 'lyp';
        console.log(name);
    }
    console.log(name); // lyp

    {
        let age = 23;
        console.log(age);
    }
    // console.log(age); // Uncaught ReferenceError: age is not defined
    // ==========================================================
  
  

    // ==========================================================
    // 没有块级作用域引起的问题
    var func;
    if (true) {
        var firstName = 'andy';

        func = function () {
            console.log(firstName);
        }
    }
    // 在这里修改 firstName
    firstName = 'kobe';
    func(); // kobe
    // ==========================================================

2.2 块级作用域对 for 循环的影响

  1. 因为回调函数是一个异步的操作,导致循环执行完成之后才会值回调函数,解决这个问题可以使用闭包。使用立即函数 + 闭包,因为函数拥有自己的作用域。
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
const btns = document.querySelectorAll('button');
/**
 * ES5中的var是没有块级作用域的
 *
 * 使用闭包 :
 * 为什么闭包可以解决问题 , 因为函数是有作用域的
 * javascript 类似 if 和 for 都是没有作用域的
 * 在Es6之前因为if和for都没有块级作用域所以在很多时候,我们都必须借助与function的作用域来解决应用外边变量1的问题
 *
 * 在ES6中加入的let是有块级作用域的
 */
for (let i = 0; i < btns.length; i++) {
  // 这里面的i使用let声明有自己的块级作用域
  btns[i].addEventListener('click', () => {
    console.log('第' + (i + 1) + '个按钮被点击');
  });
}

2.3 使用let关键字改写 2.1 的案例解决问题

  1. 因为let有自己的块级作用域。
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
for (let i = 0; i < btns.length; i++) {
  // 这里面的i使用let声明有自己的块级作用域
  btns[i].addEventListener('click', () => {
    console.log('第' + (i + 1) + '个按钮被点击');
  });
}

2.4 三种方案对比

  1. 定义多个按钮的点击事件。由于var 没有块级作用域而引发的问题。有两种解决方案:
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
var btns = document.querySelectorAll('button');
    console.log(btns);

    // 1. 不使用闭包的方式
    /*for (var i = 0; i < btns.length; i++) {
        btns[i].addEventListener('click', () => {
            console.log('第' + (i + 1) + '个按钮被点击了!');
        });
    }*/

    // 2. 使用闭包处理
    /*for (var i = 0; i < btns.length; i++) {
        (function (i) {
            btns[i].addEventListener('click', () => {
                console.log('第' + (i + 1) + '个按钮被点击了!');
            });
        })(i)
    }*/


    // 3. 使用let关键字
    for (let i = 0; i < btns.length; i++) {
        btns[i].addEventListener('click', () => {
            console.log('第' + (i + 1) + '个按钮被点击了!');
        });
    }

2.5 const的使用和注意点

  1. 建议 : 在开发中优先使用const只有需要在改变某个标识符的时候才使用let

  2. 常量是不允许被修改的 一旦给const修饰的标识符被赋值之后不能进行修改。

  3. 在使用const 定义标识符,必须进行赋值。

  4. 常量的含义是指向的对象不能修改,但是可以改变对象内部的属性。


    // 建议 : 在开发中优先使用const只有需要在改变某个标识符的时候才使用let
    const name = 'sa easy';

    // 1. 常量是不允许被修改的 一旦给const修饰的标识符被赋值之后不能进行修改。
    name = 'change';

    // 2. 在使用const定义标识符,必须进行赋值
    const name;

    // 3. 常量的含义是指向的对象不能修改,但是可以改变对象内部的属性
    const obj = {
        name: 'lyp',
        age: 23,
        height: 181
    };

    obj.height = 192;

    // 不能改变其指向
    obj = 123;

2.6 对象字面量的增强写法

/**
     * 对象字面量写法
     * @type {{}}
     */
    const objA = {
        name: '张三',
        age: 23,
        run: function () {
            console.log('您在干嘛');
        }
    };


    const name = '王五';
    const age = 25;
    const height = 181;

    /**
     * ES5写法
     * */
    const objC = {
        name: name,
        age: age,
        height: height
    };

    /**
     * Es6 增强写法
     * @type {{}}
     */
    const objB = {
        name,
        age,
        height
    }

    // 方法增强写法
    /**
     * ES5 写法
     * @type {{run: objD.run}}
     */
    const objD = {
        run: function () {

        }
    }

    /**
     * ES6写法
     * @type {{}}
     */
    const objE = {
        run() {

        }
    }

3. 事件监听

  1. 使用 v-on 绑定事件监听器,可以缩写成@ 。 是它的语法糖简写形式。

3.1 v-on 的基本使用

  1. 下面的代码中,我们使用了 v-on:click="counter++"。另外,我们可以将事件指向一个在methods中定义的函数。
<div id="app">
  <h2>{{counter}}</h2>
  <!--<button v-on:click="increment">+</button>-->
  <!--<button v-on:click="decrement">-</button>-->
  <!-- 语法糖简写形式 -->
  <button @click="increment">+</button>
  <button @click="decrement">-</button>
</div>

<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            counter: 0
        },
        methods: {
            increment() {
                this.counter++;
            },
            decrement() {
                this.counter--;
            }
        }
    });

3.2 v-on 的参数

当通过methods 中定义方法,以供@click调用时,需要注意参数问题:
  1. 情况一:如果该方法不需要额外参数,那么方法后的()可以不添加。但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去。
  1. 情况二:如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。
<!-- 1. 函数中的默认参数是事件对象 event -->
  <button @click="btnClick">按钮1</button>
  <!-- 2. 如果明确的写出则第一个参数就是 -->
  <button @click="btnClick1('abc' , $event)">按钮2</button>
  <!-- 3. 传递的变量不存在而且未使用引号标明 -->
  <button @click="btnClick2(abc , $event)">按钮3</button>
const app = new Vue({
        el: '#app',
        data: {},
        methods: {
            btnClick(event) {
                console.log(event);// 事件对象 MouseEvent {isTrusted: true, screenX: 88, screenY: 116, clientX: 88, clientY: 13, …}
            },
            btnClick1(abc, event) {
                console.log(abc, event); // abc MouseEvent {isTrusted: true, screenX: 88, screenY: 116, clientX: 88, clientY: 13, …}
            },
      btnClick2(abc,event) {
                console.log(abc, event); // Property or method "abc" is not defined on the instance
        // undefined MouseEvent {isTrusted: true, screenX: 152, screenY: 119, clientX: 152, clientY: 16, …}
      }
        }
    });

3.3 v-on 的修饰符

  1. 在某些情况下,我们拿到 event 的目的可能是进行一些事件处理。
Vue 提供了修饰符来帮助我们方便的处理一些事件:
  1. .stop - 调用 event.stopPropagation()

  2. .prevent - 调用 event.preventDefault()

  3. .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。

  4. .native - 监听组件根元素的原生事件。

  5. .once - 只触发一次回调。

4. v-if、v-else-if、v-else

  1. 这三个指令与JavaScript 的条件语句if、else、else if 类似。

  2. Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件。

v-if 的基本使用
<div id="app">
  <div v-if="isShow">
    <h1>abc</h1>
    <h1>abc</h1>
    <h1>abc</h1>
    <h1>abc</h1>
    <h1>abc</h1>
  </div>
  <div v-else>
    <h1>当isShow为false的时候显示</h1>
  </div>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            isShow: true
        }
    });
v-if不同分数显示不同的词
<div id="app">
  <div>
    <h2 v-if="score>=90">优秀</h2>
    <h2 v-else-if="score>=80">良好</h2>
    <h2 v-else-if="score>=60">及格</h2>
    <h2 v-else>不及格</h2>
  </div>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
    el:'#app',
    data:{
        score:99
    }
  });
当出现复杂的逻辑的时候不建议在标签中使用多个v-if标签。
<div id="app">
  <div>
    <h2>{{result}}</h2>
  </div>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            score: 99
        },
        computed: {
            result() {
                let showMessage = '';
                if (this.score >= 90) {
                    showMessage = '优秀';
                } else if (this.score >= 80) {
                    showMessage = '良好';
                } else if (this.score >= 60) {
                    showMessage = '及格';
                } else {
                    showMessage = '不及格';
                }
                return showMessage;
            }
        }
    });
v-if 的原理:v-if 后面的条件为false时,对应的元素以及其子元素不会渲染。也就是根本没有不会有对应的标签出现在DOM中。

4.1 条件渲染案例

  1. 点击切换登录方式按钮,切换不同的登录方式。
<div id="app">
  <span v-if="isUser">
    <label for="username">用户名:</label>
    <input type="text" name="username" id="username" placeholder="输入用户名">
  </span>
  <span v-else="isUser">
    <label for="email">邮箱名:</label>
    <input type="text" name="email" id="email" placeholder="输入邮箱地址" >
  </span>
  <button @click="isUser = !isUser">切换类型</button>
</div>

<script src="../../js/vue.js"></script>
    const app = new Vue({
        el: '#app',
        data: {
            isUser: true
        }
    });
  1. 问题:如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入内容。

  2. 问题解答:这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。

  3. 解决办法:如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key,并且我们需要保证key的不同。

5. v-for

  1. 当我们有一组数据需要进行渲染时,我们就可以使用 v-for 来完成。v-for 的语法类似于JavaScript中的for循环。格式如下:item in items 的形式。

5.1 v-for 遍历数组

<div id="app">
  <ul>
    <!-- 1. 获取数组的元素 -->
    <li v-for="item in arr">{{item}}</li>
  </ul>

  <ul>
    <!-- 2. 获取数组元素和 index -->
    <li v-for="(item,index) in arr">{{index + 1}} . {{item}}</li>
  </ul>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            arr: ['盲僧', '雷霆咆哮', '腕豪', '石头人', '瑟提', '寡妇', '男枪']
        }
  });

5.2 v-for 遍历对象

<div id="app">
  <!-- 1. 获取对象的值 -->
  <ul>
    <li v-for="value in obj">{{value}}</li>
  </ul>

  <!-- 2. 获取对象的 键 和 值 -->
  <ul>
    <li v-for="(value , key) in obj">{{ key }} => {{value}}</li>
  </ul>

  <!-- 3. 获取对象的 value 和 key 和 index -->
  <ul>
    <li v-for="(value,key ,index) in obj">{{index + 1}} . {{key}} => {{value}}</li>
  </ul>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            obj: {
                name: '张三',
                age: 23,
                height: 188
            }
        }
    });

5.3 组件的 key 属性

  1. 官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key 属性。这个key的值需要和遍历的值是一一对应的才能提高渲染效率。
为什么需要这个key属性呢(了解)?
  1. 这个其实和Vue的虚拟DOMDiff算法有关系。

  2. 这里我们借用React’s diff algorithm 中的一张图来简单说明一下:

diff
diff
节点移动流程
有key和没有key
说明: 当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点
  1. 我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的。

  2. 即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?

所以我们需要使用key 来给每个节点做一个唯一标识
  1. Diff 算法就可以正确的识别此节点;

  2. 找到正确的位置区插入新的节点。

所以一句话,key 的作用主要是为了高效的更新虚拟DOM
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容