HTML cargo cult in the modern frontend

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.







Breaking the template







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.







Sinister Valley







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
      
      





xml.tree:







! 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>
      
      





Stormtrooper effect







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. - , , , , , "" -, , .







And inside she has a neon







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



- ?

. .







, : . .







And the king is naked!







?



, . β€” . , . JS , . , "" JS. .







, , 2 :







  • , .
  • , .


"" "", , -, β€” .







view.tree, $mol:







$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, .







Dart, yes not Dart







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 .









JSX JS , - JIT-:













β€” , . β€” 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>
      
      







JSX . DOM . :







<Dialog>
    <Hello ref={ setHelloRef } />
    <World ref={ setWorldRef } />
</Dialog>
      
      





, , . :







<Dialog>
    <Message key="hello">Hello</Message>
    <Message key="world">World</Message>
</Dialog>
      
      





, , .







, , , .









HTML. :







<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



.







" " . "$mol: " $mol.







, , , .







What doesn't kill you makes you ... weirder!







Boredom, boredom, BORING!








All Articles