HTML 구성 요소를 파이썬으로 직접 작성하면 아름답지만 논쟁의 여지가있는 혼합물이 있습니다.
예, 논쟁의 여지가 있습니다 .
마음에 들지 않으면 무시하십시오 (그러나 HTML-in-Python 부분없이 사용할 수 있습니다. 아래 참조;)).
pyxl을 기반으로 합니다 . Python 3.6+ 전용 및 데이터 검증에 입력을 사용하십시오.
일단 HTML이 있으면 원하는대로 할 수 있습니다. 클래식 템플릿 엔진의 대체물로 생각하십시오.
소스 코드 : https://github.com/twidi/mixt/
문서 : https://twidi.github.io/mixt/
pypi : https://pypi.org/project/mixt/
CI (Circleci) : https://circleci.com/gh/twidi/workflows/mixt/
파일 example.py 만들어 봅시다
# coding: mixt
from mixt import html , Element , Required
class Hello ( Element ):
class PropTypes :
name : Required [ str ]
def render ( self , context ):
return < div > Hello , { self . name } < / div >
print ( < Hello name = "World" / > )그리고 그것을 실행하십시오 :
$ python example.py
< div > Hello, World < /div >파이썬으로 HTML을 쓰는 것을 좋아하지 않더라도 여전히 사용할 수 있습니다.
from mixt import html , Element , Required
class Hello ( Element ):
class PropTypes :
name : Required [ str ]
def render ( self , context ):
return html . Div ()( "Hello, " , self . name )
print ( Hello ( name = "World" ))예, React (주로 JSX)에서 영감을 얻었으며 우리는 몇 가지 개념을 빌립니다.
그리고 우리는 다음을 추가했습니다.
이 두 명령을 실행하십시오. 두 번째는 Python에게 HTML 내부의 파일을 이해하는 방법을 알려줍니다.
pip install mixt
mixt-post-install모든 것이 준비되었는지 확인하려면 실행하십시오.
python -m mixt.examples.simple이 출력이 있어야합니다.
< div title =" Greeting " > Hello, World </ div > HTML-in-python 물건을 사용하지 않으려면 mixt-post-install 실행하지 마십시오. 그런 다음 (동일한 출력을 갖기 위해)로 테스트하십시오.
python -m mixt.examples.simple_pure_pythonGIT 프로젝트를 복제 한 다음 :
make dev모든 것이 준비되었는지 확인하려면 실행하십시오.
python -m mixt.examples.simple이 출력이 있어야합니다.
< div title =" Greeting " > Hello, World </ div >일부 코드를 작성한 후 :
make testsmake lint codec 디렉토리에서 물건을 터치하면 pyc Python 파일을 제거하기 위해 make dev (또는 최소한 make full-clean )를 실행해야합니다.
우리의 CI는 모든 커밋이 make lint 통과하고 make tests 하고 make check-doc . 따라서 각 커밋마다이를 실행하는 것을 잊지 마십시오.
밀기 전에 수행하는 한 가지 방법은 다음과 같습니다.
git rebase develop --exec ' git log -n 1; make checks ' 참고 : src/mixt/examples/user_guide 에서이 사용자 안내서의 최종 코드를 찾을 수 있습니다 ( mixt.py 및 pure_python.py 찾을 수 있습니다).
다음과 같이 실행하십시오.
python -m mixt.examples.user_guide... TODO 목록을 만들어 봅시다!
그러나 전에는 기억하십시오. 이것은 반응이 아니며 브라우저에 있지 않으며 여기에 관련된 JavaScript가 없습니다. 우리는 HTML을 렌더링하는 것에 대해서만 이야기합니다.
그러나 당신은 그것으로 원하는 것을 할 수 있습니다. JavaScript 핸들러, 간단한 양식 추가 ...
양식에 대해 이야기하는 ...
TODO 목록에서 우리는 할머니를 추가 할 수 있기를 원합니다. 간단한 텍스트 입력입니다.
그래서 첫 번째 구성 요소 인 TodoForm 만들어 봅시다. 입력 텍스트와 버튼이있는 양식을 원합니다.
구성 요소는 Element 클래스의 서브 클래스이며 render 메소드가 작성해야합니다.
# coding: mixt
from mixt import Element , html # html is mandatory to resolve html tags
class TodoForm ( Element ):
def render ( self , context ): # Ignore the ``context`` argument for now.
return # The ```` is only for a better indentation below
< form method = "post" action = "???" >
< label > New Todo : < / label > < itext name = "todo" / >
< button type = "submit" > Add < / button >
< / form >이것은 간단한 기능으로 작성 될 수 있습니다.
# coding: mixt
from mixt import Element , html
def TodoForm ():
return
< form method = "post" action = "???" >
< label > New Todo : < / label > < itext name = "todo" / >
< button type = "submit" > Add < / button >
< / form >구성 요소를 인쇄하면이 두 가지가 동일한 결과를 제공합니다.
print ( < TodoForm / > ) < form method =" post " action =" ??? " > < label > New Todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >형식이 어떻게 형식화되는지 주목하십시오 : 태그 사이의 공간이 없습니다. 실제로 JSX와 같습니다.
JSX는 라인의 시작과 끝에서 공백을 제거합니다. 또한 빈 줄도 제거됩니다. 태그에 인접한 새로운 라인이 제거됩니다. 문자열 리터럴 중간에 발생하는 새로운 라인은 단일 공간으로 압축됩니다.
공간이나 신축을 추가하려면 파이썬을 통과 할 수 있습니다. 예를 들어, 레이블 앞에 Newline을 추가합시다.
#...
< form method = "post" action = "???" >
{ ' n ' } < label > New Todo : < / label > < itext name = "todo" / >
#...이제 우리는이 출력을 가지고 있습니다.
< form method =" post " action =" /todo/add " >
< label > New Todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >이제 더 가자.
양식의 action 속성에 주목하십시오. 우리는 무언가를 전달해야합니다. 그러나 하드 코딩은 제대로 들리지 않습니다. WWE는 구성 요소로 전달해야합니다.
Mixt React 와 마찬가지로 속성의 개념을 가지고 있습니다.
Mixt 에서는 구성 요소 내부의 클래스에서 PropTypes 라는 유형으로 정의합니다.
class TodoForm ( Element ):
class PropTypes :
add_url : str
def render ( self , context ):
return
< form method = "post" action = { self . add_url } >
< label > New Todo : < / label > < itext name = "todo" / >
< button type = "submit" > Add < / button >
< / form > 여기서 우리는 String ( str )이어야하는 add_url 이라는 소품을 정의했습니다. 이것은 파이썬 타이핑 구문을 사용합니다.
form 태그의 action 속성을 어떻게 변경했는지 확인하십시오. 이제 "???" 대신 {self.add_url} 입니다. .
속성이 곱슬 버팀대 사이에 전달되면 런타임에 순수한 파이썬으로 해석됩니다. 실제로, mixt 파서가 Python 통역사를 실행하게하기 전에 Mixt 파서가 전체 파일을 Pure Python으로 변환 할 때, 동일하게 유지되면 HTML 만 변환됩니다. 따라서이를 수행 할 형벌은 없습니다.
우리의 구성 요소가 순수한 파이썬으로 작성된 경우 이것이 어떻게 보이는지보십시오.
from mixt import Element , html
class TodoForm ( Element ):
class PropTypes :
add_url : str
def render ( self , context ):
return html . Form ( method = 'post' , action = self . add_url )(
html . Label ()(
html . Raw ( "New Todo: " )
),
html . InputText ( name = 'todo' ),
html . Button ( type = 'submit' )(
html . Raw ( "Add" ) # or html.Rawhtml(text="Add")
),
) 소품이 어떻게 구성된 인수로 구성 요소로 전달되는지, action 통과되는 방법 : action=self.add_url 주목하십시오.
이 Pure-Python 구성 요소는 또한 작동 방식을 보여줍니다. 소품은 구성 요소 클래스에 명명 된 인수로 전달 된 다음이 구성 요소가 호출되어 어린이 구성 요소를 호출에 위치 인수로 전달합니다.
ComponentClass ( prop1 = "val1" , prop2 = "val2" )(
Children1 (),
Children2 (),
)아이들은 무엇입니까? 어린이는 다른 태그 내부의 태그입니다.
<div id="divid"><span /><p>foo</p></div> 에서 : 우리는 다음과 같습니다.
id 와 두 자녀가있는 html.Div 구성 요소 :html.Span 구성 요소html.P 구성 요소 :html.RawHtml 구성 요소소품과 아이들과 함께 놀 수 있습니다. 먼저 Pure-Python의 버전은 작동 방식을 보여줍니다.
def render ( self , context ):
props = { "prop1" : "val1" , "prop2" : "val2" }
children = [ Children1 (), Children2 ()]
return ComponentClass ( ** props )( * children )
# You can pass a list of children to to the call, so this would produce the same result:
# ComponentClass(**props)(children) 그런 다음 mixt 버전 :
def render ( self , context ):
props = { "prop1" : "val1" , "prop2" : "val2" }
children = [ < Children1 / > , < Children2 / > ]
return < ComponentClass { ** props } > { * children } < / ComponentClass >
# or, the same, passing the children as a list:
# return <ComponentClass {**props}>{children}</ComponentClass> 이제 소품 add_url 로 돌아 갑시다.
구성 요소에 전달하는 방법?
우리가 HTML 태그에 속성을 전달했던 것과 똑같은 방법 : 실제로 HTML 복합물 ( mixt.html 에 정의 됨)에 정의 된 소품입니다. 우리는 서면 시점에서 HTML5에서 유효한 (더 이상 사용되지 않은) 태그를 지원합니다.
그러니 이것을하자 :
print ( < TodoForm add_url = "/todo/add" / > ) < form method =" post " action =" /todo/add " > < label > New Todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >좋아, 우리는 렌더링 된 HTML에 소품이 있습니다.
문자열을 통과하지 않으면 어떻게해야합니까? 우리는 PropTypes 에서 우리가 줄을 원한다고 말했습니다 ...
시도해 봅시다 :
print ( < TodoForm add_url = 1 / > ) < form method =" post " action =" 1 " > < label > New Todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >작동합니다! 하지만 ... 그것은 끈이 아닙니다 !! 실제로 숫자에 대한 특별한 경우가 있습니다. 문자열 대신 숫자로 전달할 수 있으며 필요한 경우 변환됩니다 ...
그러니 다른 것을 시도해 봅시다.
print ( < TodoForm add_url = True / > ) mixt . exceptions . InvalidPropValueError :
< TodoForm > . add_url : `True` is not a valid value for this prop ( type : < class 'bool' > , expected : < class 'str' > ) 그리고 우리가 파이썬에서 True 통과하면 동일합니다.
print ( < TodoForm add_url = { True } / > ) mixt . exceptions . InvalidPropValueError :
< TodoForm > . add_url : `True` is not a valid value for this prop ( type : < class 'bool' > , expected : < class 'str' > ) 좋아, 시스템을 속이고 "True" 를 문자열로 통과합시다.
print ( < TodoForm add_url = "True" / > ) mixt . exceptions . InvalidPropValueError :
< TodoForm > . add_url : `True` is not a valid value for this prop ( type : < class 'bool' > , expected : < class 'str' > )여전히 동일하지만 여기서 우리는 끈을 통과했습니다! 예,하지만 항상 평가되는 4 가지 값이 있습니다.
None 것과는 다릅니다)이 값 중 하나를 문자열로 전달하는 유일한 방법은 문자열로 파이썬을 통해 전달하는 것입니다.
print ( < TodoForm add_url = { "True" } / > ) < form method =" post " action =" True " > < label > New Todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form > 이 4 값과 숫자를 제외하고 속성으로 전달되는 모든 값은 문자열로 간주됩니다. HTML5의 HTML과 같이 인용문이 없더라도 일부 문자가없는 문자열에 대한 인용문이 필수적이지 않습니다 (공백 없음, / ...).
다른 것을 전달하려면 곱슬 괄호 안에있는 가치를 둘러싸고 있어야합니다 (이 경우 곱슬 교정기 주위에 따옴표가 필요하지 않습니다.
좋아, 이제 우리는 우리가 문자열 만 받아 들일 것이라고 확신합니다 ....하지만 아무것도 통과하지 않으면 어떻게 될까요? 그리고 ... "아무것도"무엇입니까?
파이썬의 빈 문자열부터 시작하겠습니다.
print ( < TodoForm add_url = { "" } / > ) < form method =" post " action ="" > < label > New Todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >좋아, 우리는 문자열을 원했고, 문자열이 있습니다.
이제이 빈 문자열을 직접 전달해 봅시다.
print ( < TodoForm add_url = "" / > ) < form method =" post " action ="" > < label > New Todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >그것은 여전히 끈이기 때문에 여전히 작동합니다. 인용문을 제거하겠습니다.
print ( < TodoForm add_url = / > ) mixt . exceptions . GeneralParserError : < mixt parser > Unclosed Tags : < TodoForm > 흠 예, 이것은 유효한 HTML이 아닙니다. 그러니 = 제거합시다.
print ( < TodoForm add_url / > ) mixt . exceptions . InvalidPropValueError :
< TodoForm > . add_url : `True` is not a valid value for this prop ( type : < class 'bool' > , expected : < class 'str' > ) 무엇? 예, required 같은 html5 속성에 대해 생각해보십시오. checked ... 값이없는 속성으로만 참석하면 True 로 간주됩니다. 따라서 속성에 값이 없으면 부울이고 True 입니다.
값을 전달하지 않을뿐만 아니라 다른 두 가지 방법은 html5에서 부울에 대한 True 에 유효합니다.
required=""required="required"편의를 위해 다른 방법을 추가했습니다.
True (Case는 중요하지 않음), Python 또는 문자열로 : required=True , required={True} , required="true" 그리고 그 상대방은 False 전달합니다.
False (case가 중요하지 않음), 파이썬 또는 문자열로 : required=False , required={False} , required="false" 부울 속성에 대해서는 괜찮습니다. 우리의 경우가 아닙니다. 우리가 할 수있는 마지막 일은 속성을 전혀 설정하지 않는 것입니다.
print ( < TodoForm / > )
# this is the same: ``print(<TodoForm add_url=NotProvided />)```
# (``NotProvided`` must be imported from ``mixt``) mixt . exceptions . UnsetPropError : < TodoForm > . add_url : prop is not set이해할 수 있습니다 : 우리는 설정되지 않은 소품에 액세스하려고 노력합니다. 물론 사용할 수는 없습니다.
그러나 우리가 액세스하지 않으면 어떻게됩니까? 구성 요소를 인쇄하지 않으면 렌더링되지 않습니다.
< TodoForm / > < TodoForm at 0x7fbd18ea5630 >따라서 인스턴스를 만들 수 있지만 렌더링 시간에 실패합니다. 그러나 그것을 막는 방법이 있습니다.
기본적으로 모든 속성은 선택 사항입니다. 또한 Python typing 모듈의 Optional 유형을 사용할 필요가 없으므로 각 소품에 대해 수행하는 것이 번거 롭습니다.
대신 mixt Optionnal 과 동일한 방식으로 사용하도록 Required 유형을 제공합니다.
from mixt import Element , Required , html
class TodoForm ( Element ):
class PropTypes :
add_url : Required [ str ]
def render ( self , context ):
# ...그래서 우리는 방금 끈을 원한다고 말했고 필요하다고 말했습니다.
소품없이 다시 만들어 봅시다.
< TodoForm / > mixt . exceptions . RequiredPropError : < TodoForm > . add_url : is a required prop but is not set이제 우리는 프로그램 초기에 제기 된 예외가 있습니다.
소품의 다른 가능성을 보려면 텍스트 레이블을 변경하기 위해 새 제품을 추가해 봅시다. 그러나 우리는 그것을 요구하고 싶지 않고 대신 기본값을 가지고 있습니다.
이를 위해서는 PropTypes 클래스에서 소품에 값을 추가하는 것만 큼 쉽습니다.
class TodoForm ( Element ):
class PropTypes :
add_url : Required [ str ]
label : str = 'New Todo'
def render ( self , context ):
return
< form method = "post" action = { self . add_url } >
< label > { self . label }: < / label > < itext name = "todo" / >
< button type = "submit" > Add < / button >
< / form >이제 소품을 전달하지 않고 시도해 봅시다.
print ( < TodoForm add_url = "/todo/add" / > ) < form method =" post " action ="" > < label > New Todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >그리고 우리가 하나를 통과하면 :
print ( < TodoForm add_url = "/todo/add" label = "Thing to do" / > ) < form method =" post " action =" /todo/add " > < label > Thing to do: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >예상대로 작동합니다.
소품이 Required 동안 기본값을 줄 수 없습니다. 말이되지 않으므로 가능한 빨리 점검하고 class 구성됩니다.
class TodoForm ( Element ):
class PropTypes :
add_url : Required [ str ]
label : Required [ str ] = 'New Todo' mixt . exceptions . PropTypeRequiredError : < TodoForm > . label : a prop with a default value cannot be required물론 기본값은 유형과 일치해야합니다!
class TodoForm ( Element ):
class PropTypes :
add_url : Required [ str ]
label : str = { 'label' : 'foo' } mixt . exceptions . InvalidPropValueError :
< TodoForm > . label : `{'label': 'foo'}` is not a valid value for this prop ( type : < class 'dict' > , expected : < class 'str' > ) 우리가 구성 요소에서하고 싶은 또 다른 일은 라벨을 구성하고 "유형"을 전달하지만 선택을 제한하는 것입니다. 이를 위해 Choices 유형을 사용할 수 있습니다.
from mixt import Choices , Element , Required , html
class TodoForm ( Element ):
class PropTypes :
add_url : Required [ str ]
type : Choices = [ 'todo' , 'thing' ]
def render ( self , context ):
return
< form method = "post" action = { self . add_url } >
< label > New { self . type }: < / label > < itext name = "todo" / >
< button type = "submit" > Add < / button >
< / form >시도해 봅시다 :
print ( < TodoForm add_url = "/todo/add" type = "todo" / > )
print ( < TodoForm add_url = "/todo/add" type = "thing" / > ) < form method =" post " action =" /todo/add " > < label > New todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >
< form method =" post " action =" /todo/add " > < label > New thing: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >그리고 우리가 사용 가능한 선택 이외의 다른 것을 통과 시키려고한다면 어떨까요? 예상대로 실패합니다.
print ( < TodoForm add_url = "/todo/add" type = "stuff" / > ) mixt . exceptions . InvalidPropChoiceError : < TodoForm > . type : `stuff` is not a valid choice for this prop ( must be in [ 'todo' , 'thing' ])그러나 아마도 우리는 그것을 전달하고 기본값을 사용하고 싶지 않을 수도 있습니다. 결과는 무엇입니까?
print ( < TodoForm add_url = "/todo/add" / > ) mixt . exceptions . UnsetPropError : < TodoForm > . type : prop is not set 따라서 필요에 따라 type 소품을 표시해야합니다.
class PropTypes :
add_url : Required [ str ]
type : Required [ Choices ] = [ 'todo' , 'thing' ]우리가 그것을 통과하지 않으면, 그것은 일찍 실패합니다.
print ( < TodoForm add_url = "/todo/add" / > ) mixt . exceptions . RequiredPropError : < TodoForm > . type : is a required prop but is not set그러나 그것은 우리가 원하는 것이 아니라 기본값을 원합니다.
실제로, Choices 이외의 유형의 경우 PropTypes 에서 값을 설정하면 기본값이 제공됩니다. 그러나 Choices 의 경우 가치가 선택 목록이므로 다릅니다.
이를 위해서는 DefaultChoices 가 있습니다. Choices 과 동일하게 작동하지만 목록의 첫 번째 항목을 기본값으로 사용합니다. 물론 다른 유형의 기본값과 마찬가지로 Required 수 없습니다.
시도해 봅시다 :
from mixt import DefaultChoices , Element , Required , html
class TodoForm ( Element ):
class PropTypes :
add_url : Required [ str ]
type : DefaultChoices = [ 'todo' , 'thing' ] print ( < TodoForm add_url = "/todo/add" / > ) < form method =" post " action =" /todo/add " > < label > New todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >예상대로 작동합니다.
그때까지 우리는 간단한 유형을 사용했지만 더 복잡한 유형을 사용할 수 있습니다.
예를 들어, add_url Prop를 만들어 type 소품을 기반으로 URL을 계산할 함수를 수락합니다. 그러나 우리는 또한 문자열과 기본값을 허용하고 싶습니다.
타이핑을 통해 그렇게 할 수 있습니다. 우리의 함수는 문자열, type 취하고 문자열 인 URL을 반환합니다.
따라서 구문은 호출 가능을 위해 Callable[[str], str] , 우리는 유형의 Callable 또는 str 의 것을 받아들이 기 위해 Union 사용합니다.
from typing import Union , Callable
from mixt import DefaultChoices , Element , Required , html
class TodoForm ( Element ):
class PropTypes :
add_url : Union [ Callable [[ str ], str ], str ] = "/todo/add"
type : DefaultChoices = [ 'todo' , 'thing' ]
def render ( self , context ):
if callable ( self . add_url ):
add_url = self . add_url ( self . type )
else :
add_url = self . add_url
return
< form method = "post" action = { add_url } >
< label > New { self . type }: < / label > < itext name = "todo" / >
< button type = "submit" > Add < / button >
< / form > 먼저, 기본값이 있으므로 add_url 소품없이 시도해 봅시다.
print ( < TodoForm / > ) < form method =" post " action =" /todo/add " > < label > New todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >문자열을 통과하면 다음과 같이 작동해야합니다.
print ( < TodoForm add_url = "/todolist/add" / > ) < form method =" post " action =" /todolist/add " > < label > New todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >이제 기능을 전달할 수 있습니다.
def make_url ( type ):
return f"/ { type } /add"
print ( < TodoForm add_url = { make_url } / > ) mixt . exceptions . InvalidPropValueError : < TodoForm > . add_url :
`<function make_url at 0x7fe2ae87be18>` is not a valid value for this prop ( type : < class 'function' > , expected : Union [ Callable [[ str ], str ], str ])오? 왜? 문자열을 인수로 받아들이고 문자열을 반환하는 함수를 전달했습니다. 예,하지만 유형을 확인한 것을 잊지 마십시오! 따라서 기능에 유형을 추가해야합니다.
def make_url ( type : str ) -> str :
return f"/ { type } /add"
print ( < TodoForm add_url = { make_url } / > ) < form method =" post " action =" /todo/add " > < label > New todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >다른 유형을 통과하면 URL이 그에 따라 변경되어야합니다.
print ( < TodoForm add_url = { make_url } type = "thing" / > ) < form method =" post " action =" /thing/add " > < label > New todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >이 기능을 소품의 기본값으로 만들 수도 있습니다.
from typing import Union , Callable
from mixt import DefaultChoices , Element , Required , html
def make_url ( type : str ) -> str :
return f"/ { type } /add"
class TodoForm ( Element ):
class PropTypes :
add_url : Union [ Callable [[ str ], str ], str ] = make_url
type : DefaultChoices = [ 'todo' , 'thing' ]
def render ( self , context ):
if callable ( self . add_url ):
add_url = self . add_url ( self . type )
else :
add_url = self . add_url
return
< form method = "post" action = { add_url } >
< label > New { self . type }: < / label > < itext name = "todo" / >
< button type = "submit" > Add < / button >
< / form > print ( < TodoForm / > ) < form method =" post " action =" /todo/add " > < label > New todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form >이제 당신은 궁금해지기 시작할 것입니다 ... 파이썬 타이핑은 번거롭고 검증하는 데 귀중한 시간이 걸릴 수 있습니다.
대답하자 :
기본적으로 mixt "dev-mode"에서 실행됩니다. Dev-Mode에서는 구성 요소로 전달 될 때 소품이 검증됩니다. "Dev-Mode"에 있지 않으면 유효성 검사가 건너 뜁니다. 따라서 생산시 Dev-Mode를 비활성화 할 수 있으며 (1 분 안에 방법을 볼 수 있습니다) 매우 빠르게 소품을 전달할 수 있습니다.
Choices 소품이 실제로 선택 목록에 있는지 확인하지 않습니다.render 방법에서는 이해할 수있는 이상한 일이 발생합니다. 그러나 유효성 검사가 중요하다는 것이 생산에 있다고 말할 수 있습니다. 물론. 그러나 물론 코드는 테스트로 완전히 다루고, 개발자로 실행되며, 프로덕션에서는이 유효성 검사가 필요하지 않습니다! 그런데 NODE_ENV=production 과 함께 React가 작동하는 방식입니다.
Dev-Mode를 변경하는 방법? 우리는 환경 변수를 시행하지 않지만 일부 기능을 제안합니다. 그들에게 전화하는 것은 당신에게 달려 있습니다.
from mixt import set_dev_mode , unset_dev_mode , override_dev_mode , in_dev_mode
# by default, dev-mode is active
assert in_dev_mode ()
# you can unset the dev-mode
unset_dev_mode ()
assert not in_dev_mode ()
# and set it back
set_dev_mode ()
assert in_dev_mode ()
# set_dev_mode can take a boolean
set_dev_mode ( False )
assert not in_dev_mode ()
set_dev_mode ( True )
assert in_dev_mode ()
# and we have a context manager to override for a block
with override_dev_mode ( False ):
assert not in_dev_mode ()
with override_dev_mode ( True ):
assert in_dev_mode ()
assert not in_dev_mode ()
assert in_dev_mode () 그래서 type 소품으로 이것을 시도해 봅시다. 기억하십시오.
type : DefaultChoices = [ 'todo' , 'thing' ]우리는 먼저 Dev-Mode에서 다른 선택을 전달하려고 노력합니다.
with override_dev_mode ( True ):
print ( < TodoForm type = "stuff" / > ) mixt . exceptions . InvalidPropChoiceError : < TodoForm > . type : `stuff` is not a valid choice for this prop ( must be in [ 'todo' , 'thing' ])예상대로 실패합니다.
그리고 지금은 개발자를 비활성화함으로써 :
with override_dev_mode ( False ):
print ( < TodoForm type = "stuff" / > ) < form method =" post " action =" /stuff/add " > < label > New stuff: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form > 그것은 작동합니다. 우리는 우리의 선택에 사용되지 않은 Todo 유형을 가지고 있으며 action 에도 있습니다. 그것은 당신이 유효하지 않은 소품을 전달하지 않도록하기 위해 테스트의 작업입니다.
이제 우리는 양식을 가지고 있습니다. Todo List 앱에 필요한 다른 구성 요소는 무엇입니까?
물론, 우리는 TODO 항목을 표시하는 방법이 필요합니다.
그러나 Todo 항목이란 무엇입니까? 기본 TodoObject 만들어 봅시다 :
class TodoObject :
def __init__ ( self , text ):
self . text = text매우 간단한 수업이지만 물론 원하는 것을 사용할 수 있습니다. Django 모델 등이 될 수 있습니다 ...
따라서 우리는 Todo 구성 요소를 만들 수 있으므로 필요한 TodoObject 소품으로 받아 들일 수 있습니다.
class Todo ( Element ):
class PropTypes :
todo : Required [ TodoObject ]
def render ( self , context ):
return < li > { self . todo . text } < / li >그리고 우리는 그것을 사용할 수 있습니다 :
todo = TodoObject ( "foo" )
print ( < Todo todo = { todo } / > ) < li > foo </ li > 이제 우리는 Todos 목록을 원합니다. TodoObject 목록을 소품으로 받아들이는 TodoList 구성 요소를 만들어 봅시다.
그러나 render 메소드에서 HTML 태그 만 사용하는 다른 두 구성 요소와 다른 점은 이제 구성 요소를 다른 구성 요소로 캡슐화 할 것입니다. 어떻게 보자.
class TodoList ( Element ):
class PropTypes :
todos : Required [ List [ TodoObject ]]
def render ( self , context ):
return < ul > {[ < Todo todo = { todo } / > for todo in self . todos ]} < / ul > 예, 그것은 단순합니다. 당신은 html 태그를 사용하는 것처럼 Todo 구성 요소에 <Todo...> 사용합니다. 유일한 차이점은 HTML 태그의 경우 직접 가져올 필요가 없다는 것입니다 ( mixt 에서 간단한 html ). 컨벤션에 의해 우리는 소문자로 작성합니다. 일반적인 구성 요소의 경우 메일을 가져와야합니다 (여전히 from mylib import components 및 <components.MyComponent ...> 에서 수행 할 수 있습니다). 정확한 경우를 사용해야합니다.
우리가 목록을 요구하는 방법에 주목하고 Curly-Braces의 목록 이해를 통해 <ul> 에 전달했습니다.
원한다면 다르게 일을 할 수 있습니다.
HTML에서 목록 이해를 분리하는 것과 같습니다.
def render ( self , context ):
todos = [
< Todo todo = { todo } / >
for todo
in self . todos
]
return < ul > { todos } < / ul >또는 전용 방법 (테스트에 유용한) :
def render_todos ( self , todos ):
return [
< Todo todo = { todo } / >
for todo
in todos
]
def render ( self , context ):
return < ul > { self . render_todos ( self . todos )} < / ul >그것은 당신에게 달려 있습니다 : 결국 그것은 단지 파이썬입니다.
이 구성 요소에 의해 렌더링 된 것을 보자.
todos = [ TodoObject ( "foo" ), TodoObject ( "bar" ), TodoObject ( "baz" )]
print ( < TodoList todos = { todos } / > ) < ul > < li > foo </ li > < li > bar </ li > < li > baz </ li > </ ul > 마지막으로 양식과 목록을 캡슐화하는 TodoApp 구성 요소가 있습니다.
class TodoApp ( Element ):
class PropTypes :
todos : Required [ List [ TodoObject ]]
type : DefaultChoices = [ 'todo' , 'thing' ]
def render ( self , context ):
return
< div >
< h1 > The "{self.type}" list < / h1 >
< TodoForm type = { self . type } / >
< TodoList todos = { self . todos } / >
< / div > todos = [ TodoObject ( "foo" ), TodoObject ( "bar" ), TodoObject ( "baz" )]
print ( < TodoList todos = { todos } type = "thing" / > ) < div > < h1 > The "thing" list </ h1 > < form > ... </ form > < ul > < li > foo </ li > < li > bar </ li > < li > baz </ li > </ ul > </ div >이 HTML을 HTML Beautifier에게 전달합시다.
< div >
< h1 > The "thing" list </ h1 >
< form method =" post " action =" /thing/add " >
< label > New thing: </ label >
< input type =" text " name =" todo " />
< button type =" submit " > Add </ button >
</ form >
< ul >
< li > foo </ li >
< li > bar </ li >
< li > baz </ li >
</ ul >
</ div > 그리고 그게 다야, 우리는 Todo-List 앱이 있습니다! 페이지에서 사용하려면 HTML베이스 마크 업을 렌더링하고 TodoApp 구성 요소를 통합하는 구성 요소를 작성하십시오. 구성 요소조차 필요하지 않습니다.
todos = [ TodoObject ( "foo" ), TodoObject ( "bar" ), TodoObject ( "baz" )]
print (
< html >
< body >
< TodoApp todos = { todos } type = "thing" / >
< / body >
< / html >
)미화 출력은 다음과 같습니다.
< html >
< body >
< div >
< h1 > The "thing" list </ h1 >
< form method =" post " action =" /thing/add " >
< label > New thing: </ label >
< input type =" text " name =" todo " />
< button type =" submit " > Add </ button >
</ form >
< ul >
< li > foo </ li >
< li > bar </ li >
< li > baz </ li >
</ ul >
</ div >
</ body >
</ html >우리는 일반적인 할 일 목록을 가지고 있지만 가용 한 유형의 할머니를 따라 "Todo-list"와 "사물 목록"을 원할 수 있습니다.
TodoApp 에는 기본적으로 todo 유형이 있기 때문에 이미 TODO 목록이 있습니다.
그러니 ThingApp 만들어 봅시다.
이 작업을 수행하는 첫 번째 방법은 TodoApp 에서 물려받는 것입니다. 그러나 상속을 통해 부모로부터 소품 type 제거 할 수 없습니다 (실제로는 사실이 아닙니다. 나중에 볼 수 있습니다). 그러나 우리는 "사물"이외의 다른 것을 받아들이고 싶지 않습니다. 따라서 다음과 같은 type 소품을 재정의 할 수 있습니다.
class ThingApp ( TodoApp ):
class PropTypes :
type : DefaultChoices = [ 'thing' ]이 구성 요소를 사용해 봅시다.
print ( < ThingApp todos = {[ TodoObject ( "foo" )]} / > ) < div > < h1 > The "thing" list </ h1 > < form method =" post " action =" /thing/add " > < label > New todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form > < ul > < li > foo </ li > </ ul > </ div > type 소품에 "Todo"를 통과하려고하면 작동하지 않습니다.
print ( < ThingApp todos = {[ TodoObject ( "foo" )]} type = "todo" / > ) mixt . exceptions . InvalidPropChoiceError :
< ThingApp > . type : `todo` is not a valid choice for this prop ( must be in [ 'thing' ])그러나 여전히 유형을 통과 할 수있는 것은 이상합니다.
다른 방법을 시도해 봅시다 : 부모 구성 요소. 아이들과 함께 일을하고 그것을 돌려주는 다른 일을하는 구성 요소. 우리가 여기서 원하는 것은 "사물"으로 강제로 제안 된 type 의 TodoApp 반환 할 구성 요소입니다.
이렇게합시다
class ThingApp ( Element ):
class PropTypes :
todos : Required [ List [ TodoObject ]]
def render ( self , context ):
return < TodoApp todos = { self . todos } type = "thing" / > print ( < ThingApp todos = {[ TodoObject ( "foo" )]} / > ) < div > < h1 > The "thing" list </ h1 > < form method =" post " action =" /thing/add " > < label > New todo: </ label > < input type =" text " name =" todo " /> < button type =" submit " > Add </ button > </ form > < ul > < li > foo </ li > </ ul > </ div > 작동하며 이번에는 type 소품을 전달할 수 없습니다.
print ( < ThingApp todos = {[ TodoObject ( "foo" )]} / > ) mixt . exceptions . InvalidPropNameError : < ThingApp > . type : is not an allowed prop todos 소품의 유형을 어떻게 정의해야했는지 주목하십시오. TodoApp 와 TodoThing 에서.
이를 처리하는 방법에는 여러 가지가 있습니다.
첫 번째는 TodoApp 에서 체크인하기 때문에 The Type in ThingApp 의 유형을 무시하는 것입니다. 그래서 우리는 Any 유형을 사용할 것입니다.
from typing import Any
#...
class ThingApp ( Element ):
class PropTypes :
todos : Any
#...유효한 Todos 목록으로 시도해 봅시다.
print ( < ThingApp todos = {[ TodoObject ( "foo" )]} / > ) < div > < h1 > The "thing" list </ h1 > < form > ... </ form > < ul > < li > foo </ li > </ ul > </ div >그러나 우리가 다른 것을 통과한다면 어떨까요?
print ( < ThingApp todos = "foo, bar" / > ) mixt . exceptions . InvalidPropValueError :
< TodoApp > . todos : `foo, bar` is not a valid value for this prop ( type : < class 'str' > , expected : List [ TodoObject ]) 예상대로 작동하지만 오류는 TodoApp 수준에서보고되며 이는 완벽하게 정상입니다.
또 다른 방법은 유형을 더 높은 수준으로 정의하는 것입니다.
TodoObjects = Required [ List [ TodoObject ]]
class TodoApp ( Element ):
class PropTypes :
todos : TodoObjects
# ...
class ThingApp ( Element ):
class PropTypes :
todos : TodoObjects
# ...이제 다른 것을 전달하면 오류가 올바른 수준에서보고되었습니다.
print ( < ThingApp todos = "foo, bar" / > ) mixt . exceptions . InvalidPropValueError :
< TodoThing > . todos : `foo, bar` is not a valid value for this prop ( type : < class 'str' > , expected : List [ TodoObject ]) 그러나 그렇게 할 수 없거나 원하지 않는 경우, TodoApp et에 정의 prop_type 유형을 유지할 수 있습니다.
class ThingApp ( Element ):
class PropTypes :
todos : TodoApp . prop_type ( "todos" )
# ... 그러나 ThingApp 또는 TodoApp 에 대한 오류를 제기하는 것이 중요합니까? 결국, 수표를 수행 해야하는 것은 실제로 TodoApp 때문입니다.
그래서 이것은보다 일반적인 방식으로 이것을하는 방법이어야합니다 ..
우리는 구성 요소가 구성 요소를 렌더링하는 단일 함수가 될 수 있음을 앞에서 보았습니다. HTML 태그 인 구성 요소를 반환하면됩니다. 클래스 구성 요소와의 한 가지 차이점은 PropTypes 없으므로 검증이 없다는 것입니다. 그러나 ... 그것은 우리가 필요로하는 것입니다.
우리는 우리의 ThingApp 일부 소품 ( todos Prop)을 받아들이고 특정 type 소품으로 TodoApp 반환하기를 원합니다.
그래서 우리는 할 수 있습니다 :
def ThingApp ( todos ):
return < TodoApp type = "thing" todos = { todos } / > 여기서 우리는 type ThingsApp 에 전달할 수 없다는 것을 알 수 있습니다. 그것은 유효한 주장이 아닙니다.
시도해 봅시다 :
print ( < ThingApp todos = {[ TodoObject ( "foo" )]} / > ) < div > < h1 > The "thing" list </ h1 > < form > ... </ form > < ul > < li > foo </ li > </ ul > </ div > 여기에는 통과 할 소품이 하나뿐이므로 쉽습니다. 그러나 우리가 많은 것을 가지고 있다고 상상해보십시오. {**props} 구문을 사용할 수 있습니다.
def ThingApp ( ** props ):
return < TodoApp type = "thing" { ** props } / >그리고 당신은 더 적은 문자로 할 수 있습니다 (중요하다면).
ThingApp = lambda ** props : < TodoApp type = "thing" { ** props } / >이 두 onctions는 정확히 동일하게 행동합니다.
TodoApp 오류가되기 때문에 type 소품을 전달할 수 없습니다.
print ( < ThingApp todos = {[ TodoObject ( "foo" )]} type = "thing" / > ) TypeError : BaseMetaclass object got multiple values for keyword argument 'type' (예, 구성 요소 클래스를 만드는 메타 클래스 인 BaseMetaclass 에 대해 이야기합니다).
그리고 다른 잘못된 소품은 TodoApp 에 의해 검증됩니다.
print ( < ThingApp todos = {[ TodoObject ( "foo" )]} foo = "bar" / > ) mixt . exceptions . InvalidPropNameError : < TodoApp > . foo : is not an allowed prop 이를 염두에두고, 우리는 type 소품을 수락하는 구성 요소의 유형을 강제로하는 일반적인 팬션을 만들 수있었습니다.
Thingify = lambda component , ** props : < component type = "thing" { ** props } / > print ( < Thingify component = { TodoApp } todos = {[ TodoObject ( "foo" )]} / > ) < div > < h1 > The "thing" list </ h1 > < form > ... </ form > < ul > < li > foo </ li > </ ul > </ div > 렌더링 된 구성 요소는 TodoApp 이고, type 소품은 "사물"이고 다른 소품 (여기서는 todos )이 올바르게 통과됩니다.
이제이 개념을보다 일반적인 경우 "고차 구성 요소"로 확장하십시오. React에서 "고차 구성 요소"는 "구성 요소를 취하고 새 구성 요소를 반환하는 함수"입니다.
아이디어는 :
EnhancedComponent = higherOrderComponent ( WrappedComponent )이를 수행하는 전형적인 방법은 새로운 구성 요소 클래스를 반환하는 것입니다.
def higherOrderComponent ( WrappedComponent ):
class HOC ( Element ):
__display_name__ = f"higherOrderComponent( { WrappedComponent . __display_name__ } )"
class PropTypes ( WrappedComponent . PropTypes ):
pass
def render ( self , context ):
return < WrappedComponent { ** self . props } > { self . childre ()} < / WrappedComponent >
return HOC PropTypes 클래스를 포장 구성 요소 중 하나에서 상속하기 위해 어떻게 설정하고, 모든 소품을 어린이들과 함께 랩핑 된 구성 요소에 전달하는 방법에 주목하십시오. 반환 된 구성 요소를 사용하면 포장 된 구성 요소와 동일한 유형의 구성 요소가 동일한 소품을 허용합니다.
또한 __display_name__ 주목하십시오. 예외에 사용하여 이제 제기 한 구성 요소를 제공 할 것입니다. 여기서 강요하지 않으면 서 HOC 로 설정되었을 것입니다. 대신, 우리는 그것이 전달 된 구성 요소의 변환 된 버전임을 나타냅니다.
여기에는 유용한 기능이 없습니다.
이 예에서는 다음을 수행 할 수있었습니다.
def thingify ( WrappedComponent ):
class HOC ( Element ):
__display_name__ = f"thingify( { WrappedComponent . __display_name__ } )"
class PropTypes ( WrappedComponent . PropTypes ):
__exclude__ = { 'type' }
def render ( self , context ):
return < WrappedComponent type = "thing" { ** self . props } > { self . children ()} < / WrappedComponent >
return HOC여기에 두 가지 중요한 사항이 있습니다.
__exclude__ = {'type'} 사용하여 WrappedComponent.PropTypes 에서 상속 한 제품에서 type 제거하는 방법에 주목하십시오. 따라서 반환 된 구성 요소는 type 제외하고 래핑 된 소품과 정확히 동일한 소품을 기대합니다.{self.children()} 을 추가했습니다. 실제로 우리가 랩하는 구성 요소, TodoApp , 어린이를 데려 가지 않을 수는 없지만 (아무것도 할 수는 없지만 아무것도 할 수 없음), 우리는 항상 사실이 될 것이라고 말할 수 없으며,이 높은 순서가 다른 구성 요소가 TodoApp 보다 다른 구성 요소를 감싸는 데 사용되지 않는다고 말할 수 없기 때문입니다. 따라서 항상 이것을하는 것이 좋습니다. 그리고 이제 우리는 우리의 ThingApp 만들 수 있습니다.
ThingApp = thingify ( TodoApp )그리고 그것을 사용하십시오 :
print ( < ThingApp todos = {[ TodoObject ( "foo" )]} / > ) < div > < h1 > The "thing" list </ h1 > < form > ... </ form > < ul > < li > foo </ li > </ ul > </ div >우리가 유형을 통과하려고한다면 :
print ( < ThingApp todos = {[ TodoObject ( "foo" )]} type = "thing" / > ) mixt . exceptions . InvalidPropNameError : < thingify ( TodoApp ) > . type : is not an allowed prop 따라서 계획대로, 우리는 유형을 전달할 수 없습니다. __display_name__ 가 어떻게 사용되는지 주목하십시오.
이것이 얼마나 강력한 지 생각합시다.
우리의 TodoApp 이 TodoObject 목록을 유지하고 싶다고 가정 해 봅시다. 그러나 우리는 그것들을 "소스"에서 얻고 싶습니다.
우리는이 새로운 고차 성분을 일반적인 방식으로 직접 쓸 수도 있습니다.
def from_data_source ( WrappedComponent , prop_name , get_source ):
class HOC ( Element ):
__display_name__ = f"from_data_source( { WrappedComponent . __display_name__ } )"
class PropTypes ( WrappedComponent . PropTypes ):
__exclude__ = { prop_name }
def render ( self , context ):
props = self . props . copy ()
props [ prop_name ] = get_source ( props , context )
return < WrappedComponent { ** props } > { self . children ()} < / WrappedComponent >
return HOC 이번에는 from_data_source 기능은 WrappedComponent 외에 두 가지 인수를 취합니다.
prop_name : 일부 데이터로 채우는 포장 구성 요소의 소품의 이름입니다.get_source : 데이터를 얻기 위해 호출되는 함수입니다. 포장 구성 요소에서 PropTypes 물려받은 방법과 prop_name 제외한 방법을 살펴보십시오. 따라서 우리는 데이터를 새로운 구성 요소에 전달하지 못하고 할 수 없습니다.
그런 다음 render 에서 get_source 에 대한 호출 결과와 함께 WrappedComponent 로 전달할 소품을 설정했습니다.
소품과 컨텍스트를 취하고 일부 데이터를 반환하는 매우 간단한 기능 (캐싱, 필터링으로 복잡한 기능이 될 수 있습니다 ...).
def get_todos ( props , context ):
# here it could be a call to a database
return [
TodoObject ( "fooooo" ),
TodoObject ( "baaaar" ),
]그리고 우리는 구성 요소를 구성 할 수 있습니다.
SourcedTodoApp = from_data_source ( TodoApp , 'todos' , get_todos )
ThingApp = thingify ( SourcedTodoApp )그리고 그것을 실행하십시오 :
print ( < ThingApp / > ) < div > < h1 > The "thing" list </ h1 > < form > ... </ form > < ul > < li > fooooo </ li > < li > baaaar </ li > </ ul > </ div >예상대로 작동하며 데이터는 구성 요소를 렌더링해야 할 때만 가져옵니다.
따라서 외부 소스에서 데이터를 가져올 수있는 TODO 목록이 있습니다. 그러나 사용자에 따라 데이터가 다르기를 원할 수도 있습니다.
우리가 할 수있는 일은 주 레벨에 있으며, 사용자를 데려 가서 모든 구성 요소에 전달하여 각 구성 요소가 현재 로그인 한 사용자를 얻을 수 있는지 확인하십시오.
번거롭지 않습니까?
이 사용 사례를 해결하는 것은 mixt 가 제공하는 Context 개념의 정확한 목적입니다. 물론 그것은 반응의 맥락의 개념에서 영감을 얻었습니다.
그리고 그들이 말했듯이 :
컨텍스트는 현재 인증 된 사용자, 테마 또는 선호하는 언어와 같은 React 구성 요소의 트리에 대해 "글로벌"으로 간주 될 수있는 데이터를 공유하도록 설계되었습니다.
컨텍스트를 작성하는 것은 BaseContext 에서 상속되고 render 메소드가 필요하지 않다는 점을 제외하고 구성 요소를 작성하는 것만 큼 간단합니다 (어린이를 렌더링 함).
그리고 컨텍스트가 수락하고 트리를 내려 놓을 데이터 유형을 정의하는 PropTypes 클래스가 필요합니다.
인증 된 사용자의 ID를 보유 할 컨텍스트를 작성하겠습니다.
from mixt import BaseContext
class UserContext ( BaseContext ):
class PropTypes :
authenticated_user_id : Required [ int ] 이제 authenticated_user_id 고려하기 위해 get_todos 메소드를 업데이트하려고합니다.
우리는 그것을 소품과 컨텍스트를 전달했습니다. 컨텍스트는 여기에서 유용합니다.
def get_todos ( props , context ):
return {
1 :[
TodoObject ( "1-1" ),
TodoObject ( "1-2" ),
],
2 : [
TodoObject ( "2-1" ),
TodoObject ( "2-2" ),
]
}[ context . authenticated_user_id ]이제 우리는 컨텍스트와 함께 앱을 렌더링 할 수 있습니다.
print (
< UserContext authenticated_user_id = 1 >
< ThingApp / >
< / UserContext >
) < div > < h1 > The "thing" list < / h1 > < form > ... < / form > < ul > < li > 1 - 1 < / li > < li > 1 - 2 < / li > < / ul > < / div >우리는 사용자 1에 대한 TODO 항목을 볼 수 있습니다.
사용자 2와 함께 시도합시다.
print (
< UserContext authenticated_user_id = 2 >
< ThingApp / >
< / UserContext >
) < div > < h1 > The "thing" list < / h1 > < form > ... < / form > < ul > < li > 2 - 1 < / li > < li > 2 - 2 < / li > < / ul > < / div >우리는 사용자 2에 대한 TODO 항목을 볼 수 있습니다.
이 경우 물론 사용자 ID를 소품으로 전달할 수있었습니다. 그러나 TODO 앱이 구성 요소 트리에 깊이 들어가는 것을 상상해보십시오. 이런 식으로 통과하는 것이 훨씬 쉽습니다.
그러나 React 문서에서 언급했듯이 :
소품을 몇 레벨을 낮추지 않도록 컨텍스트를 사용하지 마십시오. 여러 수준의 많은 구성 요소에서 동일한 데이터에 액세스 해야하는 경우를 고수하십시오.
컨텍스트가 없을 때 render 메소드의 context 인수는 EmptyContext 로 설정되며 None 아닙니다. 따라서 has_prop 메소드를 직접 사용하여 컨텍스트를 통해 소품을 사용할 수 있는지 확인할 수 있습니다.
인증 된 사용자가없는 경우 get_todos 함수를 업데이트하여 빈 객체 목록을 반환합니다.
def get_todos ( props , context ):
if not context . has_prop ( 'authenticated_user_id' ) or not context . authenticated_user_id :
return []
return {
1 :[
TodoObject ( "1-1" ),
TodoObject ( "1-2" ),
],
2 : [
TodoObject ( "2-1" ),
TodoObject ( "2-2" ),
]
}[ context . authenticated_user_id ]이것을 시도해 봅시다 :
print ( < ThingApp / > ) < div > < h1 > The "thing" list < / h1 > < form > ... < / form > < ul > < / ul > < / div >그리고 여전히 컨텍스트에서 사용자와 함께 작동합니다.
print (
< UserContext authenticated_user_id = 1 >
< ThingApp / >
< / UserContext >
) < div > < h1 > The "thing" list < / h1 > < form > ... < / form > < ul > < li > 1 - 1 < / li > < li > 1 - 2 < / li > < / ul > < / div >상황에 대한 중요한 메모 : 많은 맥락을 가질 수 있습니다! 그러나 많은 맥락에서 동일한 소품을 정의하면 정의되지 않은 행동으로 이어질 수 있습니다.
모두는 아름다운 디자인과 어쩌면 약간의 상호 작용을 좋아합니다.
HTML을 생성하고 HTML은 일부 CSS 및 JS를 포함 할 수 있습니다.
먼저 상호 작용을 추가합시다 : TodoForm 에 항목을 추가 할 때 목록에 추가하겠습니다.
먼저 우리는 TodoForm 구성 요소를 render_javascript 메소드를 추가하여 (우리는 더 잘 할 수 있지만 포인트가 아닙니다) JavaScript를 호스팅합니다.
class TodoForm ( Element ):
# ...
def render_javascript ( self , context ):
return html . Raw ( """
function on_todo_add_submit(form) {
var text = form.todo.value;
alert(text);
}
""" )시작하려면 새로운 TODO 텍스트 만 표시합니다.
이제 render 메소드를 업데이트 하여이 JavaScript를 반환합니다 ( render_javascript 메소드의 사용은 문제를 분리하는 것 뿐이며 render 메소드에 직접있을 수 있습니다.
class TodoForm ( Element ):
# ...
def render ( self , context ):
# ...
return
< Fragment >
< script > { self . render_javascript ( context )} < / script >
< form method = "post" action = { add_url } onsubmit = "return on_todo_add_submit(this);" >
< label > New { self . type }: < / label > < itext name = "todo" / >
< button type = "submit" > Add < / button >
< / form >
< / Fragment > Fragment 태그를 주목하십시오. React에서와 같이 반환 할 많은 요소를 캡슐화하는 방법입니다. 간단한 목록이었을 수도 있지만 결국에는 Comas가 있습니다.
return [
< script > ... < / script > ,
< form >
...
< / form >
] 이제 목록에 항목을 추가하고 싶습니다. 이를 수행하는 것은 TodoForm 의 역할이 아니라 목록에 있습니다. 따라서 TodoList 구성 요소에 JS를 추가 할 것입니다 : 일부 텍스트를 취하고 새 항목을 작성하는 함수.
TodoForm 은 (여전히 나쁜) JavaScript와 함께 render_javascript 메소드를 추가합니다.
class TodoList ( Element ):
# ...
def render_javascript ( self , context ):
todo_placeholder = < Todo todo = { TodoObject ( text = 'placeholder' )} / >
return html . Raw ( """
TODO_TEMPLATE = "%s";
function add_todo(text) {
var html = TODO_TEMPLATE.replace("placeholder", text);
var ul = document.querySelector('#todo-items');
ul.innerHTML = html + ul.innerHTML;
}
""" % ( todo_placeholder )) render 메소드를 업데이트하여 <script> 태그와 id JavaScript에 사용하는 ul 태그에 추가합니다.
class TodoList ( Element ):
# ...
def render ( self , context ):
return
< Fragment >
< script > { self . render_javascript ( context )} < / script >
< ul id = "todo-items" > {[ < Todo todo = { todo } / > for todo in self . todos ]} < / ul >
< / Fragment > 이제 우리는 TodoForm 구성 요소의 render_javascript 메소드를 업데이트하여 새로운 add_toto javaScript 함수를 사용합니다.
class TodoForm ( Element ):
# ...
def render_javascript ( self , context ):
return html . Raw ( """
function on_todo_add_submit(form) {
var text = form.todo.value;
add_todo(text);
}
""" )그리고 그게 전부입니다. 실제로 특별한 것은 없습니다.
그러나 OU TodoApp 의 출력을 살펴 보겠습니다.
print (
< UserContext authenticated_user_id = 1 >
< ThingApp / >
< / User >
)미화 출력은 다음과 같습니다.
< div >
< h1 > The "thing" list </ h1 >
< script >
function on_todo_add_submit ( form ) {
var text = form . todo . value ;
add_todo ( text ) ;
}
</ script >
< form method =" post " action =" /thing/add " onsubmit =" return on_todo_add_submit(this); " >
< label > New thing: </ label >
< input type =" text " name =" todo " />
< button type =" submit " > Add </ button >
</ form >
< script >
TODO_TEMPLATE = "<li>placeholder</li>" ;
function add_todo ( text ) {
var html = TODO_TEMPLATE . replace ( "placeholder" , text ) ;
var ul = document . querySelector ( '#todo-items' ) ;
ul . innerHTML = html + ul . innerHTML ;
}
</ script >
< ul id =" todo-items " >
< li > 1-1 </ li >
< li > 1-2 </ li >
</ ul >
</ div > 그래서 우리는 많은 script 태그가 있습니다. 하나만 있으면 좋을 수 있습니다.
mixt 다른 곳에 두도록 렌더링 된 부분의 일부를 "수집"하는 방법이 있습니다. 우리는 두 개의 간단한 수집가를 처분하여 구성 요소로 사용됩니다 : JSCollector 및 CSSCollector .
이 구성 요소는 자녀 나무의 일부를 수집합니다.
첫 번째 방법은 수집기 Collect 태그를 사용하는 것입니다.
먼저 메인 호출을 변경하겠습니다.
from mixt import JSCollector
print (
< JSCollector render_position = "after" >
< UserContext authenticated_user_id = 1 >
< ThingApp / >
< / User >
< / JSCollector >
) 이것은 모든 JSCollector.Collect 태그의 내용을 수집합니다.
TodoForm 업데이트하고 JSCollector.Collect 태그로 script 태그를 바꾸겠습니다.
class TodoForm ( Element ):
# ...
def render ( self , context ):
if callable ( self . add_url ):
add_url = self . add_url ( self . type )
else :
add_url = self . add_url
return
< JSCollector . Collect > { self . render_javascript ( context )} < / JSCollector . Collect >
< form method = "post" action = { add_url } onsubmit = "return on_todo_add_submit(this);" >
< label > New { self . type }: < / label > < itext name = "todo" / >
< button type = "submit" > Add < / button >
< / form >
< / Fragment > 우리는 TodoList 와 똑같이 할 수 있습니다.
class TodoList ( Element ):
# ...
def render ( self , context ):
return
< Fragment >
< JSCollector . Collect > { self . render_javascript ( context )} < / JSCollector . Collect >
< ul id = "todo-items" > {[ < Todo todo = { todo } / > for todo in self . todos ]} < / ul >
< / Fragment >이제 업데이트 된 코드를 실행하겠습니다.
print (
< JSCollector render_position = "after" >
< UserContext authenticated_user_id = 1 >
< ThingApp / >
< / User >
< / JSCollector >
)미화 출력은 다음과 같습니다.
< div >
< h1 > The "thing" list </ h1 >
< form method =" post " action =" /thing/add " onsubmit =" return on_todo_add_submit(this); " >
< label > New thing: </ label >
< input type =" text " name =" todo " />
< button type =" submit " > Add </ button >
</ form >
< ul id =" todo-items " >
< li > 1-1 </ li >
< li > 1-2 </ li >
</ ul >
</ div >
< script type =" text/javascript " >
function on_todo_add_submit ( form ) {
var text = form . todo . value ;
add_todo ( text ) ;
}
TODO_TEMPLATE = "<li>placeholder</li>" ;
function add_todo ( text ) {
var html = TODO_TEMPLATE . replace ( "placeholder" , text ) ;
var ul = document . querySelector ( '#todo-items' ) ;
ul . innerHTML = html + ul . innerHTML ;
}
</ script > 보시다시피, 모든 스크립트는 끝에 단일 script 태그에 있습니다. 더 정확하게는 render_position="after" 사용했기 때문에 JSCollector 태그가있는 곳의 끝에 더 정확하게. 또 다른 가능성은 JSCollector 태그가 시작된 곳에 이것을 넣기 위해 render_position="before" 입니다.
이 모든 것이 CSSCollector 태그와 동일한 방식으로 작동합니다. 여기서 컨텐츠는 <style type="text/css> tag로 표시됩니다.
HTML 세계에서 JS/CSS를 사용하는 것이 매우 일반적이므로, 우리는이 모든 것을 더 쉽게 할 수 있도록 설탕을 추가했습니다.
render_js 메소드가있는 경우 JSCollector 이 메소드의 결과를 자동으로 수집합니다. CSSSelector 및 render_css 메소드에 대해 동일합니다.
이를 통해 JSCollector.Collect 태그가 필요하지 않습니다.
우리의 예 에서이 작업을 만들기 위해 TodoForm 과 TodoList 에서 :
JSCollector.Collect 태그를 제거하십시오Fragment 태그를 제거하십시오render_javascript 메소드의 이름을 render_js 로 바꿉니다.render_js 자체를 호출 할 때 필요하지 않으므로 render_js 에서 html.Raw 로 호출을 제거하십시오. 출력이 문자열 인 경우 "원시"로 간주됩니다.이런 식으로 우리는 정확히 같은 결과를 얻습니다.
우리는 render_js 메소드가있는 어린이의 인스턴스 만 있기 때문에 지금 작동합니다.
그러나 자녀가 많으면이 방법은 각 어린이마다 요구됩니다. 사실이 인스턴스에 매우 특정한 코드 만 포함해야합니다.
구성 요소 클래스의 경우 JS/CSS를 한 번만 서비스하려면 render_js_global 또는 render_css_global ( classmethod 예상)을 사용해야합니다.
render_js 메소드를 수집하기 전에 처음으로 수집되며 처음으로 인스턴스가 발견됩니다.
따라서 여기에서 render_js render_js_global 로 변경하여 @classmethod 로 장식하면 여전히 동일하게 작동합니다.
이제 우리는 JavaScript 또는 스타일을 다시 그룹화 할 수 있습니다. 그러나 head 태그 나 body 태그의 끝에서 다른 곳에두고 싶다면 어떻게해야합니까?
언급, 즉 "Refs"에 가능합니다. 물론 DOM 부분이없는 React와 같은 맥락입니다.
심판을 만들고 구성 요소로 전달하면 어디서나 사용할 수 있습니다.
이를 위해 메인 코드를 업데이트합시다.
먼저 우리는 참조를 만듭니다.
from mixt import Ref
js_ref = Ref () 이것은 구성 요소에 대한 참조를 보유 할 새 개체를 만듭니다. 구성 요소에서는 Ref 가져올 필요가 없으며 js_ref = self.add_ref() 사용할 수 있지만 여기서 구성 요소가 아닙니다.
심판을 저장하기 위해 간단히 ref 소품으로 전달합니다.
< JSCollector ref = { js_ref } > ... < / JSCollector > 우리는 render_position prop를 제거했습니다. 이제 우리는 태그 전후에 JS를 넣기를 원하지 않기 때문에 다른 곳에서는 주목하십시오.
Ref가 참조한 구성 요소에 액세스하려면 current 속성을 사용하십시오.
js_collector = js_ref . current물론 이것은 렌더링 후에 만 수행 할 수 있습니다.
이것을 사용하여 head 에 script 태그를 추가 할 수 있습니까?
먼저 HTML을 업데이트하여 클래식 html , head 및 body 태그를 포함하십시오.
return str (
< html >
< head >
< / head >
< body >
< JSCollector ref = { js_ref } >
< UserContext authenticated_user_id = 1 >
< ThingApp / >
< / UserContext >
< / JSCollector >
< / body >
< / html >
) 이 시점에서 출력에 script 태그가 없습니다.
< html >
< head > </ head >
< body >
< div >
< h1 > The "thing" list </ h1 >
< form method =" post " action =" /thing/add " onsubmit =" return on_todo_add_submit(this); " >
< label > New thing: </ label >
< input type =" text " name =" todo " />
< button type =" submit " > Add </ button >
</ form >
< ul id =" todo-items " >
< li > 1-1 </ li >
< li > 1-2 </ li >
</ ul >
</ div >
</ body >
</ html > 가장 먼저 알아야 할 사항 : 수집가는 render_collected 메소드를 부르면서 수집 한 모든 것들을 렌더링 할 수 있습니다.
그리고 이미 script 태그가 포함되어 있다는 것을 기억하면 다음을 수행 할 수 있습니다.
# ...
< head >
{ js_ref . current . render_collected ()}
< / head >
# ...그러나 이것은 작동하지 않습니다.
AttributeError : 'NoneType' object has no attribute 'render_collected'렌더링 시간에 현재 값에 액세스하려고 노력하기 때문입니다. 이후에 완료해야합니다.
이를 위해서는 mixt 의 기능을 사용할 수 있습니다. 트리에 추가 된 것이 호출 가능하면 렌더링 후 문자열로 변환 할 때 호출됩니다.
그래서 우리는 예를 들어 람다를 사용할 수 있습니다.
# ...
< head >
{ lambda : js_ref . current . render_collected ()}
< / head >
# ...그리고 이제 작동합니다.
< html >
< head >
< script type =" text/javascript " >
function on_todo_add_submit ( form ) {
var text = form . todo . value ;
add_todo ( text ) ;
}
TODO_TEMPLATE = "<li>placeholder</li>" ;
function add_todo ( text ) {
var html = TODO_TEMPLATE . replace ( "placeholder" , text ) ;
var ul = document . querySelector ( '#todo-items' ) ;
ul . innerHTML = html + ul . innerHTML ;
}
</ script >
</ head >
< body >
< div >
< h1 > The "thing" list </ h1 >
< form method =" post " action =" /thing/add " onsubmit =" return on_todo_add_submit(this); " >
< label > New thing: </ label >
< input type =" text " name =" todo " />
< button type =" submit " > Add </ button >
</ form >
< ul id =" todo-items " >
< li > 1-1 </ li >
< li > 1-2 </ li >
</ ul >
</ div >
</ body >
</ html > Hurray 우리는 그것을 만들었습니다! mixt 의 모든 주요 특징은 설명했다. 이제 자신의 프로젝트에서 mixt 사용할 수 있습니다.
다음 단계로 API 문서를 읽으십시오.