1-Vue基础

271 阅读9分钟

1.介绍

Vue是响应式的,数据驱动视图是Vue最大的特点,即当数据发生变化的时候,用户界面发生相应的变化,开发者不需要手动去修改DOM。

2.导入

官网下载:https://cn.vuejs.org
在线CDN:https://cdn.jsdelivr.net/npm/vue/dist/vue.js

3.Vue快速入门

Vue实例通过new关键字创建,new Vue({...})括号里面传一个对象, el属性后面写绑定的元素,data属性后面是一个对象,这个对象内保存数据,methods属性后面也是一个对象,这个对象内保存方法。在绑定元素中使用Vue实例中属性的数据、方法等时需要用到插值表达式{{ }}

<body>
    <div id="app">
        <div>{{ msg }}</div>
        <div>{{ getMsg() }}</div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                msg: 'hello Vue'
            },
            methods: {
                getMsg() {
                    console.log(this.msg);
                    return this.msg;
                }
            }
        })
    </script>
</body>

{{ }}插值表达式中可以填写各种数据类型,包括三元运算符、函数等

需要注意的是:插值表达式中填写对象时,包裹对象的大括号需要用空格与插值表达式的大括号分开,否则会导致不能识别,会报错。这边建议在使用插值表达式时最好在表达式的前后都加一个空格与插值表达式的大括号分隔开。

<div id="app">
    <div>{{ msg }}</div> <!-- data中的属性 -->
    <div>{{ getMsg() }}</div> <!-- 函数 -->
    <div>{{ {'name':'tom'} }}</div> <!-- 对象 -->
    <div>{{ 1 }}</div> <!-- 数字 -->
    <div>{{ 'A' }}</div> <!-- 字符串 -->
    <div>{{ 2>3?'正确':'错误' }}</div> <!-- 三元运算符 -->
    <div>{{ msg.split('').reverse().join('') }}</div> <!-- 字符串反转 -->
</div>

4.Vue指令

v-text和v-html

v-text:和插值表达式{{ }}作用一样,只能插入值,相当于js中的innerText。
v-html:既能插入值,又能插入标签,相当于js中的innerHTML。

<body>
    <div id="app">
        <div>{{ msg }}</div>
        <div v-text="msg"></div>
        <div v-html="htmlMsg"></div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                msg: 'hello Vue',
                htmlMsg: '<p>tom</p>'
            }
        })
    </script>
</body>

v-if、v-show条件渲染

v-ifv-else-ifv-else就相当于if、else if、else之间的关系,当判断的条件为true时显示渲染的内容,否则不显示。
v-show也是通过条件来判断渲染的内容是否显示。但是二者之间还是有区别的:

v-if是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

总结:一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好。

<body>
    <div id="app">
        <div v-if="getNumber()===1">1</div>
        <div v-else-if="getNumber()===2">2</div>
        <div v-else="getNumber()===3">3</div>
        <div v-show="flag">show</div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                flag: true
            },
            methods: {
                getNumber() {
                    return parseInt(Math.random() * 3 + 1);
                }
            }
        })
    </script>
</body>

v-bind

v-bind绑定标签属性、Class、Style等。v-bind可以简写为:。使用v-bind可以对属性值、类名等进行动态切换。

<style>
    .active {
        color: #ff6700
    }

    .fontClass {
        font-size: 24px;
    }
</style>

<body>
    <div id="app">
        <a v-bind:href="msg.url" v-bind:title="msg.title">{{ msg.name }}</a>
        <!-- 简写 -->
        <img :src="imgSrc" alt="">
        <p :class='{active:isActive}'>v-bind</p>
        <p :style="{color:color,fontSize:size+'px'}">hello Vue</p>
        <!-- 使用'[]'一次绑定多个类 -->
        <p :class="[activeClass,fontClass]">hello Vue</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                msg: {
                    name: '百度',
                    url: 'https://www.baidu.com',
                    title: '百度一下'
                },
                imgSrc: 'http://img2.imgtn.bdimg.com/it/u=1429901244,1138924742&fm=26&gp=0.jpg',
                isActive: true,
                color: 'blue',
                size: 24,
                activeClass: 'active',
                fontClass: 'fontClass'
            }
        })
    </script>
