Axii
source link: https://axii.js.org/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Axii is a javascript UI framework. The main features are:
- Using function and jsx to create component, not template.
- Using vue 3 like reactive data as state。Only update related element when data changed, not re-rendering component.
- It provides extending and overwriting mechanism for component to improve efficiency for both component developer and user.
The following contents will give you a tutorial to Axii basic usage. More documents is working in progress, help will be appreciated. Thanks to creators of React and Vue.
Using Reactive Data
- Use
atom
to make non-object data likestring
/number
/boolean
reactive. Object which do not need deep watch can use it too. - use
.value
to get and set value of data created by usingatom
. Don't use.value
to bind data to element property. - Use
computed
to create computed value.
/** @jsx createElement */
import { createElement, atom, computed } from 'axii'
export default function Basic() {
const firstName = atom('')
const lastName = atom('')
const fullName = computed(() => {
if (!firstName.value && !lastName.value) return 'please enter your name'
return `${firstName.value}-${lastName.value}`
})
const setFirstName = (e) => {
firstName.value = e.target.value
}
const setLastName = (e) => {
lastName.value = e.target.value
}
return (
<div>
<input value={firstName} placeholder="firstName" onInput={setFirstName}/>
<input value={lastName} placeholder="lastName" onInput={setLastName}/>
<div>Hello, {fullName}.</div>
</div>
)
}
Axii only update element which use a reactive data as children or property, which means component only render once if the references of component props is not changed.Dynamic Structure
Use function to represent a dynamic structure such as repeated structure or conditional structure. (Axii will auto detect dynamic structure expression in jsx in the future, so user do not need to use function to declare.)
Example/** @jsx createElement */
import {createElement, atom, reactive} from 'axii'
export default function Code() {
const inputItem = atom('')
const items = reactive([])
const setInputItem = (e) => inputItem.value = e.target.value
const addItem = () => {
items.push(inputItem.value)
inputItem.value = ''
}
const removeItem = (index) => {
items.splice(index, 1)
}
return (
<div>
<input value={inputItem} placeholder="plan something" onInput={setInputItem}/>
<button onClick={addItem}>add</button>
<ul>
{function List() {
return items.map((item, i) => (
<li>
<span>{item}</span>
<button onClick={() => removeItem(i)}>x</button>
</li>
))
}}
</ul>
</div>
)
}
Create Better Component
Axii provides an API called createComponent
to help user create better component with:
- Better style system
- Allows separating style related logic from structure logic.
- Allows separating layout and non-layout style.
- More powerful component behavior control.
- Auto revoke callback property.
- Auto implement controlled/uncontrolled/delegate mode.
- Feature based component logic separation.
Layout Style
Layout Style refers to position or size related style, such as display
/width
/height
.
Axii introduces a special attribute type called Layout Attribute to help user deal with layout style. Here are the guidelines of how to use Layout Attribute:
- Use semantic tag name instead of
div
orspan
. - Use layout attribute
text
/inline
/block
to declare layout type. Axii will applydisplay: inline
totext
type anddisplay: inline-block
toinline
type. - Use
flex-display
or extend display by yourself to handle complex layout(Docs upcoming). - Use box related attribute like
width
/heigth
with layout type as prefix.
import { createElement } from 'axii'
export default function Code() {
return (
<todoList block block-width="100%">
<todoItem block flex-display flex-justify-content="space-between">
<name>swimming</name>
<action>delete</action>
</todoItem>
<todoItem block flex-display flex-justify-content-space-between>
<name>swimming</name>
<action>delete</action>
</todoItem>
</todoList>
)
}
Pass Layout Attribute To Component
Layout attribute can be passed to Component to control component box style. Simply add layout:
namespace to attribute.
import { createElement } from 'axii'
function Box() {
return <div style={{background: '#cecece'}}>box</div>
}
export default function Code() {
return (
<container>
<line block>
<Box layout:inline/>
</line>
<line block block-margin-top-10px>
<Box layout:inline layout:inline-width-300px/>
</line>
<line block block-margin-top-10px>
<Box layout:block/>
</line>
</container>
)
}
Non-Layout Style
Non-Layout style refer to layout irrelated style such as color
/font-family
.
User can write better non-layout style by implementing feature.
/** @jsx createElement */
import { createElement, atom, createComponent, propTypes } from 'axii'
function Component({ onFocus, onBlur}) {
return <container>
<line block>
<box use="input" onFocus={onFocus} onBlur={onBlur}/>
</line>
<line block>
<textLink use="a" href="#">a link</textLink>
</line>
</container>
}
Component.Style = (fragments) => {
fragments.root.elements.box.style(({focused}) => {
return {
borderStyle: 'solid',
borderWidth: 1,
outline: 'none',
borderColor: focused.value ? 'blue': 'black'
}
})
// pseudo class
fragments.root.elements.textLink.match.hover.style({
color: 'green'
})
}
Component.Style.propTypes = {
focused: propTypes.bool.default(() => atom(false)),
onFocus: propTypes.callback.default(() => ({focused}) => focused.value = true),
onBlur: propTypes.callback.default(() => ({focused}) => focused.value = false)
}
export default createComponent(Component)
Checkout more about feature mechanism bellow.Component Controlling
Component created by using createComponent
is enhanced with:
- Auto state manage modes support:
- controlled mode
- uncontrolled mode
- delegated mode
- Component default mutation prevention support
Component event callback declared in proptypes is more powerful than simple function property. It allows you:
- Receiving 3 extra arguments:
- draftProps: the data you can mutate.
- props: the origin props object, do not mutate it.
- event: the event object.
- Preventing default event callback mutation by
return false
. - Completely overwriting component callback by mark callback with
overwrite
.
Component state managing have three modes differed by passing different type of data, See details below:
Base Example Component
Example/** @jsx createElement */
import { createElement, atom, propTypes } from 'axii'
export default function Input({ value, onChange}) {
return <input value={value} onInput={onChange}/>
}
Input.propTypes = {
value: propTypes.string.default(() => atom('')),
onChange: propTypes.callback.default(() => ({value}, props, e) => {
value.value = e.target.value
})
}
Controlled Mode
Passing in non-reactive data makes component fully controlled by upper scope. Component default event callback will not mutate state.
Example/** @jsx createElement */
import { createElement } from 'axii'
import Input from './Input.jsx'
export default function ControlledComponent() {
const value = 'controlled value'
return <Input value={value}/>
}
Uncontrolled Mode
If no data passed in, component will create and mutate state according to proptypes.
Example/** @jsx createElement */
import { createElement } from 'axii'
import Input from './Input.jsx'
export default function InputDemo() {
const onChange = () => {
}
return <Input onChange={onChange}/>
}
Delegated Mode
If pass in reactive data, component will mutate it with event callback defined in proptypes.
Example/** @jsx createElement */
import { createElement, atom } from 'axii'
import Input from './Input.jsx'
export default function InputDemo() {
const value = atom('')
return (
<container>
<Input value={value}/>
<div>{() => `value: ${value.value}`}</div>
</container>
)
}
Extending & Overwriting Component
Axii feature mechanism help you split component code by features, not structure. A feature can:
- Define its own state、event and style。
- Mutate component structure.
- Attach event callback to any element.
/** @jsx createElement */
import { createElement, createComponent, atom, propTypes } from 'axii'
function Input({ value, onChange}) {
return <input value={value} onInput={onChange}/>
}
Input.propTypes = {
value: propTypes.string.default(() => atom('')),
onChange: propTypes.callback.default(() => ({value}, props, e) => {
value.value = e.target.value
})
}
function InputStyle(fragments) {
fragments.root.elements.input.onFocus((e, {onFocus}) => {
onFocus()
})
fragments.root.elements.input.onBlur((e, {onBlur}) => {
onBlur()
})
fragments.root.elements.input.style(({focused}) => {
return {
borderStyle: 'solid',
borderWidth: 1,
outline: 'none',
borderColor: focused.value ? 'blue': 'black'
}
})
}
InputStyle.propTypes = {
focused: propTypes.string.default(() => atom(false)),
onFocus: propTypes.callback.default(() => ({focused}) => focused.value = true),
onBlur: propTypes.callback.default(() => ({focused}) => focused.value = false),
}
export const InputWithStyle = createComponent(Input, [InputStyle])
export default function InputDemo() {
return <InputWithStyle />
}
User can use Component.extend
to extend component by self, no extra support of developer needed.
/** @jsx createElement */
import { createElement } from 'axii'
import { InputWithStyle } from "./FeatureBasedInput.jsx";
const InputWithCustomStyle = InputWithStyle.extend(function CustomStyle(fragments){
fragments.root.elements.input.style(({focused}) => ({
borderColor: focused.value ? 'red' : 'black'
}))
})
export default function InputDemo() {
return <InputWithCustomStyle />
}
Quick Start
Creating App
npx degit ariesate/engine/packages/vite-axii my-project
cd my-project
npm install
npm run dev
Component Library & Icon Library
npm install axii-components --save
npm install axii-icons --save
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK