学习前提在学习Vue之前最好是已了解关于 HTML、CSS 和 JavaScript 的知识。如果你刚开始学习前端开发,建议先把基础知识学完后在继续学习。

Vue简介

什么是 Vue?

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的 渐进式的js框架,发布于 2014 年 2 月。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库(如: vue-router , vue-resource , vuex 等)或既有项目整合。

MVVM 模式的实现者——双向数据绑定模式

模型—视图—视图模型(Model-View-ViewModel - MVVM),本质上是MVC(模型—视图—控制器)的改进版,其最重要的特性即是数据绑定(data binding),此外还包括依赖注入、路由配置、数据模板等一些特性。

  • Model:模型层,在这里表示 JavaScript 对象
  • View:视图层,在这里表示 DOM(HTML 操作的元素)
  • ViewModel:连接视图和数据的中间件,Vue.js 就是 MVVM 中的 ViewModel 层的实现者

Vue基础 - 图1

在 MVVM 架构中,是不允许 数据视图 直接通信的,只能通过 ViewModel 来通信,而 ViewModel 就是定义了一个 Observer 观察者

  • ViewModel 能够观察到数据的变化,并对视图对应的内容进行更新
  • ViewModel 能够监听到视图的变化,并能够通知数据发生改变

至此,我们就明白了,Vue.js 就是一个 MVVM 的实现者,他的核心就是实现了 DOM 监听 与 数据绑定

:::info VM 的一个实现原理

  • view model中内置了一个观察者,这个观察者观察两个维度。
    • 观察视图的变化:当视图变了 ,就通知数据进行变化
    • 观察数据的变化:当数据变了 ,就通知视图进行变化。
  • MVVM通过VM实现了双向数据绑定。

:::

其它 MVVM 实现者

  • AngularJS
  • ReactJS
  • 微信小程序

