Closed marioplus closed 1 year ago
vite 模块的打包是基于 rollup,目前 rollup 并不支持 iife top await 一个可行的解决方法是把异步代码包裹在 async 里
import {xxx} from './xxx'
(async () => {
console.log(await fetch('/'));
})();
另一种可行的方法是使用自定义 plugin 在 build 模式下修改入口代码
// src/main.ts
import { x } from './util';
console.log(x);
// top-await-wrap-start
console.log(await fetch('/'));
// vite.config.ts
import { defineConfig } from 'vite';
import monkey, { cdn } from 'vite-plugin-monkey';
export default defineConfig(async ({ command, mode }) => ({
plugins: [
monkey({
entry: 'src/main.ts',
userscript: {
namespace: 'https://github.com/lisonge',
icon: 'https://vitejs.dev/logo.svg',
match: 'https://i.songe.li/*',
description: 'default_description',
},
}),
{
name: 'entry-top-await',
apply: 'build',
enforce: 'pre',
transform(code, id) {
if (
id.endsWith('src/main.ts') &&
code.includes(`// top-await-wrap-start`)
) {
return (
code.replace('// top-await-wrap-start', '(async()=>{\n') + '\n})();'
);
}
},
},
],
}));
尝试解决方案
后仍然报错是因为这时候 vite 在同时使用 esm 和 iife 构建,esm 没有报错正常输出,是之前的 iife 在报错
如你所见 vite 必须以 target: 'esnext'
构建才能使用 top-await ,这意味着不做任何语法转译,在非最新浏览器存在兼容性问题
但是代码在 tampermonkey
中实际上是被包裹在 async function
中运行,并不是直接运行单个文件
window['__p__9012200.7534899'] = function () {
((context, powers, fapply) => {
with (context) {
((module) => {
'use strict';
try {
fapply(module, context, [
undefined,
undefined,
powers.CDATA,
powers.uneval,
powers.define,
powers.module,
powers.exports,
powers.GM,
powers.GM_info,
]);
} catch (e) {
if (e.message && e.stack) {
console.error(
"ERROR: Execution of script 'New Userscript' failed! " +
e.message
);
console.log(e.stack);
} else {
console.error(e);
}
}
})(async function (
context,
fapply,
CDATA,
uneval,
define,
module,
exports,
GM,
GM_info
) {
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://i.songe.li/
// @icon https://www.google.com/s2/favicons?sz=64&domain=songe.li
// @grant none
// ==/UserScript==
(function () {
'use strict';
// Your code here...
})();
console.log(await 1);
});
}
})(this.context, this.powers, this.fapply);
//# sourceURL=chrome-extension://iikmkjmpaadaobahmlepeloendndfphd/userscript.html?name=New%2520Userscript.user.js&id=78aba2bf-2407-41ab-8072-ca1bbbbb5f6f
};
综合下来的我比较推荐的解决方案是将你的主代码用 (async()=>{
和 })();
包裹运行
原来是这样,再次感谢大佬解惑,祝您生活愉快。
@marioplus 一些小小的建议
vite 是支持 import json
的,这意味着你的 src/County.ts
可以改成
export type County = {
code: string;
name: string;
nameEn: string;
currencyCode: string;
};
import countyCurrencyCodes from './countyCurrencyCodes.json';
export const counties = new Map<string, County>(
Object.entries(countyCurrencyCodes).map(([k, v]) => [
k,
{
...v,
currencyCode: v.currency,
},
])
);
另外你的 src/realMain.ts#L8-L31 里的 css 也可以提取出来成为单个 css 文件,然后改成 import css
的格式
好处是可以利用 vite 的模块热替换,dev 模式下只需要更改这个 css 文件而无需刷新页面即可看到样式效果,在build模式下这块的css还会被压缩,可以减少构建产物
/*src/style.css*/
.tab_item_discount {
min-width: 113px !important;
width: unset;
}
.discount_final_price {
display: inline-block !important;
}
/*商店搜索列表*/
.search_result_row
.col.search_price {
width: 175px;
}
.search_result_row
.col.search_name {
width: 200px;
}
/*市场列表*/
.market_listing_their_price {
width: 160px;
}
// src/realMain.ts
import {GM_cookie, GM_xmlhttpRequest} from 'vite-plugin-monkey/dist/client'
import {ConverterManager} from './converter/ConverterManager'
import {counties} from './County'
import {ExchangeRateManager} from './remote/ExchangeRateManager'
import './style.css'
export async function main() {
// 获取国家代码
let countyCode: string = await getCountyCode()
if (!countyCode || countyCode.length === 0) {
throw Error('获取国家代码失败!')
}
console.log('countyCode', countyCode)
if (countyCode === 'CN') {
console.log('人名币无需转换')
} else {
await convert(countyCode)
}
}
另外你的 reflect-metadata
也可以使用 externalGlobals
使用 cdn 加载,这可以让你的构建产物大大减少
import {defineConfig} from 'vite'
import monkey,{cdn} from 'vite-plugin-monkey'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
monkey({
entry: 'src/main.ts',
userscript: {
name: 'steam价格转换',
author: 'marioplus',
description: 'steam商店中的价格转换为人民币',
version: '1.0.3',
icon: 'https://vitejs.dev/logo.svg',
namespace: 'https://github.com/marioplus/steam-price-converter',
license: 'AGPL-3.0-or-later',
match: [
'https://store.steampowered.com/*',
'https://steamcommunity.com/*'
],
connect: [
'open.er-api.com',
'store.steampowered.com'
]
},
build:{
externalGlobals:{
'reflect-metadata':cdn.jsdelivr('', 'Reflect.js')
}
}
}),
],
})
以上操作可以让你的构建产物从 70.46 KiB
减少到 46.23 KiB
另外如果你觉得 vite-plugin-monkey/dist/client
名字太长,可以使用 $
代替
也就是 import { GM_cookie, GM_xmlhttpRequest } from 'vite-plugin-monkey/dist/client';
可以改成
import { GM_cookie, GM_xmlhttpRequest } from '$';
感谢百忙中抽时间指点,上面的几点建议我觉得十分有用。不过其中关于第一点建议我有点疑问,通过import json
的方式导入数据相对我之前直接使用Map
定义的优势在哪里?是json
格式数据更方便的应用在其他的地方,且后期需要对数据维护更方便吗?
另外有一个不情之请,如果可以的话,可以帮忙做下项目的 code review 吗?即使您不愿意也很正常。再次感谢指点,祝您生活愉快。
感谢百忙中抽时间指点
哈哈,百忙
不至于,只是国庆家里蹲7天太无聊了
不好意思 json 那块代码写错了,应该是
import countyCurrencyCodes from './countyCurrencyCodes.json';
export const counties = new Map<string, County>(
countyCurrencyCodes.map((v) => [
v.code,
{
...v,
currencyCode: v.currency,
},
])
);
通过import json的方式导入数据相对我之前直接使用Map定义的优势在哪里?
只是个人习惯,我习惯 单一功能原则
减少重复代码
因为你的 src/County.ts
和 src/countyCurrencyCodes.json
有很高的重复度
后续修改只需要改一处而不是多处
可以帮忙做下项目的 code review 吗?
感觉其他地方也没必要修改了,更多的是个人习惯问题
比如我会把 src/remote/Http.ts
里的 import {XhrRequest} from 'vite-plugin-monkey/src/client/types'
修改成 import type { XhrRequest } from '$';
reflect-metadata
的 cdn 使用 bug 说明
感谢大佬,真是长见识了,@require
还能这样写,这样解决这个问题,真是绝了。
感谢抽空查看我问题,我是前端新手,所以对问题的描述可能有点混乱,请见谅。
问题描述
项目是以
纯ts
模式开发的,运行vite build
出现以下错误:如何重现
可在此处查看项目代码
在
main.ts
中使用await
关键字,执行vite build
指令失败,例如:在
main.ts
中不使用await
关键字,执行vite build
指令成功,例如:项目配置
可以在[此处]()获取项目的源码
尝试解决方案
将修改vite配置增加
build
配置会出现以下错误
虽然出现了错误,但
dist
文件夹有构建脚本输出,且脚本能正常使用