JavaScript Coffee script & Javascript 结构与组织

mafai · 2012年11月28日 · 最后由 JeremyLi 回复于 2014年04月07日 · 8982 次阅读

Title: Coffee script & Javascript organize Tags: coffeescript,javascript Slug: coffeescript Excerpt: rails coffee script Date: 2012-09-25

CofeeScript & JavaScript Organize

我需要解决以下问题,相信其它朋友也会碰到,这是我翻查的结果,结论已经在我的项目用了,感觉不错,分享一下。原博文在 http://scriptogr.am/mafai/post/coffeescript

Target

  • 所有 js compile 在一个 js,不论是否需要
  • 不同页面需要运行的 js 才运行,不需要的不运行

Solution 1

init 不同 namespace 的 js

application.html.erb 这段不能用 coffee script,原因是动态 call init 的方法 可以再细分按 action name 或用正则表达式

:javascript $(function() { #{"Window.#{params[:controller]}"}.init(); });

转换成的代码,当在访问 /apples

Window.apples.init()

对应的 apples.js.coffeelike只是为了 demo,可以用 ujs 代替

Window.apples = like: (id) -> console.log("like apple:" + id) return init: () -> console.log("init apple") $(".apple").click () -> console.log("click me") console.log( $(this).attr("id") ) Window.apples.like($(this).attr("id")) return return

oranges.js.coffee

oranges = init: () -> console.log("init orange") return eat: (id) -> console.log("eat orange:" + id) return

当访问 /apples 时,只会 init apples 的 js,如果是/oranges,则只会 init orange js

Solution 2

方案 1 的变形,不过组织起来更舒服一占 o,与方法 1 差不多

common.js.coffeeapples, oranges 等作为 namespace

Window.FruitsSite = common: init: () -> console.log("init common") apples: init: ()-> console.log("init apples") like: (id) -> console.log("like apple id: " + id) oranges: init: () -> console.log("init oranges") eat: (id) -> console.log("eat orange id: " + id)

application.haml 动态按 controller 名去调用

Window.FruitsSite["#{params[:controller]}"].init();

Solution 3

方案 2 的变形,将需要用到的 js 类放到 body 中,同时细分到不同 action 页面级别

<body data-controller="apples" data-action="index""> 找出对应 apples 的 index 页面需要 init 的东西 init 一下

<body data-js="apples autoscroll"> Iint apples 和 autoscroll

查找一下data-js,然后找出对应的模块init一下就好

例子

apples.js.coffee

Window.FruitsSite = apples: index: init: () -> console.log("init apples index page") return

applicatoin.haml 其实就是多一个层级而已,另外就是可以将这下面这段代码转成 coffee script,不用像上面两个例子写一些恶心 js 在页面上

var $body = $("body"); var controller = $body.attr("data-controller"); var action = $body.attr("data-action"); Window.FruitsSite[controller][action].init();

Solution 4

重写 jquery ready 方法,利用 body class 调用对应的 ready

重写 jquery ready 方法,看不明 proxy 的那个方法,不过没关系 这段代码来自于 https://github.com/Verba/jquery-readyselector/blob/master/jquery.readyselector.js

(function ($) { console.log("try to reset the ready function"); var ready = $.fn.ready; $.fn.ready = function (fn) { console.log(this); console.log("Context:" + this.context); console.log(this.context); if (this.context === undefined) { console.log("no context defined"); // The $().ready(fn) case. ready(fn); } else if (this.selector) { console.log("Selector:" + this.selector); ready($.proxy(function(){ $(this.selector, this.context).each(fn); }, this)); } else { console.log("Context have but no selector"); ready($.proxy(function(){ $(this).each(fn); }, this)); } } })(jQuery);

写不同的 ready 函数

$('.apples.index').ready(function () { console.log("apple index page init"); }); $('.oranges.index').ready(function(){ console.log("oranges index page init"); });

ready 这个方法就可有可无了

$(function() { });

body 中的 class 一定要以空间分隔

%body{:class => "#{params[:controller]} #{params[:action]}"}

其它的方法还有

用条件语句去控制,只是不那么高明

$(body).hasClass("autoscroll")

只当需要时才 new instance
Window.FruitsSite.apples = new Apples;

只能先实例化

创建实例

class Apples index: init: () -> console.log("init apples index page") return console.log(window.FruitsSite) window.FruitsSite.apples = new Apples

简版

window.FruitsSite.apples = index: init: () -> console.log("init apples index page") return console.log(window.FruitsSite)

CoffeeScript 整合 vim

node.js 下载一下 pkg(Mac) 装完后

sudo npm install -g coffee-script

mkdir -p ~/.vim/autoload ~/.vim/bundle curl 'www.vim.org/scripts/download_script.php?src_id=16224' > ~/.vim/autoload/pathogen.vim

修改一下 ~/.vimrc

filetype plugin indent on call pathogen#infect()

安装 vim plugin

cd ~/.vim/bundle git clone https://github.com/kchmck/vim-coffee-script.git cd ~/.vim/bundle/vim-coffee-script git pull

人肉安装(可选方法)

unzip -od ~/.vim vim-coffee-script-HASH.zip

用 vim 打开一个 coffee script 文件

命令行,就可以将 coffee script 转成 js 分屏显示,爽啊

:CoffeeCompile

Reference

How-to: Organizing JavaScript in Ruby on Rails

What is the best way to organize unobtrusive JavaScript across multiple pages?

Maintainable front-end code in Ruby on Rails applications

A Simple Pattern to Namespace and Selectively Execute Certain Bits of JavaScript Depending on Which Rails Controller and Action are Active

Extending Paul Irish’s comprehensive DOM-ready execution

Rails 3.1 javascript execution

很有启发,谢谢

分析得很清晰。

总结得很好,我一般用 Solution 3

很不错,扩展了思维

哪有 Solution 3 的简单实例啊?求助。

#5 楼 @doitian 能不能给我指点一下呢?

#8 楼 @lentg 就是通过 Rails 在 view 里控制 js 的执行路径啊。 敲了个简单的例子,可以在 controller 里进行控制 https://gist.github.com/4200680(直接网页敲的,没测试,意思到了就好)

#9 楼 @doitian 简单看了一下,还不明白,去敲敲,谢谢咯!

#9 楼 @doitian 这个问题解决了,谢谢你。现在我还有一个问题请教你,就是用 capistrano 来部署成功后,修改了代码"git push"后,运行"cap deploy"成功后,更改生效,可是在项目根目录下的 public/upload 目录里的文件为什么每次都清空删除的?该怎么做才不会删除上传的文件?请求帮忙一下。

#11 楼 @lentg Override sym_link 那个方法,具体名字不记得了,然后 link 去 shared/中的 upload 文件目录。

好的,谢谢,有个方向就容易了。3Q

#13 楼 @lentg 最简单就是用 public/system , cap 默认就 link 了。

#14 楼 @doitian 嗯,谢谢,现在解决了,得多谢你了。不过现在还是问题多多啊。比如:http://106.187.101.190/u/images/s/ZQ%20106/1.jpg http://106.187.101.190/u/images/m/ZQ%20106/1.jpg http://106.187.101.190/u/images/l/ZQ%20106/1.jpg 上面这三个链接怎么只能打开两个,第三个链接打不开?

#14 楼 @doitian

  desc "Symlink the data directory" # 数据存储目录
  task :symlink_upload, :except => {:no_release => true} do
    run "mkdir -p #{shared_path}/u/book"
    run "mkdir -p #{shared_path}/u/video"
    run "mkdir -p #{shared_path}/u/tmp"
    %w[l m s].each { |v| run "mkdir -p #{shared_path}/u/images/#{v}" }
    %w[l m s].each { |v| run "mkdir -p #{shared_path}/u/thumb/#{v}" }

    run "ln -nfs #{shared_path}/u #{release_path}/public/u"
  end
  after 'deploy:update_code', 'deploy:symlink_upload'

当受了

赞一个 我也在用 solution 3 和 css 差不多是一样的方式,很简单,很实用。

我按照 solution 3 进行了测试,当在 apples.js.coffee 里定义 Window.FruitsSite = apples: index: init: () -> console.log("init apples index page") return

然后再 oranges.js.coffee 里定义 Window.FruitsSite = oranges: index: init: () -> console.log("init oranges index page") return 之后调用的时候,oranges 里 Window.FruitsSite 的值会覆盖 apples 里的值啊,导致调用 apples 的 index 函数时没反应啊,该如何解决啊?

#19 楼 @JeremyLi 解决了,只要在相应的 apples.js.coffee 里定义的函数加前缀 window.就可以声明全局变量,再在另外一个文件 init.js.coffee 里定义 window.FruitsSite,调用 apples.js.coffee 里的函数时加上 window.前缀就可以。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号