Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save odyright/8330375fba55624dd877f03202bfc222 to your computer and use it in GitHub Desktop.
Save odyright/8330375fba55624dd877f03202bfc222 to your computer and use it in GitHub Desktop.
Vue-cli 3, Phoenix 1.3, a complete how-to

Introduction

I have been struggling to start a new project with Phoenix 1.3 and the new vue-cli 3 for Vue.js. There are tons of example already but none of them suited my needs, because:

  • I want to use the new Vue-cli to select the features that I want,
  • I do NOT want to setup Webpack (I know, what a shame!). The new Vue-cli includes the new vue-cli-service, which uses an instance of webpack-dev-server, so you don't have to import it manually in your project.
  • I do not want to use Brunch.

Create your Phoenix App

Assuming that you have Elixir and Phoenix 1.3 are both installed, let's build our new App.

$ mix phx.new hello --no-brunch
$ cd hello
$ mix ecto.create

You should have the directory structure of your freshly created project.

Creating the structure of the front-end part, including Vue.js

The next step will be to create what's inside the assets folder. Phoenix 1.3 introduce some changes where everything from the front-end must be inside this folder. I think that's an amazing approach, leaving all the related files such as package.json isolated from the rest.

Then, if you haven't done yet, let's install the Vue-cli toolkit. More information can be found here: https://github.com/vuejs/vue-cli.

$ npm install -g @vue/cli

Make sure you are inside your project folder, and then simply create a new project with this command. It will create a directory named assets that will be specially made for your needs.

$ vue create assets

That's where the fun starts! The vue-cli tool asks us which features we want. It guides you through the selection. It even asks you for which style pre-processor you want to use, linter, unit testing environments (Hello to Jest!!!), etc.

Vue CLI v3.0.0-beta.1
? Please pick a preset: Manually select features
? Check the features needed for your project:
 ◉ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◉ Router
 ◉ Vuex
 ◉ CSS Pre-processors
 ◯ Linter / Formatter
❯◉ Unit Testing
 ◯ E2E Testing

Once this is done, make sure the structure is in the assets folder, and just contemplate on how small is the package.json file:

{
  "name": "assets",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve --open",
    "build": "vue-cli-service build",
    "test": "vue-cli-service test"
  },
  "dependencies": {
    "vue": "^2.5.13",
    "vue-router": "^3.0.1",
    "vuex": "^3.0.1"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.0.0-beta.1",
    "@vue/cli-plugin-unit-jest": "^3.0.0-beta.1",
    "@vue/cli-service": "^3.0.0-beta.1",
    "@vue/test-utils": "^1.0.0-beta.10",
    "babel-core": "^7.0.0-0",
    "babel-jest": "^22.0.4",
    "node-sass": "^4.7.2",
    "sass-loader": "^6.0.6",
    "vue-template-compiler": "^2.5.13"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

The first build

We'll come to Hot Module Reloading (HMR) later. For now, all we need is to produce an index.html that can be served by Phoenix. Before doing so, we must tell Vue-cli to produce its own build in the priv/static folder, which is the official directory for static content. (Keep in mind that in production, this is where our front-end build will reside). For this, we'll add a vue.config.js in our assets directory, containing the following lines:

const path = require('path');

module.exports = {
  lintOnSave: true,
  configureWebpack: {
    output: {
      path: path.resolve(__dirname, '../priv/static'),
    },
  },
};

Once this is done, run a production build:

$ npm run build

 DONE  Compiled successfully in 19858ms                                                                                                                                                 17 h 19 min 02 s

  File                                    Size              Gzipped

  ../priv/static/js/vendor.13e9fbad.js    94.72 kb          32.19 kb
  ../priv/static/js/app.5859fc59.js       12.82 kb          8.07 kb
  ../priv/static/css/app.d88a32fa.css     0.42 kb           0.26 kb

  Images and other types of assets omitted.

 DONE  Build complete. The ../priv/static directory is ready to be deployed.

Finally, we must tell Phoenix to serve this fantastic index.html we just created. It will also be our default route, and because I want Vue's router to use the HTML5 History Mode to handle 404's, we need to make it our fallback default route.

First, edit lib/hello_web/controllers/page_controller.ex so it ressembles to this:

defmodule HelloWeb.PageController do
  use HelloWeb, :controller

  def index(conn, _) do
    conn
    |> put_resp_header("content-type", "text/html; charset=utf-8")
    |> Plug.Conn.send_file(200, "priv/static/index.html")
    |> halt()
  end
end

We simply tell Phoenix to serve the static index.html.

Then, modify the following line in lib/morrier_web/router.ex so it matches anything:

    get "/*anything", PageController, :index

Rerun the server and try to navigate to http://localhost:4000/about, it'll bring you to the specific page of our app!

HMR (Hot Module Reload)

We will be using the webpack-dev-server that is packaged with Vue-cli, and not the one that comes bundled with Phoenix. That means two servers will co-exists in your dev environment.

To run the backend process:

$ mix phx.server

And to run the front-end server (usually localhost:8080):

$ cd assets
$ npm run serve

Production Build

To deploy (on Heroku), you don't need the Phoenix buildpack. I've deceided to build before push to heroku. So run npm run build and commit the files in priv/static.

Also, make sure all you images are in assets/public and referenced correctly in your code. I found it weird at first to import images in JS, but that's how Webpack works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment