link

  • Element source architecture mind map version
  • Element source architecture video version
  • github
  • How to quickly Build your own component library for your team (Part 2) — Build your own component library for your team based on Element-UI

How to quickly build your own component library for your team?

Component library is an indispensable infrastructure in modern front-end domain. It can improve code reusability and maintainability, improve team productivity, and better serve the future.

So how do you build your own component library for your team? The ideal solution is to borrow the community’s ability to tailor a good open source library, keeping only the things you need, such as its architectural, engineering and documentation capabilities, and some of its basic components. In the process of tailoring, you may find some problems with it, and then optimize and solve them in your component library.

Element source architecture

Because the technical stack of the team is Vue, we chose to carry out secondary development based on Element. Before starting, we analyzed the source code of Element framework in detail to store knowledge for building the component library. The source code of element framework consists of engineering, official website, component library, testing and type declaration.

engineering

Element’s architecture is really good, with a lot of scripting and good engineering, and a commitment to letting component library developers focus on the thing. For example, when adding a new component, one click to generate all the files of the component and complete the basic structure of these files and related import configuration. A total of 13 files are added and changed, and you only need to complete the component definition. The engineering of Element consists of five parts: the engineering configuration and scripts in the build directory, esLint, Travis CI, Makefile, package.json scripts.

build

The build directory stores engineering configurations and scripts. For example, javascript scripts in the /build/bin directory allow the component library developer to focus on the development of the component and do nothing else; Build/MD-loader is the key to implement component Demo + documents according to Markdown. There are also examples such as continuous integration, WebPack configuration, etc. These configurations and scripts are described in detail below.

/build/bin/build-entry.js

Component configuration file (components.json) combines with the string template library to automatically generate/SRC /index.js file, avoiding manually importing and exporting components in/SRC /index.js each time a new component is added.

/** * create/SRC /index.js */

// Key is the package name and path is the value
var Components = require('.. /.. /components.json');
var fs = require('fs');
/ / template library
var render = require('json-templater/string');
// Convert a comp-name string to CompName
var uppercamelcase = require('uppercamelcase');
var path = require('path');
var endOfLine = require('os').EOL;

// Output path/SRC /index.js
var OUTPUT_PATH = path.join(__dirname, '.. /.. /src/index.js');
// Import CompName from '.. /packages/comp-name/index.js'
var IMPORT_TEMPLATE = 'import {{name}} from \'.. /packages/{{package}}/index.js\'; ';
// ' CompName'
var INSTALL_COMPONENT_TEMPLATE = ' {{name}}';
// / SRC /index.js template
var MAIN_TEMPLATE = `/* Automatically generated by './build/bin/build-entry.js' */ {{include}} import locale from 'element-ui/src/locale'; import CollapseTransition from 'element-ui/src/transitions/collapse-transition'; const components = [ {{install}}, CollapseTransition ]; const install = function(Vue, opts = {}) { locale.use(opts.locale); locale.i18n(opts.i18n); components.forEach(component => { Vue.component(component.name, component); }); Vue.use(InfiniteScroll); Vue.use(Loading.directive); Vue.prototype.$ELEMENT = { size: opts.size || '', zIndex: opts.zIndex || 2000 }; Vue.prototype.$loading = Loading.service; Vue.prototype.$msgbox = MessageBox; Vue.prototype.$alert = MessageBox.alert; Vue.prototype.$confirm = MessageBox.confirm; Vue.prototype.$prompt = MessageBox.prompt; Vue.prototype.$notify = Notification; Vue.prototype.$message = Message; }; /* istanbul ignore if */ if (typeof window ! == 'undefined' && window.Vue) { install(window.Vue); } export default { version: '{{version}}', locale: locale.use, i18n: locale.i18n, install, CollapseTransition, Loading, {{list}} }; `;

delete Components.font;

// Get all package names, [comp-name1, comp-name2]
var ComponentNames = Object.keys(Components);

// Store all import statements
var includeComponentTemplate = [];
// Array of component names
var installTemplate = [];
// Array of component names
var listTemplate = [];

// Iterate over all package names
ComponentNames.forEach(name= > {
  // Convert the hyphenated package name to a big hump, that is, the component name, such as form-item = FormItem
  var componentName = uppercamelcase(name);

  Import FromItem from '.. import FromItem from '... /packages/form-item/index.js'
  includeComponentTemplate.push(render(IMPORT_TEMPLATE, {
    name: componentName,
    package: name
  }));

  // These components are removed from the Components array, do not require global registration, and are mounted to the prototype chain, as written in the install method of the template string
  if (['Loading'.'MessageBox'.'Notification'.'Message'.'InfiniteScroll'].indexOf(componentName) === -1) {
    installTemplate.push(render(INSTALL_COMPONENT_TEMPLATE, {
      name: componentName,
      component: name
    }));
  }

  // Put all the components into listTemplates and export them
  if(componentName ! = ='Loading') listTemplate.push(`  ${componentName}`);
});

// Replace four variables in the template
var template = render(MAIN_TEMPLATE, {
  include: includeComponentTemplate.join(endOfLine),
  install: installTemplate.join(', ' + endOfLine),
  version: process.env.VERSION || require('.. /.. /package.json').version,
  list: listTemplate.join(', ' + endOfLine)
});

// Write the ready template to/SRC /index.js
fs.writeFileSync(OUTPUT_PATH, template);
console.log('[build entry] DONE:', OUTPUT_PATH);

Copy the code

/build/bin/build-locale.js

Babel translates all ES Module style translation files (/ SRC /locale/lang) to UMD style.

/** * Use Babel to translate ES Module style files into UMD style */
var fs = require('fs');
var save = require('file-save');
var resolve = require('path').resolve;
var basename = require('path').basename;

// Translate the file directory for the official website
var localePath = resolve(__dirname, '.. /.. /src/locale/lang');
// Get all the translation files in the directory
var fileList = fs.readdirSync(localePath);

// Conversion function
var transform = function(filename, name, cb) {
  require('babel-core').transformFile(resolve(localePath, filename), {
    plugins: [
      'add-module-exports'['transform-es2015-modules-umd', {loose: true}]],moduleId: name
  }, cb);
};

