1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        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、圖片通過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. 開發(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ì)成員要開發(fā)新功能(Feather)或改 BUG(Fix) 時(shí),就從 master 分支開一個(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)容: 描述為什么修改, 做了什么樣的修改, 以及開發(fā)的思路等等
        3. 頁(yè)腳注釋: 可以寫注釋,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ù)選的問題
        // 示例2 下面圓括號(hào)里的 common 為通用管理的名稱
        fix(common): 修復(fù)字體過小的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è)示例差不多,就不說了。

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

        驗(yàn)證 git commit 規(guī)范,主要通過 git 的 pre-commit 鉤子函數(shù)來進(jìn)行。當(dāng)然,你還需要下載一個(gè)輔助工具來幫助你進(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)在來解釋下各個(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 編寫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ù)的編譯問題,但是還沒發(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 編寫Entry入口

        3. 自動(dòng)化

        3.1 文檔自動(dòng)化

        文檔自動(dòng)化其實(shí)就是根據(jù)代碼自動(dòng)生成開發(fā)文檔。比如element3項(xiàng)目中的。https://element3-ui.com/其實(shí)可以用StoryBook。這個(gè)我們后面寫專題更新。大家保持關(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)目就按照這種寫法就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ù)集成

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

        獲取持續(xù)集成通過徽標(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è)開源的測(cè)試結(jié)果展示平臺(tái),將測(cè)試結(jié)果可視化。Github上許多開源項(xiàng)目都使用了Codecov來展示單測(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 開源許可證

        每個(gè)開源項(xiàng)目都需要配置一份合適的開源許可證來告知所有瀏覽過我們的項(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)記和說明,這些小圖標(biāo)給項(xiàng)目增色不少,不僅簡(jiǎn)單美觀,而且還包含清晰易懂的信息。

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

        4.3 申請(qǐng)開源徽標(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在開發(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插件開發(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á)最大的支持!


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

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲骚| 午夜色电影| 九一福利 | 成人黄色电影在线观看入口 | 美女视频黄a视频全免费观看 | 久久精品国产亚洲夜色av网站 | 做爰过程很黄口述 | 日韩欧美一区视频 | bbww性欧美 | 久久久久成人精品无码 |