这几天休息了一下,周一继续肝vue,今天会发两篇vue的文章,之后我会结合mybatis更新后端的一些知识分享。今天主要就是脚手架搭建项目之前的最后的一些基础知识的储备。
之前我们写过在一个标签被插入页面之前之后以及触发更新标签属性的时刻进行相应的动作的内容,在自定义指令的时候,我们可以指定指令绑定的标签在与页面绑定时,插入时以及被页面重新解析时的各个处理函数(如下代码,放在vue实例中的directives对象里,例如下面的自定义指令在调用时只需要在标签内部加上v-focus即可):
// 对象式自定义vue指令
focus: {
// 指令与标签等元素成功绑定时
bind() {
console.log('bind')
},
// 指令所在元素被插入页面时
inserted(element) {
console.log('insert')
// 加载立即获取焦点
element.focus()
},
// 指令所在模板重新解析时
update() {
// 这里发现我们利用点击事件@click="color = 'black'"
// 修改color的值的时候,模板被重新解析了!!!vue的实时响应与渲染果然是很优秀的
console.log('update')
}
}
那么连自定义的指令对于绑定这个指令的标签而言在页面上拥有的不同的时机就会有不同的函数去调用,而且在update函数的作用里就是模板被重新解析时就会回调这个函数,然后我们要是想在vue实例从初始化到销毁时做出不同时机的动作,又该如何呢?vue提供了相应的钩子函数,在不同的时机回调。
new Vue({
el: '#app',
data() {
return {
}
},
// 生命周期总共分为五个结点:初始化(这是一个vue实例被初始化的阶段,这个时候创建数据代理),
// (前)创建(后),(前)挂载(后),(前)更新(后),(前)销毁(后)
beforeCreate() {
},
created() {
},
beforeMount() {
},
mounted() {
},
beforeUpdate() {
},
updated() {
},
beforeDestroy() {
},
destroyed() {
},
})
生命周期钩子函数分为:beforeCreate()创建前,create()创建后,beforeMount()挂载前,mounted()挂载后,beforeUpdate()修改前,updated()修改后,beforeDestroy()销毁前,destroyed()销毁后。总共八大生命周期钩子函数,分别处于四大生命周期的前后阶段。
由于还没开始使用vue的脚手架工具进行vue项目的创建,现在我们写组件的方式还是在一个html文件中进行组件的编写,这样写出来的组件叫做非单文件组件,以后会有vue为后缀的单文件组件,组件component,主要的作用就是解耦合提高复用性,便于模块的管理。
组件的主要命令就是Vue.extend({配置项}),内部的配置项除了没有el挂载这一项之外其他均有,原因后续在分析vueComponent构造实例对象的原型链时会说到,这里想看一下组件的案例:
这里需要注意一个小小的问题,在编写非单文件组件时,我们需要先将组件初始化的代码写出来再去vue实例中注册,不然会报出没有定义这个组件的信息。那么我们组件的调用是不是很方便呢?等创建脚手架项目之后,真香两个字迸发在我的心头。
我们知道,组件调用是可以实现组件间嵌套使用的,无非是组件们被vue实例领导定位,除了不需要挂载在容器上这个特性外,组件拥有vue的所有属性,当然也包括components:{}组件注册的功能了,所以组件注册也可以在组件内部进行!这个现象我们叫做组件嵌套:
这里就利用app这个组件去注册使用school组件,再用vue实例去注册组件实现页面上调用组建的功能。
Vue.extnd({配置项})的源码其实是这样的:
Vue.extend = function (extendOptions) {
extendOptions = extendOptions || {};
var Super = this;
var SuperId = Super.cid;
var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
var name = extendOptions.name || Super.options.name;
if (name) {
validateComponentName(name);
}
var Sub = function VueComponent (options) {
this._init(options);
};
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.cid = cid++;
Sub.options = mergeOptions(
Super.options,
extendOptions
);
Sub['super'] = Super;
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
initProps$1(Sub);
}
if (Sub.options.computed) {
initComputed$1(Sub);
}
// allow further extension/mixin/plugin usage
Sub.extend = Super.extend;
Sub.mixin = Super.mixin;
Sub.use = Super.use;
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type];
});
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub;
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options;
Sub.extendOptions = extendOptions;
Sub.sealedOptions = extend({}, Sub.options);
// cache constructor
cachedCtors[SuperId] = Sub;
return Sub
};
}
我们可以发现源码的返回值是一个Sub,而这个Sub的来源是:
var Sub = function VueComponent (options) {
this._init(options);
};
显然我们每一次调用一次组件就会触发Vue.extend方法也会在内部去初始化也就是new VueComponent()一次,所以这个VueComponent构造函数一定还有别的作用,为什么vue实例拥有的属性组件也会拥有?组件的调用会返回一个新的VueComponent实例对象?十万个为什么?哈哈哈哈哈,一切答案都在源码里:
var Super = this;
....
Sub.prototype = Object.create(Super.prototype);
我们不难在上面的源码里看到这个显式原型属性的值是this的显式原型属性,那么这个this实际上就是Vue,而按照原型链的指向,继续往下找就是Vue的原型对象,再往下找就是Object的原型对象null了,所谓一切皆空?可不是嘛!
所以当我们调用组件的内部属性时,其实是按照原型链去寻找vue原型对象中有没有这些属性了!
以上这张图能更好的把我源码分析的结果画出来给大家观看。