CSS-in-JS الإطاري-مع الدعم لتقديم جانب الخادم ، وبادئة المتصفح ، وتوليد الحد الأدنى من CSS.
دعم colocating أنماطك مع مكون JavaScript الخاص بك.
:hover
، :active
، إلخ. دون الحاجة إلى تخزين تحوم أو حالة نشطة في المكونات. :visited
تعمل على ما يرام أيضا.@font-face
والإدراج.يتم توزيع أفروديت عبر 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 ، ومخزن المؤقت الحقن ، وذاكرة التخزين المؤقت المحقونة. مفيد عندما يحتاج أفروديت إلى هدم وتراجع.
import { reset } from 'aphrodite' ;
reset ( ) ;
في حين يمكن استخدام وظيفة resetInjectedStyle
لإعادة ضبط ذاكرة التخزين المؤقت المحقونة لمفتاح واحد (عادة ما يكون اسم الفئة).
import { resetInjectedStyle } from 'aphrodite' ;
resetInjectedStyle ( 'class_1sAs8jg' ) ;
لتنفيذ عرض من جانب الخادم ، قم بإجراء مكالمة إلى StyleSheetServer.renderStatic
، الذي يأخذ رد الاتصال. قم بتقديمك داخل رد الاتصال وأرجع HTML المتولد. سيتم جمع جميع المكالمات إلى css()
داخل رد الاتصال وسيتم إرجاع CSS المولدة وكذلك HTML المولدة.
يتيح الترطيب أفروديت معرفة الأنماط التي تم إدراجها بالفعل في الصفحة. إذا لم تقم بترطيب ، فقد يضيف Aphrodite أنماطًا مكررة إلى الصفحة.
لأداء الإرهيات StyleSheetServer.renderStatic
ورقة StyleSheet.rehydrate
.
ملاحظة: إذا كنت تستخدم 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
لأسلوب التعريفات. يهدف هذا إلى جعل الاندماج مع قاعدة كود موجودة مسبقًا أسهل. إذا كنت ترغب في تجنب هذا السلوك ، فبدلاً من استيراد 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 أن يتم إدخال قاعدة Global @font-face
لهذا الخط مرة واحدة فقط ، بغض النظر عن عدد المرات التي يتم الإشارة إليها.
على غرار الوجوه Font ، يدعم Aphrodite الرسوم المتحركة Keyframe ، ولكن يتم التعامل معها كحالة خاصة. بمجرد أن نجد مثيلًا للرسوم المتحركة التي يتم الرجوع إليها ، يتم إنشاء قاعدة @keyframes
global وإلحاقها بالصفحة.
يتم توفير الرسوم المتحركة ككائنات تصف الرسوم المتحركة ، بأسلوب @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
لا يتم تكرارها أبدًا ، بغض النظر عن عدد المرات التي تتم الإشارة إلى قاعدة معينة.
تم بناء أفروديت مع مراعاة رد الفعل ولكن لا يعتمد على رد الفعل. هنا ، يمكنك رؤيتها تستخدم مع مكونات الويب:
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 تلقائيًا أن يكتب Buffer إلى هذه العلامة <style>
بحيث يحدث عدد أدنى من تعديلات DOM.
يستخدم Aphrodite في أسرع وقت ممكن لجدولة Flushing المخزن المؤقت. إذا قمت بقياس أبعاد عناصر 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 ;
}
عند الجمع بين أنماط أفروديت متعددة ، يوصى بشدة بدمج جميع أنماطك في مكالمة واحدة إلى css()
، ويجب ألا تجمع بين أسماء الفئات التي تم إنشاؤها والتي يخرج أفروديت (عبر سلسلة متسلسلة ، 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
Outdridge bar
! الطريقة لحل هذا هي تمرير كل من الأنماط إلى مكالمة css()
Aphrodite. بعد ذلك ، سوف ينتج اسم فئة واحدة ، مثل 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 ;
وهو مختلف بشكل دلالي ، لأن النمط الذي يظهر لاحقًا سيتجاوز النمط قبله.
قد يظهر هذا أيضًا كمشكلة عند عرض جانب الخادم ، إذا ظهرت الأنماط التي تم إنشاؤها بترتيب مختلف على العميل وعلى الخادم.
إذا واجهت هذه المشكلة حيث لا تظهر الأنماط في CSS التي تم إنشاؤها بالترتيب الذي تظهر في كائناتك ، فهناك حلان:
لا تستخدم الخصائص المختصرة. على سبيل المثال ، في مثال الهامش أعلاه ، من خلال التبديل من استخدام خاصية مختصرة وخاصية طويلة في نفس الأنماط لاستخدام خصائص Longhand فقط ، يمكن تجنب المشكلة.
const styles = StyleSheet . create ( {
ordered : {
marginTop : 0 ,
marginRight : 0 ,
marginBottom : 0 ,
marginLeft : 15 ,
} ,
} ) ;
حدد ترتيب أنماطك من خلال تحديدها باستخدام Map
. نظرًا لأن Map
S الحفاظ على طلب الإدراج الخاص بهم ، فإن Aphrodite قادر على وضع أنماطك بالترتيب الصحيح.
const styles = StyleSheet . create ( {
ordered : new Map ( [
[ "margin" , 0 ] ,
[ "marginLeft" , 15 ] ,
] ) ,
} ) ;
لاحظ أن Map
S ليست مدعومة بالكامل في جميع المتصفحات. يمكن تعدده باستخدام حزمة مثل ES6-SHIM.
يمكن إضافة ميزات إضافية إلى أفروديت باستخدام الامتدادات.
لإضافة امتدادات إلى 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 ( {
...
} ) ;
ملاحظة : قد يؤدي استخدام الامتدادات إلى عدم عمل أنماط أفروديت بشكل صحيح. يضمن أفروديت عادي ، عند استخدامه بشكل صحيح ، أن يتم تطبيق الأنماط الصحيحة دائمًا على العناصر. نظرًا لقواعد خصوصية CSS ، قد تسمح لك الامتدادات بإنشاء أنماط تتعارض مع بعضها البعض ، مما تسبب في عرض أنماط غير صحيحة. انظر الامتداد العالمي أدناه لمعرفة ما يمكن أن يحدث.
حاليا ، لا يوجد سوى نوع واحد من التمديد المتاح: معالجات المحدد. تتيح لك هذه الأنواع من الامتدادات إلقاء نظرة على المحددات التي يحددها شخص ما وإنشاء محددات جديدة بناءً عليها. يتم استخدامها للتعامل مع الأشكال الزائفة والاستعلامات الإعلامية داخل أفروديت. راجع مستندات 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
حقوق الطبع والنشر (ج) 2016 أكاديمية خان