mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-30 17:37:14 +00:00
Merge pull request #77 from nuxt/feature-layout
Layouts feature + bug fixes
This commit is contained in:
commit
593e12f348
60
examples/custom-layouts/README.md
Normal file
60
examples/custom-layouts/README.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Layouts
|
||||||
|
|
||||||
|
> Nuxt.js allows you to extend the main layout or create custom layout by adding them in the `layouts/` directory
|
||||||
|
|
||||||
|
## layouts/default.vue
|
||||||
|
|
||||||
|
You can extend the main layout by adding a `layouts/default.vue` file.
|
||||||
|
|
||||||
|
*Make sure to add the `<nuxt>` component when creating a layout to display the page component.*
|
||||||
|
|
||||||
|
The default layout source code is:
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<nuxt/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
## layouts/error.vue
|
||||||
|
|
||||||
|
You can customize the error page by adding a `layouts/error.vue` file.
|
||||||
|
|
||||||
|
This layout is special since your should not include `<nuxt/>` inside its template, see this layout as a component displayed when an error occurs (404, 500, etc).
|
||||||
|
|
||||||
|
The default error page source code is available on: https://github.com/nuxt/nuxt.js/blob/master/lib/app/components/nuxt-error.vue
|
||||||
|
|
||||||
|
## layouts/*.vue
|
||||||
|
|
||||||
|
See the [demonstration video](https://www.youtube.com/watch?v=YOKnSTp7d38).
|
||||||
|
|
||||||
|
Every file (*first level*) in the `layouts/` directory will create a custom layout accessible with the `layout` property in the page component.
|
||||||
|
|
||||||
|
*Make sure to add the `<nuxt>` component when creating a layout to display the page component.*
|
||||||
|
|
||||||
|
Example of `layouts/blog.vue`:
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div>My blog navigation bar here</div>
|
||||||
|
<nuxt/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
And then in `pages/posts.vue` I can tell Nuxt.js to use this custom layout:
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
layout: 'blog'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Go to [http://localhost:3000](http://localhost:3000) and navigate trough the app. To see the custom error page: [http://localhost:3000/404](http://localhost:3000/404)
|
21
examples/custom-layouts/layouts/dark.vue
Normal file
21
examples/custom-layouts/layouts/dark.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dark">
|
||||||
|
<nuxt/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.dark {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
background: black;
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.dark a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<nuxt-container>
|
<div>
|
||||||
<img src="logo.png"/>
|
<img src="logo.png"/>
|
||||||
<nuxt/>
|
<nuxt/>
|
||||||
</nuxt-container>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
23
examples/custom-layouts/layouts/error.vue
Normal file
23
examples/custom-layouts/layouts/error.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Sorry, page not found</h1>
|
||||||
|
<nuxt-link to="/">Home page</nuxt-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['error']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
font-family: sans-serif;
|
||||||
|
padding-top: 10%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "nuxt-extend-app",
|
"name": "nuxt-custom-layouts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nuxt": "latest"
|
"nuxt": "latest"
|
||||||
},
|
},
|
@ -7,9 +7,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
|
layout: 'dark',
|
||||||
data ({ req }) {
|
data ({ req }) {
|
||||||
return {
|
return {
|
||||||
name: req ? 'server' : 'client'
|
name: req ? 'server' : 'client2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
BIN
examples/custom-layouts/static/logo.png
Normal file
BIN
examples/custom-layouts/static/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
@ -1,42 +0,0 @@
|
|||||||
# Extending the main app
|
|
||||||
|
|
||||||
> Nuxt.js allows you to extend the main application by adding a `layouts/app.vue` file
|
|
||||||
|
|
||||||
## The default app
|
|
||||||
|
|
||||||
The default source code of the main app is:
|
|
||||||
```html
|
|
||||||
<template>
|
|
||||||
<nuxt-container>
|
|
||||||
<nuxt/>
|
|
||||||
</nuxt-container>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## The `layouts/app.vue` file
|
|
||||||
|
|
||||||
### 🎬 [Example video](https://www.youtube.com/watch?v=wBhia7uBxDA)
|
|
||||||
|
|
||||||
You have to make sure to add the `<nuxt-container>` and `<nuxt>` components when extending the app.
|
|
||||||
|
|
||||||
It is important that the code you add stays inside `<nuxt-container>`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```html
|
|
||||||
<template>
|
|
||||||
<nuxt-container>
|
|
||||||
<div>My navigation bar here</div>
|
|
||||||
<nuxt/>
|
|
||||||
</nuxt-container>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Demo
|
|
||||||
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
Go to [http://localhost:3000](http://localhost:3000) and navigate trough the app. Notice how the logo at the top right stays between the pages, even on the error page: [http://localhost:3000/404](http://localhost:3000/404)
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.0 KiB |
@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div>
|
||||||
<p>Hi from {{ name }}</p>
|
<p>Hi from {{ name }}</p>
|
||||||
|
<nuxt-link to="/">Home page</nuxt-link>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -1,5 +1,53 @@
|
|||||||
<template>
|
<template>
|
||||||
<nuxt-container>
|
<div id="__nuxt">
|
||||||
<nuxt/>
|
<component v-if="layout" :is="layout"></component>
|
||||||
</nuxt-container>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let layouts = {
|
||||||
|
<%
|
||||||
|
var layoutsKeys = Object.keys(layouts);
|
||||||
|
layoutsKeys.forEach(function (key, i) { %>
|
||||||
|
_<%= key %>: process.BROWSER_BUILD ? () => System.import('<%= layouts[key] %>') : require('<%= layouts[key] %>')<%= (i + 1) < layoutsKeys.length ? ',' : '' %>
|
||||||
|
<% }) %>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
head: <%= JSON.stringify(head) %>,
|
||||||
|
data: () => ({
|
||||||
|
layout: null,
|
||||||
|
layoutName: ''
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
setLayout (layout) {
|
||||||
|
if (!layout || !layouts['_' + layout]) layout = 'default'
|
||||||
|
this.layoutName = layout
|
||||||
|
let _layout = '_' + layout
|
||||||
|
if (typeof layouts[_layout] === 'function') {
|
||||||
|
return this.loadLayout(_layout)
|
||||||
|
}
|
||||||
|
this.layout = layouts[_layout]
|
||||||
|
return Promise.resolve(this.layout)
|
||||||
|
},
|
||||||
|
loadLayout (_layout) {
|
||||||
|
return layouts[_layout]()
|
||||||
|
.then((Component) => {
|
||||||
|
layouts[_layout] = Component
|
||||||
|
this.layout = layouts[_layout]
|
||||||
|
return this.layout
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
if (this.$nuxt) {
|
||||||
|
return this.$nuxt.error({ statusCode: 500, message: e.message })
|
||||||
|
}
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<% css.forEach(function (c) { %>
|
||||||
|
<style src="<%= (typeof c === 'string' ? c : c.src) %>" lang="<%= (c.lang ? c.lang : 'css') %>"></style>
|
||||||
|
<% }) %>
|
||||||
|
@ -58,16 +58,22 @@ function loadAsyncComponents (to, ___, next) {
|
|||||||
function render (to, from, next) {
|
function render (to, from, next) {
|
||||||
let Components = getMatchedComponents(to)
|
let Components = getMatchedComponents(to)
|
||||||
if (!Components.length) {
|
if (!Components.length) {
|
||||||
|
// Default layout
|
||||||
|
this.setLayout()
|
||||||
|
.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.', url: to.path })
|
||||||
return next()
|
return next()
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// Update ._data and other properties if hot reloaded
|
// Update ._data and other properties if hot reloaded
|
||||||
Components.forEach(function (Component) {
|
Components.forEach(function (Component) {
|
||||||
if (!Component._data) {
|
if (!Component._data) {
|
||||||
Component._data = Component.options.data || noopData
|
Component._data = Component.options.data || noopData
|
||||||
}
|
}
|
||||||
if (Component._Ctor && Component._Ctor.options && Component._dataFn) {
|
if (Component._Ctor && Component._Ctor.options) {
|
||||||
Component.options.fetch = Component._Ctor.options.fetch
|
Component.options.fetch = Component._Ctor.options.fetch
|
||||||
|
if (Component._dataFn) {
|
||||||
const originalDataFn = Component._data.toString().replace(/\s/g, '')
|
const originalDataFn = Component._data.toString().replace(/\s/g, '')
|
||||||
const dataFn = Component._dataFn
|
const dataFn = Component._dataFn
|
||||||
const newDataFn = (Component._Ctor.options.data || noopData).toString().replace(/\s/g, '')
|
const newDataFn = (Component._Ctor.options.data || noopData).toString().replace(/\s/g, '')
|
||||||
@ -76,10 +82,15 @@ function render (to, from, next) {
|
|||||||
Component._data = Component._Ctor.options.data || noopData
|
Component._data = Component._Ctor.options.data || noopData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
this.setTransitions(mapTransitions(Components, to, from))
|
this.setTransitions(mapTransitions(Components, to, from))
|
||||||
this.error()
|
this.error()
|
||||||
let nextCalled = false
|
let nextCalled = false
|
||||||
|
// Set layout
|
||||||
|
this.setLayout(Components[0].options.layout)
|
||||||
|
.then(() => {
|
||||||
|
// Pass validation?
|
||||||
let isValid = true
|
let isValid = true
|
||||||
Components.forEach((Component) => {
|
Components.forEach((Component) => {
|
||||||
if (!isValid) return
|
if (!isValid) return
|
||||||
@ -93,7 +104,7 @@ function render (to, from, next) {
|
|||||||
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.', url: to.path })
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
Promise.all(Components.map((Component, i) => {
|
return Promise.all(Components.map((Component, i) => {
|
||||||
// Check if only children route changed
|
// Check if only children route changed
|
||||||
Component._path = compile(to.matched[i].path)(to.params)
|
Component._path = compile(to.matched[i].path)(to.params)
|
||||||
if (Component._path === _lastPaths[i] && (i + 1) !== Components.length) {
|
if (Component._path === _lastPaths[i] && (i + 1) !== Components.length) {
|
||||||
@ -110,7 +121,8 @@ function render (to, from, next) {
|
|||||||
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)
|
||||||
promise.then((data) => {
|
promise.then((data) => {
|
||||||
Component.options.data = () => data || {}
|
Component._cData = () => data || {}
|
||||||
|
Component.options.data = Component._cData
|
||||||
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
||||||
if (Component._Ctor && Component._Ctor.options) {
|
if (Component._Ctor && Component._Ctor.options) {
|
||||||
Component._Ctor.options.data = Component.options.data
|
Component._Ctor.options.data = Component.options.data
|
||||||
@ -127,6 +139,7 @@ function render (to, from, next) {
|
|||||||
}
|
}
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
}))
|
}))
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
_lastPaths = Components.map((Component, i) => compile(to.matched[i].path)(to.params))
|
_lastPaths = Components.map((Component, i) => compile(to.matched[i].path)(to.params))
|
||||||
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
||||||
@ -157,11 +170,13 @@ function fixPrepatch (to, ___) {
|
|||||||
}
|
}
|
||||||
return instance.constructor.options.__file
|
return instance.constructor.options.__file
|
||||||
})
|
})
|
||||||
|
hotReloadAPI(this)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special hot reload with data(context)
|
// Special hot reload with data(context)
|
||||||
function hotReloadAPI (_app) {
|
function hotReloadAPI (_app) {
|
||||||
|
if (!module.hot) return
|
||||||
const $nuxt = _app.$nuxt
|
const $nuxt = _app.$nuxt
|
||||||
var _forceUpdate = $nuxt.$forceUpdate.bind($nuxt)
|
var _forceUpdate = $nuxt.$forceUpdate.bind($nuxt)
|
||||||
$nuxt.$forceUpdate = function () {
|
$nuxt.$forceUpdate = function () {
|
||||||
@ -173,6 +188,14 @@ function hotReloadAPI (_app) {
|
|||||||
Component._Ctor = Component
|
Component._Ctor = Component
|
||||||
}
|
}
|
||||||
let promises = []
|
let promises = []
|
||||||
|
// If layout changed
|
||||||
|
if (_app.layoutName !== Component.options.layout) {
|
||||||
|
let promise = _app.setLayout(Component.options.layout)
|
||||||
|
promise.then(() => {
|
||||||
|
hotReloadAPI(_app)
|
||||||
|
})
|
||||||
|
promises.push(promise)
|
||||||
|
}
|
||||||
const next = function (path) {
|
const next = function (path) {
|
||||||
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
||||||
router.push(path)
|
router.push(path)
|
||||||
@ -185,12 +208,16 @@ function hotReloadAPI (_app) {
|
|||||||
Component._data = Component._Ctor.options.data || noopData
|
Component._data = Component._Ctor.options.data || noopData
|
||||||
let p = promisify(Component._data, context)
|
let p = promisify(Component._data, context)
|
||||||
p.then((data) => {
|
p.then((data) => {
|
||||||
Component.options.data = () => data || {}
|
Component._cData = () => data || {}
|
||||||
|
Component.options.data = Component._cData
|
||||||
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
||||||
Component._Ctor.options.data = Component.options.data
|
Component._Ctor.options.data = Component.options.data
|
||||||
<%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
|
<%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
|
||||||
})
|
})
|
||||||
promises.push(p)
|
promises.push(p)
|
||||||
|
} else if (Component._cData) {
|
||||||
|
Component.options.data = Component._cData
|
||||||
|
Component._Ctor.options.data = Component.options.data
|
||||||
}
|
}
|
||||||
// Check if fetch has been updated
|
// Check if fetch has been updated
|
||||||
const originalFetchFn = (Component.options.fetch || noopFetch).toString().replace(/\s/g, '')
|
const originalFetchFn = (Component.options.fetch || noopFetch).toString().replace(/\s/g, '')
|
||||||
@ -233,7 +260,8 @@ const resolveComponents = flatMapComponents(router.match(path), (Component, _, m
|
|||||||
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
|
||||||
if (NUXT.serverRendered) {
|
if (NUXT.serverRendered) {
|
||||||
Component.options.data = () => NUXT.data[index] || {}
|
Component._cData = () => NUXT.data[index] || {}
|
||||||
|
Component.options.data = Component._cData
|
||||||
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
||||||
}
|
}
|
||||||
if (Component._Ctor && Component._Ctor.options) {
|
if (Component._Ctor && Component._Ctor.options) {
|
||||||
@ -264,6 +292,13 @@ function nuxtReady (app) {
|
|||||||
Promise.all(resolveComponents)
|
Promise.all(resolveComponents)
|
||||||
.then((Components) => {
|
.then((Components) => {
|
||||||
const _app = new Vue(app)
|
const _app = new Vue(app)
|
||||||
|
|
||||||
|
return _app.setLayout(Components.length ? Components[0].options.layout : '')
|
||||||
|
.then(() => {
|
||||||
|
return { _app, Components }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(({ _app, Components }) => {
|
||||||
const mountApp = () => {
|
const mountApp = () => {
|
||||||
_app.$mount('#__nuxt')
|
_app.$mount('#__nuxt')
|
||||||
<% if (loading) { %>
|
<% if (loading) { %>
|
||||||
@ -271,7 +306,7 @@ Promise.all(resolveComponents)
|
|||||||
_app.$loading = _app.$nuxt.$loading
|
_app.$loading = _app.$nuxt.$loading
|
||||||
<% } %>
|
<% } %>
|
||||||
// Hot reloading
|
// Hot reloading
|
||||||
if (module.hot) hotReloadAPI(_app)
|
hotReloadAPI(_app)
|
||||||
// Call window.onNuxtReady callbacks
|
// Call window.onNuxtReady callbacks
|
||||||
Vue.nextTick(() => nuxtReady(_app))
|
Vue.nextTick(() => nuxtReady(_app))
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div id="__nuxt">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'nuxt-container',
|
|
||||||
head: <%= JSON.stringify(head) %>
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<% css.forEach(function (c) { %>
|
|
||||||
<style src="<%= (typeof c === 'string' ? c : c.src) %>" lang="<%= (c.lang ? c.lang : 'css') %>"></style>
|
|
||||||
<% }) %>
|
|
@ -22,6 +22,8 @@ export default {
|
|||||||
Vue.prototype.$nuxt = this
|
Vue.prototype.$nuxt = this
|
||||||
// Add this.$root.$nuxt
|
// Add this.$root.$nuxt
|
||||||
this.$root.$nuxt = this
|
this.$root.$nuxt = this
|
||||||
|
// Bind $nuxt.setLayout(layout) to $root.setLayout
|
||||||
|
this.setLayout = this.$root.setLayout.bind(this.$root)
|
||||||
// add to window so we can listen when ready
|
// add to window so we can listen when ready
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.$nuxt = this
|
window.$nuxt = this
|
||||||
|
@ -4,14 +4,11 @@ import Vue from 'vue'
|
|||||||
import Meta from 'vue-meta'
|
import Meta from 'vue-meta'
|
||||||
import router from './router.js'
|
import router from './router.js'
|
||||||
<% if (store) { %>import store from '~store/index.js'<% } %>
|
<% if (store) { %>import store from '~store/index.js'<% } %>
|
||||||
import NuxtContainer from './components/nuxt-container.vue'
|
|
||||||
import NuxtChild from './components/nuxt-child.js'
|
import NuxtChild from './components/nuxt-child.js'
|
||||||
import NuxtLink from './components/nuxt-link.js'
|
import NuxtLink from './components/nuxt-link.js'
|
||||||
import Nuxt from './components/nuxt.vue'
|
import Nuxt from './components/nuxt.vue'
|
||||||
import App from '<%= appPath %>'
|
import App from '<%= appPath %>'
|
||||||
|
|
||||||
// Component: <nuxt-container>
|
|
||||||
Vue.component(NuxtContainer.name, NuxtContainer)
|
|
||||||
// Component: <nuxt-child>
|
// Component: <nuxt-child>
|
||||||
Vue.component(NuxtChild.name, NuxtChild)
|
Vue.component(NuxtChild.name, NuxtChild)
|
||||||
// Component: <nuxt-link>
|
// Component: <nuxt-link>
|
||||||
|
3
lib/app/layouts/default.vue
Normal file
3
lib/app/layouts/default.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<nuxt/>
|
||||||
|
</template>
|
@ -73,6 +73,10 @@ export default context => {
|
|||||||
}
|
}
|
||||||
return Component
|
return Component
|
||||||
})
|
})
|
||||||
|
// Set layout
|
||||||
|
return _app.setLayout(Components.length ? Components[0].options.layout : '')
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
// Call .validate()
|
// Call .validate()
|
||||||
let isValid = true
|
let isValid = true
|
||||||
Components.forEach((Component) => {
|
Components.forEach((Component) => {
|
||||||
|
@ -132,6 +132,14 @@ function * buildFiles () {
|
|||||||
|
|
||||||
function * generateRoutesAndFiles () {
|
function * generateRoutesAndFiles () {
|
||||||
debug('Generating routes...')
|
debug('Generating routes...')
|
||||||
|
// Layouts
|
||||||
|
let layouts = {}
|
||||||
|
const layoutsFiles = yield glob('layouts/*.vue', { cwd: this.srcDir })
|
||||||
|
layoutsFiles.forEach((file) => {
|
||||||
|
let name = file.split('/').slice(-1)[0].replace('.vue', '')
|
||||||
|
if (name === 'error') return
|
||||||
|
layouts[name] = r(this.srcDir, file)
|
||||||
|
})
|
||||||
// Generate routes based on files
|
// Generate routes based on files
|
||||||
const files = yield glob('pages/**/*.vue', { cwd: this.srcDir })
|
const files = yield glob('pages/**/*.vue', { cwd: this.srcDir })
|
||||||
this.routes = _.uniq(_.map(files, (file) => {
|
this.routes = _.uniq(_.map(files, (file) => {
|
||||||
@ -146,7 +154,6 @@ function * generateRoutesAndFiles () {
|
|||||||
'router.js',
|
'router.js',
|
||||||
'server.js',
|
'server.js',
|
||||||
'utils.js',
|
'utils.js',
|
||||||
'components/nuxt-container.vue',
|
|
||||||
'components/nuxt-loading.vue',
|
'components/nuxt-loading.vue',
|
||||||
'components/nuxt-child.js',
|
'components/nuxt-child.js',
|
||||||
'components/nuxt-link.js',
|
'components/nuxt-link.js',
|
||||||
@ -165,6 +172,7 @@ function * generateRoutesAndFiles () {
|
|||||||
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)),
|
||||||
appPath: './App.vue',
|
appPath: './App.vue',
|
||||||
|
layouts: layouts,
|
||||||
loading: (typeof this.options.loading === 'string' ? r(this.srcDir, this.options.loading) : this.options.loading),
|
loading: (typeof this.options.loading === 'string' ? r(this.srcDir, this.options.loading) : this.options.loading),
|
||||||
transition: this.options.transition,
|
transition: this.options.transition,
|
||||||
components: {
|
components: {
|
||||||
@ -174,12 +182,15 @@ function * generateRoutesAndFiles () {
|
|||||||
}
|
}
|
||||||
// Format routes for the lib/app/router.js template
|
// Format routes for the lib/app/router.js template
|
||||||
templateVars.router.routes = createRoutes(files, this.srcDir)
|
templateVars.router.routes = createRoutes(files, this.srcDir)
|
||||||
if (fs.existsSync(join(this.srcDir, 'layouts', 'app.vue'))) {
|
if (layoutsFiles.includes('layouts/error.vue')) {
|
||||||
templateVars.appPath = r(this.srcDir, 'layouts/app.vue')
|
|
||||||
}
|
|
||||||
if (fs.existsSync(join(this.srcDir, 'layouts', 'error.vue'))) {
|
|
||||||
templateVars.components.ErrorPage = r(this.srcDir, 'layouts/error.vue')
|
templateVars.components.ErrorPage = r(this.srcDir, 'layouts/error.vue')
|
||||||
}
|
}
|
||||||
|
// If no default layout, create its folder and add the default folder
|
||||||
|
if (!layouts.default) {
|
||||||
|
yield mkdirp(r(this.dir, '.nuxt/layouts'))
|
||||||
|
templatesFiles.push('layouts/default.vue')
|
||||||
|
layouts.default = r(__dirname, 'app', 'layouts', 'default.vue')
|
||||||
|
}
|
||||||
let moveTemplates = templatesFiles.map((file) => {
|
let moveTemplates = templatesFiles.map((file) => {
|
||||||
return readFile(r(__dirname, 'app', file), 'utf8')
|
return readFile(r(__dirname, 'app', file), 'utf8')
|
||||||
.then((fileContent) => {
|
.then((fileContent) => {
|
||||||
@ -347,7 +358,14 @@ function createRenderer (bundle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function watchPages () {
|
function watchPages () {
|
||||||
const patterns = [ r(this.srcDir, 'pages/*.vue'), r(this.srcDir, 'pages/**/*.vue') ]
|
const patterns = [
|
||||||
|
r(this.srcDir, 'pages'),
|
||||||
|
r(this.srcDir, 'pages/*.vue'),
|
||||||
|
r(this.srcDir, 'pages/**/*.vue'),
|
||||||
|
r(this.srcDir, 'layouts'),
|
||||||
|
r(this.srcDir, 'layouts/*.vue'),
|
||||||
|
r(this.srcDir, 'layouts/**/*.vue')
|
||||||
|
]
|
||||||
const options = {
|
const options = {
|
||||||
ignoreInitial: true
|
ignoreInitial: true
|
||||||
}
|
}
|
||||||
|
16
package.json
16
package.json
@ -45,7 +45,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-html": "^0.0.6",
|
"ansi-html": "^0.0.6",
|
||||||
"autoprefixer": "^6.5.4",
|
"autoprefixer": "^6.6.0",
|
||||||
"babel-core": "^6.21.0",
|
"babel-core": "^6.21.0",
|
||||||
"babel-loader": "^6.2.10",
|
"babel-loader": "^6.2.10",
|
||||||
"babel-polyfill": "^6.20.0",
|
"babel-polyfill": "^6.20.0",
|
||||||
@ -55,7 +55,7 @@
|
|||||||
"co": "^4.6.0",
|
"co": "^4.6.0",
|
||||||
"cross-spawn": "^5.0.1",
|
"cross-spawn": "^5.0.1",
|
||||||
"css-loader": "^0.26.1",
|
"css-loader": "^0.26.1",
|
||||||
"debug": "^2.4.5",
|
"debug": "^2.5.1",
|
||||||
"es6-object-assign": "^1.0.3",
|
"es6-object-assign": "^1.0.3",
|
||||||
"es6-promise": "^4.0.5",
|
"es6-promise": "^4.0.5",
|
||||||
"extract-text-webpack-plugin": "2.0.0-beta.4",
|
"extract-text-webpack-plugin": "2.0.0-beta.4",
|
||||||
@ -64,7 +64,7 @@
|
|||||||
"glob": "^7.1.1",
|
"glob": "^7.1.1",
|
||||||
"hash-sum": "^1.0.2",
|
"hash-sum": "^1.0.2",
|
||||||
"html-minifier": "^3.2.3",
|
"html-minifier": "^3.2.3",
|
||||||
"lodash": "^4.17.2",
|
"lodash": "^4.17.3",
|
||||||
"lru-cache": "^4.0.2",
|
"lru-cache": "^4.0.2",
|
||||||
"memory-fs": "^0.4.1",
|
"memory-fs": "^0.4.1",
|
||||||
"path-to-regexp": "^1.7.0",
|
"path-to-regexp": "^1.7.0",
|
||||||
@ -72,16 +72,16 @@
|
|||||||
"serialize-javascript": "^1.3.0",
|
"serialize-javascript": "^1.3.0",
|
||||||
"serve-static": "^1.11.1",
|
"serve-static": "^1.11.1",
|
||||||
"url-loader": "^0.5.7",
|
"url-loader": "^0.5.7",
|
||||||
"vue": "^2.1.6",
|
"vue": "^2.1.7",
|
||||||
"vue-loader": "^10.0.2",
|
"vue-loader": "^10.0.2",
|
||||||
"vue-meta": "^0.5.3",
|
"vue-meta": "^0.5.3",
|
||||||
"vue-router": "^2.1.1",
|
"vue-router": "^2.1.1",
|
||||||
"vue-server-renderer": "^2.1.6",
|
"vue-server-renderer": "^2.1.7",
|
||||||
"vue-template-compiler": "^2.1.6",
|
"vue-template-compiler": "^2.1.7",
|
||||||
"vuex": "^2.1.1",
|
"vuex": "^2.1.1",
|
||||||
"webpack": "2.2.0-rc.1",
|
"webpack": "2.2.0-rc.2",
|
||||||
"webpack-dev-middleware": "^1.9.0",
|
"webpack-dev-middleware": "^1.9.0",
|
||||||
"webpack-hot-middleware": "^2.13.2"
|
"webpack-hot-middleware": "^2.14.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ava": "^0.17.0",
|
"ava": "^0.17.0",
|
||||||
|
51
test/dynamic-routes.test.js
Normal file
51
test/dynamic-routes.test.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import test from 'ava'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
import fs from 'fs'
|
||||||
|
import pify from 'pify'
|
||||||
|
const readFile = pify(fs.readFile)
|
||||||
|
|
||||||
|
// Init nuxt.js and create server listening on localhost:4000
|
||||||
|
test.before('Init Nuxt.js', async t => {
|
||||||
|
const Nuxt = require('../')
|
||||||
|
const nuxt = new Nuxt({
|
||||||
|
rootDir: resolve(__dirname, 'fixtures/dynamic-routes'),
|
||||||
|
dev: false
|
||||||
|
})
|
||||||
|
await nuxt.build()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Check .nuxt/router.js', t => {
|
||||||
|
return readFile(resolve(__dirname, './fixtures/dynamic-routes/.nuxt/router.js'), 'utf-8')
|
||||||
|
.then((routerFile) => {
|
||||||
|
routerFile = routerFile.slice(
|
||||||
|
routerFile.indexOf('routes: ['),
|
||||||
|
-3
|
||||||
|
)
|
||||||
|
.replace('routes: [', '[')
|
||||||
|
.replace(/ _[0-9A-z]+,/g, ' "",')
|
||||||
|
let routes = eval('( ' + routerFile + ')') // eslint-disable-line no-eval
|
||||||
|
// pages/test/index.vue
|
||||||
|
t.is(routes[0].path, '/test')
|
||||||
|
t.is(routes[0].name, 'test')
|
||||||
|
// pages/parent.vue
|
||||||
|
t.is(routes[1].path, '/parent')
|
||||||
|
t.falsy(routes[1].name) // parent route has no name
|
||||||
|
// pages/parent/*.vue
|
||||||
|
t.is(routes[1].children.length, 3) // parent has 3 children
|
||||||
|
t.deepEqual(routes[1].children.map((r) => r.path), ['', 'teub', 'child'])
|
||||||
|
t.deepEqual(routes[1].children.map((r) => r.name), ['parent', 'parent-teub', 'parent-child'])
|
||||||
|
// pages/test/users.vue
|
||||||
|
t.is(routes[2].path, '/test/users')
|
||||||
|
t.falsy(routes[2].name) // parent route has no name
|
||||||
|
// pages/test/users/*.vue
|
||||||
|
t.is(routes[2].children.length, 3) // parent has 3 children
|
||||||
|
t.deepEqual(routes[2].children.map((r) => r.path), ['', ':id', ':index/teub'])
|
||||||
|
t.deepEqual(routes[2].children.map((r) => r.name), ['test-users', 'test-users-id', 'test-users-index-teub'])
|
||||||
|
// pages/_slug.vue
|
||||||
|
t.is(routes[3].path, '/:slug?')
|
||||||
|
t.is(routes[3].name, 'slug')
|
||||||
|
// pages/_key/_id.vue
|
||||||
|
t.is(routes[4].path, '/:key?/:id?')
|
||||||
|
t.is(routes[4].name, 'key-id')
|
||||||
|
})
|
||||||
|
})
|
185
test/fixtures/dynamic-routes/README.md
vendored
185
test/fixtures/dynamic-routes/README.md
vendored
@ -1,185 +0,0 @@
|
|||||||
# Defining custom routes with Nuxt.js
|
|
||||||
|
|
||||||
> Nuxt.js is based on `vue-router` and let you to defined custom routes easily :rocket:
|
|
||||||
|
|
||||||
## Concept
|
|
||||||
|
|
||||||
Nuxt.js generates automatically the `vue-router` configuration according to your file tree of `.vue` files inside the `pages/` directory.
|
|
||||||
|
|
||||||
## Basic routes
|
|
||||||
|
|
||||||
This file tree:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pages/
|
|
||||||
--| team/
|
|
||||||
-----| index.vue
|
|
||||||
-----| about.vue
|
|
||||||
--| index.vue
|
|
||||||
```
|
|
||||||
|
|
||||||
will automatically generate:
|
|
||||||
|
|
||||||
```js
|
|
||||||
router: {
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
name: 'index',
|
|
||||||
path: '/',
|
|
||||||
component: 'pages/index.vue'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'team',
|
|
||||||
path: '/team',
|
|
||||||
component: 'pages/team/index.vue'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'team-about',
|
|
||||||
path: '/team/about',
|
|
||||||
component: 'pages/team/about.vue'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dynamic routes
|
|
||||||
|
|
||||||
To define a dynamic route with a param, you need to define a `.vue` file **prefixed by an underscore**.
|
|
||||||
|
|
||||||
This file tree:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pages/
|
|
||||||
--| users/
|
|
||||||
-----| _id.vue
|
|
||||||
-----| index.vue
|
|
||||||
```
|
|
||||||
|
|
||||||
will automatically generate:
|
|
||||||
|
|
||||||
```js
|
|
||||||
router: {
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
name: 'users',
|
|
||||||
path: '/users',
|
|
||||||
component: 'pages/users/index.vue'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'users-id',
|
|
||||||
path: '/users/:id',
|
|
||||||
component: 'pages/users/_id.vue'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Additional feature: validate (optional)
|
|
||||||
|
|
||||||
Nuxt.js lets you define a validator function inside your dynamic route component (In this example: `pages/users/_id.vue`).
|
|
||||||
|
|
||||||
If the validate method does not return `true`, Nuxt.js will automatically load the 404 error page.
|
|
||||||
|
|
||||||
```js
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
validate ({ params }) {
|
|
||||||
return /^\d+$/.test(params.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Nested Routes (children)
|
|
||||||
|
|
||||||
To define a nested route, you need to create a `.vue` file with the **same name as the directory** which contain your children views.
|
|
||||||
> Don't forget to put `<nuxt-child></nuxt-child>` inside your parent `.vue` file.
|
|
||||||
|
|
||||||
This file tree:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pages/
|
|
||||||
--| users/
|
|
||||||
-----| _id.vue
|
|
||||||
--| users.vue
|
|
||||||
```
|
|
||||||
|
|
||||||
will automatically generate:
|
|
||||||
|
|
||||||
```js
|
|
||||||
router: {
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
path: '/users',
|
|
||||||
component: 'pages/users.vue',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: ':id',
|
|
||||||
component: 'pages/users/_id.vue',
|
|
||||||
name: 'users-id'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dynamic Nested Routes
|
|
||||||
|
|
||||||
This file tree:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pages/
|
|
||||||
--| posts/
|
|
||||||
-----| _slug/
|
|
||||||
--------| _name.vue
|
|
||||||
--------| comments.vue
|
|
||||||
-----| _slug.vue
|
|
||||||
-----| index.vue
|
|
||||||
--| posts.vue
|
|
||||||
```
|
|
||||||
|
|
||||||
will automatically generate:
|
|
||||||
|
|
||||||
```js
|
|
||||||
router: {
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
path: '/posts',
|
|
||||||
component: 'pages/posts.vue',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path '',
|
|
||||||
component: 'pages/posts/index.vue',
|
|
||||||
name: 'posts'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ':slug',
|
|
||||||
component: 'pages/posts/_slug.vue',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'comments',
|
|
||||||
component: 'pages/posts/_slug/comments.vue',
|
|
||||||
name: 'posts-slug-comments'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ':name',
|
|
||||||
component: 'pages/posts/_slug/_name.vue',
|
|
||||||
name: 'posts-slug-name'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Demo
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
Go to [http://localhost:3000](http://localhost:3000) and navigate through the pages.
|
|
5
test/fixtures/dynamic-routes/nuxt.config.js
vendored
5
test/fixtures/dynamic-routes/nuxt.config.js
vendored
@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
build: {
|
|
||||||
vendor: ['axios']
|
|
||||||
}
|
|
||||||
}
|
|
13
test/fixtures/dynamic-routes/package.json
vendored
13
test/fixtures/dynamic-routes/package.json
vendored
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "nuxt-custom-routes",
|
|
||||||
"description": "",
|
|
||||||
"dependencies": {
|
|
||||||
"axios": "^0.15.2",
|
|
||||||
"nuxt": "latest"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"dev": "nuxt",
|
|
||||||
"build": "nuxt build",
|
|
||||||
"start": "nuxt start"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container">
|
|
||||||
<h2>Users</h2>
|
|
||||||
<ul class="users">
|
|
||||||
<li v-for="user in users">
|
|
||||||
<nuxt-link :to="'/users/'+user.id">{{ user.name }}</nuxt-link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return axios.get('https://jsonplaceholder.typicode.com/users')
|
|
||||||
.then((res) => {
|
|
||||||
return { users: res.data }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.container {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 100px;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
.users {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
.users li a {
|
|
||||||
display: inline-block;
|
|
||||||
width: 200px;
|
|
||||||
border: 1px #ddd solid;
|
|
||||||
padding: 10px;
|
|
||||||
text-align: left;
|
|
||||||
color: #222;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.users li a:hover {
|
|
||||||
color: orange;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,33 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="user">
|
|
||||||
<h3>{{ name }}</h3>
|
|
||||||
<h4>@{{ username }}</h4>
|
|
||||||
<p>Email : {{ email }}</p>
|
|
||||||
<p><nuxt-link to="/">List of users</nuxt-link></p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
validate ({ params }) {
|
|
||||||
return !isNaN(+params.id)
|
|
||||||
},
|
|
||||||
data ({ params, error }) {
|
|
||||||
return axios.get(`https://jsonplaceholder.typicode.com/users/${+params.id}`)
|
|
||||||
.then((res) => res.data)
|
|
||||||
.catch(() => {
|
|
||||||
error({ message: 'User not found', statusCode: 404 })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.user {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 100px;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
</style>
|
|
5
test/fixtures/with-config/layouts/app.vue
vendored
5
test/fixtures/with-config/layouts/app.vue
vendored
@ -1,5 +0,0 @@
|
|||||||
<template>
|
|
||||||
<nuxt-container>
|
|
||||||
<nuxt/>
|
|
||||||
</nuxt-container>
|
|
||||||
</template>
|
|
6
test/fixtures/with-config/layouts/custom.vue
vendored
Normal file
6
test/fixtures/with-config/layouts/custom.vue
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>Custom layout</h1>
|
||||||
|
<nuxt/>
|
||||||
|
</div>
|
||||||
|
</template>
|
6
test/fixtures/with-config/layouts/default.vue
vendored
Normal file
6
test/fixtures/with-config/layouts/default.vue
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>Default layout</h1>
|
||||||
|
<nuxt/>
|
||||||
|
</div>
|
||||||
|
</template>
|
6
test/fixtures/with-config/pages/about.vue
vendored
6
test/fixtures/with-config/pages/about.vue
vendored
@ -4,3 +4,9 @@
|
|||||||
<nuxt-link to="/">Home page</nuxt-link>
|
<nuxt-link to="/">Home page</nuxt-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
layout: 'custom'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
@ -27,9 +27,17 @@ test('/', async t => {
|
|||||||
test('/test/ (router base)', async t => {
|
test('/test/ (router base)', async t => {
|
||||||
const window = await nuxt.renderAndGetWindow(url('/test/'))
|
const window = await nuxt.renderAndGetWindow(url('/test/'))
|
||||||
const html = window.document.body.innerHTML
|
const html = window.document.body.innerHTML
|
||||||
|
t.true(html.includes('<h1>Default layout</h1>'))
|
||||||
t.true(html.includes('<h1>I have custom configurations</h1>'))
|
t.true(html.includes('<h1>I have custom configurations</h1>'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('/test/about (custom layout)', async t => {
|
||||||
|
const window = await nuxt.renderAndGetWindow(url('/test/about'))
|
||||||
|
const html = window.document.body.innerHTML
|
||||||
|
t.true(html.includes('<h1>Custom layout</h1>'))
|
||||||
|
t.true(html.includes('<h1>About page</h1>'))
|
||||||
|
})
|
||||||
|
|
||||||
test('/test/env', async t => {
|
test('/test/env', async t => {
|
||||||
const window = await nuxt.renderAndGetWindow(url('/test/env'))
|
const window = await nuxt.renderAndGetWindow(url('/test/env'))
|
||||||
const html = window.document.body.innerHTML
|
const html = window.document.body.innerHTML
|
||||||
|
Loading…
Reference in New Issue
Block a user