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 -->- Sites using eva.js
- Features
- Install
- Usage
- Concepts
- Vue constructor
- Access $store and $router outside component
- Server-side rendering
- API
- Development
- License
Sites using eva.js
Feel free to add your project here!
Features
- 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
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