在业务中多个项目需要用到公共组件库,所以决定封装组件库上传到npm私服管理,使用到的技术是react+webpack+ts+antd+less
讲在前面的话:这篇文章呢只是初稿的一些记录,代码只满足基本的功能,如果这篇文章对你有点作用的话,请在该基础上增加需要的内容,以及适当的优化,包括压缩、代码分割等功能。后面我会记录一些关于lerna管理多个包的相关文章,与大家分享
1.文件结构目录
|-- @lxy/demo
|-- .babelrc //babel配置
|-- .gitignore
|-- README.md
|-- package-lock.json
|-- package.json
|-- tsconfig.json //ts编译配置
|-- yarn.lock
|-- config //webpack配置文件
| |-- webpack.dev.config.js
| |-- webpack.prod.config.js
|-- example //测试demo,本地运行yarn start
| |-- app.js // start 的入口文件
| |-- index.html
| |-- TestDemo //组件的调试源码
|-- lib //打包输出文件
|-- src // 组件源码
|-- index.tsx // 组件入口
|-- types
|-- components
|-- Tag
|-- index.less
|-- index.tsx
2.package.json
针对该文件重点说明一下以下重要字段:
-
name:上传到npm的包名
-
version:上传所需的版本号
-
main:打包之后输出的js路径
-
types:打包输出的d.ts路径
-
files:需要上传到npm的文件,此处lib是我打包输出的文件
-
peerDependencies:关于这个字段我就不做过多的叙述,可参考这篇文章
{ "name": "@tech/components", "version": "0.0.1", "author": "风控开发部", "description": "风控开发部组件", "main": "lib/bundle.js", "types": "lib/index.d.ts", "files": [ "lib" ], "keywords": [], "license": "ISC", "scripts": { "start": "webpack-dev-server --config ./config/webpack.dev.config.js", "build": "webpack --config ./config/webpack.prod.config.js" }, "dependencies": { "@ant-design/icons": "^4.6.3", "@ant-design/pro-form": "^1.42.0", "@ant-design/pro-layout": "^6.5.0", "@ant-design/pro-table": "^2.49.0" }, "devDependencies": { "react": "^16.4.0", "react-dom": "^16.4.0", "antd": "^4.16.13", "webpack-dev-server": "^3.11.2", "html-webpack-plugin": "^4.5.1", "mini-css-extract-plugin": "^2.4.4", "webpack": "^5.18.0", "webpack-cli": "^3.3.12", "webpack-node-externals": "1.6.0", "@babel/core": "^7.2.2", "@babel/preset-env": "7.2.3", "@babel/preset-react": "7.0.0", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "babel-loader": "8.0.5", "ts-import-plugin": "^1.6.7", "css-loader": "0.28.9", "file-loader": "^6.2.0", "less": "^3.11.1", "less-loader": "^5.0.0", "style-loader": "0.19.1", "ts-loader": "~8.2.0", "typescript": "^4.4.4", "url-loader": "^4.1.1" }, "peerDependencies": { "antd":"4.x", "react": ">=16.9.0", "react-dom": ">=16.9.0" } }
3. .babelrc文件配置
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
4.config文件配置
webpack.dev.config.js目的为了能够边预览边开发,及时响应效果
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const tsImportPluginFactory = require('ts-import-plugin')
module.exports = {
mode: "development",
entry: path.join(__dirname, "../example/app"), // 项目入口,处理资源文件的依赖关系
output: {
filename: "bundle.js",
path: path.join(__dirname, "../lib"),
},
module: {
rules: [
{
test: /\.(jsx|tsx|js|ts)$/,
loader: 'ts-loader',
options: {
transpileOnly: true,
getCustomTransformers: () => ({
before: [tsImportPluginFactory(
[{
libraryName:'antd',
style:true
}])]
}),
compilerOptions: {
module: 'es2015'
}
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
{
loader: "less-loader",
options: {
javascriptEnabled: true,
},
},
],
},
{
test: /\.svg$/,
use: ["file-loader"],
},
],
},
plugins: [
new htmlWebpackPlugin({
filename: "index.html",
template: path.join(__dirname, "../example/index.html"),
}),
],
devServer: {
contentBase: path.join(__dirname, "../lib"),
compress: true,
port: 3002,
open: true,
},
resolve: {
//后缀名自动补全,引入时可不必写后缀名
extensions: [".ts", ".tsx", ".js", ".jsx"],
},
};
webpack.prod.config.js
const path = require("path");
const nodeExternals = require("webpack-node-externals");
const tsImportPluginFactory = require('ts-import-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "production",
entry: path.join(__dirname, "../src/index.tsx"),
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "../lib"),
library: "laputarednerer",
libraryTarget: "umd",
},
module: {
rules: [
{
test: /\.(jsx|tsx|js|ts)$/,
loader: 'ts-loader',
options: {
getCustomTransformers: () => ({
before: [tsImportPluginFactory(
[{
libraryName:'antd',
style:true
}])]
}),
compilerOptions: {
module: 'es2015'
}
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
{
loader: "less-loader",
options: {
javascriptEnabled: true,
},
},
],
},
{
test: /\.svg$/,
use: ["url-loader"],
},
],
},
plugins: [
new MiniCssExtractPlugin(),
],
resolve: {
//后缀名自动补全,引入时可不必写后缀名
extensions: [".ts", ".tsx", ".js", ".jsx", ".less", ".css"],
},
externals: ["react", "react-dom", "antd", nodeExternals()],
};
5.tsconfig.js
{
"compilerOptions": {
"outDir": "./lib",
"target": "ES2015",
"module": "CommonJS",
"esModuleInterop": true,
"declaration": true,
"jsx": "react",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
},
"include": [
"src/**/*",
],
"exclude": [
"node_modules"
],
}
6.src正文内容
src下新建index.ts
import Tag from "./components/Tag";
export { Tag };
src/components下新建Tag文件:内容根据自己的业务实现,这里就不写了
src/types下新增externals.d.ts,处理less文件未定义问题
declare module '*.less'
declare module '*.png';
declare module '*.svg' {
export function ReactComponent(
props: React.SVGProps<SVGSVGElement>,
): React.ReactElement;
const url: string;
export default url;
}
以上基本的配置完成,需注意
-
import styles1 from './index.less' 以该方式引入less,样式文件如下:
.aa{ color:red } -
import './index.less'以该方式引入less,样式文件如下:
:global{ .aa{ color:red } }
7.example预览文件配置
example/index.js
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import { Tag } from '../src/index';
const Test = () => {
return (
<div>
<h1>本地测试组件</h1>
<Tag />
</div>
);
};
ReactDOM.render(<Test />, document.getElementById("root"));
example/index.html
<html>
<head>
<title>My Component Demo</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>
以上基本的组件库配置几经完毕,如果过程中觉得认为可以优化的电可以自行优化,或者有不懂的细节处可以请一定要弄明白
打包执行yarn build 会发现出现一个lib文件,为了减少上传npm的次数,我们可以先在本地进行测试。打完包执行npm link,在需要的使用组件库的项目下执行npm link @lxy/dmeo
使用组件的方法如下:
import {Tag} form "@lxy/demo"
import "@lxy/demo/lib/style/main.css"
然后之后正常使用组件,观察组件即可
8.npm上传
-
切换镜像,可以学习一下nrm管理镜像
npm set registry 你的镜像地址
-
先申请npm账号,控制台输入,依次填入相关信息
npm adduser --registry 你的镜像地址
可以通过npm whoami查看当前登录的账户
-
上传npm
npm publish
上传的时候注意每一个版本号都不能和之前的相同否则上传不上去
- 删除已经上传的包,一定要在当前的npm内网所在的镜像哟~
删除整个包
npm unpublish @lxy/demo -force
删除某个版本
npm unpublish @lxy/demo -force
8.项目中怎么安装依赖中存在公网以及内网的依赖
解决方案一:
npm设置全局处理:npm config set @lxy:registry 你的镜像地址
解决方案二:
文件项目根目录添加.npmrc : @lxy:registry 你的镜像地址
上面的意思是遇到以@lxy开始的依赖都去你的镜像地址安装
好啦,基本的东西都写完了,从0到1基本的坑都过了一遍,有什么问题欢迎留言指教讨论