Framework-Agnostic CSS-in-Js พร้อมการสนับสนุนสำหรับการแสดงผลฝั่งเซิร์ฟเวอร์คำนำหน้าเบราว์เซอร์และการสร้าง CSS ขั้นต่ำ
รองรับการจัดรูปแบบของคุณด้วยองค์ประกอบ JavaScript ของคุณ
:hover
, :active
ฯลฯ โดยไม่จำเป็นต้องเก็บโฮเวอร์หรือสถานะที่ใช้งานอยู่ในส่วนประกอบ :visited
ผลงานก็ดีเช่นกัน@font-face
อัตโนมัติAphrodite กระจายผ่าน NPM:
npm install --save aphrodite
หากคุณต้องการดูวิดีโอเบื้องต้นคุณสามารถค้นหาได้ที่นี่
import React , { Component } from 'react' ;
import { StyleSheet , css } from 'aphrodite' ;
class App extends Component {
render ( ) {
return < div >
< span className = { css ( styles . red ) } >
This is red.
< / span >
< span className = { css ( styles . hover ) } >
This turns red on hover.
< / span >
< span className = { css ( styles . small ) } >
This turns red when the browser is less than 600px width.
< / span >
< span className = { css ( styles . red , styles . blue ) } >
This is blue.
< / span >
< span className = { css ( styles . blue , styles . small ) } >
This is blue and turns red when the browser is less than
600px width.
< / span >
< / div > ;
}
}
const styles = StyleSheet . create ( {
red : {
backgroundColor : 'red'
} ,
blue : {
backgroundColor : 'blue'
} ,
hover : {
':hover' : {
backgroundColor : 'red'
}
} ,
small : {
'@media (max-width: 600px)' : {
backgroundColor : 'red' ,
}
}
} ) ;
หมายเหตุ: หากคุณต้องการใช้สไตล์อย่างมีเงื่อนไขนั่นก็ทำได้ง่ายๆผ่าน:
const className = css (
shouldBeRed ( ) ? styles . red : styles . blue ,
shouldBeResponsive ( ) && styles . small ,
shouldBeHoverable ( ) && styles . hover
)
< div className = { className } > Hi < / div>
สิ่งนี้เป็นไปได้เพราะอาร์กิวเมนต์ที่ผิดพลาดจะถูกละเว้น
หากต้องการรวมสไตล์ให้ผ่านหลายสไตล์หรืออาร์เรย์ของสไตล์ลงใน css()
นี่เป็นเรื่องธรรมดาเมื่อรวมสไตล์จากส่วนประกอบเจ้าของ:
class App extends Component {
render ( ) {
return < Marker styles = { [ styles . large , styles . red ] } / > ;
}
}
class Marker extends Component {
render ( ) {
// css() accepts styles, arrays of styles (including nested arrays),
// and falsy values including undefined.
return < div className = { css ( styles . marker , this . props . styles ) } / > ;
}
}
const styles = StyleSheet . create ( {
red : {
backgroundColor : 'red'
} ,
large : {
height : 20 ,
width : 20
} ,
marker : {
backgroundColor : 'blue'
}
} ) ;
ฟังก์ชั่น reset
สามารถใช้เพื่อรีเซ็ตแท็กสไตล์ HTML บัฟเฟอร์การฉีดและแคชที่ฉีด มีประโยชน์เมื่อ Aphrodite ต้องถูกฉีกขาดและตั้งค่ากลับ
import { reset } from 'aphrodite' ;
reset ( ) ;
ในขณะที่ฟังก์ชัน resetInjectedStyle
สามารถใช้เพื่อรีเซ็ตแคชที่ฉีดสำหรับคีย์เดียว (โดยปกติจะเป็นชื่อคลาส)
import { resetInjectedStyle } from 'aphrodite' ;
resetInjectedStyle ( 'class_1sAs8jg' ) ;
ในการดำเนินการเรนเดอร์ฝั่งเซิร์ฟเวอร์ให้โทรไปยัง StyleSheetServer.renderStatic
ซึ่งใช้การโทรกลับ ทำการเรนเดอร์ของคุณภายในการโทรกลับและส่งคืน HTML ที่สร้างขึ้น การโทรทั้งหมดไปยัง css()
ภายในการโทรกลับจะถูกรวบรวมและ CSS ที่สร้างขึ้นรวมถึง HTML ที่สร้างขึ้นจะถูกส่งกลับ
Rehydrating ช่วยให้ Aphrodite รู้ว่ารูปแบบใดที่ได้รับการแทรกลงในหน้าแล้ว หากคุณไม่คืนความชุ่มชื้น Aphrodite อาจเพิ่มสไตล์ที่ซ้ำกันในหน้า
ในการดำเนินการ rehydration ให้ใช้รูปแบบการโทร StyleSheet.rehydrate
พร้อมรายการชื่อคลาสที่สร้างขึ้นกลับมาให้คุณโดย StyleSheetServer.renderStatic
หมายเหตุ: หากคุณใช้ aphrodite/no-important
ในโครงการของคุณและคุณต้องการแสดงผลทางฝั่งเซิร์ฟเวอร์ให้แน่ใจว่าได้นำเข้า StyleSheetServer
จาก aphrodite/no-important
มิฉะนั้นคุณจะได้รับข้อผิดพลาด
เป็นตัวอย่าง:
import { StyleSheetServer } from 'aphrodite' ;
// Contains the generated html, as well as the generated css and some
// rehydration data.
var { html , css } = StyleSheetServer . renderStatic ( ( ) => {
return ReactDOMServer . renderToString ( < App / > ) ;
} ) ;
// Return the base HTML, which contains your rendered HTML as well as a
// simple rehydration script.
return `
<html>
<head>
<style data-aphrodite> ${ css . content } </style>
</head>
<body>
<div id='root'> ${ html } </div>
<script src="./bundle.js"></script>
<script>
StyleSheet.rehydrate( ${ JSON . stringify ( css . renderedClassNames ) } );
ReactDOM.render(<App/>, document.getElementById('root'));
</script>
</body>
</html>
` ;
!important
โดยค่าเริ่มต้น Aphrodite จะต่อท้าย !important
กับคำจำกัดความสไตล์ สิ่งนี้มีวัตถุประสงค์เพื่อให้การรวมเข้ากับ codebase ที่มีอยู่แล้วง่ายขึ้น หากคุณต้องการหลีกเลี่ยงพฤติกรรมนี้แทนที่จะนำเข้า aphrodite
ให้นำเข้า aphrodite/no-important
มิฉะนั้นการใช้งานจะเหมือนกัน:
import { StyleSheet , css } from 'aphrodite/no-important' ;
โดยค่าเริ่มต้น Aphrodite จะลดชื่อสไตล์ลงไปที่แฮชในการผลิต ( process.env.NODE_ENV === 'production'
) คุณสามารถแทนที่พฤติกรรมนี้โดยเรียก minify
ด้วย true
หรือ false
ก่อนที่จะเรียก StyleSheet.create
สิ่งนี้มีประโยชน์หากคุณต้องการอำนวยความสะดวกในการดีบักในการผลิตเช่น
import { StyleSheet , minify } from 'aphrodite' ;
// Always keep the full style names
minify ( false ) ;
// ... proceed to use StyleSheet.create etc.
การสร้างใบหน้าแบบอักษรที่กำหนดเองเป็นกรณีพิเศษ โดยทั่วไปคุณต้องกำหนดกฎ @font-face
ทั่วโลก ในกรณีของ Aphrodite เราต้องการแทรกกฎนั้นเท่านั้นหากมีการอ้างอิงโดยคลาสที่อยู่ในหน้า เราได้ทำเพื่อให้คุณสมบัติ fontFamily
สามารถยอมรับวัตถุแบบฟอนต์ (ไม่ว่าโดยตรงหรือภายในอาร์เรย์) กฎ Global @font-face
จะถูกสร้างขึ้นตามคำจำกัดความของตัวอักษร
const coolFont = {
fontFamily : "CoolFont" ,
fontStyle : "normal" ,
fontWeight : "normal" ,
src : "url('coolfont.woff2') format('woff2')"
} ;
const styles = StyleSheet . create ( {
headingText : {
fontFamily : coolFont ,
fontSize : 20
} ,
bodyText : {
fontFamily : [ coolFont , "sans-serif" ]
fontSize : 12
}
} ) ;
Aphrodite จะมั่นใจได้ว่ากฎ @font-face
ทั่วโลกสำหรับตัวอักษรนี้จะถูกแทรกเพียงครั้งเดียวไม่ว่าจะมีการอ้างอิงกี่ครั้งก็ตาม
เช่นเดียวกับใบหน้าแบบอักษร Aphrodite รองรับแอนิเมชั่น Keyframe แต่มันถือว่าเป็นกรณีพิเศษ เมื่อเราพบอินสแตนซ์ของภาพเคลื่อนไหวที่ถูกอ้างอิงกฎ @keyframes
ส่วนกลางจะถูกสร้างและผนวกเข้ากับหน้า
แอนิเมชั่นมีให้เป็นวัตถุที่อธิบายภาพเคลื่อนไหวในแฟชั่น @keyframes
ทั่วไป การใช้คุณสมบัติ animationName
คุณสามารถจัดหาวัตถุแอนิเมชั่นเดียวหรืออาร์เรย์ของวัตถุแอนิเมชั่น คุณสมบัติแอนิเมชั่นอื่น ๆ เช่น animationDuration
สามารถให้เป็นสตริงได้
const translateKeyframes = {
'0%' : {
transform : 'translateX(0)' ,
} ,
'50%' : {
transform : 'translateX(100px)' ,
} ,
'100%' : {
transform : 'translateX(0)' ,
} ,
} ;
const opacityKeyframes = {
'from' : {
opacity : 0 ,
} ,
'to' : {
opacity : 1 ,
}
} ;
const styles = StyleSheet . create ( {
zippyHeader : {
animationName : [ translateKeyframes , opacityKeyframes ] ,
animationDuration : '3s, 1200ms' ,
animationIterationCount : 'infinite' ,
} ,
} ) ;
Aphrodite จะตรวจสอบให้แน่ใจว่ากฎ @keyframes
ไม่เคยทำซ้ำไม่ว่าจะมีการอ้างอิงกฎที่กำหนดกี่ครั้ง
Aphrodite ถูกสร้างขึ้นโดยคำนึงถึง React ในใจ แต่ไม่ได้ขึ้นอยู่กับ React ที่นี่คุณสามารถเห็นมันใช้กับส่วนประกอบของเว็บ:
import { StyleSheet , css } from 'aphrodite' ;
const styles = StyleSheet . create ( {
red : {
backgroundColor : 'red'
}
} ) ;
class App extends HTMLElement {
attachedCallback ( ) {
this . innerHTML = `
<div class=" ${ css ( styles . red ) } ">
This is red.
</div>
` ;
}
}
document . registerElement ( 'my-app' , App ) ;
Aphrodite จะพยายามสร้างแท็ก <style>
โดยอัตโนมัติในองค์ประกอบ <head>
ของเอกสารเพื่อใส่สไตล์ที่สร้างขึ้น Aphrodite จะสร้างแท็ก <style>
หนึ่งแท็กและจะเพิ่มสไตล์ใหม่ในช่วงเวลานี้ หากคุณต้องการควบคุมแท็กสไตล์ Aphrodite ที่ใช้ให้สร้างแท็กสไตล์ตัวเองด้วยแอตทริบิวต์ data-aphrodite
และ Aphrodite จะใช้สิ่งนั้นแทนที่จะสร้างให้คุณ
เพื่อเพิ่มความเร็วในการฉีดสไตล์ Aphrodite จะพยายามเขียนบัฟเฟอร์ลงในแท็ก <style>
นี้โดยอัตโนมัติเพื่อให้จำนวนการปรับเปลี่ยน DOM ขั้นต่ำเกิดขึ้น
Aphrodite ใช้ ASAP เพื่อกำหนดเวลาล้างบัฟเฟอร์ หากคุณวัดมิติขององค์ประกอบ DOM ใน componentDidMount
หรือ componentDidUpdate
คุณสามารถใช้ setTimeout
หรือ flushToStyleTag
เพื่อให้แน่ใจว่ารูปแบบทั้งหมดจะถูกฉีด
import { StyleSheet , css } from 'aphrodite' ;
class Component extends React . Component {
render ( ) {
return < div ref = "root" className = { css ( styles . div ) } / > ;
}
componentDidMount ( ) {
// At this point styles might not be injected yet.
this . refs . root . offsetHeight ; // 0 or 10
setTimeout ( ( ) => {
this . refs . root . offsetHeight ; // 10
} , 0 ) ;
}
}
const styles = StyleSheet . create ( {
div : {
height : 10 ,
} ,
} ) ;
เมื่อกำหนดสตริงให้กับคุณสมบัติ content
จะต้องใช้ราคาสองเท่าหรือเดียวใน CSS ดังนั้นด้วย Aphrodite คุณต้องระบุราคาภายในสตริงค่าสำหรับ content
เพื่อให้ตรงกับวิธีการแสดงใน CSS
เป็นตัวอย่าง:
const styles = StyleSheet . create ( {
large : {
':after' : {
content : '"Aphrodite"' ,
} ,
} ,
} ,
small : {
':before' : {
content : "'Aphrodite'" ,
} ,
} ,
} ) ;
CSS ที่สร้างขึ้นจะเป็น:
. large_im3wl1 : after {
content : "Aphrodite" !important ;
}
. small_ffd5jf : before {
content : 'Aphrodite' !important ;
}
เมื่อรวมรูปแบบ aphrodite หลายรูปแบบคุณขอแนะนำอย่างยิ่งให้รวมสไตล์ทั้งหมดของคุณเข้ากับการเรียกเดียวกับ css()
และไม่ควรรวมชื่อคลาสที่สร้างขึ้นที่ aphrodite เอาต์พุต (ผ่านการเชื่อมต่อสตริง classnames
ฯลฯ ) ตัวอย่างเช่นหากคุณมีรูปแบบพื้นฐานของ foo
ที่คุณพยายามแทนที่ด้วย bar
:
const styles = StyleSheet . create ( {
foo : {
color : 'red'
} ,
bar : {
color : 'blue'
}
} ) ;
// ...
const className = css ( styles . foo , styles . bar ) ;
const styles = StyleSheet . create ( {
foo : {
color : 'red'
} ,
bar : {
color : 'blue'
}
} ) ;
// ...
const className = css ( styles . foo ) + " " + css ( styles . bar ) ;
ทำไมมันถึงสำคัญ? แม้ว่าอันที่สองจะสร้างชื่อคลาสที่ถูกต้อง แต่ก็ไม่สามารถรับประกันได้ว่าสไตล์ bar
จะแทนที่ foo
วิธีการทำงานของ CSS ไม่ใช่ ชื่อคลาสที่มาจากองค์ประกอบ ที่สำคัญ แต่เป็นความจำเพาะ เมื่อเราดู CSS ที่สร้างขึ้นเราพบว่าชื่อคลาสทั้งหมดมีความเฉพาะเจาะจงเหมือนกันเนื่องจากพวกเขาทั้งหมดเป็นชื่อคลาสเดียว:
. foo_im3wl1 {
color : red;
}
. bar_hxfs3d {
color : blue;
}
ในกรณีที่ความจำเพาะเหมือนกันสิ่งที่สำคัญคือ ลำดับที่สไตล์ปรากฏในสไตล์ชีท นั่นคือถ้ารูปแบบชีทที่สร้างขึ้น
. foo_im3wl1 {
color : red;
}
. bar_hxfs3d {
color : blue;
}
จากนั้นคุณจะได้รับเอฟเฟกต์ที่เหมาะสมของสไตล์ bar
ที่เอาชนะ foo
แต่ถ้าสไตล์ชีทดูเหมือน
. bar_hxfs3d {
color : blue;
}
. foo_im3wl1 {
color : red;
}
จากนั้นเราก็จบลงด้วยเอฟเฟกต์ตรงกันข้ามกับ foo
Overriding bar
! วิธีการแก้ปัญหานี้คือการผ่านทั้งสองรูปแบบไปยังการโทร css()
จากนั้นมันจะสร้างชื่อคลาสเดียวเช่น foo_im3wl1-o_O-bar_hxfs3d
ด้วยสไตล์ที่ถูกแทนที่อย่างถูกต้องดังนั้นการแก้ปัญหา:
. foo_im3wl1-o_O-bar_hxfs3d {
color : blue;
}
เมื่อมีการระบุรูปแบบใน Aphrodite ลำดับที่ปรากฏในสไตล์ชีทจริงขึ้นอยู่กับลำดับที่คีย์จะดึงมาจากวัตถุ การสั่งซื้อนี้จะถูกกำหนดโดยเอ็นจิ้น JavaScript ที่ใช้ในการแสดงสไตล์ บางครั้งลำดับที่สไตล์จะปรากฏในรูปแบบชีทสำหรับความหมายของ CSS ตัวอย่างเช่นขึ้นอยู่กับเครื่องยนต์สไตล์ที่สร้างขึ้นจาก
const styles = StyleSheet . create ( {
ordered : {
margin : 0 ,
marginLeft : 15 ,
} ,
} ) ;
css ( styles . ordered ) ;
คุณอาจคาดหวังว่า CSS ต่อไปนี้จะถูกสร้างขึ้น:
margin : 0 px ;
margin-left : 15 px ;
แต่ขึ้นอยู่กับการสั่งซื้อของคีย์ในวัตถุสไตล์ CSS อาจปรากฏเป็น
margin-left : 15 px ;
margin : 0 px ;
ซึ่งแตกต่างกันไปตามความหมายเนื่องจากสไตล์ที่ปรากฏในภายหลังจะแทนที่สไตล์ก่อนหน้า
สิ่งนี้อาจแสดงให้เห็นว่าเป็นปัญหาเมื่อการเรนเดอร์ฝั่งเซิร์ฟเวอร์หากรูปแบบที่สร้างขึ้นจะปรากฏขึ้นในลำดับที่แตกต่างกันบนไคลเอนต์และบนเซิร์ฟเวอร์
หากคุณพบปัญหานี้ที่สไตล์ไม่ปรากฏใน CS ที่สร้างขึ้นตามลำดับที่ปรากฏในวัตถุของคุณมีสองวิธี:
อย่าใช้คุณสมบัติชวเลข ตัวอย่างเช่นในตัวอย่างมาร์จิ้นด้านบนโดยการเปลี่ยนจากการใช้คุณสมบัติชวเลขและคุณสมบัติมือยาวในรูปแบบเดียวกันกับการใช้คุณสมบัติมือยาวเท่านั้นปัญหาสามารถหลีกเลี่ยงได้
const styles = StyleSheet . create ( {
ordered : {
marginTop : 0 ,
marginRight : 0 ,
marginBottom : 0 ,
marginLeft : 15 ,
} ,
} ) ;
ระบุการสั่งซื้อสไตล์ของคุณโดยระบุโดยใช้ Map
เนื่องจาก Map
รักษาลำดับการแทรกของพวกเขา Aphrodite จึงสามารถวางสไตล์ของคุณตามลำดับที่ถูกต้อง
const styles = StyleSheet . create ( {
ordered : new Map ( [
[ "margin" , 0 ] ,
[ "marginLeft" , 15 ] ,
] ) ,
} ) ;
โปรดทราบว่า Map
S ไม่ได้รับการสนับสนุนอย่างเต็มที่ในเบราว์เซอร์ทั้งหมด มันสามารถถูก polyfilled โดยใช้แพ็คเกจเช่น ES6-Shim
คุณสมบัติพิเศษสามารถเพิ่มลงใน Aphrodite โดยใช้ส่วนขยาย
หากต้องการเพิ่มส่วนขยายให้กับ Aphrodite ให้ใช้ StyleSheet.extend
โทรติดต่อกับส่วนขยายที่คุณกำลังเพิ่ม ผลลัพธ์จะเป็นวัตถุที่มีการส่งออกตามปกติของ Aphrodite ( css
, StyleSheet
ฯลฯ ) ซึ่งจะรวมส่วนขยายของคุณไว้ด้วย ตัวอย่างเช่น:
// my-aphrodite.js
import { StyleSheet } from "aphrodite" ;
export default StyleSheet . extend ( [ extension1 , extension2 ] ) ;
// styled.js
import { StyleSheet , css } from "my-aphrodite.js" ;
const styles = StyleSheet . create ( {
...
} ) ;
หมายเหตุ : การใช้ส่วนขยายอาจทำให้สไตล์ของ Aphrodite ทำงานไม่ถูกต้อง Aphrodite ธรรมดาเมื่อใช้อย่างถูกต้องทำให้มั่นใจได้ว่ารูปแบบที่ถูกต้องจะถูกนำไปใช้กับองค์ประกอบเสมอ เนื่องจากกฎความจำเพาะของ CSS ส่วนขยายอาจช่วยให้คุณสร้างสไตล์ที่ขัดแย้งกันทำให้รูปแบบไม่ถูกต้องแสดง ดูส่วนขยายทั่วโลกด้านล่างเพื่อดูว่าจะเกิดอะไรขึ้น
ปัจจุบันมีส่วนขยายเพียงประเภทเดียวเท่านั้น: ตัวจัดการตัวเลือก ส่วนขยายประเภทนี้ช่วยให้คุณดูตัวเลือกที่มีคนระบุและสร้างตัวเลือกใหม่ตามพวกเขา พวกเขาใช้เพื่อจัดการกับสไตล์หลอกและการสืบค้นสื่อภายในของ Aphrodite ดูเอกสาร defaultSelectorHandlers
สำหรับข้อมูลเกี่ยวกับวิธีการสร้างฟังก์ชั่นตัวเลือกตัวเลือก
ในการใช้ส่วนขยายของคุณให้สร้างวัตถุที่มีคีย์ของชนิดของส่วนขยายที่คุณสร้างและส่งผ่านไปยัง StyleSheet.extend()
:
const mySelectorHandler = ... ;
const myExtension = { selectorHandler : mySelectorHandler } ;
const { StyleSheet : newStyleSheet , css : newCss } = StyleSheet . extend ( [ myExtension ] ) ;
ตัวอย่างเช่นคุณสามารถเขียนส่วนขยายที่สร้างสไตล์ทั่วโลกเช่น
const globalSelectorHandler = ( selector , _ , generateSubtreeStyles ) => {
if ( selector [ 0 ] !== "*" ) {
return null ;
}
return generateSubtreeStyles ( selector . slice ( 1 ) ) ;
} ;
const globalExtension = { selectorHandler : globalSelectorHandler } ;
สิ่งนี้อาจทำให้เกิดปัญหาเมื่อสองสถานที่พยายามสร้างสไตล์สำหรับตัวเลือกระดับโลกเดียวกัน! ตัวอย่างเช่นหลังจาก
const styles = StyleSheet . create ( {
globals : {
'*div' : {
color : 'red' ,
} ,
}
} ) ;
const styles2 = StyleSheet . create ( {
globals : {
'*div' : {
color : 'blue' ,
} ,
} ,
} ) ;
css ( styles . globals ) ;
css ( styles2 . globals ) ;
ไม่ได้กำหนดว่า divs จะเป็นสีแดงหรือสีน้ำเงิน
ชื่อคลาส minify โดยการตั้งค่าตัวแปรสภาพแวดล้อม process.env.NODE_ENV
เป็นการ production
ค่าสตริง
StyleSheet.create
และดู CSS ที่สร้างขึ้นลิขสิทธิ์ (c) 2016 Khan Academy