
IT学徒、技术民工、斜杠青年,机器人爱好者、摄影爱好 PS、PR、LR、达芬奇潜在学习者
共 212 篇文章
目录

 前端框架,快速开发页面,函数式编程,与后端api快速搭建
react中构建像excel一样功能的库
安装
npm install react-datasheet --save使用
import ReactDataSheet from 'react-datasheet';
// Be sure to include styles at some point, probably during your bootstrapping
import 'react-datasheet/lib/react-datasheet.css';
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      grid: [
        [{ value: 1 }, { value: 3 }],
        [{ value: 2 }, { value: 4 }],
      ],
    };
  }
  render() {
    return (
      <ReactDataSheet
        data={this.state.grid}
        valueRenderer={cell => cell.value}
        onCellsChanged={changes => {
          const grid = this.state.grid.map(row => [...row]);
          changes.forEach(({ cell, row, col, value }) => {
            grid[row][col] = { ...grid[row][col], value };
          });
          this.setState({ grid });
        }}
      />
    );
  }
}对color进行操作,类似于moment操作时间
var colors = tinycolor("#f00").analogous();
colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#ff0066", "#ff0033", "#ff0000", "#ff3300", "#ff6600" ]录制web操作、重放
rrweb 主要由 3 部分组成:
安装
npm install --save rrwebrrweb 同时提供 commonJS 和 ES modules 两种格式的打包文件,易于和常见的打包工具配合使用。
使用,录制
rrweb.record({
  emit(event) {
    // 用任意方式存储 event
  },
});
let stopFn = rrweb.record({
  emit(event) {
    if (events.length > 100) {
      // 当事件数量大于 100 时停止录制
      stopFn();
    }
  },
});回放
const replayer = new rrweb.Replayer(events);
// 播放
replayer.play();
// 从第 3 秒的内容开始播放
replayer.play(3000);
// 暂停
replayer.pause();
// 暂停至第 5 秒处
replayer.pause(5000);
// 销毁播放器 (提示: 这个操作不可逆)
replayer.destroy();包含比较流行的icons
安装
npm install react-icons --save使用
import { FaBeer } from 'react-icons/fa';
class Question extends React.Component {
    render() {
        return <h3> Lets go for a <FaBeer />? </h3>
    }
}https://github.com/react-icons/react-icons
放在邮件中的react组件
https://github.com/zenorocha/react-email
安装
npm install @react-email/button -E使用
import { Button } from '@react-email/button';
const Email = () => {
  return (
    <Button href="https://example.com" style={{ color: '#61dafb' }}>
      Click me
    </Button>
  );
};js的lru缓存
npm install lru-cache --save使用
// hybrid module, either works
import { LRUCache } from 'lru-cache'
// or:
const { LRUCache } = require('lru-cache')
// or in minified form for web browsers:
import { LRUCache } from 'http://unpkg.com/lru-cache@9/dist/mjs/index.min.mjs'
// At least one of 'max', 'ttl', or 'maxSize' is required, to prevent
// unsafe unbounded storage.
//
// In most cases, it's best to specify a max for performance, so all
// the required memory allocation is done up-front.
//
// All the other options are optional, see the sections below for
// documentation on what each one does.  Most of them can be
// overridden for specific items in get()/set()
const options = {
  max: 500,
  // for use with tracking overall storage size
  maxSize: 5000,
  sizeCalculation: (value, key) => {
    return 1
  },
  // for use when you need to clean up something when objects
  // are evicted from the cache
  dispose: (value, key) => {
    freeFromMemoryOrWhatever(value)
  },
  // how long to live in ms
  ttl: 1000 * 60 * 5,
  // return stale items before removing from cache?
  allowStale: false,
  updateAgeOnGet: false,
  updateAgeOnHas: false,
  // async method to use for cache.fetch(), for
  // stale-while-revalidate type of behavior
  fetchMethod: async (
    key,
    staleValue,
    { options, signal, context }
  ) => {},
}
const cache = new LRUCache(options)
cache.set('key', 'value')
cache.get('key') // "value"
// non-string keys ARE fully supported
// but note that it must be THE SAME object, not
// just a JSON-equivalent object.
var someObject = { a: 1 }
cache.set(someObject, 'a value')
// Object keys are not toString()-ed
cache.set('[object Object]', 'a different value')
assert.equal(cache.get(someObject), 'a value')
// A similar object with same keys/values won't work,
// because it's a different object identity
assert.equal(cache.get({ a: 1 }), undefined)
cache.clear() // empty the cache安装
yarn add events//event.ts
import {EventEmitter} from 'events'
export default new EventEmitter()
//发布
import emitter from './event'
class Father extends React.Component {
  constructor(props){
    super(props)
  }
  handleClick = () =>{
    emitter.emit('info','来自father的info')
  }
}
export default Father
//订阅
//emitter.addListener()事件监听订阅
//emitter.removeListener()进行事件销毁,取消订阅
import emitter from './event'
class Son extends React.Component {
  constructor(props){
    super(props)
  }
}很小的发布订阅库 200b
安装
npm install --save mitt使用
import mitt from 'mitt'
const emitter = mitt()
// listen to an event
emitter.on('foo', e => console.log('foo', e) )
// listen to all events
emitter.on('*', (type, e) => console.log(type, e) )
// fire an event
emitter.emit('foo', { a: 'b' })
// clearing all events
emitter.all.clear()
// working with handler references:
function onFoo() {}
emitter.on('foo', onFoo)   // listen
emitter.off('foo', onFoo)  // unlisten安装
npm install --save react-flow-renderer使用
import React from 'react';
import ReactFlow from 'react-flow-renderer';
const elements = [
  { id: '1', type: 'input', data: {lable: 'Node 1'},position: {x: 250, y: 50}},
  { id: '2', data: {lable: <div>Node 2</div>}, position: {x: 100, y: 100}},
  { id: 'el-2', source: '1', targetL '2', animated: true}
]
export default ()=> <ReactFlow elements={elements}></ReactFlow>React Flow有两个背景变体:点和线。也可以通过将其作为子级传递给ReactFlow组件来使用它
import ReactFlow, { Background } from 'react-flow-renderer';
const FlowWithBackground = () => (
  <ReactFlow elements={elements}>
    <Background
      variant="dots"
      gap={12}
      size={4}
    />
  </ReactFlow>
);可以通过将mini-map插件作为子级传递给ReactFlow组件来使用它:
import ReactFlow, { MiniMap } from 'react-flow-renderer';
const FlowWithMiniMap = () => (
  <ReactFlow elements={elements}>
    <MiniMap
      nodeColor={(node) => {
        switch (node.type) {
          case 'input': return 'red';
          case 'default': return '#00ff00';
          case 'output': return 'rgb(0,0,255)';
          default: return '#eee';
        }
      }}
    />
  </ReactFlow>
);控制面板包含zoom-in、zoom-out、fit-view和一个锁定/解锁按钮。
import ReactFlow, { Controls } from 'react-flow-renderer';
const FlowWithControls = () => (
  <ReactFlow elements={elements}>
    <Controls />
  </ReactFlow>
);如果需要访问ReactFlow组件外部的React Flow的内部状态和操作,可以用ReactFlowProvider组件包装它
import ReactFlow, { ReactFlowProvider } from 'react-flow-renderer';
const FlowWithOwnProvider = () => (
  <ReactFlowProvider>
    <ReactFlow
      elements={elements}
      onElementClick={onElementClick}
      onConnect={onConnect}
    />
  </ReactFlowProvider>
);https://www.5axxw.com/wiki/content/obkffc
配合Dagrejs可以画出自动布局的流程图
import React, { useCallback } from 'react';
import ReactFlow, { addEdge, ConnectionLineType, useNodesState, useEdgesState } from 'reactflow';
import dagre from 'dagre';
import 'reactflow/dist/style.css';
import { initialNodes, initialEdges } from './nodes-edges.js';
import './index.css';
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
const nodeWidth = 172;
const nodeHeight = 36;
const getLayoutedElements = (nodes, edges, direction = 'TB') => {
  const isHorizontal = direction === 'LR';
  dagreGraph.setGraph({ rankdir: direction });
  nodes.forEach((node) => {
    dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
  });
  edges.forEach((edge) => {
    dagreGraph.setEdge(edge.source, edge.target);
  });
  dagre.layout(dagreGraph);
  nodes.forEach((node) => {
    const nodeWithPosition = dagreGraph.node(node.id);
    node.targetPosition = isHorizontal ? 'left' : 'top';
    node.sourcePosition = isHorizontal ? 'right' : 'bottom';
    // We are shifting the dagre node position (anchor=center center) to the top left
    // so it matches the React Flow node anchor point (top left).
    node.position = {
      x: nodeWithPosition.x - nodeWidth / 2,
      y: nodeWithPosition.y - nodeHeight / 2,
    };
    return node;
  });
  return { nodes, edges };
};
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
  initialNodes,
  initialEdges
);
const LayoutFlow = () => {
  const [nodes, setNodes, onNodesChange] = useNodesState(layoutedNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(layoutedEdges);
  const onConnect = useCallback(
    (params) =>
      setEdges((eds) =>
        addEdge({ ...params, type: ConnectionLineType.SmoothStep, animated: true }, eds)
      ),
    []
  );
  const onLayout = useCallback(
    (direction) => {
      const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
        nodes,
        edges,
        direction
      );
      setNodes([...layoutedNodes]);
      setEdges([...layoutedEdges]);
    },
    [nodes, edges]
  );
  return (
    <div className="layoutflow">
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        connectionLineType={ConnectionLineType.SmoothStep}
        fitView
      />
      <div className="controls">
        <button onClick={() => onLayout('TB')}>vertical layout</button>
        <button onClick={() => onLayout('LR')}>horizontal layout</button>
      </div>
    </div>
  );
};
export default LayoutFlow;import React from 'react';
import { toPng } from 'html-to-image';
function downloadImage(dataUrl) {
  const a = document.createElement('a');
  a.setAttribute('download', 'reactflow.png');
  a.setAttribute('href', dataUrl);
  a.click();
}
function DownloadButton() {
  const onClick = () => {
    toPng(document.querySelector('.react-flow'), {
      filter: (node) => {
        // we don't want to add the minimap and the controls to the image
        if (
          node?.classList?.contains('react-flow__minimap') ||
          node?.classList?.contains('react-flow__controls')
        ) {
          return false;
        }
        return true;
      },
    }).then(downloadImage);
  };
  return (
    <button className="download-btn" onClick={onClick}>
      Download Image
    </button>
  );
}
export default DownloadButton;返回组件中的文字
npm install --save react-children-utilities使用
import React from 'react';
import Children from 'react-children-utilities';
const MyComponent = ({ children }) => {
  const onlySpans = Children.filter(children, (child) => child.type === 'span');
  return <div>{onlySpans}</div>;
};其他api:
https://www.npmjs.com/package/react-children-utilities
不建议使用react-intl,而使用React-intl-universal实现
建立英文和中文语言包,可以是json或者js文件
const en_US = {
  'hello':'nihao',
  'name': 'zhangsan',
  'age': '30',
  'changelang': 'qiehuanyuyan',
}
export default en_US中文包
const zh_CN = {
  'hello':'nihao',
  'name': 'zhangsan',
  'age': '30',
  'changelang': 'qiehuanyuyan',
}
export default zh_CN使用
import intl from 'react-intl-universal'
import cn from '../../assets/locals/zh-CN'
import us from '../../assets'
class IntlExample extends React.Component{
  constructor(){
    super();
    this.locals = {
      'zh_CN': cn,
      'en_US': us
    }
    this.state = {
      intl: cn
    }
  }
  
