组件库初始化

使用monorepo管理项目,需要使用yarn和lerna创建管理项目

安装lerna

  1. yarn add lerna -g
  2. lerna init

lerna项目配置 lerna.json

  1. {
  2. "packages": [
  3. "packages/*"
  4. ],
  5. "version": "0.0.0",
  6. "npmClient": "yarn", // 使用yarn管理
  7. "useWorkspaces": true // 使用workspace,需要配置package.json
  8. }

配置项目 package.json

  1. {
  2. "name": "root",
  3. "private": true,
  4. "workspaces": [
  5. "packages/*"
  6. ],
  7. "devDependencies": {
  8. "lerna": "^3.22.1"
  9. }
  10. }
  • 设置private为true:lerna管理的monorepo必须设置
  • 设置workspaces:这样yarn安装依赖时,会把packages下的项目做为依赖进行安装。

    使用lerna创建组件

    1. lerna create button
    2. lerna create icon
    1. ├─packages
    2. ├─button
    3. package.json
    4. README.md
    5. ├─src
    6. | ├─ button.vue
    7. ├─index.ts # 组件入口
    8. └─__tests__ # 测试相关
    9. └─icon
    10. package.json
    11. README.md
    12. ├─src
    13. ├─ icon.vue
    14. ├─index.ts # 组件入口
    15. └─__tests__

    配置ts环境

    1. yarn add typescript -W
    2. npx tsc --init
    1. {
    2. "compilerOptions": {
    3. "target": "ESNext", // 打包的目标语法
    4. "module": "ESNext", // 模块转化后的格式
    5. "esModuleInterop": true, // 支持模块转化
    6. "skipLibCheck": true, // 跳过类库检测
    7. "forceConsistentCasingInFileNames": true, // 强制区分大小写
    8. "moduleResolution": "node", // 模块解析方式
    9. "jsx": "preserve", // 不转化jsx
    10. "declaration": true, // 生成声明文件
    11. "sourceMap": true // 生成映射文件
    12. }
    13. }

    设置 esModuleInterop 属性 可以把commonjs转为esm格式

  1. import fs from 'fs'; // 编译前
  2. let fs = require('fs');
  3. fs.default // 编译后 fsdefault属性,所引引用时会出问题

创建组件 和 展示组件的项目examples

组件初始化

安装vue yarn add vue@next -W

编辑button.vue

  1. <template>
  2. <button>button</button>
  3. </template>
  4. <script lang="ts">
  5. import { defineComponent } from "vue";
  6. export default defineComponent({
  7. name: "MButton",
  8. });
  9. </script>

编辑icon.vue

  1. <template>
  2. <i>icon</i>
  3. </template>
  4. <script lang="ts">
  5. import { defineComponent } from "vue";
  6. export default defineComponent({
  7. name: "MIcon",
  8. });
  9. </script>

入口声明对应的install方法

  1. import Button from "./src/button.vue";
  2. import { App } from "vue";
  3. Button.install = (app: App):void => {
  4. app.component(Button.name, Button)
  5. };
  6. type IWithInstall<T> = T & { install(app: App): void };
  7. const _Button: IWithInstall<typeof Button> = Button;
  8. export default _Button;
  1. import Icon from "./src/icon.vue";
  2. import { App } from "vue";
  3. Icon.install = (app: App):void => {
  4. app.component(Icon.name, Icon)
  5. };
  6. type IWithInstall<T> = T & { install(app: App): void };
  7. const _Icon: IWithInstall<typeof Icon> = Icon;
  8. export default _Icon;

默认无法解析.vue文件后缀的文件,增加typings. typings/vue-shim.d.ts

  1. declare module '*.vue' {
  2. import {App,defineComponent} from 'vue';
  3. const component: ReturnType<typeof defineComponent> & {
  4. install(app:App):void
  5. };
  6. export default component
  7. }

收集整理所有组件

创建ming-ui项目lerna create ming-ui

  1. import { App } from "vue";
  2. import MButton from "@ming-ui/button";
  3. import MIcon from "@ming-ui/icon";
  4. const components = [MButton, MIcon];
  5. const install = (app: App): void => {
  6. components.forEach((component) => {
  7. app.component(component.name, component);
  8. });
  9. };
  10. export default {
  11. install,
  12. };

