如何使用 Vue2 | 课程及文档学习记录

169 阅读2分钟

参考课程

尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通_哔哩哔哩_bilibili


1、概念简介

特点:

  1. 渐进式构建数据页面的JS框架

  2. 组件化 - .vue=.html+.css+.js

  3. 声明式

  4. 虚拟DOM+Diff算法


2、第一个vue

  1. 在 HTML 中准备一个容器 <div id="roo"></div>

    1. id/class 都可以
  2. 在 JS 中 new 一个 vue 实例,内部只传一个参数,即为配置对象 options,基本的参数如下:

    1. el:绑定一个容器

      • 原生DOM/./# 都可以绑
    2. data:保存 vue 实例中变化的数据,这些数据在 <template> 中使用方法如下:

      • HTML 中标签体用插值语法 {{}} 取值;
      • 标签属性用指令语法 v-bind/v-model 等取值;
      • 内部必须写 JS 表达式;
  3. Vue 实例和容器一一对应


3、配置对象 options


el

el: '',
  • 第一种写法:el:'#root'

  • 第二种写法:vue.$mount('#root')


data

data() {
    return {
        // content 
    }
}
  • 对象式写法:数据很多,采用对象的形式;

  • 函数式写法:后续使用组件,必须用函数的形式,返回对象

    • 组件里定义的变量,如果不用函数式写法会污染全局变量,这是闭包原理

      一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝


methods

methods: {
    <方法名>(参数) {
        // content 
    }
}

computed

计算属性也是属性,里面的属性是对象的形式/函数

