Skip to content

Instantly share code, notes, and snippets.

@gtarsia
Last active December 6, 2021 16:38
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gtarsia/109e80ccca449605c8a37e99a29c0a5b to your computer and use it in GitHub Desktop.
Save gtarsia/109e80ccca449605c8a37e99a29c0a5b to your computer and use it in GitHub Desktop.
(rails + webpacker + coffeescript 2 + vue + vuex + jsx) from an existing rails project, with proper structure

Caveats

Remember that using vue jsx has some caveats. Most importantly that most vue builtin directives are not supported (for example, v-for, v-model, etc.), expect for v-show. Some of them can be implemented manually, but v-model for example is kind of a pain in the ass.
https://vuejs.org/v2/guide/render-function.html#Replacing-Template-Features-with-Plain-JavaScript
https://github.com/vuejs/babel-plugin-transform-vue-jsx#vue-directives
There's this though:
https://github.com/nickmessing/babel-plugin-jsx-v-model

Also the sourcemap result is kind of crappy, things like @ are not mapped correctly, I resorted to just using 'eval' as webpack config.devtool, it's good enough for what I'm doing.

Instructions

(From now on, if you are on rails version < 5.0, and you see bundle exec rails anywhere below, replace it with bundle exec rake)
On an existing rails project:

  1. Run these commands:
bundle add webpacker
bundle exec rails webpacker:install
bundle exec rails webpacker:install:coffee
# current coffee version is now 1.x, we need to update to 2.x to enable saiyan es6 power
yarn upgrade coffeescript --latest
yarn add vue vuex
# let's add vue jsx support
yarn add babel-plugin-syntax-jsx babel-plugin-transform-vue-jsx \
         babel-helper-vue-jsx-merge-props babel-preset-env
  1. Remove the app/javascript/pack/hello_coffee.coffee file
  2. Update/Add the files shown below. (These files have comments inside about the changes done, except for .babelrc which is a json, and you can't put comments in a json file; I only added "transform-vue-jsx" to "plugins" to have jsx support)
  3. (These files you added defined a component Todo). Add the component <todo/> in a view of your choice.
  4. Restart your rails server
  5. Run ./bin/webpack-dev-server. (I wished I could make it work with just rails server but I can't)
  6. Browse that view and you should see this:
    celeryman
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": "> 1%",
"uglify": true
},
"useBuiltIns": true
}]
],
"plugins": [
"transform-vue-jsx",
"syntax-dynamic-import",
"transform-object-rest-spread",
["transform-class-properties", { "spec": true }]
]
}
# app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
...
<%= javascript_pack_tag 'index' %>
<%# I have no idea how to make turbolinks work with this %>
<%# Also, you can put this in <head>, I don't know what is the right choice %>
</body>
</html>
// config/webpack/environment.js
const { environment } = require('@rails/webpacker')
const coffee = require('./loaders/coffee')
/* Moving coffee-loader before babel-loader */
environment.loaders.append('coffee', coffee)
const babelLoader = environment.loaders.get('babel')
babelLoader.test = /\.(coffee|js|jsx)(\.erb)?$/
/* this lets you import vue instead of the runtime */
environment.config.resolve.alias = {
'vue$': 'vue/dist/vue.js'
}
module.exports = environment
# app/javascript/pack/index.coffee
import './../inst'
# app/javascript/inst.coffee
# This file is called inst, as in Vue instance
import Vue from 'vue'
import todo from './components/todo_component'
import store from './store/todo_store'
new Vue
el: '#app'
store: store
render: (h) -> h todo
# app/javascript/components/todo_component.coffee
export default
name: 'todo'
render: (h) ->
tasks = for task in @tasks
<button>{ task.name }</button>
<div>
{ @greeting }
<div>
{ tasks }
</div>
</div>
# components: { contacts, texter } # put your imported child components
computed:
greeting: () -> @$store.getters.greeting
tasks: () -> @$store.getters.tasks
# app/javascript/store/todo_store.coffee
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export default new Vuex.Store
strict: true
getters:
greeting: () -> 'hello there'
tasks: () -> [{name: 'do stuff'}, {name: 'do more stuff'}]
# modules: { contacts } # put your imported your child stores
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment