https://juejin.im/post/5e34fe4ee51d4557e632f143css
項目地址:react-draggable-layer前端
技術博客:www.linxiangjun.comreact
前言
2019年以來,愈來愈多的公司與項目都選擇使用強類型語言TypeScript做爲主要語言,能夠預見的是2020年TypeScript將成爲前端開發所須要具有的能力之一。git
目前前端工程師使用最多的無疑問是微軟開源的VSCode,它自己提供的功能並很少,可是經過支持插件的方式使得其具有了IDE的強大能力。同時,VSCode是使用TypeScript基於Eletron開發的,使得它對於TypeScript的支持很是的強大。若是你使用的編輯器是VSCode,那麼項目中徹底使用TypeScript編程將會大大的提升編程效率,甚至使用一段時間後你會發現,已經漸漸離不開TypeScript。github
應用新的語言仍是語法都會面臨着許多困難和陷阱,這個時候若是可以參考一些現成的寫法確定會帶來收益。這也是我寫這篇文章的初衷。npm
下面是react-draggable-layer使用到的一些技術細節:編程
- TypeScript
- React Hooks
- React Portals
- emotion
- Babel
實現
![效果動畫](http://static.javashuo.com/static/loading.gif)
Portals
實現一個彈層類組件,React提供了Portals
方法,可以將子節點渲染到存在於父組件之外的DOM節點上。咱們先實現一個封裝了Portals
方法的通用組件:前端工程師
components/Portal.jsxapp
import React, { useEffect, useRef } from "react"; import ReactDOM from "react-dom"; interface PortalProps { children: React.ReactElement; } const Portal: React.FC<PortalProps> = props => { // 使用useRef建立ref對象,能夠用來保存節點 const { current: el } = useRef(document.createElement("div")); // 等同於類組件的componentDidMount方法 useEffect(() => { const root = document.getElementById("root"); if (root) { root.appendChild(el); } // 組件銷燬後記得移除div return () => { if (root) { root.removeChild(el); } }; }, []); return ReactDOM.createPortal(props.children, el); }; export default Portal; 複製代碼
css-in-js
有了Portal組件以後在可拖拽彈層組件中引入,這樣組件在使用時就能夠掛載到root
節點下。在組件中樣主要是以css-in-js的方式來實現的,它的優勢有:dom
- 不須要引入css樣式文件或者css預處理器
- 可避免由於樣式名衝突帶來的全局污染問題
- 不須要各類繁瑣的命名規則
這並非說它就沒有缺點的,一樣的,使用css-in-js來開發會使得不少高階css用法很是的複雜,因此須要根據項目狀況來辯證的思考與選擇。不過對於此項目來講css-in-js就很是的合適,它使得咱們能夠將更多的精力放在組件化上面。另外這裏推薦下emotion這個庫。
React.FC
這裏先來基於項目說一下TypeScript開發React函數式組件的規範:
interface DraggableLayerProps { children?: React.ReactElement; titleName?: string; visible?: boolean; onClose?: () => void; } const DraggableLayer: React.FC<DraggableLayerProps> = props => <Component /> export default DraggableLayer 複製代碼
使用函數式組件時須要將組件申明爲React.FC
類型,也就是Functional Component的意思,另外props
須要申明各個參數的類型,而後經過泛型傳遞給React.FC
。
draggable
可拖拽功能的實現是基於實時計算組件x軸與y軸的距離來實現的,具體代碼比較簡潔:
let initX = 0; let initY = 0; let isMouseDown = false; const handleMouseDown = (e: MouseEvent) => { isMouseDown = true; initX = e.offsetX; initY = e.offsetY; draggableRef.current.style.opacity = 0.5; }; const handleMouseMove = (e: MouseEvent) => { if (isMouseDown) { const x = e.clientX - initX; const y = e.clientY - initY; draggableRef.current.style.left = `${x}px`; draggableRef.current.style.top = `${y}px`; } }; const handleMouseUp = () => { isMouseDown = false; if (draggableRef && draggableRef.current) { draggableRef.current.style.opacity = 1; } }; 複製代碼
穩健性
在組件獲取props
時有不少可選的參數,好比titleName
,由於使用了TypeScript,因此對可選參數進行賦值操做時提示錯誤信息,這點在開發時可以有效的避免出錯。另外,組件銷燬後須要在useEffect
的return
回調函數中清除綁定的event。這些細節須要注意。
總結
很是歡迎嘗試本項目,能夠直接使用npm install react-draggable-layer
來使用,有好的建議也但願可以收到提交的issues。一些更加詳細的使用說明在項目中的README文件中。若是本文或者項目對你有幫助的話,很是高興您可以點下star支持。