Nuxt.js是什么
Nuxt.js是一个基于Vue.js生态的第三方开源服务端渲染应用框架,用于构建同构应用。
为什么要使用Nuxt.js
传统的服务端渲染在用户访问网页地址时,服务端读取模板和数据,并将模板和数据组合成最终的渲染结果发送给客户端,所有操作都在服务端进行,服务端的压力较大,前后端耦合度高,不利于开发和维护,开发成本也较高。每次获取新的页面内容都要刷新页面,用户体验也不好。
而客户端渲染在用户访问网页地址时,首先服务端返回一个空白的HTML页面,然后客户端通过ajax请求数据,数据返回之后再根据数据动态渲染页面。这样就造成客户端的首次渲染会发送多次请求,首屏渲染速度比较慢。客户端渲染访问页面时只会返回空白的HTML页面,需要等到执行js代码之后才会根据请求到的数据渲染页面内容,SEO只会读取根据网页地址返回的HTML字符串内容,因此客户端渲染几乎没有供给SEO提取的内容。
为了解决客户端渲染首屏渲染以及不利于SEO的缺点,现代化的服务端渲染应运而生。
现代化的服务端渲染也称同构渲染,是一种服务端渲染加客户端渲染的组合渲染方式。首屏渲染利用服务端渲染直接返回首屏渲染的内容,来解决首屏渲染速度慢以及不利于SEO的缺点。然后由客户端渲染接管页面交互。
同构渲染的实现可以通过Vue、React等js框架提供的官方解决方案来初步搭建,也可以通过第三方解决方案来实现,如React生态的Next.js,Vue生态的Nuxt.js。
Nuxt.js的特点
Nuxt.js集成了完整的Vue生态,包括Vue-Router、Vuex,并集成了现代化的打包功能,可以做到开箱即用,无需做额外的配置,只需要按照Nuxt.js的约定开发即可。
Nuxt.js用于初始化项目,或者在已有的Node服务端项目中,将Nuxt作为一个中间件继承到Node服务中。也可以在现有的Vue项目中使用,只是需要针对Nuxt.js的规则做一些调整。
Nuxt.js项目的创建
- 创建项目文件夹,并肩初始化package.json。
- 安装nuxt.js
- 根据Nuxt.js规则编写代码
- 修改package.json,配置启动命令。scripts中添加"dev": “nuxt”,运行yarn dev会自动编译,进入开发模式。
Nuxt.js 基本规则
路由规则
-
Nuxt会根据pages文件夹下的文件自动生成路由,以文件的路径作为path。
上面的目录结构会生成下面的路由
-
路由导航和Vue-Router的路由导航类似,通过nuxt-link组件或this.$router来实现,nuxt-link的使用和router-link的使用一致。
-
使用动态路由的部分的文件夹或文件需要使用“_”开头,路由参数则通过
this.$route.params
或this.$route.query
来访问。
上面的目录结构会生成下面的路由
-
嵌套路由:创建与父路由页面同名的文件夹,存放子路由页面,父路由页面要添加nuxt-child标签作为子路由出口
上面的目录结构会生成下面的路由
父路由页面示例:<template> <div> <h1>User 父路由</h1> <nuxt-child /> </div> </template> <script> export default { name: 'User' } </script> <style> </style>
-
自定义router配置,创建nuxt的配置文件nuxt.config.js,并在导出对象的router属性中添加router配置选项。在router中配置extendRoutes函数来添加、删除或替换路由。
export default { router: { // base: '/pre/', // 路由前缀 extendRoutes(routes, resolve) { // 清空路由 routes.splice(0) // 配置路由规则 routes.push(...[ { path: '/', component: resolve(__dirname, 'pages/layout'), children: [ { name: 'home', path: '', component: resolve(__dirname, 'pages/home') }, { name: 'login', path: '/login', component: resolve(__dirname, 'pages/login') }, { name: 'register', path: '/register', component: resolve(__dirname, 'pages/login') }, { name: 'profile', path: '/profile/:username', component: resolve(__dirname, 'pages/profile') }, { name: 'settings', path: '/settings', component: resolve(__dirname, 'pages/settings') }, { name: 'editor', path: '/editor', component: resolve(__dirname, 'pages/editor') }, { name: 'dynamic-editor', path: '/editor/:slug', component: resolve(__dirname, 'pages/editor') }, { name: 'article', path: '/article/:slug', component: resolve(__dirname, 'pages/article') } ] } ]) } }, // 服务信息配置 server: { host: '0.0.0.0', //服务启动ip,0.0.0.0表示服务器的所有ip地址 port: '3000' }, // 注册插件 plugins: [ '~/plugins/request.js', '~/plugins/dayjs.js' ] }
视图规则
-
视图模板
定制默认的html模板,服务端渲染的内容,在根目录下创建app.html文件。Nuxt.js会将其视作html模板。可以在模板中引入第三方的库。{{ APP }}
是固定的页面渲染入口,其他选项则可以在配置文件中配置。<!DOCTYPE html> <html {{ HTML_ATTRS }}> <head {{ HEAD_ATTRS }}> {{ HEAD }} <link href="https://cdn.jsdelivr.net/npm/ionicons@2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css"> <link href="//fonts.googleapis.com/css?family=Titillium+Web:700|Source+Serif+Pro:400,700|Merriweather+Sans:400,700|Source+Sans+Pro:400,300,600,700,300italic,400italic,600italic,700italic" rel="stylesheet" type="text/css"> <!-- Import the custom Bootstrap 4 theme from our hosted CDN --> <link rel="stylesheet" href="/main.css"> </head> <body {{ BODY_ATTRS }}> {{ APP }} </body> </html>
-
布局
与父路由类似,nuxt会自动读取根目录下layouts文件夹内的文件作为布局,文件名作为布局名,默认布局为default.vue。layouts下的文件必须具有nuxt标签作为子页面的出口。<template> <div> <h1>layouts/default.vue</h1> <nuxt /> </div> </template> <script> export default { name: 'defaultLayout' } </script> <style> </style>
果要使用布局,在具体页面添加layout属性,指定要添加的布局名称,即文件名,如果没有指定,会默认使用default.vue作为布局。
异步数据规则
-
Nuxt.js扩展了Vue.js,增加了asyncData方法,使我们可以在设置数据之前异步获取或处理数据,比如动态获取SEO的关键字。
-
asyncData方法会在服务端渲染期间或客户端路由更新之前被调用。
-
asyncData返回的数据会和data中的数据合并,asyncData中的返回的数据会覆盖data中的数据。aysncData中可以执行异步任务,data中不能。
-
asyncData只能在页面组件中定义和调用,即pages中的页面。
-
asyncData中没有this。
-
asyncData适用于需要获取首屏使用动态数据的场景。
<template> <div class="profile-page"> <div class="user-info"> <div class="container"> <div class="row"> <div class="col-xs-12 col-md-10 offset-md-1"> <img :src="profile.image" class="user-img" /> <h4>{{ profile.username }}</h4> <p> {{ profile.bio }} </p> <nuxt-link v-if="$store.state.user.username === profile.username" class="btn btn-sm btn-outline-secondary action-btn" :to="{ name: 'settings', }" > <i class="ion-gear-a"></i> Edit Profile Settings </nuxt-link> <button v-else class="btn btn-sm btn-outline-secondary action-btn" :disabled="doFollowing" @click="onFollowUser()" > <i class="ion-plus-round"></i> {{ profile.following ? 'Unfollow Eric Simons' : 'Follow Eric Simons'}} </button> </div> </div> </div> </div> </div> </template> <script> import { getProfile, followUser, unfollowUser } from '@/api/profile' import { getArticles } from '@/api/article' import articleList from '@/components/article-list'; export default { name: 'profileIndex', middleware: 'authenticated', components: { articleList }, async asyncData({ params }) { const { data } = await getProfile(params.username) return { profile: data.profile } } } </script>
-
asyncData参数,第一个参数为上下文对象context,包括下列属性:
在nuxt 中使用vuex
-
新建store文件夹,然后新建index.js,nuxt会默认读取index.js中的内容,也不需要手动创建vuex对象
-
在index.js中需要导出state,mutations,actions。其中state应该定义为一个函数,返回数据对象,这是因为服务端渲染期间运行都是同一个实例,为防止数据冲突/污染,state必须是一个函数,这样每次都会返回新的数据对象。
-
nuxtServerInit是一个特殊的 action 方法,会在服务端渲染期间自动调用。并且只会在服务端渲染期间调用。作用是初始化容器数据,传递给客户端使用。
export const actions = { // vuex 在 nuxt 中特有的 action 方法,该方法会在服务端渲染期间自动调用,并且只会在服务端调用 // 方法的作用是初始化容器数据,然后传递给客户端使用 nuxtServerInit({ commit }, { req }) { let user = null if (req.headers.cookie) { // 从 cookie 中获取保存的用户信息,使用cookieParser将cookie字符串转化为对象 const parsed = cookieparser.parse(req.headers.cookie) if (parsed.user) { try { user = JSON.parse(parsed.user) } catch(e) { } } } // 将 user 信息保存到 vuex 中 commit('saveUser', user) } }
nuxt中间件
允许定义一个自定义函数运行在一个页面或一组页面渲染之前
- 每一个中间件应该放置在middleware/目录下,文件名即为中间件名。
- 中间件接受一个context作为第一个参数
- 中间件的执行流程:nuxt.config.js -> 匹配的布局 -> 匹配的页面
- 使用中间件时需要在组件内通过middleware声明使用那些中间件
中间件代码示例:
组件内声明使用的中间件export default function({store, redirect}) { if (!store.state.user) { return redirect('/login') } }
<script> import { getProfile, followUser, unfollowUser } from '@/api/profile' import { getArticles } from '@/api/article' import articleList from '@/components/article-list'; export default { name: 'profileIndex', middleware: 'authenticated', // ['authenticated', 'test'] components: { articleList }, async asyncData({ params }) { const { data } = await getProfile(params.username) return { profile: data.profile } } } </script>
监听路由参数变化
在组件内部通过定义watchQuery,来监听路由参数的变化,然后执行组件方法(asyncData,fetch,validate,layout,…)。
<script>
export default {
name: 'HomeIndex',
middleware: 'authenticated',
data() {
},
components: {
articleList
},
/* 监听参数变化,触发 asyncData */
watchQuery: ['page', 'tag', 'tab'],
async asyncData({ query }) {
const page = Number.parseInt(query.page || 1)
const limit = 10
const tag = query.tag
const tab = query.tab || 'global'
const loadArticles = tab === 'your' ? getFeedArticles : getArticles
const [articlesRes, tagRes] = await Promise.all([
loadArticles({
limit,
offset: (page - 1) * limit,
tag,
}),
getTags()
])
const { articles, articlesCount } = articlesRes.data
const { tags } = tagRes.data
// 为articles的每一项添加 favoriteDisable 属性
articles.forEach(item => {
item.favoriteDisable = false
})
return {
articles,
articlesCount,
page,
limit,
tags,
tag,
tab
}
}
}
</script>
nuxt插件
允许在运行Vue应用程序之前执行js插件。
在Vue组件的生命周期内,只有beforeCreate和created会在客户端和服务端被调用。
- nuxt.js插件一般存放在plugins文件夹下。
- 插件必须在nuxt.config.js的plugins中注册,插件注册时需要通过~/来表示根目录。
- 插件文件默认导出一个函数,函数的第一个参数是上下文对象context,其中包括app,route,query,params,store,req,res等属性。
插件示例代码:
import axios from 'axios';
export const request = axios.create({
baseURL: 'https://conduit.productionready.io'
})
// 通过插件机制获取上下文对象(query,params,req,res,app,store,)
export default ({ store }) => {
// 添加请求拦截器
request.interceptors.request.use(function (config) {
const { user } = store.state
if (user && user.token) {
// 在发送请求之前做些什么
// 注入请求头token
config.headers = {
Authorization: `Token ${user.token}`
}
}
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
}
在nuxt.config.js中注册插件:
export default {
router: {
// base: '/pre/', // 路由前缀
// 服务信息配置
server: {
host: '0.0.0.0', //服务启动ip,0.0.0.0表示服务器的所有ip地址
port: '3000'
},
// 注册插件
plugins: [
'~/plugins/request.js',
'~/plugins/dayjs.js'
]
}
SEO优化配置
nuxt.js允许在nuxt.config.js或组件内部定义head属性或方法,来配置页面标题和meta标签。
在nuxt.config.js中配置head属性,将作用于所有组件。
在组件内部定义head,只在当前组件所在页面生效。
Nuxt.js项目的打包部署
-
首先需要在package.json中配置build和start命令
-
在nuxt.config.js中添加server配置,此处是配置启动服务的主机和端口,0.0.0.0表示所有网卡的主机
-
运行yarn build打包nuxt应用,生成.nuxt文件
-
将.nuxt、static、package.json、package-lock.js/yarn-lock.js、nuxt.config.js上传到服务器指定文件夹下
-
在该文件夹下执行yarn install/npm install命令下载第三方依赖
-
运行yarn start 命令启动应用。yarn start会直接从生成的文件中读取文件,不用重新打包生成文件。
-
改用pm2管理node服务