前言

我是一个前端小白,都不算前端,以前从来没有接触过前端,我只是今年转到了互联网,做UI自动化测试,发现对前端这些知识的理解是很有必要的,所以我就开始自己学前端,希望在我遇到元素定位,和页面内容出现的异常,不需要次次找前端开发请教,而是希望我自己能够排查定位,节省大家的时间。

以上是我学习前端的初衷。然后就在掘金遇到了若川大佬,他真的是一个很nice的人,在开始进源码共读活动组之前,我自己先在w3cschool学了一遍基础知识,包括html,css, js,然后才有勇气找川哥(大家都这么叫他)拉我进组,参加这个源码共读活动。

但是进展原没有我想的那么顺利,打开这个最简单的一期,粗略看了一下validate-npm-package-name源码,前几行代码就把我看懵了,是我在js基础语法里没看到的,然后往下看,除了js的一些基本语法,还有根据以往的编程基础(c和python)能知道这是个根据用户输入的名字,来进行判断是否符合命名规则,然后走不同的分支。

学习笔记

要学习的知识点蛮多的

npm

首先对这个包名就不是很理解,然后我就查了下资料。

npm是前端开发广泛使用的包管理工具,它让js开发者分享、复用代码更方便。可以重复的框架代码被称为包(package)或者模块(module),一个包可是是一个文件夹里放着几个文件夹,还有一个package.json文件。

npm的作用就是让我们把可复用的框架代码发布到一个地方,可以供大家一起使用

跟python的pip 很像.

源码分析

(1)use strict

​'use strict'​

可能看的教程,真的是太基础了,这个也没见过,我也去查了下资料 这个use strict 表明js代码是在严格模式下执行,减少出错,我心中不自觉的又和c和python做了对比。

详细分析

// 创造一个正则对象
var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
// 加载builtin模块的所有方法
var builtins = require('builtins')
// 一个字符串数组
var blacklist = [
'node_modules',
'favicon.ico'
]

这几行代码,能理解的是创造了一个正则对象和blacklist字符数组,但是那个require是什么,就不明白了,我又去查了下资料 require是加载其他模块,我感觉像c中的#include和 python中的import,可以导入其他模块,用其他模块中的方法,此处应该是加载导入builtins模块中的方法。

关于这个正则的解释,组内成员讲的特别详细,图也好看,他的文章链接​​juejin.cn/post/701204…​

接下来继续看

