1. 前言
学习如何打包发布前端项目,需要学习以下相关知识:
package.json
如何初始化配置,以及学习npm配置项;- 模块类型
type
配置, 这是nodejs的package.json的配置 main
入口文件的配置
- 模块类型
webpack
是一个用于现代 JavaScript 应用程序的 静态模块打包工具,使用说明,推荐阅读;babel-loader
JavaScript通常需要做语法转化和polyfills以便可以使用高级语法而不必担心浏览器兼容性问题,Babel的作用便在于此,而babel-loader正好可以与webpack结合使用;eslint
一个好的项目离不开代码格式规范;jest
一款js测试框架,写好测试用例覆盖测试的功能点,确保软件质量,推荐阅读;
2. 概念区别
Node.js是JavaScript的一种运行环境,是对Google V8引擎进行的封装。是一个服务器端的JavaScript的解释器。npm(Node Package Manager)是nodejs的包管理器。
有一些概念容易混淆,注意区分。
2.1 CommonJS
vs Es module
关于type
的配置值有:
module
可以指示 Node.js 通过使用.cjs
扩展名命名特定文件,将其解释为CommonJS
commonjs
可以通过使用.mjs
扩展名命名特定文件,指示 Node.js 将其解释为ES module
ps:js文件两种类型都能识别。
ES module 更加现代化和灵活,支持动态导入、异步加载、静态作用域等特性,
而 CommonJS 更加简单和适用于早期的 Node.js 环境。
在实际开发中,需要根据具体的项目需求和环境来选择使用哪种模块系统。
看到深入浅出 Commonjs 和 Es Module一文描述的很详细,感兴趣可以详细了解。
2.2 package.json入口main
、module
、brower
总结:其他项目引用时,会根据项目自身的type
来选择定义的lib的入口文件,三个配置的主要区别在于优先级。
一般通常认为browser = browser+mjs > module > browser+cjs > main
推荐阅读入口文件配置的区别一文。
2.3 ES5 vs ES6
- ES5指的是ECMScript的第五个版本,发布于2009年,是目前最广泛使用的JavaScript版本。
- ES6是ECMScript的第六个版本,也成为ES2015,发布于2015年,引入了许多新的语言特性和语法糖。
- ES2015是ES6的官方名称,但是由于ES6引入了太多的新特性,因此人们通常使用ES2015来指代ES6。
推荐阅读ES5和ES6的区别以及ES6常用特性
2.4 webpack config中的 output.library.type
官方使用说明中配置可选项很多,这里介绍:
commonjs
module
umd
统一模块定义,这种模块语法,兼容了以上的CommonJS、AMD、ES Module使用方式,也就是Vue脚手lib模式打包的这种模式,设置改值后,注意globalObject
配置项可设置值为'this'
推荐阅读CommonJS/AMD/UMD/ES Module介绍和区别
3. 项目实战
源码:https://github.com/SkylerHu/js-enum
3.1 目录结构
3.2 主要配置文件
3.2.1 .babelrc
{"presets": ["@babel/preset-env"],"plugins": ["@babel/plugin-proposal-class-properties"]
}
3.2.2 .eslintignore
# node_modules
node_modules/# build
build/# dist
dist/
docs/
3.2.3 .estlintrc.json
{"env": {"browser": true,"es6": true,"mocha": true,"jest": true,"node": true},"globals": {"dashjs": true,"WebKitMediaSource": true,"MediaSource": true,"WebKitMediaKeys": true,"MSMediaKeys": true,"MediaKeys": true},"parser": "babel-eslint","rules": {"no-caller": 2,"no-undef": 2,"no-unused-vars": 2,"no-use-before-define": 0,"object-curly-spacing": ["error", "always"],"strict": 0,"semi": 2,"no-loop-func": 0,"no-multi-spaces": "error","keyword-spacing": ["error",{"before": true,"after": true}],"quotes": ["error","single",{"allowTemplateLiterals": true}],"indent": ["error",2,{"SwitchCase": 1}]},"ignorePatterns": ["releases/**/*"],"overrides": [{"files": ["tests/**/*"],"env": {"jest": true}}]
}
3.2.3 jest.config.json
{"verbose": true,"collectCoverage": true,"coverageDirectory": "./.coverage","moduleFileExtensions": ["js","json"],"testMatch": ["**/tests/**/*.js"],"collectCoverageFrom": ["src/**/*.{js,jsx}","!**/node_modules/**"],"testEnvironment": "node"
}
3.2.2 package.json
{"author": "SkylerHu","name": "js-enumerate","version": "1.0.2","private": false,"type": "module","main": "dist/index.js","files": ["dist"],"publishConfig": {"access": "public","registry": "https://registry.npmjs.org/"},"engines": {"node": "^14.21.3"},"scripts": {"lint": "eslint .","lint:fix": "eslint . --fix","build": "webpack --config webpack.config.js","test": "jest"},"dependencies": {},"devDependencies": {"@babel/core": "^7.24.4","@babel/plugin-proposal-class-properties": "^7.18.6","@babel/preset-env": "^7.24.4","@jest/globals": "^29.7.0","babel-eslint": "^10.1.0","babel-loader": "^9.1.3","clean-webpack-plugin": "^4.0.0","eslint": "7.32.0","eslint-loader": "^4.0.2","identity-obj-proxy": "^3.0.0","jest": "^29.7.0","jest-environment-jsdom": "^29.7.0","webpack": "^5.91.0","webpack-cli": "^5.1.4"},"description": "Enum is a javascript enumeration module. It works with Node.js and the browser.","keywords": ["enum","enumerate","javascript","js-enum","js-enumerate","react-enum","vue-enum"],"homepage": "https://github.com/SkylerHu/js-enum","repository": {"type": "git","url": "git@github.com:SkylerHu/js-enum.git"},"bugs": {"url": "https://github.com/SkylerHu/js-enum/issues"},"license": "MIT"
}
3.2.3 webpack.config.js
import path from 'path';
import { CleanWebpackPlugin } from 'clean-webpack-plugin';import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';const __dirname = dirname(fileURLToPath(import.meta.url));const PATHS = {src: path.join(__dirname, 'src'),build: path.join(__dirname, 'dist'),
};const config = {mode: 'production',// devtool: 'source-map',entry: path.join(PATHS.src, 'index.js'),output: {path: PATHS.build,clean: true,filename: 'index.js',library: {name: 'Enum',type: 'umd', // 采用通用模块定义export: 'default', // 兼容 ES6 的模块系统、CommonJS 和 AMD 模块规范},globalObject: 'this',},resolve: {extensions: ['.js', '.jsx', '.json'],},module: {rules: [{test: /\.(js|jsx)$/,exclude: /node_modules/, //排除node_modules文件夹enforce: 'pre', //提前加载使用use: { //使用eslint-loader解析loader: 'eslint-loader'}},{// 使用 babel-loader 来编译处理 js 和 jsx 文件test: /\.(js|jsx)$/,exclude: /node_modules/,use: {loader: 'babel-loader',},}]},plugins: [new CleanWebpackPlugin(),],optimization: {minimize: true,},
};export default config;
3.3 如何构建发版
- 安装依赖
npm install .
- 代码格式
npm run lint
- 测试用例
npm run test
- 构建
npm run build
- 发版
npm publish
,具体命令可以npm help
查看,也可以查看官方文档- 需要在nodejs.org上注册账号,可以
npm adduser
通过命令行操作; - publish前需要
npm login
登录账号; - 也可以直接
npmrc
配置中配置好账号,或者创建auth_token
配置
- 需要在nodejs.org上注册账号,可以
3.4 其他注意的问题
- 3.4.1
jest
、webpack
版本对node版本
的要求,node版本可以通过nvm
控制;
> jest
./node_modules/jest/node_modules/jest-cli/build/run.js:135if (error?.stack) {^
SyntaxError: Unexpected token '.'
升级node版本解决,该项目使用的node 14+
;
语法标准中,可选链运算符(?.) 要求node版本14+
。
- 3.4.2 Babel编译缺少plugin
> jestFAIL tests/test_enum.js● Test suite failed to runJest encountered an unexpected tokenJest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax....Details:SyntaxError: ./src/index.js: Missing class properties transform.8 |9 | export default class Enum {> 10 | #items = [];| ^^^^^^^^^^^^11 | #config = {};12 | /**13 | *
通过安装和配置@babel/plugin-proposal-class-properties
解决,参考
- 3.4.3 webpack配置output.type为
umd
时,注意配置globalObject: 'this'
;
file:///.../node_modules/js-enumerate/dist/index.js:1
ReferenceError: self is not definedat file:///Users/skyler/Documents/github/test/node_modules/js-enumerate/dist/index.js:1:190at ModuleJob.run (internal/modules/esm/module_job.js:183:25)at async Loader.import (internal/modules/esm/loader.js:178:24)at async Object.loadESM (internal/process/esm_loader.js:68:5)at async handleMainPromise (internal/modules/run_main.js:59:12)
报错参考typescript-webpack-library-generates-referenceerror-self-is-not-defined解决。
- 3.4.4 当nodejs.org上仅publish一个版本,操作
npm unpublish
后,导致无法找寻项目名,24小时内无法再次publish- 具体阅读关于取消版本发布的说明;
npm ERR! code E403
npm ERR! 403 403 Forbidden - PUT https://registry.npmjs.org/js-enumerate - js-enumerate cannot be republished until 24 hours have passed.
npm ERR! 403 In most cases, you or one of your dependencies are requesting
npm ERR! 403 a package version that is forbidden by your security policy.npm ERR! A complete log of this run can be found in:
npm ERR! ./.npm/_logs/2024-04-21T18_27_39_505Z-debug.log