lg-grid 拒绝框架绑定!原生 JS 实现「自动宫格布局」

7 阅读4分钟

🌐 拒绝框架绑定!原生 JS 实现「自动宫格布局」,Vue/React/Angular/纯 HTML 通吃

在前端技术栈日益分裂的今天,我们是否还需要为每个框架单独写一套布局逻辑?

  • Vue 项目写个 Directive?
  • React 项目写个 Hook + Component?
  • Angular 项目写个 Directive?
  • 老项目或静态页面还得手写 CSS Media Query?

NO! 🙅‍♂️

今天介绍一个完全独立、零框架依赖的轻量级布局引擎:lg-grid (来自 lg.web)。 它不依赖 Vue、React 或 Angular 的运行时,直接基于 原生 JavaScriptHTML 属性指令 驱动。无论你的项目是最新的 Next.js、Nuxt,还是传统的 jQuery 页面,甚至是一个简单的 .html 文件,都能即插即用

✨ 核心亮点:为什么选择“原生指令”?

1. 🚀 真正的框架无关 (Framework Agnostic)

底层纯原生 JS 实现,通过解析 HTML 自定义属性(如 lg-grid="310")自动初始化。

  • Vue/React/Angular: 直接当普通 HTML 用,无额外包装器,性能无损。
  • 静态页面: 引入 JS/CSS 即可生效,无需构建步骤。
  • 微前端: 在不同子应用间共享,无冲突。

2. 📐 智能自动计算

只需告诉它“我想要每个格子最小多宽”(例如 310px),它会自动:

  • 监听容器宽度变化。
  • 实时计算最佳列数。
  • 动态注入 CSS 变量控制布局。
  • 多区域联动:同一父容器下的多个 .lg-grid 自动对齐列宽。

3. ⚡️ 极致轻量

  • 0 框架依赖:包体积极小,无 Virtual DOM 开销。
  • 按需加载:不污染全局命名空间,只操作指定的 DOM 节点。

💻 全场景使用指南

第一步:引入资源

无论什么项目,只需引入 JS 和 CSS:

import 'lg.web/index.css';
import { grid } from 'lg.web';
grid.install()

💡 注意:这里没有 app.use(),没有 <GridProvider>,只有纯粹的 grid.install()

场景 A:原生 HTML / 传统 jQuery 项目

直接在 HTML 标签上使用属性,无需任何框架语法。这是最基础的用法,适合老项目改造或静态页面。

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lg.web@1.0.1/dist/grid/index.css">
<style>
    /* 自定义网格项样式 */
    .grid-item {
        border: 1px solid #764ba2;
        line-height: 32px;
        text-align: center;
    }
</style>
<script type="module">
    import { grid } from 'https://esm.sh/lg.web@1.0.1/dist/grid/index.js';
    grid.install();
    window.onload = () => {
        document.querySelectorAll('[lg-grid]').forEach(grid => {
            grid.addEventListener('lg-grid-change', (event) => {
                console.log(event.detail);
            });
        });
    };
</script>
<h2 class="group-title">不分组展示 (lg-grid="200")</h2>
<div lg-grid="200" class="lg-grid">
    <div class="grid-item">Item 1</div>
    <div class="grid-item">Item 2</div>
    <div class="grid-item">Item 3</div>
    <div class="grid-item">Item 4</div>
    <div class="grid-item">Item 5</div>
    <div class="grid-item">Item 6</div>
</div>
<h2 class="group-title">分组展示 (lg-grid="500")</h2>
<div lg-grid="500" onlg-grid-change="console.log(this);">
    <h3 class="sub-group-title first">第一组</h3>
    <div class="lg-grid">
        <div class="grid-item">Item 1</div>
        <div class="grid-item">Item 2</div>
        <div class="grid-item">Item 3</div>
        <div class="grid-item">Item 4</div>
    </div>
    <h3 class="sub-group-title">第二组</h3>
    <div class="lg-grid">
        <div class="grid-item">Item A</div>
        <div class="grid-item">Item B</div>
        <div class="grid-item">Item C</div>
        <div class="grid-item">Item D</div>
        <div class="grid-item">Item E</div>
        <div class="grid-item">Item F</div>
    </div>
</div>

场景 B:Vue 3 / Nuxt 项目

在 Vue 中,它被视为普通的 HTML 属性。Vue 的模板编译器会原样保留 lg-grid 属性,运行时由 lg.web 的 JS 接管。

