<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Vue3組件庫(kù)工程化實(shí)戰(zhàn) --Element3

          共 45102字,需瀏覽 91分鐘

           ·

          2021-03-22 00:13

          Element3組件庫(kù)工程化實(shí)戰(zhàn)

          隨著對(duì)前端功能和性能的不斷提高,前端早就不是一段內(nèi)嵌于頁(yè)面的一段JS代碼了。已經(jīng)進(jìn)化為一個(gè)系統(tǒng)復(fù)雜的工程了。下面我就結(jié)合element3組件庫(kù)的搭建經(jīng)驗(yàn)。帶大家搭建一個(gè)mini版組件庫(kù)。

          https://github.com/hug-sun/mini-element

          一、前端工程化是什么

          前端工程化概述 https://juejin.im/post/6844904073817227277

          前端工程化大體可以分為四個(gè)方面內(nèi)容。

          1. 模塊化一個(gè)文件分拆為多個(gè)互相依賴的文件,最后進(jìn)行統(tǒng)一打包和加載,保證高效多人協(xié)作。

            • JS模塊  CMD AMD CommonJS 及 ES6 Module
            • CSS模塊  Sass Less Stylus
            • 資源模塊化 文件、CSS、圖片通過(guò)JS進(jìn)行統(tǒng)一依賴關(guān)聯(lián)
          2. 組件化 相對(duì)于文件的拆分,組件是對(duì)于UI層面的拆分,每一個(gè)組件需要包括對(duì)應(yīng)的CSS、圖片、JS邏輯、視圖模板等并且能完成一個(gè)獨(dú)立的功能。

          3. 自動(dòng)化

            • 調(diào)試
            • 編譯
            • 部署
            • 測(cè)試
            • 文檔化
          4. 規(guī)范性

            • 項(xiàng)目目錄結(jié)構(gòu)
            • 語(yǔ)法提示
            • 編碼風(fēng)格規(guī)范
            • 聯(lián)調(diào)規(guī)范
            • 文件命名規(guī)范
            • 代碼樣式規(guī)范
            • git flow

          二、實(shí)戰(zhàn)步驟

          1. 開(kāi)發(fā)規(guī)范

          • JS代碼規(guī)范
            • airbnb-中文版
            • standard (24.5k star) 中文版
            • 百度前端編碼規(guī)范 3.9k
          • CSS代碼規(guī)范
            • styleguide 2.3k
            • spec 3.9k

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

          .
          ├── build        # 編譯腳本
          ├── coverage         # 覆蓋率報(bào)告
          ├── examples       # 代碼范例
          ├── lib         # CSS樣式 編譯后
          ├── node_modules 
          ├── packages      # 組件代碼
          ├── rollup-plugin-vue
          ├── scripts       # 腳本 發(fā)布、提交信息檢查
          ├── src         # 通用代碼
          ├── test        # 測(cè)試
          └── types        # TS類型定義

          1.2 文件命名規(guī)范

          .
          ├── button                   
          │   ├── Button.vue        # 組件SFC
          │   ├── __tests__        
          │   │   └── Button.spec.js   # 測(cè)試文件
          │   └── index.js        # 組件入口

          1.3 代碼樣式規(guī)范(ESLint)

          • JS代碼規(guī)范
            • airbnb-中文版
            • standard (24.5k star) 中文版
            • 百度前端編碼規(guī)范 3.9k
          • CSS代碼規(guī)范
            • styleguide 2.3k
            • spec 3.9k
          # .eslintrc.js
          module.exports = {
            root: true,
            env: {
              browser: true,
              es2020: true,
              node: true,
              jest: true
            },
            globals: {
              ga: true,
              chrome: true,
              __DEV__: true
            },
            extends: [
              'plugin:json/recommended',
              'plugin:vue/vue3-essential',
              'eslint:recommended',
              '@vue/prettier'
            ],
            parserOptions: {
              parser: 'babel-eslint'
            },
            rules: {
              'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
              'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
              'prettier/prettier''error'
            }
          }
          # .eslintignore
          src/utils/popper.js
          src/utils/date.js
          examples/play
          *.sh
          node_modules
          lib
          coverage
          *.md
          *.scss
          *.woff
          *.ttf
          src/index.js
          dist
          yarn add eslint
          yarn add eslint-formatter-pretty
          yarn add eslint-plugin-json
          yarn add eslint-plugin-prettier
          yarn add eslint-plugin-vue
          yarn add @vue/eslint-config-prettier
          yarn add babel-eslint
          yarn add prettier

          package.json

          {
          "scripts": {
             "lint""eslint --no-error-on-unmatched-pattern --ext .vue --ext .js --ext .jsx packages/**/ src/**/ --fix",
            },
          }

          1.6 Git版本規(guī)范

          分支管理

          一般項(xiàng)目分主分支(master)和其他分支。當(dāng)有團(tuán)隊(duì)成員要開(kāi)發(fā)新功能(Feather)或改 BUG(Fix) 時(shí),就從 master 分支開(kāi)一個(gè)新的分支。比如你修改一個(gè)Bug應(yīng)該用bug的編號(hào)作為分支(例:[Fix:12323])

          Commit規(guī)范

          • 內(nèi)容規(guī)范
          <type>(<scope>): <subject>
          <BLANK LINE>
          <body>
          <BLANK LINE>
          <footer>
          復(fù)制代碼

          大致分為三個(gè)部分(使用空行分割):

          1. 標(biāo)題行: 必填, 描述主要修改類型和內(nèi)容
          2. 主題內(nèi)容: 描述為什么修改, 做了什么樣的修改, 以及開(kāi)發(fā)的思路等等
          3. 頁(yè)腳注釋: 可以寫(xiě)注釋,BUG 號(hào)鏈接
          • type: commit 的類型
            • feat: 新功能、新特性
            • fix: 修改 bug
            • perf: 更改代碼,以提高性能
            • refactor: 代碼重構(gòu)(重構(gòu),在不影響代碼內(nèi)部行為、功能下的代碼修改)
            • docs: 文檔修改
            • style: 代碼格式修改, 注意不是 css 修改(例如分號(hào)修改)
            • test: 測(cè)試用例新增、修改
            • build: 影響項(xiàng)目構(gòu)建或依賴項(xiàng)修改
            • revert: 恢復(fù)上一次提交
            • ci: 持續(xù)集成相關(guān)文件修改
            • chore: 其他修改(不在上述類型中的修改)
            • release: 發(fā)布新版本
            • workflow: 工作流相關(guān)文件修改
          1. scope: commit 影響的范圍, 比如: route, component, utils, build...
          2. subject: commit 的概述
          3. body: commit 具體修改內(nèi)容, 可以分為多行.
          4. footer: 一些備注, 通常是 BREAKING CHANGE 或修復(fù)的 bug 的鏈接.

          示例

          fix(修復(fù)BUG)

          如果修復(fù)的這個(gè)BUG只影響當(dāng)前修改的文件,可不加范圍。如果影響的范圍比較大,要加上范圍描述。

          例如這次 BUG 修復(fù)影響到全局,可以加個(gè) global。如果影響的是某個(gè)目錄或某個(gè)功能,可以加上該目錄的路徑,或者對(duì)應(yīng)的功能名稱。

          // 示例1
          fix(global):修復(fù)checkbox不能復(fù)選的問(wèn)題
          // 示例2 下面圓括號(hào)里的 common 為通用管理的名稱
          fix(common): 修復(fù)字體過(guò)小的BUG,將通用管理下所有頁(yè)面的默認(rèn)字體大小修改為 14px
          // 示例3
          fix: value.length -> values.length
          復(fù)制代碼
          feat(添加新功能或新頁(yè)面)
          feat: 添加網(wǎng)站主頁(yè)靜態(tài)頁(yè)面

          這是一個(gè)示例,假設(shè)對(duì)點(diǎn)檢任務(wù)靜態(tài)頁(yè)面進(jìn)行了一些描述。
           
          這里是備注,可以是放BUG鏈接或者一些重要性的東西。
          復(fù)制代碼
          chore(其他修改)

          chore 的中文翻譯為日常事務(wù)、例行工作,顧名思義,即不在其他 commit 類型中的修改,都可以用 chore 表示。

          chore: 將表格中的查看詳情改為詳情
          復(fù)制代碼

          其他類型的 commit 和上面三個(gè)示例差不多,就不說(shuō)了。

          自動(dòng)化提交驗(yàn)證

          驗(yàn)證 git commit 規(guī)范,主要通過(guò) git 的 pre-commit 鉤子函數(shù)來(lái)進(jìn)行。當(dāng)然,你還需要下載一個(gè)輔助工具來(lái)幫助你進(jìn)行驗(yàn)證。

          下載輔助工具

          npm i -D husky

          package.json 加上下面的代碼

          "husky": {
            "hooks": {
              "pre-commit""npm run lint",
              "commit-msg""node script/verify-commit.js",
              "pre-push""npm test"
            }
          }
          復(fù)制代碼

          然后在你項(xiàng)目根目錄下新建一個(gè)文件夾 script,并在下面新建一個(gè)文件 verify-commit.js,輸入以下代碼:

          const msgPath = process.env.HUSKY_GIT_PARAMS
          const msg = require('fs')
          .readFileSync(msgPath, 'utf-8')
          .trim()

          const commitRE = /^(feat|fix|docs|style|refactor|perf|test|workflow|build|ci|chore|release|workflow)(\(.+\))?: .{1,50}/

          if (!commitRE.test(msg)) {
              console.log()
              console.error(`
                  不合法的 commit 消息格式。
                  請(qǐng)查看 git commit 提交規(guī)范:https://github.com/woai3c/Front-end-articles/blob/master/git%20commit%20style.md
              `
          )

              process.exit(1)
          }
          復(fù)制代碼

          現(xiàn)在來(lái)解釋下各個(gè)鉤子的含義:

          1. "pre-commit": "npm run lint",在 git commit 前執(zhí)行 npm run lint 檢查代碼格式。
          2. "commit-msg": "node script/verify-commit.js",在 git commit 時(shí)執(zhí)行腳本 verify-commit.js 驗(yàn)證 commit 消息。如果不符合腳本中定義的格式,將會(huì)報(bào)錯(cuò)。
          3. "pre-push": "npm test",在你執(zhí)行 git push 將代碼推送到遠(yuǎn)程倉(cāng)庫(kù)前,執(zhí)行 npm test 進(jìn)行測(cè)試。如果測(cè)試失敗,將不會(huì)執(zhí)行這次推送。

          /scripts/verifyCommit.js

          // Invoked on the commit-msg git hook by yorkie.

          const chalk = require('chalk')
          const msgPath = process.env.GIT_PARAMS
          const msg = require('fs').readFileSync(msgPath, 'utf-8').trim()

          const commitRE = /^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|release)(\(.+\))?(.{1,10})?: .{1,50}/
          const mergeRe = /^(Merge pull request|Merge branch)/

          if (!commitRE.test(msg)) {
            if (!mergeRe.test(msg)) {
              console.log(msg)
              console.error(
                `  ${chalk.bgRed.white(' ERROR ')} ${chalk.red(
                  `invalid commit message format.`
                )}\n\n` +
                  chalk.red(
                    `  Proper commit message format is required for automated changelog generation. Examples:\n\n`
                  ) +
                  `    ${chalk.green(`feat(compiler): add 'comments' option`)}\n` +
                  `    ${chalk.green(
                    `fix(v-model): handle events on blur (close #28)`
                  )}\n\n` +
                  chalk.red(
                    `  See https://github.com/vuejs/vue-next/blob/master/.github/commit-convention.md for more details.\n`
                  )
              )
              process.exit(1)
            }
          }

          2. 模塊化與組件化

          npm init -y

          https://github.com/cuixiaorui/course-vue3-test/tree/main/chapters/two參考資料

          2.1 編寫(xiě)B(tài)uttun組件

          yarn add vue@next

          /packages/button/Button.vue

          <template>
            <button
              class="el-button"
              @click="handleClick"
              :disabled="buttonDisabled || loading"
              :autofocus="autofocus"
              :type="nativeType"
              :class="[
                type ? 'el-button--' + type : '',
                buttonSize ? 'el-button--' + buttonSize : '',
                {
                  'is-disabled': buttonDisabled,
                  'is-loading': loading,
                  'is-plain': plain,
                  'is-round': round,
                  'is-circle': circle,
                },
              ]"

            >

              <i class="el-icon-loading" v-if="loading"></i>
              <i :class="icon" v-if="icon && !loading"></i>
              <span v-if="$slots.default">
                <slot></slot>
              </span>
            </button>
          </template>
          <script>
          import { computed, inject, toRefs, unref, getCurrentInstance } from "vue";

          export default {
            name"ElButton",

            props: {
              type: {
                typeString,
                default"default",
              },
              size: {
                typeString,
                default"",
              },
              icon: {
                typeString,
                default"",
              },
              nativeType: {
                typeString,
                default"button",
              },
              loadingBoolean,
              disabledBoolean,
              plainBoolean,
              autofocusBoolean,
              roundBoolean,
              circleBoolean,
            },
            emits: ["click"],
            setup(props, ctx) {
              const { size, disabled } = toRefs(props);

              const buttonSize = useButtonSize(size);
              const buttonDisabled = useButtonDisabled(disabled);

              const handleClick = (evt) => {
                ctx.emit("click", evt);
              };

              return {
                handleClick,
                buttonSize,
                buttonDisabled,
              };
            },
          };

          const useButtonSize = (size) => {
            const elFormItem = inject("elFormItem", {});

            const _elFormItemSize = computed(() => {
              return unref(elFormItem.elFormItemSize);
            });

            const buttonSize = computed(() => {
              return (
                size.value ||
                _elFormItemSize.value ||
                (getCurrentInstance().proxy.$ELEMENT || {}).size
              );
            });

            return buttonSize;
          };

          const useButtonDisabled = (disabled) => {
            const elForm = inject("elForm", {});

            const buttonDisabled = computed(() => {
              return disabled.value || unref(elForm.disabled);
            });

            return buttonDisabled;
          };
          </script>

          2.2 集成Babel

          yarn add babel
          yarn add babel-plugin-syntax-dynamic-import
          yarn add babel-plugin-syntax-jsx
          yarn add babel-preset-env
          yarn add @babel/plugin-proposal-optional-chaining
          yarn add @babel/preset-env
          yarn add @vue/babel-plugin-jsx

          新建.babelrc文件

          {
            "presets": [["@babel/preset-env", { "targets": { "node""current" } }]],
            "plugins": [
              "syntax-dynamic-import",
              ["@vue/babel-plugin-jsx"],
              "@babel/plugin-proposal-optional-chaining",
              "@babel/plugin-proposal-nullish-coalescing-operator"
            ],
            "env": {
              "utils": {
                "presets": [
                  [
                    "env",
                    {
                      "loose"true,
                      "modules""commonjs",
                      "targets": {
                        "browsers": ["> 1%""last 2 versions""not ie <= 8"]
                      }
                    }
                  ]
                ],
                "plugins": [
                  [
                    "module-resolver",
                    {
                      "root": ["element-ui"],
                      "alias": {
                        "element-ui/src""element-ui/lib"
                      }
                    }
                  ]
                ]
              },
              "test": {
                "plugins": ["istanbul"],
                "presets": [["env", { "targets": { "node""current" } }]]
              },
              "esm": {
                "presets": [["@babel/preset-env", { "modules"false }]]
              }
            }
          }

          2.2 集成VTU

          安裝依賴

          yarn add jest
          # 此版本這個(gè)支持Vue3.0
          yarn add [email protected]
          yarn add babel-jest             
          yarn add @vue/[email protected]
          yarn add @vue/test-utils@next
          yarn add typescript

          jest.config.js

          module.exports = {
            testEnvironment: 'jsdom', // 默認(rèn)JSdom
            roots: [
              '<rootDir>/src',
              '<rootDir>/packages',
            ], // 
            transform: {
              '^.+\\.vue$': 'vue-jest', // vue單文件
              '^.+\\js$': 'babel-jest' // esm最新語(yǔ)法 import
            },
            moduleFileExtensions: ['vue', 'js', 'json', 'jsx', 'ts', 'tsx', 'node'],
            testMatch: ['**/__tests__/**/*.spec.js'],
            // 別名
            moduleNameMapper: {
              '^element-ui(.*)$': '<rootDir>$1',
              '^main(.*)$': '<rootDir>/src$1'
            }
          }

          /packages/button/tests/Button.spec.js

          import Button from "../Button.vue";
          import { mount } from "@vue/test-utils";

          it("content", () => {
            const Comp = {
              template`<div><Button>默認(rèn)按鈕</Button></div>`,
            };

            const wrapper = mount(Comp, {
              global: {
                components: {
                  Button,
                },
              },
            });

            expect(wrapper.findComponent({ name"ElButton" }).text()).toContain(
              "默認(rèn)按鈕"
            );
          });

          describe("size", () => {
            it("should have a el-button--mini class when set size prop value equal to mini", () => {
              const wrapper = mount(Button, {
                props: {
                  size"mini",
                },
              });

              expect(wrapper.classes()).toContain("el-button--mini");
            });

            it("should have a el-button--mini class by elFormItem ", () => {
              const wrapper = mount(Button, {
                global: {
                  provide: {
                    elFormItem: {
                      elFormItemSize"mini",
                    },
                  },
                },
              });

              expect(wrapper.classes()).toContain("el-button--mini");
            });
            it("should have a el-button--mini class by $ELEMENT value ", () => {
              const wrapper = mount(Button, {
                global: {
                  config: {
                    globalProperties: {
                      $ELEMENT: {
                        size"mini",
                      },
                    },
                  },
                },
              });

              expect(wrapper.classes()).toContain("el-button--mini");
            });
          });

          it("type", () => {
            const wrapper = mount(Button, {
              props: {
                type"primary",
              },
            });

            expect(wrapper.classes()).toContain("el-button--primary");
          });

          it("plain", () => {
            const wrapper = mount(Button, {
              props: {
                plaintrue,
              },
            });

            expect(wrapper.classes()).toContain("is-plain");
          });
          it("round", () => {
            const wrapper = mount(Button, {
              props: {
                roundtrue,
              },
            });

            expect(wrapper.classes()).toContain("is-round");
          });

          it("circle", () => {
            const wrapper = mount(Button, {
              props: {
                circletrue,
              },
            });

            expect(wrapper.classes()).toContain("is-circle");
          });
          it("loading", () => {
            const wrapper = mount(Button, {
              props: {
                loadingtrue,
              },
            });

            expect(wrapper.find(".el-icon-loading").exists()).toBe(true);
            expect(wrapper.classes()).toContain("is-loading");
          });

          describe("icon", () => {
            it("should show icon element", () => {
              const wrapper = mount(Button, {
                props: {
                  icon"el-icon-edit",
                },
              });

              expect(wrapper.find(".el-icon-edit").exists()).toBe(true);
            });

            it("should not show icon element when set loading prop equal to true", () => {
              const wrapper = mount(Button, {
                props: {
                  loadingtrue,
                  icon"el-icon-edit",
                },
              });

              expect(wrapper.find(".el-icon-edit").exists()).toBe(false);
            });
          });

          describe("click", () => {
            it("should emit click event ", () => {
              const wrapper = mount(Button);

              wrapper.trigger("click");

              expect(wrapper.emitted("click")).toBeTruthy();
            });

            it("should not emit click event when disabled equal to true", () => {
              const wrapper = mount(Button, {
                props: {
                  disabledtrue,
                },
              });

              wrapper.trigger("click");

              expect(wrapper.emitted("click")).toBeFalsy();
            });

            it("should not emit click event when elForm disabled equal to true", () => {
              const wrapper = mount(Button, {
                global: {
                  provide: {
                    elForm: {
                      disabledtrue,
                    },
                  },
                },
              });

              wrapper.trigger("click");

              expect(wrapper.emitted("click")).toBeFalsy();
            });

            it("should not emit click event when loading prop equal to true", () => {
              const wrapper = mount(Button, {
                props: {
                  loadingtrue,
                },
              });

              wrapper.trigger("click");

              expect(wrapper.emitted("click")).toBeFalsy();
            });
          });

          it("native-type", () => {
            const wrapper = mount(Button, {
              props: {
                nativeType"button",
              },
            });

            expect(wrapper.attributes("type")).toBe("button");
          });

          測(cè)試

          "test""jest --runInBand"# 序列化執(zhí)行

          2.4 樣式打包

          yarn add gulp
          yarn add gulp-autoprefixer
          yarn add gulp-sass
          yarn add gulp-cssmin

          # cp-cli
          yarn add cp-cli
          yarn add tslib

          /bin/gen-cssfile

          package.json

          "build:theme""gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",

          2.4 Rollup打包

          https://www.rollupjs.com/ Rollup中文網(wǎng)https://juejin.im/post/6844903731343933453 使用 rollup 打包 JS

          yarn add rollup
          yarn add rollup-plugin-peer-deps-external
          yarn add rollup-plugin-scss
          yarn add rollup-plugin-terser
          yarn add rollup-plugin-vue
          yarn add @rollup/plugin-node-resolve
          yarn add @rollup/plugin-commonjs
          yarn add @rollup/plugin-json
          yarn add @rollup/plugin-replace
          yarn add @rollup/plugin-babel
          yarn add rollup-plugin-vue

          Package.json

          "build:next""rollup -c",
          import pkg from './package.json'
          // 等 rollup-plugin-vue 發(fā)版后在切換官方版
          // 暫時(shí)先用本地的 rollup-plugin-vue
          // 修復(fù)了 render 函數(shù)的編譯問(wèn)題,但是還沒(méi)發(fā)版
          // import vuePlugin from 'rollup-plugin-vue'
          const vuePlugin = require('./rollup-plugin-vue/index')
          import scss from 'rollup-plugin-scss'
          import peerDepsExternal from 'rollup-plugin-peer-deps-external'
          import resolve from '@rollup/plugin-node-resolve'
          import commonjs from '@rollup/plugin-commonjs'
          import json from '@rollup/plugin-json'
          import replace from '@rollup/plugin-replace'
          import babel from '@rollup/plugin-babel'
          import { terser } from 'rollup-plugin-terser'

          const name = 'Element3'

          const createBanner = () => {
            return `/*!
            * ${pkg.name} v${pkg.version}
            * (c) ${new Date().getFullYear()} kkb
            * @license MIT
            */`

          }

          const createBaseConfig = () => {
            return {
              input'src/entry.js',
              external: ['vue'],
              plugins: [
                peerDepsExternal(),
                babel(),
                resolve({
                  extensions: ['.vue''.jsx']
                }),
                commonjs(),
                json(),
                vuePlugin({
                  csstrue
                }),
                scss()
              ],
              output: {
                sourcemapfalse,
                banner: createBanner(),
                externalLiveBindingsfalse,
                globals: {
                  vue'Vue'
                }
              }
            }
          }

          function mergeConfig(baseConfig, configB{
            const config = Object.assign({}, baseConfig)
            // plugin
            if (configB.plugins) {
              baseConfig.plugins.push(...configB.plugins)
            }

            // output
            config.output = Object.assign({}, baseConfig.output, configB.output)

            return config
          }

          function createFileName(formatName{
            return `dist/element3-ui.${formatName}.js`
          }

          // es-bundle
          const esBundleConfig = {
            plugins: [
              replace({
                __DEV__`(process.env.NODE_ENV !== 'production')`
              })
            ],
            output: {
              file: createFileName('esm-bundler'),
              format'es'
            }
          }

          // es-browser
          const esBrowserConfig = {
            plugins: [
              replace({
                __DEV__true
              })
            ],
            output: {
              file: createFileName('esm-browser'),
              format'es'
            }
          }

          // es-browser.prod
          const esBrowserProdConfig = {
            plugins: [
              terser(),
              replace({
                __DEV__false
              })
            ],
            output: {
              file: createFileName('esm-browser.prod'),
              format'es'
            }
          }

          // cjs
          const cjsConfig = {
            plugins: [
              replace({
                __DEV__true
              })
            ],
            output: {
              file: createFileName('cjs'),
              format'cjs'
            }
          }
          // cjs.prod
          const cjsProdConfig = {
            plugins: [
              terser(),
              replace({
                __DEV__false
              })
            ],
            output: {
              file: createFileName('cjs.prod'),
              format'cjs'
            }
          }

          // global
          const globalConfig = {
            plugins: [
              replace({
                __DEV__true,
                'process.env.NODE_ENV'true
              })
            ],
            output: {
              file: createFileName('global'),
              format'iife',
              name
            }
          }
          // global.prod
          const globalProdConfig = {
            plugins: [
              terser(),
              replace({
                __DEV__false
              })
            ],
            output: {
              file: createFileName('global.prod'),
              format'iife',
              name
            }
          }

          const formatConfigs = [
            esBundleConfig,
            esBrowserProdConfig,
            esBrowserConfig,
            cjsConfig,
            cjsProdConfig,
            globalConfig,
            globalProdConfig
          ]

          function createPackageConfigs({
            return formatConfigs.map((formatConfig) => {
              return mergeConfig(createBaseConfig(), formatConfig)
            })
          }

          export default createPackageConfigs()

          2.3 編寫(xiě)Entry入口

          3. 自動(dòng)化

          3.1 文檔自動(dòng)化

          文檔自動(dòng)化其實(shí)就是根據(jù)代碼自動(dòng)生成開(kāi)發(fā)文檔。比如element3項(xiàng)目中的。https://element3-ui.com/其實(shí)可以用StoryBook。這個(gè)我們后面寫(xiě)專題更新。大家保持關(guān)注。

          3.2 規(guī)范檢查

          yarn add husky

          .huskyrc

          {
              "hooks": {
                  "pre-commit""npm run lint",
                  "commit-msg""node scripts/verifyCommit.js",
                  "pre-push""npm run test"
            },
          }

          3.4 回歸測(cè)試

          GitHub Action

          .github/workflows/main.yml

          3.3 持續(xù)集成CI

          Travis CI 提供的是持續(xù)集成服務(wù),它僅支持 Github,不支持其他代碼托管。它需要綁定 Github 上面的項(xiàng)目,還需要該項(xiàng)目含有構(gòu)建或者測(cè)試腳本。只要有新的代碼,就會(huì)自動(dòng)抓取。然后,提供一個(gè)虛擬機(jī)環(huán)境,執(zhí)行測(cè)試,完成構(gòu)建,還能部署到服務(wù)器。只要代碼有變更,就自動(dòng)運(yùn)行構(gòu)建和測(cè)試,反饋運(yùn)行結(jié)果。確保符合預(yù)期以后,再將新代碼集成到主干。

          這個(gè)項(xiàng)目需要Travis在提交后自動(dòng)進(jìn)行測(cè)試并且向codecov提供測(cè)試報(bào)告。

          • 測(cè)試
          • 報(bào)告分析

          登錄TravicCI網(wǎng)站

          登錄https://www.travis-ci.org/網(wǎng)站

          使用github賬號(hào)登錄系統(tǒng)

          配置.travis.yml

          運(yùn)行自動(dòng)化測(cè)試框架

          language: node_js               # 項(xiàng)目語(yǔ)言,node 項(xiàng)目就按照這種寫(xiě)法就OK了
          node_js:
          - 13.2.0    # 項(xiàng)目環(huán)境
          cache:    # 緩存 node_js 依賴,提升第二次構(gòu)建的效率
            directories:
            - node_modules
          test:
            - npm run test # 運(yùn)行自動(dòng)測(cè)試框架

          參考教程:Travis CI Tutorial

          上傳配置到github

          啟動(dòng)持續(xù)集成

          通過(guò)github賬號(hào)登錄travis

          獲取持續(xù)集成通過(guò)徽標(biāo)

          將上面 URL 中的 {GitHub 用戶名} 和 {項(xiàng)目名稱} 替換為自己項(xiàng)目的即可,最后可以將集成完成后的 markdown 代碼貼在自己的項(xiàng)目上

          http://img.shields.io/travis/{GitHub 用戶名}/{項(xiàng)目名稱}.svg
          復(fù)制代碼

          3.5 持續(xù)交付CD - 上傳Npm庫(kù)

          創(chuàng)建發(fā)布腳本

          publish.sh

          #!/usr/bin/env bash
          npm config get registry # 檢查倉(cāng)庫(kù)鏡像庫(kù)
          npm config set registry=http://registry.npmjs.org
          echo '請(qǐng)進(jìn)行登錄相關(guān)操作:'
          npm login # 登陸
          echo "-------publishing-------"
          npm publish # 發(fā)布
          npm config set registry=https://registry.npm.taobao.org # 設(shè)置為淘寶鏡像
          echo "發(fā)布完成"
          exit

          執(zhí)行發(fā)布

          ./publish.sh
          復(fù)制代碼

          填入github用戶名密碼后

          3.7 覆蓋率測(cè)試Codecov

          Codecov是一個(gè)開(kāi)源的測(cè)試結(jié)果展示平臺(tái),將測(cè)試結(jié)果可視化。Github上許多開(kāi)源項(xiàng)目都使用了Codecov來(lái)展示單測(cè)結(jié)果。Codecov跟Travis CI一樣都支持Github賬號(hào)登錄,同樣會(huì)同步Github中的項(xiàng)目。

          yarn add codecov
           "scripts": {
           ...,
           "codecov""codecov"
           }

          4. 其他

          4.1 標(biāo)準(zhǔn)的README文檔

          4.2 開(kāi)源許可證

          每個(gè)開(kāi)源項(xiàng)目都需要配置一份合適的開(kāi)源許可證來(lái)告知所有瀏覽過(guò)我們的項(xiàng)目的用戶他們擁有哪些權(quán)限,具體許可證的選取可以參照阮一峰前輩繪制的這張圖表:

          那我們又該怎樣為我們的項(xiàng)目添加許可證了?其實(shí) Github 已經(jīng)為我們提供了非常簡(jiǎn)便的可視化操作: 我們平時(shí)在逛 github 網(wǎng)站的時(shí)候,發(fā)現(xiàn)不少項(xiàng)目都在 README.md 中添加徽標(biāo),對(duì)項(xiàng)目進(jìn)行標(biāo)記和說(shuō)明,這些小圖標(biāo)給項(xiàng)目增色不少,不僅簡(jiǎn)單美觀,而且還包含清晰易懂的信息。

          1. 打開(kāi)我們的開(kāi)源項(xiàng)目并切換至 Insights 面板
          2. 點(diǎn)擊 Community 標(biāo)簽
          3. 如果您的項(xiàng)目沒(méi)有添加 License,在 Checklist 里會(huì)提示您添加許可證,點(diǎn)擊 Add 按鈕就進(jìn)入可視化操作流程了

          4.3 申請(qǐng)開(kāi)源徽標(biāo) (Badge)

          Github 徽章 https://docs.github.com/cn/free-pro-team@latest/actions/managing-workflow-runs/adding-a-workflow-status-badge

          三、附錄

          3.1 Vue組件與插件

          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <meta http-equiv="X-UA-Compatible" content="ie=edge" />
              <title>Document</title>
              <script src="/node_modules/vue/dist/vue.global.js"></script>
              <script src="/dist/element3-ui.global.js"></script>
              <link href="/lib/theme-chalk/index.css" rel="stylesheet" />
              <style></style>
            </head>

            <body>
              <div id="app"></div>
              <script>
                const { createApp, reactive, computed, watchEffect } = Vue;

                const MyButton = {
                  name"MyButton",
                  datafunction ({
                    return {
                      count0,
                    };
                  },
                  template:
                    '<button v-on:click="count++">You clicked me {{ count }} times.</button>',
                };

                // 添加插件
                MyButton.install = (app) => app.component("MyButton", MyButton);

                // 組件庫(kù)
                const Element = {
                    MyButton,
                    installapp => {
                        app.use(MyButton)
                    }
                }

                const MyComponent = {
                  template`
                          <my-button />
                      `
          ,
                };

                createApp(MyComponent)
                  // .use(MyButton)
                  .use(Element)
                  .mount("#app");
              
          </script>
            </body>
          </html>

          3.2 rollup打包

          rollup是一款小巧的javascript模塊打包工具,更適合于庫(kù)應(yīng)用的構(gòu)建工具;可以將小塊代碼編譯成大塊復(fù)雜的代碼,基于ES6 modules,它可以讓你的 bundle 最小化,有效減少文件請(qǐng)求大小,vue在開(kāi)發(fā)的時(shí)候用的是webpack,但是最后將文件打包在一起的時(shí)候用的是 rollup.js

          首次發(fā)表在個(gè)人博客

          • rollup官方文檔
          • rollupGithub

          https://juejin.im/post/6844903570974703629 Rollup基礎(chǔ)

          Button

          /src/MyButton.js

          export default {
            name"MyButton",
            datafunction ({
              return {
                count0,
              };
            },
            template:
              '<button v-on:click="count++">You clicked me {{ count }} times.</button>',
          };

          入口

          /src/entry.js

          import MyButton from "./MyButton";
          import SfcButton from "./SfcButton.vue";
          import JsxButton from "./JsxButton.vue";

          // 添加插件
          MyButton.install = (app) => app.component("MyButton", MyButton);
          SfcButton.install = (app) => app.component("SfcButton", SfcButton);
          JsxButton.install = (app) => app.component("JsxButton", JsxButton);

          // 組件庫(kù)
          const Element = {
            MyButton,
            SfcButton,
            JsxButton,
            install(app) => {
              app.use(MyButton);
              app.use(SfcButton);
              app.use(JsxButton);
            },
          };

          export default Element;

          格式聲明

          https://juejin.im/post/6885542715782594568 AMD CMD UMD區(qū)別

          • amd – 異步模塊定義,用于像 RequireJS 這樣的模塊加載器
          • cjs – CommonJS,適用于 Node 和 Browserify/Webpack
          • es – 將軟件包保存為 ES 模塊文件
          • iife – 一個(gè)自動(dòng)執(zhí)行的功能,適合作為<script>標(biāo)簽。(如果要為應(yīng)用程序創(chuàng)建一個(gè)捆綁包,您可能想要使用它,因?yàn)樗鼤?huì)使文件大小變小。)
          • umd – 通用模塊定義,以 amd,cjs 和 iife 為一體
          const vuePlugin = require("../../rollup-plugin-vue/index");
          import babel from "@rollup/plugin-babel";
          // import vuePlugin from "rollup-plugin-vue";
          const es = {
            input"src/entry.js",
            output: {
              file"dist/index.js",
              name"Element",
              format"iife",
              globals: {
                vue"Vue",
              },
            },
            external: ["vue"],
            plugins: [
              babel(),
              vuePlugin({
                csstrue,
              }),
            ],
          };

          import { terser } from "rollup-plugin-terser";
          const minEs = {
            input"src/entry.js",
            external: ["vue"],
            output: {
              file"dist/index.min.js",
              name"Element",
              format"umd",
            },
            plugins: [
              babel(),
              vuePlugin({
                csstrue,
              }),
              terser(),
            ],
          };

          const cjs = {
            input"src/entry.js",
            external: ["vue"],
            output: {
              file"dist/index.cjs.js",
              name"Element",
              format"cjs",
            },
            plugins: [
              babel(),
              vuePlugin({
                csstrue,
              }),
            ],
          };

          export default [es, minEs, cjs];

          測(cè)試頁(yè)面

          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <meta http-equiv="X-UA-Compatible" content="ie=edge" />
              <title>Document</title>
              <script src="/node_modules/vue/dist/vue.global.js"></script>
              <script src="dist/index.js"></script>
              <style></style>
            </head>

            <body>
              <div id="app"></div>
              <script>
                const { createApp, reactive, computed, watchEffect } = Vue;

                const MyComponent = {
                  template`
                          <my-button />
                          <sfc-button />
                          <jsx-button />
                      `
          ,
                };

                createApp(MyComponent)
                  .use(Element)
                  .mount("#app");
              
          </script>
            </body>
          </html>

          單文件組件

          <template>
            <button>Sfc 666</button>
          </template>
          <script>
          export default {
            name"SfcButton",
          };
          </script>
          const vuePlugin = require("../../rollup-plugin-vue/index");
          // import vuePlugin from "rollup-plugin-vue";
          # plugin
          vuePlugin({
          css: true,
          }),

          JSX支持

          jsx的定義

          JSX 是一種類似于 XML 的 JavaScript 語(yǔ)法擴(kuò)展 JSX 不是由引擎或?yàn)g覽器實(shí)現(xiàn)的。相反,我們將使用像 Babel 這樣的轉(zhuǎn)換器將 JSX 轉(zhuǎn)換為常規(guī) JavaScript。基本上,JSX 允許我們?cè)?JavaScript 中使用類似 HTML 的語(yǔ)法。

          jsx的優(yōu)勢(shì)

          1. 可以將 模版分離 這樣模版的每個(gè)部分更加獨(dú)立,又可以隨機(jī)的組合,復(fù)用性更高。相比與組件的組合,粒度更細(xì)
          2. 使用 js 可配置每項(xiàng)要渲染的 dom,更加動(dòng)態(tài)可配置化
          import babel from "@rollup/plugin-babel";
          # plugin
          babel(),
          <script>
          export default {
            name"JsxButton",
            render() {
              return <button>JSX 666</button>;
            },
          };
          </script>

          3.3 Vue-cli插件開(kāi)發(fā)

          請(qǐng)參考 https://juejin.cn/post/6899334776860180494

          關(guān)注數(shù):10億+ 文章數(shù):10億+
          粉絲量:10億+ 點(diǎn)擊量:10億+

           


          微信群管理員請(qǐng)掃描這里

          微信群管理員請(qǐng)掃描這里

          喜歡本文的朋友,歡迎關(guān)注公眾號(hào) 程序員哆啦A夢(mèng),收看更多精彩內(nèi)容

          點(diǎn)個(gè)[在看],是對(duì)小達(dá)最大的支持!


          如果覺(jué)得這篇文章還不錯(cuò),來(lái)個(gè)【分享、點(diǎn)贊、在看】三連吧,讓更多的人也看

          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本午夜精品理论片A级app发布 | 成人三级片在线播放 | 精品青青草 | 国产一级乱伦片 | 色撸撸在线视频 |