mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-27 08:02:01 +00:00
Merge branch 'dev' of github.com:Atinux/nuxt.js into dev
This commit is contained in:
commit
fdc84d2a4a
69
examples/async-component-injection/assets/css/common.css
Normal file
69
examples/async-component-injection/assets/css/common.css
Normal file
@ -0,0 +1,69 @@
|
||||
body {
|
||||
font-family: "Roboto", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, serif;
|
||||
text-rendering: optimizelegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
word-spacing: 1px;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
a {
|
||||
color: #666;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
.header {
|
||||
letter-spacing: 5px;
|
||||
margin: 50px auto 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.header a {
|
||||
font-size: 15px;
|
||||
color: #444;
|
||||
}
|
||||
.links {
|
||||
text-align: center;
|
||||
font-family: "Roboto", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, serif;
|
||||
color: #999;
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
}
|
||||
.links a {
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
margin: 0 3px;
|
||||
}
|
||||
.links img {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
.header,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif;
|
||||
font-weight: 400;
|
||||
color: #444;
|
||||
}
|
||||
.main {
|
||||
max-width: 600px;
|
||||
margin: 50px auto;
|
||||
padding: 0 30px 50px;
|
||||
position: relative;
|
||||
}
|
||||
@media screen and (max-width: 420px) {
|
||||
.header {
|
||||
margin: 40px auto 10px;
|
||||
}
|
||||
.header a {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
57
examples/async-component-injection/assets/css/index.css
Normal file
57
examples/async-component-injection/assets/css/index.css
Normal file
@ -0,0 +1,57 @@
|
||||
.main > ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
padding-top: 4px;
|
||||
}
|
||||
.main > ul > li {
|
||||
position: relative;
|
||||
padding: 30px 0 30px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
.main > ul > li:first-child {
|
||||
margin-top: -30px;
|
||||
}
|
||||
.main h2,
|
||||
.main h3 {
|
||||
letter-spacing: 1px;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.main h2 {
|
||||
font-size: 20px;
|
||||
letter-spacing: 1px;
|
||||
margin-left: 120px;
|
||||
}
|
||||
.main h2 a {
|
||||
color: #444;
|
||||
}
|
||||
.main h2 a:hover {
|
||||
color: #f33;
|
||||
}
|
||||
.main h3 {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 33px;
|
||||
}
|
||||
@media screen and (max-width: 420px) {
|
||||
.main h2 {
|
||||
font-size: 16px;
|
||||
margin-left: 0;
|
||||
}
|
||||
.main h2 a:hover {
|
||||
color: #f66;
|
||||
}
|
||||
.main h3 {
|
||||
font-size: 11px;
|
||||
position: static;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.main ul li {
|
||||
padding: 18px 0 20px;
|
||||
}
|
||||
.main ul li:first-child {
|
||||
margin-top: -35px;
|
||||
}
|
||||
}
|
307
examples/async-component-injection/assets/css/post.css
Normal file
307
examples/async-component-injection/assets/css/post.css
Normal file
@ -0,0 +1,307 @@
|
||||
.gutter pre {
|
||||
color: #999;
|
||||
}
|
||||
pre {
|
||||
color: #525252;
|
||||
}
|
||||
pre .function .keyword,
|
||||
pre .constant {
|
||||
color: #0092db;
|
||||
}
|
||||
pre .keyword,
|
||||
pre .attribute {
|
||||
color: #e96900;
|
||||
}
|
||||
pre .number,
|
||||
pre .literal {
|
||||
color: #ae81ff;
|
||||
}
|
||||
pre .tag,
|
||||
pre .tag .title,
|
||||
pre .change,
|
||||
pre .winutils,
|
||||
pre .flow,
|
||||
pre .lisp .title,
|
||||
pre .clojure .built_in,
|
||||
pre .nginx .title,
|
||||
pre .tex .special {
|
||||
color: #2973b7;
|
||||
}
|
||||
pre .class .title {
|
||||
color: #fff;
|
||||
}
|
||||
pre .symbol,
|
||||
pre .symbol .string,
|
||||
pre .value,
|
||||
pre .regexp {
|
||||
color: #42b983;
|
||||
}
|
||||
pre .title {
|
||||
color: #a6e22e;
|
||||
}
|
||||
pre .tag .value,
|
||||
pre .string,
|
||||
pre .subst,
|
||||
pre .haskell .type,
|
||||
pre .preprocessor,
|
||||
pre .ruby .class .parent,
|
||||
pre .built_in,
|
||||
pre .sql .aggregate,
|
||||
pre .django .template_tag,
|
||||
pre .django .variable,
|
||||
pre .smalltalk .class,
|
||||
pre .javadoc,
|
||||
pre .django .filter .argument,
|
||||
pre .smalltalk .localvars,
|
||||
pre .smalltalk .array,
|
||||
pre .attr_selector,
|
||||
pre .pseudo,
|
||||
pre .addition,
|
||||
pre .stream,
|
||||
pre .envvar,
|
||||
pre .apache .tag,
|
||||
pre .apache .cbracket,
|
||||
pre .tex .command,
|
||||
pre .prompt {
|
||||
color: #42b983;
|
||||
}
|
||||
pre .comment,
|
||||
pre .java .annotation,
|
||||
pre .python .decorator,
|
||||
pre .template_comment,
|
||||
pre .pi,
|
||||
pre .doctype,
|
||||
pre .deletion,
|
||||
pre .shebang,
|
||||
pre .apache .sqbracket,
|
||||
pre .tex .formula {
|
||||
color: #b3b3b3;
|
||||
}
|
||||
pre .coffeescript .javascript,
|
||||
pre .javascript .xml,
|
||||
pre .tex .formula,
|
||||
pre .xml .javascript,
|
||||
pre .xml .vbscript,
|
||||
pre .xml .css,
|
||||
pre .xml .cdata {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.main .post {
|
||||
position: relative;
|
||||
padding-bottom: 30px;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
.main .post h1,
|
||||
.main .post h2 {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0px;
|
||||
}
|
||||
.main .post h1 a:hover,
|
||||
.main .post h2 a:hover {
|
||||
border-bottom: 3px solid #666;
|
||||
}
|
||||
.main .post h1 {
|
||||
font-size: 32px;
|
||||
margin: 0 0 45px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.main .post h2 {
|
||||
font-size: 24px;
|
||||
margin: 60px 0 30px;
|
||||
position: relative;
|
||||
}
|
||||
.main .post h2:before {
|
||||
content: '';
|
||||
border-left: 5px solid #41b883;
|
||||
position: absolute;
|
||||
left: -15px;
|
||||
height: 75%;
|
||||
top: 12%;
|
||||
}
|
||||
.main .post h3 {
|
||||
margin: 30px 0 15px;
|
||||
}
|
||||
.main .post .date {
|
||||
font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
margin: 0 0 30px;
|
||||
letter-spacing: 1px;
|
||||
position: initial;
|
||||
text-transform: none;
|
||||
}
|
||||
.main .post .content {
|
||||
text-align: left;
|
||||
line-height: 1.8em;
|
||||
}
|
||||
.main .post .content p,
|
||||
.main .post .content ul,
|
||||
.main .post .content ol {
|
||||
margin: 1em 0 1.5em;
|
||||
}
|
||||
.main .post .content strong {
|
||||
font-weight: 600;
|
||||
color: #444;
|
||||
}
|
||||
.main .post .content ol {
|
||||
padding-left: 1.6em;
|
||||
}
|
||||
.main .post .content ul {
|
||||
padding-left: 15px;
|
||||
list-style-type: none;
|
||||
}
|
||||
.main .post .content ul li:before {
|
||||
position: absolute;
|
||||
font-weight: 600;
|
||||
content: " · ";
|
||||
margin: 0;
|
||||
left: 0;
|
||||
}
|
||||
.main .post .content a {
|
||||
color: #41b883;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
.main .post .content a:hover {
|
||||
color: #41b883;
|
||||
border-bottom-color: #41b883;
|
||||
}
|
||||
.main .post .content .highlight,
|
||||
.main .post .content .highlight table {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.main .post .content .highlight {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.main .post .content .highlight table,
|
||||
.main .post .content .highlight tr,
|
||||
.main .post .content .highlight td {
|
||||
padding: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.main .post .content code {
|
||||
font-family: "Roboto Mono", "Menlo", "Consolas", monospace;
|
||||
font-size: 13px;
|
||||
background-color: #f6f6f6;
|
||||
padding: 3px 10px;
|
||||
margin: 0 5px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.main .post .content pre {
|
||||
font-family: "Roboto Mono", "Menlo", "Consolas", monospace;
|
||||
font-size: 13px;
|
||||
overflow-x: auto;
|
||||
text-align: left;
|
||||
padding: 15px 25px;
|
||||
background-color: #f6f6f6;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
.main .post .content .code pre {
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
.main .post .content .gutter pre {
|
||||
padding: 15px 0 15px 15px;
|
||||
color: #75715e;
|
||||
border-top-left-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.main .post .content blockquote {
|
||||
margin: 2em 0;
|
||||
padding-left: 30px;
|
||||
border-left: 5px solid #e6e6e6;
|
||||
}
|
||||
.main .post .content blockquote p {
|
||||
font-size: 17px;
|
||||
font-style: italic;
|
||||
line-height: 1.8em;
|
||||
color: #999;
|
||||
}
|
||||
.main .post img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
.blog-nav {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: #999;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
letter-spacing: 1px;
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
.blog-nav:hover {
|
||||
color: #333;
|
||||
border-bottom-color: #333;
|
||||
}
|
||||
#newer {
|
||||
left: 40px;
|
||||
}
|
||||
#older {
|
||||
right: 40px;
|
||||
}
|
||||
.show-comments {
|
||||
font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
.show-comments a {
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
}
|
||||
.show-comments a:hover {
|
||||
color: #666;
|
||||
}
|
||||
@media screen and (max-width: 900px) {
|
||||
.main .post {
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
.blog-nav {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
}
|
||||
#newer {
|
||||
left: 0;
|
||||
}
|
||||
#older {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 420px) {
|
||||
.main {
|
||||
margin-top: 32px;
|
||||
}
|
||||
.main .post h1 {
|
||||
font-size: 24px;
|
||||
margin: 0 0 30px;
|
||||
}
|
||||
.main .post h2 {
|
||||
font-size: 20px;
|
||||
margin: 30px 0 15px;
|
||||
}
|
||||
.main .post h3 {
|
||||
font-size: 16px;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
.main .post .date {
|
||||
font-size: 12px;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
.main .post .content {
|
||||
font-size: 15px;
|
||||
}
|
||||
.main .post .content pre {
|
||||
font-size: 12px;
|
||||
}
|
||||
.main .post .content blockquote p {
|
||||
font-size: 16px;
|
||||
}
|
||||
.blog-nav {
|
||||
font-size: 14px;
|
||||
color: #444;
|
||||
}
|
||||
}
|
BIN
examples/async-component-injection/assets/img/github.png
Normal file
BIN
examples/async-component-injection/assets/img/github.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
examples/async-component-injection/assets/img/swimmer.jpg
Normal file
BIN
examples/async-component-injection/assets/img/swimmer.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 139 KiB |
BIN
examples/async-component-injection/assets/img/twitter.png
Normal file
BIN
examples/async-component-injection/assets/img/twitter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
15
examples/async-component-injection/layouts/default.vue
Normal file
15
examples/async-component-injection/layouts/default.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="header">
|
||||
<nuxt-link to="/">NUXT BLOG</nuxt-link>
|
||||
</div>
|
||||
<p class="links">
|
||||
<a href="https://twitter.com/nuxt_js" target="_blank"><img src="~assets/img/twitter.png"></a>
|
||||
<a href="https://github.com/nuxt/nuxt.js/tree/dev/examples/async-component-injection" target="_blank"><img src="~assets/img/github.png"></a>
|
||||
</p>
|
||||
<div class="main">
|
||||
<nuxt/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
22
examples/async-component-injection/nuxt.config.js
Normal file
22
examples/async-component-injection/nuxt.config.js
Normal file
@ -0,0 +1,22 @@
|
||||
module.exports = {
|
||||
head: {
|
||||
title: 'Nuxt Blog',
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' }
|
||||
],
|
||||
link: [
|
||||
{ rel: 'icon', href: '/favicon.ico', type: 'image/x-icon' },
|
||||
{ rel: 'stylesheet', href: 'http://fonts.googleapis.com/css?family=Montserrat|Roboto:400,400italic,600|Roboto+Mono', type: 'text/css' }
|
||||
]
|
||||
},
|
||||
css: [
|
||||
'@/assets/css/common.css'
|
||||
],
|
||||
generate: {
|
||||
routes: [
|
||||
'/deep-dive-into-ocean',
|
||||
'/welcome-to-my-blog'
|
||||
]
|
||||
}
|
||||
}
|
21
examples/async-component-injection/pages/_slug.vue
Normal file
21
examples/async-component-injection/pages/_slug.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="post">
|
||||
<component :is="component"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// See https://vuejs.org/v2/guide/components.html#Advanced-Async-Components
|
||||
const getPost = (slug) => ({
|
||||
component: import(`@/posts/${slug}`),
|
||||
error: require('@/posts/404')
|
||||
})
|
||||
|
||||
export default {
|
||||
beforeCreate () {
|
||||
this.component = () => getPost(this.$route.params.slug)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="@/assets/css/post.css"/>
|
22
examples/async-component-injection/pages/index.vue
Normal file
22
examples/async-component-injection/pages/index.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<ul>
|
||||
<li v-for="(post, index) in posts" :key="index">
|
||||
<h3>{{ post.date }}</h3>
|
||||
<h2><nuxt-link :to="post.link">{{ post.title }}</nuxt-link></h2>
|
||||
</li>
|
||||
<li style="border:none;text-align: center;font-size: 14px;">Design from <a href="http://blog.evanyou.me" target="_blank">EvanYou.me</a></li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: () => ({
|
||||
posts: [
|
||||
{ date: 'Jul 10, 2017', title: 'Deep dive into the Ocean', link: '/deep-dive-into-ocean' },
|
||||
{ date: 'Jul 08, 2017', title: 'Welcome to my blog', link: '/welcome-to-my-blog' }
|
||||
]
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src="@/assets/css/index.css"/>
|
3
examples/async-component-injection/posts/404.vue
Normal file
3
examples/async-component-injection/posts/404.vue
Normal file
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<h1 style="text-align: center;">Article not found</h1>
|
||||
</template>
|
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3 class="date">Jul 10, 2017</h3>
|
||||
<h1>Deep dive into the Ocean</h1>
|
||||
<div class="content">
|
||||
<img src="~assets/img/swimmer.jpg">
|
||||
<h2>Subtitle #1</h2>
|
||||
<p>Far far away, behind the word mountains, far from the countries Vokalia and Consonantia, there live the blind texts. Separated they live in Bookmarksgrove right at the coast of the Semantics, a large language ocean. A small river named Duden flows by their place and supplies it with the necessary regelialia. It is a paradisematic country, in which roasted parts of sentences fly into your mouth. Even the all-powerful Pointing has no control about the blind texts it is an almost unorthographic life One day however a small line of blind text by the name of Lorem Ipsum decided to leave for the far World of Grammar.</p>
|
||||
<h2>Another subtitle</h2>
|
||||
<ul>
|
||||
<li>Vue.js</li>
|
||||
<li>Nuxt.js</li>
|
||||
<li>= <3</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3 class="date">Jul 08, 2017</h3>
|
||||
<h1>Welcome to my blog</h1>
|
||||
<div class="content">
|
||||
<h2>What is Lorem Ipsum?</h2>
|
||||
<p><b>Lorem Ipsum</b> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
BIN
examples/async-component-injection/static/favicon.ico
Normal file
BIN
examples/async-component-injection/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Article 1</h1>
|
||||
<p>Hello I am the first article :)</p>
|
||||
<nuxt-link to="/">Home</nuxt-link>
|
||||
</div>
|
||||
</template>
|
@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Article 2</h1>
|
||||
<p>Hello I am the second article!</p>
|
||||
<nuxt-link to="/">Home</nuxt-link>
|
||||
</div>
|
||||
</template>
|
@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<component :is="comp"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const components = {}
|
||||
const files = require.context('@/articles', false, /\.vue$/)
|
||||
files.keys().forEach((filename) => {
|
||||
const name = filename.replace('./', '').replace('.vue', '')
|
||||
components[name] = () => import('@/articles/' + name + '.vue').then((m) => m.default || m)
|
||||
})
|
||||
|
||||
export default {
|
||||
async asyncData({ params, error }) {
|
||||
return { comp: params.slug }
|
||||
},
|
||||
components
|
||||
}
|
||||
</script>
|
@ -1,9 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Articles</h1>
|
||||
<ul>
|
||||
<li><nuxt-link to="/article-1">Article #1</nuxt-link></li>
|
||||
<li><nuxt-link to="/article-2">Article #2</nuxt-link></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
1
lib/app/empty.js
Normal file
1
lib/app/empty.js
Normal file
@ -0,0 +1 @@
|
||||
// This file is intentially left empty for noop aliases
|
@ -9,40 +9,16 @@ import Nuxt from './components/nuxt.vue'
|
||||
import App from '<%= appPath %>'
|
||||
import { getContext } from './utils'
|
||||
<% if (store) { %>import { createStore } from './store.js'<% } %>
|
||||
|
||||
if (process.browser) {
|
||||
// window.onNuxtReady(() => console.log('Ready')) hook
|
||||
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
||||
window._nuxtReadyCbs = []
|
||||
window.onNuxtReady = function (cb) {
|
||||
window._nuxtReadyCbs.push(cb)
|
||||
}
|
||||
}
|
||||
|
||||
<% if (plugins.filter(p => p.ssr).length) { %>
|
||||
// Require plugins
|
||||
<% plugins.filter(p => p.ssr).forEach(plugin => { %>
|
||||
let <%= plugin.name %> = require('<%= relativeToBuild(plugin.src) %>')
|
||||
<%= plugin.name %> = <%= plugin.name %>.default || <%= plugin.name %>
|
||||
<% plugins.forEach(plugin => { %>import <%= plugin.name %> from '<%= plugin.name %>'
|
||||
<% }) %>
|
||||
<% } %>
|
||||
|
||||
<% if (plugins.filter(p => !p.ssr).length) { %>
|
||||
// Require browser-only plugins
|
||||
if (process.browser) {
|
||||
<% plugins.filter(p => !p.ssr).forEach(plugin => { %>
|
||||
let <%= plugin.name %> = require('<%= relativeToBuild(plugin.src) %>')
|
||||
<%= plugin.name %> = <%= plugin.name %>.default || <%= plugin.name %>
|
||||
<% }) %>
|
||||
}
|
||||
<% } %>
|
||||
|
||||
|
||||
// Component: <nuxt-child>
|
||||
Vue.component(NuxtChild.name, NuxtChild)
|
||||
|
||||
// Component: <nuxt-link>
|
||||
Vue.component(NuxtLink.name, NuxtLink)
|
||||
// Component: <nuxt>
|
||||
|
||||
// Component: <nuxt>`
|
||||
Vue.component(Nuxt.name, Nuxt)
|
||||
|
||||
// vue-meta configuration
|
||||
@ -132,24 +108,17 @@ async function createApp (ssrContext) {
|
||||
route: router.currentRoute,
|
||||
next,
|
||||
error: app._nuxt.error.bind(app),
|
||||
<% if(store) { %> store,<% } %>
|
||||
<% if(store) { %>store,<% } %>
|
||||
req: ssrContext ? ssrContext.req : undefined,
|
||||
res: ssrContext ? ssrContext.res : undefined,
|
||||
}, app)
|
||||
|
||||
<% if (plugins.filter(p => p.ssr).length) { %>
|
||||
<% plugins.filter(p => p.ssr).forEach(plugin => { %>
|
||||
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx)
|
||||
<% }) %>
|
||||
<% } %>
|
||||
|
||||
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx)<% }) %>
|
||||
<% if (plugins.filter(p => !p.ssr).length) { %>
|
||||
if (process.browser) {
|
||||
<% plugins.filter(p => !p.ssr).forEach(plugin => { %>
|
||||
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx)
|
||||
<% }) %>
|
||||
}
|
||||
<% } %>
|
||||
if (process.browser) { <% plugins.filter(p => !p.ssr).forEach(plugin => { %>
|
||||
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx)<% }) %>
|
||||
}<% } %>
|
||||
|
||||
return {
|
||||
app,
|
||||
@ -158,4 +127,4 @@ async function createApp (ssrContext) {
|
||||
}
|
||||
}
|
||||
|
||||
export { createApp, NuxtError }
|
||||
export { createApp, NuxtError }
|
@ -2,6 +2,15 @@ import Vue from 'vue'
|
||||
|
||||
const noopData = () => ({})
|
||||
|
||||
// window.onNuxtReady(() => console.log('Ready')) hook
|
||||
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
||||
if (process.browser) {
|
||||
window._nuxtReadyCbs = []
|
||||
window.onNuxtReady = function (cb) {
|
||||
window._nuxtReadyCbs.push(cb)
|
||||
}
|
||||
}
|
||||
|
||||
export function applyAsyncData (Component, asyncData = {}) {
|
||||
const ComponentData = Component.options.data || noopData
|
||||
// Prevent calling this method for each request on SSR context
|
||||
|
@ -49,6 +49,14 @@ export default class Builder extends Tapable {
|
||||
this._buildStatus = STATUS.INITIAL
|
||||
}
|
||||
|
||||
get plugins () {
|
||||
return this.options.plugins.map((p, i) => {
|
||||
if (typeof p === 'string') p = { src: p }
|
||||
p.src = r(this.options.srcDir, p.src)
|
||||
return { src: p.src, ssr: (p.ssr !== false), name: `plugin${i}` }
|
||||
})
|
||||
}
|
||||
|
||||
async build () {
|
||||
// Avoid calling build() method multiple times when dev:true
|
||||
/* istanbul ignore if */
|
||||
@ -119,6 +127,7 @@ export default class Builder extends Tapable {
|
||||
'router.js',
|
||||
'server.js',
|
||||
'utils.js',
|
||||
'empty.js',
|
||||
'components/nuxt-error.vue',
|
||||
'components/nuxt-loading.vue',
|
||||
'components/nuxt-child.js',
|
||||
@ -137,11 +146,7 @@ export default class Builder extends Tapable {
|
||||
middleware: fs.existsSync(join(this.options.srcDir, 'middleware')),
|
||||
store: this.options.store,
|
||||
css: this.options.css,
|
||||
plugins: this.options.plugins.map((p, i) => {
|
||||
if (typeof p === 'string') p = { src: p }
|
||||
p.src = r(this.options.srcDir, p.src)
|
||||
return { src: p.src, ssr: (p.ssr !== false), name: `plugin${i}` }
|
||||
}),
|
||||
plugins: this.plugins,
|
||||
appPath: './App.vue',
|
||||
layouts: Object.assign({}, this.options.layouts),
|
||||
loading: typeof this.options.loading === 'string' ? this.relativeToBuild(this.options.srcDir, this.options.loading) : this.options.loading,
|
||||
@ -264,15 +269,33 @@ export default class Builder extends Tapable {
|
||||
|
||||
async webpackBuild () {
|
||||
debug('Building files...')
|
||||
let compilersOptions = []
|
||||
const compilersOptions = []
|
||||
|
||||
// Client
|
||||
let clientConfig = clientWebpackConfig.call(this)
|
||||
const clientConfig = clientWebpackConfig.call(this)
|
||||
compilersOptions.push(clientConfig)
|
||||
|
||||
// Server
|
||||
let serverConfig = serverWebpackConfig.call(this)
|
||||
compilersOptions.push(serverConfig)
|
||||
const serverConfig = serverWebpackConfig.call(this)
|
||||
if (this.options.build.ssr) {
|
||||
compilersOptions.push(serverConfig)
|
||||
}
|
||||
|
||||
// Alias plugins to their real path
|
||||
this.plugins.forEach(p => {
|
||||
const src = this.relativeToBuild(p.src)
|
||||
|
||||
// Client config
|
||||
if (!clientConfig.resolve.alias[p.name]) {
|
||||
clientConfig.resolve.alias[p.name] = src
|
||||
}
|
||||
|
||||
// Server config
|
||||
if (!serverConfig.resolve.alias[p.name]) {
|
||||
// Alias to noop for ssr:false plugins
|
||||
serverConfig.resolve.alias[p.name] = p.ssr ? src : './empty.js'
|
||||
}
|
||||
})
|
||||
|
||||
// Simulate webpack multi compiler interface
|
||||
// Separate compilers are simpler, safer and faster
|
||||
|
@ -60,11 +60,23 @@ export default function webpackClientConfig () {
|
||||
config.plugins = []
|
||||
}
|
||||
|
||||
// Generate output HTML
|
||||
// Generate output HTML for SSR
|
||||
if (this.options.build.ssr) {
|
||||
config.plugins.push(
|
||||
new HTMLPlugin({
|
||||
filename: 'index.ssr.html',
|
||||
template: this.options.appTemplatePath,
|
||||
inject: false // Resources will be injected using bundleRenderer
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// Generate output HTML for SPA
|
||||
config.plugins.push(
|
||||
new HTMLPlugin({
|
||||
filename: 'index.spa.html',
|
||||
template: this.options.appTemplatePath,
|
||||
inject: this.options.ssr === false,
|
||||
inject: true,
|
||||
chunksSortMode: 'dependency'
|
||||
})
|
||||
)
|
||||
|
@ -46,16 +46,57 @@ export default function Options (_options) {
|
||||
options.store = true
|
||||
}
|
||||
|
||||
// Resolve mode
|
||||
let mode = options.mode
|
||||
if (typeof mode === 'function') {
|
||||
mode = mode()
|
||||
}
|
||||
if (typeof mode === 'string') {
|
||||
mode = Modes[mode]
|
||||
}
|
||||
|
||||
// Apply mode
|
||||
_.defaultsDeep(options, mode)
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
const Modes = {
|
||||
universal: {
|
||||
build: {
|
||||
ssr: true
|
||||
},
|
||||
render: {
|
||||
ssr: true
|
||||
}
|
||||
},
|
||||
spa: {
|
||||
build: {
|
||||
ssr: false
|
||||
},
|
||||
render: {
|
||||
ssr: false
|
||||
}
|
||||
},
|
||||
static: {
|
||||
build: {
|
||||
ssr: true
|
||||
},
|
||||
render: {
|
||||
ssr: 'static'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultOptions = {
|
||||
dev: (process.env.NODE_ENV !== 'production'),
|
||||
mode: 'universal',
|
||||
dev: process.env.NODE_ENV !== 'production',
|
||||
buildDir: '.nuxt',
|
||||
nuxtAppDir: resolve(__dirname, '../lib/app/'), // Relative to dist
|
||||
build: {
|
||||
analyze: false,
|
||||
extractCSS: false,
|
||||
ssr: undefined,
|
||||
publicPath: '/_nuxt/',
|
||||
filenames: {
|
||||
css: 'common.[chunkhash].css',
|
||||
@ -64,7 +105,6 @@ export const defaultOptions = {
|
||||
app: 'nuxt.bundle.[chunkhash].js'
|
||||
},
|
||||
vendor: [],
|
||||
loaders: [],
|
||||
plugins: [],
|
||||
babel: {},
|
||||
postcss: [],
|
||||
@ -133,10 +173,10 @@ export const defaultOptions = {
|
||||
scrollBehavior: null,
|
||||
fallback: false
|
||||
},
|
||||
ssr: true,
|
||||
render: {
|
||||
bundleRenderer: {},
|
||||
resourceHints: true,
|
||||
ssr: undefined,
|
||||
http2: {
|
||||
push: false
|
||||
},
|
||||
|
@ -42,7 +42,8 @@ export default class Renderer extends Tapable {
|
||||
this.resources = {
|
||||
clientManifest: null,
|
||||
serverBundle: null,
|
||||
appTemplate: null,
|
||||
ssrTemplate: null,
|
||||
spaTemplate: null,
|
||||
errorTemplate: parseTemplate('<pre>{{ stack }}</pre>') // Will be loaded on ready
|
||||
}
|
||||
|
||||
@ -121,7 +122,18 @@ export default class Renderer extends Tapable {
|
||||
}
|
||||
|
||||
get noSSR () {
|
||||
return this.options.ssr === false
|
||||
return this.options.render.ssr === false
|
||||
}
|
||||
|
||||
get staticSSR () {
|
||||
return this.options.render.ssr === 'static'
|
||||
}
|
||||
|
||||
get isReady () {
|
||||
if (this.noSSR) {
|
||||
return this.resources.spaTemplate
|
||||
}
|
||||
return this.bundleRenderer && this.resources.ssrTemplate
|
||||
}
|
||||
|
||||
createRenderer () {
|
||||
@ -297,7 +309,7 @@ export default class Renderer extends Tapable {
|
||||
|
||||
async renderRoute (url, context = {}) {
|
||||
/* istanbul ignore if */
|
||||
if (!(this.noSSR || this.bundleRenderer) || !this.resources.appTemplate) {
|
||||
if (!this.isReady) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => resolve(this.renderRoute(url, context)), 1000)
|
||||
})
|
||||
@ -315,7 +327,7 @@ export default class Renderer extends Tapable {
|
||||
let APP = '<div id="__nuxt"></div>'
|
||||
let HEAD = ''
|
||||
|
||||
let html = this.resources.appTemplate({
|
||||
let html = this.resources.spaTemplate({
|
||||
HTML_ATTRS: '',
|
||||
BODY_ATTRS: '',
|
||||
HEAD,
|
||||
@ -340,15 +352,19 @@ export default class Renderer extends Tapable {
|
||||
}
|
||||
|
||||
let resourceHints = ''
|
||||
if (this.options.render.resourceHints) {
|
||||
resourceHints = context.renderResourceHints()
|
||||
HEAD += resourceHints
|
||||
}
|
||||
HEAD += context.renderStyles()
|
||||
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};</script>`
|
||||
APP += context.renderScripts()
|
||||
|
||||
let html = this.resources.appTemplate({
|
||||
if (!this.staticSSR) {
|
||||
if (this.options.render.resourceHints) {
|
||||
resourceHints = context.renderResourceHints()
|
||||
HEAD += resourceHints
|
||||
}
|
||||
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};</script>`
|
||||
APP += context.renderScripts()
|
||||
}
|
||||
|
||||
HEAD += context.renderStyles()
|
||||
|
||||
let html = this.resources.ssrTemplate({
|
||||
HTML_ATTRS: 'data-n-head-ssr ' + m.htmlAttrs.text(),
|
||||
BODY_ATTRS: m.bodyAttrs.text(),
|
||||
HEAD,
|
||||
@ -422,8 +438,13 @@ const resourceMap = [
|
||||
transform: JSON.parse
|
||||
},
|
||||
{
|
||||
key: 'appTemplate',
|
||||
fileName: 'index.html',
|
||||
key: 'ssrTemplate',
|
||||
fileName: 'index.ssr.html',
|
||||
transform: parseTemplate
|
||||
},
|
||||
{
|
||||
key: 'spaTemplate',
|
||||
fileName: 'index.spa.html',
|
||||
transform: parseTemplate
|
||||
}
|
||||
]
|
||||
|
@ -7,3 +7,5 @@ function $reverseStr (str) {
|
||||
}
|
||||
|
||||
Vue.prototype.$reverseStr = $reverseStr
|
||||
|
||||
export default undefined
|
||||
|
7
test/fixtures/module/router.js
vendored
7
test/fixtures/module/router.js
vendored
@ -3,18 +3,21 @@ import Router from 'vue-router'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
const indexPage = () => import('~/views/index.vue').then(m => m.default || m)
|
||||
const aboutPage = () => import('~/views/about.vue').then(m => m.default || m)
|
||||
|
||||
export function createRouter () {
|
||||
return new Router({
|
||||
mode: 'history',
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
component: require('~/views/index.vue').default,
|
||||
component: indexPage,
|
||||
name: 'index'
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
component: require('~/views/about.vue').default,
|
||||
component: aboutPage,
|
||||
name: 'about'
|
||||
}
|
||||
]
|
||||
|
@ -90,7 +90,7 @@ test('/test/about-bis (added with extendRoutes)', async t => {
|
||||
|
||||
test('Check stats.json generated by build.analyze', t => {
|
||||
const stats = require(resolve(__dirname, 'fixtures/with-config/.nuxt/dist/stats.json'))
|
||||
t.is(stats.assets.length, 26)
|
||||
t.is(stats.assets.length, 27)
|
||||
})
|
||||
|
||||
test('Check /test.txt with custom serve-static options', async t => {
|
||||
|
Loading…
Reference in New Issue
Block a user