Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于组件规范的几个想法,大家看下 #6

Closed
jpssff opened this issue Mar 27, 2014 · 29 comments
Closed

关于组件规范的几个想法,大家看下 #6

jpssff opened this issue Mar 27, 2014 · 29 comments

Comments

@jpssff
Copy link

jpssff commented Mar 27, 2014

组件类

  1. 建议给出一个具体的指导风格,比如使用Control.extend()扩展原型。

  2. 接口规范。

    一方面可以统一api,另一方面,可以更好在业务层面做控制。目前想到的一个必须实现的接口。 @jinzhubaofu @chriswong 看看呢,缺少这个规范,感觉对贡献者有点疑问。

    prototype.dispose()  释放资源
    

版本控制

根据之前沟通,使用上可以分为最新版,带版本号两种方式。版本号和release的三位号码一致。如:

require('ui/tabs');   //最新版
require('ui_0.6.8/tabs');  //带版本号

其中moye组件最新版前缀为'ui', 带版本号前缀格式为'ui_版本号'

求拍。

@leeight
Copy link
Member

leeight commented Mar 27, 2014

肯定不应该是ui_0.6.8/tabs

@jpssff
Copy link
Author

jpssff commented Mar 27, 2014

给个建议个格式?

@leeight
Copy link
Member

leeight commented Mar 27, 2014

require.config({
  "packages": [
    {
      "name": "ui",
      "location": "dep/ui/0.6.8/src",
      "main": "main"
    }
  ]
});

require( "ui/tabs" );

@jpssff
Copy link
Author

jpssff commented Mar 27, 2014

@leeight 可能有点误解我的意思。这里解释下最新版,带版本号

  1. 两者同时提供,即:

    require.config({
      "packages": [
        {
          "name": "ui",
          "location": "dep/ui/0.6.8/src",
          "main": "main"
        },
        {
          "name": "ui_0.6.8",
          "location": "dep/ui/0.6.8/src",
          "main": "main"
        },
        {
          "name": "ui_0.6.5",
          "location": "dep/ui/0.6.5/src",
          "main": "main"
        }
      ]
    })
    
    require('ui/tabs'); //最新版
    require('ui_0.6.5/tabs'); //采用一个旧版本,比如一些专题页,以后不打算升级的。
    

    最新版configlocation会随着moye新的realease版本而变化,而带版本号,不会变了,直到退休而被删除。

  2. 统一require.config:因为多个ZX模板可能出现在同一页面,所以一个页面中可能存在多个版本的moye代码。如果把require.config的使用权下放到模板,则很有可能相互覆盖,导致混乱。

@leeight
Copy link
Member

leeight commented Mar 27, 2014

require('ui_0.6.5/tabs'); //采用一个旧版本,比如一些专题页,以后不打算升级的

那在专题页面require.config配置不同的内容?我感觉一个页面不应该同时依赖两个ui的版本的情况吧

@otakustay
Copy link
Member

拿刀逼着 @errorrikmap实现了不就能搞定这事了吗╮(╯▽╰)╭

@jpssff
Copy link
Author

jpssff commented Mar 27, 2014

那在专题页面require.config配置不同的内容?我感觉一个页面不应该同时依赖两个ui的版本的情况吧

设想一个场景:zx模板数量达到几百以后,已经有数十个模板采用tabs组件。但这时组件要做一次整体升级,因为交互和样式发生了巨大变化,导致无法兼容升级,这时新的一批模板采用了新版tabs,而之前那数十个模板,因为人力等原因,短期内还无法升级到最新版,所以在搜索结果页就会出现新旧版共存情况。

拿刀逼着 @errorrik 把map实现了不就能搞定这事了吗╮(╯▽╰)╭

map也没法完美解决一个页面多个ui版本共存情况。当然,带版本号只是一个备用方案,绝大情况下还是不需要带版本的。

@otakustay
Copy link
Member

多版本共存是需要的,但首贴的例子有些问题,小版本的升级应该是向后兼容的,而0.x1.x同时存在倒是非常合理的需求

map也没法完美解决一个页面多个ui版本共存情况