  componentDidMount() {
    this.initLocale();
  }
  initLocale(locale="zh_CN"){
    
  }
}react国际化包
安装
npm install react-i18next使用
import { useTranslation } from 'react-i18next';
const { i18n, t } = useTranslation();
i18n.changeLanguage(language).catch(() => {});
<html>{t('a')}</html>与react-intl对比
React-i18next初始化的时候需要将初始化配置放置在初始化文件(i18n.js)中,然后将初始化文件(i18n.js)通过import的方式引入到入口文件中即可。当然也可以通过I18nextProvider将i18n往下传递到各子组件。React-intl提供的是context api初始化方案,需要将初始化配置放在IntlProvider组件中,并且将入口文件的组件(如
React-i18next提供了切换语言的接口(i18n.changeLanguage),react-intl则需要对切换做一些封装的工作;
React-i18next提供了三种方式进行国际化操作(render props、hook和hoc), react-intl提供了api(intl.formatMessage())和组件(
React-i18next的语言资源文件为json格式,react-intl为js格式,同时支持变量传值;
React-i18next有很多插件可以使用比如检测当前系统语言,从后端获取数据等;
React-intl除文本翻译外还提供日期、时间和金额的国际化支持;
检测浏览器的语言
安装
npm install i18next-browser-languagedetector使用
import i18next from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
i18next.use(LanguageDetector).init(i18nextOptions);uuid是通用唯一识别码(Universally Unique Identifier)的缩写。是一种软件建构辨准,亦为开发软件基金会组织在分布式计算环境领域的一部分。其目的是让分布式系统中的所有元素具有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。
UUID由一组32位数的16进制数字构成。对于UUID,就算每纳秒产生一百万个UUID,要花100亿年才会将所有UUID用完。
格式
uuid32个16进制数字用连字号分成五组来显示,所以共有36个字符
UUID版本通过M表示,当前规范有5个版本,可选值为1、2、3、4、5,这5个版本使用不同的算法,利用不同的信息产生UUID,各版本有各版本的优势,具体来说:
uuid.v1():创建版本1(时间戳)UUID
uuid.v3():创建版本3(md5命名空间)UUID
uuid.v4():创建版本4(随机)UUID
uuid.v5():创建版本5(带SHA-1的命名空间)IIOD
安装
npm install uuid 使用
import { v4 as uuidv4} from 'uuid'
uuidv4()可以使用uuid进行验证登陆,未登陆状态下生产uuid
let uuid = sessionStorage.getItem('uuid')
if(!uuid){
  sessionStorage.setItem('uuid')
}
if(getToken()){
  sessionStorage.removeItem('uuid');
}else {
  let uuid = sessionStorage.getItem('uuid');
  if(!uuid){
    sessionStorage.setItem('uuid',uuidv4());
  }
}k-bar可以给react部署的站点提供一个舒服的搜索框样式
安装
npm install kbar使用
import {
  KBarProvider,
  KBarPortal,
  KBarPositioner,
  KBarAnimator,
  KBarSearch,
  useMatches,
  NO_GROUP
} from "kbar";
function MyApp() {
  const actions = [
    {
      id: "blog",
      name: "Blog",
      shortcut: ["b"],
      keywords: "writing words",
      perform: () => (window.location.pathname = "blog"),
    },
    {
      id: "contact",
      name: "Contact",
      shortcut: ["c"],
      keywords: "email",
      perform: () => (window.location.pathname = "contact"),
    },
  ]
  
  return (
    <KBarProvider actions={actions}>
      <KBarPortal> // Renders the content outside the root node
        <KBarPositioner> // Centers the content
          <KBarAnimator> // Handles the show/hide and height animations
            <KBarSearch /> // Search input
            <RenderResults />;
          </KBarAnimator>
        </KBarPositioner>
      </KBarPortal>
      <MyApp />
    </KBarProvider>;
  );
}自定义搜索结果样式
import {
  // ...
  KBarResults,
  useMatches,
  NO_GROUP,
} from "kbar";
function RenderResults() {
  const { results } = useMatches();
  return (
    <KBarResults
      items={results}
      onRender={({ item, active }) =>
        typeof item === "string" ? (
          <div>{item}</div>
        ) : (
          <div
            style={{
              background: active ? "#eee" : "transparent",
            }}
          >
            {item.name}
          </div>
        )
      }
    />
  );
}使用
import {AutoSizer, List, CellMeasurerCache, CellMeasurer} from 'react-virtualized'
// 宽度固定
const measureCache = new CellMeasurerCache({
    fixedWidth: true,
    minHeight: 60, 
})
// 每一行内容
const rowRenderer = ({ index, key, parent, style }) => {
    const item = records[index]
    return (
        <CellMeasurer cache={measureCache} key={key} parent={parent} rowIndex={index}>
            <div style={style}>
                content
            </div>
        </CellMeasurer>
    )
}
<AutoSizer>
    {({width, height}) => (
        <List
            width={width}
            height={height}
            deferredMeasurementCache={measureCache}
            rowCount={records.length}
            rowHeight={measureCache.rowHeight}
            rowRenderer={rowRenderer}
            className={styles.list}
        />
    )}
</AutoSizer>无限滚动 + 可编辑表格
import {Table, Column} from 'react-virtualized'
import {Input, Empty} from 'antd'
// 防止Input组件不必要的渲染
const MemoInput = React.memo(function (props) {
    const {rowIndex, field, handleFieldChange, ...restProps} = props
    return <Input {... restProps} onChange={(e) => handleFieldChange(field, e, rowIndex)} />
})
function VirtualTable(props) {
    // ...
    // 列, 使用useCallback优化
    const nameColumn = ({cellData, rowIndex, dataKey }) => {
        // ...
        return (
            <div>
                <MemoInput
                    placeholder="请输入姓名"
                    value={cellData}
                    rowIndex={rowIndex}
                    field="姓名"
                    handleFieldChange={handleFieldChange}
                />
            </div>
        )
    }
    
    // 表头
    const columnHeaderRenderer = useCallback(({dataKey}) => dataKey, [])
    const rowGetter = useCallback(({index}) => dataSource[index], [dataSource])
    const noRowsRenderer = useCallback(() => <Empty className={styles.empty} />, [])
    return <Table
        ref={tableRef}
        className={styles.virtualTable}
        headerClassName={styles.header}
        rowClassName={styles.row}
        headerHeight={TableHeaderHeight}
        width={TableWidth}
        height={TableHeight}
        noRowsRenderer={noRowsRenderer}
        rowHeight={TableRowHeight}
        rowGetter={rowGetter}
        rowCount={dataSource.length}
        overscanRowCount={OverscanRowCount}
    >
        <Column
            width={120}
            dataKey="姓名"
            headerRenderer={columnHeaderRenderer}
            cellRenderer={nameColumn}
        />
    </Table>
}react虚拟列表库 React Window是一个有效呈现大型列表和表格数据的组件,是React-virtualized的完全重写。
React Window专注于使软件包更小,更快,同时API(和文档)对初学者尽可能友好。
安装
npm i react-window固定高度列表
import { FixedSizeList as List } from 'react-window';
const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);
const Example = () => (
  <List
    height={150}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);VariableSizeList (可变尺寸列表)
import { VariableSizeList } from 'react-window';
 
const rowHeights = new Array(1000)
  .fill(true)
  .map(() => 25 + Math.round(Math.random() * 50));
 
const getItemSize = index => rowHeights[index]; // 此处采用随机数作为每个列表项的高度
 /** 
    * 每个列表项的组件
    * @param index:列表项的下标;style:列表项的样式(此参数必须传入列表项的组件中,否则会出现滚动到下方出现空白的情况)
    **/ 
const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);
 
