使用Rails 6,您将“页面特定的” JavaScript代码放在哪里?

时间:2019-12-26 21:49:48

标签: ruby-on-rails ruby-on-rails-6 webpacker

我的问题与Using Rails 3.1, where do you put your "page specific" JavaScript code?相同,仅适用于Rails 6而不是Rails 3.1。

假设我有一些要用于帖子索引页面的JavaScript。该文件放在哪里,如何包含它?

在上一个问题中,答案通常使用Rails资产管道。但是,对于Rails 6,我的理解是,它使用webpacker代替了JavaScript文件的Asset Pipeline。

注意:我不希望文件始终包含在 中。例如。如果我在作者索引页面上,则不希望包含帖子索引页面的JavaScript文件。为什么?假设我的帖子索引页面的JS文件中有$('li').on('click', changeColor);。我可能不希望该代码在作者索引页面上运行,但是如果包含该文件,它将可以运行。您可以通过命名空间来解决此问题,但我认为不包含不必要的文件会更干净(并且性能更高)。

6 个答案:

答案 0 :(得分:2)

让我们在一个空的Rails 6应用程序中浏览此目录的内容。

▶ tree app/javascript
app/javascript
├── channels
│   ├── consumer.js
│   └── index.js
└── packs
    └── application.js

2 directories, 3 files

packs目录对我们很重要,所以让我们看看它包含的内容。

// app/javascript/application.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")

webpack具有入口点的概念,入口点是开始编译JavaScript代码时首先查找的文件。

Webpacker gem以app / javascript / packs下的application.js文件的形式创建应用程序包。如果您还记得资产管道,那么此文件等效于app / assets / javascripts / application.js文件

简单示例

  • 如何将Javascript添加到Ruby on Rails(将Javascript作为模块添加)
    1. 在Javascript路径app/javascript中创建文件夹,例如:“ post”
    2. 将您的javascript文件添加到index.js之类的文件夹中
    3. post-> app/javascript/application.js
    4. 中添加require('post')模块

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("post")

添加post模块后,让我们在Rails 6应用程序中浏览此目录的内容

▶ tree app/javascript
app/javascript
├── channels
│   ├── consumer.js
│   └── index.js
└── packs
|    └── application.js
|ـــ post
     |__ index.js

这种使用webpack的简单方法与以前的rails风格相同。

答案 1 :(得分:0)

我会尝试用咖啡脚本回答您的问题 因为它更容易理解 关键是使用事件“ turbolinks:load”检查内容 如果rails使用新方法加载帖子页面,则默认情况下该页面基本上将具有.posts.new

在您的app.post.coffee内部

class App.Post
  render: ->
    console.log "Post Edit / new render"
    console.log "all this script below will only run"
    console.log "if you open Post with new or edit method"

$(document).on "turbolinks:load", ->
  if $(".posts.new")[0] || $(".posts.edit")[0]  
    post = new App.Post
    post.render()

在您的app.author.coffee内部

class App.Author
  render: ->
    console.log "Author Edit / newrender"
    console.log "all this script below will only run"
    console.log "if you open Author with new or edit method"

$(document).on "turbolinks:load", ->
  if $(".authors.new")[0] || $(".authors.edit")[0]  
    author = new App.Author
    author.render()

如果我将咖啡脚本转换为js,则为post.js

App.Post = class Post {
  render() {
    console.log("Post Edit / new render");
    console.log("all this script below will only run");
    return console.log("if you open Post with new or edit method");
  }

};

$(document).on("turbolinks:load", function() {
  var post;
  if ($(".posts.new")[0] || $(".posts.edit")[0]) {
    post = new App.Post;
    return post.render();
  }
});

答案 2 :(得分:0)