我觉得是可以的,map可以配置某个模块依赖ui的版本,因此开发模块foo时还是require('ui'),只是在map中指定foo中使用的ui0.6.8就行,具体还有什么场景解决不了给个案例?

@chriswong
Copy link
Member

jQuery 这么大的库都接入了, esl 支持 map 增加的体积应该可以接受,@errorrik 你就从了吧,要不然实现 require.config 的 上下文 require 也能解决

@jpssff
Copy link
Author

jpssff commented Mar 27, 2014

我觉得是可以的,map可以配置某个模块依赖ui的版本,因此开发模块foo时还是require('ui'),只是在map中指定foo中使用的ui是0.6.8就行,具体还有什么场景解决不了给个案例?

首先,先解释下,moye自身的模块是不受版本号影响的。 因为require的都是相对路径。

而在模板的开发中,开发者需要先配置下map,可能有下面两种方式:

var _require = require('mapper').map({'ui':'ui_0.6.8'}); // 仅作示例

var tabs = _require('ui/tabs');
require.map(
    [{'ui':'ui_0.6.8'}],
    function(require){
        var tabs = require('ui/tabs');    
    }
);

无论哪种方式,都需要开发者先配置map,那么就存在命名可能不规范、冲突情况。

如果对map的理解有误,请指出。

带版本号方式如下:

var tabs = require('ui_0.6.8/tabs');

相比之下,尽管看起来不太优雅,但模块名称是不能随意修改的,可以避免命名混乱,而且调用方式也不复杂。

@otakustay
Copy link
Member

感觉map和你说的不一样,在AMD里map是这么工作的:

首先你有个foo模块,要用0.6.8版本,有个bar模块,要用1.2.1版本,你可以这么写foo.js

// foo.js
define(
    'foo',
    function (require) {
        var tab = require('ui/tab'); // 0.6.8
    }
);

然后这么写bar.js

// bar.js
define(
    'bar',
    function (require) {
        var tab = require('ui/tab'); // 1.2.1
    }
);

你会发现其实foobar根本不关心版本号,然后你可以这么写配置

require.config({
    packages: [
        {
            name: 'ui0.6.8',
            location: 'dep/ui/0.6.8/src'
        },
        {
            name: 'ui1.2.1',
            location: 'dep/ui/1.2.1/src'
        }
    ],
    map: {
        foo: {
            ui: 'ui0.6.8'
        },
        bar: {
            ui: 'ui1.2.1'
        }
    }
});

总之你本身模块根本不关心自己用的ui是啥版本,哪天foo可以用1.2.1版本了,只需要改map配置即可

@jpssff
Copy link
Author

jpssff commented Mar 27, 2014

结果页模板的情况是:

  1. 可能出现多个阿拉丁模板;
  2. 同一个模板可能出现多次;
  3. 不同模板可能使用了同一个ui组件的不同版本;

你会发现其实foo和bar根本不关心版本号,然后你可以这么写配置

如果这么配置,则是全局生效的,比如foo在页面中被两个模板用到,被分别配置为1.x, 2.x,那么就出现了上面说到的“冲突情况”。而我上面举得两个例子,map的作用都限制到单个模板中。

所以, @otakustay 说的这种map方式,不太合适。

总之,这种map要解决的问题是:

  1. 一个页面存在多个模板,且模板使用了一个ui组件的不同版本,需要避免冲突;
  2. 尽量不要在模板中修改require.config. 如果需要修改,则需要保证不能重名,或者只在当前模板的js执行时生效。

@otakustay
Copy link
Member

  1. 一个页面存在多个模板,多个模板不应该对应多个AMD模块吗,每个模块再通过map配置对应ui的版本
  2. 不修改require.config的目的是什么呢,为了缓存吗?不重名是很容易做到的一件事

比如foo在页面中被两个模板用到,被分别配置为1.x, 2.x,那么就出现了上面说到的“冲突情况”。

通过build脚本,先把foo也变成package,复制2份,分别在packages中设定为foo1foo2,2个模板通过map将自己的foo映射为foo1foo2,随后再通过mapfoo1foo2依赖的ui版本指定

