支持复杂场景的vue和react集成引用的JS插件

先上一张牛逼的测试页面截图!!

image.png

测试页面地址是 https://devilwjp.github.io/vuereact-for-cra-demo/build/index.html#/eleInReact
这个测试页充分说明了这个插件强壮的地方,它把react antd和vue element-ui成功的混合在了一起互相套用!并且竟然还能在react里支持vue的v-model双向绑定!

Vue和React快捷集成的工具包,并且适合复杂的集成场景

如何在Vue中嵌入React组件?如何在React中嵌入Vue组件?[下方高能]

image

可以在任何的Vue和React项目中使用另一个类型框架的组件,并且解决了复杂的集成问题

安装

npm i vuereact-combined -S

Why?

让vue和react的同学们一起来完成同一个项目同一个页面甚至同一个组件

  • 使项目的人员选择性和机动性变得更强,vue和react的技术栈都可以加入项目
  • 使项目的第三方插件选择性更强,vue和react的插件都可以通用
  • 使研发人员的技术交流性更强,研发人员不应该被技术栈所限制
  • 使项目可以集成更多的业务代码,其他vue和react项目的优秀代码可以快速引入
  • 使前端研发人员可以更好的学习vue和react,了解两者的精华,促进团队在前端技术栈的广度
  • 使用方式极其简便

遇到的困难

众所周知,React更纯粹,Vue做的封装更多,所以大多数的难度都是集中在react的组件引用vue组件的过程中

支持程度

在react组件中引入vue组件

功能 支持程度 说明
普通属性 完全支持
html片段属性 变向支持 通过$slots,在vue中使用具名插槽获取
render props 变向支持 通过$scopedSlots,在vue中使用作用域插槽获取
children(普通插槽) 完全支持
组件合成事件 完全支持 通过on属性
组件原生事件(.native) 不支持 react没有这种感念,可以自己包囊div
v-model 变向支持 通过$model,并且支持vue组件中随意自定义model属性
context传入vue 暂不支持 未来会支持,当前只有在vue中使用redux做了polyfill
html片段中使用react或者vue组件 完全支持 react组件直接传入,vue组件继续通过applyVueInReact转换
懒加载vue组件 完全支持 通过lazyVueInReact
redux共享 完全支持 使用applyRedux
mobx共享 变向支持 mobx本身就有react和vue的连接方式
vuex共享 完全支持 使用applyVuex
sync装饰 变向支持 使用$sync
事件修饰(key.enter、click.once) 不支持 自行处理
透传 变向支持 使用data-passed-props

在vue组件中引入react组件

功能 支持程度 说明
普通属性 完全支持
具名插槽 完全支持 在react中使用属性获取
作用域插槽 完全支持 在react中使用属性获取,类型是个函数
普通插槽 完全支持
组件合成事件 完全支持 在react中使用属性获取
组件原生事件(.native) 暂不支持
v-model 不支持 react组件没有这个概念
provider/inject传入react 暂不支持 未来会支持
sync装饰 不支持 react组件没有这个概念
redux共享 完全支持 使用applyRedux
mobx共享 变向支持 mobx本身就有react和vue的连接方式
vuex共享 完全支持 使用applyVuex
事件修饰(key.enter、click.once) 不支持 react组件没有这个概念
懒加载react组件 完全支持 通过lazyReactInVue
透传 变向支持 使用data-passed-props

使用前提

项目中要同时安装react和vue的相关环境

如果是通过vue-cli3创建的项目

请参考 https://github.com/devilwjp/vuereact-for-vuecli3-demo

如果通过react-create-app创建的项目(react版本需要>=16.3)

请参考 https://github.com/devilwjp/vuereact-for-cra-demo

安装

npm i vuereact-combined

重要!

由于react hooks的取名规范是use开头,所以将use开头的方法全部修改成了apply开头,老的use开头方法仍然有效

0.3.6新增

sync修饰(applyVueInReact)

