编译模版的静态标记
1 2 3 4 5
| <div id="app"> <p>周一呢</p> <p>明天就周二了</p> <div>{{week}}</div> </div>
|
vue2 会被解析成一下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function render() { with (this) { return _c( 'div', { attrs: { id: 'app', }, }, [ _c('p', [_v('周一呢')]), _c('p', [v('明天就周二了')]), _c('div', [_v(_s(week))]), ] ) } }
|
可以看出,两个p标签是完全静态的,以至于后续渲染中,其实没有任何变化, 但是在vue2.x中依然会使用_c新建成一个vdom.在diff的时候依然需要去比较,这就造成了一定量的性能消耗
在vue3中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import { createVNode as _createVNode, toDisplayString as _toDisplayString, } from 'vue'
export function render(_ctx, _cache) { return ( _openBlock(), _createVNode('div', { id: 'app' }, [ _createVNode('p', null, '周一呢'), _createVNode('p', null, '明天就周二了'), _createVNode( 'div', null, _toDisplayString(_ctx.week), 1 ), ]) ) }
|
只有当_createVNode 的第四个参数不为空的时候,这时才会被遍历, 而静态节点就不会被遍历到
同时发现了在vue3最后一个非静态的节点编译后: 出现了 /* TEXT */, 这是为了标记当前内容的类型以便进行diff, 如果不同的标记,只需要去比较对比相同的类型,这就不会去浪费时间对其他类型进行遍历
1 2 3 4 5 6 7 8 9 10 11 12 13
| export const enum PatchFlags { TEXT = 1, CLASS = 1 << 1, STYLE = 1 << 2, PROPS = 1 << 3, FULL_PROPS = 1 << 4, HYDRARE_EVENTS = 1 << 5, STABLE_FRAGMENT = 1 << 6, KEYED_FRAGMENT = 1 << 7, UNKEYED_FRAGMENT = 1 << 8, NEED_PATCH = 1 << 9, DYNAMIC_SLOTS = 1 << 10, }
|
如果存在两种类型, 那么只需要对这两个值对应的pathflag进行位或运算
如 TEXT 和 PROPS
1 2
| TEXT: 1, PROPS: 1<<3 = 8,
|
事件存储
绑定事件会存储在缓存中
1 2 3
| <div id="app"> <button @click="handleClick">周五拉</button> </div>
|
经过转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { createVNode as _createVNode, toDisplayString as _toDisplayString, } from 'vue'
export function render(_ctx, _cache) { return ( _openBlock(), _createVNode('div', { id: 'app' }, [ _createVNode( 'button', { onClick: _cache[1] || (_cache[1] = ($event, ...args) => _ctx.handleClick($event, ...args)), }, '周五啦' ), ]) ) }
|
在代码中可以看出在绑定点击事件的时候, 会生成并缓存了一个内联函数cache中,变成了一个静态的节点
静态提升
1 2 3 4 5 6 7 8
| <template> <div id="app"> <p>周一了</p> <p>周二了</p> <div>{{ week }}</div> <div :class="{ red: isRed }">周三呢</div> </div> </template>
|
转换成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import { createVNode as _createVNode, toDisplayString as _toDisplayString, } from 'vue'
const _hoisted_1 = { id: 'app' } const _hoisted_2 = _createVNode( 'p', null, '周一了', -1 ) const _hoisted_3 = _createVNode( 'p', null, '周二了', -1 )
export function render(_ctx, _cache) { return ( _openBlock(), _createVNode('div', _hoisted_1, [ _hoisted_2, _hoisted_3, _createVNode( 'div', null, _toDisplayString(_ctx.week), 1 ), _createVNode( 'div', { class: { red: _ctx.isRed }, }, '周三呢', 2 ), ]) ) }
|
在这里可以看出将一些静态的节点放在了render函数的外部,这样就避免了每次render都会生成一次静态节点
全家桶修改
vite的使用放弃了vue2.x使用的webpack
- 开发服务器启动后不需要进行打包操作
- 可以自定义开发服务器
const {createServe} = require('vite')
- 热模块替换的性能和模块数量无关, 替换变快, 即时热模块替换
- 生产环境和rollup捆绑
其他
- 提供了treeShaking,在打包的时候自动去除没有用到的vue模块
- 更好的ts支持,类型定义提示,tsx支持,class组件支持