Vue.extend

使用基础 Vue 构造器,创建一个子类: 先创建一个类 Sub,接着通过原型继承的方式将该类继承基础 Vue 类,然后给 Sub 类添加一些属性以及将父类的某些属性复制到 Sub 类上,最后将 Sub 类返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// 创建构造器
<div id="mount-point"></div>;
var Profile = Vue.extend({
template: "<p>{{firstName}} {{lastName}} aka {{alias}}</p>",
data: function () {
return {
firstName: "Walter",
lastName: "White",
alias: "Heisenberg",
};
},
});
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount("##mount-point");

// 实现
Vue.extend = function (extendOptions) {
extendOptions = extendOptions || {}; // 传入的一个包含组件选项的对象参数
const Super = this; // 指向父类,即基础 Vue类
const SuperId = Super.cid; // 父类的cid属性,无论是基础 Vue类还是从基础 Vue类继承而来的类,都有一个cid属性,作为该类的唯一标识
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); // 缓存池,用于缓存创建出来的类
// 结果缓存
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId];
}

// 获取到传入的选项参数中的name字段,并且在开发环境下校验name字段是否合法
const name = extendOptions.name || Super.options.name;
if (process.env.NODE_ENV !== "production" && name) {
validateComponentName(name);
}

// 创建一个类Sub,这个类就是将要继承基础Vue类的子类,接着让该类去继承基础Vue类,让其具备一些基础Vue类的能力
const Sub = function VueComponent(options) {
this._init(options);
};
// 1. 将父类的原型继承到子类中,并且为子类添加唯一标识cid
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.cid = cid++;
// 2. 将父类的options与子类的options进行合并,将合并结果赋给子类的options属性
Sub.options = mergeOptions(Super.options, extendOptions);
// 3. 将父类保存到子类的super属性中,以确保在子类中能够拿到父类
Sub["super"] = Super;

// 4. 如果选项中存在props属性,则初始化它
if (Sub.options.props) {
initProps(Sub);
}
// 把参数中传入的props选项代理到原型的_props中
function initProps(Comp) {
const props = Comp.options.props;
for (const key in props) {
proxy(Comp.prototype, `_props`, key);
}
}
// 5. 如果选项中存在computed属性,则初始化它
if (Sub.options.computed) {
initComputed(Sub);
}
// 将每一项都调用defineComputed函数定义到子类原型上
function initComputed(Comp) {
const computed = Comp.options.computed;
for (const key in computed) {
defineComputed(Comp.prototype, key, computed[key]);
}
}

// 6. 将父类中的一些属性复制到子类中
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;
}

// 7. 给子类新增三个独有的属性
Sub.superOptions = Super.options;
Sub.extendOptions = extendOptions;
Sub.sealedOptions = extend({}, Sub.options);

// 8. 用父类的cid作为key,创建好的子类Sub作为value,存入缓存池cachedCtors中
cachedCtors[SuperId] = Sub;
return Sub;
};

Vue.nextTick

该 API 的原理同实例方法 $nextTick 原理一样,此处不再重复。唯一不同的是实例方法 $nextTick 中回调的 this 绑定在调用它的实例上

Vue.set

Vue.delete

Vue.directive

注册或获取全局指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 注册
Vue.directive("my-directive", {
bind: function () {},
inserted: function () {},
update: function () {},
componentUpdated: function () {},
unbind: function () {},
});

// 注册 (指令函数)
Vue.directive("my-directive", function () {
// 这里将会被 `bind` 和 `update` 调用
});

// getter,返回已注册的指令
var myDirective = Vue.directive("my-directive");

// 原理
Vue.options = Object.create(null);
Vue.options["directives"] = Object.create(null); // 存放指令的位置

Vue.directive = function (id, definition) {
// 获取指令
if (!definition) {
return this.options["directives"][id];
} else {
// 注册指令
// 如果是函数,则默认监听bind和update两个事件,即将definition函数分别赋给bind和update两个属性
if (type === "directive" && typeof definition === "function") {
definition = { bind: definition, update: definition };
}
// 不是一个函数,那么即认为它是用户自定义的指令对象,直接将其保存在this.options['directives']中
this.options["directives"][id] = definition;
return definition;
}
};

