“This is the third day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.

Written in the beginning

After the study of the first two articles, we can say that the infrastructure of the project has been set up, and the XD has been set up. (✪ ✪ omega)

So what are we going to learn in this chapter? The only goal of this chapter is to learn the contents of a file in the elemental-UI source code: elemental-dev /build/bin/gen-cssfile.js.

The bin directory is a directory for runnable files. There are a lot of things you can do to improve your productivity. There’s a lot to learn.

This will cover a lot of Node knowledge, so hopefully you have some basic knowledge of Node. However, it does not matter, xiaobian will carefully write notes, it should be not difficult to read ha, do not understand you are also welcome to give me a message comment oh.

Divider component implementation – Divider

“Divider” before we get to the topic of this chapter, let’s review our current project. There is only one Button component, which is a little bit tacky. We add another component to the project – Divider for better demonstration later.

Had chosen this component, simply because it is simpler, and the series of articles, the entire source code is not the important content of components of the specific implementation details, because most of the components inside out alone, can tell a long, such as small make up previously written an article how to make a Toast like ElementUI high-quality components? There is no heat, but the components are still very useful. ( ̄▽ ̄)

So without further ado, let’s think about what we need to do to add a new component to a project. I summarized the three steps as follows:

Step 1 – Create the component directory structure

Create a divider component directory consisting of divider/index.js and divider/ SRC /main.vue files.

The index.js file does the same import and leaves an entry for on-demand import.

// index.js import Divider from './src/main.vue'; /* Istanbul ignore next */ Divider. Install = function(Vue) {Vue.component(Divider. Name, Divider); }; export default Divider;Copy the code

Main. vue file, here is relatively simple, directly is the source code to move over.

<template functional>
  <div v-bind="data.attrs" v-on="listeners"
    :class="[data.staticClass, 'el-divider', `el-divider--${props.direction}`]" >
    <div v-if="slots().default && props.direction !== 'vertical'"
      :class="['el-divider__text', `is-${props.contentPosition}`]">
      <slot />
    </div>
  </div>
</template>

<script>
export default {
  name: "ElDivider",
  props: {
    // 分割线方向
    direction: {
      type: String,
      default: "horizontal",
      validator(val) {
        return ["horizontal", "vertical"].indexOf(val) !== -1;
      },
    },
    // 分割线文案的位置
    contentPosition: {
      type: String,
      default: "center",
      validator(val) {
        return ["left", "center", "right"].indexOf(val) !== -1;
      },
    },
  },
};
</script>
Copy the code

Some of the more difficult things are about functional components:

  • Functional: a Vue optimization strategy for components. In recent years, you’ve heard a lot about the idea of componentization, the idea that pages are made up of little pieces, divided and ruled, and that’s a good thing; The componentization of Vue also inherits this concept, but everything has two sides, advantages and disadvantages. Componentization takes us out of the complexity, but also brings additional “costs”. As we know, each component in Vue is an instance, and the “overhead” of an instance is very high, for example, the instance has a large number of instance methods mounted on it, a large amount of response data, etc., which consumes resources. If any page functionality is split into components, there will be a large number of component instances, which can be scary. To this end, Vue introduced functional components, for simple functions, components without any state management, we can define it as functional components, it does not generate component instances, that is, there is no such thing as this, it is stateless, it can effectively reduce some unnecessary component overhead. (That’s a bit much, but just remember the first sentence.)

  • V-bind =”data.attrs” : In common components, attributes that are not defined as prop are automatically added to the root element of the component, and existing attributes with the same name are replaced or intelligently-merged with them.

    In simple terms, as in the following example, we add an ID to the child component, which is appended to the root element of the child component. But functional components do not, and will be ignored. Functional components require you to explicitly define this behavior by adding V-bind =”data.attrs” to the child components.

    // parent.vue
    <template>
      <div>
        <child id="yd"></child>
      </div>
    </template>
    Copy the code
  • V-on =”listeners” : the same thing as v-bind=”data.attrs”, but the former is for event methods and the latter is for attributes.

  • Slot.default: The default slot we normally use is this.$slot.default, but the slot object specified by the functional component is a function.

Step 2 – Create the component style file

Second, we need to create a.scss file for the new component, paying attention to the file location.

The divider. SCSS file still contains the same content, which we imported directly from the element-UI source code:

