als-view
v0.1.5
Published
Server-side rendering framework for dynamic pages with on-the-fly component bundling, supporting SEO and layout management.
Downloads
210
Maintainers
Readme
als-view
⚠ Warning: Beta Testing Stage
This library is currently in beta testing and is not yet recommended for use in production environments. While it offers a powerful framework for dynamic server-side rendering, active development and testing are ongoing, and certain features or behaviors may be subject to change.
Introduction
als-view
is a powerful yet straightforward Node.js library designed to simplify the server-side rendering of dynamic web pages with integrated, on-the-fly component rendering. Built to meet the needs of modern applications, als-view
allows developers to create robust, SEO-friendly pages that render JSX-like components dynamically—no pre-build required.
By leveraging two core tools, als-layout
and als-render
, this library enables a fluid and high-performance approach to web page generation:
SEO Optimization and Content Management: Using
als-layout
,als-view
provides a DOM-like structure with rapid configuration of SEO elements, scripts, styles, and other dynamic content. This allows you to fully manage page content with flexible, server-side tools.Dynamic Component Rendering: The integration with
als-render
letsals-view
render React-like components on the server and in the browser without any build step. JSX-like components work directly on the server and can be executed in the browser as well, bridging server and client seamlessly.Automatic Environment Handling:
als-view
supports both Development and Production modes, allowing efficient workflow adjustments. In Production mode, the library generates component bundles as separate files to optimize browser caching and load times.
Key Advantages
- Simplified Dynamic Page Creation:
als-view
combines the ease of creating SEO-optimized dynamic pages with JSX-like component rendering. - Unified Rendering for Server and Client: Components are rendered on demand and can function in both server and browser environments without requiring a separate build process.
- Production-Optimized Caching: In Production mode,
als-view
automatically writes component bundles to static files, allowing browsers to cache scripts efficiently for optimal performance.
Whether you’re building a full-fledged server-side rendered application or need a lightweight solution for server-rendered pages with reusable components, als-view
offers the flexibility and performance to meet your needs.
Installation
Requirements
- Node.js version 14 or higher
Install als-view
To get started with als-view
, use npm to install the package:
npm install als-view
Initial Setup
To configure als-view
for your project, initialize the environment settings. This setup allows you to specify paths, development modes, and logging options.
// Import als-view
const View = require('als-view');
// Set environment options (optional)
View.setEnv({
publicPath: './public', // Directory to store static files in Production mode
dev: process.env.NODE_ENV !== 'production', // Enable Dev mode based on environment
logger: console // Custom logger (optional)
});
Setting Up Your Directory Structure
For als-view
to work efficiently, ensure your project’s directory structure includes a views
folder with your JSX-like component files and a public
folder where static files can be stored. For example:
my-project/
├── public/ # Stores static files in Production mode
└── views/
└── App.js # JSX-like components
Now you’re ready to start creating pages and rendering components dynamically!
Initializing Project Structure with als-view
To quickly set up your project structure with als-view
, use the provided copy.js
script. This script creates a default folder structure within your project’s directory, which includes essential resources like layouts, views, pages, and error handling components.
Running the copy.js
Script
Run the Command: To execute the script, use the following command from your project's root directory. This will copy the default
als-view
folder structure into your project:node ./node_modules/als-view/build/copy
Using the
--overwrite
Flag: The--overwrite
flag allows you to overwrite any existing resources within the target directory. This is useful if you want to reset the folder structure or update it with the latest version:node ./node_modules/als-view/build/copy --overwrite
Note: Use the
--overwrite
flag carefully, as it will replace any existing files in the target directory.
Folder Structure Created by the copy.js
Script
After running the copy.js
script, your project directory will contain the following resources
structure:
my-project/
├── resources/
│ ├── index.js # Main entry point for configuring views and environment
│ ├── components/
│ ├── layouts/
│ │ └── main.js # Base layout with core settings (e.g., SEO, scripts, styles)
│ ├── pages/
│ │ └── Root.js # Example page component
│ └── views/
│ ├── main.js # Main view setup using the base layout
│ └── error/
│ ├── error-codes.js # Map of error codes to status messages
│ ├── error-page.js # Error page view and layout configuration
│ └── styles.js # CSS styles specific to error pages
Overview of the Created Files
resources/index.js
:- Sets up the main configuration for
als-view
by setting environment variables such aspublicPath
,dirName
,dev
mode, andlogger
. - Imports all views to ensure they are accessible from the main
View
instance.
- Sets up the main configuration for
resources/layouts/main.js
:- Defines a base
Layout
that includes core settings, such as viewport, title, and default styles or scripts. - This layout serves as the foundation for all views that will be rendered in the application.
- Defines a base
resources/pages/Root.js
:- A sample JSX-like component to serve as a starting point for creating new pages.
- This component can be updated or duplicated to create additional pages as needed.
resources/views/main.js
:- Configures the main view by cloning the base layout (
layouts/main.js
) and adding pages to the view. - Includes example code for adding a
root
page using theaddPage
method.
- Configures the main view by cloning the base layout (
resources/views/error
Folder:- Contains error-specific configurations:
error-codes.js
: Map of HTTP status codes to user-friendly messages.error-page.js
: Sets up an error view with a custom layout for displaying error messages.styles.js
: Defines CSS styles for error pages.
- Contains error-specific configurations:
Usage Example
Once the structure is created, you can use it directly in your application code to configure and serve views and pages. Here’s a quick example of setting up the root and error pages:
// Import the main View setup
const View = require('./resources');
// Serve the root page
app.get('/', (req, res) => {
View.views.main.pages.root.response({}, req, res).end();
});
// Serve error pages
app.use((req, res) => {
const status = 404;
View.views.error.pages.error.response({ status }).end();
});
With this setup, your als-view
project structure is ready to use for server-side rendering, including error handling and SEO configurations.
Core Concepts and Components
In als-view
, the main point of interaction is the View
class. Through this class, you can create and manage layouts, pages, and components, as well as generate responses dynamically based on user requests. Here’s a high-level overview of how the core concepts and components work together in als-view
:
1. View
: The Core Interface
View
is the central class in als-view
, managing layouts, pages, and rendering configurations. All actions within als-view
—such as setting up base layouts, adding pages, configuring views, and creating responses—are performed through View
.
- Layout Management:
View
can create or receive a base layout, which defines core elements like styles, scripts, favicon, host, and SEO metadata. - Pages and Views: For each view (e.g., a section like a dashboard), you can define specific layouts and configuration. Pages within a view (e.g.,
users
,products
) inherit the layout and can add their own components or scripts as needed.
Workflow with View
Create a Base Layout: The base layout is a
Layout
instance that defines common scripts, styles, and metadata to be shared across views.const Layout = require('als-layout') const baseLayout = new Layout('',{host:'http://some.com'}) .viewport() .title('My Application'); .favicon('/path/to/favicon.ico'); // styles, links, scripts, description, ...
Initialize a
View
with the Layout: Using the base layout, initialize aView
for a specific section or purpose. For example, create a dashboard view to manage user, product, or analytics pages.const dashboardView = new View(baseLayout, 'dashboard');
Define Pages within the View: Add pages to the
View
by specifying their names and optional configurations. Each page can include JSX-like components, adding a level of interactivity. For pages with components,RenderedPage
can be used to dynamically render and serve components.dashboardView.addPage('users', { title: 'Users Page' }); dashboardView.addPage('products', { title: 'Products Page' }, './views/Products');
Use Hooks for Custom Logic: Hooks can be added at the
View
orPage
level, allowing custom logic to execute before rendering. For example, a hook can modify data before it’s passed to the component for rendering.dashboardView.addHook((data, response) => { data.user = getUserInfo(); // Add user info to the data });
Generate Responses: Once views and pages are defined,
als-view
allows you to access them directly by name for response generation. This provides an organized and reusable approach to creating dynamic responses for different parts of your application.const { users } = View.views.dashboard.pages users.response(data, req, res).end(200); // Send the response with generated HTML and scripts
Summary of Key Classes
Layout
: The DOM-like structure that defines a page’s core elements. It serves as the basis for eachView
.View
: Manages layouts, pages, and hooks. Allows access to defined views and their pages for generating responses.Page
: Represents a standard server-rendered page, supporting SEO tags, custom hooks, and a structured response.RenderedPage
: ExtendsPage
to include dynamically rendered JSX-like components, creating a seamless link between server and client.
Accessing and Using Views and Pages
With als-view
, you can directly retrieve views and pages by name and create responses programmatically:
// Access a page directly from View
const usersPage = View.views.dashboard.pages.users;
// Create and send a response
const response = usersPage.response(data, req, res);
response.end(); // Sends the generated HTML and assets to the client
By focusing on View
as the main interface, als-view
enables a streamlined approach to building, organizing, and serving dynamic pages in server-side applications.
Development and Production Modes
als-view
is designed to work flexibly in both Development (Dev) and Production (Prod) environments. By setting dev
to true
or false
in Env
, als-view
automatically adjusts behavior to optimize performance and efficiency for the given environment.
Key Differences Between Dev and Prod Modes
1. Minification
In Production mode, als-view
minimizes file sizes for improved load times. When dev
is set to false
:
- HTML output from the layout is minified.
- Component bundles are created as separate files and minified before they are sent to the client.
In Development mode, files are not minified, making it easier to read, debug, and troubleshoot the generated code.
2. Bundling Components
When creating a RenderedPage
, als-view
dynamically manages component bundling based on the environment:
Dev Mode (
dev: true
):- Scripts are added directly as inline bundles in the layout using the
layout.script()
method. This keeps all files in-memory and allows for faster adjustments without writing to disk. - This setup speeds up development by allowing real-time updates without needing to reload static files.
- Scripts are added directly as inline bundles in the layout using the
Prod Mode (
dev: false
):- The component scripts are written to the
publicPath
directory as separate, cacheable files using theFiles
class. These static files allow browsers to cache resources, which significantly reduces loading times for returning users. - Bundles are stored in the
/bundles
directory withinpublicPath
, and each file is linked as a<script src>
tag in the layout, allowing the browser to download them only when necessary.
- The component scripts are written to the
Component-Specific Options in addPage
When adding a RenderedPage
, als-view
provides additional configuration options that influence the rendering of components and the behavior of script bundling.
includeBundle
: Determines if the component’s JavaScript bundle should be included in the layout.- When set to
true
, the component bundle is created and added as a<script>
tag. - When set to
false
, the page will render without the component bundle, which may be useful if you only want server-side rendering without any additional client-side interactivity.
- When set to
selector
: Specifies the element where the rendered component’s output will be injected within the layout.- The component will replace
<content></content>
within the selected element. - By default,
selector
is set to'body'
, meaning the component’s HTML will be inserted into the body of the layout.
- The component will replace
Example Usage of Dev/Prod Modes
Development Mode (In-Memory Bundling)
In Development mode, bundles are included as inline scripts. This example illustrates setting up a RenderedPage
with a JSX-like component (App.js
) that updates in real-time:
// Set Dev mode
View.setEnv({ dev: true, publicPath: './public' });
const view = new View(baseLayout, 'dashboard');
view.addPage('app', { title: 'App Page', includeBundle: true }, './views/App');
// Render and send response
const response = view.pages.app.response(data, req, res);
response.end();
In this case, the App.js
component is rendered and its bundle is added inline in the HTML output, with no static files written to disk.
Production Mode (Static Bundling for Caching)
In Production mode, component bundles are created as separate, cacheable files. Below, the same example as above is shown in Production mode:
// Set Prod mode
View.setEnv({ dev: false, publicPath: './public' });
const view = new View(baseLayout, 'dashboard');
view.addPage('app', { title: 'App Page', includeBundle: true }, './views/App');
// Render and send response
const response = view.pages.app.response(data, req, res);
response.end();
Here, App.js
is bundled as a standalone .js
file in the public/bundles
directory, allowing the client to cache it for subsequent page visits.
Using these modes, als-view
adapts to the needs of both development and production environments, providing flexibility, speed, and optimized performance.
Using Hooks in als-view
als-view
provides flexible hook management that allows you to modify data, layout elements, response status, and more before a page is rendered. Hooks are available at both the global View
level and individual Page
level, giving you control over both shared and specific behaviors across views and pages.
Global and Page-Level Hooks
Global Hooks (View Level):
- Hooks added at the
View
level apply to all pages within that view. These hooks are useful for shared functionality, such as injecting user data into all responses or setting up common layout modifications.
const view = new View(layout, 'main'); // Example global hook to add user data from the session to the response data view.addHook((data, response) => { data.user = response.req.session?.user || null; });
- Hooks added at the
Page-Level Hooks:
- Hooks added at the
Page
level apply only to the specified page, allowing you to define page-specific behavior. For example, a hook on aprofile
page might check if a user is authenticated before rendering.
const profilePage = view.addPage('profile', { title: 'User Profile' }); // Example page-specific hook to ensure user is authenticated profilePage.addHook((data, response) => { if (!data.user) { response.status = 401; // Unauthorized response.layout.body.innerHTML = '<h1>Unauthorized Access</h1>'; } });
- Hooks added at the
Accessing and Modifying Hooks
Hooks are stored in an array (hooks
) at both the View
and Page
levels. You can modify this array to reorder hooks, add new hooks, or remove existing ones. This flexibility allows you to fully control the sequence and behavior of your hooks.
// Reordering hooks for a view
view.hooks.reverse(); // Example: reversing the order of hooks
Parameters Available in Hooks
Each hook function receives three parameters:
data
: An object containing the data that will be passed to the page for rendering. You can modify this data within the hook.response
: An instance ofPageResponse
, which contains properties likelayout
,status
,req
andres
. etc.
Common Modifications in Hooks
response.layout
: Modify the layout before it’s rendered. For example, add custom HTML to the body or update the title.response.status
: Set the HTTP status code for the response (e.g., 200, 404, 500).data
: Add or update properties withindata
, which is ultimately passed to the component during rendering. This is ideal for injecting session data or other context-specific information.
Hook Example: Modifying Layout, Status, and Data
Here’s a combined example showing how to use hooks at both the View
and Page
levels to handle layout modifications, custom status codes, and data enrichment.
// Global hook for all pages in the 'main' view
view.addHook((data, response) => {
// Access session data and set a user property in data
data.user = response.req.session?.user || null;
// Modify the layout title based on the user session
if (data.user) {
response.layout.title(`Welcome, ${data.user.name}`);
} else {
response.layout.title('Welcome, Guest');
}
});
// Page-specific hook to handle authentication for 'profile' page
const profilePage = view.addPage('profile', { title: 'User Profile' });
profilePage.addHook((data, response) => {
if (!data.user) {
// Redirect or display an error if not authenticated
response.status = 403; // Forbidden
response.layout.body.innerHTML = '<h1>Access Denied</h1>';
}
});
In this example:
- The
view
-level hook is used to set common data (user information from the session) and dynamically adjust the layout title. - The
profilePage
-level hook checks if the user is authenticated and modifies theresponse.status
andresponse.layout.body
accordingly.
Executing Hooks in Order
By default, hooks are executed in the order they are added. If you need to adjust the sequence, you can directly modify the hooks
array:
// Adjust the order of hooks at the view level
view.hooks.reverse(); // Reverse the order of execution
Hooks in als-view
provide powerful customization options, allowing you to set up complex data manipulations and response configurations that adapt to your application's needs.
API Reference for als-view
The als-view
library provides a streamlined API for setting up server-side rendered views with layouts, pages, and component-based rendering. Below is a detailed explanation of each available class and their purpose.
Main Entry Point: View
The View
class is the central interface for using als-view
. Through the View
class, you can:
- Configure a base
Layout
for your application. - Add
Page
instances to create views for different routes. - Manage
hooks
at both theView
andPage
levels for custom functionality.
Static Properties and Classes
The View
class exposes several static properties, which provide direct access to key classes used within the library:
View.Files
: Exposes theFiles
class for managing file handling and script bundling.View.Env
: Exposes theEnv
class for environment configuration, such as setting paths, modes, and logging.View.PageResponse
: Exposes thePageResponse
class, which represents the response for each page and provides methods for modifying the response and ending it.View.Page
: Exposes thePage
class, representing a standard page without components.View.RenderedPage
: Exposes theRenderedPage
class, which extendsPage
to support JSX-like components and client-side bundling.
Class Details
1. Files
The Files
class handles file operations for scripts and static assets, including:
- Constructor:
Files(publicPath, dirName)
publicPath
: The root path for public files.dirName
: The directory where bundled scripts are stored.
write(path, content, layout)
: Copies files from thepath
and writes thecontent
to the specified location in thelayout
. This method automatically adds the.js
extension if missing.
2. Env
The Env
class manages the environment configuration and initializes essential settings for the application.
- Constructor:
Env(options = {})
options
: Contains properties such aspublicPath
,dirName
,dev
mode, andlogger
.
init({ publicPath, dirName, dev, logger })
: Initializes or updates environment options.layout(layout)
: Returns a newLayout
instance, minified ifdev
mode is disabled.
3. PageResponse
PageResponse
is responsible for managing the response, allowing custom data, status, and HTML modifications.
- Constructor:
PageResponse(page, data, req, res)
page
: The page instance associated with this response.data
: The data passed to the page.req
: The request object.res
: The response object.
end(status = this.status)
: Ends the response with the specified status, applying any modifications from the layout.
4. Page
The Page
class represents a standalone page with its own layout, hooks, and response handling.
- Constructor:
Page(name, options, view)
name
: A unique identifier for the page.options
: Page-specific options likeurl
,title
,description
, and other SEO-related settings.view
: The parent view to which the page belongs.
addHook(hook)
: Adds a hook to the page’shooks
array.response(data, req, res)
: Returns aPageResponse
for handling HTTP responses.
5. RenderedPage
The RenderedPage
class extends Page
and supports client-side bundling and rendering for JSX-like components.
- Constructor:
RenderedPage(name, path, options = {}, view)
name
: The name of the page.path
: The path to the component file.options
: Additional options likeincludeBundle
andselector
.view
: The parent view.
buildRender(path, options)
: Builds the component renderer and includes bundles based onincludeBundle
.addBundle()
: Manages bundle files, writing them to disk if in Prod mode or as inline scripts in Dev mode.
Example Usage
const View = require('als-view');
const layout = require('./resources/layouts/main'); // Main layout
// Initialize the environment with options
View.setEnv({
publicPath: './public',
dirName: 'bundles',
dev: true,
logger: console,
});
// Create a new view using the base layout
const mainView = new View(layout, 'main');
// Add a global hook to set user data for all pages in this view
mainView.addHook((data, response) => {
data.user = response.req.session?.user || null;
});
// Add a `Page` with only server-side rendering
mainView.addPage('home', { title: 'Home Page' });
// Add a `RenderedPage` for component-based rendering with bundling
mainView.addPage('dashboard', { includeBundle: true, selector: 'body' }, './components/Dashboard');
// Access pages and render response
const response = mainView.pages.dashboard.response(data, req, res);
response.end();
The als-view
library combines flexible server-side and client-side rendering capabilities, with hooks and layout handling that provide developers with powerful tools for creating dynamic and SEO-optimized web pages.