响应式前端框架

96 阅读10分钟

认识Vue

  • Vue是一套用于构建用户界面的渐进式JS框架
  • 渐进式框架: 表示我们可以在项目中一点点来引用和使用Vue, 而不是需要全部使用Vue来开发整个项目

Vue初体验

  • 案例: 计数器

  • 当前计数: {{counter}}

    + -

声明式和命令式

  • 命令式编程关注的是“how to do” 自己完成整个how的过程
  • 声明式编程关注的是“what to do” 由框架完成how的过程
  • 早期JS是命令式编程
    • 我们每完成一个操作,都需要通过JS代码编写一条代码,来给浏览器一个指令
  • Vue是声明式编程
    • 在createApp传入的对象中声明需要的内容:
      • data(){} 数据
      • methods:{} 方法
      • template:{} 模板

MVVM模型

  • MVC和MVVM都是一种软件的体系结构
    • MVC是Model-View-Controller
    • MVVM是Model-View-ViewModel
  • Vue虽然没有完全遵守MVVM的模型, 但是整个设计是受它的启发

属性

  • data属性
    • data属性是传入一个函数, 并且该函数需要返回一个对象
    • data中返回的对象会被Vue的响应式系统劫持, 之后对该对象的修改或者访问都会在劫持中被处理
      • 所以我们在template或者app中通过 {{counter}} 访问counter, 可以从对象中获取到数据
      • 所以我们修改counter值时, app中的 {{counter}} 也会发生改变
  • methods属性
    • methods属性是一个对象, 我们在这个对象中定义很多方法
      • 这些方法被绑定在 模板中
      • 在该方法中, 我们可以使用this来直接访问到data函数中返回的对象的属性
    • 两个问题
      • 问题一: 为什么不能使用箭头函数?
        • 我们在methods中要使用data返回对象中的数据, 那么这个this是必须有值的, 并且应该可以通过this获取到data返回对象中的数据
        • 这个this不能是window, 因为window无法获取到data返回对象中的数据
      • 问题二: this到底指向什么?
        • 事实上源码当中就是对methods中的所有函数进行了便利, 并且通过bind绑定了this

Mustache双大括号语法

  • 如果我们希望把数据显示到模板(template)中, 使用最多的语法是“Mustache”语法的文本插值
  • 正确的用法
    • 基本用法: {{message}}
    • JS表达式: {{counter * 2}}
    • 调用一个methods中的函数: {{reverse(message)}}
  • 错误的用法
    • 这是一个赋值语句,不是表达式: {{var name = “Hello”}}
    • 控制流的if语句不支持, =可以用三元运算符

v-once指令(了解)

  • v-once用于指定元素或组建只渲染一次
  • 当数据发生改变时, 元素或组件以及其所有的子元素将视为静态内容并且跳过

v-text指令(了解)

  • 用于更新元素的textContent(下面两个表达式等价)

  • {{msg}}

v-html

  • 默认情况下, 如果我们展示的内容本身是html的, 那么vue并不会对其进行特殊的解析, 如果我们希望这个内容被vue解析出来, 可以用v-html来展示

v-pre

  • v-pre用于跳过元素和它的子元素的编译过程, 显示原始的Mustache标签

v-cloak

  • 这个指令保持在元素上直到关联组件实例结束编译

v-bind (:)

  • v-bind用于绑定一个或多个属性值, 或者向另一个组件传递props值
  • 可以动态绑定: 图片的链接src、网站的链接href、动态绑定一些类、样式等
  • 用法

绑定class介绍

  • 在开发中, 有时候我们的元素class也是动态的, 比如

    • 当数据为某个状态时, 字体显示红色
    • 当数据另一个状态时, 字体显示黑色
  • 绑定class有两种方法

    • 对象语法

      • 普通的绑定方式:

        {{message}}
      • 对象绑定:

        {{message}}
      • 绑定对象:

        {{message}}
      • 从methods中获取:

        {{message}}
    • 数组语法

      • 直接传入一个数组:

        {{message}}
      • 数组中也可以使用三元运算符:

        {{message}}
      • 数组中也可以使用对象语法:

        {{message}}

