一个 Vue3 的类 Umi 框架(基于 Vite)

4,489 阅读6分钟

快速上手

  1. 安装 convue.
yarn add convue -D
  1. 在项目中使用

在 vite.config.js 中使用

import convue from 'convue';

export default defineConfig({
  plugins: [
    ...convue({}),
  ],
});
  1. 在 main.js 中引入需要的包,如果不需要的则可以不引入(保证安装了vue-router, vuex, vue-i18n等这些你需要的依赖)
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; // 为了让 middleware 能获取到组件实例,如果不需要使用可以移除该行

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

使用脚手架(推荐)

convue 提供了一套初始化项目的脚手架工具,目前支持 sfc (vue 单文件) 形式和 tsx 方式两种开发模式。

# step 1
yarn global add convue-cli
# step 2
mkdir my-app && cd my-app
# step 3 - 在命令行中输入 convue, 后续会提示操作
convue
# step 4 - 开始
npm run dev

项目目录

convue 采用约定式的目录形式,因此我们需要遵守这一套开发方式.往往在项目中要做到统一规范,提高效率,这是一种有效的措施。

如果需要改变目录结构,可以参考配置项

以 tsx 形式为例:

.
├── 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

路由

convue 默认会自动加载 /src/pages 目录下的 .vue|.js|.jsx|.ts|.tsx 文件,并且生成对应文件名称的路由。

比如 /src/pages 目录下的 index.tsx 文件对应的路由地址就是 /, user.tsx 文件对应的路由地址就是 /user。

动态路由

动态路由的命名规则为 _param[.vue|.js|.jsx|.ts|.tsx]

添加路由信息

在 /src/pages/index.tsx 文件中添加一个 route 标签。

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

import { defineComponent } from 'vue';

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

如果是 sfc (vue 单文件) 则不需要注释。

route 标签支持的语法有 'json5' | 'json' | 'yaml',默认为 yaml,如果是 json 语法则指定 route 的 lang 即可。

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

import { defineComponent } from 'vue';

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

路由404重定向

当网站的地址不在路由注册表中,如果 /src/pages 下存在 404 页面,则重定向到 /404 地址,否则就重定向到 / 地址。

Layout

Layout指的是网站的一个公共区域的容器组件。

convue 默认会加载 /src/layouts 目录下的 .vue|.js|.jsx|.ts|.tsx 文件,并且在路由表中引入,默认加载的是 default[.vue|.js|.jsx|.ts|.tsx]。

文件中必须包含 router-view 组件。

import { defineComponent } from 'vue';

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

改变当前页面的 layout

在文件中添加一个 route 标签,并且在 meta 中指定 layout,layout 的值对应 /src/layouts 下的文件名。

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

import { defineComponent } from 'vue';

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

其他规则请参考 layout 配置项

中间件

convue 默认会加载 /src/middleware 目录下的 .ts | .js 文件,并且在路由全局的前置钩子中执行。

比如编写一个 auth 的中间件

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

参数

提供的这些参数方便开发:

  • query: 当前路由的 query 参数
  • params: 当前路由的 params 参数
  • route: 当前路由的信息
  • redirect: 重定向函数,接受一个 url 作为参数
  • store: 全局状态访问
  • app: 当前 vue 实例
  • env: 环境变量列表

其他规则请参考 page 配置项

loading

  • Type: string
  • Default: undefined

页面的 vue 实例创建完成前的 loading 颜色设置

使用指南请参考head

progress

  • Type: boolean | Progress
  • Default: true

路由切换时的进度条设置

如果设置为 false, 则不会显示,同时也不会引入该段代码。

Progress 的类型

export interface Progress {
  color?: string;
  size?: string;
}

传入对象的话,可以设置进度条的颜色和尺寸。

全局 store

convue 默认会加载 /src/store 目录下的 .js|.ts 文件,并且在 vuex 中自动配置。

文件的内容结构与 vuex 统一,如下

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

说明

/src/store 下的 index[.js|.ts] 会直接加载 vuex 项,其他文件会以模块的形式配置。

比如存在 index.js 和 user.js 两个文件。

index.js

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

user.js

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

那么 vuex store 实际上是这种结构

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

其他规则请参考 store 配置项

全局组件

convue 默认会注册 /src/components 目录下的 .vue|.js|.jsx|.ts|.tsx 文件为全局组件。

比如在 /src/components 下有一个 Hello.tsx 组件

import { defineComponent } from 'vue';

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

在页面中使用

import { defineComponent } from 'vue';
// import Hello from '/src/components/hello'; 不需要加载注册

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

全局组件推荐用 lowercase 的写法,自定义组件用 大驼峰 的写法。

多级目录

如果 /src/components 下还存在多级目录,那么组件的的命名会以 folder-file 的形式连接。

比如 src/components/app/navbar.tsx, 那么使用该组件的话需要加上 app 的前缀(app-navbar),更多层级以此类推。

其他规则请参考 component 配置项

插件

convue 默认会自动加载 /src/plugins 目录下的 .js|.ts 文件。

比如编写一个的 plugin 文件

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!');
  });
};

访问 sayHello 函数

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

参数

函数有两个参数,第一个为组件实例相关的信息,第二个为 inject 函数(通过 inject 函数注册的函数会自动加载进 app.config.global.properties 中)。

第一个参数说明:

  • app: 当前 vue 实例
  • store: 全局状态访问
  • router: 当前路由对象
  • route: 当前路由的信息
  • env: 环境变量列表

其他规则请参考 plugin 配置项

设置 head 标签的内容

通常情况下,head 标签内包含了 title、meta 和 link 标签, srcript 标签我们通常写在 body 的最后面。

占位符

  • 通过 占位 title 标签的内容
  • 通过 占位 head 标签需要加载的 meta 和 link 标签
  • 通过 占位 vue 实例挂载的元素以及 loading
<!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>

全局设置

在 vite.config.js 中的 convue 配置项中传入的为全局配置

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',
          },
        ],
      },
    }),
  ],
});

如果 title 不传的话,默认会取 packgae.json 中的 name 字段。

页面单独设置

我们也可以为某一个页面单独设置 head,最终该页面的 head 会包含全局设置的加上页面单独设置的内容。

同样是在 route 标签中使用 meta 对象。

{
  /* <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>;
  },
});

其他规则请参考 head 配置项

参考