Vue - Vue基础

1. Vue概述

Vue:渐进式JavaScript框架

声明式渲染 -> 组件系统 -> 客户端路由 -> 集中式状态管理 -> 项目构建

官网:https://cn.vuejs.org/v2/guide

  • 易用:熟悉HTML、CSS、JavaScript知识后可以快速上手
  • 灵活:在一个库和一套完整框架之间自如伸缩
  • 高效:20kB运行大小,超快虚拟 DOM

2. Vue基本使用

2.1 hello world基本步骤

    <div id="app">
        <div>{{msg}}</div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        // Vue的基本使用步骤:
        // 1. 需要提供标签用于填充数据
        // 2. 引入vue.js库文件
        // 3. 可以使用vue的语法做功能
        // 4. 把vue提供的数据填充到标签里面
        var vm = new Vue({
            el: '#app',
            data: {
                msg: 'Hello Vue'
            }
        });
    </script>

2.2 hello world 细节解析

2.2.1 实例参数
  • el:元素的挂载位置(值可以是CSS选择器或者DOM元素)
  • data:模型数据(值是一个对象)
2.2.2 插值表达式

双括号 {{}}

  • 将数据填充到HTML标签中
  • 插值表达式支持基本的计算操作
2.2.3 Vue代码运行原理
  • 概述编译过程的概念(Vue语法 -> 原生语法)

                   Vue框架
Vue代码 ------------------>原生JS代码

3. Vue模板语法

3.1 模板语法概述

前端渲染:把数据填充到HTML标签中

前端渲染方式:

  • 原生JS拼接字符串
  • 使用前端模板引擎
  • 使用vue特有的模板语法

Vue模板语法概览:

  • 差值表达式
  • 指令
  • 事件绑定
  • 属性绑定
  • 样式绑定
  • 分支循环结构

3.2 指令

3.2.1 指令 :
  • 指令就是自定义属性
  • 指令的格式:以 v-开始(如:v-cloak)
3.2.2 v-cloak指令用法
  • 插值表达式存在的问题:“闪动”( 如:在hello world案例中刷新过程中有时候会闪过{{}} )
  • 如何解决该问题:使用v-cloak指令
  • 原理:先隐藏,替换好值之后再显示最终的值

在这里插入图片描述

    <style>
        [v-cloak] {
            display: none;
        }
    </style>
</head>

<body>
    <div id="app">
        <div v-cloak>{{msg}}</div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        // v-cloak指令的用法:
        // 1. 提供样式
        //     [v-cloak]{
        //         display: none;
        //     }
        // 2. 在插值表达式所在的标签中添加v-cloak

		// 原理:先通过样式隐藏内容,然后在内存中进行值的替换,替换好之后再显示最终的结果
        var vm = new Vue({
            el: '#app',
            data: {
                msg: 'Hello Vue'
            }
        });
    </script>
</body>
3.2.3 数据绑定指令
  • v-text 填充纯文本

    1. 相比插值表达式更加简洁
      在这里插入图片描述
  • v-html 填充HTML片段

    1. 存在安全问题
    2. 本网站内部数据可用,来自第三方的数据不可用
      在这里插入图片描述
  • v-pre 填充原始信息

    1. 显示原始信息,跳过编译过程
      在这里插入图片描述
    <div id="app">
        <div>{{msg}}</div>
        <!-- v-text没有闪动问题 -->
        <div v-text='msg'></div>
        <!-- v-html -->
        <div v-html='msg1'></div>
        <!-- v-pre显示原始信息,跳过编译过程 -->
        <div v-pre>{{msg}}</div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                msg: 'Hello Vue',
                msg1: '<h1>HTML</h1>'
            }
        });
    </script>

在这里插入图片描述

3.2.4 数据响应式
  • 响应式

    1. html5中的响应式(屏幕尺寸的变化导致样式的变化)
    2. 数据的响应式(数据的变化导致页面内容的变化)
  • 数据绑定

    1. 将数据填充到标签中
  • v-once 只编译一次

    1. 显示内容之后不再具有响应式功能
      在这里插入图片描述
    <div id="app">
        <div>{{msg}}</div>
        <div v-once>{{info}}</div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
    // v-once的应用场景:
    // 如果显示的信息后续不需要再修改,可以使用v-once,这样可以提高性能。
        var vm = new Vue({
            el: '#app',
            data: {
                msg: 'Hello Vue',
                info: 'world'
            }
        });
    </script>

在这里插入图片描述

3.3 双向数据绑定

3.3.1 v-model指令用法
<input type='text' v-model='uname' />

在这里插入图片描述

    <div id="app">
        <div>{{msg}}</div>
        <div>
            <input type="text" v-model='msg'>
        </div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                msg: 'Hello Vue',
            }
        });
    </script>

