Инженерный бой библиотеки компонентов Vue3 --Element3

Vue.js
Инженерный бой библиотеки компонентов Vue3 --Element3

Автор этой статьи:Дядя Хуагуошанран

Практика разработки библиотеки компонентов Element3

Благодаря постоянному совершенствованию интерфейсных функций и производительности интерфейс больше не является частью кода JS, встроенного в страницу. Он превратился в систематически сложный проект. Далее я объединим опыт построения библиотеки компонентов element3. Возьмите вас, чтобы построить мини-версию библиотеки компонентов.

GitHub.com/Ху Гэ-Сан/Мин…

1. Что такое фронтенд-инжиниринг?

Обзор интерфейсной разработкиnuggets.capable/post/684490…

Фронтенд-инжиниринг можно условно разделить на четыре аспекта.

  1. модульный

Файл разбивается на несколько взаимозависимых файлов и, наконец, упаковывается и загружается единообразно, чтобы обеспечить эффективную совместную работу нескольких человек.

  • Модуль JS CMD Модуль AMD CommonJS и ES6
  • Модули CSS Sass Less Stylus
  • Модульность ресурсов. Документы, CSS и изображения унифицированы и связаны с помощью JS.
  1. составной

По сравнению с разделением файлов компоненты разделены на уровне пользовательского интерфейса.Каждый компонент должен включать соответствующий CSS, изображения, логику JS, шаблоны просмотра и т. д. и может выполнять независимую функцию.2. Автоматизация

  • отладка
  • компилировать
  • развертывать
  • тестовое задание
  • Задокументировано
  1. нормативный
    • Структура каталогов проекта
    • подсказки по грамматике
    • Рекомендации по стилю кодирования
    • Совместная спецификация отладки
    • соглашение об именах файлов
    • Рекомендации по стилю кода
    • git flow

2. Практические шаги

1. Спецификации разработки

1.1 Структура каталога проекта

.
├── build 							# 编译脚本
├── coverage      			# 覆盖率报告
├── examples 						# 代码范例
├── lib									# CSS样式 编译后
├── node_modules	
├── packages						# 组件代码
├── rollup-plugin-vue
├── scripts							# 脚本 发布、提交信息检查
├── src									# 通用代码
├── test								# 测试
└── types								# TS类型定义

1.2 Соглашения об именах файлов

.
├── button               				
│   ├── Button.vue 							# 组件SFC
│   ├── __tests__								
│   │   └── Button.spec.js			# 测试文件
│   └── index.js								# 组件入口

1.3 Спецификация стиля кода (ESLint)

# .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

управление филиалом

Общий проект делится на основную ветку (мастер) и другие ветки. Когда член команды хочет разработать новую функцию (Feather) или изменить ОШИБКУ (Fix), новая ветка открывается из основной ветки. Например, если вы изменяете ошибку, вы должны использовать номер ошибки в качестве ветви (например: [Fix:12323]).

Зафиксировать спецификацию

  • Спецификация содержания
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

Грубо разделен на три части (разделенные пустыми строками):

  1. Строка заголовка: обязательна, опишите основной тип модификации и содержание
  2. Содержание темы: Опишите, почему была сделана модификация, какая модификация была сделана, а также идеи развития и т. д.
  3. Примечания в нижнем колонтитуле: вы можете писать заметки, ссылка на номер ОШИБКИ
  • тип: тип фиксации
    • подвиг: новые функции, новые функции
    • исправить: исправить ошибку
    • perf: изменения кода для повышения производительности
    • рефакторинг: рефакторинг кода (рефакторинг, модификация кода без влияния на внутреннее поведение и функции кода)
    • документы: Изменения в документации
    • стиль: модификация формата кода, обратите внимание, что это не модификация css (например, модификация точки с запятой)
    • test: добавлять и изменять тестовые случаи
    • build: влияет на сборку проекта или модификацию зависимостей
    • revert: вернуть последнюю фиксацию
    • ci: изменение файла, связанное с непрерывной интеграцией
    • рутинная работа: другие модификации (модификации, не относящиеся к вышеперечисленным типам)
    • релиз: выпустить новую версию
    • рабочий процесс: изменение файла, связанное с рабочим процессом
  1. scope: область затронутой фиксации, например: маршрут, компонент, утилиты, сборка...
  2. тема: обзор коммитов
  3. тело: зафиксируйте конкретное содержимое модификации, которое можно разделить на несколько строк.
  4. нижний колонтитул: некоторые примечания, обычно ссылка на КРИТИЧЕСКОЕ ИЗМЕНЕНИЕ или исправленную ошибку.