computed: {
    <计算属性>: {
        // 初次读取时调用
        // 对应数据发生变化时调用
        get(){ // content },
        // 涉及到该属性的修改时调用,并且要更新依赖的相关属性,几乎不太用
        set(){}
    }
}

原理:底层用的就是 Object.defineproperty

定义:属性原本不存在,需要通过已有属性计算得来

优势:能够缓存,复用计算结果,效率高,调试方便

// 简写-默认为 getter,此时只考虑读取,不考虑修改
// fullName:function() {}
computed: {
    <计算属性>() {
        // content
    }
}

watch

watch: {
  data/computed 中的属性:{
    // 配置对象
    immediate: false/true,
    deep: false/true,
    handler(){ // content },
  }
}
  • Immediate - 页面初始化时立即执行一次

    • 默认值是 false,在第一次进入页面时,如果没有发生数据变化,watch并不会立即监听
  • handler(新值, 旧值)

    • 值在更改时被调用,不传参时可以调用 event
  • Deep - 深度监视

    • 默认值是 false,不监测对象内部数据的改变
// 简写-当只配置handler,而不需要其他配置项的时候
// isHot:{ handler(){} }
watch: {
    <监视属性>(newVal, oldVal) {
        // content
    }
}

Watch 和 computed 的区别:

  1. Computed 能完成的功能,watch 都能完成
  2. Watch 能完成的功能,computed 不一定能完成,例如异步操作

两个重要的原则:

  1. 所有被 vue 管理的函数,最好写成普通函数,这样 this 指向才是 vm 或者组件实例对象
  2. 所有不被 vue 管理的函数(如定时器的回调函数,ajax的回调函数等),最好写成箭头函数,这样 this 指向才是 vm 或者组件实例对象

!当两者都能实现功能时,优先使用 computed


Filters

filters: {
    <过滤器函数>() {
        // 内容
    }
}

管道 | 左侧的 time 作为参数传入右侧的过滤器 timeFormater

然后其返回值直接替代整个 {{}} 内部内容,在页面中显示

作用域

  1. 局部过滤器

    1. new Vue{filters:{}}
      
  2. 全局过滤器

    1. Vue.filter('<过滤器函数>', function() {})
      

Directives

// html - 使用自定义指令 v-big
<div v-big="n"></div>

// js - 配置自定义指令 big
new Vue({
    directives:{
        big(element, binding){
            // content
        }
    }
})

自定义指令可以收到两个参数:

  1. Element : 该指令所在的标签(html 元素)

    1. element.innerText
    2. element.value
  2. Binding:将 html 标签与 自定义指令绑定

    1. binding.value

Directives 里的自定义指令什么时候会被调用?

  1. 指令与元素成功绑定时(初始
  2. 指令所在的模板被重新解析时

进阶写法

// 特定的函数在特定的时间点调用
directives: {
    fbind: {
        // 指令与元素成功绑定时(初始
        bind(element, binding){}
        // 指令所在元素被插入页面时
        inserted(element, binding){}
        // 指令所在的模板被重新解析时
        update(element, binding){}
    }
}

作用域

  1. 局部过滤器

    1. new Vue{directives:{}}
      
  2. 全局过滤器

    1. // 复杂写法,自定义指令的内容是配置对象
      Vue.directive('<自定义指令>',  {})
      // 简单写法,自定义指令的内容是函数
      Vue.directive('<自定义指令>',  function(element, binding){})
      

重点

  • 自定义指令里的 this 是 window

Template

相当于把 html 里 template 的内容放在 vue 实例里进行配置

!注意内容物只能有一个根节点

// 第一种方式  没有 :x="n" 了
<div id="root" :x="n">

</div>

new Vue({
    el:'#root',
    template:`
    <div>
        <h1>{{msg}}</h1>
        <h2>kk</h2>
    </div>
    `
})
// 第二种方式  :x="1"
<div id="root" :x="n">
    <h1>{{msg}}</h1>
    <h2>kk</h2>
</div>

new Vue({
    el:'#root',
})

两种方式的差别在于

  • 第一种方式中 div 被 template 完全替代
  • 第二种方式中 div 作为根节点被渲染解析,:x="1"

Components

在 vue.extend 中需要注册组件时使用


4、基础语法 - 指令

语法:在F12页面中调试,需要用DOM去操作


V-bind

<h1 :id="msg">hello</h1>
  1. HTML 标签中绑定标签属性(区别于 CSS 属性)
  2. 属性值在 JS 中 vue 实例的 data 中

两种使用方法

  1. 标签<>里头,用 :
  2. 标签外头,用 {{}}

V-model

<input v-model="请输入">
  1. 表格数据(用 vue 插件调试)
  2. 默认与 表单 -> 输入标签 -> value值 进行双向绑定
  3. v-model:value 可简写为 v-model

事件修饰符

  • .number

    • 原生的 type=number ,可以控制“只输入数字”,但是无法控制 value 值的数据类型
    • Vue 提供的 v-model.number,不能控制输入框的输入,但是在收集数据时,会自动做类型转换,控制该 value 值的数据类型
    • // 正确的使用方法
      <input type="number" v-model.number="age">
      
  • .lazy

    • v-model.lazy,能够在失去焦点的瞬间收集数据,而不是实时收集
  • .trim

    • v-model.trim,能够在收集数据时,将输入框内的前后空格自动剪除

V-on

<button @click="function()"></button>
<button @click="function"></button>
  1. HTML 标签中绑定事件,比如 click 事件

DOM 里的 event 对象类似于 function 里的 arguments

  1. 函数后头的 () 加不加都行

    1. 用插值语法 {{}} 去调用函数,约等于把函数返回值插入模板
    2. 不管函数需不需要传参,都得带 ()。否则会输出整个函数体,而不是执行函数
    3. <span>{{function()}}</span>
      

事件修饰符

事件修饰符能连着写,如 @click.stop.prevent="function",有先后顺序

键盘事件

  • @keyup 事件
  • @keydown 事件
<input @keyup.enter="function">
<input @keydown.delete="function">
  • 自定义按键别名,注意用 kebab-case 方式命名
vue.config.keyCodes.自定义键名 = 键码

按键修饰符也能连着写,如 @keyup.ctrl.y="function"


V-show

让 DOM 元素不显示的方式有:
display:none
visibility:hidden
opacity:0
  • V-show 的底层实现就是调整 display 的取值,即使不显示,结构也是在的
  • template 标签上不能用 v-show 控制显示与否

V-if

V-if 用于条件渲染时,不需要显示的标签会将其结构从dom树上抹除

V-show 和 v-if

如果标签需要切换的频率较高,建议使用 v-show

  • 节点存在,只是动态控制是否显示

如果标签需要切换的频率不高,可以使用 v-if

  • v-if 需要在 dom 树中进行增删

标签只能配合v-if 不能配合v-show使用


v-else-if

  1. 几个 if if if 并列

效率低,每个 if 都需要进行条件判断

  1. 用 if else-if else-if 并列

效率高,作为一组判断

  • 特殊情况-1

当 v-if 与 v-else-if 的判断条件写得相同时,会跳过 v-else-if 判断条件的执行

第二条的 n===1 会被跳过不执行

  • 特殊情况-2

当一组 v-if v-else-if 没有一个判断条件为 true 时,会直接返回最后一条 v-else-if / v-else 的内容

最后一条判断的 n===4 没有任何作用


V-for

1、遍历
// 语法
<li v-for="item in/of arr" :key="item.id"></li>

// 遍历数组
// 第一个值是 item 数据
// 第二个值是 index 索引
<li v-for="(item, index) in arr"></li>

// 遍历对象
// 第一个参数是 value
// 第二个参数是 key
<li v-for="(value, key) of car" :key="key"></li>

// 遍历字符串
// 第一个参数是 字符 char
// 第二个参数是 对应的索引 index
<li v-for="(char, index) of str" :key="index"></li>

2、key值
  1. 可以绑定 item 对象中的 id 字段
  2. 可以绑定 index 值
  3. 也可以不绑定

场景:从数组头部插入数据

// HTML
<button @click.once="add">添加一个老刘</button>
<ul>
    <li v-for="(p, index) of persons" :key="index">
        {{p.name}}-{{p.age}}
        <input type="text" value="">
    </li>
</ul>

// JS
const vm = new Vue({
      el: '#root',
      data() {
        return {
          persons:[
            { id:'1', name:'一', age:11 },
            { id:'2', name:'二', age:22 },
            { id:'3', name:'三', age:33 },
          ]
        }
      },
      methods: {
        add() {
          const p = { id:'4', name:'四', age:44 }
          this.persons.unshift(p)
        }
      }
    })

问题:

  • 效率问题
  • 数据显示问题


3、Diff 对比算法


4、key 的内部原理
  1. 虚拟 DOM 中 key 的作用:

Key 是虚拟 DOM 对象的标识,当数据发生变化时,vue会根据【新数据】生成【新的虚拟DOM】

随后 vue 进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

  1. 对比规则

    1. 旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key:

      1. 若虚拟 DOM 中内容没变,直接使用之前的真实 DOM
      2. 若虚拟 DOM 中内容变了,则生成新的真实 DOM,随后替换掉页面中之前的真实 DOM
    2. 旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key

      1. 创建新的真实 DOM,随后渲染到页面
  2. 用 index 作为 key 可能会引发的问题

    1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实 DOM 更新(界面效果没问题,但效率低)
    2. 如果结构中还包含输入类的 DOM:会产生错误 DOM 更新(界面有问题)
  3. 开发中如何选择 key

    1. 最好使用每条数据的唯一标识作为 key
    2. 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用 index 作为 key 是没有问题的

5、列表过滤

用 JS 中的 filter 和 indexOf 方法


内置指令

v-text
  1. 作用:向其所在的节点中渲染文本内容
  2. 与插值语法的区别:v-text 会替换掉节点中的内容,{{}}不会
V-html
  1. 作用:向指定节点中渲染包含 html 结构的内容

  2. 与插值语法的区别:

    1. v-html 会替换掉节点中的内容,{{}}不会
    2. v-html 可以识别 html 结构
  3. 严重注意:v-html 有安全性问题!!!

    1. 在网站上动态渲染任意 HTML 是非常危险的,容易导致 XSS 攻击
    2. 一定要在可信的内容上使用 v-html,永远不要用在用户提交的内容上
V-cloak
  1. 该指令没有值,直接用
  2. 本质:是一个特殊属性,Vue 实例创建完毕并接管容器后,会删掉 v-cloak 属性
  3. 作用:使用 css 配合 v-cloak 解决网速慢时页面展示出 {{}} 的问题
// css
[cloak]{
    display: none;
}

// html
<div v-cloak>{{msg}}</div>

网络加载时,v-cloak 属性存在

当网络加载完毕,vue实例挂载到容器上后,v-cloak 属性被去掉了

V-once
  1. 特点:v-once 所在节点在初次动态渲染后,就视为静态内容了
  2. 以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能
V-pre
  1. 作用:跳过所在节点的编译过程
  2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译

自定义指令

  • 使用 vue 中的配置项 directives
  • 涉及自定义指令名很长时,需要 小写+杠
// html
<div v-big-number></div>

//js 复杂写法
new Vue({
    directives:{
        'big-number': function(element, binding){
            // content
        }
    }
})

//js 简写
new Vue({
    directives:{
        'big-number'(element, binding){
            // content
        }
    }
})

5、进阶内容

vue对象


vue原型


MVVM模型

  • model 中 data 里所有的属性,最后都会通过数据代理,出现在 vm 实例中

  • 本质上说:模板 view 里可以调用所有 vm 身上的所有属性及其原型上所有的属性


Object.defineProperty

  1. 数据属性
  • [[Configurable]]:删除
  • [[Enumerable]]:枚举
  • [[Writable]]:修改
  • [[Value]]:值
  1. 访问器属性
  • [[Configurable]]:删除

  • [[Enumerable]]:枚举

  • [[Get]]:读

  • [[Set]]:写


数据代理

通过一个对象代理对另一个对象中属性的操作 读/写

本质:通过 Object.defineProperty 中的 getter 和 setter 来实现

Vue 中的 data 数据做了数据代理,放到 vm 实例中的 _data 里

Vue 中的 methods 方法没有用数据代理,直接赋给了 vm 实例中

给 methods 加数据代理没有任何意义,方法只是用于调用,而不是被更改的

简而言之:数据代理就是 vm实例上有属性了,但是属性值不给你,是动态更新的


数据劫持

数据代理图片中,_data里有数据劫持,也就是说,Vm 在拿到 data 中的数据时,做了两部分工作:

  1. 加工 data

    1. 目的:vue 的响应式(当页面改变时,数据也发生改变)
    2. 方式:为每个数据加一个 getter 和 setter
  2. vm._data = data

举例:

*name 改变时,触发 setter 的调用,重新解析模板,生成新的虚拟 DOM ,新旧 DOM 对比,更新页面

具体做法:

这是简单的 data 数据劫持逻辑,vue 比我们考虑的更多:

  1. 既可以通过 vm._data.属性 操作数据,也可以通过 vm.属性 操作数据,因为有数据代理
  2. 当 data 中存在对象多层嵌套的数据时, vue 可以为每层数据进行数据劫持,加了递归

数据监视更新的原理

  • vue 如何监测对象里数据改变:

    • 为每个数据添加 getter 和 setter

      • 更改对象内容:可以直接用=修改
      • 添加新的响应式属性:可以用vue.set
  • vue 如何监测数组里数据改变:

    • Vue 并不会为数组里的每个数据添加 getter/setter

    • 也就是说,当通过索引值改变数组内容时,不会触发页面的响应式更新

      • 更改对象内容:不可以用=修改,需要用7种改变数组的方法修改
      • 添加新的响应式值:得用7种改变数组的方法修改


数据监测

对象更新时的问题

两种 data 中对象更新的方法:

  1. 用.的方式取数组对象中的属性,用=赋值

  1. 直接用=对数组中的对象进行赋值

无法正常显示的原因:

后续通过=为vm._data中的数据添加属性和属性值时,vue不会为这种属性配备 getter/setter,因此该属性不是响应式数据,读取时不调用 setter,自然不会引起模板解析,也就不会显示到页面上了

解决办法: vue.set

// 第一种:调用 vue 上的 set 方法
vue.set(<对象>,<属性>,<属性值>)
vue.set(vm._data.student,'sex','男') // 例子
vue.set(vm.student,'sex','男') // 例子,用了数据代理

// 第二种:调用 vm 实例上的 $set 方法
vm.$set(<对象>,<属性>,<属性值>)
this.$set() // 在 vm 实例中

局限性

*vue.set 只能给 data 里的某个对象增加新的属性


官方文档


数组更新时的问题

  • 当通过索引值改变数组内容时,不会触发页面的响应式更新

解决办法1:调用对应的数组方法

  • Vue 能够进行响应式监测的数组方法有7个:

    • Push
    • Pop
    • Shift
    • Unshift
    • Splice
    • Sort
    • Reverse

*这些数组方法都是能够影响原数组的方法

在调用这些方法时,实际上调用的不是 array 原型对象上的方法了,而是 vue 包装过的方法

解决办法2:vue.set

 vue.set(<数组><索引值><更改后的值>)

Vue 监测数据的原理

  1. Vue 会监测 data 中所有层次的数据

  2. 如何监测对象中的数据?

    1. 通过 setter 实现监视,且要在 new vue 时就传入要监测的数据

      1. 对象中后追加的属性,vue 默认不做响应式处理

      2. 如需给后添加的属性做响应式,请使用如下 API:

        1. vue.set() 或 vm.$set()
  3. 如何监测数组中的数据?

    1. 通过包裹数组更新元素的方法实现,本质就是做了两件事:

      1. 调用原生对应的方法对数组进行更新
      2. 重新解析模板,进而更新页面
  4. 在 vue 修改数组中某个元素,一定要用到如下方法:

    1. 使用 api:push/pop/shift/unshift/splice/sort/reverse
    2. vue.set() 或 vm.$set()
  5. 特别注意:vue.set() 和 vm.$set() 不能给 vm 或 vm的根数据对象 添加属性


生命周期

如果在 methods 里面开定时器,并且在内部改变 data 中的值,那么会导致定时器个数成指数倍增长的情况。

Methods 里面只写回调函数

生命周期里的 this 都是 vue 实例

created

  • 数据监测和数据代理都开了
  • 可以访问 data 和 methods
  • 但页面中还没有显示解析好的内容,vue 没有解析模板,没有生成虚拟 dom

mounted

  • 虚拟 dom 转为真实 dom 插入页面了
  • 页面中呈现已经编译好的 dom
  • 在此时可以 开启定时器、订阅消息、绑定自定义事件等初始化操作

Updated

  • 数据是新的,页面也是新的,完成 model->view 的更新
  • 涉及新旧 DOM 元素对比

beforeDestroyed

  • 所有配置项都处于可用状态,但是所有对数据的修改不会触发 更新/模板解析 了,所以不建议使用
  • 在此时可以 关闭定时器、取消订阅、解绑自定义事件等收尾操作


6、业务需要

绑定样式

css
// 
:class="xxx"
v-bind:class="xxx"
  1. 不确定是否使用样式时,动态切换样式,vue 绑定 boolean 数据实现
  • 适用于:样式类名不确定,需要动态指定
<div :class="mood" @click="changeMood"></div>
// methods
changeMood() {
    const arr = ['happy', 'sad', 'normal']
    this.mood = arr[Math.floor(Math.random() * 3)]
}
  1. 不确定用几个样式时,动态切换样式的个数,vue 绑定数组实现
  • 适用于要绑定的样式个数不确定,名字也不确定
<div :class="arr"></div>
// data
arr:['at1', 'at2', 'at3']
  1. 样式个数和类名确定,但不确定到底用不用时,vue绑定对象实现
<div :class="classObj"></div>
// data
classObj: {
    at1: false,
    at2: true
}
  • 在标签内写js表达式时,带引号与不带引号意义不同
  1. 带引号:表示引号内是值
  2. 不带引号:表示该参数是一个变量,需要找其对应的值
<div :class="['at1', 'at2', 'at3']"></div>
// data
<div :class="[at1, at2, at3]"></div>

style
  • 当需要绑定行内样式时,用对象的形式
  • 1.css 里的 font-size 对应 js 里的 fontSize
<div :style="{fontSize: fsize+'px'}"></div>
// data
fsize: 40
  • 2.对象的形式
<div :style="styleObj"></div>
// data
styleObj: {
    fontSize: '40px'
}
  • 3.数据的写法
<div :style="[styleObj, styleObj2]"></div>
// data
styleObj: {
    fontSize: '40px',
    color: 'red'
}
styleObj2: {
    backgroundColor: 'orange'
}

过滤器

显示格式化后的时间

  • 推荐开源库 moment.js / day.js

代码折叠

使用 region 包裹需要折叠的代码,这样无论发生什么,该段代码都可以正常折叠

#region
...
#endregion

0、注意

  • 一旦 data 里数据改了,vue 会重新解析模板,和该数据相关的模板、方法都会更新
  • 对象里的key是字符串,但是 new vue 实例的时候,经常把 key 简写,还经常把函数简写
  • Vue 在配置对象里写的所有东西都可以在 vm.$options 里获取得到
  • Form 里的标签取值,可以用 v-bind/v-model 绑在 <> 标签里
  • Form 外的标签取值,标签属性仍然可以用 v-bind/v-model 绑在<>标签里,非标签属性则需要用 {{}} 取值
  • 读取 data 中某个对象不存在的属性时,返回 undefine,vue 不会显示 undefine,因此最终页面上为空