在这里插入图片描述

  1. 用户修改页面表单中的数据时会影响数据中的内容变化
  2. 数据中的内容变化会反过来影响页面中数据
3.3.2 MVVM设计思想
  1. M(model)-> DOM
  2. V(view)-> Plain JavaScript Objects
  3. VM(View-Model)-> Vue
  • View和Model两者之间不能直接交互,必须通过VM
  • 从视图到模型用事件监听方式实现交互
  • 从模型到视图用数据绑定实现交互

3.4 事件绑定

3.4.1 Vue如何处理事件
  • v-on 指令用法
<input type='button' v-on:click='num++' />
  • v-on 简写形式
<input type='button' @click='num++' />

例:

    <div id="app">
        <div>{{num}}</div>
        <div>
            <!-- 指令用法 -->
            <button v-on:click='num++'>点击</button>
            <!-- 简写用法 -->
            <button @click='num--'>点击1</button>
        </div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        // 事件绑定
        var vm = new Vue({
            el: '#app',
            data: {
                num: 0
            }
        });
    </script>

在这里插入图片描述

3.4.2 事件函数的调用方式
  • 直接绑定函数名称
<button v-on:click='say'>Hello</button>
  • 调用函数
<button v-on:click='say()'>Say Hi</button>

例:

    <div id="app">
        <div>{{num}}</div>
        <div>
            <button @click='handle'>点击2</button>

            <button @click='handle()'>点击3</button>
        </div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        // 事件绑定
        var vm = new Vue({
            el: '#app',
            data: {
                num: 0
            },
            methods: {
                handle: function() {
                    // this是Vue的实例对象
                    this.num++;
                }
            }
        });
    </script>
3.4.3 事件函数参数传递
  • 普通事件和事件对象
<button v-on:click='say("hi", $event)'>Say Hi</button>
    <div id="app">
        <div>{{num}}</div>
        <div>
            <button v-on:click='handle'>点击1</button>
            <!-- <button v-on:click='handle(123)'>点击2</button> -->
            <button v-on:click='handle(123, $event)'>点击2</button>
        </div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
    // 事件绑定--参数传递
    // 1. 如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数
    // 2. 如果事件绑定函数调用,事件对象必须作为最后一个参数显示传递,并且事件对象的名称必须是$event
        var vm = new Vue({
            el: '#app',
            data: {
                num: 0
            },
            methods: {
                handle: function(p) {
                    console.log(p);  // 123
                    console.log(event.target.innerHTML);  // 点击2
                    // this是Vue的实例对象
                    this.num++;
                }
            }
        });
    </script>
3.4.4 事件修饰符
  • .stop 阻止冒泡
<a v-on:click.stop="handle">跳转</a>
  • .prevent 阻止默认行为
<a v-on:click.prevent="handle">跳转</a>
    <div id="app">
        <div>{{num}}</div>
        <div v-on:click="handle0">
            <!-- 可以简化相关操作 -->
            <!-- 阻止冒泡 -->
            <button @click.stop='handle1'>点击1</button>
        </div>
        <div>
            <!-- 阻止跳转 -->
            <a href="http://www.baidu.com" v-on:click.prevent='handle2'>百度</a>
        </div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        // 事件绑定
        var vm = new Vue({
            el: '#app',
            data: {
                num: 0
            },
            methods: {
                handle0: function() {
                    // 未阻止冒泡前,点击按钮num发生改变
                    this.num++;
                },
                handle1: function(event) {
                    // 阻止冒泡 原生JS
                    // event.stopPropagation();
                },
                handle2: function(event) {
                    // 阻止默认行为 原生JS
                    // event.preventDefault();
                }
            }
        });
    </script>

常用事件修饰符:

<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用v-on:click.prevent.self 会阻止所有的点击,
v-on:click.self.prevent只会阻止对元素自身的点击。

3.4.5 按键修饰符
  • .enter 回车键
<input v-on:keyup.enter='sunmit'>
  • .delete 删除键
<input v-on:keyup.delete='handle'>

例:

    <div id="app">
        <form action="">
            <div>
                <!-- 点击删除键 内容清空 -->
                用户名:
                <input type="text" v-on:keyup.delete='clearContent' v-model='uname'>
            </div>
            <div>
                <!-- 点击回车键提交信息 -->
                密码:
                <input type="text" v-on:keyup.enter='handleSubmit' v-model='pwd'>
            </div>
            <div>
                <input type="button" v-on:click='handleSubmit' value="提交">
            </div>
        </form>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                uname: '',
                pwd: ''
            },
            methods: {
                handleSubmit: function() {
                    console.log(this.uname, this.pwd);
                },
                clearContent: function() {
                    this.uname = '';
                }
            }
        });
    </script>
