首先,讓我們將必要的套件更新到最新版本。
如果您使用的是 NPM
npm install styled-components@^6.0.0 stylis@^4.0.0 npm uninstall @types/styled-components
如果您使用的是 Yarn
yarn add styled-components@^6.0.0 stylis@^4.0.0 yarn remove @types/styled-components
由於 styled-components 現在提供自己的類型,因此不再需要社群類型。
好消息!現在 styled-components 是使用 TypeScript 原生編寫的!即使您以前沒有使用過 TypeScript,也建議使用它來提高專案的可靠性,因為當您使用未知的 prop 或 prop 的值與預期不同時,它可以提醒您。
但是,如果您在專案中沒有使用 TypeScript,也不用擔心!像 VS Code 這樣的 IDE 仍然會擷取類型並在您編寫程式碼時提供提示。
shouldForwardProp
預設不再提供如果您尚未將樣式遷移到使用暫時性 props ($prefix
),您可能會注意到在 v6 中 React 關於樣式 props 傳遞到 DOM 的警告。 要恢復 v5 行為,請使用 StyleSheetManager
import isPropValid from '@emotion/is-prop-valid'; import { StyleSheetManager } from 'styled-components'; function MyApp() { return ( <StyleSheetManager shouldForwardProp={shouldForwardProp}> {/* other providers or your application's JSX */} </StyleSheetManager> ) } // This implements the default behavior from styled-components v5 function shouldForwardProp(propName, target) { if (typeof target === "string") { // For HTML elements, forward the prop if it is a valid HTML attribute return isPropValid(propName); } // For other elements, forward all props return true; }
由於網路和瀏覽器在 2023 年已顯著成熟,因此供應商前綴通常是不必要的。 因此,對於 v6 版本,我們決定預設省略自動前綴,以減少傳遞到頁面的 CSS 量。 如果您喜歡 v5 行為,您可以透過 StyleSheetManager
還原它。
import { StyleSheetManager } from 'styled-components'; function MyApp() { return ( <StyleSheetManager enableVendorPrefixes> {/* other providers or your application's JSX */} </StyleSheetManager> ) }
為了適應這種變化,原始的 disableVendorPrefixes
prop 已反轉為 enableVendorPrefixes
;如果您已設定 disableVendorPrefixes
,則現在可以將其刪除,因為它是新的預設值。
styled-components v6 使用更新的 stylis v4;如果您向 StyleSheetManager
提供 stylisPlugins
,請確保插件是最新的。 例如,stylis-plugin-rtl
發布了一個新版本以支援更新的 stylis。
隨著 stylis v4 的升級,嵌套選擇器的處理方式發生了變化,現在可以正確地反映瀏覽器行為。 具體來說,不以 &
開頭的偽選擇器(例如 :before
)將不再隱式添加「&」符號。
v5 行為
styled.div` :hover { color: red; } ` // .[classname]:hover { color: red; } styled.div` &:hover { color: red; } ` // .[classname]:hover { color: red; }
v6 行為
styled.div` :hover { color: red; } ` // .[classname] :hover { color: red; } (equivalent to .[classname] *:hover) styled.div` &:hover { color: red; } ` // .[classname]:hover { color: red; }
$as
和 $forwardedAs
props為了減少應用順序的混淆,我們捨棄了暫時性 $as
和 $forwardedAs
props。 請改用常規的 as
和 forwardedAs
props。
withComponent()
API這個變更已經很久了。 不再支援 withComponent
API,請改用 as
prop。 您可以透過 attrs
在定義時或在執行時指定 as
import styled from 'styled-components'; const Button = styled.button` background: blue; color: white; `; const ButtonLink = styled(Button).attrs({ as: 'a' })``; // These are equivalent, but `ButtonLink` allows for attaching further styling if desired. <Button as="a" href="https://styled-components.dev.org.tw"> <ButtonLink href="https://styled-components.dev.org.tw">
根據 Node 的維護排程,我們現在支援 v16 作為仍在接收安全修補程式的最舊執行階段。
準備好了嗎?
npm install styled-components@^5.0.0 react@^16.8 react-dom@^16.8 react-is@^16.8
如果您使用的是 React Native,您將至少需要 v0.59(第一個支援 hooks 的版本。)
就這樣。 💥
styled-components v5 沒有引入任何破壞性的公共 API 變更,並添加了以下內容
核心樣式表引擎的全面重寫,針對效能進行調整
新的基於 hooks 的組件模型
StyleSheetManager
有新的 props
disableCSSOMInjection
disableVendorPrefixes
stylisPlugins
stylis-plugin-rtl
嘗試滿足您的雙向需求!注意:v4 中已棄用的子函數物件形式語法 .attrs({ prop: props => {} })
在 v5 中已移除。請改用函數形式的 attrs .attrs(props => ({}))
(您應該已經看到主控台警告,以便提前進行此更新。)
查看官方公告文章以了解更多資訊,並了解 v5 的內容!
更新至 jest-styled-components v7
npm install jest-styled-components@^7.0.0
@import
和 createGlobalStyle
的注意事項目前,由於瀏覽器透過 CSSOM API 處理 @import
的方式存在一些問題,我們不建議在 cGS 中使用 @import
。最好將它們放在核心 index.html
檔案(生成的或靜態的)中,放在一個典型的 <style>
標籤內。
這是一個相當大的版本,底層和 API 層面都有很多變化。隨著 Beta 版的推進,我們將嘗試發布程式碼修改工具,以簡化以下項目。此外,如果您發現以下步驟有任何問題,請留下建設性的意見!
npm install styled-components@^4.0.0
react
>= 16.3;我們在內部使用了新的 React.forwardRef
API 和新的 context API,如果您想嘗試為舊版 React 版本提供 polyfill 的話。npm install react@^16.3 react-dom@^16.3
如果您使用的是 enzyme
或其他依賴項,例如 react-test-renderer
,如果您使用的是舊版本的 react
,則可能需要完成更多相關的升級。
.extend
API,請將您的組件切換為使用 styled(StyledComponent)
。可以使用程式碼修改工具來加速此過程。
🚫
import styled from 'styled-components' const Component = styled.div` background: blue; color: red; ` const ExtendedComponent = Component.extend` color: green; `
✅
import styled from 'styled-components' const Component = styled.div` background: blue; color: red; ` const ExtendedComponent = styled(Component)` color: green; `
請參閱「擴展樣式」文件以了解更多範例。
injectGlobal
API 將全域樣式添加到您的頁面,請改用新的createGlobalStyle
輔助組件。可以使用程式碼修改工具來加速此過程。
🚫
import { injectGlobal } from 'styled-components' injectGlobal` body { color: red; } `
✅
import { createGlobalStyle } from "styled-components" const GlobalStyle = createGlobalStyle` body { color: red; } ` // later in your app's render method <React.Fragment> <Navigation /> <OtherImportantTopLevelComponentStuff /> <GlobalStyle /> </React.Fragment>
請參閱createGlobalStyle
的文件,了解您可以使用它 انجام哪些以前使用 injectGlobal
無法做到的事情!
innerRef
prop,請將其更改為普通的 ref
。🚫
const Component = styled.div` background: blue; color: red; ` // later in your render method <Component innerRef={element => { this.myElement = element }}>Something something</Component>
✅
const Component = styled.div` background: blue; color: red; ` // later in your render method <Component ref={element => { this.myElement = element }}>Something something</Component>
css
輔助函數的情況下,在部分組件中使用 keyframes
組件,則現在需要使用輔助函數。一般來說,在組合樣式部分以插入到樣式化組件時,請始終使用 css 輔助函數。🚫
import styled, { keyframes } from 'styled-components' const animation = keyframes` 0% { opacity: 0; } 100 { opacity: 1; } ` const animationRule = ` ${animation} 1s infinite alternate ` const Component = styled.div` animation: ${animationRule}; `
✅
import styled, { css, keyframes } from 'styled-components' const animation = keyframes` 0% { opacity: 0; } 100 { opacity: 1; } ` const animationRule = css` ${animation} 1s infinite alternate; ` const Component = styled.div` animation: ${animationRule}; `
attrs({})
而且您傳遞給它的一些屬性是函數,建議您切換到新的 attrs(props => ({}))
語法,以便更容易、更強大的組合。🚫
import styled from 'styled-components' const Input = styled.input.attrs({ type: props => props.inputType, })` background: blue; color: red; `
✅
import styled from 'styled-components' const Input = styled.input.attrs(props => ({ type: props.inputType, }))` background: blue; color: red; `
npm install @types/styled-components
就這樣!除了遷移之外,我們還強烈建議您閱讀新的"as" prop
,它旨在將來取代withComponent API
。
可以:嵌套是從 Sass 故意移植過來的功能。少量使用它可以減少為每個元素創建顯式類別的需求,從而減輕程式碼的負擔。
父組件也可以使用它來定義上下文約束,而這些約束並非受影響子組件的真正關注點。
FirstSecondThirdFirstSecondThird
同時定位媒體查詢也非常方便,因為我們可以一目了然地看到組件在任何解析度下的反應。
Hello world!
將現有的 CSS 框架與 styled-components 集成非常容易!您可以將其現有的類別名稱與您的組件一起使用。
例如,假設您有一個現有的應用程式,其中包含兩個您想再次使用的類別:.small
和 .big
。如果您希望該類別始終附加到組件,則應使用attrs
方法來附加它。如果您只想在某些情況下附加它,您可以像往常一樣使用 className
props!
如果框架有一堆需要包含在頁面上的原始全域 CSS,您可以使用createGlobalStyle
API新增它。這也適用於 CSS 重置等。
請注意,對於 styled-components v3 及以下版本,以前的全域樣式 API 是injectGlobal
。
使用高特異性覆蓋樣式的方法很簡單,就是提高您自己樣式的特異性。這可以使用 !important
來完成,但這容易出錯,通常不是一個好主意。
我們建議使用以下技巧
const MyStyledComponent = styled(AlreadyStyledComponent)` &&& { color: #BF4F74; font-weight: bold; } `
每個 &
都會被生成的類別取代,因此注入的 CSS 看起來像這樣
.MyStyledComponent-asdf123.MyStyledComponent-asdf123.MyStyledComponent-asdf123 { color: #BF4F74; font-weight: bold; }
重複的類別將特異性提高到足以覆蓋原始順序,而編寫起來並不太繁瑣!
內聯樣式始終優先於外部 CSS,因此您不能僅僅透過提高特異性來覆蓋它。
然而,有一個巧妙的技巧,那就是將樣式 element-attr
CSS 選擇器與 !important
結合使用
const MyStyledComponent = styled(InlineStyledComponent)` &[style] { font-size: 12px !important; color: blue !important; } `
每個節點實際上都連接著兩個類別:一個是每個組件的靜態類別,這意味著樣式化組件的每個元素都具有此類別。它沒有附加任何樣式。相反,它用於快速識別 DOM 物件屬於哪個樣式化組件,或在開發工具中進行少量更改。它也用於組件選擇器。靜態類別可能看起來像這樣:.sc-fVOeaW
。
另一個是動態的,這意味著根據插值結果的不同,樣式化組件的每個元素在不同的 props 下都會有所不同。它可能看起來像 .fVOeaW
(注意缺少「sc」前綴。)
例如,樣式化組件 <Button />
每次都會以相同的靜態類別呈現。如果使用插值更改樣式,例如 <Button secondary />
,則動態類別將是不同的,而靜態類別將保持不變。
您可以使用 attrs 將屬性傳遞給樣式化組件,但並非總是合理的做法。
經驗法則是,當您希望樣式化組件的每個實例都具有該屬性時,使用 attrs
;而當每個實例需要不同的屬性時,則直接傳遞屬性。
const PasswordInput = styled.input.attrs(props => ({ // Every <PasswordInput /> should be type="password" type: "password" }))`` // This specific one is hidden, so let's set aria-hidden <PasswordInput aria-hidden="true" />
這同樣適用於可以根據另一個屬性的「模式」推斷出的屬性。在這種情況下,您可以將 attrs
上的屬性設定為一個函式,該函式根據其他屬性計算該屬性。
styled-components
與我的函式庫捆綁在一起嗎?如果您是函式庫作者,我們建議您不要將 styled-components
模組與您的函式庫捆綁並發佈。要做到這一點,您需要執行兩個步驟
styled-components
標記為外部依賴項styled-components
styled-components
標記為外部依賴項為此,您需要將其從 dependencies
移至 devDependencies
,並將其包含在您的 package.json
檔案中的 peerDependencies
列表中。
{ - "dependencies" : { + "devDependencies" : { "styled-components": "^3.4.9" }, + "peerDependencies" : { + "styled-components": ">= 3" + } }
將 styled-components
移至 devDependencies
將確保它不會與您的函式庫一起安裝(安裝函式庫時,npm install
或 yarn add
會忽略 devDependencies
)。
將 styled-components
新增至 peerDependencies
將向您的函式庫使用者發出訊號,表明 styled-components
未包含在函式庫中,他們需要自行安裝。
另外,請注意,在 peerDependencies
部分中,版本字串已設為更寬鬆的 >= 3
。這允許未來版本的 styled-components 自動運作,如果最終新增了重大變更,您可以簡單地通過對您的函式庫進行修補更新來縮小範圍。
styled-components
如果您在發佈函式庫之前將其捆綁,請確保您沒有將 styled-components
與其捆綁在一起。以下是一些如何使用一些流行的模組捆綁工具執行此操作的範例
如果您使用的是 Microbundle,它將自動處理此步驟。Microbundle 將 peerDependencies
列表中的每個依賴項都視為外部依賴項,並將其從構建中排除。
如果您使用的是 Rollup.js,您應該在您的配置中提供一個 external
選項
export default { entry: "my-awesome-library.js", + external: [ + "styled-components" + ] }
另一種方法是使用 rollup-plugin-peer-deps-external 插件,它將自動為您將 peerDependencies
新增到 external
選項陣列中。
+ import peerDepsExternal from 'rollup-plugin-peer-deps-external'; export default { entry: "my-awesome-library.js", + plugins: [ + // Preferably set as first plugin. + peerDepsExternal(), + ] }
如果您使用的是 Webpack,您應該在您的配置中提供一個 externals
選項
modules.export = { entry: "my-awesome-library.js", + externals: { + "styled-components": { + commonjs: "styled-components", + commonjs2: "styled-components", + amd: "styled-components", + }, + }, }
您可以在 Webpack 文件的 「撰寫函式庫」 部分找到更多關於如何使用 Webpack 捆綁函式庫的有用資訊。
如果您在控制台中看到如下所示的警告訊息,則您的頁面上可能初始化了多個 styled-components
實例。
It looks like there are several instances of "styled-components" initialized in this application. This may cause dynamic styles not rendering properly, errors happening during rehydration process and makes you application bigger without a good reason. If you are using a building tool like webpack, consider checking your bundle for duplication of the "styled-components" module.
如果您使用伺服器端渲染,這可能會導致動態樣式無法正常工作,甚至在重新水合過程中出錯。
發生這種情況有幾個常見原因
styled-components
的應用程式(例如,webpack 中的多個入口點都載入了同一個頁面)styled-components
函式庫styled-components
模組在多個套件中都是依賴項(這或多或少與前一個原因相同)如果您在一個頁面上運行了多個應用程式,請考慮為所有應用程式使用一個 styled-components
模組。如果您使用的是 webpack,則可以使用 CommonsChunkPlugin 建立一個 明確的供應商區塊,其中將包含 styled-components
模組
module.exports = { entry: { + vendor: ["styled-components"], app1: "./src/app.1.js", app2: "./src/app.2.js", }, plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + name: "vendor", + minChunks: Infinity, + }), ] }
node_modules
中的重複模組如果您認為問題出在依賴項中某處的重複 styled-components
模組,則可以使用多種方法檢查這一點。您可以在應用程式資料夾中使用 npm ls styled-components
、yarn list --pattern styled-components
或 find -L ./node_modules | grep /styled-components/package.json
命令。
如果以上指令都無法找出重複項目,請嘗試分析您的 bundle 中是否有多個 styled-components
實例。您可以直接檢查 bundle 原始碼,或使用像是 source-map-explorer 或 webpack-bundle-analyzer 等工具。
如果您確認重複項目是您遇到的問題,您可以嘗試以下幾種方法來解決它
如果您使用的是 npm
,您可以嘗試執行 npm dedupe
指令。這個指令會搜尋本地端的 dependencies,並嘗試將共同的 dependencies 移到目錄樹的上層來簡化結構。
請注意,npm dedupe
指令在處理符號連結資料夾(例如,當您使用 npm link
時)的效果不佳。
如果您使用的是 webpack,您可以更改它 解析 styled-components
模組的方式。您可以覆寫 webpack 搜尋 dependencies 的預設順序,讓您的應用程式 node_modules
的優先順序高於預設的 node 模組解析順序。
resolve: { + alias: { + "styled-components": path.resolve(appFolder, "node_modules", "styled-components"), + } }
要讓 styled-components 在 Lerna monorepo 中跨套件運作,一個可能的解決方法是將共用的 dependencies 提升 (hoist) 到 monorepo 根目錄。嘗試使用 `--hoist` 旗標執行 bootstrap 選項。
lerna bootstrap --hoist
或者,您可以從 package.json
檔案中移除 styled-components
,並手動將其提升到頂層的 package.json 檔案中。
Lerna 根資料夾中 package.json 檔案的範例
{ "name": "my-styled-monorepo", "devDependencies": { "lerna": "3.6.0" }, "dependencies": { "styled-components": "3.4.5" }, "scripts": { "bootstrap": "lerna bootstrap", "clean": "lerna clean", "start": "lerna run start", "build": "lerna run build" } }
在 React 元件的 render 方法中宣告 styled component,會在每次渲染時動態建立一個新的元件。這表示 React 必須在每次後續渲染時捨棄並重新計算 DOM 子樹的該部分,而不是僅計算它們之間差異。這會導致效能瓶頸和不可預測的行為。
🚫
const Header = () => { const Title = styled.h1` font-size: 10px; ` return ( <div> <Title /> </div> ) }
✅
const Title = styled.h1` font-size: 10px; ` const Header = () => { return ( <div> <Title /> </div> ) }
以下警告訊息表示非標準屬性正被附加到 HTML DOM 元素,例如 <div>
或 <a>
。如果您看到此警告訊息,則可能是您或您正在使用的程式庫將 props 作為屬性附加到 HTML DOM 元素。
Warning: Received "true" for a non-boolean attribute
如果您看到此警告,您可能在應該使用 "true"
的地方傳遞了 true
。這很可能來自 .attrs
屬性,或者來自您傳遞給 styled(Component)
元件的完全不相關的 prop。
要瞭解更多關於 props 如何傳遞的資訊,請參閱此章節。
例如
const Link = props => ( <a {...props} className={props.className}> {props.text} </a> ) const StyledComp = styled(Link)` color: ${props => (props.red ? 'red' : 'blue')}; ` <StyledComp text="Click" href="https://www.styled-components.com/" red />
這將會渲染
<a text="Click" href="https://www.styled-components.com/" red="true" class="[generated class]">Click</a>
React 會針對附加的非標準屬性發出警告,例如 "red" 和 "text",這些屬性對於 <a>
元素而言不是有效的 HTML 屬性。
要解決此問題,您可以使用 transient props 或解構 props
您可以使用 transient props 來解決此問題
const Link = ({ className, text, ...props }) => ( <a {...props} className={className}> {text} </a> ) const StyledComp = styled(Link)` color: ${props => (props.$red ? 'red' : 'blue')}; ` <StyledComp text="Click" href="https://www.styled-components.com/" $red />
如果您使用的版本低於 5.1 或無法使用 transient props,您可以使用參數解構來拉出那些已知的樣式 props
const Link = ({ className, red, text, ...props }) => ( <a {...props} className={className}> {text} </a> ) const StyledComp = styled(Link)` color: ${props => (props.red ? 'red' : 'blue')}; ` <StyledComp text="Click" href="https://www.styled-components.com/" red />
這將會渲染
<a href="https://www.styled-components.com/" class="[generated class]">Click</a>
當您使用參數解構時,從 props 物件中拉出的任何變數將不會包含在展開套用剩餘 props (...props
) 時;
styled-components 支援與目前 React 版本相同的瀏覽器。
Evergreen 瀏覽器包含 Chrome 和 Firefox(及其衍生版本),因為它們可以更新,不受作業系統版本限制。 Edge 和 Safari 也應該可以正常運作,因為過去幾年的所有版本都支援相關的 API。
create-react-app
使用 styled-components?程式庫的基本功能應該可以像其他程式庫一樣開箱即用。
但是,如果您想進行伺服器端渲染或利用 styled-components babel 插件 的一些進階功能而不需 eject,則需要設定 react-app-rewired
和 react-app-rewire-styled-components
。
npm link
或 yarn link
時,如何解決問題?本地連結是同時協作開發專案的 útil 工具。然而,它會為像是 React 和 styled-components 這種應該以單例模式使用的程式庫,創造出混亂的局面,因為您的每個本地專案可能都下載了一整套開發 dependencies(而且 bundler 預設會優先使用本地版本的 dependencies)。
解決方案是新增別名。以下是一個 webpack 的設定範例
// const path = require('path'); { resolve: { alias: { // adjust this path as needed depending on where your webpack config is 'styled-components': path.resolve('../node_modules/styled-components') } } }
這可確保您的建置始終使用程式庫的相同副本,即使是跨符號連結的專案也是如此。
如果您在具有連結元件的專案上使用 collectStyles
函式,您將會遇到複雜的情況。基本上,由於 v4 新的靜態 context API,不同的 styled-component 模組現在管理自己的 styled-components 列表來渲染,從主應用程式看來,似乎沒有任何東西可以提取,因為在該作用域中沒有建立任何 styled components,它們是從連結的套件作用域建立的。
一種解決方案是在 styled-components
模組路徑解析中新增一個別名,使其始終指向「主」應用程式。幸運的是,有很多程式庫可以做到這一點,我們將在此範例中使用 module-alias
。在您的 SSR 索引檔案的最頂端新增
const path = require('path'); const moduleAlias = require('module-alias'); moduleAlias.addAlias('styled-components', path.join(__dirname, '../node_modules/styled-components'));
這會告知 node 將所有 styled-components 的 import/require 解析為 __dirname, '../node_modules/styled-components'
當使用像是 createGlobalStyle
或以前的 injectGlobal
等全域樣式 API 時,在 DOM 中新增和移除某些樣式(例如 @font-face
定義)可能會導致頁面上的文字 momentarily 閃爍。這通常發生在伺服器端渲染的注水階段。我們仍在調整這些行為的運作方式,以長期避免此問題。
但是,在 font-display
CSS 規則 中有一個 CSS 解決方案可以解決此問題。藉由將規則設定為「fallback」模式,一旦載入字型後,就不會再次載入。這可以消除閃爍問題。
@font-face { font-family: 'Foo'; src: url('/path/to/foo.woff') format('woff'); font-style: normal; font-weight: 400; font-display: fallback; /* <- this can be added to each @font-face definition */ }
styled-components/native
的宣告?如果您收到錯誤訊息
Could not find a declaration file for module 'styled-components/native'
在使用 TypeScript 的 React Native 專案中,這是因為您需要新增 @types/styled-components-react-native
。