Nuxt 系列 - #5 Pages 開發大小事
Async Data
Nuxt.js 增加了一個 asyncData 的方法,在 Vue 組件初始化前執行,用來取得 api 資料,因此在這個方法內無法使用 this 來引用組件的實體對象,僅能使用傳入的 context 來獲得相關的資訊與方法。官方建議呼叫 api 使用 axios 套件,因此接下來的程式碼範例將會使用 axios。
asyncData 使用方式有兩種,可以挑選自已熟悉的方式來使用,方法如下:
- 返回 Promise
export default { asyncData ({ params }) { return axios.get(`https://my-api/posts/${params.id}`) .then((res) => { return { title: res.data.title } }) } }
- 使用 async、await
export default { async asyncData ({ params }) { const { data } = await axios.get(`https://my-api/posts/${params.id}`) return { title: data.title } } }
asyncData 回覆的結果將會與 Vue 組件中 data 方法回傳的 json 物件合併,提供給組件使用。與開發一般 Vue 應用程式時,通常都在 mounted 生命周期中去呼叫 api 有些許不同。
asyncData 傳入的 context 包含三大類可用屬性與方法,分別為 Universal、Server-side、Client-side。
除了 asyncData 有 context 可用,還有其他 Nuxt.js 的方法 fetch、plugins、middleware、nuxtServerInit 也都會傳入 context。在上述這些方法中還可以使用 process.server 判斷是否為 Server-side 或 process.client 判斷是否為 Client-side。
以下程式碼可以看出目前 context 內包含了那些屬性與方法
function (context) {
// Universal keys
const {
app,
store,
route,
params,
query,
env,
isDev,
isHMR,
redirect,
error
} = context
// Server-side
if (process.server) {
const { req, res, beforeNuxtRender } = context
}
// Client-side
if (process.client) {
const { from, nuxtState } = context
}
}
如何在 asyncData 取得動態路由傳入的參數?
解構 context 取得 params,"params.參數名稱"即可取得傳入參數值
export default {
asyncData ({ params }) {
console.log('pages/article/_id.vue 傳入參數 id => ', params.id)
}
}
錯誤處理
解構 context 取得 error 方法,當需要拋出錯誤時呼叫 error 方法,Nuxt.js 就會顯示錯誤頁面
export default {
asyncData ({ params, error }) {
return axios.get(`https://my-api/posts/${params.id}`)
.then((res) => {
return { title: res.data.title }
})
.catch((e) => {
error({ statusCode: 404, message: 'Post not found' })
})
}
}
Plugins
當需要使用 Vue.use、Vue.filter、Vue.component 時,應在 plugins 資料夾建立程式檔案並且在 nuxt.config.js 增加載入 plugins 的設定。
目前專案有使用 bootstrap-vue、vue-disqus 與 @fortawesome/vue-fontawesome 套件,另外也增加了一些 filter 的設定,都是透過 plugins 方式載入。套件的使用方式基本上都參照套件內的說明,filter 的設定方式也跟原本 Vue 的寫法一樣,只是在 Nuxt.js 需要在 nuxt.config.js 中設定 plugins。
目前 blog 文章內容格式是使用 Markdown,因此必需將 Markdown 轉換為 HTML 的內容,利用 marked 套件來做轉換,搭配 Vue filter 的方式使用。另外還有增加一個顯示日期的 filter。
新增 plugins/filters.js 檔案
import Vue from 'vue'
import highlightjs from 'highlight.js'
import marked, { Renderer } from 'marked'
import pathHelper from '~/helpers/path'
// Create your custom renderer.
const renderer = new Renderer()
renderer.link = (href, title, text) => `<a target="_blank" href="${href}" title="${title}">${text}</a>`
renderer.table = (header, body) => `<table class="table table-striped">${header}${body}</table>`
// Set the renderer to marked.
marked.setOptions({
renderer,
baseUrl: pathHelper.getBaseUrl(),
highlight: function (code, language) {
// Check whether the given language is valid for highlight.js.
const validLang = !!(language && highlightjs.getLanguage(language))
// Highlight only if the language is valid.
const highlighted = validLang ? highlightjs.highlight(language, code).value : code
// Render the highlighted code with `hljs` class.
return `<pre><code class="hljs ${language}">${highlighted}</code></pre>`
}
})
Vue.filter('parseMd', marked)
Vue.filter('parseDatetime', function (datetime) {
const parseDate = moment(datetime, 'YYYY-MM-DD')
return parseDate.format('LL')
})
在 nuxt.config.js 增加 plugins 設定
export default {
plugins: [
'~/plugins/filters.js',
// or
{ src: '~/plugins/filters.js' }
]
}
載入 plugins 也是可以選擇要在 client、server 或者都要使用,只需要在 nuxt.config.js 設定即可。設定方式有2種:
- 傳入物件包含 src 與 mode 設定
export default { plugins: [ { src: '~/plugins/both-sides.js' }, // both client & server { src: '~/plugins/client-only.js', mode: 'client' }, // only in client side { src: '~/plugins/server-only.js', mode: 'server' } // only in server side ] }
- 檔案命名
export default { plugins: [ '~/plugins/baz.js', // both client & server '~/plugins/foo.client.js', // only in client side '~/plugins/bar.server.js' // only in server side ] }
Assets
Nuxt 預設使用 file-loader 與 url-loader 來處理圖片與字型檔案的載入與使用。如果有不需要透過 webpack 處理的靜態資源檔案,可以放置在 static 資料夾,執行打包後會將檔案複製到產出結果的資料夾。需要透過 webpack 處理的圖片與字型檔案則統一放置於 assets 資料夾。
Webpack Assets 檔案位置:
assets/
∟ image.png
Webpack Assets 使用方式:
<template>
<img src="~/assets/image.png">
</template>
Static Assets 檔案位置:
static/
∟ image.png
Static Assets 使用方式:
<template>
<img src="/image.png">
</template>
注意事項:
從 Nuxt 2.0 後,在 css 使用路徑別名 ~/ 時必需移除斜線,才能正確的載入檔案。例如:background: url("~assets/img/avatar.jpg");
樣式配置
使用 SCSS 撰寫樣式
SCSS 樣式檔案結構
assets/
∟ sass/
∟ helpers/
∟ _functions.scss
∟ _mixins.scss
∟ _variables.scss
∟ main.scss
將 SCSS functions、mixins 與 variables 分檔案放置方便管理與維護
Global 樣式放置於 assets/sass/main.scss
Pages 與 Components 自身樣式設定於各自的 .vue 檔案內,並且啟用 scoped 設定
目前規畫的 _variables.scss 內容,主要是顏色的設定
$primary-color: #1dc8cd;
$secondary-color: lighten(saturate($primary-color, 25), 50);
$tertiary-color: #1de099;
$marked-primary-color: #c91414;
$marked-secondary-color: lighten(saturate($marked-primary-color, 25), 50);
若要讓 _functions.scss、_mixins.scss 與 _variables.scss 能夠在整個應用程式的樣式中使用,必需使用 @nuxtjs/style-resources 套件引入上述3個檔案
@nuxtjs/style-resources 使用方式:
- 安裝 @nuxtjs/style-resources 套件
npm add -D @nuxtjs/style-resources
- 在 nuxt.config.js 增加 buildModules 與 styleResources 設定
export default { buildModules: [ '@nuxtjs/style-resources', ], styleResources: { // your settings here scss: [ path.resolve(__dirname, 'assets/sass/helpers/_variables.scss'), path.resolve(__dirname, 'assets/sass/helpers/_functions.scss'), path.resolve(__dirname, 'assets/sass/helpers/_mixins.scss') ] } }
設置完成後即可在任何 .scss 檔案、Pages 與 Components 使用 functions、mixins 與 variables。以下為目前 blog 專案內 pages/article/_id.vue style 設定範例
<style lang="scss" scoped>
.md-content {
& /deep/ img {
max-width: 100%;
}
}
.article__date::after {
content: "";
}
.other_article_link {
color: $primary-color;
}
</style>
最後在 nuxt.config.js 設定全站要使用的所有樣式檔案
export default {
css: [
'@fortawesome/fontawesome-svg-core/styles.css',
// 載入bootstrap
'bootstrap/scss/bootstrap.scss',
'bootstrap-vue/dist/bootstrap-vue.css',
// 載入highlight.js樣式(可選擇不同theme)
'highlight.js/styles/zenburn.css',
// 載入aos樣式
'aos/src/sass/aos.scss',
// 主要css樣式(customer)
'~/assets/sass/main.scss'
]
}
針對 Pages 的說明大概到這邊,下一篇要介紹內容管理的服務 contentful,如何在上面建立、管理 blog 的內容與如何跟 Nuxt.js 整合。
參考資料
Nuxt.js - Async Data
Nuxt.js - Assets
Nuxt.js - Plugins
@nuxtjs/style-resources