我的观点是

  1. AMD提供的标准config肯定能满足需求,只要系统本身有一个良好的架构设计
  2. esl尽量控制在AMD标准里,不要超出太多

如果真的不喜欢在标准的范围下通过架构和设计来满足,那就直接在源码里写版本号吧……我认为前面 @jpssff 提的2个方案都不靠谱:

  1. 第一个方案需要有一个_require,意味着build的时候要再去关注这个_require,同时还要知道_require对应的map或者paths等信息,不然代码合并和模块编译会有坑,而这个成本是非常大的,这里面的map的提取不见得可靠,因此复杂度远大于单纯找require
  2. 第二个方案,esl不可能去提供这种map接口

@leeight
Copy link
Member

leeight commented Mar 27, 2014

还是把svn发出来讨论吧

@chriswong
Copy link
Member

其实 requirejs 有个类似的实现:

var requireContext = require.config({ .... } );

requireContext(['a'], function () {} );

这样在 a 模块相关的 require 都使用 requireContext 的配置

@chriswong
Copy link
Member

组件类

  1. 建议给出一个具体的指导风格,比如使用Control.extend()扩展原型。

  2. 接口规范。

    一方面可以统一api,另一方面,可以更好在业务层面做控制。目前想到的一个必须实现的接口。 @jinzhubaofu >@chriswong 看看呢,缺少这个规范,感觉对贡献者有点疑问。

prototype.dispose() 释放资源

这方面文档在下周一之前出

@jpssff
Copy link
Author

jpssff commented Mar 27, 2014

后面讨论的有点偏了。

上面我说的map示例,只是是表达map的作用以及和版本号方式比较。并不推荐使用map。

@jpssff
Copy link
Author

jpssff commented Mar 28, 2014

又看了下上面的讨论,感觉我没太描述清楚为什么要加版本号map为什么不能解决版本号问题,这里再补充几句:

为什么要加版本号

  • 在组件进行非兼容的大改版时,允许老的代码依然可以采用旧版本组件;
  • 在具体项目中,一个低版本的组件已经完全满足需求,开发者更愿意采用低版本进行开发。比如担心代码量、兼容性啥的。。

map为什么不能解决版本号问题

因为map实际是用来解决不同组件依赖的一个组件版本这个问题的,比如a依赖tabs0.0.1,而b依赖tabs0.0.2,在一个模板中同时使用a``b不会出现依赖错误。而在百度结果页中,实际情形是模板1使用了tabs0.0.1 ,而模板2使用了tabs0.0.2, 当模板1模板2出现在同一页面,各自require('tabs')想获取正确的版本时, @otakustay 提到的map(即amd常见的实现方式),就没法解决了,也就是上面我说没法“完美解决”的原因。

关于我提到两种map方式,仅仅表达如何设计map,才能实现上面情形下,每个模板require('tabs')得到自己想要的版本。不建议采用。我的观点是采用版本号。

所以,我第一帖关于版本的,建议的用法是直接在前缀加上版本号。比如require('ui_0.6.8/tabs')

@leeight
Copy link
Member

leeight commented Mar 31, 2014

刚才跟大佛在群里讨论的解决方式是这样子的:

因为不同的模板代码有可能都会输出到大搜索的结果页,但是输出的时候,把匿名的模块改成具名的模块就可以继续使用map来处理这个问题了。例如:

<!-- 1.tpl -->
<div>blabla...</div>
<script>define(function(){ require( 'tabs' )})</script>

<!-- 2.tpl -->
<div>blabla...</div>
<script>define(function(){ require( 'tabs' )})</script>

当输出到大搜页面的时候,编译成如下的代码:

<div>blabla...</div>
<script>define( 'tpl/1/main', function(){ require( 'tabs' )})</script>
<div>blabla...</div>
<script>define( 'tpl/2/main', function(){ require( 'tabs' )})</script>

这样子一切都可以在amd的规则下运行。

@errorrik
Copy link

jQuery 这么大的库都接入了, esl 支持 map 增加的体积应该可以接受,@errorrik 你就从了吧,要不然实现 require.config 的 上下文 require 也能解决

