Home

Awesome

<p align="center"> <img src="./media/evajs.jpg" /><br><br> <br><strong>eva.js</strong> is a complete solution to <br>building modern webs with Vue.js. </p> <p align="center"> <a href="https://npmjs.com/package/eva.js"><img src="https://img.shields.io/npm/v/eva.js.svg?style=flat-square" alt="NPM version"></a> <a href="https://npmjs.com/package/eva.js"><img src="https://img.shields.io/npm/dm/eva.js.svg?style=flat-square" alt="NPM downloads"></a> <a href="https://circleci.com/gh/egoist/eva.js/tree/master"><img src="https://img.shields.io/circleci/project/egoist/eva.js/master.svg?style=flat-square"></a> <a href="https://david-dm.org/egoist/eva.js"> <img src="https://david-dm.org/egoist/eva.js.svg?style=flat-square" alt="david dm"> </a> </p>

tl;dr

// model
app.model()
// router
app.router()
// bootstrap
app.start()

Play with the JSBin example or the simple webpack example 😀

<details><summary>Table of Contents</summary> <!-- toc --> <!-- tocstop --> </details>

Sites using eva.js

Feel free to add your project here!

Features

Install

$ npm install --save eva.js

In case you may want to use it directly in browser instead, view https://unpkg.com/eva.js/dist/, and add:

<!-- global variable `EVA` is available as a constructor(class) -->
<!-- note that, you should use new EVA.default() to create app instance in browser -->
<script src="/path/to/eva.js"></script>

If you use the commonjs version and wanna include the runtime for vue template, follow the official guide:

Usage

import EVA from 'eva.js'

// Create app instance
const app = new EVA()

// A counter model
app.model({
  state: {count: 0},
  mutations: {
    INCREMENT(state) {state.count++}
  }
})

// A home view
const Home = {
  computed: {
    count() {
      return this.$store.state.count
    }
  },
  render(h) {
    return (
      <div>
        <h1>Home</h1>
        <button on-click={() => this.$store.commit('INCREMENT')}>
          {this.count}
        </button>
      </div>
    )
  }
}

// Apply views to relevant routes
// route(path, view, child_routes)
app.router(route => [
  route('/', Home)
])

// Start app
const App = {
  render(h) {
    return (
      <div id="app">
        <router-view></router-view>
      </div>
    )
  }
}
app.start(App, '#app')
// equal to
// app.start(App)
// app.mount('#app')

Concepts

Models

A model contains it's initial state and the methods you use to update its state, in fact, it's a typical Vuex module too.

Top-level model:

// An app instance only have at most one top-level model
app.model({
  state: {count: 0},
  mutations: {
    INCREMENT(state) {state.count++}
  }
})

Named/Namespaced model:

// An app could have multiple named models
app.model({
  name: 'user',
  state: {login: false},
  mutations: {
    LOG_IN(state) {state.login = true}
  }
})

By default only state are registered locally under provided name, eg state.user.login. But mutations actions getters are still in global namespace, to enforce name for those too, please change name to namespace:

app.model({
  namespace: 'user',
  state: {login: false},
  mutations: {
    LOG_IN(state) {state.login = true}
  },
  actions: {
    login({commit}) {
      commit('LOG_IN') //=> user/LOG_IN
    }
  }
})

Check out official docs for modules in vuex: http://vuex.vuejs.org/en/modules.html

In most cases using namespaces is beneficial, as having clear boundaries makes it easier to follow logic.

Helpers:

As how you use Vuex^2, you can use its helpers too:

const {mapState, mapActions, mapGetters} = require('eva.js')
// or ES6 modules
import {mapState, mapActions, mapGetters} from 'eva.js'

// of course you can directly import from 'vuex' too
import {mapState, mapActions, mapGetters} from 'vuex'

Router

The router could render the component which matches the URL path. It has a route helper for creating an actual route object used in vue-router. routes are passed in as a nested array.

app.router(route => [
  route('/', Home),
  route('/settings', Settings, [
    route('/profile', SettingsProfile),
    route('/password', SettingsPassword)
  ])
])

// use an object as route argument:
route({path: '/', component: Home, /*...*/})

// use an object as router argument:
app.router({
  mode: 'history',
  routes: []
})

The router state is effortlessly synced in vuex store.

Views

A view is a simple Vue component, that easy :)

Vue constructor

If you wan to access Vue constructor directly, simply do:

import {Vue} from 'eva.js'
// or without any change
// import Vue from 'vue'
// works too

Vue.use(yourPlugin)

Access $store and $router outside component

You can initialize your app and bootstrap it later:

// ./src/app.js
import EVA from 'eva.js'

const app = new EVA()

app.model() //...
app.router() //...

export default app.start()

// ./src/index.js
import app from './app'
app.mount('#app')

// ./some/other/file.js
import app from './path/to/src/app.js'

app.$router.push('/somewhere')

Server-side rendering

Similar to the official hackernews example:

// ./src/app.js
import EVA from 'eva.js'
import App from './App.vue'

const app = new EVA()

export default app.start(App)
// without selector!
// otherwise it will be mounted to the selector

Then for the server-entry.js:

// ./src/server-entry.js
import app from './app'

export default context => {
  // you can access app.$router / app.$store
  return Promise.all(operations)
    .then(() => {
      return app.instance
    })
}

For client-entry.js:

import app from './app'

app.mount('#app')

Promise polyfill

Some browsers do not have native Promise, like IE, but vuex requires Promise. Thus eva.js provides an lightweight Promise polyfill with promise-polyfill.

import 'eva.js/promise-polyfill'
import EVA from 'eva.js'
// ... your app code

API

new EVA([options: object])

Create an app instance.

options.mode

The router mode, can be either hash (default) or history.

app.model(model: object)

Register a model, a.k.a. store module in Vuex. You can omit the name and namespace property to make it top-level.

app.router(handler: function)

Register routes.

app.use(plugin: function, [options: object])

The same as Vue.use, you can apply any Vue plugin.

app.start([App: object], [selector: string])

Create app instance. Optionally mount App component to a domNode if selector is defined.

If App is not specified, we use a default value:

const defaultApp = {
  render(h) {
    return <div id="app"><router-view></router-view></div>
  }
}

If selector is not specified, we won't mount the app instance to dom.

app.mount(selector)

Mounted app instance to dom, must be call after app.start([App]) (without selector argument). Default selector is #app

app.syncRouterInStore()

keep vue-router and vuex store in sync, i.e. keep router state in vuex store.

The method will be called automatically in app.start(), you can also call it manually before app.start() and app.start() won't call it again.

app.$store

The vuex store instance.

app.$router

The vue-router instance.

app.instance

The Vue instance created by app.start(), most likely you will use this in server-side rendering.

Development

# build and watch source files
$ npm run watch

# launch server for simple html example
$ http-server .
# run webpack example
$ npm run webpack

# build for publish to npm
# cjs and umd and compressed umd
$ npm run build

License

MIT © EGOIST