preface

Build your own VUE component air-UI (9) from the previous section — document writing with Vuepress already documents the Button tag. We also write 7 examples to describe button and button-group properties, so the final button.md code is as follows:

Still very detailed, specific document effects at the end of the section shown in GIF. Next we continue to document the other completed components.

How w to document the built-in service components?

The problem is that we can also introduce the tag component as a local component in the document. What about this internal service component, or directive type component? How do you introduce…? After all, in a Vuepress environment, there is no global registry of component libraries in main.js like air-UI projects. So how to find the Vue object of Vuepress?

If you write this directly in the Vuepress demo:

this.$notify({
  title: 'warning'.message: 'This is a warning message.'.type: 'warning'
});
Copy the code

This.$notify is not a function because the vuepress prototype does not have a $notify method.

Vuepress application level configuration enhanceapp.js

On a later check, vuePress can also be configured at the application level, including various plug-ins.

Since VuePress is a standard Vue application, we can do some application-level configuration by creating a.vuepress/ enhanceapp.js file, which will be imported into the application when it exists. Enhanceapp.js should export default a hook function and take an object containing some application-level properties as an argument. You can use this hook to install additional Vue plugins, register global components, add additional routing hooks, etc:

export default ({
  Vue, // VuePress is using the Vue constructor
  options, // Some options to attach to the root instance
  router, // Route instance of the current application
  siteData // Site metadata= > {})/ /... Do some other application-level optimizations
}
Copy the code

One example is:

/** * Extend the VuePress app */
import VueHighlightJS from 'vue-highlight.js';
import 'highlight.js/styles/atom-one-dark.css';
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import VueECharts from 'vue-echarts' // Registration chart
 
import './public/css/index.css' // Component CSS file
 
export default ({
 Vue, // VuePress is using the Vue constructor
 options, // Some options to attach to the root instance
 router, // Route instance of the current application
 siteData // Site metadata= > {})/ /... Do some other application-level optimizations
 Vue.use(VueHighlightJS)
 Vue.use(Element)
 Vue.component('chart', VueECharts)
}
Copy the code

I tried using Element-UI later and found that it was ok. You can use the element-UI components directly in the demo without having to declare them locally.

Access enhanceApp. Js

Create.vuepress/ enhanceapp.js and say:

import DemoBlock from '.. /.. /src/components/demo-block'
import AirUI from '.. /.. /src/components/index'
import '.. /.. /src/styles/index.scss'

export default ({
    Vue, // VuePress is using the Vue constructor= > {})/ /... Do some other application-level optimizations
  Vue.use(DemoBlock);
  Vue.use(AirUI);
}
Copy the code

Button /demo-base. Vue: Button /demo-base. Vue:

<script>
  export default {
    name: 'button-demo-base',
  }
</script>
Copy the code

Only name is retained and all other imports are removed. Because now all globally registered components are used, and demo-block is set to global registration.

Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
Copy the code

The result of running the interface, directly error?? Cannot assign to read only property ‘exports’ of object

There is nothing wrong with the code. When webpack is packaged, you can mix require and export in js files. Module. exports is not allowed in Webpack 2. The solution is to change it to ES6 and use export default.

Module. exports: components/index.js exports: module. The home-built VUE component Air-UI (8) – Implementation introduction component is mentioned. At that time, there was no problem with air-UI packaging, but at least it proved that Webpack 3.x could be mixed. So there are two paths open to us:

  1. To get rid ofcomponents/index.jsmodule.exportsGrammar, all goexport defaultIn this way, no error will be reported. It looks likeair-uiThe components will all bevuepressThe VUE object in is registered as a global component. butPartial import of syntax sugarYou can’t use it (although you don’t use it because you don’t save bulk)
  2. Keep mixing, but can’t import component libraries, can only import and reference individual components one by one.

Kids make choices. I want them all. Don’t you think it’s cool to partially introduce grammar candy? Why get rid of it. So I chose the second option (or the first option if you’re watching) and the new code looks like this:

import DemoBlock from '.. /.. /src/components/demo-block'
import Loading from '.. /.. /src/components/loading/index.js'
import Notification from '.. /.. /src/components/notification/index.js'
import Button from '.. /.. /src/components/button/index.js'
import ButtonGroup from '.. /.. /src/components/button-group/index.js'

import '.. /.. /src/styles/index.scss'

export default ({
    Vue, // VuePress is using the Vue constructor
  }) => {
  Vue.component(DemoBlock.name, DemoBlock);
  Vue.component(Button.name, Button);
  Vue.component(ButtonGroup.name, ButtonGroup);
  Vue.use(Loading.directive);
  Vue.prototype.$loading = Loading.service;
  Vue.prototype.$notify = Notification;
}
Copy the code

This is also possible, but is manual import, manual initialization. The downside of this, however, is that every time I add a component, the file has to add a reference to that component. But that’s ok, I’ll write an automated script later to simplify the process.

Write a demo of notification

Now that enhanceapp.js has been written, we can feel at ease to write the demo of Notification. Add the component navigation in config.js and notification. md in the Component directory, and you can write the demo example:

Now you can see the effect:

Perfect, now write component demo, without all that messy introduction, just not too convenient.

Automates picking component code

Although writing the demo is cool, but writing the document is still very uncomfortable, because I have to copy the code twice, first write the document, after tuning, and then put the demo some related component code, and then pull out into the MD file. Is there an automated way to handle this process? For example, take button/demo-disabled.vue as an example:

<template> <demo-block> <air-row> <air-button disabled> default </air-button> <air-button type="primary" <air-button type="success" disabled </air-button> <air-button type="info" <air-button > <air-button type="warning" disabled </air-button> <air-button type="danger" <air-button > <air-row> <air-row> <air-button plain disabled> <air-button > <air-button type="primary" <air-button > <air-button type="success" <air-button > <air-button type="info" <air-button > <air-button type="warning" plain disabled <air-button > <air-button type="danger" Plain disabled> Danger button </air-button> </air-row> </demo-block> </template> <script> export default {name: 'button-demo-disabled', } </script>Copy the code

In fact, the only thing to write to the MD file is the contents of the Demo-Block component (air-row is the component in the component library I created later). That is:

No script tag is required, and no style tag content is required. However, for the notification/demo-base.vue example:

<template>
  <demo-block>
    <air-button
      plain
      @click="open1">Automatic closing</air-button>
    <air-button
      plain
      @click="open2">Does not automatically shut down</air-button>
  </demo-block>
</template>

<script>
  export default {
    name: 'notification-demo-base'.methods: {
      open1() {
        const h = this.$createElement;

        this.$notify({
          title: 'Title name'.message: h('i', { style: 'color: teal'}, 'This is the prompt copy this is the prompt copy this is the prompt copy this is the prompt copy this is the prompt copy this is the prompt copy this is the prompt copy this is the prompt copy this is the prompt copy')}); }, open2() {this.$notify({
          title: 'tip'.message: 'This is a message that doesn't automatically close.'.duration: 0}); }}}</script>
Copy the code

I just want the HTML in the demo-block and the script tag, but I don’t need the name attribute because it’s misleading:

Sometimes I even need CSS to be listed, for example:

So I need to write an automated build script that requires the following:

  1. I just need to follow some rules to automatically introduce the demo
  2. And read the code in the demo, and then, according to some rules, decide which code to keep.

Component/button.md for example, I’ll just say:

::demo====button-demo-base==== uses the 'type', 'plain', 'round', and 'circle' attributes to define the style of a Button. ::demo====button-demo-disabled==== You can use the 'disabled' property to define whether the button is available, It accepts a Boolean value ## Text buttons buttons without borders and background colors :::demo====button-demo-text==== ## icon buttons Buttons with ICONS can enhance identification (with text) or save space (without text) :::demo====button-demo-icon==== Set the 'icon' attribute. The list of ICONS can refer to the icon component of Element or set the icon on the right side of the text. You can use the 'I' label and customize the icon. ## Button groups appear as button groups and are used for many similar operations :::demo====button-demo-group==== use the '<air-button-group>' tag to nest your buttons. Click the button to load data, and display the loading status on the button. :::demo====button-demo-loading==== To enable loading, simply set the 'loading' property to 'true'. The Button component provides three sizes in addition to the default, allowing you to choose the appropriate Button size for different scenarios. :::demo====button-demo-size==== Additional sizes: 'medium', 'small', 'mini', which can be configured by setting the 'size' property. Type # # Attributes | parameters | | | | alternatives, the default value | | -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- - | | size | | size String | medium/small/mini | -- - | | type | | type string | primary/success/warning/danger/info/text | -- - | | Plain | | whether simple button Boolean | -- - | false | | round whether | | round button Boolean | -- - | false | | circle whether | | circular button Boolean | -- - | false | in the | | loading | whether loading Boolean | -- - | false | | disabled whether | | disabled state Boolean | -- - | false | | icon | | icon class name string | -- - | -- - | | autofocus default focus | | whether Boolean | -- - | false | | native -type | | native type attribute string | button/submit/reset | button |Copy the code

The script will change me to this:

This is actually done through a gulP task:

// Optimize the document packaging logic
gulp.task('doc', cb => {
  return gulp.src('./docs/component_origin/*.md')
    // Template address modification
    .pipe(replace(/:::demo====(\w+)-(.+)====(.*)/g.function (match, p1, p2, p3) {
      // console.log("demo:" + match);
      // console.log("p3:" + p3);
      var str = `
<ClientOnly>
 <${p1}-${p2}></${p1}-${p2}>
</ClientOnly>`;
      str += '\n\n';
      if (p3.toString().trim()) {
        str += `::: tip\n${p3}\n:::\n\n`;
      }
      var fileName = `docs/.vuepress/components/${p1}/${p2}.vue`;
      // Then read the file to get the code inside
      // return fileName;
      var fileStr = fs.readFileSync(path.resolve(fileName));
      // Line by line
      str += "```vue\n";
      var tplArr = [], cssArr = [], jsArr = [], canAddTpl = false, canAddCss = false, canAddJs = false;
      fileStr.toString().replace(/(.+)/g.function (match) {
        // console.log(match);
        if (match.indexOf("</demo-block>") > - 1) {
          canAddTpl = false;
        }

        if (canAddTpl && match.trim()) {
          // It is best to indent this side twice more, i.e. 4 Spaces
          tplArr.push(match.substring(4, match.length));
        }
        if (canAddCss && match.trim()) {
          cssArr.push(match);
        }

        // Determine whether to ignore some js
        if (match.indexOf("script--ignore--start") > - 1) {
          canAddJs = false;
        }

        if (canAddJs && match.trim()) {
          jsArr.push(match);
        }
        // Determine whether to import the script document
        if (match.indexOf("<script doc>") > - 1) {
          canAddJs = true;
          jsArr.push("<script>");
        }

        if (match.indexOf("script--ignore--end") > - 1) {
          canAddJs = true;
        }

        if (match.indexOf("<demo-block") > - 1) {
          canAddTpl = true;
        }

        if (match.indexOf('<style scoped') > - 1) {
          canAddCss = true;
          cssArr.push("<style>");
        }

        if (match.indexOf("</style>") > - 1) {
          canAddCss = false;
        }

        if (match.indexOf("</script>") > - 1) {
          canAddJs = false; }});// Finally put it together
      str += tplArr.join("\n");
      if (jsArr.length) {
        str += "\n\n" + jsArr.join("\n");
      }
      if (cssArr.length) {
        str += "\n\n" + cssArr.join("\n");
      }
      str += "\n```";
      return str;
    }))
    .pipe(gulp.dest('./docs/component'));
});
Copy the code

Here I will analyze the main logic:

  1. Get the directory./docs/component_origin/*.mdAll of the md files under
  2. By this rule/:::demo====(\w+)-(.+)====(.*)/Get the values of p1, p2, p3. For example, suppose we have a row that looks like this:
:::demo====button-demo-disabled==== you can use it`disabled`Property to define whether the button is available, which accepts one`Boolean`valueCopy the code

P1 = button; P2 = demo-disabled; p3 = button;

You can use`disabled`Property to define whether the button is available, which accepts one`Boolean`value`
Copy the code
  1. Based on the values of p1 and p2, we can embed the syntax of demo:
var str = `
<ClientOnly>
 <${p1}-${p2}></${p1}-${p2}>
</ClientOnly>`;
Copy the code
  1. From the value of p3, we can embed the syntax of tip:
if (p3.toString().trim()) {
  str += `::: tip\n${p3}\n:::\n\n`;
}
Copy the code
  1. Next is the highlight, we need to according to the rules of the Vue demo, from the vue demo file to the corresponding code out
    1. Read the code in vue demo and extract it line by line:
        var fileName = `docs/.vuepress/components/${p1}/${p2}.vue`;
        // Then read the file to get the code inside
        // return fileName;
        var fileStr = fs.readFileSync(path.resolve(fileName));
    Copy the code
    1. extract<demo-block>... <demo-blcok>The code inside
    2. If you include<script doc>Next, extract the JS code. If no, skip the JS code
    3. If in the process, encounterscript--ignore--startThis flag indicates that the following JS code is not extracted
    4. If in the process, encounter"script--ignore--end"This tag indicates that the next JS code continues to extract
    5. And if you come across</script>This tag indicates the end of extracting JS code
    6. And if you come across<style scopedThis tag indicates that the CSS code is to be extracted next. If not, skip the CSS code
    7. And if you come across</style>This tag indicates the end of extracting CSS code
  2. The last few strings just add up. Take the above sentence for example, and the effect is:

The effect is very good, this way as long as the demo is written (prior to which code to extract, which do not extract, to set up first), then convert to MD document, it is not too convenient. The cost, of course, is that we need to create another docs/compnent_origin/ directory to write the unconverted document contents:

For example, suppose the demo is written like this:

<template> <demo-block customClass="demo-card"> <air-row> <air-col :span="8" v-for="(o, index) in 2" :key="o" :offset="index > 0 ? 2 : 0"> <air-card :body-style="{ padding: '0px' }"> <img src="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png" class="image"> <div style="padding: 14px;" > <span> <div class="bottom clearfix"> <time class="time">{{currentDate}}</time> <air-button type="text" Class ="button"> </air-button> </div> </air-card> </air-col> </air-row> </demo > </template> <script doc> //script--ignore--start import './card.scss' //script--ignore--end export default { //script--ignore--start name: 'card-demo-img', //script--ignore--end data() { return { currentDate: new Date().toLocaleString() }; } } </script> <style scoped> .time { font-size: 13px; color: #999; } .bottom { margin-top: 13px; line-height: 12px; } .button { padding: 0; float: right; } .image { width: 100%; display: block; } .clearfix:before, .clearfix:after { display: table; content: ""; } .clearfix:after { clear: both } </style>Copy the code

So this is what documentation looks like:

Of course the build needs to be changed, plus this task:

"docs:dev": "gulp doc && vuepress dev docs".Copy the code

Vuepress overwrites the constants palette. Styl

When I was working on the button document, I found that some examples exceeded the fixed width, resulting in line folding:

So to see if there’s a way to make the container a little wider, take a look at the console:

.theme-default-content:not(.custom) {
    max-width: 740px;
    margin: 0 auto;
    padding: 2rem 2.5 rem;
}
Copy the code

The maximum width is only 740px, which is not enough. We need to find a way to make it wider. The vuepress theme can be exported (of course, it can only be viewed, but not overwritten):

vuepress eject
Copy the code

There will be a.vuepress directory in the execution directory. Click on the source code for this theme. . Then you can from vuepress/theme/styles/wrapper. Styl find this configuration:

$wrapper max-width $contentWidth margin 0 auto padding 2rem 2.5 reem@media (max-width: $MQNarrow) PADDING 2reem@media (max-width: $MQMobileNarrow) padding 1.5remCopy the code

There’s this constant that’s the maximum width, and we just need to change the value of that variable. Vuepress provides the docs /. Vuepress/styles/palette. Styl can override the value of some variable, so we created, the document on the content is like this:

$contentWidth = 1000px;
Copy the code

So that’s going to make the width bigger, so if I recompile it, it’s going to make it wider.

Of course, you can also modify other variables based on personal habits, such as:

$accentColor =blue// default theme color $textColor = red// default font color $borderColor = #eaecef// default borderColor $codeBgColor = #282c34// default background colorCopy the code

Vuepress changes the global style

If you think vuepress’s default theme style is too ugly, you can actually change the global style. For example, the original table style looks like this:

I feel a bit ugly, I can create the file: docs /. Vuepress/styles/index styl, then to rewrite it to change the style:

tr
  border-top none
th.td
  border: none
  border-bottom: 1.px solid #dfe2e5
Copy the code

The effect will look better:

Optimize enhanceApp. Js

As mentioned above, we use manual import in enhanceapp.js, which means that every time I add a component, I have to modify the file to add a reference to the new component. This is a bit confusing, so is there any case that it is not needed? Json file in the root directory, which is used to package the component separately later. That is, once I add a component, I add the component’s name and its reference path to the components.json. So we’ll just read the file, and then the script will generate references to these components and initialize them, so we don’t need to change enhanceapp.js every time.

This automation script is also generated using gulp:

/ / = = = = = = = = = = = = = vuepress enhanceApp. Js building start = = = = = = = = = = =
const Components = require('./components.json');
// There are some components to filter out, including built-in components and directive components
const excludedComponents = ['locale'.'loading'.'message'.'message-box'.'notification'.'infinite-scroll'];
let needComponents = [];
gulp.task('vuepress', cb => {
  return gulp.src('./docs/.vuepress/enhanceApp_origin.js')
    // Template address modification
    .pipe(replace(/(.*gulpStart::(.*)::gulpEnd.*)/g.function (match, p1, p2) {
      var str = '//======== the code in between is all generated by the build ====\n';
      if (p2 === 'import') {
        _.each(Components, function (path, item) {
          // console.log("item:" + item);
          if (_.indexOf(excludedComponents, item) === - 1) {
            var name = upperName(item);
            needComponents.push(name);
            // SRC =... // SRC =... // /.. /src
            path = '.. /. ' + path;
            str += `import ${name} from '${path}'\n`}}); }if (p2 === 'use') {
        _.each(needComponents, function (item) {
          str += `  Vue.component(${item}.name, ${item}); \n`;
        })
      }
      str += '//======== the code in between is all generated by the build ====\n';
      return str;
    }))
    .pipe(rename('enhanceApp.js'))
    .pipe(gulp.dest('./docs/.vuepress'));
});
/ / = = = = = = = = = = = = = vuepress enhanceApp. Js building end = = = = = = = = = = =
gulp.task('buildDoc'['doc'.'vuepress']);
Copy the code

The logic is not hard:

  1. readenhanceApp_origin.js, read line by line
  2. If any flags are encountered, read the action
  3. If it isimport, then willcomponents.jsonThe components inside are imported
  4. If it isuse, then willcomponents.jsonAll the components inside are initialized
  5. Remove any non-label components that do not apply to this template

So enhanceapp_origine.js reads as follows:

/** * Extension VuePress, this js component is built by the GLUP task, do not add it to this file, if there is any change, add */ to enhanceapp_origine.js
import DemoBlock from '.. /.. /src/components/demo-block'
import Loading from '.. /.. /src/components/loading/index.js'
import InfiniteScroll from '.. /.. /src/components/infinite-scroll/index.js'
import Message from '.. /.. /src/components/message/index.js'
import MessageBox from '.. /.. /src/components/message-box/index.js'
import Notification from '.. /.. /src/components/notification/index.js'
//gulpStart::import::gulpEnd
import '.. /.. /src/styles/index.scss'

export default ({
  Vue, // VuePress is using the Vue constructor
}) => {
  Vue.component(DemoBlock.name, DemoBlock);
  //gulpStart::use::gulpEnd
  // Next handle the built-in functions
  // Loading is a special component. It cannot be mounted as a component, only as instructions and built-in functions
  Vue.use(InfiniteScroll);
  Vue.use(Loading.directive);
  // The following components also need to be loaded separately. They also have no label mode, only built-in service mode
  Vue.prototype.$loading = Loading.service;
  Vue.prototype.$message = Message;
  Vue.prototype.$msgbox = MessageBox;
  Vue.prototype.$alert = MessageBox.alert;
  Vue.prototype.$confirm = MessageBox.confirm;
  Vue.prototype.$prompt = MessageBox.prompt;
  Vue.prototype.$notify = Notification;
}
Copy the code

Enhanceapp.js will become:

/** * Extension VuePress, this js component is built by the GLUP task, do not add it to this file, if there is any change, add */ to enhanceapp_origine.js
import DemoBlock from '.. /.. /src/components/demo-block'
import Loading from '.. /.. /src/components/loading/index.js'
import InfiniteScroll from '.. /.. /src/components/infinite-scroll/index.js'
import Message from '.. /.. /src/components/message/index.js'
import MessageBox from '.. /.. /src/components/message-box/index.js'
import Notification from '.. /.. /src/components/notification/index.js'
//======== The code in between is all generated by the build ====
import Button from '.. /.. /src/components/button/index.js'
import ButtonGroup from '.. /.. /src/components/button-group/index.js'
import Row from '.. /.. /src/components/row/index.js'
import Col from '.. /.. /src/components/col/index.js'
import Container from '.. /.. /src/components/container/index.js'
import Header from '.. /.. /src/components/header/index.js'
import Footer from '.. /.. /src/components/footer/index.js'
import Main from '.. /.. /src/components/main/index.js'
import Aside from '.. /.. /src/components/aside/index.js'
import Checkbox from '.. /.. /src/components/checkbox/index.js'
import CheckboxButton from '.. /.. /src/components/checkbox-button/index.js'
import CheckboxGroup from '.. /.. /src/components/checkbox-group/index.js'
import Link from '.. /.. /src/components/link/index.js'
import Radio from '.. /.. /src/components/radio/index.js'
import RadioButton from '.. /.. /src/components/radio-button/index.js'
import RadioGroup from '.. /.. /src/components/radio-group/index.js'
import Input from '.. /.. /src/components/input/index.js'
import Select from '.. /.. /src/components/select/index.js'
import Option from '.. /.. /src/components/option/index.js'
import OptionGroup from '.. /.. /src/components/option-group/index.js'
import Tag from '.. /.. /src/components/tag/index.js'
import Scrollbar from '.. /.. /src/components/scrollbar/index.js'
import Autocomplete from '.. /.. /src/components/autocomplete/index.js'
import CollapseTransition from '.. /.. /src/transitions/collapse-transition'
import InputNumber from '.. /.. /src/components/input-number/index.js'
import Cascader from '.. /.. /src/components/cascader/index.js'
import CascaderPanel from '.. /.. /src/components/cascader-panel/index.js'
import Switch from '.. /.. /src/components/switch/index.js'
import Tooltip from '.. /.. /src/components/tooltip/index.js'
import Slider from '.. /.. /src/components/slider/index.js'
import DatePicker from '.. /.. /src/components/date-picker/index.js'
import TimeSelect from '.. /.. /src/components/time-select/index.js'
import TimePicker from '.. /.. /src/components/time-picker/index.js'
import Upload from '.. /.. /src/components/upload/index.js'
import Progress from '.. /.. /src/components/progress/index.js'
import Dialog from '.. /.. /src/components/dialog/index.js'
import Form from '.. /.. /src/components/form/index.js'
import FormItem from '.. /.. /src/components/form-item/index.js'
import Table from '.. /.. /src/components/table/index.js'
import TableColumn from '.. /.. /src/components/table-column/index.js'
import Popover from '.. /.. /src/components/popover/index.js'
import Menu from '.. /.. /src/components/menu/index.js'
import Submenu from '.. /.. /src/components/submenu/index.js'
import MenuItem from '.. /.. /src/components/menu-item/index.js'
import MenuItemGroup from '.. /.. /src/components/menu-item-group/index.js'
import Dropdown from '.. /.. /src/components/dropdown/index.js'
import DropdownMenu from '.. /.. /src/components/dropdown-menu/index.js'
import DropdownItem from '.. /.. /src/components/dropdown-item/index.js'
import Rate from '.. /.. /src/components/rate/index.js'
import ColorPicker from '.. /.. /src/components/color-picker/index.js'
import Transfer from '.. /.. /src/components/transfer/index.js'
import Tree from '.. /.. /src/components/tree/index.js'
import Pagination from '.. /.. /src/components/pagination/index.js'
import Badge from '.. /.. /src/components/badge/index.js'
import Avatar from '.. /.. /src/components/avatar/index.js'
import Alert from '.. /.. /src/components/alert/index.js'
import Tabs from '.. /.. /src/components/tabs/index.js'
import TabPane from '.. /.. /src/components/tab-pane/index.js'
import Breadcrumb from '.. /.. /src/components/breadcrumb/index.js'
import BreadcrumbItem from '.. /.. /src/components/breadcrumb-item/index.js'
import PageHeader from '.. /.. /src/components/page-header/index.js'
import Steps from '.. /.. /src/components/steps/index.js'
import Step from '.. /.. /src/components/step/index.js'
import Card from '.. /.. /src/components/card/index.js'
import Carousel from '.. /.. /src/components/carousel/index.js'
import CarouselItem from '.. /.. /src/components/carousel-item/index.js'
import Collapse from '.. /.. /src/components/collapse/index.js'
import CollapseItem from '.. /.. /src/components/collapse-item/index.js'
import Timeline from '.. /.. /src/components/timeline/index.js'
import TimelineItem from '.. /.. /src/components/timeline-item/index.js'
import Divider from '.. /.. /src/components/divider/index.js'
import Calendar from '.. /.. /src/components/calendar/index.js'
import Image from '.. /.. /src/components/image/index.js'
import Backtop from '.. /.. /src/components/backtop/index.js'
import Icon from '.. /.. /src/components/icon/index.js'
import Drawer from '.. /.. /src/components/drawer/index.js'
//======== The code in between is all generated by the build ====

import '.. /.. /src/styles/index.scss'

export default ({
  Vue, // VuePress is using the Vue constructor
}) => {
  Vue.component(DemoBlock.name, DemoBlock);
//======== The code in between is all generated by the build ====
  Vue.component(Button.name, Button);
  Vue.component(ButtonGroup.name, ButtonGroup);
  Vue.component(Row.name, Row);
  Vue.component(Col.name, Col);
  Vue.component(Container.name, Container);
  Vue.component(Header.name, Header);
  Vue.component(Footer.name, Footer);
  Vue.component(Main.name, Main);
  Vue.component(Aside.name, Aside);
  Vue.component(Checkbox.name, Checkbox);
  Vue.component(CheckboxButton.name, CheckboxButton);
  Vue.component(CheckboxGroup.name, CheckboxGroup);
  Vue.component(Link.name, Link);
  Vue.component(Radio.name, Radio);
  Vue.component(RadioButton.name, RadioButton);
  Vue.component(RadioGroup.name, RadioGroup);
  Vue.component(Input.name, Input);
  Vue.component(Select.name, Select);
  Vue.component(Option.name, Option);
  Vue.component(OptionGroup.name, OptionGroup);
  Vue.component(Tag.name, Tag);
  Vue.component(Scrollbar.name, Scrollbar);
  Vue.component(Autocomplete.name, Autocomplete);
  Vue.component(CollapseTransition.name, CollapseTransition);
  Vue.component(InputNumber.name, InputNumber);
  Vue.component(Cascader.name, Cascader);
  Vue.component(CascaderPanel.name, CascaderPanel);
  Vue.component(Switch.name, Switch);
  Vue.component(Tooltip.name, Tooltip);
  Vue.component(Slider.name, Slider);
  Vue.component(DatePicker.name, DatePicker);
  Vue.component(TimeSelect.name, TimeSelect);
  Vue.component(TimePicker.name, TimePicker);
  Vue.component(Upload.name, Upload);
  Vue.component(Progress.name, Progress);
  Vue.component(Dialog.name, Dialog);
  Vue.component(Form.name, Form);
  Vue.component(FormItem.name, FormItem);
  Vue.component(Table.name, Table);
  Vue.component(TableColumn.name, TableColumn);
  Vue.component(Popover.name, Popover);
  Vue.component(Menu.name, Menu);
  Vue.component(Submenu.name, Submenu);
  Vue.component(MenuItem.name, MenuItem);
  Vue.component(MenuItemGroup.name, MenuItemGroup);
  Vue.component(Dropdown.name, Dropdown);
  Vue.component(DropdownMenu.name, DropdownMenu);
  Vue.component(DropdownItem.name, DropdownItem);
  Vue.component(Rate.name, Rate);
  Vue.component(ColorPicker.name, ColorPicker);
  Vue.component(Transfer.name, Transfer);
  Vue.component(Tree.name, Tree);
  Vue.component(Pagination.name, Pagination);
  Vue.component(Badge.name, Badge);
  Vue.component(Avatar.name, Avatar);
  Vue.component(Alert.name, Alert);
  Vue.component(Tabs.name, Tabs);
  Vue.component(TabPane.name, TabPane);
  Vue.component(Breadcrumb.name, Breadcrumb);
  Vue.component(BreadcrumbItem.name, BreadcrumbItem);
  Vue.component(PageHeader.name, PageHeader);
  Vue.component(Steps.name, Steps);
  Vue.component(Step.name, Step);
  Vue.component(Card.name, Card);
  Vue.component(Carousel.name, Carousel);
  Vue.component(CarouselItem.name, CarouselItem);
  Vue.component(Collapse.name, Collapse);
  Vue.component(CollapseItem.name, CollapseItem);
  Vue.component(Timeline.name, Timeline);
  Vue.component(TimelineItem.name, TimelineItem);
  Vue.component(Divider.name, Divider);
  Vue.component(Calendar.name, Calendar);
  Vue.component(Image.name, Image);
  Vue.component(Backtop.name, Backtop);
  Vue.component(Icon.name, Icon);
  Vue.component(Drawer.name, Drawer);
//======== The code in between is all generated by the build ====

  // Next handle the built-in functions
  // Loading is a special component. It cannot be mounted as a component, only as instructions and built-in functions
  Vue.use(InfiniteScroll);
  Vue.use(Loading.directive);
  // The following components also need to be loaded separately. They also have no label mode, only built-in service mode
  Vue.prototype.$loading = Loading.service;
  Vue.prototype.$message = Message;
  Vue.prototype.$msgbox = MessageBox;
  Vue.prototype.$alert = MessageBox.alert;
  Vue.prototype.$confirm = MessageBox.confirm;
  Vue.prototype.$prompt = MessageBox.prompt;
  Vue.prototype.$notify = Notification;
}
Copy the code

Of course, this method only applies to component additions of the label component. If it is a directive or service type, you still need to add it manually, but you need to add it to enhanceapp_origine.js, otherwise the package will be overwritten. But many of the subsequent tags are also tag components. The final build instruction is changed:

"docs:dev": "gulp buildDoc && vuepress dev docs".Copy the code

Optimized the way home.vue is written

Many of our demo examples are written on vuePress. Then home.vue is useless. This home page is still very useful, because later we do theme customization, custom a new theme, when debugging, is the need for a page to show the effect of debugging, this page had better contain all the components of all the demo, so that you can see at a glance. And Home.vue plays that role. This is where all the component demos are displayed for later debugging of theme customization. Suppose we now have two wiki examples, Button and Notification. Then home.vue is changed to:

<template> <div class="wrap"> <h1 class="warp-title">{{MSG}}</h1> <h2> Common components </h2> <h4>Button <button-demo-base></button-demo-base> <button-demo-disabled></button-demo-disabled> <button-demo-group></button-demo-group> <button-demo-icon></button-demo-icon> <button-demo-loading></button-demo-loading> <button-demo-size></button-demo-size> <button-demo-text></button-demo-text> <h2> < H4 >< notification-demo-base></notification-demo-base> <notification-demo-close></notification-demo-close> <notification-demo-html></notification-demo-html> <notification-demo-icon></notification-demo-icon> <notification-demo-offset></notification-demo-offset> <notification-demo-position></notification-demo-position> </div> </template> <script> import ButtonDemoBase from '.. /.. /docs/.vuepress/components/button/demo-base' import ButtonDemoDisabled from '.. /.. /docs/.vuepress/components/button/demo-disabled' import ButtonDemoGroup from '.. /.. /docs/.vuepress/components/button/demo-group' import ButtonDemoIcon from '.. /.. /docs/.vuepress/components/button/demo-icon' import ButtonDemoLoading from '.. /.. /docs/.vuepress/components/button/demo-loading' import ButtonDemoSize from '.. /.. /docs/.vuepress/components/button/demo-size' import ButtonDemoText from '.. /.. /docs/.vuepress/components/button/demo-text' import NotificationDemoBase from '.. /.. /docs/.vuepress/components/notification/demo-base' import NotificationDemoClose from '.. /.. /docs/.vuepress/components/notification/demo-close' import NotificationDemoHtml from '.. /.. /docs/.vuepress/components/notification/demo-html' import NotificationDemoIcon from '.. /.. /docs/.vuepress/components/notification/demo-icon' import NotificationDemoOffset from '.. /.. /docs/.vuepress/components/notification/demo-offset' import NotificationDemoPosition from '.. /.. /docs/.vuepress/components/notification/demo-position' export default { data () { return { msg: }}, components: {[ButtonDemoBase. Name]: ButtonDemoBase, [ButtonDemoDisabled. Name]: {[ButtonDemoBase. ButtonDemoDisabled, [ButtonDemoGroup.name]: ButtonDemoGroup, [ButtonDemoIcon.name]: ButtonDemoIcon, [ButtonDemoLoading.name]: ButtonDemoLoading, [ButtonDemoSize.name]: ButtonDemoSize, [ButtonDemoText.name]: ButtonDemoText, [NotificationDemoBase.name]: NotificationDemoBase, [NotificationDemoClose.name]: NotificationDemoClose, [NotificationDemoHtml.name]: NotificationDemoHtml, [NotificationDemoIcon.name]: NotificationDemoIcon, [NotificationDemoOffset.name]: NotificationDemoOffset, [NotificationDemoPosition.name]: NotificationDemoPosition, }, methods: { }, mounted () { } } </script> <style lang="scss"> .wrap{ color: #2c3e50; margin: 10px; margin-top: 60px; .warp-title { text-align: center; } } h1,h3{ margin: 0; } h3{ font-weight: normal; font-size: 16px; } h4{ font-weight: normal; font-size: 14px; text-align: left; background-color: #e9e9e9; padding: 10px; } h2{ text-align: left; width: 100%; border-bottom: 1px dashed #ededed; font-size: 16px; height: 40px; line-height: 40px; text-indent: 10px; margin-top: 30px; margin-bottom: 0; } .demo-block{ margin-bottom: 10px; } </style>Copy the code

Note one more detail, because every demo refers to the demo-block component, which is registered as global, so register it in main.js:

import DemoBlock from './components/demo-block'

Vue.use(DemoBlock)
Copy the code

This works:

Same as in the Vuepress document.

However, there is still a problem here, that is, the files of the two components are displayed very long, and they are repetitive. Can I use automatic scripts to process them? Just like the md files and enhanceapp.js, I still use gulp to do this:

//============= home vue build start ===========
var componentsMap = {};
var loadUIComponent = function (name) {
  var files = fs.readdirSync(path.resolve(`docs/.vuepress/components/${name}`));
  // Iterate over the list of files read
  files.forEach(function (filename) {
    console.log(filename);
    // only files ending in.vue are fetched
    if (filename.indexOf(".vue") > - 1) {
      componentsMap[name] = componentsMap[name] || [];
      // The.vue after the channel file name
      componentsMap[name].push(filename.split(".") [0]); }});return componentsMap[name];
};

// Optimize the document packaging logic
gulp.task('homeVue', cb => {
  return gulp.src('./src/views/home_origin.vue')
    // Template address modification
    .pipe(replace(/(.*gulpStart::(.*)::gulpEnd.*)/g.function (match, p1, p2) {
      var str = ' ';
      if (p2.indexOf("ui__") > - 1) {
        var uiName = p2.split("__") [1];
        var arr = loadUIComponent(uiName);
        arr = _.map(arr, function (item) {
          return `    <${uiName}-${item}></${uiName}-${item}> `;
        });
        str = arr.join("\n");
      }

      if (p2 === 'import') {
        _.each(componentsMap, function (arr, uiName) {
          // console.log("uiName:" + uiName);
          // console.log("uiArr:" + JSON.stringify(arr));
          _.each(arr, function (item) {
            var itemUpperStr = _.map(item.split("-"), function (tmpItem) {
              // Uppercase
              return tmpItem.replace(tmpItem[0], tmpItem[0].toUpperCase());
            }).join("");
            str += `  import ${uiName.replace(uiName[0], uiName[0].toUpperCase())}${itemUpperStr}from '.. /.. /docs/.vuepress/components/${uiName}/${item}'\n`; })});// Remove the last space
        var strArr = str.split("\n");
        strArr = strArr.splice(0, strArr.length - 1);
        str = strArr.join("\n");
      }

      if (p2 === 'init') {
        _.each(componentsMap, function (arr, uiName) {
          // console.log("uiName:" + uiName);
          // console.log("uiArr:" + JSON.stringify(arr));
          _.each(arr, function (item, index) {
            var itemUpperStr = _.map(item.split("-"), function (tmpItem) {
              // Uppercase
              return tmpItem.replace(tmpItem[0], tmpItem[0].toUpperCase());
            }).join("");
            var uiModuleName = `${uiName.replace(uiName[0], uiName[0].toUpperCase())}${itemUpperStr}`;
            str += `      [${uiModuleName}.name]: ${uiModuleName},\n`; })});// Remove the comma from the last line, and wrap the last line
        var strArr = str.split("\n");
        // Remove blank lines
        strArr = strArr.splice(0, strArr.length - 1);
        str = strArr.join("\n");
        // Remove the comma
        str = str.substring(0, str.length - 1);
      }

      return str;
    }))
    .pipe(rename('home.vue'))
    .pipe(gulp.dest('./src/views'));
});
Copy the code

The logic is not complicated. We can read the original home_origine.vue file and then build it to generate the real home.vue in the same directory. Then write the corresponding string

  1. I read it line by line, and I read it to P2
  2. If I have p2, then I have 2vuepressComponent directory document, and then get the name of the demo file in that directory, and then if encounteredimportImport, if you encounterinitInitialize.

Suppose the content of home_origine.vue looks like this:

< the template > < div class = "wrap" > < h1 class = "warp - the title" > {{MSG}} < / h1 > < h2 > common component < / h2 > < h4 > Button Button < / h4 > <! -- gulpStart ui__button: : gulpEnd - > < h2 > notice < / h2 > < h4 > Notification notice < / h4 > <! --gulpStart::ui__notification::gulpEnd--> </div> </template> <script> //gulpStart::import::gulpEnd export default { data () {return {MSG: ` AIR - UI - based on vue2. X, reusable UI components `}}, components: {/ / gulpStart: : init: : gulpEnd}, the methods: { }, mounted () { } } </script> <style lang="scss"> .wrap{ color: #2c3e50; margin: 10px; margin-top: 60px; .warp-title { text-align: center; } } h1,h3{ margin: 0; } h3{ font-weight: normal; font-size: 16px; } h4{ font-weight: normal; font-size: 14px; text-align: left; background-color: #e9e9e9; padding: 10px; } h2{ text-align: left; width: 100%; border-bottom: 1px dashed #ededed; font-size: 16px; height: 40px; line-height: 40px; text-indent: 10px; margin-top: 30px; margin-bottom: 0; } .demo-block{ margin-bottom: 10px; } </style>Copy the code

So home.vue looks something like this:

<template> <div class="wrap"> <h1 class="warp-title">{{MSG}}</h1> <h2> Common components </h2> <h4>Button <button-demo-base></button-demo-base> <button-demo-disabled></button-demo-disabled> <button-demo-group></button-demo-group> <button-demo-icon></button-demo-icon> <button-demo-loading></button-demo-loading> <button-demo-size></button-demo-size> <button-demo-text></button-demo-text> <h2> < H4 >< notification-demo-base></notification-demo-base> <notification-demo-close></notification-demo-close> <notification-demo-html></notification-demo-html> <notification-demo-icon></notification-demo-icon> <notification-demo-offset></notification-demo-offset> <notification-demo-position></notification-demo-position> </div> </template> <script> import ButtonDemoBase from '.. /.. /docs/.vuepress/components/button/demo-base' import ButtonDemoDisabled from '.. /.. /docs/.vuepress/components/button/demo-disabled' import ButtonDemoGroup from '.. /.. /docs/.vuepress/components/button/demo-group' import ButtonDemoIcon from '.. /.. /docs/.vuepress/components/button/demo-icon' import ButtonDemoLoading from '.. /.. /docs/.vuepress/components/button/demo-loading' import ButtonDemoSize from '.. /.. /docs/.vuepress/components/button/demo-size' import ButtonDemoText from '.. /.. /docs/.vuepress/components/button/demo-text' import NotificationDemoBase from '.. /.. /docs/.vuepress/components/notification/demo-base' import NotificationDemoClose from '.. /.. /docs/.vuepress/components/notification/demo-close' import NotificationDemoHtml from '.. /.. /docs/.vuepress/components/notification/demo-html' import NotificationDemoIcon from '.. /.. /docs/.vuepress/components/notification/demo-icon' import NotificationDemoOffset from '.. /.. /docs/.vuepress/components/notification/demo-offset' import NotificationDemoPosition from '.. /.. /docs/.vuepress/components/notification/demo-position' export default { data () { return { msg: }}, components: {[ButtonDemoBase. Name]: ButtonDemoBase, [ButtonDemoDisabled. Name]: {[ButtonDemoBase. ButtonDemoDisabled, [ButtonDemoGroup.name]: ButtonDemoGroup, [ButtonDemoIcon.name]: ButtonDemoIcon, [ButtonDemoLoading.name]: ButtonDemoLoading, [ButtonDemoSize.name]: ButtonDemoSize, [ButtonDemoText.name]: ButtonDemoText, [NotificationDemoBase.name]: NotificationDemoBase, [NotificationDemoClose.name]: NotificationDemoClose, [NotificationDemoHtml.name]: NotificationDemoHtml, [NotificationDemoIcon.name]: NotificationDemoIcon, [NotificationDemoOffset.name]: NotificationDemoOffset, [NotificationDemoPosition.name]: NotificationDemoPosition }, methods: { }, mounted () { } } </script> <style lang="scss"> .wrap{ color: #2c3e50; margin: 10px; margin-top: 60px; .warp-title { text-align: center; } } h1,h3{ margin: 0; } h3{ font-weight: normal; font-size: 16px; } h4{ font-weight: normal; font-size: 14px; text-align: left; background-color: #e9e9e9; padding: 10px; } h2{ text-align: left; width: 100%; border-bottom: 1px dashed #ededed; font-size: 16px; height: 40px; line-height: 40px; text-indent: 10px; margin-top: 30px; margin-bottom: 0; } .demo-block{ margin-bottom: 10px; } </style>Copy the code

So next time you have a new component to add to the demo, just add it to the template:

<h4>Input Input box </h4> <! --gulpStart::ui__input::gulpEnd-->Copy the code

You will go to the docs /. Vuepress/components/input the directory to find all the demo files, and imported into it. Of course, the final build should also change:

"start": "gulp homeVue && npm run dev".Copy the code

There is a detail here, because the demo is introduced as a component, so every demo needs to initialize the name property, otherwise it will not be found. That’s why we set the name in the demo, just for this.

conclusion

This section focuses on the vuepress documentation framework, adding a lot of advanced things, and in order to make things easier, some gulp tasks have been added to make it easier for developers to write demos and introduce demo examples. But don’t you think there are no more problems with Vuepress? No, no, no, we haven’t talked about pit entering and pit climbing. In the next section, we will talk about the pit climbing experience of Vuepress.


Series of articles:

  • Air-ui (1) — Why do I need to build an Element UI component
  • Self-built VUE component AIR-UI (2) — Take a look at the Element UI project
  • Self-built VUE component AIR-UI (3) – CSS development specification
  • Air-ui (4) — Air-UI environment setup and directory structure
  • Air-ui (5) — Create the first vUE component, Button
  • Self-built VUE component AIR-UI (6) – Creates built-in service components
  • Build vUE component AIR-UI (7) – Create command component
  • Self-built VUE component AIR-UI (8) — Implementation part introduces components
  • Build your own VUE component air-UI (9) — document with Vuepress
  • Air-ui (10) — Vuepress Documentation (Advanced version)
  • Vue Component Air-UI (11) — Vuepress Documentation (Crawl version)
  • Self-built VUE component AIR-UI (12) — Internationalization mechanism
  • Self-built VUE Component AIR-UI (13) — Internationalization Mechanism (Advanced Version)
  • Self-built VUE component AIR-UI (14) — Packaged Build (Dev and Dist)
  • Self-built VUE component AIR-UI (15) — Theme customization
  • Self-built VUE component AIR-UI (16) – Packages to build pub tasks
  • Build your own VUE component AIR-UI (17) – Develop a pit crawl and summary