zoukankan      html  css  js  c++  java
  • Vue2.5 Web App 项目搭建 (TypeScript版)

    参考了几位同行的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     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).pathname
    374         }
    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 的 sourceMap
    12     "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 options
    60 const i18n = new VueI18n({
    61     locale: 'zh-CN', // set locale
    62     messages, // set locale messages
    63     silentTranslationWarn: true
    64 })
    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     routes
    74 })
    75 
    76 const vm = new Vue({
    77     el: "#app",
    78     data: {rootid: "ac"},
    79     // components: {
    80     //     echarts
    81     // },
    82     router,
    83     render: h => h(App),
    84     i18n
    85 })
  • 相关阅读:
    rgb随机颜色函数
    mapshaper转geojson
    postgis
    Draw
    ol 聚类ol.source.Cluster的使用
    ol ---- overlay autoPan的使用
    多层数据注入同一个图层源时,要批量删除某一种要素
    js遍历数组,并从数组中删除元素
    echarts加载geojson
    centos65编译安装lamp和lnmp
  • 原文地址:https://www.cnblogs.com/clockdotnet/p/VueTypeScriptProject.html
Copyright © 2011-2022 走看看