为什么要使用 Vue.js

  • 轻量级,体积小是一个重要指标。Vue.js 压缩后有只有 20多kb (Angular 压缩后 56kb+,React 压缩后44kb+
  • 移动优先。更适合移动端,比如移动端的 Touch 事件
  • 易上手,学习曲线平稳,文档齐全
  • 吸取了 Angular( 模块化 )和 React( 虚拟 DOM )的长处,并拥有自己独特的功能,如: 计算属性
  • 开源,社区活跃度高

Vue.js 的两大核心要素

ject.defineProperty是Es5中无

Vue基础 - 图2

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用Object.defineProperty 把这些属性全部转为 getter/setter 。

Object.defineProperty是ES5中一个无法shim的特性,这也就是为什么Vue不支持IE8以及更低版本浏览器。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vuedevtools 来获取更加友好的检查接口。

每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

组件化

  • 页面上每个独立的可交互的区域视为一个组件
  • 每个组件对应一个工程目录,组件所需的各种资源在这个目录下就近维护
  • 页面不过是组件的容器,组件可以嵌套自由组合(复用)形成完整的页面

什么是CDN

CDN的全称是Content Delivery Network,即 内容分发网络 。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。

CDN 简单来说就是内容分发网络,这是一种加速策略,能够从离自己最近的服务器快速的获得外部的资源。

项目里引入Vue的方式

  1. 使用一个框架,我们第一步要做什么呢?安装下载它
  • 方式一:直接CDN引入
  1. <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  2. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  3. <!-- 生产环境版本,优化了尺寸和速度 -->
  4. <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  • 方式二:下载和引入
  1. // 开发环境 https://vuejs.org/js/vue.js
  2. // 生产环境 https://vuejs.org/js/vue.min.js
  • 方式三:NPM(包管理器)安装后续通过Vue-Cli4(vue官方的脚手架)方式引入,实际开发中我们使用该方式

Hello Vue案例

  1. 在页面引入vue的js文件即可
  1. //官网上的cdn
  2. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  1. 在页面中绑定vue元素
  1. <!--创建一个div,id是app-->
  2. <div id="app">
  3. <p>大家好,我是{{name}},我今年{{age}}岁了</p>
  4. </div>

创建vue对象,设计对象的内容

  1. <script type="text/javascript">
  2. new Vue({
  3. el:"#app", //Vue对象绑定在哪一个Div上
  4. data:{
  5. name:"张三",
  6. age:19
  7. } //提供数据的里面存放键值对
  8. })
  9. </script>
  1. 在页面的元素中使用插值表达式来使用vue对象中的内容
  1. <div id="app">
  2. {{ title }}
  3. </div>
  1. 效果
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <p>大家好,我是{{name}},我今年{{age}}岁了</p>
  10. </div>
  11. </body>
  12. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  13. <script type="text/javascript">
  14. new Vue({
  15. el:"#app", //Vue对象绑定在哪一个Div上
  16. data:{
  17. name:"张三",
  18. age:19
  19. } //提供数据的里面存放键值对
  20. })
  21. </script>
  22. </html>

Vue基础 - 图3

Vue基础语法

插值表达式

插值表达式的作用是在View中获得Model中的内容

  • 语法:{{vue的内容}}
  • 作用:插值运算符可以对数据进行显示,也可以在插值运算符中进行表达式计算。
  • 注意:差值表达式不能写在html的标签中,不能作为属性的值的部分
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <h2>Hello {{name}}</h2>
  10. <h2>{{firstname}} {{lastname}}</h2>
  11. <h2>{{count*2}}</h2>
  12. </div>
  13. </body>
  14. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  15. <script type="text/javascript">
  16. new Vue({
  17. el:'#app',
  18. data:{
  19. name:'Vue',
  20. firstname:'张',
  21. lastname:'三',
  22. count:13
  23. }
  24. })
  25. </script>
  26. </html>

效果

Vue基础 - 图4

除了上面的简单用法还可以使用下面的用法

  1. <div id="app">
  2. {{title}}
  3. {{[1,2,3,4][2]}} <!--取值前面数组的第2个元素,小标从0开始-->
  4. {{ {"name":"xiaoyu","age":20}.age }}
  5. {{ sayHello()}}
  6. </div>
  1. <script type="text/javascript">
  2. new Vue({
  3. el:"#app",
  4. data:{
  5. title:"hello world!"
  6. } ,
  7. methods:{
  8. sayHello:function(){
  9. return "hello vue";
  10. }
  11. }
  12. })
  13. </script>

Vue基础 - 图5

双向数据绑定

双向数据绑定:是将标签的value值与vue实例中的data属性值进行绑定。 可以根据下面这个动图进行理解。输入框输入数据,会显示到 span 元素上

Vue基础 - 图6

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <input type="text" v-model="title" />
  10. <p>{{title}}</p>
  11. </div>
  12. </body>
  13. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  14. <script type="text/javascript">
  15. new Vue({
  16. el:'#app',
  17. data:{
  18. title:"hello word"
  19. }
  20. })
  21. </script>
  22. </html>

练习

v-model绑定select,实现默认选中项的效果。

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <select v-model="mySelect">
  10. <option value="java">Java从入门到精通</option>
  11. <option value="python">python从入门到精通</option>
  12. <option value="php">php从入门到精通</option>
  13. <option value="mysql">mysql从删库到跑路</option>
  14. </select>
  15. </div>
  16. </body>
  17. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  18. <script type="text/javascript">
  19. new Vue({
  20. el:'#app',
  21. data:{
  22. mySelect: 'java'
  23. }
  24. })
  25. </script>
  26. </html>

也可以使用JS进行实现

  1. <body>
  2. <div id="app">
  3. <input type="text" id="input"/>
  4. <span id="span"></span>
  5. </div>
  6. <script>
  7. var inputObj = document.getElementById('input');
  8. var spanObj = document.getElementById('span');
  9. inputObj.oninput = function() {
  10. spanObj.innerHTML = this.value;
  11. }
  12. </script>
  13. </body>

事件绑定 v-on

v-on绑定事件,如下案例:事件是input,响应行为(方法)是changeTitle。也就是说,当input元素发生输入事件时,就会调用vue里定义的changeTitle方法

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <input type="text" v-on:input="changeTitle" />
  10. </div>
  11. </body>
  12. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  13. <script type="text/javascript">
  14. new Vue({
  15. el:'#app',
  16. data:{
  17. title:"hello word"
  18. },
  19. methods:{
  20. changeTitle:function(){
  21. console.log("ceshi");//往日志里写
  22. }
  23. }
  24. })
  25. </script>
  26. </html>

Vue基础 - 图7

event.target.value == 当前事件的对象(input元素)的value值 注意:此时的this指的是当前vue对象

所以:如果在method里要想使用当前vue 对象中的data里的内容,必须加上this.

  1. changeTitle:function(event){
  2. this.title = event.target.value;
  3. }

事件绑定简化版:使用@替换v-on:

  1. <input type="text" @input="changeTitle" />

属性绑定 v-bind

前面我们都是通过指令来将值插入到我们的标签内容中,但除了内容外,我们还需要动态绑定某些属性,比如:a标签中的href属性,img标签中的src属性;这个时候我们就可以使用v-bind指令,它的作用就是可以动态绑定属性。

html里的所有属性,都不能使用插值表达式。下面的这种做法是错误的,但可以使用绑定属性绑定来解决:

  1. <a href="{{link}}">baidu</a>
  2. new Vue({
  3. el:"#app",
  4. data:{
  5. title:"hello world!",
  6. link:"http://www.baidu.com"
  7. },
  8. ...

我们知道差值表达式是不能写在html的标签的属性内的,那如果一定要用vue中的属性作为html标签的属性的内容,就可以通过v-bind进行属性绑定

  1. <a v-bind:href="link">baidu</a>
  2. <!--可以缩写成 冒号-->
  3. <a :href="link">baidu</a>

v-bind绑定class

如果我们希望能动态切换class,选中字体颜色变红,初始状态字体为黑色;绑定class有两种方式:对象语法,数组语法。

  • 对象语法
    • 用法一:直接通过{'',''}绑定一个class
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. <style>.active {color: red;}</style>
  7. </head>
  8. <body>
  9. <div id="app">
  10. <h2 :class="{'active': isActive}">Hello Vue!</h2>
  11. </div>
  12. </body>
  13. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  14. <script type="text/javascript">
  15. new Vue({
  16. el: '#app',
  17. data: {
  18. isActive: true
  19. }
  20. })
  21. </script>
  22. </html>
  1. - 用法二:同一个标签和普通的class类同时存在,并不冲突
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. <style>
  7. .active {color: red;}
  8. .line {text-decoration: underline;}
  9. .title {font-size: 20px;}
  10. </style>
  11. </head>
  12. <body>
  13. <div id="app">
  14. <h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello World</h2>
  15. </div>
  16. </body>
  17. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  18. <script type="text/javascript">
  19. new Vue({
  20. el: '#app',
  21. data: {
  22. isActive: true,
  23. isLine: true,
  24. }
  25. })
  26. </script>
  27. </html>
  1. - 如果过于复杂,可以放在一个methods或者computed
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. <style>
  7. .active {color: red;}
  8. .line {text-decoration: underline;}
  9. .title {font-size: 20px;}
  10. </style>
  11. </head>
  12. <body>
  13. <div id="app">
  14. <h2 class="title" :class="classes()">Hello World</h2>
  15. </div>
  16. </body>
  17. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  18. <script type="text/javascript">
  19. new Vue({
  20. el: '#app',
  21. data: {
  22. isActive: true,
  23. isLine: true
  24. },
  25. methods: {
  26. classes() {
  27. return {
  28. 'active': this.isActive,
  29. 'line': this.isLine
  30. }
  31. }
  32. }
  33. })
  34. </script>
  35. </html>
  • 数组语法
    • 用法一:通过[]来绑定一个类
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <style>
            .active {color: red;}
        </style>
    </head>
    <body>
        <div id="app">
            <h2 :class="['active']">Hello Vue!</h2>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#app',
            data: {

            }
        })
    </script>
</html>
-  用法二:同一个标签上和普通的类同时存在,并不冲突 
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <style>
            .active {color: red;}
            .line {text-decoration: underline;}
                        .title {font-size: 20px;}
        </style>
    </head>
    <body>
        <div id="app">
            <h2 class="title" :class="['active', 'line']">Hello Vue!</h2>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#app',
            data: {

            }
        })
    </script>
