赞
踩
先把仓库地址贴上:dxx-mobile-ui。
上周遗留的问题已解决,现已支持在Vue SFC
中引入外部类型定义。
第一次搭建项目看再多文档都不如参考官方文档:项目搭建。
简单来说就以下三个步骤:
全局安装 vue-cli
pnpm install -g @vue/cli
创建以 typescript 开发的工程
npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project
更新到最新正式版
npx @dcloudio/uvm@latest
项目就创建好了
接下来的步骤在 HBuilderX 里操作比较保险,万一以后它文件目录又变了呢,是吧。
为什么叫
插件化准备
,因为我们要开发一个公用的组件库,让大家通过npm install xxx
就可以使用,那我们就要发布到npm
上面去,那么在uniapp
上我们就要开发插件来实现,最后把插件上传到npm
。插件化开发官方文档参考:https://uniapp.dcloud.net.cn/plugin/uni_modules.html#%E5%BC%80%E5%8F%91-uni-modules-%E6%8F%92%E4%BB%B6
1.打开 HBuilderX
,导入刚才创建的项目
2.右键根目录,选择 新建uni_modules目录(X)
,新建后就会在 src
下生成一个 uni_modules
文件
3.右键 uni_modules
,选择 新建uni_modules插件(H)
,会弹出一个弹窗。
4.填写插件 ID,就相当于我们组件库的名字,我这儿就取名dxx-mobile-ui
(插件 ID 最好唯一,如果你要发布到插件市场的话),点击创建。
5.创建后,会在 uni_modules
下自动生成一个文件,文件下的目录就是这样子。
6.接下来我们就可以在components
下开发自己的组件了。先把compenents
下自动生成的组件改造一下,组件名字请不要乱取,建议规范一下,后面使用的时候好设置easycom
。
代码如下:
<template> <view class="content">{{ title }}, 我是d-test组件 </view> </template> <script lang="ts"> export default { name: "d-test", // 重要!引用组件的名字,必须导出 }; </script> <script setup lang="ts"> import { ref } from "vue"; const title = ref("Hello"); </script> <style> .content { display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 36rpx; color: #8f8f94; } </style>
此时你已经可以在 src/pages/index/index.vue
里调试此组件了(不管你是在vscode
终端输入pnpm run dev:h5
,还是直接从HBuilderX
里点击运行到浏览器
,都可以调试,运行到其他端也可以),只需要在里面写上如下代码即可,无需手动引入。
<template>
<d-test></d-test>
</template>
<script setup lang="ts">
</script>
<style>
</style>
7.但是我们是要发布到npm上,那就需要一个统一的入口,所以我们在src/uni_modules/dxx-mobile-ui
下新建一个index.ts文件,代码如下:
const install = (App: any) => {
Object.values(
import.meta.glob("./components/*/index.ts", { eager: true }) // 动态引入components下的index.ts文件
).forEach((item: any) => {
App.component(item.name, item);
});
};
export default {
install,
};
2.要在src/uni_modules/dxx-mobile-ui/components/d-test
文件下新增一个入口文件index.ts
,以后就按这种规范来开发组件了,代码如下:
import { ref } from "vue";
import type DTest from "./d-test.vue";
const getDTestRef = () => ref<InstanceType<typeof DTest> | null>(null);
export { getDTestRef };
export default { name: "DTest", title: "测试组件" };
到这里我们的准备工作已经做完了,接下来我们就可以在components
下开发我们的组件了。我又在下面添加了一个组件,等会看看是否两个组件都能使用正常。
发布到npm
需要切换镜像,有时候会遇到ETIMEDOUT 443
错误,这需要我们翻墙才行,我的建议是单独开一个终端来代理,终端代理,是单独的,不会影响到其他地方。如果你还没有代理服务器,恐怕需要你自行查阅了,我的有时候会抽风,不知道什么原因。
1.完善 src/uni_modules/dxx-mobile-ui
下的 package.json
,新增这两行:
"name": "dxx-mobile-ui",
"private": false,
如果想了解其他字段什么意思,请查看该文:package.json配置。
2.打开文件src/uni_modules/dxx-mobile-ui
,在这个文件下开一个cmd
终端,我们要把这个插件上传到npm
。
3.切换镜像
npm config set registry https://registry.npmjs.org
4.输入代理服务器,登录npm,接下来会叫你输入npm用户名、npm密码、邮箱,稍后会发一个验证码到你的邮箱,输入验证码就登录成功。(没有npm账号的去注册一个)
set https_proxy=http://127.0.0.1:7890
set http_proxy=http://127.0.0.1:7890
pnpm login
5.发布,接着执行:
pnpm publish
发布成功
然后你就可以去npm
搜到你发的这个包了。接下来就是如何使用的问题了。
另起一个项目,方法一样,安装
pnpm add dxx-mobile-ui
在入口文件src/main.ts
引入
import { createSSRApp } from 'vue'
import App from './App.vue'
import DxxMobileUI from 'dxx-mobile-ui'
export function createApp() {
const app = createSSRApp(App)
app.use(DxxMobileUI)
return {
app,
}
}
在src/pages.json中添加配置,这样就可以不用每次使用都导入了。
"easycom": {
"autoscan": true,
"custom": {
"^d-(.*)": "dxx-mobile-ui/components/d-$1/d-$1"
}
}
然后在页面中直接使用即可
<d-button></d-button>
<d-test></d-test>
效果如下
好了,到这里就基本结束了,但是开发过程中,我们难免会使用 typescript ,有意思的是,vue
并不支持什么复杂类型从外部引入,意思就是你这样写会报错:
你只能在内部写类型,不能从外部引入
const emits = defineEmits<{...}>();
既然有这个问题,就让我们来解决一下吧。
npm config set registry https://registry.npmmirror.com
pnpm add prettier decomment -D
vite.config.ts
文件import { defineConfig } from "vite"; import uni from "@dcloudio/vite-plugin-uni"; import fs from "fs"; import path from "path"; import prettier from "prettier"; import decomment from "decomment"; import type { Plugin } from "vite"; // 支持在Vue SFC中引入外部类型定义 const vueSfcImportType = (): Plugin => { return { name: "vue-sfc-import-type", enforce: "pre", async transform(code, id) { // 不是vue文件 返回 if (!/\.(vue)$/.test(id)) return; const typePath = path.resolve(id, "../type.sfc.ts"); // 不存在type.sfc.ts文件 返回 if (!fs.existsSync(typePath)) return; // 文件内容 let typeFileContent = decomment(fs.readFileSync(typePath).toString()); typeFileContent = await prettier.format(typeFileContent, { semi: true, parser: "typescript", }); // 将文件内容去掉换行符 变成一行 typeFileContent = typeFileContent.replace(/[\r\n]/g, ""); // 替换的正则表达式 const regex = /import .* from "\.\/type\.sfc"/g; // 将导入type.sfc的那一行进行替换 const res = code.replace(regex, typeFileContent); return res; }, }; }; // https://vitejs.dev/config/ export default defineConfig(async ({ command, mode }) => { return { plugins: [uni(), vueSfcImportType()], }; });
此时就不报错了,原理就是将导出的内容变成一行代码,然后与vue
文件中import...type.sfc
的那一行进行替换。
移动端插件化与web端不同,且网上不易搜索,明白了原理你就会发现,其实就是在uni_modules下写插件而已,最后把这个插件发布就行了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。