在react组件中使用vue组件,如果要使用vue的sync修饰,使用sync

  • 属性名
    ++ value
    ++ setter 纯函数,接收一个值修改state
    render () {
        return (
             {
                  console.log(val)
                  this.setState({
                    test1: val
                  })
                }
              }
            }}/>
        )
      }
    

    applyVueInReact

    在react组件中使用vue的组件

    import React from 'react'
    import VueComponent from '../views/test2.vue' // vue组件
    import { applyVueInReact } from 'vuereact-combined'
    let VueComponentInReact = applyVueInReact(VueComponent)
    class demo1 extends React.Component{
      render(){
        return (
          

    我是普通的插槽

    ) } } export default demo1

    在react组件中,向vue组件传递具名插槽和作用域插槽,以及绑定自定义事件,以及v-model应用
    react本身并不支持v-model,所以需要通过model的value和setter也不需要变化

    import React from 'react'
    import VueComponent from '../views/test2' // vue组件
    import { applyVueInReact } from 'vuereact-combined'
    let VueComponentInReact = applyVueInReact(VueComponent)
    class demo1 extends React.Component{
      constructor (props) {
        super(props)
        this.event1 = this.event1.bind(this)
        this.state = {
          aaa: 1111
        }
      }
      event1 (...args) {
        console.log(args)
      }
      render(){
        return (
          
    插槽A
    , slotB:
    插槽B
    }} $scopedSlots={{ slotC: (context) =>
    我是作用域插槽:{context.value}
    }} $model={{ value: this.state.aaa, // value必须是一个state setter: (value) => { this.setState({ aaa: value }) } // setter必须是直接修改state }}>

    我是普通的插槽

    ) } } export default demo1

    test2.vue

    
    
    
    

    VueContainer

    在react组件动态引用vue组件,类似vue的

    import React from 'react'
    import VueComponent from '../views/test2' // vue组件
    import { VueContainer } from 'vuereact-combined'
    class demo1 extends React.Component{
      constructor (props) {
        super(props)
        this.event1 = this.event1.bind(this)
      }
      event1 (...args) {
        console.log(args)
      }
      render(){
        return (
          
    插槽A
    , slotB:
    插槽B
    }} $scopedSlots={{ slotC: (context) =>
    我是作用域插槽:{context.value}
    }}>

    我是普通的插槽

    ) } } export default demo1

    在react中使用vue的全局注册组件

    与react不同,vue有全局注册组件的功能,使每个组件不需要再单独引入
    将vue全局组件的id作为参数传入applyVueInReact中,或者将id作为component属性的值传入VueContainer中
    示例:在react中使用全局的vue版本element-ui的DatePicker

    const ElDatePickerInReact = appluVueInReact('ElDatePicker') // 将el-date-picker转换成ElDatePicker就是id
    // 或者
    
    

    applyReactInVue

    在Vue的组件中使用React组件

    
    
    
    

    在Vue组件中,向React组件传递具名插槽和作用域插槽,以及绑定自定义事件
    由于React没有插槽的概念,所有都是以属性存在,Vue的具名插槽和作用域插槽会被转化为React的属性,其中作用域插槽会转换成render props的方式
    并且Vue组件的事件也会被转化成React的属性

    
    
    
    
    

    cc.jsx

    import React from 'react'
    class cc extends React.Component {
      constructor (props) {
        super(props)
        this.state = {
          slotValue: {
            value: '本地作用域'
          }
        }
      }
      componentDidMount () {
        this.props.event1(11,22)
      }
      render () {
        return (
          
    我是React组件
    属性1:{this.props.prop1}
    属性2:{this.props.prop2}
    {this.props.slotA} {this.props.children} {this.props.slotB} {this.props.slotC(this.state.slotValue)}
    ) } } export default cc

    applyReactInVue的复杂案例

    比如react版本的antd的Card组件,在react中的使用示例如下

    render () {
        return (More}>
                 

    Card content

    Card content

    Card content

    ) }

    react版本的antd,在vue组件中使用的示例如下

    
        
        
        

    Card content

    Card content

    Card content

    applyRedux

    作用:使得所有的Vue组件可以使用redux的状态管理
    对工具包开启redux状态管理,这个场景一般存在于以React为主的项目中,为了使Vue组件也可以共享到redux,需要在项目的入口文件引入applyRedux方法(整个项目应该只引一次),将redux的store以及redux的context作为参数传入(或者至少在redux的Provider高阶组件引入的地方使用applyRedux方法)

    // 第二个参数是redux的context,之所以需要传第二个参数,是因为有如下场景
    // Provider -> ReactCom1 -> VueCom1 -> ReactCom2
    // Provider无法直接透过Vue组件传递给之后的React组件,所以applyRedux提供了第二个参数,作用就是可以使通过Vue组件之后的React组件继续可以获取到redux的context
    import { ReactReduxContext } from 'react-redux'
    import store from '../reactComponents/reduxStore'
    applyRedux({ store, ReactReduxContext })
    

    store.js

    // 原生的redux store的创建方式
    import { createStore } from 'redux'
    import someCombineReducer from './reducer' // 建议通过react-redux的combineReducer输出
    let store = createStore(someCombineReducer)
    export default store
    

    React组件连接redux的方式这里就不再做介绍了,应该使用react-redux的connect方法
    这里介绍Vue组件如何使用redux,工具包尽可能的实现了vue组件使用vuex的方式去使用redux,通过vm.$redux可以在组件实例里获取到redux状态管理

    
    
    
    

    applyVuex

    作用:使得所有的Redux组件可以使用Vuex的状态管理
    对工具包开启vuex状态管理,这个场景一般存在于以Vue为主的项目中,为了使React组件也可以共享到vuex,需要在项目的入口文件引入applyVuex方法(整个项目应该只引一次),将vuex的store作为参数传入

    import store from '../store' // vuex的store文件
    applyVuex(store)
    

    connectVuex

    类似react-redux的connect方法,在React组件中使用,由于vuex的关键字比redux多,所以将参数改成了对象,包含了mapStateToProps、mapCommitToProps、mapGettersToProps、mapDispatchToProps,每个都是一个纯函数,返回一个对象(和redux的connect使用方式完全一致)

    export default connectVuex({
      mapStateToProps (state) {
        return {
          vuexState: state,
          state1: state.state1,
          moduleAstate: state.moduleA
        }
      },
      mapCommitToProps (commit) {
        return {
          vuexCommit: commit
        }
      },
      // mapGettersToProps = (getters) => {},
      // mapDispatchToProps = (dispatch) => {},
    })(ReactComponent)
    

    lazyVueInReact

    在React的router里懒加载Vue组件

    import React, { lazy, Suspense } from "react"
    import { lazyVueInReact } from 'vuereact-combined'
    const Hello = lazy(() => import("./react_app/hello"));
    //懒加载vue组件
    const TestVue = lazyVueInReact(() => import("./vue_app/test.vue"))
    
    
    export default [
    {
        path: "/reactHello",
        component: () => {
            return (
                Loading...}>
                    
                
            );
        }
    },
    {
        path: "/vuetest1",
        component: () => {
            return (
                Loading...}>
                    

    我是一个vue组件

    ); } }]

    lazyReactInVue

    在Vue的router里懒加载React组件

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import { lazyReactInVue } from 'vuereact-combined'
    Vue.use(VueRouter)
    
    const routes = [
      {
        path: '/',
        name: 'home',
        component: () => import('../views/Home')
      },
      {
        path: '/reactInVueDemo',
        name: 'reactInVueDemo',
        component: lazyReactInVue(() => import('../reactComponents/cc.jsx'))
      }
    ]
    
    const router = new VueRouter({
      routes
    })
    
    export default router
    

    v0.3.3新增

    data-passed-props(透传)

    每个通过applyVueInReact的的vue组件,以及通过applyReactInVue的react组件,都可以收到一个data-passed-props的属性,这个属性可以帮助你不做任何包装的,被之后再次使用applyVueInReact或applyReactInVue的组件收到全部的属性(由于是跨框架透传,原生的透传方式并不会自动做相应的封装和转换)

    // react组件透传给vue组件
    const VueComponent = applyVueInReact(require('./anyVueComponent'))
    class theReactComponentFromVue extends React.Component{
        render () {
            return 
        }
    }
    
    
    
    

    需要注意的包囊性问题

    由于在每一次跨越一个框架进行组件引用时,都会出现一层包囊,这个包囊是以div呈现,并且会被特殊属性标注
    React->Vue,会在vue组件的dom元素外包囊一层标识data-use-vue-component-wrap的div
    Vue->React,会在react组件的dom元素外包囊一层标识__use_react_component_wrap的div
    如果引发样式问题,可以全局对这些标识进行样式修正

    你可能感兴趣的:(支持复杂场景的vue和react集成引用的JS插件)