早年间有幸在Raychee哥门下当小弟,学到两把刷子。在编程路上,他的很多思想深深影响了我,比如笔者今天要分享的主题。在程序开发中,有个utils
包,叫做实用程序包,程序员们会把项目中通用的东西抽离出来放到这个里面,这有利于项目工程化的落地,提高项目的可维护性,减少代码冗余,锻炼编码能力,提高编码效率,理解编程思想。
在开始之前,我们先思考下,创建一个规范的项目我们需要关注哪些点?我觉得吧,第一个是创建信息的完整性,一个信息完整的项目可以引导读者与作者交流与合作,这个在后面的package.json
里面向大家介绍;第二个是代码的规范性和兼容性,正所谓,没有规矩不成方圆,良好的代码规范会巧妙地杜绝屎上雕花的行为发生,这个后面跟大家介绍下eslint+prettier+babal
的知识点;第三个项目目录的规范性,这个也会在后面介绍。基于前面三点,我们可以做出一个自产自销的项目,如果把这个过程比作拉翔,那它会很通畅,拉的很舒服,不会便秘。但是远远不够,就比如程序员A拿到程序员B写的项目,那么程序员B怎么去证明给A看,我的枪好使且我的话可信。这就引入了后面两个话题,第四点就是把你的作品发出去让别人能看得到,《何以笙箫默》中有句台词,“如果我们走散你找不到我,那我就站在最高的舞台中央让你看见我。”男同胞们听懂了吗?你想要脱单,一个不成熟的建议,站在舞台中央,发出滋滋滋的求偶声,跳出Michael Jackson
妖娆的舞步,just beat it
,just beat it
.喜欢你的说不定就有了,主动一点就会有故事。这个后面笔者介绍下git
工作流以及npm
的发包;第五点就是测试,提高可信度。这里我会结合karma
、mocha
、chai
、travis
、codecov
来向大家介绍单元测试、持续集成、代码覆盖率测试。最后的话,我会结合相关的开发工具做一个简单的搭配使用介绍吧。好的,我们开始吧。
注意: 因为笔者目前前端接触的比较多,所以这个库的定义就是给前端环境用的,不是很推荐用在nodejs开发上使用,因为其后面涉及到了一些DOM之类的操作是对nodjs没什么卵用的,所以采用ES Module
的语法来书写,若想在node环境使用,请配合babel
,webpack
等工具使用,请确保电脑上安装了nodejs
环境。
举个例子,比如我要创建一个项目叫utils
,可以怎么做?(考虑到0基础的同学,我会讲的比较细,老司机请直接跳过这章节)
如果你只是想玩玩,不想一步一步去配置,那么你只需要执行mkdir utils && npm init -y
, 这句话的意思是说创建了一个文件夹叫utils
,然后初始化一个npm
管理的项目,-y
表示yes
,也就是都选是。这个时候它就会在项目文件夹下创建一个粗糙的package.json
文件。
新手我还是建议你一步一个脚印走一遍,执行mkdir utils && npm init
,它会一步一步让你确认该项目的相关描述啊,协议啊,联系方式啊,项目地址啥的,这里笔者贴出一份该项目的npm配置。
配置说明:
name
: 项目名version
: 项目版本号description
: 项目描述main
: 项目主入口文件scripts
: 项目执行npm命令repository
: 项目仓库keywords
: 项目关键词author
: 项目作者license
: 授权协议bugs
: bug反馈homepages
: 项目主页devDependencies
: 开发环境依赖,不会随项目打包, 使用npm i @ataola/utils -D
安装dependencies
: 开发环境依赖,会随着项目打包,使用npm i @ataola/utils -S
安装husky
: 在本地提交之前,做一次lint反馈,这个需要安装相关npm包再配置lint-staged
: 只会校验提交修改的部分,这个也是需要安装相关npm包再配置,建议你和楼上那位一起用{
"name": "@ataola/utils",
"version": "0.1.5",
"description": "ataola's utils: maybe publish a feature one week, to record something i think or meet.",
"main": "index.js",
"scripts": {
"push": "./push",
"pull": "./pull",
"codecov": "codecov",
"eslint": "eslint . --ext .js --fix",
"husky:prepare": "husky install",
"husky:add": "husky add .husky/pre-commit 'npm run lint'",
"git:add": "git add -A",
"lint": "lint-staged",
"karma:init": "karma init ./karma.conf.js",
"karma:test": "karma start ./karma.conf.js",
"format": "prettier --write '**/*.{js,jsx,ts,tsx,json,md}'"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ataola/utils.git"
},
"keywords": [
"javascript",
"utils"
],
"author": "ataola (zjt613@gmail.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/ataola/utils/issues"
},
"homepage": "https://github.com/ataola/utils#readme",
"devDependencies": {
"@babel/core": "^7.13.15",
"@babel/eslint-parser": "^7.13.14",
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-transform-arrow-functions": "^7.13.0",
"@babel/plugin-transform-async-to-generator": "^7.13.0",
"@babel/plugin-transform-runtime": "^7.13.15",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.13.15",
"@babel/runtime": "^7.13.10",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"babel-plugin-istanbul": "^6.0.0",
"chai": "^4.3.4",
"codecov": "^3.8.1",
"core-js": "^3.11.0",
"eslint": "^7.24.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-prettier": "^3.3.1",
"husky": "^6.0.0",
"karma": "^6.3.2",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.0.3",
"karma-mocha": "^2.0.1",
"karma-mocha-reporter": "^2.2.5",
"karma-webpack": "^5.0.0",
"lint-staged": "^10.5.4",
"mocha": "^8.3.2",
"prettier": "^2.2.1",
"webpack": "^5.31.2"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,ts,jsx,tsx}": [
"eslint . --fix",
"prettier --config .prettierrc --write ."
]
}
}
查询npm
的相关命令是npm --help
, 比如我不知道npm init
后面可以跟什么,那么执行npm init --help
就可以罗列出相关信息。
➜ ~ npm init --help
npm init [--force|-f|--yes|-y|--scope]
npm init <@scope> (same as `npx <@scope>/create`)
npm init [<@scope>/]<name> (same as `npx [<@scope>/]create-<name>`)
aliases: create, innit
➜ ~
如果你发现npm install
很慢,多半是长城的问题,建议你改成国内的淘宝源npm install --registry=https://registry.npm.taobao.org
, 如果发现也还是不好使,终极解决方案: 科X学X上X网,逃~。
努力做好六件事:
EditorConfig
这个对应我们上面努力做好的第一件事 - 不同编辑器下的代码规范。在现实多人开发中,由于开发者的行为习惯不同可以会导致代码的风格有所不同,有些人喜欢用vscode,有些人喜欢用webstorm,也许他们用的编辑器是一样的,但是由于开发者在全局配置了一些设置,会导致整个项目代码不符合预期,所以,我们需要一个在编辑器层面去协调各个编辑器环境下的代码风格,EditorConfig是一个不错的选择,这个是本项目用到的关于EditorConfig的一些配置。
配置说明:
root=true
: 表示是最顶层的配置文件,发现设为true时,才会停止查找.editorconfig文件When opening a file, EditorConfig plugins look for a file named .editorconfig in the directory of the opened file and in every parent directory. A search for .editorconfig files will stop if the root filepath is reached or an EditorConfig file with root=true is found.
EditorConfig files are read top to bottom and the most recent rules found take precedence. Properties from matching EditorConfig sections are applied in the order they were read, so properties in closer files take precedence.
原文地址:https://github.com/editorconfig/editorconfig/issues/376
[*]
: 表示所有文件end_of_line = lf
注意这个不是if
,而是lf
, 表示换行符,它有lf
、crlf
、cr
等等,跟系统关系比较大,反正大家都统一一下用lf
,可以看下这个有名的故事-GitHub 第一坑:换行符自动转换insert_final_newline = true
: 表示在末尾插入新行[*.{js,py}]
: 表示js和python文件charset = utf-8
: 表示字符集为utf-8
indent_style = space
: 表示代码锁进格式用空格indent_size = 2
: 表示一个缩进大小两个空格quote_type = single
: 字符串设置为单引号trim_trailing_whitespace = true
: 表示是否在行尾修剪空白# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Denotes whether to trim whitespace at the end of lines
trim_trailing_whitespace = true
# Matches multiple files with brace expansion notation
[*.{js}]
charset = utf-8
quote_type = single
indent_style = space
indent_size = 2
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2
额,我觉得学这部分是有捷径的,就是去嫖名项目它们的配置,然后把它们搞懂再应用到自己或者团队的项目中。比如JQuery, Bootstrap的,跟对项目,做对事可以少走很多弯路的。
ESLint
这个对应第二件事 -eslint作语法规范。eslint用来做一些js语法规范,避免一些语法上的错误,当然也可以做格式上的规范。这个是本项目用到的关于eslint的一些配置。
配置说明:
extends
: 继承,表示它继承了某些配置, 比如eslint:recommended
表示继承了其推荐的配置,可以继承多个的,用数组表示
plugins
: 表示安装的插件, 写配置的时候可以省略前面的前缀eslint-plugin-
parserOptions
: 表示解析选项
ecmaVersion
: 表示es语法的版本, 默认为 3, 5。2015表示es6, 后面可自推
sourceType
: 默认是scirpt
,如果是ES模块用module
ecmaFeatures
: 表示额外的语言特性
parser
: 解析器,比如babel-eslint
, 表示一个对Babel解析器的包装,使其能够与 ESLint 兼容
rules
: 表示 启用的规则及其各自的错误级别, 0, 1,2
分别对应off, warn, error
no-console
: 表示禁止调用console
对象的方法
func-names
: 禁止命名的 function 表达式
no-unused-vars
: 表示禁止未使用的变量
object-shorthand
: 要求变量自变量简写
prettier/prettier
: 表示eslint下prettier的规则兼容
arrow-body-style
: 要求箭头函数使用大括号
prefer-arrow-callback
: 要求使用箭头函数作为回调
camelcase
: 使用驼峰拼写法
space-before-function-paren
: 禁止函数圆括号之前有空格
env
: 指定脚本的运行环境,比如在其里面写"es6": true
, 表示自动启动es6语法, "browser": true
表示支持浏览器环境
{
"extends": ["prettier", "plugin:prettier/recommended"],
"plugins": ["prettier"],
"parserOptions": {
"ecmaVersion": 2015,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true,
"globalReturn": true,
"impliedStrict": true
}
},
"parser": "babel-eslint",
"rules": {
"no-console": "off",
"func-names": "off",
"no-unused-vars": "warn",
"object-shorthand": "off",
"prettier/prettier": [
"error",
{
"endOfLine": "auto",
"singleQuote": true,
"trailingComma": "es5"
}
],
"arrow-body-style": "off",
"prefer-arrow-callback": "off",
"camelcase": "off",
"no-new": "off",
"space-before-function-paren": "off"
},
"env": {
"es6": true,
"browser": true
}
}
Prettier
这个对应第三件事 - prettier作代码的格式规范, 这个是本项目关于prettier的配置
配置说明:
semi
: 句尾添加分号tabWidth
: 缩进字节数singleQuote
: 使用单引号代替双引号endOfLine
: 结尾是 \n \r \n\r auto
trailingComma
: 在对象或数组最后一个元素后面是否加逗号bracketSpacing
:在对象,数组括号与文字之间加空格 "{ foo: bar }"alwaysParens
:(x) => {}
箭头函数参数只有一个时是否要有小括号。avoid:省略括号eslintIntegration
: 不让prettier使用eslint的代码格式进行校验jsxSingleQuote
: 在jsx中使用单引号代替双引号{
"semi": true,
"tabWidth": 2,
"singleQuote": true,
"endOfLine": "lf",
"trailingComma": "es5",
"bracketSpacing": true,
"alwaysParens": "always",
"eslintIntegration": true,
"jsxSingleQuote": true
}
看到这里,我们先停一停思考下,这么多配置,它们会不会产生冲突呢?那我要怎么去避免冲突,或者解决冲突呢?其实楼上已经提到了用eslintIntegration
不让prettier
使用eslint
的代码风格校验。然后在之前的eslint学习中,也可以通过在rule下新增规则作为补充。
babel
这个对应第四件事 -做好代码兼容性处理。babel是一个Javascript编译器,可以将高版本的es语法,转换成低版本的,以便能够运行在低版本浏览器或者其他环境,楼下是这个项目的babel
的配置文件
配置说明:
presets
: 预设,进行相关语法转义plugins
:插件,补丁转义器,弥补楼上先天不足env
:环境变量{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": [">0.25%", "not ie 11", "not op_mini all"]
},
"exclude": [
"@babel/plugin-transform-async-to-generator",
"@babel/plugin-transform-arrow-functions"
],
"corejs": { "version": "3.8", "proposals": true },
"useBuiltIns": "usage"
}
]
],
"plugins": [
"@babel/transform-runtime",
"@babel/plugin-proposal-class-properties"
],
"env": {
"test": {
"plugins": ["istanbul"]
}
}
}
可以看下我之前写的关于babel的一篇文章- Babel:下一代Javascript语法编译器
一般来讲有其配置文件,也会有其配置忽略文件, 比如`.prettierrc
和.prettierignore
, 其它的读者自行触类旁通,然后配置的文件格式也有很多种,比如说json文件,js文件,rc结尾的文件等等, 这里纯粹是个人习惯, 笔者一般是用 .xxxrc
这里笔者以手动挡开头,我觉得非常应景和带感。与之对应的便是自动挡智能格式化。举个例子吧,比如你选择边打边格式化,未免也太浪费资源了,而且可能它格式化的会和你当时的想法有冲突。所以每次按下CTRL + S
进行格式化的话,是一个很好的方案。它就好比开车,停车的话,挂空挡,拉手刹,下车干饭,是一气呵成的,那个CTRL + S
就好比驾驶员手握的挂挡器,带感。什么?刹车失灵?不存在的,阿Sir!!!
来看一下效果:
vscode-code-format
前面我们提到的是我们在平时开发中,对于单个文件的代码规范手段,那么对于整个项目,我们应该在每次提交前再去检查确认一遍,这样子我们提交到远程的代码才有保障。细心的同学可能已经发现了,是的,在文章开头讲到的package.json
中可以配置husky
和lint-staged
去做这件事。husky
做提交前的检查, 而lint-staged
则优化了检查的范围是要提交检查的,从而加快速度提高效率。
由于husky和lint-staged的版本不同配置也不同,这里笔者用的是最新的配置,具体的参考了这位国际友人的文章https://qiita.com/sprout2000/items/29e8a637dda259bab26d
我这里的话, 就是在每次提交的时候对js、ts等文件进行eslint和prettier格式化,配置如下:
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,ts,jsx,tsx}": [
"eslint . --fix",
]
}
来看一下效果:
husky-lint-staged
这里为了让大家更明显直观看到效果,笔者没有加prettier格式化那一句在lint-staged里面,后续加上后,关于格式的问题会被自动修复, 如下
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,ts,jsx,tsx}": [
"eslint . --fix",
"prettier --config .prettierrc --write ."
]
}
效果如下
root@ccb5f768c839:/home/coder/utils# git commit -m "test husky and lint-staged"
> @ataola/utils@0.1.5 lint-staged
> lint-staged
✔ Preparing...
✔ Running tasks...
✔ Applying modifications...
✔ Cleaning up...
[main 0e5f4d3] sadasd
1 file changed, 2 insertions(+), 1 deletion(-)
root@ccb5f768c839:/home/coder/utils#
目录说明:
LICENSE
: 授权文件README.md
: 说明文件coverage
: 代码覆盖率文件夹docs
: 文档文件夹img
: 图片文件夹index.js
: 入口文件log
: 日志文件夹node_modules
: 安装的npm依赖文件夹package-lock.json
: npm的配置文件锁package.json
: npm的配置文件pull
: 拉取远程github仓库的脚本push
: 上传远程github仓库以及npm发包的脚本test
: 单元测试文件夹➜ utils git:(main) tree -L 1
.
├── LICENSE
├── README.md
├── coverage
├── docs
├── img
├── index.js
├── karma.conf.js
├── lib
├── logs
├── node_modules
├── package-lock.json
├── package.json
├── pull
├── push
└── test
7 directories, 8 files
➜ utils git:(main)
项目命名规范建议:
应该使其文件或文件名命名具有语意,不会你就翻字典
推荐 城市 city
鄙视 城市 chengshi
严重鄙视 城市 cs
要么用命名缩写,要么用全名,建议全名用复数形式
因为img是image的缩写,你再加个s就没有啥语意了,而全名的images表示图片,这里可能有读者会钻牛角尖,你那个docs
不是和楼上冲突了吗?不是的, doc英文单词是文档,docs是其复数形式, 这要和document区分开。
推荐缩写 img
不推荐缩写 imgs
推荐全写 images
不推荐全写 image
我们可以通过tree
命令去查看项目文件结构,-L
表示深度层数, mac用户可以通过brew install tree
安装,ubuntu用户可以通过apt-get install tree -y
安装,centos用户可以通过yum install tree -y
安装,window用户请下载相关tree包并配置到path环境变量里去, 或者去搜下window下的包管理命令`
努力做两件事:
我们先思考下,在git工作流中,有这样三个概念, 萌萌哒的我, 远程仓库,本地仓库。那,以这三个概念造句子,可以这么玩。萌萌哒的我爽朗地把本地仓库推向了远程仓库,远程仓库被萌萌哒的我潇洒地拉到了本地仓库。是的,这个在生活中有很形象的例子,还是萌萌哒的我饥馋碌碌地推开肯德基的大门去干饭,半个小时过去了,满怀滋润的我拉开了肯德基的大门扬长而去。综上所述,我们大致可以概括出两个行为,推(push)和拉(pull),好上脚本。
#!/usr/bin/env bash
set -e
function git_branch_name() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'
}
function e() {
echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
printf "┃$(tput bold) %-40s $(tput sgr0)┃\n" "$*"
echo "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
"$@"
}
CURRENT_BRANCH=$(git_branch_name)
if [[ $CURRENT_BRANCH = feature/* ]]; then
e git stash
e git checkout main
e git pull
e git checkout "$CURRENT_BRANCH"
e git merge main
e git push
e git stash pop || true
elif [[ $CURRENT_BRANCH = main ]]; then
e git stash
e git push
e nrm use npm
e npm publish --access public
e nrm use taobao
e git stash pop || true
fi
在push这个行为上,我们需要考虑两点。第一,远程代码有更新吗?跟我本地会有冲突吗?第二,我当前是在哪个分支,我代码才刚写到一半,我不想提交这么办?git stash
就是将你当前的代码改动存入暂缓区,使得其恢复上一次提交的状态,这个时候你从远程拉下来代码,再去merge
下,然后你执行git stash pop
,git checkout
是切换分支。
上面代码的意思是,如果我是在某个特性分支,那么就先把我目前的改动存入暂缓区,然后切到主分支main,去拉取远程代码,然后切回我当前的分支,再去对主分支进行merge
,然后执行push
,最后再把我的改动从暂缓区拿出来,然后就可以继续开发了。如果我当前是主分支,那太开心了,先把当前改动存入暂缓区,然后直接push,再来个npm发包,然后把当前改动弹出来。
#!/usr/bin/env bash
set -e
function git_branch_name() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'
}
function e() {
echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
printf "┃$(tput bold) %-40s $(tput sgr0)┃\n" "$*"
echo "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
"$@"
}
CURRENT_BRANCH=$(git_branch_name)
if [[ $CURRENT_BRANCH = feature/* ]]; then
e git stash
e git checkout main
e git pull
e git checkout "$CURRENT_BRANCH"
e git merge main
e git stash pop || true
elif [[ $CURRENT_BRANCH = main ]]; then
e git stash
e git pull
e git stash pop || true
fi
这个pull和上面的push类似的,就不赘述了,读者照着楼上的push去理解下pull吧。
为什么会有这个想法呢?由于不可描述的原因,墙对于天朝开发者来说始终是一个神秘的存在,当我们在使用GitHub的时候,有时会遇到DNS污染,有时可能是墙的问题,总之就是提交也很难提交上去,拉也拉不下来。特别是在自己的云服务器上去拉GitHub上的代码,等的花儿都谢了,算了放弃吧,先走为敬。这个时候码云是个神奇的存在,用它拉取代码速度是相当的快,于是我蠢蠢欲动地加了一个码云的remote,直接提交到码云了,这样子一个好处是,我本地就起一份代码就好了,用不着同一个项目搞两份代码。嗯,remote真香!!!
下面是我添加码云的remote地址,然后把它上传到码云的步骤:
git remote add gitee https://gitee.com/taoge2021/utils.git
git fetch gitee
git checkout -b gitee-main gitee/main
git merge main
git push gitee main
这个其实在楼上代码已经有所体现了,这里简单讲下就是,你先去https://www.npmjs.com/
去注册一个账号,然后本地npm login
去登陆这个账号,如果你想发布一个形如@ataola/utils
的包,那么执行npm publish --access public
, 如果你不想的话npm publish
就可以了。
注意:发包的时候不要切到淘宝源,是在npm源上提交,可以通过 npm config set registry作转化, 也可以用nrm这个包作源的管理
努力做三件事:
做测试的技术选型搭配其实有很多,我这里用到楼上这三位。是这样子的,因为我这个库定义是给前端用的,后续会涉及到一些DOM,BOM等等的相关测试,我期望它是真的开了个浏览器去测试我的代码。而Karma这个测试运行器它可以做到这点,而且它还是开源的。mocha是比较有名的测试框架,后面的chai是用来作断言的。
karam的配置创建可以看下package.json
里面我配置的script脚本
"karma:init": "karma init ./karma.conf.js",
"karma:test": "karma start ./karma.conf.js",
npm run karma:init
表示创建一个karma的配置文件,而npm run karma:test
表示启动karma相关测试。
附上一份karma.conf.js
, 由于配置较多,这里如果默认生成的话,大部分都不需要你动,就挑几个讲下,具体的还是要去看官方文档的http://karma-runner.github.io/6.3/config/configuration-file.html
framework
: 表示你装的一些框架plugins
: 故名思义,装的插件files
: 表示要加载浏览器的文件preprocessors
: 一些预处理操作browsers
: 可提供的浏览器webpack
: 暴露的webpack配置接口mochaReporter
:暴露的mocha配置接口// Karma configuration
// Generated on Sat Apr 10 2021 00:13:46 GMT+0800 (中国标准时间)
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha', 'chai', 'webpack'],
plugins: [
'karma-chrome-launcher',
'karma-mocha',
'karma-mocha-reporter',
'karma-chai',
'karma-webpack',
'karma-coverage',
],
// list of files / patterns to load in the browser
// test all
files: ['lib/**/*.js', 'test/**/*.js'],
// test single file
// files: ['test/**/judge.test.js'],
// list of files / patterns to exclude
exclude: [],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
// test all
preprocessors: {
'lib/**/*.js': ['webpack', 'coverage'],
'test/**/*.js': ['webpack'],
},
// test single file
// preprocessors: {
// 'test/**/judge.test.js': ['webpack'],
// },
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// https://github.com/litixsoft/karma-mocha-reporter
reporters: ['mocha', 'coverage'],
mochaReporter: {
colors: {
success: 'blue',
info: 'bgGreen',
warning: 'cyan',
error: 'bgRed',
},
symbols: {
success: '+',
info: '#',
warning: '!',
error: 'x',
},
output: 'autowatch',
showDiff: true,
divider: '',
},
coverageReporter: {
dir: 'coverage/',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text', subdir: '.', file: 'text.txt' },
{ type: 'text-summary', subdir: '.', file: 'text-summary.txt' },
],
},
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome', 'ChromeHeadless', 'ChromeHeadlessNoSandbox'],
// you can define custom flags
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
flags: ['--no-sandbox'],
},
},
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: !!process.env.CI,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity,
webpack: {
mode: 'development',
// entry: ['@babel/polyfill'],
// entry: ['./index.js'],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
corejs: { version: '3.8', proposals: true },
useBuiltIns: 'usage',
},
],
],
plugins: ['istanbul'],
},
},
},
],
},
},
});
};
这里如果我是写Node的话,我会用jest,因为配置会简单些。具体的读者可以阅读下我之前写的文章使用jest进行单元测试, 附上一个完整实战的例子,这个是我刷leetcode做的单元测试的项目地址,https://github.com/ataola/coding
travis是做持续集成的,贴一份笔者的配置,需要注意的是,版本的不同可能配置也不太一样,具体的还是要去看官方文档https://docs.travis-ci.com/
language: node_js
node_js: stable
notifications:
email:
recipients:
- zjt613@gmail.com
on_success: change
on_failure: always
branches:
only:
- main
cache:
apt: true
directories:
- node_modules
os: linux
# https://docs.travis-ci.com/user/reference/overview/
dist: xenial
addons:
chrome: stable
services:
- xvfb
sudo: required
# turn off the clone of submodules for change the SSH to HTTPS in .gitmodules to avoid the error
git:
submodules: false
before_install:
- 'export DISPLAY=:99.0'
- sleep 3 # give xvfb some time to start
- '/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16'
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
install:
- npm set progress=false
- npm install
script:
- npm run karma:test
after_script:
- npm run codecov
codecov是做代码覆盖率测试的, 执行npm install codecov -D
去安装它,然后在packge.json
里面配置好script就好了"codecov": "codecov"
, 我们在做持续集成的时候,最下面在执行完相关karma测试后,最后会执行npm run codecov
去读取 coverage
目录中的 lcov.info
文件,然后上传到 Codecov 网站
测试这块做了这么多工作,其实就是当了一回场面人,在仓库首页给它一个特写,这里加了travis持续集成的构建结果和codecov的代码覆盖率以增加项目的可信度和逼格。
unit test
思考两件事:
为什么会有这个问题,也还是源自生活中遇到的事。笔者最开始为了一步到位,将相关的prettier、eslint等等的相关配置都写到了全局的,也就是user下面,后来在拉取项目的时候发现,很多时候特别是多人开发,由于eslint和prettier的配置不一样,或者根本就没有这块的配置,导致代码堆积如屎山难以维护,这促使我有了进一步的思考是,区分编辑器的共性和不同。举个例子,比如说terminal这个插件,它其实可以配置调节在终端光标的粗细,我就不是很喜欢那种肥肥的光标,就把它改成line
,这种是属于不同,是你的个性,不会因为说你设置了这个会影响到整个项目,别人电脑里没设置还是肥肥的光标。那么什么是共性,就比如最开始笔者说的将prettier、eslint配置到全局的做法,违背了共性,这里需要说明的一点是,违背不代表我是错的,在这件事情上没有对错,大环境决定的,如果一只队伍里大家都认为 1 + 1 =3,那么即使你认为1 + 1 = 2,从大局上考虑,这里就姑且迁就下1 + 1 = 3吧,你可以沉默不说话,但你心里要有你坚定的真理的答案,这个叫站队。
具体的解决方案我认为是,你可以在全局里去配置以那种方式去做一件事,但是具体的规则和形式需要单独拎出来,不能写全局里面。可以新建一个.vscode
文件夹,然后在这个项目里面单独配置,结合.prettierrc
、.eslintrc
等,可以参考下这个项目https://github.com/ataola/coding
vscode
笔者以前也是个使用插件狂魔,总喜欢去试试倒腾这个插件那个插件好不好使好不好玩,再后来我那个 多年前买的window不堪重负萎靡不振,我就没有这个想法了,插件只是个辅助工具,根据使用频繁度和实用性去考量吧,老罗有句话说得好, 又不是不能用?
又不是不能用
关于处理url参数转成对象的格式,这个是前端开发面试的常考题,因为它实用性强,涉及基础的数组字符串处理,答案还不唯一,所以这里笔者抛砖引玉,就以它为例子去讲吧。
getQueryParameters
如果对正则不熟悉的话,这里可以用字符串分割分割再分割来做,具体的如下
/**
*
* @param {string} url
* @returns {object}
*/
function getQueryParameters(url) {
const paramStr = decodeURIComponent(url).split('?')[1];
if (!paramStr) {
return {};
}
const paramArr = paramStr.split('&');
const res = {};
paramArr.forEach((param) => {
const [key, value] = param.split('=');
res[key] = value;
});
return res;
}
相关测试
import { expect } from 'chai';
import { getQueryParameters } from '../lib/url';
describe('lib: url test', function () {
it('getQueryParameters: expect { name: "ataola", age: "24" } when call function with params "https://zhengjiangtao.cn?name=ataola&age=24"', function () {
expect(
getQueryParameters('https://zhengjiangtao.cn?name=ataola&age=24')
).to.deep.equals({ name: 'ataola', age: '24' });
});
});
});
至此,笔者已经向读者们介绍了一个前端项目从有想法到去实践再到总结分享的心路历程。谢谢大家的赏脸阅读,谈起为什么写这个项目,第一是项目做多了,自然而然就会有些想法,明人不说暗话我想偷点懒划水,想早点下班哇,所以工作之余就勤快点把平时工作或者刷题常用到的总结整理下,打磨成一把瑞士军刀,提高战斗力;第二是像我们搞程序的,都挺单纯的,有句话说得好”no BB, show me the code!“,可能不是很会表达自己吧,那就上代码吧,希望面试官看了能够加点印象分或者综合得分,哈哈。
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/SyefO9NJ2ajU6Xb2GDB3Vg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。