forked from fanruan/fineui
zsmj
2 years ago
20 changed files with 309 additions and 32 deletions
@ -0,0 +1,13 @@
|
||||
import { WorkerAbilityTestActionType } from '../../utils/action_type'; |
||||
import { WorkerAbilityTestPayload, WorkerAbilityTestReponse } from '../../utils/payload_type'; |
||||
|
||||
export class WorkerAbilityTestMainThreadAction extends BI.Workers.WorkerBaseAction { |
||||
/** |
||||
* 通信能力检测 |
||||
*/ |
||||
public communicationTest(): Promise<WorkerAbilityTestReponse['CommunicationTest']> { |
||||
const mainThreadPostTime: WorkerAbilityTestPayload['CommunicationTest'] = Date.now(); |
||||
|
||||
return this.controller.requestPromise(WorkerAbilityTestActionType.CommunicationTest, mainThreadPostTime); |
||||
} |
||||
} |
@ -0,0 +1,25 @@
|
||||
import { WorkerAbilityTestMainThreadAction } from './action/action.worker_ability_test'; |
||||
// 不需要一起打包的话则不需要引入这行 |
||||
import { workerUrl } from 'fui-worker!../worker_thread/${workerName}_worker_thread'; |
||||
|
||||
class ${WorkerName}MainTreadWorker extends BI.Workers.MainThreadWorker { |
||||
private communicationTest: WorkerAbilityTestMainThreadAction; |
||||
|
||||
public initActions(): void { |
||||
this.communicationTest = this.createAction(WorkerAbilityTestMainThreadAction); |
||||
} |
||||
|
||||
public testCommunication() { |
||||
return this.communicationTest.communicationTest(); |
||||
} |
||||
} |
||||
|
||||
const ${workerName}MainTreadWorker = BI.Workers.createWorker(${WorkerName}MainTreadWorker, { |
||||
workerUrl, |
||||
workerName: BI.UUID(), |
||||
}); |
||||
|
||||
${workerName}MainTreadWorker.testCommunication() |
||||
.then(v => { |
||||
console.log(v); |
||||
}); |
@ -1,5 +0,0 @@
|
||||
class CrudMainTreadWorker extends BI.Workers.MainThreadWorker { |
||||
protected initActions(): void { |
||||
// to init some actions
|
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
/* |
||||
* Worker 事务标识 |
||||
* 每类事务有命名空间, 包含多个具体事务 |
||||
*/ |
||||
|
||||
export const enum WorkerAbilityTestActionType { |
||||
CommunicationTest = 'CommunicationTest', |
||||
} |
@ -0,0 +1,13 @@
|
||||
/** |
||||
* 跨线程通信各事务的发送数据类型声明 |
||||
*/ |
||||
export interface WorkerAbilityTestPayload { |
||||
CommunicationTest: number; |
||||
} |
||||
|
||||
/** |
||||
* 跨线程通信各事务的响应数据类型声明 |
||||
*/ |
||||
export interface WorkerAbilityTestReponse { |
||||
CommunicationTest: number; |
||||
} |
@ -0,0 +1,24 @@
|
||||
import { WorkerAbilityTestActionType } from '../../utils/action_type'; |
||||
import { WorkerAbilityTestPayload, WorkerAbilityTestReponse } from '../../utils/payload_type'; |
||||
|
||||
export class WorkerAbilityTestWorkerThreadAction extends BI.Workers.WorkerBaseAction { |
||||
protected addActionHandler(): void { |
||||
this.controller.addActionHandler( |
||||
WorkerAbilityTestActionType.CommunicationTest, |
||||
this.communicationTest.bind(this) |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* 通信能力检测的处理器 |
||||
*/ |
||||
private communicationTest( |
||||
payload: WorkerAbilityTestPayload['CommunicationTest'] |
||||
): WorkerAbilityTestReponse['CommunicationTest'] { |
||||
const mainThreadPostTime = payload; |
||||
// 收到主线程信息的耗时 |
||||
const workerGetMessageDuration = Date.now() - mainThreadPostTime; |
||||
|
||||
return workerGetMessageDuration; |
||||
} |
||||
} |
@ -0,0 +1,12 @@
|
||||
// TODO: 这边需要先import fineui资源 |
||||
import { WorkerAbilityTestWorkerThreadAction } from './action/action.worker_ability_test'; |
||||
|
||||
class ${WorkerName}WorkerTreadWorker extends BI.Workers.WorkerThreadWorker { |
||||
public communicationTest: WorkerAbilityTestWorkerThreadAction; |
||||
|
||||
public initActions(): void { |
||||
this.communicationTest = this.createAction(WorkerAbilityTestWorkerThreadAction); |
||||
} |
||||
} |
||||
|
||||
export const ${workerName}WorkerTreadWorker = BI.Workers.createWorker(${WorkerName}WorkerTreadWorker); |
@ -1,5 +0,0 @@
|
||||
class CrudWorkerTreadWorker extends BI.Workers.MainThreadWorker { |
||||
protected initActions(): void { |
||||
// to init some actions
|
||||
} |
||||
} |
@ -0,0 +1,9 @@
|
||||
const WorkerPluginName = 'FuiWorkerPlugin'; |
||||
const WorkerLoaderName = 'FuiWorkerWorkerLoader'; |
||||
const FileNamePrefix = 'worker-'; |
||||
|
||||
module.exports = { |
||||
WorkerPluginName, |
||||
WorkerLoaderName, |
||||
FileNamePrefix, |
||||
}; |
@ -0,0 +1,45 @@
|
||||
/* |
||||
* worker-plugin |
||||
*/ |
||||
|
||||
const path = require('path'); |
||||
const webpack = require('webpack'); |
||||
const { WorkerPluginName } = require('./constants'); |
||||
|
||||
class FuiWorkerPlugin { |
||||
constructor(options = {}) { |
||||
this.options = options; |
||||
} |
||||
|
||||
apply(compiler) { |
||||
// 为主线程构建添加 __WORKER__ 环境变量, 构建中区分不同线程源码, 实现代码拆减
|
||||
compiler.hooks.afterPlugins.tap(WorkerPluginName, compiler => { |
||||
new webpack.DefinePlugin({ |
||||
// __WORKER__ 表示当前所在线程是否是 worker 线程
|
||||
// 主线程构建中为 false
|
||||
__WORKER__: false, |
||||
}).apply(compiler); |
||||
}); |
||||
|
||||
// 添加自定义的worker entry-loader
|
||||
compiler.hooks.afterResolvers.tap(WorkerPluginName, compiler => { |
||||
/** |
||||
* https://webpack.js.org/configuration/resolve/#resolveloader
|
||||
* 使用 resolveloader 添加自定义的 worker loader |
||||
*/ |
||||
if (!compiler.options.resolveLoader) { |
||||
compiler.options.resolveLoader = { |
||||
alias: {}, |
||||
}; |
||||
} |
||||
if (!compiler.options.resolveLoader.alias) { |
||||
compiler.options.resolveLoader.alias = {}; |
||||
} |
||||
|
||||
// 动态添加 worker 的 worker-loader, 命名为 "fui-worker"
|
||||
compiler.options.resolveLoader.alias['fui-worker'] = path.resolve(__dirname, './worker-loader.js'); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
module.exports = FuiWorkerPlugin; |
@ -0,0 +1,109 @@
|
||||
/* |
||||
* fui-worker worker-loader |
||||
*/ |
||||
|
||||
const webpack = require('webpack'); |
||||
const loaderUtils = require('loader-utils'); |
||||
const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin'); |
||||
const { WorkerLoaderName, FileNamePrefix } = require('./constants'); |
||||
|
||||
// 正常 loader 处理逻辑
|
||||
function loader() { |
||||
const callback = this.async(); |
||||
this.cacheable(false); |
||||
|
||||
// 过滤掉当前的 worker-loader, 保留 worker 侧构建需要的其他 loader(babel-loader/ts-loader 等)
|
||||
const otherLoaders = this.loaders.filter((loader, index) => { |
||||
if (index === this.loaderIndex) { |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
}); |
||||
/** |
||||
* 拼接构建需要的 loader 字符串, 用于指定 childCompiler 的构建 loader |
||||
* 比如: /path/to/babel-loader/lib/index.js!/path/to/ts-loader/index.js! |
||||
*/ |
||||
const loaderPath = otherLoaders.reduce((pre, loader) => `${pre}${loader.path}!`, ''); |
||||
|
||||
/** |
||||
* worker 独立构建的 entry |
||||
* 构建 loader + worker 源码入口文件路径 |
||||
* |
||||
* https://webpack.js.org/concepts/loaders/#inline
|
||||
* `!!` 实现在 childCompiler 中忽略其他所有 loader, 只保留主构建的 loader |
||||
* 不然 worker 入口在 childCompiler 中会继续由 worker-loader 处理, 造成死循环 |
||||
*/ |
||||
const workerEntry = `!!${loaderPath}${this.resourcePath}`; |
||||
|
||||
// 把资源纳入构建流程的依赖, 实现 dev 模式下的 watch
|
||||
this.addDependency(workerEntry); |
||||
|
||||
// 生成的 service 独立 bundle 名称
|
||||
const entryFileName = `${FileNamePrefix}index`; |
||||
|
||||
// 获取传递给 loader 的 options
|
||||
loaderUtils.getOptions(this) || {}; |
||||
|
||||
// 创建 childCompiler, 用于实现 worker 构建为独立 js 资源
|
||||
const childCompiler = this._compilation.createChildCompiler(WorkerLoaderName, { |
||||
globalObject: 'this', |
||||
}); |
||||
childCompiler.context = this._compiler.context; |
||||
|
||||
// 指定独立构建的 entry 和生成 js 资源名称
|
||||
new SingleEntryPlugin(this.context, workerEntry, entryFileName).apply(childCompiler); |
||||
|
||||
// 设置 worker 侧的环境变量
|
||||
new webpack.DefinePlugin({ |
||||
__WORKER__: true, |
||||
}).apply(childCompiler); |
||||
|
||||
// 添加 window 全局对象, 映射为 worker 线程全局对象 self
|
||||
// 如果在 worker 源码中添加, 可能没有前置到所有引用模块前
|
||||
new webpack.BannerPlugin({ |
||||
banner: 'self.window = self;', |
||||
raw: true, |
||||
entryOnly: true, |
||||
}).apply(childCompiler); |
||||
|
||||
const subCache = `subcache ${__dirname} ${workerEntry}`; |
||||
childCompiler.hooks.compilation.tap(WorkerLoaderName, compilation => { |
||||
if (compilation.cache) { |
||||
if (!compilation.cache[subCache]) compilation.cache[subCache] = {}; |
||||
compilation.cache = compilation.cache[subCache]; |
||||
} |
||||
}); |
||||
|
||||
childCompiler.runAsChild((error, entries, compilation) => { |
||||
if (!error && compilation.errors && compilation.errors.length) { |
||||
// eslint-disable-next-line no-param-reassign
|
||||
error = compilation.errors[0]; |
||||
} |
||||
|
||||
// compatible with Array (v4) and Set (v5) prototypes
|
||||
const entry = entries && entries[0] && entries[0].files.values().next().value; |
||||
if (!error && !entry) { |
||||
// eslint-disable-next-line no-param-reassign
|
||||
error = Error(`${WorkerLoaderName}, no entry for ${workerEntry}`); |
||||
} |
||||
|
||||
if (error) { |
||||
return callback(error); |
||||
} |
||||
|
||||
return callback( |
||||
null, |
||||
// 插入代码的转译和压缩由主构建配置的 babel/ts loader 处理, 不需要 worker-worker 来处理
|
||||
// 添加 @ts-nocheck 避免 ts-check 报错
|
||||
`// @ts-nocheck
|
||||
const servicePath = __webpack_public_path__ + ${JSON.stringify(entry)}; |
||||
export const workerUrl = servicePath; |
||||
` |
||||
); |
||||
}); |
||||
|
||||
return; |
||||
} |
||||
|
||||
module.exports = loader; |
Loading…
Reference in new issue