</html>
-  用法三:如果过于复杂,可以放在一个methods或者computed中 
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <style>
            .active {color: red;}
            .line {text-decoration: underline;}
            .title {font-size: 20px;}
        </style>
    </head>
    <body>
        <div id="app">
            <h2 class="title" :class="classes()">Hello Vue!</h2>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#app',
            data: {

            },
            methods: {
                classes() {
                    return ['active', 'line']
                }
            }
        })
    </script>
</html>

v-bind绑定style

我们可以利用v-bind:style来绑定一个内嵌样式。

注意:

  1. 我们可以使用驼峰式语法:比如font-size —-> fontSize
  2. 短横线分隔 font-size

绑定class有两种方式:

  1. 对象语法
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <div id="app">
            <div :style="{color: currentColor, fontSize: fontsize + 'px'}">
                Hello Vue
            </div>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#app',
            data: {
                currentColor: 'blue',
                fontsize: 30
            }
        })
    </script>
</html>
  1. 数组语法
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <div id="app">
            <div :style="[colorStyle, fontStyle]">
                Hello Vue
            </div>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#app',
            data: {
                colorStyle: {color: 'blue'},
                fontStyle: {'font-size': '20px'}
            }
        })
    </script>
</html>

v-once指令

指明此元素的数据只出现一次,数据内容的修改不影响此元素

