iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0
Modern Web

Rails,我要進來囉系列 第 17

第十七天:在 Rails 7 用 jsbundling + webpack 安裝並使用 bootstrap

  • 分享至 

  • xImage
  •  

開場白

鼬~~哩賀,我是寫程式的山姆老弟,昨天跟大家一起實驗了用 importmap 來安裝 bootstrap,今天來繼續延續 Rails 7 的研究,要試試看用 jsbundling-rails 這個 gem 來處理 JS,這個 gem 可以讓我們選擇四種處理 JS 的方法,第一個是預設的 importmap,第二個是最近很紅的 esbuild,第三個是 rollup,第四個是 webpack,我們今天要實驗的是選擇 webpack 來試試看,看用起來感覺怎麼樣、跟 Rails 6 使用 webpacker 的感覺有沒有差別,夠夠~

創一個實驗用的新專案

$ rails new rails7_webpack -j webpack

在 Rails 7 中,如果沒有帶 -j,預設就是使用 importmap

研究一下專案結構

  1. package.json: 使用 webpacker 打包時期,熟悉的 package.json 出現了!

  2. Procfile.dev: 紀錄啟動 Rails 和 webpack 的啟動程序

    web: bin/rails server -p 3000
    js: yarn build --watch
    
  3. webpack.config.js: 之前 webpacker 時期是放在 config/webpack/xxx.js,現在被搬出來外面了

  4. Gemfile: 跟昨天的 importmap 實驗相比,這邊就改安裝 jsbundling-rails gem,其他的 gem 就跟昨天一樣

    ...
    
    # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
    gem "rails", "~> 7.0.4"
    
    # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
    gem "sprockets-rails"
    
    # Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]
    gem "jsbundling-rails"
    
    # Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
    gem "turbo-rails"
    
    # Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
    gem "stimulus-rails"
    
    # Use Sass to process CSS
    # gem "sassc-rails"
    
    ...
    

創一個實驗場域

  1. $ rails g controller home index

  2. 把新的頁面設為首頁 config/routes.rb

    Rails.application.routes.draw do
      root 'home#index'
    end
    

做個簡單的實驗

  1. 檢查 app/views/layouts/application.html.erb 引用 js 的方法

    <!DOCTYPE html>
    <html>
      <head>
        <title>Rails7Webpack</title>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <%= csrf_meta_tags %>
        <%= csp_meta_tag %>
    
        <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
        <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
      </head>
    
      <body>
        <%= yield %>
      </body>
    </html>
    

    我一開始還以為是錯的,我一度把 javascript_include_tag 改回 javascript_pack_tag,一改就報錯,才發現原來 javascript_pack_tagwebpacker 的 helper method,所以這邊預設的 javascript_include_tag 是對的,不需要特別去動它

  2. app/views/home/index.html.erb 新增一個極簡按鈕,並給他一個 id

    <!-- app/views/home/index.html.erb -->
    <h1>Home#index</h1>
    <p>Find me in app/views/home/index.html.erb</p>
    
    <%= button_tag 'Click Me', id: 'btn-click' %>
    

    有了上次 webpacker 實驗的教訓之後,這次已經知道不能直接在 view 裡面呼叫 js 的 function 了,所以我們就給它一個 id,後面再由 js 來操作

  3. 新增 app/javascript/my.js,針對 btn 新增一個 click 的 listener

    // app/javascript/my.js
    console.log('my.js')
    
    function clicked() {
      alert('Clicked')
    }
    
    var btn = document.getElementById('btn-click')
    btn.addEventListener("click", function() {
      clicked()
    });
    
  4. app/javascript/application.js 引用 my.js

    // app/javascript/application.js
    import './my.js'
    
  5. 啟動 $ ./bin/dev,並打開 127.0.0.1:3000,確認一下按鈕有沒有作用(./bin/dev 這東東是由 foreman 這個 gem 幫忙生成的,在創立專案的時候就自動安裝了,而且是沒有記錄在 Gemfile.lock 的)

    因為要用 webpack 打包 js,所以如果只有啟動 $ rails s,那前端的 js 並不會被 webpack 打包到了

    之前使用 webpacker 的時候,只需要使用 $ rails s 就可以了,這就是整合的威力 XD

  6. 成功~

    https://raw.githubusercontent.com/shrimp509/my-img-host/master/relacs-studio/Rails%E6%88%91%E8%A6%81%E9%80%B2%E4%BE%86%E5%9B%89/day17-1.png

使用 webpack 安裝並使用 bootstrap