绑定style介绍

  • 我们可以利用 v-bind:style 来绑定一些CSS内联样式

  • CSSproperty名可以用驼峰式或横线分隔来命名

  • 绑定class有两种方法

    • 对象语法

      • 基本使用: 传入一个对象, 并且对象内容都是确定的

        {{message}}
      • 变量数据: 传入一个对象, 值会来自于data

        {{message}}
      • 对象数据: 直接在data中定义好对象在这里使用

        {{message}}
    • 数组语法

      • :style的数组语法可以将多个样式对象应用到同一个元素上

        {{message}}

动态绑定属性

  • 前端我们无论绑定src、href、class、style, 属性名称都是固定的

  • 如果属性名称不是固定的, 我们可以使用: [属性名]=“值” 的格式来定义

  • 这样的绑定方式称为动态绑定属性

    {{message}}

绑定一个对象

  • 如果我们希望将一个对象的所有属性, 绑定到元素上的所有属性, 可以直接使用v-bind绑定一个对象

    {{message}}

v-on (@)

  • 使用v-on监听用户发生的事件, 比如点击、拖拽、键盘事件等等

  • 基本用法

    • @click=“btn” 点击
    • @mousemove=“abc” 移动
  • 一个元素绑定多个事件, 这时候可以传入一个对象(不能使用语法糖)

  • v-on参数传递

    • 当通过methods中定义方法, 以供@click调用时, 需要注意
    • 情况一: 如果该方法不需要额外参数, 那么方法后的()可以不添加
      • 如果方法本身中有一个参数, 那么会默认将原生事件event参数传递进去
    • 情况二: 如果需要同时传入某个参数, 同时需要event时, 可以通过$event传入事件
    • 理解成@click=“函数名”(调用函数), 这个函数在methods中定义, 不需要参数时可以省略(), 需要参数时不能省略
    • v-on的修饰符.stop、.self、.once、.left、.right...

条件渲染

  • v-if、v-else、v-else-if
    • 作为元素指令添加到元素上, 当条件为true时, 元素和内容才会被渲染出来
    • v-if=“判断语句” 其他同理
  • template元素
    • 因为v-if是一个指令, 所以必须将其添加到一个元素上
      • 但是如果我们希望切换的是多个元素呢
      • 此时我们渲染div, 但是我们并不希望div这种元素被渲染
      • 这个时候, 我们可以选择使用template
    • template元素可以当作不可见的包裹元素, 并且在v-if上使用, 但是最终template不会被渲染出来
    • 简单来说template元素可以使它的子元素全部统一拥有v-if, 并且自己不会成为一个盒子
  • v-show
    • v-show和v-if的用法区别
      • v-show不支持template
      • v-show不能和v-else一起使用
    • v-show和v-if的本质区别
      • v-show元素无论是否需要显示到浏览器上, 它的DOM实际都是存在的, 只是通过CSS的display属性来进行切换
      • v-if当条件为false时, 其对应的原生压根不会被渲染到DOM上
    • 如何选择
      • 如果只是需要在显示和隐藏之间频繁切换, v-show
      • 如果不会频繁切换, v-if

v-for

  • 通过v-for实现列表渲染

  • v-for基本使用

    • v-for的格式是“item in 数组”
      • 数组通常来自data或者prop, 也可以是其他方式
      • item自定义名字
    • 一个数组的遍历经常需要拿到数组的索引
      • 索引格式: “(item, index) in 数组”
      • 注意顺序: 数组元素项item是在前面的, 索引项index是在后面的
    • v-for=“item in 数组”、v-for=“(item, index) in 数组”, 拿到item和index后可以通过Mustache语法在元素内容中展示
  • v-for支持的类型

    • 支持遍历对象

      {{index}}-{{key}}-{{value}}
    • 支持遍历数字

      {{index}}
    • 也可以遍历其他可迭代对象

  • templqte元素

    • 和v-if一样, 如果想包裹元素又不想渲染