此时该标签中的差值表达式,只获取一次数据。之后数据的变化不影响此差值表达式的值。。

<p v-once>{{titile}}</p>

案例

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
        <!-- <script src="js/vue.js" type="text/javascript" charset="utf-8"></script>-->
    </head>
    <body>
        <div id="app">
            <input type="text" v-model="title" />
            <p v-once>{{title}}</p>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el:'#app',
            data:{
                title:"hello word"
            }
        })

    </script>
</html>

v-html 和 v-text

v-html:会将vue中的属性的值作为html的元素来使用,就好比是innerHTML

v-text:会将vue中的属性的值只作为纯文本来使用,纯文本输出内容

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <div id="app">
            <p v-html="myLink"></p>
            <p v-text="myLink"></p>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el:'#app',
            data:{
                link:"https://www.baidu.com/",
                myLink:'<a href="https://www.baidu.com/">百度</a>'
            }
        })

    </script>
</html>

效果

Vue基础 - 图8

分支语句:v-if、v-else-if、v-else

v-if、v-else-if、v-else。这三个指令与JS中的条件判断if、else if、else类似。

v-if可以根据表达式中的值渲染或者销毁元素和组件。满足条件才显示对应的元素

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
      <div id="app">
        <div v-if="score >= 90">优秀</div>
        <div v-else-if="score >= 75">良好</div>
        <div v-else-if="score >= 60">及格</div>
        <div v-else>不及格</div>
        <button @click="delScore">减分</button>
      </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el:'#app',
            data:{
                score: 100
            },
            methods: {
                delScore() {
                    this.score -= 10
                }
            }

        })
    </script>
</html>

v-show

v-show的用法与v-if类似,也是用于条件判断的。

二者的区别:

  • v-if是完全不创建DOM元素,而v-show则是创建了DOM元素,仅仅是使用display:none隐藏了该元素。
  • 当需要频繁的显示、隐藏一些内容时,使用v-show。当我们仅有一次切换,某些v-if场景根本不会显示出来的时候,用v-if。