const Example = () => (
  <VariableSizeList
    height={150} // 列表可视区域的高度
    itemCount={1000} // 列表数据长度
    itemSize={getItemSize} // 设置列表项的高度
    layout= "vertical" // (vertical/horizontal) 默认为vertical,此为设置列表的方向
    width={300}
  >
    {Row}
  <VariableSizeList>
);结合react-virtualized-auto-sizer使列表自适应当前页面的宽高
import { FixedSizeList } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
const Example = () => (
  <AutoSizer>
    {({ height, width }) => (
      <FixedSizeList
        className="List"
        height={height}
        itemCount={1000}
        itemSize={35}
        width={width}
      >
        {Row}
      </FixedSizeList>
    )}
  </AutoSizer>
);虚拟列表/表格库
安装
npm install react-virtuoso使用
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { Virtuoso } from 'react-virtuoso'
const App = () => {
  return <Virtuoso style={{ height: '400px' }} totalCount={200} itemContent={index => <div>Item {index}</div>} />
}
ReactDOM.render(<App />, document.getElementById('root'))让组件实现类似position-sticky的效果
安装
npm install react-sticky使用
import React from 'react';
import { StickyContainer, Sticky } from 'react-sticky';
class App extends React.Component {
  render() {
    return (
      <StickyContainer>
        {/* Other elements can be in between `StickyContainer` and `Sticky`,
        but certain styles can break the positioning logic used. */}
        <Sticky>
          {({
            style,
 
            // the following are also available but unused in this example
            isSticky,
            wasSticky,
            distanceFromTop,
            distanceFromBottom,
            calculatedHeight
          }) => (
            <header style={style}>
              {/* ... */}
            </header>
          )}
        </Sticky>
        {/* ... */}
      </StickyContainer>
    );
  },
}sticky上可以添加不同的属性
<StickyContainer>
  ...
  <Sticky topOffset={80} bottomOffset={80} disableCompensation>
    { props => (...) }
  </Sticky>
  ...
