Vue2.5 Web App 项目搭建 (TypeScript版)
参考了几位同行的Blogs和StackOverflow上的许多问答,在原来的ng1加TypeScript以及Webpack的经验基础上,搭建了该项目,核心文件如下,供需要的人参考。
package.json
{
"name": "app",
"version": "1.0.0",
"description": "App package.json from the documentation, supplemented with testing support",
"author": "",
"private": true,
"scripts": {
"dev": "webpack-dev-server -d --inline --hot --env.dev",
"build": "rimraf dist && webpack --progress --hide-modules"
},
"dependencies": {
"axios": "^0.18.0",
"bootstrap": "^4.0.0",
"bootstrap-vue": "^2.0.0-rc.2",
"element-ui": "^2.2.2",
"font-awesome": "^4.7.0",
"jointjs": "^2.0.1",
"jquery": "^3.3.1",
"js-md5": "^0.7.3",
"layui-src": "^2.2.5",
"linq": "^3.0.9",
"lodash": "^4.17.5",
"pdfmake": "^0.1.36",
"popper.js": "^1.14.1",
"tinymce": "^4.7.12",
"uuid": "^3.2.1",
"vue": "^2.5.16",
"vue-class-component": "^6.2.0",
"vue-echarts-v3": "^1.0.19",
"vue-i18n": "^7.6.0",
"vue-i18n-extensions": "^0.1.0",
"vue-lazyload": "^1.2.3",
"vue-pdf": "^3.3.1",
"vue-property-decorator": "^6.0.0",
"vue-router": "^3.0.1",
"vue-socket.io": "^2.1.1-b",
"vue-tinymce": "github:lpreterite/vue-tinymce",
"vue-video-player": "^5.0.2",
"vuex": "^3.0.1",
"vuex-class": "^0.3.0"
},
"engines": {
"node": ">=6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"devDependencies": {
"@kazupon/vue-i18n-loader": "^0.3.0",
"@types/lodash": "^4.14.106",
"ajv": "^6.3.0",
"autoprefixer": "^8.2.0",
"babel-core": "^6.26.3",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.4",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-vue-jsx": "^3.7.0",
"babel-preset-env": "^1.6.1",
"bootstrap-loader": "^2.2.0",
"clean-webpack-plugin": "^0.1.19",
"compression-webpack-plugin": "^1.1.11",
"copy-webpack-plugin": "^4.5.1",
"css-loader": "^0.28.11",
"cssnano": "^3.10.0",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.11",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.1.0",
"image-webpack-loader": "^4.2.0",
"json-loader": "^0.5.7",
"node-sass": "^4.7.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"postcss-import": "^11.1.0",
"postcss-loader": "^2.1.3",
"postcss-url": "^7.3.2",
"resolve-url-loader": "^2.3.0",
"rimraf": "^2.6.2",
"sass-loader": "^6.0.7",
"sass-resources-loader": "^1.3.3",
"style-loader": "^0.20.3",
"ts-loader": "^3.1.1",
"tslint": "^5.9.1",
"tslint-config-standard": "^7.0.0",
"tslint-loader": "^3.6.0",
"typescript": "^2.8.3",
"uglifyjs-webpack-plugin": "^1.2.5",
"url-loader": "^1.0.1",
"vue-loader": "^14.2.1",
"vue-style-loader": "^4.1.0",
"vue-template-compiler": "^2.5.16",
"webpack": "^3.1.0",
"webpack-dev-server": "^2.9.4",
"webpack-parallel-uglify-plugin": "^1.1.0"
}
}
webpack.config.js
const {resolve} = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
const ParallelUglifyPlugin=require('webpack-parallel-uglify-plugin') ;
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const url = require('url');
const publicPath = '/public/'; function getVueStyleLoader(isDev) {
if (!isDev) {
return ExtractTextPlugin.extract({
fallback: "vue-style-loader",
use: ["css-loader", "postcss-loader", "sass-loader",
"sass-resources-loader?resources=./src/common/style/sass-resources.scss"]
});
}
return "vue-style-loader!css-loader?sourceMap!postcss-loader?sourceMap!sass-loader?sourceMap!sass-resources-loader?resources=./src/common/style/sass-resources.scss";
} function getCssLoader(isDev) {
if (!isDev) {
return ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader", "postcss-loader"]
});
}
return [
{loader: 'style-loader'},
{loader: 'css-loader', options: {sourceMap: true}},
{loader: 'postcss-loader', options: {sourceMap: true}},
];
} function getScssLoader(isDev) {
if (!isDev) {
return ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader", "postcss-loader", "sass-loader",
"sass-resources-loader?resources=./src/common/style/sass-resources.scss"]
});
}
return [
{loader: 'style-loader'},
{loader: 'css-loader', options: {sourceMap: true}},
{loader: 'postcss-loader', options: {sourceMap: true}},
{loader: 'sass-loader', options: {sourceMap: true}},
{
loader: 'sass-resources-loader',
options: {resources: './src/common/style/sass-resources.scss'}
}
];
} function getPlugins(isDev, plugins) {
if (!isDev) {
plugins.push(
new ExtractTextPlugin({
filename: 'assets/css/[name].[contenthash:8].css',
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorOptions: { discardComments: {removeAll: true}},
canPrint: true,
}),
new ParallelUglifyPlugin({
uglifyJS: {
output: {
comments: false //去掉注释
},
compress: {
warnings: false,
drop_debugger: true,
drop_console: true
},
sourceMap: false,
}
}),
// new CompressionWebpackPlugin({
// asset: '[path].gz[query]', //目标文件名
// algorithm: 'gzip', //使用gzip压缩
// test: new RegExp( //满足正则表达式的文件会被压缩
// '\\.(' + ['js', 'css'].join('|') + ')$'
// ),
// threshold: 10240, //资源文件大于10240B=10kB时会被压缩
// minRatio: 0.8 //最小压缩比达到0.8时才会被压缩
// }),
new CopyWebpackPlugin([
{
from: resolve(__dirname, 'static'),
to: resolve(__dirname, `../web/static`),
ignore: ['.*'] //忽视.*文件
},
{
from: 'favicon.ico',
to: resolve(__dirname, '../web/'),
force: true
}], {}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new CleanWebpackPlugin([
`../web/${publicPath}/chunks`,
`../web/${publicPath}/assets`,
`../web/static`], {
root: __dirname,
verbose: true,
dry: false,
allowExternal: true
}),
);
}
return plugins;
} module.exports = (options = {}) => ({
entry: {
vendor: [
'./src/vendor.ts',
`bootstrap-loader/lib/bootstrap.loader?${!options.dev ? 'extractStyles' : ''}&configFilePath=${__dirname}/.bootstraprc!bootstrap-loader/no-op.js`,
'lodash',
'linq'
],
main: './src/main.ts'
},
output: {
path: resolve(__dirname, '../web' + publicPath),
filename: '[name].js',
chunkFilename: 'chunks/[name].[chunkhash:8].js',
publicPath: options.dev ? '/' : publicPath
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve(__dirname, 'src'),
}
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
// exclude: file => (
// /node_modules/.test(file) &&
// !/\.vue\.js/.test(file)
// ),
include: [
resolve('src'),
resolve('node_modules/vue-echarts-v3/src'),
resolve('node_modules/vue-pdf/src')
]
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
enforce: 'pre',
loader: 'tslint-loader'
},
{
test: /\.tsx?$/,
exclude: /node_modules|vue\/src/,
use: [
"babel-loader",
{
loader: "ts-loader",
options: {
appendTsSuffixTo: [/\.vue$/],
transpileOnly: true,
}
}
]
},
{
test: /\.vue$/,
use: [{
loader: 'vue-loader',
options: {
loaders: {
js: "babel-loader",
ts: "ts-loader!tslint-loader",
tsx: "babel-loader!ts-loader!tslint-loader",
scss: getVueStyleLoader(options.dev),
i18n: "@kazupon/vue-i18n-loader"
}
}
}]
},
{
test: /\.css$/,
use: getCssLoader(options.dev),
},
{
test: /\.scss$/,
use: getScssLoader(options.dev),
exclude: /node_modules/
},
{
test: /favicon\.png$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}]
},
{
test: /\.((woff2?|svg)(\?v=[0-9]\.[0-9]\.[0-9]))|(woff2?|svg|jpe?g|png|gif|ico)$/,
exclude: /favicon\.png$/,
use: [
// 小于10KB的图片会自动转成dataUrl
{
loader: 'url-loader',
options: {
limit: 10240,
name: "assets/image/[name].[hash:8].[ext]"
}
},
{
loader: 'image-webpack-loader',
options: {
query: {
mozjpeg: {
progressive: true,
},
gifsicle: {
interlaced: true,
},
optipng: {
bypassOnDebug: true,
progressive: true,
pngquant: {quality: "65-80", speed: 4}
}
}
}
}
]
},
{
test: /\.((ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9]))|(ttf|eot)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
name: "assets/font/[name].[hash:8].[ext]"
}
}]
},
{
test: /\.json$/,
loader: 'json-loader',
exclude: /node_modules/
}
],
loaders: [
{
test: require.resolve('tinymce/tinymce'),
loaders: [
'imports?this=>window',
'exports?window.tinymce'
]
},
{
test: /tinymce\/(themes|plugins)\//,
loaders: [
'imports?this=>window'
]
}]
},
plugins: getPlugins(options.dev, [
new CopyWebpackPlugin([
{ from: './node_modules/layui-src/dist/lay', to: './chunks/lay' },
{ from: './node_modules/layui-src/dist/css', to: './chunks/css' },
{ from: './node_modules/tinymce/plugins', to: './chunks/plugins' },
{ from: './node_modules/tinymce/themes', to: './chunks/themes' },
{ from: './node_modules/tinymce/skins', to: './chunks/skins' },
// {from: 'viewer',
// to: (options.dev ? '/' : resolve(__dirname, './build/public/viewer/')),
// force: true}
], {}),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks(module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
resolve(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity,
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'main',
async: 'common',
children: true,
minChunks: 2
}),
new HtmlWebpackPlugin({
template: 'src/index.html',
filename: options.dev ? 'index.html' : resolve(__dirname, '../web/index.html'),
inject: true, //注入的js文件将会被放在body标签中,当值为'head'时,将被放在head标签中
chunks: ["manifest", "vendor", "common", "main"],
hash: true,
minify: { //压缩配置
removeComments: true, //删除html中的注释代码
collapseWhitespace: true, //删除html中的空白符
removeAttributeQuotes: true //删除html元素中属性的引号
},
chunksSortMode: 'dependency' //按dependency的顺序引入
}),
// 该处设定的参数可在程序中访问,但必须以/开头和结尾,并且/也会是值的一部分
new webpack.DefinePlugin({
__API_PATH__: options.dev ? '/api/' : '/ /', // 此处必须写成/ / ,必须以/开头和结尾,且中间必须有一个空格
__ASSETS_PATH__: options.dev ? '/' : publicPath
}),
new webpack.ProvidePlugin({
_: 'lodash',
Enumerable: 'linq'
})
]),
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
},
devServer: {
host: '127.0.0.1',
port: 8081,
proxy: {
'/api/*': {
target: 'http://127.0.0.1:8080',
secure: false,
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
},
historyApiFallback: {
index: url.parse(options.dev ? '/' : publicPath).pathname
}
},
devtool: options.dev ? '#cheap-module-eval-source-map' : '#source-map',
})
tsconfig.json
{
"compilerOptions": {
// 编译输出目标 ES 版本
"target": "es5",
// 采用的模块系统
"module": "esnext",
// 如何处理模块
"moduleResolution": "node",
// 以严格模式解析
"strict": false,
// 是否包含可以用于 debug 的 sourceMap
"sourceMap": true,
// 允许从没有设置默认导出的模块中默认导入
"allowSyntheticDefaultImports": true,
// 将每个文件作为单独的模块
"isolatedModules": false,
// 启用装饰器
"experimentalDecorators": true,
// 启用设计类型元数据(用于反射)
"emitDecoratorMetadata": true,
"removeComments": false,
// 在表达式和声明上有隐含的any类型时报错
"noImplicitAny": false,
// 不是函数的所有返回路径都有返回值时报错。
"noImplicitReturns": true,
// 从 tslib 导入外部帮助库: 比如__extends,__rest等
"importHelpers": true,
"suppressImplicitAnyIndexErrors": true,
"noResolve": false,
// 允许编译javascript文件
"allowJs": true,
// 解析非相对模块名的基准目录
"baseUrl": "./",
// 指定特殊模块的路径
"paths": {
"jquery": [
"node_modules/jquery/dist/jquery"
]
},
"lib": ["es2017", "dom"],
"jsx": "preserve"
},
"exclude": [
"node_modules"
]
}
.babelrc
{
"presets": ["env"],
"plugins": [
"syntax-dynamic-import",
"transform-vue-jsx"
]
}
typings.d.ts
import {AxiosStatic} from "axios"; declare module "*.png" {
const value: any;
export default value;
} declare module "*.jpg" {
const value: any;
export default value;
} declare module 'vue/types/vue' {
interface Vue {
$http: AxiosStatic,
$socket: any,
}
}
vue-shim.d.ts
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
main.ts
import Vue, { AsyncComponent } from 'vue';
import Vuex from "vuex";
import VueRouter from "vue-router";
import VueLazyLoad from "vue-lazyload";
import VueI18n from 'vue-i18n'
import axios from "axios"; import BootstrapVue from "bootstrap-vue";
import ElementUI from "element-ui"; import enLocaleElementUI from 'element-ui/lib/locale/lang/en'
import zhCNLocaleElementUI from 'element-ui/lib/locale/lang/zh-CN'
import enLocaleCommon from './common/lang/en'
import zhCNLocaleCommon from './common/lang/zh-CN'
import enLocaleApp from './lang/en'
import zhCNLocaleApp from './lang/zh-CN' import VueSocketio from 'vue-socket.io'; import App from "./app.vue";
import routes from "./framework/routes"; import "@/common/style/baseStyle.css";
import "@/common/style/var.scss";
import "@/common/style/layout.scss"; // import VueECharts from "vue-echarts/components/ECharts.vue";
// import ECharts modules manually to reduce bundle size;
// import "echarts/lib/chart/bar";
// import "echarts/lib/component/tooltip"; Vue.use(Vuex);
Vue.use(VueRouter); Vue.use(VueLazyLoad, {
// error:"./static/error.png",
// loading:"./static/loading.png"
}) Vue.use(VueI18n) Vue.prototype.$http = axios; Vue.use(BootstrapVue); const messages = {
"en": {
...enLocaleElementUI,
...enLocaleCommon,
...enLocaleApp,
},
"zh-CN": {
...zhCNLocaleElementUI,
...zhCNLocaleCommon,
...zhCNLocaleApp,
}
} // Create VueI18n instance with options
const i18n = new VueI18n({
locale: 'zh-CN', // set locale
messages, // set locale messages
silentTranslationWarn: true
}) Vue.use(ElementUI, {
i18n: (key, value) => i18n.t(key, value)
}) Vue.use(VueSocketio, 'http://127.0.0.1:9092'); const router = new VueRouter({
routes
}) const vm = new Vue({
el: "#app",
data: {rootid: "ac"},
// components: {
// echarts
// },
router,
render: h => h(App),
i18n
})
Vue2.5 Web App 项目搭建 (TypeScript版)的更多相关文章
- 【饿了么】—— Vue2.0高仿饿了么核心模块&移动端Web App项目爬坑(三)
前言:接着上一篇项目总结,这一篇是学习过程记录的最后一篇,这里会梳理:评论组件.商家组件.优化.打包.相关资料链接.项目github地址:https://github.com/66Web/ljq_el ...
- .Net Core 3.1浏览器后端服务(一) Web API项目搭建
一.前言 基于CefSharp开发的浏览器项目已有一段时间,考虑到后期数据维护需要Server端来管理,故开启新篇章搭建浏览器后端服务.该项目前期以梳理服务端知识为主,后期将配合CefSharp浏览器 ...
- 第一次,触碰Web App项目,栽过的那些坑。
此项目是一个IPad上的Web App项目,页面的滚动用了最新的IScroll 5.0 插件, 确实是挺潮的. 项目用时 1个月 完成的, 准备今天晚上上线. 这是年前的最后一篇文章了,与众位博友分享 ...
- Web自动化测试项目搭建目录
Web自动化测试项目搭建(一) 需求与设计 Web自动化测试项目(二)BasePage实现 Web自动化测试项目(三)用例的组织与运行 Web自动化测试项目(四)测试报告 Web自动化测试项目(五)测 ...
- django学习笔记二:一个项目多个App项目搭建
django充许在一个项目中存在多个app,如一个大门户网站中可以包含论坛,新闻等内容,其中每一个模块称之为一个App,也可以理解为一个个独立的小型项目最终集成在一个门户网站中最终呈现给用户 本次测试 ...
- 一个项目多个App项目搭建
在testDjango项目中找到testDjango文件夹,打开urls.py路由配置文件并添加以下配置 from django.conf.urls import url,includefrom dj ...
- Maven项目搭建-Eclipse版
一.Maven简单介绍 Maven是基于Java平台的项目构建(mvn clean install).依赖管理(中央仓库,Nexus)和项目信息管理的项目管理工具. Maven是基于项目对象模型(PO ...
- vue2.0 安装及项目搭建(一)
基本环境安装 1.安装node:从node.js官网下载并安装node.测试:win+R(打开命令行)-------输入cmd-------敲入node -v.如果出现相应版本号,即安装成功: 2.测 ...
- 学习笔记:flutter项目搭建(mac版)
什么是flutter Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面. Flutter可以与现有的代码一起工作.在全世界,Flutter正在被越来越多的 ...
随机推荐
- 大型运输行业实战_day07_2_数据字典实现
1.数据字典表 CREATE TABLE `dic` ( `id` ) NOT NULL AUTO_INCREMENT, `table_name` ) DEFAULT NULL, `field_nam ...
- 修改hosts,***
某些网站之所以在国内上不了,是因为dns受到干扰,无法解析出正确的ip地址. 可以在hosts文件中加入网站对应的正确ip地址,进行访问. 1.打开hosts文件, 路径为 C:\Windows\S ...
- keras—神经网络CNN—CIFAR_10图像识别
1 from keras.datasets import cifar10 from keras.utils import np_utils import matplotlib.pyplot as pl ...
- .net调用web邮箱发送邮件(转载)
public static void SendEmail() { System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient( ...
- vi 编辑器常用命令(转)
常用vi编辑器命令行 对于VI的命令行,不需要特意的去记忆,写下来,让要用到的时候能找到就行 游标控制 h 游标向左移 j 游标向下移 k 游标向上移 l (or spacebar) 游标向右移 w ...
- win10如何安装和创建 证书
.下载winsdksetup.exe .在 MMC 管理单元中查看证书 打开一个命令提示符窗口. 类型mmc然后按 ENTER 键. 请注意,若要查看本地计算机存储中的证书,您必须具有管理员角色. 上 ...
- HBase Filter程序样例及Shell(图)
==过滤器执行流程== reset() : reset the filter state before filtering a new row. filterAllRemaining(): true ...
- 2018软工项目UML设计(团队)
团队信息 队名:火箭少男100 本次作业课上成员 短学号 名 本次作业博客链接 2507 俞辛(临时队长) https://www.cnblogs.com/multhree/p/9821080.htm ...
- 2018.09.27 网络协议(tarjan)
描述 一些学校连接在一个计算机网络上.学校之间存在软件支援协议.每个学校都有它应支援的学校名单(学校 a 支援学校 b ,并不表示学校 b 一定支援学校 a ).当某校获得一个新软件时,无论是直接得到 ...
- 2018.08.27 rollcall(非旋treap)
描述 初始有一个空集,依次插入N个数Ai.有M次询问Bj,表示询问第Bj个数加入集合后的排名为j的数是多少 输入 第一行是两个整数N,M 接下来一行有N个整数,Ai 接下来一行有M个整数Bj,保证数据 ...