最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue3+Typescript 搭建PC端组件库

    正文概述 掘金(小天师)   2021-03-26   1404

    前言


    本项目技术栈为vue3+typescript,PC端组件库。 项目gitee地址

    本文将从环境搭建开始,一步步完成组件库的打包工作。组件库内组件不定期更新。

    1. 代码管理方式

    组件库采用monorepo的代码管理方式。

    1)概念

    monorepo 是管理项目代码的一种方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个repo。

    2)优劣势

    monorepo最主要的优势是统一的工作流Code Sharing。一套代码管理多个package包的构建和发布。劣势是如果每个package都有自己的独立的依赖,频繁的install包,node_modules会变得很大。

    2. monorepo的解决方案

    本项目使用lernayarnworkspaces特性,来管理monorepo

    项目中lerna.json声明packages后,lerna为项目提供统一的依赖安装 (lerna bootstrap)统一的执行package scripts(lerna run)统一的npm发版 (lerna publish)等特性。

    使用yarn作为包管理器,在package.json中用workspaces字段声明packagesyarn就会以monorepo的方式管理packages

    相比lernayarn突出的是对依赖的管理,包括packages的相互依赖、packages对第三方的依赖,yarn会以semver约定来分析dependencies的版本,安装依赖时更快、占用体积更小;但欠缺了统一工作流方面的实现。

    大多monorepo即会使用lerna又会在package.json声明workspaces。这样无论你的包管理器是npm还是yarn,都能发挥monorepo的优势;要是包管理是yarnlerna就会把依赖安装交给yarn处理。怎么交给yarn处理呢?配置lerna.jsonnpmClient字段为yarn

    关于monorepo更多的了解,请看好文Monorepo-大型项目代码管理方式。本文关于monorepo的介绍都取自该文。

    一、环境搭建


    1. lerna安装

    全局安装lerna,注意node版本要在v10.4以上。

    npm install lerna -g
    

    2. 生成lerna.json和package.json文件

    lerna init
    
    // lerna.json
    {
      "packages": [
        "packages/*"  // 表示管理packages下的所有模块
      ],
      "npmClient": "yarn", // 依赖包的安装交给yarn
      "useWorkspaces": true, // 是否使用空间,lerna本身不使用workspace,yarn要使用。
      "version": "0.0.0"
    }
    
    // package.json
    {
      "name": "root",
      "private": true,
      "workspaces": [ // yarn管理packages下所有模块
        "packages/*"
      ],
      "devDependencies": {
        "lerna": "^4.0.0"
      }
    }
    
    // 安装本地依赖 lerna
    yarn install
    

    3. vue@next安装

    yarn add vue@next -W  # -W忽略提示,默认安装到根目录全局使用
    

    编写vue声明文件

    // typings/vue-shim.d.ts
    
    declare module '*.vue' {
        // 取defineComponent的返回值,标识组件类型
        import { defineComponent, App } from 'vue';
        const component: ReturnType<typeof defineComponent> & { install(app: App): void }
        // 导出组件类型
        export default component;
    }
    

    4. typescript安装,tsconfig.json生成

    yarn add typescript -W
    npx tsc --init             # 生成tsconfg.json配置文件
    
    // tsconfig.json
    {
      "compilerOptions": {
        "target": "ESNext",                         # 打包的目标语法
        "module": "ESNext",                         # 模块转化后的格式
        "esModuleInterop": true, # 支持模块转化 import fs from 'fs'; 编译前 let fs = require('fs'); fs.default编译后,fs无default属性,所引引用时会出问题
        "skipLibCheck": true,                       # 跳过类库检测
        "forceConsistentCasingInFileNames": true,   # 强制区分大小写
        "moduleResolution": "node",                 # 模块解析方式
        "jsx": "preserve",                          # 不转化jsx
        "declaration": true,                        # 生成声明文件
        "sourceMap": true                           # 生成映射文件
      }
    }
    

    5. 初始化组件

    lerna create button
    
    ├─button
    │  │  package.json
    │  │  README.md
    │  ├─src
    |  ├─button.vue # 组件实现
    │  ├─index.ts   # 组件入口
    │  └─__tests__  # 测试相关
    
    <template>
      <button>按钮</button>
    </template>
    <script lang="ts">
    import { defineComponent } from "vue";
    export default defineComponent({
      name: "WButton"
    });
    </script>
    
    // 入口 index.ts 对应的install方法
    
    import { App, defineComponent, createApp } from 'vue';
    import Button from './src/button.vue'; // .vue后缀的类型声明在 vue-shim.d.ts中。定义了.vue后缀的组件的类型
    
    Button.install = (app: App): void => {
        // 全局注册组件
        console.log('button组件注册全局');
        app.component(Button.name, Button);
    }
    
    type IWithInstall = ReturnType<typeof defineComponent> & { install(app: App): void };
    
    let _Button: IWithInstall = Button;
    export default _Button;
    
    // createApp({}).use(_Button); // use调用,需要组件提供install方法
    

    6. 整合所有组件

    lerna create will-ui
    
    // will-ui/index.ts
    
    import { App } from 'vue';
    import Button from '@will-ui/button';
    const components = [
        Button
    ];
    
    const install = (app: App): void => {
        components.forEach(component => {
            // 遍历组件,挂载到全局
            app.component(component.name, component);
        })
    }
    
    export default {
        install // 导出install方法。createApp({}).use() 需要install方法
    }
    

    7. theme-chalk样式管理

    样式遵循BEM样式规范。

    lerna create theme-chalk
    
    // BEM规范
    $namespace: 'w'; // 命名空间
    $state-prefix: 'is-'; // 状态
    $modifier-separator: '--'; // 元素修饰作用 
    $element-separator: '__'; // 元素直接的分割
    
    // 示例:
    <div class="w-xxx">
            <button class="w-button--primary"></button>
            <button class="is-readonly is-disabled"></button>
        <div class="w-xxx__header"></div>
        <div class="w-xxx__body"></div>
        <div class="w-xxx__footer"></div>
    </div>
    

    yarn installnode_modules下生成包的软链。

    二、搭建文档


    1. 安装webpack及编译打包依赖

    yarn add webpack webpack-cli webpack-dev-server vue-loader@next @vue/compiler-sfc -D -W
    
    webpack-cli        # webpack命令行解析工具
    webpack-dev-server # 启动静态服务
    vue-loader         # 处理.vue文件,解析vue模板
    @vue/compiler-sfc  # 解析vue模板
    
    yarn add babel-loader @babel/core @babel/preset-env @babel/preset-typescript @babel/plugin-transform-typescript babel-plugin-module-resolver url-loader file-loader html-webpack-plugin css-loader sass-loader style-loader sass -D -W
    
    babel-loader                              # babel解析js语法
    @babel/core                               # babel-loader默认会调babel-core(babel核心包)
    @babel/preset-env                         # 将高级语法转成低级预发
    @babel/preset-typescript                  # babel转化解析ts
    @babel/plugin-transform-typescript        # .vue文件中使用ts,对ts代码进行转化
    babel-plugin-module-resolver              # babel模块解析插件
    url-loader                                # url-loader解析文件资源(编译成base64)
    file-loader                               # file-loader解析文件资源(生成真实文件)
    html-webpack-plugin                       # 调用html的插件
    css-loader sass-loader style-loader sass  # 处理css样式
    
    // babel.config.js
    
    module.exports = {
        presets: [ // babel解析的预设,是反着执行的
            '@babel/preset-env',
            '@babel/preset-typescript'
        ],
        overrides: [{ // .vue文件中使用了ts,对ts代码进行转化
            test: /\.vue$/,
            plugins: [
                '@babel/transform-typescript',
            ]
        }]
    }
    
    // webpack.config.js
    
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const path = require('path');
    const { VueLoaderPlugin } = require('vue-loader');
    
    module.exports = {
        mode: 'development',
        devtool: 'source-map',
        entry: path.resolve(__dirname, 'main.ts'),
        output: {
            path: path.resolve(__dirname, '../website-dist'),
            filename: 'bundle.js'
        },
        resolve: {
            extensions: ['.ts', '.tsx', '.js', '.vue']
        },
        module: {
            rules: [
                { test: /\.(ts|js)x?$/, exclude: /node_modules/, loader: 'babel-loader' },
                { test: /\.vue$/, loader: 'vue-loader' },
                { test: /\.(svg|otf|ttf|woff|woff2|eot|gif|png)$/, loader: 'url-loader' },
                {
                    test: /\.(scss|css)$/,
                    use: [
                        'style-loader',
                        'css-loader',
                        'sass-loader'
                    ]
                }
            ]
        },
        plugins: [
            new VueLoaderPlugin(), // vue-loader 解析.vue文件
            new HtmlWebpackPlugin({
                template: path.resolve(__dirname, 'template.html')
            })
        ]
    }
    
    // package.json
    "scripts": {
        "website-dev": "webpack serve --config ./website/webpack.config.js"
    }
    
    // main.ts
    mport { createApp } from 'vue';
    import App from './App.vue';
    
    // 引入组件库
    import WillUI from 'will-ui'; // 开发阶段
    import 'theme-chalk/src/index.scss'; // 开发阶段引入样式
    // 创建应用并使用组件库
    createApp(App).use(WillUI).mount('#app');
    

    配置好website后,执行npm run website-dev,启动本地服务,之后看效果就在这儿了。

    三、组件库打包


    1. 打包umd格式组件库

    使用webpack打包成umd格式。

    // builds/webpack.config.js
    const path = require('path');
    const { VueLoaderPlugin } = require('vue-loader');
    
    module.exports = {
        mode: 'production',
        entry: path.resolve(__dirname, '../packages/will-ui/index.ts'),
        output: {
            path: path.resolve(__dirname, '../lib'),
            filename: 'index.js',
            libraryTarget: 'umd', // umd打包格式,支持commonjs和amd,不支持es6,可以在浏览器直接使用。
            library: 'will-ui' // 全局名称
        },
        externals: { // 打包忽略vue,防止把vue源码打进去
            vue: {
                root: 'Vue', // 根下的Vue
                commonjs: 'vue', // commonjs规范的(import {xxx} from 'vue')
                commonjs2: 'vue'
            }
        },
        resolve: {
            extensions: ['.ts', '.tsx', '.js', '.vue']
        },
        module: {
            rules: [
                { test: /\.(ts|js)x?$/, exclude: /node_modules/, loader: 'babel-loader' },
                { test: /\.vue$/, loader: 'vue-loader' }
            ]
        },
        plugins: [
            new VueLoaderPlugin() // vue-loader 解析.vue文件
        ]
    }
    
    // package.json
    
    "scripts": {
        "website-dev": "webpack serve --config ./website/webpack.config.js",
        "build": "webpack --config builds/webpack.config.js",
      },
    

    2. 全量打包esModel格式组件库

    使用rollup打包esmodel格式。

    yarn add rollup rollup-plugin-typescript2 @rollup/plugin-node-resolve rollup-plugin-vue -D -W
    
    @rollup/plugin-node-resolve # 解析第三方模块
    rollup-plugin-vue           # 支持vue
    
    // builds/rollup.config.bundle.js
    
    // 全量打包
    import typescript from 'rollup-plugin-typescript2';
    import { nodeResolve } from '@rollup/plugin-node-resolve';
    import path from 'path';
    import vue from 'rollup-plugin-vue'
    
    export default {
        input: path.resolve(__dirname, `../packages/will-ui/index.ts`),
        output: {
            format: 'es',
            file: `lib/index.esm.js`,
        },
        plugins: [
            nodeResolve(), // 支持第三方模块,vue,typescript
            vue({
                target: 'browser'
            }),
            typescript({ // 默认调用tsconfig.json  帮我们生成声明文件
                tsconfigOverride: {
                    exclude: [
                        'node_modules',
                        'website'
                    ]
                }
            })
        ],
        external(id) { // 排除vue本身
            return /^vue/.test(id)
        }
    }
    
    // package.json
    
    "scripts": {
        "website-dev": "webpack serve --config ./website/webpack.config.js",
        "build:esm-bundle": "rollup -c ./builds/rollup.config.bundle.js"
      },
    

    3. 组件单独打包esModel格式组件库(按需加载)

    // rollup.config.js
    
    import typescript from 'rollup-plugin-typescript2';
    import { nodeResolve } from '@rollup/plugin-node-resolve';
    import path from 'path';
    import { getPackagesSync } from '@lerna/project';
    import vue from 'rollup-plugin-vue'
    
    // 获取package.json,找到以 @will-ui 开头的
    const inputs = getPackagesSync().map(pck => pck.name).filter(name => name.includes('@will-ui'));
    
    export default inputs.map(name => {
        const pckName = name.split('@will-ui')[1] // button icon
        return {
            input: path.resolve(__dirname, `../packages/${pckName}/index.ts`),
            output: {
                format: 'es',
                file: `lib/${pckName}/index.js`,
            },
            plugins: [
                nodeResolve(),
                vue({
                    target: 'browser'
                }),
                typescript({
                    tsconfigOverride: {
                        compilerOptions: { // 打包单个组件的时候不生成ts声明文件
                            declaration: false,
                        },
                        exclude: [
                            'node_modules'
                        ]
                    }
                })
            ],
            external(id) { // 对vue本身 和 自己写的包 都排除掉不打包
                return /^vue/.test(id) || /^@will-ui/.test(id)
            },
        }
    })
    
    "scripts": {
        "website-dev": "webpack serve --config ./website/webpack.config.js",
        "build:esm": "rollup -c ./builds/rollup.config.js"
      },
    

    4. 组件样式单独打包

    yarn add gulp gulp-autoprefixer gulp-cssmin gulp-dart-sass gulp-rename -D -W
    
    gulp-autoprefixer   # 加前缀
    gulp-cssmin         # 压缩css
    gulp-dart-sass      # 处理sass
    gulp-rename         # 重命名
    
    // gulp.config.js
    const { series, src, dest } = require('gulp')
    const sass = require('gulp-dart-sass')
    const autoprefixer = require('gulp-autoprefixer')
    const cssmin = require('gulp-cssmin')
    
    
    function compile() { // 处理scss文件
        return src('./src/*.scss')
            .pipe(sass.sync())
            .pipe(autoprefixer({}))
            .pipe(cssmin())
            .pipe(dest('./lib'))
    }
    function copyfont() { // 拷贝字体样式
        return src('./src/fonts/**').pipe(cssmin()).pipe(dest('./lib/fonts'))
    }
    
    exports.build = series(compile, copyfont);
    

    四、组件编写

    稍微复杂一点的组件,会有流程图,帮助理解,简单组件大家看代码就懂了。

    持续更新中。。。

    1. checkbox、checkbox-group

    checkbox: Vue3+Typescript 搭建PC端组件库

    checkbox-group: Vue3+Typescript 搭建PC端组件库

    参考资料


    • Element-plus
    • Monorepo-大型项目代码管理方式
    • lerna官网
    • Workspaces | Yarn

    起源地下载网 » Vue3+Typescript 搭建PC端组件库

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元