eva.js is a complete solution to
building modern webs with Vue.js.
// model
app.model()
// router
app.router()
// bootstrap
app.start()
Play with the JSBin example or the simple webpack example π
Table of Contents
Feel free to add your project here!
- Battery included, Vue 2 and its friends (Vuex & Vue-Router)
- Small APIs, just Vue and the official plugins you already play well with!
- Support server-side rendering, of course!
- Inspired by the choo framework which is inpired by the elm architecture
$ 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:
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')
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.
// An app instance only have at most one top-level model
app.model({
state: {count: 0},
mutations: {
INCREMENT(state) {state.count++}
}
})
// 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.
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'
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.
A view is a simple Vue component, that easy :)
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)
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')
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')
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
Create an app instance.
The router mode, can be either hash
(default) or history
.
Register a model, a.k.a. store module in Vuex. You can omit the name
and namespace
property to make it top-level.
Register routes.
The same as Vue.use
, you can apply any Vue plugin.
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.
Mounted app instance to dom, must be call after app.start([App])
(without selector
argument). Default selector
is #app
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.
The vuex store instance.
The vue-router instance.
The Vue instance created by app.start()
, most likely you will use this in server-side rendering.
# 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
MIT Β© EGOIST