1. Vue.use
在 Vue 中引入组件时,一般会用到 Vue.use 进行组件的注册。但有些组件就不需要注册,比如 axios。
因为需要注册的组件中有 install 的部分,而 axios 这类组件则没有。
写一个具有 install 部分的组件流程:
-
创建文件,定义一个组件
<template> <div class="loading-box">loading...</div> </template> -
在 index.js 中引入组件,并导出。
// 引入组件 import LoadingComponent from './loading.vue' // 定义 Loading 对象 const Loading={ // install 是默认的方法。当外界在 use 这个组件的时候,就会调用本身的 install 方法,同时传一个 Vue 这个类的参数。 install:function(Vue){ Vue.component('Loading',LoadingComponent) } } // 导出 export default Loading -
在 main.js 中引入 loading 文件下的 index.js
// 其中'./components/loading/index' 的 /index 可以不写,webpack会自动找到并加载 index 。如果是其他的名字就需要写上。 import Loading from './components/loading/index' // 这时需要 use(Loading),如果不写 Vue.use()的话,浏览器会报错,大家可以试一下 Vue.use(Loading)
2. Vue.mixin
混入(mixin)是一种分发 Vue 组件中可复用功能的方法。混入对象可包含任意组件选项,所有混入对象将被混入该组件本身的选项。
用法:
-
定义一个混入对象。
let MIXIN = { data() { return { name: 'mixin' } }, created() { console.log('mixin...', this.name); }, mounted() {}, methods: {} }; export default MIXIN; -
全局引用混入对象。
//全局引用 import mixin from './mixin' Vue.mixin(mixin) //在vue文件中引用 import '@/mixin'; // 引入mixin文件 export default { mixins: [mixin] }
选项合并:
当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合。
- 数据对象(data)在内部会进行递归合并,在和组件的数据发生冲突时以组件数据优先。
- 同名钩子函数(created,mounted...)将混合为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
- 值为对象的选项(methods, components 和 directives)将被混合为同一个对象。两个对象键名冲突时,取组件对象的键值对。
3. Vue.compile
render 函数:
- render 函数的实质是生成 template 模板。
- 通过调用一个方法来生成,而这个生成方法是通过 render 函数的参数传递的。
- 该函数有三个参数,分别提供标签名,标签相关属性,标签内部的 HTML 内容。
对比:
-
非使用 render 函数的情况下:
<div id="app"> <child :level="1">Hello world!</child> </div> <script type="text/x-template" id="template"> <div> <h1 v-if="level === 1"> <slot></slot> </h1> <h2 v-if="level === 2"> <slot></slot> </h2> </div> </script> <script type="text/javascript"> Vue.component('child', { template: '#template', props: { level: { type: Number, required: true } } }) new Vue({ el: "#app" }) </script>代码说明:
- Vue.component 这部分代码,第一个参数表示注册了一个 Vue 的组件,标签名是 child (即 child 标签会替换)。
- 第二个参数中,template 属性表示匹配 id="template" 的标签(这个标签是 script 又或者是 div 再或者是 template 都没有关系),然后将这个标签作为组件的模板。
- props 表示传递给这个组件的变量,并限制其变量类型为 number ,并强制需要传递。
- slot 表示分发,具体来说就是把组件中源 HTML 内容分发到 slot 标签中。
-
使用 render 函数的情况下:
<div id="app"> <child v-bind:level="2">Hello world!</child> </div> <script type="text/javascript"> Vue.component('child', { render: function (createElement) { return createElement( 'h' + this.level, // tag name 标签名称 this.$slots.default // 子组件中的阵列 ) }, props: { level: { type: Number, required: true } } }) new Vue({ el: "#app" }) </script>代码说明:
- 使用和不使用的区别:
- 没有显式的模板内容,直接通过 render 函数生成。
- 使用了 createElement 方法。
- 关于 createElement 方法,它是通过 render 函数传递进来的,该方法有三个参数:
- 第一个参数主要用于提供 DOM 的 HTML 内容,类型可以是字符串,对象或函数。如 "div" 就是创建一个 div 标签。
- 第二个参数主要用于设置这个 DOM 的一些样式、属性、组件参数、绑定事件。
- 第三个参数主要用于在父节点下放置其他子节点。
4. Vue.observable
Vue 内部会用它来处理 data 函数返回的对象。这些对象可直接用于渲染函数和计算属性内,并且会再发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器进行数据状态共享。
在 index.js 中:
import Vue from 'vue'
export const store = Vue.observable({
count: 0,
name: '李四'
})
export const mutations = {
setCount (count) {
store.count = count
},
changeName (name) {
store.name = name
}
}
在组件中:
<template>
<div class="container">
<button @click="setCount(count+1)">count++</button>
</div>
</template>
<script>
import { store, mutations } from '@/store/index'
export default {
computed: {
// 如果store中的数据发生变化重新计算
count () {
return store.count
},
name () {
return store.name
}
},
methods () {
// 将store中的mutations方法赋值拿到
setCount: mutations.setCount
}
}
</script>
5. Vue.version
用于提供字符串形式的 Vue 版本号,这让我们可以根据不同的版本号采取不同的策略。其原理就是读取 package.json 中的 version 字段。
用法:
var version = Number(Vue.version.split('.')[0]);
if (version === 2) {
// Vue v2.x.x
} else if (version === 1) {
// Vue v1.x.x
} else {
// Unsupported versions of Vue
}
6. 选项/生命周期钩子
生命周期是指 Vue 实例从创建之初到销毁的过程,Vue 所有功能的实现都是围绕其生命周期进行的,在生命周期的不同阶段调用对应的钩子函数可以实现组件数据管理和 DOM 渲染两大重要功能。
beforeCreate:
创建前,在实例初始化之后,数据观测和 event/watcher 事件配置之前被调用。此时也因为数据观察和事件机制未形成不能获取 DOM 节点。
- options 合并,将本实例创建时传进来的 options 和父构造函数默认的 options 合并,没有的就添加上去,有的就覆盖掉,并赋值给 $options 实例变量后暴露出来。
- 给 $parent、$root、$children、$refs 赋值。
- $root 为根组件。
- $parent 除了根组件以外为 undefined ,其他为直接父级。
- 给 $solts、$scopedSlots、$attrs、$listeners 赋值。
- $slots 编程式用于访问内部插槽。
- $scopedSlots 用于访问作用域插槽,Vue3 中将替代 $slots。
- $attrs 包含了父作用域中不作为 prop 被识别(且获取)的 attribute 绑定,class 和 style 除外。
- $listeners 包含了父作用域中的 v-on 事件监听器,不包含 .native修饰器,它可以通过 v-on="$listeners" 传入内部组件,在创建更高层次的组件时非常有用。
总结: beforeCreate 主要是做准备工作,将该实例的 options 合并整理出来,再把 $xxx 初始化。
created:
// created 源码
initInjections(vm) // 在状态初始化之前将高级插件注入
initState(vm) // 初始化 Props/methods/data/computed/watch 数据劫持等
initProvide(vm) // 解决提供数据后的问题,与 inject 是一对
callHool(vm, 'created') // 触发 created 钩子
- initInjections 与 initProvide 需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深。并且在其上下游关系成立的时间里始终生效。
- 对合并后的 $options 中的选项(props, methods, data, computed, watch)进行数据劫持等操作。
- 对于 props:合法化,缓存 key 进数组中方便下次迭代。
- 对于 data:检验 props,methods 中是否有重名的属性。
- 对于 methods:代理到 vm 实例上,方便使用this.method.name 调用方法。
- 对于 computed:封装成 watcher 并用该 watcher 的 value 缓存该计算属性的 value,再在每个计算属性上劫持一层 getter 和 setter。在第一次调用 getter 时取得最新的 value,并将依赖缓存下来,之后在依赖不变的前提下,getter 只返回 watcher 的 value ,而不是又去取一遍值。在依赖发生变化的时候,通知 watcher 更新,watcher 取最新值作为 value,从而实现依赖更新,计算属性才更新。
- 对于 watch:调用 $watch 封装成一个 user watcher ,如果有 immediate options 传入,就在封装的时候调用一遍 callback, 有 deep options传进来的话就将该属性的所有嵌套属性记为依赖。
总结:created 主要是对 options 做操作,其中 data、props 做数据劫持,methods 代理在 vm 实例上,computed、watch 封装成不同类型的 watcher。