1
1
mirror of https://github.com/aelve/guide.git synced 2024-12-23 04:42:24 +03:00

Vue frontend initial

This commit is contained in:
zeot 2018-09-02 16:04:44 +03:00
parent 63348e0a7f
commit f1e18a97b4
31 changed files with 5607 additions and 0 deletions

22
front/.babelrc Normal file
View File

@ -0,0 +1,22 @@
{
"presets": [
["env", { "modules": false }],
["stage-2"]
],
"plugins": [
"transform-runtime",
["transform-imports", {
"vuetify": {
"transform": "vuetify/es5/components/${member}",
"preventFullImport": true
}
}]
],
"comments": false,
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["istanbul"]
}
}
}

9
front/.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

4
front/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.DS_Store
node_modules/
dist/
npm-debug.log

19
front/README.md Normal file
View File

@ -0,0 +1,19 @@
# Vuetify Webpack SSR Template
> Vuetify SSR Webpack Template
## Build Setup
``` bash
vue init vuetifyjs/webpack-ssr
cd webpack-ssr
# npm
npm install
# yarn
yarn
```
For additional information, please visit the [Official Documentation](https://vuetifyjs.com).

109
front/assets/App.vue Normal file
View File

@ -0,0 +1,109 @@
<template>
<v-app dark>
<v-navigation-drawer
:mini-variant="miniVariant"
:clipped="clipped"
v-model="drawer"
fixed
app
>
<v-list>
<v-list-tile
router
:to="item.to"
:key="i"
v-for="(item, i) in items"
exact
>
<v-list-tile-action>
<v-icon v-html="item.icon"></v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title v-text="item.title"></v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-navigation-drawer>
<v-toolbar fixed>
<v-toolbar-side-icon @click.stop="drawer = !drawer"></v-toolbar-side-icon>
</v-toolbar>
<v-toolbar fixed app :clipped-left="clipped">
<v-toolbar-side-icon @click.stop="drawer = !drawer"></v-toolbar-side-icon>
<v-btn
icon
@click.native.stop="miniVariant = !miniVariant"
>
<v-icon v-html="miniVariant ? 'chevron_right' : 'chevron_left'"></v-icon>
</v-btn>
<v-btn
icon
@click.native.stop="clipped = !clipped"
>
<v-icon>web</v-icon>
</v-btn>
<v-btn
icon
@click.native.stop="fixed = !fixed"
>
<v-icon>remove</v-icon>
</v-btn>
<v-toolbar-title v-text="title"></v-toolbar-title>
<v-spacer></v-spacer>
<v-btn
icon
@click.native.stop="rightDrawer = !rightDrawer"
>
<v-icon>menu</v-icon>
</v-btn>
</v-toolbar>
<v-content>
<v-container fluid>
<v-slide-y-transition mode="out-in">
<router-view></router-view>
</v-slide-y-transition>
</v-container>
</v-content>
<v-navigation-drawer
temporary
:right="right"
v-model="rightDrawer"
fixed
>
<v-list>
<v-list-tile @click.native="right = !right">
<v-list-tile-action>
<v-icon light>compare_arrows</v-icon>
</v-list-tile-action>
<v-list-tile-title>Switch drawer (click me)</v-list-tile-title>
</v-list-tile>
</v-list>
</v-navigation-drawer>
<v-footer :fixed="fixed" app>
<span>&copy; 2017</span>
</v-footer>
</v-app>
</template>
<script>
import Meta from 'mixins/meta'
export default {
mixins: [Meta],
data () {
return {
clipped: false,
drawer: true,
fixed: false,
items: [
{ icon: 'apps', title: 'Welcome', to: '/' },
{ icon: 'bubble_chart', title: 'Inspire', to: '/inspire' }
],
miniVariant: false,
right: true,
rightDrawer: false,
title: 'Vuetify.js'
}
}
}
</script>

76
front/assets/app.js Normal file
View File

@ -0,0 +1,76 @@
import Vue from 'vue'
import {
Vuetify,
VApp,
VNavigationDrawer,
VFooter,
VList,
VBtn,
VIcon,
VGrid,
VToolbar,
VCard,
transitions
} from 'vuetify'
import '../node_modules/vuetify/src/stylus/app.styl'
import App from './App.vue'
import Components from 'components/_index'
import { createStore } from 'store/index'
import { createRouter } from 'router/index'
import { sync } from 'vuex-router-sync'
Vue.use(Vuetify, {
components: {
VApp,
VNavigationDrawer,
VFooter,
VList,
VBtn,
VIcon,
VGrid,
VToolbar,
VCard,
transitions
},
theme: {
primary: '#ee44aa',
secondary: '#424242',
accent: '#82B1FF',
error: '#FF5252',
info: '#2196F3',
success: '#4CAF50',
warning: '#FFC107'
}
})
Object.keys(Components).forEach(key => {
Vue.component(key, Components[key])
})
// Expose a factory function that creates a fresh set of store, router,
// app instances on each call (which is called for each SSR request)
export function createApp (ssrContext) {
// create store and router instances
const store = createStore()
const router = createRouter()
// sync the router with the vuex store.
// this registers `store.state.route`
sync(store, router)
// create the app instance.
// here we inject the router, store and ssr context to all child components,
// making them available everywhere as `this.$router` and `this.$store`.
const app = new Vue({
router,
store,
ssrContext,
render: h => h(App)
})
// expose the app, the router and the store.
// note we are not mounting the app here, since bootstrapping will be
// different depending on whether we are in a browser or on the server.
return { app, router, store }
}

View File

@ -0,0 +1,41 @@
import Vue from 'vue'
import 'es6-promise/auto'
import { createApp } from './app'
const { app, router, store } = createApp()
// prime the store with server-initialized state.
// the state is determined during SSR and inlined in the page markup.
if (window.__INITIAL_STATE__) {
store.replaceState(window.__INITIAL_STATE__)
}
// wait until router has resolved all async before hooks
// and async components...
router.onReady(() => {
// Add router hook for handling asyncData.
// Doing it after initial route is resolved so that we don't double-fetch
// the data that we already have. Using router.beforeResolve() so that all
// async components are resolved.
router.beforeResolve((to, from, next) => {
const matched = router.getMatchedComponents(to)
const prevMatched = router.getMatchedComponents(from)
let diffed = false
const activated = matched.filter((c, i) => {
return diffed || (diffed = (prevMatched[i] !== c))
})
if (!activated.length) {
return next()
}
Promise.all(activated.map(c => {
if (c.asyncData) {
return c.asyncData({ store, route: to })
}
})).then(() => {
next()
}).catch(next)
})
// actually mount to DOM
app.$mount('#app')
})

View File

@ -0,0 +1,47 @@
import { createApp } from './app'
const isDev = process.env.NODE_ENV !== 'production'
// This exported function will be called by `bundleRenderer`.
// This is where we perform data-prefetching to determine the
// state of our application before actually rendering it.
// Since data fetching is async, this function is expected to
// return a Promise that resolves to the app instance.
export default context => {
return new Promise((resolve, reject) => {
const s = isDev && Date.now()
const { app, router, store } = createApp(context)
// set router's location
router.push(context.url)
// wait until router has resolved possible async hooks
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
// no matched routes
if (!matchedComponents.length) {
reject({ code: 404 })
}
// Call fetchData hooks on components matched by the route.
// A preFetch hook dispatches a store action and returns a Promise,
// which is resolved when the action is complete and store state has been
// updated.
Promise.all(matchedComponents.map(component => {
return component.asyncData && component.asyncData({
store,
route: router.currentRoute
})
})).then(() => {
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
// After all preFetch hooks are resolved, our store is now
// filled with the state needed to render the app.
// Expose the state on the render context, and let the request handler
// inline the state in the HTML response. This allows the client-side
// store to pick-up the server-side state without having to duplicate
// the initial data fetching on the client.
context.state = store.state
resolve(app)
}).catch(reject)
}, reject)
})
}

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<meta name="mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<meta name="description" content="aelve-front">
<meta name="keywords" content="">
<link rel="shortcut icon" href="/static/favicon.ico">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css">
</head>
<body>
<noscript id="deferred-styles"></noscript>
<!--vue-ssr-outlet-->
</body>
</html>

