1. 首页

webpack在vue-cli3中详细配置指南

webpack的原理和手动实践实现webpack后续再进行补充。

在vue/cli 3 中为减少使用者对webpack繁杂的配置项,将webpack集成在cli内部,同时提供vue.config.js文件允许用户进行配置。

为涵盖了使用 vue-cli 开发过程中大部分配置需求,下面细致全面的总结了 vue-cli3 配置信息。

1.配置多环境变量

在package.json中指定 –mode来配置不同环境的命令项;

只有以 VUE_APP 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中,代码中可以通过 process.env.VUE_APP_BASE_API 访问

NODE_ENV 和 BASE_URL 是两个特殊变量,在代码中始终可用;

可以通过process来访问环境变量

 javascript
 console.log("BASE_URL", process.env.BASE_URL);
 console.log("VUE_APP_API", process.env.VUE_APP_API);


配置

​ 在项目的根目录中新建环境变量文件(.env, .env.production, .env.analyz等)

  • .env
 javascript
 // 即: npm run serve 默认的本地开发环境配置
NODE_ENV = "development"
BASE_URL = "./"
VUE_APP_PUBLIC_PATH = './'
VUE_APP_API = "https://development.com/api"  //开发环境域名


  • .env.production
 javascript
    // 即:build 默认的环境配置
    NODE_ENV = 'production'
    BASE_URL = 'https://production.com/'
    VUE_APP_PUBLIC_PATH = 'https://production/blog'
    VUE_APP_API = 'https://production/api'

    ACCESS_KEY_ID = 'xxxxxxxxxxxxx'
    ACCESS_KEY_SECRET = 'xxxxxxxxxxxxx'


  • .env.analyz
 javascript
    // 即:自定义的build环境配置
    NODE_ENV = 'production'
    BASE_URL = 'https://production.com/'
    VUE_APP_PUBLIC_PATH = https://production.com/blog'
    VUE_APP_API = 'https://production.com/api'

    ACCESS_KEY_ID = 'xxxxxxxxxxxxx'
    ACCESS_KEY_SECRET = 'xxxxxxxxxxxxx'

    IS_ANALYZE = true


修改package.json文件:

 javascript
"scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "analyz": "vue-cli-service build --mode analyz",
    "lint": "vue-cli-service lint"
 },


2.配置基础的vue.config.js

区别开发和生产环境,并进行相应的配置(其他的配置后文循序渐进完善)

 javascript
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)

module.exports = {
  publicPath: IS_PROD ? process.env.VUE_APP_PUBLIC_PATH : './', // 默认'/',部署应用包时的基本 URL
  // outputDir: process.env.outputDir || 'dist', // 'dist', 生产环境的构建文件的目录,可以在环境变量中配置
  // assetsDir: "", // 相对于outputDir的静态资源(js、css、img、fonts)目录
  lintOnSave: false,
  runtimeCompiler: true, // 是否使用包含运行时编译器的 Vue 构建版本
  productionSourceMap: !IS_PROD, // 生产环境的 source map,开发开启,生产关闭
  parallel: require('os').cpus().length > 1,
  pwa: {}
}


3.配置 proxy 代理

配置代理可用于解决开发中的跨域的问题。

devServer.proxy 可以是一个指向开发环境 API 服务器的字符串,也可以使用一个 path: options 成对的对象;

 javascript
module.exports = {
  devServer: {
    // https: true, //当proxy中为https时,需要设置开启
    proxy: 'http://localhost:4000'
  }
}


path:options的方式:

 javascript
module.exports = {
  devServer: {
    // overlay: { // 让浏览器 overlay 同时显示警告和错误
    //   warnings: true,
    //   errors: true
    // },
    // open: false, // 是否打开浏览器
    // host: "localhost",
    // port: "8080", // 代理断就
    // https: false,
    // hotOnly: false, // 热更新
    proxy: {
      '/api': {
        target:
          'https://www.easy-mock.com/mock/5bc75b55dc36971c160cad1b/sheets', // 目标代理接口地址
        secure: false,
        changeOrigin: true, // 开启代理,在本地创建一个虚拟服务端
        // ws: true, // 是否启用websockets
        pathRewrite: {
          '^/api': '/'
        }
      }
    }
  }
}


4.配置别名

配置项目的别名,方便文件的引入,特别一些自定义的文件

 javascript
const path = require('path')
const resolve = dir => path.join(__dirname, dir)
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)

module.exports = {
  chainWebpack: config => {
    // 添加别名
    config.resolve.alias
      .set('vue$', 'vue/dist/vue.esm.js')
      .set('@', resolve('src'))
      .set('@assets', resolve('src/assets'))
      .set('@scss', resolve('src/assets/scss'))
      .set('@components', resolve('src/components'))
      .set('@plugins', resolve('src/plugins'))
      .set('@views', resolve('src/views'))
      .set('@router', resolve('src/router'))
      .set('@store', resolve('src/store'))
      .set('@layouts', resolve('src/layouts'))
      .set('@static', resolve('src/static'))
  }
}


5. 图片的打包处理

对于vue/cli 3默认的url-loader,默认是对小于4096字节的图片进行base64转化,对于大图片我们采用image-webpack-loader来进行压缩;

 javascript
// 添加图片压缩的loader
npm i -D image-webpack-loader


补充:如果使用npm安装后,运行失败报错缺少依赖,例如:

 javascript
Module build failed (from ./node_modules/image-webpack-loader/index.js):
Error: Cannot find module 'gifsicle'


猜测可能是网络的原因,采用淘宝镜像进行安装:

 javascript
// 先卸载
npm uninstall image-webpack-loader
// 添加淘宝镜像
npm install cnpm -g --registry=https://registry.npm.taobao.org
// 再次安装
cnpm install --save-dev  image-webpack-loader


在vue.config.js中添加配置:关于image-webpack-loader的配置信息可以查看官网:www.npmjs.com/package/ima…,关于chainWebpack的信息可以查看:chainWebpack

 javascript
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('images')
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({
        mozjpeg: { progressive: true, quality: 65 },
        optipng: { enabled: false },
        pngquant: { quality: [0.65, 0.9], speed: 4 },
        gifsicle: { interlaced: false },
        webp: { quality: 75 }
      })
  }
}


6. 自动生成雪碧图

默认 src/assets/icons 中存放需要生成雪碧图的 png 文件。首次运行 npm run serve/build 会生成雪碧图,并在根目录生成 icons.json 文件。再次运行命令时,会对比 icons 目录内文件与 icons.json 的匹配关系,确定是否需要再次执行 webpack-spritesmith 插件。

 javascript
// 添加 plugin
npm i -D webpack-spritesmith


修改配置文件:vue.config.js

const SpritesmithPlugin = require('webpack-spritesmith')
const path = require('path')
const fs = require('fs')

let has_sprite = true;
let files = [];
const icons = {};

try {
  fs.statSync(resolve("./src/assets/icons"));
  files = fs.readdirSync(resolve("./src/assets/icons"));
  files.forEach(item => {
    let filename = item.toLocaleLowerCase().replace(/_/g, "-");
    icons[filename] = true;
  });
} catch (error) {
  fs.mkdirSync(resolve("./src/assets/icons"));
}

if (!files.length) {
  has_sprite = false;
} else {
  try {
    let iconsObj = fs.readFileSync(resolve("./icons.json"), "utf8");
    iconsObj = JSON.parse(iconsObj);
    has_sprite = files.some(item => {
      let filename = item.toLocaleLowerCase().replace(/_/g, "-");
      return !iconsObj[filename];
    });
    if (has_sprite) {
      fs.writeFileSync(resolve("./icons.json"), JSON.stringify(icons, null, 2));
    }
  } catch (error) {
    fs.writeFileSync(resolve("./icons.json"), JSON.stringify(icons, null, 2));
    has_sprite = true;
  }
}

// 雪碧图样式处理模板
const SpritesmithTemplate = function(data) {
  // pc
  let icons = {};
  let tpl = `.ico {
  display: inline-block;
  background-image: url(${data.sprites[0].image});
  background-size: ${data.spritesheet.width}px ${data.spritesheet.height}px;
}`;

  data.sprites.forEach(sprite => {
    const name = "" + sprite.name.toLocaleLowerCase().replace(/_/g, "-");
    icons[`${name}.png`] = true;
    tpl = `${tpl}
.ico-${name}{
  width: ${sprite.width}px;
  height: ${sprite.height}px;
  background-position: ${sprite.offset_x}px ${sprite.offset_y}px;
}
`;
  });
  return tpl;
};

module.exports = {
  configureWebpack: config => {
    const plugins = [];
    if (has_sprite) {
      plugins.push(
        new SpritesmithPlugin({
          src: {
            cwd: path.resolve(__dirname, "./src/assets/icons/"), // 图标根路径
            glob: "**/*.png" // 匹配任意 png 图标
          },
          target: {
            image: path.resolve(__dirname, "./src/assets/images/sprites.png"), // 生成雪碧图目标路径与名称
            // 设置生成CSS背景及其定位的文件或方式
            css: [
              [
                path.resolve(__dirname, "./src/assets/scss/sprites.scss"),
                {
                  format: "function_based_template"
                }
              ]
            ]
          },
          customTemplates: {
            function_based_template: SpritesmithTemplate
          },
          apiOptions: {
            cssImageRef: "../images/sprites.png" // css文件中引用雪碧图的相对位置路径配置
          },
          spritesmithOptions: {
            padding: 2
          }
        })
      );
    }

    config.plugins = [...config.plugins, ...plugins];
  }
};


**注意点:**替换icons中得图片名来更改图片,可能会造成雪碧图不更新;避免使用同名的图片;

7.SVG图片转成font字体

添加依赖:


npm i -D svgtofont

在项目的根目录中新增scripts目录,并新建svg2font.js文件

缺点:转化后的font字体,每一个仅支持一种颜色;若需要保留svg的设计稿,建议采用

const svgtofont = require('svgtofont')
const path = require('path')
const pkg = require('../package.json')

svgtofont({
  src: path.resolve(process.cwd(), "src/assets/svg"), //svg图标目录路径
  dist: path.resolve(process.cwd(), "src/assets/fonts"), //输出到指定目录
  fontName: 'icon', //设置字体名称
  css: true, //生成字体文件
  startNumber: 20000, // unicode起始编号
  svgicons2svgfont: {
    fontHeight:1000,
    normalize: true
  },
  website: {
    title: 'icon',
    logo: '',
    version: pkg.version,
    meta: {
      description: '',
      keywords: ''
    },
    description: '',
    links: [
      {
        title: 'Font Class',
        url: "index.html"
      },
      {
        title: "Unicode",
        url: 'unicode.html'
      }
    ],
    footerInfo: ``
  }
}).then(()=>{
  console.log('Successful transformation, congratulations!')
}).catch(err=>{
  console.log(err)
})


**使用:**在mian.js中引入src/assets/fonts下的icon.css;使用可以好看同文件下的html文件

8.使用SVG组件

为了弥补font的缺点,采用svg全局组件的方式;

//添加loader
npm i -D svg-sprite-loader


添加公共组件: /src/components/common/SvgIcon.vue

<template>
  <svg class="svg-icon" :style="iconStyle" aria-hidden="true">
    <use :xlink:href="name"/>
  </svg>
</template>
<script>
export default {
  // iconName: 需要展示的svg图片名
  // iconStyle: 自定义的行类样式
  name: 'SvgIcon',
  props: {
    iconName: {
      type: String,
      required: true
    },
    iconStyle: {
      type:String,
      default: ''
    }

  },
  computed: {
    name() {
      return `#icon-${this.iconName}`
    }
  }
}
</script>

<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>


添加svg资源和相应的脚本:在src下创建icons文件夹,用于处理svg的组件和逻辑;在icons文件下添加svg文件放svg图片,以及添加index.js

import SvgIcon from "@/components/common/SvgIcon";
import Vue from "vue";

 /* require.context("./test", false, /.test.js$/);
    这行代码就会去 test 文件夹(不包含子目录) 下面的找所有文件名以 .test.js 结尾的文件能被 require 的文件。
    更直白的说就是 我们可以通过正则匹配引入相应的文件模块。
     require.context有三个参数:
     directory:说明需要检索的目录
     useSubdirectories:是否检索子目录
     regExp: 匹配文件的正则表达式 */

// 注册到全局
Vue.component("svg-icon", SvgIcon);

const requireAll = requireContext => requireContext.keys().map(requireContext);
const req = require.context("./svg", false, /\.svg$/);
requireAll(req);


在main.js中引入:

import "@/icons/index.js";


添加svg的loader配置,修改vue.config.js

const path = require("path");
const resolve = dir => path.join(__dirname, dir);

module.exports = {
  chainWebpack: config => {
    const svgRule = config.module.rule("svg");
    svgRule.uses.clear();
    svgRule.exclude.add(/node_modules/);
    svgRule
      .test(/\.svg$/)
      .use("svg-sprite-loader")
      .loader("svg-sprite-loader")
      .options({
        symbolId: "icon-[name]"
      });

    const imagesRule = config.module.rule("images");
    imagesRule.exclude.add(resolve("src/icons"));
    config.module.rule("images").test(/\.(png|jpe?g|gif|svg)(\?.*)?$/);
  }
};


9.添加打包分析

vue/cli 3中已包含依赖,直接在vue.config.js中修改

const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  .BundleAnalyzerPlugin;

module.exports = {
  chainWebpack: config => {
    // 打包分析
    if (IS_PROD) {
      config.plugin("webpack-report").use(BundleAnalyzerPlugin, [
        {
          analyzerMode: "static"
        }
      ]);
    }
  }
};


10.开启gzip压缩

// 添加依赖
npm i -D compression-webpack-plugin


修改vue.config.js

const CompressionWebpackPlugin = require("compression-webpack-plugin");
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;

module.exports = {
  configureWebpack: config => {
    const plugins = [];
    if (IS_PROD) {
      plugins.push(
        new CompressionWebpackPlugin({
          algorithm: "gzip",
          test: productionGzipExtensions,
          threshold: 10240,
          minRatio: 0.8
        })
      );
    }
    config.plugins = [...config.plugins, ...plugins];
  }
};


11.生产环境去除console.log

使用 babel-plugin-transform-remove-console 插件

npm i -D babel-plugin-transform-remove-console


在babel.config.js中添加配置,没有这个文件则在根目录新建一个:

const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);

const plugins = [];
if (IS_PROD) {
  plugins.push("transform-remove-console");
}

module.exports = {
  presets: ["@vue/app", { useBuiltIns: "entry" }],
  plugins
};


12.添加IE兼容

npm i -S @babel/polyfill


 在 main.js 中添加

import "@babel/polyfill";


配置 babel.config.js

const plugins = [];

module.exports = {
  presets: [["@vue/app", { useBuiltIns: "entry" }]],
  plugins: plugins
};

13. SplitChunksPlugin

很强大,也特别有用的插件;详细可参考看:juejin.im/post/684490…

14. externals缓存第三方资源

使用externals插件排除一些固定资源,使之不会打包到dist中;一般是结合cdn进行使用;配置也比较简单,此处只放官网;webpack.js.org/configurati…

15.加快编译 hard-source-webpack-plugin

npm地址: www.npmjs.com/package/har…

作用:第一次构建插件就会默认把缓存结果存到node_modules下的.cache目录下,第二次构建的时候再取出缓存使用。故可以加过构建的速度

作者:小灰灰同学
链接:https://juejin.im/post/6893885335018110983

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:webpack在vue-cli3中详细配置指南

链接:https://www.javascriptc.com/4630.html

« webpack简易打包原理
Webpack4性能优化实践»
Flutter 中文教程资源

相关推荐

QR code