github
更多学习资源和知识整理:https://github.com/ChenMingK/WebKnowledges-Notes
组件
模板
.vue 文件的模板大致是这样的:
<template>
<div class="component-class"></div>
</template>
<script>
import { } from ...
export default {
mixins: [], // 混入(组合)
components: {}, // 子组件(模板内使用的资源)
props: { title: { type: Boolean, default: true} }, // 接口
data () {}, // 本地状态-实例的数据对象
computed: {}, // 本地状态-计算属性
watch: {}, // 侦听属性
methods: { plus: function () { this.a++ } }, // 方法
// 生命周期钩子
created () {},
mounted () {}
}
</script>
<style lang="scss" scoped>
@import "../../mixin.scss"
</style>
组件通信
组件间的通信可分为父子组件通信、兄弟组件通信、跨级组件通信。
1.父子组件通信
props
选项:子组件可以通过 props 选项来接收父组件的数据,数据流的传输是单向的。$emit
与v-on
:子组件通过调用内置的 $emit 方法并传入事件名称来触发一个事件,父组件使用 v-on 监听子组件实例的任何事件。$children
与$parent
(不推荐使用):在子组件中,使用this.$parent
可以直接访问该组件的父实例或组件,父组件也可以通过this.$children
访问它所有的子组件,而且可以递归向上或向下无限访问,直到根实例或最内层的组件。
-ref
:父组件可以用特殊的属性 ref 来为子组件(或者DOM)指定一个索引名称。通过this.$refs.xxx
可以访问到这个子组件。
2.兄弟组件通信与跨级组件通信
- 中央事件总线 bus:创建一个名为 bus 的空 Vue 实例。其他组件都通过 bus 来发送事件并且监听来自 bus 的事件。
- vuex:如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,就需要使用 vuex,vuex 的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。
插槽
1.作用域:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!--
这里的 `url` 会是 undefined,因为 "/profile" 是
传递给 <navigation-link> 的而不是
在 <navigation-link> 组件*内部*定义的。
-->
</navigation-link>
2.后备内容:我们可能希望在一个<button>
内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为后备内容,我们可以将它放在<slot>
标签内:
<button type="submit">
<slot>Submit</slot>
</button>
现在当我在一个父级组件中使用 <submit-button>
并且不提供任何插槽内容时:
<submit-button></submit-button>
后备内容“Submit”将会被渲染:
<button type="submit">
Submit
</button>
内置组件
1.<component>
<!-- 动态组件由 vm 实例的属性值 `componentId` 控制 -->
<component :is="componentId"></component>
我们还需要引入使用到的组件,注意是放在 data 选项中:
import StaffInfo from '../components/StaffInfo.vue'
import LeaveTable from '../components/LeaveTable.vue'
import SalaryTable from '../components/SalaryTable.vue'
import GateTable from '../components/GateTable.vue'
export default {
data: function () {
return {
StaffInfo,
LeaveTable,
SalaryTable,
GateTable
}
},
2.<transition>
- 使用 v-show / v-if 动态显示或隐藏元素时,会触发过渡动画
- transition 需要指定 name,并包裹一个含 v-show / v-if 的 div
- vue 会为 transition 包裹的 div 动态添加 class,共6种
v-enter: 显示之前 v-enter-to: 显示之后 v-enter-active: 显示的过程
v-leave: 隐藏之前 v-leave-to: 隐藏之后 v-leave-active: 隐藏的过程
注意 transition 的样式必须和包裹的 div 同级(scss)
例如:
<transition name="fade">
<div class="popup-bg" @click.stop.prevent="hide()" v-if="popupVisible"></div>
<!--灰色的背景图层, 点击后弹窗隐藏. 禁止事件冒泡和预设行为-->
</transition>
这是一个淡入淡出的效果:
.fade-enter, .fade-leave-to {
opacity: 0;
}
.fade-enter-to, .fade-leave {
opacity: 1;
}
.fade-enter-active, .fade-leave-active {
transition: all .3s linear;
}
3.<transition-group>
- 不同于 ,它会以一个真实元素呈现:默认为一个。你也可以通过 tag 特性更换为其他元素。
- 过渡模式不可用,因为我们不再相互切换特有的元素。
- 内部元素总是需要提供唯一的 key 属性值。 不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move 特性,它会在元素的改变定位的过程中应用。 像之前的类名一样,可以通过 name 属性来自定义前缀,也可以通过 move-class 属性手动设置。
<transition-group name="list"
tag="div"
id="item-list">
<div class="item" v-for="(item, index) in items" :key="item.id"></div>
</transition-group>
.list-move {
transition: transform 1s;
}
.list-leave-active {
display: none;
}
还需要注意以下事项:
- name:用于自动生成 CSS 过渡类名
- tag:这个<transition-group>组件在DOM中实际以什么样的形式存在,默认渲染为 <span>
- 每个 <transition-group> 的子节点必须有独立的 key ,动画才能正常工作
可以看下这个 demo
4.<keep-alive>
https://cn.vuejs.org/v2/api/#keep-alive
生命周期
生命周期函数就是组件在初始化或者数据更新时会触发的钩子函数。
Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
- beforeCreate(创建前) 在实例初始化之后,数据观测 (data observer) 和event/watcher 事件配置之前被调用。从下面截取的 vue 源码可以看到
beforeCreate
调用的时候,是获取不到 props 或者 data 中的数据的,因为这些数据的初始化都在initState
中
Vue.prototype._init = function(options) {
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate') // 拿不到 props data
initInjections(vm)
initState(vm)
initProvide(vm)
callHook(vm, 'created')
}
- created(创建后) 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,
$el
属性目前不可见。
export function mountComponent {
callHook(vm, 'beforeMount')
// ...
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
}
-
beforeMount(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。
-
mounted(载入后) 在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的 html 内容替换 el 属性指向的 DOM 对象。完成模板中的 html 渲染到 html 页面中。
注意mounted
不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用vm.$nextTick
替换掉mounted
:
通过上图可以看到在 mounted 之前 p 中还是通过{{message}}进行占位的,因为此时还没有挂在到页面上,还是 JavaScript 中的虚拟 DOM 形式存在的。在 mounted 之后可以看到 h1 中的内容发生了变化。
-
beforeUpdate(更新前) 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
-
updated(更新后) 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性]取而代之。
注意
updated
不会承诺所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以用vm.$nextTick
替换掉updated
:
updated: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered
})
}
- beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。
- destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。