Author: Ran Shu Hua Guo Shan

Element3 Component library engineering practice

With the continuous improvement of front-end functions and performance, front-end is no longer a section of embedded javascript code in the page. It has evolved into a system of complex engineering. Here I combine my experience with building the Element3 component library. Take you to build a mini version of the component library.

Github.com/hug-sun/min…

What is front-end engineering

Overview of front-end engineering juejin.cn/post/684490…

Front-end engineering can be divided into four aspects.

  1. modular

A file is divided into multiple interdependent files, which are packaged and loaded in a unified manner to ensure efficient multi-user collaboration.

  • CMD AMD CommonJS and ES6 Module
  • CSS module Sass Less Stylus
  • Resources modular files, CSS, and images are associated with each other through JS
  1. componentization

In contrast to file splitting, components are split at the UI level. Each component needs to include corresponding CSS, images, JS logic, view templates, etc., and can complete an independent function.Automation 2.

  • debugging
  • compile
  • The deployment of
  • test
  • document
  1. normative
    • Project directory structure
    • Grammar tip
    • Coding style specification
    • Alignment specification
    • File naming conventions
    • Code style specification
    • git flow

Two, actual combat steps

1. Develop specifications

  • JS code specification
    • Reality – Chinese version
    • Standard (24.5K STAR) Chinese version
    • Baidu front-end coding specification 3.9K
  • CSS code specification
    • Styleguide 2.3 k
    • The spec 3.9 k

1.1 Project directory structure

. ├ ─ ─ the build# compile script├ ─ ─ coverage# Coverage report├ ─ ─ examples# Code examples├ ─ ─ lib# CSS style after compiling├ ─ ─ node_modules ├ ─ ─ packages# Component code├ ─ ─ a rollup plugin - vue ├ ─ ─ scriptsCheck the script release and submission information├ ─ ─ the SRC# Common code├ ─ ─test								# test└ ─ ─ types# TS type definition
Copy the code

1.2 File naming conventions

.├ ── Button │ ├─ Button.vue# component SFC│ ├── ├─ ├.spec# test file│ └ ─ ─ index. JsComponent entry
Copy the code

1.3 Code Style Specification (ESLint)

  • JS code specification
    • Reality – Chinese version
    • Standard (24.5K STAR) Chinese version
    • Baidu front-end coding specification 3.9K
  • CSS code specification
    • Styleguide 2.3 k
    • The spec 3.9 k
# .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'
  }
}
Copy the code
# .eslintignoresrc/utils/popper.js src/utils/date.js examples/play *.sh node_modules lib coverage *.md *.scss *.woff *.ttf src/index.js  distCopy the code
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
Copy the code

package.json

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

1.6 Git version specifications

Branch management

General projects are divided into master branches and other branches. When a team member wants to Feather a new feature or Fix a BUG, start a new branch from the Master branch. For example, you should branch a Bug with the Bug number (e.g. [Fix:12323]).

Commit specification

  • The contents
<type>(<scope>) :<subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>Copy the codeCopy the code

Roughly divided into three parts (separated by blank lines):

  1. Title line: Mandatory, describes the main modification type and content
  2. Topic content: describe why the changes were made, what changes were made, and the development ideas, etc
  3. Footer comments: you can write comments, BUG number links
  • Type: Indicates the type of the commit
    • Feat: New features, new features
    • Fix: fix bugs
    • Perf: Changes code to improve performance
    • Refactor: code refactoring (refactoring, code changes that do not affect the internal behavior or functionality of the code)
    • Docs: Document modification
    • Style: code format changes, note not CSS changes (e.g., semicolon changes)
    • Test: A test case is added or modified
    • Build: Affects project builds or dependency changes
    • Revert: Restores a previous submission
    • Ci: Continuous integration related file modification
    • Chore: Other Modifications (modifications not in the above type)
    • Release: Releases a new version
    • Workflow: Modifies workflow-related files
  1. Scope: commit Specifies the scope of the impact, for example: route, Component, utils, build…
  2. Subject: Overview of commit
  3. Body: commit The modified content can be divided into multiple lines.
  4. Footer: Some notes, usually a link to BREAKING CHANGE or bug fixes.

The sample

Fix a BUG

If the fixed BUG only affects the currently modified file, the scope is not added. If the scope of influence is relatively large, the scope description should be added.

For example, if this BUG fix affects the whole world, add global. If a directory or function is affected, you can add the path of the directory or the function name.

/ / sample 1
fix(global): Fix checkbox failure// Common in parentheses below example 2 is the name of the common administrationFix (Common): Fix the BUG of too small fonts by changing the default font size for all pages under general management to 14px/ / sample 3
fix: value.length -> value.length copies the codeCopy the code
Feat (Add a new feature or page)
Feat: Add static Page for site Home Page This is an example, assuming some description of the static page for the dot check task. Here is a note, which can be a BUG link or something important. Copy the codeCopy the code
Chore (Other Modifications)

