首页
Search
1
解决visual studio code (vscode)安装时没有选择安装路径问题
313 阅读
2
如何在 Clash for Windows 上配置服务
210 阅读
3
Linux 下 Bash 脚本 bad interpreter 报错的解决方法
150 阅读
4
Arch Linux 下解决 KDE Plasma Discover 的 Unable to load applications 错误
148 阅读
5
uniapp打包app提示通讯录权限问题,如何取消通讯录权限
112 阅读
clash
服务器
javascript
全部
游戏资讯
登录
Search
加速器之家
累计撰写
1,072
篇文章
累计收到
0
条评论
首页
栏目
clash
服务器
javascript
全部
游戏资讯
页面
搜索到
1072
篇与
的结果
2024-08-24
配置 SplitChunksPlugin 减少 Webpack 打包体积
为什么要手动优化在优化之前我们必须先问一个问题,需不需要手动优化?过早的优化是魔鬼,许多情况下 Webpack 的自动分块已经可以满足。手动优化一般是在项目组件变得庞大之后,我们希望根据业务对部分依赖作特殊的处理,从而大幅度减少打包体积。CommonsChunkPlugin 的局限在过去 Webpack 提供了 CommonsChunkPlugin 来配置拆分和组合块(chunks),如今这个插件已被淘汰,取而代之的是更具拓展性的 SplitChunksPlugin 。两者有什么不同?从名字我们就可以看出些线索。CommonsChunkPlugin 顾名思义,其目的是提出公用的块放在一起,而 SplitChunksPlugin 则更倾向于如何把块切分出去。在 CommonsChunkPlugin 中,如果需要手动优化,提取多个公共块需要通过多次 new CommonsChunkPlugin 来分别配置。这里就有一个问题,如果一个模块存在多个公共块中该如何处理?答案是每个公共块都会存一份。因为这里每个配置都是独立的,很难再进一步统筹优化。SplitChunksPlugin 一站式配置SplitChunksPlugin 通过引入 cacheGroups 来解决这个问题。在 SplitChunksPlugin 中,手动切分多个公共块不再需要多次 new SplitChunksPlugin,而是统一在 cacheGroups 域下配置。module.exports = { //... optimization: { splitChunks: { cacheGroups: { vendor: { test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/, name: 'vendor', chunks: 'all', } } } } };cacheGroups 默认会继承 optimization 的部分配置,而通过 priority 和 reuseExistingChunk 我们解决了复用的问题。模块可视化细调之前先安装 webpack-bundle-analyzer 直观地查看模块与块。沙拉查词的优化首先这是沙拉查词优化前的样子,使用了默认的 optimization 配置,打包后 5.63MB(包含一个 PDF 浏览器)。第一眼可以非常明显地观察到一个巨型包 @ant-design/icons/lib,这是 antd 3.9 引入的坑爹改变,目前没有很好的解决方式,所以这里我们手动给 antd 全家桶割一个块。因为只有三个地方用到,我们不希望其它入口加载时也引入这个块,所以要声明这次分割影响到的块,cacheGroups 中提供了 test 来匹配。同时因为这三个地方都是沙拉查词中独立的页面,一般需要用户手动访问才会加载,故这里索性将 node_modules 中三者公共的模块都切出来。neutrino.config .optimization .merge({ splitChunks: { cacheGroups: { antd: { test: /[\\/]node_modules[\\/]/, name: 'antd', chunks: ({ name }) => /^(notebook|options|history)$/.test(name), reuseExistingChunk: true } } }, })这里我配置了 reuseExistingChunk 是期望如果 node_modules 中有的模块在其它地方已经分割了,那么尽可能复用,因为如前面所述这三个页面只有用户需要时才访问,所以没有关系。效果如图,打包后 3.74MB(包含一个 PDF 浏览器)。减少了 1.89MB 的体积!接下来可以看到还有几个大块,这个主要来自于沙拉查词的查词面板,包括 React 系的模块以及各个词典的模块。它们有一个特点是必然在一起出现,所以我们给它们割一个块。neutrino.config .optimization .merge({ splitChunks: { cacheGroups: { dictpanel: { test: /([\\/]src[\\/]content[\\/])|([\\/]components[\\/]dictionaries[\\/])|([\\/]node_modules[\\/]react)/, name: 'dictpanel', chunks: ({ name }) => !/^(selection|audio-control|background)$/.test(name) }, antd: { test: /[\\/]node_modules[\\/]/, name: 'antd', chunks: ({ name }) => /^(notebook|options|history)$/.test(name), reuseExistingChunk: true } } }, })其中 src/content/ 下放的是词典面板的组件,components/dictionaries/ 下是各个词典的模块,最后匹配了所有 react 开头的模块。同时我们隔离了用不到词典面板的几个入口,避免因为用到其中的一两个模块而引入整个分割出来的块。效果如图,打包后 3.44MB(包含一个 PDF 浏览器)。相比原来减少了 2.19MB 的体积!这里我觉得已经差不多了,再往下压意义不大。可见通过简单的配置我们减少了 2.19MB 体积,还是比较划算的。最后当然 SplitChunksPlugin 能做的远不止这些,遇到问题可以多翻文档找灵感。祝大家国庆节快乐!
2024年08月24日
7 阅读
0 评论
0 点赞
2024-08-24
利用 Webpack API 获取资源清单
为什么如今几乎每个 Webpack 打包的项目都会用到 HTML Webpack Plugin。这个插件可以生成 HTML 文件带上打好的包。这在我实现一个浏览器扩展脚手架时提供了灵感。每个扩展都会有一个清单文件,里面列举了这个扩展需要加载的各种资源。{ "background": { "scripts": ["jquery.js", "my-background.js"], }, "content_scripts": [ { "matches": ["*://blog.crimx.com/*"], "js": ["common.js", "my-content.js"], "css": ["my-content.css"] } ], // ... }通常这些是手写上去的,但如果结合 Webpack 流程,我设想是能不能像 HTML Webpack Plugin 一样自动生成这些配置。如此便可发挥 Webpack 自动拆分块以及添加哈希的优势。Plugin基本的 Webpack Plugin 十分简单,在 constructor 处理配置,暴露 apply 方法实现逻辑。class WexExtManifestPlugin { constructor (options) { this.options = options } apply (compiler) { } }Tapable在 Webpack 中,API 通过 hook 勾上 Tapable 来挂载回调。不同的 Tapable 子类用于不同种类的回调。我们这里使用 Promise 处理异步回调。class WexExtManifestPlugin { constructor (options) { this.options = options } apply (compiler) { compiler.hooks.done.tapPromise('WexExtManifestPlugin',async ({ compilation }) => {}) } }CompilationCompilation 是 Webpack 最重要的 API 之一,通过 entrypoints 我们可以获得每个包的 entry 和 name ,通过 entry.getFiles() 可以获取该入口下所有文件,通过 name 可以定位到相应包名,从配置中获取其它信息。class WexExtManifestPlugin { constructor (options, neutrinoOpts) { this.options = options } apply (compiler) { compiler.hooks.done.tapPromise( 'WexExtManifestPlugin', async ({ compilation }) => { compilation.entrypoints.forEach((entry, name) => {const files = entry.getFiles().map(file => file.replace(/\.(css|js)\?.*$/, '.$1'))}) } ) } }完整的实现在这里。通过获取资源清单,脚手架可以利用 Webpack 实现复杂的优化;同时复用 Neutrino 的配置,扩展的资源配置统一到 Neutrino 入口中,不再需要手动维护。
2024年08月24日
6 阅读
0 评论
0 点赞
2024-08-24
实现一个有趣的 RxJS Operator
问题最近有这么一个情况,生产者会产生 { id, value } 结构的值,下游接收发起异步操作。如果是同个 id 那么后产生的值总会覆盖前者,前者发起的异步如果返回得比较晚则需要丢弃过时的值。所以这里就有点类似于 switchMap 但不同的是,switchMap 总会抛弃前者,而这里只有 id 相同才会抛弃。往下阅读之前不妨想想可以如何解决。排除首先这里肯定不能是基于 switchMap,因为我们需要保留不同 id 发起的异步结果。那么剩下的子流归并操作是 mergeMap 和 concatMap。concatMap 一般用于子流产生多个顺序值,所以这里也不适用。mergeMap 是最普通的归并,没有其它合适 Operator 情况下我们就根据它来实现一个自定义的 Operator。思路从另一个角度看这个问题,我们只需要根据 id 产生一条子流,之后如果出现同个 id 的项则取消这条子流。对于判断后来的同个 id 值,我们可以借用一条只有这个 id 值的流。takeUntil(input$.pipe(filter(input => input.id === id)))所以这个思路就很明显了。import { Observable, OperatorFunction } from 'rxjs' import { mergeMap, takeUntil, filter } from 'rxjs/operators' export function switchMapBy( key: keyof T, project: (val: T) => Observable ): OperatorFunction { return input$ => { return input$.pipe( mergeMap(val => project(val).pipe( takeUntil(input$.pipe(filter(input => input[key] === val[key]))) ) ) ) } }优化在复用了流的情况下,如果这个 Operator 使用时排在较后的位置,那么它前面的操作就要都执行两次,我们可以用将流转热避免这个问题。import { Observable, OperatorFunction } from 'rxjs' import { mergeMap, takeUntil, filter, share } from 'rxjs/operators' export function switchMapBy( key: keyof T, project: (val: T) => Observable ): OperatorFunction { return input$ => { const input$$ = input$.pipe(share()) return input$$.pipe( mergeMap(val => project(val).pipe( takeUntil(input$$.pipe(filter(input => input[key] === val[key]))) ) ) ) } }最后我们还可以让 project 支持返回 Promise。import { Observable, OperatorFunction, from } from 'rxjs' import { mergeMap, takeUntil, filter, share } from 'rxjs/operators' export function switchMapBy( key: keyof T, project: (val: T) => Observable | Promise ): OperatorFunction { return input$ => { const input$$ = input$.pipe(share()) return input$$.pipe( mergeMap(val => from(project(val)).pipe( takeUntil(input$$.pipe(filter(input => input[key] === val[key]))) ) ) ) } }最后实现自定义 Operator 的确是一个比较好的练手机会,对于重新审视理解流有一定帮助。这种流复用的思考方式还得多加训练才能一步到位。
2024年08月24日
5 阅读
0 评论
0 点赞
2024-08-24
修复 Deepin Wine 迅雷崩溃
从 /usr/share/applications/deepin.com.thunderspeed.desktop 中找到运行方式 "/opt/deepinwine/apps/Deepin-ThunderSpeed/run.sh" -u %u。运行发现错误为 wine 不知为什么没能找到加载迅雷目录下的 dlls 。0028:fixme:msvcp:_Locinfo__Locinfo_ctor_cat_cstr (008AED28 1 C) semi-stub 0028:fixme:msvcp:_Locinfo__Locinfo_ctor_cat_cstr (008AED78 1 C) semi-stub 0028:fixme:msvcp:_Locinfo__Locinfo_ctor_cat_cstr (008AEA2C 1 C) semi-stub 0028:fixme:msvcp:_Locinfo__Locinfo_ctor_cat_cstr (008AED28 1 C) semi-stub 0028:fixme:heap:RtlSetHeapInformation 0x8c0000 0 0x8ae5c0 4 stub 0030:fixme:winsock:WSCGetProviderPath ({e70f1aa0-ab8b-11cf-8ca3-00805f48a192} 0x102f0ac 0x102f0a8 0x102f0a4) Stub! 0009:err:module:import_dll Library XLFSIO.dll (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library XLLuaRuntime.dll (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library XLGraphic.dll (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library XLUE.dll (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library DownloadKernel.dll (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library libexpat.dll (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library XLUserS.DLL (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library BaseCommunity.DLL (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library XLGraphicPlus.dll (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library zlib1.dll (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library xlstat.dll (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:import_dll Library mini_unzip_dll.dll (which is needed by L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe") not found 0009:err:module:LdrInitializeThunk Importing dlls for L"c:\\Program Files\\Thunder Network\\Thunder\\Program\\Thunder.exe" failed, status c0000135 0041:fixme:winhttp:request_set_option 0 (null) (null)尝试将目录添加到环境变量env WINEPREFIX="$HOME/.deepinwine/Deepin-ThunderSpeed" wine .deepinwine/Deepin-ThunderSpeed/drive_c/windows/regedit.exe 打开注册表,定位到 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment,编辑 PATH 加入 C:\Program Files\Thunder Network\Thunder\Program,注意以分号 ; 相隔。重新启动迅雷,问题解决。
2024年08月24日
5 阅读
0 评论
0 点赞
2024-08-24
TypeScript 函数泛型部分赋值
问题首先,我们知道 TypeScript 很早(2017)前就实现了带默认值的泛型(generic)type Foo = T // $ExpectType number type T1 = Foo // $ExpectType string type T2 = Foo如果我们定义这么一个部分泛型带默认值的类型type Pluck = TObj[TKey]那么这个默认值是可以被正确推导的type Obj = { a: number, b: string } // $ExpectType string | number type Props = Pluck // $ExpectType string type Prop = Pluck对于函数来说,TypeScript 允许泛型从参数中推导function identity(arg: T): T { return arg } const text = identity('text') // $ExpectType 'text' typeof text有时如果泛型不在参数中,那么使用时我们就要提供function fetchJSON(src: string): Promise { return fetch(src).then(r => r.json()) } // $ExpectType Promise fetchJSON('http://blog.crimx.com/json')现在问题就来了,如果我们希望只提供部分的泛型,而剩下的泛型可以推导function fetchData( src: string, key: TKey ): Promise { return fetch(src) .then(r => r.json()) .then(json => json[key]) } // Argument of type '"result"' is not assignable to parameter of type 'never'.ts(2345) // $ExpectError fetchData('http://blog.crimx.com/json', 'result') // Expected 2 type arguments, but got 1.ts(2558) // $ExpectError fetchData('http://blog.crimx.com/json', 'result')可以看到,如果没有给泛型提供值,那么 TKey 会以 TResult 定义时的值 unknown 进行推导;如果只提供 TResult 那么 TypeScript 会要求提供全部值。这个特性目前依然存在争议,要求泛型从两个地方进行推导可能会引起混淆。解决看回我们的问题,其实我们之所以要提供 TResult 是为了声明 fetch 的结果。我们是做了这两件事fetchJSON('http://blog.crimx.com/json') .then(json => json[key])可以用柯里化的方式解决function fetchData( src: string ): (key: TKey) => Promise { return key => fetch(src) .then(r => r.json()) .then(json => json[key]) } // $ExpectType Promise fetchData('http://blog.crimx.com/json')('result')其它的应用也可以归类到这个模式,这严格上说不算是“解决”而是“变通”。牺牲运行时来让 ts 编译器满意实在有点膈应,希望未来的读者有缘读到这篇时已经出现了更好的解决方案。谢谢阅读!
2024年08月24日
5 阅读
0 评论
0 点赞
1
...
143
144
145
...
215