View File

@ -0,0 +1,67 @@
const path = require('path')
const webpack = require('webpack')
const MFS = require('memory-fs')
const clientConfig = require('./webpack.client.config')
const serverConfig = require('./webpack.server.config')
module.exports = function setupDevServer (app, cb) {
let bundle, clientManifest
let resolve
let resolved = false
const readyPromise = new Promise(r => { resolve = r })
const ready = (...args) => {
if (!resolved) resolve()
cb(...args)
}
// modify client config to work with hot middleware
clientConfig.entry.app = ['webpack-hot-middleware/client', clientConfig.entry.app]
clientConfig.output.filename = '[name].js'
clientConfig.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
)
// dev middleware
const clientCompiler = webpack(clientConfig)
const devMiddleware = require('webpack-dev-middleware')(clientCompiler, {
publicPath: clientConfig.output.publicPath,
noInfo: true
})
app.use(devMiddleware)
clientCompiler.plugin('done', () => {
const fs = devMiddleware.fileSystem
const readFile = file => fs.readFileSync(path.join(clientConfig.output.path, file), 'utf-8')
clientManifest = JSON.parse(readFile('vue-ssr-client-manifest.json'))
if (bundle) {
ready(bundle, {
clientManifest
})
}
})
// hot middleware
app.use(require('webpack-hot-middleware')(clientCompiler))
// watch and update server renderer
const serverCompiler = webpack(serverConfig)
const mfs = new MFS()
serverCompiler.outputFileSystem = mfs
serverCompiler.watch({}, (err, stats) => {
if (err) throw err
stats = stats.toJson()
stats.errors.forEach(err => console.error(err))
stats.warnings.forEach(err => console.warn(err))
const readFile = file => mfs.readFileSync(path.join(clientConfig.output.path, file), 'utf-8')
// read bundle generated by vue-ssr-webpack-plugin
bundle = JSON.parse(readFile('vue-ssr-server-bundle.json'))
if (clientManifest) {
ready(bundle, {
clientManifest
})
}
})
return readyPromise
}