@import "common/var"; @import "mixins/mixins"; @include b(divider) { background-color: $--border-color-base; position: relative; @include m(horizontal) { display: block; height: 1px; width: 100%; margin: 24px 0; } @include m(vertical) { display: inline-block; width: 1px; height: 1em; margin: 0 8px; vertical-align: middle; position: relative; } @include e(text) { position: absolute; background-color: $--color-white; padding: 0 20px; font-weight: 500; color: $--color-text-primary; font-size: 14px; @include when(left) { left: 20px; transform: translateY(-50%); } @include when(center) { left: 50%; transform: translateX(-50%) translateY(-50%); } @include when(right) { right: 20px; transform: translateY(-50%); }}}Copy the code

Here it also uses the content of some other files, we also need to complete it, otherwise there may be errors in the packaging process, here you can directly copy the style of small series to add to the corresponding file.

// minxins.scss @mixin m($modifier) { $selector: &; $currentSelector: ""; @each $unit in $modifier { $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","}; } @at-root { #{$currentSelector} { @content; } } } @mixin e($element) { $E: $element ! global; $selector: &; $currentSelector: ""; @each $unit in $element { $currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","}; } @if hitAllSpecialNestRule($selector) { @at-root { #{$selector} { #{$currentSelector} { @content; } } } } @else { @at-root { #{$currentSelector} { @content; } } } } @mixin when($state) { @at-root { &.#{$state-prefix + $state} { @content; }}}Copy the code
// var.scss $--color-text-primary: #303133 ! default;Copy the code

The ABOVE SCSS style is not our focus, you can copy all of it. If you’re interested, learn the syntax of SCSS or have a cameo file with its.css file below, which has a cameo appearance: ٩(❛ᴗ❛ danjun) p.

After writing the component’s style file, you also need to introduce the following in the overall style file:

// index.scss
@import "./button.scss";
@import "./divider.scss";
Copy the code

You can run the NPM run build:theme command to check whether the theme-chalk/lib directory generates the divider. CSS file and the index. CSS file are correct.

Step 3 – General entry file import component

SRC /index.js in the project root directory: SRC /index.js

// src/index.js import Button from '.. /packages/button/index.js'; import Divider from '.. /packages/divider/index.js'; const components = [ Button, Divider ]; const install = (Vue) => { components.forEach(component => { Vue.component(component.name, component) }) } export default { install, Button, Divider }Copy the code

Finally, as usual, test the component. Test steps:

NPM run build NPM pack Test project: NPM install Install NPM run serve NPM run build NPM pack test project: NPM install install NPM run serve

Test code:

<div> <span> A clear sky overhead, </span> < el-Divider content-position="left"> </ el-Divider > <span> </span> < el-Divider ></ el-Divider >< span> For the value that cannot be calculated </span> < el-Divider content-position="right"> Ali Cloud </ el-Divider > </div>Copy the code

gen-cssfile.js

After implementing the splitter component, let’s start with a question. Adding a new component to a project goes through the above three steps each time. ElementUI has a large number of components, does the ElementUI team create them file by file, directory by directory? The answer is no.

Just to give you an idea of the final answer, all three of these steps can be automated because they all have some similarities, and the scripts to automate them are in the Build /bin directory of the ElementUI source code. The “gen-cssfile.js” file, which is the focus of this chapter, is one of them, and ultimately we just need to execute these scripts with simple commands.

This section will take you through the implementation of step 2 to automatically create the component’s.scSS style file and generate the contents of the overall style file index. SCSS. The first and third steps will be explained in a future article.

Build /bin/gen/cssfile.js

Configure the script file with a command in package.json:

"scripts": {
  "gen": "node build/bin/gen-cssfile.js"
},
Copy the code

Automatically creates.scSS files for components

Fs.writefilesync (‘ file path ‘, ‘file content ‘,’ file code/error callback ‘); , it writes the contents to a file and automatically creates the file if the given file path does not exist.

Let’s take a look at the following:

// gen-cssfile.js
var fs = require('fs');
fs.writeFileSync('./packages/theme-chalk/src/loading.scss', 
                `.el-loading{background-color: red}`, 'utf-8');
Copy the code

Run the NPM run gen command.

You can see the file generation and content in the corresponding path:

With this simple tip in mind, we can go straight to the contents of the gen-cssfile.js file in the original ElementUI source code.

But to do that, we need to create a component. json file in the project root directory, which will look like this:

