jing-js / jing.js

Flexible javascript MVW framework
MIT License
0 stars 0 forks source link

思考记录(一) #1

Open YuhangGe opened 9 years ago

YuhangGe commented 9 years ago

这个issue用于记录jing.js初期进行功能和架构等相关设计和思考的点滴,用于日后整理

YuhangGe commented 9 years ago

关于jing.js

jing.js是构想中的全新的Web App开发脚手架。构想是指,这个项目还处于萌芽阶段;但拒绝嘴炮和吹逼,拒绝拖沓。全新强调的是这是一个和己有的东西在核心上有本质区别的玩意;它可能会借鉴包括jquery,angular,react等优秀产品的思想或功能。Web App是指,在浏览器上的以单页为主的富交互、多逻辑、强数据的html5应用;不关注以信息展示为主的诸如传统门户网站。开发脚手架是指,该项目将提供全新的Web App驱动核心以及围绕该核心的工具产品;在前期jing.js还没有实体成型前,我们尽可能拒绝使用“框架”一类的词汇;但jing.js的目标和野心,确实是期望能做出一个全新的Web App开发框架。

欢迎有如下特质的同学加入共同思考和探索:

YuhangGe commented 9 years ago

设计理念

YuhangGe commented 9 years ago

将工程化工具和逻辑核心统一起来进行设计

YuhangGe commented 9 years ago

设计概要

这儿是设计概要,每一块之后都会进一步详细补充设计。

双向绑定

双向绑定是跟angular的双向绑定概念和基本用法一致,同时也会吸收PC软件和手机APP开发中的双向绑定思想。目前来看,jing.js的双向绑定核心将会使用javascript的defineProperty。

逻辑属性

逻辑属性是angular的directive的概念抽取。它基本等价于不带template和directive。逻辑属性是写在dom元素上的会被jing.js解析的attribute。逻辑属性的定位是非常轻量的逻辑控制,在之后的设计中,它不会拥有优先级等概念。不大准确的例子可以理解为,逻辑属性是诸如ng-click, ng-model这一类的angular-directive,但ng-repeat等在jing.js中将会以泛化组件的概念存在。

泛化组件

泛化组件是指将一切组件化。其思想来源于react,整个页面都是由一个层次一个层次的组件组合和拼凑而成。但和react会有本质不同的是,jing.js仍然是基于html而不是jsx。

泛化组件包括两大形式。一是独立的组件,它可以像npm/seajs一样地独立开发,有独立版本,组件之间能够相互依赖;但和npm/seajs不一样的是,jing.js的组件只能服务于使用jing.js的项目,并且组件不只是包含js,还有html/css/less/resources。二是页面区块组件,可能就只是简单的container,或是angular的ng-controller用于隔离逻辑。区块组件和独立组件拥有相同的生命周期和状态转化,可以相互依赖,无缝链接。

(泛化组件的英文应该是generalize component,简写gc容易和垃圾回收Garbage collection混淆。日后看看如何更好的命名这个概念)

数据源

数据源是jing.js期待解决的目前还没有框架解决好了的核心问题。数据源思想来源于PC软件和手机APP开发中已经广泛采纳的DataSource概念,其价值在于希望简化数据在服务器和App之间的传递和处理。在jing.js中,数据源将会和泛化组件的设计进行深度结合。

路由

路由基本借鉴angular、react以及服务器端框架(nodejs/php/java)的路由,可以进一步通过在jing.js核心中融入路由的设计来尝试增强。

YuhangGe commented 9 years ago

泛化组件

泛化两字完全是为了取名而取名。其实就是组件,只不过将组件概念广泛化了。泛化组件在表现形式上参考了angular的很多设计,但内部完全不同。

<env key="somEnv">
  <div>
    <repeat source="item in somelist | someFilter" >
      <div>{{item.name}}</div>
      <div>{{item.value}}</div>
      <div><button click=""></div>
    </repeat>
  </div>
  <form>
     <input t-bing="someVarInEnv" />
     <canlendar t-bind="someVarInEnv" />
  </form>
</env>

其中<evn><repeat><canlendar>都是泛化组件,而envrepeat是页面组件,canlendar是独立组件。

页面组件可以理解成angular的directive的一种,比如ng-controller和ng-repeat,不一样的是在jing.js中一定是以Element(而不是attribute)的形式。这样的做法是为了更严谨而直观地处理像优先级一类的问题,同时也为了更好的处理泛化组件的状态变化。

独立组件其实就好比npm或seajs里的模块概念。jing.js框架在解析到这个组件时,如果发现这个组件还不存在,则会去下载。独立组件会包括逻辑Js代码,html模板和css以及其它静态资源。如何处理组件之间的依赖,包括静态资源的依赖等都还在进一步思考。

此外,除非主动指定属性dom="div",泛化组件的DOM元素本身不会存在于最后的浏览器DOM树里,这一点跟Angular是不同的,可以具有更好的可控性和灵活性。比如<repeat>组件就可以实现每一次循环插入多个div。

YuhangGe commented 9 years ago

泛化组件(状态)

不论是页面级的只带逻辑的组件(如env, repeat)还是自带html/css的独立组件,它们都会有统一的状态,统一的生命周期,统一的事件。

状态

downloading 下载中

如果组件不存在,则需要先下载。对于页面组件,这个状态会被直接跳过。

initializing 初始化中

这个状态对于泛化组件非常重要。在该状态中,泛化组件会去加载其依赖的数据源。比如从一个或多个URL接口获取数据,处理和拼接数据。也可能是建立基于socket的双向数据通道并取得初始数据。如果一个泛化组件没有依赖需要异步获取的数据,这个状态会被直接跳过。

