nuxt是一個(gè)基于 Vue.js的服務(wù)端渲染應(yīng)用框架,為了解決C端項(xiàng)目諸如"首屏加載速度" "SEO優(yōu)化"之類的痛點(diǎn)而生,從發(fā)布到現(xiàn)在已經(jīng)接近兩年趨于穩(wěn)定.我接手這個(gè)商城也有一段時(shí)間了,本著提高自己的目的再來梳理下我對這個(gè)框架的認(rèn)識.
應(yīng)用流程
下圖闡述的是Nuxt.js 應(yīng)用一個(gè)完整的服務(wù)器請求到渲染(或用戶通過 <nuxt-link> 切換路由渲染頁面)的流程.首先是服務(wù)器端接受到請求,然后nuxt服務(wù)啟動(dòng),這時(shí)候也初始化了vuex.然后依次通過中間件去匹配 配置(config.js) 布局(layout) 具體頁面,在頁面組件被加載之前服務(wù)器端執(zhí)行異步數(shù)據(jù)方法(asyncData 和 fetch),最后輸出模板.這里我們要注意的是,只有beforeCreate和created會(huì)在服務(wù)器端和瀏覽器端均被調(diào)用.
項(xiàng)目結(jié)構(gòu)
我們這個(gè)項(xiàng)目應(yīng)該是用官方提供的starter模板啟動(dòng),讓我們一個(gè)個(gè)來分析.
- nuxt是自動(dòng)生成的,關(guān)于布局 請求 loading 中間件 路由 vuex的一些官方封裝,無需過多關(guān)注.
- api用于存放項(xiàng)目地址和localstorage的一些命名.
- assets用于存放一些靜態(tài)資源,我們這里有 全局CSS iconfont文件 圖片 element-ui主題 全局工具類js函數(shù).這里的文件會(huì)被webpack做構(gòu)建編譯處理,而static不會(huì).
- components用于存放一些公共組件,框架并不會(huì)對其進(jìn)行增強(qiáng).
- layouts是一些布局文件,404頁面也放在這里了.在page下的頁面文件里指定layout就可以使用這里的布局.
- locales是我們用于國際化的一些語言文件
- middleware是一些運(yùn)行在頁面渲染之前的自定義函數(shù),我們這里主要是語言切換和loading.
- pages用于組織應(yīng)用的路由和視圖.nuxt比較省心的地方是已經(jīng)約定了路由配置的自動(dòng)生成,所以同時(shí)也需要注意文件的地址.
-
plugins配置需要在 根vue.js應(yīng)用實(shí)例化之前需要運(yùn)行的 Javascript 插件,我們這里有全局過濾器,element-ui的國際化配置,用于站長統(tǒng)計(jì)的piwik,以及對于第三方庫vue-loading的引用和配置.
- static 用于存放應(yīng)用的靜態(tài)文件,此類文件不會(huì)被 Nuxt.js 調(diào)用 Webpack 進(jìn)行構(gòu)建編譯處理。 服務(wù)器啟動(dòng)的時(shí)候醉蚁,該目錄下的文件會(huì)映射至應(yīng)用的根路徑 / 下啤它。我們這里只有favicon.ico.
- store用于組織應(yīng)用的Vuex 狀態(tài)樹文件.這里涉及到兩個(gè)方法,一個(gè)是fetch,會(huì)在渲染頁面前被調(diào)用,作用是填充store 數(shù)據(jù);一個(gè)是nuxtServerInit 方法,是在啟動(dòng)nuxt應(yīng)用時(shí)去填充stores數(shù)據(jù).這個(gè)方法我們就是用來拿token和進(jìn)行其他初始化.
- 下面的文件都比較常見了,eslint代碼檢查 package.json依賴管理 lock版本鎖定,mes_pc.js里面存了給服務(wù)器用的啟動(dòng)配置.nuxt.config.js是核心,在下一小節(jié)進(jìn)行講解.
nuxt.config.js分析
const webpack = require('webpack')
const NLE = require('./api/constant')
module.exports = {
/*
** Headers of the page
*/
head: {
title: '歐亞商城',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width' },
{ hid: 'description', name: 'description', content: 'Nuxt.js project' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
/*
** Customize the progress bar color
*/
loading: { color: '#019ae0' },
plugins: [
{ src: '~plugins/vue-lazyload', ssr: true },
{ src: '~plugins/piwik', ssr: false },
{ src: '~plugins/filter', ssr: true },
{ src: '~plugins/i18n', ssr: true }
],
css: [
// 項(xiàng)目中的 CSS 文件
'bootstrap/dist/css/bootstrap.min.css',
'~/assets/fontsize/iconfont.css',
'~/assets/theme/index.css',
'~/assets/style/common.css',
'~/assets/style/global.css',
'~/assets/style/home.css',
'~/assets/style/transform.css'
],
modules: [
'@nuxtjs/axios'
],
router: {
middleware: ['startLoading', 'i18n']
},
axios: {
baseURL: NLE.BASE_API_URL, // 配置接口地址
browserBaseURL: NLE.BASE_API_URL,
credentials: false,
init (axios, ctx) {
axios.defaults.timeout = 60000 // 響應(yīng)時(shí)間
axios.defaults.responseType = 'json'
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8' // 配置請求頭
},
requestInterceptor (config, {store}) {
// console.log(process.env.SERVER, 111)
process.client && store.commit('onLoading', true)
config.headers.versions = 'multi-specification'
config.headers.language = store.state.locale
const token = store.state.login_state
if (token) config.headers.Authorization = token
return config
},
responseInterceptor (response, {store}) {
process.client && store.commit('onLoading', false)
return response
},
errorHandler (error, {redirect, store}) {
process.client && store.commit('onLoading', false)
const status = error.response.status
if (status === 401 || status === 402) redirect('/login')
return error
},
redirectError: {
401: '/login'
}
},
/*
** Build configuration
*/
build: {
plugins: [
new webpack.optimize.MinChunkSizePlugin({minChunkSize: 15000})
],
vendor: ['element-ui', 'localStorage', 'vue-i18n'],
/*
** Run ESLint on save
*/
extend (config, ctx) {
if (ctx.dev && ctx.isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
}
}
- head用于配置應(yīng)用的的title和meta等信息
- 在頁面切換的時(shí)候,Nuxt.js 使用內(nèi)置的加載組件顯示加載進(jìn)度條遭商。通過Loading我們可以進(jìn)行定置,這里我們指定了顏色.
- plugins指定了實(shí)例化之前運(yùn)行的Vue.js插件,首先配置路徑,然后用 ssr: false 變量來配置插件只從客戶端還是服務(wù)端運(yùn)行.
- css指定了全局css文件和第三方css庫
- router可用于覆蓋 Nuxt.js 默認(rèn)的 vue-router 配置,我們這里主要是給每個(gè)頁面設(shè)置默認(rèn)中間件.
- axios配置可以單獨(dú)抽成一個(gè)文件.這里我們設(shè)置了如響應(yīng)時(shí)間 返回格式 請求格式 baseUrl之類的基本參數(shù),然后配置了請求攔截 返回?cái)r截 錯(cuò)誤處理,主要是對 loading的管理 token的攜帶 語言包配置在請求頭里,以及特定狀態(tài)碼的重定向等等.
- build是自定義webpack配置,會(huì)覆蓋掉nuxt.js默認(rèn)的.這里用MinChunkSizePlugin指定了chunk 體積,vendor提取出了會(huì)被頻繁使用的公共庫.extend這里主要是通過一些參數(shù),使得在開發(fā)環(huán)境&客戶端環(huán)境下使用了eslint代碼檢查.
- 此外還有一些配置項(xiàng),比較實(shí)用的是env用來定義環(huán)境變量 dev配置開發(fā)還是生產(chǎn)模式,這樣我們的axios就不用手動(dòng)改baseUrl了.
單個(gè)頁面分析
(未完待續(xù))