// `components.json`
{
  "button": "./packages/button/index.js",
  "divider": "./packages/divider/index.js",
  "loading": "./packages/loading/index.js",
  "alert": "./packages/alert/index.js"
}
Copy the code

So what is this file for? Fs.writefilesync (‘./packages/theme-chalk/ SRC /loading.scss’); To create a.scss file, we need to use a file path, because the project will have a large number of components, each component needs to create a.scss file, so we first define all the components in a single file, we just need to import the file, and then a loop. Components. Json is also associated with the component’s configuration file.

Gen-cssfile.js gen-cssfile.js

var fs = require('fs'); var path = require('path'); var Components = require('.. /.. /components.json'); // Import the component configuration file Components = object.keys (Components); // Components = ['button', 'divider', 'loading', 'alert'] /** File type: * 'theme-chalk': indicates that the.scss file is automatically generated; / var themes = [' theme-default']; / var themes = [' theme-default']; Var basepath = path.resolve(__dirname, '.. /.. /packages/'); Function fileExists(filePath) {try {return fs.statsync (filePath).isfile (); } catch (err) { return false; Theme.foreach ((theme.foreach) => {// check whether it is a.scss file, true var isSCSS = theme! == 'theme-default'; ForEach (function(key) {SCSS if (['icon', 'option', 'option-group'].indexOf(key) > -1) return; Var fileName = key + (isSCSS? '.scss' : '.css'); // Splice path: '.. /.. /packages/' + 'theme-chalk' + SRC + filePath = path.resolve(basepath, theme, 'SRC ', fileName); // Create the component only if the.scss file does not exist. fileExists(filePath)) { fs.writeFileSync(filePath, '', 'utf8'); Console. log(theme, 'create missing ', fileName,' file '); }}); });Copy the code

The above code is not much, xiaobian have written a note, just don’t know how much you can read? (◕ strap strap ◕) If you don’t understand anything while reading the annotations, then you can slowly debug your comprehension by constantly executing the NPM run gen command.

When we run the NPM run gen command, we can see that the missing component style file is automatically created for us, and the console prompts accordingly.

Automatically generates the total style index.scss file

Above we implemented the.scSS style file for automatically creating components, but that’s not all. The automatically created style file needs to be introduced in the overall style file index. SCSS to take effect.

So we need to change the gen-cssfile.js file again:

. themes.forEach((theme) => { var isSCSS = theme ! == 'theme-default'; Var indexContent = isSCSS? Var indexContent = isSCSS? '@import "./base.scss"; \n' : '@import "./base.css"; \n'; Components.forEach(function(key) { if (['icon', 'option', 'option-group'].indexOf(key) > -1) return; var fileName = key + (isSCSS ? '.scss' : '.css'); // The component introduces indexContent += '@import "./' + fileName + '"; \n'; var filePath = path.resolve(basepath, theme, 'src', fileName); if (! fileExists(filePath)) { fs.writeFileSync(filePath, '', 'utf8'); Console. log(theme, 'create missing ', fileName,' file '); }}); Fs.writefilesync (path.resolve(basepath, theme, 'SRC ', isSCSS? 'index.scss' : 'index.css'), indexContent); });Copy the code

After adding three lines of code and executing the NPM run gen command again, you can see that the automatically created style file is also automatically introduced in the overall style file, and you’re done. Of course, we manually create a base. SCSS file to prevent packaging errors.

At this point, we have basically automated the creation and introduction of component style files. You can re-define a new component in components.json and then go through the whole testing process yourself.

But maybe that doesn’t seem like much of an efficiency boost? Is that the reason? Of course it’s not finished yet!! Continue to read the following articles, you will feel the experience, so keep your hopes up.





Content of the past

  • ElementUI source code series 1 – Building project architecture from scratch, project preparation, project packaging, project testing process
  • ElementUI source code series 2 – introducing SCSS, using GULp to convert SCSS to CSS and complete and compress, using CP – CLI to move directories and files
  • ElementUI source code series 3 – Learn the gen-cssfile.js file for automatically creating component. SCSS files and generating the contents of index.scss files
  • ElementUI source code series 4 – Learn how to automatically create component directory structure and generate components. Json file contents in new.js
  • ElementUI source code series 5 – Learn the contents of the build-entry.js file that automatically generates the master entry file index.js




At this point, this article is finished, sa Hua Sa hua.

I hope this article has been helpful to you. If you have any questions, I am looking forward to your comments. Same old, likes + comments = you know it, favorites = you know it.