成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

React 造輪子系列:Icon 組件思路

stormzhang / 1732人閱讀
簡(jiǎn)介

本輪子是通過(guò) React + TypeScript + Webpack 搭建的,至于環(huán)境的搭建這邊就不在細(xì)說(shuō)了,自己動(dòng)手谷歌吧。當(dāng)然可以參考我的源碼。

這里我也是通過(guò)別人學(xué)的,主要做些總結(jié)及說(shuō)明造各個(gè)輪子的一種思路,方便今后使用別人的的輪子時(shí)自己腦中有造輪子的思想,能通過(guò)修改源碼及時(shí)修改 bug,按時(shí)上線。

本文的 Icon 組件主要是參考 Framework7 中的 Icon React Component 寫的。

想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你!

為什么要造輪子

1.為了不求人

假設(shè)你使用某個(gè)UI框架發(fā)現(xiàn)有一個(gè) bug,于是你反饋給開發(fā)者,開發(fā)者說(shuō)兩周后修復(fù),而你的項(xiàng)目一周后就要上線,你怎么辦?

為什么很多大公司都不使用其他公司的輪子,要自己造?為了把控自己的業(yè)務(wù),不被別人牽著走。

2.為了不流于平庸

大家都是寫增刪改查,你跟別人比有什么優(yōu)勢(shì)?你如果能說(shuō)一局【我公司的人都在用我寫的UI框架】是不是就很牛逼?造 UI 輪子會(huì)遇到很多技術(shù)層面而非業(yè)務(wù)層面的知識(shí)?比如一些算法。

3.為了創(chuàng)造

你為別人做了這么久的事情,有沒(méi)有自己做什么?自驅(qū)動(dòng)力。

4.為什么是 UI 輪子,不是其他方面的輪子

比如,為什么不自己寫一個(gè) React 框架,要寫 React UI 框架呢?

React.FunctionComponent 與 IconPropps

本輪子使用 React + TypeScript 來(lái)寫的,那么在 ts 中如何聲明函數(shù)組件及級(jí) Icon 組件傳遞參數(shù)呢,答案是使用React提供的靜態(tài)方法 React.FunctionComponent 及 TypeScript 提供的接口定義。

// lib/icon.tsx

import React from "react"

interface IconProps {
  name: string
}

const Icon: React.FunctionComponent = () => {
  return (
    icon
  )
}

export default Icon

在 index.txt 中調(diào)用:

import React from "react";
import ReactDOM from "react-dom";
import Icon from "./icon"
  
ReactDOM.render(
, document.body)

對(duì)于上面的定義方式,后面的輪子會(huì)經(jīng)常使用,所以不必?fù)?dān)心看不懂。

使用 svg-sprite-loader 加載 SVG

在上面我們指定了 Iconnamewechat,那怎么讓它顯示微信的圖標(biāo)呢,首先在阿里的 Iconfont 下載對(duì)應(yīng)的 SVG

接著如何顯示 svg");rules 中添加:

{
  test: /.svg$/,
  loader: "svg-sprite-loader"
}

在 Icon 中引用,當(dāng)然對(duì)應(yīng) tsconfig.json 也要配置(這不是本文的重點(diǎn)):

import React from "react"
import wechat from "./icons/wechat.svg"

console.log(wechat)
interface IconProps {
  name: string
}

const Icon: React.FunctionComponent = () => {
  return (
    
      
        
      
    
  )
}

export default Icon

運(yùn)行效果:

當(dāng)然 svg 里面不能直接寫死,我們需要根據(jù)外部傳入的 name 來(lái)指定對(duì)應(yīng)的圖像:

// 部分代碼
import  "./icons/wechat.svg"
import "./icons/alipay.svg"

const Icon: React.FunctionComponent = (props) => {
  return (
    
      
        
      
    
  )
}

外部調(diào)用:

ReactDOM.render(
, document.getElementById("root"))

運(yùn)行效果:

importAll