创建预览组件环境

这里可以使用element-ui的md文件预览,也可以采用vuepress框架,也可以自定义创建项目。

初始化环境

  1. yarn add webpack webpack-cli webpack-dev-server vue-loader@next @vue/compiler-sfc babel-loader @babel/core @babel/preset-env @babel/preset-typescript babel-plugin-module-resolver url-loader file-loader html-webpack-plugin css-loader sass-loader style-loader sass -D -W

设置babel.config.js,解析ts语法

  1. module.exports = {
  2. presets: [
  3. "@babel/preset-env",
  4. "@babel/preset-typescript", // 解析ts语法,在采用preset-env
  5. ],
  6. overrides: [
  7. {
  8. test: /\.vue$/,
  9. plugins: [
  10. "@babel/transform-typescript",
  11. ],
  12. },
  13. ],
  14. env: {
  15. utils: {
  16. plugins: [
  17. [
  18. "babel-plugin-module-resolver", // 为了能正确找到ming-ui模块
  19. { root: "ming-ui" },
  20. ],
  21. ],
  22. },
  23. },
  24. };

设置webpack.config.js,打包编译配置

  1. const path = require("path");
  2. const HtmlWebpackPlugin = require("html-webpack-plugin");
  3. const { VueLoaderPlugin } = require("vue-loader");
  4. module.exports = {
  5. mode: "development",
  6. devtool: "source-map",
  7. entry: path.resolve(__dirname, "../main.ts"),
  8. output: {
  9. path: path.join(__dirname, "../dist"),
  10. filename: "bundle.js",
  11. },
  12. resolve: {
  13. // 表示解析的文件类型
  14. extensions: [".js", ".ts", ".tsx", ".vue"],
  15. },
  16. module: {
  17. rules: [
  18. {
  19. // 识别vue
  20. test: /\.vue$/,
  21. use: "vue-loader",
  22. },
  23. { test: /\.(js|ts)x?$/, exclude: /node_modules/, loader: "babel-loader" },
  24. {
  25. // 识别图标...
  26. test: /\.(svg|otf|ttf|woff|eot|gif|png)$/,
  27. loader: "url-loader",
  28. },
  29. {
  30. // 识别样式
  31. test: /\.(scss|css)$/,
  32. use: ["style-loader", "css-loader", "sass-loader"],
  33. },
  34. ],
  35. },
  36. plugins: [
  37. new VueLoaderPlugin(),
  38. new HtmlWebpackPlugin({
  39. template: path.resolve(__dirname, "../public/index.html"),
  40. }),
  41. ],
  42. };

在package.json添加预览组件的脚本

  1. "scripts": {
  2. "preview-ui": "webpack serve --config ./examples/build/webpack.config.js"
  3. }

执行 yarn run preview-ui可以预览项目

  1. import {createApp} from 'vue';
  2. import MingUI from "ming-ui";
  3. import App from './App.vue'
  4. createApp(App).use(MingUI).mount('#app'); // 入口文件中使用组件即可
  1. <template>
  2. <div>
  3. <m-button></m-button>
  4. <m-icon></m-icon>
  5. </div>
  6. </template>

组件库打包

以上步骤完成了本地创建组件并可以在项目中使用预览,接下来进行打包操作,打包之后发布到npm就可以供其他项目使用。

  • 使用webpack打包umd格式
  • 使用rollup打包esm格式

    打包成umd格式

    在根目录下创建builds/webpack.config.js ```javascript const path = require(“path”); const { VueLoaderPlugin } = require(“vue-loader”); module.exports = { mode: “production”, entry: path.resolve(dirname, “../packages/ming-ui/index.ts”), output: { path: path.join(dirname, “../lib”), filename: “index.js”, libraryTarget: “umd”, // umd支持commonjs和amd ,可以在浏览器运行,但是不支持es6 library: “ming-ui”, }, externals: { vue: {
    1. // 排除掉项目中引入的vue,不让打进ui包中
    2. root: "Vue",
    3. commonjs: "vue",
    4. commonjs2: "vue",
    }, }, resolve: { // 表示解析的文件类型 extensions: [“.js”, “.ts”, “.tsx”, “.vue”], }, module: { rules: [
    1. {
    2. // 识别vue
    3. test: /\.vue$/,
    4. use: "vue-loader",
    5. },
    6. { test: /\.(js|ts)x?$/, exclude: /node_modules/, loader: "babel-loader" },
    ], }, plugins: [new VueLoaderPlugin()], };
  1. <a name="yJ1hy"></a>
  2. ### 添加打包入口脚本
  3. ```javascript
  4. "scripts": {
  5. "build": "webpack --config builds/webpack.config.js"
  6. }

