A Vite plugin, unified team norms, do not do repetitive things
Published: 2021-02-20 06:48:58

> Since the team has been using Nuxt.js, I like many of its features, especially the convention-style directory structure. Vite really feels great after being exposed to it. Although it still lacks many features, most of them can make up for it by themselves. Taking advantage of the New Year holiday, I wrote a Vite plugin with a contracted directory structure. Access address: [Document](https://ziping-li.github.io/convue/) | [GitHub](https://github.com/ziping-li/convue)


## Get started

1. Install convue.

```bash
yarn add convue -D
```

2. Use in the project

Use in vite.config.js

```js
import convue from'convue';

export default defineConfig({
  plugins: [
    ...convue({}),
  ],
});
```

3. Introduce the required packages in main.js, if you don’t need them, you don’t need to introduce them

```js
import {createApp} from'vue';
import App from'./src/App.vue';
import router from'pages-generated';
import globalComponent from'components-generated';
import store from'store-generated';
import plugin from'plugin-generated';
import i18n from'locale-generated';

const app = createApp(App);
window.__APP__ = app; // In order for the middleware to get the component instance, you can remove this line if you don’t need to use it

app.use(router);
app.use(globalComponent);
app.use(store);
app.use(plugin);
app.use(i18n);
app.mount('#app');
```

### Use cli

Convue provides a set of scaffolding tools for initializing projects. Currently, it supports two development modes: sfc (vue single file) and tsx.

```bash
# step 1
yarn global add convue-cli
# step 2-Enter convue in the command line, and you will be prompted for operation later
convue
# step 3-Enter project file
npm run dev
```

### Project Directory

Convue uses a convention-style catalog form, so we need to abide by this set of development methods. It is often an effective measure to achieve a unified standard in the project and improve efficiency.

If you need to change the directory structure, you can [refer to configuration items](https://ziping-li.github.io/convue/config/page).

Take the tsx form as an example:

```js
.
├── public
├── src
    ├── components
    ├── layouts
        ├── default.tsx
    ├── locales
        ├── en-US.ts
        ├── zh-CN.ts
    ├── middleware
    ├── pages
        └── index.tsx
    ├── plugins
    ├── store
    └── App.tsx
├── .babelrc
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── .stylelintrc.json
├── index.html
├── package.json
├── shims.d.ts
├── tsconfig.json
├── vite.config.ts
├── yarn.lock
```

## Router

By default, convue will automatically load the .vue|.js|.jsx|.ts|.tsx file in the /src/pages directory and generate a route corresponding to the file name.

For example, the router address corresponding to the index.tsx file in the /src/pages directory is /, and the routing address corresponding to the user.tsx file is /user.

### Dynamic router

The naming rule for dynamic router is: param[.vue|.js|.jsx|.ts|.tsx]

### Add router information

Add a route tag in the /src/pages/index.tsx file.

```js
{
  /* <route>
  name:'test'
  meta:
    title: 111
</route> */
}

import {defineComponent} from'vue';

export default defineComponent({
  setup() {
    return () => <div></div>;
  },
});
```

If it is sfc (vue single file), no comment is required.

The syntax supported by the route tag is'json5' |'json' |'yaml', the default is yaml, if it is json syntax, just specify the lang of the route.

```js
{
  /* <route lang="json">
  {
    name:'test',
    meta: {
      title: 111
    }
  }
</route> */
}

import {defineComponent} from'vue';

export default defineComponent({
  setup() {
    return () => <div></div>;
  },
});
```

### Router 404 redirect

When the address of the website is not in the routing registry, if there is a 404 page under /src/pages, it will be redirected to the /404 address, otherwise it will be redirected to the / address.

## Layout

Layout refers to a container component in a public area of ​​a website.

By default, convue will load the .vue|.js|.jsx|.ts|.tsx file in the /src/layouts directory and import it into the routing table. The default loaded is default[.vue|.js|.jsx|. ts|.tsx].

The router-view component must be included in the file.

```js
import {defineComponent} from'vue';

export default defineComponent({
  setup() {
    return () => (
      <div>
        <span>default layout</span>
        <router-view></router-view>
      </div>
    );
  },
});
```

### Change the layout of the current page

Add a route tag to the file and specify the layout in meta. The value of layout corresponds to the file name under /src/layouts.

```js
{
  /* <route>
  name:'test'
  meta:
    title: 111
    layout: empty
</route> */
}

import {defineComponent} from'vue';

export default defineComponent({
  setup() {
    return () => <div></div>;
  },
});
```

For other rules, please [refer to layout configuration items](https://ziping-li.github.io/convue/config/layout).

## Middleware

By default, convue will load the .ts | .js file in the /src/middleware directory and execute it in the pre-hook of the routing global.

For example, write an auth middleware

```js
export default ({ redirect, store }) => {
  if (!store.state.isLogined) {
    redirect('/login');
  }
};
```

### Parameters

These parameters are provided to facilitate development:

-query: query parameter of the current route
-params: params parameters of the current route
-route: current route information
-redirect: redirect function, accept a url as a parameter
-store: access to global status
-app: current vue instance
-env: list of environment variables

For other rules, please [refer to page configuration items](https://ziping-li.github.io/convue/config/page).


## loading

-Type: `string`
-Default: `undefined`

The loading color setting before the vue instance of the page is created

For the usage guide, please [refer to head](https://ziping-li.github.io/convue/guide/head).

## progress

-Type: `boolean | Progress`
-Default: `true`

Progress bar setting during route switching

If set to false, it will not be displayed and the code will not be introduced.

Progress type
```ts
export interface Progress {
  color?: string;
  size?: string;
}
```
If you pass in an object, you can set the color and size of the progress bar.

## Global store

By default, convue will load the .js|.ts file in the /src/store directory and configure it automatically in vuex.

The content structure of the file is unified with vuex, as follows

```js
export default {
  state: () => (()),
  mutations: {},
  actions: {},
  getters: {},
};
```

### Description

The index[.js|.ts] under /src/store will load the vuex item directly, and other files will be configured in the form of modules.

For example, there are two files index.js and user.js.

index.js

```js
export default {
  state: () => ({
    text:'hello',
  }),
};
```

user.js

```js
export default {
  state: () => ({
    name:'convue',
  }),
};
```

Then the vuex store is actually this structure

```js
export default {
  state: () => ({
    text:'hello',
  }),
  modules: {
    user: {
      state: () => ({
        text:'convue',
      }),
    },
  },
};
```

For other rules, please [refer to store configuration items](https://ziping-li.github.io/convue/config/store).

## Global components

By default, convue will register the .vue|.js|.jsx|.ts|.tsx file in the /src/components directory as a global component.

For example, there is a Hello.tsx component under /src/components

```js
import {defineComponent} from'vue';

export default defineComponent({
  setup() {
    return () => <div>Hello Convue!</div>;
  },
});
```

Use in the page

```js
import {defineComponent} from'vue';
// import Hello from'/src/components/hello'; no need to load registration

export default defineComponent({
  setup() {
    return () => <hello></hello>;
  },
});
```

It is recommended to use lowercase for global components, and big hump for custom components.

### Multi-level directory

If there are multiple levels of directories under /src/components, then the naming of the components will be connected in the form of folder-file.

For example, src/components/app/navbar.tsx, then you need to add the app prefix (app-navbar) to use this component, and so on for more levels.

For other rules, please [refer to component configuration items](https://ziping-li.github.io/convue/config/component).

## Plugin

By default, convue will automatically load the .js|.ts file in the /src/plugins directory.

For example, write a plugin file

```js
import Antd from'ant-design-vue';
import'ant-design-vue/dist/antd.css';

export default ({ app }, inject) => {
  app.use(Antd);

  inject('sayHello', (obj) => {
    console.log('Hello Convue!');
  });
};
```

Access the sayHello function

```js
const instance = getCurrentInstance();
const toString = instance?.appContext.config.globalProperties.$toString;
```

### Parameters

The function has two parameters, the first is the information about the component instance, and the second is the inject function (functions registered through the inject function will be automatically loaded into app.config.global.properties).

The first parameter description:

-app: current vue instance
-store: access to global status
-router: current routing object
-route: current route information
-env: list of environment variables

For other rules, please [refer to plugin configuration item](https://ziping-li.github.io/convue/config/plugin).

## Set the content of the head tag

Under normal circumstances, the head tag contains title, meta and link tags, and the srcript tag is usually written at the end of the body.

## Placeholder

-Pass <!-- TITLE --> placeholder title tag content
-The meta and link tags that need to be loaded through the <!-- HEAD --> placeholder head tag
-Via <!-- APP --> placeholder elements mounted by the vue instance and loading

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><!-- TITLE --></title>
    <!-- HEAD -->
  </head>
  <body>
    <!-- APP -->
    <script type="module">
      // ...
    </script>
  </body>
</html>
```

### Global Settings

Passed in in the convue configuration item in vite.config.js is the global configuration

```js
import {defineConfig} from'vite';
import convue from'convue';

export default defineConfig({
  plugins: [
    ...convue({
      head: {
        title:'Convue',
        meta: [
          {name:'language', content:'en-US' },
          {name:'author', content:'ziping' },
        ],
        link: [
          {
            rel:'dns-prefetch',
            href:'https://www.googletagmanager.com',
            crossorigin:'crossorigin',
          },
          {
            rel:'dns-prefetch',
            href:'https://www.google-analytics.com/analytics.js',
            crossorigin:'crossorigin',
          },
        ],
      },
    }),
  ],
});
```

If the title is not passed, the name field in packgae.json will be taken by default.

### Page individually set

We can also set the head separately for a page, and finally the head of the page will contain the content of the global settings plus the individual settings of the page.

The meta object is also used in the route tag.

```js
{
  /* <route>
  name:'test'
  meta:
    head:
      title: Convue
      meta:
        -
          name: language
          content: en-US
        -
          name: author
          content: ziping
</route> */
}

import {defineComponent} from'vue';

export default defineComponent({
  setup() {
    return () => <div></div>;
  },
});
```

For other rules, please [refer to head configuration item](https://ziping-li.github.io/convue/config/head).

## Reference
-[vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages)
-[vite-plugin-vue-layouts](https://github.com/JohnCampionJr/vite-plugin-vue-layouts)