1、Nuxt.js 简介
主要用来做 SSR(服务端渲染),vue 的 SPA(单页应用)对搜索引擎是不友好的(抓取不了),如 新闻类网站主要流量来自搜索引擎
1.1 SSR
在服务器端把 .vue
文件渲染成html
返回给浏览器
**优点:**对 SEO 支持非常友好、比 SPA 渲染速度更快
1.2 特点
- 基于 Vue.js
- 自动代码分层
- 服务端渲染
- 强大的路由功能,支持异步数据
- 静态文件服务
- ES6/ES7 语法支持
- 打包和压缩 JS 和 CSS
- HTML头部标签管理
- 本地开发支持热加载
- 集成ESLint
- 支持各种样式预处理器: SASS、LESS、 Stylus等等
1.3 与 thymeleafe
、vue.js
的区别
- thymeleafe
这种属于模版引擎类型,也就是把数据跟html结合到一起。展示给用户看。可以在服务端完成渲染,也可以在客户端通过JS完成异步渲染
优点:有利于SEO,源码也很规整
缺点:不能前后端分离开发,前端人员写好了样式和html,交给后台开发人员整合到web中
- vue.js
优点:客户端渲染,减少服务端的性能开支;可以前后端分离开发,相互独立,提高开发效率和有利于项目管理。数据双向绑定,渲染非常方便
缺点:不利于SEO 应用场景:一般开发管理后台
- nuxt.js
基于vue.js的一个框架。它支持服务端渲染,也支持客户端渲染。这样子就解决了vue.js的seo问题了
优点:有利于seo优化,可以前后端分离开发
缺点:需要占用较多的服务端资源,高并发访问。优化思路:可以做一些缓存处理,或者可以根据用户登录状态判断,没登录的服务端渲染,登录了客户端渲染,可以减轻一下服务器的负担
2、使用
2.1 安装
# npx(npx 在 NPM 版本 5.2.0 默认安装了)
npx create-nuxt-app <项目名>
2.2 目录结构
├─nuxt-demo
| ├─.editorconfig -- 默认规则
| ├─.gitignore
| ├─.prettierrc
| ├─nuxt.config.js -- 组织 Nuxt.js 应用的个性化配置
| ├─package.json
| ├─README.md
| ├─store -- 状态管理
| ├─static -- 静态资源(打包后是原样)
| ├─plugins -- 插件目录
| ├─pages -- 页面目录
| ├─middleware -- 中间件目录
| ├─layouts -- 布局目录
| ├─components -- 组件目录
| ├─assets -- 资源目录
2.3 配置
2.3.1 配置主机端口
// package.json
"config": {
"nuxt": {
"host": "127.0.0.1", // 可以选自己的域名
"port": "8080" // 改端口
}
}
2.3.2 配置全局css
// nuxt.config.js
module.exports = {
css: [
// 项目里要用的 CSS 文件
'@/assets/css/main.css',
// 项目里要使用的 SCSS 文件
'@/assets/css/main.scss'
]
}
2.3.3 webpack 配置
在 build
选项中进行相关配置
// nuxt.config.js
module.exports = {
build: {
}
}
2.3.4 配置代理
-
安装
npm install @nuxtjs/proxy
-
nuxt.config.js
中添加配置:modules: [ '@nuxtjs/proxy' ], axios: { proxy: true, // 开启 axios 代理 prefix: '/union', // 请求前缀 credentials: true // 开启验证 }, proxy: { '/union': { target: 'https://api.sunofbeach.net/shop', pathRewrite: { changeOrigin: true, // target 是域名,需设置为true '^/union': '' } } }
2.4 路由
依据*pages
目录结构自动生成 vue-router
模块的路由配置
2.4.1 基础路由
假设 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'
}
]
}
2.4.2 nuxt-link 标签
与 router-link 使用方法一致,最终渲染成 a
标签
<template>
<nuxt-link to="/">首页</nuxt-link>
<nuxt-link to="/about">关于</nuxt-link>
<nuxt-link to="/news">新闻</nuxt-link>
</template>
2.4.3 动态路由传参
带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录
pages/
--| news/
-----| _id.vue
--| index.vue
# 路由配置表
{
name: 'news-id',
path: '/news/:id?',
component: 'pages/news/_id.vue'
}
query
拿参(参数名可以改)
// index.vue
<li><nuxt-link :to="{path: '/news', query: {newsId: 3306}}">新闻</nuxt-link></li>
// news/_id.vue
<p>newsId:{
{
$route.query.newsId }}</p>
params
拿参
// index.vue
<li><nuxt-link :to="'/news/lisi'">新闻</nuxt-link></li>
// news/_id.vue
<p>newsId:{
{
$route.params.id }}</p>
2.4.4 路由过渡动效
Nuxt.js 默认使用的过渡效果名称为 page
, 在全局样式文件 assets/main.css
里添加一下样式:
.page-enter-active,
.page-leave-active {
transition: opacity 0.5s;
}
.page-enter,
.page-leave-active {
opacity: 0;
}
某个页面过渡:
// main.css
.test-enter-active,
.test-leave-active {
transition: opacity 0.5s;
}
.test-enter,
.test-leave-active {
opacity: 0;
}
// 需要过渡效果的页面配置
export default {
transition: 'test'
}
2.5 视图
2.5.1 定制模板
定制化默认的 html 模板
- 在 src 文件夹下(默认是应用根目录)创建一个
app.html
的文件 - 或者在
.nuxt/views/app.template.html
配置
注意:需要重启服务器
<!-- {
{ HEAD }} 是 nuxt.config.js 配好的头部 -->
<!DOCTYPE html>
<html>
<head>
{
{ HEAD }}
</head>
<body>
<p>这是一个寂寞的天 -- app.html</p>
{
{ APP }}
</body>
</html>
2.5.2 定制布局
默认布局
通过编辑 layouts/default.vue
文件来扩展应用的默认布局,一般都是定制默认布局
<!-- layouts/default.vue -->
<template>
<p>这是一个寂寞的天 -- layouts</p>
<nuxt /> <!-- 被默认布局包裹的组件 -->
</template>
<script>
// 渲染结果:
/*
* 这是一个寂寞的天 -- app.html -> 定制模板
* 这是一个寂寞的天 -- layouts -> 默认布局
* 路由出口
*/
</script>
自定义布局
可以覆盖默认布局,但不能覆盖定制模板
<!-- layouts/blog.vue -->
<template>
<div>
<div>我的博客导航栏在这里</div>
<nuxt />
</div>
</template>
// pages/blog.vue
export default {
layout: 'blog',
// 或
layout(context) {
return 'blog'
}
}
pages/blog.vue
会使用 layouts/blog.vue
作为当前页面组件的布局文件
2.5.3 定制错误页
通过添加layouts/error.vue
文件来扩展应用的错误请求页
<!-- layouts/error.vue -->
<template>
<div class="container">
<h1>应用发生错误异常</h1>
<nuxt-link to="/">首 页</nuxt-link>
</div>
</template>
2.6 资源文件
2.6.1 Webpack 构建
所有的资源 URL 例如 img src="...">
background: url(...)
和 CSS 中的 @import
均会被解析成模块通过 require
引用
目录结构:
-| assets/
----| image.png
-| pages/
----| index.vue
使用 url('~assets/image.png')
, 那么编译后它将被转换成 require('~/assets/image.png')
2.6.2 静态文件
- 静态资源文件需要 Webpack 做构建编译处理,可以放到
assets
目录 - 不需要 Webpack 做构建可以放到
static
目录中去
<!-- 引用 static 目录下的图片 -->
<img src="/my-image.png" />
<!-- 引用 assets 目录下经过 webpack 构建处理后的图片 -->
<img src="~/assets/my-image-2.png" />
2.7 Vuex 状态树
自动找到 src 目录下的 store 目录引用 vuex
Nuxt.js 支持两种使用 store
的方式 :
- 模块方式:
store
目录下的每个.js
文件会被转换成为状态树指定命名的子模块 (当然,index
是根模块)
// store/state.js
export default () => ({
counter: 0
})
// store/mutations.js
export default {
increment(state) {
state.counter++
}
}
- Classic(不建议使用):
store/index.js
返回创建Vuex.Store
实例的方法
// store/index.js
export const state = () => ({
counter: 0
})
export const mutations = {
increment(state) {
state.counter++
}
}
// store/todos.js 模块文件(moudles)
export const state = () => ({
list: []
})
// todo.vue 访问 todos.js 里的 state
this.$store.state.todos.list
无论使用那种模式,state
的值应该始终是function
,为了避免返回引用类型,会导致多个实例相互影响
2.8 配置环境变量
-
根目录下新建
env.js
文件// env.js module.exports = { dev: { MODE: 'development', ENV_API: 'http://xxxxxxxx:8000' //测试服务器地址 }, prod: { MODE: 'production', ENV_API: 'https://xxxxxxxxx' // 正式服务器地址 } }
-
nuxt.config.js
配置// nuxt.config.js import env from './env' export default { env: { BASE_URL: env[process.env.MODE].ENV_API } }
-
package.json
修改// package.json "scripts": { "dev": "cross-env MODE=dev nuxt", "build": "cross-env MODE=prod nuxt build", "start": "nuxt start", "generate": "nuxt generate" }
-
使用
console.log(process.env.BASE_URL)
3、API
3.1 validate 动态路由校验
如果校验方法返回的值不为 true
, Nuxt.js 将自动加载显示 404 错误页面
// index.vue
<li><nuxt-link :to="'/news/123'">新闻</nuxt-link></li>
// news/_id.vue
<p>newsId:{
{ $route.params.id }}</p>
export default {
validate({ params }) {
// 参数必须是数字(出现1次或以上)
return /^\d+$/.test(params.id)
}
}
3.2 transition 路由过渡动效
默认值:
{
name: 'page',
mode: 'out-in'
}
指定页面过渡:
export default {
layoutTransition: 'layout'
// or
transition: {
name: 'layout',
mode: 'out-in'
}
}
// 全局 CSS
.layout-enter-active,
.layout-leave-active {
transition: opacity 0.5s;
}
.layout-enter,
.layout-leave-active {
opacity: 0;
}
3.3 asyncData 组件加载前调用
能够在渲染组件之前异步获取数据 ,内部无法使用this
获取组件实例,asyncData
是在组件加载之前被调用
类型: Function
- 第一个参数被设定为当前页面的上下文对象
<li>{
{
project }}</li>
<p>{
{
info }}</p>
export default {
data() {
return {
project: 'default' }
},
asyncData(context) {
return {
project: 'nuxt' }
// return { info: 'about' }
}
}
// nuxt
// about
3.4 layout 布局
export default {
layout: 'blog',
// 或
layout(context) {
return 'blog'
}
}
Nuxt.js 会使用 layouts/blog.vue
作为当前页面组件的布局文件
3.5 head 设置头部
设置当前页面的头部标签 , 可通过 this
关键字来获取组件的数据,可以利用页面组件的数据来设置个性化的 meta
标签
export default {
data() {
return {
title: 'Hello World!'
}
},
head() {
return {
title: this.title,
meta: [
{
hid: 'description',
name: 'description',
content: 'My custom description'
}
]
}
}
}
hid
: 为了避免子组件中的 meta 标签不能正确覆盖父组件中相同的标签而产生重复的现象,建议利用 hid
键为 meta 标签配一个唯一的标识编号
content
: 描述信息
3.6 fetch 组件加载前调用
fetch
方法的第一个参数是页面组件的上下文对象context
, 与asyncData
方法类似,不同的是fetch
不会设置组件的数据
注意:内部无法使用this
获取组件实例,fetch
是在组件初始化之前被调用
获取异步信息
export default {
async fetch({ store, params }) {
let { data } = await axios.get('http://my-api/stars')
store.commit('setStars', data)
}
}
4、安装第三方插件
-
下包
npm i element-ui -S
-
导包
// plugins 目录下新建 element.js import Vue from 'vue' import ElementUI from 'element-ui' Vue.use(ElementUI)
-
配置
// nuxt.config.js css: [ 'element-ui/lib/theme-chalk/index.css' ], plugins: [ { src: '~plugins/element', ssr: true } ],
-
用包
<el-button>默认按钮</el-button> <el-button type="primary">主要按钮</el-button>