01-学习vue笔记分享-mustache语法

mustache语法与部分指令

 

语法

  • mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式 ;

  • mustache用{{}}表示;

<div id="app">
        <h2>{{message}}</h2>
        <h2>{{message}},kjk</h2>
        <!-- mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式 -->
        <h2>{{firstName + lastName}}</h2>
        <h2>{{firstName + " " + lastName}}</h2>
        <h2>{{firstName}} {{lastName}}</h2>
        <h2>{{counter * 2}}</h2>

    </div>
    <script src="../vue.js"></script>
    <script>
        const app = new Vue({
            el: "#app",
            data: {
                message: "hello",
                firstName: "kobe",
                lastName: "bryant",
                counter: 1000
            },
        })
    </script>

 

 

指令

  • v-once

     1、该指令表示元素和组件只渲染一次,不会跟随数据的改变而改变。

     2、该指令后面不需要跟任何表达式(v-for则需要)。

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <h2 v-once>{{message}}</h2>
        <!-- v-once不随数据改变而改变 -->
        <h2>{{message}}</h2>
    </div>
    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                message: "hello vscode"
            },
            methods: {}
        });
    </script>
</body>

 在控制台输入以下代码改变message的数据,可发现v-once所在行数据不会重新渲染。

 

 

 

  • v-text  v-html

v-text与v-html指令都可以更新页面元素的内容,不同的是,v-text会将数据以字符串文本的形式更新,而v-html则是将数据以HTML标签的形式更新。需要注意的是,v-text、v-html指令会替换掉页面整个的内容。

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <h2>************{{message}}************</h2>
        <h2>************{{html}}************</h2>
        <h2 v-text="message">************</h2>
        <h2 v-text="html">************</h2>
        <h2 v-html="html">************</h2>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                message: "hello",
                html: "<h2 style='color:blue'>Vue是现在最流行的框架之一</h2>"
            },
            methods: {}
        });
    </script>
</body>

分析:

1、页面内容******在有v-text  v-html俩个指令的时候都会被替换成相应内容;

2、v-text会将数据以字符串文本的形式更新,而v-html则是将数据以HTML标签的形式更新。

 

  • v-pre

类似于HTML的一个标签pre,指定显示的内容的格式;

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <h2>{{message}}</h2>
        <h2 v-pre>{{message}}</h2>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                message: "hello",
            },
            methods: {}
        });
    </script>
</body>

分析:即使data里面定义了message这里仍然是显示的{{message}}

 

  • v-cloak( 需要配合css使用(解决:插值表达式闪烁的问题)

在某些情况下,对于vue.js的引用因为某些原因没有加载完成,或者是加载有延迟时,未编译的Mustache标签就无法正常显示。我们可以用v-cloak,这个指令保持在元素上直到关联实例结束编译。和 CSS : [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。

当我正常代码运行时,在页面加载时会闪烁,然后才会加载完成,如下:

 

为了更明显,我在下面的例子中,模拟将网页加载速度变慢,此时就可以看见,页面最先开始会显示出插值表达式,只有vue.js加载完成后,才会渲染成正确的数据。

    setTimeout(() => {
            new Vue({
                el: '#app',
                data: {
                    msg: 'Vue'
                }
            })
        }, 2000)

 

 

 这个问题加入CSS可以被很好地解决: [v-cloak] { display: none } ,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。

同样的,在 html 中的加载点加上 v-cloak,也可以解决这一问题

<body>
<div id="app" v-cloak>
    <p>{{message}}</p>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            message: 'hello world!'
        }
    });
</script>

但是,也会发生失效的案例:那就只好在样式CSS写上[v-cloak] { display: none };

 

  • v-bind

1、前面几个指令主要作用都是插入模板内容中,而v-bind可以用来动态绑定属性(比如 img 的  src、title 属性和 a 元素的 href 属性等等)和样式(可以用style的形式进行内联样式的绑定,也可以通过指定 class 的形式指定样式);

2、缩写是  :

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <input type="button" value="按钮" v-bind:title="Title" v-bind:style="{color:Color,width:Width+'px'}">
    </div>
    <!--v-bind:可以用来在标签上绑定标签的属性和样式,对于绑定的内容,可以对该内容进行编写合法的 JavaScript表达式-->
    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                Title: "这是我自定义的title属性",
                Color: "red",
                Width: "120"
            },
            methods: {}
        });
    </script>
</body>

效果如下:

分析:

1、值得关注的是,v-bind绑定的属性值中,可以对该内容进行编写合法的 JavaScript表达式;如:{color:Color,width:Width+'px'}

2、注意此处px要加引号

3、上面代码可以简写成<input type="button" value="按钮" :title="Title" :style="{color:Color,width:Width+'px'}">

 

  • v-bind动态绑定class(对象语法)也就是class后面跟着是一个对象
