vue.config.js配置打包之类操作 webpack(压缩js,css,图片)

1,160 阅读4分钟

 

// 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