将serviceWorker改为使用esm,加载时判断是否可以使用ts,sfc改为使用esm

This commit is contained in:
nonameShijian 2024-03-26 16:20:53 +08:00
parent 0eb9f8d81d
commit 8b19079c56
6 changed files with 48292 additions and 48271 deletions

4
game/canUse.ts Normal file
View File

@ -0,0 +1,4 @@
// apk每次安装后第一次启动加载Service Worker会失败
// 所以每次导入这个ts判断是否会成功失败的话重启一次
export const text: string = 'ts文件导入成功';

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -203,15 +203,23 @@ new Promise(resolve => {
}); });
try { try {
const registration_1 = await navigator.serviceWorker.register(`${scope}service-worker.js`, { const registration_1 = await navigator.serviceWorker.register(`${scope}service-worker.js`, {
type: 'module',
updateViaCache: "all", updateViaCache: "all",
scope, scope,
}); });
// 初次加载worker需要重新启动一次 // 初次加载worker需要重新启动一次
if (!findServiceWorker) location.reload(); if (!findServiceWorker) location.reload();
// 接收消息,暂时没用到
navigator.serviceWorker.addEventListener('message', e => { navigator.serviceWorker.addEventListener('message', e => {
console.log(e); console.log(e);
}); });
registration_1.update().catch(console.error); registration_1.update().catch(e => console.error('worker update失败', e));
if (!sessionStorage.getItem('canUseTs')) {
await import('./canUse.ts').then(({ text }) => console.log(text)).catch(() => {
sessionStorage.setItem('canUseTs', '1');
location.reload();
});
}
} catch (e_1) { } catch (e_1) {
console.log('serviceWorker加载失败: ', e_1); console.log('serviceWorker加载失败: ', e_1);
} }

View File