<style>
        .active {
            color: red;
        }

        .line {
            background-color: royalblue;
            width: 100px;
        }
    </style>

设置激活类的样式如上

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <!-- v-bind:class="{类名1:属性值1,类名2:属性值2}" -->
        <h2 v-bind:class="{active:isActive,line:isLine}">{{msg}}</h2>
        <button v-on:click="btnClick">按钮</button>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                msg: "kk",
                isActive: true,
                isLine: true
            },
            methods: {
                btnClick: function () {
                    this.isActive = !this.isActive
                    this.isLine = !this.isLine
                }
            }
        });
    </script>
</body>

代码结果如下:

分析:

1、先给不同类名赋予样式,用v-bind动态绑定类名时,样式也会随之切换;

2、对象语法:v-bind:class="{类名1:属性值1,类名2:属性值2}"

3、active,line俩个类先在实例中的data默认布尔值为true,确保能够被应用。再给按钮一个点击事件(v-on:click),实现点击的时候,类的布尔值取反也就是实现了点击实现再点击取消的效果,原则上就是类的显示和隐藏

 

总结:

还有以下用法:

1、直接通过{}绑定一个类 : <h2 :class = "{'active : isActive'}">hello world</h2>,此时isActive是布尔值

2、也可以通过判断,传入多个值 : <h2 :class = "{'active : isActive', 'line' : isLine}">hello world</h2>此时isActive、 isLine是布尔值

3、和普通的类同时存在,并不冲突,注:如果俩者都为真,则会同时存在三个类

<h2 class="title" ; :class = "{'active : isActive', 'line' : isLine}">hello world</h2>

4、如果过于复杂,可以放在一个methods或者computed中,注:下面的classes是一个计算属性

<h2 class="title" ; :class = "classes">hello world</h2>

{'active : isActive', 'line' : isLine}则是放在了计算属性中,以下是放在方法中的案例

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <!-- v-bind:class="{类名1:属性值1,类名2:属性值2}" -->
        <h2 v-bind:class="{active:isActive,line:isLine}">{{msg}}</h2>
        <h2 v-bind:class="classes()">{{msg}}</h2>
        <button v-on:click="btnClick">按钮</button>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                msg: "kk",
                isActive: true,
                isLine: true
            },
            methods: {
                btnClick: function () {
                    this.isActive = !this.isActive
                    this.isLine = !this.isLine
                },
                classes: function () {
                    return {
                        active: this.isActive,
                        line: this.isLine
                    }
                }
            }
        });
    </script>
</body>
  • v-bind动态绑定class(数组语法)class后面跟得是数组
<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <h2 :class="[active,line]">{{msg}}</h2>
        <h2 :class="classes()">{{msg}}</h2>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                msg: "hello",
                active: "a",
                line: "b"
            },
            methods: {
                classes: function () {
                    return [this.active, this.line]
                }
            }
        });
    </script>
</body>

 

作业1:v-for与v-bind的结合--作业需求:点击列表中的哪一项,则该项的文字变红色 

 1、CSS部分

    <style>
        .active {
            color: red;
        }
    </style>

2、vue部分

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <ul>
            <li v-for="(item,index) in movies" :class="{active: currentIndex === index}" @click="liClick(index)">
                {{item}}</li>
        </ul>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                movies: ["海王", "海贼王", "火影忍者", "钢琴师"],
                currentIndex: 0
            },
            methods: {
                // 跟下标有关,参数必须写index,不然运行不了
                liClick(index) {
                    this.currentIndex = index
                }
            }
        });
    </script>
</body>

 

分析:

1、先把基础的列表显示完成

2、再把第一行默认为红色(赋予CSS激活类,先写死)

3、第二部,通过遍历的下标,与自己给定的data中变量currentIndex(默认为0)比较,若相等,则为true,也就是激活CSS类

4、绑定点击事件,并给定方法liClick(index),把点击的事件的下标赋予currentIndex,因为与index有关,所以方法的参数需要写上index,调用的时候也需要。

5、一开始写也不太熟悉,多看几次,理解原理就好了。

 

  • v-bind动态绑定style(对象语法)style后面跟得是对象
<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <!-- 语法格式: :style="{key:value}" -->
        <h2 :style="{fontSize : size + 'px',backgroundColor : color}">{{msg}}</h2>
        <h2 :style="getStyle()">{{msg}}</h2>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                msg: "hello",
                size: "100",
                color: "red"
            },
            methods: {
                getStyle: function () {
                    return {
                        fontSize: this.size + 'px',
                        backgroundColor: this.color
                    }
                }
            }
        });
    </script>
</body>

分析:

1、<h2 :style="{fontSize : size + 'px',backgroundColor : color}">{{msg}}</h2>

2、style后面跟得是一个对象类型,对象的value是具体赋的值,可以来自data中的属性;

 

  • v-bind动态绑定style(数组语法)style后面跟得是数组
