multiple pages in Vue.js CLI
I'm having trouble figuring out how to have multiple pages in a Vue CLI project. Right now I have my home page with a few components and I want to create another page but I do not know how to do that. Am I supposed to create multiple html files where the index.html by default is? In a simple file structure with css js img folder and html files as pages I know that creating another html file means making another page. But I don't understand how this works with Vue CLI project.
I saw stuff like vue-router and "pages" in Vue documentation but I do not understand them very well. What are my alternatives? Is there a guide that explains that in detail, because I wasn't able to find any, let alone detailed. Would be very happy if you could help! Thank you!
First: always read the official documentation. With Vue you can build a SPA, and a MPA is also no problem. Just follow the guides:
- https://cli.vuejs.org/
- https://cli.vuejs.org/guide/html-and-static-assets.html#building-a-multi-page-app
- https://cli.vuejs.org/config/#pages
You should create a new project with Vue CLI 3. Once you've created your project set it to be manually configured. Make sure you don't choose the SPA option. Vue will then create a nice "start" project using a MPA approach. After that, just repeat the config on vue.config.js.
Updated #1
It seems that some updates on Vue Cli, have changed the way to build a MPA app, so:
- Create a new application
vue create test
- Choose Manual configuration
The boilerplate created will be for a SPA. So make the following changes:
-
Create a folder under
src
namedpages
(optional) -
Into this folder create your own pages: Home, About, etc.
-
Copy and paste the App.vue and main.js from src, into your new folders - Home, etc.
-
Format the
App.vue
into this folders, to your liking. -
Create a vue.config.js and set it like this: https://cli.vuejs.org/config/#pages
Below, I have three images demonstrating this:
- First: a fresh new app
- Second: this same app, with the changes I made above
- Third: the vue.config.js from this app
You don't need to create the pages
folder, this is just to get the idea.
Link to GitHub: Building a MPA App
EDIT: Vue has this built-in. Skip to the bottom for more.
Original answer:
There are two ways to interpret your question, and therefore to answer it.
The first interpretation is: "how can I support routing to different pages within the same single-page app, e.g. localhost:8080/about and localhost:8080/report etc?". The answer to this is to use the router. It's reasonably straightforward and works well.
The second interpretation is: "my app is complex, and I have multiple single-page applications, e.g. one app for the 'website' part, one app for consumers to log in and do work, one app for admins, etc - how can vue do this, without making three entirely separate repositories?"
The answer to the latter is a single repository with multiple single-page apps. This demo looks like exactly what you're after:
https://github.com/Plortinus/vue-multiple-pages/
Look in particular at: https://github.com/Plortinus/vue-multiple-pages/blob/master/vue.config.js
Updated answer:
It turns out that vuejs has the idea of multiple top-level pages built-in. I mean, it makes sense - it's going to be really common, despite what many incorrect answers are saying about "no, it's for single page apps"!
You want the pages
option in the vue.config.js
file:
https://cli.vuejs.org/config/#pages
If your project doesn't have that file in the root directory, create it and vuejs will discover it.
There is a long and a short way to define each page. I used the short form here:
module.exports = {
pages: {
index: 'src/pages/index/main.ts',
experiment: 'src/pages/experiment/main.ts'
}
}
You don't have to put your work under "pages". It could be "/src/apps/index/index.ts" or whatever.
After moving code around and changing some imports from:
import HelloWorld from './components/HelloWorld'
to
import HelloWorld from '@/components/HelloWorld'
The app works - but the "experiment" app in my repo had to be loaded like this:
http://localhost:8080/experiment.html
Pretty ugly, and even worse because it uses the router which resulted in URLs like:
http://localhost:8080/experiment.html/about
Ugh.
Fortunately, this stackoverflow answer solved it. Update the vue.config.js
file to include devServer
options (make sure this is at the top level of the exported object:
devServer: {
historyApiFallback: {
rewrites: [
{ from: /\/index/, to: '/index.html' },
{ from: /\/experiment/, to: '/experiment.html' }
]
}
}
Then also modify the router.ts
file to append the extra path (in my case "experiment/":
export default new Router({
mode: 'history',
base: process.env.BASE_URL + 'experiment/',
...
Then URLs resolve nicely, e.g.: http://localhost:8080/experiment/about
This may not be relevant to the question, but bear with me, maybe my answer can help someone. I use webpack+vue, and I have figured out how to build multiple pages applications. Here my webpack.config.js:
const path = require('path');
const fs = require('fs')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
entry: {
app: './src/app.js',
mgmt: ['./src/modules/mgmt/mgmt.js'],
login: './src/modules/login/login.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
// publicPath: '/ahezime/',
filename: (chunkData) => {
console.log('chuckData.chunk.name => ', chunkData.chunk.name)
return chunkData.chunk.name === 'app' ? './[name].bundle.js' : './[name]/[name].bundle.js';
}
},
optimization: {
minimizer: [
new TerserPlugin(),
new OptimizeCSSAssetsPlugin({})
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
new CleanWebpackPlugin(['dist']),
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
title: 'app',
template: './src/app.html',
// inject: false,
chunks: ['app'],
filename: './index.html'
}),
new HtmlWebpackPlugin({
title: 'mgmt',
template: './src/modules/mgmt/mgmt.html',
// inject: false,
chunks: ['mgmt'],
filename: './mgmt/index.html'
}),
new HtmlWebpackPlugin({
title: 'login',
template: './src/modules/login/login.html',
// inject: false,
chunks: ['login'],
filename: './login/index.html'
})
],
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-proposal-object-rest-spread']
}
}
}
],
rules: [
{
test: /\.vue$/,
exclude: /node_modules/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.scss?$/,
use: ['style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
]
}
};
And here's my directory structure:
https://i.stack.imgur.com/uFvKx.png
And you can jump pages:
<template>
<div>
<h1>App</h1>
<div>
<a href="./login">Please click me, and let take you into the login page!!!</a>
</div>
<span>Before computed: {{ message }} </span>
<br>
<span>Afer computed: {{ computedMessage() }} </span>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello World!'
}
},
computed: {
reversedMessage: function() {
return this.message.split('').reverse().join('')
}
},
methods: {
computedMessage: function() {
return this.message.split('').reverse().join('')
}
}
}
</script>