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.
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.
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"
]
}
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!
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
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.