记一次VUE的SSR体验 --小米原创
开始
nuxtjs是vue官网推荐的SSR框架
vue init nuxt-community/starter-template
目录结构
Nuxt.js 的应用目录架构提供了良好的代码分层结构,适用于开发或大或小的应用。 当然,你也可以根据自己的偏好组织应用代码。
目录
资源目录
资源目录 assets 用于组织未编译的静态资源如 LESS、SASS 或 JavaScript。
组件目录
组件目录 components 用于组织应用的 Vue.js 组件。Nuxt.js 不会扩展增强该目录下 Vue.js 组件,即这些组件不会像页面组件那样有 asyncData 方法的特性。
布局目录
布局目录 layouts 用于组织应用的布局组件。
该目录名为Nuxt.js保留的,不可更改。
中间件目录
middleware 目录用于存放应用的中间件。
页面目录
页面目录 pages 用于组织应用的路由及视图。Nuxt.js 框架读取该目录下所有的 .vue 文件并自动生成对应的路由配置。
该目录名为Nuxt.js保留的,不可更改。
插件目录
插件目录 plugins 用于组织那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件。
静态文件目录
静态文件目录 static 用于存放应用的静态文件,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。 服务器启动的时候,该目录下的文件会映射至应用的根路径 / 下。
举个例子: /static/robots.txt 映射至 /robots.txt
该目录名为Nuxt.js保留的,不可更改。
Store 目录
store 目录用于组织应用的 Vuex 状态树 文件。 Nuxt.js 框架集成了 Vuex 状态树 的相关功能配置,在 store 目录下创建一个 index.js 文件可激活这些配置。
该目录名为Nuxt.js保留的,不可更改。
nuxt.config.js 文件
nuxt.config.js 文件用于组织Nuxt.js 应用的个性化配置,以便覆盖默认配置。
该文件名为Nuxt.js保留的,不可更改。
package.json 文件
package.json 文件用于描述应用的依赖关系和对外暴露的脚本接口。
该文件名为Nuxt.js保留的,不可更改。
别名
别名 | 目录 |
---|---|
~ | / |
~assets | /assets |
~components | /components |
~pages | /pages |
~plugins | /plugins |
~static | /static |
~store | /store |
## 文件别名: |
|别名| 使用方法| 描述|
|:-:|:-:|
|store| const store = require(‘store’)| 导入 vuex 状态树实例。|
|router| const router = require(‘router’) |导入 vue-router 实例。|
配置
Nuxt.js 默认的配置涵盖了大部分使用情形,可通过 nuxt.config.js 来覆盖默认的配置。
build
Nuxt.js 允许你在自动生成的 vendor.bundle.js 文件中添加一些模块,以减少应用 bundle 的体积。如果你的应用依赖第三方模块,这个配置项是十分实用的。
cache
该配置项让你开启组件缓存策略以提升渲染性能。
css
该配置项用于定义应用的全局(所有页面均需引用的)样式文件、模块或第三方库。
dev
该配置项用于配置 Nuxt.js 应用是开发还是生产模式。
env
该配置项用于定义应用客户端和服务端的环境变量。
generate
该配置项用于定义每个动态路由的参数,Nuxt.js 依据这些路由配置生成对应目录结构的静态文件。
head
该配置项用于配置应用默认的meta标签。
loading
该配置项用于个性化定制 Nuxt.js 使用的加载组件。
plugins
该配置项用于配置那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件。
rootDir
该配置项用于配置 Nuxt.js 应用的根目录。
router
该配置项可用于覆盖 Nuxt.js 默认的 vue-router 配置。
srcDir
该配置项用于配置应用的源码目录路径。
transition
该配置项用于个性化配置应用过渡效果属性的默认值。
路由
Nuxt.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置。
基础路由
假设 pages 的目录结构如下:
pages/
--| user/
-----| index.vue
-----| one.vue
--| index.vue
那么,Nuxt.js 自动生成的路由配置如下:
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'user',
path: '/user',
component: 'pages/user/index.vue'
},
{
name: 'user-one',
path: '/user/one',
component: 'pages/user/one.vue'
}
]
}
动态路由
在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录。
以下目录结构:
pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue
Nuxt.js 生成对应的路由配置表为:
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'users-id',
path: '/users/:id?',
component: 'pages/users/_id.vue'
},
{
name: 'slug',
path: '/:slug',
component: 'pages/_slug/index.vue'
},
{
name: 'slug-comments',
path: '/:slug/comments',
component: 'pages/_slug/comments.vue'
}
]
}
你会发现名称为 users-id 的路由路径带有 :id? 参数,表示该路由是可选的。如果你想将它设置为必选的路由,需要在 users/_id 目录内创建一个 index.vue 文件。
路由参数校验
Nuxt.js 可以让你在动态路由组件中定义参数校验方法。
举个例子: pages/users/_id.vue
export default {
validate ({ params }) {
// Must be a number
return /^\d+$/.test(params.id)
}
}
如果校验方法返回的值不为 true, Nuxt.js 将自动加载显示 404 错误页面。
嵌套路由
你可以通过 vue-router 的子路由创建 Nuxt.js 应用的嵌套路由。
创建内嵌子路由,你需要添加一个 Vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件。
别忘了在父级 Vue 文件内增加
假设文件结构如:
pages/
--| users/
-----| _id.vue
-----| index.vue
--| users.vue
Nuxt.js 自动生成的路由配置如下:
router: {
routes: [
{
path: '/users',
component: 'pages/users.vue',
children: [
{
path: '',
component: 'pages/users/index.vue',
name: 'users'
},
{
path: ':id',
component: 'pages/users/_id.vue',
name: 'users-id'
}
]
}
]
}
动态嵌套路由
这个应用场景比较少见,但是 Nuxt.js 仍然支持:在动态路由下配置动态子路由。
假设文件结构如下:
pages/
--| _category/
-----| _subCategory/
--------| _id.vue
--------| index.vue
-----| _subCategory.vue
-----| index.vue
--| _category.vue
--| index.vue
Nuxt.js 自动生成的路由配置如下:
router: {
routes: [
{
path: '/',
component: 'pages/index.vue',
name: 'index'
},
{
path: '/:category',
component: 'pages/_category.vue',
children: [
{
path: '',
component: 'pages/_category/index.vue',
name: 'category'
},
{
path: ':subCategory',
component: 'pages/_category/_subCategory.vue',
children: [
{
path: '',
component: 'pages/_category/_subCategory/index.vue',
name: 'category-subCategory'
},
{
path: ':id',
component: 'pages/_category/_subCategory/_id.vue',
name: 'category-subCategory-id'
}
]
}
]
}
]
}
过渡动效
Nuxt.js 使用 Vue.js 的
全局过渡动效设置
Nuxt.js 默认使用的过渡效果名称为 page
如果想让每一个页面的切换都有淡出 (fade) 效果,我们需要创建一个所有路由共用的 CSS 文件。所以我们可以在 assets/ 目录下创建这个文件:
在全局样式文件 assets/main.css 里添加一下样式:
.page-enter-active, .page-leave-active {
transition: opacity .5s;
}
.page-enter, .page-leave-active {
opacity: 0;
}
然后添加到 nuxt.config.js 文件中:
module.exports = {
css: [
'assets/main.css'
]
}
页面过渡动效设置
如果想给某个页面自定义过渡特效的话,只要在该页面组件中配置 transition 字段即可:
在全局样式 assets/main.css 中添加一下内容:
.test-enter-active, .test-leave-active {
transition: opacity .5s;
}
.test-enter, .test-leave-active {
opacity: 0;
}
然后我们将页面组件中的 transition 属性的值设置为 test 即可:
export default {
transition: 'test'
}
中间件
中间件允许您定义一个自定义函数运行在一个页面或一组页面渲染之前。
每一个中间件应放置在 middleware/ 目录。文件名的名称将成为中间件名称(middleware/auth.js将成为 auth 中间件)。
一个中间件接收 context 作为第一个参数:
export default function (context) {
context.userAgent = context.isServer ? context.req.headers['user-agent'] : navigator.userAgent
}
中间件执行流程顺序:
- nuxt.config.js
- 匹配布局
- 匹配页面
- 中间件可以异步执行,只需要返回一个 Promise 或使用第2个 callback 作为第一个参数:
import axios from 'axios'
export default function ({ route }) {
return axios.post('http://my-stats-api.com', {
url: route.fullPath
})
}
然后在你的 nuxt.config.js 、 layouts 或者 pages 中使用中间件:
module.exports = {
router: {
middleware: 'stats'
}
}
stats 中间件将在每个路由改变时被调用。
视图
本章节的内容阐述了如何在 Nuxt.js 应用中为指定的路由配置数据和视图,包括应用模板、页面、布局和HTML头部等内容。
模板
你可以定制化 Nuxt.js 默认的应用模板。
定制化默认的 html 模板,只需要在应用根目录下创建一个 app.html 的文件。
默认模板为:
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>
举个例子,你可以修改模板添加 IE 的条件表达式:
<!DOCTYPE html>
<!--[if IE 9]><html lang="en-US" class="lt-ie9 ie9" {{ HTML_ATTRS }}><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html {{ HTML_ATTRS }}><!--<![endif]-->
<head>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>
布局
Nuxt.js 允许你扩展默认的布局,或在 layout 目录下创建自定义的布局。
默认布局
可通过添加 layouts/default.vue 文件来扩展应用的默认布局。
别忘了在布局文件中添加
默认布局的源码如下:
<template>
<nuxt/>
</template>
错误页面
你可以通过编辑 layouts/error.vue 文件来定制化错误页面.
这个布局文件不需要包含
默认的错误页面源码在 这里.
举一个个性化错误页面的例子 layouts/error.vue:
<template>
<div class="container">
<h1 v-if="error.statusCode === 404">页面不存在</h1>
<h1 v-else>应用发生错误异常</h1>
<nuxt-link to="/">首 页</nuxt-link>
</div>
</template>
<script>
export default {
props: ['error'],
layout: 'blog' // 你可以为错误页面指定自定义的布局
}
</script>
个性化布局
layouts 根目录下的所有文件都属于个性化布局文件,可以在页面组件中利用 layout 属性来引用。
请确保在布局文件里面增加 <nuxt/>
组件用于显示页面非布局内容。
举个例子 layouts/blog.vue:
<template>
<div>
<div>这里是博客导航</div>
<nuxt/>
</div>
</template>
在 pages/posts.vue 里, 可以指定页面组件使用 blog 布局。
<script>
export default {
layout: 'blog'
}
</script>
页面
页面组件实际上是 Vue 组件,只不过 Nuxt.js 为这些组件添加了一些特殊的配置项(对应 Nuxt.js 提供的功能特性)以便你能快速开发通用应用。
<template>
<h1 class="red">Hello {{ name }}!</h1>
</template>
<script>
export default {
asyncData (context) {
// called every time before loading the component
return { name: 'World' }
},
fetch () {
// The fetch method is used to fill the store before rendering the page
},
head () {
// Set Meta Tags for this Page
},
// and more functionality to discover
...
}
</script>
<style>
.red {
color: red;
}
</style>
Nuxt.js 为页面提供的特殊配置项:
属性名 | 描述 |
---|---|
asyncData | 最重要的一个键, 支持 异步数据处理,另外该方法的第一个参数为当前页面组件的 上下文对象。 |
fetch | 与 asyncData 方法类似,用于在渲染页面之前获取数据填充应用的状态树(store)。不同的是 fetch 方法不会设置组件的数据。 |
head | 配置当前页面的 Meta 标签, 详情参考 页面头部配置API。 |
layout | 指定当前页面使用的布局(layouts 根目录下的布局文件)。 |
transition | 指定页面切换的过渡动效, 详情请参考 页面过渡动效。 |
scrollToTop | 布尔值,默认: false。 用于判定渲染页面前是否需要将当前页面滚动至顶部。这个配置用于 嵌套路由的应用场景。 |
validate | 校验方法用于校验 动态路由的参数。 |
middleware | 指定页面的中间件,中间件会在页面渲染之前被调用 |
HTML 头部
Nuxt.js 使用了 vue-meta 更新应用的 头部标签(Head) and html 属性。
Nuxt.js 使用以下参数配置 vue-meta:
{
keyName: 'head', // 设置 meta 信息的组件对象的字段,vue-meta 会根据这 key 值获取 meta 信息
attribute: 'n-head', // vue-meta 在监听标签时所添加的属性名
ssrAttribute: 'n-head-ssr', // 让 vue-meta 获知 meta 信息已完成服务端渲染的属性名
tagIDKeyName: 'hid' // 让 vue-meta 用来决定是否覆盖还是追加 tag 的属性名
}
默认 Meta 标签
Nuxt.js 允许你在 nuxt.config.js 里定义应用所需的所有默认 meta 标签,在 head 字段里配置就可以了:
一个使用自定义 viewport 和 谷歌字体 的配置示例:
head: {
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
],
link: [
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Roboto' }
]
}
个性化特定页面的 Meta 标签
注意:为了避免子组件中的meta标签不能正确覆盖父组件中相同的标签而产生重复的现象,建议利用 hid 键为meta标签配一个唯一的标识编号。请阅读关于 vue-meta 的更多信息。
资源文件
资源文件
默认情况下 Nuxt
使用 vue-loader
、file-loader
以及 url-loader
这几个 Webpack
加载器来处理文件的加载和引用。对于不需要通过 Webpack
处理的静态资源文件,可以放置在 static
目录中。
Webpack 构建
默认情况下, vue-loader自动使用 css-loader
和Vue模板编译器来编译处理vue文件中的样式和模板。在此编译过程中,所有的资源URL例如 <img src="...">
、 background: url(...)
和 CSS中的 @import
均会被解析成模块通过 require
引用。
举个例子, 假设我们有以下文件目录结构:
-| assets/
----| image.png
-| pages/
----| index.vue
如果我们在CSS代码中使用 url('~assets/image.png')
, 那么编译后它将被转换成 require('~assets/image.png')
。
又或者如果我们在 pages/index.vue
中使用以下代码引用图片资源:
<template>
<img src="~assets/image.png">
</template>
那么编译后会被转换成:
createElement('img', { attrs: { src: require('~assets/image.png') }})
.png
并非 JavaScript
文件, 因此 Nuxt.js
通过配置Webpack
使用file-loader
和 url-loader
这两个加载器来处理此类引用。
这样做的好处有:
file-loader
能让你指定从什么地方拷贝资源文件以及发布后放到哪个目录去,并能让你使用版本哈希码来重命名发布后的文件来实现增量更新和更好的缓存策略。url-loader
能根据你指定的文件大小阈值,来判断一个文件是转换成内联的base-64码(如果该文件尺寸小于该阈值)还是使用file-loader来降级处理。小文件base-64化能有效减少HTTP请求数。
实际上, Nuxt.js 默认的加载器配置如下:
[
{
test: /\.(png|jpe?g|gif|svg)$/,
loader: 'url-loader',
query: {
limit: 1000, // 1KB
name: 'img/[name].[hash:7].[ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
query: {
limit: 1000, // 1 KB
name: 'fonts/[name].[hash:7].[ext]'
}
}
]
也即文件(图片或字体)的尺寸小于1K的时候,它将会被转换成 Base-64 data URL 来内联引用;否则它将被拷贝至指定的子目录(在 .nuxt 目录下),并被重命名(加上7位的哈希码作为版本标识)以实现更好的缓存策略。
当用 nuxt 命令运行我们的应用时,pages/index.vue 中的模板代码:
<template>
<img src="~assets/image.png">
</template>
将被编译生成:
<img src="/_nuxt/img/image.0c61159.png">
静态文件
如果你的静态资源文件需要 Webpack 做构建编译处理,可以放到 assets
目录,否则可以放到 static
目录中去。
Nuxt 服务器启动的时候,该目录下的文件会映射至应用的根路径 /
下,像 robots.txt
或 sitemap.xml
这种类型的文件就很适合放到 static
目录中。
你可以在代码中使用根路径 /
结合资源相对路径来引用静态资源:
<!-- 引用 static 目录下的图片 -->
<img src="/my-image.png"/>
<!-- 引用 assets 目录下经过 webpack 构建处理后的图片 -->
<img src="/assets/my-image-2.png"/>
为完待续—-