【若川视野 x 源码共读】6-10 期 总结

144 阅读3分钟

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

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

简单讲update-notifier、validate-npm-package-name、tiny-emitter、create-vue@next、configstore的作用、原理和亮点。

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

6、update-notifier

川:www.yuque.com/ruochuan12/…

我:www.yuque.com/ruochuan12/…

github:github.com/yeoman/upda…

使用与作用

需要自己读取 package.json传给插件

import updateNotifier from 'update-notifier';
import packageJson from './package.json' assert {type: 'json'};

updateNotifier({pkg: packageJson}).notify();
// 以前用法,不得不感慨import正在一步步干掉require啊
const updateNotifier = require('update-notifier');
const pkg = require('./package.json');

updateNotifier({pkg}).notify();

终端打印提示当前项目和在npm上有更新

img

流程

1 由用户主动获取 package.json,传给这个插件,用里面的信息初始化

2 以项目名为key去存 configstore,判断当前时间和上次检查时间相差是否过了更新检查间隔时间(默认是一天),没过就返回,过了就接着执行

3 通过 latest-version 插件 判断当前项目版本号是否最新,没最新就发出通知提示更新

认识了很多插件,以后写个总结。

7、validate-npm-package-name

源码:github1s.com/npm/validat…

我:www.yuque.com/ruochuan12/…

作用

校验npm包名是否合规,避免创建的项目以后发布的时候不合规,

当然npm可能有同名的项目,那也是不行的

原理

一些正则等的判断,必须要小写字符串,一堆校验。

这一块 split('/').slice(-1)[0] 我是真觉得骚

  // slice(-1)[0] 保证永远截取包名正确,a.split('/')截不到返回[a]
   'koa'.split('/').slice(-1)[0] // 'koa'
   '@babel/core'.split('/').slice(-1)[0] // 'core'

8、tiny-emitter

我:www.yuque.com/ruochuan12/…

川:www.yuque.com/ruochuan12/…

好几年没更新了,满满的老语法,但耐用

作用

事件的发布订阅库,像vue3事件总线

自己写个,面试防备

class Observe{
    constructor(){
        this.map = new Map()
    }
    on(name,fn){
        //以后还是写成this.map[name]吧
        if(!this.map.name){this.map.name=[]}
        this.map.name.push(fn)
        return ()=>{
            this.map.name=this.map.name.filter(i=>i!==fn)
            //this.map.name.splice(this.map.name.indexOf(fn),1)
        }
    };
    emit(name,data){
        if(this.map.name){
            this.map.name.forEach(i=>i(data))
        }
    };
    off(name,fn){
        if(this.map.name){
            this.map.name = this.map.name.filter(i=>i!==fn)
        }
    }
    once(name,fn){
        let listener = ()=>{
            this.off(name,listener)
            fn.apply(this,arguments)
        }
        this.on(name,listener)
    }
}
let emitter = new Observe()
let xx = function(data){
    console.log('xx',data)
}
// emitter.on('ss',xx)
// emitter.off('ss',xx) 
//这里学习下redux-reducer
let off = emitter.on('ss',xx)
off()
emitter.emit('ss','xx')

9、create-vue@next

川:www.yuque.com/ruochuan12/…

我:www.yuque.com/ruochuan12/…

作用

一种创建基于vite的vue3简单脚手架

使用:

npm init vue@3
以前是npm init vue@next

流程

从终端命令行获取 即将创建的项目名和依赖、有两种形式获取,一种是交互填写,一种通过--ts这种

根据收集的数据去渲染相应的模板

获取系统相应的终端运行环境,做相应的提醒,npm/yarn install、cd、dev

学到

  • npm init

npm init <initializer> 时转换成npx命令:

  • npm init foo -> npx create-foo
  • npm init @usr/foo -> npx @usr/create-foo
  • npm init @usr -> npx @usr/create

所以 npm init vue@3 = 》 npx create-vue@3

也就是说 creat 前缀初始化的命令 已经是业界潜规则了

  • 检查文件夹名本地是否存在
function canSafelyOverwrite(dir) {
    //existsSync 是否存在这个文件夹
    //readdirSync 文件夹下的文件列表
  return !fs.existsSync(dir) || fs.readdirSync(dir).length === 0
}
  • 合并两个 package.json
  • 模板文件有些是 .开头的,fs可能找不到,所以改成 _ ,渲染模板时找到再改名即可。
  • 递归删除文件
emptyDir(root)
function emptyDir(dir) {
  postOrderDirectoryTraverse(
    dir,
      //rmdirSync 删除文件夹
      //unlinkSync 删除文件
    (dir) => fs.rmdirSync(dir),
    (file) => fs.unlinkSync(file)
  )
}
function postOrderDirectoryTraverse(dir, dirCallback, fileCallback) {
  for (const filename of fs.readdirSync(dir)) {
    const fullpath = path.resolve(dir, filename)
    //检查是否是目录 fs.lstatSync(fullpath).isDirectory()
    if (fs.lstatSync(fullpath).isDirectory()) {
      postOrderDirectoryTraverse(fullpath, dirCallback, fileCallback)
      dirCallback(fullpath)
      continue
    }
    fileCallback(fullpath)
  }
}
  • git subtree,可以看Git Subtree 简明使用手册
  • 有时候为了脚手架轻便,一些简单的库可以自己实现,Vue-CLIvue create vue-project 命令是用官方的npmvalidate-npm-package-name,删除文件夹一般都是使用 rimrafcreate-vue 是自己实现emptyDirisValidPackageName
  • 二进制的一种遍历方式
  arr:['typescript', 'jsx', 'router', 'vuex', 'with-tests']
  for (let i = 1; i < 1 << arr.length; i++) {//00001~11111
    const combination = []
    for (let j = 0; j < arr.length; j++) {
        
      if (i & (1 << j)) {
        combination.push(arr[j])
      }
    }
    combinations.push(combination)
  }
得到 [['typescript'],['jsx','router']...]等32种排序。 1-31 

10、configstore

我:www.yuque.com/ruochuan12/…

川:www.yuque.com/ruochuan12/…

作用

方便加载和保留配置,而无需考虑存哪

可以认为是 浏览器的localStorage,第六期就用他存 包的校验时间

import Configstore from 'configstore';

const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));

// Create a Configstore instance.
const config = new Configstore(packageJson.name, {foo: 'bar'});

console.log(config.get('foo'));
//=> 'bar'

原理

1 拼接存储路径

linux下 /home/sindresorhus/.config/configstore/${id}.json

其他系统 系统临时文件的默认目录路径/随机字符串/configstore/${id}.json

2 支持点设值,最终值存文件中

config.set('bar.baz', true);

学到

一些依赖,点路径库 dotProp、唯一随机数 unique-string