搭建自己的vue3脚手架

124 阅读6分钟

前言

我们每次搭建一个新的项目时,总要去配置那些常用的工具模块。重复去配置就会觉得非常麻烦,当然会有很多开源的脚手架,比如vue的脚手架vue-cli等,脚手架为我们完成项目初始化的基础配置和一些必要的模块引入及配置,我们在此基础上再做项目和依赖的定制就省了很多事,懒人必备。

脚手架配置的内容

  • vite的基础配置
  • i18n多语言方案
  • 多主题方案
  • axios请求封装
  • router路由封装配置
  • vue全局组件封装注册
  • vuex配置
  • 静态资源配置
  • 多环境发布配置

以上配置都做到开箱即用,让我们直接投入到生产中去,再不要担心配置问题版本问题了。 通过vite创建我们的脚手架项目

yarn create vite

在命令行选择vue项目框架 选择vue-ts

vite.config.ts构建工具配置

这里做一些打包构建的配置,脚手架这里就做一些项目常用的基础配置就好了,以后用的时候再根据需求二次定制

引入scss预编译样式 设置静态资源 设置代理 多语言依赖导入 常用便于开发的配置

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueI18n from "@intlify/vite-plugin-vue-i18n";
import path from "path";
// vite 首次加载慢且报错---添加缓存插件
import OptimizationPersist from "vite-plugin-optimize-persist";
import PkgConfig from "vite-plugin-package-config";
import vueJsx from "@vitejs/plugin-vue-jsx";

const { resolve } = require("path");

// https://vitejs.dev/config/
export default defineConfig({
  // 引入自定义主题
  css: {
    preprocessorOptions: {
      // 导入scss预编译程序
      scss: {
        additionalData: `@use "@/styles/modules/element/themeCommon.scss" as *;`,
      },
    },
  },
  plugins: [
    PkgConfig(),
    OptimizationPersist(),
    vue(),
    vueJsx(),
    vueI18n({
      include: path.resolve(__dirname, "./src/utils/locales/**"),
    }),
  ],
  // 设置为打包资源相对路径,默认绝对
  base: "./", //
  // 静态资源服务的文件夹, 默认"public"
  publicDir: "public",
  // 配置别名
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "src"),
    },
  },
  // 打包构建环境
  build: {
    outDir: "dist",
    rollupOptions: {
      input: {
        main: resolve(__dirname, "index.html"),
      },
    },
  },
  server: {
    port: 8080,
    proxy: {
      // 配置代理 gapi
      "/gapi": {
        target: "https://lcplatdev.zkcserv.com",
        changeOrigin: true,
        rewrite: path => path.replace(/^\/gapi/, ""),
      },
    },
  },
});

tsconfig.json TypeScript规则配置

因为我们项目中使用的ts,所以需要这个配置文件,TypeScript 编译器可以根据它的规则来对代码进行编译。 关于详细配置查看官网文档 这里我们就做一些一般需要用到的基础的配置

