Webpacker - Cứu cánh trong việc sử dụng Webpack với Rails
Bài đăng này đã không được cập nhật trong 7 năm
Vài năm trước thì Rails là 1 Web framework có thể đáp ứng đủ hoàn toàn nhu cầu của các web developer trong việc phát triển, nhưng mà với sự thay đổi, tiến bộ vượt bậc của JavaScript trong các năm gần đây, dẫn đến việc sử dụng các Javascript framework (AngularJS, ReactJS, Vue, ...) ngày càng nhiều hơn. Điều này làm anh em developer khá là khó khăn trong việc tích hợp, set up môi trường phát triển bởi vì Rails thiếu 1 công cụ tử tế để bundling Javascript. Nhưng điều này sắp tới sẽ thay đổi trong Rails 5.1 - sắp được phát hành trong năm nay, với Gem Webpacker.
Và trong bài post này thì mình xin giới thiệu các tính năng mà nó hỗ trợ trong alpha version, và tại sao mà Gem này sẽ là cứu cánh trong việc tích hợp React, Vue, Angular trong Rails.
Yêu cầu
- Ruby 2.2.6 +
- Rails 5+
- Node [https://github.com/riywo/ndenv]
- Yarn [https://yarnpkg.com/lang/en/docs/install/]
- Webpack
- Javavascript
- Git
- Terminal :v
Cài đặt
Trước khi bắt đầu thì mọi người phải chắc chắn là ruby, node, và yarn đã được cài đặt.
- Dùng terminal để tạo thư mục mới
mkdir webpacker-example-app
cd webpacker-example-app
touch Gemfile
- Chỉnh sửa Gemfile với nội dung sau đây:
source 'https://rubygems.org'
ruby '2.2.6'
gem 'rails', '~> 5.1.x'
gem 'sprockets', '~> 4.x'
- Sau khi đã có Gemfile thì chúng ta cần chạy câu lệnh bundle để cài đặt các thư viện phụ. Từ thư mục webpacker-example-app thì chúng ta chạy 1 trong các câu lệnh duới đây để cài đặt Webpack:
bundle exec rails new . --webpack --force
// hoặc là những những câu dưới đây để install webpack với react, angular, vue
bundle exec rails new . --webpack=react --force
bundle exec rails new . --webpack=angular --force
bundle exec rails new . --webpack=vue --force
Với tiền tố mới là --webpack, thì webpack sẽ được cài đặt với Yarn sau khi khởi tạo 1 Rails 5.1 app. Sau khi mà Gem và node_modules được tạo thành công thì chúng đã sẵn sàng để sử dụng webpack với Rails app mới.
Webpacker cũng hỗ trợ Rails 4.2++ apps trở lên bằng cách thêm gem 'webpacker' vào Gemfile và chạy câu lệnh như sau:
rails webpacker:install
rails webpacker:install:[react, angular or vue]
Mọi chi tiết về gem Webpacker mọi người có thể vào xem tại đây : https://github.com/rails/webpacker
Cấu hình
1. Cấu trúc thư mục
Nhìn vào thư mục vừa mới được khởi tạo là app/javascript. Đây là thư mục chưa toàn các JavaScript của app và webpack entry point. Mặc định sẽ là app/javascript/pack.
Và điều tiện lợi ở đây là mọi webpack entry point sẽ được đặt ở trong thư mục app/javacsript/packs và các modules có thể đặt ở thư mục app/javascript 1 cách tự do. Giả sử, chúng ta xây dựng 1 cái app xem lịch chẳng hạn, thì cấu trúc thư mục có thể được đặt như này.
// entry: app/javascript/packs/calendar.js
require('calendar') or import 'calendar'; // in es6 world
// 1. Angular app
// Main index file that loads all dependent code and bootstraps module to DOM if required
require('./components/calendar.js') or import './components/calendar.js';
require('./models/calendar.js') or import './models/calendar.js';
// app/javascript/calendar/index.js
// Modular js much like rails app folder
// app/javascript/calendar/components/calendar.js
// app/javascript/calendar/models/month.js
// app/javascript/calendar/templates/calendar.js
// app/javascript/calendar/services/calendar.js
// app/javascript/calendar/routes/calendar.js
// 1. React app
// Main index file that loads all dependent code and bootstraps module to DOM if required
require('./components/calendar.js') or import './components/calendar.js';
require('./routes/calendar.js') or import './routes/calendar.js';
// app/javascript/calendar/index.js
// Modular js much like rails app folder
// app/javascript/calendar/components/calendar.jsx
// app/javascript/calendar/routes/calendar.jsx
// app/javascript/calendar/data/calendar.js
2. Cấu hình Webpack
Generators của webpacker sẽ tự động thêm file cấu hình của web cho từng môi trường ở trong thư mục config/webpack. Những file cấu hình này hỗ trợ rất nhiều các tính năng của webpack cho từng môi trường như:
- code-splitting
- assets-fingerprinting
- linking static assets like image and styleshhets
Tất cả các options đều được config ở file config/webpack/paths.yml
3. Xây dưng 1 app demo
1. Setup
Đầu tiên, thêm gem foreman vào Gemfile để quản đa tiến trình trong mồi trường phát triển. Sau đó tạo 2 file Procfile, Procfile.dev ở thư mục gốc.
Procfile
web: bundle exec puma -p $PORT
Procfile.dev
web: bundle exec rails s
# watcher: ./bin/webpack-watcher
hot: ./bin/webpack-dev-server
Tiếp theo tạo 1 file binstub để chạy Procfile.dev, ở thư mục bin/ tạo 1 file tên là server và paste những command sau:
#!/bin/bash -i
bundle install
bundle exec foreman start -f Procfile.dev
Để có thể chạy file này thì có thể chúng ta sẽ cần phải cấp quyền cho nó. Chạy chmod 777 bin/server trong trường hợp này. Sau đó chúng ta chỉ cần chạy ./bin/server và Procfile.dev sẽ được thực thi, webpack và puma cũng thế .
2. Code
Chúng ta sẽ tạo 1 module counter để làm ví dụ. Đầu tiên chúng sẽ tạo folder counter trong app/javascipt dùng để chưa code js cho module này. Tiếp đó chúng ta sẽ thêm 2 file: index.js và counter.js với code sau:
counter.js
// A simple counter example
// The setup will be more complicated in modern apps built using React
const incrementNode = document.getElementById('increment');
const decrementNode = document.getElementById('decrement');
const inputNode = document.getElementById('counter');
const counter = {
initialize() {
incrementNode.addEventListener('click', (event) => {
event.preventDefault();
const currentValue = inputNode.value;
inputNode.value = parseInt(currentValue, 0) + 1;
});
decrementNode.addEventListener('click', (event) => {
event.preventDefault();
const currentValue = inputNode.value;
if (currentValue > 0) {
inputNode.value = parseInt(currentValue, 0) - 1;
}
});
}
};
export default counter;
index.js
// Initialize the counter code when DOM is ready
import counter from './counter';
document.addEventListener('DOMContentLoaded', () => {
counter.initialize();
});
Và ở trong thư mục app/javascript/packs chúng ta cần tạo 1 entry point cho webpack, tạo 1 file tên là counter.js trong đấy và fill vào code sau:
// Require or import the counter module
import '../counter';
3.View
Cuối cùng, chúng ta sẽ thêm counter module này vào phần view của chúng ta. Tạo 1 controller :
bundle exec rails g controller pages index
routes.rb
root 'pages#index'
và cuối cùng ở file pages/index.html.erb:
<div class=”counter-wrapper”>
<h1>Counter</h1>
<form class=’counter’>
<button id=’increment’>Increment</button>
<input type=”number” name=”counter” id=’counter’ value=’0' />
<button id=’decrement’>Decrement</button>
</form>
</div>
<%= javascript_pack_tag 'counter' %>
Ở đây, javascript_pack_tag
là 1 helper mới trong rails sẽ, với nhiệm vụ là kéo kéo code javascript đã complied và references nó trong app:
<script src=”http://localhost:8080/counter.js"></script>
4. Chạy chương trình
Mở terminal ra và chạy lệnh:
./bin/server
# or
bundle exec foreman start -f Procfile.dev
Giờ app sẽ được chạy ở http://localhost:3000
5. Styling
Giờ chúng ta sẽ thêm 1 chút style cho counter module bằng Sass cho màu mè . Tạo 1 file style.sass ở app/javascript/counter/style.sass:
$grey: #f2f2f2
.counter-wrapper
max-width: 500px
margin: 100px auto
padding: 10px
border: 1px solid $grey
form
input
display: block
margin-bottom: 10px
button
display: inline-block
và cuối cùng thêm nó vào counter/index.js và view:
// Initialize the counter code when DOM is ready
import './style.sass';
import counter from './counter';
document.addEventListener('DOMContentLoaded', () => {
counter.initialize();
});
view raw
<%= stylesheet_pack_tag ‘counter’ %>
6. Deployment
Mặc định thì heroku sẽ cài đặt yarn và node nếu như chúng ta deploy app với Webpack (nhớ replace sqlite gem với pg gem ở Gemfile).
heroku create
heroku addons:create heroku-postgresql:hobby-dev
git push heroku master
Giờ thì mình xin kết thúc bài viết ở đây, bài viết tuy sơ sài nhưng mong là mọi người sẽ nắm bắt được sơ bộ về Gem Webpacker này . Link tham khảo: https://medium.com/statuscode/introducing-webpacker-7136d66cddfb
All rights reserved