easiest v1.2.3
easiest
A minimal web mvvm framework.一个小Web mvvm库。 Now we support for typescript!
demo
npm run startnpm 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
$bootstrapModuleto bootstrap a root moduleEsModuleconst easiest = new Easiest(); easiest.$bootstrapModule(M1); easiest.$use(router); // if u are using a router easiest.$init();Create a router:
routes must an
ArrayincludesObject- routes must incloude rootPath
'/' - routes must set an component name like
component: 'R1',R1is declared inEsModulein$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) => {}1. use `this.$setLocation((path: String, query: Object, params: Object)` to go to Path or `location.href` 2. use `this.$getLocation()` to get location states 3. `Router` : `http://localhost:1234/R1`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
Componentin typescript or use functionComponentin javascript Componentaccepts an objecttemplate: string;state?: any;please use $setState or $setProps after lifecycle
constructor()- typescript
- to use decorator
Componentdeclaretemplateandstate - to implements interface
HasRender, OnInit, WatchState, BeforeMount, AfterMount, RouteChangeto use lifecycle to use decorator
Injectableto injectServiceinconstructor'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
Componentdeclaretemplateandstate - to use lifecycle
esOnInit esBeforeMount esAfterMount esOnDestory esHasRender esWatchState esRouteChangein Class to use
constructor's arguments ofComponentfor 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 inconstructorwithheroServiceclass 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: Objectis data whichclass Controllersends toclass Componentprops: Objectcan only be changed or used after lifecycleconstructor()props: Objectcan 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. AnEsModuleis 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?: Functioninoptionsimportsimports otherEsModuleand respect it'sexportscomponentsdeclareComponents. Key: name, Value: ComponentsprovidersdeclareServiceexports:exportsComponentsfor otherEsModulesbootstrapdeclareComponentfor 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:eventthis.$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.xxxthis.props.xxx - Boolean:
truefalse - 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: Objectandthis.$setState(parmars: Function || Object) - if u have some variable, u can set
this.stateinconstructor(){} - if u want to change State, plz use
this.$setState, parmars can beObjectorFunctionwhich must return anObject - and u can recive this change in life cycle
esWatchState(oldData, newData)
WatcherandKeyWatcher
import {Watcher, KeyWatcher}
Watcher
- Watcher expects two arguments:
data, watcher - data is an Object
- watcher is a function which has two arguments:
oldData, newDatanew 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, newDatanew 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
Serviceto send communication betweenComponent, because we have realized singleton. Serviceaccepts an objectisSingletonMode: booleanto decide use singleton or not.- typescript
to use decorator
Injectableto injectServiceinconstructor'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 ofServicefor 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 inconstructorwithheroSearchServiceclass 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
Typescriptto build an app, u can easily use our Dependency Injection.Only use@Injectablebefore theClasswhich need to use other services, that which are declarated inthis.$providersofEsModuleor 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 ofServicefor 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 inconstructorwithheroSearchService 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