The Chinese translation of chore is daily affairs and routine work. As the name implies, the modifications that are not in other commit types can be expressed as chore.

Chore: Changes the view details in the table to the detail copy codeCopy the code

The other types of COMMIT are similar to the three examples above, but not to mention.

Automated submit validation

Verify the Git commit specification by using git’s pre-commit hook function. Of course, you’ll also need to download an assistive tool to help you verify.

Download tools

npm i -D husky
Copy the code

Add the following code to package.json

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

Create a script folder in your project root directory and create a verify-commit.js file under it. Type the following code:

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(Invalid COMMIT message format. Please see the git commit to submit specifications: https://github.com/woai3c/Front-end-articles/blob/master/git%20commit%20style.md `)

    process.exit(1} copy the codeCopy the code

Now let’s explain what each hook means:

  1. "pre-commit": "npm run lint"In thegit commitFormer executivenpm run lintCheck the code format.
  2. "commit-msg": "node script/verify-commit.js"In thegit commitExecute the scriptverify-commit.jsValidate the COMMIT message. If it does not conform to the format defined in the script, an error will be reported.
  3. "pre-push": "npm test"Before you executegit pushExecute before pushing the code to the remote repositorynpm testTest. If the test fails, this push will not be executed.

/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 the 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) } }Copy the code

Modularization and componentization

npm init -y
Copy the code

Github.com/cuixiaorui/… The resources

2.1 Write the Buttun component

yarn add vue@next
Copy the code

/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>
Copy the code

2.2 integrated 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
Copy the code

Create a.babelrc file