</body>

v-on事件绑定

v-on指令监听DOM事件,并在触发时运行一些JavaScript代码。v-on可以简写为@。此外Vue还提供一些非常实用的事件修饰符按键修饰符等修饰符,比如以往的阻止事件冒泡行为等,需要到js代码中去编写event.stopPropagation(),而在Vue中只需要在绑定事件的时候加.stop即可,即@click.stop。具体有哪些修饰符可以到官网去查看。

    <style>
        .active {
            color: #ff6700
        }
    </style>

<body>
    <div id="app">
        <p :class='{active:isActive}'>v-bind</p>
        <button v-on:click="classChange">切换</button>
        <p>{{ num }}</p>
        <!-- 简写,添加修饰符 -->
        <button @click.once="add">+1</button><!-- 添加once事件修饰符,点击事件只会触发一次 -->
        <input type="text" @keyup.enter="sysout"><!-- 添加enter按键修饰符,按下enter键是触发事件 -->
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                isActive: true,
                num: 0
            },
            methods: {
                classChange() {
                    this.isActive = !this.isActive;
                },
                add() {
                    this.num++;
                },
                sysout() {
                    alert('回车');
                }
            }
        })
    </script>
</body>

使用v-on的好处

  1. 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
  2. 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
  3. 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。

v-for列表渲染

v-for指令基于一个数组来渲染一个列表。v-for指令需要使用item in items形式的特殊语法,其中items是源数据数组,而item则是被迭代的数组元素的别名

<body>
    <div id="app">
        <div>
            <!-- 遍历数组 -->
            <ul>
                <!-- 得到item是数组中的每一个对象,index是遍历时的索引 -->
                <li v-for="(item, index) in hobbies" :key="item.id">
                    <!-- 分别取到对象的id和name -->
                    index:{{ index }}--id:{{ item.id }}--name:{{ item.name }}
                </li>
            </ul>
            <!-- 遍历对象 -->
            <ol>
                <!-- 得到的val是对象中的值,key是对象中的键 -->
                <li v-for="(val, key) in book" :key="key">
                    {{ key }}--{{ val }}
                </li>
            </ol>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                hobbies: [{ id: 1, name: '篮球' }, { id: 2, name: '排球' }, { id: 3, name: '羽毛球' }],
                book: {
                    title: 'Vue教程',
                    author: 'tom'
                }
            }
        })
    </script>
</body>

可以发现每一个v-for渲染的列表后面都绑定了一个key属性,这个key属性到底用来干嘛。

key

当Vue正在更新使用v-for渲染的元素列表时,它默认使用“就地更新”的策略。这个模式是高效的。如果给列表中的每一个项绑定一个唯一的key,Vue就能根据这个key去跟踪、识别每一个节点的身份。当列表中的项发生改变时,如果绑定有key,那么Vue就不会对整个DOM进行更新,而是只对改变的项进行就地更新。否则会对整个DOM进行更新,增大开销。因此,使用v-for的时候一定要在后面绑定一个key。当然key的作用也不仅如此,具体有哪些可以去官网查看。

注意:不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。

v-model表单输入绑定(双向数据绑定)

输入框

通过v-model绑定input输入框的value值,在对输入框中的值进行修改的同时修改了data中的info,而div中又获取了info的值,从而实现了双向数据绑定。输入框中的值发生改变时,div中的值能够实时的做出相应的改变。

<body>
    <div id="app">
        <div>{{ info }}</div>
        <input type="text" v-model="info">
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                info:'hello Vue'
            }
        })
    </script>
</body>

复选框

单个复选框,绑定checked值

<body>
    <div id="app">
        <label>{{ flag }}</label>
        <input type="checkbox" v-model="flag">
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                flag: false
            }
        })
    </script>
</body>

多个复选框,绑定到同一个数组

<body>
    <div id="app">
        <label for="tom">Tom</label>
        <input type="checkbox" id="tom" value='Tom' v-model="names">
        <label for="jhon">Jhon</label>
        <input type="checkbox" id="jhon" value='Jhon' v-model="names">
        <label for="jerry">Jerry</label>
        <input type="checkbox" id="jerry" value='Jerry' v-model="names">
        <p>{{ names }}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                names: []
            }
        })
    </script>
