哈哈,和我一起踏上 bug 尋找之旅吧
前言
在最近的一個專案中,用到了 ant-design 的 輪播圖元件 Carousel
,發現無法按照預期給每個 slide 加樣式(內聯樣式),後來發現是 ant-design 的輪播圖元件引用的三方庫 react-slick
,通過原始碼查詢,終於發現是程式碼把內聯樣式重寫了
情景再現
按照 ant-design 的官方示例,把demo拷貝過來
import { Carousel } from 'antd';
function onChange(a, b, c) {
console.log(a, b, c);
}
ReactDOM.render(
<Carousel afterChange={onChange}>
<div><h3>1</h3></div>
<div><h3>2</h3></div>
<div><h3>3</h3></div>
<div><h3>4</h3></div>
</Carousel>,
mountNode
);
複製程式碼
一切正常。然後按業務改動程式碼,加入背景圖
import urlBg001 from "./images/urlBg001.png";
import urlBg002 from "./images/urlBg002.png";
import urlBg003 from "./images/urlBg003.png";
ReactDOM.render(
<Carousel afterChange={onChange}>
<div style={{ backgroundImage: `url(${urlBg001})` }}><h3>1</h3></div>
<div style={{ backgroundImage: `url(${urlBg002})` }}><h3>2</h3></div>
<div style={{ backgroundImage: `url(${urlBg003})` }}><h3>3</h3></div>
</Carousel>,
mountNode
);
複製程式碼
然而,沒有任何效果(不顯示背景圖)
思考問題所在
為何會這樣呢
首先,我以為是圖片路徑的問題,做了個驗證,發現圖片正常顯示
ReactDOM.render(
<Carousel afterChange={onChange}>
<div><img src={urlBg001} /></div>
</Carousel>,
mountNode
);
複製程式碼
然後,我就懷疑是不是 Carousel
這個元件對插槽做了限制,進行了進一步的驗證
ReactDOM.render(
<Carousel afterChange={onChange}>
<div id="test-slide" style={
{
backgroundImage: `url(${urlBg001})`,
color:"#f02",
width:"50px",
}}>1</div>
</Carousel>,
mountNode
);
複製程式碼
結果,沒有任何效果,開啟控制檯,找到 id 是 test-slide
的那個元素,發現渲染的結果是下面這樣:
<div id="test-slide" tabindex="-1" style="width: 100%; display: inline-block;">1</div>
複製程式碼
style
屬性被重寫了,我自己加的 style
不見了,更加確信了是 Carousel
元件內部搞的鬼
開啟 github
扒 ant-design 原始碼,沒有發現對 style
屬性做任何更改,然後在頭部找到該元件其實是引入一個 react-slick
,然後包裝了下
// https://github.com/ant-design/ant-design/blob/master/components/carousel/index.tsx#L23
const SlickCarousel = require("react-slick").default;
export default class Carousel extends React.Component<CarouselProps, {}> {
// 部分程式碼省略 ...
renderCarousel = ({ getPrefixCls }: ConfigConsumerProps) => {
// 部分程式碼省略 ...
return (
<div className={className}>
<SlickCarousel ref={this.saveSlick} {...props} />
</div>
);
};
render() {
return <ConfigConsumer>{this.renderCarousel}</ConfigConsumer>;
}
}
複製程式碼
真相慢慢的付出了水面,再次扒 react-slick
的原始碼,終於功夫不負有心人,在這個檔案裡發現了問題的源頭
// https://github.com/akiran/react-slick/blob/master/src/slider.js#L184
React.cloneElement(children[k], {
key: 100 * i + 10 * j + k,
tabIndex: -1,
style: {
width: `${100 / settings.slidesPerRow}%`,
display: "inline-block"
}
})
複製程式碼
直接將子元素children
的 style
屬性覆蓋了
最後在 react-slick
找到了相關 issue,然額這個 issue 仍然是 open 狀態
在 PR 中找到 pull#1372,有人嘗試修復過,但是 PR 又關了,不知道什麼原因
解決方案
由於不知道原始碼作者為何要這麼設定,我只能先找其他方法解決
- 方法一 多巢狀一層,只要不是在第一層元素上操作就行
ReactDOM.render(
<Carousel>
<div>
<div style={{ backgroundImage: `url(${logo})` }}>1</div>
</div>
</Carousel>,
mountNode
);
複製程式碼
- 方法二
用其他方式達成目的
ReactDOM.render(
<Carousel>
<img src={logo} alt="001"/>
</Carousel>,
mountNode
);
複製程式碼
總結
- 找這個 bug 還是挺費時間的,不過,通過這次 bug 之旅,學到一定要用科學的方法,精準定位問題來源,多做空白試驗參照,進行對比,這樣才能更快的解決問題
- 一定要多看原始碼,可以學到很多技巧
備註
可以點選下面的連結檢視 “情景重現” 和元件原始碼
- “情景重現”
demo codesandbox.io/s/92n1x0w1m… - 原始碼
ant-design github.com/ant-design/…
react-slick github.com/akiran/reac…