插件
插件是自包含的代码,通常向 Vue 添加全局级功能。它可以是公开 install() 方法的 object,也可以是 function
插件的功能范围没有严格的限制 —— 一般有下面几种:
- 添加全局方法或者 property。如:vue-custom-element
- 添加全局资源:指令/过滤器/过渡等。如:vue-touch)
- 通过全局混入来添加一些组件选项。(如vue-router)
- 添加全局实例方法,通过把它们添加到
config.globalProperties
上实现。 - 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
好吧,上面来自于官网。
简单的说,插件就是一个“高级组件”,或者说是组件的集合,以及专用类库。
其实我们在做工程化开发的时候,就使用了几个插件,比如负责路由的 Vue-Router、负责状态的 Vuex、还有UI库等。
我们可以先看看这几个是怎么实现插件的。
main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import 'dayjs/locale/zh-cn'
import locale from 'element-plus/lib/locale/lang/zh-cn'
createApp(App)
.use(store)
.use(router)
.use(ElementPlus, { locale, size: 'small' })
.mount('#app')
这是常见的 main.js 函数,创建了几个实例,然后通过 use 挂到 Vue的实例里面。
那么我们写插件的话,也可以模仿一下。
我们先看看 router 、store 是什么样子的。
router
这里的 install 就是 插件需要的函数。
install(app) {
const router = this;
app.component('RouterLink', RouterLink);
app.component('RouterView', RouterView);
app.config.globalProperties.$router = router;
Object.defineProperty(app.config.globalProperties, '$route', {
get: () => unref(currentRoute),
});
// this initial navigation is only necessary on client, on server it doesn't
// make sense because it will create an extra unnecessary navigation and could
// lead to problems
if (isBrowser &&
// used for the initial navigation client side to avoid pushing
// multiple times when the router is used in multiple apps
!started &&
currentRoute.value === START_LOCATION_NORMALIZED) {
// see above
started = true;
push(routerHistory.location).catch(err => {
if ((process.env.NODE_ENV !== 'production'))
warn('Unexpected error when starting the router:', err);
});
}
const reactiveRoute = {};
for (let key in START_LOCATION_NORMALIZED) {
// @ts-ignore: the key matches
reactiveRoute[key] = computed(() => currentRoute.value[key]);
}
app.provide(routerKey, router);
app.provide(routeLocationKey, reactive(reactiveRoute));
app.provide(routerViewLocationKey, currentRoute);
let unmountApp = app.unmount;
installedApps.add(app);
app.unmount = function () {
installedApps.delete(app);
if (installedApps.size < 1) {
removeHistoryListener();
currentRoute.value = START_LOCATION_NORMALIZED;
started = false;
ready = false;
}
unmountApp.call(this, arguments);
};
if (((process.env.NODE_ENV !== 'production') || __VUE_PROD_DEVTOOLS__) && true) {
addDevtools(app, router, matcher);
}
},
好吧,没完全看懂,大概是先注册了两个组件,RouterLink 和 RouterView,这个眼熟吧。
然后判断了一下浏览器的环境。
然后把路由规则注入进来。
最后做销毁。
其他没看懂。
store
store 的 install 放在了 prototype 里面。
Store.prototype.install = function install (app, injectKey) {
app.provide(injectKey || storeKey, this);
app.config.globalProperties.$store = this;
};
这个比较简单,只做了两件事情,
- 把 store 通过 provide 注入到根节点。
- 把 store 挂到 globalProperties 上面。
既然是 用 provide 注入的,那么是不是说可以用 inject 给取出来?
console.log('inject的状态', inject('store'))
真的可以,只是好像也没啥大用。
编写插件
看完了别人的插件,然后自己的插件要如何写呢?
每当这个插件被添加到应用程序中时,如果它是一个对象,就会调用 install 方法。如果它是一个 function,则函数本身将被调用。在这两种情况下——它都会收到两个参数:由 Vue 的 createApp 生成的 app 对象和用户传入的选项。
我们先写一个空的插件体验一下:
./plugins/test-plugin.js
export default {
install: (app, options) => {
// Plugin code goes here
console.log('app', app)
console.log('options', options)
}
}
然后在 mian.js 里面挂到实例上面。
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import 'dayjs/locale/zh-cn'
import locale from 'element-plus/lib/locale/lang/zh-cn'
// 测试插件
import testPlugin from './plugins/test-plugin'
createApp(App)
.use(store)
.use(router)
.use(testPlugin, { title: '测试一下插件' })
.use(ElementPlus, { locale, size: 'small' })
.mount('#app')
这样我们写的空插件就挂上了,还是老规矩,看看效果。
option
这个比较明显了,就是我们在 use 里面传进去的参数,一般可以做默认设置。app
这个就比较复杂了,属性比较多。-
app.config.globalProperties
全局属性,
globalProperties
路由和状态已经挂上了,还有其他的一些不认识的,也不知道是啥时候挂上去的。
写了这么多好像也没啥实际内容,只是对插件有了一个感性的认知。
后面的基本上就是可以在 install 里面注册插件、注册自定义指令、注入、等等。需要掌握一些基础知识,否则肯定看不懂。
这些还是应该有一个比较实际的例子才会体会的更深刻吧,官网的那个 18n 的例子,我是看的晕晕乎乎的。
我打算写一个自己的状态管理的插件,前面的也构想过一些功能,现在基本了解插件了,打算用插件的形式包装一下。