(catalog协议) == pnpm (5)

目录协议 (catalog:)

一旦在 pnpm-workspace.yaml 中定义了目录,

pnpm-workspace.yaml

复制代码
packages:
  - packages/*

# Define a catalog of version ranges.
catalog:
  react: ^18.3.1
  redux: ^5.0.1

可以使用 catalog: 协议代替版本范围本身。

packages/example-app/package.json

复制代码
{
  "name": "@example/app",
  "dependencies": {
    "react": "catalog:",
    "redux": "catalog:"
  }
}

这相当于直接编写版本范围(例如 ^18.3.1)。

packages/example-app/package.json

复制代码
{
  "name": "@example/app",
  "dependencies": {
    "react": "^18.3.1",
    "redux": "^5.0.1"
  }
}

工作区(即 monorepo 或多包存储库)中,许多包通常使用相同的依赖。目录在编写 package.json 文件时减少了重复,并提供了一些好处:

  • 维护唯一版本 --- 通常希望工作区中只有一个版本的依赖。目录使其更易于维护。重复的依赖可能会在运行时发生冲突并导致错误。使用打包器时,重复项也会增加大小。

  • 更容易升级 - 升级依赖时,只需要编辑 pnpm-workspace.yaml 中的目录条目,而不是使用该依赖的所有 package.json 文件。这节省了时间 - 只需要更改一行,而不是很多行。

  • 更少的合并冲突 --- 由于在升级依赖时不需要编辑 package.json 文件,因此这些文件中不再发生 git 合并冲突。

可以通过 catalog:default 引用这些版本范围。仅对于默认目录,也可以使用特殊的 catalog: 简写。将 catalog: 视为扩展为 catalog:default 的简写。

复制代码
# pnpm-workspace.yaml

packages:
  - "packages/*"
  - "apps/*"

catalog:
  "@dcloudio/uni-app": "3.0.0-4080720251210001"
  "@dcloudio/uni-app-harmony": "3.0.0-4080720251210001"
  "@dcloudio/uni-app-plus": "3.0.0-4080720251210001"
  "@dcloudio/uni-components": "3.0.0-4080720251210001"
  "@dcloudio/uni-h5": "3.0.0-4080720251210001"
  "@dcloudio/uni-mp-alipay": "3.0.0-4080720251210001"
  "@dcloudio/uni-mp-baidu": "3.0.0-4080720251210001"
  "@dcloudio/uni-mp-harmony": "3.0.0-4080720251210001"
  "@dcloudio/uni-mp-jd": "3.0.0-4080720251210001"
  "@dcloudio/uni-mp-kuaishou": "3.0.0-4080720251210001"
  "@dcloudio/uni-mp-lark": "3.0.0-4080720251210001"
  "@dcloudio/uni-mp-qq": "3.0.0-4080720251210001"
  "@dcloudio/uni-mp-toutiao": "3.0.0-4080720251210001"
  "@dcloudio/uni-mp-weixin": "3.0.0-4080720251210001"
  "@dcloudio/uni-mp-xhs": "3.0.0-4080720251210001"
  "@dcloudio/uni-quickapp-webview": "3.0.0-4080720251210001"
  "vue": "3.4.21"
  "vue-i18n": "^9.1.9"
  "@dcloudio/types": "3.4.19"
  "@dcloudio/uni-automator": "3.0.0-4080720251210001"
  "@dcloudio/uni-cli-shared": "3.0.0-4080720251210001"
  "@dcloudio/uni-stacktracey": "3.0.0-4080720251210001"
  "@dcloudio/vite-plugin-uni": "3.0.0-4080720251210001"
  "@vue/runtime-core": "3.4.21"
  "vite": "5.2.8"

  "sass": "1.77.6"
  "sass-loader": "^10.1.1"
  "@dcloudio/uni-ui": "^1.5.10"

  "dependencies": {
    "@dcloudio/uni-app": "catalog:",
    "@dcloudio/uni-app-harmony": "catalog:",
    "@dcloudio/uni-app-plus": "catalog:",
    "@dcloudio/uni-components": "catalog:",
    "@dcloudio/uni-h5": "catalog:",
    "@dcloudio/uni-mp-alipay": "catalog:",
    "@dcloudio/uni-mp-baidu": "catalog:",
    "@dcloudio/uni-mp-harmony": "catalog:",
    "@dcloudio/uni-mp-jd": "catalog:",
    "@dcloudio/uni-mp-kuaishou": "catalog:",
    "@dcloudio/uni-mp-lark": "catalog:",
    "@dcloudio/uni-mp-qq": "catalog:",
    "@dcloudio/uni-mp-toutiao": "catalog:",
    "@dcloudio/uni-mp-weixin": "catalog:",
    "@dcloudio/uni-mp-xhs": "catalog:",
    "@dcloudio/uni-quickapp-webview": "catalog:",
    "vue": "catalog:",
    "vue-i18n": "catalog:",
    "@dcloudio/uni-ui": "catalog:",
    "monorepo-utils": "workspace:*"
  },
  "devDependencies": {
    "@dcloudio/types": "catalog:",
    "@dcloudio/uni-automator": "catalog:",
    "@dcloudio/uni-cli-shared": "catalog:",
    "@dcloudio/uni-stacktracey": "catalog:",
    "@dcloudio/vite-plugin-uni": "catalog:",
    "@vue/runtime-core": "catalog:",
    "vite": "catalog:",
    "sass": "catalog:",
    "sass-loader": "catalog:"
  }

uni-ui

vue-cli 项目中可以使用 npm 安装 uni-ui 库 ,或者直接在 HBuilderX 项目中使用 npm

注意 cli 项目默认是不编译 node_modules 下的组件的,导致条件编译等功能失效 ,导致组件异常 需要在根目录创建 vue.config.js 文件 ,增加 @dcloudio/uni-ui 包的编译即可正常

复制代码
// vue.config.js
module.exports = {
		transpileDependencies:['@dcloudio/uni-ui']
}

准备 sass

vue-cli 项目请先安装 sass 及 sass-loader,如在 HBuliderX 中使用,可跳过此步。

  • 安装 sass

    npm i sass -D 或 yarn add sass -D

  • 安装 sass-loader

    npm i sass-loader@10.1.1 -D 或 yarn add sass-loader@10.1.1 -D

如果 node 版本小于 16 ,sass-loader 请使用低于 @11.0.0 的版本,sass-loader@11.0.0 不支持 vue@2.6.12 如果 node 版本大于 16 , sass-loader 建议使用 v8.x 版本

安装 uni-ui

复制代码
npm i @dcloudio/uni-ui   或   yarn add @dcloudio/uni-ui

配置easycom

使用 npm 安装好 uni-ui 之后,需要配置 easycom 规则,让 npm 安装的组件支持 easycom

打开项目根目录下的 pages.json 并添加 easycom 节点:

复制代码
// pages.json
{
	"easycom": {
		"autoscan": true,
		"custom": {
			// uni-ui 规则如下配置
			"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
		}
	},
	
	// 其他内容
	pages:[
		// ...
	]
}

template 中使用组件:

复制代码
<uni-badge text="1"></uni-badge>
<uni-badge text="2" type="success" @click="bindClick"></uni-badge>
<uni-badge text="3" type="primary" :inverted="true"></uni-badge>

注意

  • uni-ui 现在只推荐使用 easycom ,如自己引用组件,可能会出现组件找不到的问题

  • 使用 npm 安装的组件,默认情况下 babel-loader 会忽略所有 node_modules 中的文件 ,导致条件编译失效,需要通过配置 vue.config.js 解决:

    复制代码
    // 在根目录创建 vue.config.js 文件,并配置如下
    module.exports = {
       transpileDependencies: ['@dcloudio/uni-ui']
    }
    // 如果是 vue3 + vite, 无需添加配置
  • uni-ui 是uni-app内置组件的扩展。注意与web开发不同,uni-ui不包括基础组件,它是基础组件的补充。web开发中有的开发者习惯用一个ui库完成所有开发,但在uni-app体系中,推荐开发者首先使用性能更高的基础组件,然后按需引入必要的扩展组件。

  • uni-ui 不支持使用 Vue.use() 的方式安装

引入自定义组件

repo-countdown 组件

pnpm init -y

复制代码
{
  "name": "monorepo-ui",
  "version": "1.0.0",
  "main": "src/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "exports": {
    ".": "./src/index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": ""
}

repo-countdown 组件

复制代码
<template>
  <!-- 验证码倒计时按钮 -->
  <button
    class="sms-btn"
    :disabled="isDisabled"
    :hover-class="hover ? 'button-hover' : 'none'"
    @click="handleClick"
    :style="[buttonStyle]"
  >
    {{ buttonText }}
  </button>
</template>

<script>
/**
 * 验证码倒计时按钮组件
 * @description 用于发送短信验证码的倒计时按钮,支持自定义样式和倒计时时间
 * @property {String} text - 按钮默认文本
 * @property {String} sendText - 发送中的按钮文本
 * @property {String} countdownText - 倒计时文本后缀
 * @property {Number} seconds - 倒计时秒数
 * @property {String} width - 按钮宽度
 * @property {String} height - 按钮高度
 * @property {String} padding - 按钮内边距
 * @property {String} margin - 按钮外边距
 * @property {String} radius - 按钮圆角
 * @property {Number} size - 字体大小
 * @property {String} color - 字体颜色
 * @property {String} background - 背景颜色
 * @property {String} borderWidth - 边框宽度
 * @property {String} borderColor - 边框颜色
 * @property {Boolean} isOpacity - 倒计时状态是否透明
 * @property {Boolean} hover - 是否有点击效果
 * @property {Number/String} successVal - 触发倒计时的值,值变化时开始倒计时
 * @property {Number/String} resetVal - 重置倒计时的值,值变化时重置倒计时
 * @property {Boolean} start - 是否自动开始倒计时
 * @property {Number/String} params - 传递给事件的参数
 * @event {Function} send - 点击发送按钮时触发
 * @event {Function} countdown - 倒计时变化时触发
 * @event {Function} end - 倒计时结束时触发
 */
export default {
  name: "SmsCountdownButton",
  /**
   * Vue3 现在提供了一个emits选项,类似于现有props选项。此选项可用于定义组件可以向其父对象发出的事件
   强烈建议使用emits记录每个组件发出的所有事件。
   这一点特别重要,因为去除了.native修饰符。emits 现在在未使用声明的事件的所有侦听器都将包含在组件的中$attrs,默认情况下,该侦听器将绑定到组件的根节点。
   */
  emits: ["countdown", "send", "end"], // 显式声明自定义事件
  props: {
    text: { type: String, default: "发送验证码" }, // 按钮默认文本
    sendText: { type: String, default: "请稍候..." }, // 发送中的按钮文本
    countdownText: { type: String, default: "s后获取" }, // 倒计时文本后缀
    seconds: { type: Number, default: 60 }, // 倒计时秒数
    width: { type: String, default: "182rpx" }, // 按钮宽度
    height: { type: String, default: "56rpx" }, // 按钮高度
    padding: { type: String, default: "0" }, // 按钮内边距
    margin: { type: String, default: "0" }, // 按钮外边距
    radius: { type: String, default: "6rpx" }, // 按钮圆角
    size: { type: Number, default: 24 }, // 字体大小
    color: { type: String, default: "#5677fc" }, // 字体颜色
    background: { type: String, default: "transparent" }, // 背景颜色
    borderWidth: { type: String, default: "1px" }, // 边框宽度
    borderColor: { type: String, default: "#5677fc" }, // 边框颜色
    isOpacity: { type: Boolean, default: true }, // 倒计时状态是否透明
    hover: { type: Boolean, default: true }, // 是否有点击效果
    successVal: { type: [Number, String], default: 0 }, // 触发倒计时的值
    resetVal: { type: [Number, String], default: 0 }, // 重置倒计时的值
    start: { type: Boolean, default: false }, // 是否自动开始倒计时
    params: { type: [Number, String], default: 0 }, // 传递给事件的参数
    disabledColor: { type: String, default: "" } // 禁用状态的字体颜色
  },
  data() {
    return {
      state: "idle", // 按钮状态:idle(空闲)、pending(发送中)、countdown(倒计时)
      remaining: this.seconds, // 剩余秒数
      timer: null // 定时器
    }
  },
  computed: {
    /**
     * 按钮是否禁用
     * @return {Boolean} 非空闲状态时禁用按钮
     */
    isDisabled() {
      return this.state !== "idle"
    },
    /**
     * 按钮文本
     * @return {String} 根据状态返回不同的按钮文本
     */
    buttonText() {
      // 空闲状态
      if (this.state === "idle") {
        return this.text
        // 发送状态
      } else if (this.state === "pending") {
        return this.sendText
        // 倒计时状态
      } else if (this.state === "countdown") {
        return `${this.remaining}${this.countdownText}`
      }
      return ""
    },
    /**
     * 按钮样式
     * @return {Object} 样式对象
     */
    buttonStyle() {
      const style = {
        width: this.width,
        height: this.height,
        padding: this.padding,
        margin: this.margin,
        color: this.color,
        background: this.background,
        borderWidth: this.borderWidth,
        borderColor: this.borderColor,
        borderRadius: this.radius,
        fontSize: this.size + "rpx",
        borderStyle: "solid",
        display: "flex",
        alignItems: "center",
        justifyContent: "center"
      }
      // 倒计时状态且需要透明时设置透明度
      if (this.state === "countdown" && this.isOpacity) {
        style.opacity = 0.5
      }

      // 倒计时状态且需要禁用时设置背景颜色
      if (this.disabledColor && this.state === "countdown") {
        style.background = this.disabledColor
      }
      return style
    }
  },
  watch: {
    /**
     * 监听成功值变化,触发倒计时
     */
    successVal(newVal, oldVal) {
      if (newVal !== oldVal) {
        this.success()
      }
    },
    /**
     * 监听重置值变化,重置倒计时
     */
    resetVal(newVal, oldVal) {
      if (newVal !== oldVal) {
        this.reset()
      }
    }
  },
  mounted() {
    // 如果设置了自动开始,则立即开始倒计时
    if (this.start) {
      this.success()
    }
  },
  // 在 Vue3 中组件卸载的生命周期被重新命名  destroyed 修改为 unmounted
  // #ifdef VUE2
  beforeUnmount() {
    // 组件销毁前清除定时器
    if (this.timer) {
      clearInterval(this.timer)
      this.timer = null
    }
  },
  // #endif
  // #ifdef VUE3
  unmounted() {
    // 组件销毁前清除定时器
    if (this.timer) {
      clearInterval(this.timer)
      this.timer = null
    }
  },
  // #endif
  methods: {
    /**
     * 开始倒计时
     */
    startCountdown() {
      // 清除可能存在的定时器
      if (this.timer) {
        clearInterval(this.timer)
      }
      // 设置状态为倒计时
      this.state = "countdown"
      this.remaining = this.seconds
      // 触发倒计时事件 {因为倒计时事件是每秒触发一次,最开始要触发一次}
      this.$emit("countdown", { seconds: this.remaining, params: this.params })
      // 设置定时器
      this.timer = setInterval(() => {
        // 倒计时
        this.remaining--
        if (this.remaining > 0) {
          // 每秒触发倒计时事件
          this.$emit("countdown", {
            seconds: this.remaining,
            params: this.params
          })
        } else {
          // 倒计时结束,清除定时器
          clearInterval(this.timer)
          this.timer = null
          // 设置状态为空闲
          this.state = "idle"
          // 触发结束事件
          this.$emit("end", { params: this.params })
        }
      }, 1000)
    },
    /**
     * 成功发送验证码,开始倒计时
     */
    success() {
      // 如果按钮状态不为倒计时,则开始倒计时 [空闲状态|发送中状态都可以进入]
      // 自动开始时是空闲,手动点击时是发送中
      if (this.state !== "countdown") {
        this.startCountdown()
      }
    },
    /**
     * 重置按钮状态
     */
    reset() {
      // 清除定时器
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
      }
      // 重置状态
      this.state = "idle"
      // 重置剩余秒数
      this.remaining = this.seconds
    },
    /**
     * 按钮点击处理
     */
    handleClick() {
      // 如果按钮状态为空闲,则设置状态为发送中,并触发发送事件
      if (this.state === "idle") {
        // 设置状态为发送中
        this.state = "pending"
        // 触发发送事件
        this.$emit("send", { params: this.params })
      }
    }
  }
}
</script>

<style scoped>
/* 按钮基本样式 */
.sms-btn {
  display: inline-block; /* 内联块级元素 */
  text-align: center; /* 文本居中 */
  cursor: pointer; /* 鼠标样式 */
}
/* 禁用状态样式 */
.sms-btn:disabled {
  cursor: not-allowed; /* 禁用状态的鼠标样式 */
}
.button-hover {
  transform: scale(0.98); /* 按钮悬停时的缩放 */
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); /* 按钮悬停时的阴影 */
}
</style>

index.js

复制代码
import RepoCountdown from "./components/repo-countdown/repo-countdown.vue"
export { RepoCountdown }

加入组件

复制代码
pnpm --filter app add monorepo-ui --workspace

使用组件

复制代码
<template>
  <view class="content">
    <image class="logo" @click="handleClick" src="/static/logo.png"></image>
    <view class="text-area">
      <text class="title">{{ title }}</text>
      <uni-badge text="1"></uni-badge>
      <uni-badge text="2" type="success"></uni-badge>
      <uni-badge text="3" type="primary" :inverted="true"></uni-badge>
      <repo-countdown></repo-countdown>
    </view>
  </view>
</template>

<script>
import { modal } from "monorepo-utils/extendApi"
import { RepoCountdown } from "monorepo-ui"

export default {
  components: {
    RepoCountdown
  },
  data() {
    return {
      title: "Hello"
    }
  },
  onLoad() {},
  methods: {
    handleClick() {
      modal({
        title: "提示",
        content: "您确定执行该操作吗?"
      })
    }
  }
}
</script>

<style>
.content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.logo {
  height: 200rpx;
  width: 200rpx;
  margin-top: 200rpx;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 50rpx;
}

.text-area {
  display: flex;
  justify-content: center;
}

.title {
  font-size: 36rpx;
  color: #8f8f94;
}
</style>

自动引入

添加全部导出

复制代码
  "exports": {
    ".": "./src/index.js",
    "./*": "./src/*"
  },

添加自动引入

复制代码
  "easycom": {
    "autoscan": true,
    "custom": {
      "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
      "^repo-(.*)": "monorepo-ui/components/repo-$1/repo-$1.vue"
    }
  }
相关推荐
JarvanMo2 小时前
深度解析:如何彻底终结 Flutter 异步操作中的 BuildContext 崩溃?
前端
wxr06162 小时前
部署Spring Boot项目+mysql并允许前端本地访问
前端·spring boot·mysql·持续部署
假装我不帅2 小时前
jquery-validation使用
前端·javascript·jquery
怕浪猫2 小时前
React从入门到出门第六章 事件代理机制与原生事件协同
前端·javascript·react.js
天府之绝2 小时前
uniapp 中使用uview表单验证时,自定义扩展的表单,在改变时无法触发表单验证处理;
开发语言·前端·javascript·vue.js·uni-app
be or not to be2 小时前
Html-CSS动画
前端·css·html
初恋叫萱萱2 小时前
技术基石与职场进阶:构建从Web后端到高性能架构的完整知识图谱
前端·架构·知识图谱
木木木一2 小时前
Rust学习记录--C9 错误处理
前端·学习·rust
局外人LZ2 小时前
libsodium.js:web端与 Node.js 的现代加密工具集,构建前端安全加密体系
前端·javascript·node.js