前言:学完 NuxtJs 发现太好用了,完全不需要担心乱七八槽的配置,全部自己生成,很良心有没有♥。项目中一般 React 都是纯手配,可以看成手动挡的车🚗,除了需要完全自己配置而且概念比较多,Vue 好一点,插件都是官方指定比较稳定容易上手👆,没有选择恐惧症😱,尤其是 cli 比较好用,可类比为半自动档的车,但是如果你用完 NuxtJs 会发现完全是自动挡。身为开发者的车手如果技术老成肯定喜欢手动挡去开赛车飙技术,新手肯定是喜欢自动挡滴了 。当你学会了 NuxtJs 框架,恭喜你点亮了全部 Vue 技能。🍾
一、为什么需要服务端渲染
大多数单页面(SPA)应用都只是有一个div
入口标签,如果项目只是内部或固定的人群使用是没问题的,但是如果涉及到新闻等类似面向普通用户的网站,就必须的考虑到 SEO,Nuxt.js 的服务端渲染就是用来解决 Vue 项目的 SEO 问题。虽然 Nuxt.js 也能创建单页面应用。
二、什么是 SSR ( 服务端渲染 )
SSR 即服务器渲染,就是 (Vue) 页面在服务器端渲染完生成 html 文件,浏览器访问时就将 html 文件直接传递给浏览器。因为数据在服务端就渲染完成,所以减少了浏览器的 HTTP 请求,看不见接口更加安全,减少了首屏加载时间。
三、安装
安装 Node 的前提下:
C:\Users\hpzhan\Desktop\test>npx create-nuxt-app app
create-nuxt-app v2.15.0
✨ Generating Nuxt.js project in app
? Project name app_test
? Project description 这是一个关于nuxt.js的项目(默认为(My beautiful Nuxt.js project))
? Author name Condor Hero
? Choose programming language JavaScript
? Choose the package manager Npm
? Choose UI framework Element
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios
? Choose linting tools ESLint
? Choose test framework None
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)
Installing packages with npm
#安装依赖成功
� Successfully created project app_test
To get started:
cd app
npm run dev
To build & start for production:
cd app
npm run build
npm run start
进入项目文件cd app
,通过 npm run dev
来启动项目,然后浏览器输入 http://localhost:3000/,就可以看到结果。
四、Nuxt目录结构详讲
其实每个目录里面都有一个 README.md文件,清楚的告诉大家每个目录的作用。
|-- .nuxt // Nuxt自动生成,临时的用于编辑的文件,build
|-- assets // 用于组织未编译的静态资源入LESS、SASS 或 JavaScript
|-- components //Vue组件放置的地方
|-- layouts // 布局目录,用于组织应用的布局组件,文件名比较固定,不可更改。
|-- middleware // 用于存放中间件,类似路由钩子的作用
|-- pages // 用于存放写的页面,我们主要的工作区域
|-- plugins // 用于存放项目的第三方插件,例如element UI
|-- static // 用于存放静态资源文件,比如图片
|-- store // 用于组织应用的Vuex 状态管理。
|-- .editorconfig // 开发工具编辑器格式配置
|-- .eslintrc.js // ESLint的配置文件,用于检查代码格式
|-- .gitignore // 配置git不上传的文件
|-- nuxt.config.json // 用于组织Nuxt.js应用的个性化配置,已覆盖默认配置,对单页面应用类似是index.html和webpack.config.js两个文件的集成。
|-- package-lock.json // npm自动生成,用于package依赖包固定版本的,yarn也有相同的操作
|-- package.json // npm包管理配置文件
统一代码风格工具——editorConfig
.editorconfig 文件,初次见到他是在用 Vue-cli 的时候,那时候没有深究怎么用的,里面的配置就是简单的看看,这次又见到了,结果用的时候完全不起作用,不起作用学习里面的语法可学不进去,而且官网 www.editorconfig.org 也是简单到看不懂😅。终于捯饬了一下午才发现需要安装插件😔,哎还以为编辑器自动支持。
在平常的项目开发过程中,每个开发者喜欢的编码风格是不一样的,就以缩进为例,有的人喜欢两个空格缩进,有的人喜欢四个空格的缩进,项目团队的人如果少的话,这么做是问题不大的。但是在多人合作开发项目。统一代码风格就显得十分有必要。editorConfig 有部分 ESLint 功能的感觉。
有些编辑器默认支持 editorConfig,如 webstorm;而有些编辑器则需要安装 editorConfig 插件,如 ATOM、Sublime、VS Code 等,在对应的插件市场直接搜索安装就行了。
这时候 .editorconfig
文件就能起作用了,编辑器的行为会与. editorconfig
文件中定义的一致,并且其优先级比编辑器自身的设置要高。
当打开一个文件时,EditorConfig 插件会在打开文件的目录和其每一级父目录查找.editorconfig文件,直到有一个配置文件里面有 root=true
才停止继续向上索引。
EditorConfig 的配置文件是从上往下读取的并且最近的 EditorConfig 配置文件会被最先读取,最近的配置文件中的配置项拥有优先权。如果 .editorconfig
文件没有进行某些配置,则使用编辑器默认的设置。
看看官网 https://editorconfig.org/ 有哪些配置规则:
符号 | 作用 |
---|---|
* | 匹配除/之外的任意字符串 |
** | 匹配任意字符串 |
? | 匹配任意单个字符 |
[name] | 匹配name中的任意一个单一字符 |
[!name] | 匹配不存在name中的任意一个单一字符 |
{s1,s2,s3} | 匹配给定的字符串中的任意一个(用逗号分隔) |
{num1..num2} | 匹配num1到num2之间的任意一个整数, 这里的num1和num2可以为正整数也可以为负整 |
indent_style | 设置缩进风格(tab是硬缩进,space为软缩进) |
indent_size | 用一个整数定义的列数来设置缩进的宽度,如果indent_style为tab,则此属性默认为tab_width |
tab_width | 用一个整数来设置tab缩进的列数。默认是indent_size |
end_of_line | 设置换行符,值为lf、cr和crlf |
charset | 设置编码,值为latin1、utf-8、utf-8-bom、utf-16be和utf-16le,不建议使用utf-8-bom |
trim_trailing_whitespace | 设为true表示会去除换行行首的任意空白字符。 |
insert_final_newline | 设为true表示使文件以一个空白行结尾 |
root | 表示是最顶层的配置文件,发现设为true时,才会停止查找.editorconfig文件 |
注意:所有的属性和值都是忽略大小写的. 解析时它们都是小写的
在来看官网一个例子:
# top-most EditorConfig file
root = true
# 使用Unix-style 换行符,并且每个文件以换行结束
[*]
end_of_line = lf
# CR:Carriage Return,对应ASCII中转义字符\r,表示回车
# LF:Linefeed,对应ASCII中转义字符\n,表示换行
# CRLF:Carriage Return & Linefeed,\r\n,表示回车并换行,windows的记事本换行规则
insert_final_newline = true
# 可以使用通配符匹配多个文件
# 设置默认编码为utf-8
[*.{js,py}]
charset = utf-8
# py文件 4 个空格缩进
[*.py]
indent_style = space
indent_size = 4
# 使用Tab缩进
[Makefile]
indent_style = tab
# lib目录下的js使用2个空格缩进
[lib/**.js]
indent_style = space
indent_size = 2
# 配置 package.json 或 .travis.yml文件 设置其为2个空格缩进
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2
end_of_line
设置设置换行符规则,我们需要只要/r ,/n ,/r/n
的区别和不同:
英文名 | 中文名 | 编辑器缩写 | ASCII |
---|---|---|---|
carriage return | 回车 | CR | \n |
linefeed | 换行 | LF | \r |
carriage return & linefeed | 回车换行 | CRLF | \r\n |
文章中的电传打印机,这种终端不能独立使用,必须连接到一台主机,相当于显示器+键盘。这里有个Teletype Model 37 的演示视频
项目开发的时候,例如
.gitignore
和.editorconfig
等文件一旦配置完成几乎不需要改动,留着就有一点碍眼了,如何在 VSCode 目录栏中隐藏这些文件。
按图改下编辑器字体的大小按 ctrl+s 保存,这会在项目中生成
.vscode
目录,里面有一个 settings.json
文件,输入files.exclude
会感应出出如下 JSON:
{
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true
}
}
需要隐藏的文件类似添加就 OK 了。这时别忘了先在 .gitignore
文件里面忽略.vscode 目录。
package.js文件
{
"name": "condor",
"version": "1.0.0",
"description": "My marvelous Nuxt.js project",
"author": "Condor Hero",
"private": true,
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate"
},
"dependencies": {
"nuxt": "^2.0.0"
},
"devDependencies": {},
"config":{
"nuxt":{
"host":"127.0.0.1",
"port":"8888"
}
}
}
主要是 scripts 字段里面的内容:
指令 | 描述 |
---|---|
nuxt | 开启一个监听3000端口的服务器,同时提供热加载功能 |
nuxt build | 构建整个应用,压缩合并JS和CSS文件(用于生产环境) |
nuxt start | 开启一个生产模式的服务器(必须先运行nuxt build命令) |
nuxt generate | 构建整个应用,并为每一个路由生成一个静态页面(用于静态服务器) |
其中通过 config
字段能解决 ip 占用问题,自定义端口和 IP。
nuxt.config.js
nuxt.config.js 如果对照单页面应用的话,就是综合了 index.html 和 webpack.config.js 两个文件。只捡一些重要的讲:
head 配置竟然还专门有一个网站。😀
很明显 nuxt.js 内置了这个插件,如果想自己安装也是可以的
npm i vue-meta
plugins 配置要结合 plugins 目录使用,主要是为 Vue 插件提供服务的
此处以 element-ui
为例:
npm install -D element-ui
文件根目录 plugins/ 目录下创建相应的插件文件,我创建一个名为 element-ui.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:[
'~/plugins/element-ui'
]
这时已经配置完成,可直接在 page 目录里面直接使用了。常见的插件还有 axios ,如果你不使用官方提供的话。可自行在此处配置,不过一般推荐使用 NuxtJs 官方推荐的 @nuxtjs/axios 插件。
使用 modules 来配置 @nuxtjs/axios
第一步安装:
npm i -D @nuxtjs/axios
第二步在 nuxt.config.js 文件的 modules 字段配置:
modules: [
'@nuxtjs/axios'
]
这时可通过可在组件内直接使用 this.$axios
访问 axios 的实例。如果你需要通过注册拦截器或者改变全局设置来定制化axios, 你需要在 plugins 字段配置,同时在 plugins 目录下创建一个文件,我创建的文件名为 axios.js ,然后在添加到 nuxt.config.js 文件 plugins 字段里面:
plugins: [
'~/plugins/element-ui',
'@/plugins/axios'
]
axios.js 的文件内容为:
export default function (ctx) {
const { $axios, redirect } = ctx;
/* $axios发送ajax两种方式1、$axios.$get 2、$axios.get */
console.log($axios.defaults.headers);
console.log($axios.defaults.baseURL);
console.log($axios.defaults.timeout);
console.log($axios.setToken);
/* 源码提供三个特别有用的函数 */
/* setBaseURL(baseURL) {
this.defaults.baseURL = baseURL
};
setHeader(name, value, scopes = 'common') {
for (let scope of Array.isArray(scopes) ? scopes : [scopes]) {
if (!value) {
delete this.defaults.headers[scope][name];
return
}
this.defaults.headers[scope][name] = value
}
};
setToken(token, type, scopes = 'common') {
const value = !token ? null : (type ? type + ' ' : '') + token
this.setHeader('Authorization', value, scopes)
}; */
// 拦截器主要支持下面几个
/* onRequest(config)
onResponse(response)
onError(err)
onRequestError(err)
onResponseError(err) */
$axios.onRequest(config => {
console.log('Making request to ' + config.url)
});
$axios.onError(error => {
const code = parseInt(error.response && error.response.status)
if (code === 400) {
redirect('/400')
};
});
};
参考:
完整的 nuxt.config.js
export default {
// 'spa': 没有服务器端渲染(只有客户端路由导航等)
// 'universal': 同构应用程序(服务器端呈现+客户端路由导航等)
mode: 'universal',
/*
** Headers of the page
*/
head: {
// 页面标题
title: process.env.npm_package_name || '',
// 页面最终展示的标题,%s就是title的占位符
titleTemplate:"%s - Hero",
// html和body标签增加属性
htmlAttrs: {
lang: 'zh-CN',
amp: true
},
bodyAttrs: {
class: ['dark-mode', 'mobile']
},
// 元数据
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
// 支持模板☞
{ names: 'template', content: 'Condor' , template:chunk=>`${chunk} - Hero`},
// 注意:为了避免子组件中的meta标签不能正确覆盖父组件中相同的标签而产生重复的现象,
// 建议利用 hid 键为meta标签配一个唯一的标识编号。不加hid,子和父相同时,不能覆盖父
{ hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
// 单组件中可调用head方法,自定义此页面的head信息
/* head () {
return {
title: this.title,
meta: [
{ hid: 'description', name: 'description', content: 'My custom description' }
]
}
} */
],
// <link>元素
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
],
// style元素,自动创建style标签
style: [
{ cssText: '.foo { background-color: red }', type: 'text/css' }
],
// script标签
script: [
{ src: '/test.js', async: true, defer: true }
],
},
/*
** Customize the progress-bar color
** 页面最顶端加载进度条
** https://zh.nuxtjs.org/api/configuration-loading/#__layout
*/
loading: { color: 'blue' , height: '5px'},
/*
** Global CSS
** 引入全局的 CSS 文件,之后每个页面都会被引入。
*/
css: [
'@/assets/css/reset.css', //引入assets下的reset.css全局标签重置样式
'element-ui/lib/theme-chalk/index.css'
],
/*
** Plugins to load before mounting the App
** plugins 配置要结合 plugins 目录使用
** @和~都表示从根目录开始索引
*/
plugins: [
'~/plugins/element-ui',
'@/plugins/axios'
],
/*
** Nuxt.js dev-modules
** 构建模块
*/
buildModules: [
// Doc: https://github.com/nuxt-community/eslint-module
// Simple usage
'@nuxtjs/eslint-module',
// With options
// ['@nuxtjs/eslint-module', { /* module options */ }]
],
// Using top level options
eslint: {
/* module options */
},
/*
** Nuxt.js modules
** modules是Nuxt.js扩展,里面放置的是NuxtJs推荐的插件
** 一般命名类似为'@nuxtjs/axios',
*/
modules: [
'@nuxtjs/axios'
],
/*
** Build configuration
** 这个配置项用来配置 Nuxt.js 项目的构建规则,即 Webpack 的构建配置
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {
}
}
}
五、Nuxt 的默认模版(app.html)和默认布局(default.vue)
在开发应用时,经常会用到一些公用的元素,比如网页的标题是一样的,每个页面都是一模一样的标题。这时候我们有两种方法:
- 第一种方法是作一个公用的组件出来,公用组件更加灵活,但是每次都需要自己手动引入。
- 第二种方法是修改默认模版。但是模板每个页面都会引入。
Nuxt 为我们提供了超简单的默认模版订制方法,需要在应用根目录下创建一个 app.html 的文件。在保持 nuxt.config.js 文件里创建的 index.html 不变的情况下,新增内容。记住设置重启下。
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
<h1>每个页面都有我</h1>
{{ APP }}
</body>
</html>
{{xxx}}
里面是读取 index.html 的默认设置。
和默认模板类似的功能还有默认布局,但是从名字上你就可以看出来,默认布局主要针对于页面的统一布局使用。它在位置根目录下的 layouts/default.vue
。需要注意的是在默认布局里不要加入头部信息,只是关于<template>标签下的内容统一订制。
<template>
<div>
<h1>每个页面都有我</h1>
<!-- nuxt标签相当于Vue项目中第一个router-view标签 -->
<nuxt />
</div>
</template>
这厮不需要重启服务器。layouts 目录还有一个重要的文件 error.vue:
当用户输入路由错误的时候,我们需要给他一个明确的指引,所以说在应用程序开发中404页面是必不可少的。Nuxt.js支持直接在默认布局文件夹里建立错误页面。
<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' // 你可以为错误页面指定自定义的布局
/*
layouts 根目录下的所有文件都属于个性化布局文件,可以在页面组件中利用 layout 属性来引用。
*/
}
</script>
代码用 v-if 进行判断错误类型,返回不同的结果,需要注意的是这个错误变量 error 是需要在<script> 里 通过 props 接收的。
六、asyncData 方法获取数据
你可能想要在服务器端获取并渲染数据。Nuxt.js添加了asyncData方法使得你能够在渲染组件之前异步获取数据。asyncData方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,return 的东西和 data 函数的 return 用法类似。
注意:由于asyncData方法是在组件 初始化 前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象。
<template>
<div>
<h1>{{ title }}</h1>
<h2>My name is {{ name }}</h2>
<nuxt-link to="/news">新闻</nuxt-link>
<nuxt-link to="/about">关于</nuxt-link>
<nuxt-link to="/shares">分享</nuxt-link>
</div>
</template>
<script>
export default {
data(){
return {
title:"首页"
}
},
asyncData({isDev, route, store, env, params, query, req, res, redirect, error}) {
// 发送Ajax拿到数据return出去
return { name : "Condor Hero"}
},
}
</script>
七、Vuex 状态树(内置)
Nuxt.js 会尝试找到应用根目录下的 store 目录,引用 vuex 模块,store
目录下的每个 .js
文件会自动被转换成为状态树指定命名的子模块 (当然,index
是根模块),一个简单的加法器。
store/index.js
export default {
namespaced: true,
state:{
count:0
},
mutations:{
ADD(state) {
state.count++;
},
UNADD(state) {
state.count--;
}
}
}
pages/index.vue
<template>
<div>
<h3>
{{$store.state.count}}
</h3>
<el-button @click="$store.commit('ADD')">加加</el-button>
<el-button @click="$store.commit('UNADD')">减减</el-button>
</div>
</template>
除此之外,NuxtJs 还专门提供了一个 fetch 方法,用来修改状态树。
fetch 方法会在渲染页面前被调用,作用是填充状态树 (store) 数据,与 asyncData 方法类似,不同的是它不会设置组件的数据。
八、路由
NuxtJs 的路由和 vue-router 路由的用法和 API 几乎一摸一样。例如路由跳转:
<nuxt-link to="/news">新闻</nuxt-link>
<router-link to="/news">新闻</router-link>
<!-- 虽然不建议这么做,但是router-link在nuxt项目中也能起作用 -->
对了,NuxtJs 路由没有 #,不是单页面应用,而是多页面。但是路由用法啥的没变。
重点来了,NuxtJs 路由表是自动生成。路由的用法没啥好讲的,主要涉及的知识会 Vue-router 就会,罗列下知识点:
- 基础路由(简单的一层路由)
- 动态路由(:id形式)
在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录。 - 路由参数校验
// Nuxt.js 可以让你在动态路由组件中定义参数校验方法。
export default {
validate({ params }) {
// 如果校验方法返回的值不为 true或Promise中resolve 解析为false或抛出Error , Nuxt.js 将自动加载显示 404 错误页面或 500 错误页面。
return true;
}
}
- 嵌套路由(文件嵌套模仿 vue-router 的 children)父组件必须设置
<nuxt-child></nuxt-child>
- 动态嵌套路由
- 过渡动效(vue 的 transition)
- 路由传参:动态路由参数 params和查询路由参数 query
- 中间件(middleware对应 vue-router 的路由钩子)
主要讲下中间件也就是 vue-router 中的路由钩子。
每一个中间件应放置在 middleware/
目录下。文件名的名称将成为中间件名称(middleware/auth.js
将成为 auth
中间件)。路由鉴权的 auth 中间件是最常用的。
auth.js
export default function (ctx) {
const { route , redirect} = ctx;
console.log("☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆");
redirect("/");
console.log("☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆");
};
一个中间件接收 context 作为第一个参数,使用的位置有三个地方可选:
- nuxt.config.js
module.exports = {
router: {
middleware: 'auth'
}
}
- layouts
<script>
export default {
middleware: 'auth'
}
</script>
- pages
<script>
export default {
middleware: 'auth'
}
</script>
参考链接:使用dotenv统一管理node环境变量和配eslint
完。
2020年3月23日19点52分农历二月三十