我将按照提高使用Webpack和Webpacker的难度的难度来描述一些选项。

  1. 忘记页面特定的JavaScript,并将所有内容放入application.js捆绑包中。这绝对是最容易理解的方法。对于小型应用程序,折衷可能是值得的,因为拥有易于维护的应用程序可能会超过学习如何最好地利用Webpack最好地拆分JS代码而不增加性能的代价。

  2. 使用dynamic imports延迟加载特定于页面的代码。在这种情况下,您仍将所有JavaScript放在application.js依赖关系图中,但并非所有这些代码都将被预先加载。 Webpack会在编译JS时识别动态import()函数,并将导入的块拆分成一个单独的文件,该文件可以使用JS框架路由器或简单触发器按需在浏览器中加载。

    例如,您可能具有如下代码:

    if (document.querySelectorAll(".post-index").length) {
      import("./post/index") // webpack will load this JS async
    }
    
  3. 使用特定于页面的“包装”,并结合the splitChunks configuration API。在这种情况下,您无需为每个页面都构造一个页面,而不必使用application.js包,例如posts.jsadmin.js等。使用splitChunks插件意味着您的捆绑包可以正确共享代码。我强烈建议您谨慎使用此方法,直到您了解Webpack的工作方式或愿意学习选择此路径的Webpack的过程。 Webpack通常在假设您每个页面仅使用一个入口点的情况下效果最好,否则,除非您知道自己在做什么,否则最终可能会导致捆绑包中的代码重复。

答案 3 :(得分:0)

这是我通过Turbolinks提供页面特定代码的方式:

# javascript/packs/application.js
import {posts_show              } from '../per_page/posts_show';
import {users_new               } from '../per_page/users_new';
const pages = {posts_show, users_new};
document.addEventListener("turbolinks:load", () => {
    # I am using gon to save the page name, but you can add page name to document body and then const page = document.body.className
    const page = gon.controller_full
    # check if method exist if true execute
    if ('function' === typeof pages[page]){
      new pages[page]
    }
});

您已经注意到,我正在将所有js编译到一个文件中,并根据需要执行它们。

以下是post_show的示例:

# javascript/per_page/posts_show.js
import {default as share_menu_init} from './methods/share_menu_init.js'
export class posts_show {
    constructor() {
        share_menu_init()
        ... # some other code here
    }
}

您可能会看到我正在导入share_menu_init模块。如果我需要在页面之间共享某些方法,则将它们存储在模块中,并且Webpack非常聪明,不会创建重复项(如果我两次将其导入posts_show和其他页面中),它将仅组织文件,因此我的{{1} }可以使用此方法。

posts_show

不确定是提供网页特定代码的最佳方法,如果有更好的方法,我想听听。

答案 4 :(得分:0)

您可以使用$.getScript

https://coderwall.com/p/xubjcq/handling-large-javascript-files-with-turbolinks

(function(){
  var requiredScripts = [];

  window.requireOnce = function (path, cb) {
    if ($.inArray(path, requiredScripts) == -1) {
      requiredScripts.push(path)
      $.getScript(path, cb)
    } else {
      cb()
    }
  }
})();

像这样使用它:

requireOnce('wysiwyg.js', function(){
  $('textarea').wysiwyg()
});

答案 5 :(得分:0)

您应该这样做的方法是创建一个文件夹结构以匹配您的视图:

app/javascript
├── channels
│   ├── consumer.js
│   └── index.js
└── packs
     └── application.js
     └── post
         └── index.js

然后在您想要包含的视图 index.js 中:

<%= javascript_pack_tag('post/index') %>

添加后,在您的 application.rb 中添加:

config.assets.precompile += ['listings/index.js']

这样你只在你需要的特定视图中加载它,你可以在创建视图特定的 JS 时继续附加更多资产。

注意事项:

  • 一个常见的论点是,您可能不小心推送了预编译列表中缺少资产的生产代码,因此您应该只加载所有 JS 但这只是懒惰,如果缺少资产,您甚至无法加载页面,如果您推送的代码缺少您未测试任何内容的资产。

参考文献:

  1. https://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline
相关问题