3.4.6 自定义案件修饰符
  • 全局config.keyCodes对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112

规则:自定义按键修饰符名字是自定义的,但是对应的值必须是按键对应event.keyCode的值

3.5 简单的计算器案例

<body>
    <!-- 简单的加法计算:分别输入a和b的数值,点击计算按钮,结果显示在下面 -->

    <!-- 1. v-model指令实现数值a和b的绑定
    2. 给按钮绑定事件,实现计算逻辑
    3. 将计算结果绑定到对应位置 -->

    <div id="app">
        <h1>简单计算器</h1>
        <div>
            <span>数值A: </span>
            <span>
                <input type="text" v-model='a'>
            </span>
        </div>
        <div>
            <span>数值B: </span>
            <span>
                <input type="text" v-model='b'>
            </span>
        </div>
        <div>
            <button v-on:click='handle'>计算</button>
        </div>
        <div>计算结果: </div>
        <span v-text='result'></span>
    </div>
    <script src="js/vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                a: '',
                b: '',
                result: ''
            },
            methods: {
                handle: function() {
                    // 这种写法最终结果实现的是字符串拼接
                    // this.result = this.a + this.b;
                    this.result = parseInt(this.a) + parseInt(this.b);
                }
            }
        })
    </script>
</body>

3.6 属性绑定

3.6.1 Vue动态处理属性
  • v-bind 指令用法
<a v-bind:href='url'>跳转</a>
  • 缩写形式
<a :href='url'>跳转</a>

例:

    <div id="app">
        <a v-bind:href="url">淘宝</a>
        <button v-on:click='handle'>切换</button>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                url: 'http://www.taobao.com'
            },
            methods: {
                handle: function() {
                    // 修改url地址
                    this.url = 'http://www.baidu.com';
                }
            }
        });
    </script>
3.6.2 v-model的低层实现原理分析
<input v-bind:value="msg" v-on:input="msg=$event.target.value">

说明:

    <div id="app">
        <div>{{msg}}</div>
        
        <input type="text" v-bind:value="msg" v-on:input='handle'>

		<!-- 简化的做法 -->
        <input type="text" v-bind:value="msg" v-on:input='msg=$event.target.value'>

		<!-- v-model做法 -->
		<input type="text" v-model='msg'>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                msg: 'hello'
            },
            methods: {
                handle: function(event) {
                // 使用输入域中的最新的数据覆盖原来的数据
                    this.msg = event.target.value;
                }
            }
        });
    </script>

3.7 样式绑定

3.7.1 class样式绑定
  • 对象语法
<div v-bind:class="{ active: isActive }"></div>

对象语法使用:

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .active {
            width: 100px;
            height: 100px;
            border: 1px solid red;
        }
        
        .error {
            background-color: rgb(255, 208, 122);
        }
    </style>
</head>

<body>
    <div id="app">
        <div v-bind:class="{active: isActive, error: isError}"></div>
        <button v-on:click='handle'>切换</button>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                isActive: true,
                isError: true
            },
            methods: {
                handle: function() {
                    // 控制isActive的值在true和false之间进行切换
                    this.isActive = !this.isActive;
                    this.isError = !this.isError;
                }
            }
        });
    </script>
</body>

在这里插入图片描述

  • 数组语法
<div v-bind:class="{activeClass, errorClass}"></div>

数组语法使用:

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .active {
            width: 100px;
            height: 100px;
            border: 1px solid red;
        }
        
        .error {
            background-color: rgb(255, 208, 122);
        }
    </style>
</head>

<body>
    <div id="app">
        <div v-bind:class='[activeClass, errorClass]'>测试样式</div>
        <button v-on:click='handle'>切换</button>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                activeClass: 'active',
                errorClass: 'error'
            },
            methods: {
                handle: function() {
                    this.activeClass = '';
                    this.errorClass = ''
                }
            }
        });
    </script>
</body>

样式绑定相关语法细节:

  1. 对象绑定和数组绑定可以结合使用
  2. class绑定的值可以简化操作
  3. 默认的class会保留
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .active {
            width: 100px;
            height: 100px;
            border: 1px solid red;
        }
        
        .error {
            background-color: rgb(255, 208, 122);
        }
        
        .test {
            color: red;
        }
        
        .base {
            font-size: 28px;
        }
    </style>
</head>