{
  "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}}}}]]Copy the code

2.2 integrated VTU

Install dependencies

yarn add jest
This version supports Vue3.0Yarn add [email protected] yarn add babel-jest yarn add @vue/[email protected] yarn add @vue/test-utils@next yarn add typescriptCopy the code

jest.config.js

module.exports = {
  testEnvironment: 'jsdom', / / the default JSdom
  roots: [
    '<rootDir>/src',
    '<rootDir>/packages',
  ], // 
  transform: {
    '^.+\\.vue$': 'vue-jest', // vue single file
    '^.+\\js$': 'babel-jest' // The latest esM syntax import
  },
  moduleFileExtensions: ['vue', 'js', 'json', 'jsx', 'ts', 'tsx', 'node'],
  testMatch: ['**/__tests__/ * * /*.spec.js'],
  / / alias
  moduleNameMapper: {
    '^element-ui(.*)$': '<rootDir>$1',
    '^main(.*)$': '<rootDir>/src$1'}}Copy the code

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

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

it("content".() = > {
  const Comp = {
    template: 
      
'
};const wrapper = mount(Comp, { global: { components: { Button, }, }, }); expect(wrapper.findComponent({ name: "ElButton" }).text()).toContain( "Default button" ); }); 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"); }); Copy the code

test

"Test ": "jest --runInBand", # serialization executionCopy the code

2.4 Style Packaging

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

# cp-cli
yarn add cp-cli
yarn add tslib
Copy the code

/bin/gen-cssfile

package.json

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

2.4 a Rollup packaging

www.rollupjs.com/ Rollup juejin.cn/post/684490… Package JS using rollup

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-vueCopy the code

Package.json

"build:next": "rollup -c",
Copy the code
import pkg from './package.json'
// Wait for rollup-plugin-vue to change the official version
// Use the local rollup-plugin-vue for now
// Fixed the render function compilation issue, but hasn't been released yet
// 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) The ${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()
Copy the code

2.3 Writing Entry Entry

3. The automation

3.1 Document Automation

Document automation is the automatic generation of development documents from code. Such as in the Element3 project.element3-ui.com/ You can actually use a StoryBook. We will write a thematic update on this later. Stay tuned.

3.2 Specification Inspection

yarn add husky
Copy the code

.huskyrc

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

3.4 Regression test

GitHub Action

.github/workflows/main.yml
Copy the code

3.3 Continuous integration CI

Travis CI provides a continuous integration service that only supports Github and does not support other code hosting. It needs to be tied to a project on Github, and it also needs to contain build or test scripts. As soon as there is new code, it will be automatically fetched. Then, provide a virtual machine environment, perform tests, complete builds, and deploy to the server. Run builds and tests automatically whenever the code changes and feed back the results. After making sure that you are as expected, integrate the new code into the trunk.

The project requires Travis to automatically test and provide codecov with a test report after submission.

  • test
  • The report analysis

Check out the TravicCI website

Go to www.travis-ci.org/

Log in to the system as user Github

Configuration. Travis. Yml

Run the automated test framework

language: node_js               The node project is written in this way
node_js:
- 13.2. 0 			# Project environment
cache:				# Cache node_js dependencies to improve the efficiency of the second build
  directories:
  - node_modules
test:
  - npm run test Run the automated test framework
Copy the code

Reference: Travis CI Tutorial

Upload the configuration to Github

Start continuous integration

Log in to Travis using a Github account

Get continuous integration through the logo

Replace {GitHub user name} and {project name} in the above URL with your own project. Finally, you can paste the Markdown code after integration on your own project

http://img.shields.io/travis/ {making user name} / {item name}. The SVG duplicate codeCopy the code

3.5 Continuous Delivery of CDS – Upload the Npm library

Creating a publishing script

publish.sh

#! /usr/bin/env bash
npm config get registry Check the repository mirror
npm config set registry=http://registry.npmjs.org
echo 'Please perform login related operations:'
npm login # landing
echo "-------publishing-------"
npm publish # release
npm config set registry=https://registry.npm.taobao.org # Set to Taobao mirror
echo "Release completed"
exit
Copy the code

Perform publish

/publish.sh Copies the codeCopy the code

Enter the Github user name and password

3.7 Coverage test Codecov

Codecov is an open source test results presentation platform that visualizes test results. Codecov is used by many open source projects on Github to present single test results. Codecov, like Travis CI, supports Github login and syncs projects on Github.

yarn add codecov
Copy the code
"scripts": { ... , "codecov": "codecov" }Copy the code

4. Other

4.1 Standard README documents

4.2 Open Source Licenses

Every open source project needs to be equipped with an appropriate open source license to inform all users who browse our project what permissions they have. For specific license selection, please refer to this chart drawn by ruan Yifeng:

So how do we add licenses to our projects? In fact, Github already provides us with a very easy visualization: when we visit Github, we find that many projects add logos to readme. md to mark and explain the project. These small ICONS add a lot of color to the project, not only simple and beautiful, but also contain clear and understandable information.

  1. Open our open source project and switch to the Insights panel
  2. Click on the Community TAB
  3. If no License is added to your project, you will be prompted to Add License in the Checklist. Click the Add button to enter the visual operation process

4.3 Applying for Open Source Logo (Badge)

Making the badge docs.github.com/cn/free-pro…

Third, the appendix

3.1 Vue Components and plug-ins

<! DOCTYPEhtml>
<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>'};// Add a plug-in
      MyButton.install = (app) = > app.component("MyButton", MyButton);

      / / component library
      const Element = {
          MyButton,
          install: app= > {
              app.use(MyButton)
          }
      }

      const MyComponent = {
        template: ` 
        `}; createApp(MyComponent)// .use(MyButton)
        .use(Element)
        .mount("#app");
    </script>
  </body>
</html>
Copy the code

3.2 a rollup packaging

Rollup is a compact javascript module packaging tool that is better suited for building library applications. The ability to compile small chunks of code into large, complex chunks of code, based on ES6 modules, minimizes your bundle, effectively reducing the size of file requests. Vue was developed using WebPack, but packaged together using rollup.js

First published on personal blog

  • Rollup official documentation
  • rollupGithub

Juejin. Cn/post / 684490… A Rollup basis

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>'};Copy the code

The entrance

/src/entry.js

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

// Add a plug-in
MyButton.install = (app) = > app.component("MyButton", MyButton);
SfcButton.install = (app) = > app.component("SfcButton", SfcButton);
JsxButton.install = (app) = > app.component("JsxButton", JsxButton);

/ / component library
const Element = {
  MyButton,
  SfcButton,
  JsxButton,
  install: (app) = >{ app.use(MyButton); app.use(SfcButton); app.use(JsxButton); }};export default Element;
Copy the code

Format statement

Juejin. Cn/post / 688554… AMD CMD UMD difference

  • Amd – Asynchronous module definition for module loaders like RequireJS
  • CJS — CommonJS, for Node and Browserify/Webpack
  • Es – Save the package as an ES module file
  • Iife – an auto-executing feature, suitable as<script>The label. (If you’re creating a bundle for your application, you might want to use it because it makes the file size smaller.)
  • Umd – Common module definition, amd, CJS and IIFE all in one
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];
Copy the code

The test page

<! DOCTYPEhtml>
<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: ` 
        
        
        `}; createApp(MyComponent) .use(Element) .mount("#app");
    </script>
  </body>
</html>
Copy the code

Single file component

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

JSX support

The definition of JSX

JSX is an XML-like extension to JavaScript syntax JSX is not implemented by engines or browsers. Instead, we’ll use a converter like Babel to convert JSX to regular JavaScript. Basically, JSX allows us to use HTML-like syntax in JavaScript.

The advantage of the JSX

  1. The template can be separated so that each part of the template is more independent and can be combined randomly for higher reusability. Finer granularity than composition with components
  2. Using JS to configure the DOM for each item to render, more dynamically configurable
import babel from "@rollup/plugin-babel";
# plugin
babel(),
Copy the code
<script>
export default {
  name: "JsxButton".render() {
    return <button>JSX 666</button>; }};</script>
Copy the code

3.3 VUE-CLI plug-in development

Please refer to the juejin. Cn/post / 689933…

Stay tuned to Element3 and the Blossom Hill team as we continue to update the best content. Also welcome star and PR