标签 前端 下的文章 - 星野梦璃
首页
友链
导航
关于我
归档
生活
随记
个人总结
技术
日志
折腾
学习日记
测评
硬件
软件
收藏
网站
电脑软件
手机软件
开发工具
项目收藏
书籍
技术文章
其他文章
首页
友链
导航
关于我
推荐
百度一下
腾讯视频
搜 索
1
使用XRay一键脚本安装V2Ray+Reality协议
1,038 阅读
2
在Windows上使用Koishi+LLOneBot插件搭建QQ机器人
694 阅读
3
Giffgaff 使用手册
597 阅读
4
Git 克隆错误 error: RPC failed; curl 28 Recv failure: Connection was reset
552 阅读
5
git走v2ray的代理端口解决 Github port 443 : Timed out
550 阅读
归档
生活
随记
个人总结
技术
日志
折腾
学习日记
测评
硬件
软件
收藏
网站
电脑软件
手机软件
开发工具
项目收藏
书籍
技术文章
其他文章
登录
搜 索
标签搜索
网站配置
软件
学习
教程
前端
折腾
收藏
git
转载
生活
网络
分享
文章
思考
极简
github项目
英语
php
语录
windows
星野梦璃
累计撰写
70
篇文章
累计收到
0
条评论
首页
栏目
归档
生活
随记
个人总结
技术
日志
折腾
学习日记
测评
硬件
软件
收藏
网站
电脑软件
手机软件
开发工具
项目收藏
书籍
技术文章
其他文章
页面
友链
导航
关于我
推荐
百度一下
腾讯视频
用户登录
登录
找到
7
篇与
前端
相关的结果
2026-04-27
daisyUI 常用组件速查(按使用频率排序)
daisyUI 常用组件速查(按使用频率排序)基于:daisyUI🔥 第一梯队(高频核心组件)👉 后台 / 表单 / 页面核心1️⃣ Button(按钮)<button class="btn">默认按钮</button> <button class="btn btn-primary">主要按钮</button> <button class="btn btn-success">成功</button> <button class="btn btn-error">错误</button>常用组合<button class="btn btn-primary btn-sm">小按钮</button> <button class="btn btn-outline">描边按钮</button> <button class="btn btn-ghost">幽灵按钮</button> <button class="btn loading">加载中</button>2️⃣ Input(输入框)<input class="input input-bordered" placeholder="请输入内容">常用组合<input class="input input-primary" placeholder="主要输入框"> <input class="input input-error" placeholder="错误状态"> <input class="input input-bordered w-full" placeholder="全宽">3️⃣ Select(下拉框)<select class="select select-bordered"> <option>选项1</option> <option>选项2</option> </select>4️⃣ Textarea(多行输入)<textarea class="textarea textarea-bordered" placeholder="输入内容"></textarea>5️⃣ Checkbox / Radio(选择)<input type="checkbox" class="checkbox"> <input type="radio" class="radio">6️⃣ Card(卡片)<div class="card bg-base-100 shadow"> <div class="card-body"> <h2 class="card-title">标题</h2> <p>内容</p> <button class="btn btn-primary">操作</button> </div> </div>7️⃣ Alert(提示)<div class="alert alert-success">成功提示</div> <div class="alert alert-error">错误提示</div>8️⃣ Badge(标签)<span class="badge">默认</span> <span class="badge badge-primary">主要</span>9️⃣ Table(表格)<table class="table"> <thead> <tr><th>姓名</th><th>年龄</th></tr> </thead> <tbody> <tr><td>张三</td><td>18</td></tr> </tbody> </table>🔟 Modal(弹窗)<dialog id="my_modal" class="modal"> <div class="modal-box"> <h3 class="font-bold text-lg">标题</h3> <p>内容</p> <div class="modal-action"> <form method="dialog"> <button class="btn">关闭</button> </form> </div> </div> </dialog> <button class="btn" onclick="my_modal.showModal()">打开弹窗</button>⚡ 第二梯队(常用辅助组件)1️⃣ Navbar(导航栏)<div class="navbar bg-base-100"> <div class="flex-1">Logo</div> <div class="flex-none"> <button class="btn">登录</button> </div> </div>2️⃣ Drawer(侧边栏)<div class="drawer"> <input id="drawer-toggle" type="checkbox" class="drawer-toggle"> <div class="drawer-content"> <label for="drawer-toggle" class="btn btn-primary">打开菜单</label> </div> <div class="drawer-side"> <ul class="menu p-4 w-80 bg-base-200"> <li><a>首页</a></li> <li><a>用户</a></li> </ul> </div> </div>3️⃣ Menu(菜单)<ul class="menu bg-base-200 w-56"> <li><a>首页</a></li> <li><a>用户</a></li> </ul>4️⃣ Dropdown(下拉菜单)<div class="dropdown"> <label tabindex="0" class="btn">菜单</label> <ul tabindex="0" class="dropdown-content menu p-2 bg-base-100 shadow w-52"> <li><a>选项1</a></li> <li><a>选项2</a></li> </ul> </div>5️⃣ Tabs(标签页)<div role="tablist" class="tabs tabs-boxed"> <a role="tab" class="tab tab-active">Tab1</a> <a role="tab" class="tab">Tab2</a> </div>6️⃣ Progress(进度条)<progress class="progress progress-primary w-56" value="50" max="100"></progress>7️⃣ Loading(加载动画)<span class="loading loading-spinner"></span>8️⃣ Tooltip(提示)<button class="btn" data-tip="提示内容">悬停</button>9️⃣ Divider(分割线)<div class="divider">分割</div>💀 最实用总结核心组件: button / input / table / modal / card 后台结构: drawer / menu / navbar 增强组件: alert / badge / loading / tooltip🚀 使用建议不需要死记组件多复制官方示例多组合 class多做页面练习👉 实战优先,记忆其次📌 推荐练习登录页(input + button)表格列表(table)弹窗操作(modal)后台布局(drawer + menu)👉 做完这些基本就能上手项目下面是 daisyui+alpinejs的示例<html lang="en" data-theme="bumblebee-custom"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Not Found</title> <link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" /> <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> <link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" /> <script src="https://unpkg.com/alpinejs" defer></script> </head> <body class="bg-base-200 min-h-screen p-6"> <div x-data="" class="max-w-5xl mx-auto"> <div class="flex justify-end"> <div class="dropdown dropdown-end"> <div tabindex="0" role="button" class="btn m-1 gap-2 group"> <div class="bg-base-100 group-hover:border-base-content/20 border-base-content/10 grid shrink-0 grid-cols-2 gap-0.5 rounded-md border p-1 transition-colors"> <div class="bg-base-content size-1 rounded-full"></div> <div class="bg-primary size-1 rounded-full"></div> <div class="bg-secondary size-1 rounded-full"></div> <div class="bg-accent size-1 rounded-full"></div> </div> <span>Theme</span> <svg width="12px" height="12px" class="inline-block h-2 w-2 fill-current opacity-60 transition-transform group-hover:rotate-180" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 2048"> <path d="M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z"></path> </svg> </div> <ul tabindex="-1" class="dropdown-content bg-base-300 rounded-box z-1 h-96 w-56 overflow-y-auto p-2 shadow-2xl"> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Light" value="light" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Dark" value="dark" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Cupcake" value="cupcake" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Bumblebee" value="bumblebee" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Emerald" value="emerald" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Corporate" value="corporate" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Synthwave" value="synthwave" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Retro" value="retro" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Cyberpunk" value="cyberpunk" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Valentine" value="valentine" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Halloween" value="halloween" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Garden" value="garden" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Forest" value="forest" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Aqua" value="aqua" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Lofi" value="lofi" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Pastel" value="pastel" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Fantasy" value="fantasy" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Wireframe" value="wireframe" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Black" value="black" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Luxury" value="luxury" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Dracula" value="dracula" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="CMYK" value="cmyk" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Autumn" value="autumn" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Business" value="business" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Acid" value="acid" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Lemonade" value="lemonade" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Night" value="night" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Coffee" value="coffee" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Winter" value="winter" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Dim" value="dim" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Nord" value="nord" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Sunset" value="sunset" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Caramellatte" value="caramellatte" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Abyss" value="abyss" /></li> <li><input type="radio" name="theme-dropdown" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start" aria-label="Silk" value="silk" /></li> </ul> </div> </div> <section class="card bg-base-100 shadow mt-4"> <div class="card-body"> <h2 class="card-title">弹窗示例</h2> <p class="text-base-content/70">点击按钮打开弹窗,测试不同主题下的视觉效果。</p> <div> <button class="btn btn-primary" @click="open = true">打开弹窗</button> </div> </div> </section> <div class="modal" :class=""> <div class="modal-box"> <h3 class="font-bold text-lg">Hello!</h3> <p class="py-4">这就是 daisyUI + Alpine</p> <div class="modal-action"> <button class="btn" @click="open = false">关闭</button> </div> </div> </div> </div> <div x-data="" class="max-w-5xl mx-auto mt-6"> <div class="card bg-base-100 shadow"> <div class="card-body"> <h2 class="card-title">Alpine 控制子菜单</h2> <p class="text-base-content/70"> 通过 Alpine 添加/删除 <code>menu-dropdown-show</code> 类来控制子菜单展开状态。 </p> <ul class="menu bg-base-200 rounded-box w-72"> <li><a>Item 1</a></li> <li> <span class="menu-dropdown-toggle cursor-pointer" :class="" @click="subMenuOpen = !subMenuOpen"> Parent </span> <ul class="menu-dropdown" :class=""> <li><a>Submenu 1</a></li> <li><a>Submenu 2</a></li> </ul> </li> </ul> </div> </div> </div> <section class="max-w-5xl mx-auto mt-6" x-data="{ currentTheme: 'bumblebee-custom', themes: ['light','dark','cupcake','bumblebee','emerald','corporate','synthwave','retro','cyberpunk','valentine','halloween','garden','forest','aqua','lofi','pastel','fantasy','wireframe','black','luxury','dracula','cmyk','autumn','business','acid','lemonade','night','coffee','winter','dim','nord','sunset','caramellatte','abyss','silk'], applyTheme(theme) { this.currentTheme = theme; document.documentElement.setAttribute('data-theme', theme); } }" x-init="currentTheme = document.documentElement.getAttribute('data-theme') || 'light'"> <div class="card bg-base-100 shadow"> <div class="card-body"> <h2 class="card-title">主题预览网格</h2> <p class="text-base-content/70">点击任意卡片即可切换页面主题。</p> <div class="rounded-box grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5"> <template x-for="theme in themes" :key="theme"> <button type="button" class="border-base-content/20 hover:border-base-content/40 overflow-hidden rounded-lg border outline-2 outline-offset-2 outline-transparent transition" :class="" @click="applyTheme(theme)"> <div :data-theme="theme" class="bg-base-100 text-base-content w-full cursor-pointer font-sans text-left"> <div class="grid grid-cols-5 grid-rows-3"> <div class="bg-base-200 col-start-1 row-span-2 row-start-1"></div> <div class="bg-base-300 col-start-1 row-start-3"></div> <div class="bg-base-100 col-span-4 col-start-2 row-span-3 row-start-1 flex flex-col gap-1 p-2"> <div class="font-bold" x-text="theme"></div> <div class="flex flex-wrap gap-1"> <div class="bg-primary flex aspect-square w-5 items-center justify-center rounded lg:w-6"><div class="text-primary-content text-sm font-bold">A</div></div> <div class="bg-secondary flex aspect-square w-5 items-center justify-center rounded lg:w-6"><div class="text-secondary-content text-sm font-bold">A</div></div> <div class="bg-accent flex aspect-square w-5 items-center justify-center rounded lg:w-6"><div class="text-accent-content text-sm font-bold">A</div></div> <div class="bg-neutral flex aspect-square w-5 items-center justify-center rounded lg:w-6"><div class="text-neutral-content text-sm font-bold">A</div></div> </div> </div> </div> </div> </button> </template> </div> </div> </div> <div class="overflow-x-auto"> <table class="niah-table"> <!-- head --> <thead> <tr> <th></th> <th>Name</th> <th>Job</th> <th>Favorite Color</th> </tr> </thead> <tbody> <!-- row 1 --> <tr> <th>1</th> <td>Cy Ganderton</td> <td>Quality Control Specialist</td> <td>Blue</td> </tr> <!-- row 2 --> <tr> <th>2</th> <td>Hart Hagerty</td> <td>Desktop Support Technician</td> <td>Purple</td> </tr> <!-- row 3 --> <tr> <th>3</th> <td>Brice Swyre</td> <td>Tax Accountant</td> <td>Red</td> </tr> </tbody> </table> </div> </section> <div class="max-w-5xl mx-auto mt-6" x-data="{ expanded: , rows: [ , , , , ], hasChildren(id) { return this.rows.some((row) => row.parentId === id); }, isExpanded(id) { return !!this.expanded[id]; }, toggle(id) { this.expanded[id] = !this.isExpanded(id); }, isVisible(row) { if (!row.parentId) return true; let parent = this.rows.find((item) => item.id === row.parentId); while (parent) { if (!this.isExpanded(parent.id)) return false; if (!parent.parentId) return true; parent = this.rows.find((item) => item.id === parent.parentId); } return true; } }"> <div class="card bg-base-100 shadow"> <div class="card-body"> <h2 class="card-title">Tree Table 示例</h2> <p class="text-base-content/70">点击箭头可展开/收起子节点。</p> <div class="overflow-x-auto rounded-box border border-base-content/5 bg-base-100"> <table class="table"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Job</th> <th>Favorite Color</th> </tr> </thead> <tbody> <template x-for="row in rows" :key="row.id"> <tr x-show="isVisible(row)"> <th x-text="row.id"></th> <td> <div class="flex items-center gap-2" :style="`padding-left: $rem`"> <button type="button" class="btn btn-ghost btn-xs" x-show="hasChildren(row.id)" @click="toggle(row.id)"> <span x-text="isExpanded(row.id) ? '▾' : '▸'"></span> </button> <span x-show="!hasChildren(row.id)" class="w-5 inline-block"></span> <span x-text="row.name"></span> </div> </td> <td x-text="row.job"></td> <td x-text="row.color"></td> </tr> </template> </tbody> </table> </div> </div> </div> </div> </body> </html>
2026年04月27日
4 阅读
0 评论
0 点赞
2026-04-27
Alpine.js 第五阶段学习笔记(插件 Plugins)(按需使用)
Alpine.js 第五阶段学习笔记(插件 Plugins)基于:Alpine.js🧠 第五阶段核心插件 = 官方扩展功能(按需使用)👉 Alpine 本体只提供核心能力👉 插件用于补充常见功能(避免重复造轮子)⚠️ 使用原则不用不用不用 需要再用👉 插件不是必须👉 只在有需求时引入1️⃣ Persist(本地存储)👉 作用自动将数据保存到 localStorage,刷新页面不丢失✅ 示例<!DOCTYPE html> <html> <head> <script src="https://unpkg.com/alpinejs" defer></script> <script src="https://unpkg.com/@alpinejs/persist" defer></script> </head> <body> <div x-data=""> <button @click="count++">+1</button> <span x-text="count"></span> </div> </body> </html>💡 对比(传统写法)localStorage.setItem(...) localStorage.getItem(...)2️⃣ Intersect(滚动监听)👉 作用元素进入视口时触发(类似懒加载 / 滚动动画)✅ 示例<!DOCTYPE html> <html> <head> <script src="https://unpkg.com/alpinejs" defer></script> <script src="https://unpkg.com/@alpinejs/intersect" defer></script> </head> <body style="height:2000px"> <div style="margin-top:800px" x-data x-intersect="alert('进入视口')"> 滚动到这里触发 </div> </body> </html>💡 用途懒加载滚动动画曝光统计3️⃣ Collapse(折叠动画)👉 作用实现更流畅的展开 / 收起动画✅ 示例<!DOCTYPE html> <html> <head> <script src="https://unpkg.com/alpinejs" defer></script> <script src="https://unpkg.com/@alpinejs/collapse" defer></script> </head> <body> <div x-data=""> <button @click="open = !open">切换</button> <div x-show="open" x-collapse> 折叠内容 </div> </div> </body> </html>💡 优势自动处理高度动画比 x-transition 更自然4️⃣ Focus(焦点管理)👉 作用自动管理焦点(适用于弹窗 / 表单)✅ 示例<!DOCTYPE html> <html> <head> <script src="https://unpkg.com/alpinejs" defer></script> <script src="https://unpkg.com/@alpinejs/focus" defer></script> </head> <body> <div x-data=""> <button @click="open = true">打开</button> <div x-show="open" x-trap="open"> <input placeholder="自动聚焦"> <button @click="open = false">关闭</button> </div> </div> </body> </html>💡 用途模态框输入体验优化5️⃣ Mask(输入格式)👉 作用限制输入格式(手机号 / 日期 / 编号等)✅ 示例<!DOCTYPE html> <html> <head> <script src="https://unpkg.com/alpinejs" defer></script> <script src="https://unpkg.com/@alpinejs/mask" defer></script> </head> <body> <input x-mask="999-9999" placeholder="格式:123-4567"> </body> </html>💡 用途手机号身份证日期格式💀 第五阶段总结Persist → 本地存储 Intersect → 滚动监听 Collapse → 折叠动画 Focus → 焦点管理 Mask → 输入限制🔥 推荐组合(实用)Persist + Collapse👉 已足够应对大多数后台系统需求🧠 阶段总结阶段能力第一阶段数据绑定第二阶段控制DOM第三阶段响应式逻辑第四阶段组件复用第五阶段功能扩展🚀 学习建议插件按需引入优先掌握核心(前四阶段)遇到需求再查插件👉 不要为了“学插件”而学插件
2026年04月27日
3 阅读
0 评论
0 点赞
2026-04-27
Alpine.js 第四阶段学习笔记(组件化与复用)
Alpine.js 第四阶段学习笔记(组件化与复用)基于:Alpine.js🧠 第四阶段核心组件化 + 逻辑复用👉 从“写页面”升级为“写组件”1️⃣ Alpine.data()(组件定义)👉 作用将一段逻辑封装为可复用组件✅ 完整示例<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script src="https://unpkg.com/alpinejs" defer></script> </head> <body> <script> document.addEventListener('alpine:init', () => { Alpine.data('counter', () => ({ count: 0, inc() { this.count++ } })) }) </script> <div x-data="counter"> <button @click="inc()">+1</button> <span x-text="count"></span> </div> <div x-data="counter"> <button @click="inc()">+1</button> <span x-text="count"></span> </div> </body> </html>💡 特点每个组件实例独立数据互不影响可重复使用2️⃣ 组件参数(可配置组件)👉 作用像函数一样给组件传参数✅ 示例<script> document.addEventListener('alpine:init', () => { Alpine.data('counter', (init = 0) => ({ count: init, inc() { this.count++ } })) }) </script> <div x-data="counter(5)"> <button @click="inc()">+1</button> <span x-text="count"></span> </div>💡 理解counter(5)3️⃣ 逻辑拆分(函数封装)👉 作用避免 HTML 中逻辑过多✅ 示例<script> function userForm() { return { name: '', submit() { alert("提交:" + this.name) } } } </script> <div x-data="userForm()"> <input x-model="name"> <button @click="submit()">提交</button> </div>💡 优势逻辑清晰易维护易扩展4️⃣ 多组件独立👉 作用页面多个模块互不干扰✅ 示例<div x-data=""> <span x-text="msg"></span> </div> <div x-data=""> <span x-text="msg"></span> </div>💡 特点每个组件独立作用域不会污染其他组件5️⃣ 全局状态(Alpine.store)👉 作用多个组件共享数据✅ 示例<script> document.addEventListener('alpine:init', () => { Alpine.store('user', { name: '张三' }) }) </script> <div> <span x-text="$store.user.name"></span> </div> <div> <input x-model="$store.user.name"> </div>💡 类似Vuex / Pinia(但更轻量)💀 第四阶段核心总结Alpine.data() → 定义组件 参数传递 → 提高复用 函数封装 → 拆逻辑 多组件 → 页面结构 store → 全局状态🔥 能力提升阶段能力第一阶段数据绑定第二阶段控制DOM第三阶段响应式逻辑第四阶段组件复用🧠 本质变化之前:写页面 现在:写组件🚀 实战建议练习一个小项目:👉 用户管理页面输入用户名(x-model)点击添加(x-on)列表展示(x-for)删除数据(事件处理)👉 用 Alpine.data 封装📌 学习建议少看文档,多写代码每个组件自己改一遍尝试复用组件👉 不动手 = 学不会
2026年04月27日
3 阅读
0 评论
0 点赞
2026-04-27
Alpine.js 第三阶段学习笔记(对比 jQuery)
Alpine.js 第三阶段学习笔记(对比 jQuery)基于:Alpine.jsjQuery1️⃣ x-transition(动画)👉 作用给 x-show / x-if 增加过渡动画✅ Alpine 完整示例<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script src="https://unpkg.com/alpinejs" defer></script> </head> <body> <div x-data=""> <button @click="open = !open">切换</button> <div x-show="open" x-transition style="padding:20px;background:#4ade80;"> 动画内容 </div> </div> </body> </html>🔁 jQuery 对比<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script> <button id="btn">切换</button> <div id="box" style="display:none;">动画内容</div> <script> $("#btn").click(function() { $("#box").fadeToggle(); }); </script>2️⃣ $refs(操作 DOM)👉 作用直接获取 DOM 元素(替代 querySelector / $)✅ Alpine 示例<div x-data> <input x-ref="input"> <button @click="$refs.input.focus()">聚焦</button> </div>🔁 jQuery 对比<input id="input"> <button id="btn">聚焦</button> <script> $("#btn").click(function() { $("#input").focus(); }); </script>3️⃣ $watch(监听数据变化)👉 作用监听变量变化,自动执行逻辑✅ Alpine 示例<div x-data="" x-init="$watch('count', val => alert(val))"> <button @click="count++">+1</button> </div>🔁 jQuery 对比(本质不支持)let count = 0; $("#btn").click(function() { count++; alert(count); });👉 jQuery 无法真正“监听变量”4️⃣ x-effect(自动副作用)👉 作用数据变化时自动执行代码✅ Alpine 示例<div x-data=""> <div x-effect="document.title = 'Count: ' + count"></div> <button @click="count++">+1</button> </div>🔁 原生 / jQuery 对比let count = 0; function update() { document.title = "Count: " + count; } $("#btn").click(function() { count++; update(); });👉 必须手动调用5️⃣ x-init(初始化)👉 作用页面加载时执行逻辑✅ Alpine 示例<div x-data="" x-init="msg = '页面加载完成'"> <span x-text="msg"></span> </div>🔁 jQuery 对比$(document).ready(function() { $("#text").text("页面加载完成"); });💀 第三阶段核心总结x-transition → 动画 $refs → 操作DOM $watch → 监听数据 x-effect → 自动执行逻辑 x-init → 初始化🔥 本质区别方式思维jQuery操作 DOMAlpine操作 数据🧠 核心理解传统写法: 点击 → 操作 DOM Alpine: 修改数据 → DOM 自动更新🚀 学习建议建议你做一个练习:点击按钮打开弹窗(x-show)带动画(x-transition)输入框自动聚焦($refs)输入变化自动打印($watch)👉 做完这个,第三阶段就通了
2026年04月27日
3 阅读
0 评论
0 点赞
2026-04-27
Alpine.js 第二阶段学习笔记
Alpine.js 第二阶段学习笔记📦 引入 Alpine<script src="https://unpkg.com/alpinejs" defer></script>1️⃣ x-show(显示 / 隐藏)<div x-data=""> <button @click="open = !open">切换</button> <div x-show="open"> 这是可显示/隐藏的内容 </div> </div>👉 特点:控制 displayDOM 一直存在2️⃣ x-if(创建 / 销毁)<div x-data=""> <button @click="open = !open">切换</button> <template x-if="open"> <div>这是动态创建的内容</div> </template> </div>👉 特点:不存在时 DOM 中完全没有性能更优(适合复杂组件)⚠️ x-show vs x-if项目x-showx-ifDOM 是否存在一直存在会销毁切换速度快稍慢动画支持✅❌👉 选择建议:频繁切换 → x-show不常用内容 → x-if3️⃣ x-for(列表渲染)<div x-data=""> <template x-for="item in items" :key="item"> <div x-text="item"></div> </template> </div>👉 带索引<template x-for="(item, index) in items" :key="index"> <div x-text="index + ':' + item"></div> </template>👉 动态增删<div x-data=""> <button @click="items.push('新项')">添加</button> <button @click="items.pop()">删除</button> <template x-for="item in items" :key="item"> <div x-text="item"></div> </template> </div>🧠 第二阶段总结指令作用x-show控制显示隐藏x-if控制是否存在x-for渲染列表🔥 实战建议练习这些场景:下拉菜单(x-show)弹窗(x-show)条件模块(x-if)列表渲染(x-for)动态添加删除数据(x-for)👉 这些就是后台系统的核心交互💀 核心理解x-show = 控制可见性 x-if = 控制DOM存在 x-for = 控制DOM数量👉 Alpine 的本质:用数据驱动 DOM🚀 下一步建议学习:x-transition(动画)$refs(操作DOM)$watch(监听数据)👉 进入“交互增强阶段”
2026年04月27日
3 阅读
0 评论
0 点赞
2026-04-27
Alpine.js 第一阶段学习笔记
Alpine.js 第一阶段学习笔记📦 引入 Alpine<script src="https://unpkg.com/alpinejs" defer></script>1️⃣ x-data(定义状态)<div x-data=""> <span x-text="count"></span> </div>👉 作用:定义组件的数据一切的起点2️⃣ x-text(渲染数据)<div x-data=""> <span x-text="msg"></span> </div>👉 作用:把数据渲染到页面(文本)3️⃣ x-on(事件)<div x-data=""> <button @click="count++">+1</button> <span x-text="count"></span> </div>👉 说明:@click = x-on:click用于绑定事件4️⃣ x-bind(动态属性)<div x-data=""> <button @click="active = !active">切换</button> <p :class=""> 文字颜色变化 </p> </div>👉 说明::class = x-bind:class用于动态绑定属性(class / style / disabled 等)5️⃣ x-model(双向绑定)<div x-data=""> <input type="text" x-model="name"> <span x-text="name"></span> </div>👉 作用:输入框数据 ↔ 状态 自动同步🧠 第一阶段总结指令作用x-data定义数据x-text显示数据x-on事件处理x-bind动态属性x-model双向绑定🔥 学习建议不要只看,自己改:把 count 改成倒计时把 input 改成登录表单给按钮加 class 切换效果👉 不动手 = 没学会
2026年04月27日
4 阅读
0 评论
0 点赞
2024-12-23
Pornhub 技术栈首度公开-你不想看看PornHub用了哪些技术吗?
成人网站在推动 Web 发展方面所起到的作用无可辩驳。从突破浏览器的视频能力限制,到利用 WebSocket 推送广告(防止被广告拦截器拦截),你必须 不 断想出各种聪明的办法,让自己处在 Web 技术创新的最前沿。 最近,我有幸采访了大型成人网站 Pornhub 的一位 Web 开发工程师,了解了相关的开发技术、 Web API 的改进,以及作为成人网站开发工程师是一种怎样的体验。 注意:因为成人网站这个行业竞争相当激烈,有一些问题他们不能回答我,这一点我表示理解。 成人网站需要显示大量的图像内容,在开发过程中,你是否使用了大量的图片和视频占位符?开发过程中的内容体验和最终产品差距大吗? 实际上,在开发这个网站时我们并没有使用占位符!归根结底,代码和功能才是最重要的东西,至于界面什么的,到了这个时候我们已经很熟悉了。刚开始时有一点难度,但很快我们就适应了。 在开发过程中,你们是如何模拟直播视频流和第三 方广告 脚本的?它们都是很重要的资源。 播放器被分为两个组件,基本组件实现了核心功能,用于触发事件。开发是单独进行的,在进行集成时,我们需要用到第三方脚本和广告,这样可以尽早发现问题。对于一些特殊情况,我们会与广告主合作,通过手动的方式来触发一些随机事件。 一般页面上至少会有一个视频、一些 GIF 广告、一些直播预览和其他视频的缩略图。你是如何测定页面性能的?以及如何尽量提升页面的性能? > 我们使用了一些测评系统。 播放器会将视屏播放的性能和用户播放情况发送给我们; 我们使用了第三方的 RUM 系统; 我们使用了 WebpageTest ,这样就可以知道在某个时段发生了什么事情。 我假设播放器是前端的一个最重要也最复杂的功能。在视频前面插入广告、标记视频的关键部分、改变播放速度,等等,你是如何保持播放器的性能、功能和稳定性的? 我们有一个专门负责开发播放器的团队,他们的首要任务是持续地监控播放器的性能。我们用上了所有可用的工具:浏览器性能工具、 WebpageTest 、性能指标,等等。每次在发布更新之前,我们都会进行一轮严格的 QA 来保证稳定性和质量。 视频团队有多少专职开发人员?有多少前端开发人员? 我只能说,如果从整个产品的规模来看,我们的团队规模算是中等的。 在从事成人网站开发期间,你看到前端领域经历了哪些发展?有哪些新的 Web API 给你带来很大的帮助? 我看到前端技术在很多方面都有进步。 从使用纯 CSS 到使用 LESS 和 Mixin ,再到使用灵活的栅格系统和图像标签来适应不同的分辨率和屏幕大小; jQuery 和 jQueryUI 逐渐淡出了我们的视线,我们回到了更加面向对象的纯 JavaScript 编程。一些框架在某些场景下也起到非常有趣的作用; 我们很喜欢新的 IntersectionObserver API ,用它来加载图像 非常 高效; 我们还使用了画中画 API ,让视频漂浮在页面上,不过现在还在争取用户对这个想法的反馈。 展望未来,有没有哪些 Web API 是你希望发生变化、改进的?或者出现新的 Web API ? > 我们希望这些 API 能够发生变化或改进: Beacon 、 WebRTC 、 Service Worker 和 Fetch 。 Beacon :在 iOS 上有些问题,对 pageHide 事件支持得不太好; Fetch :没有下载进度,也没有提供拦截请求的方式; WebRTC :在进行直播时,如果分辨率不够大就会有所限制; Service Worker :调用 navigator.serviceWorker.register 不会被 Service Worker 的 Fetch 事件处理器拦截到。 WebVR 在过去几年已经有所改进。目前来看,它的作用有多大?成人网站会投入多大精力来支持 VR 内容? Pornhub 的 WebVR 有涉及触觉技术吗? 我们正在研究如何将 WebXR 应用在沉浸式空间场景中。作为最大的内容分发平台,我们有必要为用户提供让他们能够按照自己的方式来体验网站内容的机会。但我们还在探索,在使用这些新媒体时,内容和平台应该是什么样子。 我们是支持 VR 、计算机视觉和虚拟主播的一个主要平台,我们将继续推动新技术的发展。 每个页面上都有不同类型的媒体和内容,对于桌面版或移动版来说,最需要考虑的东西是什么? 我们主要考虑操作系统和浏览器对功能方面的限制。比如, iOS 和 Android 在访问权限和功能方面就非常不一样。 一些 iOS 设备不允许在全屏时使用自定义播放器,它们会强制使用原生的 QuickTime 播放器。而 Android 则给了我们完全的控制权限,可以在全屏时使用我们的播发器。 另一个例子是 HLS 视频流, IE 和 Edge 对 HLS 视频流质量非常挑剔,所以我们需要控制视频的质量,否则在播放时就断断续续或者出现重影。 目前 Pornhub 可以支持的最低浏览器版本是哪个?现在还支持 IE 吗? 我们支持 IE 很长时间了,但最近不支持 IE 11 之前的版本。另外,我们也停止支持 Flash 播发器。我们现在主要支持 Chrome 、 Firefox 和 Safari 。 可以分享一下 Pornhub 的技术 栈 吗?从服务器端到前端,你们使用了哪些库? 基本上,我们使用了这些东西: Nginx ; PHP ; MySQL ; Memcached / Redis 。 其他技术还包括 Varnish 、 ElasticSearch 、 NodeJS 、 Go 语言、 Vertica 。 前端方面,我们主要使用了纯 JavaScript 。我们在逐步淘汰 jQuery ,并开始使用框架,比如 Vue.js 。 在外行看来,成人网站的网页上一般充斥着各种视频缩略图、视频、直播和广告。从开发者的角度来看,是什么东西让一个成人网站变得与众不同? 我们努力让每一个品牌都具备一定程度的独特性,不同的内容、界面体验和功能,还使用了很多不同的算法。 在面试 Pornhub 时,你是怎么想的?你有犹豫过吗?如果有,又是怎么消除这种情绪的? 我没有感到有什么不妥,毕竟这个挑战对我来说充满了吸引力。一想到有数百万人会用到我开发的东西,我就感到很兴奋。这个想法很快就得到了验证,当我开发的功能第一次上线时,我感到很自豪,我还叫我的朋友们也去看看!成人网站永远都不会消亡,它为我们提供了稳定的工作来源。 与开发一般的网站相比,开发成人网站可能会有所不同。当你告诉你的朋友、家人和熟人自己在开发成人网站,你会觉得这是一种耻辱吗?你会犹豫告诉他们这些吗? 我为自己开发的东西感到自豪,我身边的人都知道,也很喜欢它们。这也成了我们的茶余饭后的谈资,非常有意思。 你也在其他地方开发过其他网站,在 Pornhub 的工作氛围有什么不同吗? 这里的氛围非常轻松友好,我不觉得跟在其他地方有什么不同。 作为前端开发人员,你需要与哪些团队密切接触?你们平常常用哪些交流方式? 我们需要与后端开发人员、 QA 和产品经理打交道。大部分时间我们会跑到各自的工位上讨论问题,其次是使用聊天工具( Microsoft Teams ),然后是电子邮件。 最后,作为一名在成人网站工作的开发工程师,你还有什么想要分享的吗? 我非常高兴能够参与开发这个有如此大规模用户的产品。我们身处技术发展的最前沿,这让一切都变得有趣且颇具挑战性。 后记 这个采访很有启发性。我很惊讶他们在开发时居然没有使用图像。 Pornhub 走在 Web 技术的最前沿 —— WebXR 、 WebRTC 和 Intersection Observer API 。我也很高兴看到他们开始逐步淘汰 jQuery ,因为现在的 Web API 很给力。 我很想从他那里 挖到更过有关 技术和性能的细节,我敢肯定他们的源代码里有很多值得一学的东西。换了是你,你会想问哪些问题? 档铺网——在线文档免费处理
2024年12月23日
61 阅读
0 评论
0 点赞