// Walk through all the files
fileList
  // Only js files are processed. There are no non-JS files in the directory
  .filter(function(file) {
    return /\.js$/.test(file);
  })
  .forEach(function(file) {
    var name = basename(file, '.js');

    // Call the conversion function and write the converted code to the lib/umd/locale directory
    transform(file, name, function(err, result) {
      if (err) {
        console.error(err);
      } else {
        var code = result.code;

        code = code
          .replace('define(\''.'define(\'element/locale/')
          .replace('global.'.'global.ELEMENT.lang = global.ELEMENT.lang || {}; \n global.ELEMENT.lang.');
        save(resolve(__dirname, '.. /.. /lib/umd/locale', file)).write(code);

        console.log(file); }}); });Copy the code

/build/bin/gen-cssfile.js

Automatic – chalk/packages/theme/SRC/index. The SCSS | CSS style of the introduction of various components package, registered in full amount need to use this style file when the component library, Namely the import ‘- chalk/SRC/packages/theme index. The SCSS.

/ * * * automatically in - chalk/packages/theme/SRC/index. The SCSS introduce each component package | CSS style * registered in full amount when the component libraries need to use the style file, The import '- chalk/SRC/packages/theme index. The SCSS * /
var fs = require('fs');
var path = require('path');
var Components = require('.. /.. /components.json');
var themes = [
  'theme-chalk'
];
// Get all package names
Components = Object.keys(Components);
// Base path for all component packages, /packages
var basepath = path.resolve(__dirname, '.. /.. /packages/');

// Check whether the specified file exists
function fileExists(filePath) {
  try {
    return fs.statSync(filePath).isFile();
  } catch (err) {
    return false; }}/ / to iterate through all the component package, the introduction of all components package style of import statements, and then automatically generated - chalk/SRC/packages/theme index. The SCSS | CSS file
themes.forEach((theme) = > {
  // If it is SCSS, elemental-UI uses SCSS by default
  varisSCSS = theme ! = ='theme-default';
  / / import base style file @ import ". / base. SCSS | CSS "; \n
  var indexContent = isSCSS ? '@import "./base.scss"; \n' : '@import "./base.css"; \n';
  / / to iterate through all the component package, and generate @ import ". / comp - package. SCSS | CSS "; \n
  Components.forEach(function(key) {
    // Skip the three component packages
    if (['icon'.'option'.'option-group'].indexOf(key) > -1) return;
    // comp-package.scss|css
    var fileName = key + (isSCSS ? '.scss' : '.css');
    / / import statements, @ import ". / comp - package. SCSS | CSS "; \n
    indexContent += '@import "./' + fileName + '"; \n';
    // If the package style file does not exist, such as /packages/form-item/theme-chalk/ SRC /form-item. SCSS does not exist, it is considered to be omitted and the file is created
    var filePath = path.resolve(basepath, theme, 'src', fileName);
    if(! fileExists(filePath)) { fs.writeFileSync(filePath,' '.'utf8');
      console.log(theme, 'Create missing', fileName, 'file'); }});/ / generated/packages/theme - chalk/SRC/index. The SCSS | CSS, is responsible for the introduction of all components package style
  fs.writeFileSync(path.resolve(basepath, theme, 'src', isSCSS ? 'index.scss' : 'index.css'), indexContent);
});

Copy the code

/build/bin/i18n.js

Generate.vue files for official website pages in four languages according to templates (/examples/pages/template).

'use strict';

var fs = require('fs');
var path = require('path');
// The official website page translation configuration, built-in four languages
var langConfig = require('.. /.. /examples/i18n/page.json');

// Iterate over all languages
langConfig.forEach(lang= > {
  // Create /examples/pages/{lang}, for example, /examples/pages/ zh-cn
  try {
    fs.statSync(path.resolve(__dirname, `.. /.. /examples/pages/${ lang.lang }`));
  } catch (e) {
    fs.mkdirSync(path.resolve(__dirname, `.. /.. /examples/pages/${ lang.lang }`));
  }

  // Create a.vue file based on page. TPL
  Object.keys(lang.pages).forEach(page= > {
    / / such as/examples/pages/template/index. The TPL
    var templatePath = path.resolve(__dirname, `.. /.. /examples/pages/template/${ page }.tpl`);
    // /examples/pages/zh-CN/index.vue
    var outputPath = path.resolve(__dirname, `.. /.. /examples/pages/${ lang.lang }/${ page }.vue`);
    // Read the template file
    var content = fs.readFileSync(templatePath, 'utf8');
    // Read the configuration of all key-value pairs in the index page
    var pairs = lang.pages[page];

    // Iterate over these key-value pairs, replacing the corresponding key in the template with a regular match
    Object.keys(pairs).forEach(key= > {
      content = content.replace(new RegExp(`<%=\\s*${ key }\\s*>`.'g'), pairs[key]);
    });

    // Write the replacement to the vue file
    fs.writeFileSync(outputPath, content);
  });
});

Copy the code

/build/bin/iconInit.js

According to the selector in the icon. SCSS style file, match all icon names by means of regular matching, and then form these icon names into an array, and write the array into the /examples/icon.json file. This file is used to automatically generate all icon ICONS on the icon page of the official website.

'use strict';

/** * Match all icon names by regular matching according to the selectors in icon. SCSS style file. Json file * this file is used in the icon icon page of the official website to automatically generate all icon ICONS */
var postcss = require('postcss');
var fs = require('fs');
var path = require('path');
// icon. SCSS file contents
var fontFile = fs.readFileSync(path.resolve(__dirname, '.. /.. /packages/theme-chalk/src/icon.scss'), 'utf8');
// Get the style node
var nodes = postcss.parse(fontFile).nodes;
var classList = [];

// Iterate over all the style nodes
nodes.forEach((node) = > {
  // Match the icon name from the selector, such as el-icon-add, to add
  var selector = node.selector || ' ';
  var reg = new RegExp(/\.el-icon-([^:]+):before/);
  var arr = selector.match(reg);

  // Write the icon name to the array,
  if (arr && arr[1]) {
    classList.push(arr[1]); }}); classList.reverse();// You want the CSS file to be sorted in reverse order

// Write an array of icon names to /examples/icon.json
fs.writeFile(path.resolve(__dirname, '.. /.. /examples/icon.json'), JSON.stringify(classList), () = > {});

Copy the code

/build/bin/new-lang.js

Add a new language to the component library, such as FR (French), and configure the language for the related files (components. Json, page.json, route.json, nav.config.json, docs). You only need to translate these English configuration items into the corresponding language in the corresponding file.

'use strict';

