├── src ├── assets │ └── css │ │ ├── app.css │ │ └── nprogress.css ├── components │ ├── avator │ │ ├── assets │ │ │ └── avator.png │ │ └── Avator.vue │ ├── header │ │ ├── Header.vue │ │ └── TopHeader.vue │ └── menu │ │ └── Menu.vue ├── api │ ├── index.js │ ├── create-api-client.js │ └── create-api-server.js ├── store │ ├── index.js │ ├── loader.js │ └── modules │ │ ├── Counter.js │ │ ├── Theme.js │ │ └── Themes.js ├── App.vue ├── util │ ├── util.js │ └── title.js ├── app.js ├── views │ ├── Counter.vue │ ├── Theme.vue │ ├── Themes.vue │ ├── About.vue │ ├── Widget.vue │ └── Home.vue ├── router │ └── index.js ├── index.template.html ├── client-entry.js └── server-entry.js ├── .eslintignore ├── .travis.yml ├── .gitignore ├── public ├── images │ └── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── 404.html ├── css │ ├── bootstrap-theme.min.css │ └── bootstrap-theme.css └── js │ ├── bootstrap.min.js │ └── bootstrap.js ├── .babelrc ├── test └── unit │ ├── index.js │ ├── .eslintrc │ ├── specs │ └── Home.spec.js │ └── karma.conf.js ├── .editorconfig ├── config └── index.js ├── processes.json ├── .eslintrc.js ├── README.md ├── package.json └── server.js /src/assets/css/app.css: -------------------------------------------------------------------------------- 1 | 2 | .view { 3 | padding: 20px 4 | } 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "7" 5 | script: npm run unit --single-run -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | output/ 5 | npm-debug.log 6 | .vscode/ 7 | yarn.lock -------------------------------------------------------------------------------- /public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-ssr-template/HEAD/public/images/favicon.ico -------------------------------------------------------------------------------- /src/components/avator/assets/avator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-ssr-template/HEAD/src/components/avator/assets/avator.png -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-ssr-template/HEAD/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-ssr-template/HEAD/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-ssr-template/HEAD/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-ssr-template/HEAD/public/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | import config from 'config' 2 | import { createAPI } from 'create-api' 3 | 4 | const api = createAPI(config) 5 | 6 | export default api 7 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["es2015", { "modules": false }], "stage-2"], 3 | "ignore": ["node_modules/*"] 4 | } -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | // require all test files (files that ends with .spec.js) 2 | var testsContext = require.context('./specs', true, /\.spec$/) 3 | testsContext.keys().forEach(testsContext) 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |{{jsons}}
8 | {{item.description}}
15 |
9 |
10 | const isProd = process.env.NODE_ENV === 'production'
11 |
12 | const app = express()
13 |
14 | let renderer
15 | if (isProd) {
16 | // 生产环境使用本地打包文件来渲染
17 | const bundle = require('./output/vue-ssr-bundle.json')
18 | const template = fs.readFileSync(resolve('./output/index.html'), 'utf-8')
19 | renderer = createRenderer(bundle, template)
20 | } else {
21 | // 开发环境使用webpack热更新服务
22 | require('./build/dev-server')(app, (bundle, template) => {
23 | renderer = createRenderer(bundle, template)
24 | })
25 | }
26 |
27 | function createRenderer(bundle, template) {
28 | return require('vue-server-renderer').createBundleRenderer(bundle, {
29 | template,
30 | cache: require('lru-cache')({
31 | max: 1000,
32 | maxAge: 1000 * 60 * 15
33 | })
34 | })
35 | }
36 |
37 | app.get('*', (req, res) => {
38 | if (!renderer) {
39 | return res.end('waiting for compilation... refresh in a moment.')
40 | }
41 |
42 | const s = Date.now()
43 |
44 | res.setHeader("Content-Type", "text/html")
45 |
46 | const errorHandler = err => {
47 | if (err && err.code === 404) {
48 | // 未找到页面
49 | res.status(404).sendfile('public/404.html');
50 | } else {
51 | // 页面渲染错误
52 | res.status(500).end('500 - Internal Server Error')
53 | console.error(`error during render : ${req.url}`)
54 | console.error(err)
55 | }
56 | }
57 |
58 | const context = {
59 | title: 'vue-ssr-template',
60 | url: req.url,
61 | cookies: req.cookies
62 | }
63 |
64 | renderer.renderToString(context, (err, html) => {
65 | if (err) {
66 | return errorHandler(err)
67 | }
68 | res.end(html)
69 | console.log(`whole request: ${Date.now() - s}ms`)
70 | })
71 | })
72 |
73 |