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。
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
json
{
"presets": [
"@babel/preset-env"
],
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}
3.2.2 .eslintignore
text
# node_modules
node_modules/
# build
build/
# dist
dist/
docs/
3.2.3 .estlintrc.json
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
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
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
javascript
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
控制;
shell
> jest
./node_modules/jest/node_modules/jest-cli/build/run.js:135
if (error?.stack) {
^
SyntaxError: Unexpected token '.'
升级node版本解决,该项目使用的node 14+
;
语法标准中,可选链运算符(?.) 要求node版本14+
。
- 3.4.2 Babel编译缺少plugin
shell
> jest
FAIL tests/test_enum.js
● Test suite failed to run
Jest encountered an unexpected token
Jest 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'
;
shell
file:///.../node_modules/js-enumerate/dist/index.js:1
ReferenceError: self is not defined
at file:///Users/skyler/Documents/github/test/node_modules/js-enumerate/dist/index.js:1:190
at 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- 具体阅读关于取消版本发布的说明;
shell
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