var validate = module.exports = function (name) {
var warnings = []
var errors = []

这个module.exports又把我难住了,查资料吧,经过了解是node.js中的,后续我估计要去啃node.js, 这个module.exports特别像c++类中的public,python中的类虽没有显示声明的public方法的关键字,但也用其他方式体现了,哪些方法接口是可以让外部访问的,这行代码应该就是定义了一个可以让外部访问的validate方法,然后在其他模块就可以通过require导入进来进行访问。

看多了其他语言,这种写法我第一次看挺不理解的,前端闺蜜跟我说,学习先不要探索为什么,先学着去接受,后面实践多了,你就理解了,想想也是,我当初学编程不也这样么,估计这也是当你习惯一种语言,再去接受另一门的时候,总是会冒出为啥啊为啥啊,容易钻进牛角尖。js也在用自己的方式展现自己的魅力啊,虽然各种语言有时干的是同一件事

后面的就容易理解多了

看到这里,整个包的源码结构也清晰了,可以分为三块:

  1. 第一块,初始化一些变量,留待后续用
  2. 第二块,核心内容,验证名字是否输入合法
  3. 第三块,第二部分中的通用输出抽出来写了一个done方法

先来看done方法

validate-npm-package-name源码学习笔记_ico 这个方法的参数是第一块定义的两个数组,根据这两个数组,定义了一个result对象,对象中有四个属性:新的命名规则是否合法的布尔值, 旧的命名规则的布尔值,warnings数组,errors数组。

分支1
// 名字为空,errors数组添加提示信息,返回result结果
// validForNewPackages:false
// validForOldPackages:false
// errors:'name cannot be null'
if (name === null){
errors.push('name cannot be null')
return done(warnings, errors)
}

分支2

// 名字为undefined,errors数组添加提示信息,返回result结果
// validForNewPackages:false
// validForOldPackages:false
// errors:'name cannot be undefined'
if (name === undefined) {
errors.push('name cannot be undefined')
return done(warnings, errors)
}

分支3

// 名字不是字符串,errors数组添加提示信息,返回result结果
// validForNewPackages:false
// validForOldPackages:false
// errors:'name must be a string'
if (typeof name !== 'string') {
errors.push('name must be a string')
return done(warnings, errors) }

分支4

// 名字长度为0,errors数组添加提示信息,返回result结果 
// validForNewPackages:false
// validForOldPackages:false
// errors:'name length must be greater than zero'
if (!name.length) {
errors.push('name length must be greater than zero')
}

分支5

// 名字不能以‘.’开头,errors数组添加提示信息,返回result结果 
// validForNewPackages:false
// validForOldPackages:false
// errors:'name cannot start with a period'
if (name.match(/^\./)) {
errors.push('name cannot start with a period')
}

分支6

// 名字不能以‘_’开头,errors数组添加提示信息,返回result结果 
// validForNewPackages:false
// validForOldPackages:false
// errors:'name cannot start with an underscore'
if (name.match(/^_/)) {
errors.push('name cannot start with an underscore')
}

分支7

// 名字首尾不能有空格,errors数组添加提示信息,返回result结果 
// validForNewPackages:false
// validForOldPackages:false
// errors:'name cannot start with an underscore'
if (name.trim() !== name) {
errors.push('name cannot contain leading or trailing spaces') }

分支8

// 名字不能是node_modules和favicon.ico,errors数组添加提示信息,返回result结果 
// validForNewPackages:false
// validForOldPackages:false
// errors:'name cannot start with an underscore'
blacklist.forEach(function (blacklistedName) {
if (name.toLowerCase() === blacklistedName) {
errors.push(blacklistedName + ' is a blacklisted name')
}
}
)

分支9

// 名字不能是builtin, warnings数组添加提示信息,返回result结果 
// validForNewPackages:false
// validForOldPackages:true
// warnings:builtin + ' is a core module name'
builtins.forEach(function (builtin) {
if (name.toLowerCase() === builtin) {
warnings.push(builtin + ' is a core module name')
}
})

分支10

// 名字长度不能大于214个字符, warnings数组添加提示信息,返回result结果 
// validForNewPackages:false
// validForOldPackages:true
// warnings:'name can no longer contain more than 214 characters'
if (name.length > 214) {
warnings.push('name can no longer contain more than 214 characters')
}

分支11

// 名字不能包含大写字母, warnings数组添加提示信息,返回result结果 
// validForNewPackages:false
// validForOldPackages:true
// warnings:'name can no longer contain capital letters'
if (name.toLowerCase() !== name) {
warnings.push('name can no longer contain capital letters')
}

分支12

// 名字不能包含特殊字符, warnings数组添加提示信息,返回result结果 
// validForNewPackages:false
// validForOldPackages:true
// warnings:'name can no longer contain special characters ("~\'!()*")'
if (/[~'!()*]/.test(name.split('/').slice(-1)[0])) {
warnings.push('name can no longer contain special characters ("~\'!()*")')
}

分支13

// 名字不能contain any non-url-safe characters, errors数组添加提示信息,返回result结果 
// validForNewPackages:false
// validForOldPackages:false
// errors:'name can only contain URL-friendly characters'
if (encodeURIComponent(name) !== name) {
// Maybe it's a scoped package name, like @user/package
var nameMatch = name.match(scopedPackagePattern)
if (nameMatch) {
var user = nameMatch[1]
var pkg = nameMatch[2]
if (encodeURIComponent(user) === user && encodeURIComponent(pkg)=== pkg)
{ return done(warnings, errors) }
}
errors.push('name can only contain URL-friendly characters')
}

至此源码的部分基本结束

验证练习

var validate = require("validate-npm-package-name")

validate("some-package")

validate("example.com")

validate("under_score")

validate("123numeric")

validate("@npm/thingy")

validate("@jane/foo.js")

我在我的电脑上配置了node.js开发环境,安装此包,然后引用进行练习,结果如图:

validate-npm-package-name源码学习笔记_提示信息_02

结束语

获益良多,收获满满,前端之路真是路漫漫其修远兮,吾将上下而求索! 再次感谢川哥!!!