目录
(一) Vue.js 概述
Vue: 是一套用于构建用户界面的渐进式框架, 与其它大型框架不同的是, Vue 被设计为可以自底向上逐层应用. 渐进式意味着你可以将Vue作为你应用的一部分嵌入其中, 带来更丰富的交互体验. Vue拥有区别其他前端框架的专属特点:
- 解耦视图和数据
- 可复用的组件
- 前端路由技术 (Vue Router)
- 状态管理 (Vuex)
- 虚拟DOM
(二) Vue.js 安装
- 方式一: 直接CDN引入
- 开发环境版本, 包含了有帮助的命令行警告
<script src=“https://cdn.jsdelivr.net/npm/vue/dist/vue.js”> - 生产环境版本, 优化尺寸和速度
<script src=“https://cdn.jsdelivr.net/npm/vue@2.6.11”>
- 开发环境版本, 包含了有帮助的命令行警告
- 方式二: 下载和引入
- 开发环境: https://cn.vuejs.org/js/vue.js
- 生产环境: https://cn.vuejs.org/js/vue.min.js
- 方式三: NPM安装
$ npm install vue
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{message}}</div>
</div>
<script src="../js/vue.js"></script>
<script>
// 命令式编程(传统js做法, 显示messige) 向 声明式编程 的转变
const app = new Vue({
el: '#app', // 用于挂载要管理的元素(选择器)
data() { // 定义变量, 存储数据
return {
message: 'Hellow, Vue !!'
}
}
})
</script>
</body>
</html>
(三) Vue 中的 MVVM
MVVM(Model-View-ViewModel): 它对MVC(Model-View-Controller)的改进版, MVVM有助于将图形用户界面的开发与业务逻辑或后端逻辑(数据模型)的开发分离开来,这是通过置标语言或GUI代码实现的。MVVM的视图模型是一个值转换器, 这意味着视图模型负责从模型中暴露(转换)数据对象,以便轻松管理和呈现对象。在这方面,视图模型比视图做得更多,并且处理大部分视图的显示逻辑。 视图模型可以实现中介者模式,组织对视图所支持的用例集的后端逻辑的访问。------摘自维基百科
Vue中的MVVM 图解
- View层: 在前端开发中, 通常指的是DOM层. 主要作用是给用户展示各种信息
- Model层: 数据层中的数据可以是固定数据, 也可以是从服务器请求的数据
- ViewMdoel层: 视图模型层是 View 与 Model 沟通的桥梁, 一方面它实现了Data Binding 数据绑定, 将Model的改变实时响应到View中; 另一方面它实现了DOM Listener DOM监听, 可以监听到View(DOM)中发生的事件, 并在需要的情况先改变对应Model.
(四) Vue 中的Options选项
- el:
类型:string | Element
提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。 - template:
类型: string
一个字符串模板作为 Vue 实例的标识使用。模板将会替换挂载的元素, template使用优先级比较高 - data:
类型:Object | Function
Vue 实例对应的数据对象, 组件的定义只接受 function函数 (data 如果还是是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!) - methods:
类型: { string : Function }
methods 将被混入到 Vue 实例中。可以直接通过实例访问这些方法,或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例 - 生命周期钩子函数(8个)
- 计算属性
…
Vue 中的Options选项还有非常多, 在这就不一一列举了, 可以参考 Vue 中文官方文档
(五) Vue 中的生命周期函数
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如: 每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁,这就是一个组件所谓的生命周期. 在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
图解vue生命周期
这个8个生命周期钩子的函数, 最终会被Vue实例内部的callHook()函数调用
(六) 插值操作-mustache语法
Mustache: 是一个logic-less(轻逻辑)模板解析引擎,它是为了使用户界面与业务数据(内容)分离而产生的.
{{ keyName }} Mustache 的标示符,双括号里的 keyName 表示键名,这句的作用是直接输出与键名匹配的键值, 也可以写简单的表达式语法
<h3>{{message}}</h3>
<h3>{{flag}} {{!flag}}</h3>
<h3>{{num1 + num2}}</h3>
...
data() {
return {
message: 'Hellow, Vue!!',
num1: 10,
num2: 20,
flag: true
}
}
(七) Vue 指令
1. v-once
v-once: 指令表示元素和组件只会渲染一次, 不会随着数据的改变而改变. 且 该指令不需要跟任何表达式
<h3>{{message}}</h3>
<h3 v-once>{{message}}</h3>
2. v-html
v-html: 指令会将String类型的html解析并进行渲染. v-html=“String类型的HTML”
<h3>{{url}}</h3>
<h3 v-html="url"></h3>
data() {
return {
url: '<a href="https://www.baidu.com">百度一下</a>'
}
}
3. v-pre
v-pre: 指令用于跳过这个元素和它子元素的编译过程, 显示原本的Mustache语法
<h3>{{message}}</h3>
<h3 v-pre>{{message}}</h3>
data() {
return {
message: 'Hellow, Vue !!',
}
}
3. v-cloak
v-cloak: 隐藏还未编译的Mustache标签, 可能因为网络延迟等其他问题导致Vue实例延迟创建或内部出错, 此时, 不应该展示还未编译的Mustache标签.
原理: v-cloak 是个自定义属性绑定到挂彩目标上, 在Vue实例创建成功后移除 v-cloak属性. 从而达到隐藏效果
<h3 v-cloak>{{message}}</h3>
<style>
[v-cloak] {
display: none;
}
</style>
setTimeout(() => {
const app = new Vue({
el: '#app',
data() {
return {
message: 'Hellow, Vue !!',
}
}
})
}, 1000);
4. v-bind
v-bind: 主要用于属性动态绑定, v-bind:属性名 = “变量”, 可以缩写为 :属性名 = “变量”
<a :href="aHref">百度一下</a>
data() {
return {
aHref: 'https://www.baidu.com',
}
}
v-bind动态绑定class(对象语法)
<style>
.classB {
color: red;
}
</style>
<div id="app">
<!-- class 和 :class 中的对象, 最终会合并在一起: calss = 'classA, classB'-->
<!-- 对象语法: {类名1: boolean1, 类名2: boolean2, ...} boolean为true的对象才会被引用 -->
<h3 class="classA" :class="{classB: isClassB, classC: isClassC}">{{message}}</h3>
<button @click="changeClass">按钮</button>
</div>
const app = new Vue({
el: '#app',
data() {
return {
message: 'Hellow, Vue !!',
isClassB: true,
isClassC: false
}
},
methods: {
changeClass () {
this.isClassB = !this.isClassB;
}
}
})
v-bind动态绑定style(对象语法)
<!-- :style="{key(属性名): vlaue(属性值变量 ), ...}" -->
<h3 :style="{font-szie: '50px'}">{{message}}</h3>
<h3 :style="{fontSzie: size}">{{message}}</h3>
data() {
return {
size: 50px
}
}
5. v-on
v-on: 指令用于绑定事件监听器(原生 DOM 事件)。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句. 如: v-on:click、v-on:blur、v-on:keydown… 也可以缩写为: @click、@blur、@keydown…
- 方法参数: 默认传递浏览器event事件
<button @click="handleClick">按钮</button>
methods: {
handleClick(event) {
console.log("handleClick click...", event)
}
}
同时需要event对象 和 其他参数对象
<button @click="handleClick('张三', $evnet)">按钮</button>
methods: {
handleClick(name, event) {
console.log("handleClick click...", name, event)
}
}
- 修饰符
- .stop: 相当于调用 event.stopPropagation(), 阻止事件的冒泡方法,不让事件向document上蔓延,但是默认事件任然会执行
- .prevent: 相当于调用 event.preventDefault(), 取消事件的默认动作, 如 取消form表单中 type=‘submit’ 的按钮, 自动提交到action事件
- .{keyCode(键编码) | keyAlias(键别名)}:只当事件是从特定键触发时才触发回调, 如:
<input @keyup.enter=“handleKeyup” /> 只监听Enter键按下事件 - .native: 监听组件根元素的原生事件, 注: 组件必须通过.native修饰符才可以监听DOM事件
- .once: 只触发一次回调
5. v-if、v-else-if、v-else
根据表达式的值的 truthiness 来有条件地渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。如果元素是 <template>,将提出它的内容作为条件块。
推荐使用computed 来替换较为复杂的v-if、v-else-if、v-else 的指令
6. v-show
v-show: 根据表达式之真假值,切换元素的 display CSS property.
v-show与v-if 非常相似, 是控制一个元素是否显示, 但两者差异在于:
- v-if: 当条件为false时, 不会渲染, 元素不存在于浏览器中的DOM
- v-show: 当条件为false时, 只会给元素添加一个行内样式: display: none
<h3 v-if="false" id="if">Hello, Vue!!</h3>
<h3 v-show="false" id="show">Hello, Vue!!</h3>
7. v-for
v-for: 指令用于将数组或对象遍历循环显示渲染
- 遍历普通数组
<!-- item 代表 当先遍历项 --> <li v-for="item in arr">{{item}}</li> <!-- index 代表 当先遍历项的下标索引 --> <li v-for="(item, index) in arr">{{index}} {{item}}</li>
- 遍历对象
<!-- value 代表 当先遍历对象的value --> <li v-for="value in obj">{{value}}</li> <!-- key, value 分别代表 当先遍历对象的key, value --> <li v-for="(key, value) in obj">{{key}} {{value}}</li> ```
Vue官方推荐在使用v-for时, 给对应的元素或组件添加一个key属性, 是为了高效的更新虚拟DOM
Vue在重新渲染虚拟DOM时, 会采用就地复用策略: 会对已有的标签进行复用, 不会将整个DOM树删除然后重新创建, 如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素, 如图:
添加一个key唯一标识属性, vue(diff算法)可以正确的匹配识别此节点, 然后找到正确的位置插入到新的结点
8. v-model
v-model: 指令实现表单元素和数据的双向绑定
单向绑定: 把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新. 在Vue中可以使用mustache语法{{}} 或 v-bind 实现
双向绑定: 在单向绑定的基础上, 实现用户更新了View, Model中的数据也被自动更新. 在Vue中使用v-model指令实现
<input type="text" v-model="message" />
<h3>{{message}}</h3>
v-model原理: 是一个语法糖, 它本质包含两个操作
- v-bind:value 指令绑定value属性
- v-on:input 指令绑定<input>输入框的 input的事件
<input type="text" :value="message" @input="message = $event.target.value"/>
<h3>{{message}}</h3>
v-model 修饰符:
- .lazy: v-model 默认是在input事件中同步输入框的数据的, lazy修饰符可以让数据在失去焦点或者回车才会更新
- .number: 修饰符会将输入框输入的字符串类型转为数字类型
- .trim: 修饰符会将输入框首尾空格过滤
(八) 计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护
<div id="example">
{{ message.split('').reverse().join('') }} <!-- Hello-->
</div>
Computed计算属性: 可以对data中的属性进行逻辑计算, 且计算的值将被缓存. 只有当其依赖的属性的值发生变化时,计算属性会重新计算,反之,则使用缓存中的属性值
<h3>{{fullName}}</h3> <!-- Lin Ken-->
data () {
return {
firstName: 'Lin',
lastName: 'Ken'
}
},
computed: {
fullName: function() {
return this.firstName + ' ' + this.lastName
}
}
Computed计算属性的get、set: 计算属性默认只有 get方法(只读属性), 上面的reversedMessage计算属性是简写方式, 只有提供了get方法. 为此, 可以提供set方法
data () {
return {
firstName: 'Lin',
lastName: 'Ken'
}
},
computed: {
fullName: {
set: function(newValue ) {
// newValue : fullName set 的值
console.log('fullName.set 方法被调用: ', newValue)
// 赋值给data对象中的 firstName 和 lastName
const nameArr = newValue.split(' ')
this.firstName = nameArr[0]
this.lastName = nameArr[1]
},
get: function() {
return this.firstName + ' ' + this.lastName
}
}
}
计算属性缓存 vs 方法:
<div>{{getFullName()}}, {{getFullName()}}, {{getFullName()}}, {{getFullName()}}</div>
<div>{{fullName}}, {{fullName}}, {{fullName}}, {{fullName}} </div>
data() {
return {
firstName: 'Lin',
lastName: 'Ken'
}
},
computed: {
fullName: function () {
console.log('fullName running...')
return this.firstName + ' ' + this.lastName
}
},
methods: {
getFullName: function() {
console.log('getFullName running...')
return this.firstName + ' ' + this.lastName
}
}
计算属性缓存和方法, 这两种方式的最终结果是完全相同的. 但方法每次调用都会执行, 而计算属性是基于它们的响应式依赖进行缓存的, 只在相关响应式依赖发生改变时它们才会重新求值
(九) 浅谈响应式原理
响应式是 Vue最独特的特性之一, 它可以监听Model数据层的数据变更操作, 并实时响应到对应的View视图中. 极大的将我们从繁琐的DOM操作中解放出来
Vue响应式原理离不开关键的两个点:
- 如何监听data数据改变操作, 并在监听到这一操作中做了什么了?
- 如何知道哪些组件需要动态响应更新? 即 如何记录哪些组件使用了data数据? (发布订阅者模式)
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。
Object.defineProperty(obj, ‘prop’, {descriptor}): 方法会直接在一个对象上定义一个新属性key,或者修改一个对象的现有属性,并返回此对象
- obj: 要定义或修改属性的对象
- prop: 要定义或修改属性的名称, 字符串类型
- descriptor: 要定义或修改属性的描述符
{ get() { return value }, // 当访问该属性时,会调用此函数。该函数的返回值会被用作属性的值 set(newValue) {} // 当属性值被修改时,会调用此函数. }
get() 中实现发布订阅者模式: 当访问该属性时, 会调用此函数. Vue 在此方法中监听哪些组件使用了data数据. Vue对这些使用了data数据的组件分别创建一个 Watcher 观察者对象, 这些Watcher对象 订阅 Dep发布者对象(用来收集Watcher 、删除Watcher 和 向Watcher发送消息)
set() 中通知订阅者更新数据
// data对象
const obj = {
name: 'zhangsan',
age: 4,
addr: 'BeJing'
}
Object.keys(obj).forEach(key => {
let value = obj[key]
Dep dep = new Dep() // 发布者对象: 一个key 对应 一个Dep对象
Object.defineProperty(obj, key, {
get() {
// 监听获取data数据操作, 为使用data数据的组件创建Watcher对象, 并订阅(Dep收集Watcher)
dep.push(new Watcher(obj, key, fn))
return value
},
set(newValue) {
this.value = newValue
// 监听修改data数据操作, 通知订阅者更新数据
dep.notify()
},
})
})
// 观察者对象
class Watcher {
constructor(obj, key, fn) {
}
// 更新数据
update() {
// 获得新值
this.value = this.obj[this.key]
// 我们定义一个 fn 函数,这个函数用来模拟视图更新,调用它即代表更新视图
this.cb(this.value)
}
}
// 发布者对象
class Dep {
// 构造器方法, 初始化订阅者Watcher对象
constructor() {
this.subs = []; // 用来存放已订阅的Watcher对象的数组
}
// 在subs数组中添加一个订阅Watcher对象
addSub(sub) {
this.subs.push(sub)
}
// 向存储在subs数组中的Watcher发送消息
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
}
注: 上面的js代码是伪代码, 无法真正运行, 只是为了说明响应式的原理. 下图为Vue响应式原理图解