Vue框架: 基于MVVM模型的渐进式框架

一. 环境准备

1 准备Node环境

由于大部分Vue项目都是依赖于Node环境. 在开始之前, 我们需要先准备好Node环境

访问Node中文官网下载最新版的Node.js

image.png
找到下载的node-v12.16.1-x64.msi, 双击安装

2 配置npm镜像

npm默认的仓库地址是在国外网站,速度较慢,建议大家设置到淘宝镜像。但是切换镜像是比较麻烦的。推荐一款切换镜像的工具:nrm
我们首先安装nrm,这里-g代表全局安装

  1. npm install nrm -g

通过nrm ls命令查看npm的仓库列表,带*的就是当前选中的镜像仓库

通过nrm use taobao来指定要使用的镜像源

3 安装Vue

新建一个文件夹 code

1) 初始化

使用如下指令初始化

  1. npm init -y

发现在目录下会多一个文件package.json, 这个文件用来管理该项目使用了哪些包

2) 安装vue

执行如下命令安装vue

  1. npm install vue --save

以上命令可以简写为

  1. npm i vue

在项目目录会产生一个文件夹node_modules和一个文件package-lock.json

扩展

vue的各种版本

  1. vue.js是完整版, (在初学阶段使用)
  • common: 使用CommonJS规范导入导出module.exports,这种文件不能在浏览器中使用
  • esm(ES Module): 使用ES的模块规范导入导出export default
  • runtime: 运行时版本, 相对于编译版本, 体积更小, 效率更高

    二. 使用Vue

vue使用的3步曲

  1. 引入vue.js
  2. 编写页面(视图)
  3. 编写vue实例

1 引入vue.js

在html的头部, 通过<script src>引入vue.js

这里我用到了VSCode插件: Path Intellisense 快速补全路径

示例

  1. <!-- 1. 引入vue.js -->
  2. <script src="../node_modules/vue/dist/vue.js"></script>

2 编写页面(视图)

在boby中, 编写一个div元素, id为app, 所有视图部分将在这部分渲染

示例

<!-- 2. 编写页面 -->
<div id="app"></div>

3 编写vue实例

在body的最末尾, 使用<script>, 编写vue实例

示例

<!-- 3. 编写vue实例 -->
<script>
  const vm = new Vue({
    el: '#app', // 对应操作的元素, 必填参数
    data: {}, // 数据部分
    methods: {} // 方法部分
  })
</script>
  • el(element元素): 表示vue实例操作页面元素
  • data(数据): 数据部分
  • methods(方法): 方法部分

    4 渲染数据

    需求

将数据渲染到页面中

1) 定义数据

在data中定义数据

示例

<!-- 3. 编写vue实例 -->
<script>
  const vm = new Vue({
    el: '#app', // 对应操作区的元素, 必填参数
    data: {
      msg: 'hello world'
    }, // 数据部分
    methods: {} // 方法部分
  })
</script>

使用键值对方式定义数据. 其中

  • msg: 相当于变量名
  • ‘hello world’: 相当于变量值

    2) 在页面中渲染

示例

<div id="app">
  {{ msg }}
</div>
  • 通过{{}}(插值表达式)使用在data部分定义的变量
  • 插值表达式里是js表达式,不能是js语句
  • 差值表达式可以直接访问vue实例对象的属性 eg:_data

    三. MVVM模型

MVVM由 Model(数据模型) View(视图)VM(ViewModel) 组成

核心思想

实现 数据视图双向绑定

各自的作用

  • Model(数据模型): 操作数据
  • View(视图): 显示数据
  • VM: 模型与视图间的双向操作

双向绑定:

  • 只要 Model 发生改变, View 就可以自然的表现出来
  • 只要用户操作了 View, Model 中的数据也会跟着变化

    四. Vue 实例

    1 属性和方法

Vue 应用的核心就是 Vue 实例 , 一个基本的 Vue 实例包括如下图所示:

new Vue({
  el: '#app',
  data: {},
  methods: {}
})
  • el: 表示要操作的页面元素
  • data: 数据, 可以理解为面向对象中类的”属性
  • methods: 方法, 可以理解为面向对象中类的”方法

属性

