vite和webpack的性能优化和区别
性能优化概述
1.分包策略
Vite和Webpack都支持性能优化-分包策略,下面简要介绍一下它们的实现方法:
Vite分包策略
Vite是一款基于ESM的构建工具,Vite的分包策略主要通过动态引入实现,可以自动进行代码拆分,实现按需加载和运行时优化。在Javascript中,可以使用ES6的import()语法来实现动态引入:
const module = import('./module.js');
Vite还支持使用预构建Chunk来优化加载速度,可以在vite.config.js文件中设置rollupOptions项来实现预构建Chunk:
module.exports = {
build: {
rollupOptions: {
output: {
manualChunks: {
react: ['react', 'react-dom']
}
}
}
}
};
除了动态引入和预构建Chunk,Vite还支持手动配置Chunk,以实现更细粒度的控制。可以使用 import.meta.glob()
函数来匹配文件对 Chunk 进行手动配置。
import.meta.glob('../components/*.js').then(modules => {
Promise.all(modules.values()).then(components => {
// ...
})
})
Webpack分包策略
Webpack可以使用以下方式实现分包策略:
- 使用代码分割插件(Model Splitting)
通过代码分割插件在构建过程中实现自动化的代码分割。
const path = require('path');
module.exports = {
entry: {
app: './src/app.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
};
-
使用动态导入(Dynamic Imports)
通过动态导入将代码拆分成多个小块,按需加载。
import('./module.js')
.then((module) => {
// ...
})
.catch((err) => {
// ...
});
Webpack还支持使用webpackPrefetch和webpackPreload来实现预加载预取的功能,提高用户体验。
// webpackPreload
const Component = () => import(/* webpackPreload: true */ './Component.js');
// webpackPrefetch
const Component = () => import(/* webpackPrefetch: true */ './Component.js');
以上就是Vite和Webpack的分包策略实现方法和代码示例。
2.gzip压缩
Vite和Webpack都提供了Gzip压缩的功能,以便在前端性能优化中使用。
在Vite中,可以通过在vite.config.js
中设置compress
参数来开启Gzip压缩:
// vite.config.js
export default {
server: {
compress: true
}
}
在Webpack中,可以通过在webpack.config.js
中配置CompressionPlugin
插件来开启Gzip压缩:
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
plugins: [
new CompressionPlugin({
algorithm: 'gzip'
})
]
};
这些配置将为我们自动压缩生成的文件,并在传输到客户端时解压文件以提高性能。
值得注意的是,由于Gzip压缩需要花费一些CPU时间,建议在服务器端启用压缩。如果你正在使用CDN,你也可以在CDN端配置Gzip压缩。
3.动态引入
Webpack和Vite都支持使用动态引入来提高前端性能。动态引入的主要作用是延迟加载,只在需要的时候才加载相关的模块,减少了首次加载时的负担。
在Vite中,可以实现动态引入的几种方式:
- 使用ES6动态导入语法
import('./path/to/module').then(result => {
// do something with the module
}).catch(error => {
// handle error
});
这将以异步方式加载模块,可以在需要时动态引入。
- 在组件的
setup
函数中使用异步加载
import { defineComponent, ref, onMounted } from 'vue';
export default defineComponent({
setup() {
const moduleRef = ref(null);
onMounted(async () => {
const result = await import('./path/to/module');
moduleRef.value = result;
});
return {
moduleRef,
};
},
});
这种方式使用了Vue 3中的setup
函数,使用Promise
来异步加载模块,并将结果存储在ref中。
- 使用异步组件
const AsyncComponent = defineAsyncComponent(() => import('./path/to/module'));
export default defineComponent({
components: {
AsyncComponent,
},
template: `<AsyncComponent />`,
});
这里使用了Vue 3中的defineAsyncComponent
函数,通过传入异步加载模块的路径来创建异步组件。这样组件的加载过程将会被延迟到当组件被使用时再进行。
需要注意的是,为了使动态引入能够真正发挥作用,还需要配置Vite的按需动态导入功能。在vite.config.js
文件中,可以通过设置rollupOptions
参数来开启此功能:
// vite.config.js
export default {
rollupOptions: {
output: {
manualChunks: {},
},
},
};
此配置将分离每个动态导入的文件,并生成一个独立的代码块。这样可以按需加载模块,从而提高性能。
在Webpack中,可以实现动态引入的几种方式:
- 使用ES6动态导入语法
import('./path/to/module').then(result => {
// do something with the module
}).catch(error => {
// handle error
});
这将以异步方式加载模块,可以在需要时动态引入。
- 在React懒加载组件中使用
const MyComponent = lazy(() => import('./path/to/module'));
使用了React.lazy()
函数来实现懒加载。此函数接受一个返回动态加载模块的import()
函数的参数。
- 使用webpack的内置
require.ensure()
require.ensure(['./path/to/module'], function (require) {
var result = require('./path/to/module');
// do something with the module
});
这种方式使用了webpack的内置函数require.ensure()
,可以将块(chunk)分离出来并延迟加载模块。
在使用动态引入时,还可以通过配置webpack的output.publicPath
属性,将异步块的基础路径设置为CDN地址,以加快异步加载速度。例如:
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: 'https://cdn.example.com/'
}
需要注意的是,在使用动态引入时,还需要配置webpack的代码分离和优化功能。可以通过在webpack.config.js
文件中设置optimization.splitChunks
来实现此目的:
optimization: {
splitChunks: {
chunks: 'async',
minSize: 20000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\/]node_modules[\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
这样,webpack会自动分离出符合条件的模块,生成独立的代码块,在需要时进行加载。
值得注意的是,动态引入虽然可以优化首次加载时间,但在使用多个包时可能会导致过多的HTTP请求,这可能导致首屏渲染延迟。因此,需要根据实际情况选择何时使用动态引入。
补充:
为了避免这些问题,并且进一步提高性能,可以采用以下一些方法:
- 预加载和预取。在Webpack中,可以使用
webpackPrefetch
和webpackPreload
注释,以启用自动预取和/或预加载。例如:
import(/* webpackPrefetch: true */ './path/to/module');
这将告诉浏览器提前下载此模块,以便在需要时立即加载。
- 按用户需求加载。如果你有一些模块是高频访问的,那么可以在页面加载时先加载这些模块,而将其他模块延迟加载。这样可以防止不必要的HTTP请求,并加快页面响应速度。
- 避免过多的动态引入。虽然动态引入可以解决代码分离的问题,但是如果不加以控制,也可能导致模块的数量过于庞大,进而导致过多的HTTP请求和降低性能的可能。因此,在使用动态引入时,必须注意模块数量的把握,避免过度动态引入。
4.CDN加速
Vite和Webpack都支持使用CDN加速来优化前端性能。使用CDN可以将静态资源放置在离用户更近、更容易访问的地方,这样可以减少网络延迟,加快资源加载速度。
在Vite中,可以通过以下方式来使用CDN资源加速:
- 在
vite.config.js
文件中设置base
属性
export default {
base: 'https://cdn.example.com/',
// ...
}
设置 base
属性后,资源的 baseURL
将会被设置为此处的值,JavaScript、CSS、图片等静态资源都将从这个地址加载。
- 在
index.html
文件中手动引入 CDN 资源
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My App</title>
<link rel="stylesheet" href="https://cdn.example.com/path/to/main.css">
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
这种方式可以手动指定要加载的静态资源,并从CDN加载,但需要注意的是,这种方式可能会增加HTML文件的大小和复杂度。
- 在
vite.config.js
文件中使用CDN
插件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { VitePluginCdn } from 'vite-plugin-cdn';
export default defineConfig({
plugins: [
vue(),
VitePluginCdn({
modules:[
{
name: 'vue',
var: 'Vue',
path: 'vue/dist/vue.min.js'
},
{
name: 'vue-router',
var: 'VueRouter',
path: 'vue-router/dist/vue-router.min.js'
},
{
name: 'chart.js',
var: 'Chart',
path: 'chart.js/dist/Chart.min.js'
}
],
prodUrl: 'https://cdn.example.com/{{module}}/{{version}}/{{path}}',
// 以下配置可以是 CDN 来加速引入的资源。
// 必须是数组形式,且选项都必须包含 `test` 和 `method` 属性。
// 具体可选项请参考 `preload-webpack-plugin` 的 `include` 参数。
allowList: [
{
test: /bootstrap-vue/(.*?).(js|css|woff|woff2|ttf|svg|png|jpe?g|gif)$/,
method: 'GET',
//可选的扩展加载,vue使用
ext: 'js'
}
]
})
]
})
使用 vite-plugin-cdn
插件可以方便地自动将指定的模块从CDN加载。需要注意的是,这个插件需要按照指定规则的方式来定义模块和版本。具体用法可以参考 vite-plugin-cdn
的官方文档。
以上这些方法都可以实现CDN资源加速,根据实际使用场景可以灵活选择。
在Webpack中,可以通过以下方式来使用CDN资源加速:
- 使用
HtmlWebpackPlugin
插件
在 webpack.config.js
文件中使用 HtmlWebpackPlugin
插件,在 template
配置中引入CDN地址
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
cdn: {
js: [
'https://cdn.example.com/jquery.min.js',
'https://cdn.example.com/bootstrap.min.js'
],
css: [
'https://cdn.example.com/bootstrap.min.css'
]
}
})
],
// ...
};
在 HTML 模板文件中,将 js
和 css
链接插入到模板中:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My App</title>
<% for (var i in htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
<% } %>
</head>
<body>
<div id="app"></div>
<% for (var i in htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body>
</html>
这种方式可以将静态资源链接插入到运行 HTML 模板源文件中,以指定CDN地址加载。
- 在
output.publicPath
中设置 CDN 地址
在 webpack.config.js
文件中设置 output.publicPath
属性:
module.exports = {
output: {
publicPath: 'https://cdn.example.com/',
},
// ...
}
这样设置后,通过Webpack打包后的静态资源将从指定的CDN地址加载,比如JS、CSS、图片等静态资源。
以上这些方法都可以实现CDN资源加速,根据实际使用场景可以灵活选择。
vite和webpack的区别
综述:
- 构建速度:Vite的构建速度比Webpack快得多。Vite使用现代浏览器原生支持的 ES 模块标准,避免了打包和编译的大量时间,而Webpack则需要在打包时对所有模块进行解析和编译。
- 开发模式:Vite支持快速的“即时重载”(Instant Reload)功能,当你编辑代码后,浏览器会自动更新页面,更新速度非常快。Webpack则需要手动刷新浏览器。
- 配置:Webpack需要通过复杂的配置来管理各种不同的loader、插件、代码分割和优化设置等等。相比之下,Vite的默认配置非常简单,只需要少量配置即可满足大部分需求。
- 生态系统和插件支持:由于Webpack已经成为主流的前端构建工具,因此有大量的生态系统和社区支持,包括各种loader、插件、优化工具等等。相比之下,Vite是较新的工具,社区和插件支持还比较有限。
当使用Vite和Webpack来构建一个简单的React应用时,它们之间会有以下区别:
使用Vite构建React应用的代码示例:
// vite.config.js
import reactRefresh from '@vitejs/plugin-react-refresh';
export default {
plugins: [reactRefresh()],
};
// src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
const App = () => {
const [count, setCount] = React.useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
使用Webpack构建React应用的代码示例:
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[chunkhash].js',
},
module: {
rules: [
{
test: /.(js|jsx)$/,
exclude: /node_modules/,
use: 'babel-loader',
},
{
test: /.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
}),
new MiniCssExtractPlugin(),
],
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
},
};
可以看到,在Vite中,非常简单地设置了一个插件,然后在React组件中使用了Hooks,并在index.jsx
文件中使用ReactDOM.render
方法来渲染,而在Webpack的代码示例中,需要通过Webpack配置文件来引入插件、使用Loader加载器和Plugin插件以及配置打包输出文件的路径和名称等等,相比之下更为复杂。
1. 开发模式下的运行方式不同
Vite在开发模式下使用 Rollup 进行打包,并提供了一个内置的 HTTP 服务器来提供服务。它使用 ES modules 作为默认的模块系统,可以快速地进行热更新和快速构建。
Webpack在开发模式下使用自己的开发服务器进行工作,支持热更新,但通常需要使用一些插件来实现一些高级功能,如HMR(热模块替换)。
以下是一个使用Vite的示例代码,index.html
文件中直接引用模块,浏览器通过 Vite 内置的 HTTP 服务器加载模块:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
以下是一个使用Webpack的示例代码,webpack-dev-server
通过监听文件变化来触发热更新:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
},
devServer: {
contentBase: './dist',
hot: true,
},
plugins: [
new HtmlWebpackPlugin({
title: 'My App',
}),
],
};
2. 构建方式和性能
Vite在开发模式下使用Rollup进行构建,支持按需编译和充分利用现代浏览器的原生ES模块,所以它的速度更快,比如热刷新延时更短、构建速度更快等。但在实际项目中,如需要自定义构建方式和高级功能的话,需要对Rollup的运行机制有一定的了解。
Webpack基于模块化思想和依赖分析,在生产模式下可以更好地实现代码压缩、分离、图片压缩等复杂的构建操作。同时Webpack还内置了一些优化策略,如启用多线程并发构建、使用Tree shaking技术等,因此Webpack在处理大型项目时更具优势。
以下是一个使用Vite的示例代码,build
命令会通过 Rollup 进行构建:
// vite.config.js
export default {
build: {
outDir: 'dist',
assetsDir: 'assets',
rollupOptions: {
input: 'src/main.js',
},
},
}
以下是一个使用Webpack的示例代码,npm run build
命令会通过 Webpack 进行构建:
// webpack.config.js
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'My App',
}),
],
};
3. 配置方式
Vite使用ES modules进行导入和导出,而配置文件也是一个ES module,可以直接使用ES6语法,简易直观。此外,Vite还提供了一个内置的插件管理系统和一些现成的插件,方便用户管理和使用。
Webpack使用CommonJS方式来导入和导出模块,配置文件是一个CommonJS模块,使用的是Node.js的API,需要一定的配置经验,但也更加灵活,可以满足
4. 支持的语言
Vite除了支持JavaScript和CSS,还支持以.vue文件为代表的单文件组件,这种组件可以将HTML、CSS和JavaScript打包到一个文件中。Vue.js等框架也积极支持Vite,称其是其未来的构建工具。
Webpack虽然可以支持JavaScript和CSS,但是它的打包方式更加灵活,支持Sass、Less等CSS预处理器,同时也可以使用Babel来支持ES6以上的JavaScript语法。
以下是一个使用Vite的示例代码,.vue
文件包含HTML、CSS和JavaScript等三种代码:
<template>
<div>
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
msg: 'Welcome to Your Vue.js App - Vite',
}
},
}
</script>
<style>
h1 {
color: #00f;
}
</style>
以下是一个使用Webpack的示例代码,.scss
文件包含CSS代码和变量:
$primary-color: #00f;
body {
background-color: lighten($primary-color, 50%);
color: darken($primary-color, 30%);
}
5. 插件生态和拓展性
Webpack的插件生态很丰富,提供了很多社区开发的插件,可以满足各种需求,可以针对性的选择安装,非常方便。同时Webpack还支持自定义插件,可以非常灵活地拓展其功能。
Vite的插件生态相对较少,但Vite插件系统提供了许多钩子和API,开发者可以通过编写自定义插件来扩展Vite的功能,比如添加自定义的路由、中间件等。
以上这些都是Vite和Webpack的区别。
转载自:https://juejin.cn/post/7235908012673253413