/** * Add a new language to the component library, For example, fr (French) * sets the configuration of the language for the files involved (components. Json, page.json, route.json, nav.config.json, docs). You only need to translate these English configuration items into the corresponding language in the corresponding file */

console.log();
process.on('exit'.() = > {
  console.log();
});

if(! process.argv[2]) {
  console.error('[language] is required! ');
  process.exit(1);
}

var fs = require('fs');
const path = require('path');
const fileSave = require('file-save');
const lang = process.argv[2];
// const configPath = path.resolve(__dirname, '.. /.. /examples/i18n', lang);

// Add to components.json
const componentFile = require('.. /.. /examples/i18n/component.json');
if (componentFile.some(item= > item.lang === lang)) {
  console.error(`${lang} already exists.`);
  process.exit(1);
}
let componentNew = Object.assign({}, componentFile.filter(item= > item.lang === 'en-US') [0], { lang });
componentFile.push(componentNew);
fileSave(path.join(__dirname, '.. /.. /examples/i18n/component.json'))
  .write(JSON.stringify(componentFile, null.' '), 'utf8')
  .end('\n');

// Add to page.json
const pageFile = require('.. /.. /examples/i18n/page.json');
// The default configuration of the new language is English, you just need to go to page.json and translate the language configuration into this language
let pageNew = Object.assign({}, pageFile.filter(item= > item.lang === 'en-US') [0], { lang });
pageFile.push(pageNew);
fileSave(path.join(__dirname, '.. /.. /examples/i18n/page.json'))
  .write(JSON.stringify(pageFile, null.' '), 'utf8')
  .end('\n');

// Add to route.json
const routeFile = require('.. /.. /examples/i18n/route.json');
routeFile.push({ lang });
fileSave(path.join(__dirname, '.. /.. /examples/i18n/route.json'))
  .write(JSON.stringify(routeFile, null.' '), 'utf8')
  .end('\n');

// Add to nav.config.json
const navFile = require('.. /.. /examples/nav.config.json');
navFile[lang] = navFile['en-US'];
fileSave(path.join(__dirname, '.. /.. /examples/nav.config.json'))
  .write(JSON.stringify(navFile, null.' '), 'utf8')
  .end('\n');

// create a new folder under docs
try {
  fs.statSync(path.resolve(__dirname, `.. /.. /examples/docs/${ lang }`));
} catch (e) {
  fs.mkdirSync(path.resolve(__dirname, `.. /.. /examples/docs/${ lang }`));
}

console.log('DONE! ');

Copy the code

/build/bin/new.js

This script is used when adding a new component to the component library. It generates all the files of the component in one click and completes the basic structure of these files and related introduction configuration. A total of 13 files are added and changed, such as: Make New City city list. This script exists so that when you develop a new component for the component library, you can just focus on writing the component code and nothing else.

'use strict';

/** * Add new components * for example: /examples/docs/{lang}/city.md * 3. Create unit test files for components. / test/unit/specs/city. The spec. Js * 4, create component style file, / packages/theme - chalk/SRC/city. The SCSS * 5, creating components type declaration documents, / types/city. Which s * * 6, configuration in/components. The components that is configured in the json file information * in/examples/nav. Config. This component is added to the json, * the routing configuration /types/ elemental-ui.d. ts; /packages/theme-chalk/ SRC /index.scss Lets you just focus on writing your component code and forget about the rest

console.log();
process.on('exit'.() = > {
  console.log();
});

if(! process.argv[2]) {
  console.error('[Component name] Mandatory - Please enter new Component name');
  process.exit(1);
}

const path = require('path');
const fs = require('fs');
const fileSave = require('file-save');
const uppercamelcase = require('uppercamelcase');
// Component name, such as city
const componentname = process.argv[2];
// Chinese name of the component
const chineseName = process.argv[3] || componentname;
// Convert the component name to a big hump, city => city
const ComponentName = uppercamelcase(componentname);
Package directory, /packages/city
const PackagePath = path.resolve(__dirname, '.. /.. /packages', componentname);
// The list of files to add and the basic structure of the file contents
const Files = [
  // /packages/city/index.js
  {
    filename: 'index.js'.// Import the component, define the component static method install to register the component, and then export the component
    content: `import ${ComponentName} from './src/main';

/* istanbul ignore next */
${ComponentName}.install = function(Vue) {
  Vue.component(${ComponentName}.name, ${ComponentName});
};

export default ${ComponentName}; `
  },
  / / define the basic structure of the components, / packages/city/SRC/main vue
  {
    filename: 'src/main.vue'.// File contents, SFC
    content: `<template>
  <div class="el-${componentname}"></div>
</template>

<script>
export default {
  name: 'El${ComponentName}'}; </script>`
  },
  // Document in four languages, /examples/docs/{lang}/city.md, and set the file title
  {
    filename: path.join('.. /.. /examples/docs/zh-CN'.`${componentname}.md`),
    content: ` # #${ComponentName} ${chineseName}`
  },
  {
    filename: path.join('.. /.. /examples/docs/en-US'.`${componentname}.md`),
    content: ` # #${ComponentName}`
  },
  {
    filename: path.join('.. /.. /examples/docs/es'.`${componentname}.md`),
    content: ` # #${ComponentName}`
  },
  {
    filename: path.join('.. /.. /examples/docs/fr-FR'.`${componentname}.md`),
    content: ` # #${ComponentName}`
  },
  / / component test files, / test/unit/specs/city. The spec. Js
  {
    filename: path.join('.. /.. /test/unit/specs'.`${componentname}.spec.js`),
    // The basic structure of the test file is given
    content: `import { createTest, destroyVM } from '.. /util'; import${ComponentName} from 'packages/${componentname}';

describe('${ComponentName}', () => {
  let vm;
  afterEach(() => {
    destroyVM(vm);
  });

  it('create', () => {
    vm = createTest(${ComponentName}, true);
    expect(vm.$el).to.exist;
  });
});
`
  },
  // Packages /theme-chalk/ SRC /city.scss
  {
    filename: path.join('.. /.. /packages/theme-chalk/src'.`${componentname}.scss`),
    // Basic file structure
    content: `@import "mixins/mixins";
@import "common/var";

@include b(${componentname}`) {}
  },
  // Component type declaration file
  {
    filename: path.join('.. /.. /types'.`${componentname}.d.ts`),
    // Type declaration file basic structure
    content: `import { ElementUIComponent } from './component'

/** ${ComponentName} Component */
export declare class El${ComponentName} extends ElementUIComponent {
}`}];Json, {City: './packages/ City /index.js'}
const componentsFile = require('.. /.. /components.json');
if (componentsFile[componentname]) {
  console.error(`${componentname}Existing. `);
  process.exit(1);
}
componentsFile[componentname] = `./packages/${componentname}/index.js`;
fileSave(path.join(__dirname, '.. /.. /components.json'))
  .write(JSON.stringify(componentsFile, null.' '), 'utf8')
  .end('\n');

