diff --git a/package.json b/package.json
index 724db93a7..356ac1f51 100644
--- a/package.json
+++ b/package.json
@@ -209,7 +209,7 @@
 		"vue-cropperjs": "2.2.0",
 		"vue-js-modal": "1.3.13",
 		"vue-json-tree-view": "2.1.4",
-		"vue-loader": "15.0.5",
+		"vue-loader": "15.0.11",
 		"vue-router": "3.0.1",
 		"vue-template-compiler": "2.5.16",
 		"vuedraggable": "2.16.0",
diff --git a/webpack.config.ts b/webpack.config.ts
index d56ed2397..3aeecbd8a 100644
--- a/webpack.config.ts
+++ b/webpack.config.ts
@@ -146,27 +146,20 @@ module.exports = {
 			}, {
 				loader: 'replace',
 				query: {
-					search: /%base64:(.+?)%/g.toString(),
-					replace: 'base64replacement'
-				}
-			}, {
-				loader: 'replace',
-				query: {
-					search: i18nPattern.toString(),
-					replace: 'i18nReplacement',
-					i18n: true
-				}
-			}, {
-				loader: 'replace',
-				query: {
-					search: faPattern.toString(),
-					replace: 'faReplacement'
-				}
-			}, {
-				loader: 'replace',
-				query: {
-					search: /^<template>([\s\S]+?)\r?\n<\/template>/.toString(),
-					replace: 'collapseSpacesReplacement'
+					qs: [{
+						search: /%base64:(.+?)%/g.toString(),
+						replace: 'base64replacement'
+					}, {
+						search: i18nPattern.toString(),
+						replace: 'i18nReplacement',
+						i18n: true
+					}, {
+						search: faPattern.toString(),
+						replace: 'faReplacement'
+					}, {
+						search: /^<template>([\s\S]+?)\r?\n<\/template>/.toString(),
+						replace: 'collapseSpacesReplacement'
+					}]
 				}
 			}]
 		}, {
diff --git a/webpack/loaders/replace.js b/webpack/loaders/replace.js
index d8a81c245..fd6bb3617 100644
--- a/webpack/loaders/replace.js
+++ b/webpack/loaders/replace.js
@@ -5,18 +5,26 @@ function trim(text, g) {
 }
 
 export default function(src) {
+	const fn = options => {
+		const search = options.search;
+		const g = search[search.length - 1] == 'g';
+		const file = this.resourcePath.replace(/\\/g, '/');
+		const replace = options.i18n ? global[options.replace].bind(null, {
+			src: file,
+			lang: options.lang
+		}) : global[options.replace];
+		if (typeof search != 'string' || search.length == 0) console.error('invalid search');
+		if (typeof replace != 'function') console.error('invalid replacer:', replace, this.request);
+		src = src.replace(new RegExp(trim(search, g), g ? 'g' : ''), replace);
+	};
+
 	this.cacheable();
 	const options = getOptions(this);
-	const search = options.search;
-	const g = search[search.length - 1] == 'g';
-	const file = this.resourcePath.replace(/\\/g, '/');
-	const replace = options.i18n ? global[options.replace].bind(null, {
-		src: file,
-		lang: options.lang
-	}) : global[options.replace];
-	if (typeof search != 'string' || search.length == 0) console.error('invalid search');
-	if (typeof replace != 'function') console.error('invalid replacer:', replace, this.request);
-	src = src.replace(new RegExp(trim(search, g), g ? 'g' : ''), replace);
+	if (options.qs) {
+		options.qs.forEach(q => fn(q));
+	} else {
+		fn(options);
+	}
 	this.callback(null, src);
 	return src;
 }