<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      從零搭建基于 Vue 3.x + ElementPlus 的組件庫(kù)

      前言

      由于最近在我司開發(fā)中開啟了Vue3的重構(gòu)工作。于是乎,Vue組件庫(kù)的抽離工作開啟,此次算是基于 vue3.x + element-plus 的二次封裝,封裝常見通用組件。一年前做了 react+antd的組件庫(kù) 的整合及示例,一年后再看該方案還有諸多缺陷,發(fā)現(xiàn)了很多新的東西可以用,在本次的Vue組件庫(kù)技術(shù)選型中調(diào)研了更多的方式方法,最終選型不代表是最好的,但一定是現(xiàn)階段我認(rèn)為最合適該場(chǎng)景的技術(shù)選型,實(shí)際上寫組件庫(kù)并不復(fù)雜,技術(shù)調(diào)研和技術(shù)選型的整個(gè)流程下來(lái),算是對(duì)各種方法方法及其優(yōu)缺點(diǎn)有了一個(gè)稍微寬泛的認(rèn)識(shí),本文目的是記錄下本次選型到最終成品的全過程?;A(chǔ)配置不在贅述,文章只大致記錄大致的開發(fā)框架以及打包和單測(cè),僅供參考。
      不想看文章只想看代碼的點(diǎn)這里 想先看下交互文檔效果的點(diǎn)這里

      目的

      為什么要做組件庫(kù)?這個(gè)問題也是老生常談了,PC端后臺(tái)xx管理系統(tǒng)這種場(chǎng)景下,通用的東西很容易抽象出來(lái),這樣就不需要每開一個(gè)新項(xiàng)目就在項(xiàng)目里寫一套基礎(chǔ)組件了,在可直接使用基礎(chǔ)組件的基礎(chǔ)上開發(fā)能省不少時(shí)間,本次只對(duì)集成度和通用度較高的幾個(gè)組件進(jìn)行抽離出幾個(gè) npm 包,先實(shí)現(xiàn)常見通用功能,后續(xù)擴(kuò)展則可以在不改變?cè)瓉?lái)架構(gòu)的基礎(chǔ)上進(jìn)行添加功能。這些組件包括通用的布局組件、通用的表格組件、通用的表單組件、通用的文件圖片上傳組件等。

      尤其是表格和表單組件,直接在頁(yè)面中使用的話會(huì)出現(xiàn)很多重復(fù)代碼,寫起來(lái)代碼冗余多,所以一般項(xiàng)目里就會(huì)封裝這些基礎(chǔ)組件來(lái)實(shí)現(xiàn)通用,想要實(shí)現(xiàn)的目的就是只需要通過不同的配置就能在頁(yè)面中通用,一般這些其實(shí)都大同小異,這也是想要開發(fā)這樣基礎(chǔ)組件庫(kù)的初衷,不必要在每個(gè)項(xiàng)目里都寫一套,直接將組建庫(kù)丟到 npm 管理已經(jīng)后續(xù)升級(jí),這樣一來(lái)就能慢慢的沉淀出公司自己特有的基礎(chǔ)和業(yè)務(wù)組件庫(kù),后續(xù)新項(xiàng)目開箱即用。

      開始之前

      開始之前,除了定好技術(shù)棧 Vue 3.x + ElementPlus 外,需要明確組件庫(kù)的開發(fā)原則:簡(jiǎn)潔、高效、靈活、可擴(kuò)展。
      首先要有可讀性好的文檔庫(kù),有示例可交互;其次能自動(dòng)化的重復(fù)工作絕不手動(dòng)復(fù)制,文檔庫(kù)自動(dòng)化部署;最后,最好還有組件測(cè)試來(lái)保證組件的正確性和完成性。

      項(xiàng)目結(jié)構(gòu)

      ├── docs                                     /* 組件庫(kù)文檔 */
      │   .vuepress                                  /* vuepress 配置 */
      │   ├── clientAppEnhance.ts         /* 注冊(cè)全局組件 */
      │   ├── config.ts                         /* vuepress配置文件 */
      │   index.md                                   /* 文檔 */
      └── packages                              /* 包 */
      │   ├── layout                             /* 布局組件 */
      │   │   ├── src                           /* vue組件 */
      │   │   ├── package.json             /* 組件配置文件 */
      │   │   ├── typings                     /* 組件聲明文件 */
      │   ├── form                               /* 表單組件 */
      │   ├── table                              /* 表格組件 */
      ├── templates                             /* plop 配置clone的模板文件夾 */
      ├── typings                                /* 聲明文件夾 */
      ├── .eslintrc.js                            /* eslint 配置 */
      ├── .gitignore                             /* gitignore 配置 */
      ├── .prettierrc                            /* prettier 配置 */
      ├── .stylelintrc                            /* stylelint 配置 */
      ├── babel.config.js                      /* babel 配置 */
      ├── jest.config.js                        /* jest 配置 */
      ├── LICENSE                             /* license */
      ├── package.json                       /* package.json */
      ├── plopfile.js                            /* plop 配置 */
      ├── tsconfig.json                       /* ts 配置 */
      ├── rollup.config.js                    /* rollup 打包配置 */
      └── README.md                      /* 文檔說(shuō)明文件 */
      

      包管理模式

      由于是組件庫(kù),多個(gè)組件包會(huì)有共用的依賴,為減少重復(fù)代碼,因此選用 lerna + yarn workspace 來(lái)進(jìn)行包管理,這也是現(xiàn)如今大多數(shù)組件庫(kù)的選擇。

      組件打包

      組件打包選用 rollup,因?yàn)楸敬蔚慕M件是針對(duì)幾個(gè)通用場(chǎng)景來(lái)封裝組件,打算分開包來(lái)進(jìn)行管理,rollup 打包能打包多種模式的包 esm, cjs, umd 等等,并且esm自帶 tree-shaking,打出來(lái)的包語(yǔ)義明確,也比較易于調(diào)試。
      rollup 打包配置文件放到了最外層,對(duì)組件的打包進(jìn)行統(tǒng)一配置

      下面的配置有幾個(gè)關(guān)鍵點(diǎn):

      1. 多入口,每個(gè)組件分開打包,并且分別打包出 umd 格式的 index.js 文件以及 esm 格式的 index.module.js 文件
      2. babel 配置的時(shí)候需要手動(dòng)添加 .ts 和 .vue 的擴(kuò)展名來(lái)正常的編譯 ts 和 vue 文件
      3. 每個(gè)包下的 package.json 聲明 main module 和 typings ,當(dāng)支持 esm 方式加載的時(shí)候回默認(rèn)加載 index.module.js,否則加載 index.js
      4. 配置的時(shí)候?qū)?peerDependencies 添加到 external 配置項(xiàng)中,將peerDependencies的包不打包進(jìn)去,減小包體積,提高打包效率
      5. esm 支持 tree-shaking,故css不分開打包,這樣直接使用 esm 格式就會(huì)按需加載,無(wú)需借助插件

      rollup.config.js

      /* eslint-disable @typescript-eslint/no-var-requires */
      import fs from 'fs'
      import path from 'path'
      import json from '@rollup/plugin-json'
      import postcss from 'rollup-plugin-postcss'
      import vue from '@vitejs/plugin-vue'
      import { terser } from 'rollup-plugin-terser'
      import { nodeResolve } from '@rollup/plugin-node-resolve'
      import typescript from '@rollup/plugin-typescript'
      import babel from '@rollup/plugin-babel'
      import commonjs from '@rollup/plugin-commonjs'
      import { DEFAULT_EXTENSIONS } from '@babel/core'
      
      const isDev = process.env.NODE_ENV !== 'production'
      // packages 文件夾路徑
      const root = path.resolve(__dirname, 'packages')
      
      // 公共插件配置
      const getPlugins = () => {
          return [
              vue(),
              typescript({
                  tsconfig: './tsconfig.json'
              }),
              nodeResolve({
                  mainField: ['jsnext:main', 'browser', 'module', 'main'],
                  browser: true
              }),
              commonjs(),
              json(),
              postcss({
                  plugins: [require('autoprefixer')],
                  // 把 css 插入到 style 中
                  inject: true,
                  // 把 css 放到和js同一目錄
                  // extract: true
                  // Minimize CSS, boolean or options for cssnano.
                  minimize: !isDev,
                  // Enable sourceMap.
                  sourceMap: isDev,
                  // This plugin will process files ending with these extensions and the extensions supported by custom loaders.
                  extensions: ['.sass', '.less', '.scss', '.css']
              }),
              babel({
                  exclude: 'node_modules/**',
                  babelHelpers: 'runtime',
                  // babel 默認(rèn)不支持 ts 需要手動(dòng)添加
                  extensions: [...DEFAULT_EXTENSIONS, '.ts', '.tsx', '.vue']
              }),
              // 如果不是開發(fā)環(huán)境,開啟壓縮
              !isDev && terser({ toplevel: true })
          ]
      }
      
      module.exports = fs
          .readdirSync(root)
          // 過濾,只保留文件夾
          .filter(item => fs.statSync(path.resolve(root, item)).isDirectory())
          // 為每一個(gè)文件夾創(chuàng)建對(duì)應(yīng)的配置
          .map(item => {
              const pkg = require(path.resolve(root, item, 'package.json'))
              return {
                  input: path.resolve(root, item, 'src/main.ts'),
                  output: [
                      {
                          name: 'index',
                          file: path.resolve(root, item, pkg.main),
                          format: 'umd',
                          sourcemap: isDev,
                          globals: {
                              vue: 'vue',
                              'element-plus': 'element-plus'
                          }
                      },
                      {
                          name: 'index.module',
                          file: path.join(root, item, pkg.module),
                          format: 'es',
                          sourcemap: isDev,
                          globals: {
                              vue: 'vue',
                              'element-plus': 'element-plus'
                          }
                      }
                  ],
                  onwarn: function (warning) {
                      if (warning.code === 'THIS_IS_UNDEFINED' || warning.code === 'CIRCULAR_DEPENDENCY') {
                          return
                      }
                      console.error(`(!) ${warning.message}`)
                  },
                  plugins: getPlugins(),
                  external: Object.keys(require(path.join(root, item, 'package.json'))?.peerDependencies || {})
              }
          })
      
      

      組件庫(kù)文檔

      sum-ui 組件庫(kù)文檔
      本次對(duì)比之前的文檔庫(kù)考慮上有所不同,用的是 vuepress ,選它的原因之一是頁(yè)面簡(jiǎn)潔靈活,利用插件不僅可以配置組件交互說(shuō)明,還能配置其他說(shuō)明引導(dǎo)文檔,之前考慮了 vue-styleguidst ,但是其局限性比較強(qiáng),只能配置組件交互文檔并且頁(yè)面樣式?jīng)]有vuepress 的簡(jiǎn)潔好看,還調(diào)研了 vitepress ,但因?yàn)?vitepress 還一直在 WIP 并且把 vuepress 里的 plugins 等多項(xiàng)配置去掉了,如果是純說(shuō)明文檔用這個(gè)完全夠,但我們需要有組件交互說(shuō)明,因而最終還是選擇了支持Vue3的 vuepress@next。

      vuepress 打包

      除了webpack,vuepress@next 還添加了 vite 開發(fā)打包的方式,可以在 .vuepress/config.ts 下進(jìn)行配置

      下面的配置有幾個(gè)關(guān)鍵點(diǎn):

      1. 讀取packges文件夾下的文件夾名,給引用的包添加 alias 別名
      2. 由于組件里支持了jsx語(yǔ)法,所以添加了 @vitejs/plugin-vue-jsx 插件
      3. bundler 的配置(@vuepress/webpack / @vuepress/vite ),如果不設(shè)置則默認(rèn) webpack, 如果安裝了 vuepress-vite 則默認(rèn)vite打包
      4. 添加 vuepress 插件 vuepress-plugin-demoblock-plus ,該插件參照了 element-plus 的文檔渲染實(shí)現(xiàn)做了交互組件渲染
      5. 由于使用了 GitHub Actions 自動(dòng)化部署文檔到 GitHub pages, 所以 base 選項(xiàng)的配置需要和github的項(xiàng)目名保持一致,因?yàn)榧虞d的靜態(tài)資源路徑是該文件夾下的

      .vuepress/config.js

      const { readdirSync } = require('fs')
      const { join } = require('path')
      const chalk = require('chalk')
      const headPkgList = []; // 非 @sum-ui/開頭的組件
      
      const pkgList = readdirSync(join(__dirname, '../../packages')).filter(
        (pkg) => pkg.charAt(0) !== '.' && !headPkgList.includes(pkg),
      );
      
      const alias = pkgList.reduce((pre, pkg) => {
        pre[`@sum-ui/${pkg}`] = join(__dirname, '../../packages', pkg, 'src/Index.vue');
        return {
          ...pre,
        };
      }, {});
      
      console.log(`?? alias list \n${chalk.blue(Object.keys(alias).join('\n'))}`);
      
      module.exports = {
        title: "sum-ui", // 頂部左側(cè)標(biāo)題
        description: 'Vue3 + ElementPlus 組件庫(kù)',
        base: '/sum-ui/',
        bundler: '@vuepress/vite',
        bundlerConfig: {
          viteOptions: {
            plugins: [
              vueJsx()
            ]
          }
        },
        alias,
        head: [
          // 設(shè)置 描述 和 關(guān)鍵詞
          [
            "meta",
            { name: "keywords", content: "Vue3 UI 組件庫(kù)" },
          ]
        ],
        themeConfig: {
          sidebar: {
            // 側(cè)邊欄
            "/": [
              {
                text: "介紹",
                children: [
                  { text: "安裝", link: "/guide/install" },
                  { text: "快速上手", link: "/guide/start" },
                ],
              },
              {
                text: "組件",
                children: [
                  
                  { text: "Layout 布局", link: "/components/layout" },
                  { text: "Table 表格", link: "/components/table" }
                ],
              },
            ],
          },
          nav: [
            // 頂部右側(cè)導(dǎo)航欄
            { text: "介紹", link: "/", activeMatch: "^/$|^/guide/" },
            {
              text: "組件",
              link: "/components/layout.html",
              activeMatch: "^/$|^/components/"
            }
          ],
          // page meta
          editLinkText: '在 GitHub 上編輯此頁(yè)',
          lastUpdatedText: '上次更新',
          contributorsText: '貢獻(xiàn)者',
        },
        plugins: ['demoblock-plus'] // vuepress-plugin-demoblock-plus 插件,作用是展示交互文檔和代碼展開
      };
      

      .vuepress/clientAppEnhance.ts

      除 config.ts 的配置外,還需要全局注冊(cè)組件才生效,需要加 clientAppEnhance.ts 來(lái)進(jìn)行配置

      
      import { defineClientAppEnhance } from '@vuepress/client'
      import 'element-plus/theme-chalk/src/index.scss' // 全量引入樣式文件 scss TODO: 這里如果用element-plus 文檔里的方法 vite-plugin-element-plus 插件按需引入的話,dev 正常但是 vuepress build 打包 scss @import 就會(huì)報(bào)錯(cuò)
      import SumTable from '@sum-ui/table'
      import SumLayout from '@sum-ui/layout'
      
      export default defineClientAppEnhance(({ app }) => {
        app.component('SumTable', SumTable)
        app.component('SumLayout', SumLayout)
      })
      

      組件開發(fā)預(yù)覽

      交互文檔庫(kù)配置完成之后,就能邊開發(fā)組件庫(kù),邊看組件最終效果了

      yarn docs:dev // vuepress 文檔庫(kù)開發(fā)模式
      yarn docs:build // vuepress 文檔庫(kù)打包成靜態(tài)資源文件
      

      打包生成的資源文件可以利用 Github Actions 自動(dòng)部署到 GitHub Pages 上
      sum-ui組件庫(kù)文檔地址

      組件測(cè)試

      組件測(cè)試放到每個(gè)組件目錄下,組件寫完可以寫該組件的單元測(cè)試
      vue 的單測(cè)用 @vue/test-utils 就可以,另外在組件測(cè)試中導(dǎo)入組件的時(shí)候,不可直接識(shí)別 ts、vue 文件,需要 ts-jest vue-jest babel-jest 來(lái)做轉(zhuǎn)換

      配置 jest.config.js

      const alias = require('./alias')
      
      module.exports = {
          globals: {
              // work around: https://github.com/kulshekhar/ts-jest/issues/748#issuecomment-423528659
              'ts-jest': {
                  diagnostics: {
                      ignoreCodes: [151001]
                  }
              }
          },
          testEnvironment: 'jsdom',
          transform: {
              '^.+\\.vue$': 'vue-jest',
              '^.+\\.(t|j)sx?$': [
                  'babel-jest',
                  {
                      presets: [
                          [
                              '@babel/preset-env',
                              {
                                  targets: {
                                      node: true
                                  }
                              }
                          ],
                          [
                              '@babel/preset-typescript',
                              {
                                  isTSX: true,
                                  allExtensions: true
                              }
                          ]
                      ]
                  }
              ]
          },
          moduleNameMapper: alias, // 聲明別名以便于在jest中導(dǎo)入文件加載的時(shí)候能夠正確加載文件
          moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
          // u can change this option to a more specific folder for test single component or util when dev
          // for example, ['<rootDir>/packages/input']
          roots: ['<rootDir>']
      }
      

      vue 支持 tsx

      babel 的 preset 配置 isTSX: true, allExtensions: true 兩個(gè)選項(xiàng),allExtensions 為 true 支持所有擴(kuò)展名,主要是為了支持 .vue 文件的解析,isTSX 為 true 支持 jsx 語(yǔ)法的解析

      babel.config.js

      module.exports = {
          // ATTENTION!!
          // Preset ordering is reversed, so `@babel/typescript` will called first
          // Do not put `@babel/typescript` before `@babel/env`, otherwise will cause a compile error
          // See https://github.com/babel/babel/issues/12066
          presets: [
              '@vue/cli-plugin-babel/preset',
              [
                  '@babel/typescript',
                  {
                      isTSX: true,
                      allExtensions: true
                  }
              ]
          ],
          plugins: ['@babel/transform-runtime']
      }
      
      

      主題色相關(guān)

      由于 element-plus 使用了 css 變量,可以通過改變 css 變量來(lái)覆蓋主題色

      main.js

      import { themeVarsKey } from 'element-plus'
      const themeVars = {
        '--el-color-primary': '#29b6b0'
      }
      const app = createApp(App)
      app.provide(themeVarsKey, themeVars)
      

      其他可能會(huì)遇到的問題

      Cannot read property 'isCE' of null 報(bào)錯(cuò) https://github.com/vuejs/vue-next/issues/4344

      在我本地打包組件后 yarn link 到全局,然后在其他項(xiàng)目里引用的時(shí)候遇到這個(gè)報(bào)錯(cuò),是由于多個(gè) Vue 包引用問題,發(fā)布到 npm 之后從 npm 安裝引用正常

      posted @ 2021-09-14 16:51  c-137Summer  閱讀(2496)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: аⅴ天堂国产最新版在线中文| 日韩人妻精品中文字幕| 国产精品高清一区二区三区| 高清性欧美暴力猛交| 国产av综合色高清自拍| 精品中文字幕一区在线| 亚洲国产精品一区二区第一页| 亚洲中文无码永久免费| 中文字幕精品人妻av在线 | 四虎在线永久免费看精品| 国产精品久久久久久久9999| 国内精品久久久久影视| 自拍视频在线观看成人| 国产精品国产亚洲区久久| 精品视频在线观看免费观看| 亚洲美女厕所偷拍美女尿尿| 国产99在线 | 亚洲| 亚洲欧美综合中文| 日韩精品专区在线影院重磅| 潘金莲高清dvd碟片| 国产精品亚洲欧美大片在线看| 梁平县| 国产精成人品日日拍夜夜| 日本一区二区三区专线| 久久婷婷五月综合色国产免费观看| 韩国午夜理伦三级| 亚洲美女av一区二区| 日韩日韩日韩日韩日韩| 开心久久综合激情五月天| 高清无码爆乳潮喷在线观看| 国产萌白酱喷水视频在线观看| 无人区码一码二码三码区| 天天爽夜夜爱| 自拍偷区亚洲综合第二区| 国产午夜福利视频合集| 在线高清免费不卡全码| 国产自拍偷拍视频在线观看| 内射干少妇亚洲69XXX| 国产午夜福利片在线观看| 安吉县| 久久亚洲精品11p|