If you are curious about how Vite and Webpack compare in a real-world scenario, this post will show you a practical example of using both tools to build and serve a Vue project. We will use the same code base and configuration for both tools, and measure their performance in terms of startup time, rebuild time, and bundle size.
The project we will use is a simple Vue app that displays a list of products fetched from a mock API. It uses Vue 3, TypeScript, Single File Components (SFC), and Tailwind CSS. You can find the source code on GitHub.
The project has two scripts in the package.json file: one for running Vite and one for running Webpack. Both scripts use the same tsconfig.json and tailwind.config.js files. The only difference is that Vite uses vite.config.ts and Webpack uses webpack.config.js for their specific configuration.
The Configuration
The configuration files for both tools are very similar. They both specify the entry point of the app, the output directory, the public path, and the alias for resolving imports. They also both use PostCSS with Tailwind CSS and Autoprefixer plugins.
The main difference is that Webpack needs to use loaders for handling TypeScript and SFC files, while Vite can handle them natively. Webpack also needs to use HtmlWebpackPlugin to generate an HTML file that includes the bundled script, while Vite can serve the index.html file directly.
Here are the configuration files for both tools:
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
outDir: 'dist',
assetsDir: 'static',
sourcemap: true
},
base: '/vite-example/',
resolve: {
alias: {
'@': '/src'
}
}
})
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/main.ts',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'static/js/[name].[contenthash].js',
publicPath: '/webpack-example/'
},
module: {
rules: [
{
test: /.ts$/,
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/.vue$/]
}
},
{
test: /.vue$/,
loader: 'vue-loader'
},
{
test: /.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['tailwindcss', 'autoprefixer']
}
}
}
]
}
]
},
resolve: {
extensions: ['.ts', '.js', '.vue'],
alias: {
'@': path.resolve(__dirname, 'src')
}
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
],
devtool: 'source-map'
}
We’ll assess the tools by executing them with various commands and recording their outputs in a script. The script will also use Bundle Analyzer to generate reports on the bundle size of both tools.
- `npm run vite` to start Vite in development mode
- `npm run webpack` to start Webpack in development mode
- `npm run build:vite` to build Vite in production mode
- `npm run build:webpack` to build Webpack in production mode
The script records command completion time and rebuild time of each tool after file modification. It will also log the output of each command and save it to a file.
Here is the completed script:
const { execSync } = require('child_process')
const fs = require('fs')
const commands = [
{ name: 'vite', cmd: 'npm run vite' },
{ name: 'webpack', cmd: 'npm run webpack' },
{ name: 'build:vite', cmd: 'npm run build:vite' },
{ name: 'build:webpack', cmd: 'npm run build:webpack' }
]
const results = {}
for (const { name, cmd } of commands) {
console.log(`Running ${name}...`)
const start = Date.now()
const output = execSync(cmd, { stdio: 'pipe' }).toString()
const end = Date.now()
const time = end - start
console.log(`Finished ${name} in ${time} ms`)
results[name] = { time, output }
fs.writeFileSync(`./output/${name}.txt`, output)
}
console.log('Generating bundle reports...')
execSync('npx webpack-bundle-analyzer dist/static/js/*.js -m static -r ./output/webpack-report.html')
execSync('npx vite build --report')
execSync('mv dist/report.html ./output/vite-report.html')
console.log('Done!')
console.log(results)
So this is all about Vite vs Webpack.
What do you think?