├── static
└── .gitkeep
├── .eslintignore
├── public
└── pages
│ └── index
│ ├── components
│ ├── not-found.vue
│ ├── Tab2.vue
│ ├── Tab3.vue
│ └── Tab1.vue
│ ├── assets
│ └── img
│ │ └── logo.png
│ ├── vuex
│ ├── mutation-types.js
│ ├── actions.js
│ ├── modules
│ │ ├── tab1.js
│ │ ├── tab2.js
│ │ └── tab3.js
│ └── store.js
│ ├── main.js
│ ├── App.vue
│ └── route-config.js
├── server
├── views
│ ├── error.ejs
│ ├── page
│ │ └── index.ejs
│ └── layout.ejs
├── middlewares
│ └── hashedAssets.js
├── routes
│ └── index.js
└── app.js
├── .babelrc
├── .gitignore
├── .editorconfig
├── .eslintrc.js
├── README.md
├── config
└── index.js
├── tools
├── list-entries.js
├── webpack.prod.conf.js
├── build.js
├── webpack.dev.conf.js
├── utils.js
└── webpack.base.conf.js
├── bin
└── www
└── package.json
/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/*.js
2 | config/*.js
3 |
--------------------------------------------------------------------------------
/public/pages/index/components/not-found.vue:
--------------------------------------------------------------------------------
1 |
2 | 页面不存在
3 |
4 |
--------------------------------------------------------------------------------
/server/views/error.ejs:
--------------------------------------------------------------------------------
1 |
<%= message %>
2 | <%= error.status %>
3 | <%= error.stack %>
4 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-2"],
3 | "plugins": ["transform-runtime"],
4 | "comments": false
5 | }
6 |
--------------------------------------------------------------------------------
/public/pages/index/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanhaoxiang/vue-webpack/HEAD/public/pages/index/assets/img/logo.png
--------------------------------------------------------------------------------
/public/pages/index/vuex/mutation-types.js:
--------------------------------------------------------------------------------
1 | // counter
2 | export const INCREMENT = 'INCREMENT'
3 | export const DECREMENT = 'DECREMENT'
4 |
--------------------------------------------------------------------------------
/server/views/page/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | server/logs/
5 | .idea
6 | npm-debug.log
7 | selenium-debug.log
8 | test/unit/coverage
9 | test/e2e/reports
10 | webpack-assets.json
11 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/server/middlewares/hashedAssets.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 |
3 | module.exports = function(jsonPath) {
4 | return function(req, res, next) {
5 | res.locals.__assets = JSON.parse(fs.readFileSync(jsonPath))
6 | next();
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/public/pages/index/vuex/actions.js:
--------------------------------------------------------------------------------
1 | import * as types from './mutation-types'
2 |
3 | export const incrementAsync = ({dispatch}) => {
4 | setTimeout(() => {
5 | dispatch(types.INCREMENT)
6 | }, 1000)
7 | }
8 |
9 | export const decrementAsync = ({dispatch}) => {
10 | setTimeout(() => {
11 | dispatch(types.DECREMENT)
12 | }, 1000)
13 | }
14 |
--------------------------------------------------------------------------------
/public/pages/index/vuex/modules/tab1.js:
--------------------------------------------------------------------------------
1 | import * as types from '../mutation-types'
2 |
3 | const state = {
4 | count: 0
5 | }
6 | const mutations = {
7 | [types.INCREMENT](state) {
8 | state.count++
9 | },
10 | [types.DECREMENT](state) {
11 | state.count--
12 | },
13 | }
14 |
15 | export default {
16 | state,
17 | mutations
18 | }
19 |
--------------------------------------------------------------------------------
/public/pages/index/vuex/modules/tab2.js:
--------------------------------------------------------------------------------
1 | import * as types from '../mutation-types'
2 |
3 | const state = {
4 | count: 0
5 | }
6 | const mutations = {
7 | [types.INCREMENT](state) {
8 | state.count++
9 | },
10 | [types.DECREMENT](state) {
11 | state.count--
12 | },
13 | }
14 |
15 | export default {
16 | state,
17 | mutations
18 | }
19 |
--------------------------------------------------------------------------------
/public/pages/index/vuex/modules/tab3.js:
--------------------------------------------------------------------------------
1 | import * as types from '../mutation-types'
2 |
3 | const state = {
4 | count: 0
5 | }
6 | const mutations = {
7 | [types.INCREMENT](state) {
8 | state.count++
9 | },
10 | [types.DECREMENT](state) {
11 | state.count--
12 | },
13 | }
14 |
15 | export default {
16 | state,
17 | mutations
18 | }
19 |
--------------------------------------------------------------------------------
/server/routes/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 | /* GET home page. */
4 | router.get('/', function(req, res, next) {
5 | res.render('page/index', {
6 | title:'Vue webpack',
7 | description:'This is a site using Vue.js with webpack'
8 | });
9 | });
10 |
11 | module.exports = router;
12 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parserOptions: {
4 | sourceType: 'module'
5 | },
6 | // extends: 'airbnb-base',
7 | // required to lint *.vue files
8 | plugins: [
9 | 'html'
10 | ],
11 | // add your custom rules here
12 | 'rules': {
13 | 'import/no-unresolved': 0,
14 | // allow debugger during development
15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/public/pages/index/main.js:
--------------------------------------------------------------------------------
1 | import { configRouter } from './route-config'
2 | import Vue from 'vue'
3 | import VueRouter from 'vue-router';
4 |
5 | Vue.use(VueRouter);
6 |
7 | // create router
8 | const router = new VueRouter({
9 | history:true,
10 | linkActiveClass:'active',
11 | saveScrollPosition: true
12 | })
13 |
14 | configRouter(router);
15 |
16 | const App = Vue.extend(require('./App.vue'))
17 | router.start(App, '#app');
18 |
19 |
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 介绍
2 |
3 | > Vue服务端路由(express router)+前端路由(vue-router)结合的Demo,支持多页面和前端单页
4 |
5 | ## 使用方式
6 |
7 | ``` bash
8 | # 安装依赖
9 | npm install -g babel
10 | npm install -g webpack
11 | npm install
12 |
13 | # 启动调试
14 | npm run dev
15 |
16 | # 打开浏览器访问 http://localhost:3000, 支持hot reload
17 |
18 | # 编译工程
19 | npm run build:prod
20 |
21 | ```
22 |
23 | > 注意: 多页面的情况下是共享vendor.js的,所以建议将一些比较大的js库放在cdn上单独加载, debug模式下编译出来的vendor是比较大的, build:prod后会小很多
24 |
25 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | // see http://vuejs-templates.github.io/webpack for documentation.
2 | var path = require('path')
3 |
4 | /*TODO: Assign a production public path*/
5 | var publicPath = process.env.NODE_ENV=='production'?'/':'/';
6 |
7 | module.exports = {
8 | assetsRoot: path.resolve(__dirname, '../dist'),
9 | assetsSubDirectory: 'assets',
10 | assetsPublicPath: publicPath,
11 | sourceMap: process.env.NODE_ENV!=='production' //do not use source map in production environment
12 | }
13 |
--------------------------------------------------------------------------------
/tools/list-entries.js:
--------------------------------------------------------------------------------
1 | var fs=require('fs');
2 | var path=require('path');
3 |
4 | /**
5 | * load entries from dir with format{ foo:'bar/main.js'}
6 | * warning: please notice that the name of mian.js is important
7 | * @param dir
8 | * @param entries
9 | * @returns {*|Array}
10 | */
11 | function loadEntries (dir){
12 | var files = fs.readdirSync(dir);
13 | var entries ={};
14 | files.forEach((filename) => {
15 | var filePath = path.join(dir, filename);
16 | if (fs.statSync(filePath).isDirectory()) {
17 | entries[filename]=filePath+'/main.js';
18 | }
19 | });
20 | return entries;
21 | };
22 |
23 | module.exports=loadEntries;
24 |
--------------------------------------------------------------------------------
/public/pages/index/components/Tab2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 | content of tab2
10 |
11 |
12 |
13 |
14 |
15 |
28 |
29 |
30 |
33 |
--------------------------------------------------------------------------------
/public/pages/index/components/Tab3.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 | content of tab3
10 |
11 |
12 |
13 |
14 |
15 |
28 |
29 |
30 |
33 |
--------------------------------------------------------------------------------
/tools/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var config = require('../config')
3 | var utils = require('./utils')
4 | var webpack = require('webpack')
5 | var merge = require('webpack-merge')
6 | var baseWebpackConfig = require('./webpack.base.conf')
7 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
8 |
9 | var webpackConfig = merge(baseWebpackConfig, {
10 | devtool: false,
11 | output: {
12 | path: config.assetsRoot,
13 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
14 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
15 | },
16 | vue: {
17 | loaders: utils.cssLoaders({
18 | sourceMap: config.sourceMap,
19 | extract: true
20 | })
21 | },
22 | plugins: [
23 | new webpack.optimize.UglifyJsPlugin({
24 | compress: {
25 | warnings: false
26 | }
27 | })
28 | ]
29 | })
30 |
31 | module.exports = webpackConfig
32 |
--------------------------------------------------------------------------------
/public/pages/index/components/Tab1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |

11 |
{{msg}}
12 |
13 |
14 |
15 |
16 |
17 |
30 |
31 |
32 |
36 |
--------------------------------------------------------------------------------
/public/pages/index/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
21 |
22 |
28 |
29 |
34 |
--------------------------------------------------------------------------------
/tools/build.js:
--------------------------------------------------------------------------------
1 | // https://github.com/shelljs/shelljs
2 | require('shelljs/global')
3 | var path = require('path')
4 | var config = require('../config')
5 | var ora = require('ora')
6 | var webpack = require('webpack')
7 | var ENV_CONFIG_MAP={
8 | development:'./webpack.dev.conf',
9 | production:'./webpack.prod.conf'
10 | }
11 | var webpackConfig = require(ENV_CONFIG_MAP[process.env.NODE_ENV]||'./webpack.dev.conf')
12 |
13 | var spinner = ora('building for '+process.env.NODE_ENV+'...')
14 | spinner.start()
15 |
16 | var assetsPath = path.join(config.assetsRoot, config.assetsSubDirectory)
17 | rm('-rf', assetsPath)
18 | mkdir('-p', assetsPath)
19 | cp('-R', 'static/', assetsPath)
20 |
21 | webpack(webpackConfig, function (err, stats) {
22 | spinner.stop()
23 | if (err) throw err
24 | process.stdout.write(stats.toString({
25 | colors: true,
26 | modules: false,
27 | children: false,
28 | chunks: false,
29 | chunkModules: false
30 | }) + '\n')
31 | })
32 |
--------------------------------------------------------------------------------
/public/pages/index/vuex/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import createLogger from 'vuex/logger'
4 |
5 | import tab1 from './modules/tab1'
6 | import tab2 from './modules/tab2'
7 | import tab3 from './modules/tab3'
8 |
9 | Vue.use(Vuex)
10 |
11 | const middlewares = []
12 |
13 | if (__DEV__) {
14 | middlewares.push(createLogger())
15 | }
16 |
17 | const store = new Vuex.Store({
18 | modules: {
19 | tab1,
20 | tab2,
21 | tab3
22 | },
23 | middlewares
24 | })
25 |
26 | export default store
27 |
28 | if (module.hot) {
29 | module.hot.accept([
30 | './modules/tab1',
31 | './modules/tab2',
32 | './modules/tab3'
33 | ], () => {
34 | try {
35 | store.hotUpdate({
36 | modules: {
37 | tab1: require('./modules/tab1').default,
38 | tab2: require('./modules/tab2').default,
39 | tab3: require('./modules/tab3').default
40 | }
41 | })
42 | } catch (e) {
43 | console.log(e.stack)
44 | }
45 | })
46 | }
47 |
--------------------------------------------------------------------------------
/tools/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var config = require('../config')
3 | var webpack = require('webpack')
4 | var merge = require('webpack-merge')
5 | var utils = require('./utils')
6 | var baseWebpackConfig = require('./webpack.base.conf')
7 | var hotMiddlewareScript = 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true';
8 |
9 | // add hot-reload related code to entry chunks
10 | Object.keys(baseWebpackConfig.entry).forEach(function (name) {
11 | baseWebpackConfig.entry[name] = [baseWebpackConfig.entry[name],hotMiddlewareScript]
12 | })
13 |
14 | module.exports = merge(baseWebpackConfig, {
15 | output: {
16 | path: config.assetsRoot,
17 | filename: utils.assetsPath('js/[name].[hash].js'),
18 | chunkFilename: utils.assetsPath('js/[id].[hash].js')
19 | },
20 | devtool: 'eval-source-map',// eval-source-map is faster for development
21 | plugins: [
22 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
23 | new webpack.HotModuleReplacementPlugin(),
24 | new webpack.NoErrorsPlugin()
25 | ]
26 | })
27 |
--------------------------------------------------------------------------------
/public/pages/index/route-config.js:
--------------------------------------------------------------------------------
1 | import store from './vuex/store'
2 | import { sync } from 'vuex-router-sync'
3 |
4 | export function configRouter (router) {
5 | router.hashbang=true;
6 | // normal routes
7 | router.map({
8 | '/tab1': {
9 | name:'tab1',
10 | component: require('./components/Tab1.vue')
11 | },
12 |
13 | '/tab2':{
14 | name:'tab2',
15 | component: require('./components/Tab2.vue')
16 | },
17 |
18 | '/tab3':{
19 | name:'tab3',
20 | component: require('./components/Tab3.vue')
21 | },
22 |
23 | '*': {
24 | name:'not-found',
25 | component: require('./components/not-found.vue')
26 | }
27 | })
28 |
29 | router.redirect({
30 | '/':'/tab1'
31 | })
32 |
33 | // global before
34 | // 3 options:
35 | // 1. return a boolean
36 | // 2. return a Promise that resolves to a boolean
37 | // 3. call transition.next() or transition.abort()
38 | router.beforeEach((transition) => {
39 | if (transition.to.path === '/forbidden') {
40 | router.app.authenticating = true
41 | setTimeout(() => {
42 | router.app.authenticating = false
43 | alert('this route is forbidden by a global before hook')
44 | transition.abort()
45 | }, 3000)
46 | } else {
47 | $.init();
48 | transition.next()
49 | }
50 | })
51 |
52 | sync(store, router) // done.
53 | }
54 |
--------------------------------------------------------------------------------
/server/views/layout.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ">
10 | <%= title%>
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 | <%- body%>
22 |
23 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/tools/utils.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var config = require('../config')
3 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
4 |
5 | exports.assetsPath = function (_path) {
6 | return path.posix.join(config.assetsSubDirectory, _path)
7 | }
8 |
9 | exports.cssLoaders = function (options) {
10 | options = options || {}
11 | // generate loader string to be used with extract text plugin
12 | function generateLoaders (loaders) {
13 | var sourceLoader = loaders.map(function (loader) {
14 | var extraParamChar
15 | if (/\?/.test(loader)) {
16 | loader = loader.replace(/\?/, '-loader?')
17 | extraParamChar = '&'
18 | } else {
19 | loader = loader + '-loader'
20 | extraParamChar = '?'
21 | }
22 | return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
23 | }).join('!')
24 |
25 | if (options.extract) {
26 | return ExtractTextPlugin.extract('vue-style-loader', sourceLoader)
27 | } else {
28 | return ['vue-style-loader', sourceLoader].join('!')
29 | }
30 | }
31 |
32 | // http://vuejs.github.io/vue-loader/configurations/extract-css.html
33 | return {
34 | css: generateLoaders(['css']),
35 | postcss: generateLoaders(['css']),
36 | less: generateLoaders(['css', 'less']),
37 | sass: generateLoaders(['css', 'sass?indentedSyntax']),
38 | scss: generateLoaders(['css', 'sass']),
39 | stylus: generateLoaders(['css', 'stylus']),
40 | styl: generateLoaders(['css', 'stylus'])
41 | }
42 | }
43 |
44 | // Generate loaders for standalone style files (outside of .vue)
45 | exports.styleLoaders = function (options) {
46 | var output = []
47 | var loaders = exports.cssLoaders(options)
48 | for (var extension in loaders) {
49 | var loader = loaders[extension]
50 | output.push({
51 | test: new RegExp('\\.' + extension + '$'),
52 | loader: loader
53 | })
54 | }
55 | return output
56 | }
57 |
--------------------------------------------------------------------------------
/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | var app = require('../server/app');
8 | var debug = require('debug')('vue-webpack:server');
9 | var http = require('http');
10 |
11 | /**
12 | * Get port from environment and store in Express.
13 | */
14 |
15 | var port = normalizePort(process.env.PORT || '3000');
16 | app.set('port', port);
17 |
18 | /**
19 | * Create HTTP server.
20 | */
21 |
22 | var server = http.createServer(app);
23 |
24 | /**
25 | * Listen on provided port, on all network interfaces.
26 | */
27 |
28 | server.listen(port);
29 | server.on('error', onError);
30 | server.on('listening', onListening);
31 |
32 | /**
33 | * Normalize a port into a number, string, or false.
34 | */
35 |
36 | function normalizePort(val) {
37 | var port = parseInt(val, 10);
38 |
39 | if (isNaN(port)) {
40 | // named pipe
41 | return val;
42 | }
43 |
44 | if (port >= 0) {
45 | // port number
46 | return port;
47 | }
48 |
49 | return false;
50 | }
51 |
52 | /**
53 | * Event listener for HTTP server "error" event.
54 | */
55 |
56 | function onError(error) {
57 | if (error.syscall !== 'listen') {
58 | throw error;
59 | }
60 |
61 | var bind = typeof port === 'string'
62 | ? 'Pipe ' + port
63 | : 'Port ' + port;
64 |
65 | // handle specific listen errors with friendly messages
66 | switch (error.code) {
67 | case 'EACCES':
68 | console.error(bind + ' requires elevated privileges');
69 | process.exit(1);
70 | break;
71 | case 'EADDRINUSE':
72 | console.error(bind + ' is already in use');
73 | process.exit(1);
74 | break;
75 | default:
76 | throw error;
77 | }
78 | }
79 |
80 | /**
81 | * Event listener for HTTP server "listening" event.
82 | */
83 |
84 | function onListening() {
85 | var addr = server.address();
86 | var bind = typeof addr === 'string'
87 | ? 'pipe ' + addr
88 | : 'port ' + addr.port;
89 | debug('Listening on ' + bind);
90 | }
91 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-webpack",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "blank",
6 | "private": true,
7 | "scripts": {
8 | "dev": "NODE_ENV=development PORT=3000 node bin/www",
9 | "build:dev": "npm run clean && NODE_ENV=development node tools/build.js",
10 | "build:prod": " npm run clean && NODE_ENV=production node tools/build.js",
11 | "clean": "rm -rf dist"
12 | },
13 | "dependencies": {
14 | "async": "^1.5.2",
15 | "babel-runtime": "^6.0.0",
16 | "body-parser": "~1.13.2",
17 | "compression": "^1.6.1",
18 | "cookie-parser": "~1.3.5",
19 | "debug": "^2.2.0",
20 | "ejs": "~2.3.3",
21 | "express": "~4.13.1",
22 | "express-ejs-layouts": "^2.0.0",
23 | "file-stream-rotator": "0.0.6",
24 | "lodash": "^4.6.1",
25 | "morgan": "~1.6.1",
26 | "serve-favicon": "~2.3.0",
27 | "superagent": "^1.8.0",
28 | "vue": "^1.0.21",
29 | "vue-resource": "^0.9.3",
30 | "vue-router": "^0.7.13",
31 | "vuex": "^0.8.2",
32 | "vuex-router-sync": "^2.1.1"
33 | },
34 | "devDependencies": {
35 | "assets-webpack-plugin": "^3.4.0",
36 | "babel-core": "^6.0.0",
37 | "babel-loader": "^6.0.0",
38 | "babel-plugin-transform-runtime": "^6.0.0",
39 | "babel-preset-es2015": "^6.0.0",
40 | "babel-preset-stage-2": "^6.0.0",
41 | "browser-sync": "^2.13.0",
42 | "compression-webpack-plugin": "^0.3.1",
43 | "connect-history-api-fallback": "^1.1.0",
44 | "css-loader": "^0.23.0",
45 | "eslint": "^2.10.2",
46 | "eslint-config-airbnb-base": "^3.0.1",
47 | "eslint-friendly-formatter": "^2.0.5",
48 | "eslint-loader": "^1.3.0",
49 | "eslint-plugin-html": "^1.3.0",
50 | "eslint-plugin-import": "^1.8.1",
51 | "eventsource-polyfill": "^0.9.6",
52 | "express": "^4.13.3",
53 | "extract-text-webpack-plugin": "^1.0.1",
54 | "file-loader": "^0.8.4",
55 | "function-bind": "^1.0.2",
56 | "html-webpack-plugin": "^2.8.1",
57 | "http-proxy-middleware": "^0.12.0",
58 | "json-loader": "^0.5.4",
59 | "ora": "^0.2.0",
60 | "shelljs": "^0.6.0",
61 | "url-loader": "^0.5.7",
62 | "vue-hot-reload-api": "^1.2.0",
63 | "vue-html-loader": "^1.0.0",
64 | "vue-loader": "^8.3.0",
65 | "vue-style-loader": "^1.0.0",
66 | "webpack": "^1.12.2",
67 | "webpack-dev-middleware": "^1.4.0",
68 | "webpack-hot-middleware": "^2.6.0",
69 | "webpack-merge": "^0.8.3"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var path = require('path');
3 | var favicon = require('serve-favicon');
4 | var logger = require('morgan');
5 | var fs = require('fs')
6 | var cookieParser = require('cookie-parser');
7 | var bodyParser = require('body-parser');
8 | var expressLayouts = require('express-ejs-layouts');
9 | var config=require('../config')
10 | var staticPath = path.posix.join(config.assetsPublicPath, config.assetsSubDirectory)
11 | var routes = require('./routes/index');
12 |
13 | var app = express();
14 |
15 | // view engine setup
16 | app.set('views', path.join(__dirname, 'views'));
17 | app.set('view engine', 'ejs');
18 | app.use(expressLayouts);
19 | app.set('layout', 'layout')
20 |
21 | // middlewares setup
22 | if(app.get('env')==='development') {
23 | app.use(logger('dev'));
24 |
25 | var webpack = require('webpack');
26 | var webpackConfig = require('../tools/webpack.dev.conf');
27 | var compiler = webpack(webpackConfig);
28 | app.use(require('webpack-dev-middleware')(compiler, {
29 | publicPath: webpackConfig.output.publicPath,
30 | lazy:false,
31 | // noInfo:true,
32 | stats: {
33 | colors: true,
34 | chunks: false
35 | }
36 | }))
37 | app.use(require('webpack-hot-middleware')(compiler,{
38 | log: console.log, path: '/__webpack_hmr', heartbeat: 10 * 1000
39 | }))
40 |
41 | app.use(staticPath, express.static('./static'))
42 | }
43 | else{
44 | var FileStreamRotator = require('file-stream-rotator');
45 | var compression = require('compression')
46 | var logDirectory = __dirname + '/logs';
47 | fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory)
48 | var accessLogStream = FileStreamRotator.getStream({
49 | date_format: 'YYYYMMDD',
50 | filename: logDirectory + '/access-%DATE%.log',
51 | frequency: 'daily',
52 | verbose: false
53 | })
54 | app.use(compression());
55 | app.use(logger('combined', {stream: accessLogStream}));
56 | app.use(express.static(path.join(__dirname, '../dist')));
57 | }
58 |
59 | app.use(require('connect-history-api-fallback')({
60 | index:'/',
61 | rewrites:[{from:'/tab',to:'/'}],
62 | logger:app.get('env')=='development'?console.log:null
63 | }))
64 | app.use(bodyParser.json());
65 | app.use(bodyParser.urlencoded({ extended: false }));
66 | app.use(cookieParser());
67 |
68 | app.use('*',function(req,res,next){
69 | if(app.get('env')=='development'){
70 | console.log('>>>>>:',req.baseUrl);
71 | }
72 | res.locals.title='Vue webpack';
73 | res.locals.description='Vue webpack web';
74 | next();
75 | })
76 |
77 |
78 | //install hashed assets middleware
79 | app.use(require('./middlewares/hashedAssets')(path.resolve(__dirname,'../config/webpack-assets.json')));
80 |
81 | app.use('/',routes);
82 |
83 | // catch 404 and forward to error handler
84 | app.use(function(req, res, next) {
85 | var err = new Error('Not Found');
86 | err.status = 404;
87 | next(err);
88 | });
89 |
90 | // error handlers
91 |
92 | // development error handler
93 | // will print stacktrace
94 | if (app.get('env') === 'development') {
95 | app.use(function(err, req, res, next) {
96 | res.status(err.status || 500);
97 | res.render('error', {
98 | message: err.message,
99 | error: err
100 | });
101 | });
102 | }
103 |
104 | // production error handler
105 | // no stacktraces leaked to user
106 | app.use(function(err, req, res, next) {
107 | res.status(err.status || 500);
108 | res.render('error', {
109 | message: err.message,
110 | error: {}
111 | });
112 | });
113 |
114 | module.exports = app;
115 |
--------------------------------------------------------------------------------
/tools/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var config = require('../config')
3 | var utils = require('./utils')
4 | var SaveHashes = require('assets-webpack-plugin')
5 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
6 | var projectRoot = path.resolve(__dirname, '../')
7 | var webpack=require('webpack');
8 | var env=process.env.NODE_ENV||'development';
9 | module.exports = {
10 | entry: require('./list-entries')(path.resolve(__dirname,'../public/pages')),
11 | output:{
12 | publicPath: config.assetsPublicPath
13 | },
14 | vue: {
15 | loaders: utils.cssLoaders({
16 | sourceMap: config.sourceMap,
17 | extract: true
18 | })
19 | },
20 | resolve: {
21 | extensions: ['', '.js', '.vue'],
22 | //A directory (or array of directories absolute paths), in which webpack should look for modules that weren’t found in resolve.root or resolve.modulesDirectories.
23 | fallback: [path.join(__dirname, '../node_modules')],
24 | alias: {
25 |
26 | }
27 | },
28 | resolveLoader: {
29 | fallback: [path.join(__dirname, '../node_modules')]
30 | },
31 | plugins:[
32 | new webpack.DefinePlugin({
33 | '__DEV__':env=='development',
34 | 'process.env': {
35 | NODE_ENV:JSON.stringify(env)
36 | }
37 | }),
38 | new webpack.optimize.OccurenceOrderPlugin(),
39 | new SaveHashes({path: path.join(__dirname, '../config')}),
40 | // extract css into its own file
41 | new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
42 |
43 | // split vendor js into its own file
44 | new webpack.optimize.CommonsChunkPlugin({
45 | name: 'vendor',
46 | minChunks: function (module, count) {
47 | // any required modules inside node_modules are extracted to vendor
48 | return (
49 | module.resource &&
50 | /\.js$/.test(module.resource) &&
51 | module.resource.indexOf(
52 | path.join(__dirname, '../node_modules')
53 | ) === 0
54 | )
55 | }
56 | }),
57 | // extract webpack runtime and module manifest to its own file in order to
58 | // prevent vendor hash from being updated whenever app bundle is updated
59 | new webpack.optimize.CommonsChunkPlugin({
60 | name: 'manifest',
61 | chunks: ['vendor']
62 | })
63 | ],
64 | module: {
65 | preLoaders: [
66 | {
67 | test: /\.vue$/,
68 | loader: 'eslint',
69 | include: projectRoot,
70 | exclude: /node_modules/
71 | },
72 | {
73 | test: /\.js$/,
74 | loader: 'eslint',
75 | include: projectRoot,
76 | exclude: /node_modules/
77 | }
78 | ],
79 | loaders: utils.styleLoaders({ sourceMap: config.sourceMap, extract: true }).concat([
80 | {
81 | test: /\.vue$/,
82 | loader: 'vue'
83 | },
84 | {
85 | test: /\.js$/,
86 | loader: 'babel',
87 | include: projectRoot,
88 | exclude: /node_modules/
89 | },
90 | {
91 | test: /\.json$/,
92 | loader: 'json'
93 | },
94 | {
95 | test: /\.html$/,
96 | loader: 'vue-html'
97 | },
98 | {
99 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
100 | loader: 'url',
101 | query: {
102 | limit: 10000,
103 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
104 | }
105 | },
106 | {
107 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
108 | loader: 'url',
109 | query: {
110 | limit: 10000,
111 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
112 | }
113 | }
114 | ])
115 | },
116 | eslint: {
117 | formatter: require('eslint-friendly-formatter')
118 | }
119 | }
120 |
--------------------------------------------------------------------------------