Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit af44a6d

Browse files
committed
使用portal-vue处理vue的单例继承
1 parent 9b6398f commit af44a6d

File tree

2 files changed

+82
-20
lines changed

2 files changed

+82
-20
lines changed

src/applyReactInVue.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ const createReactContainer = (Component, options, wrapInstance) => class applyRe
119119
return this.state.children || this.props.children
120120
}
121121
if ((Object.getPrototypeOf(Component) !== Function.prototype && !(typeof Component === "object" && !Component.render)) || applyReact.catchVueRefs()) {
122-
refInfo.ref = this.setRef
123122
return (
124123
<Component {...props}
125124
{...{ "data-passed-props": __passedProps }} {...refInfo}>
@@ -142,6 +141,11 @@ export default function applyReactInVue(component, options = {}) {
142141
// 处理附加参数
143142
options = setOptions(options, undefined, true)
144143
return {
144+
data() {
145+
return {
146+
portals: []
147+
}
148+
},
145149
created() {
146150
// this.vnodeData = this.$vnode.data
147151
this.cleanVnodeStyleClass()
@@ -156,9 +160,12 @@ export default function applyReactInVue(component, options = {}) {
156160
render(createElement) {
157161
this.slotsInit()
158162
const { style, ...attrs } = options.react.componentWrapAttrs
159-
return createElement(options.react.componentWrap, { ref: "react", attrs, style })
163+
return createElement(options.react.componentWrap, { ref: "react", attrs, style }, this.portals.map((Portal, index) => Portal(createElement, index)))
160164
},
161165
methods: {
166+
pushVuePortal(vuePortal) {
167+
this.portals.push(vuePortal)
168+
},
162169
// hack!!!! 一定要在render函数李触发,才能激活具名插槽
163170
slotsInit() {
164171
Object.keys(this.$slots).forEach((key) => {
@@ -351,7 +358,7 @@ export default function applyReactInVue(component, options = {}) {
351358
reactRootComponent,
352359
container
353360
)
354-
reactWrapperRef.pushPortal(this.reactPortal)
361+
reactWrapperRef.pushReactPortal(this.reactPortal)
355362
return
356363
}
357364
ReactDOM.render(

src/applyVueInReact.js

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import applyReactInVue from './applyReactInVue'
55
import vueRootInfo from './vueRootInfo'
66
import { reactRouterInfo, setReactRouterInVue, updateReactRouterInVue } from './applyReactRouterInVue'
77
import globalOptions, {setOptions} from './options'
8+
import ReactDOM from "react-dom";
9+
import {MountingPortal} from 'portal-vue'
810

911
const unsafePrefix = parseFloat(version) >= 17 ? 'UNSAFE_' : ''
1012
const optionsName = 'vuereact-combined-options'
@@ -82,9 +84,9 @@ class VueComponentLoader extends React.Component {
8284
this.vueComponentContainer = this.createVueComponentContainer()
8385
}
8486

85-
pushPortal (portal) {
87+
pushReactPortal (reactPortal) {
8688
const { portals } = this.state
87-
portals.push(portal)
89+
portals.push(reactPortal)
8890
this.setState({ portals })
8991
}
9092

@@ -128,6 +130,13 @@ class VueComponentLoader extends React.Component {
128130
}
129131

130132
componentWillUnmount () {
133+
// 删除portal
134+
if (this.vuePortal) {
135+
const { portals } = this.parentVueWrapperRef
136+
const index = portals.indexOf(this.vuePortal)
137+
portals.splice(index, 1)
138+
return
139+
}
131140
this.vueInstance.$destroy()
132141
}
133142

@@ -228,14 +237,25 @@ class VueComponentLoader extends React.Component {
228237
}
229238
return scopedSlots
230239
}
240+
241+
function setVueInstance(instance) {
242+
if (!this.vueInstance) {
243+
this.vueInstance = instance
244+
}
245+
}
246+
setVueInstance = setVueInstance.bind(this)
231247
// 将vue组件的inheritAttrs设置为false,以便组件可以顺利拿到任何类型的attrs
232248
// 这一步不确定是否多余,但是vue默认是true,导致属性如果是函数,又不在props中,会出警告,正常都需要在组件内部自己去设置false
233249
// component.inheritAttrs = false
234-
// 创建vue实例
235-
this.vueInstance = new Vue({
250+
const vueOptionsData = { ...this.doSync(this.doVModel(props)), 'data-passed-props': __passedProps }
251+
const vueOptions = {
236252
...vueRootInfo,
237-
el: targetElement,
238-
data: { ...this.doSync(this.doVModel(props)), 'data-passed-props': __passedProps },
253+
data() {
254+
return vueOptionsData
255+
},
256+
created() {
257+
setVueInstance(this)
258+
},
239259
methods: {
240260
// 获取具名插槽
241261
// 将react组件传入的$slots属性逐个转成vue组件,但是透传的插槽不做处理
@@ -397,23 +417,58 @@ class VueComponentLoader extends React.Component {
397417
// 这一步有点繁琐,但是又必须得处理
398418
const attrs = filterAttrs({ ...lastProps })
399419
return createElement(
400-
'use_vue_wrapper',
401-
{
402-
props: lastProps,
403-
on: lastOn,
404-
nativeOn,
405-
attrs,
406-
'class': className,
407-
style,
408-
scopedSlots: { ...scopedSlots }
409-
},
410-
lastSlots
420+
'use_vue_wrapper',
421+
{
422+
props: lastProps,
423+
on: lastOn,
424+
nativeOn,
425+
attrs,
426+
'class': className,
427+
style,
428+
scopedSlots: { ...scopedSlots }
429+
},
430+
lastSlots
411431
)
412432
},
413433
components: {
414434
'use_vue_wrapper': component
415435
}
436+
}
437+
438+
if (!targetElement) return
439+
440+
Vue.nextTick(() => {
441+
const targetId = '__vue_wrapper_container_' + (Math.random() + '').substr(2)
442+
targetElement.id = targetId
443+
let parentInstance = this._reactInternals.return
444+
let vueWrapperRef
445+
// 向上查找react包囊层
446+
while (parentInstance) {
447+
if (parentInstance.stateNode?.parentVueWrapperRef) {
448+
vueWrapperRef = parentInstance.stateNode.parentVueWrapperRef
449+
break
450+
}
451+
if (parentInstance.stateNode?.vueWrapperRef) {
452+
vueWrapperRef = parentInstance.stateNode.vueWrapperRef
453+
break
454+
}
455+
parentInstance = parentInstance.return
456+
}
457+
// 如果存在包囊层,则激活portal
458+
if (vueWrapperRef) {
459+
// 存储包囊层引用
460+
this.parentVueWrapperRef = vueWrapperRef
461+
// 存储portal引用
462+
this.vuePortal = (createElement, key) => createElement(MountingPortal, {props: {mountTo: '#' + targetId, slim:true, targetSlim: true}, key}, [createElement(vueOptions)])
463+
vueWrapperRef.pushVuePortal(this.vuePortal)
464+
return
465+
}
466+
467+
468+
// 创建vue实例
469+
this.vueInstance = new Vue({...vueOptions, el: targetElement})
416470
})
471+
417472
}
418473

419474
updateVueComponent (nextComponent) {

0 commit comments

Comments
 (0)