Пример

исправить (исправить ОШИБКУ)

Если исправляемая ошибка влияет только на изменяемый в данный момент файл, область действия не требуется. Если сфера влияния относительно велика, следует добавить описание сферы влияния.

Например, это исправление ошибки влияет на файл global, вы можете добавить файл global. Если это влияет на определенный каталог или определенную функцию, вы можете добавить путь к каталогу или соответствующее имя функции.

// 示例1
fix(global):修复checkbox不能复选的问题
// 示例2 下面圆括号里的 common 为通用管理的名称
fix(common): 修复字体过小的BUG,将通用管理下所有页面的默认字体大小修改为 14px
// 示例3
fix: value.length -> values.length

подвиг (добавление новых функций или новых страниц)
feat: 添加网站主页静态页面

这是一个示例,假设对点检任务静态页面进行了一些描述。
 
这里是备注,可以是放BUG链接或者一些重要性的东西。

работа (другие модификации)

В переводе с китайского chore — повседневные дела и рутинная работа, как следует из названия, то есть модификации, которых нет в других типах коммитов, могут быть представлены chore.

chore: 将表格中的查看详情改为详情

Другие типы коммитов аналогичны приведенным выше трем примерам, поэтому я не буду о них говорить.

Автоматическая проверка отправки

Проверьте спецификацию фиксации git, в основном через gitpre-commitфункция крючка. Конечно, вам также потребуется загрузить вспомогательный инструмент, который поможет вам проверить.

скачать вспомогательные средства

npm i -D husky

существуетpackage.jsonдобавьте следующий код

"husky": {
  "hooks": {
    "pre-commit": "npm run lint",
    "commit-msg": "node script/verify-commit.js",
    "pre-push": "npm test"
  }
}

Затем создайте новую папку в корневом каталоге вашего проекта.script, и создайте новый файл ниже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 消息格式。
        请查看 git commit 提交规范:https://github.com/woai3c/Front-end-articles/blob/master/git%20commit%20style.md
    `)

    process.exit(1)
}

Теперь, чтобы объяснить значение каждого хука:

  1. "pre-commit": "npm run lint",существуетgit commitперед исполнениемnpm run lintПроверьте формат кода.
  2. "commit-msg": "node script/verify-commit.js",существуетgit commitВыполнить скрипт, когдаverify-commit.jsПодтвердите сообщение фиксации. Если он не соответствует формату, определенному в скрипте, будет сообщено об ошибке.
  3. "pre-push": "npm test", после выполненияgit pushПеред отправкой кода в удаленный репозиторий выполнитеnpm testпровести тестирование. Если тест не пройден, отправка не будет выполнена.

/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

GitHub.com/Цуй Сяоруй/…использованная литература

2.1 Написание компонента Buttun

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: {
      type: String,
      default: "default",
    },
    size: {
      type: String,
      default: "",
    },
    icon: {
      type: String,
      default: "",
    },
    nativeType: {
      type: String,
      default: "button",
    },
    loading: Boolean,
    disabled: Boolean,
    plain: Boolean,
    autofocus: Boolean,
    round: Boolean,
    circle: Boolean,
  },
  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 Встроенный ВТУ

Установить зависимости

yarn add jest
# 此版本这个支持Vue3.0
yarn add vue-jest@5.0.0-alpha.5
yarn add babel-jest													
yarn add @vue/compiler-sfc@3.0.2
yarn add @vue/test-utils@next
yarn add typescript

jest.config.js

module.exports = {
  testEnvironment: 'jsdom', // 默认JSdom
  roots: [
    '<rootDir>/src',
    '<rootDir>/packages',
  ], // 
  transform: {
    '^.+\\.vue$': 'vue-jest', // vue单文件
    '^.+\\js$': 'babel-jest' // esm最新语法 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>默认按钮</Button></div>`,
  };

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

  expect(wrapper.findComponent({ name: "ElButton" }).text()).toContain(
    "默认按钮"
  );
});

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: {
      plain: true,
    },
  });

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

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

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

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

  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: {
        loading: true,
        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: {
        disabled: true,
      },
    });

    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: {
            disabled: true,
          },
        },
      },
    });

    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: {
        loading: true,
      },
    });

    wrapper.trigger("click");

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

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

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

