vite原理
webpack
在使用vue2开发大项目时,每次启动需要耗时很久,这是因为Webpack需要预先将所有的模块文件进行编译打包。如果项目过大会导致时间变长,很影响到开发效率。
vite
vite基于esbuild开发的新一代前端打包工作,它拥有极快的打包速度,依赖于esm
,也是作为了vue3或者其他框架的构建工具。
vite在启动时并不会把所有的依赖构建一遍,而是根据所访问的页面,动态编译。比如:我访问app.vue文件,那么它并不会把整个项目重新编译,而是只对app.vue这个文件依赖进行重新编译。如下图
而webpack则会全部进行打包成chunk
vue3 效率提升
编译提升
静态节点会被提升(没有动态绑定的内容)
属性也会被提升
预字符串化
当编译器遇见大量的静态节点,就不构建createvNode了(影响效率),它会将整体节点形成一个字符串。
当编译器遇到大量连续的静态内容,会直接将其编译为一个普通字符串节点
<div class=\"logo\"><h1>logo</h1></div><ul class=\"nav\"><li><a href=\"\">menu</a></li><li><a href=\"\">menu</a></li><li><a href=\"\">menu</a></li><li><a href=\"\">menu</a></li><li><a href=\"\">menu</a></li></ul><div class=\"user\"></div>
事件缓存
<button @click="count++">{{ count }}</button>
// vue2
render(ctx){
return createVNode("button", {
onClick: function($event){
ctx.count++;
}
})
}
// vue3
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return _openBlock(), _createElementBlock(
"button",
{
onClick: _cache[0] || (_cache[0] = ($event) => $setup.count++)
},
_toDisplayString($setup.count),
1
/* TEXT */
);
}
Block tree
vue2新旧DOM树对比时是逐层对比,这样会影响效率,而vue3是将把动态节点收集起来, 只对比动态的节点,静态的不进行对比。
<form>
<div>
<label>账号:</label>
<input v-model="user.loginId" />
</div>
<div>
<label>密码:</label>
<input v-model="user.loginPwd" />
</div>
</form>
Patch flag
patch flag是vue3确定哪些是动态的节点
<div class="menu-bar-container">
<div class="logo">
<h1>logo</h1>
</div>
<ul class="nav">
<li><a href="">menu</a></li>
<li><a href="">menu</a></li>
<li><a href="">menu</a></li>
<li><a href="">menu</a></li>
<li><a href="">menu</a></li>
</ul>
<div class="user">
<span>{{ user.name }}</span>
</div>
</div>
const _hoisted_2 = /* @__PURE__ */ _createStaticVNode('<div class="logo"><h1>logo</h1></div><ul class="nav"><li><a href="">menu</a></li><li><a href="">menu</a></li><li><a href="">menu</a></li><li><a href="">menu</a></li><li><a href="">menu</a></li></ul>', 2);
const _hoisted_4 = { class: "user" };
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return _openBlock(), _createElementBlock("div", _hoisted_1, [
_hoisted_2,
_createElementVNode("div", _hoisted_4, [
_createElementVNode(
"span",
null,
_toDisplayString($setup.user.name),
1
/* TEXT */
)
])
]);
}
标注着1为动态节点,这样只对比内容
为什么vue3去掉了Vue构造函数
- vue2在拥有多个vue实例时,所挂载的插入会在vue构造函数之上,并不是在实例之上,这样一来,在打包的时候体积会很大
- vue构造函数集成了太多功能,无法进行tree shaking,vue3则把这些功能分离出去。
- Vue构造函数既是一个应用又是一个组件,没有进行分开,所以vue3通过createApp创建应用,如果需要其他插入在后面use进行。
响应式变化
- vue3不再使用Object.defineProperty的方式定义完成数据响应式,而是使用Proxy。
- 除了Proxy本身效率比Object.defineProperty更高之外,由于不必递归遍历所有属性,而是直接得到一个Proxy。所以在vue3中,对数据的访问是动态的,当访问某个属性的时候,再动态的获取和设置,这就极大的提升了在组件初始阶段的效率。
- 同时,由于Proxy可以监控到成员的新增和删除,因此,在vue3中新增成员、删除成员、索引访问等均可以触发重新渲染,而这些在vue2中是难以做到的。
vue3 模板变化
写法一 通过旧方法实现组件双向绑定
父组件
如何后面不跟:默认为modelValue,可以往后追加修饰符,如:title.trim
<template>
<!--<check-sel :modelValue="sel" @update:modelValue="sel = $event"></check-sel>-->
<!--简写-->
<check-sel v-model="sel" v-model:title.trim="title"></check-sel>
</template>
子组件
- 使用props接收父组件传递过来的参数。并绑定到子组件模板中
- 使用emit(“update:title”) 将值传递给父组件形成双向绑定
- 使用setup第二个参数 ctx.attrs 获取修饰符参数
<template>
<div class="check-editor">
<div class="check-editor-inner">
<div class="checkbox" @click="handleChecked" :class="{ checked: modelValue }"></div>
<input type="text" @input="handleTitle($event)" :value="title" class="editor" />
</div>
</div>
</template>
<script>
export default {
props: {
title: {
type: String
},
modelValue: {
type: Boolean
}
},
setup(prop, ctx) {
console.log('ctx:', ctx)
console.log('prop:', prop)
const { titleModifiers } = ctx.attrs //获取修饰符参数
const handleTitle = (event) => {
let value = event.target.value
if (titleModifiers && titleModifiers.trim) {
value = value.replace(/ /g, '')
}
ctx.emit('update:title', value)
}
const handleChecked = () => {
ctx.emit('update:modelValue', !prop.modelValue)
}
return {
handleTitle,
handleChecked
}
}
}
</script>
写法二
父组件同上
子组件
使用defineModel方法实现双向绑定
<script setup>
const emit = defineEmits('update:modelValue')
const sel = defineModel()
const [title, titleModifiers] = defineModel('title', {
set(value) {
if (titleModifiers.trim) {
return value.replace(/ /g, '')
}
return value
}
})
const handleChecked = () => {
emit('update:modelValue', !sel.value)
}
</script>
key
● 当使用进行v-for循环时,需要把key值放到中,而不是它的子元素中
● 当使用v-if v-else-if v-else分支的时候,不再需要指定key值,因为vue3会自动给予每个分支一个唯一的key
即便要手工给予key值,也必须给予每个分支唯一的key,不能因为要重用分支而给予相同的 key
Fragment
vue3现在允许组件出现多个根节点
vue3 组件变化
- defineAsyncComponent 异步组件
- Suspance
- teleport(将节点插入到顶层)