打包成esm格式

安装rollup的依赖包

  1. yarn add rollup rollup-plugin-typescript2 @rollup/plugin-node-resolve rollup-plugin-vue -D -W

全量打包

  1. import typescript from "rollup-plugin-typescript2";
  2. import { nodeResolve } from "@rollup/plugin-node-resolve";
  3. import path from "path";
  4. import vue from "rollup-plugin-vue";
  5. export default {
  6. input: path.resolve(__dirname, `../packages/ming-ui/index.ts`),
  7. output: {
  8. format: "es",
  9. file: `lib/index.esm.js`,
  10. },
  11. plugins: [
  12. nodeResolve(),
  13. vue({
  14. target: "browser",
  15. }),
  16. // 对ts编译时,会生成声明文件
  17. typescript({
  18. tsconfigOverride: {
  19. exclude: ["node_modules", "examples"],
  20. },
  21. }),
  22. ],
  23. external(id) {
  24. // 排除vue本身
  25. return /^vue/.test(id);
  26. },
  27. };
  1. "build:esm-bundle": "rollup -c ./builds/rollup.config.bundle.js"

为了给每个添加组件声明文件,每个组件的index.ts要增加设置

  1. type IWithInstall<T> = T & { install(app: App): void };
  2. const _Button: IWithInstall<typeof Button> = Button;
  3. export default _Button;
  1. type IWithInstall<T> = T & { install(app: App): void };
  2. const _Icon: IWithInstall<typeof Icon> = Icon;
  3. export default _Icon;

分组件打包

  1. import typescript from "rollup-plugin-typescript2";
  2. import { nodeResolve } from "@rollup/plugin-node-resolve";
  3. import path from "path";
  4. import { getPackagesSync } from "@lerna/project";
  5. import vue from "rollup-plugin-vue";
  6. // 获取package.json 找到以@ming-ui 开头的组件包
  7. const inputs = getPackagesSync()
  8. .map((pck) => pck.name)
  9. .filter((name) => name.includes("@ming-ui"));
  10. export default inputs.map((name) => {
  11. const pckName = name.split("@ming-ui")[1];
  12. return {
  13. input: path.resolve(__dirname, `../packages/${pckName}/index.ts`),
  14. output: {
  15. format: "es",
  16. file: `lib/${pckName}/index.js`,
  17. },
  18. plugins: [
  19. nodeResolve(),
  20. vue({
  21. target: "browser",
  22. }),
  23. typescript({
  24. tsconfigOverride: {
  25. compilerOptions: {
  26. // 打包单个组件时,不用生成ts的声明文件,在全局打包时i已经生成声明文件
  27. declaration: false,
  28. },
  29. exclude: ["node_modules"],
  30. },
  31. }),
  32. ],
  33. external(id) {
  34. // 排除vue本身 和自己设置的ming-ui包
  35. return /^vue/.test(id) || /^@ming-ui/.test(id);
  36. },
  37. };
  38. });
  1. "build:esm": "rollup -c ./builds/rollup.config.js"

组件样式打包

scss格式文件编写