// Introduce the component style file in index. SCSS
const sassPath = path.join(__dirname, '.. /.. /packages/theme-chalk/src/index.scss');
const sassImportText = `${fs.readFileSync(sassPath)}@import "./${componentname}.scss"; `;
fileSave(sassPath)
  .write(sassImportText, 'utf8')
  .end('\n');

// Introduce the component's type declaration file in element-ui.d.ts
const elementTsPath = path.join(__dirname, '.. /.. /types/element-ui.d.ts');

let elementTsText = `${fs.readFileSync(elementTsPath)}/ * *${ComponentName} Component */
export class ${ComponentName} extends El${ComponentName}{} `;

const index = elementTsText.indexOf('export') - 1;
const importString = `import { El${ComponentName} } from './${componentname}'`;

elementTsText = elementTsText.slice(0, index) + importString + '\n' + elementTsText.slice(index);

fileSave(elementTsPath)
  .write(elementTsText, 'utf8')
  .end('\n');

// Iterate through the Files array, create all the listed Files and write the file contents
Files.forEach(file= > {
  fileSave(path.join(PackagePath, file.filename))
    .write(file.content, 'utf8')
    .end('\n');
});

// Add the routing configuration for the new component in nav.config.json
const navConfigFile = require('.. /.. /examples/nav.config.json');

// Go through the languages in the configuration and add the routing configuration for this component to all language configurations
Object.keys(navConfigFile).forEach(lang= > {
  let groups = navConfigFile[lang][4].groups;
  groups[groups.length - 1].list.push({
    path: ` /${componentname}`.title: lang === 'zh-CN'&& componentname ! == chineseName ?`${ComponentName} ${chineseName}`
      : ComponentName
  });
});

fileSave(path.join(__dirname, '.. /.. /examples/nav.config.json'))
  .write(JSON.stringify(navConfigFile, null.' '), 'utf8')
  .end('\n');

console.log('DONE! ');

Copy the code

One drawback is that/SRC /index.js is not automatically regenerated when a new component is created, meaning that the newly generated component is not automatically introduced into the component library entry. Nodebuild /bin/new.js $(filter-out $@,$(MAKECMDGOALS)) &&npm run build:file

/build/bin/template.js

Listen to all template files in the /examples/pages/template directory. When template files are changed, NPM run i18n is automatically executed, that is, execute the i18n.js script to generate.vue files in four languages.

/** * listen to all template files in /examples/pages/template directory, automatically execute NPM run i18n when template files are changed, * that is, execute i18n.js script to generate.vue files in four languages */

const path = require('path');
// Listen to the directory
const templates = path.resolve(process.cwd(), './examples/pages/template');

// The library in charge of listening
const chokidar = require('chokidar');
// Listen to the template directory
let watcher = chokidar.watch([templates]);

// NPM run i18n is automatically executed when files in the directory are changed
watcher.on('ready'.function() {
  watcher
    .on('change'.function() {
      exec('npm run i18n');
    });
});

// Take charge of executing commands
function exec(cmd) {
  return require('child_process').execSync(cmd).toString().trim();
}

Copy the code

/build/bin/version.js

Json file. The /examples/version.json file is automatically generated to record the version information of the component library. These versions are used in the header navigation bar of the component page on the official website.

/** * Automatically generates /examples/version.json according to package.json, which is used to record the version information of the component library
var fs = require('fs');
var path = require('path');
var version = process.env.VERSION || require('.. /.. /package.json').version;
var content = { '1.4.13: '1.4'.'2.0.11': '2.0'.'2.1.0': '2.1'.'2.2.2': '2.2'.'2.3.9': '2.3'.'version 2.4.11': '2.4'.'2.5.4': '2.5'.'2.6.3': '2.6'.'2.7.2': '2.7'.'2.8.2': '2.8'.'2.9.2': '2.9'.'2.10.1': '2.10'.'2.11.1': '2.11'.'2.12.0': '2.12'.'2.13.2': '2.13'.'2.14.1': '2.14' };
if(! content[version]) content[version] ='2.15';
fs.writeFileSync(path.resolve(__dirname, '.. /.. /examples/versions.json'), JSON.stringify(content));

Copy the code

/build/md-loader

It is a Loader, and is responsible for most of the component Demo + documentation patterns on the component page.

Can be in/examples/route. Config. See registerRoute js method generated page routing configuration, loadDocs method is used to load/examples/docs / {lang} / comp., md. Note that the Markdown document is loaded instead of the usual Vue file, but can be rendered as a VUE component on the page just like a VUE file. How can this be done?

As we know, the idea of Webpack is that all resources can be required, just by configuring the appropriate loader. /build/webpack.demo.js/module.rules /build/webpack.demo.js/module.rules /build/webpack.demo.js It is then handed to vue-loader, which eventually generates SFC (VUE single file component) rendering to the page. This will see the documentation of the component page + the component demo presentation.

{
  test: /\.md$/,
  use: [
    {
      loader: 'vue-loader'.options: {
        compilerOptions: {
          preserveWhitespace: false}}}, {loader: path.resolve(__dirname, './md-loader/index.js')}}]Copy the code

If you are interested in the specific implementation of Loader, you can read in depth.

/build/config.js

Common configuration for Webpack, such as externals, alias, and so on. Through the configuration of externals, part of the component library code redundancy problem is solved, such as component and component library common module code, but the component style redundancy problem has not been solved; The Alias alias configuration provides convenience for developing component libraries.

/** * Webpack common configuration, such as externals, alias */
var path = require('path');
var fs = require('fs');
var nodeExternals = require('webpack-node-externals');
var Components = require('.. /components.json');

var utilsList = fs.readdirSync(path.resolve(__dirname, '.. /src/utils'));
var mixinsList = fs.readdirSync(path.resolve(__dirname, '.. /src/mixins'));
var transitionList = fs.readdirSync(path.resolve(__dirname, '.. /src/transitions'));
/** * Externals externals externals externals externals externals externals externals externals externals externals externals If not, you will see two copies of the Checkbox component code. * Common locales, utils, mixins, transitions, etc., also have redundant code * But with externals, packages that tell WebPack they don't need to be bundled into the bundle, The runtime retrieves these extension dependencies externally. The compiled table.js dependency on the Checkbox component is introduced in the packaged /lib/tables.js: Module. Exports = require("element-ui/lib/checkbox") * Export.exports = require("element-ui/lib/checkbox") * Export.exports = require("element-ui/lib/checkbox") /lib/theme-chalk/table. CSS and /lib/theme-chalk/checkbox. CSS have checkbox components */
var externals = {};

Object.keys(Components).forEach(function(key) {
  externals[`element-ui/packages/${key}`] = `element-ui/lib/${key}`;
});

externals['element-ui/src/locale'] = 'element-ui/lib/locale';
utilsList.forEach(function(file) {
  file = path.basename(file, '.js');
  externals[`element-ui/src/utils/${file}`] = `element-ui/lib/utils/${file}`;
});
mixinsList.forEach(function(file) {
  file = path.basename(file, '.js');
  externals[`element-ui/src/mixins/${file}`] = `element-ui/lib/mixins/${file}`;
});
transitionList.forEach(function(file) {
  file = path.basename(file, '.js');
  externals[`element-ui/src/transitions/${file}`] = `element-ui/lib/transitions/${file}`;
});

externals = [Object.assign({
  vue: 'vue'
}, externals), nodeExternals()];

exports.externals = externals;

// Set an alias for easy use
exports.alias = {
  main: path.resolve(__dirname, '.. /src'),
  packages: path.resolve(__dirname, '.. /packages'),
  examples: path.resolve(__dirname, '.. /examples'),
  'element-ui': path.resolve(__dirname, '.. / ')};exports.vue = {
  root: 'Vue'.commonjs: 'vue'.commonjs2: 'vue'.amd: 'vue'
};

exports.jsexclude = /node_modules|utils\/popper\.js|utils\/date\.js/;

Copy the code

/build/deploy-ci.sh

A continuous integration script that is used in conjunction with Travis CI. This script is executed in the.travis. Yml file. After the code is submitted to the Github repository, it is automatically executed by the Tavis CI, which automatically finds the.travis. But we probably don’t need this. Most teams have their own continuous integration solutions.

/build/git-release.sh

The main thing here is to diff and merge with the remote dev branch.

#! /usr/bin/env sh

#The main thing here is to diff and merge with the remote dev branchgit checkout dev if test -n "$(git status --porcelain)"; then echo 'Unclean working tree. Commit or stash changes first.' >&2; exit 128; fi if ! git fetch --quiet 2>/dev/null; then echo 'There was a problem fetching your branch. Run `git fetch` to see more... '> & 2; exit 128; fi if test "0" ! = "$(git rev-list --count --left-only @'{u}'... HEAD)"; then echo 'Remote history differ. Please pull changes.' >&2; exit 128; fi echo 'No conflicts.' >&2;Copy the code

/build/release.sh

The script does the following:

  • Merge dev branch to master,

  • Modify the version numbers of style packs and component libraries

  • Publish style packs and component libraries

  • Commit the master and dev branches to the remote repository

This script can be used when publishing component libraries, especially with the ability to automatically change the version number (forgetting to change the version number every time you publish). It is simple to submit the code to the remote repository log here, and more detailed log submission is provided by updating the log file changelog.{lang}.md.

#! /usr/bin/env sh
set -e

#Merge the dev branch to master
#Compile the package
#Modify the version numbers of style packs and component libraries
#Publish style packs and component libraries
#Commit the master and dev branches to the remote repository

#Merge the dev branch to master
git checkout master
git merge dev

#Version Select CLI
VERSION=`npx select-version-cli`

#Whether to confirm the current version information
read -p "Releasing $VERSION - are you sure? (y/n)" -n 1 -r
echo    # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
  echo "Releasing $VERSION ..."

  #Build, build and package
  VERSION=$VERSION npm run dist

  # ssr test
  node test/ssr/require.test.js            

  # publish theme
  echo "Releasing theme-chalk $VERSION ..."
  cd packages/theme-chalk
  #Changes the version information of the theme package
  npm version $VERSION --message "[release] $VERSION"
  #Release themeif [[ $VERSION =~ "beta" ]] then npm publish --tag beta else npm publish fi cd .. /..
  # commit
  git add -A
  git commit -m "[build] $VERSION"
  #Change the version information of the component library
  npm version $VERSION --message "[release] $VERSION"

  #Publish, push master to remote repository
  git push eleme master
  git push eleme refs/tags/v$VERSION
  git checkout dev
  git rebase master
  git push eleme dev

  #Publish component libraries
  if [[ $VERSION =~ "beta" ]]
  then
    npm publish --tag beta
  else
    npm publish
  fi
fi

Copy the code

/build/webpack.xx.js

  • Webpack.common.js, build commonJs2 specification package, will play a full package

  • Webpack.component.js, build commonJS2 specification package, support loading on demand

    Support for on-demand loading focuses on the configuration of Entry and Ouput, which breaks each component into a separate package

  • Webpack.conf.js, the package that builds the UMD specification, will make a full package

  • Webpack.demo. Js, webpack configuration for official website project

  • Webpack.extensibility. Js, theme editor chorme plugin project webPack configuration, project in the extension directory

  • Webpack.test.js, this file is not useful, but the name is supposed to be used to test the webpack configuration of the project, but now the test is using the Karma framework

eslint

Element uses ESLint to ensure code style consistency, and has also written Elemefe as an extension rule configuration for ESLint. To ensure the quality of the official website project, the eslint-loader rule is configured in /build/webpack.demo.js to enforce code quality checks at project startup. However, Element does not do enough to control code quality. For example, it is weak in automatic code formatting and only ensures the quality of code in/SRC, /test, / Packages, and /build directories. It does not do enough for official website projects, especially document formats. Prettier for formatting restrictions while ESLint focuses on syntax restrictions, as shown in the code quality section of Building your own typescript project + Developing your own scaffolding tool TS-CLI

travis ci

Travis CI is a scripting approach to continuous integration, but it may not be useful for internal projects, as Travis CI is only available on Github, and gitLab is generally used internally, as well as continuous integration

Makefile