<body>
    <div id="app">
        <!-- 对象绑定和数组绑定可以结合使用 -->
        <div v-bind:class='[activeClass, errorClass, {test: isTest}]'>测试样式</div>
        <!-- 简化处理 -->
        <div v-bind:class='arrClasses'></div>
        <div v-bind:class='objClasses'></div>
        <!-- 默认的class -->
        <div class="base" v-bind:class='objClasses'></div>
        <button v-on:click='handle'>切换</button>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                activeClass: 'active',
                errorClass: 'error',
                isTest: true,
                arrClasses: ['active', 'error'],
                objClasses: {
                    active: true,
                    error: true
                }
            },
            methods: {
                handle: function() {
                    // this.isTest = false;
                    this.objClasses.error = false;
                }
            }
        });
    </script>
</body>
3.7.2 style样式处理
  • 对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize }"></div>
  • 数组语法
<div v-bind:style="[baseStyles, overridingStyles]"></div>

示例:

    <div id="app">
        <div v-bind:style='{border: borderStyle, width: widthStyle, height: heightStyle}'></div>
        <!-- 简化写法 -->
        <div v-bind:style='objStyles'></div>

        <!-- 数组语法 -->
        <div v-bind:style='[objStyles, overrideStyles]'></div>
        <button v-on:click='handle'>切换</button>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                borderStyle: '1px solid red',
                widthStyle: '100px',
                heightStyle: '200px',
                objStyles: {
                    border: '1px solid blue',
                    width: '200px',
                    height: '100px'
                },
                overrideStyles: {
                    border: '5px solid orange',
                    backgroundColor: 'pink'
                }
            },
            methods: {
                handle: function() {
                    this.heightStyle = '100px';
                    this.objStyles.width = '100px'
                }
            }
        });
    </script>

3.8 分支循环结构

3.8.1 分支结构
  • v-if
  • v-else
  • v-else-if
  • v-show
    <div id="app">
        <div v-if='score>=90'>优秀</div>
        <div v-else-if='score<90&&score>=80'>良好</div>
        <div v-else-if='score<80&&score>=60'>一般</div>
        <div v-else>不及格</div>
        <div v-show='flag'>测试v-show</div>
        <button v-on:click='handle'>点击</button>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                score: 85,
                flag: false
            },
            methods: {
                handle: function() {
                    this.flag = true;
                }
            }
        });
    </script>
3.8.2 v-if 和 v-show 的区别
  • v-show原理:控制元素样式是否显示,即display(已经渲染到页面)
  • v-if控制元素是否渲染到页面
3.8.3 循环结构 遍历数组
  • v-for 遍历数组
<li v-for='item in list'>{{item}}</li>
<li v-for='(item, index) in list'>{{item}} + '---' + {{index}}</li>
  • key的作用:帮助Vue区分不同的元素,从而提高性能
<li :key='item.id' v-for='(item, index) in list'>{{item + '---' + index}}</li>

例:

    <div id="app">
        <div>水果列表</div>
        <ul>
            <li v-for='item in fruits'>{{item}}</li>
            <!-- 添加索引 -->
            <li v-for='(item, index) in fruits'>{{item + '---' + index}}</li>
            <!-- 遍历更加复杂的数据 -->
            <li :key='item.id' v-for='item in myFruits'>
                <span>{{item.ename}}</span>
                <span>---</span>
                <span>{{item.cname}}</span>
            </li>
        </ul>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                fruits: ['apple', 'orange', 'banana'],
                myFruits: [{
                    id: 1,
                    ename: 'apple',
                    cname: '苹果'
                }, {
                    id: 2,
                    ename: 'orange',
                    cname: '橘子'
                }, {
                    id: 3,
                    ename: 'banana',
                    cname: '香蕉'
                }]
            },
            methods: {

            }
        });
    </script>
3.8.4 循环结构 遍历对象
  • v-for 遍历对象
<div v-for='(value, key, index) in object'></div>

示例:

    <div id="app">
        <div v-for='(v, k, i) in obj'>{{v + '---' + k + '---' + i}}</div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var obj = {
            uname: 'lisi',
            age: 12,
            gender: 'male'
        }
        for (var key in obj) {
            console.log(key, obj[key]);
        }
        var vm = new Vue({
            el: '#app',
            data: {
                obj: {
                    uname: 'lisi',
                    age: 12,
                    gender: 'male'
                }
            },
            methods: {

            }
        });
    </script>
  • v-if 和 v-for 结合使用
<div v-if='value==12' v-for='(value, key, index) in object'></div>

示例:

    <div id="app">
        <!-- 结果只显示 13---age---1 -->
        <div v-if='v==13' v-for='(v, k, i) in obj'>{{v + '---' + k + '---' + i}}</div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var obj = {
            uname: 'lisi',
            age: 12,
            gender: 'male'
        }
        for (var key in obj) {
            console.log(key, obj[key]);
        }
        var vm = new Vue({
            el: '#app',
            data: {
                obj: {
                    uname: 'lisi',
                    age: 13,
                    gender: 'male'
                }
            },
            methods: {

            }
        });

