写点什么

Vue 源码学习 | 从源码中学习 Javascript 技巧

用户头像
devpoint
关注
发布于: 1 小时前
Vue源码学习 | 从源码中学习Javascript技巧

阅读代码是提高编码水平的好方法,优秀的源代码就像一部文学巨作,开拓思维,提供启示。最近在阅读 vue2 的源代码,学到了很多 JS 的编码技巧,后续陆续分享出来供参考,顺便总结一下代码阅读成果。

1. 缓存函数

先来看一个需求,假设有一个逻辑复杂的函数 superComputed 执行很费时间,如果每次使用都去计算一次,就会给用户带来很长的等待。这个时候需要考虑将计算结果缓存起来供后续程序调用,缓存函数需要实现当参数相同的情况下,直接取缓存结果。这跟服务器端为避免过多的查询数据库而用文件缓存查询结果相似,在前端如何实现呢?


const superComputed = (str) => {    // 假设这个函数执行时间很长    console.info("===> 超级计算开始了……");    return `输入:${str}`;};
复制代码


编写一个 cached 函数来封装目标函数,这个 cached 函数接受目标函数作为参数,然后返回一个封装好的新函数。在 cached 函数的内部,可以使用 ObjectMap 缓存前一个函数调用的结果。


在 VUE 项目文件的第 153 行(点击进去)。


vue/src/shared/util.js
复制代码


这个 cached 的代码如下:


/** * Create a cached version of a pure function. */export function cached<F: Function>(fn: F): F {    const cache = Object.create(null);    return (function cachedFn(str: string) {        const hit = cache[str];        return hit || (cache[str] = fn(str));    }: any);}
复制代码


现在将 cached 稍微改下,让其可以执行,每次执行 superComputed 函数都会打印 ===> 超级计算开始了……,以方便查看函数是否被缓存, 如下:


const superComputed = (str) => {    // 假设这个函数执行时间很长    console.info("===> 超级计算开始了……");    return `输入:${str}`;};const cached = (fn) => {    const cache = Object.create(null);    return (str) => {        const hit = cache[str];        return hit || (cache[str] = fn(str));    };};
const cacheSuperComputed = cached(superComputed);console.log(cacheSuperComputed("DevPoint"));console.log(cacheSuperComputed("DevPoint"));console.log(cacheSuperComputed("juejin"));
复制代码


执行后的结果如下:


===> 超级计算开始了……输入:DevPoint输入:DevPoint===> 超级计算开始了……输入:juejin
复制代码


从结果不难看出,函数执行结果在参数不变的情况下,取得缓存的数据。

2. 将dev-point转换为devPoint

在项目开发过程中,通常会出现变量风格不一致的问题,可以编写一个函数将其转换为统一的风格。


在 VUE 项目文件的第 160 行(点击进去)。


vue/src/shared/util.js
复制代码


/** * Camelize a hyphen-delimited string. */const camelizeRE = /-(\w)/gexport const camelize = cached((str: string): string => {  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')})
复制代码


将其稍微修改,里面的 cached 函数就是之前介绍的缓存函数。


const camelizeRE = /-(\w)/g;const camelize = cached((str) => {    return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ""));});
console.log(camelize("dev-point")); // devPoint
复制代码

3. 自定义函数判断

这里所说的自定义函数,指的是开发人员自定义的函数,不是 Javascript 原生宿主函数。可能想到原理就是将函数转换为字符串,先来看下结果:


console.log(cached.toString());console.log(toString.toString());
复制代码


执行结果如下:


// 下面是自定义函数的结果(fn) => {    const cache = Object.create(null);    return (str) => {        const hit = cache[str];        return hit || (cache[str] = fn(str));    };}// 下面是原生宿主函数的结果function toString() { [native code] }  
复制代码


从执行结果来看,原生宿主函数 toString 的结果始终是 function fnName() { [native code] } 格式,因此就可以通过这个来区分,接下来看看 VUE 项目中的实现方式。


vue/src/core/util/env.js
复制代码


在文件的第 58 行,代码如下:


/* istanbul ignore next */export function isNative (Ctor: any): boolean {  return typeof Ctor === 'function' && /native code/.test(Ctor.toString())}
复制代码

4. JS 运行环境

在前端快速发展的今天, JavaScript 代码可以在不同的运行环境中执行。为了更好的适应各种运行环境,需要判断当前代码是在哪个运行环境中执行的,下面来学习一下 Vue 是如何判断运行环境的:


vue/src/core/util/env.js
复制代码


在文件的第 6 行开始,代码如下:


// Browser environment sniffingexport const inBrowser = typeof window !== 'undefined'export const inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platformexport const weexPlatform = inWeex && WXEnvironment.platform.toLowerCase()export const UA = inBrowser && window.navigator.userAgent.toLowerCase()export const isIE = UA && /msie|trident/.test(UA)export const isIE9 = UA && UA.indexOf('msie 9.0') > 0export const isEdge = UA && UA.indexOf('edge/') > 0export const isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android')export const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios')export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdgeexport const isPhantomJS = UA && /phantomjs/.test(UA)export const isFF = UA && UA.match(/firefox\/(\d+)/)
复制代码


这些判断代码都值得借鉴的,这里不展开介绍了,之前在《Vue源码学习 | 4个实用的Javascript技巧》介绍了浏览器的判断。

发布于: 1 小时前阅读数: 2
用户头像

devpoint

关注

细节的追求者 2011.11.12 加入

专注前端开发,用技术创造价值!

评论

发布
暂无评论
Vue源码学习 | 从源码中学习Javascript技巧