Hello, my name is Dmitry Karlovsky and I ... I love to rip templates. And in the frontend, there are just a lot of misconceptions around template design. So let's tear them into shreds from bottom to top and from right to left.
Next, we'll take a look at what templates are. Their key strengths and fatal weaknesses. Why are they needed and why they are not needed. We will form an idea of ββthe correct solution and skate through the popular ones. So the full range of feelings is provided to us.
Please go to the table ..
What is a template?
, : . , . , ..
:
"Hello, ${name}!"
:
"Hello" + name + "!"
, , β ? , , . , , .
XSLT:
<xsl:template name="page">
<acticle>
<h1>
<xsl:copy-of select="./head" />
</h1>
<xsl:copy-of select="./body" />
</article>
</xsl:template>
, 1 3 , , , , JSX-:
const head = <h1>{ headContent }</h1>
const body = 'Hello, World'
const article = <article>{ head }{ body }</article>
, , , , . , , , .
, JSX , . , HTML.
HTML?
HTML. HTML , DOM . HTML . .
, HTML, JSON, ProtoBuf . , DOM , JS-API, HTML . , DOM HTML, DOM.
, HAML:
!!! %html{ :lang => "ru" } %head %title= title %meta{ 'http-equiv' => 'Content-Type', :content => 'text/html' }/ %body %h1= title %p= description
! DOCTYPE html html @ lang \ru head title ? title meta @ content \text/html; charset=utf-8 @ http-equiv \Content-Type body h1 ? title p ? description
JSON. , .
HTML- , :
<!DOCTYPE html>
<html lang='ru'>
<head>
<title>{title}</title>
<meta
content='text/html; charset=utf-8'
http-equiv='Content-Type'
/>
</head>
<body>
<h1>{title}</h1>
<p>{description}</p>
</body>
</html>
HTML?
HTML DOM. . β . , .
HTML, DOM , . , .
"":
<bi-panel class="example">
<check-box
class="editable"
side="left"
[(checked)]="editable"
i18n
>
Editable
</check-box>
<text-area
#input
class="input"
side="left"
[(value)]="text"
[enabled]="editable"
placeholer="Markdown content.."
i18n-placeholder="Showed when input is empty"
/>
<div
*ngIf="text"
class="output-label"
side="right"
i18n
>
Result
</div>
<mark-down
*ngIf="text"
class="output"
side="right"
text="{{text}}"
/>
</bi-panel>
HTML, HTML, Angular-. DOM ( HTML) . , : , . , , , .
HTML. , :
#input
β , TS.class="editable"
β CSS.side="left"
β , .[(checked)]="editable"
β .[enabled]="editable"
β .text="{{text}}"
β .placeholer="Markdown content.."
β - .i18n-placeholder="Showed when input is empty"
β , , ,placeholder
, .*ngIf="text"
β , .
4 , , , . , HTML, . β , HTML. - , , , , , "" -, , .
HTML ?
HTML β . , "", . "" .
HTML . VR , . . . . , , , . . .
, , . , . β , , . , β . , β ", - , ".
:
_ = ()=> (
()=> ,
=> ( ) ,
_ => _( _ ),
__ => ( )( __ ),
_ => _( _ )
)
RDF:
_ _ _ _ _ _ _ __ __
. JSX, HTML, JS, , :
const Example = ( props: {
className?: string
text?: string
onTextChanged?: ( next: string )=> void
editable?: boolean
onEditableToggle?: ( next: boolean )=> void
} )=> {
const [ stateText, setStateText ] = useState( props.text ?? '' )
const [ stateEditable, setStateEditable ] = useState( props.editable ?? true )
const [ inputElement, setInputElement ] = useState< HTMLTextAreaElement >( null )
const className = ( props.className ?? '' ) + ' example'
const text = props.text ?? stateText
const editable = props.editable ?? stateEditable
const setText = useCallback( ( next: string )=> {
setStateText( next )
props.onTextChanged?.( next )
}, [ props.onTextChanged ] )
const setEditable = useCallback( ( next: boolean )=> {
setStateEditable( next )
props.onEditableToggle?.( next )
}, [ props.onEditableToggle ] )
return (
<BiPanel
className={ className }
left={
<>
<CheckBox
className="editable"
checked={ editable }
onToggle={ setEditable }
>
{ l10n( 'Editable' ) }
</CheckBox>
<TextArea
ref={ setInputElement }
className="input"
value={ text }
onChange={ setText }
enabled={ editable }
placeholder={ l10n( 'Markdown content..' ) }
/>
</>
}
right={
text
? <>
<div
className="output-label"
>
{ l10n( 'Result' ) }
</div>
<MarkDown
className="output"
text={ text }
/>
</>
: <></>
}
/>
)
}
. , ..
?
. AST, l10n
, , - , , .
,CheckBox.checked
,TextArea.enabled
props.editable
?
. , , , . , , checked={ editable } onToggle={ setEditable }
.
,editable
, ?
. data-flow . .
, CSS-.example .output .link
- ?
. .
, : . .
?
, . β . , . JS , . , "" JS. .
, , 2 :
- , .
- , .
"" "", , -, β .
$my_example $mol_view sub / <= Panel $my_bipanel left <= input / <= Editable $mol_check_box checked?val <=> editable?val true title @ \Editable <= Input $mol_textarea hint @ \Markdown content.. value?val <=> text?val \ enabled <= editable right <= output / <= Output_label $mol_paragraph sub / <= output_label @ \Result <= Output $mol_text text <= text
, JSX , , , . , , - . β , :
export class $my_example extends $.$my_example {
output() {
return this.text() ? super.output() : []
}
}
, - JSX , . , , -DSL HTML, .
JSX?
, , JSX ...
Push
, , . VDOM:
return (
<Dialog visible={ opened } >
{ ()=> <>Heavy content</> }
</Dialog>
)
, , "":
- .
- useCallback, .
- .
- VDOM API .
:
const dialogContent = useCallback( ()=> (
<>Heavy content</>
) )
return userObserver( ()=> (
<Dialog visible={ opened } >
{ dialogContent }
</Dialog>
) )
push pull β . pull: , , . push .
β , . β FireFox.
JSX . β . β useCallback , ( , - ):
const setName = useCallBack( ( name: string )=> {
setInfo({ ... info, name })
}, [ info, setInfo ] )
return <Input value={ info.name } onChange={ setName }>
, useCallback , info
, , , info.name
. Input
info
.
, . , , , .
- HTML , β :
<input type="password" minLength={ 5 } className={ 'password ' + className } />
:
<Dialog>
<Hello />
<World />
</Dialog>
<Dialog
children={[
<Hello />,
<World />,
]}
/>
β : HTML JS.
:
<Dialog>
<Hello />
{/* World */}
</Dialog>
<Dialog>
<Hello ref={ setHelloRef } />
<World ref={ setWorldRef } />
</Dialog>
, , . :
<Dialog>
<Message key="hello">Hello</Message>
<Message key="world">World</Message>
</Dialog>
, , .
, , , .
<ThemeContext.Provider value={theme} >
<UserContext.Provider value={signedInUser} >
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
<ThemeContext.Consumer>
{ theme => (
<UserContext.Consumer>
{ user => (
<ProfilePage user={user} theme={theme} />
) }
</UserContext.Consumer>
) }
</ThemeContext.Consumer>
, , . , , , , . , , , , , , :
<div className="tag-list">
{tags.map((tag) => (
<button
key={tag}
className="tag-pill tag-default"
onClick={() =>
dispatch({
type: 'SET_TAB',
tab: { type: 'TAG', label: tag },
})
}
>
{tag}
</button>
))}
</div>
, "" , , β . , , . , , ..
view.tree ?
, , , ..
IDE
Microsoft JSX TypeScript, , Language Server. VSCode, IDE.
, Microsoft TS. view.tree
, , TS, , IDE . , , . .
. , .
, null
any
:
/**
* Placeholder null
*/
Placeholder() {
return null as any
}
:
/**
* name!id?next \Unknown
*/
@ $mol_mem_key
name(id: any, next?: any) {
if ( next !== undefined ) return next as never
return "Unknown"
}
, . , , :
/**
* Placeholder null $mol_view
*/
Placeholder() {
return null as null | $mol_view
}
/**
* name!number?string \Unknown
*/
@ $mol_mem_key
name(id: number, next?: string) {
if ( next !== undefined ) return next
return "Unknown"
}
?
, . , , ..
"Tree β AST " tree
. " sourcemaps " . "$mol β " view.tree
.
, , , .