經過了昨天使用 importmap 安裝 bootstrap 的慘痛經驗,今天再次體驗看看用 webpack 來使用 bootstrap

  1. Gemfile 安裝 bootstrapsassc-rails gem,$ bundle install

    # Gemfile
    ...
    
    # Use Sass to process CSS
    gem "sassc-rails"
    
    # Use bootstrap
    gem 'bootstrap'
    
  2. 重新命名 app/assets/stylesheets/application.cssapp/assets/stylesheets/application.scss,並引用 bootstrap css

    @import 'bootstrap';
    
  3. 安裝 bootstrap js 部分,$ yarn add bootstrap @popperjs/core

  4. app/javascript/application.js 引用 bootstrap 和 popperjs

    // app/javascript/application.js
    ...
    
    import './my.js'
    import '@popperjs/core'
    import 'bootstrap'
    
  5. app/views/home/index.html.erb 使用 bootstrap 的 navbar, popover, tooltip, dropdown btn, modal

    <nav class="navbar navbar-expand-lg navbar-dark bg-dark" aria-label="Ninth navbar example">
      <div class="container-xl">
        <a class="navbar-brand" href="#">Container XL</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarsExample07XL" aria-controls="navbarsExample07XL" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
    
        <div class="collapse navbar-collapse" id="navbarsExample07XL">
          <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
              <a class="nav-link active" aria-current="page" href="#">Home</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Link</a>
            </li>
            <li class="nav-item">
              <a class="nav-link disabled">Disabled</a>
            </li>
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
              <ul class="dropdown-menu">
                <li><a class="dropdown-item" href="#">Action</a></li>
                <li><a class="dropdown-item" href="#">Another action</a></li>
                <li><a class="dropdown-item" href="#">Something else here</a></li>
              </ul>
            </li>
          </ul>
          <form role="search">
            <input class="form-control" type="search" placeholder="Search" aria-label="Search">
          </form>
        </div>
      </div>
    </nav>
    
    <h1>Home#index</h1>
    <p>Find me in app/views/home/index.html.erb</p>
    
    <%= content_tag(:p, 0, id: 'count') %>
    <%= button_tag 'Click Me', class: 'btn btn-primary', id: 'btn-click' %>
    
    <div class="dropdown">
      <button class="btn btn-secondary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
        Dropdown button
      </button>
      <ul class="dropdown-menu">
        <li><h6 class="dropdown-header">Dropdown header</h6></li>
        <li><a class="dropdown-item" href="#">Action</a></li>
        <li><a class="dropdown-item" href="#">Another action</a></li>
        <li><a class="dropdown-item" href="#">Something else here</a></li>
        <li><hr class="dropdown-divider"></li>
        <li><a class="dropdown-item" href="#">Separated link</a></li>
      </ul>
    </div>
    <!-- Button trigger modal -->
    <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
      Launch demo modal
    </button>
    
    <!-- Modal -->
    <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <div class="modal-body">
            ...
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary">Save changes</button>
          </div>
        </div>
      </div>
    </div>
    
    <button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top">
      Tooltip on top
    </button>
    
    <!-- popover -->
    <button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus." aria-describedby="popover307994">
      Popover on top
    </button>
    
  6. 啟動 $ ./bin/dev,會發現只有 dropdown 和 modal 是正常的,tooltip 和 popver 不能正常運作

  7. 改一下 app/javascript/application.js,從 import 'bootstrap' 變成 import * as bootstrap from 'bootstrap',再從 bootstrap 的 Enable TooltipsEnable Popover,複製來貼上,變成以下這樣

    import './my.js'
    import '@popperjs/core'
    import * as bootstrap from 'bootstrap'
    
    const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
    const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))
    const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
    const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
    
  8. 再次啟動 $ ./bin/dev,並打開 127.0.0.1:3000,驗證一下元件是不是都可以運作

  9. 成功~

    https://raw.githubusercontent.com/shrimp509/my-img-host/master/relacs-studio/Rails%E6%88%91%E8%A6%81%E9%80%B2%E4%BE%86%E5%9B%89/day17-2.png

總結

結果看樣子,應該是我昨天錯怪 importmap 了…,是我少加了 enable tooltips 和 popover 的程式碼了,我有把今天 enable tooltip 和 popover 的方法貼過去 importmap 專案,確定是可以正常運作的,抱歉了 importmap QQ

不過這兩天用起來,體感上使用 webpack 還是比 importmap 好,雖然 webpack 用起來還是有些延遲,有時候我改完 js 並存檔之後,我還在等 webpack 打包,打包完我才重新整理頁面,有時候我乾脆不等,就直接重開 $ ./bin/dev,聽說 esbuild 會比 webpack 更快,我期待明天的實驗,就來玩玩看 esbuild,我們明天見~


上一篇
第十六天:初探 Rails7 預設的 importmap,結果慘不忍睹…?!
下一篇
第十八天:在 Rails 7 用 jsbundling + esbuild 安裝並使用 bootstrap
系列文
Rails,我要進來囉30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言