4. Vue常用特性

4.1 常用特性概览

  • 表单操作
  • 自定义指令
  • 计算属性
  • 过滤器
  • 侦听器
  • 生命周期

4.2 表单操作

4.2.1 基于Vue的表单操作
  • input 单行文本
  • textarea 多行文本
  • select 下拉多选
  • radio 单选框
  • checkbox 多选框

示例:

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style type="text/css">
        form div {
            height: 40px;
            line-height: 40px;
        }
        
        form div:nth-child(4) {
            height: auto;
        }
        
        form div span:first-child {
            display: inline-block;
            width: 100px;
        }
    </style>

</head>

<body>
    <div id="app">
        <form action="http://www.baidu.com">
            <div>
                <span>姓名:</span>
                <span>
              <input type="text" v-model='uname'>
            </span>
            </div>
            <div>
                <span>性别:</span>
                <span>
              <input type="radio" id="male" value="1" v-model='gender'>
              <label for="male"></label>
              <input type="radio" id="female" value="2" v-model='gender'>
              <label for="female"></label>
            </span>
            </div>
            <div>
                <span>爱好:</span>
                <input type="checkbox" id="ball" value="1" v-model='hobby'>
                <label for="ball">篮球</label>
                <input type="checkbox" id="sing" value="2" v-model='hobby'>
                <label for="sing">唱歌</label>
                <input type="checkbox" id="code" value="3" v-model='hobby'>
                <label for="code">写代码</label>
            </div>
            <div>
                <span>职业:</span>
                <select v-model='occupation'>
              <option value="0">请选择职业...</option>
              <option value="1">教师</option>
              <option value="2">软件工程师</option>
              <option value="3">律师</option>
            </select>
            </div>
            <div>
                <span>个人简介:</span>
                <textarea v-model='desc'></textarea>
            </div>
            <div>
                <input type="submit" value="提交" @click.prevent='handle'>
            </div>
        </form>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                uname: '11',
                gender: 1,
                hobby: [],
                occupation: 0,
                desc: ''
            },
            methods: {
                handle: function() {}
            }
        });
    </script>
</body>
4.2.2 表单域修饰符
  • number:转化为数值
  • trim:去掉开始和结尾的空格
  • lazy:将input事件切换为change事件(change事件:鼠标失去焦点后才触发)
<input v-model.number="age" type="number">

示例:

    <div id="app">
        <!-- 结果为字符串拼接 -->
        <!-- <input type="text" v-model='age'> -->

        <!-- 结果为输入的数值+1 -->
        <input type="text" v-model.number='age'>
        <!-- 去掉首尾空格 -->
        <input type="text" v-model.trim='info'>
        <!-- input事件切换为change事件 -->
        <input type="text" v-model.lazy='msg'>
        <div>{{msg}}</div>

        <button @click='handle'>点击</button>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                age: '',
                info: '',
                msg: ''
            },
            methods: {
                handle: function() {
                    console.log(this.age + 1);
                    console.log(this.info.length);
                }
            }
        });
    </script>

4.3 自定义指令

内置指令不满足需求。

4.3.1 自定义指令的语法规则(获取元素焦点)
Vue.directive('focus' {
	inserted: function(el) {
		// 获取元素焦点
		el.focus();
	}
})
4.3.2 自定义指令用法
<input type="text" v-focus>

(自行查阅文档,多用bind 和 inserted)

输入框聚焦状态案例:

    <div id="app">
        <input type="text">
        <!-- 刷新添加定位效果 -->
        <input type="text" v-focus>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        Vue.directive('focus', {
            inserted: function(el) {
                // el表示指令所绑定的元素
                el.focus();
            }
        });
        var vm = new Vue({
            el: '#app',
            data: {

            },
            methods: {

            }
        });
    </script>
4.3.3 带参数的自定义指令(改变元素背景色)
Vue.directive('color', {
	inserted: function(el, binding) {
		el.style.backgroundColor = binging.value.color;
	}
})
4.3.4 指令的用法
<input type="text" v-color='{color:"orange"}'>

改变背景颜色示例:

    <div id="app">
        <input type="text" v-color='msg'>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        Vue.directive('color', {
            bind: function(el, binding) {
                // console.log(binding);
                // 根据参数指令的参数设置背景色
                el.style.backgroundColor = binding.value.color;
            }
        });
        var vm = new Vue({
            el: '#app',
            data: {
                msg: {
                    color: 'orange'
                }
            },
            methods: {

            }
        });
    </script>
4.3.5 局部指令
directives: {
	focus: {
		// 指令的定义
		inserted: function(el) {
			el.focus()
		}
	}
}

