Closed enpitsuLin closed 1 year ago
虽然实际上开发模式不应该用 style标签的形式插入,应该手动实现一次 vite 逻辑来满足 HMR,但不是很重要,主要是后来构建上出现了点问题。(这个逻辑是不是可以通过 vite-plugin-monkey 提供一个虚拟模块来处理 resloveId + 生成合适的 Element)
实际上 HMR 稍微卡住了😅人家用的 vite updateStyle 似乎还是需要插件来处理
比较粗糙的实现了专门针对 unocss 的 hmr 我觉得通用化应该也不复杂,提供某种方式可以提供将 css 导入成 HTMLStyleElement 的功能
在 vite build 阶段提前去 build 确实是一个很好的想法
下面代码是根据 userscript/plugins/build.ts 改进后的代码,将 unocss 转为 style 对象,并且自动根据构建环境使用不同的 unocss 代码,当 unocss hmr 时,自动同步 css 代码,而浏览器端代码只需要简单导入一个 style 对象即可
// packages/userscript/plugins/build.ts
import { tmpdir } from 'node:os';
import { resolve } from 'node:path';
import { readFile } from 'node:fs/promises';
import process from 'node:process';
import { build } from '@unocss/cli';
import Unocss from 'unocss/vite';
import { type Plugin } from 'vite';
const template = `
const style = document.createElement('style');
style.textContent = __TEXT__;
if (import.meta.env.DEV) {
(async () => {
while (true) {
const unoStyle = document.querySelector(
'head style[data-vite-dev-id="/__uno.css"]',
);
if (unoStyle) {
style.textContent = unoStyle.textContent;
new MutationObserver(() => {
if (style.textContent != unoStyle.textContent) {
style.textContent = unoStyle.textContent;
}
}).observe(unoStyle, {
childList: true,
attributes: true,
});
return;
}
await new Promise((res) => setTimeout(res, 500));
}
})();
}
export default style;
`.trim();
export function UnocssBuildPlugin(isBuild: boolean): Plugin[] {
return [
...(isBuild ? [] : Unocss()),
{
name: 'unocss:style',
resolveId(source) {
if (source === 'unocss?style') return '/__unocss_style';
},
async load(id) {
if (id === '/__unocss_style') {
if (isBuild) {
const outFile = resolve(tmpdir(), `unocss_${+new Date()}.css`);
await build({
outFile,
cwd: process.cwd(),
config: resolve(__dirname, '../unocss.config.ts'),
patterns: ['./src/**/*.vue', './src/**/*.css'],
stdout: false,
});
const css = (await readFile(outFile))
.toString()
.split('\n')
.join('')
.trim();
return {
code: template.replace(`__TEXT__`, JSON.stringify(css)),
};
} else {
return {
code:
`import 'uno.css';` +
template.replace(`__TEXT__`, JSON.stringify('')),
};
}
}
},
},
];
}
// vite.config.ts
// Omitting unrelated code / 省略无关代码
import { UnocssBuildPlugin } from './plugins/build';
export default defineConfig(async ({ command }) => {
return {
plugins: [
AutoImport({ }),
Component({ dts: 'src/components.d.ts' }),
VueI18nPlugin({ }),
UnocssBuildPlugin(command=='build'), // here
vue(),
monkey({
entry: 'src/main.ts',
}),
],
};
});
// packages/userscript/src/main.ts
// Omitting unrelated code / 省略无关代码
import style from 'unocss?style'
function initUserjsDigger(attachShadow = HTMLElement.prototype.attachShadow) {
customElements.define(
'userjs-digger',
class extends HTMLElement {
app: VueApp
constructor() {
super()
const shadow = attachShadow.call(this, { mode: 'open' })
shadow.appendChild(style) // here
shadow.appendChild(app)
this.app = createApp(App)
this.app.use(i18n)
this.app.provide('container', app)
this.app.mount(app)
}
},
)
const userDigger = document.createElement('userjs-digger')
document.body.append(userDigger)
}
我的意思是 没必要改 vite-plugin-monkey 的代码
你直接用上面改进的自定义插件也能在 web components 内部无缝使用 unocss,而且 unocss hmr 也能正常使用
嗯 我本来以为 动态引入会添加 preload 并执行错误的逻辑,测试了下应该是只有 unocss 才会出现这样的情况,那我自己处理就ok了
使用 iframe 可以避免宿主环境创建 style[data-vite-dev-id="/__uno.css"]
标签
如果宿主环境也使用 css 原子类,这可以避免脚本 unocss 样式在 dev 模式影响宿主样式
改进后的代码完整示例 test-example
由于使用 unocss 还有老哥的提的 pr 的解决方案没有很好的解决 unocss 在这个插件中使用的问题,后来我自己简单的解决了下开发环境下面 unocss 会错误注入到目标网页 head 中的问题。
直接把 unocss 的 resolve 区分开发生产模式手动直接引入(生产时通过 unocss cli 封装的插件处理产物 ?raw 的问题),而不是走 vite 的逻辑 see here
虽然实际上开发模式不应该用 style标签的形式插入,应该手动实现一次 vite 逻辑来满足 HMR,但不是很重要,主要是后来构建上出现了点问题。(这个逻辑是不是可以通过 vite-plugin-monkey 提供一个虚拟模块来处理 resloveId + 生成合适的 Element)
Vite 构建在没有启用 lib 模式的情况下,对于上面我在生产中做的动态导入是会自动分成两个 chunk 然后通过插件引入 systemjs 解决这里,但是在 generateBundle 这里 (实际上是 L621 这里处理 deps 的代码)似乎因为什么原因被跳过了,于是构建出了有问题的代码
如果说这部分是 vite-plugin-monkey 错误的跳过了这个 hook 那这个问题应该是 bug
但是后面发现实际上对于 vite 处理这样的动态导入会添加 preload 可以通过启用 lib 模式解决,不仅解决了我的问题还不会额外引入 systemjs,直接被编译成了一个 Promise.resolve + 字符串形式,也正好达到了我的目标
所以我在想是不是可以在构建时默认启用 lib 来避免类似的动态导入的问题顺便还能去掉 systemjs 的引入?