-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【vue-cli】配置 #23
Comments
.. |
vue-cli4 全面配置(持续更新)细致全面的 vue-cli4 配置信息。涵盖了使用 vue-cli 开发过程中大部分配置需求。 不建议直接拉取此项目作为模板,希望能按照此教程按需配置,或者复制 vue.config.js 增删配置,并自行安装所需依赖。 vue-cli3 配置见 vue-cli3 分支。 其他系列★ Blog 目录
✅ 配置多环境变量通过在 package.json 里的 scripts 配置项中添加--mode xxx 来选择不同环境 只有以 VUE_APP 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中,代码中可以通过 process.env.VUE_APP_BASE_API 访问 NODE_ENV 和 BASE_URL 是两个特殊变量,在代码中始终可用 配置在项目根目录中新建.env, .env.production, .env.analyz 等文件
serve 默认的本地开发环境配置 NODE_ENV = "development"
BASE_URL = "./"
VUE_APP_PUBLIC_PATH = "./"
VUE_APP_API = "https://test.staven630.com/api"
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"
自定义 build 环境配置(预发服务器) NODE_ENV = "production"
BASE_URL = "https://crm.staven630.com/"
VUE_APP_PUBLIC_PATH = "https://crm.oss.com/staven-blog"
VUE_APP_API = "https://crm.staven630.com/api"
ACCESS_KEY_ID = "xxxxxxxxxxxxx"
ACCESS_KEY_SECRET = "xxxxxxxxxxxxx"
REGION = "oss-cn-hangzhou"
BUCKET = "staven-crm"
PREFIX = "staven-blog"
IS_ANALYZE = true; 修改 package.json "scripts": {
"build": "vue-cli-service build",
"crm": "vue-cli-service build --mode crm"
} 使用环境变量<template>
<div class="home">
<!-- template中使用环境变量 -->
API: {{ 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.jsconst 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('proxy:', res);
});
}
};
</script> ✅ 修复 HMR(热更新)失效如果热更新失效,如下操作: module.exports = {
chainWebpack: config => {
// 修复HMR
config.resolve.symlinks(true);
}
}; ✅ 修复 Lazy loading routes Error: Cyclic dependency vuejs/vue-cli#1669module.exports = {
chainWebpack: config => {
// 如果使用多页面打包,使用vue inspect --plugins查看html是否在结果数组中
config.plugin("html").tap(args => {
// 修复 Lazy loading routes Error
args[0].chunksSortMode = "none";
return args;
});
}
}; ✅ 添加别名 aliasconst 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 在某些版本的 OSX 上安装可能会因缺少 libpng 依赖项而引发错误。可以通过安装最新版本的 libpng 来解决。 brew install libpng module.exports = {
chainWebpack: config => {
if (IS_PROD) {
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 }
});
}
}
}; ✅ 自动生成雪碧图默认 src/assets/icons 中存放需要生成雪碧图的 png 文件。首次运行 npm run serve/build 会生成雪碧图,并在跟目录生成 icons.json 文件。再次运行命令时,会对比 icons 目录内文件与 icons.json 的匹配关系,确定是否需要再次执行 webpack-spritesmith 插件。 npm i -D webpack-spritesmith 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];
}
}; ✅ SVG 转 font 字体npm i -D svgtofont 根目录新增 scripts 目录,并新建 svg2font.js 文件: 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 = null, 没有演示html文件
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("done!");
}); 添加 package.json scripts 配置: "prebuild": "npm run font",
"font": "node scripts/svg2font.js", 执行: npm run font ✅ 使用 SVG 组件npm i -D svg-sprite-loader 新增 SvgIcon 组件。 <template>
<svg class="svg-icon"
aria-hidden="true">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style> 在 src 文件夹中创建 icons 文件夹。icons 文件夹中新增 svg 文件夹(用来存放 svg 文件)与 index.js 文件: import SvgIcon from "@/components/SvgIcon";
import Vue from "vue";
// 注册到全局
Vue.component("svg-icon", SvgIcon);
const requireAll = requireContext => requireContext.keys().map(requireContext);
const req = require.context("./svg", false, /\.svg$/);
requireAll(req); 在 main.js 中导入 icons/index.js import "@/icons"; 修改 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)(\?.*)?$/);
}
}; ✅ 去除多余无效的 css注:谨慎使用。可能出现各种样式丢失现象。
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]
};
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 (IS_PROD) {
config.plugin("webpack-report").use(BundleAnalyzerPlugin, [
{
analyzerMode: "static"
}
]);
}
}
}; ✅ 配置 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"
]
};
// 如果使用多页面打包,使用vue inspect --plugins查看html是否在结果数组中
config.plugin("html").tap(args => {
// html中添加cdn
args[0].cdn = cdn;
return args;
});
}
}; 在 html 中添加
✅ 多页面打包 multi-page多入口页面打包,建议在 src 目录下新建 pages 目录存放多页面模块。
配置多页面信息。src/main.js 文件对应 main 字段,其他根据参照 pages 为根路径为字段。如下: module.exports = {
'admin': {
template: 'public/index.html',
filename: 'admin.html',
title: '后台管理',
},
'mobile': {
template: 'public/index.html',
filename: 'mobile.html',
title: '移动端',
},
'pc/crm': {
template: 'public/index.html',
filename: 'pc-crm.html',
title: '预发服务',
}
}
vue.config.js 的 pages 字段为多页面提供配置 const glob = require("glob");
const pagesInfo = require("./pages.config");
const pages = {};
glob.sync('./src/pages/**/main.js').forEach(entry => {
let chunk = entry.match(/\.\/src\/pages\/(.*)\/main\.js/)[1];
const curr = pagesInfo[chunk];
if (curr) {
pages[chunk] = {
entry,
...curr,
chunk: ["chunk-vendors", "chunk-common", chunk]
}
}
})
module.exports = {
chainWebpack: config => {
// 防止多页面打包卡顿
config => config.plugins.delete("named-chunks");
return config;
},
pages
}; 如果多页面打包需要使用 CDN,使用 vue inspect --plugins 查看 html 是否在结果数组中的形式。上例中 plugins 列表中存在'html-main','html-pages/admin','html-pages/mobile', 没有'html'。因此不能再使用 config.plugin("html")。 const path = require("path");
const resolve = dir => path.join(__dirname, dir);
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
const glob = require("glob");
const pagesInfo = require("./pages.config");
const pages = {};
glob.sync('./src/pages/**/main.js').forEach(entry => {
let chunk = entry.match(/\.\/src\/pages\/(.*)\/main\.js/)[1];
const curr = pagesInfo[chunk];
if (curr) {
pages[chunk] = {
entry,
...curr,
chunk: ["chunk-vendors", "chunk-common", chunk]
}
}
});
module.exports = {
publicPath: IS_PROD ? process.env.VUE_APP_PUBLIC_PATH : "./", //
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"
]
};
// 防止多页面打包卡顿
config => config.plugins.delete("named-chunks");
// 多页面cdn添加
Object.keys(pagesInfo).forEach(page => {
config.plugin(`html-${page}`).tap(args => {
// html中添加cdn
args[0].cdn = cdn;
// 修复 Lazy loading routes Error
args[0].chunksSortMode = "none";
return args;
});
});
return config;
},
pages
}; ✅ 删除 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;
}
}; ✅ 生产环境禁止日志打印和 debuggerVue CLI 内置了 Vue CLI 3 配置方式: // vue.config.js
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
chainWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer([
new TerserPlugin({
terserOptions: {
compress: {
// 移除所有的日志打印
// drop_console: true,
// 移除所有的 debugger
drop_debugger: true,
// 该配置可以只移除一部分 log,但是必须设置 drop_console 为 false,如果值为 ['console.*'] 也是移除所有
pure_funcs: ['console.log']
}
}
})
])
}
}
} Vue CLI 4+ 配置方式: // vue.config.js
module.exports = {
chainWebpack: config => {
config.when(process.env.NODE_ENV === 'production', config => {
config.optimization.minimizer('terser').tap(args => {
// 移除所有的日志打印
// args[0].terserOptions.compress["drop_console"] = true;
// 移除所有的 debugger
args[0].terserOptions.compress['drop_debugger'] = true
args[0].terserOptions.compress['pure_funcs'] = ['console.log']
return args
})
})
}
} ✅ 去掉 console.log方法一:使用 babel-plugin-transform-remove-console 插件npm i -D babel-plugin-transform-remove-console 在 babel.config.js 中配置
方法二: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: {
common: {
name: "chunk-common",
chunks: "initial",
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
priority: 1,
reuseExistingChunk: true,
enforce: true
},
vendors: {
name: "chunk-vendors",
test: /[\\/]node_modules[\\/]/,
chunks: "initial",
priority: 2,
reuseExistingChunk: true,
enforce: true
},
elementUI: {
name: "chunk-elementui",
test: /[\\/]node_modules[\\/]element-ui[\\/]/,
chunks: "all",
priority: 3,
reuseExistingChunk: true,
enforce: true
},
echarts: {
name: "chunk-echarts",
test: /[\\/]node_modules[\\/](vue-)?echarts[\\/]/,
chunks: "all",
priority: 4,
reuseExistingChunk: true,
enforce: true
}
}
}
};
}
},
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 -D @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];
}
}; ✅ 开启 stylelint 检测scss, css语法npm i -D stylelint stylelint-config-standard stylelint-config-prettier stylelint-webpack-plugin 在文件夹创建stylelint.config.js,详细配置在这里 module.exports = {
ignoreFiles: ["**/*.js", "src/assets/css/element-variables.scss", "theme/"],
extends: ["stylelint-config-standard", "stylelint-config-prettier"],
rules: {
"no-empty-source": null,
"at-rule-no-unknown": [
true,
{
ignoreAtRules: ["extend"]
}
]
}
}; 启用webpack配置 const StylelintPlugin = require("stylelint-webpack-plugin");
module.exports = {
configureWebpack: config => {
const plugins = [];
if (IS_DEV) {
plugins.push(
new StylelintPlugin({
files: ["src/**/*.vue", "src/assets/**/*.scss"],
fix: true //打开自动修复(谨慎使用!注意上面的配置不要加入js或html文件,会发生问题,js文件请手动修复)
})
);
}
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: {
extract: IS_PROD,
sourceMap: false,
loaderOptions: {
scss: {
// 向全局sass样式传入共享的全局变量, $src可以配置图片cdn前缀
// 详情: https://cli.vuejs.org/guide/css.html#passing-options-to-pre-processor-loaders
prependData: `
@import "@scss/variables.scss";
@import "@scss/mixins.scss";
@import "@scss/function.scss";
$src: "${process.env.VUE_APP_OSS_SRC}";
`
}
}
}
}; 在 scss 中引用 .home {
background: url($src+"/images/500.png");
} ✅ 为 less 提供全局样式,以及全局变量
在src/assets/less目录下新建variables.less文件,并定义全局less变量 @primary-color: #1890ff;
@normal-color: #d9d9d9;
@text-color: #303753; vue.config.js中为其添加相应less配置。 const path = require('path')
const fs = require('fs')
const postcss = require('postcss')
const resolve = dir => path.resolve(__dirname, dir)
const IS_PROD = ['prod', 'production'].includes(process.env.NODE_ENV)
function getLessVaribles(fileUrl, list = {}) {
if (!fs.existsSync(fileUrl)) return {};
let lessFile = fs.readFileSync(fileUrl, 'utf8');
return postcss.parse(lessFile).nodes.reduce((acc, curr) => {
acc[`${curr.name.replace(/\:/, '')}`] = `${curr.params}`;
return acc;
}, list);
}
const modifyVars = getLessVaribles(resolve('./src/assets/less/variables.less'));
module.exports = {
css: {
extract: IS_PROD,
// sourceMap: false,
loaderOptions: {
less: {
modifyVars,
javascriptEnabled: true,
}
}
}
} ✅ 为 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-pluginnpm 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
module.exports = {
"/": {
title: "首页",
keywords: "首页关键词",
description: "这是首页描述"
},
"/about.html": {
title: "关于我们",
keywords: "关于我们页面关键词",
description: "关于我们页面关键词描述"
}
};
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 兼容在 main.js 中添加 import 'core-js/stable';
import 'regenerator-runtime/runtime'; 配置 babel.config.js const plugins = [];
module.exports = {
presets: [["@vue/app", { useBuiltIns: "entry" }]],
plugins: plugins
}; ✅ 静态资源自动打包上传阿里 oss、华为 obs开启文件上传 ali oss,需要将 publicPath 改成 ali oss 资源 url 前缀,也就是修改 VUE_APP_PUBLIC_PATH。具体配置参见阿里 oss 插件 webpack-oss、华为 obs 插件 huawei-obs-plugin 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 SpritesmithPlugin = require("webpack-spritesmith");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
const webpack = require("webpack");
const path = require("path");
const fs = require("fs");
const resolve = dir => path.join(__dirname, dir);
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
const glob = require('glob')
const pagesInfo = require('./pages.config')
const pages = {}
glob.sync('./src/pages/**/main.js').forEach(entry => {
let chunk = entry.match(/\.\/src\/pages\/(.*)\/main\.js/)[1];
const curr = pagesInfo[chunk];
if (curr) {
pages[chunk] = {
entry,
...curr,
chunk: ["chunk-vendors", "chunk-common", chunk]
}
}
})
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 = {
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 (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.externals = {
vue: "Vue",
"element-ui": "ELEMENT",
"vue-router": "VueRouter",
vuex: "Vuex",
axios: "axios"
};
config.plugins = [...config.plugins, ...plugins];
},
chainWebpack: config => {
// 修复HMR
config.resolve.symlinks(true);
// config.plugins.delete('preload');
// config.plugins.delete('prefetch');
config
.plugin("ignore")
.use(
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn$/)
);
// 添加别名
config.resolve.alias
.set("vue$", "vue/dist/vue.esm.js")
.set("@", resolve("src"))
.set("@apis", resolve("src/apis"))
.set("@assets", resolve("src/assets"))
.set("@scss", resolve("src/assets/scss"))
.set("@components", resolve("src/components"))
.set("@middlewares", resolve("src/middlewares"))
.set("@mixins", resolve("src/mixins"))
.set("@plugins", resolve("src/plugins"))
.set("@router", resolve("src/router"))
.set("@store", resolve("src/store"))
.set("@utils", resolve("src/utils"))
.set("@views", resolve("src/views"))
.set("@layouts", resolve("src/layouts"));
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"
]
};
// 如果使用多页面打包,使用vue inspect --plugins查看html是否在结果数组中
// config.plugin("html").tap(args => {
// // html中添加cdn
// args[0].cdn = cdn;
// // 修复 Lazy loading routes Error
// args[0].chunksSortMode = "none";
// return args;
// });
// 防止多页面打包卡顿
config => config.plugins.delete('named-chunks')
// 多页面cdn添加
Object.keys(pagesInfo).forEach(page => {
config.plugin(`html-${page}`).tap(args => {
// html中添加cdn
args[0].cdn = cdn;
// 修复 Lazy loading routes Error
args[0].chunksSortMode = "none";
return args;
});
})
if (IS_PROD) {
// 压缩图片
config.module
.rule("images")
.test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
.use("image-webpack-loader")
.loader("image-webpack-loader")
.options({
mozjpeg: { progressive: true, quality: 65 },
optipng: { enabled: false },
pngquant: { quality: [0.65, 0.90], speed: 4 },
gifsicle: { interlaced: false }
});
// 打包分析
config.plugin("webpack-report").use(BundleAnalyzerPlugin, [
{
analyzerMode: "static"
}
]);
}
// 使用svg组件
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)(\?.*)?$/);
return config;
},
pages,
css: {
extract: IS_PROD,
sourceMap: false,
loaderOptions: {
scss: {
// 向全局sass样式传入共享的全局变量, $src可以配置图片cdn前缀
// 详情: https://cli.vuejs.org/guide/css.html#passing-options-to-pre-processor-loaders
prependData: `
@import "@scss/variables.scss";
@import "@scss/mixins.scss";
@import "@scss/function.scss";
$src: "${process.env.VUE_APP_BASE_API}";
`
}
}
},
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": "/"
}
}
}
}
}; |
Cli 3.0 配置参考: // vue.config.js
const path = require('path');
module.exports = {
// 基本路径
//部署应用时的基本 URL。默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,
//例如 https://www.my-app.com/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。
//例如,如果你的应用被部署在 https://www.my-app.com/my-app/,则设置 baseUrl 为 /my-app/。
//这个值在开发环境下同样生效。如果你想把开发服务器架设在根路径,你可以使用一个条件式的值
baseUrl: process.env.NODE_ENV === 'production' ? '/my-app/' : './',
// 输出文件目录
//当运行 vue-cli-service build 时生成的生产环境构建文件的目录。
//注意目标目录在构建之前会被清除 (构建时传入 --no-clean 可关闭该行为)。
outputDir: 'dist',
//放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录
assetsDir: 'static',
//指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径。
indexPath: 'index_prod.html',
//默认情况下,生成的静态资源在它们的文件名中包含了 hash 以便更好的控制缓存。
//然而,这也要求 index 的 HTML 是被 Vue CLI 自动生成的。如果你无法使用 Vue CLI 生成的 index HTML,你可以通过将这个选项设为 false 来关闭文件名哈希。
// filenameHashing: false,
// eslint-loader 是否在保存的时候检查
//当 lintOnSave 是一个 truthy 的值时,eslint-loader 在开发和生产构建下都会被启用。
//如果你想要在生产构建时禁用 eslint-loader,你可以用如下配置:
lintOnSave: process.env.NODE_ENV !== 'production',
//是否使用包含运行时编译器的 Vue 构建版本。
//设置为 true 后你就可以在 Vue 组件中使用 template 选项了,但是这会让你的应用额外增加 10kb 左右。
runtimeCompiler: false,
//默认情况下 babel-loader 会忽略所有 node_modules 中的文件。
//如果你想要通过 Babel 显式转译一个依赖,可以在这个选项中列出来。
transpileDependencies: [],
//如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
productionSourceMap: true,
//设置生成的 HTML 中 <link rel="stylesheet"> 和 <script> 标签的 crossorigin 属性。
//使用crossorigin属性,使得加载的跨域脚本可以得出跟同域脚本同样的报错信息。
crossorigin: undefined,
//在生成的 HTML 中的 <link rel="stylesheet"> 和 <script> 标签上启用 Subresource Integrity (SRI)。
//如果你构建后的文件是部署在 CDN 上的,启用该选项可以提供额外的安全性, 这个标签是为了防止 CDN 篡改 javascript 用的。 。
integrity: false,
// webpack配置
// see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
// 链式调用
//(高级用法)这是一个一个函数,这个库提供了一个 webpack 原始配置的上层抽象,
//使其可以定义具名的 loader 规则和具名插件,并有机会在后期进入这些规则并对它们的选项进行修改。
//允许对内部的 webpack 配置进行更细粒度的修改。
chainWebpack: config => {
config
.plugin('html')
.tap(args => {
args[0].template = '/Users/username/proj/app/templates/index.html'
return args
})
},
//调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象:
//如果你需要基于环境有条件地配置行为,或者想要直接修改配置,
//那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。
//该方法的第一个参数会收到已经解析好的配置。在函数内,你可以直接修改配置,或者返回一个将会被合并的对象:
configureWebpack: (config) => {
if (process.env.NODE_ENV === 'production') {
// 为生产环境修改配置...
config.mode = 'production';
} else {
// 为开发环境修改配置...
config.mode = 'development';
}
Object.assign(config, {
// 开发生产共同配置
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@c': path.resolve(__dirname, './src/components')
}
}
});
},
// css相关配置
css: {
// 是否使用css分离插件 ExtractTextPlugin
//生产环境下是 true,开发环境下是 false
//是否将组件中的 CSS 提取至一个独立的 CSS 文件中 (而不是动态注入到 JavaScript 中的 inline 代码)。
//提取 CSS 在开发环境模式下是默认不开启的,因为它和 CSS 热重载不兼容。
//然而,你仍然可以将这个值显性地设置为 true 在所有情况下都强制提取
extract: process.env.NODE_ENV === 'production' ? true : false,
// 开启 CSS source maps?
sourceMap: false,
// css预设器配置项
loaderOptions: {
css: {
localIdentName: '[name]-[hash]',
camelCase: 'only'
},
//比如你可以这样向所有 Sass 样式传入共享的全局变量:
sass: {
// @/ 是 src/ 的别名
// 所以这里假设你有 `src/variables.scss` 这个文件
// data: `@import "@/variables.scss";`
}
},
//如果你想去掉文件名中的 .module,可以设置 vue.config.js 中的 css.modules 为 true
// 启用 CSS modules for all css / pre-processor files.
modules: false
},
// use thread-loader for babel & TS in production build
// enabled by default if the machine has more than 1 cores
parallel: require('os').cpus().length > 1,
// PWA 插件相关配置
// see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
pwa: {},
// webpack-dev-server 相关配置
devServer: {
open: process.platform === 'darwin',
host: '0.0.0.0',
port: 8080,
https: false,
hotOnly: false,
//设置为 true 时,eslint-loader 会将 lint 错误输出为编译警告。默认情况下,警告仅仅会被输出到命令行,且不会使得编译失败。
//如果你希望让 lint 错误在开发时直接显示在浏览器中,你可以使用 lintOnSave: 'error'。
//这会强制 eslint-loader 将 lint 错误输出为编译错误,同时也意味着 lint 错误将会导致编译失败。
//或者,你也可以通过设置让浏览器 overlay 同时显示警告和错误:
overlay: {
warnings: true,
errors: true
},
// proxy: {
// // 设置代理
// // proxy all requests starting with /api to jsonplaceholder
// 'http://localhost:8080/': {
// target: 'http://baidu.com:8080', //真实请求的目标地址
// changeOrigin: true,
// pathRewrite: {
// '^http://localhost:8080/': ''
// }
// }
// },
before: (app) => {}
},
// 第三方插件配置
pluginOptions: {
// ...
}
}
以上是vue.config.js详细配置,注释也是比较详细,vue-cli3也是希望通过简单的语言来抛弃之前webpack的繁杂的配置,能更好的让我们专注于逻辑代码层面。 在官方文档介绍中, 注入的webpack.config.js自带支持 PostCSS、CSS Modules 和包含 Sass、Less、Stylus 预处理器。在链式操作中我们可以修改更加贴合项目的loader。 |
Cli 3.0 知识点vue-cli-service build
环境变量和模式你可以替换你的项目根目录中的下列文件来指定环境变量: .env # 在所有的环境中被载入
.env.local # 在所有的环境中被载入,但会被 git 忽略
.env.[mode] # 只在指定的模式中被载入
.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略
代码中使用环境变量只有以 console.log(process.env.VUE_APP_SECRET) |
CDN引入资源文件我们要将 ...
<head>
<link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.0.7/theme-chalk/index.css">
</head>
<body>
<div id="app"></div>
<script src="https://cdn.bootcss.com/vue/2.5.9/vue.min.js"></script>
<script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
<script src="https://cdn.bootcss.com/element-ui/2.0.7/index.js"></script>
<!-- built files will be auto injected -->
</body> 修改 webpack配置项
module.exports = {
...
// process.env.NODE_ENV === 'production' 下配置 externals
externals: {
'vue': 'Vue',
'vuex': 'Vuex',
'vue-router': 'VueRouter',
'element-ui': 'ELEMENT'
}
...
} 修改入口文件: import VueRouter from 'vue-router'
// 注释掉
// Vue.use(VueRouter)
// 注释掉
// Vue.use(Vuex)
import ELEMENT from 'element-ui'
if (process.env.NODE_ENV === 'development') {
require('element-ui/lib/theme-chalk/index.css')
}
Vue.use(ELEMENT) 模版HTML文件 plugins: [
...
new HtmlWebpackPlugin({
filename: 'index.html',
template: process.env.NODE_ENV === 'development' ? 'index.prod.html' : 'index.dev.html',
inject: true
})
] |
共享CSS预处理器全局变量
有的时候你想要向 webpack 的预处理器 loader 传递选项。你可以使用 // vue.config.js
module.exports = {
css: {
loaderOptions: {
// 给 sass-loader 传递选项
sass: {
// @/ 是 src/ 的别名
// 所以这里假设你有 `src/variables.scss` 这个文件
data: `@import "@/variables.scss";`
}
}
}
} |
Vue CLI 3配置svg-sprite-loader与svgo-loader随着高清屏幕的普及,相比使用png等位图而言,使用SVG等矢量图形是一种全新的设计方式。更重要的是相比位图而言,SVG有着无可比拟的优势。其中,使用SVG中的Symbol元素制作Icon图标变得越来越流行,这种技术被称为SVG Sprite。这里所说的Sprite技术,类似于CSS中的Sprite技术。图标图形整合在一起,实际呈现的时候准确显示特定图标。 而Vue CLI已经更新到了3.0版本,那么Vue CLI 3.0 如何修改Webpack设置来配置svg-sprite-loader与svgo-loader实现SVG Sprite? 配置svg-sprite-loader安装svg-sprite-loader: npm install --save-dev svg-sprite-loader Vue CLI 3.0 与 2.x版本不同,3.0版本内部的 webpack 配置是通过 webpack-chain 维护的,因此,你需要熟悉 webpack-chain 的 API。 Vue CLI对svg有一个默认的rule设置,这个设置通过file-loader加载svg文件。因此,需要在vue.config.js中先清除默认的svg配置: module.exports = {
chainWebpack: config => {
config.module
.rule("svg")
.uses
.clear()
.end()
}
} 接着,在vue.config.js中配置svg-sprite-loader,将处理路径设置为你的svg图标路径(如:“./src/icons”),方法如下: module.exports = {
chainWebpack: config => {
config.module
.rule("svg")
.uses
.clear()
.end()
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]",
include: ["./src/icons"]
})
.end()
}
} 配置svgo-loader安装svgo和svgo-loader: npm install svgo svgo-loader --save-dev 在vue.config.js中加上svgo-loader的配置: module.exports = {
chainWebpack: config => {
config.module
.rule("svg")
.uses
.clear()
.end()
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]",
include: ["./src/icons"]
})
.end()
.before("svg-sprite-loader")
.use("svgo-loader")
.loader("svgo-loader")
.options({
plugins: [
{removeAttrs: {attrs: "path:fill"}}
]
})
.end()
}
}
优化配置上面直接清除默认的svg配置可能会引起异常,应该排除 const { resolve } = require('path')
module.exports = {
chainWebpack: config => {
// 清除svg默认配置对./src/icons文件夹的处理
config.module
.rule("svg")
.exclude
.add(resolve("./src/icons"))
.end()
}
} 接下来,对 const { resolve } = require('path')
module.exports = {
chainWebpack: config => {
// 添加新的rule处理./src/icons文件夹的svg文件
config.module
.rule("svg-sprite")
.test(/\.svg$/)
.include
.add(resolve("./src/icons"))
.end()
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]"
})
.end()
.before("svg-sprite-loader")
.use("svgo-loader")
.loader("svgo-loader")
.options({
plugins: [
{removeAttrs: {attrs: "path:fill"}}
]
})
.end()
}
} 完整的配置如下: const { resolve } = require('path')
module.exports = {
chainWebpack: config => {
// 清除svg默认配置对./src/icons文件夹的处理
config.module
.rule("svg")
.exclude
.add(resolve("./src/icons"))
.end()
// 添加新的rule处理./src/icons文件夹的svg文件
config.module
.rule("svg-sprite")
.test(/\.svg$/)
.include
.add(resolve("./src/icons"))
.end()
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]"
})
.end()
.before("svg-sprite-loader")
.use("svgo-loader")
.loader("svgo-loader")
.options({
plugins: [
{removeAttrs: {attrs: "path:fill"}}
]
})
.end()
}
} |
文档:
使用
参考:
The text was updated successfully, but these errors were encountered: