本文为作者在团子写的一个小的开发体验优化需求,截取片段
商户服务需要对外提供 h5 / pc 等多种形态的页面,对应页面为 /pc.html 、/h5.html 等,但目前 yarn serve 时只会干巴巴的展示 127.0.0.1:8080,点击了又访问不了;同时页面访问时可能需要若干 query 参数,需要手动拼接等,影响开发体验。
目前有些服务需要口口相传、手动拼接;account 仓库也有不同方案,心智不统一:
yarn serve 执行成功后直接输出可访问的地址,如 127.0.0.1:8080/dev.html 等,快速进入开发阶段。
方案 | 说明 | 选择 | 备注 |
---|---|---|---|
方案一:维护一个readme | 在每个服务下维护一个readme,作为文档及指导开发方式(比如入口在哪,需要怎么配置) 并且作为开发者,会自然的查看仓库下的readme,readme传递的信息比较丰富且有效。 | ||
方案二:对vue-cli打印的日志进行修改 | 在vue-cli的devServer打印的日志上做修改,修改running at的url或者在其之前之后打印一些提示信息![]() 打印日志的方式最为直接且方便,但是在实际项目中,打印的日志信息很可能被覆盖,对url的修改可能有侵入性,对代码维护可能不友好。 | ||
方案三:自动打开浏览器并指向对应的开发入口 | 不做提示,只通过devServer的打开浏览器操作打开到正确的路径。![]() 修改量最小,也能有效的引导到正确的路径,但是缺乏信息。 |
综合考虑,可以选择方案二。
在上述整体方案选择为方案二的同时,方案二也有几种实现方法
方案 | 说明 | 选择 | 备注 |
---|---|---|---|
实现一:在vue-cli输出之下添加自行拼接的url | ![]() 通过获取开发服务器的协议(http/https)、host、端口、publicPath,并拼接上开发入口的路径,形成最终完整的路径。 | ||
实现二:使用子父进程监听vue-cli的输出并修改 | 通过正则匹配到了url位置,但是其他的输出内容也有变化,比如颜色显示没有了。而且使用父进程控制yarn serve 也可能引起一些未知的问题,在这里不推荐。 |
背景说明:
@mtfe/mwallet-build
是团队中构建配置的npm
包,提供 Webpack 的诸多配置,其中handleDevServer
为开发服务器的配置文件,配置了 Mock 代理、或者泳道代理等等
在启动开发服务器时打印信息,本质利用的是 Webpack 的 完成时
生命周期,使用 compiler.hooks.done
即可注册一个钩子,每当服务器启动时,或者热更新时,会触发钩子执行。
在生命周期操作,正常是需要使用插件实现的,本着最小改造量的目的,想到 devServer
的 before
和 after
(只在 webpack4中有效,webpack5中需要使用 setupMiddlewares
替代)也可以获取到 compiler对象进行操作
通过修改devServer在webpack dev server的各个时期去获取url的部分,并最终拼接成为开发入口,即可达到本次改造的目标。
获取URL URL由protocol、host、port和publicPath组成,其中只有port是非指定的,可以是自动寻找的端口 因此获取port稍微复杂,查询webpack dev server的文档可以知道通过onListening函数拿到server实例 就可以获取 port(也仅为 webapck4 ),最终拼接 url 即可。
在 vue-cli 打印的信息之后打印 URL 并且在热更新时也打印 compiler下的 done hook 回调,可以实现在每次编译完成之后执行一段函数,就可以在这个函数中打印URL。 整体流程如下,在webpack dev server生命周期的各个部分,可以拿到不同的数据,最终拼接url。
注:本需求仅适配 webpack4
before
注册钩子和 onListening
获取端口仅在 Webpack4
生效,Webpack5
可以参考下面方法
jsmodule.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;
},
},
},
});
阶段 | 钩子名称 | 类型 | 触发时机 | 参数 |
---|---|---|---|---|
配置阶段 | 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 | 同步/异步 | 构建完成后,适合做清理工作 | () |
阶段 | 钩子名称 | 类型 | 触发时机 | 参数 |
---|---|---|---|---|
初始化阶段 | 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) | |
补充说明: |
jsimport { 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()],
})
jsconst { 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
}
},
});
本文作者:pepedd864
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!