Vue3 和 Vue2 的区别有哪些
响应式系统的底层实现完全不同
Vue2 用的是
Object.defineProperty
来监听数据变化,但这种方案对数组和新增属性的支持不够友好,比如直接修改数组下标或者给对象新增属性时不会触发更新,得用Vue.set
之类的特殊方法。而 Vue3 换成了Proxy
,它能直接监听整个对象,无论是数组操作还是新增属性都能自动捕获,底层实现更干净,性能也更好。代码组织方式的变化
Vue2 用的是 Options API,比如把数据、方法、生命周期都写在
data
、methods
这些固定选项里。这在简单组件里很清晰,但复杂组件容易让代码分散在不同区域,比如一个功能的逻辑可能分散在data
和methods
里。Vue3 引入了 Composition API,也就是setup
函数,允许我们像写函数一样按功能组织代码。比如一个搜索功能,可以把相关的响应式变量、方法和生命周期都写在一个useSearch
函数里,复用起来也更方便。性能优化
性能优化方面,Vue3 做了很多改进。比如它的虚拟 DOM 算法更智能,能跳过静态节点的比对,还支持了 Fragment(组件可以有多个根节点)、Teleport(把组件渲染到 DOM 任意位置)这些新特性。另外打包体积也更小,因为 Vue3 支持 Tree-shaking,没用到的模块不会被打包进去。
TypeScript 的支持
Vue3 本身是用 TypeScript 重写的,类型推断更友好,写起来比 Vue2 顺手很多。比如在 Vue2 里用 TS,有时候得用装饰器或者特殊的类型声明,而 Vue3 直接就能和 TS 无缝配合。
API 变化
比如生命周期钩子名字变了,
beforeDestroy
改成了beforeUnmount
,destroyed
变成unmounted
;全局 API 的创建方式从new Vue()
变成了createApp()
,这样能避免全局配置污染。还有v-model
的用法更灵活了,Vue2 里一个组件只能有一个v-model
,Vue3 可以绑定多个,比如同时用v-model:title
和v-model:content
,相当于替代了 Vue2 的.sync
修饰符。
Vue3 的静态标记
静态标记是 Vue3 性能优化里的一个关键点。简单来说,Vue3 在编译模板的时候,会‘聪明’地把那些永远不会变的元素标记出来。比如说,模板里写了个静态的<div class="title">我是标题</div>
,或者一个纯展示用的<p>这段文字永远不会改</p>
,Vue3 在生成虚拟 DOM 的时候会给这些节点打上标记,告诉后续的更新流程:‘这些内容不用再检查了,反正不会变’。
这样做最大的好处是跳过无用的比较。比如当数据变化导致组件重新渲染时,Vue2 的虚拟 DOM 会从头到尾对比新旧节点树,而 Vue3 因为有静态标记,遇到这些标记过的节点就直接跳过了,省去了大量没必要的计算。如果页面上有很多静态内容(比如企业官网的页头、页脚),性能提升会特别明显。
隐藏元素的方式
display:none
该元素不但被隐藏了,而且该元素原本占用的空间也会从页面布局中消失,也就无法响应事件。
html 的 hidden 属性
1
<div hidden>隐藏</div>
设置 hidden 属性,会应用浏览器默认样式
[hidden] {display: none;}
,所以注意不要和 display 属性同时使用。设置尺寸
1
2
3height: 0;
padding: 0;
overflow: hidden;可以通过使用最小化其尺寸被隐藏 width,height,padding,border-width 和/或 font-size。还需多设置一个 overflow: hidden;以确保内容不会溢出。
position
1
2position: absolute;
left: -999px;利用 position 属性,设置较大的值,将元素移除屏幕以实现隐藏的效果。
clip/clip-path
1
2
3
4
5
6
7.hide {
position: absolute; /*fixed*/
clip: rect(top, right, bottom, left); /* 占据空间,无法点击 */
}
.hide_2 {
clip-path: polygon(0px 0px, 0px 0px, 0px 0px, 0px 0px);
}隐藏元素的另一种方法是通过剪裁它们来实现。在以前,这可以通过 clip 属性来实现,但是这个属性被废弃了(现在浏览器依然支持),换成一个更好的属性叫做 clip-path。
z-index
1
2
3
4.hide {
position: absolute;
z-index: -1000; /* 不占据空间,无法点击 */
}transform
1
2
3.hide {
transform: scale(0, 0); /* 占据空间,无法点击 */
}opacity
1
2
3
4
5
6
7.hide {
opacity: 0; /* 占据空间,可以点击 */
}
.hide_2 {
-webkit-filter: opacity(0);
filter: opacity(0); /* 占据空间,可以点击 */
}opacity 是用来设置元素透明度的,但当设置成 0 的时候也就相当于隐藏元素了。因此,元素依然存在原来的位置,占据空间也可响应事件。如果你打算使用 opacity 属性在读屏软件中隐藏元素,很不幸,你并不能如愿。元素和它所有的内容会被读屏软件阅读,就像网页上的其他元素那样。换句话说,元素的行为就和它们不透明时一致。
visibility
1
2
3.hide {
visibility: hidden; /* 占据空间,无法点击 */
}如同 opacity 属性,被隐藏的元素依然会对我们的网页布局起作用。与 opacity 唯一不同的是它不会响应任何用户交互。此外,元素在读屏软件中也会被隐藏
有哪些性能优化的方式方法
减少资源体积和请求数量
比如图片用 WebP 压缩,小图标合并成雪碧图或者转成 SVG,第三方库尽量用按需加载(比如只引入 Lodash 用到的函数)。还有像路由懒加载,比如用 Vue Router 时,把不同页面的组件拆开,用户点进页面再下载对应代码,这样首屏能快很多。
代码层面的优化
比如用 Vue 的时候,避免在
v-for
里用复杂计算,必要时用computed
缓存结果;高频触发的操作(比如搜索框输入)用防抖节流控制;还有像v-if
和v-show
分场景用——频繁切换的用v-show
,不常用的用v-if
。浏览器缓存
比如给静态资源(JS、CSS、图片)加
Cache-Control
或ETag
,让用户第二次访问直接读本地缓存。工具链上打包优化
比如 Webpack 做代码分割(
SplitChunksPlugin
),把第三方库单独打包,利用浏览器并行下载体验优化
一个是骨架屏,数据没加载完先展示占位图,用户会觉得没那么‘卡’;另一个是预加载,比如用户鼠标悬停在菜单时,偷偷提前加载下一页的资源
其他
图片懒加载;用 CDN 托管静态资源(比如把 Vue、React 这些库换成 CDN 链接);代码压缩混淆、开启 Gzip/Brotli 压缩;
Gzip
Gzip是一种用来压缩文件的技术,简单来说就像给文件‘瘦身’,让它们在网络上传输得更快。比如一个100KB的JavaScript文件,压缩后可能变成30KB,用户下载时省流量还省时间。浏览器收到压缩后的文件会自动解压,整个过程对用户是无感的。
在项目里主要用过两种方式开启Gzip:
服务器配置(比如Nginx)
1
2
3
4gzip on; # 开启Gzip
gzip_types text/html text/css application/javascript; # 指定压缩的文件类型
gzip_min_length 1k; # 超过1KB的文件才压缩
gzip_comp_level 5; # 压缩级别,数字越高压得越小(但CPU消耗更多)前端构建工具压缩(比如Webpack)
1
2
3
4
5
6
7
8
9
10
11
12// webpack.config.js
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
plugins: [
new CompressionPlugin({
algorithm: 'gzip', // 算法
test: /\.(js|css|html)$/, // 压缩js/css/html
threshold: 10240 // 超过10KB才压缩
})
]
}这样打包时会生成
.gz
文件(比如app.js.gz
),上传到服务器后,如果服务端支持Gzip,会优先发送这个预压缩好的版本。
代码压缩
目前最流行的代码压缩工具主要有两个:UglifyJs
和Terser
UglifyJs
是一个传统的代码压缩工具,已存在多年,曾经是前端应用的必备工具,但由于它不支持ES6
语法,所以目前的流行度已有所下降。
Terser
是一个新起的代码压缩工具,支持ES6+
语法,因此被很多构建工具内置使用。webpack安装后会内置Terser
,当启用生产环境后即可用其进行代码压缩。
1 | // webpack.config.js |