</body>

单选按钮

<body>
    <div id="app">
        <label for="man"></label>
        <input type="radio" id="man" name='sex' value='man' v-model="gerder">
        <label for="woman"></label>
        <input type="radio" id="woman" name='sex' value='woman' v-model="gerder">
        <p>{{ gerder }}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                gerder: 'man'
            }
        })
    </script>
</body>

选择框

单选

<body>
    <div id="app">
        <select v-model="choice">
            <option disabled>请选择</option>
            <option value="A">A</option>
            <option value="B">B</option>
            <option value="C">C</option>
        </select>
        <p>{{ choice }}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                choice: '请选择'
            }
        })
    </script>
</body>

多选

当可以多选时,v-model需要绑定一个数组,否则会报错。

<body>
    <div id="app">
        <!-- 添加multiple属性 -->
        <select v-model="choice" multiple>
            <option disabled>请选择</option>
            <option value="A">A</option>
            <option value="B">B</option>
            <option value="C">C</option>
        </select>
        <p>{{ choice }}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                choice: []
            }
        })
    </script>
</body>
v-bind、v-for、v-model联合使用
<body>
    <div id="app">
        <select v-model="choice">
            <option v-for="option in options" :value='option.name' :key="option.id">{{option.name}}</option>
        </select>
        <p>{{ choice }}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                choice: '',
                options: [{ id: 1, name: 'tom' }, { id: 2, name: 'jhon' }, { id: 3, name: 'jerry' }]
            }
        })
    </script>
</body>

修饰符.lazy、.number、.trim

.lazy:在默认情况下,v-model在每次input事件触发后将输入框的值与数据进行同步,即实时更新。添加.lazy可以使得当前input输入框失去焦点之后再更新数据。

<body>
    <div id="app">
        <p>{{ msg }}</p>
        <input type="text" v-model.lazy="msg">
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                msg:'.lazy'
            }
        })
    </script>
</body>

.number:如果想自动将用户的输入值转为数值类型,可以给v-model添加.number修饰符。这通常很有用,因为即使在type="number"时HTML输入元素的值也总会返回字符串。如果这个值无法被parseFloat()解析,则会返回原始的值。

<body>
    <div id="app">
        <p>{{ typeof msg }}</p>
        <input type="text" v-model.number="msg">
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                msg: ''
            }
        })
    </script>
</body>

.trim:添加.trim可以自动去除输入字符串首尾的空格。

<body>
    <div id="app">
        <p>{{ msg }}</p>
        <input type="text" v-model.trim="msg">
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                msg:'.trim'
            }
        })
    </script>
</body>

5.侦听器(watch)

Vue 中的watch属性,也就是侦听器,用来侦听数据的变化,并对其做出响应。

watch 侦听基本数据类型

基本数据类型可以直接使用watch进行侦听

<body>
    <div id="app">
        <p>{{ msg }}</p>
        <input type="text" v-model.number="msg">
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                msg: ''
            },
            watch: {
                'msg': function (newV, oldV) {
                    console.log('newV:' + newV + '---' + 'oldV:' + oldV);
                }
            }
        })
    </script>
</body>
watch中是键值对的写法,其中key是data中的属性名,value是侦听数据变化后的行为。且行为函数中第
一个参数接收的是变化后的值,第二个参数接收的是变化前的值。

watch 侦听复杂数据类型(深度侦听)

复杂数据类型(Object、Array等)要使用watch的深度侦听

<body>
    <div id="app">
        <p>{{ obj.name+":"+obj.age }}</p>
        <button @click="obj.name='Alex'">改变</button>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                obj: { name: 'Holo', age: 18 }
            },
            watch: {
                'obj': {
                    //开启深度侦听
                    deep: true,
                    handler: function (newV, oldV) {
                        alert(oldV.name);
                    }
                }
            }
        })
    </script>
</body>
watch深度侦听,key是data中的属性名,value是一个对象,对象中包含deep属性和一个固定的handler
函数,深度侦听需将deep属性设置为true。