View File

@ -0,0 +1,9 @@
module.exports = {
extractCSS: process.env.NODE_ENV === 'production',
preserveWhitespace: false,
postcss: [
require('autoprefixer')({
browsers: ['last 3 versions']
})
]
}

View File

@ -0,0 +1,86 @@
const path = require('path')
const webpack = require('webpack')
const vueConfig = require('./vue-loader.config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const isProd = process.env.NODE_ENV === 'production'
const resolve = (file) => path.resolve(__dirname, file)
module.exports = {
devtool: isProd
? false
: '#cheap-module-source-map',
output: {
path: resolve('../public'),
publicPath: '/public/',
filename: '[name].[chunkhash].js'
},
resolve: {
extensions: ['*', '.js', '.json', '.vue'],
alias: {
'assets': resolve('../assets'),
'components': resolve('../components'),
'examples': resolve('../pages/examples'),
'layouts': resolve('../layouts'),
'mixins': resolve('../mixins'),
'pages': resolve('../pages'),
'public': resolve('../public'),
'router': resolve('../router'),
'static': resolve('../static'),
'store': resolve('../store'),
'vue$': 'vue/dist/vue.common.js'
}
},
module: {
noParse: /es6-promise\.js$/, // avoid webpack shimming process
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
loader: ['vue-style-loader', 'css-loader']
},
{
test: /\.styl$/,
loader: ['vue-style-loader', 'css-loader', 'stylus-loader']
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
query: {
limit: 10000,
name: 'img/[name].[hash:7].[ext]'
}
}
]
},
performance: {
maxEntrypointSize: 300000,
hints: isProd ? 'warning' : false
},
plugins: isProd
? [
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false }
}),
new ExtractTextPlugin({
filename: 'common.[chunkhash].css'
}),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/
})
]
: [
new FriendlyErrorsPlugin()
]
}

View File

