JQuery 切换到 Webpacker 后内联脚本中不存在

JQuery not present in inline scripts after switching to Webpacker

我正在将 Rails 上的 Ruby 应用程序从 sprockets 切换到 webpacker。我在现有的 slim 模板中收到内联 javascript 错误,JQuery 未定义。我尝试在 application.html.slim 中添加对 jquery 的要求,但这没有帮助。

application.html.slim

doctype html
html
  head
    title My Application
    meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"
    meta name="have-i-been-pwned-verification" value="..." /

    meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"
    = include_gon(watch: true)
    = stylesheet_link_tag 'application', media: 'all'
    = javascript_pack_tag 'application'
    = javascript_include_tag 'jquery'
    = csrf_meta_tags
body
  = render 'layouts/navigation/main_nav', disable_search: true
  = flash_messages
  main.application
    = yield

错误

Uncaught ReferenceError: $ is not defined

_comments.slim

.comments-wrapper.collapse.show.comments-collapse#comments
  .row.comments
    .col-sm-12
      .row.comment-post
        .col-sm-12.parent-comment-form
          - if user_signed_in?
            = render 'we_vote/comments/form', commentable: commentable
      - unless local_assigns[:limit_comments].present?
        .row.filter-row
          .col-sm-12.col-lg-2.offset-md-1.offset-sm-0.text-right
            .sort-label sort by
          .col-lg-4.col-sm-12.mb-2
            select.sort-select
              option Popular
              option Reply Number
              option Upvote Number
              option Newest
          .col-lg-4.offset-lg-1.col-sm-12.offset-md-0
            select.sort-select
              option Today
              option Last Week
              option This Week
      .row
        .col-sm-12.comments-container class="#{'empty' unless commentable.comments.any?}"
          ul.comments-list
            - if local_assigns[:limit_comments]
              - commentable.comments.top_level.by_popularity.limit(limit_comments).each do |comment|
                = render 'we_vote/comments/comment', comment: comment, commentable: commentable, limited: true
            - else
              - commentable.comments.top_level.by_popularity.each do |comment|
                = render 'we_vote/comments/comment', comment: comment, commentable: commentable

= render 'we_vote/comments/report_comment_modal'

javascript:

  $(function(){
    var commentId = "#{ params[:comment_id] }";
    if(commentId) {
      var $comment = $('#' + commentId);
      $comment.get(0).scrollIntoView();
      $comment.addClass('notified');
    }
  });

environment.js

const { environment } = require("@rails/webpacker");
const erb = require("./loaders/erb");
const jquery = require("./plugins/jquery");
const webpack = require("webpack");
environment.plugins.append(
  "Provide",
  new webpack.ProvidePlugin({
    "window.Tether": "tether",
    Popper: ["popper.js", "default"],
  })
);

const aliasConfig = {
  jquery: "jquery-ui-dist/external/jquery/jquery.js",
  "jquery-ui": "jquery-ui-dist/jquery-ui.js",
};

environment.config.set("resolve.alias", aliasConfig);

environment.plugins.prepend("jquery", jquery);
environment.loaders.prepend("erb", erb);
module.exports = environment;

config/webpack/plugins/jquery.js

const webpack = require("webpack");

module.exports = new webpack.ProvidePlugin({
  $: "jquery",
  jQuery: "jquery",
  "window.jQuery": "jquery",
});

application.js

import $ from "jquery";
import autosize from "autosize";
global.$ = $;
global.jQuery = $;
import tether from "tether";
global.Tether = tether;

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

require("../src/google_analytics");
require("jquery");
require("jquery-ui");
require("../src/autocomplete-rails");
require("tether");
require("popper.js/dist/umd/popper");
require("bootstrap/dist/js/bootstrap");
require("bootstrap-notify/bootstrap-notify");
require("jquery-mask-plugin");
require("select2/dist/js/select2.full.min");
require("jquery-textcomplete");
require("jquery-match-height");
require("jquery-jscroll");
require("../src/facebook");

require("../src/components/comment");
require("../src/components/discussion");
require("../src/components/follow");
require("../src/components/hashtaggable");
require("../src/components/news_feed");
require("../src/components/preview-img");
require("../src/components/question");
require("../src/components/remote_buttons");
require("../src/components/report");
require("../src/components/search");
require("../src/components/sidebar");
require("../src/components/upvote");
require("../src/components/vote");
require("../src/components/verification");

jQuery.railsAutocomplete.options.delay = 300;
jQuery.railsAutocomplete.options.autoFocus = true;

问题是您的内联脚本在 jquery 之前执行。这样做的原因是因为它内嵌在 html 中,因此可以立即使用。另一方面,jquery 不是内联的,因此浏览器必须在执行之前发出请求。

有几个选项。

选项:等待jQuery加载

一个选项是将所有内联代码包装在等待 jQuery 加载的函数中。

javascript:
  document.addEventListener('DOMContentLoaded', function () {
    var commentId = "#{ params[:comment_id] }";
    if(commentId) {
      var comment = document.getElementById(commentId);
      commment.classList.add('notified');
    }
  }, false);

选项:存根 jQuery

另一种选择是创建存根(假)$ 函数,存储对 $ 的所有调用,等待 jQuery 加载并重播这些调用。 More details here

在您的 JavaScript 打包文件(例如 app/javascript/packs/application.js)中,执行以下操作:

import jQuery from 'jquery';

global.$ = global.jQuery = jQuery;