{
  "compilerOptions": {
    "target": "esnext", //目标语言的版本
    "module": "esnext", //指定生成代码的模板标准
    "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
    "strict": true, // 开启所有严格的类型检查
    "jsx": "preserve",
    "sourceMap": true, // 生成目标文件的sourceMap文件
    "resolveJsonModule": true,
    "esModuleInterop": true, // 允许export=导出,由import from 导入
    "lib": ["esnext", "dom"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
    "types": ["vite/client", "element-plus/global"], // 加载的声明文件包
    "noImplicitAny": false, // 不允许隐式的any类型
    "allowSyntheticDefaultImports": true,
    "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
    "allowJs": true,
    "paths": {
      // 路径映射,相对于baseUrl
      "@/*": ["src/*"],
      "public/*": ["public/*"]
    }
  },
  "exclude": [
    //指定编译器需要排除的文件或文件夹。
    "node_modules"
  ],
  "include": [
    //指定编译需要编译的文件或目录。"src/**/*.vue",
    "src/**/*.ts",
    "src/**/*.d.ts",
    "./*.d.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "src/styles/common.sass",
    "global.d.ts",
    "mock/MockServer.ts"
  ]
}

多环境配置

vite自带有多环境打包适配的方法 我们根目录新建三个文件.env.development.env.production.env.test,分别作为开发、生产、测试三个环境。 这里定义环境变量值,不同环境的配置文件设置不同的值,这里也是在我们使用脚手架的时候才去设置值的。也可以新建多个新的环境配置文件

BASE_URL = '/gapi'
CDN_UPLOAD_URL = 'https://********'
CDN_CLIENT_URL='https:://********''

环境变量的使用方法很简单,原理则是通过当前不同的运行环境去不同的环境文件里取对应的值

String(import.mate.env.XXX)

当然这里还有个配置,在package.json中定义命令传参指定运行环境,通过--mode传递需要运行/打包的环境,值就是上面新建的环境文件名

"scripts": {
    "dev": "vite --mode development",
    "dev:production": "vite --mode production ",
    "dev:test": "vue-tsc --noEmit && vite --mode test ",
    "build": "vue-tsc --noEmit && vite build --mode development",
    "build:production": "vue-tsc --noEmit && vite build --mode production",
    "build:test": "vite build --mode test",
    "tsc": "vue-tsc --noEmit --skipLibCheck",
  },

支持多语言

安装vue-i18n,yarn add vue-i18n@next.配置全局i18n多语言支持 新建src/utils/locales/index.ts

import { createI18n } from "vue-i18n";
import zh_CN from './zh_CN.json'
import en_US from './en_US.json'
let i18nLange = 'zh_CN'
const i18n = createI18n({
    legacy: false,
    globalInjection: true, //注入全局
    locale: i18nLange,
    fallbackLocale: ["zh_CN"], //找不到key就用中文
    fallbackRoot:false,
    messages: {
        zh_CN,
        en_US
    }
});

export default i18n;

新建src/utils/locales/zh_CN.json,配置中文翻译

{
  "hello": "你好",
}

新建src/utils/locales/en_US.json,配置中文翻译

{
  "hello": "hello",
}

另外你可以新建其他任何语言文件,需要在index.ts中导入并传到messages参数中即可。 在main.ts中注入该多语言配置,挂载到Vue应用对象,就能全局使用多语言开发了,模板调用方式$t('hello')或vue3的this.$t('hello'),this代表上下文对象

// 引入多语言
import i18n from "@/utils/locales/index";
app.use(i18n);

静态资源管理

为便于管理,使静态资源与项目分离,我们做一个资源路径的映射管理 新建/public/img/index.js

import homeMenu from "./homeMenu";
const imgUrl = {
  common: {
    logo: "img/common/logo.png",
    photo_default_light: "img/common/photo_default_light.png",
    icon_add_px: "img/common/icon_add.png",
    icon_help: "img/common/icon_help.png",
  },
  login: {
    apple: "img/login/apple.png",
    eye_icon_z: "img/login/eye_icon_z.png",
    eye_icon_b: "img/login/eye_icon_b.png",
    facebook: "img/login/facebook.png",
    icon_success: "img/login/icon_success.png",
  },
  homeMenu:homeMenu,
};
export default imgUrl;

对资源分模块管理,可自由新建资源模块文件夹并新建映射文件index.js在/public/img/index.js导入引用,格式如代码中的homeMenu/index.js代码如下

export default {
  icon_add_link: "img/queryMen/icon_add_link@2x.png",
};

全局组件、变量

定义设置全局变量的管理,挂载到Vue应用实例中

新建/plugins/globalVal.ts

function imgManager(app) {
  // 对图片映射字典进行处理,可设置CDN前缀等
  import("../../public/img/index").then(res => {
    app.config.globalProperties.$imgUrl = res.default;
  });
}

export default function (app) {
  // 全局图片路径
  imgManager(app);
  app.config.globalProperties.$TRACK = "XXXXXXXXXXXXXXXXXXX";
  app.config.globalProperties.$TRACK_KEY = "XXXXXXXXXXXXX";
  app.config.globalProperties.$CDN_IMG = String(import.meta.env.CDN_UPLOAD_URL);
}

声明全局变量类型,必须声明了全局变量,ts中才不会报错 新建/plugins/global.d.ts

export {}; // 必须保留
// ts声明全局变量
declare module "@vue/runtime-core" {
  interface ComponentCustomProperties {
    $imgUrl: any;
    $TRACK: String;
    $CDN_IMG: string;
  }
}

注册全局组件

我们全局组件可统一放在/src/components/common/中,然后在这里统一注册到全局。就不需要在每个使用到的业务页面中导入了。 新建/plugins/common.d.ts

const componentList = import.meta.globEager('../components/common/**');
let componentArray = new Object()
Object.keys(componentList).forEach((key) => {
    let keyArray = key.split('/')
    let name = 'D' + keyArray[keyArray.length - 1].split('.')[0]
    componentArray[name] = componentList[key].default
})

export default function (app) {
    Object.keys(componentArray).forEach((key) => {
        app.component(key, componentArray[key])
    })
}

在main.ts中注入配置

//引入全局变量
import globalVal from "@/plugins/globalVal";
// 引入全局自定义组件
import common from "@/plugins/common.d";
app.use(d);
app.use(globalVal);

上述代码自动导入了/src/components/common/中的所有组件并注册到全局,添加了一个D前缀,全局组件建议大写字母开头,引用方式则为<d-xxx-aaa />或者<DXxxAaa /> 全局变量可以在页面中直接引用即可:console.log($imgUrl)

router路由管理

路由管理没有一个很标准的配置,但router又是项目中不可或缺的,这里只能做一下基础的导入配置,在使用的时候还是需要我们自己不断去根据业务去完善封装的。 新建src/router/index.ts

import { createRouter, createWebHashHistory } from "vue-router";
const moduleList = import.meta.globEager("./modules/main/**");
let routes = Object.keys(moduleList).reduce<any[]>(
  (pre, k) => [...pre, ...moduleList[k].default],
  []
);
// 去重
routes = Array.from(new Set(routes));
const routers = createRouter({
  history: createWebHashHistory(),
  routes,
});

// 全局路由守卫
routers.beforeEach((to, from, next) => {
  next();
});

export default routers;

新建文件src/router/modules/main/test.ts,这个路由文件被上述代码导入注册到了全局

export default [
  {
    name: "menuDemo",
    path: "/menuDemo",
    component: () => import("@/views/demo.vue"),
  },
];

在main.ts中注入配置

import router from "@/router";
app.use(router);

通过路由/menuDemo在浏览器即可跳转到demo.vue页面。

注意:路由配置及使用多种多样复杂的很,这里只是做了基础的使用,具体请参考vue-router官网文档

另外的axios封装、vuex等缓存方案使用也是项目中必须的,考虑到这两个插件扩展性太高,需要太多自定义配置,就不放入脚手架中了,建议参考官网文档按需配置自己项目的请求和缓存方案 axios官网 vuex官网