diff --git a/.gitignore b/.gitignore index 0f55cff..4732888 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ /hooks /platforms /cloud +/**/package.json \ No newline at end of file diff --git a/WX20190504-205634@2x.png b/WX20190504-205634@2x.png new file mode 100644 index 0000000..e71154c Binary files /dev/null and b/WX20190504-205634@2x.png differ diff --git a/addon/nger-demo/template/admin/welcome/index.tsx b/addon/nger-demo/template/admin/welcome/index.tsx index 04f496c..7f4d925 100644 --- a/addon/nger-demo/template/admin/welcome/index.tsx +++ b/addon/nger-demo/template/admin/welcome/index.tsx @@ -1,4 +1,4 @@ -import { Page } from 'nger-core'; +import { Page, Input, ChangeDetectorRef } from 'nger-core'; @Page({ path: '/admin/welcome', styleUrls: [ @@ -6,7 +6,27 @@ import { Page } from 'nger-core'; ] }) export class NgerDemoAdminWelcomePage { + + @Input() + title: string = `小明title`; + + @Input() + classStr: string = `items` + + constructor(change: ChangeDetectorRef) { + let i = 0; + setInterval(() => { + i = i + 1; + this.title += i; + this.classStr += i; + change.next({ title: this.title, classStr: this.classStr }); + }, 1000) + } render() { - return
NgerDemoAdminWelcomePage
+ return
+
+ {"title"} +
+
} } diff --git a/package.json b/package.json index 6b2121b..e4fc0db 100644 --- a/package.json +++ b/package.json @@ -163,7 +163,8 @@ "docker": "docker-compose up -d", "start": "npm run cli start koa", "build": "npm run cli build", - "platformBrowserBuild": "ts-node -r tsconfig-paths/register packages/nger-cli/lib/bin.ts build prod -n nger-platform-browser", + "build:platform:browser": "ts-node -r tsconfig-paths/register packages/nger-cli/lib/bin.ts build prod -n nger-platform-browser", + "build:nger:core": "ts-node -r tsconfig-paths/register packages/nger-cli/lib/bin.ts build prod -n nger-core", "dev": "ts-node -r tsconfig-paths/register packages/nger-cli/lib/bin.ts dev", "admin:build": "ts-node -r tsconfig-paths/register packages/nger-cli/lib/bin.ts build admin", "admin:dev": "ts-node -r tsconfig-paths/register packages/nger-cli/lib/bin.ts build admin --watch", diff --git a/packages/nger-compiler-preact/lib/task.ts b/packages/nger-compiler-preact/lib/task.ts index 620549f..898e91f 100644 --- a/packages/nger-compiler-preact/lib/task.ts +++ b/packages/nger-compiler-preact/lib/task.ts @@ -3,7 +3,8 @@ import { NgerCompilerBabel, NgerCompilerNgMetadata, metadataCache, - hasHandlerFileCache + hasHandlerFileCache, + componentRenderTransformerFactory } from 'nger-compiler' import { Injector } from 'nger-di' import { relative, extname, join } from 'path'; @@ -26,7 +27,8 @@ export const preactTask: Task = async (file: string, opt: string, injector: Inje const code = babel.compile(file, { transformers: { before: [ - await componentTransformerFactory(file, injector) + await componentTransformerFactory(file, injector), + componentRenderTransformerFactory ] } }); diff --git a/packages/nger-compiler/lib/index.ts b/packages/nger-compiler/lib/index.ts index c4a5f18..866c99a 100644 --- a/packages/nger-compiler/lib/index.ts +++ b/packages/nger-compiler/lib/index.ts @@ -9,7 +9,9 @@ import { NgerCompilerRollup } from './ts/rollup' import { NgerCompilerNgTemplate } from './html/ng' import { NgerCompilerCid } from './helper/cid' import { NgerCompilerNgMetadata } from './helper/ng_metadata' -import { controllerPropertyTransformerFactory, hasPropertyMetadata } from './transformer_factorys/controller' +import { controllerPropertyTransformerFactory, hasMetadata } from './transformer_factorys/controller' +import { componentRenderTransformerFactory } from './transformer_factorys/component' + import { WATCH_TASK, Task } from './tokens/watch_task' import { NgerCompilerBootstrap, metadataCache, hasHandlerFileCache, templateCache } from './bootstrap' import { NgModuleBootstrap } from 'nger-core' @@ -27,10 +29,11 @@ export { controllerPropertyTransformerFactory, WATCH_TASK, Task, - hasPropertyMetadata, + hasMetadata, metadataCache, hasHandlerFileCache, - templateCache + templateCache, + componentRenderTransformerFactory } const provides: StaticProvider[] = [ ...styleProviders, diff --git a/packages/nger-compiler/lib/transformer_factorys/component.ts b/packages/nger-compiler/lib/transformer_factorys/component.ts new file mode 100644 index 0000000..ec985cb --- /dev/null +++ b/packages/nger-compiler/lib/transformer_factorys/component.ts @@ -0,0 +1,55 @@ +import ts, { TransformationContext, Transformer } from 'typescript' +import {hasMetadata} from './controller'; +export const componentRenderTransformerFactory = (context: TransformationContext): Transformer => { + return (node: ts.SourceFile): ts.SourceFile => { + node.statements = ts.createNodeArray( + node.statements.map((node: ts.Statement) => { + if (ts.isImportDeclaration(node)) { + return node; + } else if (ts.isClassDeclaration(node)) { + if(hasMetadata(node.decorators,['Page','Component'])){ + return ts.createClassDeclaration( + node.decorators, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + node.members.map(member => { + if (ts.isMethodDeclaration(member)) { + if(ts.isIdentifier(member.name)){ + if(member.name.text === 'render'){ + if(member.parameters.length===0){ + return ts.createMethod( + member.decorators, + member.modifiers, + member.asteriskToken, + member.name, + member.questionToken, + member.typeParameters, + ts.createNodeArray([ + ts.createParameter(undefined,undefined,undefined,'h') + ]), + member.type, + member.body + ) + } + } + } + } + return member; + }).filter(node => !!node) + ) + } + return node; + } else { + return node; + } + }) + ) + return node; + } +} + +function needReplaceRender(){ + +} \ No newline at end of file diff --git a/packages/nger-compiler/lib/transformer_factorys/controller.ts b/packages/nger-compiler/lib/transformer_factorys/controller.ts index 2cae9e0..27f07c4 100644 --- a/packages/nger-compiler/lib/transformer_factorys/controller.ts +++ b/packages/nger-compiler/lib/transformer_factorys/controller.ts @@ -14,7 +14,7 @@ export const controllerPropertyTransformerFactory = (context: TransformationCont node.heritageClauses, node.members.map(member => { if (ts.isMethodDeclaration(member)) { - const needReplace = hasPropertyMetadata(member.decorators); + const needReplace = hasMetadata(member.decorators); if (needReplace) { return ts.createProperty( member.decorators, @@ -26,7 +26,7 @@ export const controllerPropertyTransformerFactory = (context: TransformationCont ) } } else if (ts.isPropertyDeclaration(member)) { - const needReplace = hasPropertyMetadata(member.decorators); + const needReplace = hasMetadata(member.decorators); if (needReplace) return member; } else if (ts.isConstructorDeclaration(member)) { } }).filter(node => !!node) @@ -39,7 +39,7 @@ export const controllerPropertyTransformerFactory = (context: TransformationCont return node; } } -export function hasPropertyMetadata(nodes: ts.NodeArray, decorators: string[] = ['Get', 'Post']) { +export function hasMetadata(nodes: ts.NodeArray, decorators: string[] = ['Get', 'Post']) { const item = nodes && nodes.find(node => { if (ts.isDecorator(node)) { if (ts.isCallExpression(node.expression)) { @@ -52,4 +52,4 @@ export function hasPropertyMetadata(nodes: ts.NodeArray, decorator return false; }) return !!item; -} +} \ No newline at end of file diff --git a/packages/nger-core/lib/platform/change_detector_ref.ts b/packages/nger-core/lib/platform/change_detector_ref.ts index a07763a..7c473a4 100644 --- a/packages/nger-core/lib/platform/change_detector_ref.ts +++ b/packages/nger-core/lib/platform/change_detector_ref.ts @@ -3,12 +3,8 @@ import { Injector } from 'nger-di' import { NgModuleRef } from './ng_module_ref' import { ComponentRef } from './component_ref' import { ComponentFactory } from './component_factory' -export abstract class ChangeDetectorRef { - abstract markForCheck(): void; - abstract detach(): void; - abstract detectChanges(): void; - abstract checkNoChanges(): void; - abstract reattach(): void; +export abstract class ChangeDetectorRef { + abstract next(that: Partial): void; } export abstract class ViewRef extends ChangeDetectorRef { abstract destroy(): void; @@ -61,24 +57,12 @@ export abstract class ViewContainerRef { abstract remove(index?: number): void; abstract detach(index?: number): ViewRef | null; } -import { Subject } from 'rxjs' -export class DefaultChangeDetectorRef extends ChangeDetectorRef { - constructor(public subject: Subject) { +import { BehaviorSubject } from 'rxjs' +export class DefaultChangeDetectorRef extends ChangeDetectorRef { + constructor(public subject: BehaviorSubject) { super(); } - markForCheck(): void { - this.subject.next(); - } - detach(): void { - this.subject.next(); - } - detectChanges(): void { - this.subject.next(); - } - checkNoChanges(): void { - this.subject.next(); - } - reattach(): void { - this.subject.next(); + next(that: Partial): void { + this.subject.next(that); } } \ No newline at end of file diff --git a/packages/nger-core/lib/platform/component_factory.ts b/packages/nger-core/lib/platform/component_factory.ts index 6f38e90..d152932 100644 --- a/packages/nger-core/lib/platform/component_factory.ts +++ b/packages/nger-core/lib/platform/component_factory.ts @@ -7,17 +7,10 @@ import { ComponentClassAst, ComponentOptions } from '../decorators/component'; import { ChangeDetectorRef, DefaultChangeDetectorRef } from './change_detector_ref'; import { InputMetadataKey, InputPropertyAst } from '../decorators/input'; -import { Subject } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import { handlerTypeContextToParams } from './createStaticProvider' -// 这个是编译后的模板文件 -export const ComponentTemplateToken = new InjectionToken(`ComponentTemplateToken`); -// 这个是编译后的样式文件 -export const ComponentStyleToken = new InjectionToken(`ComponentStyleToken`); -// 这个是编译后的json文件 -export const ComponentPropToken = new InjectionToken(`ComponentPropToken`); -// 这个是样式挂载文件 -export const StyleRef = new InjectionToken(`StyleRef`); - +export type Render = (injector: Injector) => (type: any, props: any, ...children: any[]) => T; +export const RENDER = new InjectionToken(`RENDER`) export interface ComponentCreator { (_context: TypeContext): any; } @@ -86,7 +79,6 @@ export class ComponentFactory { injector: Injector, ngModule?: NgModuleRef ): ComponentRef { - const { target } = this._context; // 新建一个 // Component,Directive,Pipe每次取都要创建 // Page/Controller单例 @@ -95,7 +87,7 @@ export class ComponentFactory { // const customElementRegistry = injector.get(CustomElementRegistry); // customElementRegistry.define(this) // 这个是数据监控器 - const $ngOnChange = new Subject(); + const $ngOnChange = new BehaviorSubject({}); const changeDetector = new DefaultChangeDetectorRef($ngOnChange) const currentInjector = injector.create([{ provide: this.componentType, @@ -104,13 +96,15 @@ export class ComponentFactory { const that = this; const proxy = new Proxy(instance as any, { set(target: any, p: PropertyKey, value: any, receiver: any) { - target[p] = value; // 判断是否是@Input const input = that.inputs.map(it => it.propName === p); if (input) { // 这里应该有数据拦截之类的东西,先todo吧 - changeDetector.markForCheck(); + $ngOnChange.next({ + [`${p as string}`]: value + }); } + target[p] = value; return true; } }); @@ -125,13 +119,24 @@ export class ComponentFactory { provide: ChangeDetectorRef, useValue: changeDetector, deps: [] - }], target.name); - let instance = currentInjector.get(target) as C; + }], this.componentType.name); + // 是一个proxy 外部赋值会触发更新,内部赋值需要手动更新 + let instance = currentInjector.get(this.componentType) as C; + const init = {}; + this.inputs.map(input=>{ + init[input.templateName] = instance[input.propName] + }); + $ngOnChange.next(init); // 解析一些属性并赋值 const parserVisitor = currentInjector.get(ParserVisitor); this._context.injector = currentInjector; parserVisitor.parse(instance, this._context); // 设置代理 - return new ComponentRef(currentInjector, instance, changeDetector, target, $ngOnChange); + const ref = new ComponentRef(currentInjector, instance, changeDetector, this.componentType as any, $ngOnChange); + ref.injector.setStatic([{ + provide: ComponentRef, + useValue: ref + }]) + return ref; } } diff --git a/packages/nger-core/lib/platform/component_ref.ts b/packages/nger-core/lib/platform/component_ref.ts index 0720885..4e8f137 100644 --- a/packages/nger-core/lib/platform/component_ref.ts +++ b/packages/nger-core/lib/platform/component_ref.ts @@ -1,6 +1,6 @@ import { Injector, Type } from 'nger-di' import { ChangeDetectorRef } from './change_detector_ref'; -import { Component, ComponentOptions } from '../decorators/component' +import { Component } from '../decorators/component' // 这个是真实的组件 P代表react中的Props,S代表State // 这个是为了更好的融合react/preact才加上的 import { Subject } from 'rxjs' diff --git a/packages/nger-core/lib/platform/platform_core.ts b/packages/nger-core/lib/platform/platform_core.ts index 3554e7f..0a663c0 100644 --- a/packages/nger-core/lib/platform/platform_core.ts +++ b/packages/nger-core/lib/platform/platform_core.ts @@ -14,8 +14,8 @@ import { ApplicationRef } from './application_ref' import { ComponentCreator } from './component_factory' import { PLATFORM_INITIALIZER } from './application_tokens' import { NGER_CONFIG, INgerConfig } from '../sdk/nger-config' -import { Subject } from 'rxjs' -export const topSubject = new Subject(); +import { BehaviorSubject } from 'rxjs' +export const topSubject = new BehaviorSubject({}); export const platformCore = createPlatformFactory(null, 'core', [{ provide: APP_INITIALIZER, useValue: () => { }, diff --git a/packages/nger-dom/lib/component-factory-strategy.ts b/packages/nger-dom/lib/component-factory-strategy.ts index 7f52a02..67adc71 100755 --- a/packages/nger-dom/lib/component-factory-strategy.ts +++ b/packages/nger-dom/lib/component-factory-strategy.ts @@ -223,6 +223,6 @@ export class ComponentNgElementStrategy implements NgElementStrategy { return; } this.callNgOnChanges(); - this.componentRef!.changeDetectorRef.detectChanges(); + // this.componentRef!.changeDetectorRef.detectChanges(); } } diff --git a/packages/nger-platform-browser/lib/application.tsx b/packages/nger-platform-browser/lib/application.tsx index b225b07..bbdf20c 100644 --- a/packages/nger-platform-browser/lib/application.tsx +++ b/packages/nger-platform-browser/lib/application.tsx @@ -1,5 +1,5 @@ // 负责挂载到dom 如果是小程序 可设为空 -import { ApplicationRef, ComponentFactory, ComponentRef, ElementRef, ComponentFactoryResolver, ComponentMetadataKey } from 'nger-core' +import { ApplicationRef, ComponentFactory, ComponentRef, RENDER,ElementRef, ComponentFactoryResolver, ComponentMetadataKey } from 'nger-core' import { Injector, Type, InjectFlags } from 'nger-di' const { render, h } = require('preact'); export function ngerRender(injector: Injector) { @@ -20,16 +20,15 @@ export function ngerCreateElement(injector: Injector) { const resolver = injector.get(ComponentFactoryResolver) const factory = resolver.resolveComponentFactory(tag) const ref = factory.create(injector); - if (ref) { } + if (ref) { + (ref.instance as any).render(ngerCreateElement(ref.injector)) + } } } } -(window as any).h = (injector: Injector) => (tag, attr, ...children) => { - if (typeof tag === 'string') { - } else { - } -}; +(window as any).h = h; + export class BrowserApplicationRef extends ApplicationRef { root = document.getElementById('app') as HTMLDivElement; constructor(injector: Injector) { @@ -52,13 +51,9 @@ export class BrowserApplicationRef extends ApplicationRef { //这里渲染preact if (ref.instance.render) { const tpl = ref.instance.render.bind(ref.instance); - const res = tpl() - render(res, parent.nativeElement) - // 更新试图,后面有可能会自己实现 - ref.$ngOnChange && ref.$ngOnChange.subscribe(() => { - const res = tpl() - render(res, parent.nativeElement, parent.nativeElement.lastElementChild) - }); + const h = ref.injector.get(RENDER) + const element = tpl(h(ref.injector)); + parent.nativeElement.appendChild(element) super.attachView(ref, injector); const nowTime = new Date().getTime(); const totalTime = nowTime - (window as any).nger.startTime diff --git a/packages/nger-platform-browser/lib/index.tsx b/packages/nger-platform-browser/lib/index.tsx index ce1aa27..070aa53 100644 --- a/packages/nger-platform-browser/lib/index.tsx +++ b/packages/nger-platform-browser/lib/index.tsx @@ -1,11 +1,15 @@ -import { createPlatformFactory, ApplicationRef, Http, platformCore, CustomElementRegistry, History, NgModuleBootstrap } from 'nger-core' +import { createPlatformFactory, RENDER,ApplicationRef, Http, platformCore, CustomElementRegistry, History, NgModuleBootstrap } from 'nger-core' import { Injector } from 'nger-di' import 'document-register-element'; import { createBrowserHistory } from 'history'; import { BrowserApplicationRef } from './application' import { NgerPlatformBrowser } from './bootstrap' import axios from 'axios' +import {render} from './render' export default createPlatformFactory(platformCore, 'browser', [{ + provide: RENDER, + useValue: render, +},{ provide: Http, useValue: axios }, { diff --git a/packages/nger-platform-browser/lib/render.ts b/packages/nger-platform-browser/lib/render.ts new file mode 100644 index 0000000..d6ac26f --- /dev/null +++ b/packages/nger-platform-browser/lib/render.ts @@ -0,0 +1,52 @@ +import { Injector } from 'nger-di' +import { ComponentFactoryResolver, ComponentRef, ComponentFactory } from 'nger-core'; +import { pluck, distinctUntilChanged } from 'rxjs/operators' +export function render(injector: Injector) { + return (tag: any, props: any, ...children: any[]) => { + if (typeof tag === 'string') { + const factory = injector.get(ComponentFactory) + const ref = injector.get(ComponentRef) + const element = document.createElement(tag) + const onChange = ref.$ngOnChange; + if (props) { + Object.keys(props).map(key => { + const attribute = document.createAttribute(key); + const val = props[key]; + console.log({ key, val }) + // 检查是否在 input里 + let input = factory.inputs.find(input => input.propName === val) + if (input) { + // 在input里 + if (onChange) { + onChange.pipe( + pluck(val), + distinctUntilChanged() + ).subscribe(res => attribute.value = res) + } + } else { + attribute.value = val; + } + element.setAttributeNode(attribute) + }) + } + if (children) { + children.map((child) => { + if (typeof child === 'string') { + console.log(child) + const childElement = document.createTextNode(child) + element.append(childElement) + } else { + element.append(child) + } + }) + } + if(onChange) onChange.subscribe(res=>console.log(res)) + return element; + } else { + const resolver = injector.get(ComponentFactoryResolver) + const factory = resolver.resolveComponentFactory(tag); + const ref = factory.create(injector); + (ref.instance as any).render(render(ref.injector)) + } + } +} \ No newline at end of file diff --git a/readme.md b/readme.md index 2c7e281..8153b66 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,8 @@

Vue logo

+ +项目开发中,api频繁变动中! + ## Nger > nger, 用ng的人!I am a nger!