Vue.filter

注册或获取全局过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 注册
Vue.filter("my-filter", function (value) {
// 返回处理后的值
});

// getter,返回已注册的过滤器
var myFilter = Vue.filter("my-filter");

// 原理
Vue.options = Object.create(null);
Vue.options["filters"] = Object.create(null);

Vue.filter = function (id, definition) {
if (!definition) {
return this.options["filters"][id];
} else {
this.options["filters"][id] = definition;
return definition;
}
};

Vue.component

注册或获取全局组件。注册还会自动使用给定的 id 设置组件的名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 注册组件,传入一个扩展过的构造器
Vue.component(
"my-component",
Vue.extend({
/* ... */
})
);

// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component("my-component", {
/* ... */
});

// 获取注册的组件 (始终返回构造器)
var MyComponent = Vue.component("my-component");

// 原理
Vue.options = Object.create(null);
Vue.options["components"] = Object.create(null);

Vue.filter = function (id, definition) {
if (!definition) {
return this.options["components"][id];
} else {
// 1. 首先会校验组件的name值是否合法
if (process.env.NODE_ENV !== "production" && type === "component") {
validateComponentName(id);
}
// 2. 判断传入的definition参数是否是一个对象,如果是对象,则使用Vue.extend方法将其变为Vue的子类,同时如果definition对象中不存在name属性时,则使用组件id作为组件的name属性
if (type === "component" && isPlainObject(definition)) {
definition.name = definition.name || id;
definition = this.options._base.extend(definition);
}
// 3. 将注册好的组件保存在this.options['components']
this.options["components"][id] = definition;
return definition;
}
};

Vue.use

安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入

  • 该方法需要在调用 new Vue() 之前被调用
  • 当 install 方法被同一个插件多次调用,插件将只会被安装一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Vue.use = function (plugin) {
const installedPlugins =
this._installedPlugins || (this._installedPlugins = []); // 用来存储已安装过的插件
// 1. 判断传入的插件是否存在于installedPlugins数组中(即已被安装过),如果存在的话,则直接返回,防止重复安装
if (installedPlugins.indexOf(plugin) > -1) {
return this;
}

// 2. 获取到传入的其余参数,并且使用toArray方法将其转换成数组,同时将Vue插入到该数组的第一个位置,这是因为在后续调用install方法时,Vue必须作为第一个参数传入
const args = toArray(arguments, 1);
args.unshift(this);
// 3. 判断传入的插件如果是一个提供了 install 方法的对象,那么就执行该对象中提供的 install 方法并传入参数完成插件安装
if (typeof plugin.install === "function") {
plugin.install.apply(plugin, args);
} else if (typeof plugin === "function") {
// 4. 传入的插件是一个函数,那么就把这个函数当作install方法执行,同时传入参数完成插件安装
plugin.apply(null, args);
}
// 5. 插件安装完成之后,将该插件添加进已安装插件列表中,防止重复安装
installedPlugins.push(plugin);
return this;
};

Vue.mixin

全局注册一个混入,即可以修改 Vue.options,影响注册之后所有创建的每个 Vue 实例

1
2
3
4
Vue.mixin = function (mixin) {
this.options = mergeOptions(this.options, mixin);
return this;
};

Vue.compile

在 render 函数中编译模板字符串。只在独立构建时有效

1
2
3
4
5
6
7
8
9
10
11
12
var res = Vue.compile("<div><span>{{ msg }}</span></div>");

new Vue({
data: {
msg: "hello",
},
render: res.render,
staticRenderFns: res.staticRenderFns,
});

// 原理
Vue.compile = compileToFunctions;

Vue.observable

让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const state = Vue.observable({ count: 0 });

const Demo = {
render(h) {
return h(
"button",
{
on: {
click: () => {
state.count++;
},
},
},
`count is: ${state.count}`
);
},
};

Vue.version

提供字符串形式的 Vue 安装版本号。这对社区的插件和组件来说非常有用,你可以根据不同的版本号采取不同的策略

  • 该 API 是在构建时读取了 package.json 中的 version 字段,然后将其赋值给 Vue.version
1
2
3
4
5
6
7
8
9
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
}