@ -0,0 +1,39 @@
const webpack = require('webpack')
const merge = require('webpack-merge')
const base = require('./webpack.base.config')
const SWPrecachePlugin = require('sw-precache-webpack-plugin')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
const config = merge(base, {
entry: {
app: './assets/entry-client.js'
},
plugins: [
// strip dev-only code in Vue source
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.VUE_ENV': '"client"'
}),
// extract vendor chunks for better caching
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module) {
// a module is extracted into the vendor chunk if...
return (
// it's inside node_modules
/node_modules/.test(module.context) &&
// and not a CSS file (due to extract-text-webpack-plugin limitation)
!/\.css$/.test(module.request)
)
}
}),
// extract webpack runtime & manifest to avoid vendor chunk hash changing
// on every build.
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest'
}),
new VueSSRClientPlugin()
]
})
module.exports = config

View File

@ -0,0 +1,28 @@
const webpack = require('webpack')
const merge = require('webpack-merge')
const base = require('./webpack.base.config')
const nodeExternals = require('webpack-node-externals')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
module.exports = merge(base, {
target: 'node',
devtool: '#source-map',
entry: './assets/entry-server.js',
output: {
filename: 'server-bundle.js',
libraryTarget: 'commonjs2'
},
// https://webpack.js.org/configuration/externals/#externals
// https://github.com/liady/webpack-node-externals
externals: nodeExternals({
// do not externalize CSS files in case we need to import it from a dep
whitelist: [/\.css$/, /vuetify/]
}),
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.VUE_ENV': '"server"'
}),
new VueSSRServerPlugin()
]
})

View File

@ -0,0 +1,3 @@
<template>
<h1>Vuetify</h1>
</template>

View File

@ -0,0 +1,5 @@
import Vuetify from './Vuetify'
export default {
Vuetify
}

35
front/mixins/meta.js Normal file
View File

@ -0,0 +1,35 @@
const meta = require('../router/meta.json')
export default {
watch: {
'$route' () {
this.setMeta()
}
},
created () {
if (process.env.VUE_ENV === 'client') return
const metaData = meta[this.$route.path] || {}
this.$ssrContext.title = metaData.title
this.$ssrContext.description = metaData.description
this.$ssrContext.keywords = metaData.keywords
},
mounted () {
this.setMeta()
},
methods: {
setMeta () {
if (typeof document === 'undefined') return
const metaData = meta[this.$route.path] || {}
document.title = metaData.title
document.querySelector('meta[name="description"]').setAttribute('content', metaData.description)
document.querySelector('meta[name="keywords"]').setAttribute('content', metaData.keywords)
}
}
}

67
front/package.json Normal file
View File

@ -0,0 +1,67 @@
{
"name": "aelve-front",
"description": "aelve-front",
"author": "",
"version": "0.0.1",
"scripts": {
"dev": "node server",
"start": "cross-env NODE_ENV=production node server",
"build": "rimraf dist && npm run build:client && npm run build:server",
"build:client": "cross-env NODE_ENV=production webpack --config build/webpack.client.config.js --progress --hide-modules",
"build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.config.js --progress --hide-modules"
},
"engines": {
"node": ">=7.0",
"npm": ">=4.0"
},
"dependencies": {
"babel-polyfill": "^6.23.0",
"compression": "^1.6.2",
"cross-env": "^5.0.0",
"es6-promise": "^4.1.0",
"express": "^4.15.2",
"extract-text-webpack-plugin": "^2.1.0",
"lru-cache": "^4.0.2",
"serialize-javascript": "^1.3.0",
"serve-favicon": "^2.3.2",
"vue": "^2.5.3",
"vuetify": "^1.0.0",
"vue-router": "^2.5.3",
"vue-server-renderer": "^2.5.3",
"vuex": "^2.3.1",
"vuex-router-sync": "^4.1.2"
},
"devDependencies": {
"autoprefixer": "^7.1.1",
"babel-core": "^6.22.1",
"babel-loader": "^7.0.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-preset-env": "^1.6.0",
"babel-preset-stage-2": "^6.24.1",
"babel-plugin-transform-imports": "^1.4.1",
"optimize-css-assets-webpack-plugin": "^2.0.0",
"babel-register": "^6.22.0",
"css-loader": "^0.28.0",
"eventsource-polyfill": "^0.9.6",
"file-loader": "^0.11.1",
"friendly-errors-webpack-plugin": "^1.6.1",
"highlight.js": "^9.8.0",
"html-webpack-plugin": "^2.24.1",
"pug": "^2.0.0-beta3",
"rimraf": "^2.6.1",
"script-ext-html-webpack-plugin": "^1.3.4",
"style-loader": "^0.18.1",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"sw-precache-webpack-plugin": "^0.9.1",
"url-loader": "^0.5.8",
"vue-loader": "^12.0.3",
"vue-style-loader": "^3.0.0",
"vue-template-compiler": "^2.5.3",
"webpack": "^2.2.1",
"webpack-dev-middleware": "^1.10.1",
"webpack-hot-middleware": "^2.17.1",
"webpack-merge": "^4.0.0",
"webpack-node-externals": "^1.5.4"
}
}