大家有沒(méi)有注意到,我需要使用哪個(gè) svg, 需要在對(duì)應(yīng)的 icon 組件導(dǎo)入對(duì)應(yīng)的 svg,這樣要是我需要100個(gè) svg ,我就要導(dǎo)入100次,這樣做太傻,文件也會(huì)變得冗長(zhǎng)。

因此我們需要一個(gè)動(dòng)態(tài)導(dǎo)入全部 SVG 的方法:

 // lib/importIcons.js
let importAll = (requireContext) => requireContext.keys().forEach(requireContext)
try {
  importAll(require.context("./icons/", true, /.svg$/))
} catch (error) {
  console.log(error)
}

要想看懂上訴的代碼,可能需要一點(diǎn) node.js 的基礎(chǔ),這邊建議你直接收藏好啦,下次有用到,直接拷貝過(guò)來(lái)用就行了。

接著在 Icon 組件里面導(dǎo)入就行了: import "./importIcons"

React.MouseEventHandler 的使用

當(dāng)我們需要給 Icon 注冊(cè)事件的時(shí)候,如果直接在組件上寫 onClick 事件是會(huì)報(bào)錯(cuò)的,因?yàn)樗鼪](méi)有聲明接收 onClick 事件類型,所以需要聲明,如下所示:

/lib/icon.tsx

import React from "react"
import "./importIcons"
import "./icon.scss";
interface IconProps {
  name: string,
  onClick: React.MouseEventHandler
}

const Icon: React.FunctionComponent = (props) => {
  return (
    
      
        
      
    
  )
}

export default Icon

調(diào)用方式如下:

import React from "react";
import ReactDOM from "react-dom";
import Icon from "./icon"

const fn: React.MouseEventHandler = (e) => {
  console.log(e.target);
};


ReactDOM.render(
, document.getElementById("root"))
讓Icon響應(yīng)所有事件

上述我們只監(jiān)聽了 onClick 事件 ,但對(duì)于其它事件是不支持了,所以我們需要進(jìn)一步完善。這里我們不能一個(gè)一個(gè)添加對(duì)應(yīng)的事件類型,需要一個(gè)統(tǒng)一的事件類型,那這個(gè)是什么呢?

通過(guò) react 我們會(huì)找到一個(gè) SVGAttributes 類,這里我們需要繼承它:

/lib/icon.tsx
import React from "react"
import "./importIcons"
import "./icon.scss";
interface IconProps extends React.SVGAttributes {
  name: string;
}

const Icon: React.FunctionComponent = (props) => {
  return (
    
      
        
      
    
  )
}

export default Icon

調(diào)用方式:

import React from "react";
import ReactDOM from "react-dom";
import Icon from "./icon"

const fn: React.MouseEventHandler = (e) => {
  console.log(e.target);
};


ReactDOM.render(
console.log("enter")} onMouseLeave = { () => console.log("leave")} />
, document.getElementById("root"))

上述還是會(huì)有問(wèn)題,我們還有 onFocus, onBlur, onChange 等等事件,也不可能一個(gè)一個(gè)傳遞進(jìn)來(lái),那還有什么方法呢。

icon.tsx 中我們會(huì)發(fā)現(xiàn)我們用的都是通過(guò) props 傳遞進(jìn)來(lái)的。聰明的朋友的可能立馬想到了使用展開運(yùn)算符的形式 {...props},改寫如下:

...
const Icon: React.FunctionComponent = (props) => {
  return (
    
      
        
      
    
  )
}
...

上述還是會(huì)有問(wèn)題,如果使用的人也傳入 className 呢,用過(guò) Vue 就知道 Vue 是真的好,它會(huì)把傳入和里面的合并起來(lái),但 React 就不一樣了,傳入的會(huì)覆蓋里面的,所以需要自己手動(dòng)處理:

...
const Icon: React.FunctionComponent = (props) => {
  const { className, ...restProps} = props
  return (
    
      
        
      
    
  )
}
...

上達(dá)寫法還存在問(wèn)題的,如果外面沒(méi)有寫 className ,那么內(nèi)部會(huì)多出一個(gè) undefined

