Attributes
In qingkuai, we can also add attributes to component tags just like regular HTML tags to pass in parameters. This behavior is called component prop passing and is used to provide external data or configuration to a component. With component props, we can make a component behave differently or look different in different scenarios, enhancing its reusability and flexibility.
Normal Attributes
Normal attributes are passed to components as strings and accessed within the component via the built-in props
object:
- js
- ts
<!-- Outter.qk -->
<Inner attr="value" >
<!-- Inner.qk -->
<lang-js>
console.log(props.attr) // value
</lang-js>
<!-- Outter.qk -->
<Inner attr="value" >
<!-- Inner.qk -->
<lang-js>
interface Props {
attr: string
}
console.log(props.attr) // value
</lang-js>
If you add an attribute name to the component tag without assigning it a value, the component receives a boolean true
internally:
- js
- ts
<!-- Outter.qk -->
<Inner attr />
<!-- Inner.qk -->
<lang-js>
console.log(props.attr) // true
</lang-js>
<!-- Outter.qk -->
<Inner attr />
<!-- Inner.qk -->
<lang-js>
interface Props{
attr?: boolean
}
console.log(props.attr) // true
</lang-js>
Dynamic Aattributes
Dynamic attributes passed to components are also accessed internally through the props
built-in object. However, dynamic attributes can carry various data types beyond just strings. Moreover, when the data source changes, DOM elements using that attribute inside the component will be updated accordingly:
- js
- ts
<!-- Outter.qk -->
<lang-js>
const list = ["js", "ts", "qk"]
setTimeout(list.pop, 1000)
</lang-js>
<Inner !list />
<!-- Inner.qk -->
<p>The length of list is: {props.list.length}</p>
<!-- Outter.qk -->
<lang-ts>
const list = ["js", "ts", "qk"]
setTimeout(list.pop, 1000)
</lang-ts>
<Inner !list />
<!-- Inner.qk -->
<lang-ts>
interface Props {
list: string[]
}
</lang-ts>
<p>The length of list is: {props.list.length}</p>
Events
Component events, like other non-ref props, are accessed inside the component through the built-in props
object:
- js
- ts
<!-- Outter.qk -->
<Inner @someThingHappened={console.log($args)} />
<!-- Inner.qk -->
<lang-js>
setTimeout(() => props.someThingHappened("event is triggered."), 1000)
</lang-js>
<!-- Outter.qk -->
<Inner @someThingHappened={console.log($args)} />
<!-- Inner.qk -->
<lang-ts>
interface Props {
someThingHappened: (msg: string) => void
}
setTimeout(() => props.someThingHappened("event is triggered."), 1000)
</lang-ts>
Reference Attributes
Reference attributes are a very important feature in components. They allow the component to modify values passed in from outside. Since properties in the props
object are actually read-only getters, their values cannot be modified directly. At this point, you need to use the reference-passing mechanism provided by ref attributes to achieve this. In qingkuai, the component file has a built-in refs
object for accessing ref attributes passed from outside. By modifying properties on refs
, you can not only update external data but also trigger its reactive updates. Here's a simple example:
- js
- ts
<!-- Outter.qk -->
<lang-js>
import Inner from "./Inner.qk"
let name = "Javascript"
</lang-js>
<p>name is: {name}</p>
<Inner &attr={name} />
<!-- Inner.qk -->
<p>refs.attr is: {refs.attr}</p>
<button @click={refs.attr = "QingKuai"}>Change refs.attr</button>
<!-- Outter.qk -->
<lang-ts>
import Inner from "./Inner.qk"
let name = "Javascript"
</lang-ts>
<p>name is: {name}</p>
<Inner &attr={name} />
<!-- Inner.qk -->
<lang-ts>
interface Refs {
attr: string
}
</lang-ts>
<p>refs.attr is: {refs.attr}</p>
<button @click={refs.attr = "QingKuai"}>Change refs.attr</button>
props
is itself a reference type (such as an object or array), you can modify its internal properties. For example, when props.obj
is an object, props.obj.xxx
can be modified.
Destructure Built-in Objects
Values obtained by directly destructuring the props
or refs
built-in objects are not themselves reactive. This is because accessing built-in object properties triggers the getter
to collect reactive dependencies:
const { str } = props
To destructure built-in objects properly, you need to use the reactive destructure declaration syntax we introduced earlier:
const { value } = der(refs)
const { str, arr } = der(props)
refs
, if the destructured property is a primitive (such as a string, number, or boolean), modifying it will not affect the original ref value. This is because what you get is a value copy, not a reference to the original — which is consistent with JavaScript's default behavior.
Attribute Name Format
Component attribute names in qingkuai, just like component names, support both kebab-case and camelCase. The following usages are equivalent:
<Component myAttr />
<Component my-attr />
By default, when formatting component files, all kebab-case component attribute or event names will be converted to camelCase. However, you can add a .prettierrc
file to the component file's directory or its parent directories and include the following content to change the preference to kebab-case:
{
"qingkuai": {
"componentAttributeFormatPreference": "kebab"
}
}