<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <h2 :style="[style1,style2]">{{msg}}</h2>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                msg: "hello",
                style1: {
                    backgroundColor: "red"
                },
                style2: {
                    fontSize: "100px"
                }
            },
            methods: {}
        });
    </script>
</body>

分析:

1、<h2 :style="[style1,style2]">{{msg}}</h2>

2、style后面跟得是一个数组,多个值以逗号隔开

  • v-on

1、缩写是:@,v-on:click = @click

2、在使用v-on指令对事件进行绑定时,需要在标签上指明v-on:event(click、mousedown、mouseup等)绑定的事件。

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <button v-on:click="Alert()">按钮</button>
        <button @click="Alert()">按钮</button>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {},
            methods: {
                Alert: function () {
                    alert("vue中的事件绑定")
                }
            }
        });
    </script>
</body>
  •  v-on的参数问题

1、在methods定义方法供@click使用时,如果方法不需要额外参数,那么方法后的()可以不添加。

注:但是,如果该方法本身有一个参数,那么会默认将原生事件event参数传递进去

2、如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件

 

例如:1、事件调用的方法没有参数 

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <!-- 1.事件调用的方法没有参数 -->
        <button @click="btnClick()">按钮1</button>
        <button @click="btnClick">按钮2</button>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {},
            methods: {
                //不用function是因为前面说的字面量增强
                btnClick() {
                    console.log("btnClick");
                }
            }
        });
    </script>
</body>

可以发现,加不加()都可以调对象中的方法!

 

例如:2、事件调用的方法有参数 ,分为三种情况:1、正常使用;2、不加参数;3、不加括号

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <!-- 2.在事件定义的时候,写法省略了小括号,
            但方法本身是需要一个参数的,这时候vue会默认
            将浏览器生产的event事件作为对象传入到方法 -->
        <button @click="btnClick2(123)">按钮2</button>
        <!-- 如果函数需要参数却没有参数传入,则函数的形参为undefined -->
        <button @click="btnClick2()">按钮2</button>
        <!-- 如果直接省略小括号,则vue会默认
            将浏览器生产的event事件作为对象传入到方法 -->
        <button @click="btnClick2">按钮2</button>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                abc: 123
            },
            methods: {
                btnClick2(abc) {
                    console.log("btnClick2", abc);
                }
            }
        });
    </script>
</body>

分别点击三个按钮得到上图:1、正常;2、undefined;3、event事件

所以,当你需要获得even对象时,可以将对象中的方法参数改成event,并且调用方法时不加小括号即可

<button @click="btnClick2">按钮2</button>

methods: {
                btnClick2(event) {
                    console.log("btnClick2", event);
                }

            }

 

例如:3、如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <!-- 3、如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件 -->
        <!-- 不加括号的时候会把event对象传给第一个参数abc,而第二个参数event为undefined -->
        <button @click="btnClick3">按钮3</button>
        <!-- 当括号内参数都写了以下格式,会报错,----- 123 undefined,
            显示找不到event方法,因为在调用方法时,手动获取
            浏览器参数的event对象:$event-->
        <!-- <button @click="btnClick3(123,event)">按钮3</button> -->
        <button @click="btnClick3(123,$event)">按钮3</button>
        <!-- 当第一个 参数为abc的时候,加单引号不报错,不加就报错-->
        <button @click="btnClick3(abc,$event)">按钮3</button>
        <!-- 因为第一个参数不加引号会被认为 是一个变量,会去data里面查找,没有则报错-->
        <button @click="btnClick3('abc',$event)">按钮3</button>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {},
            methods: {
                btnClick3(abc, event) {
                    console.log("-----", abc, event);

                }
            }
        });
    </script>
</body>

 注:分析在代码中注释好了!

 

  •   v-on的修饰符(后面v-for也会涉及,也就是冒泡事件)

  • .stop阻止事件冒泡( 调用的是event.stopPropagation() )
<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <div @click="divClick">
            <p>这是一段div文字!</p>
            <button @click="btnClick">按钮</button>
        </div>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {},
            methods: {
                divClick() {
                    console.log("divClick");
                },
                btnClick() {
                    console.log("btnClick");
                }
            }
        });
    </script>
</body>

分析:

可看出,当点击文字时会触发divClick方法,而当点击按钮时,俩个方法都同时被触发!这时我们可以用.stop来阻止事件冒泡,也就是点击按钮时不触发div的方法。

        <div @click="divClick">
            <p>这是一段div文字!</p>
            <button @click="btnClick">按钮</button>
            <button @click.stop="btnClick">按钮</button>
        </div>

 

 

  • .prevent阻止默认行为-调用event.preventDefault()