</StickyContainer>加密
react文字循环的小组件
安装
npm install react-text-loop使用
import TextLoop from "react-text-loop";
import Link from "react-router";
import { BodyText } from "./ui";
class App extends Component {
    render() {
        return (
            <h2>
                <TextLoop>
                    <span>First item</span>
                    <Link to="/">Second item</Link>
                    <BodyText>Third item</BodyText>
                </TextLoop>{" "}
                and something else.
            </h2>
        );
    }
}cookie插件
npm install js-cookie --save引用
import Cookies from 'js-cookie'
//设置cookie
Cookies.set('name','value',{expire:7,path:''}); //7天过期
Cookies.set('name',{foo:'bar'}); //设置一个json
//获取cookie
Cookies.get('name'); //获取cookie
Cookies.get();  //读取所有cookie
//删除cookie
Cookies.remove('name'); //删除cookiereact-color是一个拾色器,通过它获取颜色值
安装
npm i react-color -S使用
import { TwitterPicker } from 'react-dom'
function () {
  render() {
    <TwitterPicker 
      width="240px"
      
      />
  }
}按钮组件
安装
npm install ladda使用
import * as Ladda from 'ladda';
// Create a new instance of ladda for the specified button
var l = Ladda.create(document.querySelector('.my-button'));
// Start loading
l.start();
// Will display a progress bar for 50% of the button width
l.setProgress(0.5);
// Stop loading
l.stop();
// Toggle between loading/not loading states
l.toggle();
// Check the current state
l.isLoading();
// Delete the button's ladda instance
l.remove();Millionjs是一个轻量级的虚拟DOM库。
据开发者称像React和Svelte的结合体,不需要编译
安装
npm install million使用
m()函数创建虚拟dom,render()渲染
import { m, className, style } from 'million';
const vnode = m('div', { key: 'foo' }, ['Hello World']);
const vnode = m(
  'div',
  {
    className: className({ class1: true, class2: false, class3: 1 + 1 === 2 }),
    style: style({ color: 'black', 'font-weight': 'bold' }),
  },
  ['Hello World'],
);
import { _, m, render } from 'million';
let seconds = 0;
setInterval(() => {
  render(document.body, m(vnode));
  seconds++;
}, 1000);createElement创建元素
import { m, createElement, Flags } from 'million';
const vnode = m(
  'div',
  { id: 'app' },
  ['Hello World'],
  Flags.ELEMENT_TEXT_CHILDREN,
);
const el = createElement(vnode);
document.body.appendChild(el);在.bablerc中配置插件
{
  "plugins": [
    [
      "@babel/plugin-transform-react-jsx",
      {
        "runtime": "automatic",
        "importSource": "million"
      }
    ]
  ]
}Markdown js编辑器
有纯js 版本、react版本和vue版本
安装
npm install --save @toast-ui/react-editor使用
import '@toast-ui/editor/dist/toastui-editor.css';
import { Editor } from '@toast-ui/react-editor';
class MyComponent extends React.Component {
  editorRef = React.createRef();
  handleClick = () => {
    this.editorRef.current.getInstance().exec('Bold');
  };
  handleFocus = () => {
    console.log('focus!!');
  };
  render() {
    return (
      <>
        <Editor
          previewStyle="vertical"
          height="400px"
          initialEditType="markdown"
          initialValue="hello"
          ref={this.editorRef}
  				onFocus={this.handleFocus}
        />
        <button onClick={this.handleClick}>make bold</button>
      </>
    );
  }
}react中响应式显示宽高的组件
import useDimensions from "react-cool-dimensions";
const Card = () => {
  const { observe, currentBreakpoint } = useDimensions({
    // The "currentBreakpoint" will be the object key based on the target's width
    // for instance, 0px - 319px (currentBreakpoint = XS), 320px - 479px (currentBreakpoint = SM) and so on
    breakpoints: { XS: 0, SM: 320, MD: 480, LG: 640 },
    // Will only update the state on breakpoint changed, default is false
    updateOnBreakpointChange: true,
    onResize: ({ currentBreakpoint }) => {
      // Now the event callback will be triggered when breakpoint is changed
      // we can also access the "currentBreakpoint" here
    },
  });
  return (
    <div class={`card ${currentBreakpoint}`} ref={observe}>
      <div class="card-header">I'm 😎</div>
      <div class="card-body">I'm 👕</div>
      <div class="card-footer">I'm 👟</div>
    </div>
  );
};也可以使用polyfill的resize-observer包
import { ResizeObserver, ResizeObserverEntry } from "@juggle/resize-observer";
if (!("ResizeObserver" in window)) {
  window.ResizeObserver = ResizeObserver;
  // Only use it when you have this trouble: https://github.com/wellyshen/react-cool-dimensions/issues/45
  // window.ResizeObserverEntry = ResizeObserverEntry;
}文本对齐组件
安装
npm install react-wrap-balancer使用
import Balancer from 'react-wrap-balancer'
// ...
<h1>
  <Balancer>My Title</Balancer>