The configuration file of the make command is familiar to those who have written C or C++.

Run the make command to view the detailed help information. For example, run make install to install the package, make dev to start the local development environment, and make new comp-name to create a component. Using the make command is more convenient, clear, and simple than NPM run xx, but it relies on NPM run xx internally to do the real work, which is equivalent to providing a layer of encapsulation of the NPM run CMD for a better development experience.

package.json -> scripts

Elemnt has written a lot of NPM scripts, which are combined with many scripts in /build to automatically complete a lot of repetitive physical labor through scripts, which is more reliable and efficient than manual labor. I think this design is the most worthy of learning in Element. You can apply this design to your own projects to help improve business performance.

{
  / / to pack
  "bootstrap": "yarn || npm i".// Generate the following files automatically through the JS script: Generate examples/icon.json file && Generate SRC /index.js file && Generate. Vue file for the official websites of the four languages && Generate examples/version.json file, which contains the version information of the component library
  "build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js".// Create theme style: automatically introduce the style files of each component in index.scss && compile the SCSS file into CSS using gulp and export it to lib directory && copy the base style theme-chalk to lib/theme-chalk
  "build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib  lib/theme-chalk".// Compile the SRC directory with Babel, then print the compiled file to the lib directory, ignoring/SRC /index.js
  "build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js".// Compile ES Module style translation files into UMD style
  "build:umd": "node build/bin/build-locale.js".// Clean up the build artifacts
  "clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage".// Build the official website project
  "deploy:build": "npm run build:file && cross-env NODE_ENV=production webpack --config build/webpack.demo.js && echo element.eleme.io>>examples/element-ui/CNAME".// Build the theme plug-in
  "deploy:extension": "cross-env NODE_ENV=production webpack --config build/webpack.extension.js".// Start the theme plug-in development environment
  "dev:extension": "rimraf examples/extension/dist && cross-env NODE_ENV=development webpack --watch --config build/webpack.extension.js".// Start the local development environment for the component library. / /pages/template to monitor any changes in the template file. If any changes are made to the template file, the.vue file will be generated again.
  "dev": "npm run bootstrap && npm run build:file && cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js & node build/bin/template.js".// Component test projects, examples/play/index.vue can import any component library, or directly use dev started projects, use components in documentation
  "dev:play": "npm run build:file && cross-env NODE_ENV=development PLAY_ENV=true webpack-dev-server --config build/webpack.demo.js".// Build the component library
  "dist": "npm run clean && npm run build:file && npm run lint && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme".// Generate.vue files for the official website in four languages
  "i18n": "node build/bin/i18n.js".// lint ensures project code quality
  "lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet".Merge dev branch to master, package and compile, change style pack and component library version numbers, publish style pack and component library, commit code to remote repository. Note out the last script when using it. There is a problem with that script
  "pub": "npm run bootstrap && sh build/git-release.sh && sh build/release.sh && node build/bin/gen-indices.js".// Generating a test report, whether test or test:watch, takes too long to generate a test report
  "test": "npm run lint && npm run build:theme && cross-env CI_ENV=/dev/ BABEL_ENV=test karma start test/unit/karma.conf.js --single-run".// Start the test project to check for updates to the test file
  "test:watch": "npm run build:theme && cross-env BABEL_ENV=test karma start test/unit/karma.conf.js"
}
Copy the code

website

Element’s official site is in a repository with its component library, and everything on element’s official site is in the /examples directory as a VUE project.

entry.js

The official website project entry, where the full introduction of the component library, and its style.

// The entry of the official website project is a common VUE project
import Vue from 'vue';
import entry from './app';
import VueRouter from 'vue-router';
// Import the component library. Main is an alias, configured in /build/config.js
import Element from 'main/index.js';
import hljs from 'highlight.js';
// Route configuration
import routes from './route.config';
// Some components of the official website project
import demoBlock from './components/demo-block';
import MainFooter from './components/footer';
import MainHeader from './components/header';
import SideNav from './components/side-nav';
import FooterNav from './components/footer-nav';
import title from './i18n/title';

// Component library style
import 'packages/theme-chalk/src/index.scss';
import './demo-styles/index.scss';
import './assets/styles/common.css';
import './assets/styles/fonts/style.css';
// Mount the icon information to the Vue prototype chain, which will be used in the Markdown document, and display all icon ICONS on the icon page of the official website
import icon from './icon.json';

Vue.use(Element);
Vue.use(VueRouter);
Vue.component('demo-block', demoBlock);
Vue.component('main-footer', MainFooter);
Vue.component('main-header', MainHeader);
Vue.component('side-nav', SideNav);
Vue.component('footer-nav', FooterNav);

const globalEle = new Vue({
  data: { $isEle: false } // Whether the user is ele
});

Vue.mixin({
  computed: {
    $isEle: {
      get: () = > (globalEle.$data.$isEle),
      set: (data) = >{globalEle.$data.$isEle = data; }}}}); Vue.prototype.$icon = icon;// Icon list page

const router = new VueRouter({
  mode: 'hash'.base: __dirname,
  routes
});

router.afterEach(route= > {
  // https://github.com/highlightjs/highlight.js/issues/909#issuecomment-131686186
  Vue.nextTick(() = > {
    const blocks = document.querySelectorAll('pre code:not(.hljs)');
    Array.prototype.forEach.call(blocks, hljs.highlightBlock);
  });
  const data = title[route.meta.lang];
  for (let val in data) {
    if (new RegExp(A '^' + val, 'g').test(route.name)) {
      document.title = data[val];
      return; }}document.title = 'Element';
  ga('send'.'event'.'PageView', route.name);
});