当 Vue 实例创建时, 会获取 data 中的数据, 并渲染到视图中, 并监视 data 中数据的变化, 当 data 中的数据改变时, 重新渲染视图

方法

方法就是: 响应视图中的事件, 并修改 data 里的数据

重要结论

  • 属性就是用来 保存 数据的
  • 方法就是用来 修改 数据的

    1) 完成 Vue 的三步曲

在 src 目录下创建03_加法器.html

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>03_加法器</title>
    <!-- 1. 引入vue.js -->
    <script src="../node_modules/vue/dist/vue.js"></script>
  </head>
  <body>
    <!-- 2. 编写div元素 -->
    <div id="app"></div>
    <!-- 3. 编写vue实例 -->
    <script>
      const vm = new Vue({
        el: '#app'
      })
    </script>
  </body>
</html>

2) 编写页面

分析

页面由两个 input 框和一个按钮, 再加一个 input 框组成

示例

<div id="app">
  <input type="text" />+<input type="text" />
  <button>=</button>
  <input type="text" />
</div>

2 绑定属性

使用 v-model 绑定属性

html 部分

<div id="app">
    <input v-model="first" type="text">+<input v-model="second" type="text">
    <button>=</button>
    <input v-model="result" type="text">
</div>

js 部分

const vm = new Vue({
  el: '#app', // 元素
  data: {
    first: 0,
    second: 0,
    result: 0
  }, // 属性
  methods: {} // 方法
})

3 绑定方法

使用 v-on 绑定click方法

html 部分

<button v-on:click="add">=</button>

js 部分

 methods: {
     add() {
         this.result = parseInt(this.first) + parseInt(this.second)
     }
 } // 方法

五. 模板语法

什么是模板

:::info 由Vue解析的HTML字符串 :::

什么是模板语法

:::info 在模板字符串中, 具有特殊意义的语法, 通常就是一些vue指令 :::

image.png

模板语法主要用于视图的编写,以v-指令. 常见的有

  • 属性绑定: v-bind, 简写为:
  • 方法绑定: v-on, 简写为@
  • 双向绑定: v-model
  • 条件渲染: v-if
  • 列表渲染: v-for

1 属性绑定和双向绑定

在src目录下, 创建04_属性绑定.html, 完成vue的3步曲

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>04_属性绑定</title>
    <!-- 1. 引入vue.js -->
    <script src="../node_modules/vue/dist/vue.js"></script>
  </head>
  <body>
    <!-- 2. 编写div元素 -->
    <div id="app"></div>
    <!-- 3. 编写vue实例 -->
    <script>
      const vm = new Vue({
        el: '#app'
      })
    </script>
  </body>
</html>

属性绑定

:::info 将 非表单标签 的一个属性和data中的一个变量绑定 :::

html部分

<!-- 完整的写法 -->
<a v-bind:href="url">百度</a>
<!-- 简写(推荐) -->
<a :href="xzd">新中地</a>

vue官方更推荐简写的方式
image.png

js部分

const vm = new Vue({
  el: '#app',
  data: {
    url: 'http://baidu.com'
  }
})
  • 将a标签href属性和data中的url变量绑定
  • 相当于<a href="http://baidu.com"></a>

特别说明

在指令后面的引号中, 可以使用类似于{{}}中一样的语法.

  • 可以使用 js表达式
  • 直接访问vm实例上的属性和方法
    <!-- 在指令后面的""中, 可以使用跟{{}}中一样的代码 -->
    <div :data-test="msg + 'world'"></div>
    
    上面的代码, 被Vue解析成DOM如下:
    <div data-test="helloworld"></div>
    

    双向绑定

双向绑定的原理

  1. 通过绑定input框架的value属性和data中的一个变量, 实现了数据->视图的单向绑定
  2. 监听input框架的input事件, 当事件触发时, 更新data中的变量

image.png

表单标签 的value值和data中的变量双向绑定

html部分

<input type="text" v-model="uName" />

js部分

const vm = new Vue({
  el: '#app',
  data: {
    uName: 'xiaoming'
  }
})
  • 将input标签的value属性和data中的uName变量绑定
  • 相当于<input type="text" value="xiaoming" />

小结

对于input元素. v-model默认

  • 绑定value属性
  • 监听input事件

:::tips 💡 注意
不同的表单元素, 绑定的属性和监听的事件不同

