vue 混入mixins和插件的高级应用__Vue.js__前端
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利
// mixin 基本用法
const empty_mixin = {
filters:{
empty: value => {
value || '--'
}
},
created() {
console.log('注入created');
}
}
new Vue({
el: document.querySelector('#app'),
mixins: [empty_mixin],
data:{
user: ''
},
template: `<h1>hello, {{ user | empty }}</h1>`
})
作用: 1、可以将多个组件相同的逻辑,抽离出来 缺点: 1、变量来源不明确,不利于阅读 2、mixins可能会造成命名冲突 3、mixins和组件可能会出现多对多的关系,复杂度比较高
其实mixins提供的功能在某些情况下,使用utils也能解决问题,vue只是给开发者提供了一个口子,至于到底用不用,需要视实际情况而定。在简单组件中,可以使用mixins避免cv代码。
基于vue中的合并策略实现一个页面离开的时候的钩子函数。
const originalOptionMergeStrategies = Vue.config.optionMergeStrategies
originalOptionMergeStrategies.exit = originalOptionMergeStrategies.created
const notify = (name, vm) => {
let lifeCycle = vm.$options[name]
if(lifeCycle && lifeCycle.length) {
lifeCycle.forEach(lc =>lc.call(vm))
}
const children = vm.$children
if(children && children.length) {
children.forEach(child => notify(name, child))
}
}
const bind = vm => {
window.addEventListener('visibilitychange', () => {
if(document.visibilityState === 'hidden') {
notify('exit', vm)
}
})
}
const vm = new Vue({
el: document.querySelector('#app'),
template: `<div>hello</div>`,
exit() {
alert('leave current page?')
}
})
bind(vm)
vue中的插件机制的简单应用
const logger = {
// vue中的插件要求提供一个install方法 install方法有两个参数
install(Vue, options) {
Object.defineProperty(Vue.prototype, '$log', {
value:() => console.log(`打印日志了~`),
configurable:false,
enumerable:false,
writable: false
})
}
}
Vue.use(logger)
new Vue({
el: document.querySelector('#app'),
template:`<h1>hello world</h1>`,
created() {
this.$log()
}
})
vue中这种插件的机制其实用的还是挺多的。开发者可以利用这点,在开发一套基于自己当前开发项目的自己的组件,然后在项目中use之后直接使用,而无需每次使用的时候都引入。
const Button = {
name: 'el-button',
template:`<button @click='onClick'>{{ label }}</button>`,
props:{
label: {
default: '按钮'
}
},
methods:{
onClick() {
this.$emit('click')
}
}
}
const PUI = [
Button
]
PUI.install = (Vue, options) => {
PUI.forEach(({ name, ...component }) => {
Vue.component(name, component)
})
}
Vue.use(PUI)
new Vue({
el: document.querySelector('#app'),
template: `<div>
<h1>hello</h1>
<el-button label='aa' @click='handleClick'></el-button>
</div>`,
methods:{
handleClick() {
console.log('点击生效了~');
}
}
})
其实在vue的官网中也已经提及这种写法
下面组件基于如下业务场景:当前公司现需开发一个新项目,新项目中会用到原先项目中的组件,但是
现在问题是原先项目是基于react框架写的,现在就需要在vue项目中使用到react组件。
其基本思路是这样的:
1、react组件还是使用reactDOM.render进行渲染,渲染之后的组件放到一个新dom结构中
2、在vue中使用react组件的时候需要注意的是将vue组件中属性在react组件中透穿。
3、在react组件中,children有可能又是一个vue组件,所以还需要实现在react组件中渲染一
个vue组件
// react 里面挂载vue组件
class VueWrapper extends React.Component {
constructor(props){
super(props)
}
componentWillUnmount() {
this.vueInstance.$destroy()
}
createVueInstance = el => {
const { component, on, ...props } = this.props
this.vueInstance = new Vue({
el,
data: props,
render(h) {
return h('xxx-internal-component', {props: this.$data, on},
[
h('yyy-internal-react-wrapper', { props: {
component: () => React.createElement('div', {}, this.children)
}})
]
)
},
components: {
'xxx-internal-component': component,
'yyy-internal-react-wrapper': ReactWrapper
}
})
}
render() {
return React.createElement('div', { ref: this.createVueInstance })
}
}
const makeReactComponent = Component => {
return class ReactRunInVue extends React.Component {
static displayName = 'vue-react'
constructor(props) {
super(props)
this.state = {
...props
}
}
wrappChildren(children) {
return {
render : h => h('div', children)
}
}
render() {
const { children, ...rest } = this.state
const wrappedChildren = this.wrappChildren(children)
return React.createElement(
Component,
{ ...rest },
children && React.createElement(VueWrapper, { component: wrappedChildren })
)
}
}
}
// reactWrapper 组件,vue组件
// 本质
const ReactWrapper = {
props: ['component'],
render(h){
return h('div', { ref: 'react' })
},
methods: {
// 渲染react组件到div上边 核心:react要在vue里面跑的核心就是渲染到一个div上
mountReactComponent(component) {
const Component = makeReactComponent(component)
// 处理一下children让react能认识
const children = this.$slots.default !== void 0 ? { children: this.$slots.default } : {}
ReactDOM.render(
React.createElement(
Component,
{ ...this.$attrs, ...this.$listeners, ...children, ref: ref => (this.reactComponentRef = ref) }
),
this.$refs.react
)
},
mounted() {
this.mountReactComponent(this.$props.component)
},
beforeDestroy() {
ReactDOM.unmountComponentAtNode(this.$refs.react)
},
inheritAttrs: false,
watch: {
$attrs: {
deep: true,
handler() {
this.reactComponentRef.setState({...this.$attrs})
}
},
'$props.component': {
handler(newComponent) {
this.mountReactComponent(newComponent)
}
}
// ...
}
}
}
// 判断是否是react组件 如果是 走特殊逻辑
const isReactComponent = component => {
return !(
typeof component === 'function' &&
component.prototype &&
(
(component.prototype.constructor.super && component.prototype.constructor.super.isVue) ||
component.prototype instanceof Vue
)
)
}
// 如果是react组件 处理一下 就是转成 vue组件
// 输入是一个react组件 输出是一个vue组件
const resolver = (component) => {
return {
components: {
ReactWrapper //最终都是在这个组件中完成的
},
props: [],
inheritAttrs: false,
render(h) {
return h ('react-wrapper',
{
props: { component },
attrs: this.$attrs,
on: this.$listeners
}),
this.$slots.default
}
}
}
const ReactRunInVuePlugin = {
install(Vue, options) {
// 保留原始的 components合并策略
const originalComponentOptionMergeStrategies = Vue.config.optionMergeStrategies.components
Vue.config.optionMergeStrategies.components = ( parent, ...args ) => {
const mergedComponentOptions = originalComponentOptionMergeStrategies(parent, ...args)
const wrappedComponents = mergedComponentOptions
// 遍历对象
? Object.entries(mergedComponentOptions).reduce((acc, [k, v]) => ({
...acc,
[k] : isReactComponent(v) ? resolver(v) : v
}), {})
: mergedComponentOptions
// 对象mixin 一下
return Object.assign(mergedComponentOptions, wrappedComponents)
}
Vue.prototype.constructor.isVue = true
}
}
// 在vue中使用react插件
class Button extends React.Component {
render() {
return React.createElement('div', {}, [
React.createElement('h2', {},
React.createElement('em', {}, this.props.children)
)
])
}
}
Vue.use(ReactRunInVuePlugin)
new Vue({
el: document.querySelector('#app'),
components: { 'el-button': Button },
template: `<div>
<h2>在vue里面使用react组件</h2>
<el-button>click me</el-button>
</div>`,
})
在vue项目中全局注册组件的时候,还在一个一个import? 一招教你再也无需import若干个组件了,利用的还是vue插件机制,vue给我们暴露了插件这个口子,至于怎么用,那就是八仙过海各显神通了
const requireComponent = require.context('./', true, /\.vue$/);
const install = (Vue) => {
requireComponent.keys().forEach((fileName) => {
let config = requireComponent(fileName);
let componentName = parseVarNameByFilePath(fileName);
Vue.component(componentName, config.default || config);
});
};
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 前端phil 原文链接:https://juejin.im/post/7008171384098521119