{"id":18850984,"url":"https://github.com/chaimayor/packaging-element-plus","last_synced_at":"2025-09-03T12:32:17.396Z","repository":{"id":142855111,"uuid":"582919105","full_name":"ChaiMayor/Packaging-Element-Plus","owner":"ChaiMayor","description":"对 ElementPlus 组件的二次封装","archived":false,"fork":false,"pushed_at":"2023-01-05T07:48:34.000Z","size":672,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-08T03:38:39.543Z","etag":null,"topics":["elementplus","typescript","vite","vue"],"latest_commit_sha":null,"homepage":"https://chaimayor.github.io/Packaging-Element-Plus-Web","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ChaiMayor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-28T08:29:05.000Z","updated_at":"2024-06-19T09:07:15.000Z","dependencies_parsed_at":"2023-04-09T13:02:42.492Z","dependency_job_id":null,"html_url":"https://github.com/ChaiMayor/Packaging-Element-Plus","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChaiMayor%2FPackaging-Element-Plus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChaiMayor%2FPackaging-Element-Plus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChaiMayor%2FPackaging-Element-Plus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChaiMayor%2FPackaging-Element-Plus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ChaiMayor","download_url":"https://codeload.github.com/ChaiMayor/Packaging-Element-Plus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231882154,"owners_count":18440325,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elementplus","typescript","vite","vue"],"created_at":"2024-11-08T03:32:56.417Z","updated_at":"2024-12-30T15:58:16.201Z","avatar_url":"https://github.com/ChaiMayor.png","language":"TypeScript","readme":"# 一、项目初始化\n\n## 1. 创建项目\n\n项目根目录打开 `cmd`，输入 `npm init vue@latest`\n\n根据配置自行选择即可，需要有的 `ts, router`\n\n\n\n## 2. 安装依赖\n\n~~~shell\n# 组件库\nnpm i element-plus\n# 依赖\nnpm i sass sass-loader -D\n~~~\n\n\n\n# 二、项目开始\n\n## 1. 自定义 icon \n\n下载包：\n\n```shell\n# NPM\n$ npm install @element-plus/icons-vue @wangeditor/editor\n$ npm install lodash @types/lodash    # 使用的内置的 cloneDeep 深拷贝方法\n```\n\n\n\n\n\n# 三、学习到的小妙招\n\n## 1. 动态渲染组件\n\n有一大堆的组件需要渲染，恰好你还拥有他们的组件名称的数组，可以使用 `component` 这个内置组件来进行批量渲染到页面上\n\n使用方法\n\n~~~vue\n\u003ctemplate\u003e\n\t\u003cdiv class=\"el_dialog_container_item\" v-for=\"(item, index) in Object.keys(ElementPlusIconsVue)\" :key=\"index\"\u003e\n  \u003c!-- icon图标 --\u003e\n  \u003cdiv class=\"icon_container\"\u003e\n    \u003c!-- 动态的进行渲染 --\u003e\n    \u003ccomponent :is=\"`el-icon-${toLine(item)}`\"\u003e\u003c/component\u003e\n  \u003c/div\u003e\n  \u003c!-- icon名字 --\u003e\n  \u003cdiv class=\"icon_name\"\u003e\n    {{ item }}\n  \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript setup lang=\"ts\"\u003e\n// 拥有组件名称的数组\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'\n\u003c/script\u003e\n~~~\n\n\n\n## 2. 复制内容到剪贴板\n\n在 `hooks` 文件夹里面封装一个 `useCopy` 方法，用于复制指定内容\n\n~~~js\n// hooks/useCopy.js \nimport { ElMessage } from \"element-plus\";\n\n// 复制文本\nexport const useCopy = (text: string) =\u003e {\n  // 创建输入框\n  const input: HTMLInputElement = document.createElement(\"input\");\n  // 给输入框value赋值\n  input.value = text;\n  // 追加到body里面\n  document.body.appendChild(input);\n  // 选择输入框的操作\n  input.select();\n  // 执行复制操作\n  document.execCommand(\"Copy\");\n  // 删除加入的输入框\n  document.body.removeChild(input);\n  // 提示用户\n  ElMessage.success(\"复制成功\");\n};\n~~~\n\n\n\n## 3. 统一注册和分别注册全局组件\n\n### 1）组件文件格式\n\ncomponents 文件夹下创建：\n\n1. xxxx 组件名：这个组件的名字\n   1. src ：组件的主文件入口\n   2. index.ts ：方便进行分别注册全局组件\n2. index.ts ：方便进行统一全局注册组件\n\n![image-20221224111724286](https://oss.zhishiyu.online/markdown_images/202212241117385.png) \n\n\n\n### 2）分别注册全局组件\n\n将组件的文件主入口进行引入：`mChooseArea`，在这里注册使用全局组件\n\n~~~js\nimport type { App } from \"vue\";\nimport chooseArea from \"./src/index.vue\";\n\nexport default {\n  install(app: App) {\n    app.component(\"m-choose-area\", chooseArea);\n  },\n};\n~~~\n\n\n\n### 3）统一注册全局组件\n\n将所有的组件的注册文件 `index.ts` 进行引入，然后将所有的组件进行遍历注册\n\n~~~js\nimport type { App } from \"vue\";\nimport chooseIcon from \"./chooseIcon/index.ts\";\nimport chooseArea from \"./chooseArea/index.ts\";\n\nconst allComponents = [chooseIcon, chooseArea];\n\nexport default {\n  install(app: App) {\n    allComponents.map((item) =\u003e app.use(item));\n  },\n};\n~~~\n\n\n\n### 4）选择引入方式\n\n`app.use()` 使用的话，会自动执行里面的 `install` 函数\n\n---\n\n全局引入\n\n~~~js\n// main.ts \nimport mUI from \"@/components/index.ts\";   // 引入主文件\n\napp.use(mUI).mount(\"#app\");\n~~~\n\n分别引入\n\n~~~js\n// main.ts \nimport chooseArea from \"@/components/chooseArea/index.ts\";   // 引入组件的主文件\n\napp.use(chooseArea).mount(\"#app\");\n~~~\n\n\n\n## 4. 编码时注意点\n\n### 1）样式穿透\n\n在使用 `sass` 进行样式穿透的时候，使用的是 `::v-deep` 这个 vite 进行了警告说：该方法已经废弃，请使用 `:deep()` 新方法\n\n~~~bash\n[@vue/compiler-sfc] ::v-deep usage as a combinator has been deprecated. Use :deep(\u003cinner-selector\u003e) instead.\n~~~\n\n使用样式穿透的时候，应该使用 `:deep(类名)` 才对\n\n~~~scss\n:deep(.el-button){\n  \n}\n~~~\n\n\n\n### 2）Vue3 的 API 引入\n\nvue3 的 `defineProps、defineEmits` 是内置 API，不需要进行引入\n\n~~~bash\n[@vue/compiler-sfc] `defineEmits` is a compiler macro and no longer needs to be imported.\n~~~\n\n\n\n## 5. 一键传入属性到组件，不需要提前声明\n\n\u003e Vue3 内置一个属性为 `$attrs`，所有没有被提前声明的属性，都会被它所收集\n\n父组件：\n\n~~~vue\n\u003c!-- 父组件给子组件传参 --\u003e\n\u003ctemplate\u003e\n    \u003cm-menu defaultActive=\"1\" background-color=\"#bfa\" background-image=\"url(xxx)\"\u003e\u003c/m-menu\u003e\n\u003c/template\u003e\n~~~\n\n子组件接收：\n\n+ script 里面使用 `$attrs` 则需要先进行引入 `useAttrs` 函数\n+ template 里面则直接使用 `$attrs` 即可\n\n~~~vue\n\u003c!-- 子组件没有声明background类属性 --\u003e\n\u003ctemplate\u003e\n\t\u003c!-- v-bind 会自动将其属性挂载到组件上面 --\u003e\n\t\u003cel-menu :default-active=\"defaultActive\" v-bind=\"$attrs\" class=\"el-menu-vertical-demo\"\u003e\n  \u003c/el-menu\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\nimport { useAttrs } from 'vue'\n  \nlet props = defineProps({\n  // 默认展开的层级\n  defaultActive: {\n    type: String\n  },\n})\n\nconst $attrsInstance = useAttrs()\nconsole.log($attrsInstance)\n\u003c/script\u003e\n~~~\n\n打印出来的信息\n\n![image-20221226144301555](https://oss.zhishiyu.online/markdown_images/202212261443646.png)\n\n\n\n \n\n## 6. 子组件暴露数据给父组件\n\n子组件 `m-table` 使用 `defineExpose` 函数进行暴露数据，该函数不需要引入直接使用\n\n~~~vue\n\u003cscript setup lang=\"ts\"\u003e\ndefineExpose({\n  // 可编辑单元格确定\n  clickCheck,\n  // 可编辑单元格取消\n  clickClose,\n  // 可编辑行的确定\n  confirmEditRow,\n  // 可编辑行的取消\n  cancelEditRow,\n  // 删除行\n  deleteRow,\n  // 当前表格的数据\n  tableDataClone\n})\n\u003c/script\u003e\n~~~\n\n父组件使用时，通过 `ref` 获取到子组件 `m-table` 然后通过 `.value` 获取传递过来的数据\n\n~~~vue\n\u003ctamplate\u003e\n\t\u003cm-table ref=\"table\"\u003e\u003c/m-table\u003e\n\u003c/tamplate\u003e\n\n\u003cscript setup lang=\"ts\"\u003e\n  // 表格的ref\n  const table = ref()\n  \n  // 获取表格数据\n  console.log(table.value.tableDataClone[0].date);\n\u003c/script\u003e\n~~~\n\n\n\n# 四、学习到的新API\n\n## 1. useSlots \n\n\u003e 判断是否使用了 slot 插槽\n\n使用方式：\n\n~~~js\nimport { useSlots } from \"vue\"\n\nconst slots = useSlots()\nconsole.log(slots)\n~~~\n\n没有使用 slot 插槽的内容\n\n![image-20221224130852761](https://oss.zhishiyu.online/markdown_images/202212241308807.png) \n\n---\n\n使用了匿名插槽 `\u003cslot\u003e\u003c/slot\u003e`\n\n![image-20221224131001524](https://oss.zhishiyu.online/markdown_images/202212241310566.png) \n\n---\n\n使用了具名插槽\n\n![image-20221224131101648](https://oss.zhishiyu.online/markdown_images/202212241311690.png) \n\n\n\n## 2. useAttrs\n\n\u003e 用于获取父组件传递的一些没有提前声明的数据\n\n~~~vue\n\u003c!-- 子组件 --\u003e\n\u003cscript\u003e\n\timport { useAttrs } from 'vue'\n  let attrs = useAttrs()\n\n  console.log(attrs);\n\u003c/script\u003e\n~~~\n\n![image-20230103163739437](https://oss.zhishiyu.online/markdown_images/202301031637547.png) \n\n\n\n## 3. scrollIntoView 滚动条跳转指定元素位置\n\n\u003e 参考文档：[详细介绍scrollIntoView（）方法属性_永远的新手的博客-CSDN博客_scrollintoview](https://blog.csdn.net/learn8more/article/details/108047794)\n\n该`scrollIntoView()`方法将调用它的元素滚动到浏览器窗口的可见区域。\n\n语法\n~~~js\nelement.scrollIntoView（）; // 等同于element.scrollIntoView(true)\nelement.scrollIntoView（alignToTop）; //布尔参数\nelement.scrollIntoView（scrollIntoViewOptions）; //对象参数\n~~~\n\n语法参数\n\n![image-20221228115417613](https://oss.zhishiyu.online/markdown_images/202212281154719.png) \n\n示例\n\n~~~js\nvar element = document.getElementById(\"box\");\n \nelement.scrollIntoView();\nelement.scrollIntoView(false);\nelement.scrollIntoView({block: \"end\"});\nelement.scrollIntoView({\n  // 平滑滑动到元素高度\n  behavior: \"smooth\",\n})\nelement.scrollIntoView({behavior: \"instant\", block: \"end\", inline: \"nearest\"});\n~~~\n\n## 应用场景\n\nURL中hash标记的进化\n\n- 聊天窗口滚动显示最新的消息\n- 往一个列表添加item后滚动显示最新的添加的item\n- 回到顶部(#)\n- 滚动到指定位置(#xxx)\n\n\n\n## 4. flat 数组扁平化\n\n\u003e 参考文章：[es6数组的flat()方法_木蓝茶陌*_*的博客-CSDN博客_flat()](https://blog.csdn.net/jyn15159/article/details/121241124)\n\n`flat()` 方法会按照一个可指定的深度递归遍历数组，并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。\n\nArray.prototype.flat() 用于将嵌套的数组“扁平化”，将[二维数组](https://so.csdn.net/so/search?q=二维数组\u0026spm=1001.2101.3001.7020)变成一维数组。该方法返回一个新数组，对原数据没有影响。\n\n`语法`：\n\n```js\nvar newArray = arr.flat([depth])\n```\n\n参数：depth 可选，指定要提取嵌套数组的结构深度，默认值为 1。\n\n返回值：一个包含数组与子数组中所有元素的新数组。\n\n`示例`：\n\n~~~js\nvar arr1 = [\n    [0, 1],\n    [2, 3],\n    [4, 5]\n]\nvar arr2 = arr1.flat()\nconsole.log(arr2)   // [0, 1, 2, 3, 4, 5]\n\nvar arr = [1, 2, [3, 4, [5, 6]]];\nconsole.log(arr.flat());   // [1, 2, 3, 4, [5, 6]]\nconsole.log(arr.flat(2));  // [1, 2, 3, 4, 5, 6]\n~~~\n\n使用 Infinity，可展开任意深度的嵌套数组：\n\n~~~js\nvar arr3 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];\nconsole.log(arr3.flat(Infinity));  // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n~~~\n\n`flat()` 方法会移除数组中的空项：\n\n~~~js\nvar arr4 = [1, 2, , 4, 5];\nconsole.log(arr4.flat());   // [1, 2, 4, 5]\n~~~\n\n\n\n## 5. includes 查询是否存在某个值\n\n\u003e 参考文档：[includes()的使用场景和作用。_hello big_bear的博客-CSDN博客_includes()方法的作用是什么?](https://blog.csdn.net/big_beer/article/details/120023917)\n\n学习\nincludes() 方法用于判断字符串是否包含指定的子字符串，或者判断数组中是否有指定的元素。\n例如：[‘hellow’,‘world’].includes(‘hellow’) ,如果数组存在指定元素就会返回true，没有就返回false。\n\n\n\n问题：\n当在做逻辑判断的时候，发现不同状态下，会执行这个状态对应的操作，如果非常乱的时候，可以使用includes()来判断什么状态下应该执行什么样的操作。\n\n\n\n场景：\n应用场景:发送请求从后端拿到一个数据(状态),根据这个数据的数据(状态)来进行对应的操作.\n\n~~~js\n\u003cscript\u003e\n\t\tvar num = 7;//假设这是一个从后端拿到的数据，表示星期几\n\t\tif([1,3,5].includes(num)){//如果星期一、三、五吃饭堂\n\t\t\tconsole.log('吃饭堂');\n\t\t}\n\t\telse if([2,4,6].includes(num)){//如果星期二、四、六吃外卖\n\t\t\tconsole.log('吃外卖');\n\t\t}\n\t\telse{console.log('吃大餐');}//其他的就是星期日吃大餐\n\u003c/script\u003e\n~~~\n\n总结：\n\n假设 num 是一个状态，根据这个状态来执行不同的操作。上述例子是比较简单的情况，毕竟一周只有七天，但是如果是月呢？如果一个月30天，不同的日子吃什么。这种情况下如果写逻辑判断||的话就要写很多，而使用 includes 可以写出更加简洁的代码（优雅）\n\n\n\n\n\n# 五、声明类型\n\n## 1. Vue3 的 defineProps 使用声明的自定义类型\n\n使用 `vue` 提供的 `PropType` 类型来使用 ts 自定义的类型\n\n\u003e PropType 语法：\n\u003e\n\u003e 原生类型 as PropType\u003c自定义类型\u003e\n\n~~~js\n// 获取声明类型\nimport type { PropType } from 'vue'\nimport type {\n  ListOptions,\n  ActionOptions,\n} from \"../type/menu\";\n\nconst props = defineProps({\n  // 传递的列表数据\n  list: {\n    type: Array as PropType\u003cListOptions[]\u003e,\n    required: true, // 必填的\n  },\n  // 操作内容\n  actions: {\n    type: Array as PropType\u003cActionOptions[]\u003e,\n    default: () =\u003e []\t// 默认的空数组\n  }\n})\n~~~\n\n\n\n## 2. 自定义字段范围\n\n让一个属性只能输入规定范围内的几个值\n\n~~~js\nexport interface ListItem {\n\t...,\n  // 标签类型\n  tagType?: \"\" | \"success\" | \"warning\" | \"info\" | \"danger\";\n}\n\n~~~\n\n\u003e tagType 允许的值，仅有 \"\" | \"success\" | \"warning\" | \"info\" | \"danger\" 可以输入，其他值都是不合法的\n\n\n\n## 3. TS 的类型声明 - 替代 defineProps 做类型声明\n\n### 1）普通声明\n\n~~~js\n// 定义类型\nexport interface ItemType {\n  menuTitle: string,\n  index: number,\n  content: ListItem\n}\n\nconst props = defineProps\u003c{\n  name: string,\n  title: string,\n  isDot: boolean,\n  item: ItemType[]\n}\u003e()\n~~~\n\n\n\n### 2）默认值\n\n~~~js\n// withDefaults 第二个参数接收配置，可以设置默认值\nwithDefaults(defineProps\u003c{\n\ttitle:string,\n  arr:number[]\n}\u003e(),{\n  arr: ()=\u003e [666]\t\t// 默认arr数组内容 \n})\n~~~\n\n\n\n\n\n## 4. 如何修改数组对象的属性名（把key替换成想要的key，值不变）\n\n\u003e 参考文档：[如何修改数组对象的属性名（把key替换成想要的key，值不变）_小太阳...的博客-CSDN博客_把数组中对象的key 换成 另一个字段](https://blog.csdn.net/weixin_46074961/article/details/107025470)\n\n场景： 比如后端返回给一个数组对象，但是名字不是我们想要的，我们可以修改key值变成我们想要的数组。\n比如把下面的第一个数组中key值的名称改成第二个数组中key值的名称\n\n~~~js\narr: [ { name: '小太阳', year: 18}, { name: '大太阳', year: 19}] \n\nbrr: [ { userName: '小太阳', age: 18}, { userName: '大太阳', age: 19} ]\n~~~\n\n不使用 ts 方法：\n\n~~~js\nchangeKey (arr, key) {\n  let newArr = [];\n  arr.forEach((item, index) =\u003e {\n    let newObj = {};\n    for (var i = 0; i \u003c key.length; i++) {\n      newObj[key[i]] = item[Object.keys(item)[i]]\n    }\n    newArr.push(newObj);\n  })\n  console.log(newArr)\n  return newArr;\n}\n \nlet brr= changeKey (arr, ['userName', 'age']);   //name 换 userName，year 换 age\n~~~\n\n使用 ts 方法（不明觉厉版）：\n\n~~~js\n// 替换对象中的key为其他的名字\ninterface ob {\n  [key: string]: string\n}\nconst changeKey = (arr: Array\u003cob\u003e, key: string[]): ob[] =\u003e {\n  let newArr: Array\u003cob\u003e = [];\n  arr.forEach((item) =\u003e {\n    let newObj: ob = {};\n    for (var i = 0; i \u003c key.length; i++) {\n      newObj[key[i]] = item[Object.keys(item)[i]]\n    }\n    newArr.push(newObj);\n  })\n  return newArr;\n}\n~~~\n\n使用 ts 方法（Shit Mount）：\n\n~~~js\n// 替换对象中的key为其他的名字\nconst changeKey = (arr: any[], key: string[]) =\u003e {\n  let newArr: any[] = [];\n  arr.forEach((item) =\u003e {\n    let newObj: any = {};\n    for (var i = 0; i \u003c key.length; i++) {\n      newObj[key[i]] = item[Object.keys(item)[i]]\n    }\n    newArr.push(newObj);\n  })\n  return newArr;\n}\n~~~\n\n\n\n# 六、项目上线\n\n## 1. vite 打包全部组件\n\n### 1）command\n\n在根目录新建 `command` 文件夹，新建文件 `build.js` 编写 vite 和 node 代码，这段不了解，所以直接复制了\n\n~~~js\nconst path = require(\"path\");\nconst fsExtra = require(\"fs-extra\");\nconst fs = require(\"fs\");\nconst { defineConfig, build } = require(\"vite\");\nconst vue = require(\"@vitejs/plugin-vue\");\nconst vueJsx = require(\"@vitejs/plugin-vue-jsx\");\n\nconst entryDir = path.resolve(__dirname, \"../packages\");\n// const entryDir = path.resolve(__dirname, '../components')\nconst outputDir = path.resolve(__dirname, \"../m-ui\");\n\nconst baseConfig = defineConfig({\n  configFile: false,\n  publicDir: false,\n  plugins: [vue(), vueJsx()],\n});\n\nconst rollupOptions = {\n  external: [\"vue\", \"vue-router\"],\n  output: {\n    globals: {\n      vue: \"Vue\",\n    },\n  },\n};\n\n//全量构建\nconst buildAll = async () =\u003e {\n  await build(\n    defineConfig({\n      ...baseConfig,\n      build: {\n        rollupOptions,\n        lib: {\n          entry: path.resolve(entryDir, \"index.ts\"),\n          name: \"index\",\n          fileName: \"index\",\n          formats: [\"es\", \"umd\"],\n        },\n        outDir: outputDir,\n      },\n    })\n  );\n};\n\nconst buildSingle = async (name) =\u003e {\n  await build(\n    defineConfig({\n      ...baseConfig,\n      build: {\n        rollupOptions,\n        lib: {\n          entry: path.resolve(entryDir, name),\n          name: \"index\",\n          fileName: \"index\",\n          formats: [\"es\", \"umd\"],\n        },\n        outDir: path.resolve(outputDir, name),\n      },\n    })\n  );\n};\n\n// 生成组件的 package.json 文件\nconst createPackageJson = (name) =\u003e {\n  const fileStr = `{\n  \"name\": \"${name}\",\n  \"version\": \"0.0.0\",\n  \"main\": \"index.umd.js\",\n  \"module\": \"index.es.js\",\n  \"style\": \"style.css\"\n}`;\n\n  fsExtra.outputFile(\n    path.resolve(outputDir, `${name}/package.json`),\n    fileStr,\n    \"utf-8\"\n  );\n};\n\nconst buildLib = async () =\u003e {\n  await buildAll();\n  // 获取组件名称组成的数组\n  const components = fs.readdirSync(entryDir).filter((name) =\u003e {\n    const componentDir = path.resolve(entryDir, name);\n    const isDir = fs.lstatSync(componentDir).isDirectory();\n    return isDir \u0026\u0026 fs.readdirSync(componentDir).includes(\"index.ts\");\n  });\n\n  // 循环一个一个组件构建\n  for (const name of components) {\n    // 构建单组件\n    await buildSingle(name);\n\n    // 生成组件的 package.json 文件\n    createPackageJson(name);\n  }\n};\n\nbuildLib();\n\n~~~\n\n\n\n### 2）packages\n\n在根目录新建 `packages` 文件夹，把 `src/components` 内容全部复制到该文件夹下，再把 `components` 组件里面使用到的 `hooks 和 utils` 方法复制到该文件夹下\n\n如果有 `css` 文件的话，也需要复制到该文件夹下，然后在 `index.ts` 里面将 `css` 代码进行引入\n\n![image-20230104154919404](https://oss.zhishiyu.online/markdown_images/202301041549557.png) \n\n并把 `packages` 文件夹里面的所有文件的引入路径，从 `@/xxx` 全部转换为 `../../` 转换为正确的路径，因为文件结构发生了变化\n\n还需要在此新建一个文件 `vue.d.ts` 文件，写如下内容：\n\n~~~js\ndeclare module \"*.vue\" {\n  import { DefineComponent } from \"vue\";\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types\n  const component: DefineComponent\u003c{}, {}, any\u003e;\n  export default component;\n}\n~~~\n\n ![image-20230104154742612](https://oss.zhishiyu.online/markdown_images/202301041547712.png) \n\n\n\n### 3）package.json 配置脚本命令\n\n下载 `npm i fs-extra -D` 包\n\n~~~json\n\"scripts\": {\n  \"dev\": \"vite\",\n  \"build\": \"run-p type-check build-only\",\n  \"preview\": \"vite preview\",\n  // 新增内容\n  \"build:components\": \"node ./command/build.js\",\n  \"lib\": \"npm run build:components\"\n},\n~~~\n\n执行 `npm run lib` 打包组件，获得如下内容\n\n![image-20230104155651117](https://oss.zhishiyu.online/markdown_images/202301041556197.png) \n\n\n\n### 4）引入全局组件\n\n在 `src/main.ts` 里面输入如下内容：\n\n~~~js\n/**\n * 全局引入打包组件\n */\nimport mUI from \"../m-ui/index.mjs\";\nimport \"../m-ui/style.css\";\n\napp.use(mUI).mount(\"#app\")\n~~~\n\n\n\n### 5）单独引入组件\n\n在 `src/main.ts` 里面输入如下内容：\n\n~~~js\n/**\n * 单一引入某一打包组件\n */\nimport mForm from \"../m-ui/form/index.mjs\";\nimport \"../m-ui/form/style.css\";\n\napp.use(mForm).mount(\"#app\")\n~~~\n\n\n\n## 2. 发布组件库到 npm \n\n### 1）新建 package.json 配置\n\n在 `m-ui` 目录下，新建 `package.json` 文件，配置如下内容：\n\n~~~js\n{\n  \"name\": \"xiaochai-element-plus-packaging\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.umd.js\",\n  \"module\": \"index.mjs\",\n  \"types\": \"index.d.ts\",\n  \"author\": {\n    \"name\": \"xiaochai\"\n  },\n  \"keywords\": [\n    \"element-plus\",\n    \"ts\",\n    \"封装组件\",\n    \"二次封装\",\n    \"vue-components\"\n  ]\n}\n~~~\n\n~~~js\n{\n  \"name\": \"xiaochai-element-plus-packaging\",\n  \"version\": \"1.0.0\",\t\n  \"main\": \"index.umd.js\",\t\t// 主入口\n  \"module\": \"index.mjs\",\t\t// 模块\n  \"types\": \"index.d.ts\",\t\t// 告诉别人我们是vue的一个插件\n  \"author\": {\n    \"name\": \"xiaochai\"\t\n  },\n  \"keywords\": [\t\t\t// 别人通过这些关键字可以搜到这个包\n    \"element-plus\",\n    \"ts\",\n    \"封装组件\",\n    \"二次封装\",\n    \"vue-components\"\n  ]\n}\n~~~\n\n\n\n### 2）注册 npm 账号\n\n1. 访问 https://www.npmjs.com/ 网站，点击 `sign up` 按钮，进入注册用户界面\n2. 填写账号相关的信息：Full Name、Public Email、Username、Password\n3. 点击 Create an Account 按钮，注册账号\n4. 登录邮箱，点击验证链接，进行账号的验证\n\n\n\n### 3）登录 npm 账号\n\nnpm 账号注册完成后，可以在终端中执行 `npm login` 命令，依次输入用户名、密码、邮箱后，即可登录成功\n\n![image-20220823105652285](https://oss.zhishiyu.online/markdown_images/202301051127721.png) \n\n注意：\n\n+ 在运行 `npm login` 命令之前，必须先把`下包的服务器地址`切换为 npm 的官方服务器，否则登录会失败\n\n  ~~~bash\n  # 切换到 npm 官方服务器\n  nrm use npm\n  \n  # nrm 不存在的话先下载到全局\n  npm i nrm -g\n  \n  # 登录完成后记得切换回去淘宝镜像\n  nrm use taobao\n  ~~~\n\n+ 并且输入密码时，会看不到输入的内容，这里做了遮挡的操作，只要输进去密码了就行\n\n+ 输入完邮箱后，会给邮箱发一个验证码，将验证码输入到 `Enter one-time password` 即可\n\n\n\n### 4）把包发布到 npm 上\n\n将终端切换到`包的根目录`之后，运行 `npm publish` 命令，即可将包发布到 npm 上（注意：`包名不能雷同`）\n\n发布完成后就可以，[itachai-tools - npm (npmjs.com)](https://www.npmjs.com/package/itachai-tools) npm 上搜索到自己发布的包了\n\n![image-20220823110423279](https://oss.zhishiyu.online/markdown_images/202301051127725.png) \n\n\n\n### 5）删除已发布的包\n\n运行 `npm unpublish 包名 --force` 命令，即可从 npm 删除已发布的包\n\n![image-20220823111143183](https://oss.zhishiyu.online/markdown_images/202301051128316.png) \n\n注意：\n\n+ npm unpublish 命令只能删除 `72小时以内` 发布的包\n+ npm unpublish 删除的包，在 `24小时内` 不允许重复发布\n+ 发布包的时候要慎重，`尽量不要往npm上发布没有意义的包`！\n\n\n\n### 6）测试发布的包\n\n使用 vite 快速创建一个项目：\n\n~~~bash\nnpm create vite@latest my-vue-app -- --template vue-ts\n~~~\n\n安装依赖：\n\n~~~bash\n# ElementPlus 和 icon图标包\nnpm install element-plus @element-plus/icons-vue\n\n# 安装自己发布的包\nnpm i xiaochai-element-plus-packaging\n~~~\n\n\u003e 此时注意\n\u003e\n\u003e 1. 如果你使用的是 npm 官方源，那么可以正常下载，因为已经发布到 npm 官方源上面了\n\u003e 2. 如果你使用的是 taobao 镜像源，那么暂时还不能下载，因为此时 taobao 还没有及时克隆最新的 npm 官方源\n\n![image-20230105114616015](https://oss.zhishiyu.online/markdown_images/202301051146078.png) \n\n注册全局资源部包：\n\n~~~js\nimport { createApp } from \"vue\";\nimport \"./style.css\";\nimport App from \"./App.vue\";\nconst app = createApp(App);\n\n// ElementPlus 包\nimport ElementPlus from \"element-plus\";\nimport \"element-plus/dist/index.css\";\n\n// 注册ElementPlus全局icon包\nimport * as ElementPlusIconsVue from \"@element-plus/icons-vue\";\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n\tapp.component(key, component);\n}\n\n// 引入自己的组件包，并且全局注册\nimport mUI from \"xiaochai-element-plus-packaging\";\nimport \"xiaochai-element-plus-packaging/style.css\";\n\n// 注册自己的全局 icon 组件\nimport { toLine } from \"xiaochai-element-plus-packaging/utils/index\";\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n\tapp.component(`el-icon-${toLine(key)}`, component);\n}\n\n// 挂载自己的组件和ElementPlus\napp.use(ElementPlus).use(mUI).mount(\"#app\");\n\n~~~\n\n单一注册自己的组件包：\n\n~~~js\n// 引入自己的组件包\nimport chooseIcon from \"xiaochai-element-plus-packaging/chooseIcon/index\";\n// 有的组件有css，有的组件没有css，看情况引入\nimport \"xiaochai-element-plus-packaging/chooseIcon/style.css\";\n\n// 挂载自己的组件和ElementPlus\napp.use(ElementPlus).use(chooseIcon).mount(\"#app\");\n~~~\n\n\n\n### 7）更新发布的组件包\n\n将 `package.json 和 index.d.ts` 将内容复制出来，放到根目录一个叫 `demo` 的文件夹\n\n![image-20230105124443645](https://oss.zhishiyu.online/markdown_images/202301051244705.png) \n\n更新 `packages/index.ts` 文件，添加如下内容：\n\n~~~js\nimport { toLine } from \"./utils\";\n// 注册ElementPlus全局icon包\nimport * as ElementPlusIconsVue from \"@element-plus/icons-vue\";\n\nexport default {\n  install(app: App) {\n    // 注册自己的全局 icon 组件\n    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n      app.component(`el-icon-${toLine(key)}`, component);\n    }\n    allComponents.map((item) =\u003e app.use(item));\n  },\n};\n~~~\n\n更新 `command/build.js` 脚本文件：\n\n~~~js\n// 生成组件的 package.json 文件\n// 生成 index.d.ts 告诉用户，我们这个组件库是一个vue插件\nconst createPackageJson = (name) =\u003e {\n  const fileStr = `{\n  \"name\": \"${name}\",\n  \"version\": \"0.0.0\",\n  \"main\": \"index.umd.js\",\n  \"module\": \"index.es.js\",\n  \"style\": \"style.css\"\n}`;\n\n  const indexDTs = `\n  import { App } from \"vue\";\n  declare const _default: {\n    install(app: App): void;\n  };\n  export default _default;\n  `;\n\n  fsExtra.outputFile(\n    path.resolve(outputDir, `${name}/package.json`),\n    fileStr,\n    \"utf-8\"\n  );\n  fsExtra.outputFile(\n    path.resolve(outputDir, `${name}/index.d.ts`),\n    indexDTs,\n    \"utf-8\"\n  );\n};\n~~~\n\n执行 `npm run lib` 重新运行脚本文件，再把 `demo/index.d.ts和package.json` 放到 `m-ui` 目录下\n\n![image-20230105125012526](https://oss.zhishiyu.online/markdown_images/202301051250588.png) \n\n最后将版本号更新一下即可\n\n![image-20230105125022198](https://oss.zhishiyu.online/markdown_images/202301051250260.png) \n\n再次切换到 `m-ui` 终端目录下，执行 `npm run lib` 命令，更新发布的 `npm` 包\n\n\n\n### 8）再次测试更新的组件包\n\n重新下载组件包 `npm i xiaochai-element-plus-packaging` \n\n在 `main.ts` 里面重新引入文件：\n\n~~~js\nimport { createApp } from \"vue\";\nimport \"./style.css\";\nimport App from \"./App.vue\";\nconst app = createApp(App);\n\n// ElementPlus 包\nimport ElementPlus from \"element-plus\";\nimport \"element-plus/dist/index.css\";\n\n// 引入自己的组件包\nimport mUI from \"xiaochai-element-plus-packaging\";\nimport \"xiaochai-element-plus-packaging/style.css\";\n\n// 引入单一组件包\n// import chooseIcon from \"xiaochai-element-plus-packaging/chooseIcon/index\";\n// import \"xiaochai-element-plus-packaging/chooseIcon/style.css\";\n\n// 挂载自己的组件和ElementPlus\napp.use(ElementPlus).use(mUI).mount(\"#app\");\n~~~\n\n这次优化了引入文件数量，改到了组件包里面自动引入\n\n\n\n## 3. 部署组件的静态网站到 GitHub 或 Gitee\n\n### 1）修改 vue 文件配置\n\n修改路由配置从 `history` 变成 `hash` 路由\n\n~~~js\nimport {\n  createRouter,\n  createWebHistory,\n  createWebHashHistory,\n} from \"vue-router\";\n\nconst router = createRouter({\n  // 改成 hash 路由\n  history: createWebHashHistory(import.meta.env.BASE_URL),\n})\n\nexport default router;\n~~~\n\n在修改 线上 地址的 `base ` 基本路径\n\n~~~js\n// https://vitejs.dev/config/\nexport default defineConfig({\n\t...,\n  base: \"./\",\n});\n~~~\n\n构建项目 `build run npm`，打包成 `dist` 文件夹\n\n\u003e 如果命令运行失败，[2. 构建项目出错](#2. 构建项目出错) ，跳转这个链接查看\n\n\n\n### 2）发布到 Github \n\n#### A. 发布到已有的代码仓库的一个分支上面\n\n将打包出来的 `dist` 文件夹复制到桌面上\n\n在 `dist` 文件夹下新开一个终端，依次执行如下命令：\n\n~~~bash\n# 初始化 git\ngit init\n\n# 对所有文件进行暂存\ngit add .\n\n# 进行一次提交 注意是双引号，不要写单引号\ngit commit -m \"完成了二次组件代码的打包\"\n~~~\n\n将仓库源切换到已有的代码仓库：\n\n~~~bash\n# 将当前项目的仓库修改为对应的仓库\ngit remote add origin https://github.com/ChaiMayor/Packaging-ElementUI\n~~~\n\n\u003e 仓库地址获取方法：直接打开对应仓库，然后复制地址栏地址即可\n\n ![image-20230105134738472](https://oss.zhishiyu.online/markdown_images/202301051347557.png) \n\n---\n\n\n\n现在新建一个分支：\n\n~~~bash\n# 创建一个新的分支，并指向它\ngit checkout -b dist \n~~~\n\n然后推送将代码推送到远程仓库：\n\n~~~bash\n# 推送到远程仓库，-u 如果分支名不存在的话，才写\ngit push -u origin dist\n~~~\n\n这个时候查看 `GitHub仓库` 就会发现提交成功\n\n![image-20230105135542099](https://oss.zhishiyu.online/markdown_images/202301051355214.png)\n\n---\n\n\n\n在仓库里面找到 `Settings` 设置，在左侧栏中找到 `Pages`，接着在右侧找到 `Branch` 然后选择 `dist` 分支，带你级 `Save` 保存即可\n\n![image-20230105140018776](https://oss.zhishiyu.online/markdown_images/202301051400854.png) \n\n这里时候上方就会有 `蓝色的地址了`，可以通过这个地址访问 `演示项目` 了\n\n![image-20230105140204339](https://oss.zhishiyu.online/markdown_images/202301051402413.png) \n\n\n\n## 4. 先快速启动一个服务，测试打包文件是否可以正常访问\n\n使用 `http-server` 快速打开一个服务\n\n在 `dist` 文件件下，运行执行命令：`http-server -p 端口号` 快速启动一个服务\n\n![image-20230105142017567](https://oss.zhishiyu.online/markdown_images/202301051420654.png) \n\n\n\n\n\n# 七、遇到的 bug 和 错误\n\n## 1. main.ts 里面引入 App 文件飘红\n\n需要在根目录下的 `env.d.ts` 加上如下代码：\n\n~~~js\n/// \u003creference types=\"vite/client\" /\u003e\n\ndeclare module \"*.vue\" {\n  import { DefineComponent } from \"vue\";\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types\n  const component: DefineComponent\u003c{}, {}, any\u003e;\n  export default component;\n}\n~~~\n\n![image-20230105132816516](https://oss.zhishiyu.online/markdown_images/202301051328611.png) \n\n\n\n## 2. 构建项目出错\n\n![image-20230105132930851](https://oss.zhishiyu.online/markdown_images/202301051329909.png) \n\n在 `package.json` 里面将 `build` 后面的脚本改一下\n\n~~~js\n  \"scripts\": {\n    \"dev\": \"vite\",\n    // \"build\": \"run-p type-check build-only\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\n    \"build:components\": \"node ./command/build.js\",\n    \"lib\": \"npm run build:components\"\n  },\n~~~\n\n修改为：\n\n~~~js\n \"build\": \"vite build\"\n~~~\n\n\n\n## 3. GitHub 部署页面失败\n\n打开页面时，资源加载失败\n\n![image-20230105152651974](https://oss.zhishiyu.online/markdown_images/202301051526096.png) \n\n原因：\n\n1. 没有配置线上地址的 base 基本路径\n2. 没有使用 hash 路由（像这种带有前缀的路径一定要使用 hash 路由）\n\n解决：\n\n1. 在 `vite.config,ts` 里面配置线上的基本路径\n\n   ![image-20230105152914685](https://oss.zhishiyu.online/markdown_images/202301051529782.png) \n\n2. 变更路由为 `hash` 路由\n\n   ![image-20230105152956643](https://oss.zhishiyu.online/markdown_images/202301051529737.png) \n\n\u003e 参考文档：\n\u003e\n\u003e [共享配置 | Vite 官方中文文档 (vitejs.dev)](https://cn.vitejs.dev/config/shared-options.html#base)\n\u003e\n\u003e [构建生产版本 | Vite 官方中文文档 (vitejs.dev)](https://cn.vitejs.dev/guide/build.html#browser-compatibility)\n\n\n\n\n\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchaimayor%2Fpackaging-element-plus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchaimayor%2Fpackaging-element-plus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchaimayor%2Fpackaging-element-plus/lists"}