mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-30 09:27:13 +00:00
feat(vue-app): build indicator (#5820)
* feat: inline HMR progress indicator * support router base * fix nuxt err * fix space * fix indentation * return in case of ws message parsing error * close ws on beforeDestroy * ui: Update loading indicator UI * builder: Add build.indicator option * ui: Use only logo and % * hotfix: Alphabetical order * hotfix: Add fixed with and add back v-if * minor style change * rename component to build-indicator * feat: animated progress * assign name to component * update test * naming consistency * render into app to prevent dom wrapping * extra new line * better App.js formatting * update snapshot * clear interval
This commit is contained in:
parent
e161d70a4f
commit
a759196865
@ -20,6 +20,7 @@ export default class TemplateContext {
|
|||||||
isDev: options.dev,
|
isDev: options.dev,
|
||||||
isTest: options.test,
|
isTest: options.test,
|
||||||
debug: options.debug,
|
debug: options.debug,
|
||||||
|
buildIndicator: options.dev && options.build.indicator,
|
||||||
vue: { config: options.vue.config },
|
vue: { config: options.vue.config },
|
||||||
fetch: options.fetch,
|
fetch: options.fetch,
|
||||||
mode: options.mode,
|
mode: options.mode,
|
||||||
|
@ -7,6 +7,7 @@ TemplateContext {
|
|||||||
],
|
],
|
||||||
"templateVars": Object {
|
"templateVars": Object {
|
||||||
"appPath": "./App.js",
|
"appPath": "./App.js",
|
||||||
|
"buildIndicator": undefined,
|
||||||
"components": Object {
|
"components": Object {
|
||||||
"ErrorPage": "relativeBuild(test_error_page)",
|
"ErrorPage": "relativeBuild(test_error_page)",
|
||||||
},
|
},
|
||||||
|
@ -3,6 +3,7 @@ import env from 'std-env'
|
|||||||
export default () => ({
|
export default () => ({
|
||||||
quiet: Boolean(env.ci || env.test),
|
quiet: Boolean(env.ci || env.test),
|
||||||
analyze: false,
|
analyze: false,
|
||||||
|
indicator: true,
|
||||||
profile: process.argv.includes('--profile'),
|
profile: process.argv.includes('--profile'),
|
||||||
extractCSS: false,
|
extractCSS: false,
|
||||||
crossorigin: undefined,
|
crossorigin: undefined,
|
||||||
|
@ -54,6 +54,7 @@ Object {
|
|||||||
"useShortDoctype": true,
|
"useShortDoctype": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"indicator": true,
|
||||||
"loaders": Object {
|
"loaders": Object {
|
||||||
"css": Object {
|
"css": Object {
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
|
@ -41,6 +41,7 @@ Object {
|
|||||||
"useShortDoctype": true,
|
"useShortDoctype": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"indicator": true,
|
||||||
"loaders": Object {
|
"loaders": Object {
|
||||||
"css": Object {},
|
"css": Object {},
|
||||||
"cssModules": Object {
|
"cssModules": Object {
|
||||||
@ -374,6 +375,7 @@ Object {
|
|||||||
"useShortDoctype": true,
|
"useShortDoctype": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"indicator": true,
|
||||||
"loaders": Object {
|
"loaders": Object {
|
||||||
"css": Object {},
|
"css": Object {},
|
||||||
"cssModules": Object {
|
"cssModules": Object {
|
||||||
|
1
packages/config/types/build.d.ts
vendored
1
packages/config/types/build.d.ts
vendored
@ -49,6 +49,7 @@ export interface NuxtConfigurationBuild {
|
|||||||
hardSource?: boolean
|
hardSource?: boolean
|
||||||
hotMiddleware?: WebpackHotMiddlewareOptions
|
hotMiddleware?: WebpackHotMiddlewareOptions
|
||||||
html?: { minify: HtmlMinifierOptions }
|
html?: { minify: HtmlMinifierOptions }
|
||||||
|
indicator?: boolean
|
||||||
loaders?: NuxtConfigurationLoaders
|
loaders?: NuxtConfigurationLoaders
|
||||||
optimization?: WebpackOptions.Optimization
|
optimization?: WebpackOptions.Optimization
|
||||||
optimizeCSS?: OptimizeCssAssetsWebpackPluginOptions | boolean
|
optimizeCSS?: OptimizeCssAssetsWebpackPluginOptions | boolean
|
||||||
|
@ -13,6 +13,7 @@ export const template = {
|
|||||||
'server.js',
|
'server.js',
|
||||||
'utils.js',
|
'utils.js',
|
||||||
'empty.js',
|
'empty.js',
|
||||||
|
'components/nuxt-build-indicator.vue',
|
||||||
'components/nuxt-error.vue',
|
'components/nuxt-error.vue',
|
||||||
'components/nuxt-loading.vue',
|
'components/nuxt-loading.vue',
|
||||||
'components/nuxt-child.js',
|
'components/nuxt-child.js',
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./components/nuxt-loading.vue") %>'<% } %>
|
<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./components/nuxt-loading.vue") %>'<% } %>
|
||||||
|
<%if (buildIndicator) { %>import NuxtBuildIndicator from './components/nuxt-build-indicator'<% } %>
|
||||||
<% css.forEach((c) => { %>
|
<% css.forEach((c) => { %>
|
||||||
import '<%= relativeToBuild(resolvePath(c.src || c, { isStyle: true })) %>'
|
import '<%= relativeToBuild(resolvePath(c.src || c, { isStyle: true })) %>'
|
||||||
<% }) %>
|
<% }) %>
|
||||||
@ -49,10 +50,7 @@ export default {
|
|||||||
domProps: {
|
domProps: {
|
||||||
id: '<%= globals.id %>'
|
id: '<%= globals.id %>'
|
||||||
}
|
}
|
||||||
}, [
|
}, [<% if (loading) { %>loadingEl, <% } %><%if (buildIndicator) { %>h(NuxtBuildIndicator), <% } %>transitionEl])
|
||||||
<% if (loading) { %>loadingEl,<% } %>
|
|
||||||
transitionEl
|
|
||||||
])
|
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
isOnline: true,
|
isOnline: true,
|
||||||
|
155
packages/vue-app/template/components/nuxt-build-indicator.vue
Normal file
155
packages/vue-app/template/components/nuxt-build-indicator.vue
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<transition appear>
|
||||||
|
<div class="nuxt__build_indicator" v-if="building">
|
||||||
|
<svg viewBox="0 0 96 72" version="1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<path d="M6 66h23l1-3 21-37L40 6 6 66zM79 66h11L62 17l-5 9 22 37v3zM54 31L35 66h38z"/>
|
||||||
|
<path d="M29 69v-1-2H6L40 6l11 20 3-6L44 3s-2-3-4-3-3 1-5 3L1 63c0 1-2 3 0 6 0 1 2 2 5 2h28c-3 0-4-1-5-2z" fill="#00C58E"/>
|
||||||
|
<path d="M95 63L67 14c0-1-2-3-5-3-1 0-3 0-4 3l-4 6 3 6 5-9 28 49H79a5 5 0 0 1 0 3c-2 2-5 2-5 2h16c1 0 4 0 5-2 1-1 2-3 0-6z" fill="#00C58E"/>
|
||||||
|
<path d="M79 69v-1-2-3L57 26l-3-6-3 6-21 37-1 3a5 5 0 0 0 0 3c1 1 2 2 5 2h40s3 0 5-2zM54 31l19 35H35l19-35z" fill="#FFF" fill-rule="nonzero"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
{{ animatedProgress }}%
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'nuxt-build-indicator',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
building: false,
|
||||||
|
progress: 0,
|
||||||
|
animatedProgress: 0,
|
||||||
|
reconnectAttempts: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (WebSocket === undefined) {
|
||||||
|
return // Unsupported
|
||||||
|
}
|
||||||
|
this.wsConnect('<%= router.base %>_loading/ws')
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.wsClose()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
progress(val, oldVal) {
|
||||||
|
// Cancel old animation
|
||||||
|
clearInterval(this._progressAnimation)
|
||||||
|
// Average progress may decrease but ignore it!
|
||||||
|
if (val < oldVal) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Jump to edge imediately
|
||||||
|
if (val < 10 || val > 90) {
|
||||||
|
this.animatedProgress = val
|
||||||
|
}
|
||||||
|
// Animate to value
|
||||||
|
this._progressAnimation = setInterval(() => {
|
||||||
|
const diff = this.progress - this.animatedProgress
|
||||||
|
if (diff > 0) {
|
||||||
|
this.animatedProgress++
|
||||||
|
} else {
|
||||||
|
clearInterval(this._progressAnimation)
|
||||||
|
}
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
wsConnect(path) {
|
||||||
|
if (path) {
|
||||||
|
const protocol = location.protocol === 'https:' ? 'wss' : 'ws'
|
||||||
|
this.wsURL = `${protocol}://${location.hostname}:${location.port}${path}`
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ws = new WebSocket(this.wsURL)
|
||||||
|
this.ws.onclose = this.onWSClose.bind(this)
|
||||||
|
this.ws.onerror = this.onWSError.bind(this)
|
||||||
|
this.ws.onmessage = this.onWSMessage.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
|
wsReconnect(e) {
|
||||||
|
this.reconnectAttempts++
|
||||||
|
if (this.reconnectAttempts > 10) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setTimeout(() => { this.wsConnect() }, 1000)
|
||||||
|
},
|
||||||
|
|
||||||
|
onWSClose(e) {
|
||||||
|
// https://tools.ietf.org/html/rfc6455#section-11.7
|
||||||
|
if (e.code !== 1000 && e.code !== 1005) {
|
||||||
|
this.wsReconnect() // Unkown error
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onWSError(error) {
|
||||||
|
if (error.code === 'ECONNREFUSED') {
|
||||||
|
this.wsReconnect(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onWSMessage(msg) {
|
||||||
|
let data = msg.data
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (data[0] === '{') {
|
||||||
|
data = JSON.parse(data)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.progress = Math.round(data.states.reduce((p, s) => p + s.progress, 0) / data.states.length)
|
||||||
|
if (!data.allDone) {
|
||||||
|
this.building = true
|
||||||
|
} else {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.building = false
|
||||||
|
this.animatedProgress = 0
|
||||||
|
this.progress = 0
|
||||||
|
clearInterval(this._progressAnimation)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
wsClose() {
|
||||||
|
if (this.ws) {
|
||||||
|
this.ws.close()
|
||||||
|
delete this.ws
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scopped>
|
||||||
|
.nuxt__build_indicator {
|
||||||
|
position: absolute;
|
||||||
|
font-family: monospace;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background-color: #2E495E;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 1px 1px 2px 0px rgba(0,0,0,0.2);
|
||||||
|
color: #00C48D;
|
||||||
|
width: 54px;
|
||||||
|
}
|
||||||
|
.v-enter-active, .v-leave-active {
|
||||||
|
transition-delay: 0.2s;
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 0.3s;
|
||||||
|
}
|
||||||
|
.v-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
svg {
|
||||||
|
width: 1.1em;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user