Vue2 逻辑复用
mixins混入实现代码复用
合并策略(同Vue.Extend):数据对象递归合并,以组件优先;值为对象(methods, computed等)的选项以组件优先;同名钩子合并为数组,混入对象的钩子调用在前。
合并策略的类型
- 叠加形:通过原型链进行层层的叠加,
component、directives、filters - 队列形:存入一个数组,正序遍历依次执行,
生命周期函数、watch - 合并形:调用set合并、重新赋值,
data - 替换形:后者替代前者,
props、methods、inject、computed
对于想要使用自定义的选项合并逻辑,使用 'Vue.config.optionMergeStrategies'
全局混入使用
应尽量避免全局混入使用,这会对每个组件实例造成影响,大多数情况下作为注册插件使用来全局注册一些组件选项
// 以注册日期时间格式化插件为例(使用全局混入)
const FormatDateMixin = {
install(Vue) {
const options = {
data() {
return {};
},
methods: {
getFormatDate(v, format = 'time') {
return dayjs.format(v, format);
},
},
};
Vue.mixin(options);
},
};
mixins实现的弊端
- 数据来源不清晰,不利于追溯组件行为
- 多个数据源可能造成的命名冲突问题
- 多个 mixin 需要依赖共享的属性名来进行相互作用,增加了耦合性
mixins与组件区别的理解
组件在引用之后相当于在父组件内开辟了一块单独的空间,根据props传参进行通信,单本质上两者还是泾渭分明,相对独立。
而mixins则是在引入组件之后,则是将组件内部的选项与父组件相应内容进行合并。相当于在引入后,父组件被扩充了
Vue3逻辑复用
组合式函数
利用组合式api封装和复用有状态逻辑的函数
// 抽离列表公用逻辑
import { reactive, ref } from 'vue'
/**
* @description usePage 接收一个 opts 参数,返回列表数据
* @param {Object} opts.searchForm - 查询参数
* @param {Function} opts.getListApi - 获取数据
* @param {Function} opts.exportListApi - 导出数据
* @param {Function} opts.resetFunc - 重置查询条件
* @param {Function} opts.sizeChangeFunc
* @param {Function} opts.currentChangeFunc
*/
export const usePage = (opts) => {
const {
searchParams,
getListApi,
exportListApi,
resetFunc = () => {},
sizeChangeFunc = () => {},
currentChangeFunc = () => {},
// ... 一些其他的选项,例如,请求异常处理
} = opts
const reset = () => {
handleCurrentChange(1)
// ...
}
const page = reactive({
pageSize: 10,
pageNum: 1,
total: 0
})
const tableData = ref([]);
const loadingRef = ref(false);
const loadData = () => {
const opts = {
...page,
...searchParams
}
getListApi(opts).then((res) => {
// ...
})
}
const exportFile = () => {
exportListApi({ ...searchParams }).then((res) => {
// ...
})
}
const handleSizeChange = (size) => {
// ...
loadData();
}
const handleCurrentChange = (cur) => {
// ...
loadData()
}
// 避免直接返回响应性对象,这会导致对象解构过程中丢失响应性
// 或使用toRefs()保证解构时不丢失响应性
return {
reset,
page, // toRefs(page)
tableData,
handleSizeChange,
handleCurrentChange,
loadData,
exportFile
}
}
复用示例: 以逻辑关注点进行抽离,例如常见的列表增删改查等
插件使用
Vue.use(), 相同的插件仅注册一次
插件可以添加全局功能的类型
- 使用过滤器、指令添加全局资源。如添加滚动指令;图片预览指令(实际开发场景常使用utl+token进行预览)。
- 添加vue实例方法(通过添加到Vue.prototype上)。如添加全局loading, message弹窗功能。
- 使用全局混入添加一些组件选项。如添加返回列表/回退功能,添加全局的日期时间格式化处理器等。
- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router。
// 以注册自定义滚动指令为例
ScrollBarPlugin.install = function(Vue) {
Vue.directive('scrollbar', {
inserted(el) {
const rules = ['fixed', 'absolute', 'relative'];
if (!rules.includes(window.getComputedStyle(el).position)) {
.....
}
el.ps = new PerfectScrollbar(el, ...);
},
componentUpdated(el, binding, vnode) {
if (!el.ps) return;
vnode.context.$nextTick(() => {
el.ps.update();
});
},
unbind(el) {
if (!el.ps) return;
el.ps.destroy();
el.ps = null;
},
});
}
Vue.use()插件注册原理
- 获取Vue对象的已注册插件列表,判断是否已注册,不允许重复注册
- 根据接受的参数类型座不同处理,若接受的plugin为一个对象且该对象拥有install方法,则调用install方法,如果plugin是一个函数,它会被作为 install 方法被调用,最后给这个插件添加至已经添加过的插件数组中,标示已经注册过
核心代码实现:
Vue.use = function (plugin: Function | Object) {
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// additional parameters
const args = toArray(arguments, 1)
args.unshift(this)
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
综上得出插件开发的两种方式:
1. 将这个插件的逻辑封装成一个对象,最后将在 install 编写业务代码暴露给 Vue 对象。推荐这种做法,可拓展性比高
2. 将所有逻辑都编写成一个函数暴露给 Vue