创建样式目录

  1. lerna create theme-chalk
  1. ├─src
  2. ├─button.scss
  3. ├─icon.scss
  4. ├─index.scss # 整合所有scss
  5. ├─common
  6. var.scss # 提供scss变量
  7. ├─fonts # 字体
  8. └─mixins
  9. ├─config.scss # 提供名字
  10. └─mixins.scss # 提供mixin方法
  11. ├─gulpfile.js
  12. ├─package.json
  1. $namespace: "m"; // scss命名空间,。都是以m开头
  2. $state-prefix:"is-"; // 表示状态 is-readonly is-disabled
  3. $modifier-separator:"--"; //修饰作用 m-button--primary
  4. $element-separator: "__"; // 元素之间的分割 m-app__body
  1. @import "../mixins/config.scss";
  2. $--color-primary: #409EFF;
  3. $--color-white: #FFFFFF;
  4. $--color-black: #000000;
  5. $--color-success: #67C23A;
  6. $--color-warning: #E6A23C;
  7. $--color-danger: #F56C6C;
  8. $--color-info: #909399;
  1. @import "../common/var.scss";
  2. // .m-button{}
  3. @mixin b($block) {
  4. $B: $namespace+'-'+$block;
  5. .#{$B}{
  6. @content;
  7. }
  8. }
  9. // .m-button.is-xxx
  10. @mixin when($state) {
  11. @at-root {
  12. &.#{$state-prefix + $state} {
  13. @content;
  14. }
  15. }
  16. }
  17. // &--primary => .m-button--primary
  18. @mixin m($modifier) {
  19. @at-root {
  20. #{&+$modifier-separator+$modifier} {
  21. @content;
  22. }
  23. }
  24. }
  25. // &__header => .m-button__header
  26. @mixin e($element) {
  27. @at-root {
  28. #{&+$element-separator+$element} {
  29. @content;
  30. }
  31. }
  32. }

预览组件环境中使用scss文件

  1. import "theme-chalk/src/index.scss"

最终使用打包后的css引入即可,这里为了方便调试,这样引入不需要每次进行重新打包

字体图标iconfont的使用

使用iconfont字体图标,图标库地址
在 资源管理 -> 我的项目 设置
image.png
设置完成,下载到本地
主要使用 iconfont.css 的内容,添加到 theme-chalk/src/icon.scss 中

  1. @import "./common/var.scss";
  2. @font-face {
  3. font-family: "m-ui-icons"; /* Project id 3381025 */
  4. src: url('./fonts/iconfont.woff2?t=1651839500521') format('woff2'),
  5. url('./fonts/iconfont.woff?t=1651839500521') format('woff'),
  6. url('./fonts/iconfont.ttf?t=1651839500521') format('truetype'),
  7. url('./fonts/iconfont.svg?t=1651839500521#m-ui-icons') format('svg');
  8. }
  9. // .m-ui-icons {
  10. [class^="#{$namespace}-icon-"] {
  11. font-family: "m-ui-icons" !important;
  12. font-size: 16px;
  13. font-style: normal;
  14. -webkit-font-smoothing: antialiased;
  15. -moz-osx-font-smoothing: grayscale;
  16. }
  17. @keyframes rotating {
  18. 0% {
  19. transform: rotateZ(0deg);
  20. }
  21. 100% {
  22. transform: rotateZ(360deg);
  23. }
  24. }
  25. .#{$namespace}-icon-loading, .#{$namespace}-icon-exchangerate {
  26. display: inline-block ;
  27. animation: rotating 1.5s linear infinite;
  28. }
  29. // 后续样式省略

然后添加依赖的字体文件,把woff、woff2、ttf、svg文件复制到theme-chalk/src/fonts中

测试添加的图标

  1. <template>
  2. <i :class="`m-icon-${name}`"></i>
  3. </template>
  4. <script lang="ts">
  5. import { defineComponent } from "vue";
  6. export default defineComponent({
  7. name: "MIcon",
  8. props: {
  9. name: {
  10. type: String,
  11. default: "",
  12. },
  13. },
  14. });
  15. </script>

安装gulp及依赖环境

  1. yarn add gulp gulp-autoprefixer gulp-cssmin gulp-dart-sass gulp-rename -D -W

使用gulp打包,添加打包脚本

  1. "build:theme": "gulp build --gulpfile packages/theme-chalk/gulpfile.js"

编译后生成lib

  1. ├── button.css
  2. ├── fonts
  3. ├── iconfont.svg
  4. ├── iconfont.ttf
  5. ├── iconfont.woff
  6. └── iconfont.woff2
  7. ├── icon.css

代码地址