文章目录
一、npm发包需要了解的知识(1)判断包名是否合法(2)npm初始化(3)devDependencies、dependencies和peerDependencies二、测试发包三、使用react组件进行npm发包(1)搭建基本组件1. create-react-app直接创建组件即可2. 组件开发(2)基本组件开发1. header组件开发2. leftMenu组件3. base组件(3)主要函数和样式1. getPrefixCls2. Var.scss(4)导出组件四、webpack打包配置(1)yarn eject暴露出配置(2)修改配置1. 添加路径2. 修改webpack(3)修改package.json(4)发包五、测试发布的包(1)下载(2)配置组件1. 引入Base组件2. 在app中引入3. 引入样式(1)搭建基本组件1. create-react-app直接创建组件即可2. 组件开发(2)基本组件开发1. header组件开发2. leftMenu组件3. base组件(3)主要函数和样式1. getPrefixCls2. Var.scss(4)导出组件四、webpack打包配置(1)yarn eject暴露出配置(2)修改配置1. 添加路径2. 修改webpack(3)修改package.json(4)发包五、测试发布的包(1)下载(2)配置组件1. 引入Base组件2. 在app中引入3. 引入样式一、npm发包需要了解的知识
(1)判断包名是否合法
这个可以去npm官网上搜索/,也可以直接install看看,是否存在
(2)npm初始化
创建一个文件夹,我在这里取名为welkin-test
cd welkin-testnpm init
需要注意的是,需要和package.json的name对应
{"name": "welkin-test","version": "1.0.0", //这个分别代表 主版本号.次版本号.修订号"description": "基本布局",}
针对version简单介绍一下:
主版本号:不兼容的api修改次版本号:做了向下兼容的功能性新增修订号:向下兼容的问题修正
(3)devDependencies、dependencies和peerDependencies
devDependencies:开发环境所需要的依赖,使用-D;dependencies:生产环境需要的依赖,不仅生产环境使用,开发环境也可以使用;peerDependencies:进行插件开发会用到,在这里指定的依赖,是为了避免重复下载。首先会在本地进行查找是否存在,如不存在进行安装二、测试发包
在welkin-test里面新建index.js,随便写上一个函数
const a = () => {return a * 5;};export default a;
然后,npm publish
,终端上没有报错就发布成功了
使用create-react-app 创建一个测试项目,将我们刚刚发布的包下载下来看看里面的内容是不是我们写的a函数,如果是那就正常
三、使用react组件进行npm发包
我这里是准备发布一个基本组件,样式和结构如下所示:【antd里也有:https://ant.design/components/layout-cn/】
红色框内的就是准备自己开发的
(1)搭建基本组件
1. create-react-app直接创建组件即可
create-react-app xxxxxx --template typescript
2. 组件开发
其实可以先开发出自己的组件然后再分析,以我的为例子,主要分为三块组件:
header:主要是顶部,涉及到logo,title,username以及logoutUrl;leftMenu:菜单信息;涉及到渲染的菜单数据,以及使用到的路由组件base:需要整合header和leftMenu以及内容区域,以及是否涉及header头和leftMenu固定,这里使用fixed变量
(2)基本组件开发
我们将这三个组件放在一个目录下:
1. header组件开发
import React, {CSSProperties } from 'react';import {Button, Dropdown, Layout, Menu } from 'antd';import {DownOutlined } from '@ant-design/icons';import './header.scss';import getPrefixCls from '../_utils/get-prefix-cls'; //这个函数后面介绍const {Header: LayoutHeader } = Layout;//这里是header组件用到的propsexport interface Props {logoUrl?: string;title?: string;userName?: string;logoutUrl?: string;isFixed?: boolean;}const Header: React.FC<Props> = ({title,logoUrl,userName,logoutUrl,isFixed = false,}) => {//根据是否固定做出的不一样的header styleconst style: CSSProperties = isFixed? {position: 'fixed', zIndex: 3, width: '100%', backgroundColor: '#fff' }: {width: '100%', backgroundColor: '#fff' };const menu = (<Menu><Menu.Item key="1">{logoutUrl && <a href={logoutUrl}>退出</a>}</Menu.Item></Menu>);return (<LayoutHeader className={getPrefixCls('header')} style={style}><div className={getPrefixCls('header__logo')}>{logoUrl && <img src={logoUrl} alt="logo" />}{title && <span>{title}</span>}</div><nav className={getPrefixCls('header__nav')} />{userName && (<div className={getPrefixCls('header__user')}><Dropdown overlay={menu}><Button type="link"><span>{userName}</span><DownOutlined /></Button></Dropdown></div>)}</LayoutHeader>);};export default Header;
header组件的scss如下:
@import '../style/var.scss'; //这个后面介绍.#{$name}-header {display: flex;border-bottom: 1px solid #dcdcdc;&__logo {display: flex;justify-content: center;img {width: 75px;align-self: center;}span {font-size: 14px;font-family: unset;color: #383838;}}&__nav {flex: 1;}&__user {:global {.ant-avatar {cursor: pointer;}}}}
2. leftMenu组件
页面组件:
import React, {useMemo, useCallback } from 'react';import {Menu } from 'antd';import {Link } from 'react-router-dom';import './leftMenu.scss';import {Location } from 'history';import getPrefixCls from '../_utils/get-prefix-cls';const {SubMenu } = Menu;//菜单项结构export interface MenuItem {key: string;name: string;icon?: any;type: string;children?: MenuItem[];}//leftMenu组件用到的propsexport interface Props {location: Location;MenuData: MenuItem[];}const LeftMenu: React.FC<Props> = ({location, MenuData }) => {const getDefaultOpenKeys = useCallback((data: MenuItem[] = [],openKeys: string[] = [],parentKeys: string[] = []): string[] =>data.reduce((prev, curr): any => {if (curr.key === location.pathname) {return [...prev, ...openKeys, curr.key, ...parentKeys];}if (curr.children && curr.children.length !== 0) {return [...prev,...getDefaultOpenKeys(curr.children, openKeys, [...parentKeys,curr.key,]),];}return [...prev];}, []),[location.pathname]);const defaultOpenKeys = useMemo(() => getDefaultOpenKeys(MenuData), [getDefaultOpenKeys,]);// 遍历定义的菜单const MenuList: JSX.Element[] = useMemo(() =>MenuData.map((item) => {if (item.children && item.children.length !== 0) {return (<SubMenukey={item.key}title={<span>{item.icon &&React.createElement(item.icon, {style: {verticalAlign: 'middle' },})}<span>{item.name}</span></span>}>{item.children.map((subItem) => (<Menu.Item key={subItem.key}>{subItem.type === 'link' ? (<Link to={subItem.key}>{subItem.name}</Link>) : (<a target="_blank " href={subItem.key}>{subItem.name}</a>)}</Menu.Item>))}</SubMenu>);}return (<Menu.Item key={item.key}>{item.type === 'link' ? (<Link to={item.key}>{item.icon &&React.createElement(item.icon, {style: {verticalAlign: 'middle' },})}<span>{item.name}</span></Link>) : (<a target="_blank " href={item.key}>{item.icon &&React.createElement(item.icon, {style: {verticalAlign: 'middle' },})}{item.name}</a>)}</Menu.Item>);}),[]);return (<MenuclassName={getPrefixCls('leftMenu')}theme="light"mode="inline"defaultSelectedKeys={[location.pathname]}defaultOpenKeys={defaultOpenKeys}>{MenuList}</Menu>);};export default LeftMenu;
用到的scss
@import '../style/var.scss';.#{$name}-leftMenu {border-right: none;}.#{$name}-iconStyle {display: flex;align-items: center;}
3. base组件
import React, {CSSProperties, useCallback, useState } from 'react';import {Layout } from 'antd';import Header from '../Header/header';import LeftMenu, {MenuItem } from '../LeftMenu/leftMenu';import {Location } from 'history';const {Sider, Content } = Layout;export interface Props {logoUrl?: string;title?: string;userName?: string;logoutUrl?: string;location: Location;MenuData: MenuItem[];isFixed?: boolean;}const Base: React.FC<Props> = ({children,MenuData,location,title,logoUrl,userName,logoutUrl,isFixed = false,}) => {const [collapsed, setCollapsed] = useState(false);// 点击展开,再次点击关闭const handleTootle = useCallback(() => {setCollapsed((v) => !v);}, [setCollapsed]);const layoutStyle: CSSProperties = isFixed? {paddingTop: '70px',overflow: 'auto',height: '100vh',position: 'fixed',left: 0,zIndex: 1,}: {};const contentStyle = isFixed? {margin: '24px 16px',marginTop: 90,padding: 20,marginLeft: collapsed ? '100px' : '220px',background: '#fff',}: {margin: '24px 16px',padding: 20,background: '#fff',};return (<Layout style={{minHeight: '100vh' }}><Headertitle={title}logoUrl={logoUrl}logoutUrl={logoutUrl}userName={userName}isFixed={isFixed}/><Layout><Sidercollapsiblecollapsed={collapsed}onCollapse={handleTootle}theme="light"style={layoutStyle}><LeftMenu MenuData={MenuData} location={location} /></Sider><Layout><Content style={contentStyle}>{children}</Content></Layout></Layout></Layout>);};export default Base;
(3)主要函数和样式
因为在打包中,我们的包被引用之后可能出现css找不到或者被覆盖的情况,所以要给我们自己的css加一个前缀
1. getPrefixCls
该函数是给页面级组件加前缀
const getPrefixCls = (suffixCls: string,customizePrefixCls = 'fui'): string => {return `${customizePrefixCls}-${suffixCls}`;};export default getPrefixCls;
2. Var.scss
这是个scss函数主要在组件scss中引入,给scss加前缀
$name:fui
(4)导出组件
import Base from './Base';import Header from './Header';import LeftMenu from './LeftMenu';export {Base, Header, LeftMenu };export default {Base, Header, LeftMenu };
其实这个时候也可以在app中引入自己写的组件看看是否无误,就比如我在这里写了一个测试组件,之后在app中引入就可以看到该组件的样式:
测试组件代码如下:
import React, {FC } from 'react';import {Base } from './components';import {FormOutlined, FileSyncOutlined } from '@ant-design/icons';import {MenuItem } from './components/LeftMenu/leftMenu';import {useLocation } from 'react-router-dom';const Ceshi: React.FC = () => {const location = useLocation();const menuData: MenuItem[] = [{key: '/search/*',name: '账号审计',icon: FormOutlined,type: 'link',children: [{key: '/search',name: '账号全量查询',type: 'link',},{key: '/search/inactive',name: '账号不活跃查询',type: 'link',},{key: '/search/match',name: '一人多账号查询',type: 'link',},{key: '/search/share',name: '账号共享查询',type: 'link',},],},{key: '/config',name: '账号审计配置',icon: FileSyncOutlined,type: 'link',children: [{key: '/config/inactive',name: '账号不活跃管理',type: 'link',},{key: '/config/match',name: '一人多账号报备',type: 'link',},],},];return (<BaseMenuData={menuData}location={location}title={'ceshi'}userName={'welkin'}logoutUrl={'/logout/ceshi'}></Base>);};export default Ceshi;
app组件如下:
import React from 'react';import Ceshi from './ceshi';import {BrowserRouter, Route, Routes } from 'react-router-dom';const App: React.FC = () => {return (<div className="App"><BrowserRouter><Routes><Route path="/" element={<Ceshi />} /></Routes></BrowserRouter></div>);};export default App;
四、webpack打包配置
(1)yarn eject暴露出配置
yarn eject
(2)修改配置
1. 添加路径
在config/path中添加路径,因为我的组件是在components目录下的,所以添加
comIndexJs: resolveModule(resolveApp, 'src/components/index'),//这个地方就是我们的导出组件文件index
2. 修改webpack
//修改入口文件entry: isEnvProduction ? IndexJs : paths.appIndexJs,//修改output 部分output:{//修改filenamefilename: isEnvProduction? 'fui.js': isEnvDevelopment && 'static/js/bundle.js',// 修改devtoolModuleFilenameTemplatedevtoolModuleFilenameTemplate: isEnvProduction? (info) =>path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, '/'): isEnvDevelopment &&((info) =>path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),//添加如下:library: 'fui',libraryTarget: 'umd',libraryExport: 'default',}//将 HtmlWebpackPlugin 设置为isEnvDevelopment 判断plugins: [isEnvDevelopment &&new HtmlWebpackPlugin(Object.assign(xxxx)//....)]//增加 externals 里面的内容要和package.json中的peerDependencies挂钩externals: {react: {commonjs: 'react',commonjs2: 'react',amd: 'react',root: 'React',},'react-dom': {commonjs: 'react-dom',commonjs2: 'react-dom',amd: 'react-dom',root: 'ReactDOM',},'react-router-dom': {commonjs: 'react-router-dom',commonjs2: 'react-router-dom',amd: 'react-router-dom',root: 'ReactRouterDom',},},
(3)修改package.json
添加如下:
"peerDependencies": {"react": "^17.0.2","react-dom": "^17.0.2","react-router-dom": "^6.2.1"},
(4)发包
首先进行yarn build验证,如果没有问题检查package.json修改你的版本号【每一次npm publish都要在之前修改】,记住package.json重的name跟我们最开始发布的测试组件保持一致,因为这个项目是我们用create-react-app新建的,所以检查一下package.json
{"name": "welkin-test","version": "1.0.1", //这个分别代表 主版本号.次版本号.修订号"description": "基本布局",}
添加打包的出口文件
{"name": "welkin-test","version": "1.0.1","description": "基本布局","main": "build/fui.js",}
执行npm publish
五、测试发布的包
(1)下载
首先需要你另起一个项目,然后下载我们的包
npm install welkin-test
(2)配置组件
1. 引入Base组件
我在这里写了一个测试组件,如下:Fui.Base中是我瞎写的,你们随便写
import React from 'react';import {FormOutlined, FileSyncOutlined } from '@ant-design/icons';import {useLocation } from 'react-router-dom';import Fui from '@didi/layout-face';import {Row, Col, Statistic, Button, Descriptions, Badge, Timeline } from 'antd';import logo from './assets/img/logo.jpg';const Ceshi: React.FC = () => {const location = useLocation();const menuData = [{key: '/search/*',name: '菜单',icon: FormOutlined,type: 'link',children: [{key: '/search',name: '子菜单1',type: 'link',},{key: '/search/inactive',name: '子菜单2',type: 'link',},{key: '/search/match',name: '子菜单3',type: 'link',},{key: '/search/share',name: '子菜单4',type: 'link',},],},{key: '/config',name: '菜单2',icon: FileSyncOutlined,type: 'link',children: [{key: '/config/inactive',name: '子菜单1',type: 'link',},{key: '/config/match',name: '子菜单2',type: 'link',},],},];return (<Fui.BaseisFixedMenuData={menuData}location={location}logoUrl={logo}title="测试xxx系统"userName="welkin"logoutUrl="/logout/ceshi"><Row gutter={16}><Col span={12}><Statistic title="Active Users" value={112893} /></Col><Col span={12}><Statistic title="Account Balance (CNY)" value={112893} precision={2} /><Button style={{marginTop: 16 }} type="primary">Recharge</Button></Col><Col span={12}><Statistic title="Active Users" value={112893} loading /></Col></Row><Row><Descriptions title="User Info" bordered><Descriptions.Item label="Product">Cloud Database</Descriptions.Item><Descriptions.Item label="Billing Mode">Prepaid</Descriptions.Item><Descriptions.Item label="Automatic Renewal">YES</Descriptions.Item><Descriptions.Item label="Order time">-04-24 18:00:00</Descriptions.Item><Descriptions.Item label="Usage Time" span={2}>-04-24 18:00:00</Descriptions.Item><Descriptions.Item label="Status" span={3}><Badge status="processing" text="Running" /></Descriptions.Item><Descriptions.Item label="Negotiated Amount">$80.00</Descriptions.Item><Descriptions.Item label="Discount">$20.00</Descriptions.Item><Descriptions.Item label="Official Receipts">$60.00</Descriptions.Item><Descriptions.Item label="Config Info">Data disk type: MongoDB<br />Database version: 3.4<br />Package: dds.mongo.mid<br />Storage space: 10 GB<br />Replication factor: 3<br />Region: East China 1<br /></Descriptions.Item></Descriptions></Row><Row><Timeline><Timeline.Item color="green">Create a services site -09-01</Timeline.Item><Timeline.Item color="green">Create a services site -09-01</Timeline.Item><Timeline.Item color="red"><p>Solve initial network problems 1</p><p>Solve initial network problems 2</p><p>Solve initial network problems 3 -09-01</p></Timeline.Item><Timeline.Item><p>Technical testing 1</p><p>Technical testing 2</p><p>Technical testing 3 -09-01</p></Timeline.Item><Timeline.Item color="gray"><p>Technical testing 1</p><p>Technical testing 2</p><p>Technical testing 3 -09-01</p></Timeline.Item><Timeline.Item color="gray"><p>Technical testing 1</p><p>Technical testing 2</p><p>Technical testing 3 -09-01</p></Timeline.Item></Timeline></Row></Fui.Base>);};export default Ceshi;
2. 在app中引入
import React, {FC } from 'react';import {BrowserRouter, Route, Routes } from 'react-router-dom';import style from './app.module.scss';import Ceshi from './ceshi';const App: FC = () => (<div className={style.app}><BrowserRouter><Routes><Route path="/" element={<Ceshi />} /></Routes></BrowserRouter></div>);export default App;
3. 引入样式
当你启动项目看见没有报错,但却没有样式时,需要在index.tsx中引入样式
import ReactDOM from 'react-dom';import React from 'react';import App from './app';//如下所示,因为基于的antd,所以也要引入antdimport 'antd/dist/antd.css';import '@didi/layout-face/build/fui.css';ReactDOM.render(<App />, document.getElementById('app'));
然后打开页面就可以看见我们的组件了,基本上已经完成了一个npm组件的发布
红色框内的就是准备开发的
(1)搭建基本组件
1. create-react-app直接创建组件即可
create-react-app xxxxxx --template typescript
2. 组件开发
其实可以先开发出自己的组件然后再分析,以我的为例子,主要分为三块组件:
header:主要是顶部,涉及到logo,title,username以及logoutUrl;leftMenu:菜单信息;涉及到渲染的菜单数据,以及使用到的路由组件base:需要整合header和leftMenu以及内容区域,以及是否涉及header头和leftMenu固定,这里使用fixed变量
(2)基本组件开发
我们将这三个组件放在一个目录下:
1. header组件开发
import React, {CSSProperties } from 'react';import {Button, Dropdown, Layout, Menu } from 'antd';import {DownOutlined } from '@ant-design/icons';import './header.scss';import getPrefixCls from '../_utils/get-prefix-cls'; //这个函数后面介绍const {Header: LayoutHeader } = Layout;//这里是header组件用到的propsexport interface Props {logoUrl?: string;title?: string;userName?: string;logoutUrl?: string;isFixed?: boolean;}const Header: React.FC<Props> = ({title,logoUrl,userName,logoutUrl,isFixed = false,}) => {//根据是否固定做出的不一样的header styleconst style: CSSProperties = isFixed? {position: 'fixed', zIndex: 3, width: '100%', backgroundColor: '#fff' }: {width: '100%', backgroundColor: '#fff' };const menu = (<Menu><Menu.Item key="1">{logoutUrl && <a href={logoutUrl}>退出</a>}</Menu.Item></Menu>);return (<LayoutHeader className={getPrefixCls('header')} style={style}><div className={getPrefixCls('header__logo')}>{logoUrl && <img src={logoUrl} alt="logo" />}{title && <span>{title}</span>}</div><nav className={getPrefixCls('header__nav')} />{userName && (<div className={getPrefixCls('header__user')}><Dropdown overlay={menu}><Button type="link"><span>{userName}</span><DownOutlined /></Button></Dropdown></div>)}</LayoutHeader>);};export default Header;
header组件的scss如下:
@import '../style/var.scss'; //这个后面介绍.#{$name}-header {display: flex;border-bottom: 1px solid #dcdcdc;&__logo {display: flex;justify-content: center;img {width: 75px;align-self: center;}span {font-size: 14px;font-family: unset;color: #383838;}}&__nav {flex: 1;}&__user {:global {.ant-avatar {cursor: pointer;}}}}
2. leftMenu组件
页面组件:
import React, {useMemo, useCallback } from 'react';import {Menu } from 'antd';import {Link } from 'react-router-dom';import './leftMenu.scss';import {Location } from 'history';import getPrefixCls from '../_utils/get-prefix-cls';const {SubMenu } = Menu;//菜单项结构export interface MenuItem {key: string;name: string;icon?: any;type: string;children?: MenuItem[];}//leftMenu组件用到的propsexport interface Props {location: Location;MenuData: MenuItem[];}const LeftMenu: React.FC<Props> = ({location, MenuData }) => {const getDefaultOpenKeys = useCallback((data: MenuItem[] = [],openKeys: string[] = [],parentKeys: string[] = []): string[] =>data.reduce((prev, curr): any => {if (curr.key === location.pathname) {return [...prev, ...openKeys, curr.key, ...parentKeys];}if (curr.children && curr.children.length !== 0) {return [...prev,...getDefaultOpenKeys(curr.children, openKeys, [...parentKeys,curr.key,]),];}return [...prev];}, []),[location.pathname]);const defaultOpenKeys = useMemo(() => getDefaultOpenKeys(MenuData), [getDefaultOpenKeys,]);// 遍历定义的菜单const MenuList: JSX.Element[] = useMemo(() =>MenuData.map((item) => {if (item.children && item.children.length !== 0) {return (<SubMenukey={item.key}title={<span>{item.icon &&React.createElement(item.icon, {style: {verticalAlign: 'middle' },})}<span>{item.name}</span></span>}>{item.children.map((subItem) => (<Menu.Item key={subItem.key}>{subItem.type === 'link' ? (<Link to={subItem.key}>{subItem.name}</Link>) : (<a target="_blank " href={subItem.key}>{subItem.name}</a>)}</Menu.Item>))}</SubMenu>);}return (<Menu.Item key={item.key}>{item.type === 'link' ? (<Link to={item.key}>{item.icon &&React.createElement(item.icon, {style: {verticalAlign: 'middle' },})}<span>{item.name}</span></Link>) : (<a target="_blank " href={item.key}>{item.icon &&React.createElement(item.icon, {style: {verticalAlign: 'middle' },})}{item.name}</a>)}</Menu.Item>);}),[]);return (<MenuclassName={getPrefixCls('leftMenu')}theme="light"mode="inline"defaultSelectedKeys={[location.pathname]}defaultOpenKeys={defaultOpenKeys}>{MenuList}</Menu>);};export default LeftMenu;
用到的scss
@import '../style/var.scss';.#{$name}-leftMenu {border-right: none;}.#{$name}-iconStyle {display: flex;align-items: center;}
3. base组件
import React, {CSSProperties, useCallback, useState } from 'react';import {Layout } from 'antd';import Header from '../Header/header';import LeftMenu, {MenuItem } from '../LeftMenu/leftMenu';import {Location } from 'history';const {Sider, Content } = Layout;export interface Props {logoUrl?: string;title?: string;userName?: string;logoutUrl?: string;location: Location;MenuData: MenuItem[];isFixed?: boolean;}const Base: React.FC<Props> = ({children,MenuData,location,title,logoUrl,userName,logoutUrl,isFixed = false,}) => {const [collapsed, setCollapsed] = useState(false);// 点击展开,再次点击关闭const handleTootle = useCallback(() => {setCollapsed((v) => !v);}, [setCollapsed]);const layoutStyle: CSSProperties = isFixed? {paddingTop: '70px',overflow: 'auto',height: '100vh',position: 'fixed',left: 0,zIndex: 1,}: {};const contentStyle = isFixed? {margin: '24px 16px',marginTop: 90,padding: 20,marginLeft: collapsed ? '100px' : '220px',background: '#fff',}: {margin: '24px 16px',padding: 20,background: '#fff',};return (<Layout style={{minHeight: '100vh' }}><Headertitle={title}logoUrl={logoUrl}logoutUrl={logoutUrl}userName={userName}isFixed={isFixed}/><Layout><Sidercollapsiblecollapsed={collapsed}onCollapse={handleTootle}theme="light"style={layoutStyle}><LeftMenu MenuData={MenuData} location={location} /></Sider><Layout><Content style={contentStyle}>{children}</Content></Layout></Layout></Layout>);};export default Base;
(3)主要函数和样式
因为在打包中,我们的包被引用之后可能出现css找不到或者被覆盖的情况,所以要给我们自己的css加一个前缀
1. getPrefixCls
该函数是给页面级组件加前缀
const getPrefixCls = (suffixCls: string,customizePrefixCls = 'fui'): string => {return `${customizePrefixCls}-${suffixCls}`;};export default getPrefixCls;
2. Var.scss
这是个scss函数主要在组件scss中引入,给scss加前缀
$name:fui
(4)导出组件
import Base from './Base';import Header from './Header';import LeftMenu from './LeftMenu';export {Base, Header, LeftMenu };export default {Base, Header, LeftMenu };
其实这个时候也可以在app中引入自己写的组件看看是否无误,就比如我在这里写了一个测试组件,之后在app中引入就可以看到该组件的样式:
测试组件代码如下:
import React, {FC } from 'react';import {Base } from './components';import {FormOutlined, FileSyncOutlined } from '@ant-design/icons';import {MenuItem } from './components/LeftMenu/leftMenu';import {useLocation } from 'react-router-dom';const Ceshi: React.FC = () => {const location = useLocation();const menuData: MenuItem[] = [{key: '/search/*',name: '账号审计',icon: FormOutlined,type: 'link',children: [{key: '/search',name: '账号全量查询',type: 'link',},{key: '/search/inactive',name: '账号不活跃查询',type: 'link',},{key: '/search/match',name: '一人多账号查询',type: 'link',},{key: '/search/share',name: '账号共享查询',type: 'link',},],},{key: '/config',name: '账号审计配置',icon: FileSyncOutlined,type: 'link',children: [{key: '/config/inactive',name: '账号不活跃管理',type: 'link',},{key: '/config/match',name: '一人多账号报备',type: 'link',},],},];return (<BaseMenuData={menuData}location={location}title={'ceshi'}userName={'welkin'}logoutUrl={'/logout/ceshi'}></Base>);};export default Ceshi;
app组件如下:
import React from 'react';import Ceshi from './ceshi';import {BrowserRouter, Route, Routes } from 'react-router-dom';const App: React.FC = () => {return (<div className="App"><BrowserRouter><Routes><Route path="/" element={<Ceshi />} /></Routes></BrowserRouter></div>);};export default App;
四、webpack打包配置
(1)yarn eject暴露出配置
yarn eject
(2)修改配置
1. 添加路径
在config/path中添加路径,因为我的组件是在components目录下的,所以添加
comIndexJs: resolveModule(resolveApp, 'src/components/index'),//这个地方就是我们的导出组件文件index
2. 修改webpack
//修改入口文件entry: isEnvProduction ? IndexJs : paths.appIndexJs,//修改output 部分output:{//修改filenamefilename: isEnvProduction? 'fui.js': isEnvDevelopment && 'static/js/bundle.js',// 修改devtoolModuleFilenameTemplatedevtoolModuleFilenameTemplate: isEnvProduction? (info) =>path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, '/'): isEnvDevelopment &&((info) =>path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),//添加如下:library: 'fui',libraryTarget: 'umd',libraryExport: 'default',}//将 HtmlWebpackPlugin 设置为isEnvDevelopment 判断plugins: [isEnvDevelopment &&new HtmlWebpackPlugin(Object.assign(xxxx)//....)]//增加 externals 里面的内容要和package.json中的peerDependencies挂钩externals: {react: {commonjs: 'react',commonjs2: 'react',amd: 'react',root: 'React',},'react-dom': {commonjs: 'react-dom',commonjs2: 'react-dom',amd: 'react-dom',root: 'ReactDOM',},'react-router-dom': {commonjs: 'react-router-dom',commonjs2: 'react-router-dom',amd: 'react-router-dom',root: 'ReactRouterDom',},},
(3)修改package.json
添加如下:
"peerDependencies": {"react": "^17.0.2","react-dom": "^17.0.2","react-router-dom": "^6.2.1"},
(4)发包
首先进行yarn build验证,如果没有问题检查package.json修改你的版本号【每一次npm publish都要在之前修改】,记住package.json重的name跟我们最开始发布的测试组件保持一致,因为这个项目是我们用create-react-app新建的,所以检查一下package.json
{"name": "welkin-test","version": "1.0.1", //这个分别代表 主版本号.次版本号.修订号"description": "基本布局",}
添加打包的出口文件
{"name": "welkin-test","version": "1.0.1","description": "基本布局","main": "build/fui.js",}
执行npm publish
五、测试发布的包
(1)下载
首先需要你另起一个项目,然后下载我们的包
npm install welkin-test
(2)配置组件
1. 引入Base组件
我在这里写了一个测试组件,如下:Fui.Base中是我瞎写的,你们随便写
import React from 'react';import {FormOutlined, FileSyncOutlined } from '@ant-design/icons';import {useLocation } from 'react-router-dom';import Fui from '@didi/layout-face';import {Row, Col, Statistic, Button, Descriptions, Badge, Timeline } from 'antd';import logo from './assets/img/logo.jpg';const Ceshi: React.FC = () => {const location = useLocation();const menuData = [{key: '/search/*',name: '菜单',icon: FormOutlined,type: 'link',children: [{key: '/search',name: '子菜单1',type: 'link',},{key: '/search/inactive',name: '子菜单2',type: 'link',},{key: '/search/match',name: '子菜单3',type: 'link',},{key: '/search/share',name: '子菜单4',type: 'link',},],},{key: '/config',name: '菜单2',icon: FileSyncOutlined,type: 'link',children: [{key: '/config/inactive',name: '子菜单1',type: 'link',},{key: '/config/match',name: '子菜单2',type: 'link',},],},];return (<Fui.BaseisFixedMenuData={menuData}location={location}logoUrl={logo}title="测试xxx系统"userName="welkin"logoutUrl="/logout/ceshi"><Row gutter={16}><Col span={12}><Statistic title="Active Users" value={112893} /></Col><Col span={12}><Statistic title="Account Balance (CNY)" value={112893} precision={2} /><Button style={{marginTop: 16 }} type="primary">Recharge</Button></Col><Col span={12}><Statistic title="Active Users" value={112893} loading /></Col></Row><Row><Descriptions title="User Info" bordered><Descriptions.Item label="Product">Cloud Database</Descriptions.Item><Descriptions.Item label="Billing Mode">Prepaid</Descriptions.Item><Descriptions.Item label="Automatic Renewal">YES</Descriptions.Item><Descriptions.Item label="Order time">-04-24 18:00:00</Descriptions.Item><Descriptions.Item label="Usage Time" span={2}>-04-24 18:00:00</Descriptions.Item><Descriptions.Item label="Status" span={3}><Badge status="processing" text="Running" /></Descriptions.Item><Descriptions.Item label="Negotiated Amount">$80.00</Descriptions.Item><Descriptions.Item label="Discount">$20.00</Descriptions.Item><Descriptions.Item label="Official Receipts">$60.00</Descriptions.Item><Descriptions.Item label="Config Info">Data disk type: MongoDB<br />Database version: 3.4<br />Package: dds.mongo.mid<br />Storage space: 10 GB<br />Replication factor: 3<br />Region: East China 1<br /></Descriptions.Item></Descriptions></Row><Row><Timeline><Timeline.Item color="green">Create a services site -09-01</Timeline.Item><Timeline.Item color="green">Create a services site -09-01</Timeline.Item><Timeline.Item color="red"><p>Solve initial network problems 1</p><p>Solve initial network problems 2</p><p>Solve initial network problems 3 -09-01</p></Timeline.Item><Timeline.Item><p>Technical testing 1</p><p>Technical testing 2</p><p>Technical testing 3 -09-01</p></Timeline.Item><Timeline.Item color="gray"><p>Technical testing 1</p><p>Technical testing 2</p><p>Technical testing 3 -09-01</p></Timeline.Item><Timeline.Item color="gray"><p>Technical testing 1</p><p>Technical testing 2</p><p>Technical testing 3 -09-01</p></Timeline.Item></Timeline></Row></Fui.Base>);};export default Ceshi;
2. 在app中引入
import React, {FC } from 'react';import {BrowserRouter, Route, Routes } from 'react-router-dom';import style from './app.module.scss';import Ceshi from './ceshi';const App: FC = () => (<div className={style.app}><BrowserRouter><Routes><Route path="/" element={<Ceshi />} /></Routes></BrowserRouter></div>);export default App;
3. 引入样式
当你启动项目看见没有报错,但却没有样式时,需要在index.tsx中引入样式
import ReactDOM from 'react-dom';import React from 'react';import App from './app';//如下所示,因为基于的antd,所以也要引入antdimport 'antd/dist/antd.css';import '@didi/layout-face/build/fui.css';ReactDOM.render(<App />, document.getElementById('app'));
然后打开页面就可以看见我们的组件了,基本上已经完成了一个npm组件的发布