map早就实现了诶,多少年前的事情了.....

@errorrik
Copy link

我觉得 @leeight 在上两楼提的方案还是比较靠谱的。
不过话说,tpl中的module id,是不是也应该有一些规则,防止泛滥。凡是named的东西就可能冲突

@chriswong
Copy link
Member

esl 有 map 就好,多版本的问题有最优解了

@jpssff
Copy link
Author

jpssff commented Mar 31, 2014

因为不同的模板代码有可能都会输出到大搜索的结果页,但是输出的时候,把匿名的模块改成具名的模块就可以继续使用map来处理这个问题了。

这个方法看似行得通。但操作起来,仍有点问题。最终生成的模板如下:

<div>aaa...</div>
<script>
require.config({
    map: {
         'tpl/a/main': {
            'tabs': 'tabs0.1.0'
        }
    }
});
define( 'tpl/a/main', function(){ require( 'tabs' )});
</script>

<div>bbb...</div>
<script>
require.config({
    map: {
        'tpl/b/main: {
            'tabs': 'tabs0.2.0'
        }
    }
});
define( 'tpl/b/main', function(){ require( 'tabs' )});
</script>

两个问题:

  1. 如果a == b, 即一个模板出现两次,似乎define仅会执行一次;第二个模板js不会执行。出现多次,仅第一次js正常执行;
  2. 开发者仍需要关心require.config配置,尽管可以通过配置自动生成。但个人感觉仍没有版本号/modulename方便和直观。

@errorrik
Copy link

如果a == b, 即一个模板出现两次,似乎define仅会执行一次;第二个模板js不会执行。出现多次,仅第一次js正常执行;

我觉得,define的module,应该是一个template的renderer。

假设在页面内容flush前,我们就知道有哪些templates,输出就应该是:

<script>
require.config({......});
define( 'tpl/a/main', function(){ return function (wrap) {} });
define( 'tpl/b/main', function(){ return function (wrap) {} });
</script>

<body>
<div>aaa...</div>
<script>require(['tpl/a/main'], function(aMain) {
  aMain(wrapDiv)
});
</script>
<div>bbb...</div>
<script>require(['tpl/b/main'], function(bMain) {
  bMain(wrapDiv)
});
</script>

当然,如果是chunk,那可能会输出多份define,那是没办法的事情,本来代码也是输出多份的。只要实现的是动态渲染,并且使用是require(['tpl/b/main'], function(bMain) {bMain(wrapDiv)});的模式,卡片就能正常render,正确性是有保障的。

开发者仍需要关心require.config配置,尽管可以通过配置自动生成。但个人感觉仍没有版本号/modulename方便和直观。

开发者要使用基础组件的什么版本,其实是由开发者自己决定的。他可以完全不care map,直接按版本号指定自己要使用的版本。但如果这么做,会带来一个后果:组件升级后,没法同步更新到组件的使用者。当然指定map也是这样,但是如果通过map来做,当我们去掉map配置的时候,就会自动切换到最新版本,无需修改原文件。

so,我觉得这个问题,不是现在设计基础结构要考虑的,而是在指导开发者怎么开发怎么选择时,才去考虑。

@jpssff
Copy link
Author

jpssff commented Mar 31, 2014

我觉得,define的module,应该是一个template的renderer。

不错,这样没问题。大家可以再review下,我觉得讨论可以关闭了。

@leeight
Copy link
Member

leeight commented Mar 31, 2014

上面讨论的方案在ps上面怎么用?

@jpssff
Copy link
Author

jpssff commented Apr 1, 2014

@leeight 目前,ps对兼容的升级采用类似map方式使用最新版,对于非兼容的升级,修改了组件名,格式为name+版本号

A.use('tabs'); //api不变的最新版
A.use('tabs2'); //非兼容升级,api发生变化

@leeight
Copy link
Member

leeight commented Apr 1, 2014

都是A.use,有啥区别?

@jpssff
Copy link
Author

jpssff commented Apr 2, 2014

名称tabs2,后面的数字代表版本号。因为组件版本不会太多,直接采用+1方式区分版本了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants