# 1. Vue 介绍
Vue.js 是一个构建数据驱动(所谓数据驱动,是指视图是由数据驱动生成的,我们对视图的修改,不会直接操作 DOM,而是通过修改数据。)的 web 界面的 MVVM 渐进式框架。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。核心是一个响应的数据绑定系统。
在 JQuery 时期,如果需要刷新 UI 时,需要先取到对应的 DOM 再更新 UI,这样数据和业务的逻辑就和页面有强耦合。
在 MVVM 中,UI 是通过数据驱动的,数据一旦改变就会相应的刷新对应的 UI,UI 如果改变,也会改变对应的数据。这种方式就可以在业务处理中只关心数据的流转,而无需直接和页面打交道。
# Vue 和 React 的区别:
相同点:
使用 Virtual DOM
中心思想相同:一切都是组件,组件实例之间可以嵌套;都提供合理的钩子函数
不同点:
在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树
在 Vue 应用中,组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件确实需要被重渲染
React 采用特殊的 JSX 语法,Vue.js 在组件开发中推崇编写.vue 特殊文件格式
# Vue 和 Angular 的区别:
相同点:
都支持指令:内置指令和自定义指令,vue 在设计之初参考了很多 angular 的思想(例如 v-if vs ng-if)
都支持过滤器:内置过滤器和自定义过滤器
都支持双向数据绑定;都不支持低端浏览器
不同点:
在性能上,AngularJS 依赖对数据做脏检查,所以 Watcher 越多越慢;Vue 采用数据劫持实现双向绑定,但是代价是对于 ie9 以下的浏览器无法支持
# MVVM 与 MVC 区别:
MVVM 与 MVC 两者之间最大的区别就是:MVVM 实现了对 View 和 Model 的自动同步,也就是当 Model 的属性改变时,我们不用再自己手动操作 Dom 元素来改变 View 的变化,而是改变其属性后,该属性对应的 View 层数据会自动改变。
# 2. Vue 指令
# ①、v-if 和 v-show 的区别
相同点:
v-if 与 v-show 都可以动态控制 dom 元素显示隐藏
不同点:
v-if 显示隐藏是将 dom 元素整个添加或删除
而 v-show 隐藏则是为该元素添加 css--display:none,此时 dom 元素还在
TIP
- 如果需要非常频繁地切换,则使用 v-show 较好
- 如果在运行时条件很少改变,则使用 v-if 较好
# ②、v-text
<span v-text="msg"></span>
v-text 主要用来更新 textContent,可以等同于 JS 的 text 属性
# ③、v-html
双大括号的方式会将数据解释为纯文本,而非 HTML 为了输出真正的 HTML,可以用 v-html 指令 它等同于 JS 的 innerHtml 属性
<div v-html="rawHtml"></div>
这个 div 的内容将会替换成属性值 rawHtml,直接作为 HTML 进行渲染
- 在⽹站上动态渲染任意 HTML,很容易导致 XSS 攻击。所以只能在可信内容上使⽤ v-html,且永远不 能⽤于⽤户提交的内容上
# ④、v-for
用 v-for 指令根据遍历数组来进行渲染 有下面两种遍历形式
<!-- 使用in,index是一个可选参数,表示当前项的索引 -->
<div v-for="(item,index) in items"></div>
2
<!-- 使用of -->
<div v-for="item of items"></div>
2
# ⑤、v-bind
v-bind 用来动态的绑定一个或者多个特性。没有参数时,可以绑定到一个包含键值对的对象
常用于动态绑定 class 和 style。以及 href 等
简写为一个冒号【 :】
<1>对象语法:
<div :class="{'is-active':isActive, 'text-danger':hasError}"></div>
<!-- data: { isActive: true, hasError: false } -->
2
<2>数组语法:
<p :class="[{'is-active':activeClass},errorClass]">12345</p>
<!-- data: { activeClass: false, errorClass: 'text-danger' } -->
2
// 三目运算符绑定
<div :class="[loginActive ? 'activeTab' : '','tabpane']"></div>
2
<3>直接绑定数据对象:
<div :class="classObject">12345</div>
<!-- data: { classObject:{ 'is-active': false, 'text-danger':true } } -->
2
<4>样式三元运算:
:style="{'color': countdown.status ? 'blue' : '#FCB11C'}" ;
# ⑥、v-model
这个指令用于在表单上创建双向数据绑定
v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值
因为它选择 Vue 实例数据做为具体的值。
# ⑥、v-on
v-on 主要用来监听 dom 事件,以便执行一些代码块。表达式可以是一个方法名
简写为:【 @ 】
# 自定义指令:
有的情况下,需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令
directives: { // 局部注册指令
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
<input v-focus> // 在元素上使用指令
2
3
4
5
6
7
8
9
10
指令注意事项:
WARNING
- 1、当 v-for 和 v-if 处于同一节点,v-for 的优先级比 v-if 更高,永远不要把 v-if 和 v-for 同时用在同一个元素上
- 2、如果一组 v-if + v-else 的元素类型相同,最好使用 key
# 3.父子组件间通信
# 3-1、 父组件 传数据 子组件
parent.vue:
<children v-bind:toChildData="parentSourceData"></children>
children.vue:
props: ["toChildData"];
# 3-2、 父组件 调用 子组件 方法
parent.vue:
<children ref="refChildrenName"></children>
method:
this.$refs.refChildrenName.子组件的方法名();
# 3-3、 子组件 传数据 父组件
parent.vue:
<children v-on:fromChild="parentMethod"></children>
children.vue:
this.$emit("fromChild", args);
# 3-4、 子组件 调用 父组件方法
parent.vue:
<children></children>
children.vue:
this.$parent.parentMethod(args);
或者在子组件里用$emit 向父组件触发一个事件,父组件监听这个事件就行了
<child @fatherMethod="fatherMethod"></child>
childMethod() { this.$emit('fatherMethod'); }
2
3
# 3-5、 子组件使用.sync 修饰符
# 4.组件上总是必须用 key
key 的作用是为了在 diff 算法执行时更快的找到对应的节点,提高 diff 速度。
key 一般用每个元素对应固定不变的值,如 id,对于数组索引 index 由于在插入数据时会变化,不推荐使用
注意:有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误,常见于 v-for 循环渲染。
# 5.Proxy 与 Object.defineProperty 对比
Object.defineProperty 虽然已经能够实现双向绑定了,但是他还是有缺陷的
1、只能对属性进行数据劫持,所以需要深度遍历整个对象
2、对于数组不能监听到数据的变化
3、Proxy 的第二个参数可以有 13 种拦截方法,比 Object.defineProperty() 要更加丰富
4、Proxy 的兼容性不如 Object.defineProperty() 不能使用 polyfill 来处理兼容性
Proxy 能够原生支持监听数组变化,并且可以直接对整个对象进行拦截
所以在 Vue3.0 中使用 Proxy 代替 Object.defineProperty
# 备注:vue2 对数组的监听:
Vue 包含一组观察数组的变异方法(改变原数组),所以它们也将会触发视图更新。这些方法如 push()等 7 种:
对于非变异方法如 slice(),可以用新数组替换旧数组 arr = arr.slice(1)
由于 JavaScript 的限制,Vue 不能检测以下变动的数组
1、直接设置一个项时,例如:vm.items[indexOfItem] = newValue
2、修改数组的长度时,例如:vm.items.length = newLength 或者 vm.items.length++
解决方法:
对于第一种:Vue.set(vm.items, indexOfItem, newValue) 或者 vm.$set(vm.items, indexOfItem, newValue)
对于第二种:vm.items.splice(newLength)
# 6.组件的 data 必须是一个函数
因为组件是要共享的,但他们的 data 是私有的。 如果 data 是一个普通的对象,则所有的实例将共享引用同一个数据对象。 因此组件的 data 必须是一个函数,这样每个实例可以维护一份被返回对象的独立的拷贝
# 注: 在一 Vue 的根实例上 data 直接使用对象是可以的
因为只存在一个这样的实例
new Vue({
data: {
foo: "bar"
}
});
2
3
4
5
# 7.keep-alive
vue2.0 提供了一个 keep-alive 组件,
用来缓存组件,主要用于保留组件状态或避免重新渲染。
activated: keep-alive 组件激活时调用
deactivated: keep-alive 组件停用时调用
# 8.方法(methods)、计算属性(computed)、侦听属性(watch)
计算属性是基于它们的依赖进行缓存,只有在相关依赖发生改变时它们才会重新求值
缓存的作用:对于性能开销较大的计算不需重复执行
缺点:像 Date.now()的计算属性就不再更新,因为 Date.now() 不是响应式依赖,如果你不希望有缓存,用方法来替代。
计算属性默认只有 getter ,也可以提供一个 setter :
computed: {
comItem: {
get: function(){},
set: function(newval){}
}
}
// 触发顺序:页面首次加载触发get,数据变动时先触发set,然后在触发get
2
3
4
5
6
7
# methods VS computed
重新计算开销很大的话,选 computed; 不希望有缓存的选 methods
# computed VS watch
watch 有新旧值两个参数, 计算属性没有,但是计算属性可以从 setter 获得新值,computed 对于多数据变动,watch 适用于一个数据变动,watch 的对象必须事先声明,而 computed 则不用。
computed 计算属性,是依赖其他属性的计算值,并且有缓存,只有当依赖的值变化时才会更新。
watch 是在监听的属性发⽣变化时,在回调中执⾏⼀些逻辑。
所以, computed 适合在模板渲染中,某个值是依赖了其他的响应式对象甚⾄是计算属性计算⽽来,
⽽ watch 适合监听某个值的变化去完成⼀段复杂的业务逻辑。
# 9.过滤器
过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 10.Vue ref
ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
<p ref="p">hello</p>
<child-component ref="child"></child-component>
2
3
TIP
- 当 v-for 用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。
- 除了可以获取dom元素,还能获取子组件中的data和去调用子组件中的方法
# 11.Vue hooks
# 1、内部监听生命周期函数
mounted() {
// 监听窗口发生变化,resize组件
window.addEventListener('resize', this.$_handleResizeChart)
// 通过hook监听组件销毁钩子函数,并取消监听事件
this.$once('hook:beforeDestroy', () => {
window.removeEventListener('resize', this.$_handleResizeChart)
})
}
2
3
4
5
6
7
8
# 2、外部监听生命周期函数
<template>
<!--通过@hook:updated监听组件的updated生命钩子函数-->
<!--组件的所有生命周期钩子都可以通过@hook:钩子函数名 来监听触发-->
<custom-select @hook:updated="$_handleSelectUpdated" />
</template>
<script>
import CustomSelect from '../components/custom-select'
export default {
components: {
CustomSelect
},
methods: {
$_handleSelectUpdated() {
console.log('custom-select组件的updated钩子函数被触发')
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 12.v-text,v-html和{{}}的区别
v-text:是操作网页元素中的纯文本内容 {{}}
是它的另一种写法, v-text与{{}}
等价,{{}}
叫模板插值,v-text叫指令
区别:在渲染数据比较多的时候,可能会把大括号显示出来,俗称屏幕闪动
v-html:可以渲染出html
# 13.Vue常用的修饰符
.lazy(当光标离开标签时,才会将值赋值给value)
.trim(过滤掉两边的空格)
.stop(阻止事件的冒泡,相当于调用了event.preventPropagation方法)
.prevent(阻止了事件的默认行为,相当于调用了event.preventDefault方法)
.once(绑定了事件以后只能触发一次,第二次就不会触发)
.native(在自定义组件标签上绑定原生事件)
# 14. Vue在哪个生命周期发起Ajax请求
- 在获取数据后,对数据的处理如果不涉及DOM,可在
created
阶段获取,毕竟速度更快一些 - 如果数据涉及到DOM的处理,则要在
mounted
阶段获取数据
# 15. Vue.nextTick 的原理和用途
- 说明:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
Vue 在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
//改变数据
vm.message = 'changed'
//想要立即使用更新后的DOM。这样不行,因为设置message后DOM还没有更新
console.log(vm.$el.textContent) // 并不会得到'changed'
//这样可以,nextTick里面的代码会在DOM更新后执行
Vue.nextTick(function(){
console.log(vm.$el.textContent) //可以得到'changed'
})
2
3
4
5
6
7
8
- 用途:需要在视图更新之后,基于新的视图进行操作
# 16. Vue单向数据流
子组件不可以修改父组件传递的Prop
- 子组件如何修改父组件的值
1、通过$emit
2、通过.sync修饰符
# 16. v-for 和 v-if 不建议⽤在⼀起
当 v-for 和 v-if 处于同⼀个节点时, v-for 的优先级⽐ v-if 更⾼,这意味着 v-if 将分别重复 运⾏于每个 v-for 循环中。如果要遍历的数组很⼤,⽽真正要展示的数据很少时,这将造成很⼤的性 能浪费。 这种场景建议使⽤ computed ,先对数据进⾏过滤。
# 17. Vue的性能优化
尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
v-if和v-for不能连⽤
如果需要使⽤v-for给每项元素绑定事件时使⽤事件代理
SPA ⻚⾯采⽤keep-alive缓存组件
key保证唯⼀
使⽤路由懒加载、异步组件
第三⽅模块按需导⼊
⻓列表滚动到可视区域动态加载
图⽚懒加载
# 其他
Vue 不能挂载在 body、html 这样的根节点上
router.push 只能跳转路由内部的页面,要想跳转到外部页面,可以使用 window.location.href