View File

@ -0,0 +1,15 @@
<template>
<v-layout>
<v-flex text-xs-center>
<img src="/static/v.png" alt="Vuetify.js" class="mb-5" />
<blockquote class="blockquote">
&#8220;First, solve the problem. Then, write the code.&#8221;
<footer>
<small>
<em>&mdash;John Johnson</em>
</small>
</footer>
</blockquote>
</v-flex>
</v-layout>
</template>

View File

@ -0,0 +1,22 @@
<template>
<v-layout column justify-center align-center>
<v-flex xs12 sm8 md6>
<div class="text-xs-center">
<img src="/static/v.png" alt="Vuetify.js" class="mb-5" />
</div>
<v-card>
<v-card-text>
<p>Welcome to the Webpack SSR template.</p>
<p>Vuetify is a progressive Material Design component framework for Vue.js. It was designed to empower developers to create amazing applications. For more information on Vuetify, check out the <a href="https://vuetifyjs.com" target="_blank">documentation</a>. If you have questions, please join the official <a href="https://gitter.im/vuetifyjs/Lobby" target="_blank" title="chat">gitter</a>. Find a bug? Report it on the github <a href="https://github.com/vuetifyjs/vuetify/issues" target="_blank" title="contribute">issue board</a>.</p>
<p>Thank you for developing with Vuetify and I look forward to bringing more exciting features in the future.</p>
<div class="text-xs-right">
<em><small>&mdash; John Leider</small></em>
</div>
</v-card-text>
<v-card-actions>
<v-btn color="primary" flat router to="/inspire">Continue</v-btn>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</template>

3
front/router/301.json Normal file
View File

@ -0,0 +1,3 @@
{
"/home": "/"
}

42
front/router/index.js Normal file
View File

@ -0,0 +1,42 @@
import Vue from 'vue'
import Router from 'vue-router'
// The meta data for your routes
const meta = require('./meta.json')
// Function to create routes
// Is default lazy but can be changed
function route (path, view) {
return {
path: path,
meta: meta[path],
component: resolve => import(`pages/${view}View.vue`).then(resolve)
}
}
Vue.use(Router)
export function createRouter () {
const router = new Router({
base: __dirname,
mode: 'history',
scrollBehavior: () => ({ y: 0 }),
routes: [
route('/', 'Welcome'),
route('/inspire', 'Inspire'),
// Global redirect for 404
{ path: '*', redirect: '/' }
]
})
// Send a pageview to Google Analytics
router.beforeEach((to, from, next) => {
if (typeof ga !== 'undefined') {
ga('set', 'page', to.path)
ga('send', 'pageview')
}
next()
})
return router
}

12
front/router/meta.json Normal file
View File

@ -0,0 +1,12 @@
{
"/": {
"title": "Vue.js 2 Material Component Framework",
"description": "My Vuetify.js Project",
"keywords": "vue 2.0, vuetify"
},
"/inspire": {
"title": "Create something amazing",
"description": "",
"keywords": ""
}
}

144
front/server.js Normal file
View File

