公司最近要做一个新产品的官网,要求要对SEO友好,首先想到的是做服务端渲染,Vue.js的服务端渲染vue-server-renderer需要node服务.自己搭建是相当复杂的,所以我们使用了Nuxt.js。
Nuxt.js简介:
- Nuxt.js是基于Vue的服务端渲染应用框架.
- 集成了Vue2,Vue-Router,Vue SSR,Vuex,Vue-Meta.
- 可以轻松地通过配置实现服务端渲染或单页模式.
- Nuxt.js根据pages文件目录自动生成路由配置.
- 通过nuxt generate命令依据应用的路由配置将每一个路由编译成为对应的 HTML 文件,实现Vue应用的静态化.
搭建过程:
- 首先用NUXT的脚手架create-nuxt-app生成项目
yarn create nuxt-app <项目名>
执行命令后,会提示你进行一系列的选择,按项目需要进行选择即可
需要注意的是选择Nuxt模式(Universal or SPA),因为我们要做服务端渲染,所以选择Universal,选择之后在nuxt.config.js配置文件中会生成配置mode:'universal'
-
然后会生成这样的一个项目结构(红框的目录是根据需要自己建的)
Nuxt.js的文件打包和页面路由都是依据目录来生成的,所以如果没有额外配置,不要修改目录的名称
-
下面依次介绍下生成的目录:
assets:该目录下可以放css,scss,img,js,打包时会被编译处理
components:放组件,单纯的vue文件,没有Nuxt的扩展,不能执行asyncData之类的
layouts:布局文件,默认有default.vue,如果不配置,pages里的页面文件默认的布局文件都是default.vue,如果想自定义布局文件,需要在layouts中新建布局文件,并在pages的页面文件中进行配置,比如新建error布局文件:
//layouts/error.vue <template> <div> <nuxt /> </div> </template> //pages/404.vue export default { layout:'error' }middleware:中间件目录,中间件文件中允许自定义一个函数在页面渲染之前执行,可以在layouts,pages和nuxt.config.js中配置,中间件接收 context 作为第一个参数,栗子:
//middleware/stats.js export default function (context) { context.userAgent = process.server ? context.req.headers['user-agent'] : navigator.userAgent } //nuxt.config.js router: { middleware: 'stats' }pages:页面目录,Nuxt.js会自动读取该目录下的文件,生成路由配置,比如目录结构是这样的:
pages/ --| mobile/ -----| index.vue -----| one.vue --| index.vueNuxt.js会自动解析成路由:
router: { routes: [ { name: 'index', path: '/', component: 'pages/index.vue' }, { name: 'mobile', path: '/mobile', component: 'pages/mobile/index.vue' }, { name: 'mobile-one', path: '/mobile/one', component: 'pages/mobile/one.vue' } ] }Nuxt.js还支持比较复杂的动态路由,可以参考官方文档进行目录配置.
Nuxt.js还扩展了页面组件,最常用的是asyncData,可以在服务端或者路由更新时执行,支持异步数据处理,其他的还有当前页面的head,middle,切换动画transition配置等等,所有的扩展配置如下,可以根据需要进行页面的个性化配置,覆盖nginx.config.js中的全局配置.

plugins:插件目录,配置完后可以全局调用,ui插件,或者js插件,像element-ui,vue-lazyload,都可以在这里进行配置,配置方法:
//使用swiper插件 //plugins/vue-swiper.js import Vue from 'vue' import VueAwesomeSwiper from 'vue-awesome-swiper' Vue.use(VueAwesomeSwiper) //nuxt.config.js css:['swiper/dist/css/swiper.css'], plugins:[{ src: '~plugins/vue-swiper', ssr: false }] ssr:默认是true,表示在服务端执行,设置false表示该文件只在客户端打包引入static:静态文件目录,不需要webpack构建编译处理的文件,比如在head中引入的icon就放在这个文件下
store:Vuex文件目录,通过新建store/index.js激活Vuex,简单的可以像下面这样使用,如果数据较多,可以把模块分解为单独的文件state.js,actions.js,mutations.js和getters.js,参考官方文档进行配置.
//store/index.js export const state = () => ({ counter: 0 }) export const mutations = { increment (state) { state.counter++ } } export const actions = { getData (state) { //使用@nuxt/axios可以直接调用this.$axios this.$axios.post('/xxx') } } export const getters = {}nuxt.config.js:全局的配置文件,会覆盖nuxt的默认配置
-
项目中用到的nuxt.config.js配置
1.head(对SEO友好的配置,PWA配置)
head: { title: 'xxx', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: 'xxxx' }, { hid: 'keywords', name: 'keywords', content: 'xxx' } ], link: [ { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' }, { rel: 'icon', type: 'image/png', sizes: '96x96', href: '/favicon-96x96.png' }, { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }, { rel: 'icon', type: 'image/png', sizes: '192x192', href: '/android-icon-192x192.png' }, { rel: 'apple-touch-icon', sizes: '57x57', href: '/apple-icon-57x57.png' }, { rel: 'apple-touch-icon', sizes: '60x60', href: '/apple-icon-60x60.png' }, { rel: 'apple-touch-icon', sizes: '72x72', href: '/apple-icon-72x72.png' }, { rel: 'apple-touch-icon', sizes: '76x76', href: '/apple-icon-76x76.png' }, { rel: 'apple-touch-icon', sizes: '114x114', href: '/apple-icon-114x114.png' }, { rel: 'apple-touch-icon', sizes: '120x120', href: '/apple-icon-120x120.png' }, { rel: 'apple-touch-icon', sizes: '144x144', href: '/apple-icon-144x144.png' }, { rel: 'apple-touch-icon', sizes: '152x152', href: '/apple-icon-152x152.png' }, { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-icon-180x180.png' }, { rel: 'manifest', href: '/manifest.json' } ] },现在有些浏览器支持将网页添加到主屏幕,方便用户直接打开.
apple-touch-icon和manifest是配置添加到主屏幕的显示的图标和名称等信息.
app-touch-icon是针对safari浏览器的.mainfest文件格式:
{ "short_name": "短名称", "name": "这是一个完整名称", "icon": [ { "src": "icon.png", "type": "image/png", "sizes": "48x48" } ...//多个不同大小的图标 ], "start_url": "index.html" }2.css
//引入全局的css文件 css: ['~/assets/css/normalize.css'] //如果要使用sass,需要安装node-sass和sass-loader npm install --save-dev node-sass sass-loader css: ['~/assets/css/main.scss'] Nuxt.js会自动识别引入的文件扩展名,webpack会使用相应的预处理器进行解析 需要注意的是如果要写一些scss的变量或函数,就需要在build中进行一些配置3.使用swiper插件
//plugins/vue-swiper.js import Vue from 'vue' import VueAwesomeSwiper from 'vue-awesome-swiper' Vue.use(VueAwesomeSwiper) //nuxt.config.js css:['swiper/dist/css/swiper.css'], plugins:[{ src: '~plugins/vue-swiper', ssr: false }]4.配置404页面
因为项目使用静态部署,所以Nuxt官网上说的方式(在layouts中写error.vue页面),因为并没有在pages中新加页面,构建时并不会生成对应的静态页,所以这种方式并不起作用.可以实现的方法是在plugins中监听路由,如果没有匹配到已经存在的路由,跳转到404页面
//pages/error/index.vue <template> <div> 404了 </div> </template> //plugins/route.js export default ({ app }) => { const {context} = app const {matched} = context.route if(!matched.length) { window.location.href='/error' } } //nuxt.config.js plugins:[{ src: '~plugins/route.js', ssr: false}]5.设置代理
//nuxt.config.js modules: ['@nuxtjs/proxy'], proxy: { '/api': { target: 'https://http://xxx.com', changeOrigin: true } }6.NUXT有自带的axios模块@nuxtjs/axios,如果要用自带的模块,可以这样配置axios和proxy
modules: [ '@nuxtjs/axios', '@nuxtjs/proxy' ], //axios的一些配置 axios: { proxy: true, retry: { retries: 1 }//失败重试次数 }, proxy: { '/api': { target: 'https://xxx.com', changeOrigin: true } } 如果要对axios请求设置统一拦截器,可以在插件中配置 //plugins/axios.js export default function ({ $axios, redirect }) { $axios.setHeader('Content-Type', 'application/json', ['post']) $axios.setToken('123', 'Bearer', ['post']) $axios.onRequest(config => { //设置请求拦截 }) $axios.onError(error => { //设置报错处理 }) window.$axios = $axios //挂载到window上,方便在任何js文件中调用 } //nuxt.config.js plugins:[{src:'~plugins/axios',ssr:false}] //在vue文件中使用 mounted() { this.$axios.post('/xxx') } asyncData({app}) { app.$axios.post('/xxx') } //在store/actions中使用 export const actions = { getData (state) { this.$axios.post('/xxx') } }但是发现@nuxtjs/axios在asyncData中使用执行nuxt generate静态构建时会失败,目前项目中还是使用的是axios,在api目录中封装了axios的调用,像在一般的Vue项目中使用一样
7.自定义服务端口
server: { port: 8000, // default: 3000 }nuxt.config.js还可以进行很多其他的配置,后面再学习补充.