README
easiest
A minimal web mvvm framework.一个小Web mvvm库。 Now we support for typescript!
demo
npm run start
npm run start-js
- open
http://localhost:1234
Basic Usage
- Create a root DOM for route which id is root:
<div id="root"></div>
Create a Easiest:
use
$bootstrapModule
to bootstrap a root moduleEsModule
const easiest = new Easiest();
easiest.$bootstrapModule(M1);
easiest.$use(router); // if u are using a router
easiest.$init();
- Create a router:
routes must an
Array
includesObject
- routes must incloude rootPath
'/'
- routes must set an component name like
component: 'R1'
,R1
is declared inEsModule
in$declarations => this.$components
- routes can assign redirectTo and use
redirectTo: Path
- routes can assign children and use
children: Array
- routes can set a path like
path: '/:id'
- routes must incloude rootPath
if u are using
Router
, u must need torouter.$setRootPath('/RootPath')
to set an root path.router.$routeChange = (old, next)
can listen route changerouter.$init(routes);
can init Array routesif u want to watch routes changes, plz use
router.$routeChange=(old.new) => {}
- use
this.$setLocation((path: String, query: Object, params: Object)
to go to Path orlocation.href
- use
this.$getLocation()
to get location states Router
:http://localhost:1234/R1
- use
type TRouter = {
path: string;
redirectTo?: string;
component?: string;
children?: TRouter[];
};
const router = new Router();
const routes: TRouter = [
{
path: '/',
// redirectTo: '/R1',
component: 'container-wrap',
children: [
{
path: '/R1',
component: 'R1',
// redirectTo: '/R2',
children: [
{
path: '/C1',
component: 'R2',
children: [
{
path: '/D1',
redirectTo: '/R2',
},
],
},
{
path: '/C2',
redirectTo: '/R2',
},
],
},
{
path: '/R2',
component: 'R2',
children: [
{
path: '/:id',
component: 'R1',
children: [
{
path: '/D1',
redirectTo: '/R1/C1',
},
],
},
],
},
],
},
];
router.$setRootPath('/demo'); // so routes:Array => `/` is `/demo`
router.$init(routes);
router.$routeChange = function (old, next) {
console.log('esRouteChange', old, next);
};
const easiest = new Easiest();
easiest.$bootstrapModule(M1);
const routerIndex = easiest.$use(router);
easiest.$init();
- Create a Component:
create a
class
use decorator
Component
in typescript or use functionComponent
in javascriptComponent
accepts an objecttemplate: string;state?: any;
please use $setState or $setProps after lifecycle
constructor()
- typescript
- to use decorator
Component
declaretemplate
andstate
- to implements interface
HasRender, OnInit, WatchState, BeforeMount, AfterMount, RouteChange
to use lifecycle - to use decorator
Injectable
to injectService
inconstructor
's arguments ofComponent
@Injectable @Component({ state: { a: 1, testArray: [ { name: '李龙吉', sex: '男', job: [ { id: 1, name: '程序员', }, { id: 2, name: '码农', }, { id: 3, name: '帅', }, ], }, { name: '邱宝环', sex: '女', job: [ { id: 1, name: '老师', }, { id: 2, name: '英语老师', }, { id: 3, name: '美', }, ], }], testArray2: ['程序员3', '码农3', '帅3'], }, template: (` <div> <p es-on:click="this.go()">container: {{this.state.a}}</p> <input es-model="this.state.a" /> <div es-repeat="let man in this.state.testArray"> <div es-on:click="this.show(this.state.testArray2)">姓名:{{man.name}}</div> <div>性别:{{man.sex}}</div> <input es-on:click="this.show(b, $index)" es-repeat="let b in this.state.testArray2" es-model="b" es-class="b" /> <div class="fuck" es-repeat="let b in man.job"> <input es-on:click="this.show(b.name)" es-model="b.name" es-class="b.id" /> </div> </div> <router-render></router-render> </div> `), }) class Container implements OnInit, AfterMount { public ss: HeroSearchService; public state: any; public $setLocation: (path: string, query?: any, params?: any) => void; constructor( private hss: HeroSearchService, ) { this.ss = hss; this.ss.test(); console.log(this.state); } public esOnInit() { console.log('esOnInit Container'); } public esAfterMount() { console.log('esAfterMount Container'); } public go() { this.$setLocation('/R1', { b: '1' }); } public show(a: any, index?: string) { console.log('aaaa', a); console.log('$index', index); } }
- javascript
- to use function
Component
declaretemplate
andstate
- to use lifecycle
esOnInit esBeforeMount esAfterMount esOnDestory esHasRender esWatchState esRouteChange
in Class - to use
constructor
's arguments ofComponent
for injectService
, and arguments must be lowercase lette of initials lette of Service class name. For example, you want to inject a service classHeroService
, you must write argument inconstructor
withheroService
class Container { constructor( heroSearchService ) { this.ss = heroSearchService; this.ss.test(); } esOnInit() { console.log('esOnInit Container'); } go() { this.$setLocation('/R1', { b: '1' }); } show(a, index) { console.log('aaaa', a); console.log('$index', index); } } Component({ template: (` <div> <p es-on:click="this.go()">container: {{this.state.a}}</p> <input es-model="this.state.a" /> <div es-repeat="let man in this.state.testArray"> <div es-on:click="this.show(this.state.testArray2)">姓名:{{man.name}}</div> <div>性别:{{man.sex}}</div> <input es-on:click="this.show(b, $index)" es-repeat="let b in this.state.testArray2" es-model="b" es-class="b" /> <div class="fuck" es-repeat="let b in man.job"> <input es-on:click="this.show(b.name)" es-model="b.name" es-class="b.id" /> </div> </div> <router-render></router-render> </div>`), state: { a: 1, testArray: [ { name: '李龙吉', sex: '男', job: [ { id: 1, name: '程序员', }, { id: 2, name: '码农', }, { id: 3, name: '帅', }, ], }, { name: '邱宝环', sex: '女', job: [ { id: 1, name: '老师', }, { id: 2, name: '英语老师', }, { id: 3, name: '美', }, ], }], testArray2: ['程序员3', '码农3', '帅3'], }, })(Container);
props: Object
is data whichclass Controller
sends toclass Component
**
props: Object
can only be changed or used after lifecycleconstructor()
**props: Object
can only be changed by actionthis.$setProps()
andthis.$setProps()
is equal to$setState
- EsModule
Easiest apps are modular and Easiest has its own modularity system called
EsModule
. AnEsModule
is a container for a cohesive block of code dedicated to an application domain, a workflow, or a closely related set of capabilities. It can contain components, service providers, and other code files whose scope is defined by the containingEsModule
. It can import functionality that is exported from otherEsModule
, and export selected functionality for use by otherEsModule
.u need to declare
imports?: Function[]
components: {[name: string]: Function;}
providers?: Function[]
exports?: string[]
bootstrap?: Function
inoptions
imports
imports otherEsModule
and respect it'sexports
components
declareComponents
. Key: name, Value: Componentsproviders
declareService
exports:
exportsComponents
for otherEsModules
bootstrap
declareComponent
for Module bootstrap only if u don'tRouter
- typescript
@EsModule({ imports: [ M2, ], components: { 'container-wrap': Container, 'pc-component': PComponent, 'R1': R1, }, providers: [ HeroSearchService, HeroSearchService1, ], }) class M1 {}
- javascript
class M1 {} EsModule({ imports: [ M2, ], components: { 'container-wrap': Container, 'pc-component': PComponent, 'R1': R1, }, providers: [ HeroSearchService, HeroSearchService1, ], })(M1);
- Template Syntax
- 规定:指令以 es-xxx 命名
- now: es-text es-html es-model es-class es-repeat es-if es-on:Event
- 事件指令, 如 es-on:click
- Text1:
this.$template = '<p es-text="this.state.b"></p>';
- Text2:
this.$template = '<p>this.state.b是:{{this.state.b}}</p>';
- HTML:
this.$template = '<p es-html="this.state.c"></p>';
- Model for input:
this.$template = '<input es-model="this.state.c"/>';
if input is a repeat DOM, and intem of Array is'nt an object, please use$index
- Class:
this.$template = '<p class="b" es-class="this.state.a"></p>';
- Directives: ues
es-on:event
this.$template = '<p es-on:click="this.componentClick()"></p>';
- Repeat:
this.$template = '<p class="b" es-class="this.state.a" es-repeat="let a in this.state.b" es-if="a.f">{{a.z}}</p>';
- Text1:
- about function in Template Syntax
- now you can send arguments in Function
- arguments include:
- Event:
$event
- String:
'xxx'
- Number:
123
- Index:
$index
, you can only use this in repeat DOM :<input es-on:click="this.show(b, $index)" es-repeat="let b in this.state.testArray2" es-model="b" es-class="b" />
- Variable:
this.state.xxx
this.props.xxx
- Boolean:
true
false
- For es-repeat: items like:
es-repeat="let a in this.state.b" es-if="a.f"
- Event:
- Data monitor: this.state && this.$setState
- use
this.state: Object
andthis.$setState(parmars: Function || Object)
- if u have some variable, u can set
this.state
inconstructor(){}
- if u want to change State, plz use
this.$setState
, parmars can beObject
orFunction
which must return anObject
- and u can recive this change in life cycle
esWatchState(oldData, newData)
Watcher
andKeyWatcher
import {Watcher, KeyWatcher}
Watcher
- Watcher expects two arguments:
data, watcher
- data is an Object
- watcher is a function which has two arguments:
oldData, newData
new Watcher(this.object, (oldData, newData) => {})
KeyWatcher
- Watcher expects there arguments:
data, key, watcher
- data:
Object
- key is a key in Object and type is
String
- watcher is a function which has two arguments:
oldData, newData
new KeyWatcher(this.object, key,(oldData, newData) => {})
- Service
Components shouldn't fetch or save data directly and they certainly shouldn't knowingly present fake data. They should focus on presenting data and delegate data access to a service.
And u can use
Service
to send communication betweenComponent
, because we have realized singleton.Service
accepts an objectisSingletonMode: boolean
to decide use singleton or not.- typescript
- to use decorator
Injectable
to injectService
inconstructor
's arguments ofService
@Injectable @Service({isSingletonMode: false}) class HeroSearchService { public hsr: HeroSearchService1; constructor( private hsrS: HeroSearchService1, ) { this.hsr = hsrS; this.hsr.test(); } public test() { console.log('HeroSearchService !!!000000000'); } }
- javascript
- to use
constructor
's arguments ofService
for inject an otherService
, and arguments must be lowercase lette of initials lette of Service class name. For example, you want to inject a service classHeroSearchService
, you must write argument inconstructor
withheroSearchService
class HeroSearchService { constructor(heroSearchService1) { this.hsr = heroSearchService1; this.hsr.test(); } test() { console.log('HeroSearchService !!!000000000'); } } Service({ isSingletonMode: false, })(HeroSearchService);
- http
import { esHttp } from 'Easiest'; const http = esHttp; http.get(url, params); http.delete(url, params); http.post(url, params); http.put(url, params); http.patch(url, params);
Dependency Injection
Dependency injection is an important application design pattern. It's used so widely that almost everyone just calls it DI
- Use Typescript
- If u are using
Typescript
to build an app, u can easily use our Dependency Injection.Only use@Injectable
before theClass
which need to use other services, that which are declarated inthis.$providers
ofEsModule
or root module. - Use
this.
names of constructor arguments to directly useService
.
import { Injectable, Component, EsModule, Service, HasRender } from 'easiest'; @Service({ isSingletonMode: true, }) class HeroSearchService1 { constructor() {} public test() { console.log('HeroSearchService !!!1111'); } } @Injectable @Service() class HeroSearchService { public hsr: HeroSearchService1; constructor( private hsrS: HeroSearchService1, ) { this.hsrS.test(); } public test() { console.log('HeroSearchService !!!000000000'); } } @Injectable @Component({ state: { a: 'a', }, template: (` <div> <p es-on:click="this.go()">container: {{this.state.a}}</p> <input es-model="this.state.a" /> <div es-repeat="let man in this.state.testArray"> <div es-on:click="this.show(this.state.testArray2)">姓名:{{man.name}}</div> <div>性别:{{man.sex}}</div> <input es-on:click="this.show(b, $index)" es-repeat="let b in this.state.testArray2" es-model="b" es-class="b" /> <div class="fuck" es-repeat="let b in man.job"> <input es-on:click="this.show(b.name)" es-model="b.name" es-class="b.id" /> </div> </div> <router-render></router-render> </div> `), }) class PCChild implements HasRender { public props: any; public hsr: HeroSearchService; constructor( private hsrS: HeroSearchService, ) { this.hsrS.test(); } public esHasRender() {} } @EsModule({ imports: [ M2, ], components: { 'container-wrap': PCChild, }, providers: [ HeroSearchService, HeroSearchService1, ], }) class M1 {}
- Use Javascript
- to use
constructor
's arguments ofService
for inject an otherService
, and arguments must be lowercase lette of initials lette of Service class name. For example, you want to inject a service classHeroSearchService
, you must write argument inconstructor
withheroSearchService
- A little diffrence between javascript and typescript, use constructor arguments to directly use
Service
, and assign them to a variable.
class HeroSearchService1 { constructor() { super(); } test() { console.log('HeroSearchService !!!1111'); } } Service({ isSingletonMode: true, })(HeroSearchService1); class HeroSearchService { constructor( heroSearchService1, ) { super(); this.hsr = heroSearchService1; this.hsr.test(); } test() { console.log('HeroSearchService !!!000000000'); } } Service({ isSingletonMode: false, })(HeroSearchService); class Container { constructor(heroSearchService) { this.ss = heroSearchService; this.ss.test(); } } Component({ state: { a: 'a', }, template: (` <div> <p>1232{{this.state.a}}</p> </div> `), })(Container) class M1 {} EsModule({ imports: [ M2, ], components: { 'container-wrap': Container, }, providers: [ HeroSearchService, HeroSearchService1, ], })(M1);
LifeCycle hooks which from the beginning to the end:
EsModule
constructor()
Components
constructor() esOnInit(): void; esBeforeMount(): void; esAfterMount(): void; esOnDestory(): void; esHasRender(): void; esWatchState(oldData?: any, newData?: any): void; esRouteChange(lastRoute?: string, newRoute?: string): void;
Router
$routeChange((lastRoute?: string, newRoute?: string): void;
Architecture
route => EsModule => component
To do
- 类分离,通过use来绑定方法
- 无需route渲染
- 子路由(2/2)
- 组件化(3/3)
- 模块化 + EsModule
- 双向绑定
- 公共类提取
- 数据劫持
- 双向绑定模板
- Template Syntax: es-text es-html es-model es-class es-repeat es-if(6/6)
- 组件props
- 组件渲染
- 组件中使用组件
- 改用 history的pushState
- 监听路由变化动态渲染(2/2)
- Virtual DOM
- Service
- Route bug
- ts (强类型赛高)
- DI