【若川视野 x 源码共读】1-5期 总结

159 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

简单讲launch-editor、vue3-shared、vue.js发布、co、koa-compose的作用、原理和亮点。

旨在总结、复习,温故知新。

1、launch-editor

川哥:juejin.cn/post/695934…

我:www.yuque.com/ruochuan12/…

作用

支持 dev-tools 直接跳转到编译器打开当前页面所属的组件文件

open-in-editor

原理

1 在dev-tools中点击跳转时,会发出网络请求http://10.20.191.44:8080/__open-in-editor?file=src/components/HelloWorld.vue

2 vue项目启动的服务拦截到请求,传给launch-editor,解析到文件名,拼工作目录路径得到文件的绝对路径

3 基于powershell命令查当前正在运行的程序是否有编译器,不然找process.env.VISUAL或者process.env.EDITOR,再不然就失败了,报错

4 找到后 用 命令行命令 用编译器 直接打开组件文件,

childProcess.spawn(
        'cmd.exe',
        ['/C', "D:\\软件\\Microsoft VS Code\\Code.exe",
        "C:\\Users\\82454\\Desktop\\vue3\\vue3-project\\src\\components\\HelloWorld.vue"],
        { stdio: 'inherit' }
)

相当于code C:\\Users\\82454\\Desktop\\vue3\\vue3-project\\src\\components\\HelloWorld.vue

我调试时由于编辑器安装在中文路径,打不开文件,为此改了源码,让powershell读取后,转码再解压避免中文乱码,pr后,没有回复。。

学到啥

1 用code 文件路径 可以用编译器打开项目

2 搜索node_modules下的文件

​ 就是「排除的文件」右侧旁边有个设置图标「使用“排查设置”与“忽略文件”」,点击下。

3 高效终端工具-使用 ohmyzsh 打造 windows、ubuntu、mac 系统高效终端命令行工具

mp.weixin.qq.com/s/MHngeDABR…

4 window路径要 \\

5 在插件源码里改代码和添加打印代码,需要重新启动,一般的热重载不监听node_module的文件

6 我是怎么在 powershell输出中文的

  const iconv = require('iconv-lite')
  const output = iconv.decode(
        new Buffer(
          childProcess.execSync(
            'powershell -Command "Get-Process | Select-Object Path"',
            {
              stdio: ["pipe", "pipe", "ignore"],
              encoding: "binary",
            }
          ),
          "binary"
        ),
        "cp936"
      )

2、vue3-shared

我:www.yuque.com/ruochuan12/…

川:juejin.cn/post/699497…

作用

vue3的一个工具函数包,

图来源:www.yuque.com/ruochuan12/…

vue-next 工具函数.png

学到啥

  • 缓存,这里得到闭包函数,驼峰转换过的字符会存到闭包里,第二次转可以直接获取
const cacheStringFunction$1 = (fn) => {
    const cache = Object.create(null);
    return ((str) => {
        const hit = cache[str];
        return hit || (cache[str] = fn(str));
    });
};
const camelizeRE = /-(\w)/g;//匹配 -加数字、大小写字母和下划线 如-a -B
const camelize = cacheStringFunction$1((str) => {
    //'aa-a-a'.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));返回 "aaAA"
    return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
});
//'aaAaaA'.replace(/\B([A-Z])/g, '-$1').toLowerCase() "aa-aaa-a"
const hyphenateRE$1 = /\B([A-Z])/g;
const hyphenate$1 = cacheStringFunction$1((str) => str.replace(hyphenateRE$1, '-$1').toLowerCase());

有用

getGlobalThis 全局对象

像作用域链,也像原型链

const getGlobalThis = () => {
    return (_globalThis ||
        (_globalThis =
            typeof globalThis !== 'undefined'
                ? globalThis
                : typeof self !== 'undefined'
                    ? self
                    : typeof window !== 'undefined'
                        ? window
                        : typeof global !== 'undefined'
                            ? global
                            : {}));
};

hasChanged

//检测值的变化
const hasChanged = (value, oldValue) => !Object.is(value, oldValue);

hasOwn 检测是否属性是否拥有

const hasOwnProperty = Object.prototype.hasOwnProperty;
const hasOwn = (val, key) => hasOwnProperty.call(val, key);
// .call 则是函数里 this 显示指定以为第一个参数,并执行函数。
//Object.prototype.hasOwnProperty.call(x,y)


hasOwn({__proto__: { a: 1 }}, 'a') // false
hasOwn({ a: undefined }, 'a') // true
hasOwn({}, 'a') // false
hasOwn({}, 'hasOwnProperty') // false
hasOwn({}, 'toString') // false
// 是自己的本身拥有的属性,不是通过原型链向上查找的。

isMap 判断是不是 Map 对象

const objectToString$1 = Object.prototype.toString;
const toTypeString$1 = (value) => objectToString$1.call(value);
const isMap = (val) => toTypeString(val) === '[object Map]';
//Object.prototype.toString.call(new Map())
// 例子:
const map = new Map();
isMap(map); // true

isObject 是否对象

const isObject$1 = (val) => val !== null && typeof val === 'object';
// 例子:
isObject(null); // false
isObject({name: '若川'}); // true
// 判断不为 null 的原因是 typeof null 其实 是 object

