프레 넬 방정식은 다른 광학 매체 사이의 인터페이스에서 인시 될 때 빛의 반사를 설명합니다.
- https://en.wikipedia.org/wiki/fresnel_equations
# React 18+
yarn add @artsy/fresnel
# React 17
yarn add @artsy/fresnel@6목차
반응 형 구성 요소를 작성할 때 특정 조건이 충족 될 때 미디어 쿼리를 사용하여 디스플레이를 조정하는 것이 일반적입니다. 역사적으로 이것은 CSS/HTML에서 직접 이루어졌습니다.
@media screen and ( max-width : 767 px ) {
. my-container {
width : 100 % ;
}
}
@media screen and ( min-width : 768 px ) {
. my-container {
width : 50 % ;
}
} < div class =" my-container " /> @artsy/fresnel 브레이크 포인트 정의에 연결 함으로써이 선언적 접근 방식을 취해 React World로 가져옵니다.
import React from "react"
import ReactDOM from "react-dom"
import { createMedia } from "@artsy/fresnel"
const { MediaContextProvider , Media } = createMedia ( {
// breakpoints values can be either strings or integers
breakpoints : {
sm : 0 ,
md : 768 ,
lg : 1024 ,
xl : 1192 ,
} ,
} )
const App = ( ) => (
< MediaContextProvider >
< Media at = "sm" >
< MobileApp />
</ Media >
< Media at = "md" >
< TabletApp />
</ Media >
< Media greaterThanOrEqual = "lg" >
< DesktopApp />
</ Media >
</ MediaContextProvider >
)
ReactDOM . render ( < App /> , document . getElementById ( "react" ) ) 가장 중요한 것은 @artsy/fresnel 로 서버 렌더링 할 때 모든 중단 점이 서버에 의해 렌더링된다는 것입니다. 각 Media 구성 요소는 일반 CSS로 포장되어 사용자의 현재 브라우저 크기와 일치하는 경우에만 해당 중단 점 만 표시됩니다. 즉, 클라이언트가 HTML/CSS가 정확하게 렌더링을 시작할 수 있으며, 마크 업을 수신하는 동안 REACT 응용 프로그램이 부팅되기 오래 전부터 마크 업을 수신 할 수 있습니다. 이것은 최종 사용자의 인식 된 성능을 향상시킵니다.
왜 현재 장치에 필요한 것을 렌더링하지 않습니까? 장치가 서버에서 필요한 브레이크 포인트를 정확하게 식별 할 수 없습니다. 우리는 라이브러리를 사용하여 브라우저 사용자 에이전트를 스니핑 할 수 있지만 항상 정확한 것은 아니며 서버 렌더링시기에 알아야 할 모든 정보를 제공하지는 않습니다. 클라이언트 측 JS 부츠와 React가 부착되면 단순히 DOM을 씻어 내고 matchMedia 호출을 통해 불필요한 마크 업을 제거합니다.
먼저 앱에서 공유 할 수있는 Media 파일에서 @artsy/fresnel 구성합니다.
// Media.tsx
import { createMedia } from "@artsy/fresnel"
const ExampleAppMedia = createMedia ( {
breakpoints : {
sm : 0 ,
md : 768 ,
lg : 1024 ,
xl : 1192 ,
} ,
} )
// Generate CSS to be injected into the head
export const mediaStyle = ExampleAppMedia . createMediaStyle ( )
export const { Media , MediaContextProvider } = ExampleAppMedia 응용 프로그램의 시작점이 될 새 App 파일을 만듭니다.
// App.tsx
import React from "react"
import { Media , MediaContextProvider } from "./Media"
export const App = ( ) => {
return (
< MediaContextProvider >
< Media at = "sm" > Hello mobile! </ Media >
< Media greaterThan = "sm" > Hello desktop! </ Media >
</ MediaContextProvider >
)
} 클라이언트의 마운트 <App />
// client.tsx
import React from "react"
import ReactDOM from "react-dom"
import { App } from "./App"
ReactDOM . render ( < App /> , document . getElementById ( "react" ) ) 그런 다음 서버에서 SSR 렌더링을 설정하고 mediaStyle 헤더의 <style> 태그로 전달합니다.
// server.tsx
import React from "react"
import ReactDOMServer from "react-dom/server"
import express from "express"
import { App } from "./App"
import { mediaStyle } from "./Media"
const app = express ( )
app . get ( "/" , ( _req , res ) => {
const html = ReactDOMServer . renderToString ( < App /> )
res . send ( `
<html>
<head>
<title>@artsy/fresnel - SSR Example</title>
<!–– Inject the generated styles into the page head -->
<style type="text/css"> ${ mediaStyle } </style>
</head>
<body>
<div id="react"> ${ html } </div>
<script src='/assets/app.js'></script>
</body>
</html>
` )
} )
app . listen ( 3000 , ( ) => {
console . warn ( "nApp started at http://localhost:3000 n" )
} )그리고 그게 다야! 테스트하려면 JS를 비활성화하고 브라우저 창을 모바일 크기로 스케일링하고 다시로드하십시오. 사용자 에이전트 또는 다른 서버 측 "힌트"를 사용할 필요없이 모바일 레이아웃을 올바르게 렌더링합니다.
@artsy/fresnel Gatsby 또는 Next.js의 렌더링에 대한 정적 하이브리드 접근 방식과 잘 작동합니다. 간단한 구현은 아래 예제를 참조하십시오.
/examples 폴더에서 탐색 할 수있는 네 가지 예가 있습니다.
Basic 및 SSR 예제는 꽤 멀리 떨어져 있지만 @artsy/fresnel 더 많은 일을 할 수 있습니다. 기능에 대한 철저한 깊은 곳에서는 주방 싱크대 앱을 확인하십시오.
Gatsby를 사용하는 경우 Gatsby-Plugin-Fresnel을 사용하여 쉽게 구성 할 수 있습니다.
다른 기존 솔루션은 react-responsive 또는 react-media 와 같은 조건부로 렌더링 된 접근 방식을 취하 므로이 접근법은 어디에서 다릅니 까?
서버 사이드 렌더링!
그러나 먼저 조건부 렌더링은 무엇입니까?
React Ecosystem에서 선언적 응답 구성 요소를 작성하는 일반적인 접근 방식은 브라우저의 matchMedia API를 사용하는 것입니다.
< Responsive >
{ ( { sm } ) => {
if ( sm ) {
return < MobileApp />
} else {
return < DesktopApp />
}
} }
</ Responsive >클라이언트에서 주어진 중단 점이 일치 할 때 React는 조건부로 트리를 렌더링합니다.
그러나이 접근 방식은 서버 측 렌더링 설정으로 달성하고자하는 것에 대해 몇 가지 제한 사항이 있습니다.
브라우저가 필요하기 때문에 서버 렌더링 단계에서 사용자의 현재 중단 점을 안정적으로 아는 것은 불가능합니다.
사용자 에이전트 스니핑을 기반으로 브레이크 포인트 크기를 설정하는 것은 장치 기능을 크기와 정확하게 일치시킬 수 없기 때문에 오류가 발생하기 쉽습니다. 하나의 모바일 장치는 다른 픽셀 밀도보다 더 큰 픽셀 밀도를 가질 수 있고, 모바일 장치는 장치 방향을 고려할 때 여러 중단 점에 맞을 수 있으며 데스크탑 클라이언트에는 전혀 알 수있는 방법이 없습니다. 가장 좋은 개발자는 현재 중단 점을 추측하고 가정 된 상태로 <Responsive> 채우는 것입니다.
Artsy는 우리가 생각하는 것에 정착했습니다. 우리는이 문제에 다음과 같은 방식으로 접근합니다.
서버의 모든 중단 점에 대한 마크 업을 렌더링하고 와이어로 보내십시오.
브라우저는 적절한 미디어 쿼리 스타일로 마크 업을 수신하고 브라우저가있는 뷰포트 너비에 대해 예상되는 시각적 결과를 즉시 렌더링하기 시작합니다.
모든 JS가로드되고 React가 재수 화 단계를 시작하면 브라우저를 현재 어떤 중단 점에 대해 쿼리 한 다음 렌더링 된 구성 요소를 일치하는 미디어 쿼리로 제한합니다. 이것은 수명주기 방법이 숨겨진 구성 요소에서 발사되고 사용되지 않은 HTML이 DOM에 다시 작성되는 것을 방지합니다.
또한 다른 중단 점이 일치 할 때 MediaContextProvider 에 알리기 위해 브라우저에 이벤트 리스너를 등록한 다음 onlyMatch 소품의 새로운 값을 사용하여 트리를 다시 렌더링합니다.
matchMedia 사용한 구성 요소 트리가 우리의 접근 방식으로 어떻게 보일지 비교해 봅시다.
| 전에 | 후에 |
|---|---|
< Responsive >
{ ( { sm } ) => {
if ( sm ) return < SmallArticleItem { ... props } />
else return < LargeArticleItem { ... props } />
} }
</ Responsive > | < >
< Media at = "sm" >
< SmallArticleItem { ... props } />
</ Media >
< Media greaterThan = "sm" >
< LargeArticleItem { ... props } />
</ Media >
</ > |
작업 예제는 서버 측 렌더링 앱을 참조하십시오.
먼저 먼저. 애플리케이션 전체에서 사용할 수있는 미디어 구성 요소 세트를 생성하기 위해 설계에 필요한 중단 점과 상호 작용을 정의해야합니다.
예를 들어 다음 중단 점이있는 응용 프로그램을 고려하십시오.
sm 이라는 0과 768 사이의 뷰포트 너비 (768 포함) 포인트.md 라는 768에서 1024 사이의 뷰포트 너비 (1024 포함) 포인트.lg ).xl 이라는 1192 포인트 이상의 뷰포트 너비.다음과 같은 상호 작용 :
hover 라는 포인터 장치 호버링을 지원하는 장치.notHover 라는 포인터 장치 호버링을 지원하지 않는 장치.그런 다음 SO와 같은 미디어 구성 요소 세트를 생성합니다.
// Media.tsx
const ExampleAppMedia = createMedia ( {
breakpoints : {
sm : 0 ,
md : 768 ,
lg : 1024 ,
xl : 1192 ,
} ,
interactions : {
hover : "(hover: hover)" ,
notHover : "(hover: none)" ,
landscape : "not all and (orientation: landscape)" ,
portrait : "not all and (orientation: portrait)" ,
} ,
} )
export const { Media , MediaContextProvider , createMediaStyle } = ExampleAppMedia보시다시피, 브레이크 포인트는 시작 오프셋으로 정의되며, 첫 번째는 0에서 시작될 것으로 예상됩니다.
MediaContextProvider 구성 요소는 Media 구성 요소가 렌더링되는 방식에 영향을 미칩니다. 구성 요소 트리의 루트에 장착하십시오.
import React from "react"
import { MediaContextProvider } from "./Media"
export const App = ( ) => {
return < MediaContextProvider > ... </ MediaContextProvider >
} 응용 프로그램을 위해 생성 된 Media 구성 요소에는 응답 형 레이아웃을 선언하는 데 사용할 API를 구성하는 상호 배타적 인 소품이 몇 개 있습니다. 이 소품은 모두 미디어 구성 요소를 생성 할 때 제공된 명명 된 중단 점을 기반으로 작동합니다.
import React from "react"
import { Media } from "./Media"
export const HomePage = ( ) => {
return (
< >
< Media at = "sm" > Hello mobile! </ Media >
< Media greaterThan = "sm" > Hello desktop! </ Media >
</ >
)
}각 소품에 대한 예제는 상기 '설정'섹션에 정의 된대로 중단 점 정의를 사용합니다.
<Media> 에 의해 생성 된 기본 DIV를 피하고 대신 자신의 요소를 사용하려면 Render-Props 양식을 사용하되 필요하지 않은 경우 어린이를 렌더링 하지 마십시오 .
export const HomePage = ( ) => {
return (
< >
< Media at = "sm" > Hello mobile! </ Media >
< Media greaterThan = "sm" >
{ ( className , renderChildren ) => {
return (
< MySpecialComponent className = { className } >
{ renderChildren ? "Hello desktop!" : null }
</ MySpecialComponent >
)
} }
</ Media >
</ >
)
} 참고 : SSR 렌더링시에만 사용됩니다
Media 및 MediaContextProvider 구성 요소 외에도, Media 인스턴스를 사용할 수있는 모든 가능한 미디어 쿼리에 대한 CSS 스타일을 생성하는 createMediaStyle 기능이 있습니다. 브레이크 포인트 키의 서브 세트 만 사용되는 경우 출력을 최소화하기 위해 매개 변수로 지정된 선택적 일 수 있습니다. 문서의 <head> 에 <style> 태그 내에 이것을 삽입하십시오.
응용 프로그램 전체에서 쉽게 가져올 수 있도록 자체 모듈 에서이 설정을 수행하는 것이 좋습니다.
import { createMedia } from "@artsy/fresnel"
const ExampleAppMedia = createMedia ( {
breakpoints : {
sm : 0 ,
md : 768 ,
lg : 1024 ,
xl : 1192 ,
} ,
} )
// Generate CSS to be injected into the head
export const mediaStyle = ExampleAppMedia . createMediaStyle ( ) // optional: .createMediaStyle(['at'])
export const { Media , MediaContextProvider } = ExampleAppMedia 렌더링은 일치 할 미디어 쿼리 목록을 지정하여 특정 중단 점/상호 작용으로 제한 될 수 있습니다. 기본적으로 모든 것이 렌더링됩니다.
기본적으로 클라이언트 측면 렌더링되면 브라우저의 matchMedia API는 현재 일치하는 미디어 쿼리에만 onlyMatch 목록을 추가로 제한하는 데 사용됩니다. 이것은 숨겨진 구성 요소의 마운트 관련 수명주기 후크를 트리거하지 않도록하기 위해 수행됩니다.
이 동작을 비활성화하는 것은 대부분 디버깅 목적으로 의도 된 것입니다.
이를 사용하여 어린이는 특정 중단 점에서만 볼 수 있어야한다고 선언합니다. 즉, 뷰포트 너비는 중단 점의 시작 오프셋보다 크지 만, 다음 중단 점보다 적습니다.
예를 들어,이 Media 선언의 어린이는 뷰포트 너비가 0에서 768 사이 인 경우에만 볼 수 있습니다 (768 포함).
< Media at = "sm" > ... </ Media >해당 CSS 규칙 :
@media not all and ( min-width : 0 px ) and ( max-width : 767 px ) {
. fresnel-at-sm {
display : none !important ;
}
}이를 사용하여 뷰포트 너비는 지정된 중단 점의 시작 오프셋보다 낮은 반면 어린이는 볼 수 있어야한다고 선언하십시오.
예를 들어,이 Media 선언의 어린이는 뷰포트 너비가 0에서 1024 사이 인 경우에만 볼 수 있습니다 (1024 포함).
< Media lessThan = "lg" > ... </ Media >해당 CSS 규칙 :
@media not all and ( max-width : 1023 px ) {
. fresnel-lessThan-lg {
display : none !important ;
}
}이를 사용하여 뷰포트 너비가 다음 중단 점의 시작 오프셋보다 동일하거나 동일하지만 어린이 만 보이도록 선언하십시오.
예를 들어,이 Media 선언의 어린이는 뷰포트 너비가 1024 포인트보다 같은 경우에만 볼 수 있습니다.
< Media greaterThan = "md" > ... </ Media >해당 CSS 규칙 :
@media not all and ( min-width : 1024 px ) {
. fresnel-greaterThan-md {
display : none !important ;
}
}이를 사용하여 뷰포트 너비는 지정된 중단 점의 시작 오프셋 과 동일하지만 어린이는 만보아야한다고 선언하십시오.
예를 들어,이 Media 선언의 어린이는 뷰포트 너비가 768 포인트 이상인 경우에만 볼 수 있습니다.
< Media greaterThanOrEqual = "md" > ... </ Media >해당 CSS 규칙 :
@media not all and ( min-width : 768 px ) {
. fresnel-greaterThanOrEqual-md {
display : none !important ;
}
}이를 사용하여 뷰포트 너비는 첫 번째 지정된 중단 점의 시작 오프셋과 같지만 두 번째 지정된 중단 점의 시작 오프셋보다 적지 만 어린이는 볼 수 있어야한다고 선언하십시오.
예를 들어,이 Media 선언의 어린이는 뷰포트 너비가 768에서 1192 사이 인 경우에만 볼 수 있습니다 (1192 포함).
< Media between = { [ "md" , "xl" ] } > ... </ Media >해당 CSS 규칙 :
@media not all and ( min-width : 768 px ) and ( max-width : 1191 px ) {
. fresnel-between-md-xl {
display : none !important ;
}
}장점 :
단점 :
<Media> 구성 요소의 소품에 의해서만 결정됩니다. 그 마지막 요점은 흥미로운 문제를 제시합니다. 다른 중단 점에서 다르게 스타일링되는 구성 요소를 어떻게 표현할 수 있습니까? ( matchMedia 예를 상상해 봅시다.)
< Sans size = { sm ? 2 : 3 } > < >
< Media at = "sm" > { this . getComponent ( "sm" ) } </ Media >
< Media greaterThan = "sm" > { this . getComponent ( ) } </ Media >
</ > getComponent ( breakpoint ?: string ) {
const sm = breakpoint === 'sm'
return < Sans size = { sm ? 2 : 3 } />
}우리는 여전히 이것에 대한 패턴을 알아 내고 있으므로 제안이 있으면 알려주십시오.
이 프로젝트는 자동 방출을 사용하여 모든 PR에서 자동으로 릴리스됩니다. 모든 PR에는 다음 중 하나와 일치하는 레이블이 있어야합니다.
전공, 미성년자 및 패치로 인해 새로운 릴리스가 생성됩니다. 변경 사항을 깨뜨리는 데 전공, 새로운 비 분해 기능의 경우 사소한 및 버그 수정에 대한 패치를 사용하십시오. Trivial은 릴리스를 유발하지 않으며 문서 또는 비영리 코드를 업데이트 할 때 사용해야합니다.
특정 PR에서 해제하고 싶지 않지만 변경 사항이 사소하지 않은 경우 Skip Release 태그를 사용하여 적절한 버전 태그를 사용하십시오.