@ -172280,3 +172280,6 @@ ${e.message}`;
if (typeof module !== "undefined" && module.exports) { if (typeof module !== "undefined" && module.exports) {
module.exports = ts; module.exports = ts;
} }
if (typeof globalThis !== "undefined") {
globalThis.ts = ts;
}

View File

@ -1,39 +1,39 @@
/**
* @type { ServiceWorkerGlobalScope } 提供ServiceWorker的代码提示
*/
// @ts-ignore
var self = globalThis;
// 以副作用导入typescript以保证require也可以同步使用
import './game/typescript.js';
/** /**
* @type { import('typescript') } * @type { import('typescript') }
*/ */
var ts; var ts = globalThis.ts;
importScripts('./game/typescript.js'); // sfc以正常的esmodule使用
/** import * as sfc from './game/compiler-sfc.esm-browser.js';
* @type { import('./game/compiler-sfc.browser.js') }
*/
var sfc;
importScripts('./game/compiler-sfc.browser.js');
// @ts-ignore
if (typeof ts != 'undefined') { if (typeof ts != 'undefined') {
console.log(`ts loaded`); console.log(`ts loaded`, ts.version);
} else { } else {
console.log(`ts undefined`); console.error(`ts undefined`);
} }
// @ts-ignore
if (typeof sfc != 'undefined') { if (typeof sfc != 'undefined') {
console.log(`sfc loaded`); console.log(`sfc loaded`, sfc.version);
sfc.registerTS(() => ts); sfc.registerTS(() => ts);
} else { } else {
console.log(`sfc undefined`); console.error(`sfc undefined`);
} }
console.log('serviceWorker version 2.3'); console.log('serviceWorker version 2.3');
self.addEventListener("install", (event) => { self.addEventListener("install", (event) => {
// The promise that skipWaiting() returns can be safely ignored. // The promise that skipWaiting() returns can be safely ignored.
// @ts-ignore
self.skipWaiting(); self.skipWaiting();
}); });
self.addEventListener("activate", (event) => { self.addEventListener("activate", (event) => {
// 当一个 service worker 被初始注册时,页面在下次加载之前不会使用它。 claim() 方法会立即控制这些页面 // 当一个 service worker 被初始注册时,页面在下次加载之前不会使用它。 claim() 方法会立即控制这些页面
// @ts-ignore event.waitUntil(self.clients.claim());
event.waitUntil(clients.claim());
}); });
self.addEventListener('message', event => { self.addEventListener('message', event => {
@ -46,7 +46,6 @@ self.addEventListener('message', event => {
const vueFileMap = new Map(); const vueFileMap = new Map();
self.addEventListener('fetch', event => { self.addEventListener('fetch', event => {
// @ts-ignore
const request = event.request; const request = event.request;
if (typeof request.url != 'string') return console.log(request); if (typeof request.url != 'string') return console.log(request);
if (vueFileMap.has(request.url)) { if (vueFileMap.has(request.url)) {
@ -63,10 +62,9 @@ self.addEventListener('fetch', event => {
if (!['.ts', '.json', '.vue', 'css'].some(ext => request.url.endsWith(ext))) return; if (!['.ts', '.json', '.vue', 'css'].some(ext => request.url.endsWith(ext))) return;
if (request.url.endsWith('.d.ts')) return; if (request.url.endsWith('.d.ts')) return;
if (request.url.endsWith('.json') || request.url.endsWith('css')) { if (request.url.endsWith('.json') || request.url.endsWith('css')) {
// @ts-ignore
if (!event.request.headers.get('origin')) return; if (!event.request.headers.get('origin')) return;
} }
// 请求ts文件 // 请求文件
const res = fetch(request.url, { const res = fetch(request.url, {
method: request.method, method: request.method,
mode: "no-cors", mode: "no-cors",
@ -74,18 +72,19 @@ self.addEventListener('fetch', event => {
"Content-Type": "text/plain" "Content-Type": "text/plain"
}), }),
}); });
// @ts-ignore // 修改请求结果
event.respondWith( event.respondWith(
res.then(res => { res.then(res => {
if (res.status != 200) return res; if (res.status != 200) return res;
console.log('正在编译', request.url); console.log('正在编译', request.url);
return res.text().then(text => { return res.text().then(text => {
let js; let js = '';
if (request.url.endsWith('.json')) { if (request.url.endsWith('.json')) {
js = `export default ${ text }`; js = `export default ${ text }`;
} else if (request.url.endsWith('.ts')) { } else if (request.url.endsWith('.ts')) {
js = ts.transpile(text, { js = ts.transpile(text, {
module: ts.ModuleKind.ES2015, module: ts.ModuleKind.ES2015,
//@todo: ES2019 -> ES2020
target: ts.ScriptTarget.ES2019, target: ts.ScriptTarget.ES2019,
inlineSourceMap: true, inlineSourceMap: true,
resolveJsonModule: true, resolveJsonModule: true,
@ -97,7 +96,7 @@ self.addEventListener('fetch', event => {
// 后续处理sourceMap合并 // 后续处理sourceMap合并
const { descriptor } = sfc.parse(text, { filename: request.url, sourceMap: true }); const { descriptor } = sfc.parse(text, { filename: request.url, sourceMap: true });
// console.log({ descriptor }); // console.log({ descriptor });
const hasScoped = descriptor.styles.some((s) => s.scoped); const hasScoped = descriptor.styles.some(s => s.scoped);
// 编译 script因为可能有 script setup还要进行 css 变量注入 // 编译 script因为可能有 script setup还要进行 css 变量注入
const script = sfc.compileScript(descriptor, { const script = sfc.compileScript(descriptor, {
id: scopeId, id: scopeId,
@ -111,11 +110,21 @@ self.addEventListener('fetch', event => {
}); });
// 用于存放代码,最后 join('\n') 合并成一份完整代码 // 用于存放代码,最后 join('\n') 合并成一份完整代码
const codeList = []; const codeList = [];
// 保存url并且拼接参数
const url = new URL(request.url);
const scriptSearchParams = new URLSearchParams(url.search.slice(1));
scriptSearchParams.append('type', 'script');
const templateSearchParams = new URLSearchParams(url.search.slice(1));
templateSearchParams.append('type', 'template');
vueFileMap.set( vueFileMap.set(
request.url + '?type=script', url.origin + url.pathname + '?' + scriptSearchParams.toString(),
// 重写 default // 重写 default
sfc.rewriteDefault(script.attrs && script.attrs.lang == 'ts' ? ts.transpile(script.content, { sfc.rewriteDefault(script.attrs && script.attrs.lang == 'ts' ? ts.transpile(script.content, {
module: ts.ModuleKind.ES2015, module: ts.ModuleKind.ES2015,
//@todo: ES2019 -> ES2020
target: ts.ScriptTarget.ES2019, target: ts.ScriptTarget.ES2019,
inlineSourceMap: true, inlineSourceMap: true,
resolveJsonModule: true, resolveJsonModule: true,
@ -126,12 +135,13 @@ self.addEventListener('fetch', event => {
.replaceAll(`from "vue"`, `from "/game/vue.esm-browser.js"`) .replaceAll(`from "vue"`, `from "/game/vue.esm-browser.js"`)
.replaceAll(`from 'vue'`, `from '/game/vue.esm-browser.js'`) .replaceAll(`from 'vue'`, `from '/game/vue.esm-browser.js'`)
); );
codeList.push(`import { __sfc_main__ } from '${ request.url }?type=script'`); codeList.push(`import { __sfc_main__ } from '${ request.url }?type=script'`);
codeList.push(`__sfc_main__.__scopeId = '${ scopeId }'`); codeList.push(`__sfc_main__.__scopeId = '${ scopeId }'`);
// 编译模板,转换成 render 函数 // 编译模板,转换成 render 函数
const template = sfc.compileTemplate({ const template = sfc.compileTemplate({
source: descriptor.template.content, source: descriptor.template ? descriptor.template.content : '',
filename: request.url, // 用于错误提示 filename: request.url, // 用于错误提示
id: scopeId, id: scopeId,
scoped: hasScoped, scoped: hasScoped,
@ -140,7 +150,9 @@ self.addEventListener('fetch', event => {
} }
}); });
vueFileMap.set(request.url + '?type=template', template.code vueFileMap.set(
url.origin + url.pathname + '?' + templateSearchParams.toString(),
template.code
// .replace(`function render(_ctx, _cache) {`, str => str + 'console.log(_ctx);') // .replace(`function render(_ctx, _cache) {`, str => str + 'console.log(_ctx);')
.replaceAll(`from "vue"`, `from "/game/vue.esm-browser.js"`) .replaceAll(`from "vue"`, `from "/game/vue.esm-browser.js"`)
.replaceAll(`from 'vue'`, `from '/game/vue.esm-browser.js'`) .replaceAll(`from 'vue'`, `from '/game/vue.esm-browser.js'`)
@ -180,11 +192,12 @@ self.addEventListener('fetch', event => {
"Content-Type": "text/javascript" "Content-Type": "text/javascript"
}), }),
}); });
console.log(request.url, '编译成功');
return rep; return rep;
}) })
}) })
.catch(e => { .catch(e => {
console.log(e); console.error(request.url, '编译失败: ', e);
throw e; throw e;
}) })
); );