复杂data的处理方式

  • 在模板template中可以通过差值语法显示一些data中的数据
  • 但是在某些情况, 我们可能需要对数据进行一些转化后再显示、或者需要将多个数据结合起来进行显示
    • 模板中使用表达式只用于简单计算, 放入太多难以维护, 有可能大量重复代码
  • 方法
    • 将逻辑抽取到一个methods中, 放到methods的options中, 弊端是所有data使用过程中都会编程一个方法的调用
    • computed

计算属性computed

  • 案例:

    • 案例一: 我们有两个变量: firstName和lastName, 希望它们拼接之后在界面上显示
    • 案例二: 当score大于60显示及格、当score小于60显示不及格
    • 案例三: 一个变量message某些情况直接显示这段文字、某些情况需要对这段文字进行反转
  • 实现思路一: 模板语法 (多次执行不缓存、重复代码、不易维护)

  • 实现思路二: methods (显示结果都变成方法调用、没有缓存)

    methods: {
      getFullName() {
        return this.firstName + "" + this.lastName
      },
      getResult() {
        return this.score >= 60 ? "及格": "不及格"
      },
      getReverseMessage() {
        return this.message.split(" ").reverse().join(" ")
      }
    }
    
  • 实现思路三: (有缓存、使用不用加())

    computed: {
      getFullName() {
        return this.firstName + "" + this.lastName
      },
      getResult() {
        return this.score >= 60 ? "及格": "不及格"
      },
      getReverseMessage() {
        return this.message.split(" ").reverse().join(" ")
      }
    }
    
  • 计算属性的setter和getter

    • 计算属性大多数情况下, 只需要一个getter方法即可, 所以我们会将计算属性直接写成一个函数

    • 但是我们想设置计算属性的值呢 -> setter

    • computed: { fullName: { ​ get() { ​ return this.firstName + "" + this.lastNam ​ }, ​ set(value) { ​ const names = value.split(" ") ​ this.firstName = names[0] ​ this.lastname = names[1] ​ } } }

认识监听器watch

  • 开发中我们在data返回的对象中定义了数据, 这个数据通过插值语法等方式绑定到template中
  • 当数据变化时, template会自动进行更新来显示最新的数据
  • 但是某些情况下, 我们希望在代码逻辑中监听某个数据的变化, 这个时候就需要用监听器watch来完成了
  • Watch里面放函数, 函数名是data返回对象中的属性名
  • 函数默认有两个参数: newValue/oldValue
  • 深度监听info: {handler(newValue, oldValue){}},deep: true
  • 在深度监听的对象中增加 immediate:true , 第一次也会渲染
  • 监听的第二种方式, 在create(){}里放入this.$watch(“监听对象”, (newValue,oldValue)) => {},{deep:true}

书籍购物车案例

v-modol

  • 表单提交是开发中常见的功能, 也是和用户交互的重要手段

    • 比如用户在登录、注册时需要提交账号密码
    • 比如用户在检索、创建、更新信息时, 需要提交一些数据
  • 这些都要求我们可以在代码逻辑中获取到用户提交的数据, 我们通常会使用v-model指令来完成

    • v-model指令可以在表单input、textarea以及select元素上创建双向数据绑定

    • 它会根据控件类型自动选取正确的方法来更新元素

    • 尽管有些神奇, 但v-model本质上不过是语法糖, 它负责监听用户的输入事件来更新数据, 并在某种极端场景下进行一些特殊的处理

  • v-model原理

    • v-bind绑定value属性的值

    • v-on绑定input事件监听到函数中, 函数会获取最新的赋值到绑定的属性中

      <input :value="searchText" @input="searchText = $event.target.value" />

  • v-model绑定表单类型

    • textarea

      article当前的值是: {{article}}

    • checkbox

      • 单选框

        • v-model即为布尔值
        • 此时input的value属性并不影响v-model的值
        同意协议

        isAgree当前的值是: {{isAgree}}

      • 多选框

        • 当是多个复选框时, 因为可以选中多个, 所以对应的data中的属性是一个数组
        • 当选中某一个时, 就会将input的value添加到数组中
        篮球 足球 网球

        hobbies当前的值是: {{hobbies}}

    • radio

      gender当前的值是: {{gender}}

    • select

      • 单选

        • v-model绑定的是一个值
        • 当我们选中option中的一个时, 会将它对应的value赋值到fruit
        苹果 橘子 香蕉

        fruit当前的是: {{fruit}}

      • 多选

        • v-model绑定的是一个数组

        • 当选中多个值时, 就会将选中的option对应的value添加到数组fruit中

          苹果 橘子 香蕉

          fruit当前的是: {{fruit}}

  • v-model修饰符

    • v-model.lazy
      • 一般情况下, v-model在进行双向绑定时, 绑定的是input事件, 那么会在每次内容输入后就将最新的值和绑定的属性进行同步
      • 如果我们在v-model后跟上lazy修饰符, 那么会将绑定的事件切换为change事件, 只有在提交时(回车)才会出发
    • v-model.number
      • message总是string类型, 这个可以转换为number类型
    • v-model.trim
      • 自动过滤掉用户输入的首尾空格