聰明你的可能就想到了使用三目運(yùn)算符來(lái)做判斷,如:

className={`fui-icon ${className ");

但這種情況如果有多個(gè)參數(shù)要怎么辦呢?

所以有人就非常聰明專門寫了一個(gè)庫(kù)存 classnames,這個(gè)庫(kù)有多火呢,每周有300多萬(wàn)的下載量,它的作用就是處理 className 的情況。

當(dāng)然我們這邊只做簡(jiǎn)單的處理,如下所示

// helpers/classes
function classes(...names:(string | undefined )[]) {
  return names.join(" ")
}

export default classes

使用方式:

...
const Icon: React.FunctionComponent = (props) => {
  const { className, name,...restProps} = props
  return (
    
      
        
      
    
  )
}
...

這樣最終渲染出來(lái)的 className還是會(huì)多出一個(gè)空格,作為完美者,并不希望有空格的出現(xiàn)的,所以需要進(jìn)一步處理空格,這里使用 es6 中數(shù)組的 filters 方法。

// helpers/classes
function classes(...names:(string | undefined )[]) {
  return names.filter(Boolean).join(" ")
}

export default classes
單元測(cè)試

首先我們對(duì)我們的 classes 方法時(shí)行單元測(cè)試,這里使用 Jest 時(shí)行測(cè)試,也是 React 官網(wǎng)推薦的。

classes 測(cè)試用例如下:

import classes from "../classes"
describe("classes", () => {
  it("接受 1 個(gè) className", () => {
    const result = classes("a")
    expect(result).toEqual("a")
  })
  it("接受 2 個(gè) className", ()=>{
    const result = classes("a", "b")
    expect(result).toEqual("a b")
  })
  it("接受 undefined 結(jié)果不會(huì)出現(xiàn) undefined", ()=>{
    const result = classes("a", undefined)
    expect(result).toEqual("a")
  })
  it("接受各種奇怪值", ()=>{
    const result = classes(
      "a", undefined, "中文", false, null
    )
    expect(result).toEqual("a 中文")
  })
  it("接受 0 個(gè)參數(shù)", ()=>{
    const result = classes()
    expect(result).toEqual("")
  })
})
使用Snapshot測(cè)試UI

這里測(cè)試 UI 相關(guān)還需要使用一個(gè)庫(kù) Enzyme , Enzyme 來(lái)自 airbnb 公司,是一個(gè)用于 React 的 JavaScript 測(cè)試工具,方便你判斷、操縱和歷遍 React Components 輸出。Enzyme 的 API 通過(guò)模仿 jQuery 的 API ,使得 DOM 操作和歷遍很靈活、直觀。Enzyme 兼容所有的主要測(cè)試運(yùn)行器和判斷庫(kù)。

icon 的測(cè)試用例

import * as renderer from "react-test-renderer"
import React from "react"
import Icon from "../icon"
import {mount} from "enzyme"

describe("icon", () => {
  it("render successfully", () => {
    const json = renderer.create().toJSON()
    expect(json).toMatchSnapshot()
  })
  it("onClick", () => {
    const fn = jest.fn()
    const component = mount()
    component.find("svg").simulate("click")
    expect(fn).toBeCalled()
  })
})
IDE 提示找不到 describe 和 it 怎么辦?

解決辦法:

    yarn add -D @types/jest

    在文件開頭加一句 import "jest"

這是因?yàn)?describe 和 it 的定于位于 jest 的類型聲明文件中,不信你可以按住 ctrl 并點(diǎn)擊 jest 查看。

如果還不行,你需要在 WebStorm 里設(shè)置對(duì) jest 的引用:

這是因?yàn)?typescript 默認(rèn)排除了 node_modules 里的類型聲明。

總結(jié)

以上主要是在學(xué)習(xí)造輪子過(guò)程總結(jié)的,環(huán)境搭建就沒(méi)有細(xì)說(shuō)了,主要記錄實(shí)現(xiàn) Icon 輪子的一些思路及注意事項(xiàng)等,想看源碼,跑跑看的,可以點(diǎn)擊這里查看。

參考

方應(yīng)杭老師的React造輪子課程

歡迎加入前端大家庭,里面會(huì)經(jīng)常分享一些技術(shù)資源。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/6838.html

相關(guān)文章

  • React 輪子系列Icon 組件思路

    簡(jiǎn)介 本輪子是通過(guò) React + TypeScript + Webpack 搭建的,至于環(huán)境的搭建這邊就不在細(xì)說(shuō)了,自己動(dòng)手谷歌吧。當(dāng)然可以參考我的源碼。 這里我也是通過(guò)別人學(xué)的,主要做些總結(jié)及說(shuō)明造各個(gè)輪子的一種思路,方便今后使用別人的的輪子時(shí)自己腦中有造輪子的思想,能通過(guò)修改源碼及時(shí)修改 bug,按時(shí)上線。 本文的 Icon 組件主要是參考 Framework7 中的 Icon React ...

    instein 評(píng)論0 收藏0
  • React系列:Layout 組件思路

    摘要:本文是造輪系列第三篇。造輪子系列組件思路造輪系列對(duì)話框組件思路想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳博客一年百來(lái)篇優(yōu)質(zhì)文章等著你初始化參考組件分別分為五個(gè)組件。參考方應(yīng)杭老師的造輪子課程交流干貨系列文章匯總?cè)缦?,覺(jué)得不錯(cuò)點(diǎn)個(gè),歡迎加群互相學(xué)習(xí)。 本文是React造輪系列第三篇。 1.React 造輪子系列:Icon 組件思路 2.React造輪系列:對(duì)話框組件 - Dialog 思路 想閱讀更多優(yōu)質(zhì)...

    neroneroffy 評(píng)論0 收藏0
  • React系列:對(duì)話框組件 - Dialog 思路

    摘要:本文是造輪系列第二篇。實(shí)現(xiàn)方式事件處理跟差不多,唯一多了一步就是當(dāng)點(diǎn)擊或者的時(shí)候,如果外部有回調(diào)就需要調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)。 本文是React造輪系列第二篇。 1.React 造輪子系列:Icon 組件思路 本輪子是通過(guò) React + TypeScript + Webpack 搭建的,至于環(huán)境的搭建這邊就不在細(xì)說(shuō)了,自己動(dòng)手谷歌吧。當(dāng)然可以參考我的源碼。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳Git...

    qianfeng 評(píng)論0 收藏0
  • 精讀《怎么用 React Hooks 輪子

    摘要:可以看到,這樣不僅沒(méi)有占用組件自己的,也不需要手寫回調(diào)函數(shù)進(jìn)行處理,這些處理都?jí)嚎s成了一行。效果通過(guò)拿到周期才執(zhí)行的回調(diào)函數(shù)。實(shí)現(xiàn)等價(jià)于的回調(diào)僅執(zhí)行一次時(shí),因此直接把回調(diào)函數(shù)拋出來(lái)即可。 1 引言 上周的 精讀《React Hooks》 已經(jīng)實(shí)現(xiàn)了對(duì) React Hooks 的基本認(rèn)知,也許你也看了 React Hooks 基本實(shí)現(xiàn)剖析(就是數(shù)組),但理解實(shí)現(xiàn)原理就可以用好了嗎?學(xué)的是...

    Shihira 評(píng)論0 收藏0
  • 手摸手,帶你封裝一個(gè)vue component

    摘要:靈活性和針對(duì)性。所以我覺(jué)得大部分組件還是自己封裝來(lái)的更為方便和靈活一些。動(dòng)手開干接下來(lái)我們一起手摸手教改造包裝一個(gè)插件,只要幾分鐘就可以封裝一個(gè)專屬于你的。 項(xiàng)目地址:vue-countTo配套完整后臺(tái)demo地址:vue-element-admin系類文章一:手摸手,帶你用vue擼后臺(tái) 系列一(基礎(chǔ)篇)系類文章二:手摸手,帶你用vue擼后臺(tái) 系列二(登錄權(quán)限篇)系類文章三:手摸手,帶...

    pkhope 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<