特性
- 基于 Vue.js
- 自动代码分层
- 服务端渲染
- 强大的路由功能,支持异步数据
- 静态文件服务
- ES2015+ 语法支持
- 打包和压缩 JS 和 CSS
- HTML 头部标签管理
- 本地开发支持热加载
- 集成 ESLint
- 支持各种样式预处理器: SASS、LESS、 Stylus 等等
- 支持 HTTP/2 推送
nuxt渲染流程
- 客户端发起请求;
- 调用
nuxtServerInit
可以将服务端的数据(比如登录信息)传到客户端的store
的action
中; - 接下来调用
middleware
俗称中间件,中间件会在页面渲染之前运行我们定义的函数,比如用于权限控制和校验; - 然后再
validate
执行的时候对客户端携带的参数进行校验; asyncData
在渲染组件前向服务端获取数据,把请求到的数据合并到Vue中的data中;render
页面渲染,内部跳转<nuxt-link>
继续循环执行。
nuxt安装
- 运行create-nuxt-app 确保安装了npx(npx在NPM版本5.2.0默认安装了):
npx create-nuxt-app <项目名>
- 选项
- 目录结构
nuxt-test
├── assest # 存放被webpack打包的静态资源文件
├── components # vue组件,不会像页面组件那样有 asyncData 方法的特性
├── layouts # 布局页
├── middleware # 中间件
├── pages # 页面目录,该目录下所有的 .vue 文件并自动生成对应的路由配置。
├── plugins # 运行应用之前,运行的 Javascript 插件。
├── static # 不会被webpack打包的静态资源
├── store # Vuex 状态树
├── nuxt.config.js # Nuxt.js 应用的个性化配置,以便覆盖默认配置
└── package.json # 用于描述应用的依赖关系和对外暴露的脚本接口
复制代码
- 运行项目
npm run dev
路由
路由生成
pages目录中所有*.vue
文件自动生成应用的路由配置,新建:
- pages/admin.vue 商品管理页
- pages/cart.vue 购物车页
在.nuxt/router.js会生成自动路由
动态路由
以下划线作为前缀的.vue文件或目录会被定义为动态路由,如下面结构:
pages/
--| detail/
----| _id.vue
复制代码
会生成如下路由配置:
{
path: "/detail/:id?",
component:_9c9d895e,
name: "detail-id"
}
复制代码
路由路径带有 :id?
参数,表示该路由是可选的。如果你想将它设置为必选的路由,需要在 detail
目录内创建一个 index.vue
文件。
嵌套路由
创建内嵌子路由,你需要添加一个 .vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件。
文件结构如下:
pages/
--| detail/
----| _id.vue
--| detail.vue
复制代码
生成的路由配置如下:
{
path: '/detail',
component: 'pages/detail.vue',
children: [
{path: ':id?', name: "detail-id"}
]
}
复制代码
别忘了在父组件(.vue文件)
内增加 <nuxt-child/>
用于显示子视图内容
视图
为指定的路由配置数据和视图,包括应用模板、页面、布局和 HTML 头部等内容。
默认布局
- 添加路由导航 layouts/default.vue
<div>
<nuxt-link to="/">首页</nuxt-link>
<nuxt-link to="/admin">管理</nuxt-link>
<nuxt-link to="/cart">购物车</nuxt-link>
<nuxt />
</div>
复制代码
别忘了在布局文件中添加 <nuxt/>
组件用于显示页面的主体内容。
npm run dev
查看
自定义布局
创建空白布局页layouts/blank.vue,用于login.vue
<template>
<div>
<nuxt />
</div>
</template>
复制代码
页面pages/login.vue使用自定义布局:
export default {
layout: 'blank'
}
复制代码
自定义错误页面
创建layouts/error.vue
<template>
<div>
<h1 v-if="error.statusCode === 404">页面不存在</h1>
<h1 v-else>应用发生错误异常</h1>
<p>{{error}}</p>
<nuxt-link to="/">首页</nuxt-link>
</div>
</template>
<script>
export default {
props:['error'],
layout:'blank'
}
</script>
<style lang='scss' scoped>
</style>
复制代码
测试:访问一个不存在的页面
页面
页面组件就是 Vue 组件,在pages存放只不过 Nuxt.js 为这些组件添加了一些特殊的配置项 给首页添加标题和meta等,最重要的一个键, 支持asyncData 异步数据处理,另外该方法的第一个参数为当前页面组件的 上下文对象。
如pages/index.vue,配置头部信息:
export default {
head(){
return{
title:"课程列表",
// vue-meta利用hid确定要更新meta
meta:[
{name:"description",hid:"description",content:"set page meta"}
],
link:[{rel:"favicon",href:"favicon.ico"}]
}
}
}
复制代码
异步数据获取
asyncData
方法使得我们可以在设置组件数据之前异步获取或处理数据。
示例:获取商品数据
接口准备
- 安装依赖
npm i koa koa-router koa-bodyparser -S
- 创建 server/index.js
const Koa = require('koa')
const consola = require('consola')
const { Nuxt, Builder } = require('nuxt')
const app = new Koa()
// Import and Set Nuxt.js options
let config = require('../nuxt.config.js')
config.dev = !(app.env === 'production')
async function start() {
// Instantiate nuxt.js
const nuxt = new Nuxt(config)
const {
host = process.env.HOST || '127.0.0.1',
port = process.env.PORT || 3000
} = nuxt.options.server
// Build in development
if (config.dev) {
const builder = new Builder(nuxt)
await builder.build()
} else {
await nuxt.ready()
}
// 监听所有路由
app.use(ctx => {
ctx.status = 200
ctx.respond = false // Bypass Koa's built-in response handling
ctx.req.ctx = ctx // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash
nuxt.render(ctx.req, ctx.res)
})
app.listen(port, host)
consola.ready({
message: `Server listening on http://${host}:${port}`,
badge: true
})
}
start()
复制代码
- 创建接口文件
// 此文件并非nuxt生成,它为演示项目提供数据服务接口
const Koa = require('koa');
const app = new Koa();
const bodyparser = require("koa-bodyparser");
const router = require("koa-router")({ prefix: "/api" });
// 设置cookie加密秘钥
app.keys = ["some secret", "another secret"];
const goods = [
{ id: 1, text: "衣服", price: 60 },
{ id: 2, text: "鞋子", price: 200 }
];
// 配置路由
// 获取产品列表
// http://localhost:8080/api/goods
router.get("/goods", ctx => {
ctx.body = {
ok: 1,
goods
};
});
// 产品详情
router.get("/detail", ctx => {
ctx.body = {
ok: 1,
data: goods.find(good => good.id == ctx.query.id)
};
});
// 登录
router.post("/login", ctx => {
const user = ctx.request.body;
if (user.username === "jerry" && user.password === "123") {
// 将token存入cookie
const token = 'a mock token';
ctx.cookies.set('token', token);
ctx.body = { ok: 1, token };
} else {
ctx.body = { ok: 0 };
}
});
// 解析post数据并注册路由
app.use(bodyparser());
// 注册路由
app.use(router.routes());
app.listen(8080, () => console.log('api服务已启动'))
复制代码
整合axios
- 安装@nuxt/axios模块:
npm install @nuxtjs/axios -S
- 配置: nuxt.config.js
modules: [
'@nuxtjs/axios'
],
//配置跨域,如果nginx配置了则不需要
axios:{
proxy:true
},
proxy:{
"/api":"http://localhost:8080"
},
复制代码
测试代码:获取商品列表 pages/index.vue
export default {
//由于asyncData方法是在组件 初始化 前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象。
// $axios由nuxtjs/axios模块注入
// $get是axios模块封装的类fetch风格api
async asyncData({$axios,error}){
const {ok,goods} = await $axios.$get("api/goods");
if(ok){
return {goods}
}
error({statusCode:400,message:"数据查询失败"})
}
}
复制代码
中间件
中间件会在一个页面或一组页面渲染之前运行我们定义的函数,常用于权限控制、校验等任务。 示例代码:管理员页面保护,创建middleware/auth.js
// 定义中间件,参数是nuxt提供的上下文对象
export default function({route,redirect,store}){
// 上下文中通过store访问vux在的全局状态
// 通过vux中令牌存在与否判断用户是否登录
if(!store.state.user.token){
redirect("/login?redirect="+route.path);
}
}
复制代码
注册中间件,pages/admin.vue
<script>
export default {
middleware: ['auth']
}
</script>
复制代码
全局注册:将会对所有页面起作用,nuxt.config.js
router: {
middleware: ['auth']
}
复制代码
状态管理 vuex
应用根目录下如果存在 store 目录,Nuxt.js将启用vuex状态树。定义各状态树时具名导出state, mutations, getters, actions即可。导出时state是个函数,其余是对象。
示例代码:用户登录及登录状态保存,创建store/user.js
export const state = () => ({
token:''
});
export const mutations = {
init(state,token){
state.token = token;
}
};
export const getters = {
isLogin(state){
// !! 相当于将state.token直接转换为boolean值
return !!state.token
}
};
export const actions = {
login({commit,getters},u){
//this.$axios由@nuxt/axios模块提供
return this.$axios.$post("api/login",u).then(({token}) => {
if(token){
commit("init",token)
}
return getters.isLogin;
})
}
}
复制代码
登录页面逻辑 pages/login.vue
<template>
<div>
<h2>用户登录</h2>
<input v-model="user.username">
<input type="password" v-model="user.password">
<button @click="onLogin">登录</button>
</div>
</template>
<script>
export default {
layout:'blank',
data(){
return{
user:{
username:"",
password:""
}
}
},
methods:{
onLogin(){
this.$store.dispatch("user/login",this.user).then(ok => {
if(ok){
console.log(this.$route.query.redirect)
const redirect = this.$route.query.redirect || '/';
this.$router.push(redirect);
}
})
}
}
}
</script>
<style lang='scss' scoped>
</style>
复制代码
插件
Nuxt.js会在运行应用之前执行插件函数,需要引入或设置Vue插件、自定义模块和第三方模块时特别有用。
示例代码:添加请求拦截器附加token,创建plugins/interceptor.js
export default function({$axios,store}){
$axios.onRequest(config => {
if(store.state.user.token){
config.headers.Authorization = "Bearer " + store.state.user.token;
}
return config;
})
}
复制代码
注册插件,nuxt.config.js
plugins: ["@/plugins/interceptor"]
复制代码
nuxtServerInit
通过在store的根模块中定义 nuxtServerInit 方法,Nuxt.js 调用它的时候会将页面的上下文对象作 为第2个参数传给它。当我们想将服务端的一些数据传到客户端时,这个方法非常好用。
示例代码:
登陆状态初始化,从服务端cookie中拿token
store/index.js
export const actions = {
nuxtServerInit({commit},{app}){
const token = app.$cookies.get("token");
if(token){
console.log("nuxtServerInit:token:"+token);
commit("user/init",token)
}
}
}
复制代码
- 安装依赖模块: cookie-universal-nuxt
npm i cookie-universal-nuxt -S
配置,nuxt.config.js
modules:["cookie-universal-nuxt]
- nuxtServerInit只能写在store/index.js
- nuxtServerInit仅在服务端执行
发布部署
- 服务端渲染应用部署: 先进行构建,然后在启动nuxt服务
npm run build
npm start
复制代码
生成内容在.nuxt/dist中
- 静态应用部署
Nuxt.js 可依据路由配置将应用静态化,使得我们可以将应用部署至任何一个静态站点主机服务商。
npm run generate
注意渲染和接口服务器都需要处于启动状态
生成内容再dist中