• 能。但是为什么要搞?

  • @hxygsh Good, thanks for your contributions.

  • #8 楼 @hxygsh 这个没有什么时间和任务要求,完全是靠社区驱动的,有时间就来贡献点内容便好。

    加入的方式很简单,点击:https://crowdin.com/project/emberjs?auto_accepted=12858931 用你的帐号登陆就会自动把你加入进来

    如果自动加入没生效,登录 Slack:https://embercommunity.slack.com/messages/dev-translations/details/

    然后在 dev-translations 频道里(上面的链接就是)@lock,跟他说你要加入 Chinese 的翻译工作,然后告诉他你的 crowding 账号,他会帮你加入的。

  • #6 楼 @akirapanda 嗯,你说的很对,就是这个区别导致了二者入门的门槛高度不同,但是上升曲线上 ember 反而平缓很多。随着越来越多规模较大的前端工程出现和趋于成熟,ember 的好处也会越来越被人所接受,其实这一方面正如你说的,PHP 和 Rails 的差别。

  • #3 楼 @akirapanda 你恰好说反了。

    ng 不知道 npm/bower/module/di 的确可以写一个简单的 demo,但是你要做正经的项目就必须得精通这些。Why?因为 ng 没有“约定”,你没有架构师的斤两(或者说你的团队里没有这样的人)就很难以 ng 做一个可靠的大规模项目,里面所有的坑都需要你(们)自己摸索自己填(当然现在也有不少教程和经验分享了,可是也以英语为主,英语要是过关早就自己摸索过去了)

    ember 就不一样了,它是一个非常重视“约定”的体系,你不知道 npm/bower/module/di 一样可以写一个简单的 demo,而且 ng 你至少还得打个最简单的架子吧?可你看看 https://ember-twiddle.com,连架子都不需要你搭。

    再说了。像 ember 或 ng 这样的框架,那种只需要一个 html 就可以写的 demo 有什么鸟用?谁会为了这样一个 demo 专门去学一个框架?像 ng 那种自欺欺人的入门教程根本就是在小看正经开发者的智商。

    不说 demo,更重要的是做正经的项目 ember 也可以不需要你精通 npm/bower 等等(话说知道这些很难吗?我们是程序员诶),为什么?因为 ember 有全能大管家 Ember CLI,就连 ng 都知道自己的“松散”是个致命伤,所以现在有了 angular-cli 项目,这个项目说明第一句话就是:

    Prototype of a CLI for Angular 2 applications based on the ember-cli project.

    当初如果有这东西,犯得着一群开发者造一堆基于 yeoman 的 seed projects 吗?在这层面 ng 像媲美,不,追少 ember 还差的很远。这就是所谓的远见了,当初 Ember CLI 刚出来的时候,短视的人也会骂它不好用不够强,可是他们看不到它的好处,现在它逐渐成长了,知道厉害了吧?

    用 ember 的好处就是团队也不需要一名顶梁柱式的架构师,因为人家已经把架构给你安排妥当了,唯一的难点就是因为这些东西都帮你安排妥当了,所以你得先学会这一套东西!这才是为什么大家认为 ember 比 ng 难入门的原因,归根结底是因为大部分前端工程师还处于手工作坊的意识阶段,所以才感觉 ng 比 ember 容易接受,可事实的真相是从工程角度上来对比,ng 和 ember 根本就不是一个级别的水准。

    • angular-cli 是基于 ember-cli 的
    • react-router 以及 angular 2 router 是基于/ inspired by ember router 的核心 route-recognizer 的(两家的项目主页都表示了感谢)
    • ember fastboot 是第一家官方提供 SSR 实现的框架,而现在能做到 SSR 的 react 体系都需要自己造一整套轮子,ng2 的对应项目正在造轮子中——这可不是件小事,其中的坑那是无比艰辛……
    • ember-cli 是最早能让开发者直接使用 es2015 + modules 并且无需学习和整合 babel 的

    从工程和系统架构的很多方面来看 ember ng react 等等,ember 真是可以表示“我不是针对谁……”

    @hxygsh 楼主,我想提个建议给你。

    第一呢,你正在写的那些教程实在是有点过时了,如果是为了研究 discourse 那还有情可原,但如果是针对初学者的话我建议还是跟上主流的版本为好。

    discourse 作为 Ember v1 时代的工程典范的确有其值得学习和借鉴的地方,而且 discourse 的团队也为 ember 的进步发展做出了很多帮助。只是因为它的架构实在过于庞大,而且当初也针对 ember 不够完善的地方做了很多定制性的东西,所以迁移到 ember 2 的技术体系就不是那么容易了。ember 1 和 2 的架构体系差异还是蛮大的,特别是 controller 和 view 这俩层在 ember 2 几乎就没有了(view 彻底没有,controller 还剩最后一点用处,马上就没了)。所以面向未来以及初学的话,discourse 并不是一个很好的教材。

    第二,ember 经历了足够长的核心开发周期之后,未来的大方向和目标已经基本敲定,今年将会投入很大的精力在对初学者的帮助和培训上。今年特地重组了 ember 的核心团队,又特别组建了官方的文档和学习团队,重新编写了最新的手册和 API 文档,并且整合了 crowdin 做多语言翻译管理。

    如果你对这方面有兴趣并且有时间的话,欢迎你加入 Ember 文档团队的 I18n 项目,一边学习最新的 Ember 知识一边帮助整个项目做文档的多语言化和改善工作,何乐而不为呢是吧?

    有兴趣的话可以先看看这里:https://crowdin.com/project/emberjs 。愿意参与的话可以联系我或者直接 Slack Ember 的文档频道申请加入。

  • #11 楼 @mizuhashi

    1. CSS Modules 意味着可以以模块为单位 isolate stylesheets,但并不意味着 all stylesheets should be isolated,而且模块化 CSS 也还是以 class 为主的,并不是 css in js 为主
    2. 你可以去看现在很多很多开源的模块套件,特别是以 polymer 为主的(因为比较接近原汁原味的 scoped css),独立的模块也不至于要把所有的样式都隔离来写,特别是像 fontFamily 这种明显全局应用更合理的样式
    3. 如果你做一套组件发行出去或在项目里应用,也不代表这套组件没有全局性的样式部分,如果真的没有那这套也太“傻”了

    我建议别走“技术极端化主义”路线,特别是 web 开发这块,有些想法再理想也要看清楚 web 的历史和现实。

  • #9 楼 @mizuhashi 我没看错的话,你说的是 font-family 吧?一般的应用在字体上都是全局统一的,自然是直接写入 html 标签了。至于你说的隐形依赖全局,这不是理所当然的吗?难道每一个出现字体的地方你都要去覆盖一样的 font-family 吗?

    如果出现了某个组件一定要用特别的字体来显示,那可以把该字体的定义单独抽取变成一个对象,由于 Radium 是直接用 JS 写的,复用这个对象来引用该字体规则就好了。

    如果你所有的出现文字的地方都要这样单独定义字体,那肯定会大量冗余(内联样式都是独立的),可是你为什么要这样做呢?而且不要忘了,写样式还有一种东西叫做 class ,这是可复用的东东哦。虽说 Radium 非常便于写内联样式,但并不是说它不能写 class 样式,而且写内联的前提是 Critical Rendering Path 的样式才需要内联,一味的内联只能说你不懂 CSS。

  • #7 楼 @mizuhashi Medium 是 JS,引用和复用的手段远超 CSS,像你举的 fontFamily 很显然放全局。

  • #20 楼 @darkbaby123 You can set none prefix semver on global or local .npmrc, then every npm install will lock the exact version and no need to lock down versions manually...only if you really care about precise versions. Some new generation project indeed follow this practice like lux, they will review your pr very carefully in order to prevent trivial differences.

  • #5 楼 @1272729223 我就不多说了,看看这个东西的文档:https://github.com/pburtchaell/redux-promise-middleware/tree/master/docs ,特别是针对你提的问题,专门看一下:https://github.com/pburtchaell/redux-promise-middleware/blob/master/docs%2Fguides%2Fglobal-handling.md 。不过这一章的解决方案是基于该中间件自身的,所以要彻底理解其中的运作过程,还是通读一遍为好。

    基本的概念是:async action,这里以 promise 为例,总是会有特定数目的未来状态需要处理的。这些状态会涉及到 UI 状态的变化,也会涉及到 Data 状态的变化。最 raw 的方案就是若干个 actions 分别处理不同的未来状态然后连起来用。比如说:

    dispatch({type: '全局/加载', payload: true});  // action to handle loading notification
    API.fetch(url)
      .then(response => response.json())
      .then(payload => dispatch({type: '数据/入库', payload}))  // action to transfer data into corresponding reducer
      .then(() => dispatch({type: '全局/加载', payload: false}))  // action to turn loading state off
      .catch(error => dispatch({type: '全局/错误', payload: error});  // action to handle possible error(s)
    

    然而,这种“模式”是可以预期的(promise 总是这样的),所以你可以构造中间件(比如前面提到的那个)来抽象这件事情,于是你就可以统一来处理这种情形并且保证 UI 状态和 Data 状态是解耦的,而且它们各自也是很容易复用的了。


    顺便多插一句,针对前面说的多个 async actions 共用 loading 的场景,其实不用那么麻烦,完全可以:

    dispatch({type: '全局/加载', payload: true});
    
    Promise.all([
      API.fetch(url_1),
      API.fetch(url_2),
      ...
    ])
      .then(responses => responses.map(response => response.json()))
      .then([payload_1, payload_2, ...] => {
        // fire off corresponding actions
        // ...
      })
      .then(() => dispatch({type: '全局/加载', payload: false}))  // action to turn loading state off
      .catch(error => dispatch({type: '全局/错误', payload: error});  // action to handle possible error(s)
    

    这些只是 raw redux 的用法,善加利用一些简单的中间件,比如 redux-thunk / redux-promise 之类的,上述代码可以精简到非常可观的地步。要善于捕捉重复行为之中的共通模式,然后利用好 redux 的各种机制(主要是 middleware)


    不是一定要用 redux-promise-middleware 或是其他类似的中间件,不过这个文档里倒是把 async action 的许多细节都解释的很清楚,你可以视情况自己选择一些解决方案。

    我理解很多人做应用程序架构的时候都倾向于越少依赖越好,主要原因是增加抽象层次会带来学习和理解的负担。不过增加抽象层次的目的就是把解决一些复杂且琐碎的事情的过程封装起来并提供简明清楚的 API。所以如果你是负责架构的人,你就需要多多阅读周边的一些东西(在这里指的是 redux 周边),其实它们大多都是一些中间件,理解了这些中间件处理的过程,你也可以不依赖这些周边,或者是对它们胸有成竹随便用哪个也无所谓。

  • 你可以触发多个 actions 呀~

    FYI: https://github.com/reactjs/redux/issues/749

  • #10 楼 @darkbaby123 嗯,你这么一说我也明白了一些东西,因为我对 Bundler 不是很了解,所以我一直疑惑依赖包的依赖包如果冲突,你完全展平怎么解决?

    怪不得那么多人吐槽 npm 的 tree+flat 模式(不只是这里),让你这么一分析就更清楚了,多谢。

  • #8 楼 @mizuhashi 我已经没法完全弄明白你的逻辑了,可能是和我不够了解 bundle 有关吧。我只能说自有 npm3 以来,我也做过几个大规模的项目了,就好像上面那个 dump 的例子一样。第一,从未遇到过依赖冲突问题;第二,绝大部分是 flat 的,不能展平的依赖自然就是自身 shrinkwrap 的了,然而这并没有给我带来什么问题。

    因此你说你不可理解,但是现实中的痛点你又没办法 show 出来,而我又是天天接触 npm 的算是一个佐证……于是我也不知道说什么了,也许就是一个理论上的“不可理解”吧。

  • #6 楼 @mizuhashi 你声明了 shrinkwrap,对应的模块才需要树形依赖,这是因为整个依赖树里(可能)会有对同一模块的两个不同版本的依赖,这很好理解啊。

    其他没有强制依赖的模块还是 flat 安装的,谁说 shrinkwrap 会让你的整个工程全都变成树形依赖?

    你举的例子再正常不过了,大工程依赖的东西多自然安装的文件就会多,占的空间就会大,这有什么好奇怪的?

    我把我正在做的工程 dump 一下:

    $ ls node_modules | wc -l
    918
    $ du -h node_modules
    293M    node_modules
    

    包的数量没差多少,占据的空间有那么极端吗?

    最后,展平依赖树除了减少空间占用以外,还有一个原因是为了解决 windows 的深层嵌套目录结构的问题,这也是刚需,怎么就废掉了?

  • #2 楼 @steveltn 我的经验是这样的:你不必去 lock package.json,因为别人克隆下来重现你的环境的过程就是 npm install,那这个命令会依据 package.json 来安装所有的包。如果在你这里没有问题,那在别处也就没问题。(我不排除因为环境差异的问题,因为不是所有的包都有做很严谨的跨平台测试,也不是所有的包在不同平台下都表现一致)。当然你可以用 shrinkwrap 来声明对版本的严格依赖,npm 会根据 npm-shrinkwrap.json 来帮你处理多个版本的依赖,这个才是和 Gemfile.lock 类似的东西。

    Ruby 的 Gemfile 里面是不写版本号的,所以 Gemfile.lock 是一个刚需,必须得有。但是 Node 的 package.json 是自带版本号的,而且又不需要手工维护,如果依靠 npm 自身的命令就可以自动维护 package.json 里的版本声明,那么的确没有必要去 lock

    而 shrinkwrap 就相当于每一个单独的包自己锁定自己的版本依赖,如果它依赖某其他包的版本和另外的包所依赖的版本有冲突,npm 有一套机制来处理这个过程:https://docs.npmjs.com/cli/shrinkwrap

    @mizuhashi 强调 Gemfile.lock 是面向工程而不是面向包的,我不确定他指的什么层面?自己做的叫工程,依赖的别人做的东西叫包?我看这无非就是一个规模大小的问题,我自己做的东西本质还是一个包,只不过可能依赖特别多,规模特别大。我做好的工程如果我愿意,我也可以 publish 到 npm 变成一个别人可以 npm install 的包。当然,如果我这么做的话,我就应该 shrinkwrap 以确保人家用了我的东西的同时如果还依赖的别的东西,那么我的东西会有对版本依赖的强制性声明。但是,如果我做完就好了,只是自己用不用发布出去,那我有 package.json 就够了呀。

  • #3 楼 @mizuhashi 不用争论,也不在原理上费尽口舌。npm3 自发布以来我每天都在用,迄今为止没有遇见过“一定会遇到依赖冲突的问题”。你遇到了的话可以给我分享一下,说不定我有机会去给 npm 贡献一下。

  • 首先,没人会去手工编写 package.json,通常你只需要 npm install PACKAGE_NAME,npm 会自动安装注册表里该模块的最新版本,并自动更新 package.json。更新按默认规则会把版本写成 ^x.x.x 的 semver 表达式,当然你可以修改默认的前缀 ^,比如说换成 ~ 之类的。

    如果你要安装特定版本,那就 npm install PACKAGE_NAME@version 即可,如果你要升级就把 install 替换成 update。npm 会自动更新 package.json。其他的用户 clone 下来之后只需要 npm install 就会按照 package.json 里的规则安装好所有依赖包。所以一个 package.json 装出俩不同的环境这是不对的。

    如果某一个模块的依赖和另外的模块产生版本冲突,严格来说不是你(作为使用者的责任),而是该模块的作者没有做好这方面的工作。作为模块的作者,如果要特别限定依赖模块的版本,那就应该在发布前执行 npm shrinkwrap,这样会产生一个 npm-shrinkwrap.json 文件,其作用和 Gemfile.lock 是类似的。你安装的模块如果有做这件事情,那么 npm 会帮助你处理好冲突的问题。另外 npm outdated 会检查所有依赖包是否有新版本可用。

    作为目前世界上最大的软件包生态环境之一,这些问题 npm 不可能不考虑到,诚然它也走过不少弯路,可是喷它之前是不是也该至少读一遍使用说明书呀?


    补充一点:原生的 npm 只提供了包管理的最基本功能,有些时候的确也会觉得不够方便。不够 npm 自身也是 node 的一个模块,因此有不少人开发了针对它的增强包。对于管理更新/依赖这件事情,我推荐两个补充的东西:

    1. npm-check: 这个东西可以自动检查 package.json 然后告诉你哪些装了哪些没装,哪些装了但是源码里没用到,哪些可以升级,以及升级是 patch 还是 minor 或者是 major。此外它还提供了一个交互式的傻瓜自动升级命令,可以帮你列出所有待处理的模块,你自己勾选要处理哪些,然后一键搞定所有事情。

    2. GreenKeeper: 这个东西很有意思,它只能和 Github 集成(不排除未来支援更多平台,因为它还很新),其作用是自动帮你监视模块的更新情况,如果有更新,它就把你的 repo clone 下来,然后帮你升级好,接着 PR。你要做的就是 Merge Requests 就好了,全过程都可以直接在 Github 上完成,省心又省力。

      想看到效果直接:https://github.com/very-geek/univera,昨晚 greenkeeper 给提交了一个 PR 我还没处理,先留着给看效果。CI 的 failure 请直接无视,因为我还没 push tests……

  • Free for OSS only

  • 前后分离架构的探索之路 at 2016年03月22日

    #47 楼 @david_8 有事情说的话发邮件就是了。

  • #1 楼 @guxiaobai

    首先,我不觉得这俩是同一个 Model,我可以想出这种思路在很多场景下会引发的问题。我觉得你应该问问你自己:/contacts/admin/contacts 能确保始终是 Same Model 吗?

    在我看来,八成这两者会出现差异的,即使你现在觉得看不出,也不代表这样做就一定很安全。若是我的话,我会做一个 AdminContact Model,它可以继承自 Contact Model,这没有问题。

    客户端的 Model 和服务端的 Model 是不一样的,如果简单的说服务端的 Model 是对数据库的一种对象映射,那么客户端的 Model 就是对 API 的一种映射。如果一个 Model 可以同时适应 /contacts/admin/contacts,那我看不出要有两个 API endpoint 的必要。

    可是呢,如果你非要这么做那也不是没有办法,比如说你关心的 pathForType,实际上你已经看到了它是 DS.BuildURLMixin 里的一个成员属性,那么你只需要在扩展 ActiveModelAdapter 的时候用上这个 Mixin 就可以获得它了啊:

    export default ActiveModelAdapter.extend(BuildURLMixin, {
      namespace: 'api/v1',
    
      anyMethod() {
        console.log(this.pathType); // here is what you want
      }
    });
    

    可是我不觉得你在这条路上会找到解决问题的办法,我问你一个问题:谁负责在不同的路由去请求不同的 API endpoint?

    因为你只有一个 Model,你也只能用 store.findAll('contact') 这一个方法来获取所有 contacts(举例),在不同的地方调用的结果却是一样的,你如何区分哪儿是哪儿?

    即使我告诉你,store 最终是通过 adapter 里对应的方法去发出请求,OK,你决定去重写 DS.Adapter.findAll 方法,可是你还是无法判断一个 call 进来到底是要请求 /contacts 还是 /admin/contacts,因为 store.findAll('contact') 这个方法是没有变的。

    你可以做许多的 hackin 来绕过这个机制,比如说通过 naming resolver 来动态查找不同的 Adapter 之类的,但是这都不是标准的 Ember Data 的逻辑,你说不准走远了以后会遇到什么样的坑。

    所以还是我最早的结论,你这个设计有问题。如果有两个 endpoint,那么就应该有两个 client model。

    BTW,有一种非常规的方法,就是在 Model 里定义 Static Method,“人为”的发请求拿数据然后再 pushPayload——这是可行的,但是 payload 的后续控制需要你亲力亲为,如果你对 Ember Data 的各种机制不是很通的话,不建议这么玩儿。

  • #7 楼 @rubyless Thanks, nice tip.

  • #3 楼 @ugoa 哈哈,我朱时茂么?说正经的,我真没有背叛 Ember 呀,它依然是我心目中最爱 JavaScript 框架~但是同构+SSR 的确是很有好处的事情,即便是 Ember 也是可以做的,马上 EmberConf 开始了,这里面会有一个分布式 Ember 应用程序架构的 talk,我猜想里面会有一些非常有趣的事情。

  • #1 楼 @ericguo 哪里,这是应用场景需要才搞的,React 又不难。在我的眼里 React 和 Ember 是一样的,无非就是 Ember 不需要你选择,React 则可以随便选择。实际中想把 React 整合到和大 Ember 一样的程度还是很费力气的,当然整好了你会很有成就感,但这是架构师的活儿,只为开发应用还是咱大 Ember 来得省心……前提是用好了。

  • JavaScript ASI 机制详解 at 2016年03月08日

    #8 楼 @darkbaby123 嗯,知道了,就这样:

    comma-dangle: [2, "always-multiline"] comma-style: [1, "last", {exceptions: {VariableDeclaration: true} }]

    以上是我正在使用的规则,管用。

  • JavaScript ASI 机制详解 at 2016年03月08日

    #8 楼 @darkbaby123 呃,我已经很久没有写过多行 var 这样的代码了,感觉是 jQuery 时代才会这么写。实际上最近半年以来连 let 都很少用基本上需要声明一个命名都是 const,极少需要连续声明多个,如果确有必要都会试着利用函数的方式处理或者换换数据结构之类的。所以你说的逗号既要头又要尾的风格我还真没试过,刚好今天要搭个 universal redux 的架子,等会儿我试试。

  • JavaScript ASI 机制详解 at 2016年03月07日

    #3 楼 @billy 其实最后加逗号是很好的规则,习惯了之后就知道它的好处了,删除、添加、插入、上下移动都非常省心。这是很多开发者的强烈需求,ES2015 特意为此做了语法上的允许。

  • JavaScript ASI 机制详解 at 2016年03月07日

    最后的结论十分赞同,我经常在两种风格里换来换去,要么就是能不写分号的完全不写(这是绝对可行的),要么就是能写的全都写(但是可写可不写的都不写)。总的来说还是前者更符合我的口味,但是往往团队协作的时候都是后者为主,关键是 ESLint 药不能停……等你分享这个。

  • #1 楼 @shell ……这个和 Swift 没啥关系,JavaScript 做的 = =

  • 换外接显示器——或者你自己把笔记本垫起来……关键是要保持平视,于是身体也能挺直了。