Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

398371905/vue-cli3-config

Repository files navigation

vue-cli3 全面配置(持续更新)

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

不建议直接拉取此项目作为模板,希望能按照此教程按需配置,或者复制vue.config.js增删配置,并自行安装所需依赖。

其他系列

Blog

Nuxt.js 全面配置

目录

✅ 配置多环境变量

通过在package.json里的 scripts 配置项中添加--mode xxx 来选择不同环境

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

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

配置

在项目根目录中新建.env, .env.production, .env.analyz等文件

  • .env

serve 默认的本地开发环境配置

NODE_ENV = 'development'
BASE_URL = './'
VUE_APP_PUBLIC_PATH = './'
VUE_APP_API = 'https://test.staven630.com/api'
  • .env.production

build 默认的环境配置

NODE_ENV = 'production'
BASE_URL = 'https://prod.staven630.com/'
VUE_APP_PUBLIC_PATH = 'https://prod.oss.com/staven-blog'
VUE_APP_API = 'https://prod.staven630.com/api'
ACCESS_KEY_ID = 'xxxxxxxxxxxxx'
ACCESS_KEY_SECRET = 'xxxxxxxxxxxxx'
REGION = 'oss-cn-hangzhou'
BUCKET = 'staven-prod'
PREFIX = 'staven-blog'
  • .env.analyz

自定义build环境配置

NODE_ENV = 'production'
BASE_URL = 'https://prod.staven630.com/'
VUE_APP_PUBLIC_PATH = 'https://prod.oss.com/staven-blog'
VUE_APP_API = 'https://prod.staven630.com/api'
ACCESS_KEY_ID = 'xxxxxxxxxxxxx'
ACCESS_KEY_SECRET = 'xxxxxxxxxxxxx'
REGION = 'oss-cn-hangzhou'
BUCKET = 'staven-prod'
PREFIX = 'staven-blog'
IS_ANALYZE = true

修改 package.json

"scripts": {
 "serve": "vue-cli-service serve",
 "build": "vue-cli-service build",
 "analyz": "vue-cli-service build --mode analyz",
 "lint": "vue-cli-service lint"
}
使用环境变量
<template>
 <div class="home">
 <!-- template中使用环境变量 -->
 {{ api }}
 </div>
</template>
<script>
export default {
 name: "home",
 data() {
 return {
 api: process.env.VUE_APP_API
 };
 },
 mounted() {
 // js代码中使用环境变量
 console.log("BASE_URL", process.env.BASE_URL);
 console.log("VUE_APP_API", process.env.VUE_APP_API);
 }
};
</script>

さんかく 回顶部

✅ 配置基础 vue.config.js

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: {}
};

さんかく 回顶部

✅ 配置proxy代理解决跨域问题

假设mock接口为https://www.easy-mock.com/mock/5bc75b55dc36971c160cad1b/sheets/1

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": "/"
 }
 }
 }
 }
};

访问

<script>
import axios from "axios";
export default {
 mounted() {
 axios.get("/api/1").then(res => {
 console.log(res);
 });
 }
};
</script>

さんかく 回顶部

✅ 修复 HMR(热更新)失效

module.exports = {
 chainWebpack: config => {
 // 修复HMR
 config.resolve.symlinks(true);
 }
}

さんかく 回顶部

✅ 修复 Lazy loading routes Error: Cyclic dependency vuejs/vue-cli#1669

module.exports = {
 chainWebpack: config => {
 config.plugin('html').tap(args => {
 args[0].chunksSortMode = 'none';
 return args;
 });
 }
}

さんかく 回顶部

✅ 添加别名alias

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'));
 }
}

さんかく 回顶部

✅ 压缩图片

npm i -D image-webpack-loader
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: "65-90", speed: 4 },
 gifsicle: { interlaced: false },
 webp: { quality: 75 }
 });
 }
}

さんかく 回顶部

✅ 自动生成雪碧图

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