тестовое задание

"test": "jest --runInBand", # 序列化执行

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 Свернутая упаковка

www.rollupjs.com/Свернуть китайский сайтnuggets.capable/post/684490…Свяжите 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 发版后在切换官方版
// 暂时先用本地的 rollup-plugin-vue
// 修复了 render 函数的编译问题,但是还没发版
// 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({
        css: true
      }),
      scss()
    ],
    output: {
      sourcemap: false,
      banner: createBanner(),
      externalLiveBindings: false,
      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 запись записи записи

3. Автоматизация

3.1 Автоматизация документов

Автоматизация документации — это автоматическое создание документации для разработки на основе кода. Например, в проекте element3.element3-ui.com/ image.pngНа самом деле, вы можете использовать StoryBook. Позже мы напишем об этом специальное обновление. Оставайтесь с нами.

3.2 Проверка спецификации

yarn add husky

.huskyrc

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

3.4 Регрессионное тестирование

GitHub Action

.github/workflows/main.yml

3.3 Непрерывная интеграция CI

Travis CI предоставляет службу непрерывной интеграции, которая поддерживает только Github и не поддерживает другой хостинг кода. Его необходимо привязать к проекту на Github, а также необходимо, чтобы проект содержал сценарии сборки или тестирования. Всякий раз, когда появляется новый код, он будет автоматически сканироваться. Затем предоставьте среду виртуальной машины, выполните тесты, завершите сборку и выполните развертывание на сервере. Как только код изменяется, автоматически запускаются сборки и тесты, и сообщается о результатах. Убедившись, что это соответствует ожиданиям, интегрируйте новый код в магистраль.

Этот проект требует, чтобы Трэвис автоматически тестировал после отправки и предоставлял отчеты о тестировании codecov.

  • тестовое задание
  • анализ отчета

Войти на сайт TravicCI

Авторизоватьсяwww.travis-ci.org/Веб-сайт

Войдите в систему под своей учетной записью github.

настроить.travis.yml

Запуск автоматизированных тестовых фреймворков

language: node_js               # 项目语言,node 项目就按照这种写法就OK了
node_js:
- 13.2.0 			# 项目环境
cache:				# 缓存 node_js 依赖,提升第二次构建的效率
  directories:
  - node_modules
test:
  - npm run test # 运行自动测试框架

Справочное руководство:Travis CI Tutorial

Залить конфигурацию на гитхаб

Начать непрерывную интеграцию

Войдите в travis через учетную запись github

Получить логотип Continuous Integration Pass

Просто замените {имя пользователя GitHub} и {имя проекта} в приведенном выше URL-адресе на те, которые относятся к вашему собственному проекту, и, наконец, вы сможете вставить интегрированный код уценки в свой собственный проект.

http://img.shields.io/travis/{GitHub 用户名}/{项目名称}.svg

3.5 Компакт-диск непрерывной доставки — загрузка репозитория Npm

Создать сценарий выпуска

publish.sh

#!/usr/bin/env bash
npm config get registry # 检查仓库镜像库
npm config set registry=http://registry.npmjs.org
echo '请进行登录相关操作:'
npm login # 登陆
echo "-------publishing-------"
npm publish # 发布
npm config set registry=https://registry.npm.taobao.org # 设置为淘宝镜像
echo "发布完成"
exit

Выполнить выпуск

./publish.sh

После заполнения имени пользователя и пароля github

3.7 Тест покрытия Codecov

Codecov — это платформа для отображения результатов тестирования с открытым исходным кодом, которая визуализирует результаты тестирования. Многие проекты с открытым исходным кодом на Github используют Codecov для отображения результатов отдельных тестов. Как и Travis CI, Codecov поддерживает вход в учетную запись Github, а также синхронизирует проекты в Github.

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

4. Другое

4.1 Стандартный документ README

4.2 Лицензия с открытым исходным кодом

Каждый проект с открытым исходным кодом должен быть настроен с соответствующей лицензией с открытым исходным кодом, чтобы информировать всех пользователей, которые просматривали наш проект, какие разрешения у них есть.Чтобы выбрать конкретные лицензии, обратитесь к этой таблице, нарисованной старшим Руаном Ифэном:

Так как же нам добавить лицензию в наш проект? Фактически, Github предоставил нам очень простую визуальную операцию: когда мы обычно посещаем веб-сайт github, мы обнаруживаем, что многие проектыREADME.mdДобавьте логотип, чтобы обозначить и описать проект, эти маленькие значки многое добавляют в проект, который не только прост и красив, но и содержит четкую и понятную информацию.

  1. Наш открытый и переключатель на проекты с открытым исходным кодомInsightsпанель
  2. Нажмите на вкладку Сообщество
  3. Если в ваш проект не добавлена ​​лицензия, вChecklistВам будет предложено добавить лицензию, нажмитеAddкнопка для входа в визуальный процесс работы

4.3 Подать заявку на логотип с открытым исходным кодом (значок)

Значок GitHubdocs.GitHub.com/capable/free-pro…

3. Приложение

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",
        data: function () {
          return {
            count: 0,
          };
        },
        template:
          '<button v-on:click="count++">You clicked me {{ count }} times.</button>',
      };

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

      // 组件库
      const Element = {
          MyButton,
          install: app => {
              app.use(MyButton)
          }
      }

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

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