@ -0,0 +1,144 @@
const fs = require('fs')
const path = require('path')
const LRU = require('lru-cache')
const express = require('express')
const favicon = require('serve-favicon')
const compression = require('compression')
const resolve = file => path.resolve(__dirname, file)
const { createBundleRenderer } = require('vue-server-renderer')
const redirects = require('./router/301.json')
const isProd = process.env.NODE_ENV === 'production'
const useMicroCache = process.env.MICRO_CACHE !== 'false'
const serverInfo =
`express/${require('express/package.json').version} ` +
`vue-server-renderer/${require('vue-server-renderer/package.json').version}`
const app = express()
const template = fs.readFileSync(resolve('./assets/index.template.html'), 'utf-8')
function createRenderer (bundle, options) {
// https://github.com/vuejs/vue/blob/dev/packages/vue-server-renderer/README.md#why-use-bundlerenderer
return createBundleRenderer(bundle, Object.assign(options, {
template,
// for component caching
cache: LRU({
max: 1000,
maxAge: 1000 * 60 * 15
}),
// this is only needed when vue-server-renderer is npm-linked
basedir: resolve('./public'),
// recommended for performance
runInNewContext: false
}))
}
let renderer
let readyPromise
if (isProd) {
// In production: create server renderer using built server bundle.
// The server bundle is generated by vue-ssr-webpack-plugin.
const bundle = require('./public/vue-ssr-server-bundle.json')
// The client manifests are optional, but it allows the renderer
// to automatically infer preload/prefetch links and directly add <script>
// tags for any async chunks used during render, avoiding waterfall requests.
const clientManifest = require('./public/vue-ssr-client-manifest.json')
renderer = createRenderer(bundle, {
clientManifest
})
} else {
// In development: setup the dev server with watch and hot-reload,
// and create a new renderer on bundle / index template update.
readyPromise = require('./build/setup-dev-server')(app, (bundle, options) => {
renderer = createRenderer(bundle, options)
})
}
const serve = (path, cache) => express.static(resolve(path), {
maxAge: cache && isProd ? 60 * 60 * 24 * 30 : 0
})
app.use(compression({ threshold: 0 }))
app.use(favicon('./static/favicon.ico'))
app.use('/static', serve('./static', true))
app.use('/public', serve('./public', true))
app.use('/static/robots.txt', serve('./robots.txt'))
app.get('/sitemap.xml', (req, res) => {
res.setHeader("Content-Type", "text/xml")
res.sendFile(resolve('./static/sitemap.xml'))
})
// 301 redirect for changed routes
Object.keys(redirects).forEach(k => {
app.get(k, (req, res) => res.redirect(301, redirects[k]))
})
// 1-second microcache.
// https://www.nginx.com/blog/benefits-of-microcaching-nginx/
const microCache = LRU({
max: 100,
maxAge: 1000
})
// since this app has no user-specific content, every page is micro-cacheable.
// if your app involves user-specific content, you need to implement custom
// logic to determine whether a request is cacheable based on its url and
// headers.
const isCacheable = req => useMicroCache
function render (req, res) {
const s = Date.now()
res.setHeader("Content-Type", "text/html")
res.setHeader("Server", serverInfo)
const handleError = err => {
if (err && err.code === 404) {
res.status(404).end('404 | Page Not Found')
} else {
// Render Error Page or Redirect
res.status(500).end('500 | Internal Server Error')
console.error(`error during render : ${req.url}`)
console.error(err.stack)
}
}
const cacheable = isCacheable(req)
if (cacheable) {
const hit = microCache.get(req.url)
if (hit) {
if (!isProd) {
console.log(`cache hit!`)
}
return res.end(hit)
}
}
const context = {
title: 'Vuetify', // default title
url: req.url
}
renderer.renderToString(context, (err, html) => {
if (err) {
return handleError(err)
}
res.end(html)
if (cacheable) {
microCache.set(req.url, html)
}
if (!isProd) {
console.log(`whole request: ${Date.now() - s}ms`)
}
})
}
app.get('*', isProd ? render : (req, res) => {
readyPromise.then(() => render(req, res))
})
const port = process.env.PORT || 8080
app.listen(port, '0.0.0.0', () => {
console.log(`server started at localhost:${port}`)
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
front/static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

2
front/static/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-agent: *
Disallow:

9
front/static/sitemap.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>/</loc>
<lastmod>2017-05-13</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
</urlset>

BIN
front/static/v.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

16
front/store/index.js Normal file
View File

@ -0,0 +1,16 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export function createStore () {
return new Vuex.Store({
state: {},
actions: {},
mutations: {},
getters: {}
})
}

4658
front/yarn.lock Normal file

File diff suppressed because it is too large Load Diff