</h1>也可以使用Provider包裹所有的组建
import { Provider } from 'react-wrap-balancer'
// ...
<Provider>
  <App/>
</Provider>像excel一样操作数据,适合特殊场景下
安装
npm install hyperformula使用
import { HyperFormula } from 'hyperformula';
// define the options
const options = {
  licenseKey: 'gpl-v3',
};
// define the data
const data = [['10', '20', '30', '=SUM(A1:C1)']];
// build an instance with defined options and data 
const hfInstance = HyperFormula.buildFromArray(data, options);
// call getCellValue to get the calculation results
const mySum = hfInstance.getCellValue({ col: 3, row: 0, sheet: 0 });
// print the result in the browser's console
console.log(mySum);检查当前代码系统中没有被引用的文件
$ npx unimportedhttps://www.npmjs.com/package/unimported
检查react代码,避免不必要的渲染,可以使用在react16 react17和react18, RN中也同样可以使用
安装
npm install @welldone-software/why-did-you-render --save-dev使用
创建一个wdyr.js, 并在应用的最开始引入
import React from 'react';
if (process.env.NODE_ENV === 'development') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React, {
    trackAllPureComponents: true,
  });
}虚拟dom库
安装
npm install --save deku使用
/** @jsx element */
import {element, createApp} from 'deku'
import {createStore} from 'redux'
import reducer from './reducer'
// Dispatch an action when the button is clicked
let log = dispatch => event => {
  dispatch({
    type: 'CLICKED'
  })
}
// Define a state-less component
let MyButton = {
  render: ({ props, children, dispatch }) => {
    return <button onClick={log(dispatch)}>{children}</button>
  }
}
// Create a Redux store to handle all UI actions and side-effects
let store = createStore(reducer)
// Create an app that can turn vnodes into real DOM elements
let render = createApp(document.body, store.dispatch)
// Update the page and add redux state to the context
render(
  <MyButton>Hello World!</MyButton>,
  store.getState()
)react图表组件
pnpm add react-chartjs-2 chart.js
# or
yarn add react-chartjs-2 chart.js
# or
npm i react-chartjs-2 chart.js使用
import { Doughnut } from 'react-chartjs-2';
<Doughnut data={...} />捕获await的错误,相当于try-catch的语法糖
npm i await-to-js --save使用
import to from 'await-to-js';
// If you use CommonJS (i.e NodeJS environment), it should be:
// const to = require('await-to-js').default;
async function asyncTaskWithCb(cb) {
     let err, user, savedTask, notification;
     [ err, user ] = await to(UserModel.findById(1));
     if(!user) return cb('No user found');
     [ err, savedTask ] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
     if(err) return cb('Error occurred while saving task');
    if(user.notificationsEnabled) {
       [ err ] = await to(NotificationService.sendNotification(user.id, 'Task Created'));
       if(err) return cb('Error while sending notification');
    }
    if(savedTask.assignedUser.id !== user.id) {
       [ err, notification ] = await to(NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'));
       if(err) return cb('Error while sending notification');
    }
    cb(null, savedTask);
}
async function asyncFunctionWithThrow() {
  const [err, user] = await to(UserModel.findById(1));
  if (!user) throw new Error('User not found');
  
}解析路由 queryString
var qs = require('qs');
var assert = require('assert');
var obj = qs.parse('a=c');
assert.deepEqual(obj, { a: 'c' });
var str = qs.stringify(obj);
assert.equal(str, 'a=c');生成react swagger文档
react中做prefetch的组件库
npm install quicklink webpack-route-manifest --save-dev使用
import { withQuicklink } from 'quicklink/dist/react/hoc.js';
const options = {
  origins: []
};
<Suspense fallback={<div>Loading...</div>}>
  <Route path="/" exact component={withQuicklink(Home, options)} />
  <Route path="/blog" exact component={withQuicklink(Blog, options)} />
  <Route path="/blog/:title" component={withQuicklink(Article, options)} />
  <Route path="/about" exact component={withQuicklink(About, options)} />
</Suspense>在写前端的前两年,我总是热衷于找寻各种各样的组件,试图不断地充实自己使用过的组件库
但是随着前端技术的积累,我发现使用组件总是很简单的,难的是体会组件的设计思路、并能够自己根据实际情况进行修改
所以提醒自己还需潜心修炼js和多写不同场景的代码 只是沉迷组件的话。进步不大
https://ant.design/docs/react/recommendation-cn
Ant design站点下有推荐社区比较好的组件,可以在其中找到合适的组件使用