Vue 2 中常见 Vue.prototype.$api 或 $formatDate,但理解这些写法,先要区分 JavaScript 原型链、Vue 的扩展接口和组件之间的数据关系。它们不是同一种“继承”。
原型链提供属性查找
const methods = {
describe() {
return this.name + ":" + this.items.length;
}
};
function createModel(name) {
const model = Object.create(methods);
model.name = name;
model.items = [];
return model;
}
const a = createModel("A");
const b = createModel("B");
a.items.push("one");
console.log(a.describe(), b.describe()); // A:1 B:0
console.log(Object.getPrototypeOf(a) === methods); // true
describe 从原型对象查找,name 和 items 是实例自己的属性。把可变数组放到共同原型上,实例就可能意外共享状态;给实例自己的同名属性赋值,则会遮蔽原型上的属性。JavaScript 继承与原型链
Object.assign 做浅层属性复制,不是替对象建立多个原型。普通对象的直接原型只有一个;共享方法也不等于共享所有实例状态。
Vue 2 的全局实例工具
旧工程可以通过插件集中安装工具:
const TextPlugin = {
install(Vue) {
Vue.prototype.$label = function (value) {
return value == null ? "—" : String(value);
};
}
};
// 在已有 Vue 2 应用入口执行:Vue.use(TextPlugin)
它依赖 Vue 2 的全局构造器接口。全局工具应名称明确、用途稳定,避免和组件已有属性冲突;请求客户端这样的对象还应有清晰的配置和环境边界。
Vue.extend 则用于构造组件子类;mixins 合并选项,不能直接等同于 JavaScript 的多重原型继承。组件树里的父组件也不自动成为子组件 JavaScript 对象的原型。
Vue 3 换成应用级接口
在 Vue 3 已创建的 app 上,插件可使用 app.config.globalProperties:
const TextPlugin = {
install(app) {
app.config.globalProperties.$label = value =>
value == null ? "—" : String(value);
}
};
// 在已有 Vue 3 应用入口执行:app.use(TextPlugin)
这是面向该应用的全局实例属性,不是在 Vue 3 中继续修改 Vue.prototype。工具过多会让依赖隐蔽,纯函数也可直接 import 使用。Vue 3 插件文档
组件数据传递不是对象原型继承
父子组件通常通过 props 与事件交流;跨层依赖可以使用 provide/inject;跨页面共享状态则需要明确的状态所有者。provide/inject 按键寻找提供值,不意味着子组件继承父组件全部数据和方法,响应式行为也取决于提供的值。
EventBus 同样是订阅关系。若使用旧 Vue 2 事件总线,组件结束时应解绑自己的处理器,不移除其他组件的全部监听。原型扩展不会自动解决这些订阅的生命周期。
这些概念的选择取决于依赖范围:纯计算用函数,应用级工具用受控插件,组件数据按明确的接口传递,可变状态避免无意共享。原型链与垃圾回收也不是两套互斥机制,是否回收仍取决于引用可达性。