认识组件化开发

  • 将页面拆分成一个个小的功能块, 每个功能块完成属于自己这部分独立的功能

  • 注册全局组件

    • 全局组件需要使用我们全局创建的app来注册组件
    • 通过component方法传入组件名称、组件对象即可注册一个全局组件
    • 之后, 我们可以在App组件的template中直接使用这个全局组件, 把组件当成元素使用
    • app.component(“组件名称”, {template: “组件名称”})
  • 注册局部组件

    • 通过components属性选项来进行注册

    • 比如之前的App组件中, 我们有data、computed、methods等选项了, 事实上还可以有一个components选项

    • 该components选项对应的是一个对象, 对象中的键值对是 组件名称: 组件对象

      {{message}}

Vue CLI 安装和使用

  • 安装cnpm: sudo npm install -g cnpm --registry=registry.npm.taobao.org
  • 全局安装: sudo npm install @vue/cli -g
  • 升级: npm update @vue/cli -g
  • 创建项目: vue create 项目名称
  • 项目依赖: vue install

组件的拆分

  • App
    • Header
    • Main
      • Banner
      • ProducList
    • Footer

组件的通信

  • 上面的嵌套逻辑

    • App是Header、Main、Footer的父组件
    • Main是Banner、ProductList的父组件
  • 组件通信的方式

    • 父组件传递给子组件: 通过props属性(在子组件中写)
    • 子组件传递给父组件: 通过$emit触发事件
  • props

    • props的数组用法

    • props的对象用法

      • type类型有哪些: String、Number、Boolean、Array、Object、Date、Function、Symbol
    • prop大小写细节: 浏览器会把所有大写解释为小写, 所以驼峰命名的prop需要使用等价的短横线分隔命名

插槽Slot

  • 定义
    • 插槽的使用过程其实是抽取共性、预留不同
    • 我们会将共同的元素, 内容依然在组件内进行封装
    • 同时我们会将不同的元素使用slot作为占位, 让外部决定到底显示什么样的元素
  • 如何使用slot
    • Vue中将元素作为承载分发内容的出口
    • 在封装组件中, 使用特殊的元素就可以为封装组件开启一个插槽
    • 该插槽插入什么内容取决于父组件如何使用
  • 父组件的元素用分隔线命名法, 子元素.vue文件名用对应的驼峰命名法, 在子组件的template中使用即可显示父组件中的对应元素
    • 父组件的slot元素可以写html元素和组件元素和其他内容
  • 具名插槽
    • 父组件:
    • 子组件:
    • 动态插槽名
      • 通过v-slot:[dynamicSlotName]方式绑定一个名称, 在data()中return一个name
    • v-slot的缩写: #
  • 独占默认插槽的缩写
    • 如果我们的插槽是默认插槽default, 那么在使用的时候 v-slot;default="slotProps" 可以简写为 v-slot="slotProps"
    • 并且如果我们的插槽只有默认插槽时, 组件的标签可以被当作插槽的模板来使用, 这样, 我们就可以将 v-slot 直接用在组件上
    • 但是, 如果我们有默插槽和具名插槽, 那么按照完整的template来编写
    • 只要出现多个插槽, 请始终为所有的插槽完整的基于