isPromise

//判断是否是对象,是否有then和catch方法
const isPromise = (val) => {
    return isObject$1(val) && isFunction(val.then) && isFunction(val.catch);
};
// 判断是不是Promise对象
const p1 = new Promise(function(resolve, reject){
  resolve('若川');
});
isPromise(p1); // true

isSet

const isSet$1 = (val) => toTypeString$1(val) === '[object Set]';

isSymbol

const isSymbol = (val) => typeof val === 'symbol';

looseEqual 判断对象是否相同

不像Object.is要引用地址相同,属性和值相同即可

function looseCompareArrays(a, b) {
    if (a.length !== b.length)
        return false;
    let equal = true;
    for (let i = 0; equal && i < a.length; i++) {
        equal = looseEqual(a[i], b[i]);
        //这里高能,如果是我会if(!equal)return false,他这样写特别有逼格耶
    }
    return equal;
}

function looseEqual(a, b) {
    //引用地址相同,值也没啥好比的了
    if (a === b)
        return true;
    //若都是日期对象,比时间戳
    let aValidType = isDate(a);
    let bValidType = isDate(b);
    if (aValidType || bValidType) {
        return aValidType && bValidType ? a.getTime() === b.getTime() : false;
    }
    //若都是数组,比长度,再比值
    aValidType = isArray(a);
    bValidType = isArray(b);
    if (aValidType || bValidType) {
        return aValidType && bValidType ? looseCompareArrays(a, b) : false;
    }
    aValidType = isObject(a);
    bValidType = isObject(b);
    //若都是对象,比key数量,比key,比key值,key值比对跟深拷贝类似
    if (aValidType || bValidType) {
        /* istanbul ignore if: this if will probably never be called */
        if (!aValidType || !bValidType) {
            return false;
        }
        const aKeysCount = Object.keys(a).length;
        const bKeysCount = Object.keys(b).length;
        if (aKeysCount !== bKeysCount) {
            return false;
        }
        for (const key in a) {
            const aHasKey = a.hasOwnProperty(key);
            const bHasKey = b.hasOwnProperty(key);
            if ((aHasKey && !bHasKey) ||
                (!aHasKey && bHasKey) ||
                !looseEqual(a[key], b[key])) {
                return false;
            }
        }
    }
    //比字符串,String(对象):[object Object]
    return String(a) === String(b);
}

3、vue.js发布

川:juejin.cn/post/699794…

我:www.yuque.com/ruochuan12/…

作用

一行命令 自动版本号更新、测试、更新依赖、打包、changelog、提交、推送和发布

流程

  1. 确认要发布的版本
  2. 执行测试用例
  3. 更新依赖版本 3.1 updatePackage 更新包 package.jsonversion 3.2 updateDeps 更新依赖版本号
  4. 打包编译所有包
  5. 生成 changelog
  6. 提交代码 add,commit
  7. 发布包 publish
  8. 推送到 github tag,push tag,push

学到啥

对我来说基本都是好东西。

为公司项目写了个小型脚本,只推送

  "scripts": {
    "push": "node script/push.js"
  },

const execa = require('execa')
const m = process.argv.slice(2)[0]
// 执行命令和只打印命令相关信息
const run = (bin, args, opts = {}) =>
    execa(bin, args, { stdio: 'inherit', ...opts })
// const dryRun = (bin, args, opts = {}) =>
//     console.log((`[dryrun] ${bin} ${args.join(' ')}`), opts)
async function main() {
    await run('git', ['add', '.'])
    await run('git', ['commit', '-m', m || 'update'])
    await run('git', ['push'])
}
main()

打包上传 github.com/xiaojiahao/…

4、co

我:www.yuque.com/ruochuan12/…

川:www.yuque.com/ruochuan12/…

作用

自执行generator

学到啥

#https://www.yuque.com/ruochuan12/bssbzg/ci77ui
const co = gen => new Promise((resolve, reject) => {
	function next(data) {
        let { value, done } = gen.next(data);
        if (done) {
            resolve(value);
        } else {
            Promise.resolve(value).then(next, reject);
        }
    }
    next(); // 这里需要自执行一次,然后不断递归调用
})

5、koa-compose

川:juejin.cn/post/700537…

我:www.yuque.com/ruochuan12/…

作用

洋葱模型,中间件是指连贯整个 Koa 应用程序,并共享资源的独立插件,“连贯”对应“next”,“共享资源对应context”。

img

原理

1 利用app.use 存储中间件,用数组存

2 从第一个中间件开始执行,执行中间件第二个参数next可以跳转执行下一个中间件,中间件用promise包裹,所以有什么报错时可以携带错误返回上一个中间件去处理。

学到啥

1 为了防止同一个中间件里多次执行next,通过闭包计数

next 即 dispatch.bind(null, i + 1),同一个中间件中i相等,每次执行next,i会赋值给index,第二次执行next会导致i和index相等,报错。
​
index = -1
dispatch(0)
function dispatch (i) {
  if (i <= index) return Promise.reject(new Error('next() called multiple times'))
  index = i
}

2 compose 源码博大精深,koa源码值得深入挖掘,这一块单独看还是有点蒙的