摘要:很久沒上掘金發現草稿箱里存了好幾篇沒發的文章最近梳理下發出來往期面試官系列如何實現深克隆面試官系列的實現面試官系列前端路由的實現面試官系列基于數據劫持的雙向綁定優勢所在面試官系列你為什么使用前端框架前言設計前端組件是最能考驗開發者基本功的測
往期很久沒上掘金,發現草稿箱里存了好幾篇沒發的文章,最近梳理下發出來
面試官系列(1): 如何實現深克隆
面試官系列(2): Event Bus的實現
面試官系列(3): 前端路由的實現
面試官系列(4): 基于Proxy 數據劫持的雙向綁定優勢所在
面試官系列(5): 你為什么使用前端框架
設計前端組件是最能考驗開發者基本功的測試之一,因為調用Material design、Antd、iView 等現成組件庫的 API 每個人都可以做到,但是很多人并不知道很多常用組件的設計原理。
能否設計出通用前端組件也是區分前端工程師和前端api調用師的標準之一,那么應該如何設計出一個通用組件呢");
下文中提到的組件庫通常是指單個組件,而非集合的概念,集合概念的組件庫是 Antd iView這種,我們所說的組件庫是指集合中的單個組件,集合性質的組件庫需要考慮的要更多.
前端組件庫的設計原則
組件庫的技術選型
如何快速啟動一個組件庫項目
如何設計一個輪播圖組件
我們在學習設計模式的時候會遇到很多種設計原則,其中一個設計原則就是單一職責原則,在組件庫的開發中同樣適用,我們原則上一個組件只專注一件事情,單一職責的組件的好處很明顯,由于職責單一就可以最大可能性地復用組件,但是這也帶來一個問題,過度單一職責的組件也可能會導致過度抽象,造成組件庫的碎片化。
舉個例子,一個自動完成組件(AutoComplete),他其實是由 Input 組件和 Select 組件組合而成的,因此我們完全可以復用之前的相關組件,就比如 Antd 的AutoComplete組件中就復用了Select組件,同時Calendar、 Form 等等一系列組件都復用了 Select 組件,那么Select 的細粒度就是合適的,因為 Select 保持的這種細粒度很容易被復用.
那么還有一個例子,一個徽章數組件(Badge),它的右上角會有紅點提示,可能是數字也可能是 icon,他的職責當然也很單一,這個紅點提示也理所當然也可以被多帶帶抽象為一個獨立組件,但是我們通常不會將他作為獨立組件,因為在其他場景中這個組件是無法被復用的,因為沒有類似的場景再需要小紅點這個小組件了,所以作為獨立組件就屬于細粒度過小,因此我們往往將它作為 Badge 的內部組件,比如在 Antd 中它以ScrollNumber的名稱作為Badge的內部組件存在。
所以,所謂的單一職責組件要建立在可復用的基礎上,對于不可復用的單一職責組件我們僅僅作為獨立組件的內部組件即可。
我們要設計的本身就是通用組件庫,不同于我們常見的業務組件,通用組件是與業務解耦但是又服務于業務開發的,那么問題來了,如何保證組件的通用性,通用性高一定是好事嗎");
比如我們設計一個選擇器(Select)組件,通常我們會設計成這樣
這是一個我們最常見也最常用的選擇器,但是問題是其通用性大打折扣
當我們有一個需求是長這樣的時候,我們之前的選擇器組件就不符合要求了,因為這個 Select 組件的最下部需要有一個可拓展的條目的按鈕
這個時候我們難道要重新修改之前的選擇器組件,甚至再造一個符合要求的選擇器組件嗎");
Antd 的 Select 組件預留了dropdownRender來進行自定義渲染,其依賴的 rc-select組件中的代碼如下
Antd 依賴了大量以rc-開頭的底層組件,這些組件被react-component團隊(同時也就是Antd 團隊)維護,其主要實現組件的底層邏輯,Antd 則是在此基礎上添加Ant Design設計語言而實現的
當然類似的設計還有很多,通用性設計其實是一定意義上放棄對 DOM 的掌控,而將 DOM 結構的決定權轉移給開發者,dropdownRender其實就是放棄對 Select 下拉菜單中條目的掌控,Antd 的 Select 組件其實還有一個沒有在文檔中體現的方法getInputElement應該是對 Input 組件的自定義方法,Antd整個 Select 的組件設計非常復雜,基本將所有的 DOM 結構控制權全部暴露給了開發者,其本身只負責底層邏輯和最基本的 DOM 結構.
這是 Antd 所依賴的 re-select 最終 jsx 的結構,其 DOM 結構很簡單,但是暴露了大量自定義渲染的接口給開發者.
return (
<SelectTrigger
onPopupFocus={this.onPopupFocus}
onMouseEnter={this.props.onMouseEnter}
onMouseLeave={this.props.onMouseLeave}
dropdownAlign={props.dropdownAlign}
dropdownClassName={props.dropdownClassName}
dropdownMatchSelectWidth={props.dropdownMatchSelectWidth}
defaultActiveFirstOption={props.defaultActiveFirstOption}
dropdownMenuStyle={props.dropdownMenuStyle}
transitionName={props.transitionName}
animation={props.animation}
prefixCls={props.prefixCls}
dropdownStyle={props.dropdownStyle}
combobox={props.combobox}
showSearch={props.showSearch}
options={options}
multiple={multiple}
disabled={disabled}
visible={realOpen}
inputValue={state.inputValue}
value={state.value}
backfillValue={state.backfillValue}
firstActiveValue={props.firstActiveValue}
onDropdownVisibleChange={this.onDropdownVisibleChange}
getPopupContainer={props.getPopupContainer}
onMenuSelect={this.onMenuSelect}
onMenuDeselect={this.onMenuDeselect}
onPopupScroll={props.onPopupScroll}
showAction={props.showAction}
ref={this.saveSelectTriggerRef}
menuItemSelectedIcon={props.menuItemSelectedIcon}
dropdownRender={props.dropdownRender}
ariaId={this.ariaId}
>
<div
id={props.id}
style={props.style}
ref={this.saveRootRef}
onBlur={this.onOuterBlur}
onFocus={this.onOuterFocus}
className={classnames(rootCls)}
onMouseDown={this.markMouseDown}
onMouseUp={this.markMouseLeave}
onMouseOut={this.markMouseLeave}
>
<div
ref={this.saveSelectionRef}
key="selection"
className={`${prefixCls}-selection
${prefixCls}-selection--${multiple ");multiple" : "single"}`}
role="combobox"
aria-autocomplete="list"
aria-haspopup="true"
aria-controls={this.ariaId}
aria-expanded={realOpen}
{...extraSelectionProps}
>
{ctrlNode}
{this.renderClear()}
{this.renderArrow(!!multiple)}
div>
div>
SelectTrigger>
);
那么這么多需要自定義的地方,這個 Select 組件豈不是很難用");
組件的形態(DOM結構)永遠是千變萬化的,但是其行為(邏輯)是固定的,因此通用組件的秘訣之一就是將 DOM 結構的控制權交給開發者,組件只負責行為和最基本的 DOM 結構
由于CSS 本身的眾多缺陷,如書寫繁瑣(不支持嵌套)、樣式易沖突(沒有作用域概念)、缺少變量(不便于一鍵換主題)等不一而足。為了解決這些問題,社區里的解決方案也是出了一茬又一茬,從最早的 CSS prepocessor(SASS、LESS、Stylus)到后來的后起之秀 PostCSS,再到 CSS Modules、Styled-Components 等。
Antd 選擇了 less 作為 css 的預處理方案,Bootstrap 選擇了 Scss,這兩種方案孰優孰劣已經爭論了很多年了:
SCSS和LESS相比有什么優勢?
但是不管是哪種方案都有一個很煩人的點,就是需要額外引入 css,比如 Antd 需要這樣顯示引入:
import Button from "antd/lib/button";
import "antd/lib/button/style";
為了解決這種尷尬的情況,Antd 用 Babel 插件將這種情況 Hack 掉了
而material-ui并不存在這種情況,他不需要顯示引入 css,這個最流行的 React 前端組件庫里面只有 js 和 ts 兩種代碼,并不存在 css 相關的代碼,為什么呢");
他們用 jss 作為css-in-js 的解決方案,jsx 的引入已經將 js 和 html 耦合,css-in-js將 css 也耦合進去,此時組件便不需要顯示引入 css,而是直接引用 js 即可.
這不是退化到史前前端那種寫內聯樣式的時代了嗎");
并不是,史前前端的內聯樣式是整個項目耦合的狀態,當然要被拋棄到歷史的垃圾堆中,后來的樣式和邏輯分離,實際上是以頁面為維度將 js css html 解耦的過程,如今的時代是組件化的時代了,jsx 已經將 js 和 html 框定到一個組件中,css 依然處于分離狀態,這就導致了每次引用組件卻還需要顯示引入 css,css-in-js 正式徹底組件化的解決方案.
當然,我個人目前在用 styled-components,其優點引用如下:
首先,styled-components 所有語法都是標準 css 語法,同時支持 scss 嵌套等常用語法,覆蓋了所有 css 場景。
在樣式復寫場景下,styled-components 支持在任何地方注入全局 css,就像寫普通 css 一樣
styled-components 支持自定義 className,兩種方式,一種是用 babel 插件, 另一種方式是使用 styled.div.withConfig({ componentId: "prefix-button-container" }) 相當于添加 className="prefix-button-container"
className 語義化更輕松,這也是 class 起名的初衷
更適合組件庫使用,直接引用 import "module" 即可,否則你有三條路可以走:像 antd 一樣,多帶帶引用 css,你需要給 node_modules 添加 css-loader;組件內部直接 import css 文件,如果任何業務項目沒有 css-loader 就會報錯;組件使用 scss 引用,所有業務項目都要配置一份 scss-loader 給 node_modules;這三種對組件庫來說,都沒有直接引用來的友好
當你寫一套組件庫,需要多帶帶發包,又有統一樣式的配置文件需求,如果這個配置文件是 js 的,所有組件直接引用,對外完全不用關注。否則,如果是 scss 配置文件,擺在面前還是三條路:每個組件多帶帶引用 scss 文件,需要每個業務項目給 node_modules 添加 scss-loader(如果業務用了 less,還要裝一份 scss 是不);或者業務方只要使用了你的組件庫,就要在入口文件引用你的 scss 文件,比如你的組件叫 button,scss 可能叫 common-css,別人聽都沒聽過,還要查文檔;或者業務方在 webpack 配置中多帶帶引用你的 common-css,這也不科學,如果用了3個組件庫,天天改 webpack 配置也很不方便。
當 css 設置了一半樣式,另一半真的需要 js 動態傳入,你不得不 css + css-in-js 混合使用,項目久了,維護的時候發現某些 css-in-js 不變了,可以固化在 css 里,css 里固定的值又因為新去求變得可變了,你又得拿出來放在 css-in-js 里,實踐過就知道有多么煩心。
選 Typescript ,因為巨硬大法好...
可以看看知乎問題下我的回答你為什么不用 Typescript
或者看此文TypeScript體系調研報告
組件的具體實現部分當然是組件庫的核心,但是在現代前端庫中其他部分也必不可少,我們需要一堆工具來輔助我們開發,例如編譯工具、代碼檢測工具、打包工具等等。
市面上打包工具數不勝數,最火爆的當然是需要配置工程師專門配置的webpack,但是在類庫開發領域它有一個強大的對手就是 rollup。
現代市面上主流的庫基本都選擇了 rollup 作為打包工具,包括Angular React 和 Vue, 作為基礎類庫的打包工具 rollup 的優勢如下:
Tree Shaking: 自動移除未使用的代碼, 輸出更小的文件
Scope Hoisting: 所有模塊構建在一個函數內, 執行效率更高
Config 文件支持通過 ESM 模塊格式書寫 可以一次輸出多種格式:
模塊規范: IIFE, AMD, CJS, UMD, ESM Development 與 production 版本: .js, .min.js
雖然上面部分功能已經被 webpack 實現了,但是 rollup 明顯引入得更早,而Scope Hoisting更是殺手锏,由于 webpack 不得不在打包代碼中構建模塊系統來適應 app 開發(模塊系統對于單一類庫用處很小),Scope Hoisting將模塊構建在一個函數內的做法更適合類庫的打包.
由于 JavaScript 各種詭異的特性和大型前端項目的出現,代碼檢測工具已經是前端開發者的標配了,Douglas Crockford最早于2002創造出了 JSLint,但是其無法拓展,具有極強的Douglas Crockford個人色彩,Anton Kovalyov由于無法忍受 JSLint 無法拓展的行為在2011年發布了可拓展的JSHint,一時之間JSHint成為了前端代碼檢測的流行解決方案.
隨后的2013年,Nicholas C. Zakas鑒于JSHint拓展的靈活度不夠的問題開發了全新的基于 AST 的 Lint 工具 ESLint,并隨著 ES6的流行統治了前端界,ESLint 基于Esprima進行 JavaScript 解析的特性極易拓展,JSHint 在很長一段時間無法支持 ES6語法導致被 ESLint 超越.
但是在 Typescript 領域 ESLint 卻處于弱勢地位,TSLint 的出現要比 ESLint 正式支持 Typescript 早很多,目前 TSLint 似乎是 TS 的事實上的代碼檢測工具.
注: 文章成文較早,我也沒想到前陣子 TS 官方欽點了 ESLint,TSLint 失寵了,面向未來的官方標配的代碼檢測工具肯定是 ESLint 了,但是 TSLint 目前依然被大量使用,現在仍然可以放心使用
代碼檢測工具是一方面,代碼檢測風格也需要我們做選擇,市面上最流行的代碼檢測風格應該是 Airbnb 出品的eslint-config-airbnb,其最大的特點就是極其嚴格,沒有給開發者任何選擇的余地,當然在大型前端項目的開發中這種嚴格的代碼風格是有利于協作的,但是作為一個類庫的代碼檢測工具而言并不適合,所以我們選擇了eslint-config-standard這種相對更為寬松的代碼檢測風格.
以下兩種 commit 哪個更嚴謹且易于維護");
最開始使用 commit 的時候我也經常犯下圖的錯誤,直到看到很多明星類庫的 commit 才意識到自己的錯誤,寫好 commit message 不僅有助于他人 review, 還可以有效的輸出 CHANGELOG, 對項目的管理實際至關重要.
目前流行的方案是 Angular 團隊的規范,其關于 head 的大致規范如下:
type: commit 的類型
feat: 新特性
fix: 修改問題
refactor: 代碼重構
docs: 文檔修改
style: 代碼格式修改, 注意不是 css 修改
test: 測試用例修改
chore: 其他修改, 比如構建流程, 依賴管理.
scope: commit 影響的范圍, 比如: route, component, utils, build...
subject: commit 的概述, 建議符合 50/72 formatting
body: commit 具體修改內容, 可以分為多行, 建議符合 50/72 formatting
footer: 一些備注, 通常是 BREAKING CHANGE 或修復的 bug 的鏈接.
當然規范人們不一定會遵守,我最初知道此類規范的時候也并沒有嚴格遵循,因為人總會偷懶,直到用commitizen將此規范集成到工具流中,每個 commit 就不得不遵循規范了.
我具體參考了這篇文章: 優雅的提交你的 Git Commit Message
業務開發中由于前端需求變動頻繁的特性,導致前端對測試的要求并沒有后端那么高,后端業務邏輯一旦定型變動很少,比較適合測試.
但是基礎類庫作為被反復依賴的模塊和較為穩定的需求是必須做測試的,前端測試庫也可謂是種類繁多了,經過比對之后我還是選擇了目前最流行也是被三大框架同時選擇了的 Jest 作為測試工具,其優點很明顯:
開箱即用,內置斷言、測試覆蓋率工具,如果你用 MoCha 那可得自己手動配置 n 多了
快照功能,Jest 可以利用其特有的快照測試功能,通過比對 UI 代碼生成的快照文件
速度優勢,Jest 的測試用例是并行執行的,而且只執行發生改變的文件所對應的測試,提升了測試速度
當然以上是主要工具的選擇,還有一些比如:
代碼美化工具 prettier,解放人肉美化,同時利于不同人協作的風格一致
持續集成工具 travis-ci,解放人肉測試 lint,利于保證每次 push 的可靠程度
那么以上這么多配置難道要我們每次都自己寫嗎");
我們在建立 APP 項目時通常會用到框架官方提供的腳手架,比如 React 的 create-react-app,Angular 的 Angular-Cli 等等,那么能不能有一個專門用于組件開發的快速啟動的腳手架呢");
有的,我最近開發了一款快速啟動組件庫開發的命令行工具--create-component
利用
create-component init
來快速啟動項目,我們提供了豐富的可選配置,只要你做好技術選型后,根據提示去選擇配置即可,create-component 會自動根據配置生成腳手架,其靈感就來源于 vue-cli和 Angular-cli.
說了很多理論,那么實戰如何呢");
輪播圖(Carousel),在 Antd 中被稱為走馬燈,可能是前端開發者最常見的組件之一了,不管是在 PC 端還是在移動端我們總能見到他的身影.
那么我們通常是如何使用輪播圖的呢");
<Carousel>
<div><h3>1h3>div>
<div><h3>2h3>div>
<div><h3>3h3>div>
<div><h3>4h3>div>
Carousel>
問題是我們在Carousel中放入了四組div為什么一次只顯示一組呢");
圖中被紅框圈住的為可視區域,可視區域的位置是固定的,我們只需要移動后面div的位置就可以做到1 2 3 4四個子組件輪播的效果,那么子組件2目前在可視區域是可以被看到的,1 3 4應該被隱藏,這就需要我們設置overflow 屬性為 hidden來隱藏非可視區域的子組件.
復制查看動圖: images2015.cnblogs.com/blog/979044…
因此就比較明顯了,我們設計一個可視窗口組件Frame,然后將四個 div共同放入幻燈片組合組件SlideList中,并用SlideItem分別將 div包裹起來,實際代碼應該是這樣的:
<Frame>
<SlideList>
<SlideItem>
<div><h3>1h3>div>
SlideItem>
<SlideItem>
<div><h3>2h3>div>
SlideItem>
<SlideItem>
<div><h3>3h3>div>
SlideItem>
<SlideItem>
<div><h3>4h3>div>
SlideItem>
SlideList>
Frame>
我們不斷利用translateX來改變SlideList的位置來達到輪播效果,如下圖所示,每次輪播的觸發都是通過改變transform: translateX()來操作的
搞清楚基本原理那么實現起來相對容易了,我們以移動端的實現為例,來實現一個基礎的移動端輪播圖.
首先我們要確定可視窗口的寬度,因為我們需要這個寬度來計算出SlideList的長度(SlideList的長度通常是可視窗口的倍數,比如要放三張圖片,那么SlideList應該為可視窗口的至少3倍),不然我們無法通過translateX來移動它.
我們通過getBoundingClientRect來獲取可視區域真實的長度,SlideList的長度那么為:
slideListWidth = (len + 2) * width(len 為傳入子組件的數量,width 為可視區域寬度)
至于為什么要+2后面會提到.
/**
* 設置輪播區域尺寸
* @param x
*/
private setSize(x");const { width } = this.frameRef.current!.getBoundingClientRect()
const len = React.Children.count(this.props.children)
const total = len + 2
this.setState({
slideItemWidth: width,
slideListWidth: total * width,
total,
translateX: -width * this.state.currentIndex,
startPositionX: x !== undefined ");0,
})
}
獲取到了總長度之后如何實現輪播呢");onTouchStart onTouchMove onTouchEnd.
onTouchStart顧名思義是在手指觸摸到屏幕時觸發的事件,在這個事件里我們只需要記錄下手指觸摸屏幕的橫軸坐標 x 即可,因為我們會通過其橫向滑動的距離大小來判斷是否觸發輪播
/**
* 處理觸摸起始時的事件
*
* @private
* @param {React.TouchEvent} e
* @memberof Carousel
*/
private onTouchStart(e: React.TouchEvent) {
clearInterval(this.autoPlayTimer)
// 獲取起始的橫軸坐標
const { x } = getPosition(e)
this.setSize(x)
this.setState({
startPositionX: x,
})
}
onTouchMove顧名思義是處于滑動狀態下的事件,此事件在onTouchStart觸發后,onTouchEnd觸發前,在這個事件中我們主要做兩件事,一件事是判斷滑動方向,因為用戶可能向左或者向右滑動,另一件事是讓輪播圖跟隨手指移動,這是必要的用戶反饋.
/**
* 當觸摸滑動時處理事件
*
* @private
* @param {React.TouchEvent} e
* @memberof Carousel
*/
private onTouchMove(e: React.TouchEvent) {
const { slideItemWidth, currentIndex, startPositionX } = this.state
const { x } = getPosition(e)
const deltaX = x - startPositionX
// 判斷滑動方向
const direction = deltaX > 0 ");"right" : "left"
this.setState({
direction,
moveDeltaX: deltaX,
// 改變translateX來達到輪播組件跟隨手指移動的效果
translateX: -(slideItemWidth * currentIndex) + deltaX,
})
}
onTouchEnd顧名思義是滑動完畢時觸發的事件,在此事件中我們主要做一個件事情,就是判斷是否觸發輪播,我們會設置一個閾值threshold,當滑動距離超過這個閾值時才會觸發輪播,畢竟沒有閾值的話用戶稍微觸碰輪播圖就造成輪播,誤操作會造成很差的用戶體驗.
/**
* 滑動結束處理的事件
*
* @private
* @memberof Carousel
*/
private onTouchEnd() {
this.autoPlay()
const { moveDeltaX, slideItemWidth, direction } = this.state
const threshold = slideItemWidth * THRESHOLD_PERCENTAGE
// 判斷是否輪播
const moveToNext = Math.abs(moveDeltaX) > threshold
if (moveToNext) {
// 如果輪播觸發那么進行輪播操作
this.handleSwipe(direction!)
} else {
// 輪播不觸發,那么輪播圖回到原位
this.handleMisoperation()
}
}
我們常見的輪播圖肯定不是生硬的切換,一般在輪播中會有一個漸變或者緩動的動畫,這就需要我們加入動畫效果.
我們制作動畫通常有兩個選擇,一個是用 css3自帶的動畫效果,另一個是用瀏覽器提供的requestAnimationFrame API
孰優孰劣");requestAnimationFrame 靈活性更高,能實現 css3實現不了的動畫,比如眾多緩動動畫 css3都束手無策,因此我們毫無疑問地選擇了requestAnimationFrame.
雙方對比請看張鑫旭大神的CSS3動畫那么強,requestAnimationFrame還有毛線用?
想用requestAnimationFrame實現緩動效果就需要特定的緩動函數,下面就是典型的緩動函數
type tweenFunction = (t: number, b: number, _c: number, d: number) => number
const easeInOutQuad: tweenFunction = (t, b, _c, d) => {
const c = _c - b;
if ((t /= d / 2) < 1) {
return c / 2 * t * t + b;
} else {
return -c / 2 * ((--t) * (t - 2) - 1) + b;
}
}
緩動函數接收四個參數,分別是:
t: 時間
b:初始位置
_c:結束的位置
d:速度
通過這個函數我們能算出每一幀輪播圖所在的位置, 如下:
在獲取每一幀對應的位置后,我們需要用requestAnimationFrame不斷遞歸調用依次移動位置,我們不斷調用animation函數是其觸發函數體內的this.setState({ translateX: tweenQueue[0], })來達到移動輪播圖位置的目的,此時將這數組內的30個位置依次快速執行就是一個緩動動畫效果.
/**
* 遞歸調用,根據軌跡運動
*
* @private
* @param {number[]} tweenQueue
* @param {number} newIndex
* @memberof Carousel
*/
private animation(tweenQueue: number[], newIndex: number) {
if (tweenQueue.length < 1) {
this.handleOperationEnd(newIndex)
return
}
this.setState({
translateX: tweenQueue[0],
})
tweenQueue.shift()
this.rafId = requestAnimationFrame(() => this.animation(tweenQueue, newIndex))
}
但是我們發現了一個問題,當我們移動輪播圖到最后的時候,動畫出現了問題,當我們向左滑動最后一個輪播圖div4時,這種情況下應該是圖片向左滑動,然后第一張輪播圖div1進入可視區域,但是反常的是圖片快速向右滑動div1出現在可是區域...
因為我們此時將位置4設置為了位置1,這樣才能達到不斷循環的目的,但是也造成了這個副作用,圖片行為與用戶行為產生了相悖的情況(用戶向左劃動,圖片向右走).
目前業界的普遍做法是將圖片首尾相連,例如圖片1前面連接一個圖片4,圖片4后跟著一個圖片1,這就是為什么之前計算長度時要+2
slideListWidth = (len + 2) * width(len 為傳入子組件的數量,width 為可視區域寬度)
當我們移動圖片4時就不會出現上述向左滑圖片卻向右滑的情況,因為真實情況是:
圖片4 -- 滑動為 -> 偽圖片1 也就是位置 5 變成了位置 6
當動畫結束之后,我們迅速把偽圖片1的位置設置為真圖片1,這其實是個障眼法,也就是說動畫執行過程中實際上是圖片4到偽圖片1的過程,當結束后我們偷偷把偽圖片1換成真圖片1,因為兩個圖一模一樣,所以這個轉換的過程用戶根本看不出來...
如此一來我們就可以實現無縫切換的輪播圖了
我們實現了輪播圖的基本功能,但是其通用性依然存在缺陷:
提示點的自定義: 我的實現是一個小點,而 antd 是用的條,這個地方完全可以將 dom 結構的決定權交給開發者.
方向的自定義: 本輪播圖只有水平方向的實現,其實也可以有縱向輪播
多張輪播:除了單張輪播也可以多張輪播
以上都是可以對輪播圖進行拓展的方向,相關的還有性能優化方面
我們的具體代碼中有一個相關實現,我們的輪播圖其實是有自動輪播功能的,但是很多時候頁面并不在用戶的可視頁面中,我們可以根據是否頁面被隱藏來取消定時器終止自動播放.
github項目地址
以上 demo 僅供參考,實際項目開發中最好還是使用成熟的開源組件,要有造輪子的能力和不造輪子的覺悟
react-slick
復雜組件設計
精讀《請停止 css-in-js 的行為》
使用 rollup
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/7883.html
摘要:獲取的對象范圍方法獲取的是最終應用在元素上的所有屬性對象即使沒有代碼,也會把默認的祖宗八代都顯示出來而只能獲取元素屬性中的樣式。因此對于一個光禿禿的元素,方法返回對象中屬性值如果有就是據我測試不同環境結果可能有差異而就是。 花了很長時間整理的前端面試資源,喜歡請大家不要吝嗇star~ 別只收藏,點個贊,點個star再走哈~ 持續更新中……,可以關注下github 項目地址 https:...
摘要:問題回答者黃軼,目前就職于公司擔任前端架構師,曾就職于滴滴和百度,畢業于北京科技大學。最后附上鏈接問題我目前是一名后端工程師,工作快五年了。 showImg(https://segmentfault.com/img/bVbuaiP?w=1240&h=620); 問題回答者:黃軼,目前就職于 Zoom 公司擔任前端架構師,曾就職于滴滴和百度,畢業于北京科技大學。 1. 前端開發 問題 大...
摘要:經歷月份開放的簡歷,收到了蠻多詢問和面試,算是招人旺季,需要跳槽的小伙伴抓住機會。現在是面試了家公司左右,有些高頻問題會標記次數總次數,可供大家參考。最后祝大家面試順利,拿到心儀的,寫錯的地方請不吝賜教,謝謝。 經歷 7月份開放的簡歷,收到了蠻多詢問和面試,算是招人旺季,需要跳槽的小伙伴抓住機會。一開始廣泛看面試題,沒抓住重點復習,有很多平時也沒怎么用到,導致一開始面試的時候,問的問題...
摘要:經歷月份開放的簡歷,收到了蠻多詢問和面試,算是招人旺季,需要跳槽的小伙伴抓住機會。現在是面試了家公司左右,有些高頻問題會標記次數總次數,可供大家參考。最后祝大家面試順利,拿到心儀的,寫錯的地方請不吝賜教,謝謝。 經歷 7月份開放的簡歷,收到了蠻多詢問和面試,算是招人旺季,需要跳槽的小伙伴抓住機會。一開始廣泛看面試題,沒抓住重點復習,有很多平時也沒怎么用到,導致一開始面試的時候,問的問題...
摘要:經歷月份開放的簡歷,收到了蠻多詢問和面試,算是招人旺季,需要跳槽的小伙伴抓住機會。現在是面試了家公司左右,有些高頻問題會標記次數總次數,可供大家參考。最后祝大家面試順利,拿到心儀的,寫錯的地方請不吝賜教,謝謝。 經歷 7月份開放的簡歷,收到了蠻多詢問和面試,算是招人旺季,需要跳槽的小伙伴抓住機會。一開始廣泛看面試題,沒抓住重點復習,有很多平時也沒怎么用到,導致一開始面試的時候,問的問題...
閱讀 1721·2021-11-22 15:33
閱讀 2097·2021-10-08 10:04
閱讀 3548·2021-08-27 13:12
閱讀 3424·2019-08-30 13:06
閱讀 1474·2019-08-29 16:43
閱讀 1398·2019-08-29 16:40
閱讀 789·2019-08-29 16:15
閱讀 2749·2019-08-29 14:13