示例:

    <div id="app">
        <input type="text" v-color='msg' v-focus>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                msg: {
                    color: 'pink'
                }
            },
            methods: {

            },
            // 局部指令
            directives: {
                color: {
                    bind: function(el, binding) {
                        el.style.backgroundColor = binding.value.color;
                    }
                },
                focus: {
                    inserted: function(el) {
                        el.focus();
                    }
                }
            }
        });
    </script>

4.4 计算属性

表达式的逻辑计算可能比较复杂,计算属性可以使模板内容更加简洁。

4.4.1 计算属性的用法
computed: {
	reversedMessage: function() {
		return this.msg.split('').reverse().join('')
	}
}
4.4.2 计算属性和方法的区别
  • 计算属性是基于它们的依赖进行缓存的
  • 方法不存在缓存
    <div id="app">
        <!-- 计算属性对于同样的属性,多次访问用的都是第一次计算的结果 -->
        <div>{{reverseString}}</div>
        <div>{{reverseString}}</div>
        <!-- 函数每调用一次都会执行一次函数 -->
        <div>{{reverseMessage()}}</div>
        <div>{{reverseMessage()}}</div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                msg: 'hello'
            },
            methods: {
                reverseMessage: function() {
                    console.log('methods');
                    return this.msg.split('').reverse().join('');
                }
            },
            // 计算属性函数
            computed: {
                reverseString: function() {
                    console.log('computed');
                    return this.msg.split('').reverse().join('');
                }
            }
        });
    </script>

在这里插入图片描述

4.5 侦听器

4.5.1 应用场景

数据变化时执行异步或开销较大的操作。

4.5.2 侦听器的用法
watch: {
    firstName: function(val) {
        // val表示变化之后的值
        this.fullName = val + this.lastName;
    },
    lastName: function(val) {
        this.fullName = this.firstName + val;
    }
}
    <div id="app">
        <div>
            <span>名:</span>
            <span>
            <input type="text" v-model='firstName'>
          </span>
        </div>
        <div>
            <span>姓:</span>
            <span>
            <input type="text" v-model='lastName'>
          </span>
        </div>
        <div>{{fullName}}</div>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script>
        // 修改 名 或 姓, 全名随之变化
        var vm = new Vue({
            el: '#app',
            data: {
                firstName: 'Jim',
                lastName: 'Green',
                fullName: 'Jim Green'
            },
            watch: {
                firstName: function(val) {
                    this.fullName = val + ' ' + this.lastName;
                },
                lastName: function(val) {
                    this.fullName = this.firstName + ' ' + val;
                }
            }
        });
    </script>
  • 验证用户名是否可用案例:
<body>
    <div id="app">
        <div>
            <span>用户名:</span>
            <span>
                <input type="text" v-model.lazy='uname'>
            </span>
            <span>{{tip}}</span>
        </div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        // 侦听器
        // 1. 采用侦听器监听用户名的变化
        // 2. 调用后台接口进行验证
        // 3. 根据验证结果调整提示信息
        var vm = new Vue({
            el: '#app',
            data: {
                uname: '',
                tip: ''
            },
            methods: {
                checkName: function(uname) {
                    var that = this;
                    // 调用接口,但是可用使用定时任务的方式来模拟接口调用
                    setTimeout(function() {
                        // 模拟接口调用
                        if (uname == '友人A') {
                            that.tip = '用户名已存在,请更换一个'
                        } else {
                            that.tip = '用户名可以使用'
                        }
                    }, 2000)
                }
            },
            watch: {
                uname: function(val) {
                    // 调用后台接口验证用户名的合法性
                    this.checkName(val);
                    // 修改提示信息
                    this.tip = '正在验证...';
                }
            }
        });
    </script>
</body>

4.6 过滤器

4.6.1 过滤器的作用

格式化数据,如将字符串格式化为首字母大写,将日期格式化为指定的格式等。

4.6.2 自定义过滤器
Vue.filter('过滤器名称', function(value) {
	// 过滤器业务逻辑
}
4.6.3 过滤器的使用
<div>{{msg | upper}}</div>

<div>{{msg | upper | lower}}</div>

<div v-bind:id="id | foematId"></div>

示例:

    <div id="app">
        <input type="text" v-model='msg'>
        <div>{{msg | upper}}</div>
        <div>{{msg | upper | lower}}</div>
        <div :abc='msg | upper'>测试数据</div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        // 过滤器,首字母大写
        Vue.filter('upper', function(val) {
            return val.charAt(0).toUpperCase() + val.slice(1);
        });
        // 首字母小写
        Vue.filter('lower', function(val) {
            return val.charAt(0).toLowerCase() + val.slice(1);
        });
        var vm = new Vue({
            el: '#app',
            data: {
                msg: ''
            }
        });
    </script>

在这里插入图片描述

4.6.4 局部过滤器
filters: {
	capitalize: function() {}
}

示例:

    <div id="app">
        <input type="text" v-model='msg'>
        <div>{{msg | upper}}</div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                msg: ''
            },
            // 局部过滤器
            filters: {
                upper: function(val) {
                    return val.charAt(0).toUpperCase() + val.slice(1);
                }
            }
        });
    </script>