例如:当我们想要手动提交而不是通过提交按钮自动提交时,就可以用.prevent

    <div id="app">
        <form action="baidu">
            <input type="submit" value="提交" @click="submitClick">
            <input type="submit" value="提交" @click.prevent="submitClick">
        </form>
    </div>

 

 分析:

可看出加了修饰符后,页面不会自动提交,而没有修饰符则根据表单的action给定的“baidu”自动跳转。

 

  •  .{keyCode | keyAlias}--只当事件是从指定键触发时才触发回调(@keyup=“”监听键盘按键按下的时候,@click监听鼠标点击),当只要监听enter键的时候可以在@keyup后面加.enter
    <div id="app">
        <!-- 监听按键点击事件 -->
        <input type="text" @keyup="keyUp">
        <input type="text" @keyup.enter="keyUp">
        <input type="text" @keyup.13="keyUp">
    </div>
  • .native-监听组件根元素的原生事件(后面会细讲)
<cpn @click.native="cpnClick"></cpn>
  •  .once-只能触发一次回调(相关应用比如点击发送验证码60s一次)
<button @click.once="btnClick">按钮</button>

 

  • v-if、v-else、v-else-if

1、v-if指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回truthy值的时候被渲染。

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        我正在学习
        <h3 v-if="value">vue.js</h3>
        <h3 v-if="!value">javascript</h3>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                value: true,
            },
            methods: {}
        });
    </script>
</body>

 

2、可以使用v-else指令来表示v-if的“else块”,类似于JavaScript中的if…else逻辑语句

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        我正在学习
        <h3 v-if="!value">Vue.js</h3>
        <h3 v-else>Angular.js</h3>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                value: true,
            },
            methods: {}
        });
    </script>
</body>

 

3、v-else-if指令类似于条件语句中的“else-if块”,可以与v-if连续使用。

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <div v-if="type === 'A'">
            A
        </div>
        <div v-else-if="type === 'B'">
            B
        </div>
        <div v-else-if="type === 'C'">
            C
        </div>
        <div v-else>
            Not A/B/C
        </div>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                type: 'C',
            },
            methods: {}
        });
    </script>
</body>

4、如果想切换多个元素,此时可以把一个<template>元素当做不可见的包裹元素,并在上面使用v-if,最终的渲染结果将不包含<template>元素。

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <template v-if="value">
            <h3>马云想要学习</h3>
            <p>Vue.js</p>
            <p>Angular.js</p>
        </template>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                value: true,
            },
            methods: {}
        });
    </script>
</body>

 5、用key管理可复用的元素

Vue会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使Vue变得非常快之外,还有其它一些好处。例如,允许用户在不同的登录方式之间切换。

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <template v-if="loginType === 'userName'">
            <label for="">姓名</label>
            <input type="text" placeholder="输入用户名" >
        </template>
        <template v-else>
            <label for="">邮箱</label>
            <input type="text" placeholder="输入邮箱" >
        </template>
        <button @click="toggleLoginType">切换</button>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                loginType: "userName",
            },
            methods: {
                toggleLoginType: function () {
                    return this.loginType = this.loginType === "userName" ? "email" : "userName"
                }
            }
        });
    </script>
</body>

 可以优化如下:

    <div id="app">
        <template v-if="isUser">
            <label for="username">姓名</label>
            <input type="text" id="username" placeholder="输入用户名" >
        </template>
        <template v-else>
            <label for="email">邮箱</label>
            <input type="text" id="email" placeholder="输入邮箱" >
        </template>
        <button @click="isUser=!isUser">切换</button>
    </div>
        data: {
                isUser: true,
            },

 分析:

1、其中标签属性的for关联输入框的id以点击标签文字的时候可以实现对应id输入框的聚焦

2、<button @click="isUser=!isUser">切换</button>切换按钮运用取反会更简单

 

 

问题:会出现 输入框输入文字之后切换输入框类型文字不清零怎么解决?

答:用可复用的属性key,并且保证key的不同

    <div id="app">
        <template v-if="isUser">
            <label for="username">姓名</label>
            <input type="text" id="username" placeholder="输入用户名" key="username">
        </template>
        <template v-else>
            <label for="email">邮箱</label>
            <input type="text" id="email" placeholder="输入邮箱" key="email">
        </template>
        <button @click="isUser=!isUser">切换</button>
    </div>

 

  • v-show

1、用法与v-if大致一样,不同的是带有v-show的元素始终会被渲染并保留在DOM中。

2、v-show只是简单地切换元素的CSS属性display,当模板属性为true的时候,控制台显示为display:block;属性值为false的时候,控制台显示display: none。

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <h2 v-show="value">vue</h2>
        <h2 v-show="!value">js</h2>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                value: true,
            },
            methods: {}
        });
    </script>
</body>

得出结果是vue

  • v-show与v-if的区别:

  1. v-if与v-show指令都是根据表达式的真假值判断元素的显示与隐藏。
  2. v-if是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。
  3. v-if也是惰性的:如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。
  4. 相比之下,v-show就简单得多,不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS进行切换。
  5. 一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好。