循环语句 v-for

当我们有一组数据需要进行渲染时,我们就可以使用v-for完成。v-for类似于JS中的for循环。

格式:item in items。后面的items代表着你需要遍历的数组,item则表示每一项的名称。

作用:遍历数组、对象

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
      <div id="app">
        <ul>
            <!-- <li v-for="item in dataList">{{item}}</li> -->
            <li v-for="(item, index) in dataList">{{index + ':' + item}}</li>
        </ul>
      </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el:'#app',
            data:{
                dataList: [
                    'Java从入门到精通',
                    'php从入门到精通',
                    'mysql从删库到跑路',
                    'Vue从单身到脱单'
                 ]
            }
        })
    </script>
</html>

如果我们需要在遍历的过程中,获取该元素所在数组中的索引值,应该怎么操作?

<li v-for="(item, index) in dataList">{{index + ':' + item}}</li>

改进版:for语句里,key建议加上,作为标识 index 是下标

<ul>
    <!-- <li v-for="item in dataList">{{item}}</li> -->
    <li v-for="(item, index) in dataList" :key = "index">{{index + ':' + item}}</li>
</ul>

表单输入绑定 v-model

表单控件在实际开发中是非常常见的,Vue中使用 v-model 指令来实现表单元素和数据的双向绑定。

你可以用 v-model 指令在表单 <input><textarea><select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <div id="app">
            <input type="text" v-model="title" />
            <p>{{title}}</p>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el:'#app',
            data:{
                title:"hello word"
            }
        })
    </script>
</html>

案例解析:

  1. 当我们在输入框内容时,因为input使用了v-model绑定了msg, 所以会实时将输入的内容传递给定义的属性msg,data里面的属性msg也就发生改变。
  2. 因为上面我们使用了Mustache语法,所以当属性msg发生改变的时候,DOM也会跟着改变。
  3. 所以,通过v-model实现了双向的数据绑定。

过滤器

有时候我们得到的数据并不直接符合我们的需求,这时候需要对其进行改变。如果每个数据都手动编写,将非常的麻烦,我们可以将有共同特点、共同需求的数据使用过滤器统一进行处理。

Filters属性与datamethods平级,用于定义过滤器。过滤器本质上是一个方法,接收一个参数。在页面中,使用插值运算符,语法:{{数据 | 过滤器名称}},不需要写参数,不需要写括号,过滤器默认会把前面的数据作为参数传递给方法。

过滤器的方法需要返回一个数据,返回的数据作为最终结果渲染到页面中。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <div id="app">
            <h2>{{count | filterEven}}</h2>
            <h2>{{count2 | filterEven}}</h2>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el:'#app',
            data:{
                count: 10,
                count2: 11
            },
            filters: {
              filterEven(num) {
                  if (num % 2 === 0) {
                    return num
                  } else {
                    return '非法数字'
                  }
              }
            }
        })
    </script>
</html>

Vue基础 - 图9

监听器

如果我们需要监听某个值的变化,当这个值变化是,进行一些操作,可以使用监听器。监听器的属性名称是watch,和data平级。监听器中的数据是方法,接收两个参数,分别为改变后的值和改变前的值。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
         <div id="app">
            <input type="text" v-model="firstName">
            <input type="text" v-model="lastName">
            <h2>{{fullName}}</h2>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el:'#app',
            data:{
                firstName:'张',
                lastName:'三',
                fullName: ''
            },
            watch: {
                firstName: function(newVal, oldVal) {
                    // 在vue中,操作data、method必须使用this
                    this.fullName = this.firstName + this.lastName
                },
                lastName(newVal, oldVal) {
                    this.fullName = this.firstName + this.lastName
                }
            }
        })
    </script>
</html>

Vue基础 - 图10

计算属性:computed

1)计算属性的作用?

