We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
vue3.0-beta 版本已经发布了一段时间了,正式版本据说在年中发布(直播的时候说的是年中还是年终,网上传闻说是6月份)。嘴上说着学不动,身体却很诚实地创建一个vue3的项目,兴致勃勃地引入 vue2 插件的时候,眉头一皱,发现事情并没有那么简单。
浏览器无情地抛出了一个错误:
Uncaught TypeError: Cannot set property '$toast' of undefined
不是说兼容vue2的写法吗,插件不兼容,这是闹哪样?发下牢骚之后还是得解决问题。研究插件的代码,是这么实现的
var Toast = {}; Toast.install = function (Vue, options) { Vue.prototype.$toast = 'Hello World'; } module.exports = Toast;
vue2 插件的时候会暴露一个 install 方法,并通过全局方法 Vue.use() 使用插件。install 方法的第一个参数是 Vue 构造器,插件的方法就添加在 Vue 构造器的原型对象上。通过 new Vue() 实例化后,在组件中就能调用 this.$toast 使用组件了。(具体实现可以参考我之前的一篇文章:Vue.js 插件开发详解,下面也是基于这个插件demo对比修改)。
而 vue3 的 install 方法不是传入 Vue 构造器,没有原型对象,Vue.prototype 是 undefined,所以报错了。vue3 采用依赖注入的方式,在插件内部利用 provide 和 inject 并暴露出一个组合函数,在组件中使用。
下面先实现一个插件的基本框架。
import { provide, inject } from 'vue'; const ToastSymbol = Symbol(); // 用Symbol创建一个唯一标识,多个插件之间不会冲突 const _toast = () => {} // 插件的主体方法 export function provideToast(config) { // 对外暴露的方法,将 _toast 方法提供给后代组件 provide(ToastSymbol, _toast); } export function useToast() { // 后代组件可以从该方法中拿到 toast 方法 const toast = inject(ToastSymbol); if (!toast) { throw new Error('error'); } return toast; }
在 provideToast 方法中,提供了 _toast 方法,那在根组件中 调用 provideToast 方法,传入插件参数,子组件中就能使用 toast 功能了。
// app.vue 根组件 import { provideToast } from 'vue2-toast'; export default { setup() { provideToast({ width: '200px', // 配置toast宽度 duration: 2000 // 配置toast持续显示时长 }); } };
在 useToast 方法中,返回了 toast 方法,那在所有的后代组件中调用 useToast 方法,就能拿到 toast 方法了。
// child.vue 子组件 import { useToast } from 'vue2-toast'; export default { setup() { const Toast = useToast(); // 所有的子组件都要从useToast拿到toast方法 Toast('Hello World'); } };
想要控制 toast DOM 元素的显示隐藏,以及提示文本的变化,需要创建响应式的数据,在 vue3 中可以通过 reactive 方法创建一个响应式对象。
const state = reactive({ show: true, // DOM元素是否显示 text: '' // 提示文本 });
在页面中显示一个 toast,那就免不了要创建一个 DOM 并挂载到页面中。在 vue2 中是通过Vue.extend 创建一个子类,将子类生成的实例挂载到某个元素中,而 vue3 中通过 createApp 方法来实现的。
const _toast = text => { state.show = true; state.text = text; toastVM = createApp({ setup() { return () => h('div', { style: { display: state.show ? 'block' : 'none' } // display 设置显示隐藏 }, state.text ); } }); toastWrapper = document.createElement('div'); toastWrapper.id = 'toast'; document.body.appendChild(toastWrapper); // 给页面添加一个节点用来挂载toast元素 toastVM.mount('#toast'); };
上面的每一步是这个插件的关键内容,接下来就是完善下细节,比如用户配置插件的全局参数,设置 toast 的样式,持续时间,显示位置等,这里就不一一细讲了,完整示例如下:
import { provide, inject, reactive, createApp, h } from 'vue'; const ToastSymbol = Symbol(); const globalConfig = { type: 'bottom', // toast位置 duration: '2500', // 持续时长 wordWrap: false, // 是否自动换行 width: 'auto' // 宽度 }; const state = reactive({ toast: { show: false, // toast元素是否显示 text: '' }, loading: { show: false, // loading元素是否显示 text: '' } }); let [toastTimer, toastVM, toastWrapper, loadingVM, loadingWrapper] = [null, null, null, null, null]; const _toast = text => { state.toast.show = true; state.toast.text = text; if (!toastVM) { // 如果toast实例存在则不重新创建 toastVM = createApp({ setup() { return () => h( 'div', { // 这里是根据配置显示一样不同的样式 class: [ 'lx-toast', `lx-toast-${globalConfig.type}`, globalConfig.wordWrap ? 'lx-word-wrap' : '' ], style: { display: state.toast.show ? 'block' : 'none', width: globalConfig.width } }, state.toast.text ); } }); } if (!toastWrapper) { // 如果该节点以经存在则不重新创建 toastWrapper = document.createElement('div'); toastWrapper.id = 'lx-toast'; document.body.appendChild(toastWrapper); toastVM.mount('#lx-toast'); } if (toastTimer) clearTimeout(toastTimer); // 定时器,持续时长之后隐藏 toastTimer = setTimeout(() => { state.toast.show = false; clearTimeout(toastTimer); }, globalConfig.duration); }; const _loading = {}; _loading.show = text => { state.loading.show = true; state.loading.text = text; if (!loadingVM) { // 如果loging实例存在则不重新创建 loadingVM = createApp({ setup() { return () => h( 'div', { class: 'lx-load-mark', style: { display: state.loading.show ? 'block' : 'none' } }, [ h('div', { class: 'lx-load-box' }, [ h( 'div', { class: state.loading.text ? 'lx-loading' : 'lx-loading-nocontent' }, // loading 菊花节点元素 Array.apply(null, { length: 12 }).map((value, index) => { return h('div', { class: ['loading_leaf', `loading_leaf_${index}`] }); }) ), h('div', { class: 'lx-load-content' }, state.loading.text) ]) ] ); } }); } if (!loadingWrapper) { // 如果该节点以经存在则不重新创建 loadingWrapper = document.createElement('div'); loadingWrapper.id = 'lx-loading'; document.body.appendChild(loadingWrapper); loadingVM.mount('#lx-loading'); } }; _loading.close = () => { state.loading.show = false; }; export function provideToast(config = {}) { for (const key in config) { globalConfig[key] = config[key]; } provide(ToastSymbol, { Toast: _toast, Loading: _loading }); } export function useToast() { const toast = inject(ToastSymbol); if (!toast) { throw new Error('error'); } return toast; }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
vue3.0-beta 版本已经发布了一段时间了,正式版本据说在年中发布(直播的时候说的是年中还是年终,网上传闻说是6月份)。嘴上说着学不动,身体却很诚实地创建一个vue3的项目,兴致勃勃地引入 vue2 插件的时候,眉头一皱,发现事情并没有那么简单。
浏览器无情地抛出了一个错误:
不是说兼容vue2的写法吗,插件不兼容,这是闹哪样?发下牢骚之后还是得解决问题。研究插件的代码,是这么实现的
vue2 插件的时候会暴露一个 install 方法,并通过全局方法 Vue.use() 使用插件。install 方法的第一个参数是 Vue 构造器,插件的方法就添加在 Vue 构造器的原型对象上。通过 new Vue() 实例化后,在组件中就能调用 this.$toast 使用组件了。(具体实现可以参考我之前的一篇文章:Vue.js 插件开发详解,下面也是基于这个插件demo对比修改)。
而 vue3 的 install 方法不是传入 Vue 构造器,没有原型对象,Vue.prototype 是 undefined,所以报错了。vue3 采用依赖注入的方式,在插件内部利用 provide 和 inject 并暴露出一个组合函数,在组件中使用。
插件实现
基本框架
下面先实现一个插件的基本框架。
组件使用
在 provideToast 方法中,提供了 _toast 方法,那在根组件中 调用 provideToast 方法,传入插件参数,子组件中就能使用 toast 功能了。
在 useToast 方法中,返回了 toast 方法,那在所有的后代组件中调用 useToast 方法,就能拿到 toast 方法了。
数据响应
想要控制 toast DOM 元素的显示隐藏,以及提示文本的变化,需要创建响应式的数据,在 vue3 中可以通过 reactive 方法创建一个响应式对象。
挂载DOM
在页面中显示一个 toast,那就免不了要创建一个 DOM 并挂载到页面中。在 vue2 中是通过Vue.extend 创建一个子类,将子类生成的实例挂载到某个元素中,而 vue3 中通过 createApp 方法来实现的。
完整示例
上面的每一步是这个插件的关键内容,接下来就是完善下细节,比如用户配置插件的全局参数,设置 toast 的样式,持续时间,显示位置等,这里就不一一细讲了,完整示例如下:
The text was updated successfully, but these errors were encountered: