npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-vue-like

v0.2.0

Published

write react component like vue

Downloads

101

Readme

react-vue-like

NPM version NPM downloads

write react component like vue, implementation based on mbox@4.

Table of Contents:

Installation

npm install react-vue-like --save
# or
yarn add react-vue-like

Support Vue feature

props

will transfrom to react's propTypes and defaultProps

see Vue props

example:

class Test extends ReactVueLike.Component {
  static props = {
    aa: {
      type: String,
      default: 'aa',
      required: true,
    },
    bb: Boolean,
  }
}

equals:

import PropTypes from 'prop-types';
class Test extends ReactVueLike.Component {
  static propTypes = {
    aa: PropTypes.string.isRequired,
    bb: PropTypes.bool
  }
  static defaultProps = {
    aa: 'aa'
  }
}

components

if tag name has - char will be treat as a component that find from self's components section or root's components section.

see Vue components

example:

import AComponent from './AComponent';

class Test extends ReactVueLike.Component {
  static components = {
    AComponent
  }

  render() {
    return (<a-component>dd</a-component>)
  }
}

filters

example:

class Test extends ReactVueLike.Component {
  static filters = {
    prefix(val, suffix = '') {
      return `test:${val}${suffix}`;
    }
  }

  render() {
    return (<div>
      {// output: test:hello }
      { 'hello' | 'test' }  
      {// output: test:helloa suffix }
      { 'hello' | 'test'('a suffix') }
      </div>);
  }
}

directives

see Vue directives

example:

class Test extends ReactVueLike.Component {
 static directives = {
   test: {
     bind(el, binding, vnode) {

     },
     insert(el, binding, vnode) {

     },
     update(el, binding, vnode) {

     },
     unbind(el, binding, vnode) {

     },
   }
 }

 render() {
   return (<div v-test_arg$aa$bb={1+1} {/* or */} v-test:arg$aa$bb={1+1} ></div>);
 }
}

v-test_arg$aa$bb={1+1}, the binding will be:

 { 
   name: 'test', 
   arg: 'arg',
   value: 2,
   modifiers: {
     aa: true,
     bb: true
   },
   expression: '1+1'
}

mixin

see Vue mixin example:

class Test extends ReactVueLike.Component {
  static mixins = [
    {
      data() {
        return {
          text: 'aa',
        };
      },
      methods: {
        test() {
          console.log('test');
        }
      }
    }
  ]
  

  render() {
    return (<div>
      <span onClick={this.test}>{this.text}</span>
    </div>);
  }
}

data

see Vue data example:

class Test extends ReactVueLike.Component {
  static data() {
    return {
      text: 'aa',
      formData: {
        name: 'dddd'
      }
    }
  }

  render() {
    return (<div>
       <span>{this.text}</span>
       <span>{this.formData.name}</span>
      </div>);
  }
}

methods

see Vue methods

example:

class Test extends ReactVueLike.Component {
  static methods = {
    test1() {
      console.log('test1');
    }
  }

  test2() {
     console.log('test2');
  }

  render() {
    return (<div>
       <span onClick={this.test1}>aa</span>
       <span onClick={this.test2}>dd</span>
      </div>);
  }
}

computed

see Vue computed

example:

class Test extends ReactVueLike.Component {
  static data() {
    return {
      text: 'aa',
    }
  }

  static computed = {
    test1() {
      return `test1:${this.text}`;
    },
    test2: {
      get() {
        return `test2:${this.text}`;
      },
      set(v) {
        this.text = v;
      }
    }
  }

  render() {
    return (<div>
      <span>{this.text}</span>
      <span>{this.test1}</span>
      <span onClick={() => this.test2 = 'bb'}>{this.test2}</span>
      </div>);
  }
}

watch

see Vue watch

example:

class Test extends ReactVueLike.Component {
  static data() {
    return {
      text: 'aa',
    }
  }

  static watch = {
    text(newVal, olVal) {
      console.log('text chagned', newVal, oldVal);
    },
  }

  render() {
    return (<div>
      <span>{this.text}</span>
      <button onClick={() => this.text = 'bb'}>change</button>
      </div>);
  }
}

lifecycle

see Options-Lifecycle-Hooks

example:

class Test extends ReactVueLike.Component {

  breforeCreate() { }

  created() { }

  beforeMount() { }

  mounted() { }

  beforeUpdate() {  }

  updated() { }

  beforeDestroy() { }

  render() {
    return (<div>haha</div>);
  }

}

scoped style

if import's style file name has ?scoped, then it will treat as scoped style

example:

.aa .bb {
  height: 100%;
}

.aa .bb >>> .cc {
  background-color: red;
}

.aa .bb:scope > .cc {
  background-color: red;
}

:global .aa .bb {
  background-color: red;
}
import React from 'react';
import ReactVueLike from 'react-vue-like';

import './test.scss?scoped';

class Test extends ReactVueLike.Component {

  render() {
    return (<div className="aa">
      haha
      <span>dd</span>
      <a-component className="bb" />
    </div>);
  }

}

will transform to like this:

.aa .bb.v-123dse43 {
  height: 100%;
}

.aa .bb.v-123dse43 .cc {
  background-color: red;
}

.aa .bb.v-123dse43 > .cc {
  background-color: red;
}

.aa .bb {
  background-color: red;
}
import React from 'react';
import ReactVueLike from 'react-vue-like';

import './test.scss?scoped';

class Test extends ReactVueLike.Component {

  render() {
    return (<div className="v-123dse43 aa">
      haha
      <span className="v-123dse43">dd</span>
      <a-component className="v-123dse43 bb" />
    </div>);
  }

}

slot

example:

import React from 'react';
import ReactVueLike from 'react-vue-like';

class ChildComponent extends ReactVueLike.Component {

  render() {
    return (<div>
      <slot name="header">
      haha1
      {
        [1, 2, 3].map(v => <slot value={v} user={user} />)
      }
      haha2
      <slot name="footer">
    </div>);
  }

}
import React from 'react';
import ReactVueLike from 'react-vue-like';
import ChildComponent from './ChildComponent';

class ParentComponent extends ReactVueLike.Component {

  render() {
    return (<ChildComponent>
      {/* if child-component is `ReactVueLike Component` then it will has `$slots: { header, default, footer }` */}
      {/* if child-component is `React Component` then it will has `header, footer` attributes, default slot will be it's 'children'  */}
      <span slot="header">this is header</span>

      {/* scoped slot */}
      <template>
        ({ value, user }) => <span>this is body: {user.name}: {value}</span>
      </template>

      <span slot="footer">this is footer</span>
    </ChildComponent>);
  }

}

Vue Internal Directives

v-if/v-else-if/v-else,v-show,v-model, v-html see Vue Directives

example:

import React from 'react';
import ReactVueLike from 'react-vue-like';

class Test extends ReactVueLike.Component {

  static data() {
    return {
      vif: true,
      vshow: true,
      text1: '',
      text2: 0,
      text3: ''
    }
  }

  render() {
    return (<div>
      <span v-if={this.vif}>v-if showing</span>
      <span v-else>else showing</span>

      <span v-show={this.vshow}>
        v-show showing
      </span>

      <input v-model$trim={this.text1}></input>
      <input type="number" v-model$number={this.text2}></input>
      <input v-model$lazy={this.text3}></input>

      {/* equals: dangerouslySetInnerHTML={{ __html: "<a href='#'>dd</a>" }} */}
      <span v-html="<a href='#'>dd</a>"></span>
    </div>);
  }

}

Vuelike Internal Directives

v-observer see Mobx Observer

example:

import React from 'react';
import ReactVueLike from 'react-vue-like';

class Test extends ReactVueLike.Component {

  render() {
    return (<div>
      {/* will transform <Observer>{() => <span v-observer>ddd</span>}</Observer> */}
      <span v-observer>ddd</span>
      {/* will transform <Observer render={() => <span v-observer>ddd</span>}</Observer>} /> */}
      <span v-observer$render>ddd</span>
    </div>);
  }

}

Event Mechanism

you can use $emit to send event message to bind Event that bind by $on or onXXXX event. see Instance-Methods-Events

import React from 'react';
import ReactVueLike from 'react-vue-like';
import ChildComponent from './ChildComponent';

class ParentComponent extends ReactVueLike.Component {

  mounted() {
    this.$on('change-user', (user) => {
      console.log('user changed', user);
    })
  }

  handleClick() {
    console.log('you click');
  }

  handleCusomEvent(message) {
    console.log('handing custom event...', message);
  }

  render() {
    return (<ChildComponent onClick={this.handleClick} onCustomEvent={this.handleCusomEvent}>
    </ChildComponent>);
  }

}
import React from 'react';
import ReactVueLike from 'react-vue-like';

class ChildComponent extends ReactVueLike.Component {

  render() {
    return (<div>
      <button onClick={this.$emit('click')}>click</button>
      <button onClick={this.$parent.$emit('change-user', { name: 'james' })}>change user</button>
      <button onClick={this.$emit('custom-event', 'something')}>cusom event</button>
    </div>);
  }

}

attribute transform

img src attribute string value transform to require expression example:

class Test extends ReactVueLike.Component {

  render() {
    return (<div>
      { /* src will transform to `require('./image/pic1.png')` */ }
      <img src="./image/pic1.png">
    </div>);
  }

}

ref

string ref transform to ref function and set ref to $refs. seevue ref

class Test extends ReactVueLike.Component {

  static data() {
    return {
      list: [
        { key: 'a', value: 1 },
        { key: 'b', value: 2 },
        { key: 'c', value: 3 },
      ]
    }
  }
  
  test() {
    this.$refs.some.doSomething();
  }
 
  render() {
    return (<div>
      { /* 
      if ref value is string, then ref value will transform to `ref=>this.$refs['some']=ref`, otherwise do nothing.
      */ }
      <SomeComponent ref="some" onClick={this.test}></SomeComponent>

      {
        this.list.map((v, i/*if not has second param, will auto inject `$index` */) => {
          {/* support Array.map/filter/sort/slice/reverse */}
          return [
            {/* 
              will transform to:
              ref=> {
                if (!this.$refs['item1']) this.$refs['item1'] = [];
                this.$refs['item1'][$index] = ref;
              }
            */}
            <span ref="item1" key={v.key}>{v.value}</span>,

            {/* 
              will transform to:
              ref=> {
                if (!this.$refs['item1']) this.$refs['item1'] = {};
                this.$refs['item1'][v.key] = ref;
              }
            */}
            <span ref$key="item2" key={v.key}>{v.value}</span>,
          ]
        })
      }
    </div>);
  }

}

Vue like props

like $el,$options,$parent,$root,$refs,$slots,$attrs. seeInstance-Properties

Vue like methods

like $nextTick,$set,$delete,$watch,$emit,$on,$off,$once,renderError, ReactVueLike.use, ReactVueLike.configure. see Instance-Methods-Data and Instance-Methods-Events

Attrs Inheirt

default ReactVueLike component will inherit className, style, id, disabled attributes that be defined in it`s parent component

Class Attribute Support And Enhance

class attribute in jsx will transfrom to className, and now class/className support String/Array/Object types. see Vue class

class Test extends ReactVueLike.Component {
  
  static data() {
    return {
      myClass: {
        cc: true,
        dd: false
      }
    }
  }
 
  render() {
    return (<div class="root">
      <span className={['aa', 'bb', this.myClass, ['ee', 'ff'] ]}></span>
    </div>);
  }

}

Prop And Event Modifiers

see: v-model v-bind v-no

import Child from './child';

class Test extends ReactVueLike.Component {
 
  render() {
    return (<div>
      {/* equals <div aa={this.aa} onChangeAa={v=>this.aa=v}></div> */}
      <Child aa$sync={this.aa}></Child>
      <div onClick$stop={this.test}></div>
      <div onClick$prevent={this.test}></div>
      <div onClick$capture={this.test}></div>
      <div onClick$self={this.test}></div>
      <div onClick$native={this.test}></div>
      <div onClick$once={this.test}></div>
      <div onClick$left={this.test}></div>
      <div onClick$right={this.test}></div>
      <div onClick$middle={this.test}></div>
      <div onClick$passive={this.test}></div>
      <div onClick$enter={this.test}></div>
      <div onClick$13={this.test}></div>
    </div>);
  }

}
class Child extends ReactVueLike.Component {
 
  changeAa() {
    this.$emit('change:aa', 1);
  }

  render() {
    return (<div>
      <button onClick={this.changeAa}></button>
    </div>);
  }

}

Provide Inject

see provide/inject

import React from 'react';
import ReactVueLike from 'react-vue-like';
import ChildComponent from './ChildComponent';

class ParentComponent extends ReactVueLike.Component {

  static provide() {
    return {
      text: this.formData
    }
  }

  static data() {
    return {
      formData: {
        text: '111'
      }
    }
  }

  render() {
    return (<ChildComponent>
    </ChildComponent>);
  }

}
import React from 'react';
import ReactVueLike from 'react-vue-like';

class ChildComponent extends ReactVueLike.Component {

  static inject = ['formData'];

  render() {
    return (<div>{this.formData.text}</div>);
  }

}

Vuex Store

support Vuex.Store and mapState,mapMutations,mapGetters,mapActions,createNamespacedHelpers. see Vuex

Note: Store was moved to react-vuex-like.

store like Vuex.Store:

import { Store } from 'react-vuex-like';

const store = new Store({
  modules: {
    child1: {
      state: {
        aa: true
      }
    },
    child2: {
      state: {
        bb: true
      }
    }
  },
  state: {
    user: {
      name: 'name1'
    },
  },
  getters: {
    aa(state) {
      return state.globalLoading;
    }
  }
  mutations: {
    'update-user'(state, v) {
      state.user = v;
    },
    'update-user-info'(state, v) {
      Object.keys(v).forEach(key => state.user[key] = v[key]);
    }
  },
  actions: {
    'update-user-info'({ commit }, v) {
      commit('update-user', v);
    }
  },
});

export default store;

Other feature

Const Var

support __filename, __dirname, __packagename, __packageversion, __now

Instance Methods

function $computed(target, expr, value) defined a computed in ReactVueLike Component instance.

function $runAction(nameOfFn, fn?) run fn in mobx action, equals runInAction in mobx.

Static Methods

ReactVueLike.runAction(nameOfFn, fn?) - equals $runAction in component instance.

ReactVueLike.observable - observable method in mobx, just re-export.

ReactVueLike.flow - flow method in mobx, just re-export.

ReactVueLike.action - action method in mobx, just re-export.

ReactVueLike.set - set method in mobx, just re-export.

ReactVueLike.delete - remove method in mobx, just re-export.

ReactVueLike.config - config something in ReactVueLike, example: ReactVueLike.config({ enforceActions: true });

toJS, isObservable, isObservableProp, isObservableObject, isObservableArray, isObservableMap, isBoxedObservable, isArrayLike, isAction, isComputed, isComputedProp, observable, extendObservable, observe, decorate, reaction, intercept, computed, action, autorun, when, runInAction, createAtom, set, get, remove, has, flow, configure, onBecomeObserved, onBecomeUnobserved mobx methods, just re-export.

Usage

webpack.config.js:

{
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'css-loader',
          'react-vue-like/loader',
          ...
        ]
      },
      {
        test: /\.scss$/,
        use: [
          'css-loader',
          'react-vue-like/loader',
          ...
        ]
      },
      {
        test: /\.less$/,
        use: [
          'css-loader',
          'react-vue-like/loader',
          ...
        ]
      }
    ]
  }
}

babel.config.js

module.exports = {
    presets: [
    '@babel/preset-env',
    'react-vue-like/preset',
    '@babel/preset-react'
  ],
}

routes file:

// routes.js
import Test from './test';

const routes = [
  {
    path: '/',
    component: Test
  }
]

export default routes;

router file:

// router.js
import ReactViewRouter from 'react-view-router';
import routes from './routes';

const router = new ReactViewRouter({ routes });

export default router;

global filters:

// filters.js
export default {
  myFilter(value) {
    return `myFilter:${value}`;
  }

  install(ReactVueLike, { App }) {
    App.filters = this;
  }
}

global directives:

// directives.js
export default {
  test: {
    bind(el, binding, vnode) {

    }
  }

  install(ReactVueLike, { App }) {
    App.directives = this;
  }
}

global components:

import * as antd from 'antd';

const PREFIX = 'Ad';
function normalizeComps(comps, parentKey = '') {
  const COMP_REGX = /^[A-Z][A-Za-z]+/;
  let ret = {};
  Object.keys(comps).forEach(key => {
    if (!COMP_REGX.test(key)) return;
    const comp = comps[key];
    ret[`${PREFIX}${parentKey}${key}`] = comp;
    if (!parentKey) Object.assign(ret, normalizeComps(comp, key));
  });
  return ret;
}

// components.js
export default function install(ReactVueLike, { App }) {
  if (!App.components) App.components = {};
  Object.assign(App.components, normalizeComps(antd));
}

entry file:

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import ReactVueLike from 'react-vue-like';
import store from './store';
import router from './router';
import filters from './filters';
import directives from './directives';
import components from './components';
import App from 'react-vue-like';

ReactVueLike.config({ enforceActions: true });

ReactVueLike.use(store, { App });
ReactVueLike.use(router, { App });
ReactVueLike.use(filters, { App });
ReactVueLike.use(directives, { App });
ReactVueLike.use(components, { App });

router.beforeEach((to, from, next) => {
  if (to) {
    console.log(
      '%croute changed',
      'background-color:#ccc;color:green;font-weight:bold;font-size:14px;',
      to.url, to.query, to.meta, to.redirectedFrom
    );
    return next();
  }
});

ReactDOM.render(<App />, document.getElementById('#root'));

root ReactVueLike component:

  // app.jsx
import React from 'react';
import ReactVueLike from 'react-vue-like';
import { RouterView } from 'react-view-router';
import router from './router';
import SomeComponent from './SomeComponent';
// scoped css
import './app.scss?scoped';

class App extends ReactVueLike.Component {

  static isRoot = true

  static data() {
    return {
      formData: {
        text: ''
      }
    };
  }


  static computed = {
    computedText() {
      return `haha:${this.formData.text}`;
    },
  }

  static methods = {
    func1(v) {
      console.log('dddd', v);
    }
  }

  render() {
    return (<div class="root">
      {/* root RouterView need `router` prop */}
      <RouterView router={router} />
    </div>);
  }

}

export default App;
  // some-component.jsx
import React from 'react';
import ReactVueLike from 'react-vue-like';
// scoped css
import './some-component.scss?scoped';

class SomeComponent extends ReactVueLike.Component {

  static computed = {
    user() {
      return this.$store.state.user;
    }
  }

  static methods = {
    doSomething() {
      console.log('doSomething');
    }
  }

  // when render throw error, then will call renderError
  renderError(error) {
    return `render has some error:${error.message}`;
  }

  render() {
    return (<div>
      <slot name="header">
      haha1
      {
        [1, 2, 3].map(v => <slot value={v} user={user} />)
      }
      haha2
      <slot name="footer">

      {/* src will be transformed: require('./images/pic1.png') */}
      <img src="./images/pic1.png" />
    </div>);
  }
}
  // test.jsx
import React from 'react';
import ReactVueLike from 'react-vue-like';
// scoped css
import './test.scss?scoped';

class Test extends ReactVueLike.Component {

  static computed = {
    user() {
      return this.$store.state.user;
    }
  }

  static methods = {
    test() {
      this.$refs.some.doSomething();
    }
  }

  render() {
    return (<div>
      {/* donot need import,  it will find from it's root component's components section */}
      <some-component ref="some" onClick={this.test} />
    </div>);
  }
}

store like Vuex.Store:

import { Store } from 'react-vue-like';

const store = new Store({
  modules: {
    child1: {
      state: {
        aa: true
      }
    },
    child2: {
      state: {
        bb: true
      }
    }
  },
  state: {
    user: {
      name: 'name1'
    },
  },
  getters: {
    aa(state) {
      return state.globalLoading;
    }
  }
  mutations: {
    'update-user'(state, v) {
      state.user = v;
    },
    'update-user-info'(state, v) {
      Object.keys(v).forEach(key => state.user[key] = v[key]);
    }
  },
  actions: {
    'update-user-info'({ commit }, v) {
      commit('update-user', v);
    }
  },
});

export default store;

Note

  1. In ReactVueLike Component, try not to use this.props, please use this.$attrs instead. and you can use this.$slots.default instead of this.props.children;

  2. the prop name that bind to ReactVueLike Component, do not begin with '_'or'$' chars, they are recognized as internal values of ReactVueLike.

License

MIT