<template>
  <div lg-grid="310" @lg-grid-change="onColumnChange">
    <div class="lg-grid">
      <div v-for="i in 7" :key="i" class="card">
        Item {{ i }}
      </div>
    </div>
    
    <div class="lg-grid">
      <div class="card">A</div>
      <div class="card">B</div>
    </div>
  </div>
</template>

<script setup>
  // 假设已在 main.js 全局安装,若需按需加载可在此处引入
  // import { grid } from 'lg.web';
  // grid.install()
const onColumnChange = (event) => {
  // 直接获取原生事件详情
  const { cols } = event.detail;
  console.log(`🌟 Vue 捕获:当前 ${cols} 列`);
};
</script>

<style scoped>
.card {
  background: #f0f9ff;
  border: 1px solid #bae6fd;
  padding: 20px;
  border-radius: 8px;
}
</style>

场景 C:React / Next.js 项目

React JSX 完全支持自定义 HTML 属性。由于 lg-grid-change 是原生 DOM 事件而非 React 合成事件,推荐使用 useEffect + ref 进行监听,以获得最佳兼容性。

import { useEffect, useRef } from 'react';
import 'lg.web/index.css';
// import { grid } from 'lg.web'; // 建议在入口文件 (main.jsx) 执行一次 grid.install()

export default function GridPage() {
  const containerRef = useRef(null);

  useEffect(() => {
    const node = containerRef.current;
    if (!node) return;

    // 绑定原生事件监听器
    const handleGridChange = (e) => {
      console.log(`⚛️ React 捕获:列数变为 ${e.detail.columns}`);
      // 在这里更新 state 或触发其他逻辑
    };

    node.addEventListener('lg-grid-change', handleGridChange);

    // 清理监听器
    return () => {
      node.removeEventListener('lg-grid-change', handleGridChange);
    };
  }, []);

  return (
    <div ref={containerRef} lg-grid="310">
      <div className="lg-grid">
        {[1, 2, 3, 4, 5, 6].map((i) => (
          <div key={i} className="card">
            {i}
          </div>
        ))}
      </div>
      
      <div className="lg-grid">
        <div className="card">A</div>
        <div className="card">B</div>
      </div>
    </div>
  );
}

🏆 总结对比

特性UI 库组件 (Element/AntD)框架专用指令 (Vue/React Hooks)lg.web (原生指令)
框架依赖🔴 强依赖
(绑定特定框架运行时)
🔴 强依赖
(仅限某单一框架)
🟢 无依赖
(Pure JS, 零运行时开销)
适用场景该框架生态内的项目该框架生态内的项目🌍 全场景
(Vue/React/Angular/jQuery/静态页)
迁移成本🔴 极高
(换框架需完全重写组件)
🔴 极高
(换框架需完全重写逻辑)
🟢 零成本
(HTML 属性保留,JS 通用,代码无需改动)
包体积🔴
(通常几十 KB 起步)
🟡
(含框架特定逻辑)
🟢 极小
(<5KB, 纯功能代码)
事件机制框架封装的合成事件框架封装的合成事件原生 DOM 事件
(所有框架通用)
多框架混用困难
(易产生命名空间冲突)
不可能
(无法跨框架复用)
完美支持
(微前端/iframe/混合开发首选)
动态内容需手动调用 refresh 方法需依赖响应式数据触发自动观察
(自动发现新节点)
生命周期耦合框架生命周期耦合框架生命周期🔄 独立运行
(不依赖任何框架挂载过程)

🚀 结语

lg.web 不仅仅是一个宫格组件,它是一种“回归原生”的开发理念。

在这个框架层出不穷、技术栈频繁更迭的时代,最原生的方案往往是最长久、最通用的方案

  • ❌ 你不需要担心 Vue 4 会不会破坏你的自定义指令。
  • ❌ 你不需要担心 React Server Components (RSC) 是否支持你的 Client Hook。
  • ❌ 你不需要为 Angular 的大版本升级而重构布局逻辑。
  • ❌ 你不需要在 微前端 架构中为不同子应用引入多套布局库。

无论你的下一个项目是用 SolidJSQwikSvelteLit,还是仅仅是一个简单的 .html 静态文件,lg-grid 都能无缝衔接,无需任何适配成本

让布局回归简单,让代码回归标准。 一次编写,随处运行 (Write Once, Run Anywhere)。

👉 立即在你的项目中尝试吧!体验真正的框架无关开发。