ie浏览器查看vue中js_浅析 Vue.js 中那些空间换时间的操作
本期的问题:在 Vue.js 模板的编译过程中,我们已经知道静态提升的好处:针对静态节点不用每次在 render
阶段都执行一次 createVNode
创建 vnode
对象。但它有没有成本呢?为什么?
在回答问题前,我们简单回顾一下什么是静态提升,假设我们有如下模板:
<p>hello {{ msg }}p><p>staticp>
在开启 hoistStatic
编译配置的情况下最终编译结果如下:
import { toDisplayString as _toDisplayString, createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from "vue"
const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "static", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createBlock(_Fragment, null, [ _createVNode("p", null, "hello " + _toDisplayString(_ctx.msg), 1 /* TEXT */), _hoisted_1 ], 64 /* STABLE_FRAGMENT */))}
我们发现静态节点
static
编译生成 vnode
的过程被提取到 render
函数外面了,然后在 render
函数内部就可以直接拿到静态节点的编译结果 _hoisted_1
。
之所以可以这么做,是因为静态节点是不会改变的,所以它编译生成的 vnode
也不会改变,而动态节点是变化的,必须在每次 render
的时候动态创建,它的 vnode
生成过程就不能提取到外面。
显然,这样做的好处是由于 render
函数在每次组件重新渲染的时候都会执行,而针对静态节点,创建 vnode
的过程只执行一次,相当于提升了 render
的性能。
但是这么做也是有成本的,创建的 hoisted_1 vnode
对象始终会在内存中占用,并不会在每次 render
函数执行后释放。
其实,这就是典型的空间换时间的做法,在绝大部分的场景,性能好意味着更好的用户体验,而牺牲那一点内存空间完全是可接受的,对于用户也是无感知的,所以空间换时间是常见的一种优化手段。
在整个 Vue.js 源码内部,经常可以见到这种空间换时间的操作,接下来我们就来看几个 Vue.js 中常见的空间换时间的操作。
Vue.js 中常见的空间换时间操作
- reactive API
Vue.js 3.0 使用 Proxy API 把对象变成响应式,一旦某个对象经过 reactive
API 变成响应式对象后,会把响应式结果存储起来,大致如下:
function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any>) { // ... const proxyMap = isReadonly ? readonlyMap : reactiveMap const existingProxy = proxyMap.get(target) if (existingProxy) { return existingProxy } // ... const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) proxyMap.set(target, proxy) return proxy}
在整个响应式模块的内部,使用了 WeakMap
的数据结构存储响应式结果,它的 key
是原始的 Target
对象,值是 Proxy
对象。
export const reactiveMap = new WeakMapany>()export const readonlyMap = new WeakMapany>()
这样一来,同样的对象如果再次执行 reactive
,则从缓存的 proxyMap
中直接拿到对应的响应式值并返回。
- KeepAlive 组件
整个 KeepAlive
组件的设计,本质上就是空间换时间。在 KeepAlive
组件内部,在组件渲染挂载和更新前都会缓存组件的渲染子树 subTree
,如下:
const cacheSubtree = () => { if (pendingCacheKey != null) { cache.set(pendingCacheKey, getInnerChild(instance.subTree)) }}onMounted(cacheSubtree)onUpdated(cacheSubtree)
这个子树一旦被缓存了,在下一次渲染的时候就可以直接从缓存中拿到子树 vnode
以及对应的 DOM 元素来渲染。
KeepAlive
具体实现细节我在课程中有专门的一小节课说明,这里就不多赘述了。
- 工具函数 cacheStringFunction
Vue.js 源码内部的一些工具函数的实现,也利用了空间换时间的思想,比如 cacheStringFunction
函数,如下:
const cacheStringFunction = extends (str: string) => string>(fn: T): T => {const cache: Record<string, string> = Object.create(null)return ((str: string) => {const hit = cache[str]return hit || (cache[str] = fn(str)) }) as any}
cacheStringFunction
函数的实现很简单,内部定义了 cache
变量做缓存,并返回了一个新的函数。
在新函数的内部,先尝试中从缓存中拿数据,如果不存在则执行函数 fn
,并把 fn
的返回结果用 cache
缓存,这样下一次就可以命中缓存了。
我们来看看 cacheStringFunction
的几个应用场景:
const camelizeRE = /-(\w)/g
export const camelize = cacheStringFunction( (str: string): string => { return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')) })
const hyphenateRE = /\B([A-Z])/g
export const hyphenate = cacheStringFunction((str: string) => str.replace(hyphenateRE, '-$1').toLowerCase())
export const capitalize = cacheStringFunction( (str: string) => str.charAt(0).toUpperCase() + str.slice(1))
可以看到,这些字符串变形的相关函数都使用了 cacheStringFunction
,这样就保证了同样的字符串,在调用某个字符串变形函数后会把结果缓存,然后同一字符串再次执行该函数的时候就能从缓存拿结果了。
注意,Vue.js 内部之所以给这些字符串变形函数设计缓存,是因为它们的缓存命中率高,如果缓存命中率低的话,这类空间换时间的缓存设计就可能变成负优化了。
会造成内存泄漏吗
看到这里,你可能会有疑惑,空间换时间的基本操作都是通过缓存的方式,那这会造成内存泄漏吗?
回答这个问题前,你先要明白什么是内存泄漏:
内存泄漏(Memory leak)是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。
简单点说,内存泄漏就是那些你已经用不到的内存空间,由于没有释放而产生的内存浪费。
而我们空间换时间所设计的缓存,都是需要用到的内存空间,所以算是内存占用,并非内存泄漏。
关于使用 Vue.js 开发工作中可能会造成内存泄漏的场景,我在前几篇文章中提到了,如果你还不了解,建议你去看一看。
总结
综上,我们了解到 Vue.js 在编译过程中使用静态提升并非无成本,但是总体来看收益大于成本。此外,我们也了解 Vue.js 中一些空间换时间的操作,我希望你能学会这个优化思想并把它运用到自己平时的工作中。
我出这个题主要是希望你能做到以下两点:
学习 Vue.js 编译过程中的一些优化操作,并能思考它为什么能起到优化效果。
了解优化背后可能会造成的成本,学会评估成本和收益。
要记住,分析和思考的过程远比答案重要。
ie浏览器查看vue中js_浅析 Vue.js 中那些空间换时间的操作相关推荐
- js base64 php,php中的base64decode 与js中的互相转换
php中的base64decode 与js中的相互转换 function utf16to8(str) { var out, i, len, c; out = ""; len = s ...
- Vue中foreach数组与js中遍历数组的写法
场景 Vue中使用Axios发送get或者post请求,发送请求时需要在js中 对请求参数进行遍历并处理. 接收响应时需要对响应结果进行遍历和处理. 注意区分在vue和js中foreach数组的区别. ...
- vue watch 修改滚动条_Vue.js 中滚动条始终定位在底部的方法
Vue.js 中滚动条始终定位在底部的方法 发布于 2020-2-23| 复制链接 分享一篇关于vue 中滚动条始终定位在底部的方法,具有很好的参考价值,希望对大家有所帮助.一起跟随小妖过来看看吧 滚 ...
- js去el的map_转:el表达式获取map对象的内容 js中使用el表达式 js 中使用jstl 实现 session.removeattribute...
原文链接: 总结: el表达式获取map对象的内容 后端: HashMap map1 = new HashMap(); map1.put("key1","lzsb&quo ...
- php中的foreach和js中的foreach的用法和区别
PHP中的foreach循环: 主要用于遍历数组 例如: (1)// $colors=array("red","yellow","blue" ...
- JavaWeb中外部引入的js中涉及到location.href的页面跳转/jsp页面的外部js文件引入
当js在jsp页面中写的时候没有任何问题如下所示: function deleteP(obj){if(confirm("你真舍得丢弃我吗?")){//发送请求location.hr ...
- ajax中的trim方法,js中trim函数实例参考
在调用trim()的js方法上加入如下js代码: 复制代码 代码示例: string.prototype.trim = function(){ return this.replace(/(^/s*)| ...
- 组件用.vue还是.js_一个Vue.js 2.0组件,用于生成首字母缩写或基于图像的头像
组件用.vue还是.js Vue头像组件 (vue-avatar-component) This vue.js component provide a simple way to generate r ...
- vue + tone.js_用Tone.js和Vue.js构建的鼓合成器/音序器
vue + tone.js 循环器 (looperator) Looperator is a super cool browser based drum machine / sequencer. It ...
最新文章
- 田志刚:所有大公司都是小公司(二)
- 在对linux系统分区进行格式化时需要对磁盘簇(或i节点密度)的大小进行选择,请说明选择的原则。
- u-boot分析之命令实现(四)
- Greys Java在线问题诊断工具
- 沉淀2017,勇闯2018
- 2020CCPC长春
- Win11怎么打开以前的word文档
- Veritas面向OpenStack推出全新软件定义存储解决方案
- iPhone质量成迷?被吴彦祖一箭射穿,却还能开机
- 设计灵感|大火C4D立体字效在海报中的应用
- 【网络安全面试题】——如何渗透测试文件目录穿越
- Q99:当Bezier曲面(Utah Teapot)同时遇上“噪声纹理”和“Phong反射模型”
- 程序员这样面试,拿到offer的几率是非常大
- maven打包失败:自定义项目工具类打包给其他微服务使用
- 西游记与面向对象编程
- 181218每日一句
- Halcon软件安装教程
- ANSYS win10家庭版安装经验:
- 试题管理小能手,免费下载单机软件-题库管家
- Footprint 8月月报 DeFi市场多点开花,9月是否会迎来下一个爆发点
热门文章
- python opencv 界面按钮_如何使用Python构建简单的UI?
- 学python能做什么-学了Python都能干什么,哪个最赚钱?
- python必背代码-Python高手必修课:如何让 Python 代码更易读,推荐收藏
- 学python可以做什么职业-python学完之后比较适合哪些职业工作呢?
- iOS中 语音识别功能/语音转文字教程具体解释 韩俊强的博客
- 解密车载语音识别架构 车载系统能听懂人说话?
- 小米电视4A核心技术之语音识别浅析
- grafana zabbix 模板_【Grafana教程】安装Grafana并配置Zabbix数据源
- python爬虫中for循环无法每一段输出_Python入门到掌握只需要这3大,4类,5大,6种,即可,附教程...
- JavaScript -- DOM树