<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <button @click="btnClick">切换</button>
        <br>
        <!--v-if 指令控制-->
        <img v-if="msg" src="VIP-3.png" alt="" width="200">
        <!-- v-show 指令控制-->
        <img v-show="msg" src="VIP-4.png" alt="" width="200">
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                msg: true,
            },
            methods: {
                btnClick: function () {
                    this.msg = !this.msg;
                }
            }
        });
    </script>
</body>

切换前:

 

 切换后:

分析:

v-if直接不渲染,v-show修改了CSS属性

 

  • 使用v-for指令遍历元素

 其中items是源数据数组,而item则是被迭代的数组元素的别名,data: {items: ['马云', '马化腾', '蔡徐坤', '刘强东']},

        <ul>
            <li v-for="item in items">
                {{ item }}
            </li>
        </ul>

 也可以同时遍历下标

    <ul>
        <li v-for="(item,index) in items">
            {{ index }}-{{ item }}
        </li>
    </ul>

 还可以遍历对象

    <ul>
        <!-- 获得key和value 格式:(value,key) -->
            <li v-for="(value,key,index) in object">
                {{index}}--{{ key }}:{{value}}
            </li>
    </ul>
<script>
    new Vue({
        el: '#app',
        data: {
            //定义对象
            object: {
                姓名: '蔡徐坤',
                性别: '男',
                出生日期: '2019-10-10'
            }
        }
    })
</script>

 

  •  v-for使用过程添加key

1、v-for遍历数组

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <ul>
            <li v-for="item in letters">{{item}}</li>
        </ul>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                letters: ["A", "B", "C", "D", "E"]
            },
            methods: {}
        });
    </script>
</body>

2、此时插入一个元素a [ app.letters.splice(1,0,"a") ] 如下

 3、此时数组是怎么更新的?

分析:这里我们依靠vue的虚拟DOM的diff算法来解释。

(1)当我们希望在某一层相同的节点中(列表结点)插入一个新的节点,比如我们希望把a插入ABCDE的AB之间,diff算法执行过程是把B更新为a、C更新为B、D更新为C、E更新为D,最后再插入E,这样看起来是不是很没效率?

(2)所以我们需要一个唯一的key,并且key跟{{}}中的值一一对应,来给每个节点做一个唯一的标识,因此diff算法就可以正确识别此节点,找到正确位置并插入新节点

            <li v-for="item in letters" :key="item">{{item}}</li>

总结:key的作用就是为了高效地更新虚拟DOM,以后开发中使用v-for要加上key

 

  • 类似于v-if,也可以利用带有v-for的<template>来循环渲染一段包含多个元素的内容。

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <template v-for="n in items">
            <li>{{n.name}}--{{n.age}}--{{n.sex}}</li>
            <hr>
        </template>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                items: [{
                        name: '小明',
                        age: 15,
                        sex: '男'
                    },
                    {
                        name: '小红',
                        age: 14,
                        sex: '女'
                    }
                ]
            },
            methods: {}
        });
    </script>
</body>
  • v-for与v-if一同使用,当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <h3>没有报道的学生名单:</h3>
        <ul>
            <li v-for="n in items" v-if="!n.value">
                {{n.name}}
            </li>
        </ul>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                items: [{
                        name: "小明"
                    },
                    {
                        name: "小红",
                        value: "已报到"
                    },
                    {
                        name: "小洋",
                        value: "已报到"
                    },
                    {
                        name: '小思'
                    }
                ]
            },
            methods: {}
        });
    </script>
</body>

其中,v-if相当于在计算属性中利用替换数组的filter方法找出所需要的值

     computed:{
            student:function(){
                return this.items.filter(function (n) {
                    return !n.value
                })
            }
        }

数组API可分为变异方法和替换数组,变异方法和替换数组有什么区别?

变异方法:顾名思义,会改变原始数组。
替换方法:则不会改变原始数组。

变异方法push、pop、shift、unshift、splice、sort、reverse的作用?改变原始数组

push()方法可向数组的末尾添加一个或多个元素,并返回新的长度。

pop() 方法用于删除并返回数组的最后一个元素

shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值

unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。

splice()方法向/从数组中添加/删除项目,然后返回被删除的项目

splice( index, len, [item]) 用来删除/替换/添加数组内某一个或者几个值(该方法会改变原始数组)。

参数: 

index:数组开始下标 ;len:替换/删除的长度 ;item:替换的值,删除操作的话item为空

删除:

//删除起始下标为1,长度为1的一个值(len设置1,如果为0,则数组不变)

splice()方法始终会返回一个数组,该数组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组)

sort() 方法用于对数组的元素进行排序。

reverse() 方法用于颠倒数组中元素的顺序。

 