4.6.5 带参数的过滤器
Vue.filter('format', function(value, arg1) {
	// value是过滤器传过来的参数
})
4.6.6 过滤器的使用
<div>{{date | format('yyyy-MM-dd')}}</div>
  • 通过过滤器格式化日期案例(不完善版):
    <div id="app">
        <div>{{date | format('yyyy-MM-dd')}}</div>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        Vue.filter('format', function(value, arg) {
            if (arg == 'yyyy-MM-dd') {
                var ret = '';
                ret += value.getFullYear() + '-' + (value.getMonth() + 1) + '-' + value.getDate();
                return ret;
            }
            return value;
        })
        var vm = new Vue({
            el: '#app',
            data: {
                date: new Date()
            },
            methods: {

            }
        });
    </script>

4.7 生命周期

4.7.1 主要阶段
  • 挂载(初始化相关属性)

    1. beforeCreate
    2. created
    3. beforeMount
    4. mounted
  • 更新(元素或组件的变更操作)

    1. beforeUpdate
    2. updated
  • 销毁(销毁相关属性)

    1. beforedestory
    2. destoryed

(官网可查看生命周期图示)

    <div id="app">
        <div>{{msg}}</div>
        <button @click='update'>更新</button>
        <button @click='destory'>销毁</button>
    </div>
    <script src="js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                msg: '生命周期'
            },
            methods: {
                update: function() {
                    this.msg = 'hello'
                },
                destory: function() {
                    this.$destroy();
                }
            },
            beforeCreate: function() {
                console.log('beforeCreate');
            },
            created: function() {
                console.log('created');
            },
            beforeMount: function() {
                console.log('beforeMount');
            },
            mounted: function() {
                console.log('mounted');
            },
            beforeUpdate: function() {
                console.log('beforeUpdate');
            },
            updated: function() {
                console.log('updated');
            },
            beforeDestroy: function() {
                console.log('beforeDestroy');
            },
            destroyed: function() {
                console.log('destroyed');
            }
        });
    </script>
4.7.2 Vue实例的产生过程
  1. beforeCreate 在实例初始化之后,数据观测和事件配置之前被调用
  2. created 在实例创建完成后被立即调用
  3. beforeMount 在挂载开始之前被调用
  4. mounted el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子
  5. beforeUpdate 数据更新时调用,发生在虚拟DOM打补丁之前
  6. updated 由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子
  7. beforeDestroy 实例销毁之前调用
  8. destroyed 实例销毁之后调用

5. 综合案例(图书管理)

补充知识:(数组相关API)

1. 变异方法(修改原有数据)

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

2. 替换数组(生成新的数组)

  • filter()
  • concat()
  • slice()

3. 修改响应式数据

  • Vue.set(vm.items, indexOfItem, newValue)
  • vm.$set(vm.items, indexOfItem, newValue)
    1. 参数1表示要处理的数组名称
    2. 参数2表示要处理的数组的索引
    3. 参数3表示要处理的数组的值

4. 常用特性应用场景

  • 过滤器(格式化日期)
  • 自定义指令(获取表单焦点)
  • 计算属性(统计图书数量)
  • 侦听器(验证图书存在性)
  • 生命周期(图书数据处理)

代码

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style type="text/css">
        .grid {
            margin: auto;
            width: 530px;
            text-align: center;
        }
        
        .grid table {
            border-top: 1px solid #C2D89A;
            width: 100%;
            border-collapse: collapse;
        }
        
        .grid th,
        td {
            padding: 10;
            border: 1px dashed #F3DCAB;
            height: 35px;
            line-height: 35px;
        }
        
        .grid th {
            background-color: #F3DCAB;
        }
        
        .grid .book {
            padding-bottom: 10px;
            padding-top: 5px;
            background-color: #F3DCAB;
        }
        
        .grid .total {
            height: 30px;
            line-height: 30px;
            background-color: #F3DCAB;
            border-top: 1px solid #C2D89A;
        }
    </style>

</head>