rendering 渲染中

这个状态会真正地处理DOM元素,进行绑定数据等操作。并且,对于其内部的子组件,则会开始子组件的状态生命周期。也就是说,一个复杂的APP页面可能是由许多的泛化组件构成,这些组件的生命周期的开始并不是完全并行的,而是从父亲到儿子这样的顺序关系。(更新:但也存在某些场景下,需要在子组件也全部Ready后再一次性展示出来。因此考虑可以配置到底在什么时候真正地把DOM显示出来)

ready 就绪

渲染完成后,该泛化组件会将DOM元素真正地插入到浏览器DOM里面。

refreshing 刷新

泛化组件需要更新数据源时,进入到这个状态。这个状态结束后,又会跳到rending状态。

sleeping 睡眠

泛化组件在不需要活动时进入睡眠状态(比如路由变化导致页面切换时)。睡眠状态下如果是socket的双向通信数据源将不会刷新数据。当然双向通信数据源可能会在更日后的版本里设计,初期这个状态主要是给业务层代码手动进行处理(类比于在angular的$scope的$destroy事件里回收资源,但不一样的是jing.js里泛化组件只是sleep了)。

事件

上面的事件基本对应于不同状态时的切换,对内和对外通知。

YuhangGe commented 9 years ago

泛化组件(状态和UI的展示)

泛化组件的状态变化时,往往UI界面需要有的相应反馈。为了更好的抽象这些大量重复的逻辑,引入特殊的一个泛化组件。暂时用<com>来命名。

<env key="someEnv" dom="div">
  <com role="splash" dom="div" class="splash">
    <i icon="loading"></i><span>loading...</span>
  </com>
  <com role="content">
    <p>{{someObj.someProp}}</p>

  </com>
</env>

当泛化组件还处理非Ready状态时,它会显示role="splash"com。同时,com组件还有更灵活的设计。

可以全局配置com组件在某个role时的模板。比如role='splash'这种loading的提示层,统一用一个模板。这样可以实现轻量级的组件复用并且修改起来更简单。

还有一个更大的想法是用com组件来实现独立组件的灵活自定义。是否有这样一种情景,我有一个写好的组件可以使用,但我发现即便覆盖它默认的css,也很难得到我想要的UI视觉效果。我想要能够自定义html模板,但使用组件的逻辑。

在jing.js中,独立组件和页面是统一的,因此在设计和实现独立组件时,可以把关键的和逻辑相关的DOM元素指定为com并为其分配一个role。在使用独立组件时,可以通过com来进行映射,从而实现高度的自定义化组件。以下代码是一些非常原始的想法。

<!-- 组件,名称假设为 InputName -->
<div>
  <p>这里是组件默认的文案</p>
  <com-def role="name">
    <input  t-bind="name" />
  </com-def>
<div>

<!-- 简单使用组件 -->
<InputName />

<!-- 定制化使用组件 -->
<InputName>
  <h1>这里文案连html结构都变啦</h1>
  <div>
    <com role="name" />
  </div>
</InputName>
YuhangGe commented 9 years ago

泛化组件和数据源

这里记录对泛化组件和数据源之间关联和使用的初步想法。至于数据源本身如何设计还在思考中。

function SomeComponent(env) {
   var Account = require('AccountModel');  //Model是一种数据源
   var Page = require('PageModel');

   env.props = {
     account: Account.get({userId: 888}),
     pages: Page.getAll(),
     numA: 34,
     strA: 'dsdsdsd'  
   }

   env.addState(function() {
     //这里可以处理更复杂的情况,主动干预组件的状态。
     // 比如某些情况下要处理一些多消耗大量时间(web worker)的计算? 
   });
}

module.exports = function() {
  return SomeComponent;
}
<env key="SomeComponent">
  <h1>Hello, {{account.userName}}</h1>
  <repeat source="page in pages">
   {{page.detail}}
  </repeat>
</env>

每一个泛化组件都会有一个逻辑环境,也可以理解成就是该组件的实例。env.props定义了该逻辑也就是该组件的属性(类似于React的props)。如果组件发现属性是一种数据源,那么则会监听该数据源的事件。只有所有数据源的状态都是数据已经加载时,该组件才会把自己的状态更新为rendering.

YuhangGe commented 9 years ago

路由

路由的设计有两种思路。一种是采用angular-ui-router的方式,基于状态树。一种是采用服务器端路由的思路(比如php的yii框架),直接将路由做为整个框架的骨架。

目前还在琢磨两种方式的优劣,以及是否可以把二者进行结合。

YuhangGe commented 9 years ago

路由(二)

在实际业务中有如下一种场景:

一个list页面,点击里面的某个链接进入detail页面。然后再返回到list页面。期待如果detail页面没有对list页面有影响的情况下,返回时可以保持list之前的状态,包括dom结构、数据和浏览器滚动位置等。一方面可以避免多余的网络访问和计算(重新获取数据并渲染),另一方面也可以让用户查看之前的页面位置。

因此,路由在切换时,对应于Environment会进入Sleeping状态。当从Sleeping状态恢复(也就是用户重新切换回来时),可以从上一个路由状态里读取信息,从而决定是否重新加载数据。其实要不要重新加载数据,这些都是业务代码自己控制的。而路由模块,需要提供的只是简单的可以在路由状态切换时进行数据的传递。

由此考虑到的Environment在设计时,可以配置其对应的DOM块,在Sleeping的时候是只是'display:none'来隐藏,还是'remove'后保留在内存,还是测底的'remove'掉。这三种情况可能各有使用场景,比如路由切换时,相关联页面(如父状态到子状态,list页面到detail页面)使用'display:none'。非关联页面切换时,测底'remove'掉。