::: :::tips image.png :::

2 事件处理

1) 事件监听

语法

v-on:input<br />    简写<br />        @input

几种情况

**不写括号 (70%)**<br />        应用<br />            不需要传递自定义参数<br />        事件对象<br />            在methods定义中通过形参接收<br />    **写括号 (30%)**<br />        应用<br />            1 需要传递自定义参数, 不传事件对象 (20%)<br />                调用时<br />                    函数名(实参列表)<br />                声明时<br />                    函数名(形参列表)<br />            2 需要传递自定义参数, 也传递事件对象 (10%)<br />                **调用时**<br />**                    函数名(实参列表, $event)**<br />**                声明时**<br />**                    函数名(形参列表, e)**

2) 事件修饰符

常见的事件修饰符
.stop
阻止冒泡
.prevent
阻止默认行为
.once
该事件只会触发一次
使用

  • 1

  • 多个修饰符可以串联使用

    3)按键修饰符

    概念
    Vue 允许为 v-on 在监听键盘事件时添加按键修饰符

    常见的按键修饰符
    v-on:keyup.keyName (推荐)
    eg: @keyup.enter=”fn”
    v-on:keyup.keyCode

    3 样式渲染

    1) 绑定class属性(80%)

    A 对象语法
    应用
    定义
    1 先编写对应的css样式
    .add1{ color: red }
    .add2{ font-size: 40px }
    2 然后在data中定义对应属性
    data:{ obj:{add1: ture, add2: ture} }
    绑定
    对象的写法 B 数组语法 (推荐)
    应用
    定义
    1 先编写对应的css样式
    .add1{ color: red }
    .add2{ font-size: 40px }
    2 然后在data中定义对应属性
    data:{ arr: [ ‘add1’ , ‘add2’ ] }
    绑定
    数组的写法 </span

    2) 绑定style属性(20%)

    A 对象语法
    应用
    定义
    在data中定义对应属性
    data:{ obj: { color:’red’, fontSize: ‘40px’ } }
    绑定
    对象的语法
    B 数组语法
    可忽略 (跟前者类比)

    4 条件渲染

    条件渲染

    当条件满足时, 渲染到页面

    主要指令: v-ifv-show

    1) 完成vue的3步曲

    在src目录下, 创建05_条件渲染.html, 编写vue的基础模板

    示例

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>05_条件渲染</title>
        <!-- 1. 引入vue.js -->
        <script src="../node_modules/vue/dist/vue.js"></script>
      </head>
      <body>
        <!-- 2. 编写div元素 -->
        <div id="app"></div>
        <!-- 3. 编写vue实例 -->
        <script>
          const vm = new Vue({
            el: '#app'
          })
        </script>
      </body>
    </html>
    

    2) 编写页面与逻辑

    示例

    html部分

    <!-- 2. 编写div元素 -->
    <div id="app">
      <div v-if="flag">这是用v-if渲染的元素</div>
      <div v-show="flag">这是用v-show渲染的元素</div>
    </div>
    

    js部分

    <script>
      const vm = new Vue({
        el: '#app',
        data: {
          flag: true
        }
      })
    </script>
    
    • 当flag为true时, 两个元素都可以显示
    • 当flag为false时, 两个元素都不显示, 区别
      • v-if: 不会创建元素
      • v-show: 创建元素, 但是display=none

    3) 表达式

    除了使用变量外, 还可以使用表达式

    比如

    <div v-if="flag == true">这是用v-if渲染的元素</div>
    

    案例

    通过按钮控制元素的显示/隐藏

    示例

    html部分

    <div id="app">
      <button @click="flag = !flag">切换</button>
      <div v-if="flag">这是用v-if渲染的元素</div>
      <div v-show="flag">这是用v-show渲染的元素</div>
    </div>
    
    • 绑定按钮的点击事件
      • 当flag==true时点击, flag先取反, 再保存, 此时flag为false
      • 当flag==false时点击, flag先取反, 再保存, 此时flag为true

    5 列表渲染

    列表渲染也称”循环渲染”, 通过v-for遍历数组或者对象

    1) 遍历数组

    如果只希望得到每个数组元素的值, 不需要得到下标

    遍历数组设置key值建议使用book.id (数据本身主键id) 不建议使用index索引下标

    语法

    v-for="item in items"
    

    示例

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>06_列表渲染</title>
        <!-- 1. 引入vue.js -->
        <script src="../node_modules/vue/dist/vue.js"></script>
      </head>
      <body>
        <!-- 2. 编写div元素 -->
        <div id="app">
          <ul>
            <li v-for="item in items">{{ item }}</li>
          </ul>
        </div>
        <!-- 3. 编写vue实例 -->
        <script>
          const vm = new Vue({
            el: '#app',
            data: {
              items: ['test1', 'test2', 'test3']
            }
          })
        </script>
      </body>
    </html>
    

    如果只希望得到每个数组元素的值和下标

    语法

    v-for="(item, index) in items"
    

    示例

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>06_列表渲染</title>
        <!-- 1. 引入vue.js -->
        <script src="../node_modules/vue/dist/vue.js"></script>
      </head>
      <body>
        <!-- 2. 编写div元素 -->
        <div id="app">
          <ul>
            <li v-for="(item, index) in items">{{ index }}--{{ item }}</li>
          </ul>
        </div>
        <!-- 3. 编写vue实例 -->
        <script>
          const vm = new Vue({
            el: '#app',
            data: {
              items: ['test1', 'test2', 'test3']
            }
          })
        </script>
      </body>
    </html>
    

    2) 遍历对象

    • 只取值: v-for="value in obj"
    • 取键和值: v-for="(value, key) in obj"
    • 取键和值和索引: v-for="(value, key, index) in obj"

    遍历对象设置key值建议使用对象的属性值 (如 item) , 因为对象一般属性不会有相同

    3) key值使用

    1 key属性是vue里面的属性, 不会真实加载到dom中

    2 key值一定要是唯一值

    3 尽量避免使用数组的下标去绑定key值,一旦数组结构发生变化, 会导致对应key值发生变化
    比如在数组前面新增元素

    4 如果没有绑定key值, 则默认按照数组索引去设置key值

    注意不要忘记设置key 给每个元素唯一标识 !!!!!!!

    六. 计算属性与侦听器

    1 计算属性

    1) 什么是计算属性

    :::info 计算属性就是基于现有属性计算后的属性 :::

    2) 计算属性的作用

    计算属性用于对原始数据的再次加工

    3) 案例

    :::warning 需求
    实现如下效果
    反转字符串.gif :::

    使用表达式实现

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <script src="../node_modules/vue/dist/vue.js"></script>
      </head>
      <body>
        <!-- 需求: 实现字符串的反转
              eg. abc 转换成  cba 
        -->
        <div id="app">
          请输入一个字符串: <input type="text" v-model="msg" /> <br />
          <!-- 反转字符串的思路
            1. 取出字符串中的每个字符 msg.split('') 形成一个数组
            2. 反转数据. msg.split('').reverse() 形成一个反转数组
            3. 将反转数组拼接. msg.split('').reverse().join('')
          -->
          <!-- 不推荐!! 在模板中书写复杂的表达式 -->
          <h3>反转后的字符串: {{msg.split('').reverse().join('')}}</h3>
        </div>
    
        <script>
          const vm = new Vue({
            el: '#app',
            data: {
              msg: '',
            },
          })
        </script>
      </body>
    </html>
    

    使用方法实现

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <script src="../node_modules/vue/dist/vue.js"></script>
      </head>
      <body>
        <!-- 需求: 实现字符串的反转
              eg. abc 转换成  cba 
        -->
        <div id="app">
          请输入一个字符串: <input type="text" v-model="msg" /> <br />
          <!-- 反转字符串的思路
            1. 取出字符串中的每个字符 msg.split('') 形成一个数组
            2. 反转数据. msg.split('').reverse() 形成一个反转数组
            3. 将反转数组拼接. msg.split('').reverse().join('')
          -->
          <!-- 不推荐使用方法
            原因: 没有缓存, 每次调用方法, 代码会执行一次
          -->
          <h3>反转后的字符串: {{reverseMsg()}}</h3>
          <h3>反转后的字符串: {{reverseMsg()}}</h3>
          <h3>反转后的字符串: {{reverseMsg()}}</h3>
          <h3>反转后的字符串: {{reverseMsg()}}</h3>
          <h3>反转后的字符串: {{reverseMsg()}}</h3>
        </div>
    
        <script>
          const vm = new Vue({
            el: '#app',
            data: {
              msg: '',
            },
            methods: {
              reverseMsg() {
                console.log('反转函数被执行了...')
                // 返回 反转后的字符串
                return this.msg.split('').reverse().join('')
              },
            },
          })
        </script>
      </body>
    </html>
    

    使用计算属性实现

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <script src="../node_modules/vue/dist/vue.js"></script>
      </head>
      <body>
        <!-- 需求: 实现字符串的反转
              eg. abc 转换成  cba 
        -->
        <div id="app">
          请输入一个字符串: <input type="text" v-model="msg" /> <br />
          <!-- 反转字符串的思路
            1. 取出字符串中的每个字符 msg.split('') 形成一个数组
            2. 反转数据. msg.split('').reverse() 形成一个反转数组
            3. 将反转数组拼接. msg.split('').reverse().join('')
          -->
          <!-- 推荐使用 计算属性
            1. 有缓存的. 当计算属性生成一次后, 挂载到vm实例. 
              后继的访问, 直接使用vm实例上的属性
            2. 方便调试
          -->
          <h3>反转后的字符串: {{reverseMsg}}</h3>
          <h3>反转后的字符串: {{reverseMsg}}</h3>
          <h3>反转后的字符串: {{reverseMsg}}</h3>
          <h3>反转后的字符串: {{reverseMsg}}</h3>
          <h3>反转后的字符串: {{reverseMsg}}</h3>
        </div>
    
        <script>
          const vm = new Vue({
            el: '#app',
            data: {
              msg: '',
            },
            computed: {
              // 编写一个函数, 这个函数会被做为该计算属性的getter
              reverseMsg() {
                console.log('计算属性被执行了...')
                // 该函数的返回值, 做为访问计算属性的结果
                return this.msg.split('').reverse().join('')
              },
            },
          })
        </script>
      </body>
    </html>
    

    4) 特点

    :::info

    1. 有缓存
    2. 调试方便 :::

    2 侦听器

    1) 什么是侦听器

    :::info 可以通过watch配置项, 监听data中属性的改变 :::

    2) 语法

    watch: {
      // 监听data中的firstName属性
      firstName() {
       // 执行一系列的操作
      },
    },
    

    3) 特点

    在watch对应的回调函数中, 可以获取到新值旧值

    示例

    const vm = new Vue({
      el: '#app',
      data: {
        firstName: '',
        lastName: '',
      },
      // 使用watch这个配置项
      watch: {
        // 在watch对应的回调函数中, 可以得到新值和旧值
        // 对于简单数据类型, 可以获取新旧值
        // 对于引用数据类型, 不能获取旧值
        firstName(newValue, oldValue) {
          // 一对多: 监听某一个属性的改变, 做一系列的操作
          console.log('firstName改变了...')
          console.log('新的值:', newValue)
          console.log('旧的值:', oldValue)
        },
      },
    })
    

    3 区别

    1. 是否会在vm实例中挂载新属性?
      1. computed会
      2. watch不会
    2. 对应关系
      1. computed是多对一, 可以同时监听多个值改变, 最终计算得到一个新的属性
      2. watch是一对多, 主要监听一个属性的变化, 执行多种逻辑
    3. 能否获取新旧值?
      1. computed不能
      2. watch能

        4 过滤器

    1) 什么是过滤器

    :::info 过滤器是一个函数, 主要完成对数据的格式化 :::

    2) 语法

    {{ 表达式 | 过滤器1 | 过滤器2 ...}}
    

    将表达式的结果作为过滤器1参数, 再将过滤器1的结果作为过滤器2的参数, 依次类推

    3) 分类

    • 全局过滤器
    • 局部过滤器

    4) 示例

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>过滤器</title>
        <script src="../node_modules/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="app">金额: {{price | formatPrice}}</div>
    
        <script>
          const vm = new Vue({
            el: '#app',
            data: {
              price: 85,
            },
            filters: {
              formatPrice(value) {
                console.log(value)
    
                return '¥' + value.toFixed(2) + '元'
              },
            },
          })
        </script>
      </body>
    </html>