3.2 рулонная упаковка

rollup — это небольшой инструмент для упаковки модулей javascript, который больше подходит для создания инструментов для библиотечных приложений, он может компилировать небольшие блоки кода в большой и сложный код, основанный на модулях ES6, он может минимизировать ваш пакет и эффективно уменьшить размер файловых запросов. ,vueЯ использовал webpack для разработки, но в конце концов для упаковки файлов использовался rollup.js.

впервые опубликовано вличный блог

nuggets.capable/post/684490…Основы сведения

Button

/src/MyButton.js

export default {
  name: "MyButton",
  data: function () {
    return {
      count: 0,
    };
  },
  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);

// 组件库
const Element = {
  MyButton,
  SfcButton,
  JsxButton,
  install: (app) => {
    app.use(MyButton);
    app.use(SfcButton);
    app.use(JsxButton);
  },
};

export default Element;

объявление формата

Наггетс.Талант/пост/688554…Отличие AMD CMD UMD

  • amd — определение асинхронного модуля для использования с загрузчиками модулей, такими как RequireJS.
  • cjs — CommonJS, для Node и Browserify/Webpack
  • es — сохранить пакет как файл модуля ES
  • iife — автоматически выполняющаяся функция, подходящая как<script>Этикетка. (Если вы создаете пакет для своего приложения, вы можете использовать это, так как это уменьшит размер файла.)
  • 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({
      css: true,
    }),
  ],
};

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({
      css: true,
    }),
    terser(),
  ],
};

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

export default [es, minEs, cjs];

тестовая страница

<!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. JSX не реализуется движком или браузером. Вместо этого мы будем использовать преобразователь, такой как Babel, для преобразования JSX в обычный JavaScript. По сути, JSX позволяет нам использовать HTML-подобный синтаксис в JavaScript.

Преимущества jsx

  1. Шаблоны можно разделить, чтобы каждая часть шаблона была более независимой, и их можно комбинировать случайным образом для более высокой степени повторного использования. Более мелкая зернистость, чем композиция с компонентами
  2. Используйте js для настройки отображения dom для каждого элемента, что является более динамичным и настраиваемым.
import babel from "@rollup/plugin-babel";
# plugin
babel(),
<script>
export default {
  name: "JsxButton",
  render() {
    return <button>JSX 666</button>;
  },
};
</script>

3.3 Разработка плагина Vue-cli

Пожалуйста, обратитесь кНаггетс.Талант/пост/689933…

Каждый может следоватьElement3а такжеКоманда ХуагошаньМы будем продолжать обновлять лучший контент. звезда и пр тоже приветствуются