为 Vue SFC REPL 添加 UnoCSS

为 Vue SFC REPL 添加 UnoCSS 支持,可用于 Vue + UnoCss 的 demo 集。

preview 部署在 vue-repl-two.vercel.app

简析原项目

这里只挑选核心的目录/文件。

  • 📁codemirror - 编辑器的 codemirror 实现
  • 📁monaco - 编辑器的 monaco editor 实现,包括 vue language service 的 worker
  • 📁editor
    • FileSelector.vue - 输入标签列表
  • 📁output
    • Output.vue - 输出面板
    • Sandbox.vue - 预览沙盒
    • srcdoc.html - 预览沙盒的 srcdoc
  • store.ts - 源码文件、配置文件、编译器、编译输出等状态管理

编辑器输入源码和配置文件,通过 Vue SFC compiler 编译,输出并在 iframe 沙盒中预览。

改造方案

为了实现 UnoCSS 的输入、输出、预览,改造点如下:

  1. 添加 uno.config.ts 输入,在 store.ts 中和 tsconfig.json、import map 一并管理,监听并编译 uno.config.ts,生成 config。
  2. 构造 UnoCSS 生成器;监听源码,根据输入源码生成 uno.css,在 Output.vue 添加输出标签
  3. uno.css 插入到预览沙盒 iframe 的一个 <style> 标签中。

上述内容中,输入输出的添加比较简单,本文略,下面说说 uno.config.ts 的在线编译。

完整代码可查看笔者的 fork

在线编译 uno.config.ts

参考 UnoCSS Playground 实现,UnoCSS 编译的核心代码如下:

ts 复制代码
import { $fetch } from 'ofetch'

export async function evaluateUserConfig<U = UserConfig>(
   configStr: string,
   version: string = 'latest',
 ): Promise<U | undefined> {
   const modulesCache = globalCache
   const moduleMap =
     version === 'latest'
       ? unocssBundle
       : new Map(
           unocssBundle
             .keys()
             .map((p) => [
               p,
               () => import(/* @vite-ignore */ `https://esm.sh/${p}@${version}`),
             ]),
         )
 
   const code = configStr
     .replace(
       /import\s(.*?)\sfrom\s*(['"])unocss\2/g,
       'const $1 = await __import("unocss");',
     )
     .replace(
       /import\s*(\{[\s\S]*?\})\s*from\s*(['"])([\w@/-]+)\2/g,
       'const $1 = await __import("$3");',
     )
     .replace(
       /import\s(.*?)\sfrom\s*(['"])([\w@/-]+)\2/g,
       'const $1 = (await __import("$3")).default;',
     )
     .replace(/export default /, 'return ')
     .replace(/\bimport\s*\(/, '__import(')
 
   // bypass vite interop
   // eslint-disable-next-line no-new-func
   const _import = new Function('a', 'return import(a);')
   const __import = (name: string): any => {
     if (!modulesCache.has(name)) {
       modulesCache.set(
         name,
         moduleMap.has(name)
           ? moduleMap.get(name)!()
           : name.endsWith('.json')
             ? $fetch(CDN_BASE + name, { responseType: 'json' }).then((r) => ({
                 default: r,
               }))
             : _import(CDN_BASE + name),
       )
     }
     return modulesCache.get(name)
   }
 
   const fn = new AsyncFunction('__import', code)
   const result = await fn(__import)
 
   if (result) return result
 }

evaluateUserConfig 读取 uno.config.ts,将 import 语句解析并替换为预先准备好(或指定版本下载)的模块。

总结

本文简析了 Vue SFC REPL 项目,在此基础上通过在线编译 uno.config.ts 添加了UnoCSS 的输入、输出、预览,交互和原交互无缝融合。

下期预告: 现在的编辑器不支持 UnoCSS Token 的 hover、autocomplete、underline 等功能。下期加上。

相关推荐
会豪17 分钟前
Electron-Vite (一)快速构建桌面应用
前端
中微子20 分钟前
React 执行阶段与渲染机制详解(基于 React 18+ 官方文档)
前端
唐某人丶21 分钟前
教你如何用 JS 实现 Agent 系统(2)—— 开发 ReAct 版本的“深度搜索”
前端·人工智能·aigc
中微子22 分钟前
深入剖析 useState产生的 setState的完整执行流程
前端
遂心_1 小时前
JavaScript 函数参数传递机制:一道经典面试题解析
前端·javascript
Gracemark1 小时前
高德地图-地图选择经纬度问题【使用输入提示-使用Autocomplete进行联想输入】(复盘)
vue.js
小徐_23331 小时前
uni-app vue3 也能使用 Echarts?Wot Starter 是这样做的!
前端·uni-app·echarts
RoyLin1 小时前
TypeScript设计模式:适配器模式
前端·后端·node.js
遂心_1 小时前
深入理解 React Hook:useEffect 完全指南
前端·javascript·react.js
Moonbit1 小时前
MoonBit 正式加入 WebAssembly Component Model 官方文档 !
前端·后端·编程语言