版本依赖
"vue-loader": "^15.9.8",
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0",
"css-loader": "^6.7.1",
"esbuild-loader": "^3.0.1",
"mini-css-extract-plugin": "^2.6.0",
"dotenv-webpack": "^7.1.0",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"vue": "^2.7.14",
"vue-template-compiler": "^2.7.14",
"element-ui": "^2.12.0",
完整的package.json
{
"name": "project-vue",
"version": "1.0.0",
"description": "webpack搭建vue项目",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": " cross-env NODE_ENV=development node_modules/.bin/webpack serve --config webpack.config.js",
"build": "cross-env NODE_ENV=production webpack --config=webpack.config.js",
"lint": "npx lint-staged"
},
"author": "gmm",
"license": "ISC",
"devDependencies": {
"autoprefixer": "^10.4.7",
"buffer": "^6.0.3",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^3.4.1",
"dotenv-webpack": "^7.1.0",
"esbuild-loader": "^3.0.1",
"eslint": "^8.15.0",
"eslint-plugin-vue": "^8.7.1",
"html-webpack-plugin": "^5.5.0",
"husky": "^7.0.4",
"less-loader": "^10.2.0",
"lint-staged": "^12.4.1",
"mini-css-extract-plugin": "^2.6.0",
"path-browserify": "^1.0.1",
"postcss-loader": "^6.2.1",
"prettier": "^2.6.2",
"progress-bar-webpack-plugin": "^2.1.0",
"stream-browserify": "^3.0.0",
"vue-loader": "^15.9.8",
"vue-router": "^3.6.5",
"vue-style-loader": "^4.1.3",
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0"
},
"dependencies": {
"element-ui": "^2.12.0",
"expression-eval": "^3.1.1",
"query-string": "^4.3.4",
"vue": "^2.7.14",
"vue-template-compiler": "^2.7.14",
"vue-cookies": "^1.8.3",
"vuex": "^3.6.2"
},
"lint-staged": {
"src/**/*.{js,vue}": [
"npx eslint --fix",
"git add"
]
},
"browserslist": [
"defaults",
"not ie < 11",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
}
添加别名jsxFactory
resolve: {
modules: [path.resolve(__dirname, "src"), "node_modules"],
extensions: [".js", ".vue"], // 引入路径是不用写对应的后缀名
alias: {
jsxFactory: path.resolve(__dirname, "./src/jsxFactory/index.js"),
"@": path.resolve(__dirname, "./src"), // 用@直接指引到src目录下
},
},
rule js部分配置
{
test: /\.(js|jsx|ts|tsx)$/,
loader: "esbuild-loader",
options: {
loader: "jsx",
jsx: "transform",
jsxFactory: "jsxFactory",
target: "es2015",
},
include: [path.resolve(__dirname, "src")],
},
引入esbuild插件
const { EsbuildPlugin } = require("esbuild-loader");
在plugins中加上部分配置(暴露jsxFactory全局变量)
plugins:[new webpack.ProvidePlugin({ jsxFactory: ["jsxFactory", "default"], })]
css采用esbuild压缩,编译结果为es2015(esbuild最低版本只支持es2015)
optimization: {
nodeEnv: false,
minimizer: [
new EsbuildPlugin({
target: "es2015",
css: true,
}),
],
},
完整的webpack.config.js
const path = require("path"); // 引入node内置模块path
const HtmlWebpackPlugin = require("html-webpack-plugin"); // 构建html文件
const VueLoaderPlugin = require("vue-loader/lib/plugin-webpack5");
const { CleanWebpackPlugin } = require("clean-webpack-plugin"); // 清理构建目录下的文件
const ProgressBarWebpackPlugin = require("progress-bar-webpack-plugin"); // 设置打包进度条
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // webpack4以后 改为此插件 css样式分离
const Dotenv = require("dotenv-webpack"); // 支持程序获取.env配置的环境变量
const { EsbuildPlugin } = require("esbuild-loader");
const webpack = require("webpack");
module.exports = {
cache: process.env.NODE_ENV === "production" ? false : true,
mode: process.env.NODE_ENV === "production" ? "production" : "development", // 开发模式
stats: "errors-only", // 日志打印只打印错误
devServer: {
open: false, // 自动打开浏览器
hot: process.env.NODE_ENV === "production" ? false : true, // 热更新打开
host: "localhost",
port: "8899", // 端口:8888
client: {
overlay: false,
},
proxy: {},
},
entry: {
app: {
import: "./src/main.js",
},
},
output: {
// 出口文件
path: path.resolve(__dirname, "dist"), // 出口路径和目录
filename: "js/[name].js", // 编译后的名称
clean: true,
chunkFilename: "js/[name][id].js",
asyncChunks: true,
},
resolve: {
modules: [path.resolve(__dirname, "src"), "node_modules"],
extensions: [".js", ".vue"], // 引入路径是不用写对应的后缀名
alias: {
jsxFactory: path.resolve(__dirname, "./src/jsxFactory/index.js"),
vue$: "vue/dist/vue.esm.js", // 正在使用的是vue的运行时版本,而此版本中的编译器时不可用的,我们需要把它切换成运行时 + 编译的版本
"@": path.resolve(__dirname, "./src"), // 用@直接指引到src目录下
},
fallback: {
crypto: require.resolve("crypto-browserify"),
buffer: require.resolve("buffer/"),
path: require.resolve("path-browserify"),
stream: require.resolve("stream-browserify"),
},
},
optimization: {
nodeEnv: false,
splitChunks: {
chunks: "all",
minChunks: 1, //拆分前必须共享模块的最小 chunks 数。
minSize: 0,
cacheGroups: {
vueLib: {
minChunks: 1,
test: /[\\/]node_modules[\\/](vue|vue-router|vuex|axios|vuex-persistedstate)[\\/]/,
name: "vueLib",
chunks: "all",
},
},
},
minimizer: [
new EsbuildPlugin({
target: "es2015", // Syntax to compile to (see options below for possible values)
css: true,
}),
],
},
plugins: [
new HtmlWebpackPlugin({
// 自动插入到dist目录中
title: process.env.VUE_APP_TITLE,
template: "public/index.html",
// favicon: path.resolve(__dirname, `dist/eGreatWall.ico`), //配置网站图标
inject: "body",
}),
new VueLoaderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.ProvidePlugin({
Vue: ["vue/dist/vue.esm.js", "default"],
}),
process.env.NODE_ENV === "production" && new CleanWebpackPlugin(),
new ProgressBarWebpackPlugin({
complete: "█",
clear: true,
}),
new MiniCssExtractPlugin({
filename:
process.env.NODE_ENV === "production"
? "css/[name].[contenthash].css"
: "css/[name].css",
chunkFilename:
process.env.NODE_ENV === "production"
? "css/[name].[contenthash].css"
: "css/[name].css",
}),
new Dotenv({
path: path.resolve(__dirname, `./.env.${process.env.NODE_ENV}`),
}),
new webpack.ProvidePlugin({
jsxFactory: ["jsxFactory", "default"],
}),
],
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
include: [path.resolve(__dirname, "src")],
},
{
test: /\.css$/,
use: [
process.env.NODE_ENV === "production"
? MiniCssExtractPlugin.loader
: "vue-style-loader",
"css-loader",
],
},
{
test: /.less$/,
use: [
process.env.NODE_ENV === "production"
? MiniCssExtractPlugin.loader
: "vue-style-loader",
"css-loader",
"postcss-loader",
"less-loader",
],
},
{
test: /\.(js|jsx|ts|tsx)$/,
loader: "esbuild-loader",
options: {
loader: "jsx",
jsx: "transform",
jsxFactory: "jsxFactory",
target: "es2015",
},
include: [path.resolve(__dirname, "src")],
},
],
},
};
由于esbuild转换的jsx和vue jsx有差异,因此需要写转换函数对其转换一下 jsx转换 group-props.js文件
/**
*
* 参考文档 深入数据对象 https://v2.cn.vuejs.org/v2/guide/render-function.html#%E6%B7%B1%E5%85%A5%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1
* 参考文档 插件babel-plugin-transform-vue-jsx https://github.com/vuejs/babel-plugin-transform-vue-jsx/blob/master/lib/group-props.js
*
*
*/
import { h } from "vue";
function isTopLevelWrap() {
var map = Object.create(null);
var list = [
"class",
"staticClass",
"style",
"key",
"ref",
"refInFor",
"slot",
"scopedSlots",
];
let len = list.length;
for (var i = 0; i < len; i++) {
map[list[i]] = true;
}
return (val) => map[val];
}
let isTopLevel = isTopLevelWrap();
// https://v2.cn.vuejs.org/v2/guide/render-function.html#%E6%B7%B1%E5%85%A5%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1
let nestableReg = /^(props|domProps|on|nativeOn|hook)([-_A-Z])/;
let dirReg = /^v-/;
let xlinkReg = /^xlink([A-Z])/;
/**
* groupProps
* @param {*} obj
* @returns
*/
function groupProps(obj) {
let currentNewPropObjects = Object.create(null);
let props = [];
console.warn("obj", JSON.stringify(obj));
Object.keys(obj).forEach(function (key) {
props.push({
key,
value: obj[key],
});
});
props.forEach(function (prop) {
let name = prop.key;
if (isTopLevel(name)) {
currentNewPropObjects[name] = prop.value;
} else {
let nestMatch = name.match(nestableReg);
if (nestMatch) {
let prefix = nestMatch[1];
let suffix = name.replace(nestableReg, function (_, $1, $2) {
return $2 === "-" ? "" : $2.toLowerCase();
});
let nestedProp = { [suffix]: prop.value };
if (!currentNewPropObjects[prefix]) {
currentNewPropObjects[prefix] = {};
}
Object.assign(currentNewPropObjects[prefix], nestedProp);
} else if (dirReg.test(name)) {
name = name.replace(dirReg, "");
let dirs = currentNewPropObjects.directives;
if (!dirs) {
dirs = currentNewPropObjects.directives = [];
}
dirs.push({
name: name,
value: prop.value,
});
} else {
if (xlinkReg.test(prop.key)) {
prop.key = JSON.stringify(
prop.key.replace(xlinkReg, function (m, p1) {
return "xlink:" + p1.toLowerCase();
})
);
}
if (!currentNewPropObjects.attrs) {
currentNewPropObjects.attrs = {};
}
currentNewPropObjects.attrs[prop.key] = prop.value;
}
}
});
console.log("currentNewPropObjects", JSON.stringify(currentNewPropObjects));
return currentNewPropObjects;
}
/**
*
* @param {*} tag
* @param {*} props
* @param {*} children
* @returns
*/
function vueJsx(tag, props = null, ...children) {
const newPros = props ? groupProps(props) : {};
return h(tag, newPros, children);
}
export default vueJsx;
以上,就能在vue2项目中使用es-build了,速度提升10倍以上,编译产物为es2015