mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-30 09:27:13 +00:00
Middleware feature 🔥
This commit is contained in:
parent
84b8a0de3f
commit
17650c25e0
@ -99,7 +99,7 @@ Learn more: https://nuxtjs.org/api/nuxt
|
|||||||
|
|
||||||
## Using nuxt.js as a middleware
|
## Using nuxt.js as a middleware
|
||||||
|
|
||||||
You might want to use your own server with you configurations, your API and everything awesome your created with. That's why you can use nuxt.js as a middleware. It's recommended to use it at the end of your middlewares since it will handle the rendering of your web application and won't call next().
|
You might want to use your own server with you configurations, your API and everything awesome your created with. That's why you can use nuxt.js as a middleware. It's recommended to use it at the end of your middleware since it will handle the rendering of your web application and won't call next().
|
||||||
|
|
||||||
```js
|
```js
|
||||||
app.use(nuxt.render)
|
app.use(nuxt.render)
|
||||||
|
@ -17,7 +17,7 @@ if (fs.existsSync(nuxtConfigFile)) {
|
|||||||
if (typeof options.rootDir !== 'string') {
|
if (typeof options.rootDir !== 'string') {
|
||||||
options.rootDir = rootDir
|
options.rootDir = rootDir
|
||||||
}
|
}
|
||||||
options.dev = false // Force production mode (no webpack middlewares called)
|
options.dev = false // Force production mode (no webpack middleware called)
|
||||||
|
|
||||||
console.log('[nuxt] Generating...') // eslint-disable-line no-console
|
console.log('[nuxt] Generating...') // eslint-disable-line no-console
|
||||||
var nuxt = new Nuxt(options)
|
var nuxt = new Nuxt(options)
|
||||||
|
@ -14,7 +14,7 @@ if (fs.existsSync(nuxtConfigFile)) {
|
|||||||
if (typeof options.rootDir !== 'string') {
|
if (typeof options.rootDir !== 'string') {
|
||||||
options.rootDir = rootDir
|
options.rootDir = rootDir
|
||||||
}
|
}
|
||||||
options.dev = false // Force production mode (no webpack middlewares called)
|
options.dev = false // Force production mode (no webpack middleware called)
|
||||||
|
|
||||||
var nuxt = new Nuxt(options)
|
var nuxt = new Nuxt(options)
|
||||||
|
|
||||||
|
10
examples/auth-routes/middleware/auth.js
Normal file
10
examples/auth-routes/middleware/auth.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export default function ({ store, redirect, error }) {
|
||||||
|
// If user not connected, redirect to /
|
||||||
|
if (!store.state.authUser) {
|
||||||
|
// return redirect('/')
|
||||||
|
error({
|
||||||
|
message: 'You are not connected',
|
||||||
|
statusCode: 403
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,5 @@ module.exports = {
|
|||||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||||
{ hid: 'description', content: 'Auth Routes example' }
|
{ hid: 'description', content: 'Auth Routes example' }
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
loading: { color: '#3B8070' }
|
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
"name": "auth-routes",
|
"name": "auth-routes",
|
||||||
"description": "",
|
"description": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^0.15.3",
|
||||||
"body-parser": "^1.15.2",
|
"body-parser": "^1.15.2",
|
||||||
"cross-env": "^3.1.3",
|
"cross-env": "^3.1.3",
|
||||||
"express": "^4.14.0",
|
"express": "^4.14.0",
|
||||||
"express-session": "^1.14.2",
|
"express-session": "^1.14.2",
|
||||||
"nuxt": "latest",
|
"nuxt": "latest"
|
||||||
"whatwg-fetch": "^2.0.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node server.js",
|
"dev": "node server.js",
|
||||||
|
@ -8,11 +8,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
// we use fetch() because we do not need to set data to this component
|
middleware: 'auth'
|
||||||
fetch ({ store, redirect }) {
|
|
||||||
if (!store.state.authUser) {
|
|
||||||
return redirect('/')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
const Nuxt = require('nuxt')
|
const Nuxt = require('../../')
|
||||||
const bodyParser = require('body-parser')
|
const bodyParser = require('body-parser')
|
||||||
const session = require('express-session')
|
const session = require('express-session')
|
||||||
const app = require('express')()
|
const app = require('express')()
|
||||||
|
const host = process.env.HOST || '127.0.0.1'
|
||||||
|
const port = process.env.PORT || '3000'
|
||||||
|
|
||||||
// Body parser, to access req.body
|
// Body parser, to access req.body
|
||||||
app.use(bodyParser.json())
|
app.use(bodyParser.json())
|
||||||
@ -20,7 +22,7 @@ app.post('/api/login', function (req, res) {
|
|||||||
req.session.authUser = { username: 'demo' }
|
req.session.authUser = { username: 'demo' }
|
||||||
return res.json({ username: 'demo' })
|
return res.json({ username: 'demo' })
|
||||||
}
|
}
|
||||||
res.status(401).json({ error: 'Bad credentials' })
|
res.status(401).json({ message: 'Bad credentials' })
|
||||||
})
|
})
|
||||||
|
|
||||||
// POST /api/logout to log out the user and remove it from the req.session
|
// POST /api/logout to log out the user and remove it from the req.session
|
||||||
@ -29,19 +31,23 @@ app.post('/api/logout', function (req, res) {
|
|||||||
res.json({ ok: true })
|
res.json({ ok: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
// We instantiate Nuxt.js with the options
|
// Import and Set Nuxt.js options
|
||||||
const isProd = process.env.NODE_ENV === 'production'
|
|
||||||
let config = require('./nuxt.config.js')
|
let config = require('./nuxt.config.js')
|
||||||
config.dev = !isProd
|
config.dev = !(process.env.NODE_ENV === 'production')
|
||||||
|
|
||||||
|
// Init Nuxt.js
|
||||||
const nuxt = new Nuxt(config)
|
const nuxt = new Nuxt(config)
|
||||||
// No build in production
|
app.use(nuxt.render)
|
||||||
const promise = (isProd ? Promise.resolve() : nuxt.build())
|
|
||||||
promise.then(() => {
|
// Build only in dev mode
|
||||||
app.use(nuxt.render)
|
if (config.dev) {
|
||||||
app.listen(3000)
|
nuxt.build()
|
||||||
console.log('Server is listening on http://localhost:3000') // eslint-disable-line no-console
|
.catch((error) => {
|
||||||
})
|
console.error(error) // eslint-disable-line no-console
|
||||||
.catch((error) => {
|
process.exit(1)
|
||||||
console.error(error) // eslint-disable-line no-console
|
})
|
||||||
process.exit(1)
|
}
|
||||||
})
|
|
||||||
|
// Listen the server
|
||||||
|
app.listen(port, host)
|
||||||
|
console.log('Server listening on ' + host + ':' + port) // eslint-disable-line no-console
|
||||||
|
@ -1,69 +1,41 @@
|
|||||||
import Vue from 'vue'
|
import axios from 'axios'
|
||||||
import Vuex from 'vuex'
|
|
||||||
|
|
||||||
Vue.use(Vuex)
|
export const state = {
|
||||||
|
authUser: null
|
||||||
|
}
|
||||||
|
|
||||||
// Polyfill for window.fetch()
|
export const mutations = {
|
||||||
require('whatwg-fetch')
|
SET_USER: function (state, user) {
|
||||||
|
state.authUser = user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
export const actions = {
|
||||||
|
nuxtServerInit ({ commit }, { req }) {
|
||||||
state: {
|
if (req.session && req.session.authUser) {
|
||||||
authUser: null
|
commit('SET_USER', req.session.authUser)
|
||||||
},
|
|
||||||
|
|
||||||
mutations: {
|
|
||||||
SET_USER: function (state, user) {
|
|
||||||
state.authUser = user
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
login ({ commit }, { username, password }) {
|
||||||
actions: {
|
return axios.post('/api/login', {
|
||||||
|
username,
|
||||||
nuxtServerInit ({ commit }, { req }) {
|
password
|
||||||
if (req.session && req.session.authUser) {
|
})
|
||||||
commit('SET_USER', req.session.authUser)
|
.then((res) => {
|
||||||
|
commit('SET_USER', res.data)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.response.status === 401) {
|
||||||
|
throw new Error('Bad credentials')
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
|
},
|
||||||
login ({ commit }, { username, password }) {
|
|
||||||
return fetch('/api/login', {
|
|
||||||
// Send the client cookies to the server
|
|
||||||
credentials: 'same-origin',
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
username,
|
|
||||||
password
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res.status === 401) {
|
|
||||||
throw new Error('Bad credentials')
|
|
||||||
} else {
|
|
||||||
return res.json()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((authUser) => {
|
|
||||||
commit('SET_USER', authUser)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
logout ({ commit }) {
|
|
||||||
return fetch('/api/logout', {
|
|
||||||
// Send the client cookies to the server
|
|
||||||
credentials: 'same-origin',
|
|
||||||
method: 'POST'
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
commit('SET_USER', null)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
logout ({ commit }) {
|
||||||
|
return axios.post('/api/logout')
|
||||||
|
.then(() => {
|
||||||
|
commit('SET_USER', null)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
}
|
||||||
|
|
||||||
export default store
|
|
||||||
|
40
examples/middleware/components/Visits.vue
Normal file
40
examples/middleware/components/Visits.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<ul>
|
||||||
|
<li v-for="visit in visits"><i>{{ visit.date | hours }}</i> - {{ visit.path }}</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
visits () {
|
||||||
|
return this.$store.state.visits.slice().reverse()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
hours (date) {
|
||||||
|
return date.split('T')[1].split('.')[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
ul {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
ul li {
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
ul li i {
|
||||||
|
color: gray;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
</style>
|
14
examples/middleware/layouts/default.vue
Normal file
14
examples/middleware/layouts/default.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<nuxt/>
|
||||||
|
<visits/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Visits from '~components/Visits'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Visits }
|
||||||
|
}
|
||||||
|
</script>
|
3
examples/middleware/middleware/user-agent.js
Normal file
3
examples/middleware/middleware/user-agent.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default function (context) {
|
||||||
|
context.userAgent = context.isServer ? context.req.headers['user-agent'] : navigator.userAgent
|
||||||
|
}
|
3
examples/middleware/middleware/visits.js
Normal file
3
examples/middleware/middleware/visits.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default function ({ store, route }) {
|
||||||
|
store.commit('ADD_VISIT', route.path)
|
||||||
|
}
|
5
examples/middleware/nuxt.config.js
Normal file
5
examples/middleware/nuxt.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
router: {
|
||||||
|
middleware: ['visits', 'user-agent']
|
||||||
|
}
|
||||||
|
}
|
11
examples/middleware/package.json
Normal file
11
examples/middleware/package.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "nuxt-middleware",
|
||||||
|
"dependencies": {
|
||||||
|
"nuxt": "latest"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nuxt",
|
||||||
|
"build": "nuxt build",
|
||||||
|
"start": "nuxt start"
|
||||||
|
}
|
||||||
|
}
|
25
examples/middleware/pages/_slug.vue
Normal file
25
examples/middleware/pages/_slug.vue
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>{{ $route.params.slug || 'Home' }}</h1>
|
||||||
|
<pre>{{ userAgent }}</pre>
|
||||||
|
<ul>
|
||||||
|
<li><nuxt-link to="/">Home</nuxt-link></li>
|
||||||
|
<li v-for="slug in slugs"><nuxt-link :to="{ name: 'slug', params: { slug } }">{{ slug }}</nuxt-link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data ({ store, route, userAgent }) {
|
||||||
|
return {
|
||||||
|
userAgent,
|
||||||
|
slugs: [
|
||||||
|
'foo',
|
||||||
|
'bar',
|
||||||
|
'baz'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
12
examples/middleware/store/index.js
Normal file
12
examples/middleware/store/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export const state = {
|
||||||
|
visits: []
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mutations = {
|
||||||
|
ADD_VISIT (state, path) {
|
||||||
|
state.visits.push({
|
||||||
|
path,
|
||||||
|
date: new Date().toJSON()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
import middleware from './middleware'
|
||||||
import { app, router<%= (store ? ', store' : '') %> } from './index'
|
import { app, router<%= (store ? ', store' : '') %> } from './index'
|
||||||
import { getMatchedComponents, getMatchedComponentsInstances, flatMapComponents, getContext, promisify, getLocation, compile } from './utils'
|
import { getMatchedComponents, getMatchedComponentsInstances, flatMapComponents, getContext, promiseSeries, promisify, getLocation, compile } from './utils'
|
||||||
const noopData = () => { return {} }
|
const noopData = () => { return {} }
|
||||||
const noopFetch = () => {}
|
const noopFetch = () => {}
|
||||||
let _lastPaths = []
|
let _lastPaths = []
|
||||||
@ -51,16 +52,44 @@ function loadAsyncComponents (to, from, next) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function callMiddleware (Components, context, layout) {
|
||||||
|
// Call middleware
|
||||||
|
let midd = <%= serialize(router.middleware, { isJSON: true }) %>
|
||||||
|
if (layout.middleware) {
|
||||||
|
midd = midd.concat(layout.middleware)
|
||||||
|
}
|
||||||
|
Components.forEach((Component) => {
|
||||||
|
if (Component.options.middleware) {
|
||||||
|
midd = midd.concat(Component.options.middleware)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
midd = midd.map((name) => {
|
||||||
|
if (typeof middleware[name] !== 'function') {
|
||||||
|
this.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
||||||
|
}
|
||||||
|
return middleware[name]
|
||||||
|
})
|
||||||
|
if (this.$options._nuxt.err) return
|
||||||
|
return promiseSeries(midd, context)
|
||||||
|
}
|
||||||
|
|
||||||
function render (to, from, next) {
|
function render (to, from, next) {
|
||||||
if (this._hashChanged) return next()
|
if (this._hashChanged) return next()
|
||||||
|
const _next = function (path) {
|
||||||
|
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
||||||
|
nextCalled = true
|
||||||
|
next(path)
|
||||||
|
}
|
||||||
|
const context = getContext({ to<%= (store ? ', store' : '') %>, isClient: true, next: _next.bind(this), error: this.error.bind(this) })
|
||||||
let Components = getMatchedComponents(to)
|
let Components = getMatchedComponents(to)
|
||||||
this._dateLastError = this.$options._nuxt.dateErr
|
this._dateLastError = this.$options._nuxt.dateErr
|
||||||
this._hadError = !!this.$options._nuxt.err
|
this._hadError = !!this.$options._nuxt.err
|
||||||
if (!Components.length) {
|
if (!Components.length) {
|
||||||
// Default layout
|
// Default layout
|
||||||
this.setLayout()
|
this.setLayout()
|
||||||
|
.then(callMiddleware.bind(this, Components, context))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.error({ statusCode: 404, message: 'This page could not be found.', url: to.path })
|
this.error({ statusCode: 404, message: 'This page could not be found.' })
|
||||||
return next()
|
return next()
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@ -87,6 +116,7 @@ function render (to, from, next) {
|
|||||||
let nextCalled = false
|
let nextCalled = false
|
||||||
// Set layout
|
// Set layout
|
||||||
this.setLayout(Components[0].options.layout)
|
this.setLayout(Components[0].options.layout)
|
||||||
|
.then(callMiddleware.bind(this, Components, context))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Pass validation?
|
// Pass validation?
|
||||||
let isValid = true
|
let isValid = true
|
||||||
@ -99,7 +129,7 @@ function render (to, from, next) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
this.error({ statusCode: 404, message: 'This page could not be found.', url: to.path })
|
this.error({ statusCode: 404, message: 'This page could not be found.' })
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
return Promise.all(Components.map((Component, i) => {
|
return Promise.all(Components.map((Component, i) => {
|
||||||
@ -109,12 +139,6 @@ function render (to, from, next) {
|
|||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
let promises = []
|
let promises = []
|
||||||
const _next = function (path) {
|
|
||||||
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
|
||||||
nextCalled = true
|
|
||||||
next(path)
|
|
||||||
}
|
|
||||||
const context = getContext({ to<%= (store ? ', store' : '') %>, isClient: true, next: _next.bind(this), error: this.error.bind(this) })
|
|
||||||
// Validate method
|
// Validate method
|
||||||
if (Component._data && typeof Component._data === 'function') {
|
if (Component._data && typeof Component._data === 'function') {
|
||||||
var promise = promisify(Component._data, context)
|
var promise = promisify(Component._data, context)
|
||||||
|
20
lib/app/middleware.js
Normal file
20
lib/app/middleware.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<% if (middleware) { %>
|
||||||
|
let files = require.context('~/middleware', false, /^\.\/.*\.js$/)
|
||||||
|
let filenames = files.keys()
|
||||||
|
|
||||||
|
function getModule (filename) {
|
||||||
|
let file = files(filename)
|
||||||
|
return file.default
|
||||||
|
? file.default
|
||||||
|
: file
|
||||||
|
}
|
||||||
|
let middleware = {}
|
||||||
|
|
||||||
|
// Generate the middleware
|
||||||
|
for (let filename of filenames) {
|
||||||
|
let name = filename.replace(/^\.\//, '').replace(/\.js$/, '')
|
||||||
|
middleware[name] = getModule(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default middleware
|
||||||
|
<% } else { %>export default {}<% } %>
|
@ -5,8 +5,9 @@ debug.color = 4 // force blue color
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { stringify } from 'querystring'
|
import { stringify } from 'querystring'
|
||||||
import { omit } from 'lodash'
|
import { omit } from 'lodash'
|
||||||
|
import middleware from './middleware'
|
||||||
import { app, router<%= (store ? ', store' : '') %> } from './index'
|
import { app, router<%= (store ? ', store' : '') %> } from './index'
|
||||||
import { getMatchedComponents, getContext, promisify, urlJoin } from './utils'
|
import { getMatchedComponents, getContext, promiseSeries, promisify, urlJoin } from './utils'
|
||||||
|
|
||||||
const isDev = <%= isDev %>
|
const isDev = <%= isDev %>
|
||||||
const _app = new Vue(app)
|
const _app = new Vue(app)
|
||||||
@ -47,6 +48,7 @@ export default context => {
|
|||||||
context.error = _app.$options._nuxt.error.bind(_app)
|
context.error = _app.$options._nuxt.error.bind(_app)
|
||||||
|
|
||||||
<%= (isDev ? 'const s = isDev && Date.now()' : '') %>
|
<%= (isDev ? 'const s = isDev && Date.now()' : '') %>
|
||||||
|
const ctx = getContext(context)
|
||||||
let Components = getMatchedComponents(context.route)
|
let Components = getMatchedComponents(context.route)
|
||||||
<% if (store) { %>
|
<% if (store) { %>
|
||||||
let promise = (store._actions && store._actions.nuxtServerInit ? store.dispatch('nuxtServerInit', omit(getContext(context), 'redirect', 'error')) : null)
|
let promise = (store._actions && store._actions.nuxtServerInit ? store.dispatch('nuxtServerInit', omit(getContext(context), 'redirect', 'error')) : null)
|
||||||
@ -71,6 +73,26 @@ export default context => {
|
|||||||
// Set layout
|
// Set layout
|
||||||
return _app.setLayout(Components.length ? Components[0].options.layout : '')
|
return _app.setLayout(Components.length ? Components[0].options.layout : '')
|
||||||
})
|
})
|
||||||
|
.then((layout) => {
|
||||||
|
// Call middleware
|
||||||
|
let midd = <%= serialize(router.middleware, { isJSON: true }) %>
|
||||||
|
if (layout.middleware) {
|
||||||
|
midd = midd.concat(layout.middleware)
|
||||||
|
}
|
||||||
|
Components.forEach((Component) => {
|
||||||
|
if (Component.options.middleware) {
|
||||||
|
midd = midd.concat(Component.options.middleware)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
midd = midd.map((name) => {
|
||||||
|
if (typeof middleware[name] !== 'function') {
|
||||||
|
context.nuxt.error = context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
||||||
|
}
|
||||||
|
return middleware[name]
|
||||||
|
})
|
||||||
|
if (context.nuxt.error) return
|
||||||
|
return promiseSeries(midd, ctx)
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Call .validate()
|
// Call .validate()
|
||||||
let isValid = true
|
let isValid = true
|
||||||
@ -94,7 +116,6 @@ export default context => {
|
|||||||
// Call data & fetch hooks on components matched by the route.
|
// Call data & fetch hooks on components matched by the route.
|
||||||
return Promise.all(Components.map((Component) => {
|
return Promise.all(Components.map((Component) => {
|
||||||
let promises = []
|
let promises = []
|
||||||
const ctx = getContext(context)
|
|
||||||
if (Component.options.data && typeof Component.options.data === 'function') {
|
if (Component.options.data && typeof Component.options.data === 'function') {
|
||||||
Component._data = Component.options.data
|
Component._data = Component.options.data
|
||||||
let promise = promisify(Component._data, ctx)
|
let promise = promisify(Component._data, ctx)
|
||||||
@ -114,7 +135,7 @@ export default context => {
|
|||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (!Components.length) {
|
if (!Components.length) {
|
||||||
context.nuxt.error = context.error({ statusCode: 404, message: 'This page could not be found.', url: context.route.path })
|
context.nuxt.error = context.error({ statusCode: 404, message: 'This page could not be found.' })
|
||||||
<%= (store ? 'context.nuxt.state = store.state' : '') %>
|
<%= (store ? 'context.nuxt.state = store.state' : '') %>
|
||||||
return _app
|
return _app
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,8 @@ import Vue from 'vue'
|
|||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
let files
|
let files = require.context('~/store', false, /^\.\/.*\.js$/)
|
||||||
let filenames = []
|
let filenames = files.keys()
|
||||||
|
|
||||||
try {
|
|
||||||
files = require.context('~store', false, /^\.\/.*\.js$/)
|
|
||||||
filenames = files.keys()
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Nuxt.js store:', e.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getModule (filename) {
|
function getModule (filename) {
|
||||||
let file = files(filename)
|
let file = files(filename)
|
||||||
|
@ -40,6 +40,7 @@ export function getContext (context) {
|
|||||||
ctx.query = ctx.route.query || {}
|
ctx.query = ctx.route.query || {}
|
||||||
ctx.redirect = function (status, path, query) {
|
ctx.redirect = function (status, path, query) {
|
||||||
if (!status) return
|
if (!status) return
|
||||||
|
ctx._redirected = true
|
||||||
// if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
|
// if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
|
||||||
if (typeof status === 'string' && (typeof path === 'undefined' || typeof path === 'object')) {
|
if (typeof status === 'string' && (typeof path === 'undefined' || typeof path === 'object')) {
|
||||||
query = path || {}
|
query = path || {}
|
||||||
@ -57,6 +58,16 @@ export function getContext (context) {
|
|||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function promiseSeries (promises, context) {
|
||||||
|
if (!promises.length || context._redirected) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
return promisify(promises[0], context)
|
||||||
|
.then(() => {
|
||||||
|
return promiseSeries(promises.slice(1), context)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function promisify (fn, context) {
|
export function promisify (fn, context) {
|
||||||
let promise
|
let promise
|
||||||
if (fn.length === 2) {
|
if (fn.length === 2) {
|
||||||
|
15
lib/build.js
15
lib/build.js
@ -120,8 +120,8 @@ export function * build () {
|
|||||||
|
|
||||||
function * buildFiles () {
|
function * buildFiles () {
|
||||||
if (this.dev) {
|
if (this.dev) {
|
||||||
debug('Adding webpack middlewares...')
|
debug('Adding webpack middleware...')
|
||||||
createWebpackMiddlewares.call(this)
|
createWebpackMiddleware.call(this)
|
||||||
webpackWatchAndUpdate.call(this)
|
webpackWatchAndUpdate.call(this)
|
||||||
watchPages.call(this)
|
watchPages.call(this)
|
||||||
} else {
|
} else {
|
||||||
@ -148,12 +148,17 @@ function * generateRoutesAndFiles () {
|
|||||||
this.routes = _.uniq(_.map(files, (file) => {
|
this.routes = _.uniq(_.map(files, (file) => {
|
||||||
return file.replace(/^pages/, '').replace(/\.vue$/, '').replace(/\/index/g, '').replace(/_/g, ':').replace('', '/').replace(/\/{2,}/g, '/')
|
return file.replace(/^pages/, '').replace(/\.vue$/, '').replace(/\/index/g, '').replace(/_/g, ':').replace('', '/').replace(/\/{2,}/g, '/')
|
||||||
}))
|
}))
|
||||||
|
if (typeof this.options.router.extendRoutes === 'function') {
|
||||||
|
// let the user extend the routes
|
||||||
|
this.options.router.extendRoutes(this.routes)
|
||||||
|
}
|
||||||
// Interpret and move template files to .nuxt/
|
// Interpret and move template files to .nuxt/
|
||||||
debug('Generating files...')
|
debug('Generating files...')
|
||||||
let templatesFiles = [
|
let templatesFiles = [
|
||||||
'App.vue',
|
'App.vue',
|
||||||
'client.js',
|
'client.js',
|
||||||
'index.js',
|
'index.js',
|
||||||
|
'middleware.js',
|
||||||
'router.js',
|
'router.js',
|
||||||
'server.js',
|
'server.js',
|
||||||
'utils.js',
|
'utils.js',
|
||||||
@ -168,11 +173,13 @@ function * generateRoutesAndFiles () {
|
|||||||
isDev: this.dev,
|
isDev: this.dev,
|
||||||
router: {
|
router: {
|
||||||
base: this.options.router.base,
|
base: this.options.router.base,
|
||||||
|
middleware: this.options.router.middleware,
|
||||||
linkActiveClass: this.options.router.linkActiveClass,
|
linkActiveClass: this.options.router.linkActiveClass,
|
||||||
scrollBehavior: this.options.router.scrollBehavior
|
scrollBehavior: this.options.router.scrollBehavior
|
||||||
},
|
},
|
||||||
env: this.options.env,
|
env: this.options.env,
|
||||||
head: this.options.head,
|
head: this.options.head,
|
||||||
|
middleware: this.options.middleware,
|
||||||
store: this.options.store,
|
store: this.options.store,
|
||||||
css: this.options.css,
|
css: this.options.css,
|
||||||
plugins: this.options.plugins.map((p) => r(this.srcDir, p)),
|
plugins: this.options.plugins.map((p) => r(this.srcDir, p)),
|
||||||
@ -294,7 +301,7 @@ function getWebpackServerConfig () {
|
|||||||
return serverWebpackConfig.call(this)
|
return serverWebpackConfig.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createWebpackMiddlewares () {
|
function createWebpackMiddleware () {
|
||||||
const clientConfig = getWebpackClientConfig.call(this)
|
const clientConfig = getWebpackClientConfig.call(this)
|
||||||
// setup on the fly compilation + hot-reload
|
// setup on the fly compilation + hot-reload
|
||||||
clientConfig.entry.app = _.flatten(['webpack-hot-middleware/client?reload=true', clientConfig.entry.app])
|
clientConfig.entry.app = _.flatten(['webpack-hot-middleware/client?reload=true', clientConfig.entry.app])
|
||||||
@ -303,7 +310,7 @@ function createWebpackMiddlewares () {
|
|||||||
new webpack.NoEmitOnErrorsPlugin()
|
new webpack.NoEmitOnErrorsPlugin()
|
||||||
)
|
)
|
||||||
const clientCompiler = webpack(clientConfig)
|
const clientCompiler = webpack(clientConfig)
|
||||||
// Add the middlewares to the instance context
|
// Add the middleware to the instance context
|
||||||
this.webpackDevMiddleware = pify(require('webpack-dev-middleware')(clientCompiler, {
|
this.webpackDevMiddleware = pify(require('webpack-dev-middleware')(clientCompiler, {
|
||||||
publicPath: clientConfig.output.publicPath,
|
publicPath: clientConfig.output.publicPath,
|
||||||
stats: {
|
stats: {
|
||||||
|
@ -76,6 +76,7 @@ export default function () {
|
|||||||
console.error(`Could not generate the dynamic route ${route}, please add the mapping params in nuxt.config.js (generate.routeParams).`) // eslint-disable-line no-console
|
console.error(`Could not generate the dynamic route ${route}, please add the mapping params in nuxt.config.js (generate.routeParams).`) // eslint-disable-line no-console
|
||||||
return process.exit(1)
|
return process.exit(1)
|
||||||
}
|
}
|
||||||
|
route = route + '?'
|
||||||
const toPath = pathToRegexp.compile(route)
|
const toPath = pathToRegexp.compile(route)
|
||||||
routes = routes.concat(routeParams.map((params) => {
|
routes = routes.concat(routeParams.map((params) => {
|
||||||
return toPath(params)
|
return toPath(params)
|
||||||
|
@ -37,13 +37,16 @@ class Nuxt {
|
|||||||
},
|
},
|
||||||
router: {
|
router: {
|
||||||
base: '/',
|
base: '/',
|
||||||
|
middleware: [],
|
||||||
linkActiveClass: 'nuxt-link-active',
|
linkActiveClass: 'nuxt-link-active',
|
||||||
extendRoutes: null,
|
extendRoutes: null,
|
||||||
scrollBehavior: null
|
scrollBehavior: null
|
||||||
},
|
},
|
||||||
build: {}
|
build: {}
|
||||||
}
|
}
|
||||||
|
// Sanitization
|
||||||
if (options.loading === true) delete options.loading
|
if (options.loading === true) delete options.loading
|
||||||
|
if (options.router && typeof options.router.middleware === 'string') options.router.middleware = [ options.router.middleware ]
|
||||||
if (typeof options.transition === 'string') options.transition = { name: options.transition }
|
if (typeof options.transition === 'string') options.transition = { name: options.transition }
|
||||||
this.options = _.defaultsDeep(options, defaults)
|
this.options = _.defaultsDeep(options, defaults)
|
||||||
// Env variables
|
// Env variables
|
||||||
@ -54,6 +57,11 @@ class Nuxt {
|
|||||||
if (fs.existsSync(join(this.srcDir, 'store'))) {
|
if (fs.existsSync(join(this.srcDir, 'store'))) {
|
||||||
this.options.store = true
|
this.options.store = true
|
||||||
}
|
}
|
||||||
|
// If middleware defined, update middleware option to true
|
||||||
|
this.options.middleware = false
|
||||||
|
if (fs.existsSync(join(this.srcDir, 'middleware'))) {
|
||||||
|
this.options.middleware = true
|
||||||
|
}
|
||||||
// Template
|
// Template
|
||||||
this.appTemplate = _.template(fs.readFileSync(resolve(__dirname, 'views', 'app.html'), 'utf8'), {
|
this.appTemplate = _.template(fs.readFileSync(resolve(__dirname, 'views', 'app.html'), 'utf8'), {
|
||||||
imports: { serialize }
|
imports: { serialize }
|
||||||
|
@ -22,11 +22,11 @@ export function render (req, res) {
|
|||||||
const context = getContext(req, res)
|
const context = getContext(req, res)
|
||||||
return co(function * () {
|
return co(function * () {
|
||||||
if (self.dev) {
|
if (self.dev) {
|
||||||
// Call webpack middlewares only in development
|
// Call webpack middleware only in development
|
||||||
yield self.webpackDevMiddleware(req, res)
|
yield self.webpackDevMiddleware(req, res)
|
||||||
yield self.webpackHotMiddleware(req, res)
|
yield self.webpackHotMiddleware(req, res)
|
||||||
}
|
}
|
||||||
// If base in req.url, remove it for the middlewares and vue-router
|
// If base in req.url, remove it for the middleware and vue-router
|
||||||
if (self.options.router.base !== '/' && req.url.indexOf(self.options.router.base) === 0) {
|
if (self.options.router.base !== '/' && req.url.indexOf(self.options.router.base) === 0) {
|
||||||
// Compatibility with base url for dev server
|
// Compatibility with base url for dev server
|
||||||
req.url = req.url.replace(self.options.router.base, '/')
|
req.url = req.url.replace(self.options.router.base, '/')
|
||||||
|
Loading…
Reference in New Issue
Block a user