替换数组concat、slice、map、filter的作用?不会改变原始数组

  • concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组
  • slice() 方法可从已有的数组中返回选定的元素。该方法并不会修改数组,而是返回一个子数组
  • map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。map() 方法按照原始数组元素顺序依次处理元素。

是否改变原数组:否
是否对空数组进行检测:否

const arr= [4, 9, 16, 25];

const arr1 = arr.map(item => item+2)

console.log(arr)   // [4, 9, 16, 25]

console.log(arr1)  // [6, 11, 18, 27]
  • filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

是否改变原数组:否
是否对空数组进行检测:否
 

const arr= [32, 33, 16, 40];

const arr1 = arr.filter(item => item >= 18)

console.log(arr)   // [32, 33, 16, 40]

console.log(arr1)  // [32, 33, 40]
  • 以上数组的方法哪些是响应式的?

1、响应式:页面会根据代码的更新而自动更新!(vue是响应式的)

2、由上面数组代码可见得,响应式的有:push、pop() 、shift()unshift() 、splice() 、sort()、reverse() 

3、并不是所有改变数组的方法都是响应式,比如:通过索引值改变数组元素

    <div id="app">
        <ul>
            <li v-for="item in letters">{{item}}</li>
        </ul>
        <button @click="btnClick">按钮</button>
    </div>

    const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                letters: ["A", "B", "C", "D", "E"]
            },
            methods: {
                btnClick() {
                    this.letters[0] = "aaaa";
                }
            }
        });

可见无论我怎么点击按钮页面都不会更新,但数组已经发生改变!

 

  • 自定义指令

  •  自定义指令全局注册和局部注册的写法? 

(1)全局注册:通过 Vue.directive( id, [definition] ) 方式注册全局指令,第一个参数为自定义指令名称(指令名称不需要加 v- 前缀,默认是自动加上前缀的,使用指令的时候一定要加上前缀),第二个参数可以是对象数据,也可以是一个指令函数。