new Vue({ // eslint-disable-line. entry, router }).$mount('#app');

Copy the code

nav.config.json

You must understand the structure of the JSON file in order to understand the code that generates all routes in the component page in the route.config.js file.

route.config.js

The route configuration of the official website project is automatically generated based on the route configuration.

// Automatically generate routes for official website projects according to route configuration
import navConfig from './nav.config';
// All supported languages
import langs from './i18n/route';

// Load the.vue file on each page of the official website
const LOAD_MAP = {
  'zh-CN': name= > {
    return r= > require.ensure([], () = >
      r(require(`./pages/zh-CN/${name}.vue`)),
    'zh-CN');
  },
  'en-US': name= > {
    return r= > require.ensure([], () = >
      r(require(`./pages/en-US/${name}.vue`)),
    'en-US');
  },
  'es': name= > {
    return r= > require.ensure([], () = >
      r(require(`./pages/es/${name}.vue`)),
    'es');
  },
  'fr-FR': name= > {
    return r= > require.ensure([], () = >
      r(require(`./pages/fr-FR/${name}.vue`)),
    'fr-FR'); }};const load = function(lang, path) {
  return LOAD_MAP[lang](path);
};

// Load markdown files for each component on the component page of the official website
const LOAD_DOCS_MAP = {
  'zh-CN': path= > {
    return r= > require.ensure([], () = >
      r(require(`./docs/zh-CN${path}.md`)),
    'zh-CN');
  },
  'en-US': path= > {
    return r= > require.ensure([], () = >
      r(require(`./docs/en-US${path}.md`)),
    'en-US');
  },
  'es': path= > {
    return r= > require.ensure([], () = >
      r(require(`./docs/es${path}.md`)),
    'es');
  },
  'fr-FR': path= > {
    return r= > require.ensure([], () = >
      r(require(`./docs/fr-FR${path}.md`)),
    'fr-FR'); }};const loadDocs = function(lang, path) {
  return LOAD_DOCS_MAP[lang](path);
};

// Add each route configuration to the component page
const registerRoute = (navConfig) = > {
  let route = [];
  // Iterate through configurations to generate component routing configurations in four languages
  Object.keys(navConfig).forEach((lang, index) = > {
    // Specify the language configuration, such as lang = zh-cn, navs, all configuration items are written in Chinese
    let navs = navConfig[lang];
    // Routing configuration for the component page lang language
    route.push({
      // For example, / zh-cn /component
      path: ` /${ lang }/component`.redirect: ` /${ lang }/component/installation`.// Load the component.vue of the component page
      component: load(lang, 'component'),
      // All the child routes of the component page, i.e., each component, are placed here. The final route is/zh-cn /component/comp-path
      children: []});// Traverses all configuration items for the specified language
    navs.forEach(nav= > {
      if (nav.href) return;
      if (nav.groups) {
        // This item is a component
        nav.groups.forEach(group= > {
          group.list.forEach(nav= > {
            addRoute(nav, lang, index);
          });
        });
      } else if (nav.children) {
        // This is the development guide
        nav.children.forEach(nav= > {
          addRoute(nav, lang, index);
        });
      } else {
        // Others, such as update logs, Element React, Element AngularaddRoute(nav, lang, index); }}); });// Generate the child routing configuration and populate it with children
  function addRoute(page, lang, index) {
    // Load the vue file or markdown file according to path
    const component = page.path === '/changelog'
      ? load(lang, 'changelog')
      : loadDocs(lang, page.path);
    let child = {
      path: page.path.slice(1),
      meta: {
        title: page.title || page.name,
        description: page.description,
        lang
      },
      name: 'component-' + lang + (page.title || page.name),
      component: component.default || component
    };
    // Add the child route to the children above
    route[index].children.push(child);
  }

  return route;
};

// Get the routing configuration for all the sidebars of the component page
let route = registerRoute(navConfig);

const generateMiscRoutes = function(lang) {
  let guideRoute = {
    path: ` /${ lang }/guide`./ / guide
    redirect: ` /${ lang }/guide/design`.component: load(lang, 'guide'),
    children: [{
      path: 'design'.// Design principles
      name: 'guide-design' + lang,
      meta: { lang },
      component: load(lang, 'design')}, {path: 'nav'./ / navigation
      name: 'guide-nav' + lang,
      meta: { lang },
      component: load(lang, 'nav')}}];let themeRoute = {
    path: ` /${ lang }/theme`.component: load(lang, 'theme-nav'),
    children: [{path: '/'.// Topic management
        name: 'theme' + lang,
        meta: { lang },
        component: load(lang, 'theme')}, {path: 'preview'.// Theme preview editing
        name: 'theme-preview-' + lang,
        meta: { lang },
        component: load(lang, 'theme-preview')}}];let resourceRoute = {
    path: ` /${ lang }/resource`./ / resources
    meta: { lang },
    name: 'resource' + lang,
    component: load(lang, 'resource')};let indexRoute = {
    path: ` /${ lang }`./ / home page
    meta: { lang },
    name: 'home' + lang,
    component: load(lang, 'index')};return [guideRoute, resourceRoute, themeRoute, indexRoute];
};

langs.forEach(lang= > {
  route = route.concat(generateMiscRoutes(lang.lang));
});

route.push({
  path: '/play'.name: 'play'.component: require('./play/index.vue')});let userLanguage = localStorage.getItem('ELEMENT_LANGUAGE') | |window.navigator.language || 'en-US';
let defaultPath = '/en-US';
if (userLanguage.indexOf('zh-')! = = -1) {
  defaultPath = '/zh-CN';
} else if (userLanguage.indexOf('es')! = = -1) {
  defaultPath = '/es';
} else if (userLanguage.indexOf('fr')! = = -1) {
  defaultPath = '/fr-FR';
}

route = route.concat([{
  path: '/'.redirect: defaultPath
}, {
  path: The '*'.redirect: defaultPath
}]);

export default route;

Copy the code

play

These include play.js and Play /index.vue. For example, if you want to see the effect of a component in an Element, especially when the component is loaded on demand, you can use play/index.vue. Starting the project with the NPM run dev:play command is also configured using environment variables in /build/webpack.demo.js.

// play.js
import Vue from 'vue';
// Fully import the component library and its styles
import Element from 'main/index.js';
import 'packages/theme-chalk/src/index.scss';
import App from './play/index.vue';

Vue.use(Element);

new Vue({ // eslint-disable-line
  render: h= > h(App)
}).$mount('#app');

Copy the code
<! -- play/index.vue --> <template> <div style="margin: 20px;" >< el-input V-model ="input" placeholder=" please input content "></el-input> </div> </template> <script> export default {data() {return  { input: 'Hello Element UI! '}; }}; </script>Copy the code

pages

All the pages of the official website are here. The.vue files in four languages are automatically generated in the Pages directory by combining the i18n.js script with various template files in the pages/template directory. These vue files are loaded in route.config.js.

i18n

The translation configuration files for the official website page are all here.

  • Component. json, translation configuration for component pages
  • Page. json, some translation configuration for other pages, such as home page, design page, etc
  • Route. json, the language configuration, which indicates which languages the component library currently supports
  • Theme -editor.json, translation configuration for theme editor pages
  • Title. Json, the title information displayed in TAB tabs on each page of the official website

extension

Theme Editor chrome plugin project.

dom

Defines dom style manipulation methods, including determining whether a specified style exists, adding a style, removing a style, and switching styles.

// dom/class.js
export const hasClass = function(obj, cls) {
  return obj.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
};

export const addClass = function(obj, cls) {
  if(! hasClass(obj, cls)) obj.className +=' ' + cls;
};

export const removeClass = function(obj, cls) {
  if (hasClass(obj, cls)) {
    const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
    obj.className = obj.className.replace(reg, ' '); }};export const toggleClass = function(obj, cls) {
  if (hasClass(obj, cls)) {
    removeClass(obj, cls);
  } else{ addClass(obj, cls); }};Copy the code

docs

The component documentation directory provides documents in four languages by default. The directory structure is docs/{lang}/comp-name.md. These documents are loaded in the component page (configured in route.config.js), first handed to mD-Loader for processing, extracted vUE code, then handed to VUe-Loader for processing, and finally rendered to the page to form the component Demo + document.

demo-style

The layout style of the component demo displayed in the component page is independent of the style of the component itself, just as you define the layout style of the component in your business code. Because components do not display well in some scenarios, they need to be typesetted, such as button pages, icon pages, etc.

components

The official website project is a directory that houses some global components.

assets

Official website project static resource directory

Component library

The Element component library consists of two parts: / SRC and /packages.

src

Using the idea of modularization, put some common modules that components depend on in/SRC directory, and split the following modules according to their functions:

  • Utils, defines some utility methods
  • Transitions, animation,
  • Mixins, methods of global mixing
  • Locale, internationalization, and various languagesPart of the componentTranslation documents of
  • Directives, instructions

/ SRC /index.js is automatically generated by the /build/bin/build-entry.js script and is the entry to the component library. Responsible for automatically importing all components of the component library, defining the install method for fully registering component library components, and then exporting version information, install, and individual components.

/* Automatically generated from the file './build/bin/build-entry.js' */

// Import all components
import Pagination from '.. /packages/pagination/index.js';
import Dialog from '.. /packages/dialog/index.js';
// ...

// An array of components, some components are not in it, these components do not need to be registered via vue. use or Vue.component, directly mounted to the Vue prototype chain
const components = [
  Pagination,
  Dialog,
  // ...
]

// Define the install method, which is responsible for importing the full component library
const install = function(Vue, opts = {}) {
  locale.use(opts.locale);
  locale.i18n(opts.i18n);

  // Globally register components
  components.forEach(component= > {
    Vue.component(component.name, component);
  });

  Vue.use(InfiniteScroll);
  Vue.use(Loading.directive);

  // Hang something on the Vue prototype chain
  Vue.prototype.$ELEMENT = {
    size: opts.size || ' '.zIndex: opts.zIndex || 2000
  };

  // These components are not required
  Vue.prototype.$loading = Loading.service;
  Vue.prototype.$msgbox = MessageBox;
  Vue.prototype.$alert = MessageBox.alert;
  Vue.prototype.$confirm = MessageBox.confirm;
  Vue.prototype.$prompt = MessageBox.prompt;
  Vue.prototype.$notify = Notification;
  Vue.prototype.$message = Message;

};

// When importing a component library through the CDN, go through the following code to register the component library fully
if (typeof window! = ='undefined' && window.Vue) {
  install(window.Vue);
}

// Export version information, install methods, and components
export default {
  version: '2.15.0'.locale: locale.use,
  i18n: locale.i18n,
  install,
  CollapseTransition,
  Loading,
  // ...
}
Copy the code

Only a portion of the document is posted to reduce the length, but suffice to say.

/packages

Element stores all of its components in the/Packages directory. Each component is grouped in a directory. The directory structure and the basic code within it are automatically generated by the script /build/bin/new.js. The directory structure is:

  • Package-name: specifies the package name in the format of a hyphen
    • Index.js, the install method for a component, indicates that the component exists as a Vue plug-in
    • SRC, the source directory for the component
      • The basic structure of the main.vue component is now in place

For example, the directory and file of the new City component look like this:

  • city

    • index.js

      import City from './src/main';
      
      /* istanbul ignore next */
      City.install = function(Vue) {
        Vue.component(City.name, City);
      };
      
      export default City;
      Copy the code
    • src

      • main.vue

        <template>
          <div class="el-city"></div>
        </template>
        
        <script>
        export default {
          name: 'ElCity'
        };
        </script>
        Copy the code

Theme-chalk is the style directory of the component library. The style code for all components is here. Element’s component file does not define styles. The theme-Chalk directory, another project, is packaged with gulp and supports standalone publishing. Its directory structure looks like this:

  • theme-chalk

    • SRC, the source directory for component styles

      • Index.scss, imports all the style files in the directory
      • Comp.scss, component style files, such as button.scss
      • Other, such as fonts, common styles, variables, methods, etc
    • .gitignore

    • gulpfile.js

      'use strict';
      
      // gulp configuration file
      
      const { series, src, dest } = require('gulp');
      const sass = require('gulp-sass');
      const autoprefixer = require('gulp-autoprefixer');
      const cssmin = require('gulp-cssmin');
      
      // compile SCSS to CSS, compress it, and output it to./lib
      function compile() {
        return src('./src/*.scss')
          .pipe(sass.sync())
          .pipe(autoprefixer({
            browsers: ['ie > 9'.'last 2 versions'].cascade: false
          }))
          .pipe(cssmin())
          .pipe(dest('./lib'));
      }
      
      // copy./ SRC /fonts to./lib/fonts
      function copyfont() {
        return src('./src/fonts/**')
          .pipe(cssmin())
          .pipe(dest('./lib/fonts'));
      }
      
      exports.build = series(compile, copyfont);
      
      Copy the code
    • package.json

    • README.md

test

Component library test project, using the Karma framework

Type declaration

Type declaration files for each component, and TS projects have better code hints when using component libraries.

The end of the

The source code analysis of Element is over here. It is recommended that readers refer to the article and read the source code of the framework themselves and add annotations, so that they will have a deeper understanding and more conducive to the follow-up work. The next article will detail the process of building a component library for your team based on Element.

link

  • Element source architecture mind map version
  • Element source architecture video version
  • github
  • How to quickly Build your own component library for your team (Part 2) — Build your own component library for your team based on Element-UI