在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
参考了几位同行的Blogs和StackOverflow上的许多问答,在原来的ng1加TypeScript以及Webpack的经验基础上,搭建了该项目,核心文件如下,供需要的人参考。 package.json 1 { 2 "name": "app", 3 "version": "1.0.0", 4 "description": "App package.json from the documentation, supplemented with testing support", 5 "author": "", 6 "private": true, 7 "scripts": { 8 "dev": "webpack-dev-server -d --inline --hot --env.dev", 9 "build": "rimraf dist && webpack --progress --hide-modules" 10 }, 11 "dependencies": { 12 "axios": "^0.18.0", 13 "bootstrap": "^4.0.0", 14 "bootstrap-vue": "^2.0.0-rc.2", 15 "element-ui": "^2.2.2", 16 "font-awesome": "^4.7.0", 17 "jointjs": "^2.0.1", 18 "jquery": "^3.3.1", 19 "js-md5": "^0.7.3", 20 "layui-src": "^2.2.5", 21 "linq": "^3.0.9", 22 "lodash": "^4.17.5", 23 "pdfmake": "^0.1.36", 24 "popper.js": "^1.14.1", 25 "tinymce": "^4.7.12", 26 "uuid": "^3.2.1", 27 "vue": "^2.5.16", 28 "vue-class-component": "^6.2.0", 29 "vue-echarts-v3": "^1.0.19", 30 "vue-i18n": "^7.6.0", 31 "vue-i18n-extensions": "^0.1.0", 32 "vue-lazyload": "^1.2.3", 33 "vue-pdf": "^3.3.1", 34 "vue-property-decorator": "^6.0.0", 35 "vue-router": "^3.0.1", 36 "vue-socket.io": "^2.1.1-b", 37 "vue-tinymce": "github:lpreterite/vue-tinymce", 38 "vue-video-player": "^5.0.2", 39 "vuex": "^3.0.1", 40 "vuex-class": "^0.3.0" 41 }, 42 "engines": { 43 "node": ">=6.0.0", 44 "npm": ">= 3.0.0" 45 }, 46 "browserslist": [ 47 "> 1%", 48 "last 2 versions", 49 "not ie <= 8" 50 ], 51 "devDependencies": { 52 "@kazupon/vue-i18n-loader": "^0.3.0", 53 "@types/lodash": "^4.14.106", 54 "ajv": "^6.3.0", 55 "autoprefixer": "^8.2.0", 56 "babel-core": "^6.26.3", 57 "babel-helper-vue-jsx-merge-props": "^2.0.3", 58 "babel-loader": "^7.1.4", 59 "babel-plugin-syntax-dynamic-import": "^6.18.0", 60 "babel-plugin-syntax-jsx": "^6.18.0", 61 "babel-plugin-transform-vue-jsx": "^3.7.0", 62 "babel-preset-env": "^1.6.1", 63 "bootstrap-loader": "^2.2.0", 64 "clean-webpack-plugin": "^0.1.19", 65 "compression-webpack-plugin": "^1.1.11", 66 "copy-webpack-plugin": "^4.5.1", 67 "css-loader": "^0.28.11", 68 "cssnano": "^3.10.0", 69 "extract-text-webpack-plugin": "^3.0.2", 70 "file-loader": "^1.1.11", 71 "html-loader": "^0.5.5", 72 "html-webpack-plugin": "^3.1.0", 73 "image-webpack-loader": "^4.2.0", 74 "json-loader": "^0.5.7", 75 "node-sass": "^4.7.2", 76 "optimize-css-assets-webpack-plugin": "^3.2.0", 77 "postcss-import": "^11.1.0", 78 "postcss-loader": "^2.1.3", 79 "postcss-url": "^7.3.2", 80 "resolve-url-loader": "^2.3.0", 81 "rimraf": "^2.6.2", 82 "sass-loader": "^6.0.7", 83 "sass-resources-loader": "^1.3.3", 84 "style-loader": "^0.20.3", 85 "ts-loader": "^3.1.1", 86 "tslint": "^5.9.1", 87 "tslint-config-standard": "^7.0.0", 88 "tslint-loader": "^3.6.0", 89 "typescript": "^2.8.3", 90 "uglifyjs-webpack-plugin": "^1.2.5", 91 "url-loader": "^1.0.1", 92 "vue-loader": "^14.2.1", 93 "vue-style-loader": "^4.1.0", 94 "vue-template-compiler": "^2.5.16", 95 "webpack": "^3.1.0", 96 "webpack-dev-server": "^2.9.4", 97 "webpack-parallel-uglify-plugin": "^1.1.0" 98 } 99 } webpack.config.js 1 const {resolve} = require('path'); 2 const webpack = require('webpack'); 3 const CopyWebpackPlugin = require('copy-webpack-plugin'); 4 const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 const ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin'); 7 const ParallelUglifyPlugin=require('webpack-parallel-uglify-plugin') ; 8 const CompressionWebpackPlugin = require('compression-webpack-plugin'); 9 const CleanWebpackPlugin = require('clean-webpack-plugin'); 10 const url = require('url'); 11 const publicPath = '/public/'; 12 13 function getVueStyleLoader(isDev) { 14 if (!isDev) { 15 return ExtractTextPlugin.extract({ 16 fallback: "vue-style-loader", 17 use: ["css-loader", "postcss-loader", "sass-loader", 18 "sass-resources-loader?resources=./src/common/style/sass-resources.scss"] 19 }); 20 } 21 return "vue-style-loader!css-loader?sourceMap!postcss-loader?sourceMap!sass-loader?sourceMap!sass-resources-loader?resources=./src/common/style/sass-resources.scss"; 22 } 23 24 function getCssLoader(isDev) { 25 if (!isDev) { 26 return ExtractTextPlugin.extract({ 27 fallback: "style-loader", 28 use: ["css-loader", "postcss-loader"] 29 }); 30 } 31 return [ 32 {loader: 'style-loader'}, 33 {loader: 'css-loader', options: {sourceMap: true}}, 34 {loader: 'postcss-loader', options: {sourceMap: true}}, 35 ]; 36 } 37 38 function getScssLoader(isDev) { 39 if (!isDev) { 40 return ExtractTextPlugin.extract({ 41 fallback: "style-loader", 42 use: ["css-loader", "postcss-loader", "sass-loader", 43 "sass-resources-loader?resources=./src/common/style/sass-resources.scss"] 44 }); 45 } 46 return [ 47 {loader: 'style-loader'}, 48 {loader: 'css-loader', options: {sourceMap: true}}, 49 {loader: 'postcss-loader', options: {sourceMap: true}}, 50 {loader: 'sass-loader', options: {sourceMap: true}}, 51 { 52 loader: 'sass-resources-loader', 53 options: {resources: './src/common/style/sass-resources.scss'} 54 } 55 ]; 56 } 57 58 function getPlugins(isDev, plugins) { 59 if (!isDev) { 60 plugins.push( 61 new ExtractTextPlugin({ 62 filename: 'assets/css/[name].[contenthash:8].css', 63 // Setting the following option to `false` will not extract CSS from codesplit chunks. 64 // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 65 // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 66 // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 67 allChunks: true, 68 }), 69 // Compress extracted CSS. We are using this plugin so that possible 70 // duplicated CSS from different components can be deduped. 71 new OptimizeCSSPlugin({ 72 assetNameRegExp: /\.css$/g, 73 cssProcessor: require('cssnano'), 74 cssProcessorOptions: { discardComments: {removeAll: true}}, 75 canPrint: true, 76 }), 77 new ParallelUglifyPlugin({ 78 uglifyJS: { 79 output: { 80 comments: false //去掉注释 81 }, 82 compress: { 83 warnings: false, 84 drop_debugger: true, 85 drop_console: true 86 }, 87 sourceMap: false, 88 } 89 }), 90 // new CompressionWebpackPlugin({ 91 // asset: '[path].gz[query]', //目标文件名 92 // algorithm: 'gzip', //使用gzip压缩 93 // test: new RegExp( //满足正则表达式的文件会被压缩 94 // '\\.(' + ['js', 'css'].join('|') + ')$' 95 // ), 96 // threshold: 10240, //资源文件大于10240B=10kB时会被压缩 97 // minRatio: 0.8 //最小压缩比达到0.8时才会被压缩 98 // }), 99 new CopyWebpackPlugin([ 100 { 101 from: resolve(__dirname, 'static'), 102 to: resolve(__dirname, `../web/static`), 103 ignore: ['.*'] //忽视.*文件 104 }, 105 { 106 from: 'favicon.ico', 107 to: resolve(__dirname, '../web/'), 108 force: true 109 }], {}), 110 new webpack.DefinePlugin({ 111 'process.env': { 112 NODE_ENV: JSON.stringify('production') 113 } 114 }), 115 new CleanWebpackPlugin([ 116 `../web/${publicPath}/chunks`, 117 `../web/${publicPath}/assets`, 118 `../web/static`], { 119 root: __dirname, 120 verbose: true, 121 dry: false, 122 allowExternal: true 123 }), 124 ); 125 } 126 return plugins; 127 } 128 129 module.exports = (options = {}) => ({ 130 entry: { 131 vendor: [ 132 './src/vendor.ts', 133 `bootstrap-loader/lib/bootstrap.loader?${!options.dev ? 'extractStyles' : ''}&configFilePath=${__dirname}/.bootstraprc!bootstrap-loader/no-op.js`, 134 'lodash', 135 'linq' 136 ], 137 main: './src/main.ts' 138 }, 139 output: { 140 path: resolve(__dirname, '../web' + publicPath), 141 filename: '[name].js', 142 chunkFilename: 'chunks/[name].[chunkhash:8].js', 143 publicPath: options.dev ? '/' : publicPath 144 }, 145 resolve: { 146 extensions: ['.ts', '.tsx', '.js', '.vue', '.json'], 147 alias: { 148 'vue$': 'vue/dist/vue.esm.js', 149 '@': resolve(__dirname, 'src'), 150 } 151 }, 152 module: { 153 rules: [ 154 { 155 test: /\.js$/, 156 loader: 'babel-loader', 157 // exclude: file => ( 158 // /node_modules/.test(file) && 159 // !/\.vue\.js/.test(file) 160 // ), 161 include: [ 162 resolve('src'), 163 resolve('node_modules/vue-echarts-v3/src'), 164 resolve('node_modules/vue-pdf/src') 165 ] 166 }, 167 { 168 test: /\.tsx?$/, 169 exclude: /node_modules/, 170 enforce: 'pre', 171 loader: 'tslint-loader' 172 }, 173 { 174 test: /\.tsx?$/, 175 exclude: /node_modules|vue\/src/, 176 use: [ 177 "babel-loader", 178 { 179 loader: "ts-loader", 180 options: { 181 appendTsSuffixTo: [/\.vue$/], 182 transpileOnly: true, 183 } 184 } 185 ] 186 }, 187 { 188 test: /\.vue$/, 189 use: [{ 190 loader: 'vue-loader', 191 options: { 192 loaders: { 193 js: "babel-loader", 194 ts: "ts-loader!tslint-loader", 195 tsx: "babel-loader!ts-loader!tslint-loader", 196 scss: getVueStyleLoader(options.dev), 197 i18n: "@kazupon/vue-i18n-loader" 198 } 199 } 200 }] 201 }, 202 { 203 test: /\.css$/, 204 use: getCssLoader(options.dev), 205 }, 206 { 207 test: /\.scss$/, 208 use: getScssLoader(options.dev), 209 exclude: /node_modules/ 210 }, 211 { 212 test: /favicon\.png$/, 213 use: [{ 214 loader: 'file-loader', 215 options: { 216 name: '[name].[ext]?[hash]' 217 } 218 }] 219 }, 220 { 221 test: /\.((woff2?|svg)(\?v=[0-9]\.[0-9]\.[0-9]))|(woff2?|svg|jpe?g|png|gif|ico)$/, 222 exclude: /favicon\.png$/, 223 use: [ 224 // 小于10KB的图片会自动转成dataUrl 225 { 226 loader: 'url-loader', 227 options: { 228 limit: 10240, 229 name: "assets/image/[name].[hash:8].[ext]" 230 } 231 }, 232 { 233 loader: 'image-webpack-loader', 234 options: { 235 query: { 236 mozjpeg: { 237 progressive: true, 238 }, 239 gifsicle: { 240 interlaced: true, 241 }, 242 optipng: { 243 bypassOnDebug: true, 244 progressive: true, 245 pngquant: {quality: "65-80", speed: 4} 246 } 247 } 248 } 249 } 250 ] 251 }, 252 { 253 test: /\.((ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9]))|(ttf|eot)$/, 254 use: [ 255 { 256 loader: 'url-loader', 257 options: { 258 limit: 10240, 259 name: "assets/font/[name].[hash:8].[ext]" 260 } 261 }] 262 }, 263 { 264 test: /\.json$/, 265 loader: 'json-loader', 266 exclude: /node_modules/ 267 } 268 ], 269 loaders: [ 270 { 271 test: require.resolve('tinymce/tinymce'), 272 loaders: [ 273 'imports?this=>window', 274 'exports?window.tinymce' 275 ] 276 }, 277 { 278 test: /tinymce\/(themes|plugins)\//, 279 loaders: [ 280 'imports?this=>window' 281 ] 282 }] 283 }, 284 plugins: getPlugins(options.dev, [ 285 new CopyWebpackPlugin([ 286 { from: './node_modules/layui-src/dist/lay', to: './chunks/lay' }, 287 { from: './node_modules/layui-src/dist/css', to: './chunks/css' }, 288 { from: './node_modules/tinymce/plugins', to: './chunks/plugins' }, 289 { from: './node_modules/tinymce/themes', to: './chunks/themes' }, 290 { from: './node_modules/tinymce/skins', to: './chunks/skins' }, 291 // {from: 'viewer', 292 // to: (options.dev ? '/' : resolve(__dirname, './build/public/viewer/')), 293 // force: true} 294 ], {}), 295 // split vendor js into its own file 296 new webpack.optimize.CommonsChunkPlugin({ 297 name: 'vendor', 298 minChunks(module) { 299 // any required modules inside node_modules are extracted to vendor 300 return ( 301 module.resource && 302 /\.js$/.test(module.resource) && 303 module.resource.indexOf( 304 resolve(__dirname, '../node_modules') 305 ) === 0 306 ) 307 } 308 }), 309 // extract webpack runtime and module manifest to its own file in order to 310 // prevent vendor hash from being updated whenever app bundle is updated 311 new webpack.optimize.CommonsChunkPlugin({ 312 name: 'manifest', 313 minChunks: Infinity, 314 }), 315 // This instance extracts shared chunks from code splitted chunks and bundles them 316 // in a separate chunk, similar to the vendor chunk 317 // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 318 new webpack.optimize.CommonsChunkPlugin({ 319 name: 'main', 320 async: 'common', 321 children: true, 322 minChunks: 2 323 }), 324 new HtmlWebpackPlugin({ 325 template: 'src/index.html', 326 filename: options.dev ? 'index.html' : resolve(__dirname, '../web/index.html'), 327 inject: true, //注入的js文件将会被放在body标签中,当值为'head'时,将被放在head标签中 328 chunks: ["manifest", "vendor", "common", "main"], 329 hash: true, 330 minify: { //压缩配置 331 removeComments: true, //删除html中的注释代码 332 collapseWhitespace: true, //删除html中的空白符 333 removeAttributeQuotes: true //删除html元素中属性的引号 334 }, 335 chunksSortMode: 'dependency' //按dependency的顺序引入 336 }), 337 // 该处设定的参数可在程序中访问,但必须以/开头和结尾,并且/也会是值的一部分 338 new webpack.DefinePlugin({ 339 __API_PATH__: options.dev ? '/api/' : '/ /', // 此处必须写成/ / ,必须以/开头和结尾,且中间必须有一个空格 340 __ASSETS_PATH__: options.dev ? '/' : publicPath 341 }), 342 new webpack.ProvidePlugin({ 343 _: 'lodash', 344 Enumerable: 'linq' 345 }) 346 ]), 347 node: { 348 // prevent webpack from injecting useless setImmediate polyfill because Vue 349 // source contains it (although only uses it if it's native). 350 setImmediate: false, 351 // prevent webpack from injecting mocks to Node native modules 352 // that does not make sense for the client 353 dgram: 'empty', 354 fs: 'empty', 355 net: 'empty', 356 tls: 'empty', 357 child_process: 'empty' 358 }, 359 全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论