npm i -D webpack-spritesmith
const SpritesmithPlugin = require('webpack-spritesmith')
const path = require('path')
const fs = require('fs')
let has_sprite = true
try {
 let result = fs.readFileSync(path.resolve(__dirname, './icons.json'), 'utf8')
 result = JSON.parse(result)
 const files = fs.readdirSync(path.resolve(__dirname, './src/assets/icons'))
 has_sprite = files && files.length ? files.some(item => {
 let filename = item.toLocaleLowerCase().replace(/_/g, '-')
 return !result[filename]
 }) : false
} finally {
 has_sprite = false
}
// 雪碧图样式处理模板
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;
}
`
 })
 fs.writeFile(
 path.resolve(__dirname, './icons.json'),
 JSON.stringify(icons, null, 2),
 (err, data) => {}
 )
 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]
 }
}

さんかく 回顶部

✅ 去除多余无效的 css

注:谨慎使用。可能出现各种样式丢失现象。

  • 方案一:@fullhuman/postcss-purgecss
npm i -D postcss-import @fullhuman/postcss-purgecss

更新 postcss.config.js

const autoprefixer = require('autoprefixer')
const postcssImport = require('postcss-import')
const purgecss = require('@fullhuman/postcss-purgecss')
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
let plugins = []
if (IS_PROD) {
 plugins.push(postcssImport)
 plugins.push(
 purgecss({
 content: [
 './layouts/**/*.vue',
 './components/**/*.vue',
 './pages/**/*.vue'
 ],
 extractors: [
 {
 extractor: class Extractor {
 static extract(content) {
 const validSection = content.replace(
 /<style([\s\S]*?)<\/style>+/gim,
 ''
 )
 return validSection.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || []
 }
 },
 extensions: ['html', 'vue']
 }
 ],
 whitelist: ['html', 'body'],
 whitelistPatterns: [/el-.*/, /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!cursor-move).+-move$/, /^router-link(|-exact)-active$/],
 whitelistPatternsChildren: [/^token/, /^pre/, /^code/]
 })
 )
}
module.exports = {
 plugins: [...plugins, autoprefixer]
}
  • 方案二:purgecss-webpack-plugin
npm i -D glob-all purgecss-webpack-plugin
const path = require("path");
const glob = require("glob-all");
const PurgecssPlugin = require("purgecss-webpack-plugin");
const resolve = dir => path.join(__dirname, dir);
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
module.exports = {
 configureWebpack: config => {
 const plugins = [];
 if (IS_PROD) {
 plugins.push(
 new PurgecssPlugin({
 paths: glob.sync([resolve("./**/*.vue")]),
 extractors: [
 {
 extractor: class Extractor {
 static extract(content) {
 const validSection = content.replace(
 /<style([\s\S]*?)<\/style>+/gim,
 ""
 );
 return validSection.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || []
 }
 },
 extensions: ["html", "vue"]
 }
 ],
 whitelist: ["html", "body"],
 whitelistPatterns: [/el-.*/, /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!cursor-move).+-move$/, /^router-link(|-exact)-active$/],
 whitelistPatternsChildren: [/^token/, /^pre/, /^code/]
 })
 );
 }
 config.plugins = [...config.plugins, ...plugins];
 }
};

さんかく 回顶部

✅ 添加打包分析

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
 chainWebpack: config => {
 // 打包分析
 if (process.env.IS_ANALY) {
 config.plugin('webpack-report')
 .use(BundleAnalyzerPlugin, [{
 analyzerMode: 'static',
 }]);
 }
 }
}

需要添加.env.analyz文件

NODE_ENV = 'production'
IS_ANALYZ = true

package.json的scripts中添加

"analyz": "vue-cli-service build --mode analyz"

执行

npm run analyz

さんかく 回顶部

✅ 配置externals引入cdn资源

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖

module.exports = {
 configureWebpack: config => {
 config.externals = {
 vue: 'Vue',
 'element-ui': 'ELEMENT',
 'vue-router': 'VueRouter',
 vuex: 'Vuex',
 axios: 'axios'
 }
 },
 chainWebpack: config => {
 const cdn = {
 // 访问https://unpkg.com/element-ui/lib/theme-chalk/index.css获取最新版本
 css: ['//unpkg.com/element-ui@2.10.1/lib/theme-chalk/index.css'],
 js: [
 '//unpkg.com/vue@2.6.10/dist/vue.min.js', // 访问https://unpkg.com/vue/dist/vue.min.js获取最新版本
 '//unpkg.com/vue-router@3.0.6/dist/vue-router.min.js',
 '//unpkg.com/vuex@3.1.1/dist/vuex.min.js',
 '//unpkg.com/axios@0.19.0/dist/axios.min.js',
 '//unpkg.com/element-ui@2.10.1/lib/index.js'
 ] 
 };
 // html中添加cdn
 config.plugin('html').tap(args => {
 args[0].cdn = cdn
 return args
 })
 }
}

在html中添加

<!-- 使用CDN的CSS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn &&
htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
<% } %>
<!-- 使用CDN的JS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn &&
htmlWebpackPlugin.options.cdn.js) { %>
<script
 type="text/javascript"
 src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"
></script>
<% } %>

さんかく 回顶部

✅ 删除moment语言包

删除moment除zh-cn中文包外的其它语言包,无需在代码中手动引入zh-cn语言包。

const webpack = require("webpack");
module.exports = {
 chainWebpack: config => {
 config
 .plugin("ignore")
 .use(
 new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn$/)
 );
 return config;
 }
};

さんかく 回顶部

✅ 去掉 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
};
方法二:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
 configureWebpack: config => {
 if (IS_PROD) {
 const plugins = [];
 plugins.push(
 new UglifyJsPlugin({
 uglifyOptions: {
 compress: {
 warnings: false,
 drop_console: true,
 drop_debugger: false,
 pure_funcs: ['console.log']//移除console
 }
 },
 sourceMap: false,
 parallel: true
 })
 );
 config.plugins = [
 ...config.plugins,
 ...plugins
 ];
 }
 }
}

如果使用uglifyjs-webpack-plugin会报错,可能存在node_modules中有些依赖需要babel转译。

而vue-cli的transpileDependencies配置默认为[], babel-loader 会忽略所有 node_modules 中的文件。如果你想要通过 Babel 显式转译一个依赖,可以在这个选项中列出来。配置需要转译的第三方库。

さんかく 回顶部

利用splitChunks单独打包第三方模块

const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
module.exports = {
 configureWebpack: config => {
 if (IS_PROD) {
 config.optimization = {
 splitChunks: {
 cacheGroups: {
 libs: {
 name: 'chunk-libs',
 test: /[\\/]node_modules[\\/]/,
 priority: 10,
 chunks: 'initial'
 },
 elementUI: {
 name: 'chunk-elementUI',
 priority: 20,
 test: /[\\/]node_modules[\\/]element-ui[\\/]/,
 chunks: 'all'
 }
 }
 }
 }
 }
 },
 chainWebpack: config => {
 if (IS_PROD) {
 config.optimization.delete('splitChunks')
 }
 return config
 }
}

さんかく 回顶部

✅ 开启gzip压缩

npm i --D compression-webpack-plugin
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({
 filename: "[path].gz[query]",
 algorithm: "gzip",
 test: productionGzipExtensions,
 threshold: 10240,
 minRatio: 0.8
 })
 );
 }
 config.plugins = [...config.plugins, ...plugins];
 }
};

还可以开启比 gzip 体验更好的 Zopfli 压缩详见https://webpack.js.org/plugins/compression-webpack-plugin

npm i --save-dev @gfx/zopfli brotli-webpack-plugin
const CompressionWebpackPlugin = require("compression-webpack-plugin");
const zopfli = require("@gfx/zopfli");
const BrotliPlugin = require("brotli-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(input, compressionOptions, callback) {
 return zopfli.gzip(input, compressionOptions, callback);
 },
 compressionOptions: {
 numiterations: 15
 },
 minRatio: 0.99,
 test: productionGzipExtensions
 })
 );
 plugins.push(
 new BrotliPlugin({
 test: productionGzipExtensions,
 minRatio: 0.99
 })
 );
 }
 config.plugins = [...config.plugins, ...plugins];
 }
};

さんかく 回顶部

✅ 为 sass 提供全局样式,以及全局变量

可以通过在 main.js 中 Vue.prototype.$src = process.env.VUE_APP_PUBLIC_PATH;挂载环境变量中的配置信息,然后在js中使用$src 访问。

css 中可以使用注入 sass 变量访问环境变量中的配置信息

const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
module.exports = {
 css: {
 modules: false,
 extract: IS_PROD,
 sourceMap: false,
 loaderOptions: {
 sass: {
 // 向全局sass样式传入共享的全局变量, $src可以配置图片cdn前缀
 data: `
 @import "@scss/config.scss";
 @import "@scss/variables.scss";
 @import "@scss/mixins.scss";
 @import "@scss/utils.scss";
 $src: "${process.env.VUE_APP_OSS_SRC}";
 `
 }
 }
 }
};

在 scss 中引用

.home {
 background: url($src + '/images/500.png');
}

さんかく 回顶部

✅ 为 stylus 提供全局变量

npm i -D style-resources-loader
const path = require('path')
const resolve = dir => path.resolve(__dirname, dir)
const addStylusResource = rule => {
 rule
 .use('style-resouce')
 .loader('style-resources-loader')
 .options({
 patterns: [resolve('src/assets/stylus/variable.styl')]
 })
}
module.exports = {
 chainWebpack: config => {
 const types = ['vue-modules', 'vue', 'normal-modules', 'normal']
 types.forEach(type =>
 addStylusResource(config.module.rule('stylus').oneOf(type))
 )
 }
}

さんかく 回顶部

预渲染prerender-spa-plugin

npm i -D prerender-spa-plugin
const PrerenderSpaPlugin = require('prerender-spa-plugin')
const path = require('path')
const resolve = dir => path.join(__dirname, dir)
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
module.exports = {
 configureWebpack: config => {
 const plugins = []
 if (IS_PROD) {
 plugins.push(
 new PrerenderSpaPlugin({
 staticDir: resolve('dist'),
 routes: ['/'],
 postProcess(ctx) {
 ctx.route = ctx.originalRoute
 ctx.html = ctx.html
 .split(/>[\s]+</gim)
 .join('><')
 if (ctx.route.endsWith('.html')) {
 ctx.outputPath = path.join(
 __dirname,
 'dist',
 ctx.route
 )
 }
 return ctx
 },
 minify: {
 collapseBooleanAttributes: true,
 collapseWhitespace: true,
 decodeEntities: true,
 keepClosingSlash: true,
 sortAttributes: true
 },
 renderer: new PrerenderSpaPlugin.PuppeteerRenderer({
 // 需要注入一个值,这样就可以检测页面当前是否是预渲染的
 inject: {},
 headless: false,
 // 视图组件是在API请求获取所有必要数据后呈现的,因此我们在dom中存在"data view"属性后创建页面快照
 renderAfterDocumentEvent: 'render-event'
 })
 })
 )
 }
 config.plugins = [...config.plugins, ...plugins]
 } 
}

mounted()中添加document.dispatchEvent(new Event('render-event'))

new Vue({
 router,
 store,
 render: (h) => h(App),
 mounted() {
 document.dispatchEvent(new Event('render-event'))
 }
}).$mount('#app')
为自定义预渲染页面添加自定义title、description、content
  • 删除public/index.html中关于description、content的meta标签。保留title标签
  • 配置router-config.js
module.exports = {
 "/": {
 title:
 "首页",
 keywords: "首页关键词",
 description:
 "这是首页描述"
 },
 "/about.html": {
 title:
 "关于我们",
 keywords: "关于我们页面关键词",
 description:
 "关于我们页面关键词描述"
 }
};
  • vue.config.js
const path = require("path");
const PrerenderSpaPlugin = require("prerender-spa-plugin");
const routesConfig = require("./router-config");
const resolve = dir => path.join(__dirname, dir);
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
module.exports = {
 configureWebpack: config => {
 const plugins = [];
 if (IS_PROD) {
 // 预加载
 plugins.push(
 new PrerenderSpaPlugin({
 staticDir: resolve("dist"),
 routes: Object.keys(routesConfig),
 postProcess(ctx) {
 ctx.route = ctx.originalRoute;
 ctx.html = ctx.html.split(/>[\s]+</gim).join("><");
 ctx.html = ctx.html.replace(
 /<title>(.*?)<\/title>/gi,
 `<title>${
 routesConfig[ctx.route].title
 }</title><meta name="keywords" content="${
 routesConfig[ctx.route].keywords
 }" /><meta name="description" content="${
 routesConfig[ctx.route].description
 }" />`
 );
 if (ctx.route.endsWith(".html")) {
 ctx.outputPath = path.join(__dirname, "dist", ctx.route);
 }
 return ctx;
 },
 minify: {
 collapseBooleanAttributes: true,
 collapseWhitespace: true,
 decodeEntities: true,
 keepClosingSlash: true,
 sortAttributes: true
 },
 renderer: new PrerenderSpaPlugin.PuppeteerRenderer({
 // 需要注入一个值,这样就可以检测页面当前是否是预渲染的
 inject: {},
 headless: false,
 // 视图组件是在API请求获取所有必要数据后呈现的,因此我们在dom中存在"data view"属性后创建页面快照
 renderAfterDocumentEvent: "render-event"
 })
 })
 );
 }
 config.plugins = [...config.plugins, ...plugins];
 }
};

さんかく 回顶部

✅ 添加IE兼容

npm i --save @babel/polyfill

在 main.js 中添加

import '@babel/polyfill';

配置 babel.config.js

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

さんかく 回顶部

✅ 文件上传 ali oss

开启文件上传 ali oss,需要将 publicPath 改成 ali oss 资源 url 前缀,也就是修改 VUE_APP_PUBLIC_PATH。具体配置参见webpack-oss

npm i -D webpack-oss
const AliOssPlugin = require("webpack-oss");
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
const format = AliOssPlugin.getFormat();
module.exports = {
 publicPath: IS_PROD ? `${process.env.VUE_APP_PUBLIC_PATH}/${format}` : "./", // 默认'/',部署应用包时的基本 URL
 configureWebpack: config => {
 const plugins = [];
 if (IS_PROD) {
 plugins.push(
 new AliOssPlugin({
 accessKeyId: process.env.ACCESS_KEY_ID,
 accessKeySecret: process.env.ACCESS_KEY_SECRET,
 region: process.env.REGION,
 bucket: process.env.BUCKET,
 prefix: process.env.PREFIX,
 exclude: /.*\.html$/,
 format
 })
 );
 }
 config.plugins = [...config.plugins, ...plugins];
 }
};

さんかく 回顶部

✅ 完整配置

const path = require("path");
const webpack = require("webpack");
// const glob = require("glob-all");
// const PurgecssPlugin = require("purgecss-webpack-plugin");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
 .BundleAnalyzerPlugin;
// const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
// const CompressionWebpackPlugin = require("compression-webpack-plugin");
// const PrerenderSpaPlugin = require("prerender-spa-plugin");
// const AliOssPlugin = require("webpack-oss");
const resolve = dir => path.join(__dirname, dir);
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
// const SpritesmithPlugin = require('webpack-spritesmith')
// let has_sprite = true
// const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
// const addStylusResource = rule => {
// rule
// .use("style-resouce")
// .loader("style-resources-loader")
// .options({
// patterns: [resolve("src/assets/stylus/variable.styl")]
// });
// };
// try {
// let result = fs.readFileSync(path.resolve(__dirname, './icons.json'), 'utf8')
// result = JSON.parse(result)
// const files = fs.readdirSync(path.resolve(__dirname, './src/assets/icons'))
// has_sprite = files && files.length ? files.some(item => {
// let filename = item.toLocaleLowerCase().replace(/_/g, '-')
// return !result[filename]
// }) : false
// } finally {
// has_sprite = false
// }
// 雪碧图样式处理模板
// 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;
// }
// `
// })
// fs.writeFile(
// path.resolve(__dirname, './icons.json'),
// JSON.stringify(icons, null, 2),
// (err, data) => {}
// )
// return tpl
// }
// const format = AliOssPlugin.getFormat();
module.exports = {
 publicPath: IS_PROD ? process.env.VUE_APP_PUBLIC_PATH : "./", // 默认'/',部署应用包时的基本 URL
 // outputDir: process.env.outputDir || 'dist', // 'dist', 生产环境构建文件的目录
 // assetsDir: "", // 相对于outputDir的静态资源(js、css、img、fonts)目录
 configureWebpack: config => {
 const plugins = [];
 if (IS_PROD) {
 // 去除多余css
 // plugins.push(
 // new PurgecssPlugin({
 // paths: glob.sync([resolve("./**/*.vue")]),
 // extractors: [
 // {
 // extractor: class Extractor {
 // static extract(content) {
 // const validSection = content.replace(
 // /<style([\s\S]*?)<\/style>+/gim,
 // ""
 // );
 // return validSection.match(/[A-Za-z0-9-_:/]+/g) || [];
 // }
 // },
 // extensions: ["html", "vue"]
 // }
 // ],
 // whitelist: ["html", "body"],
 // whitelistPatterns: [/el-.*/],
 // whitelistPatternsChildren: [/^token/, /^pre/, /^code/]
 // })
 // );
 // plugins.push(
 // new UglifyJsPlugin({
 // uglifyOptions: {
 // compress: {
 // warnings: false,
 // drop_console: true,
 // drop_debugger: false,
 // pure_funcs: ["console.log"] //移除console
 // }
 // },
 // sourceMap: false,
 // parallel: true
 // })
 // );
 // 利用splitChunks单独打包第三方模块
 // config.optimization = {
 // splitChunks: {
 // cacheGroups: {
 // libs: {
 // name: "chunk-libs",
 // test: /[\\/]node_modules[\\/]/,
 // priority: 10,
 // chunks: "initial"
 // },
 // elementUI: {
 // name: "chunk-elementUI",
 // priority: 20,
 // test: /[\\/]node_modules[\\/]element-ui[\\/]/,
 // chunks: "all"
 // }
 // }
 // }
 // };
 // gzip
 // plugins.push(
 // new CompressionWebpackPlugin({
 // filename: "[path].gz[query]",
 // algorithm: "gzip",
 // test: productionGzipExtensions,
 // threshold: 10240,
 // minRatio: 0.8
 // })
 // );
 // 预加载
 // plugins.push(
 // new PrerenderSpaPlugin({
 // staticDir: resolve("dist"),
 // routes: ["/"],
 // postProcess(ctx) {
 // ctx.route = ctx.originalRoute;
 // ctx.html = ctx.html.split(/>[\s]+</gim).join("><");
 // if (ctx.route.endsWith(".html")) {
 // ctx.outputPath = path.join(__dirname, "dist", ctx.route);
 // }
 // return ctx;
 // },
 // minify: {
 // collapseBooleanAttributes: true,
 // collapseWhitespace: true,
 // decodeEntities: true,
 // keepClosingSlash: true,
 // sortAttributes: true
 // },
 // renderer: new PrerenderSpaPlugin.PuppeteerRenderer({
 // // 需要注入一个值,这样就可以检测页面当前是否是预渲染的
 // inject: {},
 // headless: false,
 // // 视图组件是在API请求获取所有必要数据后呈现的,因此我们在dom中存在"data view"属性后创建页面快照
 // renderAfterDocumentEvent: "render-event"
 // })
 // })
 // );
 // oss
 // plugins.push(
 // new AliOssPlugin({
 // accessKeyId: process.env.ACCESS_KEY_ID,
 // accessKeySecret: process.env.ACCESS_KEY_SECRET,
 // region: process.env.REGION,
 // bucket: process.env.BUCKET,
 // prefix: process.env.PREFIX,
 // exclude: /.*\.html$/,
 // format
 // })
 // );
 }
 // config.externals = {
 // vue: "Vue",
 // "element-ui": "ELEMENT",
 // "vue-router": "VueRouter",
 // vuex: "Vuex",
 // axios: "axios"
 // };
 // 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];
 },
 chainWebpack: config => {
 // 修复HMR
 config.resolve.symlinks(true);
 config
 .plugin("ignore")
 .use(
 new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn$/)
 );
 // const cdn = {
 // // 访问https://unpkg.com/element-ui/lib/theme-chalk/index.css获取最新版本
 // css: ["//unpkg.com/element-ui@2.10.1/lib/theme-chalk/index.css"],
 // js: [
 // "//unpkg.com/vue@2.6.10/dist/vue.min.js", // 访问https://unpkg.com/vue/dist/vue.min.js获取最新版本
 // "//unpkg.com/vue-router@3.0.6/dist/vue-router.min.js",
 // "//unpkg.com/vuex@3.1.1/dist/vuex.min.js",
 // "//unpkg.com/axios@0.19.0/dist/axios.min.js",
 // "//unpkg.com/element-ui@2.10.1/lib/index.js"
 // ]
 // };
 config.plugin("html").tap(args => {
 // 修复 Lazy loading routes Error
 args[0].chunksSortMode = "none";
 // html中添加cdn
 // args[0].cdn = cdn;
 return args;
 });
 // 添加别名
 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"));
 // 压缩图片
 // config.module
 // .rule("images")
 // .use("image-webpack-loader")
 // .loader("image-webpack-loader")
 // .options({
 // mozjpeg: { progressive: true, quality: 65 },
 // optipng: { enabled: false },
 // pngquant: { quality: "65-90", speed: 4 },
 // gifsicle: { interlaced: false },
 // webp: { quality: 75 }
 // });
 // const types = ["vue-modules", "vue", "normal-modules", "normal"];
 // types.forEach(type =>
 // addStylusResource(config.module.rule("stylus").oneOf(type))
 // );
 // 打包分析
 if (process.env.IS_ANALYZ) {
 config.plugin("webpack-report").use(BundleAnalyzerPlugin, [
 {
 analyzerMode: "static"
 }
 ]);
 }
 if (IS_PROD) {
 // config.optimization.delete("splitChunks");
 }
 return config;
 },
 css: {
 modules: false,
 extract: IS_PROD,
 sourceMap: false,
 loaderOptions: {
 sass: {
 // 向全局sass样式传入共享的全局变量, $src可以配置图片cdn前缀
 data: `
 @import "@scss/config.scss";
 @import "@scss/variables.scss";
 @import "@scss/mixins.scss";
 @import "@scss/utils.scss";
 $src: "${process.env.VUE_APP_OSS_SRC}";
 `
 }
 }
 },
 transpileDependencies: [],
 lintOnSave: false,
 runtimeCompiler: true, // 是否使用包含运行时编译器的 Vue 构建版本
 productionSourceMap: !IS_PROD, // 生产环境的 source map
 parallel: require("os").cpus().length > 1,
 pwa: {},
 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": "/"
 }
 }
 }
 }
};

About

vue-cli3配置vue.config.js持续更新

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 78.7%
  • Vue 16.7%
  • HTML 4.6%

AltStyle によって変換されたページ (->オリジナル) /