// vue.config.js的基础配置
const path = require('path')
const CompressionPlugin = require('compression-webpack-plugin') //js css 压缩
const productionGzipExtensions = ["js", "css", 'scss']; // 需要gzip压缩的文件后缀
const merge = require("lodash.merge"); //对象合并插件
const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); //用于去掉注释
const AutoCodePlugin = require("./webpack/plugins/auto.code.plugin"); //组件自动化
const resolve = dir => path.join(__dirname, dir)
module.exports = {
//将部署应用程序包的基本URL(baseUrl在Vue CLI 3.3之前称为)
publicPath: "./",
outputDir: "dist",
assetsDir: "assets", // 静态资源目录 (js, css, img, fonts)
productionSourceMap: false, // 生产环境 sourceMap,不生成map文件
lintOnSave: false,//关闭eslint
devServer: {
port: 9020, // 设置端口
open: true, // 启动后打开浏览器
overlay: {
// 当出现编译器错误或警告时,在浏览器中显示全屏覆盖层
warnings: false,
errors: true
}
// proxy: {
// "/*****/": {
// target: "https://*****.me",
// // false为http访问,true为https访问
// // secure: false,
// //是否跨域
// changeOrigin: true,
// ws: true,
// pathRewrite: {
// "^/*****": "",
// },
// },
// },
},
//是否将组件中的css文件提取到独立的css文件
css: {
extract: process.env.NODE_ENV === "production",
},
// webpack配置(大部分操作都在此--压缩。去除打印)
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') { //判断是生产环境
return {
plugins: [
// 构建时开启gzip,降低服务器压缩对CPU资源的占用,服务器也要相应开启gzip
new CompressionPlugin({
algorithm: 'gzip',//开启gzip
test: new RegExp("\.(" + productionGzipExtensions.join("|") + ")$"), // 匹配文件名
threshold: 10240, // 对超过10k的数据压缩
deleteOriginalAssets: false // 不删除源文件
}),
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false, // 去掉注释
},
compress: {
drop_console: true,
drop_debugger: false,
pure_funcs: ["console.log"], //移除console
},
},
}),
new AutoCodePlugin([
{
/** ********* 组件自动化注册 ***********/
// 文件监听等级
maxlevel: 1,
// 监听./src/router/*下的文件夹
inPath: resolve("src/components"),
// 自动在./src/router/目录下生成index.js
outPath: resolve("src/components/index.js"),
// 模板
// fileName: 文件夹名称
// filePath: 文件夹路径
templateEach: (fileName) => {
// chunk名称
return `Vue.component('${fileName}', () => import(/* webpackChunkName: "components${fileName}" */ './${fileName}/index.vue'));`;
},
/**
* 输出模板
* template: 模板名称
* modules: 模板模块名称
*/
out: (template) => {
return `
/* eslint-disable */
/**
* @desc 组件自动化注册
*/
${template}
import Vue from 'vue'
`;
},
// 自动新建index入口文件
addIndex: [
{
// 默认路由
state: "file",
name: (fileName) => `${fileName}.vue`,
template: (fileName) => {
return `
<template>
<div>
组件 ysn-${fileName}
</div>
</template>
<script>
export default {
name: 'ysn-${fileName}'
};
</script>
`;
},
},
],
/** ********* 组件自动化注册 ***********/
},
])
]
}
}
},
//插入
chainWebpack: (config) => {
config.module
.rule("vue")
.use("vue-loader")
.tap((options) => {
merge(options, {
optimizeSSR: false,
});
});
//开启图片压缩
// config.module.rule('images')
// .test(/.(png|jpe?g|gif|svg)(?.*)?$/)
// .use('image-webpack-loader')
// .loader('image-webpack-loader')
// .options({ bypassOnDebug: true })
// 压缩图片
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
mozjpeg: { progressive: true, quality: 65 },
optipng: { enabled: false },
pngquant: { quality: [0.9, 0.98], speed: 4 },
gifsicle: { interlaced: false },
webp: { quality: 75 }
})
//引入scss全局变量
const oneOfsMap = config.module.rule("scss").oneOfs.store;
oneOfsMap.forEach((item) => {
item
.use("style-resources-loader")
.loader("style-resources-loader")
.options({
// 需要插入的文件路径
patterns: "./src/assets/style/variable.scss",
// 需要插入的文件路径数组
// patterns: ["./path/to/vars.scss", "./path/to/mixins.scss"]
})
.end();
});
},
}
auto.code.plugin.js(组件自动化需要自己建文件夹)
/**
* file 自动化编码补丁
*/
const fs = require('fs')
const path = require('path')
const chokidar = require('chokidar')
const jsbeautify = require('js-beautify')
// 封装美化
const beautify = function(fileName, str, config) {
if (path.extname(fileName) === '.vue') {
return jsbeautify.html(str, config)
} else if (path.extname(fileName) === '.html') {
return jsbeautify.html(str, config)
} else if (path.extname(fileName) === '.js') {
return jsbeautify.js(str, config)
} else if (path.extname(fileName) === '.css') {
return jsbeautify.css(str, config)
} else if (path.extname(fileName) === '.scss') {
return jsbeautify.css(str, config)
} else if (path.extname(fileName) === '.sass') {
return jsbeautify.css(str, config)
} else if (path.extname(fileName) === '.compass') {
return jsbeautify.css(str, config)
} else {
return str
}
}
class AutoCodePlugin {
// 构造方法
constructor(options) {
// 观察者
this.watcher = []
// 文件操作
class AutoFile {
constructor(options) {
this.watchQueue = options
// 文件数据
this.file = {}
}
// 创建文件数据
setFileData(d) {
if (!d.key) { console.error('缺少文件key信息'); return }
this.file[d.key] = d
}
// 写入文件
write(d) {
// 获取文件主体信息
const bodyData = Object.keys(this.file).reduce((add, d) => {
return add += this.file[d].value
}, '')
// 获取文件数据 && JS美化
const data = beautify(this.watchQueue.outPath, this.watchQueue.out ? this.watchQueue.out(bodyData, this.file) : bodyData,
d.beauty ? d.beauty : { indent_size: 4 }
)
// 写入文件
fs.writeFileSync(this.watchQueue.outPath, data)
}
// 移除文件夹
delete(key) {
delete this.file[key]
}
// 获取文件等级
getLevel(p1, p2) {
const p1Resolve = this.getPath(p1)
const p2Resolve = this.getPath(p2)
const replace = p2Resolve.replace(p1Resolve, '')
return (replace.split('/').length) - 1
}
// 获取路径
getPath(p) {
return path.resolve(p).split(path.sep).join('/')
}
}
// 当监听层级 > 1时候执行 fs.wtachFile轮询
// 当监听层级 = 1时候执行 fs.watch监听
const usePolling = options.some(d => d.maxlevel > 1)
// 启动
options.forEach(d => {
// 创建异步闭包
(async d => {
// 验证必要参数
if (!d.inPath) { console.error('缺少inPath参数'); return }
if (!d.templateEach) { console.error('缺少templateEach参数'); return }
// 文件夹等级(创建默认参数)
const level = Number(d.maxlevel) ? Number(d.maxlevel) : 1
// 输出文本(创建默认参数)
if (!d.out) d.out = template => template
// 输出文件(创建默认参数)
if (!d.outPath) {
const p = path.resolve(d.inPath).split(path.sep)
p[p.length] = 'index.js'
d.outPath = p.join('/')
}
// 实列化
const autoFile = new AutoFile(d)
// 监听层级
const watchLevel = d.maxlevel ? d.maxlevel : 1
// 观察者配置
const op = {
// 层级
depth: watchLevel,
usePolling
}
if (usePolling) {
// 轮询节奏
op.interval = 1000
op.binaryInterval = 1000
}
// 创建观察者
const watcher = chokidar.watch(d.inPath, op)
this.watcher.push(watcher)
// 先写文件
autoFile.write(d)
// 增加
const addFile = (dirName, kind) => {
// 判断是否在最大监听文件目录等级内
const lev = autoFile.getLevel(d.inPath, dirName)
if (lev <= level && lev > 0) {
// 判断环境
let product = (process.env.NODE_ENV === 'production')
// 可配置生产 || 测试环境 AutoCodePlugin.NODE_ENV = 'pro';
if (this.NODE_ENV) product = (process.env.NODE_ENV === this.NODE_ENV)
// 是不是空文件夹
const isEmptyDir = (kind === 'dir' && fs.readdirSync(path.resolve(dirName)).length === 0)
// 判断是不是开发环境 && 是不是文件夹 && 是不是空文件夹 && 存在入口文件config
if (!product && kind === 'dir' && isEmptyDir && d.addIndex) {
// 创建自定义模板
d.addIndex.forEach(autoTempalte => {
((autoTempalte, dirName) => {
if (autoTempalte.state === 'file') {
// 默认创建文件
let name = ''
if (!autoTempalte.name) name = 'index.js'
if (!autoTempalte.template) autoTempalte.template = () => ''
// 增加命名函数逻辑
if (Array.prototype.toString.call(autoTempalte.name) === '[object Function]') {
name = autoTempalte.name(path.resolve(dirName).split(path.sep).pop(), autoFile.getPath(dirName))
} else {
name = autoTempalte.name
}
const template = beautify(name, autoTempalte.template(path.resolve(dirName).split(path.sep).pop(), autoFile.getPath(dirName)),
d.beauty ? d.beauty : { indent_size: 4 }
)
fs.writeFileSync(path.join(path.resolve(dirName), name), template)
} else {
// 默认创建文件
let name = ''
if (!autoTempalte.name) return
// 增加命名函数逻辑
if (Array.prototype.toString.call(autoTempalte.name) === '[object Function]') {
name = autoTempalte.name(path.resolve(dirName).split(path.sep).pop(), autoFile.getPath(dirName))
} else {
name = autoTempalte.name
}
// 创建文件夹
fs.mkdirSync(path.join(path.resolve(dirName), name))
}
})(autoTempalte, dirName)
})
}
// 创建文件数据
autoFile.setFileData({
// 文件名称
key: autoFile.getPath(dirName),
// 数据内容
value: d.templateEach ? d.templateEach(path.resolve(dirName).split(path.sep).pop(), autoFile.getPath(dirName), kind) : ''
})
autoFile.write(d)
}
}
// 删除
const deleteFile = dirName => {
autoFile.delete(autoFile.getPath(dirName))
autoFile.write(d)
}
// 监听创建文件夹
watcher.on('addDir', dirName => addFile(dirName, 'dir'))
// 监听移除文件夹
watcher.on('unlinkDir', dirName => deleteFile(dirName))
// 是否需要监听文件
if (d.useFile) {
// 防止自己引用自己
const sameFile = (p1, p2) => {
return (path.resolve(p1) === path.resolve(p2))
}
// 监听创建文件夹
watcher.on('add', dirName => {
if (!sameFile(d.outPath, dirName)) addFile(dirName, 'file')
})
// 监听移除文件夹
watcher.on('unlink', dirName => {
if (!sameFile(d.outPath, dirName)) deleteFile(dirName)
})
}
// 监听错误
watcher.on('error', function(error) {
console.error('webpack AutoCodePlugin Error happened: ', error)
})
})(d)
})
}
// 应用函数
apply(compiler) {
// 判断环境
let product = (process.env.NODE_ENV === 'production')
// 可配置生产 || 测试环境 AutoCodePlugin.NODE_ENV = 'pro';
if (this.NODE_ENV) product = (process.env.NODE_ENV === this.NODE_ENV)
// 构建完成结束监听
if (compiler.hooks.done) {
// if (product && compiler.hooks.done) {
// webpack4
compiler.hooks.done.tap('done', () => {
console.log('webpack done')
this.watcher.forEach(d => d.close())
})
} else if (product) {
// < webpack4
compiler.plugin('done', () => {
console.log('webpack done')
this.watcher.forEach(d => d.close())
})
}
}
}
module.exports = AutoCodePlugin
各种插件集合:
js-beautify
image-webpack-loader // 图片压缩
lodash.merge//对象合并插件
compression-webpack-plugin //js css 压缩
uglifyjs-webpack-plugin //用于去掉注释
建议全部使用cnpm