在vue中有3个概念很容易搞混,data,computed,props,特别是我们这些原后端开发人员。
new Vue({
el: "#x",
data: { id: 1 },
props: ["id"],
computed: {
id: function () { return 3; }
}
});
测试一下,结果是props
和data
无法共存,data
优先级高于computed
;
我经常是吧组件封装成一个extend来使用的,比如这样:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="js/vue2.2.6.js"></script>
<script src="js/vCheckBox.js"></script>
</head>
<body>
<input type="checkbox" id="test1" />
<script>
var test1 = new vCheckBox({
el: "#test1",
data: {
text: "测试多选框"
}
});
</script>
</body>
</html>
效果:
一开始用的还挺嗨的,直到有一次一个extend用了大量的conputed
然后这个组件对应的文档是这样的:
结果杯具就发生了:
var pager = new vPager({
el: "#pager",
data: {
pageSize: 10
}
});
刚才文章开头说了,data
的优先级是高于computed
的,所以导致pageSize的计算属性被覆盖了,结果可想而知。
所以最后使用的时候改成了这样:
var pager = new vPager({
el: "#pager",
created: function () {
this.pageSize = 10;
}
});
但是总感觉很不舒服
所以我决定写一个mixin来优化这个初始化的操作;
var pager = new vPager({
el: "#pager",
init: {
pageSize: 10
}
});
我希望达到这样的效果,无论是data或者computed都可以被赋值
vue.mixin({
created: function () {
var init = this.$options.init;
if (typeof init === "object") {
for (var key in init) {
if (init.hasOwnProperty(key) && this.hasOwnProperty(key)) {
this[key] = init[key];
}
}
}
}
});
这是最初的样子,挺简单的,经过一段时间的使用,又增加了一些功能,最后的变成了这样子:
(function (vue) {
if (vue == null) return;
vue.config.optionMergeStrategies.init = function (parent, child) { return child; }
vue.mixin({
beforeCreate: function () {
var opt = this.$options;
if (opt.init === undefined) return;
if (opt.created == null) {
opt.created = [];
}
var me = this;
opt.created.push(init);
var hasOnInit = "onInit" in opt.methods;
if (!hasOnInit) {
opt.methods.onInit = initCallback;
}
if ("reInit" in opt.methods === false) {
opt.methods.reInit = init;
}
//--- function ---
function initCallback(data) {
if (hasOnInit && typeof me.onInit === "function") {
me.onInit.apply(me, arguments);
}
setData(data);
me.$emit("init", { vm: me, data: data });
}
function setData(data) {
if (typeof data === "object") {
for (var key in data) {
if (data.hasOwnProperty(key) && key in me) {
me[key] = data[key];
}
}
}
}
function init() {
var initData = me.$options.init;
var callback = initCallback;
if (typeof initData === "function") {
if (initData.length > 0) {
initData = initData.call(me, callback);
callback = null;
} else {
initData = initData.call(me);
}
}
setData(initData);
callback && callback(initData);
}
}
});
})(window.Vue);
这是个全局的mixin,为每个存在init选项的Vue实例添加init功能
init功能:
- 如果init选项为
object
,则使用init选项的值初始化Vue实例的字段,并触发init
事件; - 如果init选项为无参的
function
,执行function后使用返回值初始化Vue实例字段,并触发init
事件; - 如果init选项为有参的
function
,则会传入一个回调函数,执行回调函数会触发init
事件,可以在init函数中直接返回初始值也可以在回调函数中传入初始值; - 为Vue添加一个函数
reInit()
,用于使用原始init选项重新初始化对象并触发init
事件(对象存在reInit成员该功能无效); - 为Vue添加一个函数
onInit(data)
,用于使用data参数初始化对象并触发init
事件;
关闭init功能
要关闭init功能也可以在初始化时将init:undefined
,为此需要专门写一个合并选项策略
Vue.config.optionMergeStrategies.init = function (parent, child) { return child; }
策略比较粗暴,直接让子选项覆盖父选项;
除了最初的初始化属性的功能以外还支持init为function的情况:
init 为无参的`function
var pager = new vPager({
el: "#pager",
init: function() {
var p = location.search.slice(1).split(',');
return {
pageNumber: p[0] || 1,
pageSize: p[1] || 20
}
}
});
init 为有参的`function,直接返回初始化数据,延迟触发回调
var pager = new vPager({
el: "#pager",
init: function (callback) {
$.ajax({
... ,
context: this
}).done(function (data) {
this.pageNumber = data.pageNumber;
this.pageSize = data.pageSize;
callback();
});
return {
pageNumber: 1,
pageSize: 20
}
}
});
init 为有参的`function,延迟传入初始化数据并触发回调
var pager = new vPager({
el: "#pager",
init: function (callback) {
$.ajax({
... ,
context: this
}).done(function (data) {
callback({
data.pageNumber,
data.pageSize
});
});
}
});
init 事件
触发 init 事件
vm.$emit("init", { vm: vm, data: initData })
订阅 init 事件
vm.$on("init", function(event){
event.vm ...
event.initData ...
})