Vue.directive('focus',{
		bind:function(){//每当指令绑定到元素上的时候,会立即执行这个 bind 函数,只执行一次},
		inserted:function(el){// inserted 表示元素 插入到DOM中的时候,会执行此函数,触发一次
			el.focus(); //注意:在每一个函数中,第一个参数,永远是 el ,表示被绑定了指令的 那个元素 ,这个el参数,是一个原生JS的DOM对象
		},
		updated:function(){}
	})

     (2)局部注册:

    directives:{
			'color':{//给字体设置颜色
				bind:function(el){ //这个function() 中还有第二个参数 binding ,这里不做介绍,在下边钩子函数参数中介绍
					el.style.color="red";
				}
			}
		}

 

  • 自定义指令的各个选项(钩子函数)的执行时机?

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

update:所在组件的 VNode(一个DOM上的所有属性) 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

unbind:只调用一次,指令与元素解绑时调用。

 

  • 自定义指令中钩子函数的第二个参数binding是一个对象,这个对象包含哪些属性?

每个钩子函数都可以接收两个参数:

El:作用该指令的 DOM 对象
binding一个对象,包含以下属性:name:指令名,不包括 v- 前缀。value:指令的绑定值。

<div v-demo="{ color: 'white', text: 'hello!' }">123</div>
Vue.directive('demo', function (el, binding) {
    console.log(binding.value.color); // => "white"
    console.log(binding.value.text);  // => "hello!"
})

 

综合案例:完成购物车的数量加减操作以及删除操作还有过滤器格式化操作

 

步骤1:先完成基础表格的v-for遍历,如下:

CSS部分:

<style>
        table {
            border: 1px solid #e9e9e9;
            border-collapse: collapse;
            border-spacing: 0;
        }

        th,
        td {
            padding: 8px 16px;
            border: 1px solid #e9e9e9;
            text-align: left;
        }

        th {
            background-color: #f7f7f7;
            color: #5c6b77;
            font-weight: 600;
        }
    </style>

body部分:

<body>
    <!-- 被app实例所控制的区域 -->
    <div id="app">
        <table>
            <thead>
                <th></th>
                <th>书籍名称</th>
                <th>出版日期</th>
                <th>价格</th>
                <th>购买数量</th>
                <th>操作</th>
            </thead>
            <tbody>
                <tr v-for="item in books">
                    <td v-for="value in item">{{value}}</td>
                </tr>
            </tbody>
        </table>
    </div>

    <script src="../vue.js"></script>
    <script>
        // 创建实例对象
        const app = new Vue({
            // 指定控制的区域
            el: '#app',
            data: {
                books: [{
                    id: 1,
                    name: "《算法导论》",
                    date: "2006-9",
                    price: 85,
                    num: 1
                }, {
                    id: 2,
                    name: "《UNIX编程艺术》",
                    date: "2006-2",
                    price: 59,
                    num: 1
                }, {
                    id: 3,
                    name: "《编程珠玑》",
                    date: "2008-10",
                    price: 39,
                    num: 1
                }, {
                    id: 4,
                    name: "《代码大全》",
                    date: "2006-3",
                    price: 128,
                    num: 1
                }, ]
            },
        });
    </script>
</body>

效果如图:

分析:

1、分成俩部分,表头和表体,用<thead>和 <tbody>表示,表头第一行用<th>表示,注意第一个为空!

2、表体分四行,每一行的数据都是从数据中用v-for遍历出来的,因此这里<tr>需要v-for,把数据中每一个对象都遍历出来;每一列也是每个对象中的数据,因此也需要在每个对象中遍历出来每一个value值,这就是双重遍历数组填充表格。

    如右:<tr v-for="item in books">
                    <td v-for="value in item">{{value}}</td>
                </tr>

3、数据CSS已经给出如上,即可得到以上效果图

4、发现:与原图还差加减号的实现、移除的功能、价格的¥符号以及俩位小数

 步骤2:加减号的实现、移除的功能、价格的¥符号以及俩位小数

此时需要换种写法去填充列,不用数组中遍历对象的方法,因为加减号实现不了

            <tbody>
                <tr v-for="item in books">
                    <td>{{item.id}}</td>
                    <td>{{item.name}}</td>
                    <td>{{item.date}}</td>
                    <td>
                        ¥{{item.price.toFixed(2)}}
                        <!-- {{'¥' + item.price.toFixed(2)}}
                    </td>
                    <td>
                        <button>-</button>
                        {{item.num}}
                        <button>+</button>
                    </td>
                    <td>
                        <button>移除</button>
                    </td>
                </tr>
            </tbody>

 此时我们会发现价格的插值表达式写得太长,可以用到过滤器(filters)格式:{{item.price | 过滤器}}

                    <td>
                        <!-- ¥{{item.price.toFixed(2)}} -->
                        <!-- {{'¥' + item.price.toFixed(2)}} -->
                        <!-- {{item.price | 过滤器}} -->
                        {{item.price | showPrice}}
                    </td>
            // 过滤器一般是一个函数,与methods同级
            filters: {
                showPrice(price) {
                    // price要写,不然会错,参数要写上要过滤的变量
                    return "¥" + price.toFixed(2)
                }
            }

分析:

1、同样可以得到需要的价格格式

2、要注意,filters不是写在methods中的;

3、格式:{{item.price | 过滤器}}

4、过滤器函数参数要写,写的是要过滤的变量

此时还差购物车的数量加减操作、移除操作和总价格:

1、数量加减操作

加减按钮:

                        <td>
                            <button @click="decrement(index)" :disabled="item.num <=1">-</button>
                            {{item.num}}
                            <button @click="increment(index)">+</button>
                        </td>

加减方法:

methods: {
                decrement(index) {
                    this.books[index].num--;
                },
                increment(index) {
                    this.books[index].num++;
                },
        }

分析:

1、绑定点击事件,并写加减方法,根据便利的数组下标,来确定点击的数组中第几个对象中的数量;this.books[index].num--/++;当index=0时,则为数组第一个对象的数量num——/++

2、我们要设定数量小于1之后不可以使用减少的按钮不然商品会出现负数!:disabled="item.num <=1"

动态绑定v-bind:disabled

3、点击事件的括号必须有index作为参数

2、移除操作

移除按钮:

                        <td>
                            <button @click="removeClick(index)">移除</button>
                        </td>

移除方法:

removeClick(index) {
                    // 删除数组的某一个对象
                    this.books.splice(index, 1)
                }

分析:

1、绑定点击移除事件,并写移除方法

2、方法根据遍历的数组下标得来,splice()方法去除,该数组下标为起始点,删除一个元素,即上面数组的一个item对象

3、点击事件括号内必须有唯一的参数index

3、总价格计算(computed)

 价格页面:

<h2>总价格{{totalPrice | showPrice}}</h2>

价格方法:(建议多看)

computed: {
                totalPrice() {
                    let totalPrice = 0
                    for (let i = 0; i < this.books.length; i++) {
                        // 价格*数量=总价
                        totalPrice += this.books[i].price * this.books[i].num
                    }
                    // 别忘了返回总值
                    return totalPrice
                }
            },

分析:

1、总价格也需要有对应的格式,因此要加管道符过滤器

2、先把总价默认为0,然后用for循环遍历每一个数组对象,使得每一个对象中的价格*数量=总价,最后返回总价格,若不返回则为无效方法,要中断该方法的执行。

 最后附上:购物车案例最终效果.

 

案例优化-JavaScript的高阶函数:

上文我们写到的价格方法如下:

computed: {
                totalPrice() {
                    let totalPrice = 0
                    for (let i = 0; i < this.books.length; i++) {
                        // 价格*数量=总价
                        totalPrice += this.books[i].price * this.books[i].num
                    }
                    // 别忘了返回总值
                    return totalPrice
                }
            },

由于for循环的可读性有点差,于是考虑对它进行优化!这就涉及到了JavaScript的高阶函数;

首先我们先看2种for方法的优化:(this.books是数组)

1、for(let i in this.books)

computed: {
                totalPrice() {
                    // 2.for(let i in 数组(this.books))
                    let totalPrice = 0
                    for (let i in this.books) {
                        // i是个索引值
                        // console.log(i);
                        totalPrice += this.books[i].price * this.books[i].num
                    }
                    return totalPrice
                }
            },

分析:

1、for循环得到的i是下标:0,1,2,3;

2、this.books是数组,用的是in

3、每次for循环,this.book[i]得到的是数组的每一个对象,也就是i是为了拿到this.book[i]

思考:上面的写法太复杂,有没有一步到位取到this.book[i]的方法?

 2、for(let i of this.books)

                    // 3.for(let i of 数组(this.books))
                    let totalPrice = 0
                    for (let item of this.books) {
                        // item是个对象
                        //console.log(item);
                        totalPrice += item.price * item.num
                    }
                    return totalPrice

 分析:

1、item是个对象,得到的是数组的每一个对象

2、this.books是数组,用的是of

思考:更简便的方法?

 可以用高阶函数来简便代码,函数式编程!

 

需要先介绍几个例子,然后在正式认识高阶函数!

 例子1:需要取出数组中小于100的数字

const nums = [10, 20, 111, 222, 444, 40, 50]
        // Array(4) [ 10, 20, 40, 50 ]
        let newNums = []
        for (let n of nums) {
            if (n < 100) {
                newNums.push(n)
            }
        }

例子2:需要将小于100的数字转化:全部乘以2

// 例子2:需要将小于100的数字转化:全部乘以2
        // Array(4) [ 20, 40, 80, 100 ]
        let new2Nums = []
        for (let n of newNums) {
            new2Nums.push(n * 2)
        }
        console.log(new2Nums);

例子3:将2中得到的数字相加,得到最终结果

// 3.将2中得到的数字相加,得到最终结果
        //240
        let total = 0
        for (let n of new2Nums) {
            total += n
        }
        console.log(total);

高阶函数:filter、map、reduce

  • 1、filter

// 1、filter
        // filter中的回调函数有一个要求:必须返回一个布尔值
        // true:函数内部会自动将这次回调的n加入新数组中
        // false:函数内部会过滤掉这次的n
        let newNums = nums.filter(function (n) {
            return n < 100
            // n < 100返回的是布尔值
        })
        console.log(newNums);
        // Array(4) [ 10, 20, 40, 50 ]
  • 2、map

// 2、map
        let new2Nums = newNums.map(function (n) {
            return n * 2
        })
        console.log(new2Nums);
        // Array(4) [ 20, 40, 80, 100 ]
  • 3、reduce对数组中所有内容进行汇总

// 3、reduce对数组中所有内容进行汇总
        // 参数1:function(preValue,n){}
        // 参数2:0;初始化为0
        let total = new2Nums.reduce(function (preValue, n) {
            return preValue + n
        }, 0)
        console.log(total);
        // 240
        // preValue        n
        // 1:  0           20
        // 2:  20          40
        // 3:  60          80
        // 4:  140         100
        // 返回240

继续进行优化:

// 不感觉以上高阶函数有多方便,于是再次进行优化,将三个需求并在一起
        const nums = [10, 20, 111, 222, 444, 40, 50]
        let total = nums.filter(function (n) {
            return n < 100
        }).map(function (n) {
            return n * 2
        }).reduce(function (preValue, n) {
            return preValue + n
        }, 0)
        console.log(total);
        // 240

还是太长,继续优化:

        // 感觉代码量并不少,于是看看能不能一行代码解决?
        const nums = [10, 20, 111, 222, 444, 40, 50]
        let total = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n);
        console.log(total);
        // 240

一行代码就搞定,运用的是箭头函数,仔细对比上下文代码的区别

 

 

看回购物车案例,进行优化:

computed: {
                totalPrice() {
                    let totalPrice = 0
                    for (let i = 0; i < this.books.length; i++) {
                        // 价格*数量=总价
                        totalPrice += this.books[i].price * this.books[i].num
                    }
                    // 别忘了返回总值
                    return totalPrice
                }
            },

优化如下:book相当于books中的对象item

computed: {
                totalPrice() {
                    // 方法都是基于数组进行的操作,所以使用reduce得先得到数组
                    // this.books数组、book对象
                    return this.books.reduce(function (preValue, book) {
                        return preValue + book.price * book.num
                    }, 0)
                }
            },

版权声明:本文为weixin_42647901原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。