<body>
    <div id="app">
        <div class="grid">
            <div>
                <h1>图书管理</h1>
                <div class="book">
                    <div>
                        <label>
                      编号:
                    </label>
                        <input type="text" id="id" v-model='id' :disabled="flag" v-focus>
                        <label>
                      名称:
                    </label>
                        <input type="text" id="name" v-model='name'>
                        <button @click='handle' :disabled="submitFlag">提交</button>
                    </div>
                </div>
            </div>
            <div class="total">
                <span>图书总数:</span>
                <span>{{total}}</span>
            </div>
            <table>
                <thead>
                    <tr>
                        <th>编号</th>
                        <th>名称</th>
                        <th>时间</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    <tr :key='item.id' v-for='item in books'>
                        <td>{{item.id}}</td>
                        <td>{{item.name}}</td>
                        <td>{{item.date | format('yyyy-MM-dd hh:mm:ss')}}</td>
                        <td>
                            <a href="" @click.prevent='toEdit(item.id)'>修改</a>
                            <span>|</span>
                            <a href="" @click.prevent='deleteBook(item.id)'>删除</a>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script>
        // 自定义指令
        Vue.directive('focus', {
            inserted: function(el) {
                el.focus();
            }
        });

        // 日期格式化
        Vue.filter('format', function(value, arg) {
            function dateFormat(date, format) {
                if (typeof date === "string") {
                    var mts = date.match(/(\/Date\((\d+)\)\/)/);
                    if (mts && mts.length >= 3) {
                        date = parseInt(mts[2]);
                    }
                }
                date = new Date(date);
                if (!date || date.toUTCString() == "Invalid Date") {
                    return "";
                }
                var map = {
                    "M": date.getMonth() + 1, //月份 
                    "d": date.getDate(), //日 
                    "h": date.getHours(), //小时 
                    "m": date.getMinutes(), //分 
                    "s": date.getSeconds(), //秒 
                    "q": Math.floor((date.getMonth() + 3) / 3), //季度 
                    "S": date.getMilliseconds() //毫秒 
                };
                format = format.replace(/([yMdhmsqS])+/g, function(all, t) {
                    var v = map[t];
                    if (v !== undefined) {
                        if (all.length > 1) {
                            v = '0' + v;
                            v = v.substr(v.length - 2);
                        }
                        return v;
                    } else if (t === 'y') {
                        return (date.getFullYear() + '').substr(4 - all.length);
                    }
                    return all;
                });
                return format;
            }
            return dateFormat(value, arg);
        })

        var vm = new Vue({
            el: '#app',
            data: {
                submitFlag: false,
                flag: false,
                id: '',
                name: '',
                books: []
            },
            //增删改
            methods: {
                handle: function() {
                    if (this.flag) {
                        // 编辑操作
                        // 根据当前id更新数组中数据
                        this.books.some((item) => {
                            if (item.id == this.id) {
                                item.name = this.name;
                                // 完成更新操作之后需要终止循环
                                return true;
                            }
                        });
                        this.flag = false;
                    } else {
                        // 添加操作
                        var book = {};
                        book.id = this.id;
                        book.name = this.name;
                        book.date = '';
                        this.books.push(book);
                    }
                    // 清空
                    this.id = '';
                    this.name = '';
                },
                toEdit: function(id) {
                    // 禁止修改id
                    this.flag = true;
                    // 修改图书
                    console.log(id);
                    var book = this.books.filter(function(item) {
                        return item.id == id;
                    });
                    console.log(book);
                    this.id = book[0].id;
                    this.name = book[0].name;
                },
                deleteBook: function(id) {
                    // 删除图书
                    // 方法一:
                    // 根据id从数组中查找元素的索引
                    // var index = this.books.findIndex(function(item) {
                    //     return item.id == id;
                    // });
                    // 根据索引删除数组元素
                    // this.books.splice(index, 1);
                    // ------------------------------------------
                    // 方法二: 通过filter方法进行删除
                    this.books = this.books.filter(function(item) {
                        return item.id != id;
                    });
                }
            },
            computed: {
                total: function() {
                    // 计算图书总书
                    return this.books.length;
                }
            },
            watch: {
                name: function(val) {
                    // 验证图书名称是否已经存在
                    var flag = this.books.some(function(item) {
                        return item.name == val;
                    });
                    if (flag) {
                        // 图书存在
                        this.submitFlag = true;
                    } else {
                        this.submitFlag = false;
                    }
                }
            },
            mounted: function() {
                // 该生命周期钩子函数被触发时,模板可使用
                // 一般此时用于获取后台数据,把数据填充到模板
                var data = [{
                    id: 1,
                    name: '三国演义',
                    date: 2525609975000
                }, {
                    id: 2,
                    name: '水浒传',
                    date: 2525609975000
                }, {
                    id: 3,
                    name: '红楼梦',
                    date: 2525609975000
                }, {
                    id: 4,
                    name: '西游记',
                    date: 2525609975000
                }];
                this.books = data;
            }
        })
    </script>
</body>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值