目录
一、创建一个 vue 实例(注意与创建组件区别开来)
二、创建组件
1、创建一个普通的 vue 组件
2、基于 “模块系统” 创建的 “单文件组件”
三、注册组件
1、局部注册
(1)、在 components 对象中局部注册组件
(2)、单文件组件中局部注册组件
2、全局注册
(1)、使用 Vue.component() 函数全局注册组件
(2)、单文件组件中全局注册组件
四、组件与插槽配合使用
五、组件的继承与混合
六、组件之间的通信
七、组件是如何更新的?(vue 的响应式原理 / 双向绑定原理)
八、vue 组件实例的生命周期
1、vue 生命周期的三个阶段
(1)、创建阶段
(2)、更新阶段
(3)、销毁阶段
2、Vue 父子组件的生命周期顺序
(1)、加载渲染过程
(2)、子组件更新过程
(3)、父组件更新过程
(4)、销毁过程
3、缓存组件相关的两个特有的生命周期
(1)、activated
(2)、deactivated
4、捕获后代组件错误的特有的生命周期——errorCaptured
通常一个应用会以一棵嵌套的组件树的形式来组织。
var vm = new Vue({
el: "#app",
data() {
return {
}
},
})
一个 vue 实例 与 一个 vue 组件的区别:
一个普通的 vue 组件,其实就是一个对象。
const componentA = {
data () {
return {
msg: 'hello world'
}
},
template: '{{msg}}
'
}
文件扩展名为 .vue 的文件都是 vue 的 “单文件组件”。
hello {{str}}
组件的注册类型:全局注册 和 局部注册。
局部注册组件的方式:
基于模块系统定义一个 componentA 组件:
{{str}}
局部注册并使用 componentA 组件:
【注意】
全局注册组件的方式:
全局注册的组件,可以在 Vue 根实例和所有的子组件中使用。
// 假设有一个组件 componentA,将 componentA 组件全局注册为 button-counter 组件
const componentA = {
data () {
return {
msg: 'hello world'
}
},
template: '{{msg}}
'
}
Vue.component("button-counter", componentA)
等价于:
Vue.component("button-counter", {
data () {
return {
msg: 'hello world'
}
},
template: '{{msg}}
'
})
可能你的许多组件只是包裹了一个输入框或按钮之类的元素,是相对通用的。我们有时候会把它们称为基础组件,它们会在各个组件中被频繁的用到。
所以会导致很多组件里都会有一个包含基础组件的长列表。例如:
如果你恰好使用了 webpack (或在内部使用了 webpack 的 Vue CLI 3+),那么就可以使用 require.context() 函数只全局注册这些非常通用的基础组件。这里有一份可以让你在应用入口文件 (比如 src/main.js) 中全局导入基础组件的示例代码:
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
// 其组件目录的相对路径
'./components',
// 是否查询其子目录
false,
// 匹配基础组件文件名的正则表达式
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
// 获取组件配置
const componentConfig = requireComponent(fileName)
// 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 获取和目录深度无关的文件名
fileName
.split('/')
.pop()
.replace(/\.\w+$/, '')
)
)
// 全局注册组件
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
)
})
【拓展】
require.context() 是 webpack 的 一个 api。它用来获取一个特定的上下文,主要用来实现自动化导入模块,以优化从一个文件夹引入很多模块的情况——它会遍历文件夹中的指定文件,然后自动导入,而不需要每次显式的调用 import 导入模块了。
【典例】
引入所有的 api(存放接口):
const files = require.context('.', false, /\.js$/)
const modules = {}
function getFileName (name) {
if (name.indexOf('-')) {
const _name = name.toLowerCase().split('-')
const len = _name.length
for (let i = 1; i < len; i++) {
_name[i] = _name[i].substring(0, 1).toUpperCase() + _name[i].substring(1)
}
return _name.join('')
} else {
return name
}
}
files.keys().forEach(key => {
if (key === './index.js') { return }
const _key = key.replace(/(\.\/|\.js)/g, '')
const humpName = getHumpName(_key)
modules[humpName] = files(key)
})
export default modules
额外的补充——上述代码的核心算法是:遍历树形结构,将其转化为一个对象的形式输出。
【注意】
详情请戳这里:vue 插槽 slot
请戳这里:vue 组件的继承与混合
请戳这里:vue 组件之间的通信
vue 核心——数据驱动 DOM(尽量避免直接操作 DOM)。
数据的来源:
状态(data)与属性(props)
data 触发更新条件:数据写在 data 里,并 return 出去,同时,必须在 template 中有使用。
props 触发更新条件:若以对象接收,可以且必须在 template 中有使用;若以数组接收,还得在data 中转接一下,才能且必须在 template 中有使用。
vue 响应式更新:
vue 在实例化的时候,使用 Object.definePropery() 方法或 Proxy 构造函数,对 data 进行 getter 和 setter 的处理。在组件渲染时,若用到 data 里的某个数据,这个数据就会被依赖收集进 watcher 里。当数据更新,如果这个数据在 watcher 里,就会收到通知并更新,否则不会更新——vue 采用“数据劫持”+“观察者模式(发布者-订阅者模式)”相结合的方式实现了双向绑定——vue 的响应式原理。
computed 和 watch 请戳此链接:vue 的计算属性(computed)和侦听器(watch)
截止此次更新,vue 共有 11 个钩子函数:
vue 生命周期(钩子函数) | 执行时机 |
---|---|
beforeCreate | 初始化数据 |
created | 数据初始化完成 |
beforeMount | 准备挂载数据 |
mounted | 数据挂载完毕,DOM 加载完成 |
beforeUpdate | 更新前的状态 |
updated | 更新完成的状态 |
activated | 被 keep-alive 缓存的组件激活时调用 |
deactivated | 被 keep-alive 缓存的组件失活时调用 |
beforeDestroy | 在实例销毁之前调用,实例仍然完全可用 |
destroyed | 在Vue 实例销毁后调用,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁 |
errorCaptured | 在捕获一个来自后代组件的错误时被调用 |
beforeCreate:初始化事件和生命周期
created:数据初始化完成,包括数据观测、属性、侦听器的配置等。
beforeMount:将模板编译到虚拟DOM上。
rander——生成虚拟DOM
mounted:处理“异步请求”、“操作DOM”、“定时器”等,但是在mounted中,vue不承诺子组件也会挂载到真实DOM上,为确保子组件已经挂载到真实DOM上,常常需要用this.$nextTick()在回调中对DOM进行一些操作。
【拓展】
vue获取后端数据放在created还是mounted方法里面?
beforeUpdate:移除已经添加的事件监听器等,不可以更改依赖数据(响应式数据),否则会造成死循环。
rander——生成虚拟DOM
updated:可以操作DOM,添加的事件监听器等,但是vue不承诺子组件也会挂载到真实DOM上,为确保子组件已经挂载到真实DOM上,常常需要用this.$nextTick()在回调中对DOM进行一些操作。另外,不可以更改依赖数据,否则会造成死循环。
beforeDestroy:移除已经添加的事件监听器,计时器等。
destroyed:移除已经添加的事件监听器,计时器等。
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
父beforeUpdate->子beforeUpdate->子updated->父updated
父beforeUpdate->父updated
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
被 keep-alive 缓存的组件激活时调用。
被 keep-alive 缓存的组件失活时调用。
errorCaptured:在捕获一个来自后代组件的错误时被调用。
此钩子会收到三个参数:
此钩子可以返回 false 以阻止该错误继续向上传播。以防止该组件可能会进入一个无限的渲染循环。
【拓展】Vue 的错误处理机制——errorCaptured 与 config.errorHandler
浅出Vue 错误处理机制errorCaptured、errorHandler