博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Vue2.5 Web App 项目搭建 (TypeScript版)
阅读量:4610 次
发布时间:2019-06-09

本文共 22338 字,大约阅读时间需要 74 分钟。

参考了几位同行的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: true109 }], {}),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: true123 }),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 ? '/' : publicPath144 },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的图片会自动转成dataUrl225 {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 file296 new webpack.optimize.CommonsChunkPlugin({297 name: 'vendor',298 minChunks(module) {299 // any required modules inside node_modules are extracted to vendor300 return (301 module.resource &&302 /\.js$/.test(module.resource) &&303 module.resource.indexOf(304 resolve(__dirname, '../node_modules')305 ) === 0306 )307 }308 }),309 // extract webpack runtime and module manifest to its own file in order to310 // prevent vendor hash from being updated whenever app bundle is updated311 new webpack.optimize.CommonsChunkPlugin({312 name: 'manifest',313 minChunks: Infinity,314 }),315 // This instance extracts shared chunks from code splitted chunks and bundles them316 // in a separate chunk, similar to the vendor chunk317 // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk318 new webpack.optimize.CommonsChunkPlugin({319 name: 'main',320 async: 'common',321 children: true,322 minChunks: 2323 }),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 ? '/' : publicPath341 }),342 new webpack.ProvidePlugin({343 _: 'lodash',344 Enumerable: 'linq'345 })346 ]),347 node: {348 // prevent webpack from injecting useless setImmediate polyfill because Vue349 // source contains it (although only uses it if it's native).350 setImmediate: false,351 // prevent webpack from injecting mocks to Node native modules352 // that does not make sense for the client353 dgram: 'empty',354 fs: 'empty',355 net: 'empty',356 tls: 'empty',357 child_process: 'empty'358 },359 devServer: {360 host: '127.0.0.1',361 port: 8081,362 proxy: {363 '/api/*': {364 target: 'http://127.0.0.1:8080',365 secure: false,366 changeOrigin: true,367 pathRewrite: {368 '^/api': ''369 }370 }371 },372 historyApiFallback: {373 index: url.parse(options.dev ? '/' : publicPath).pathname374 }375 },376 devtool: options.dev ? '#cheap-module-eval-source-map' : '#source-map',377 })

tsconfig.json

1 { 2   "compilerOptions": { 3     // 编译输出目标 ES 版本 4     "target": "es5", 5     // 采用的模块系统 6     "module": "esnext", 7     // 如何处理模块 8     "moduleResolution": "node", 9     // 以严格模式解析10     "strict": false,11     // 是否包含可以用于 debug 的 sourceMap12     "sourceMap": true,13     // 允许从没有设置默认导出的模块中默认导入14     "allowSyntheticDefaultImports": true,15     // 将每个文件作为单独的模块16     "isolatedModules": false,17     // 启用装饰器18     "experimentalDecorators": true,19     // 启用设计类型元数据(用于反射)20     "emitDecoratorMetadata": true,21     "removeComments": false,22     // 在表达式和声明上有隐含的any类型时报错23     "noImplicitAny": false,24     // 不是函数的所有返回路径都有返回值时报错。25     "noImplicitReturns": true,26     // 从 tslib 导入外部帮助库: 比如__extends,__rest等27     "importHelpers": true,28     "suppressImplicitAnyIndexErrors": true,29     "noResolve": false,30     // 允许编译javascript文件31     "allowJs": true,32     // 解析非相对模块名的基准目录33     "baseUrl": "./",34     // 指定特殊模块的路径35     "paths": {36       "jquery": [37         "node_modules/jquery/dist/jquery"38       ]39     },40     "lib": ["es2017", "dom"],41     "jsx": "preserve"42   },43   "exclude": [44     "node_modules"45   ]46 }

.babelrc

1 {2   "presets": ["env"],3   "plugins": [4     "syntax-dynamic-import",5     "transform-vue-jsx"6   ]7 }

typings.d.ts

1 import {AxiosStatic} from "axios"; 2  3 declare module "*.png" { 4     const value: any; 5     export default value; 6 } 7  8 declare module "*.jpg" { 9     const value: any;10     export default value;11 }12 13 declare module 'vue/types/vue' {14     interface Vue {15         $http: AxiosStatic,16         $socket: any,17     }18 }

vue-shim.d.ts

1 declare module "*.vue" {2     import Vue from "vue";3     export default Vue;4 }

main.ts

1 import Vue, { AsyncComponent } from 'vue'; 2 import Vuex from "vuex"; 3 import VueRouter from "vue-router"; 4 import VueLazyLoad from "vue-lazyload"; 5 import VueI18n from 'vue-i18n' 6 import axios from "axios"; 7  8 import BootstrapVue from "bootstrap-vue"; 9 import ElementUI from "element-ui";10 11 import enLocaleElementUI from 'element-ui/lib/locale/lang/en'12 import zhCNLocaleElementUI from 'element-ui/lib/locale/lang/zh-CN'13 import enLocaleCommon from './common/lang/en'14 import zhCNLocaleCommon from './common/lang/zh-CN'15 import enLocaleApp from './lang/en'16 import zhCNLocaleApp from './lang/zh-CN'17 18 import VueSocketio from 'vue-socket.io';19 20 import App from "./app.vue";21 import routes from "./framework/routes";22 23 import "@/common/style/baseStyle.css";24 import "@/common/style/var.scss";25 import "@/common/style/layout.scss";26 27 // import VueECharts from "vue-echarts/components/ECharts.vue";28 // import ECharts modules manually to reduce bundle size;29 // import "echarts/lib/chart/bar";30 // import "echarts/lib/component/tooltip";31 32 Vue.use(Vuex);33 Vue.use(VueRouter);34 35 Vue.use(VueLazyLoad, {36     // error:"./static/error.png",37     // loading:"./static/loading.png"38 })39 40 Vue.use(VueI18n)41 42 Vue.prototype.$http = axios;43 44 Vue.use(BootstrapVue);45 46 const messages = {47     "en": {48         ...enLocaleElementUI,49         ...enLocaleCommon,50         ...enLocaleApp,51     },52     "zh-CN": {53         ...zhCNLocaleElementUI,54         ...zhCNLocaleCommon,55         ...zhCNLocaleApp,56     }57 }58 59 // Create VueI18n instance with options60 const i18n = new VueI18n({61     locale: 'zh-CN', // set locale62     messages, // set locale messages63     silentTranslationWarn: true64 })65 66 Vue.use(ElementUI, {67     i18n: (key, value) => i18n.t(key, value)68 })69 70 Vue.use(VueSocketio, 'http://127.0.0.1:9092');71 72 const router = new VueRouter({73     routes74 })75 76 const vm = new Vue({77     el: "#app",78     data: {rootid: "ac"},79     // components: {
80 // echarts81 // },82 router,83 render: h => h(App),84 i18n85 })

 

转载于:https://www.cnblogs.com/clockdotnet/p/VueTypeScriptProject.html

你可能感兴趣的文章
[leetcode712]204. Count Primes寻找范围内的素数
查看>>
VS2010-MFC(菜单:菜单及CMenu类的使用)
查看>>
cassandra数据库
查看>>
UVA 10600 ACM Contest and Blackout 次小生成树
查看>>
浏览器内部工作原理
查看>>
Django电商项目---完成用户中心(订单中心+收货地址)day7
查看>>
C#获取当前路径的方法
查看>>
汉字转拼音
查看>>
不使用IDE,用maven命令打包war项目及出现的问题解决(最全攻略,亲测有效)
查看>>
参数传递方法(用Delphi的汇编代码解释)
查看>>
安装g++
查看>>
Android IM资料
查看>>
来自苹果、谷歌、微软等知名公司六道有趣智力面试题(转自ACM之家)
查看>>
Mac Yosemite 10.10 下利用 Launchctl 自启动 mysql
查看>>
Linux命令行与脚本编程大全第一章
查看>>
ES6 新特性
查看>>
20145238 —《Java程序设计》—第5周学习总结
查看>>
异常处理
查看>>
关系运算符及逻辑运算符
查看>>
Linux之查看系统配置命令
查看>>