如何模拟一个小程序项目打包的流程

一、Uni-app 执行 yarn run dev:mp-weixin后会发生什么

(一)准备工作

  1. 克隆项目:创建以 typescript 开发的工程(如命令行创建失败,请直接访问 https://gitee.com/dcloud/uni-preset-vue/repository/archive/vite-ts.zip 下载模板)。

    • npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project
  2. 执行 dev:mp-weixin这个命令时,yarnnpm会运行package.json文件中定义的scripts部分的对应命令。通常会类似于这样定义:

    json 复制代码
    "scripts": {
      "dev:mp-weixin": "uni -p mp-weixin" 
    }

(二)执行后发生的事情

  1. 编译项目uni -p mp-weixin命令会调用 uni-app 的 CLI 工具,编译你的项目代码。uni-app 会将你的 Vue.js 代码转换为微信小程序可以识别的代码,包括将 .vue文件编译成微信小程序的wxmlwxssjsjson文件。
  2. 生成微信小程序项目文件 :编译完成后,uni-app 会在项目的dist目录下生成一个专门为微信小程序准备的项目文件夹。这个文件夹包含了所有编译好的代码和资源文件,可以直接在微信开发者工具中打开并运行。
  3. 监视文件变化(开发模式) :如果命令是针对开发环境的(如dev:mp-weixin),uni-app CLI 通常会启动一个开发服务器,并持续监视项目文件的变化。当你修改项目中的文件时,它会自动重新编译,并更新到dist目录中的微信小程序项目文件夹中。
  4. 启动开发服务器:在某些情况下,uni-app 还可能会启动一个本地开发服务器,方便在浏览器中实时预览应用的 H5 版本,并且可以同步调试。

二、实现类似 uni 插件的 vite 插件

(一)创建命令行工具

  1. 在项目中创建一个可以在命令行执行的工具,通常可以通过在package.json中定义一个脚本来实现。可以使用 Node.js 创建一个 CLI 工具,并通过配置package.json来实现全局或本地运行。在package.json中定义vue-mini作为一个可执行命令,并且将bin/vue-mini.js文件链接到该命令。

    json 复制代码
    {
      "name": "vue-mini-plugin",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT",
      "bin": {
        "vue-mini": "./bin/vue-mini.js"
      },
      "scripts": {
        "vue-mini": "vue-mini"
      },
      "dependence": {},
      "dependencies": {}
    }

(二)项目结构

(三)编写vue-mini.js

bin目录下创建vue-mini.js文件,这个文件会作为 CLI 工具的入口文件。

javascript 复制代码
#!/usr/bin/env node

import { spawn } from "child_process";
import { resolve, dirname } from "path";
import { fileURLToPath } from "url";

// 获取当前文件的路径
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// 获取 mode 参数
const mode = process.argv.includes("--mode")
 ? process.argv[process.argv.indexOf("--mode") + 1]
  : "development";
console.log(`mode: ${mode}`);

// 运行不同的脚本
if (mode === "development") {
  const devScript = resolve(__dirname, "../scripts/development.js");

  const childProcess = spawn("node", [devScript], {
    stdio: "inherit",
    cwd: __dirname,
    shell: false,
  });

  childProcess.on("exit", (code, signal) => {
    if (code!== 0) {
      console.error(`服务启动失败,退出码: ${code}`);
    } else {
      console.log(`服务正常退出`);
    }
  });
} else {
  console.error("无效的模式参数");
}

(四)编写development.js

scripts目录下创建development.js文件,这个文件将负责实际的开发模式下的构建和启动过程。

javascript 复制代码
import { spawn } from "child_process";
import { resolve, dirname } from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const serveScript = resolve(__dirname, "../serve.js");

const childProcess = spawn("node", [serveScript], {
  stdio: "inherit",
  cwd: __dirname,
  shell: false,
});

childProcess.on("exit", (code, signal) => {
  if (code!== 0) {
    console.error(`服务启动失败,退出码: ${code}`);
  } else {
    console.log(`服务正常退出`);
  }
});

(五)编写build.js

javascript 复制代码
import express from "express";
import { dirname } from "path";
import { fileURLToPath } from "url";
import { mkdir, readFileSync, rm, writeFileSync } from "fs";
import { transform } from "esbuild";
import { parse } from "@vue/compiler-sfc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const app = express();

const port = 3000;

if (process.argv.includes("--clean")) {
  rm(__dirname + "/dist", { recursive: true }, (err) => {
    if (err) console.log(err);
  });
}

mkdir(__dirname + "/dist/index", { recursive: true }, (err) => {
  if (err) console.log(err);
});

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/src/index.html");
});

app.get("/*.js", (req, res) => {
  const path = req.path;
  const file = readFileSync(__dirname + "/src" + path, "utf-8");
  writeFileSync(__dirname + "/dist/index.js", file);
});

app.get("/*.css", (req, res) => {
  const path = req.path;
  const file = readFileSync(__dirname + "/src" + path, "utf-8");
  writeFileSync(__dirname + "/dist/index.css", file);
});

app.get("/*.ts", async (req, res) => {
  const path = req.path;
  const file = readFileSync(__dirname + "/src" + path, "utf-8");
  const transformResult = await transform(file, {
    loader: "ts",
    format: "esm",
    target: "es6",
  });
  writeFileSync(__dirname + "/dist/index.js", transformResult.code);
});

app.get("/*.vue", (req, res) => {
  const path = req.path;
  const fileContent = readFileSync(__dirname + "/src" + path, "utf-8");
  const parsed = parse(fileContent);

  if (parsed.descriptor.template) {
    writeFileSync(
      __dirname + "/dist/index/index.wxml",
      parsed.descriptor.template.content
    );
  }

  if (parsed.descriptor.scriptSetup) {
    writeFileSync(
      __dirname + "/dist/index/index.js",
      parsed.descriptor.scriptSetup.content
    );
  }

  if (parsed.descriptor.styles[0].content) {
    writeFileSync(
      __dirname + "/dist/index/index.wxss",
      parsed.descriptor.styles[0].content
    );
  }

  writeFileSync(__dirname + "/dist/index/index.json", "");

  const projectJson = readFileSync(
    __dirname + "/src/project.config.json",
    "utf-8"
  );
  writeFileSync(__dirname + "/dist/project.config.json", projectJson);
});

app.listen(port, () => {
  console.log(`http://localhost:${port}`);
});

(六)安装和测试

在项目根目录下运行以下命令,确保你的 CLI 工具可以运行:

  1. yarn
  2. yarn link

这将创建一个全局链接,允许你直接使用vue-mini命令。现在你可以测试运行以下命令:vue-mini --mode development

得到打包的文件:

相关推荐
玖釉-5 分钟前
解决PowerShell执行策略导致的npm脚本无法运行问题
前端·npm·node.js
Larcher39 分钟前
新手也能学会,100行代码玩AI LOGO
前端·llm·html
徐子颐1 小时前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
小月鸭1 小时前
如何理解HTML语义化
前端·html
jump6801 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang2 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
游戏开发爱好者82 小时前
iOS 上架要求全解析,App Store 审核标准、开发者准备事项与开心上架(Appuploader)跨平台免 Mac 实战指南
android·macos·ios·小程序·uni-app·iphone·webview
yivifu2 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花2 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js