编辑
2025-02-27
前端
00
请注意,本文编写于 73 天前,最后修改于 73 天前,其中某些信息可能已经过时。

目录

1 背景与目标
1.1 背景
1.2 目标
2 整体方案
2.1 如何去指明开发入口
2.2 怎么在控制台打印入口
2.3 如何实现
2.4 Webpack5的方法
3. 拓展
3.1 Vite 生命周期和钩子
3.2 Webpack 生命周期和钩子
3.3 Vite 如何实现
3.4 Webpack 插件实现打印

本文为作者在团子写的一个小的开发体验优化需求,截取片段

1 背景与目标

1.1 背景

商户服务需要对外提供 h5 / pc 等多种形态的页面,对应页面为 /pc.html 、/h5.html 等,但目前 yarn serve 时只会干巴巴的展示 127.0.0.1:8080,点击了又访问不了;同时页面访问时可能需要若干 query 参数,需要手动拼接等,影响开发体验。

目前有些服务需要口口相传、手动拼接;account 仓库也有不同方案,心智不统一:

  1. xx1 packages/xx1 ,放在 readme
  2. xx2 packages/xx2,根目录也会作为开发引导,暂时可开发访问的链接
  3. xx3 packages/xx3,需访问 127.0.0.1:8080/dev.html(根路径会作为 pc/h5 自适应的地址)

1.2 目标

yarn serve 执行成功后直接输出可访问的地址,如 127.0.0.1:8080/dev.html 等,快速进入开发阶段。

2 整体方案

2.1 如何去指明开发入口

方案说明选择备注
方案一:维护一个readme在每个服务下维护一个readme,作为文档及指导开发方式(比如入口在哪,需要怎么配置)
并且作为开发者,会自然的查看仓库下的readme,readme传递的信息比较丰富且有效。
方案二:对vue-cli打印的日志进行修改在vue-cli的devServer打印的日志上做修改,修改running at的url或者在其之前之后打印一些提示信息


打印日志的方式最为直接且方便,但是在实际项目中,打印的日志信息很可能被覆盖,对url的修改可能有侵入性,对代码维护可能不友好。
方案三:自动打开浏览器并指向对应的开发入口不做提示,只通过devServer的打开浏览器操作打开到正确的路径。
修改量最小,也能有效的引导到正确的路径,但是缺乏信息。

综合考虑,可以选择方案二。

2.2 怎么在控制台打印入口

在上述整体方案选择为方案二的同时,方案二也有几种实现方法

方案说明选择备注
实现一:在vue-cli输出之下添加自行拼接的url

通过获取开发服务器的协议(http/https)、host、端口、publicPath,并拼接上开发入口的路径,形成最终完整的路径。
实现二:使用子父进程监听vue-cli的输出并修改通过正则匹配到了url位置,但是其他的输出内容也有变化,比如颜色显示没有了。而且使用父进程控制yarn serve也可能引起一些未知的问题,在这里不推荐。

2.3 如何实现

背景说明: @mtfe/mwallet-build 是团队中构建配置的 npm 包,提供 Webpack 的诸多配置,其中 handleDevServer 为开发服务器的配置文件,配置了 Mock 代理、或者泳道代理等等

在启动开发服务器时打印信息,本质利用的是 Webpack 的 完成时 生命周期,使用 compiler.hooks.done 即可注册一个钩子,每当服务器启动时,或者热更新时,会触发钩子执行。

在生命周期操作,正常是需要使用插件实现的,本着最小改造量的目的,想到 devServerbeforeafter只在 webpack4中有效,webpack5中需要使用 setupMiddlewares 替代)也可以获取到 compiler对象进行操作

通过修改devServer在webpack dev server的各个时期去获取url的部分,并最终拼接成为开发入口,即可达到本次改造的目标

  1. 获取URL URL由protocol、host、port和publicPath组成,其中只有port是非指定的,可以是自动寻找的端口 因此获取port稍微复杂,查询webpack dev server的文档可以知道通过onListening函数拿到server实例 就可以获取 port(也仅为 webapck4 ),最终拼接 url 即可。

  2. 在 vue-cli 打印的信息之后打印 URL 并且在热更新时也打印 compiler下的 done hook 回调,可以实现在每次编译完成之后执行一段函数,就可以在这个函数中打印URL。 整体流程如下,在webpack dev server生命周期的各个部分,可以拿到不同的数据,最终拼接url。

注:本需求仅适配 webpack4

2.4 Webpack5的方法