我们知道,在模板中可以直接通过插值语法显示一些data中的数据。但是某些情况下,我们可能需要对数据进行转化后再显示,或者需要将多个数据结合起来进行显示

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <div id="app">
            书籍总价值:{{totalPrice}}
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#app',
            data: {
                books: [
                    {name: 'Vuejs入门', price: 99, count: 2},
                    {name: 'Vuejs底层', price: 96, count: 1},
                    {name: 'Vuejs大神', price: 20, count: 3},
                ]
            },
            computed: {
                totalPrice() {
                    console.log("进入计算")
                    return this.books.reduce((prev, current) => {
                        prev += current.price * current.count
                        return prev
                    }, 0)
                }
            }
        })
    </script>
</html>

2)计算属性与方法的区别?

为什么会有这样的一个属性呢,用methods都可以实现我们的功能,为什么多了这样一个计算属性的东西呢?

因为计算属性会进行缓存,如果多次使用,计算属性只会调用一次。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <div id="app">
            <div>{{nameToWish}}</div>
            <div>{{nameToWish}}</div>
            <div>{{nameToWish}}</div>
            <div>{{getnameToWish()}}</div>
            <div>{{getnameToWish()}}</div>
            <div>{{getnameToWish()}}</div>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        const vm = new Vue({
            el: '#app',
            data: {
                name: 'lao chen',
                wish: 'xiang bao fu'
            },
            computed: {
                nameToWish() {
                    console.log('执行了nameToWish的计算属性')
                    return this.name + ' ' + this.wish
                }
            },
            methods: {
                getnameToWish() {
                    console.log('执行了getnameToWish的方法')
                    return this.name + ' ' + this.wish
                }
            }
        })
    </script>
</html>

Vue基础 - 图11

说明

  • methods :定义方法,调用方法使用 currentTime1() ,需要带括号
  • computed :定义计算属性,调用属性使用 currentTime2 ,不需要带括号;
  • 注意:methods和computed里不能重名

4)结论

调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点;计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销

小商店练习项目

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>小商店</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <style>
    table {
      border: 1px solid #e9e9e9;
      border-collapse: collapse;
      border-spacing: 0;
    }
    td,
    th {
      padding: 8px 16px;
      border: 1px solid #e9e9e9;
      text-align: left;
    }
    th {
      background-color: #f7f7f7;
    }
  </style>
</head>

<body>
  <div id="app">
    <table>
      <thead>
        <tr>
          <th v-for="(title,index) in titles" :key="index" v-text="title"></th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(book, index) in books" :key="index">
          <td v-text="index"></td>
          <td v-text="book.name"></td>
          <td v-text="book.date"></td>
          <td>{{book.price | priceRule}}</td>
          <td>
            <button @click="sub(index)">-</button>
            <span>{{book.num}}</span>
            <button @click="add(index)">+</button>
          </td>
          <td>
            <button @click="remove(index)">移除</button>
          </td>
        </tr>
      </tbody>
    </table>
    <div>总价格:{{totalPrice | priceRule}}</div>
  </div>
</body>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      titles: ['编号', '商品名称', '生产日期', '价格', '购买数量', '操作'],
      books: [{
          name: '风车',
          date: '2020-9',
          price: 12,
          num: 1
        },
        {
          name: '辣条',
          date: '2020-2',
          price: 2,
          num: 1
        },
        {
          name: 'AD钙',
          date: '2020-10',
          price: 15,
          num: 1
        },
        {
          name: '小糖果',
          date: '2020-3',
          price: 1,
          num: 1
        },
      ]
    },
    filters: {
      priceRule(value) {
        return '¥' + value.toFixed(2)
      }
    },
    computed: {
      totalPrice() {
        return this.books.reduce((prev, current)=> {
          prev +=current.num * current.price
          return prev
        }, 0)
      }
    },
    methods: {
      add(idx){
        this.books[idx].num++
      },
      sub(idx){
        this.books[idx].num--
        if (this.books[idx].num == 0) {
          this.books.splice(idx, 1)
        }
      },
      remove(idx){
        this.books.splice(idx, 1)
      }
    }
  })
</script>

</html>