6.计算属性(computed)

简单的运算逻辑我们可以直接在插值表达式{{...}}内完成,但是一些复杂的逻辑写在插值表达式中就会显得臃肿且难以维护。此时我们可以使用 Vue 的计算属性computed来处理这些复杂的逻辑。

<body>
    <div id="app">
        <p>{{ reverseMsg }}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                msg: 'hello Vue'
            },
            computed: {
                reverseMsg() {
                    return this.msg.split('').reverse().join('');
                }
            }
        })
    </script>
</body>

诶,这个时候我们可能会问,这不是和methods一样吗?声明方法然后调用,那到底是为什么有了methods还要有computed呢?

为什么有了 methods 还要有 computed?

使用computed能够产生缓存,只有在它的依赖发生改变时才会进行重新求值,这就意味着只要msg不发生改变,多次访问computed计算属性都能立即拿到之前计算的结果,而不必对函数进行多次调用。这对于提升性能来说有很大的意义。如果你不希望产生缓存,可以使用methods 来代替。

计算属性的 setter

计算属性默认只有getter,不过在需要时你也可以提供一个setter

<body>
    <div id="app">
        <p>{{ msg }}</p>
        <input type="text" v-model='content' @input='handlerInput'>
        <button @click='handlerClick'>获取</button>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                msg: ''
            },
            methods: {
                handlerInput(event) {
                    //解构赋值 const value = event.target.value;
                    const { value } = event.target;
                    this.content = value;//注意这句代码,看下面分析
                },
                handlerClick() {
                    if (this.content) {
                        console.log(this.content);
                    }
                }
            },
            computed: {
                content: {
                    set(value) {
                        this.msg = value;
                    },
                    get() {
                        return this.msg;
                    }
                }
            }
        })
    </script>
</body>

下面对上面这段代码进行分析(个人理解):

首先我们看handlerClick函数,当我们在输入框中输入值然后点击按钮获取时能够直接在控制台看到打印结果,说明直接使用this.content会默认调用get()。我们再回过头来看handlerInput函数中的这句代码this.content = value;,我们可以先在set()中加上一句console.log(value);,此时我们可以发现当我们在输入框中输入的时候控制台会有打印结果,说明对this.content进行赋值时会默认调用set()。同时我们发现当我们每输入一个值,控制台会有两遍打印结果,按理来说我们只进行了一次赋值,应该只会调用一次set()啊,为什么会有两遍打印结果呢?我们再将this.content = value;注释掉,重新输入、获取,可以发现注释这句代码并没有造成什么影响,唯一不同的是控制台只有一遍打印结果了。我们并没有对this.content进行赋值,但是set()被调用了一次,那么问题就只能出现在v-model='content'上了,因为进行了双向数据绑定,所以当我们在输入框中输入值的时候都会默认的调用set()this.content进行赋值,相当于默认的执行了this.content = value;这句代码。

7.过滤器(filters)

Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号|指示。

过滤器分为局部过滤器全局过滤器,当二者重名时,优先采用局部过滤器。也可以给一个文本进行多次过滤{{ message | filterA | filterB }},同时需要注意,当定义全局过滤器时,一定要在创建 Vue 实例之前进行定义,否则会报错。

局部过滤器

<body>
    <div id="app">
        <!-- price默认作为第一个参数传入过滤器,'¥'作为第二个参数 -->
        <p>{{ price | myPrice('¥')}}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                price: 12
            },
            filters: {
                myPrice(price, unit) {
                    return unit + price
                }
            }
        })
    </script>
</body>

全局过滤器

<body>
    <div id="app">
        <p>{{ msg | myReverse}}</p>
        <!-- 进行多次过滤 -->
        <p>{{ price | myPrice('¥') | myReverse}}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        //定义全局过滤器
        Vue.filter('myReverse', (msg) => { return msg.split('').reverse().join('') })

        const app = new Vue({
            el: '#app',
            data: {
                price: 12,
                msg: 'hello Vue'
            },
            //局部过滤器
            filters: {
                myPrice(price, unit) {
                    return unit + price
                }
            }
        })
    </script>
</body>