before 注册钩子和 onListening 获取端口仅在 Webpack4 生效,Webpack5 可以参考下面方法

js
module.exports = defineConfig({ transpileDependencies: true, configureWebpack: { devServer: { port: 7890, setupMiddlewares(middlewares, devServer) { if (!devServer) { throw new Error("webpack-dev-server is not defined"); } devServer.compiler.hooks.done.tap("printEntry", () => { console.log("编译完成", devServer.compiler.options.devServer?.port ?? 8080); }); return middlewares; }, }, }, });

3. 拓展

3.1 Vite 生命周期和钩子

阶段钩子名称类型触发时机参数
配置阶段config同步/异步在 Vite 配置加载之前(config, { command, mode })
configResolved同步配置解析完成后(resolvedConfig)
优化阶段buildStart同步/异步构建开始时()
resolveId同步/异步模块解析时(source, importer, options)
load同步/异步模块加载时(id)
transform同步/异步模块转换时,如编译 TypeScript、处理 JSX(code, id)
handleHotUpdate异步处理热更新(HMR)时({ file, server, modules })
开发服务器阶段configureServer异步配置开发服务器时,添加中间件或路由(server)
HTML 转换阶段transformIndexHtml同步/异步转换 index.html 时,修改或注入内容(html, ctx)
构建阶段generateBundle同步/异步生成最终包之前(options, bundle, isWrite)
writeBundle同步/异步所有文件写入磁盘后()
closeBundle同步/异步构建完成后,适合做清理工作()

3.2 Webpack 生命周期和钩子

阶段钩子名称类型触发时机参数
初始化阶段environment同步准备编译环境时-
afterEnvironment同步环境准备完成后-
entryOption同步entry 配置项处理后(context, entry)
afterPlugins同步插件注册完成后(compiler)
afterResolvers同步resolver 安装完成后(compiler)
编译阶段beforeRun异步开始正式编译前(compiler)
run异步开始编译时(compiler)
watchRun异步监听模式下,开始编译时(compiler)
beforeCompile异步compilation 参数创建后(compilationParams)
compile同步一次 compilation 创建前(compilationParams)
compilation同步compilation 创建后(compilation, compilationParams)
make异步从 entry 开始递归分析依赖时(compilation)
构建阶段afterCompile异步完成构建、封存完成后(compilation)
shouldEmit同步是否输出文件(compilation)
emit异步输出 assets 到 output 目录之前(compilation)
afterEmit异步输出 assets 到 output 目录之后(compilation)
完成阶段done同步编译完成(stats)
failed同步编译失败(error)
监听相关watchClose同步监听模式停止-
infrastructureLog同步日志输出时(name, type, args)
补充说明:
  1. 钩子类型:
  2. 同步钩子:使用 tap() 方法注册
  3. 异步钩子:可使用 tapAsync () 或 tapPromise () 方法注册

3.3 Vite 如何实现

js
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' function PreStartLoggerPlugin() { return { name: "pre-start-logger", config() { console.log("🔧 准备启动 Vite 开发服务器..."); }, configResolved(config) { console.log(`🛠 当前工作目录: ${config.root}`); console.log(`📂 输出目录: ${config.build.outDir}`); }, configureServer(server) { server.httpServer?.once("listening", () => { const address = server.httpServer?.address(); if (address && typeof address === "object") { console.log( `🚀 开发服务器已启动,访问地址: http://localhost:${address.port}` ); } }); }, }; } // https://vite.dev/config/ export default defineConfig({ plugins: [vue(),PreStartLoggerPlugin()], })

3.4 Webpack 插件实现打印

js
const { defineConfig } = require("@vue/cli-service"); class PreStartLoggerPlugin { apply(compiler) { compiler.hooks.watchRun.tap("PreStartLoggerPlugin", (compiler) => { console.log("🔧 准备启动 Webpack 开发服务器..."); console.log(`🛠 当前工作目录: ${process.cwd()}`); console.log(`📂 输出目录: ${compiler.options.output.path}`); }); // 主要依靠这个hook实现打印效果 compiler.hooks.done.tap("PreStartLoggerPlugin", (stats) => { const devServer = compiler.options.devServer; const port = devServer?.port || 8080; console.log(`🚀 开发服务器已启动,访问地址: http://localhost:${port}`); }); } } module.exports = defineConfig({ transpileDependencies: true, configureWebpack: { plugins: [new PreStartLoggerPlugin()], devServer: { port: 7890 } }, });
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:pepedd864

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!