# js-lib-quick-start **Repository Path**: DoLaMi/js-lib-quick-start ## Basic Information - **Project Name**: js-lib-quick-start - **Description**: 一个可以让你快速开始开发js库的工程 - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-09-06 - **Last Updated**: 2021-10-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: JavaScript, Library, js-library ## README ### 简介 > 一个可以让你快速开始开发 js 库的工程 1. 提供两个版本,压缩版和未压缩版,即 xxxx.js、xxxx.min.js 2. 加入单元测试 jest,快速验证代码的正确性,大大减少和测试妹子亲密接触的机会,真好! 3. 代码规范检测 eslint,统一代码风格,看起来比较顺眼! 4. JsDoc 快速生成 Api 文档,清晰的 api 文档,提桶跑路时方便! 5. 参考文章 https://juejin.cn/post/6844903541509718030#heading-1 6. rollup 参考文章 https://www.cnblogs.com/tugenhua0707/p/8179686.html ### 起步 - 初始化项目即新建 package.json ``` npm init ``` - 创建必要的目录,比例 src、example、.build - 创建 git 忽略文件 .gitignore ``` .DS_Store build/ node_modules/ thumbs.db !.gitkeep ``` ```项目结构 js-lib-quick-start ├─ .babelrc ├─ .eslintignore ├─ .eslintrc.js ├─ .gitignore ├─ api_docs 保存生成api文档静态页面目录 ├─ config 项目配置 │ ├─ jsdoc.config.js │ ├─ lib.config.js │ └─ rollup.config.js ├─ dist │ ├─ jutils.js │ ├─ jutils.min.js │ └─ jutils.min.js.map ├─ package-lock.json ├─ package.json ├─ README.md ├─ src 源码 │ ├─ CommonUtil.js │ ├─ FileUtil.js │ ├─ HttpUtil.js │ └─ JUtils.js └─ test 单元测试文件 └─ jutils.test.js ``` - 安装 rollup ``` npm i -g rollup ``` - 创建 rollup.config.js ```js export default { input: './src/JUtils.js', output: { file: './dist/JUtils.js', format: 'cjs' } } ``` - 编译 js ``` rollup -c rollup -c -w 编译并观察js是否变化,如果变化会实时进行编译 ``` - 安装 rollup 工具集 https://www.rollupjs.com/guide/tools - 安装压缩 js 库,rollup-plugin-uglify 只支持 es5 的压缩 ``` npm i rollup-plugin-uglify -D ``` - 安装 rollup-plugin-terser 压缩 js ``` npm i rollup-plugin-terser -D ``` - 安装 babel 转换 es5 rollup 官网使用的 babel 是 6 版本,直接安装会是 7 版本,所以要将.babelrc 修改 ``` npm i -D babel-preset-latest babel-plugin-external-helpers npm i -D rollup-plugin-babel ``` - 创建.babelrc ``` { "presets": [ ["latest", { "es2015": { "modules": false } }] ], "plugins": ["external-helpers"] } ``` - 安装 babel7 转换 es5 ``` npm i -D babel-preset-latest babel-plugin-external-helpers npm i -D rollup-plugin-babel npm i -S core-js@3 npm i -D @babel/preset-env npm i -D @babel/core@7 ``` - 创建.babelrc ``` { "presets": [ [ "@babel/env", { "modules": false, "useBuiltIns": "usage", "corejs": "3" // 新增 } ] ] } ``` - 安装 del 库,用于删除缓存文件(最重要的是它可以同步执行) ```bash npm i del -D # 删除dist目录 # del.sync(['dist/*']) ``` ### 编译 - npm i 安装依赖 - npm run build 编译出 js,包含未压缩版和压缩版 ### JsDoc 生成 API 文档 - 安装 ``` npm i -D jsdoc ``` - package.json 添加脚本运行命令 ``` "build:docs":"jsdoc -c ./.jsdoc.config.js -R README.md" ``` - 创建配置文件 ``` 'use strict' module.exports = { tags: { allowUnknownTags: true }, source: { include: ['src/'], exclude: [], includePattern: '.+\\.js(doc|x)?$', excludePattern: '(^|\\/|\\\\)_' }, plugins: [], recurseDepth: 10, templates: { cleverLinks: false, monospaceLinks: false, default: { outputSourceFiles: true } } } ``` - 执行(没有安装到全局) ``` npm run build:docs ``` - 设置文档生成模板 - [安装 minami](https://github.com/Nijikokun/minami) ``` npm i -D minami ``` - minami 模板配置,根目录创建配置文件.jsdoc.config.js ```json { "tags": { "allowUnknownTags": true, "dictionaries": ["jsdoc"] }, "source": { "include": ["lib", "package.json", "README.md"], "includePattern": ".js$", "excludePattern": "(node_modules/|docs)" }, "plugins": ["plugins/markdown"], "templates": { "cleverLinks": false, "monospaceLinks": true, "useLongnameInNav": false, "showInheritedInNav": true }, "opts": { "destination": "./docs/", "encoding": "utf8", "private": true, "recurse": true, "template": "./node_modules/minami" } } ``` - #### [jsDoc 标签](https://jsdoc.zcopy.site/) - @namespace 标签指明对象是一个命名空间 ``` @namespace MyNamespace ``` - @async 标签指明函数是一个异步函数 ``` @async ``` - @augments 指明标识符继承自哪个父类 ``` @augments ``` - @class 标签 (同义词: JSDoc @constructor 标签) 此函数旨在需要使用"new"关键字调用,即构造函数 ``` @class [ ] ``` - @example 提供一个如何使用描述项的例子 ``` @example globalNS.method(5, 15); ``` - @memberof 标签标明成员隶属于哪一个父级标识符。 ``` @memberof ``` - @override 标签指明一个标识符覆盖其父类同名的标识符 - @param 标签 (同义词: JSDoc @arg 标签, JSDoc @argument 标签) 记录传递给一个函数的参数。 ``` @param {string} somebody Somebody's name. ``` - @property 标签 (同义词: JSDoc @prop 标签) 记录一个对象的属性。 ```js /** * @namespace * @property {object} defaults - The default values for parties. * @property {number} defaults.players - The default number of players. * @property {string} defaults.level - The default level for the party. * @property {object} defaults.treasure - The default treasure. * @property {number} defaults.treasure.gold - How much gold the party starts with. */ var config = { defaults: { players: 1, level: 'beginner', treasure: { gold: 0 } } } ``` - @private 标记标识符为私有。 - @protected 标记标识符为受保护的。 - @public 标记为公开的。 - @readonly 标记一个标识符为只读 - @returns 标签 (同义词: JSDoc @return 标签)描述一个函数的返回值 ``` @returns [{type}] [description] ``` - @version 指明被用于表示该项的版本 - @static 记录一个静态成员 - @since 标签标明一个类,方法,或其它标识符是在哪个特定版本开始添加进来的 - @see 指明可以参考另一个标识符的说明文档,或者一个外部资源 ``` @see @see /** * @see {@link foo} for further information. * @see {@link http://github.com|GitHub} */ ``` ### eslint 代码规范检测 - npm i -D rollup-plugin-eslint - 添加.eslintrc.js 配置 ```js module.exports = { root: true, // 此项是用来指定javaScript语言类型和风格,sourceType用来指定js导入的方式,默认是script,此处设置为module,指某块导入方式 parserOptions: { sourceType: 'module' }, // 此项指定环境的全局变量,下面的配置指定为node、浏览器环境 env: { browser: true, es6: true, node: true }, extends: 'eslint:recommended', globals: { __static: true }, // rules是用来设置从插件来的规范代码的规则,使用必须去掉前缀eslint-plugin- rules: { // 在所有其他情况下,箭头函数参数必须包含在括号 0关闭 1开启 'arrow-parens': 0, // 该规则旨在强化*发生器功能的间距 'generator-star-spacing': 0, // 括弧和函数名之间存在空格的配置,0:关闭,1:报警告,2:报错退出 'space-before-function-paren': 0, // 正则匹配转义检测 'no-useless-escape': 0, // 注释符号前的时候一定要有空格 // xxxx 'spaced-comment': 0, // 一定要使用全等 === eqeqeq: 0, // 缩进4个空格 // "indent": ["error", 4], indent: 0, // 禁止块空白行 'padded-blocks': 0, // 此规则在非空文件的末尾强制执行至少一个换行符(或没有换行符) 'eol-last': 0, // 对未声明变量的任何引用都会导致警告 new Object() // 'no-undef': 1, // 如果未指定,ES2015 会提供一个默认的类构造函数。因此,没有必要提供一个空的构造函数或一个简单地委托给其父类的构造函数 'no-useless-constructor': 1, // debugger 生产环境不允许使用 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 } } ``` - eslint 脚本 ```bash # 检查src目录下的脚本是否符合规范 npm run lint # 修复不符合eslint规范的行为,注意会修改你本地的文件 # 如果只想修复布局,在命package.json加上,--fix-type layout npm run lint:fix ``` ### jest 单元测试 - [官网](https://www.jestjs.cn/docs/getting-started) - 安装 jest ``` // @types/jest ts代码提示库 npm i -D jest @types/jest ``` - 编写 package.json 命令 ``` "jest": "jest" ``` - jest 暂时不支持 ES5 的 import 方式导入模块,该功能还处于实验阶段,尽量使用 require - 如果想要支持 ES Module 可以使用,[rollup-jest](https://zhuanlan.zhihu.com/p/55306830) ```bash # install npm i - D rollup-jest # config package.json "jest": { "preset": "rollup-jest" } ``` - ##### 开始测试 - 需要创建一个 xxx.test.js 的文件,执行测试命令 jest 会自动的找到该文件进行测试 - 例如:JUtils.js,测试文件 JUtils.test.js - 运行 npm run jest - 命令行配置 ``` "test": "jest" : 这个比较傻瓜式,当执行npm run test这条命令是会去对test目录下的所有文件进行相应的jest测试。 "test:help": "jest --help": 顾名思义,如果你不想全局安装jest,又想看看到底有哪些cli命令的话,就它了。 "test:debug": "jest --debug": 顾名思义,debug啊。 "test:verbose": "jest --verbose": 以层级显示地方式在控制台展示测试结果。 "test:noCache": "jest --no-cache": 顾名思义,就是设置有没有缓存,有缓存的话会快点。 "test:init": "jest --init": 执行这句就是在根目录创建一个jest.config.js文件,它在创建的时候有很多选择项给你的。 "test:caculator": "jest ./test/caculator.test.js": 单文件测试。 "test:caculator:watch": "jest ./test/caculator.test.js --watch": 单文件监视测试 "test:watchAll": "jest --watchAll": 监视所有文件改动,测试相应的测试。 ``` - 建议 - 只考虑测试,不考虑内部实现 - 充分考虑数据的边界条件 - TDD 就是先写测试,后写功能实现,全局的视角来看待需求 - 所有的方法都应该写单元测试 ### rollup 配置 - https://www.rollupjs.com/guide/introduction - 相对 webpack 代码更简洁 - 但是,如果使用 class 经过 rollup-plugin-babel 的转换后,直接使用的话,在 IDE 上会缺失代码提示 ### rollup 有用的库 - rollup-plugin-polyfill-node 关于 nodejs 模块的 - rollup-plugin-terser js 压缩 - rollup-plugin-babel 转换为 es5,最基本的 es6 语法转化器。这里可以进行 bable 的基本配置 - rollup-plugin-node-resolve rollup 无法识别 node_modules 中的包,需要安装该插件 - rollup-plugin-commonjs node_modules 中的包大部分都是 commonjs 格式的,要在 rollup 中使用必须先转为 ES6 语法,它会将 CommonJS 模块转换为 ES6 来为 Rollup 获得兼容。 - rollup-plugin-json 通过该插件可以从 JSON 文件中导入数据(比如:package.json) ```js import { version } from '../package.json' export default function () { console.log('current version is ' + version) } ``` ### 常见问题 - Node 工程 使用 import 出现以下错误 internal/process/esm_loader.js:74 internalBinding('errors').triggerUncaughtException( ```bash 把 import firstName from './testExports' 改为: import firstName from './testExports.js' ``` - node 无法执行 import 的 es6 形式导入模块 ``` # 在package.json加上 "type": "module" ```