Browse Source

update to webpack4.0 (#136)

* dev

* update

* fix

* refine

* add ScriptExtHtmlWebpackPlugin

* update

* format

* refine

* typo

* format

* docs

* docs
花裤衩 6 years ago
parent
commit
222f1c48f6

+ 27 - 22
README-zh.md

@@ -1,29 +1,32 @@
1 1
 # vueAdmin-template
2 2
 
3
-> 这是一个 极简的vue admin 管理后台 它只包含了 Element UI & axios & iconfont & permission control & lint,这些搭建后台必要的东西。
3
+> 这是一个 极简的 vue admin 管理后台 它只包含了 Element UI & axios & iconfont & permission control & lint,这些搭建后台必要的东西。
4 4
 
5 5
 [线上地址](http://panjiachen.github.io/vueAdmin-template)
6 6
 
7 7
 ## Extra
8
-如果你想要根据用户角色来动态生成侧边栏和router,你可以使用改分支[permission-control](https://github.com/PanJiaChen/vueAdmin-template/tree/permission-control)
9
10
- ## 相关项目
11
- [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
12 8
 
13
- [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
9
+如果你想要根据用户角色来动态生成侧边栏和 router,你可以使用改分支[permission-control](https://github.com/PanJiaChen/vueAdmin-template/tree/permission-control)
10
+
11
+本项目基于`webpack4`开发,若还想使用`webpack3`开发,请使用该分支[webpack3](https://github.com/PanJiaChen/vueAdmin-template/tree/webpack3)
12
+
13
+## 相关项目
14
+
15
+[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
16
+
17
+[electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
14 18
 
15 19
 写了一个系列的教程配套文章,如何从零构建后一个完整的后台项目:
16
- - [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
17
- - [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
18
- - [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
19
- - [手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板,专门针对本项目的文章,算作是一篇文档)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
20
- - [手摸手,带你封装一个vue component](https://segmentfault.com/a/1190000009090836)
21 20
 
21
+- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
22
+- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
23
+- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
24
+- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板,专门针对本项目的文章,算作是一篇文档)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
25
+- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
22 26
 
23 27
 ## Build Setup
24 28
 
25
-``` bash
26
-
29
+```bash
27 30
 # Clone project
28 31
 git clone https://github.com/PanJiaChen/vueAdmin-template.git
29 32
 
@@ -33,25 +36,28 @@ npm install
33 36
 # 建议不要用cnpm  安装有各种诡异的bug 可以通过如下操作解决npm速度慢的问题
34 37
 npm install --registry=https://registry.npm.taobao.org
35 38
 
36
-# serve with hot reload at localhost:9528
39
+# Serve with hot reload at localhost:9528
37 40
 npm run dev
38 41
 
39
-# build for production with minification
42
+# Build for production with minification
40 43
 npm run build
41 44
 
42
-# build for production and view the bundle analyzer report
45
+# Build for production and view the bundle analyzer report
43 46
 npm run build --report
44 47
 ```
45 48
 
46 49
 ## Demo
50
+
47 51
 ![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif)
48 52
 
49
-### Element-Ui 使用cdn教程
53
+### Element-Ui 使用 cdn 教程
54
+
50 55
 首先找到 `index.html` ([根目录下](https://github.com/PanJiaChen/vueAdmin-template/blob/element-ui-cdn/index.html))
51 56
 
52
-引入 Element的css和js ,并且引入 vue 。因为 Element-Ui 是依赖 vue 的,所以必须在它之前引入 vue 。
57
+引入 Element 的 css 和 js ,并且引入 vue 。因为 Element-Ui 是依赖 vue 的,所以必须在它之前引入 vue 。
58
+
59
+之后找到 [webpack.base.conf.js](https://github.com/PanJiaChen/vueAdmin-template/blob/element-ui-cdn/build/webpack.base.conf.js) 加入 `externals` 让 webpack 不打包 vue 和 element
53 60
 
54
-之后找到 [webpack.base.conf.js](https://github.com/PanJiaChen/vueAdmin-template/blob/element-ui-cdn/build/webpack.base.conf.js) 加入 `externals` 让webpack 不打包 vue 和 element
55 61
 ```
56 62
 externals: {
57 63
   vue: 'Vue',
@@ -59,7 +65,7 @@ externals: {
59 65
 }
60 66
 ```
61 67
 
62
-之后还有一个小细节是如果你用了全局对象方式引入vue,就不需要 手动 `Vue.use(Vuex)` ,它会自动挂载,具体见 [issue](https://github.com/vuejs/vuex/issues/731)
68
+之后还有一个小细节是如果你用了全局对象方式引入 vue,就不需要 手动 `Vue.use(Vuex)` ,它会自动挂载,具体见 [issue](https://github.com/vuejs/vuex/issues/731)
63 69
 
64 70
 最终你可以使用 `npm run build --report` 查看效果
65 71
 如图:
@@ -70,8 +76,7 @@ externals: {
70 76
 **[对应分支](https://github.com/PanJiaChen/vueAdmin-template/tree/element-ui-cdn)**
71 77
 
72 78
 ## License
79
+
73 80
 [MIT](https://github.com/PanJiaChen/vueAdmin-template/blob/master/LICENSE) license.
74 81
 
75 82
 Copyright (c) 2017-present PanJiaChen
76
-
77
-

+ 16 - 9
README.md

@@ -8,36 +8,43 @@
8 8
 
9 9
 ## Build Setup
10 10
 
11
-``` bash
12
-
11
+```bash
13 12
 # Clone project
14 13
 git clone https://github.com/PanJiaChen/vueAdmin-template.git
15 14
 
16 15
 # Install dependencies
17 16
 npm install
18 17
 
19
-# serve with hot reload at localhost:9528
18
+# Serve with hot reload at localhost:9528
20 19
 npm run dev
21 20
 
22
-# build for production with minification
21
+# Build for production with minification
23 22
 npm run build
24 23
 
25
-# build for production and view the bundle analyzer report
24
+# Build for production and view the bundle analyzer report
26 25
 npm run build --report
27 26
 ```
28 27
 
28
+https://panjiachen.gitee.io/vue-element-admin-site/zh/guide/
29
+
29 30
 ## Demo
31
+
30 32
 ![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif)
31 33
 
32 34
 ## Extra
35
+
33 36
 If you want router permission && generate menu by user roles , you can use this branch [permission-control](https://github.com/PanJiaChen/vueAdmin-template/tree/permission-control)
34 37
 
38
+This project is based on `webpack4` development. If you want to use `webpack3` development, please use this branch [webpack3](https://github.com/PanJiaChen/vueAdmin-template/tree/webpack3)
39
+
35 40
 ## Related Project
36
- [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
37 41
 
38
- [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
42
+[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
43
+
44
+[electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
39 45
 
40 46
 ### Element-Ui using cdn tutorial
47
+
41 48
 First find `index.html`([root directory](https://github.com/PanJiaChen/vueAdmin-template/blob/element-ui-cdn/index.html))
42 49
 
43 50
 Import css and js of `Element`, and then import vue. Because `Element` is vue-dependent, vue must be import before it.
@@ -53,7 +60,7 @@ externals: {
53 60
 ```
54 61
 
55 62
 Finally there is a small detail to pay attention to that if you import vue in global, you don't need to manually `Vue.use(Vuex)`, it will be automatically mounted, see
56
- [issue](https://github.com/vuejs/vuex/issues/731)
63
+[issue](https://github.com/vuejs/vuex/issues/731)
57 64
 
58 65
 And you can use `npm run build --report` to see the effect
59 66
 
@@ -64,8 +71,8 @@ Pictured:
64 71
 
65 72
 **[Branch](https://github.com/PanJiaChen/vueAdmin-template/tree/element-ui-cdn)**
66 73
 
67
-
68 74
 ## License
75
+
69 76
 [MIT](https://github.com/PanJiaChen/vueAdmin-template/blob/master/LICENSE) license.
70 77
 
71 78
 Copyright (c) 2017-present PanJiaChen

+ 15 - 11
build/build.js

@@ -19,13 +19,15 @@ rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
19 19
   webpack(webpackConfig, (err, stats) => {
20 20
     spinner.stop()
21 21
     if (err) throw err
22
-    process.stdout.write(stats.toString({
23
-      colors: true,
24
-      modules: false,
25
-      children: false,
26
-      chunks: false,
27
-      chunkModules: false
28
-    }) + '\n\n')
22
+    process.stdout.write(
23
+      stats.toString({
24
+        colors: true,
25
+        modules: false,
26
+        children: false,
27
+        chunks: false,
28
+        chunkModules: false
29
+      }) + '\n\n'
30
+    )
29 31
 
30 32
     if (stats.hasErrors()) {
31 33
       console.log(chalk.red('  Build failed with errors.\n'))
@@ -33,9 +35,11 @@ rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
33 35
     }
34 36
 
35 37
     console.log(chalk.cyan('  Build complete.\n'))
36
-    console.log(chalk.yellow(
37
-      '  Tip: built files are meant to be served over an HTTP server.\n' +
38
-      '  Opening index.html over file:// won\'t work.\n'
39
-    ))
38
+    console.log(
39
+      chalk.yellow(
40
+        '  Tip: built files are meant to be served over an HTTP server.\n' +
41
+          "  Opening index.html over file:// won't work.\n"
42
+      )
43
+    )
40 44
   })
41 45
 })

+ 17 - 7
build/check-versions.js

@@ -4,8 +4,11 @@ const semver = require('semver')
4 4
 const packageConfig = require('../package.json')
5 5
 const shell = require('shelljs')
6 6
 
7
-function exec (cmd) {
8
-  return require('child_process').execSync(cmd).toString().trim()
7
+function exec(cmd) {
8
+  return require('child_process')
9
+    .execSync(cmd)
10
+    .toString()
11
+    .trim()
9 12
 }
10 13
 
11 14
 const versionRequirements = [
@@ -24,23 +27,30 @@ if (shell.which('npm')) {
24 27
   })
25 28
 }
26 29
 
27
-module.exports = function () {
30
+module.exports = function() {
28 31
   const warnings = []
29 32
 
30 33
   for (let i = 0; i < versionRequirements.length; i++) {
31 34
     const mod = versionRequirements[i]
32 35
 
33 36
     if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
34
-      warnings.push(mod.name + ': ' +
35
-        chalk.red(mod.currentVersion) + ' should be ' +
36
-        chalk.green(mod.versionRequirement)
37
+      warnings.push(
38
+        mod.name +
39
+          ': ' +
40
+          chalk.red(mod.currentVersion) +
41
+          ' should be ' +
42
+          chalk.green(mod.versionRequirement)
37 43
       )
38 44
     }
39 45
   }
40 46
 
41 47
   if (warnings.length) {
42 48
     console.log('')
43
-    console.log(chalk.yellow('To use this template, you must update following to modules:'))
49
+    console.log(
50
+      chalk.yellow(
51
+        'To use this template, you must update following to modules:'
52
+      )
53
+    )
44 54
     console.log()
45 55
 
46 56
     for (let i = 0; i < warnings.length; i++) {

+ 28 - 21
build/utils.js

@@ -1,18 +1,19 @@
1 1
 'use strict'
2 2
 const path = require('path')
3 3
 const config = require('../config')
4
-const ExtractTextPlugin = require('extract-text-webpack-plugin')
4
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')
5 5
 const packageConfig = require('../package.json')
6 6
 
7
-exports.assetsPath = function (_path) {
8
-  const assetsSubDirectory = process.env.NODE_ENV === 'production'
9
-    ? config.build.assetsSubDirectory
10
-    : config.dev.assetsSubDirectory
7
+exports.assetsPath = function(_path) {
8
+  const assetsSubDirectory =
9
+    process.env.NODE_ENV === 'production'
10
+      ? config.build.assetsSubDirectory
11
+      : config.dev.assetsSubDirectory
11 12
 
12 13
   return path.posix.join(assetsSubDirectory, _path)
13 14
 }
14 15
 
15
-exports.cssLoaders = function (options) {
16
+exports.cssLoaders = function(options) {
16 17
   options = options || {}
17 18
 
18 19
   const cssLoader = {
@@ -30,8 +31,22 @@ exports.cssLoaders = function (options) {
30 31
   }
31 32
 
32 33
   // generate loader string to be used with extract text plugin
33
-  function generateLoaders (loader, loaderOptions) {
34
-    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
34
+  function generateLoaders(loader, loaderOptions) {
35
+    const loaders = []
36
+
37
+    // Extract CSS when that option is specified
38
+    // (which is the case during production build)
39
+    if (options.extract) {
40
+      loaders.push(MiniCssExtractPlugin.loader)
41
+    } else {
42
+      loaders.push('vue-style-loader')
43
+    }
44
+
45
+    loaders.push(cssLoader)
46
+
47
+    if (options.usePostCSS) {
48
+      loaders.push(postcssLoader)
49
+    }
35 50
 
36 51
     if (loader) {
37 52
       loaders.push({
@@ -42,24 +57,16 @@ exports.cssLoaders = function (options) {
42 57
       })
43 58
     }
44 59
 
45
-    // Extract CSS when that option is specified
46
-    // (which is the case during production build)
47
-    if (options.extract) {
48
-      return ExtractTextPlugin.extract({
49
-        use: loaders,
50
-        fallback: 'vue-style-loader'
51
-      })
52
-    } else {
53
-      return ['vue-style-loader'].concat(loaders)
54
-    }
60
+    return loaders
55 61
   }
56
-
57 62
   // https://vue-loader.vuejs.org/en/configurations/extract-css.html
58 63
   return {
59 64
     css: generateLoaders(),
60 65
     postcss: generateLoaders(),
61 66
     less: generateLoaders('less'),
62
-    sass: generateLoaders('sass', { indentedSyntax: true }),
67
+    sass: generateLoaders('sass', {
68
+      indentedSyntax: true
69
+    }),
63 70
     scss: generateLoaders('sass'),
64 71
     stylus: generateLoaders('stylus'),
65 72
     styl: generateLoaders('stylus')
@@ -67,7 +74,7 @@ exports.cssLoaders = function (options) {
67 74
 }
68 75
 
69 76
 // Generate loaders for standalone style files (outside of .vue)
70
-exports.styleLoaders = function (options) {
77
+exports.styleLoaders = function(options) {
71 78
   const output = []
72 79
   const loaders = exports.cssLoaders(options)
73 80
 

+ 1 - 18
build/vue-loader.conf.js

@@ -1,22 +1,5 @@
1 1
 'use strict'
2
-const utils = require('./utils')
3
-const config = require('../config')
4
-const isProduction = process.env.NODE_ENV === 'production'
5
-const sourceMapEnabled = isProduction
6
-  ? config.build.productionSourceMap
7
-  : config.dev.cssSourceMap
8 2
 
9 3
 module.exports = {
10
-  loaders: utils.cssLoaders({
11
-    sourceMap: sourceMapEnabled,
12
-    extract: isProduction
13
-  }),
14
-  cssSourceMap: sourceMapEnabled,
15
-  cacheBusting: config.dev.cacheBusting,
16
-  transformToRequire: {
17
-    video: ['src', 'poster'],
18
-    source: 'src',
19
-    img: 'src',
20
-    image: 'xlink:href'
21
-  }
4
+  //You can set the vue-loader configuration by yourself.
22 5
 }

+ 12 - 5
build/webpack.base.conf.js

@@ -2,9 +2,10 @@
2 2
 const path = require('path')
3 3
 const utils = require('./utils')
4 4
 const config = require('../config')
5
+const { VueLoaderPlugin } = require('vue-loader')
5 6
 const vueLoaderConfig = require('./vue-loader.conf')
6 7
 
7
-function resolve (dir) {
8
+function resolve(dir) {
8 9
   return path.join(__dirname, '..', dir)
9 10
 }
10 11
 
@@ -27,9 +28,10 @@ module.exports = {
27 28
   output: {
28 29
     path: config.build.assetsRoot,
29 30
     filename: '[name].js',
30
-    publicPath: process.env.NODE_ENV === 'production'
31
-      ? config.build.assetsPublicPath
32
-      : config.dev.assetsPublicPath
31
+    publicPath:
32
+      process.env.NODE_ENV === 'production'
33
+        ? config.build.assetsPublicPath
34
+        : config.dev.assetsPublicPath
33 35
   },
34 36
   resolve: {
35 37
     extensions: ['.js', '.vue', '.json'],
@@ -48,7 +50,11 @@ module.exports = {
48 50
       {
49 51
         test: /\.js$/,
50 52
         loader: 'babel-loader',
51
-        include: [resolve('src'), resolve('test') ,resolve('node_modules/webpack-dev-server/client')]
53
+        include: [
54
+          resolve('src'),
55
+          resolve('test'),
56
+          resolve('node_modules/webpack-dev-server/client')
57
+        ]
52 58
       },
53 59
       {
54 60
         test: /\.svg$/,
@@ -85,6 +91,7 @@ module.exports = {
85 91
       }
86 92
     ]
87 93
   },
94
+  plugins: [new VueLoaderPlugin()],
88 95
   node: {
89 96
     // prevent webpack from injecting useless setImmediate polyfill because Vue
90 97
     // source contains it (although only uses it if it's native).

+ 22 - 14
build/webpack.dev.conf.js

@@ -9,7 +9,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin')
9 9
 const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
10 10
 const portfinder = require('portfinder')
11 11
 
12
-function resolve (dir) {
12
+function resolve(dir) {
13 13
   return path.join(__dirname, '..', dir)
14 14
 }
15 15
 
@@ -17,8 +17,12 @@ const HOST = process.env.HOST
17 17
 const PORT = process.env.PORT && Number(process.env.PORT)
18 18
 
19 19
 const devWebpackConfig = merge(baseWebpackConfig, {
20
+  mode: 'development',
20 21
   module: {
21
-    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
22
+    rules: utils.styleLoaders({
23
+      sourceMap: config.dev.cssSourceMap,
24
+      usePostCSS: true
25
+    })
22 26
   },
23 27
   // cheap-module-eval-source-map is faster for development
24 28
   devtool: config.dev.devtool,
@@ -39,7 +43,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
39 43
     proxy: config.dev.proxyTable,
40 44
     quiet: true, // necessary for FriendlyErrorsPlugin
41 45
     watchOptions: {
42
-      poll: config.dev.poll,
46
+      poll: config.dev.poll
43 47
     }
44 48
   },
45 49
   plugins: [
@@ -47,8 +51,6 @@ const devWebpackConfig = merge(baseWebpackConfig, {
47 51
       'process.env': require('../config/dev.env')
48 52
     }),
49 53
     new webpack.HotModuleReplacementPlugin(),
50
-    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
51
-    new webpack.NoEmitOnErrorsPlugin(),
52 54
     // https://github.com/ampedandwired/html-webpack-plugin
53 55
     new HtmlWebpackPlugin({
54 56
       filename: 'index.html',
@@ -56,7 +58,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
56 58
       inject: true,
57 59
       favicon: resolve('favicon.ico'),
58 60
       title: 'vue-element-admin'
59
-    }),
61
+    })
60 62
   ]
61 63
 })
62 64
 
@@ -72,14 +74,20 @@ module.exports = new Promise((resolve, reject) => {
72 74
       devWebpackConfig.devServer.port = port
73 75
 
74 76
       // Add FriendlyErrorsPlugin
75
-      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
76
-        compilationSuccessInfo: {
77
-          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
78
-        },
79
-        onErrors: config.dev.notifyOnErrors
80
-        ? utils.createNotifierCallback()
81
-        : undefined
82
-      }))
77
+      devWebpackConfig.plugins.push(
78
+        new FriendlyErrorsPlugin({
79
+          compilationSuccessInfo: {
80
+            messages: [
81
+              `Your application is running here: http://${
82
+                devWebpackConfig.devServer.host
83
+              }:${port}`
84
+            ]
85
+          },
86
+          onErrors: config.dev.notifyOnErrors
87
+            ? utils.createNotifierCallback()
88
+            : undefined
89
+        })
90
+      )
83 91
 
84 92
       resolve(devWebpackConfig)
85 93
     }

+ 96 - 68
build/webpack.prod.conf.js

@@ -7,17 +7,23 @@ const merge = require('webpack-merge')
7 7
 const baseWebpackConfig = require('./webpack.base.conf')
8 8
 const CopyWebpackPlugin = require('copy-webpack-plugin')
9 9
 const HtmlWebpackPlugin = require('html-webpack-plugin')
10
-const ExtractTextPlugin = require('extract-text-webpack-plugin')
11
-const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
10
+const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
11
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')
12
+const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
12 13
 const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
13 14
 
14
-function resolve (dir) {
15
+function resolve(dir) {
15 16
   return path.join(__dirname, '..', dir)
16 17
 }
17 18
 
18 19
 const env = require('../config/prod.env')
19 20
 
21
+// For NamedChunksPlugin
22
+const seen = new Set()
23
+const nameLength = 4
24
+
20 25
 const webpackConfig = merge(baseWebpackConfig, {
26
+  mode: 'production',
21 27
   module: {
22 28
     rules: utils.styleLoaders({
23 29
       sourceMap: config.build.productionSourceMap,
@@ -28,37 +34,18 @@ const webpackConfig = merge(baseWebpackConfig, {
28 34
   devtool: config.build.productionSourceMap ? config.build.devtool : false,
29 35
   output: {
30 36
     path: config.build.assetsRoot,
31
-    filename: utils.assetsPath('js/[name].[chunkhash].js'),
32
-    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
37
+    filename: utils.assetsPath('js/[name].[chunkhash:8].js'),
38
+    chunkFilename: utils.assetsPath('js/[name].[chunkhash:8].js')
33 39
   },
34 40
   plugins: [
35 41
     // http://vuejs.github.io/vue-loader/en/workflow/production.html
36 42
     new webpack.DefinePlugin({
37 43
       'process.env': env
38 44
     }),
39
-    new UglifyJsPlugin({
40
-      uglifyOptions: {
41
-        compress: {
42
-          warnings: false
43
-        }
44
-      },
45
-      sourceMap: config.build.productionSourceMap,
46
-      parallel: true
47
-    }),
48 45
     // extract css into its own file
49
-    new ExtractTextPlugin({
50
-      filename: utils.assetsPath('css/[name].[contenthash].css'),
51
-      // Setting the following option to `false` will not extract CSS from codesplit chunks.
52
-      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
53
-      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
54
-      allChunks: false,
55
-    }),
56
-    // Compress extracted CSS. We are using this plugin so that possible
57
-    // duplicated CSS from different components can be deduped.
58
-    new OptimizeCSSPlugin({
59
-      cssProcessorOptions: config.build.productionSourceMap
60
-        ? { safe: true, map: { inline: false } }
61
-        : { safe: true }
46
+    new MiniCssExtractPlugin({
47
+      filename: utils.assetsPath('css/[name].[contenthash:8].css'),
48
+      chunkFilename: utils.assetsPath('css/[name].[contenthash:8].css')
62 49
     }),
63 50
     // generate dist index.html with correct asset hash for caching.
64 51
     // you can customize output by editing /index.html
@@ -75,44 +62,34 @@ const webpackConfig = merge(baseWebpackConfig, {
75 62
         removeAttributeQuotes: true
76 63
         // more options:
77 64
         // https://github.com/kangax/html-minifier#options-quick-reference
78
-      },
79
-      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
80
-      chunksSortMode: 'dependency'
81
-    }),
82
-    // keep module.id stable when vender modules does not change
83
-    new webpack.HashedModuleIdsPlugin(),
84
-    // enable scope hoisting
85
-    new webpack.optimize.ModuleConcatenationPlugin(),
86
-    // split vendor js into its own file
87
-    new webpack.optimize.CommonsChunkPlugin({
88
-      name: 'vendor',
89
-      minChunks (module) {
90
-        // any required modules inside node_modules are extracted to vendor
91
-        return (
92
-          module.resource &&
93
-          /\.js$/.test(module.resource) &&
94
-          module.resource.indexOf(
95
-            path.join(__dirname, '../node_modules')
96
-          ) === 0
97
-        )
98 65
       }
66
+      // default sort mode uses toposort which cannot handle cyclic deps
67
+      // in certain cases, and in webpack 4, chunk order in HTML doesn't
68
+      // matter anyway
99 69
     }),
100
-    // extract webpack runtime and module manifest to its own file in order to
101
-    // prevent vendor hash from being updated whenever app bundle is updated
102
-    new webpack.optimize.CommonsChunkPlugin({
103
-      name: 'manifest',
104
-      minChunks: Infinity
70
+    new ScriptExtHtmlWebpackPlugin({
71
+      //`runtime` must same as runtimeChunk name. default is `runtime`
72
+      inline: /runtime\..*\.js$/
105 73
     }),
106
-    // This instance extracts shared chunks from code splitted chunks and bundles them
107
-    // in a separate chunk, similar to the vendor chunk
108
-    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
109
-    new webpack.optimize.CommonsChunkPlugin({
110
-      name: 'app',
111
-      async: 'vendor-async',
112
-      children: true,
113
-      minChunks: 3
74
+    // keep chunk.id stable when chunk has no name
75
+    new webpack.NamedChunksPlugin(chunk => {
76
+      if (chunk.name) {
77
+        return chunk.name
78
+      }
79
+      const modules = Array.from(chunk.modulesIterable)
80
+      if (modules.length > 1) {
81
+        const hash = require('hash-sum')
82
+        const joinedHash = hash(modules.map(m => m.id).join('_'))
83
+        let len = nameLength
84
+        while (seen.has(joinedHash.substr(0, len))) len++
85
+        seen.add(joinedHash.substr(0, len))
86
+        return `chunk-${joinedHash.substr(0, len)}`
87
+      } else {
88
+        return modules[0].id
89
+      }
114 90
     }),
115
-
91
+    // keep module.id stable when vender modules does not change
92
+    new webpack.HashedModuleIdsPlugin(),
116 93
     // copy custom static assets
117 94
     new CopyWebpackPlugin([
118 95
       {
@@ -121,7 +98,41 @@ const webpackConfig = merge(baseWebpackConfig, {
121 98
         ignore: ['.*']
122 99
       }
123 100
     ])
124
-  ]
101
+  ],
102
+  optimization: {
103
+    splitChunks: {
104
+      chunks: 'all',
105
+      cacheGroups: {
106
+        libs: {
107
+          name: 'chunk-libs',
108
+          test: /[\\/]node_modules[\\/]/,
109
+          priority: 10,
110
+          chunks: 'initial' // 只打包初始时依赖的第三方
111
+        },
112
+        elementUI: {
113
+          name: 'chunk-elementUI', // 单独将 elementUI 拆包
114
+          priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app
115
+          test: /[\\/]node_modules[\\/]element-ui[\\/]/
116
+        }
117
+      }
118
+    },
119
+    runtimeChunk: 'single',
120
+    minimizer: [
121
+      new UglifyJsPlugin({
122
+        uglifyOptions: {
123
+          mangle: {
124
+            safari10: true
125
+          }
126
+        },
127
+        sourceMap: config.build.productionSourceMap,
128
+        cache: true,
129
+        parallel: true
130
+      }),
131
+      // Compress extracted CSS. We are using this plugin so that possible
132
+      // duplicated CSS from different components can be deduped.
133
+      new OptimizeCSSAssetsPlugin()
134
+    ]
135
+  }
125 136
 })
126 137
 
127 138
 if (config.build.productionGzip) {
@@ -132,9 +143,7 @@ if (config.build.productionGzip) {
132 143
       asset: '[path].gz[query]',
133 144
       algorithm: 'gzip',
134 145
       test: new RegExp(
135
-        '\\.(' +
136
-        config.build.productionGzipExtensions.join('|') +
137
-        ')$'
146
+        '\\.(' + config.build.productionGzipExtensions.join('|') + ')$'
138 147
       ),
139 148
       threshold: 10240,
140 149
       minRatio: 0.8
@@ -142,9 +151,28 @@ if (config.build.productionGzip) {
142 151
   )
143 152
 }
144 153
 
145
-if (config.build.bundleAnalyzerReport) {
146
-  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
147
-  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
154
+if (config.build.generateAnalyzerReport || config.build.bundleAnalyzerReport) {
155
+  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
156
+    .BundleAnalyzerPlugin
157
+
158
+  if (config.build.bundleAnalyzerReport) {
159
+    webpackConfig.plugins.push(
160
+      new BundleAnalyzerPlugin({
161
+        analyzerPort: 8080,
162
+        generateStatsFile: false
163
+      })
164
+    )
165
+  }
166
+
167
+  if (config.build.generateAnalyzerReport) {
168
+    webpackConfig.plugins.push(
169
+      new BundleAnalyzerPlugin({
170
+        analyzerMode: 'static',
171
+        reportFilename: 'bundle-report.html',
172
+        openAnalyzer: false
173
+      })
174
+    )
175
+  }
148 176
 }
149 177
 
150 178
 module.exports = webpackConfig

+ 6 - 9
config/index.js

@@ -6,7 +6,6 @@ const path = require('path')
6 6
 
7 7
 module.exports = {
8 8
   dev: {
9
-
10 9
     // Paths
11 10
     assetsSubDirectory: 'static',
12 11
     assetsPublicPath: '/',
@@ -35,17 +34,12 @@ module.exports = {
35 34
     // https://webpack.js.org/configuration/devtool/#development
36 35
     devtool: 'cheap-source-map',
37 36
 
38
-    // If you have problems debugging vue-files in devtools,
39
-    // set this to false - it *may* help
40
-    // https://vue-loader.vuejs.org/en/options.html#cachebusting
41
-    cacheBusting: true,
42
-
43 37
     // CSS Sourcemaps off by default because relative paths are "buggy"
44 38
     // with this option, according to the CSS-Loader README
45 39
     // (https://github.com/webpack/css-loader#sourcemaps)
46 40
     // In our experience, they generally work as expected,
47 41
     // just be aware of this issue when enabling this option.
48
-    cssSourceMap: false,
42
+    cssSourceMap: false
49 43
   },
50 44
 
51 45
   build: {
@@ -71,7 +65,7 @@ module.exports = {
71 65
 
72 66
     productionSourceMap: false,
73 67
     // https://webpack.js.org/configuration/devtool/#production
74
-    devtool: '#source-map',
68
+    devtool: 'source-map',
75 69
 
76 70
     // Gzip off by default as many popular static hosts such as
77 71
     // Surge or Netlify already gzip all static assets for you.
@@ -84,6 +78,9 @@ module.exports = {
84 78
     // View the bundle analyzer report after build finishes:
85 79
     // `npm run build --report`
86 80
     // Set to `true` or `false` to always turn it on or off
87
-    bundleAnalyzerReport: process.env.npm_config_report
81
+    bundleAnalyzerReport: process.env.npm_config_report || false,
82
+
83
+    // `npm run build:prod --generate_report`
84
+    generateAnalyzerReport: process.env.npm_config_generate_report || false
88 85
   }
89 86
 }

+ 42 - 40
package.json

@@ -13,63 +13,65 @@
13 13
     "test": "npm run lint"
14 14
   },
15 15
   "dependencies": {
16
-    "axios": "0.17.1",
17
-    "element-ui": "2.3.4",
16
+    "axios": "0.18.0",
17
+    "element-ui": "2.4.6",
18 18
     "js-cookie": "2.2.0",
19 19
     "normalize.css": "7.0.0",
20 20
     "nprogress": "0.2.0",
21
-    "vue": "2.5.10",
21
+    "vue": "2.5.17",
22 22
     "vue-router": "3.0.1",
23 23
     "vuex": "3.0.1"
24 24
   },
25 25
   "devDependencies": {
26
-    "autoprefixer": "7.2.3",
26
+    "autoprefixer": "8.5.0",
27 27
     "babel-core": "6.26.0",
28
-    "babel-eslint": "8.0.3",
28
+    "babel-eslint": "8.2.6",
29 29
     "babel-helper-vue-jsx-merge-props": "2.0.3",
30
-    "babel-loader": "7.1.2",
30
+    "babel-loader": "7.1.5",
31 31
     "babel-plugin-syntax-jsx": "6.18.0",
32 32
     "babel-plugin-transform-runtime": "6.23.0",
33
-    "babel-plugin-transform-vue-jsx": "3.5.0",
34
-    "babel-preset-env": "1.6.1",
33
+    "babel-plugin-transform-vue-jsx": "3.7.0",
34
+    "babel-preset-env": "1.7.0",
35 35
     "babel-preset-stage-2": "6.24.1",
36
-    "chalk": "2.3.0",
37
-    "copy-webpack-plugin": "4.2.3",
38
-    "css-loader": "0.28.7",
39
-    "eslint": "4.13.1",
40
-    "eslint-friendly-formatter": "3.0.0",
41
-    "eslint-loader": "1.9.0",
42
-    "eslint-plugin-html": "4.0.1",
36
+    "chalk": "2.4.1",
37
+    "copy-webpack-plugin": "4.5.2",
38
+    "css-loader": "1.0.0",
39
+    "eslint": "4.19.1",
40
+    "eslint-friendly-formatter": "4.0.1",
41
+    "eslint-loader": "2.0.0",
42
+    "eslint-plugin-html": "4.0.5",
43 43
     "eventsource-polyfill": "0.9.6",
44
-    "extract-text-webpack-plugin": "3.0.2",
45
-    "file-loader": "1.1.5",
46
-    "friendly-errors-webpack-plugin": "1.6.1",
47
-    "html-webpack-plugin": "2.30.1",
48
-    "node-notifier": "5.1.2",
44
+    "file-loader": "1.1.11",
45
+    "friendly-errors-webpack-plugin": "1.7.0",
46
+    "html-webpack-plugin": "^4.0.0-alpha",
47
+    "mini-css-extract-plugin": "0.4.1",
48
+    "node-notifier": "5.2.1",
49 49
     "node-sass": "^4.7.2",
50
-    "optimize-css-assets-webpack-plugin": "3.2.0",
51
-    "ora": "1.3.0",
52
-    "portfinder": "1.0.13",
53
-    "postcss-import": "11.0.0",
54
-    "postcss-loader": "2.0.9",
55
-    "postcss-url": "7.3.0",
50
+    "optimize-css-assets-webpack-plugin": "5.0.0",
51
+    "ora": "3.0.0",
52
+    "portfinder": "1.0.16",
53
+    "postcss-import": "12.0.0",
54
+    "postcss-loader": "2.1.6",
55
+    "postcss-url": "7.3.2",
56 56
     "rimraf": "2.6.2",
57
-    "sass-loader": "6.0.6",
58
-    "semver": "5.4.1",
59
-    "shelljs": "0.7.8",
60
-    "svg-sprite-loader": "3.5.2",
61
-    "uglifyjs-webpack-plugin": "1.1.3",
62
-    "url-loader": "0.6.2",
63
-    "vue-loader": "13.7.2",
64
-    "vue-style-loader": "3.0.3",
65
-    "vue-template-compiler": "2.5.10",
66
-    "webpack": "3.10.0",
67
-    "webpack-bundle-analyzer": "2.9.1",
68
-    "webpack-dev-server": "2.9.7",
69
-    "webpack-merge": "4.1.1"
57
+    "sass-loader": "7.0.3",
58
+    "script-ext-html-webpack-plugin": "2.0.1",
59
+    "semver": "5.5.0",
60
+    "shelljs": "0.8.2",
61
+    "svg-sprite-loader": "3.8.0",
62
+    "uglifyjs-webpack-plugin": "1.2.7",
63
+    "url-loader": "1.0.1",
64
+    "vue-loader": "15.3.0",
65
+    "vue-style-loader": "4.1.2",
66
+    "vue-template-compiler": "2.5.17",
67
+    "webpack": "4.16.5",
68
+    "webpack-bundle-analyzer": "2.13.1",
69
+    "webpack-cli": "3.1.0",
70
+    "webpack-dev-server": "3.1.5",
71
+    "webpack-merge": "4.1.4"
70 72
   },
71 73
   "engines": {
72
-    "node": ">= 4.0.0",
74
+    "node": ">= 6.0.0",
73 75
     "npm": ">= 3.0.0"
74 76
   },
75 77
   "browserslist": [

+ 1 - 1
src/icons/index.js

@@ -1,5 +1,5 @@
1 1
 import Vue from 'vue'
2
-import SvgIcon from '@/components/SvgIcon'// svg组件
2
+import SvgIcon from '@/components/SvgIcon' // svg组件
3 3
 
4 4
 // register globally
5 5
 Vue.component('svg-icon', SvgIcon)

+ 1 - 1
src/main.js

@@ -1,6 +1,6 @@
1 1
 import Vue from 'vue'
2 2
 
3
-import 'normalize.css/normalize.css'// A modern alternative to CSS resets
3
+import 'normalize.css/normalize.css' // A modern alternative to CSS resets
4 4
 
5 5
 import ElementUI from 'element-ui'
6 6
 import 'element-ui/lib/theme-chalk/index.css'

+ 14 - 3
src/utils/index.js

@@ -25,7 +25,7 @@ export function parseTime(time, cFormat) {
25 25
   }
26 26
   const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
27 27
     let value = formatObj[key]
28
-    if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
28
+    if (key === 'a') { return ['一', '二', '三', '四', '五', '六', '日'][value - 1] }
29 29
     if (result.length > 0 && value < 10) {
30 30
       value = '0' + value
31 31
     }
@@ -43,7 +43,8 @@ export function formatTime(time, option) {
43 43
 
44 44
   if (diff < 30) {
45 45
     return '刚刚'
46
-  } else if (diff < 3600) { // less 1 hour
46
+  } else if (diff < 3600) {
47
+    // less 1 hour
47 48
     return Math.ceil(diff / 60) + '分钟前'
48 49
   } else if (diff < 3600 * 24) {
49 50
     return Math.ceil(diff / 3600) + '小时前'
@@ -53,6 +54,16 @@ export function formatTime(time, option) {
53 54
   if (option) {
54 55
     return parseTime(time, option)
55 56
   } else {
56
-    return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
57
+    return (
58
+      d.getMonth() +
59
+      1 +
60
+      '月' +
61
+      d.getDate() +
62
+      '日' +
63
+      d.getHours() +
64
+      '时' +
65
+      d.getMinutes() +
66
+      '分'
67
+    )
57 68
   }
58 69
 }

+ 26 - 19
src/utils/request.js

@@ -10,23 +10,26 @@ const service = axios.create({
10 10
 })
11 11
 
12 12
 // request拦截器
13
-service.interceptors.request.use(config => {
14
-  if (store.getters.token) {
15
-    config.headers['X-Token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
13
+service.interceptors.request.use(
14
+  config => {
15
+    if (store.getters.token) {
16
+      config.headers['X-Token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
17
+    }
18
+    return config
19
+  },
20
+  error => {
21
+    // Do something with request error
22
+    console.log(error) // for debug
23
+    Promise.reject(error)
16 24
   }
17
-  return config
18
-}, error => {
19
-  // Do something with request error
20
-  console.log(error) // for debug
21
-  Promise.reject(error)
22
-})
25
+)
23 26
 
24 27
 // respone拦截器
25 28
 service.interceptors.response.use(
26 29
   response => {
27
-  /**
28
-  * code为非20000是抛错 可结合自己业务进行修改
29
-  */
30
+    /**
31
+     * code为非20000是抛错 可结合自己业务进行修改
32
+     */
30 33
     const res = response.data
31 34
     if (res.code !== 20000) {
32 35
       Message({
@@ -37,13 +40,17 @@ service.interceptors.response.use(
37 40
 
38 41
       // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
39 42
       if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
40
-        MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
41
-          confirmButtonText: '重新登录',
42
-          cancelButtonText: '取消',
43
-          type: 'warning'
44
-        }).then(() => {
43
+        MessageBox.confirm(
44
+          '你已被登出,可以取消继续留在该页面,或者重新登录',
45
+          '确定登出',
46
+          {
47
+            confirmButtonText: '重新登录',
48
+            cancelButtonText: '取消',
49
+            type: 'warning'
50
+          }
51
+        ).then(() => {
45 52
           store.dispatch('FedLogOut').then(() => {
46
-            location.reload()// 为了重新实例化vue-router对象 避免bug
53
+            location.reload() // 为了重新实例化vue-router对象 避免bug
47 54
           })
48 55
         })
49 56
       }
@@ -53,7 +60,7 @@ service.interceptors.response.use(
53 60
     }
54 61
   },
55 62
   error => {
56
-    console.log('err' + error)// for debug
63
+    console.log('err' + error) // for debug
57 64
     Message({
58 65
       message: error.message,
59 66
       type: 'error',

+ 0 - 1
src/utils/validate.js

@@ -30,4 +30,3 @@ export function validatAlphabets(str) {
30 30
   const reg = /^[A-Za-z]+$/
31 31
   return reg.test(str)
32 32
 }
33
-

+ 4 - 12
src/views/404.vue

@@ -2,10 +2,10 @@
2 2
   <div class="wscn-http404-container">
3 3
     <div class="wscn-http404">
4 4
       <div class="pic-404">
5
-        <img class="pic-404__parent" :src="img_404" alt="404">
6
-        <img class="pic-404__child left" :src="img_404_cloud" alt="404">
7
-        <img class="pic-404__child mid" :src="img_404_cloud" alt="404">
8
-        <img class="pic-404__child right" :src="img_404_cloud" alt="404">
5
+        <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
6
+        <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
7
+        <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
8
+        <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
9 9
       </div>
10 10
       <div class="bullshit">
11 11
         <div class="bullshit__oops">OOPS!</div>
@@ -21,17 +21,9 @@
21 21
 </template>
22 22
 
23 23
 <script>
24
-import img_404 from '@/assets/404_images/404.png'
25
-import img_404_cloud from '@/assets/404_images/404_cloud.png'
26 24
 
27 25
 export default {
28 26
   name: 'page404',
29
-  data() {
30
-    return {
31
-      img_404,
32
-      img_404_cloud
33
-    }
34
-  },
35 27
   computed: {
36 28
     message() {
37 29
       return '网管说这个页面你不能进......'