Build Modern Web Apps with Nuxt 3 and Vuejs
Nuxt is the de facto VueJs framework used throughout the industry to build apps, these being of the type Single Page App (SPA), Server-side Rendering (SSR), or Static-Site Generating (SSG).
It's the latter that we will use to create web app for a sample event - to be forked by our readers for their own events' landing pages.
Why Nuxt and a Static app?
Nuxt simplifies and structure of your projects, paving the path to enterprise-ready architecture with various CLIs and tools to boot.
We choose a Static app, because we want a?Universal Application, which is discoverable and optimized for various search engines. The usecase here (a landing page for events) is simple, with no external content to load or manipulate.
The Nuxt version used here, is part of the release candidate 3, which is a major upgrade of the Nuxt used in the industry, and following the release of VueJs' major update to version 3.
Initiatlize your Project
For this project we will use?yarn?instead of?npm?to manage dependencies and build the app.
To setup a project, nuxt offers a simple command to run within your node environment:
npx nuxi init app
Go to the?app?directory, and install all packages using?yarn install. Once this is done, run an?yarn dev?and navigate to?https://localhost:3000/?in your browser.
Since out of the box, nuxt3 comes with typescript, we should leverage the typing benefits by adding the required libraries and configuring type checking.
yarn add -D vue-tsc typescript
Add these libraries with:
yarn add -D vue-tsc typescript
Go to?nuxt.config.ts?in the app directory and enable automatic typechecking:
export default defineNuxtConfig({
typescript: {
typeCheck: true,
},
})
Let's add some linting. Run the eslint command?npx eslint --init?and go through the options to select vue and typescript to generate the correct eslint configuration file. For nuxt3, this is how our?.eslintrc.yaml?is configured:
env:
browser: true
es2021: true
extends:
- plugin:vue/base
- plugin:vue/vue3-recommended
- plugin:@typescript-eslint/recommended
- '@nuxtjs/eslint-config-typescript'
parser: 'vue-eslint-parser'
parserOptions:
parser: "@typescript-eslint/parser"
ecmaVersion: latest
ecmaFeatures:
jsx: true
sourceType: module
plugins:
- vue
- '@typescript-eslint'
Don't forget to add the?--fix?to the linting command created in your package.json to automatically fix your styling.
Generate the content
Project Structure
Let's plan our event app. We know we want a landing page with a home containing basic information, a registration page and an about page with details. Breaking it down in a Nuxt structure, this is what we plan:
|?public/: robots.txt and favicon.ico. Nuxt will simply copy these with no processing
|?plugins/: Additional libraries, in our case we will add Vuetify.
|?assets/: Images and CSS we will deploy to the users. These will be packaged and altered with the baseurl.
| css
| img
|?components/: Pages we will deploy. Nuxt will automatically import anything here.
|?AboutSection.vue: Put info about our event.
|?ContactSection.vue: How to register.
|?FooterSection.vue: The footer bar.
|?HomeSection.vue: The main banner and any info.
|?app.vue/: Our index page which will load all others..
Time to load a simple html section for our?app.vue, to verify this setup works. We do this by replacing the default Nuxt3?NuxtWelcome?with this html:
<template>
<div>
<h1>Landing</h1>
<p>If you see this, you navigated to the landing page.</p>
</div>
</template>
Running the?yarn dev?should load a server pointing to the index page we just created.
Make it Beautiful
Our app should be pretty, and what better way to do this than through a?Vuetify?template:
yarn add vuetify@next sass @mdi/font
yarn add -D vuetify@next vite-plugin-vuetify @fortawesome/fontawesome-free
In the plugin directory, we need to add?vuetify.ts?for the veutify configurations:
import '@mdi/font/css/materialdesignicons.css'
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
export default defineNuxtPlugin((nuxtApp) => {
const vuetify = createVuetify({
components,
directives
})
nuxtApp.vueApp.use(vuetify)
})
Extend?nuxt.config.ts?to include vuetify, fonts and other nice things, like the favicon and metadata:
export default defineNuxtConfig({
...
head: {
link: [
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Oswald|Libre+Baskerville&display=swap' },
{ hid: 'icon', rel: 'icon', type: 'image/x-icon', href: 'favicon.ico' }
],
titleTemplate: 'Vue +Nuxt3 with GH pages and Actions',
meta: [
{ charset: 'utf-8' },
{ hid: 'description', name: 'CVue +Nuxt3 with GH pages and Actions.', content: 'Meta description' }
]
},
css: ['vuetify/lib/styles/main.sass', '@/assets/css/main.scss'],
build: {
transpile: ['vuetify']
},
plugins: ['~/plugins/vuetify.ts'],
Make our home page in?app.vue?even more beautiful:
<template>
<div class="pt-8 mt-4">
<v-card
class="mx-auto rounded-xl"
width="600"
prepend-icon="mdi-home"
>
<template v-slot:title>
Landing with Veutify
</template>
<v-card-text class="text-center">
If you see this, you navigated to the prettier landing page
</v-card-text>
</v-card>
</div>
</template>
Land some Attendees
Now we build our landing page according to the plan.
Build The App
We will build a Static site generation (SSG), which means it pre-renders every route of our app so navigation happens organically through html pages. Nuxt does this with a crawler to generate each HTML file. Add these configurations to the?nuxt.config.ts?to hint that we want a full static, not and preview version - which would have fetch API calls to each static page:
export default defineNuxtConfig({
...
mode: 'universal',
target: 'static',
ssr: false,
router: {
base: '/vue-nuxt-gh-staticlanding'
},
app: {
baseURL: '/vue-nuxt-gh-staticlanding'
}
})
Note the baseUrl configuration in both?router?and?app. These will effect the final URL that Nuxt will create. This means that every image it encounters, or URL redirect it process, will be prepended with the?BASEURL. In our case, it's?/vue-nuxt-gh-staticlanding, you will see the effect it has when we deploy to Github Pages, and the assets will be served by a subdomain named after our repo.
领英推荐
The repo has the majority of the assets needed to build a landing page, most are straight forward. Though you will notic a peculiarity when processing images:
...
<v-row align="center" justify="center">
<v-col
cols="12"
md="6"
>
...
<v-img :src="contactUrl" class="rounded-lg" height="30vh" />
</v-col>
</v-row>
...
<script>
import contactImage from '~/assets/img/contact.png'
export default {
data: () => ({
contactUrl: contactImage
})
}
</script>
All images are loaded via a script, not directly through the Veutify directives. This is because in this form, veuitfy transpiles these into HTML outside of?vite's processes and missing out on any preprocessing from Vite. But if we import it as code first, vite will add any baseurls we have preconfigured - hopefully something that will be solved in the future stable versions of Nuxt and Vue.
Deploy to Github Pages
Time to deploy on github's static page hosting. To do this, we will use a plugin called push-dir, which will automatically configure our repo to host static pages and deploy our assets:
yarn add -D push-dir
What this push-dir will do is batch a series of git commands to prepare your static app to be hosted, by creating an orphaned branch called?gh-pages?and deploying from it - and deleting it in the end:
git checkout --orphan gh-pages PAGESNAME
yarn build
git --work-tree dist add --all PAGESNAME
git --work-tree dist commit -m 'Deploy'
git push origin HEAD:gh-pages --force
rm -r dist
git checkout -f master
git branch -D gh-pages
Generate the static pages and deploy:
yarn generate
yarn deploy
When the deployment is successful, your github repo settings should change to show that github pages have been enabled and are deployed from a temporary branch called?gh-pages:
Navigate to the url shown in your github pages, and you will see a 400 - WHAT?!
This is a problem all those with nuxt will encounter.
GH pages operates on a templating engine called?jekyll. Jekyll ignore all dot files, like .git and .nuxt. Nuxt processes and packages all your assets in an?.Nuxt, therefore when GH pages looks at your site, it will ignore this folder and load nothing of the assets therein.
To resolve this we create a?.nojekyll?file, to disable this templating engine, and let github pages load the?.nuxt?folder and its assets. If you create one (or use the touch command) in the assets folder, you can copy it in your?package.json:
"deploy": "cp assets/.nojekyll ./dist &&push-dir --dir=dist --branch=gh-pages --cleanup --force"
We are hoping that after the alpha version we tested of Nuxt 3, this feature would come out of the box.
With the gap closed, let's deploy again our webapp, and navigating to the url we should see a healthy landing page:
GitHub Actions for Automation
All good engineers despise manual tasks, and having to type in the commands to generate and deploy the app - that's two commands too much. Let's utilize github's own CICD: Github Actions, to automate this every time we merge to the main branch.
To create a pipeline, start by creating a?.github?folder and add the file?pages.yml:
name: Generate and Deploy to GHPages.
on:
push:
branches: ["main"]
pull_request:
branches: [ "main" ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: write
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: "18.8.0"
cache: yarn
- name: Restore cache
uses: actions/cache@v3
with:
path: |
dist
.nuxt
key: ${{ runner.os }}-nuxt-build-${{ hashFiles('dist') }}
restore-keys: |
${{ runner.os }}-nuxt-build-
- name: Install dependencies
run: yarn install
- name: Generate Static Content
run: yarn generate
- name: Setup GIT and Deploy to GHPages
run: |
git config --global user.name "GH Actions"
git config --global user.email "[email protected]"
yarn deploy
in general, you would want to use ready made git actions plugins:
We add some scripts using the?run?syntax. In that file we have the run command to generate the static app, and another to deploy. Note that the deploy adds some git config settings before, this is needed for the?push-dir?module to work.
Note these important configurations below.
...
on:
push:
branches: ["main"]
pull_request:
branches: [ "main" ]
For whenever something is pushed or merged to this branch, run the build - this is our main automation.
permissions:
contents: write
...
This allows the plugin?push-dir?to create the gh-pages branch and push to it to deploy the app.
Now everytime you update your landing page, you will see the pipeline rolling your page out to the public:
Conclusion
In this article we explored the alpha versions of Nuxt3 and Vue3. We learned the various development patterns used for webapps, and leveraged the static page generation pattern through the vue development framework nuxt.
To automate our work, we used github pages for deployment and git actions as the CI/CD.
You can fork this repo and enjoy creating your events webapps!
References
Github
Code in this article is available on?Github.
This Article?by?Adam Darmanin?is licensed under?CC BY-NC-SA 4.0