VueCli::Rails
Let's make cool boy Vue even cooler on Rails!
Installation
Add this line to your Rails application's Gemfile
:
gem 'vue_cli-rails'
And then execute:
$ bundle install
Requirements
- Ruby >= 2.3
- Rails >= 4.2
- Node >= 8.9+
- Optional:
yarn
Features
- Feel free to use
yarn
ornpm
. - Single
vue_entry
rather than confusingstylesheet_pack_tag
,javascript_packs_tag
andjavascript_packs_with_chunks_tag
. Get all benefits of @vue/cli.
- Powered by
webpack
4 - DRY: all-in-one configuration file rather than repeating for
webpack
,eslint
and etc. - Out-of-box tooling: Babel, TypeScript, PWA,
vue-router
,vuex
, CSS pre-processors, linter and testing tools. - Enhanced alias support in
jest.config.js
.
- Powered by
Run
webpack-dev-server
together with Rails server with development mode.Just single
RAILS_ENV
, no moreNODE_ENV
.Rails way configurations in
config/vue.yml
.
Getting started
Out-of-box workflow:
- Make sure you already installed
@vue/cli
globally vianpm
(npm i -g @vue/cli
) oryarn
(yarn global add @vue/cli
) bundle exec rake vue:create
and follow the steps.Don NOT select
In package.json
for "Where do you prefer placing config for Babel, PostCSS, ESLint, etc.?". Some functionalities like alias of jest may not work.Put your JavaScript files under
app/assets/vue/entry_points
.Insert your entry point by
vue_entry 'entry_point'
in views orrender vue: 'entry_point'
in controllers.webpack-dev-server
auto starts alongsiderails server
in dev mode.Invoke
env RAILS_ENV=production bundle exec rake vue:compile
to compile assets (you still must manually setRAILS_ENV
toproduction
).
More settings are available in
config/vue.yml
Usage
Core
Concept: Entry Point and File structure
The root path of your Vue assets is app/assets/vue
. This gem will generate several folders. However, app/assets/vue/entry_points
is the only one matters.
Webpack sees one JavaScript file as the center of a web page rather than HTML. Thus all styles, images, fonts and other assets are related to a JS files by import 'css/png/svg/woff2/json'
. Any .js
file under app/assets/vue/entry_points
will be a entry-point.
Please ONLY puts your entry-point files under app/assets/vue/entry_points
folder with .js
extension name.
Be aware,
.js.erb
and.vue.erb
are NOT supported. I will explain the reason in Q&A section.
If you are new to modern front-end development, or more specifically with webpack
in this case, please read Q&A section for more information.
Helper vue_entry
vue_entry
is like javascript_include_tag
and stylesheet_link_tag
which generates relative assets links for your entry point. (It's like javascript_packs_with_chunks_tag
in Webpacker 4. I will explain why it's different in Q&A.)
You may have interest of path alias in
config/vue.yml
.
Use render vue: <entry_point>
in controllers
Usually you only need <div id="app"></div>
and vue_entry 'entry/point'
to render a Vue page. You can use render vue: 'entry/point'
inside your controller.
This method is simply a wrap of render html: vue_entry('entry_point'), layout: true
. So you can pass any arguments supported by render
including layout
.
For example
```ruby # app/controllers/my_vue_controller class MyVueController < ApplicationController layout 'vue_base' def foo render vue: 'foo/bar' end end ``` ```htmlPublic Output Path
If the default setting vue_assets
does not bother you at all, you can ignore this section.
Actually public_output_path
in config/vue.yml
is very simple - just a sub path under public
directory. You might suffer some problems by changing it without understanding how it works:
- My regular assets no longer work in dev mode server.
- I lost all my files in
public
folder. (Using a VCS would get your ass saved.) - Where are my compiled assets for prod under
public/assets
directory?
TL, DR - DO NOT name it as any path used by anything else
- It's being used in: - Rails dev server will forward all request under `/#public_output_path` to `webpack-dev-server`; - Webpack will put all compiled assets under `public/#public_output_path`. Unfortunately, it always remove the entire folder before compiling. - Alternative ways - For dev proxy problem: to set different values for `development` and `production` mode in `config/vue.yml`. - For deleting folder when set it to `assets` for prod: run `rake vue:compile[with_rails_assets]` to invoke `rake compile:assets` as well.Summary
If you still feel confusing, please create a new project and select copy demo code.
I will explain what happens in Explanation by Demo.
Available Settings
General settings file is config/vue.yml
manifest_output
Where to put manifest.json
which required by Rails production mode. You can set it in development mode for inspection.
All entry-points will be compiled into assets files. Rails needs manifest.json
to know what are the files and will serve all its JS/CSS tags.
package_manager
Pretty straightforward, which package manager will be used. Valid value: npm
or yarn
. It does NOT support pnpm
or other package managers. You can find the reason in Q&A.
public_output_path
Because it is very important I put it in core section.
launch_dev_service
(NOT available forproduction
mode)
rails server
will launch it when starting by default vue-cli-service serve
. It will be invoked by npx vue-cli-service serve
or yarn exec vue-cli-service serve
depends on your package_manager
.
camelCase
settings will be used invue.config.js
Please see available options.
alias
It's basically
resolve/alias
for Webpack. However, you don't have to config this settings in.eslintrc.js
andjest.config.js
again and again.@vue/cli
will pass the settings to eslint via its plugin. The configuration for jest will be generated and passed tojest.config.js
throughvue.rails.js
.
Customize your webpack configurations in vue.config.js
Feel free to update vue.config.js
by yourself. There are some lines of boiler-plate code to adapt compression-webpack-plugin
and webpack-bundle-analyzer
.
Rake Tasks
vue:create
Install required packages and configurations. You should run this task to get @vue/cli
initializing your project.
What it does for you
- Select package manager: Y=
yarn
, N=npm
- Directly use npm if yarn has not been installed.
- Prefer yarn by default unless detect `package-lock.json`
- Auto install
@vue/cli
globally with your package manager. Invoke
vue create
to initialize Vue project.When detected existing
package.json
Y
- Yes: Fully overwriteN
- No: SkipA
- Auto: You won't loss anything (old_config.merge(new_config)
)K
- Keep: Keep all your settings already have (new_config.merge(old_config)
)
Install
js-yaml
andwebpack-assets-manifest
Deleting Vue demo code under
src
folderCopy demo code to under
app
folder and updateconfig/routes.rb
Copy
vue.rails.js
andvue.config.js
- Do not change `vue.rails.js`! This rake task will always restore `vue.rails.js` back.
- Yes you can update `vue.config.js`. Just make sure you know what are you won't break the configuration. You can chance `config/vue.yml` instead.
- Generate
config/vue.yml
- The convention is: `camelCase` for regular `vue.config.js`, `snake_case` for special usage.
- You can find a full list of [Vue CLI config options below](#valid-vue-cli-config-options).
- All available options [here](#available-options)
BE AWARE: the default option for
config/vue.yml
isY
(to replace existing file), otherwise your package manager change won't be saved. All your files won't be overwritten silently exceptvue.rails.js
.
vue:compile
Compile Vue assets. Please specify RAILS_ENV=production
to compile assets for production.
Optional argument: [with_rails_assets]
to invoke rake compile:assets
after it finished.
However, you can invoke vue-cli-service build
(if vue-cli-service
installed globally, or you can use npx vue-cli-service build
or yarn exec vue-cli-service build
) with RAILS_ENV=production
to build assets.
A good practice is to use
cross-env
to passRAILS_ENV=production
. Socross-env RAILS_ENV=production vue-cli-service build
will work on any platform and shell.
vue:json_config
Converts config/vue.yml
to JSON to be used by vue.rails.js
.
vue.rails.js
prefers parsing config/vue.yml
with js-yaml
. So this is just in case. You may suffer performance issue when your Rails app grow big.
vue:support[formats]
Adds template or style language support. Vue ships with supporting pug
, slm
, sass
, less
and stylus
out-of-box. How ever, you still have to install some loaders manually if you did not select that language with vue:create
.
You can add multiple languages at the same time: rake vue:support[pug,stylus]
vue:node_env
Adds cross-env
and npm-run-all
to your devDependencies
in package.json
, and adds dev
, prod
, serve
and rails-s
to scripts
as well.
It enables you to start rails dev server alongside webpack-dev-server
without pain, and compile production assets.
# to start `rails s` and `webpack-dev-server` together
npm run dev
# or
yarn dev
# same as `/usr/bin/env RAILS_ENV=production bundle exec vue:compile`
npm run prod
# or
yarn prod
You can update scripts/rails-s
and/or scripts/prod
if you need to more stuff:
{
"scripts": {
"rails-s": "cross-env NO_WEBPACK_DEV_SERVER=1 rails s -b 0.0.0.0",
"prod": "cross-env RAILS_ENV=production bundle exec rake vue:compile[with_rails_assets]"
}
}
vue:inspect
Alias of
vue inspect
,npx vue-cli-service inspect
oryarn exec vue-cli-service inspect
. Display the webpack configuration file.
You may need to invoke
rake
withbundle exec
. Rails 5 and above supports newrails rake:task
flavor.
Migrate from Webpacker
It's very easy to migrate from Webpacker.
- Install this gem and
bundle install
- Install
@vue/cli
globally then follow the instructions ofrake vue:create
; - Edit
config/vue.yml
, setdefault/entry_path
tosource_path
(by defaultapp/javascript
) joinssource_entry_path
(by defaultpacks
); - Change all
javascript_packs_with_chunks_tag
tovue_entry
; - Fix all nonsense
xxxx_packs_tag
; - If you mind
public_output_path
andmanifest_output
you can change them to follow Webpacker values; > I strongly not recommend to putmanifest_output.json
underpublic
folder; - Update
vue.config.js
if you have any customized webpack configurations; > You can inspect how webpack settings at anytime - Directly
rails s
to start dev server; > You can get rid ofbin/webpack-dev-server
andbin/webpack
now. However, still recommendrake vue:node_dev
and runyarn dev
so it will killwebpack-dev-server
properly when your Rails dev server stopped. - Call
env RAILS_ENV=production rake vue:compile[with_rails_assets]
instead ofenv RAILS_ENV=production rake assets:precompile
to compile all assets for production. - Delete unused Webpacker files
bin/webpack-dev-server
bin/webpack
config/webpack
config/webpacker.yml
Strongly recommend to backup your codebase before the migration.
Enjoy Hot Module Replacement now!
Valid Vue CLI config Options
You can check the full list on Vue CLI official website.
Special
- publicPath - see
public_output_path
- outputDir - see
public_output_path
- configureWebpack -
vue.rails.js
will generate it.entry
,output
andresolve/alias
are heavily required by this gem. So you must manually update it invue.config.js
very carefully.Demo
Changes to
vue.config.js
const { manifest, pickUpSettings, // isProd, - // getSettings, + getSettings, } = railsConfig; + const { configureWebpack: { entry, output, resolve, module: cwModule } } = getSettings('configureWebpack'); module.exports = { ...pickUpSettings` outputDir publicPath devServer - configureWebpack filenameHashing lintOnSave runtimeCompiler transpileDependencies productionSourceMap crossorigin css parallel pwa pluginOptions `, + configureWebpack: { + entry, + output, + resolve, + module: cwModule, + }, chainWebpack: (config) => {
- publicPath - see
Supported
- [x] filenameHashing
- [x] lintOnSave
- [x] runtimeCompiler
- [x] transpileDependencies
- [x] productionSourceMap
- [x] crossorigin
- [x] css
- [x] devServer
- [x] parallel
- [x] pwa
- [x] pluginOptions
Unsupported
- [ ] baseUrl - Deprecated
- [ ] assetsDir - ignored
- [ ] indexPath - N/A
- [ ] pages - N/A
- [ ] integrity - N/A
- [ ] chainWebpack - directly edit
vue.config.js
Trouble Shooting & Known Issues
My dev server can't find assets
Sometimes your
webpack-dev-server
might still be running while Rails dev server had terminated. For example, you had executedexit!
inpry
.Usually
webpack-dev-server
should be killed when next time you startrails server
. However, for some reason it could fail and the newwebpack-dev-server
will listen to another port. Then you must manually kill them:lsof -i:3080 -sTCP:LISTEN -Pn kill -9 <pid>
Alternatively, you can run
rake vue:node_dev
and always start your dev server with:npm run dev # or yarn dev
I know it is not Rails-way at all. I don't want to waste time to just get it worked properly in Rails way - you are already using Node, why it bothers you?
My API does not work with CSRF token
Because Vue does not have opinion of Ajax (or JSON API) preference, you must implement what
jquery-ujs
does by yourself. There is an example code in vanilla JS with querySelector whish should work for IE8+:// fetch API async (url, data) => { const $csrfParam = document.querySelector('meta[name="csrf-param"]'); const $csrfToken = document.querySelector('meta[name="csrf-token"]'); const csrfParam = ($csrfParam && $csrfParam.getAttribute('content')) || undefined; const csrfToken = ($csrfToken && $csrfToken.getAttribute('content')) || undefined; try { const response = await fetch(url, { method: 'POST', headers: { 'Content-type': 'application/json', 'X-CSRF-Token': csrfToken, }, body: JSON.stringify({ ...data, [csrfParam]: csrfToken, }), }); return response.json(); } catch (e) { // handle failed case } }
Alternatively you can turn off CSRF token and set SameSite cookie if all your clients no longer use IE. Modern browsers can handle
SameSite
flag to prevent CSRF attacks.Mocha tests not working
This is an known issue.
TypeScript can not find my aliases
This is an known issue. TS is still using
tsconfig.json
rather than a.js
or.ts
file. You must manually update it for now. I will try to find a way out.My
yarn test:...
/npm run test:...
not working properlyThe test requires setting
RAILS_ENV=test
. You can invokerake vue:test[unit]
rake vue:test[e2e]
instead.Got errors like
command "..." does not exist
forrake vue:lint/test
This rake task simply invokes
vue-cli-service test:...
. Those commands will be generated by some vue plugins. It won't be available unless you got correct plugin installed.