vue文档以及api阅读整理
更新时间2019-12-20
虽然vue3.0马上要发布了, 但是我认为许多核心api还是不会变的,趁着最近有空,再次精读文档,每次阅读都有很大的收获.
此次精读主要分析整理一些个人比较薄弱的功能模块
加上自己日常使用的一些心得, 以更加通俗的语言描述出来
对于基础性的东西文档中都讲的很清楚
基本指令
- v-text 输出渲染后的值
- v-html 输出解析HTML元素后的值
- v-if 条件判断
- v-else-if
- v-else
- v-for 循环
- v-model 表单双向数据绑定
- v-show 控制元素显示与隐藏
- v-bind 绑定元素的属性和style
- v-on 绑定事件
- v-pre 原样输出
- v-clock 渲染完之后才显示,防止将{{message}}类似的输出到页面
- v-once 只渲染一次
生命周期
- beforeCreate
- 创建前) 在数据观测和初始化事件还未开始
- created
- (创建后) 完成数据观测,属性和方法的运算,初始化事件,$属性还没有显示出来(载入前)在挂载开始之前被调用,相关的函数首次被调用。实例已完成以下的配置:编译模板,把里面的数据和模板生成。注意此时还没有挂载到页面上。(载入后)在被新创建的el属性还没有显示出来
- beforeMount
- (载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。此时打印this.$el 输出的是节点
- mounted
- (载入后) 在el 被新创建的vm . el 输出的是节点内挂载的DOM元素
- beforeUpdate
- (更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。数据改变,但是还没有渲染的时候
- updated
- (更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。页面渲染之后
- activated
- keep-alive 组件激活时调用。
- deactivated
- keep-alive 组件停用时调用.
- beforeDestroy
- (销毁前) 在实例销毁之前调用。实例仍然完全可用。
- destroyed
- (销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
- errorCaptured
计算属性
计算属性的缓存 vs 方法
在许多情况下,计算属性和方法都能达到同样的效果,我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值。这就意味着只要 依赖条件 还没有发生改变,多次访问 函数 计算属性会立即返回之前的计算结果,而不必再次执行函数。
计算属性和方法对比, 计算属性有缓存, 依赖不变不更新, 而方法每次都要执行
计算属性和watch对比, 可以简单理解 计算属性为多个参数影响一个, watch是一个变化,可以去操作多个
计算属性的 setter
- 所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。
- 计算属性默认只显示getter,当你需要的时候也可以提供一个setter
- 注意如果你为一个计算属性使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
vm.fullName = 'jack ma'
<!-- 此时会触发set -->
watch 属性
watch: {
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
# 方法名
b: 'someMethod',
# 深度 watcher
c: {
handler: function (val, oldVal) { /* ... */ },
deep: true
},
# 该回调将会在侦听开始之后被立即调用
d: {
handler: function (val, oldVal) { /* ... */ },
immediate: true
},
e: [
function handle1 (val, oldVal) { /* ... */ },
function handle2 (val, oldVal) { /* ... */ }
],
# watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
条件渲染
- v-if
- v-else // 必须紧跟在带 v-if 或者 v-else-if 的元素的后面
- v-if-else // 必须搭配v-if使用并且必须紧跟在带 v-if 或者 v-else-if 的元素之后
v-if 会尽可能的高效地复用元素,所以v-if两个代码块有相同元素的时候,很可能,会被复用,解决办法:只需添加一个具有唯一值的 key 属性即可
v-if vs v-show
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
列表渲染
- 可以渲染数组,对象,数字,字符串
<div v-for="(item, index) in items"></div>
<div v-for="(val, key) in object"></div>
<div v-for="(val, key, index) in object"></div>
- key
- 和元素复用性能有关,有相同父元素的子元素必须有独特的 key,一般可以用id
显示过滤后的数据
- items 可以是computed属性
- items 可以是methods方法
数组的更新
修改数组中的数据,但是页面渲染不会响应式的更新可以通过以下三种方式
- 变异方法
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reserve()
- 改变引用 | 重新赋值 | 替换数组
- 使用新数组替换旧的数组
- set方法 (同样适用于对象的更新)
Vue.set(vm.items, indexOfItem, newValue)
vm.$set(vm.items, indexOfItem, newValue)
对象的属性的添加删除
Vue 不能检测对象属性的添加或删除
使用set方法添加响应式属性
动态组件
- 必须使用 <component> 标签
- 使用is属性决定要渲染的组件
异步组件
<!-- 处理加载状态 -->
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
is属性的使用
在一些标签对内部元素有着严格的限制,这个时候我们想要使用组件就需要is属性
// 这里就会把tr属性渲染成对应的组件,直接使用组件不被允许
<table>
<tr is="blog-post-row"></tr>
</table>
组件的v-model
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
.sync修饰符
<!-- 简化了双向绑定的写法, -->
子组件
this.$emit('update:title', newTitle)
父组件
<text-document v-bind:title.sync="doc.title"></text-document>
Prop
传递使用了v-bind 所有传递的值都为动态的,例如
<!-- 传递的为字符串 -->
<blog-post likes="42"></blog-post>
<!-- 传递的为数字 -->
<blog-post v-bind:likes="42"></blog-post>
- 数字
- 字符串
- 布尔值
<!-- 包含该 prop 没有值的情况在内,都意味着 `true`,即使子组件设置了default为false。--> <blog-post is-published></blog-post>
- 对象
- 数组
! prop 为单向数据流,子组件接收后不能直接修改数据,如果要对数据进行加工,需要先将接收到的数据赋值给data中自定义的参数
prop验证
- type 类型检查
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
- 自定义构造函数 详见文档
- required 是否必填
- validator:function() 自定义验证函数
作用域插槽 2.6 更新
对于作用于插槽的理解:正常的插槽不论具名插槽还是匿名插槽都不带数据,只是留出一片区域让你自己插入
作用域插槽则是带有数据的,样式父组件说了算,但内容可以显示子组件插槽绑定的。
<!-- 作用域插槽可以输具名插槽,要讲本身的数据或者通过prop接收到的数据传出来 -->
<slot name="up" :data="data"></slot>
<!-- 父组件调用的时候 -->
v-solt 可以用 # 替换
#data="{userdata}"
<child>
<template v-solt:data="userdata"> # 这里使用 user接收传过来的data,必须要使用template标签和slot-scope
<div class="tmpl">
<span v-for="item in userdata">{{item}}</span>
</div>
</template>
</child>
依赖注入
个人理解在$root 和$parent 都无法很好的访问上级组件的时候使用依赖注入
依赖注入
- provide选项允许我们指定我们想要提供给后代组件的数据/方法。在这个例子中,就是
provide: function () {
return {
getMap: this.getMap
}
}
- inject 然后在任何后代组件里,我们都可以使用 inject 选项来接收指定的我们想要添加在这个实例上的属性
inject: ['getMap']
循环引用
递归组件
- 递归组件调用自身的时候只能通过name来调用,不用在自身引入注册
- 传值的时候最好做下判断如果有值再调用
组件之间的循环引用
- 组件互相引用,互为父子,形成悖论.只有组件为全局注册组件时才会解开悖论
- 解决方案
- 等到生命周期钩子 beforeCreate 时去注册它:
- 在本地注册组件的时候,你可以使用 webpack 的异步 import
动画&&过渡
- 在 CSS 过渡和动画中自动应用 class
- 可以配合使用第三方 CSS 动画库,如 Animate.css
- 在过渡钩子函数中使用 JavaScript 直接操作 DOM
- 可以配合使用第三方 JavaScript 动画库,如 Velocity.js
参考去哪儿 5-123
过渡的类名
-
v-enter
- 只存在第一帧
-
v-enter-active
- 第一帧到最后一帧
-
v-enter-to
- 第二帧到最后一帧
-
v-leave
- 只存在第一帧
-
v-leave-active
- 第一帧到最后一帧
-
v-leave-to
- 第二帧到最后一帧
v-enter-active,v-leave-active 都是处于整个流程中,故可以用来监视过渡的变化
- v-enter 第一帧设置以一个属性,第二帧属性移除 一般都是从无到有,第一帧设置无
- v-enter-to 因为第一帧已经移除,所以第二帧无需设置
- v-enter-active 检测到属性变化,设置过渡动效时间
v-leave 离开的第一帧,不设置
v-leave-to 第二帧设置为改变后的状态,并移除了第一帧,状态改变
-
v-leave-active 检测到第二帧的变化 设置过渡动效时间等
综上, 一般v-enter,v-leave-to写在一起 v-enter-active,v-leave-active写在一起设置相同的效果
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter。
CSS3动画
- 直接写在 v-*-active中即可
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
自定义过渡类名 && animate.css的使用
transtion 的API文档中有明确的解释
transition API
animate.css 是 基于@keyfamse的animated动画
自定义名称的钩子函数,通过这些钩子可以自定义对应的class过渡类名
- enter-class
- enter-active-class
- enter-to-class (2.1.8+)
- leave-class
- leave-active-class
- leave-to-class (2.1.8+)
通过自定义过渡类名结合animate.css使用
- 必须使用自定义过渡类名
- 必须先加 animated 这个类
<transition
name="custom-classes-transition"
appear // 允许首次进入加载动画
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
appear-active-class = "animated tada" // 首次进入加载的过渡效果具体
>
<p v-if="show">hello</p>
</transition>
同时使用过渡和动画
对于一些过渡和动画同时使用的情况,可以在自定义过渡类名中加入 原始的过渡类名,然后书写对应的效果
<transition
name="fade"
type= "animation | transition " // 如果过渡和动画的时间不一致,这里可以指定以谁的为准
appear
enter-active-class="animated tada fade-enter-active" // 这里加入了原始的类,可以自定义
leave-active-class="animated bounceOutRight"
appear-active-class = "animated tada"
>
- 但是,在一些场景中,你需要给同一个元素同时设置两种过渡动效,比如 animation 很快的被触发并完成了,而 transition 效果还没结束。在这种情况中,你就需要使用 type 特性并设置 animation 或 transition 来明确声明你需要 Vue 监听的类型。
- 你也可以自定义效果执行时长
<transition :duration="1000">...</transition> <transition :duration="{ enter: 500, leave: 800 }">...</transition>
JS动画钩子 && Velocity.js
文档讲的很详细了
JS动画钩子
多个组件的过渡
当有相同标签名的元素切换时,需要通过 key 特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。即使在技术上没有必要,给在 <transition> 组件中的多个元素设置 key 是一个更好的实践。
过渡模式
在多个元素或组件切换过度的时候,可以设置mode 模式来控制显隐顺序
- out-in 先隐藏后进入
- in-out 先进入后隐藏
动态组件的过渡
- 通过is属性控制组件
列表的过渡
- 使用 <transition-group> 标签
- 内部元素 总是需要 提供唯一的 key 属性值。
- 不同于 <transition>,它会以一个真实元素呈现:默认为一个 <span>。你也可以通过 tag 特性更换为其他元素
- 其他和原来一样
列表的排序过渡
- 实现添加删除等过程中列表的平滑过渡
- v-move 属性来实现 *-move
列表的交错过渡
- 通过 data 属性与 JavaScript 通信 ,就可以实现列表的交错过渡,文档讲的很清楚
可复用过渡
- 就是封装一个过渡组件 结合slot
动态过渡
- 通过数据驱动的方式设置过渡效果,具体看文档
状态过渡
- 这部分比较复杂,主要还是数据驱动的思想.通过数据的改变影响
混入
- 我对混入的理解是,你定义一个一个对象,可以写vue中的数据,方法,以及生命周期等,在对应的组件如果引入了.那么这个对象中定义的就混合为同一个对象。两个对象键名冲突时,取组件对象的键值对.因为混入对象的钩子将在组件自身钩子之前调用。
- 全局混入要慎用,因为他会影响所有实例
- 关于自定义合并策略,抱歉没搞懂 - -
extend
主要用途是组件的继承和多态
可以将一个组件扩展为Vue的一个子类
const compont = {}
const ComVue = Vue.extend(compont)
new ComVue({
el:"#root",
....
})
可以一个组件继承另一个组件
const Comp2 = {
extend: compont,
.....
}
自定义指令
因为这个文档已经写的很清楚了,所以这里写一下我的理解.当内置指令 例如 v-if 等无法满足我们需求的时候,我们可以自定义一些指令以帮助我们实现需求. 具体实现方法看文档.
// 注册
Vue.directive('my-directive', {
bind: function () {},
inserted: function () {},
update: function () {},
componentUpdated: function () {},
unbind: function () {}
})
渲染函数 && JSX
参考react就很好理解了, vue3.0开放了渲染函数
render (createElement) {
return createment (
'com-one',{
ref: 'com'
},[
createElement('span',{
ref: 'span'
},this.value)
]
)
}
插件
插件是一个经常用到的地方.主要是一些全局功能
1.使用Vue.use()方法使用插件
2.插件的开发
# wxpay
export const wxpay = {
install:() => {
Vue.prototype.$wxpay = () => {
# xxxx
}
}
}
# toast.js
var Toast = {};
Toast.install = function (Vue, options) {
Vue.prototype.$msg = 'Hello World';
}
module.exports = Toast;
keep-alive
主要用于组件以及路由的缓存, 用于保留组件状态或避免重新渲染
- 参数
- include
- exclude
- max
- 生命周期
- activated
- deactivated