UNPKG

als-component

Version:

lightweight JavaScript library for creating reactive, server-rendered, and browser-based components.

309 lines (222 loc) 7.64 kB
# als-component `als-component` is a lightweight JavaScript library for creating reactive, server-rendered, and browser-based components. It allows you to build dynamic components that work both on the server (Node.js) and in the browser, providing seamless reactivity, lifecycle hooks, and event handling. ## Installation Install via NPM: ```bash npm install als-component ``` ## Quick Start ### Example for Node.js (Server) ```js const Component = require('als-component'); class HelloComponent extends Component { constructor(props) { super(props) } render({ name }) { return `<div>Hello, ${name}!</div>`; } } const instance = new HelloComponent({ name: 'World' }); console.log(instance.call()); // Outputs: <div component="HelloComponent">Hello, World!</div> ``` ### Example for Browser ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>als-component Example</title> <script src="/node_modules/als-component/component.js"></script> <!-- Or minified version --> <script src="/node_modules/als-component/component.min.js"></script> </head> <body> <div component="App"></div> <script> class App extends Component { constructor(props) { super(props) } render({ count = 0 }) { return ` <div> <button click="${this.action('click', () => this.update({ count: count - 1 }))}">-</button> <span>${count}</span> <button click="${this.action('click', () => this.update({ count: count + 1 }))}">+</button> </div> `; } } const app = new App({ count: 0 }); app.update(); </script> </body> </html> ``` ## API Reference ### `constructor(props = {}, inner)` Initializes a new component. - `props` *(Object)*: The initial properties of the component. - `inner` *(String)*: The inner content of the component. ### `call()` Renders the component and returns the HTML string. **Example:** ```js const html = instance.call(); console.log(html); // <div component="ComponentName">Rendered content</div> ``` ### `update(props, inner)` Updates the component with new properties and re-renders it. **Example:** ```js instance.update({ count: 5 }); ``` ### `action(event, fn)` Creates an event action and binds it to the element. - `event` *(String)*: The event type (e.g., 'click'). - `fn` *(Function)*: The event handler function. - **Returns**: *(String)*: A unique action identifier. **Example:** ```js const clickId = instance.action('click', () => alert('Button clicked')); ``` ### `on(event, fn)` Registers a lifecycle hook. - `event` *(String)*: The lifecycle event ('mount' or 'unmount'). - `fn` *(Function)*: The callback function. **Example:** ```js instance.on('mount', () => console.log('Component mounted')); instance.on('unmount', () => console.log('Component unmounted')); ``` ### `runActions()` Binds all stored actions to the corresponding elements and triggers `mount` hooks. ## Asynchronous Rendering The `render` method can be asynchronous. If it is an `async` function, the component handles it automatically. **Example:** ```js class AsyncComponent extends Component { async render() { const data = await fetchData(); return `<div>${data}</div>`; } } const asyncInstance = new AsyncComponent(); asyncInstance.update(); ``` ### `async buildAsync(updateElement = true)` and `buildSync(updateElement = true)` You can call for `buildAsync` (return promise) or `buildSync` with `updateElement` flag to update or not to update the element. ### `set elementOuter` You can manualy set outerHTML for component's element. If element is not exists, it simply will do nothing. ## Lifecycle Hooks ### `mount` Called when the component is added to the DOM. ### `unmount` Called when the component is removed from the DOM. **Example:** ```js instance.on('mount', () => console.log('Mounted')); instance.on('unmount', () => console.log('Unmounted')); ``` ## Event Handling Use `action()` to bind events to elements within the component. **Example:** ```js class ClickComponent extends Component { render() { return `<button click="${this.action('click', () => alert('Button clicked'))}">Click Me</button>`; } } ``` ## Nested Components You can nest components within each other. The parent component can include a child component using `call()`. **Example:** ```js class ChildComponent extends Component { render() { return `<span>Child Component</span>`; } } class ParentComponent extends Component { render() { return `<div>Parent: ${new ChildComponent().call()}</div>`; } } const parentInstance = new ParentComponent(); parentInstance.update(); ``` ## Memory Management The `als-component` library uses `WeakMap` to store component instances, ensuring that they are garbage collected when removed from the DOM. **Example:** ```js const instance = new Component(); document.body.appendChild(instance.element); instance.update(); instance.element.remove(); instance.runActions(); ``` The component is removed from `WeakMap` automatically when it is no longer referenced. ## Optimization Tips - Use `update()` sparingly to avoid excessive DOM manipulations. - Prefer using asynchronous `render` for components that require data fetching. - Use unique keys for components to avoid collisions. ## Error Handling When using asynchronous methods or complex nested components, ensure you handle potential errors gracefully. **Example:** ```js try { await instance.update(); } catch (error) { console.error('Error updating component:', error); } ``` ## Example ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="/component.js"></script> <title>Users</title> </head> <body> <div component="Users"></div> </body> <script> class User extends Component { constructor(props) { super(props) } remove() { const {user,usersComponent} = this.props // const usersComponent = this.Component.components.Users // Another way to get component usersComponent.update({users:usersComponent.props.users.filter(({id}) => id !== user.id)}) } render({user}) { const {id,email,age,gender,username,firstName} = user return /*html*/`<div> ${firstName} <button click="${this.action('click',() => this.remove())}">delete</button> </div>` } } class Users extends Component { constructor(props={},inner) { super(props,inner) } async render({users}) { if(users === undefined) { fetch('https://dummyjson.com/users') .then(res => res.json()) .then(data => { this.update({users:data.users}) }); return /*html*/`<div>Fething users</div>` } return /*html*/`<div> ${users.map(user => new User({user,key:user.id,usersComponent:this}).call()).join('')} </div>` } } const usersComponent = new Users() usersComponent.update() </script> </html> ```