Skip to content
New issue

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

[RFC] mobx-vue v3 #60

Closed
iChenLei opened this issue Apr 28, 2021 · 18 comments
Closed

[RFC] mobx-vue v3 #60

iChenLei opened this issue Apr 28, 2021 · 18 comments

Comments

@iChenLei
Copy link
Member

iChenLei commented Apr 28, 2021

Introduction

Since mobx v6 and vue v3 was released for a while, it's time to support mobx v6 and vue v3.

Current Feature Request

Vue 3 Compatibility #50
Module Browser Build #49
Can I use mobx-vue without decorated class properties? #47
Add Documentation re Optimizing Rendering #36
Nuxt support #26 #32

Deprecate vue-class-component

Thoughts about mobx-vue #3 (comment)

I'm kinda wondering what direction this project might take with a "new vue" on the horizon. Since vue seems to move away from classes and therefore decorators is there a happy path to using mobx with vue in 2020?

vue-class-component has their own problem
vuejs/vue-class-component#406

Naive proto design (only for vue3)

No.1 idea

export a vue plugin for vue3

import { createApp } from 'vue';
import MobxVuePlugin from 'mobx-vue';
import App from './App.vue';

createApp(App)
    .use(MobxVuePlugin, { name: 'MyObserver' /** optional Observer component name */ })
    .mount('#app');

Using Observer Component in your app

<template>
  <Observer>
    <div>name: {{data.name}}</div>
    <button @click="changeName">change</button>
  </Observer>
</template>

<script setup>
  import { runInAction, observable } from mobx;
  const data = observable({ name: "iChenLei" });
  const changeName = () => {
   runInAction(() => { data.name = "Kuitos" });
 };
</script>

No.2 idea

export a observer function to hack vue export default, no need to install as plugin, you can use it directly.

<template>
  <UserComponent />
  <ProfileComponent />
</template>

<script>
  import { observer } from 'mobx-vue';
  export default observer({
      setup() {
        // balabala
      }
  });
</script>

<!-- use @ decorator -->
<script>
  import { observer } from 'mobx-vue';
  @observer
  export default {
      setup() {
        // balabala
      }
  };
</script>

But it not easy to do this , we need hack the vue @internel (e.g. $, $options, $forceUpdate)

// vue 3 internal component properties map
const publicPropertiesMap: PublicPropertiesMap = extend(Object.create(null), {
  $: i => i,
  $el: i => i.vnode.el,
  $data: i => i.data,
  $props: i => (__DEV__ ? shallowReadonly(i.props) : i.props),
  $attrs: i => (__DEV__ ? shallowReadonly(i.attrs) : i.attrs),
  $slots: i => (__DEV__ ? shallowReadonly(i.slots) : i.slots),
  $refs: i => (__DEV__ ? shallowReadonly(i.refs) : i.refs),
  $parent: i => getPublicInstance(i.parent),
  $root: i => getPublicInstance(i.root),
  $emit: i => i.emit,
  $options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type),
  $forceUpdate: i => () => queueJob(i.update),
  $nextTick: i => nextTick.bind(i.proxy!),
  $watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP)
} as PublicPropertiesMap)

And when you use <script setup>, it's also diffcult to implement a useful observer function.(any suggestion for this welcome)

No.3 idea

add the template sugar like <template pug>, <template functional>.

<tempalte observer>
  <UserProfile />
<template>

It looks like best solution for mobx-vue and vue sfc? But it need mobx-vue maintainer to maintian different tools to support this feature. (e.g. mobx-vue-loader for webpack, rollup-plugin-mobx-vue for vite and rollup, ...etc). Not a reliable choice i think. I didn't see any api allow us to change the <template> behavior. It's parsed by @vue/compiler-sfc and no public api.

My opinion

Deprecate vue-class-component and make vue sfc as first class support.

Summary

Any idea and disscussion welcome, maybe you can design the final mobx-vue v3 api.

@iChenLei iChenLei pinned this issue Apr 28, 2021
@kuitos
Copy link
Member

kuitos commented May 10, 2021

vote for idea 2, but don't need to support decorator any more.

@Cyberhan123
Copy link

Cyberhan123 commented May 10, 2021

vote for idea 2, but don't need to support decorator any more.

yes,idea 2 is good,and there some different idea,

<script>
  import { observer } from 'mobx-vue';
  export default observer({
      setup() {
        // balabala
      }
  });
</script>

<!-- use <script setup> -->
<script setup>
  import { observer } from 'mobx-vue';

export const state= observer({
 // balabala
})
//or
export default observer({
 // balabala
})
</script>

@voyageh
Copy link

voyageh commented Jun 11, 2021

import { createApp } from 'vue';
import store from './store';
import App from './App.vue';
createApp(App)
.use(store)
.mount('#app');


<!-- use <script > -->
 import { observer,inject } from 'mobx-vue'; 
 @Inject('store') 
 @observer
 export default defineComponent({
})
//or
export default observer({
   setup(props) {
      console.log(props) // {store:{}}
    }
 })

@steven-sheehy
Copy link

Please don't deprecate vue-class-component support. It's the only reason I use mobx-vue. If you want to support composition API in addition to classes that would be fine. You could release the composition support now and do another release later when vue-class-component finalizes v3 support.

@iChenLei
Copy link
Member Author

@steven-sheehy Wow, I really don't know vue-class-component support is so important for you. I get your feedback and rethink vue-class-component support.

@RobinRadic
Copy link

I would really like to use mobx-vue & vue-class-component with vue3. The code is very clear and understandable for non-js programmers in the team.

@Cyberhan123
Copy link

Cyberhan123 commented Jun 25, 2021

我真的很想将mobx-vue&vue-class-component与 vue3一起使用。代码对于团队中的非js程序员来说非常清晰易懂。

但是我感觉吧,vue3的类hook特性,使得store层变得不是特别刚需

@songgnqing
Copy link

vue3.0 不需要mobx,vue自带的reactivity可以完全替代掉mobx。

// store
export class BaseStore {
  constructor() {
    return reactive(this);
  }
}

// module like 
class UserStore extends BaseStore {
  name = ''
}

export const user = new UserStore()

// index
export class StoreCenter extends BaseStore {
  token = ''
  user = user
}

export const store = new StoreCenter();

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $store: StoreCenter
  }
}

export default {
  install: (app: App) => {
    app.config.globalProperties.$store = store
  }
}

在component中用computed来引入store的数据就可以了。

@murraybauer
Copy link

I vote for:

  • Idea 2 - without the @ decorator - so support something similar to match the React approach.
  • Idea 3 - as need 1st class Vue.js SFC support as well.

@murraybauer
Copy link

Also some food for thought of how mobx-jsx handels this:

@LengYXin
Copy link

LengYXin commented Aug 3, 2021

别启用 类语法和装饰器吧。类语言可以很好地封装继承,装饰器是未来趋势,而且很好用

@LengYXin
Copy link

LengYXin commented Aug 3, 2021

我真的很想将mobx-vue&vue-class-component与 vue3一起使用。代码对于团队中的非js程序员来说非常清晰易懂。

但是我感觉吧,vue3的类hook特性,使得store层变得不是特别刚需

我觉得也是。class 语法对于后端开发人员真的非常友好,我就是一个写过C# ,Java 后面转型js 的在搭建工程时会用后端开发模式把React,Vue设计成后端工程结构,后面后端开发人员都不需要懂React,Vue这些都能很好修改代码,专注业务

@sergeydorovsky
Copy link

I'm currently using MobX 6 with Vue 3. Here is the plugin I wrote (it's actually Quasar boot file):
Works like a charm. Hope this helps.

import { ComponentPublicInstance } from '@vue/runtime-core'
import { Reaction } from 'mobx'
import { boot } from 'quasar/wrappers'

const reactionKey = '__mobx_reaction__'

interface MobxComponent extends ComponentPublicInstance {
  [reactionKey]: Reaction
}

function getComponentName(vm: ComponentPublicInstance) {
  // @ts-ignore
  return (vm.$options.name || vm.tag || '<observed-component>') as string
}

export default boot(({ app }) => {
  app.mixin({
    created(this: MobxComponent) {
      const name = getComponentName(this)
      const reaction = new Reaction(`${name}::mobx-reaction`, () => {
        if (this.$.isMounted) {
          this.$forceUpdate()
        }
      })

      this[reactionKey] = reaction
      // @ts-ignore
      const originalRender = this.$.render

      // @ts-ignore
      this.$.render = function(...args: any) {
        let renderResult: unknown

        reaction.track(() => {
          renderResult = originalRender.call(this, ...args)
        })

        return renderResult
      }
    },
    beforeUnmount(this: MobxComponent) {
      this[reactionKey].dispose()
    },
  })
})

@wobsoriano
Copy link

wobsoriano commented Aug 12, 2021

Hey just released a really simple mobx-react-lite inspired library for Vue 3

https://github.com/wobsoriano/mobx-vue-lite

  • useLocalObservable
  • <Observer></Observer>
  • createGlobalObservable

@iChenLei
Copy link
Member Author

@wobsoriano Are you interested in mobx-vue v3 design ? or do you want transfer your mobx-vue-lite to mobxjs org ? It looks like good (mobx-vue-lite), but no test case ci, so we can do it better. what's your opinion ?

@wobsoriano
Copy link

@wobsoriano Are you interested in mobx-vue v3 design ? or do you want transfer your mobx-vue-lite to mobxjs org ? It looks like good (mobx-vue-lite), but no test case ci, so we can do it better. what's your opinion ?

I'm fine moving it to mobxjs org

@LengYXin
Copy link

LengYXin commented Sep 2, 2021

有一个很神奇的现象。就是在v3里面的 mobx 类
外面套一个 reactive 或者直接在 vue组件内 new 的话,这个mobx是正常工作的。

@Options({
  components: {},
})
export default class Home extends Vue {
  System=new SystemController();
}
//或者
config.globalProperties.System = reactive(new SystemController())

仓库地址:https://github.com/LengYXin/mamba/tree/dev/packages/micro

https://github.com/LengYXin/mamba/blob/dev/packages/micro/src/components/user.vue
还用微前端方式传递到React 夸框架也正常使用。

@iChenLei
Copy link
Member Author

closed via mobxjs/mobx-vue-lite#1

@iChenLei iChenLei unpinned this issue Sep 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests