ArcoPro自用:Vue3 + Vite6 + Vue-Router4 + Vue-18n + Tailwindcss + Tsx(Jsx) + Pinia

782 阅读6分钟

创建git仓库

echo "# web_template" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/[xxx]/[xxx].git
git push -u origin main

[xxx]:请使用自己的git仓库地址

通过Arco Pro快速搭建

环境准备

npm i -g arco-cli

安装

这一步是以 Arco Design Pro 为模版创建一个新的项目,请按照以下步骤进行:

  • 进入到一个第一步git仓库的文件夹地址,新建项目
cd web_template
arco init hello-arco-pro

选择技术栈

image.png

选择项目类型

image.png

选择项目模板

image.png

安装会遇见报错情况

image.png

忽略报错,直接进入项目地址,然后重新pnpm i重新安装

cd hello-arco-pro
pnpm i

启动验证是否安装成功

pnpm run dev

image.png

image.png

默认密码和账户均为 admin

提交git仓库

使用commitizen规范提交

安装

pnpm install -D commitizen cz-conventional-changelog @commitlint/config-conventional @commitlint/cli commitlint-config-cz cz-customizable

devDependencies:

  • commitizen 4.3.1
  • commitlint-config-cz 0.13.3
  • cz-conventional-changelog 3.3.0
  • cz-customizable 7.3.0

配置 package.json

  "scripts": {
     ...
    "commit":"git-cz",
  },
  "config": { "commitizen": { "path": "node_modules/cz-customizable" } },
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },

修改配置文件commitlint.config.js

module.exports = {
  extends: ['@commitlint/config-conventional', 'cz'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat', // 新功能(feature)
        'bug', // 此项特别针对bug号,用于向测试反馈bug列表的bug修改情况
        'fix', // 修补bug
        'ui', // 更新 ui
        'docs', // 文档(documentation)
        'style', // 格式(不影响代码运行的变动)
        'perf', // 性能优化
        'release', // 发布
        'deploy', // 部署
        'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动)
        'test', // 增加测试
        'chore', // 构建过程或辅助工具的变动
        'revert', // feat(pencil): add ‘graphiteWidth’ option (撤销之前的commit)
        'merge', // 合并分支, 例如: merge(前端页面): feature-xxxx修改线程地址
        'build', // 打包
      ],
    ],
    // <type> 格式 小写
    'type-case': [2, 'always', 'lower-case'],
    // <type> 不能为空值为2,显示警告1,不校验0
    'type-empty': [1, 'never'],
    // <scope> 范围不能为空为2,显示警告1,不校验0
    'scope-empty': [1, 'never'],
    // <scope> 范围格式
    'scope-case': [0],
    // <subject> 主要 message 不能为空
    'subject-empty': [2, 'never'],
    // <subject> 以什么为结束标志,禁用
    'subject-full-stop': [0, 'never'],
    // <subject> 格式,禁用
    'subject-case': [0, 'never'],
    // <body> 以空行开头
    'body-leading-blank': [1, 'always'],
    'header-max-length': [0, 'always', 72],
  },
};

新增配置文件,并添加自定义提示配置 .cz-config.js

module.exports = {
  types: [
      {value: 'feat',     name: 'feat:     增加新功能(feature)'},
      {value: 'bug',      name: 'bug:      测试反馈bug列表中的bug号'},
      {value: 'fix',      name: 'fix:      修复bug'},
      {value: 'ui',       name: 'ui:       更新UI'},
      {value: 'docs',     name: 'docs:     文档变更'},
      {value: 'style',    name: 'style:    代码格式(不影响代码运行的变动)'},
      {value: 'perf',     name: 'perf:     性能优化'},
      {value: 'refactor', name: 'refactor: 重构(既不是增加feature,也不是修复bug)'},
{value: 'release',  name: 'release:  发布'},
{value: 'deploy',   name: 'deploy:   部署'},
      {value: 'test',     name: 'test:     增加测试'},
      {value: 'chore',    name: 'chore:    构建过程或辅助工具的变动(更改配置文件)'},
      {value: 'revert',   name: 'revert:   回退'},
    {value: 'build',    name: 'build:    打包'}
  ],
  scopes: [
    { name: 'accounts' },
  ],
  // override the messages, defaults are as follows
  messages: {
      type: '请选择提交类型:',
      customScope: '请输入您修改的范围(可选):',
      subject: '请简要描述提交 message (必填):',
      body: '请输入详细描述(可选,待优化去除,跳过即可):',
      footer: '请输入要关闭的issue(待优化去除,跳过即可):',
      confirmCommit: '确认使用以上信息提交?(y/n/e/h)'
  },
  allowCustomScopes: true,
  skipQuestions: ['body', 'footer'],
  subjectLimit: 72
};

执行commit提交

npm run commit

image.png

Vite升级v6

替换package.json

{
  "name": "arco-design-pro-vue",
  "description": "Arco Design Pro for Vue",
  "version": "1.0.0",
  "private": true,
  "author": "ArcoDesign Team",
  "license": "MIT",
  "scripts": {
    "dev": "vite --config ./config/vite.config.dev.ts",
    "build": "vue-tsc --noEmit && vite build --config ./config/vite.config.prod.ts",
    "report": "cross-env REPORT=true npm run build",
    "preview": "npm run build && vite preview --host",
    "type:check": "vue-tsc --noEmit --skipLibCheck",
    "commit": "git-cz",
    "lint-staged": "npx lint-staged",
    "prepare": "husky install"
  },
  "config": {
    "commitizen": {
      "path": "node_modules/cz-customizable"
    }
  },
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
  "lint-staged": {
    "*.{js,ts,jsx,tsx}": [
      "prettier --write",
      "eslint --fix"
    ],
    "*.vue": [
      "stylelint --fix",
      "prettier --write",
      "eslint --fix"
    ],
    "*.{less,css}": [
      "stylelint --fix",
      "prettier --write"
    ]
  },
  "dependencies": {
    "@arco-design/web-vue": "^2.56.3",
    "@vueuse/core": "^12.0.0",
    "axios": "^1.7.9",
    "dayjs": "^1.11.5",
    "echarts": "^5.4.0",
    "lodash": "^4.17.21",
    "mitt": "^3.0.0",
    "nprogress": "^0.2.0",
    "pinia": "^2.2.8",
    "query-string": "^8.0.3",
    "sortablejs": "^1.15.0",
    "vue": "^3.5.13",
    "vue-echarts": "^6.2.3",
    "vue-i18n": "^10.0.5",
    "vue-router": "^4.5.0"
  },
  "devDependencies": {
    "@arco-plugins/vite-vue": "^1.4.5",
    "@commitlint/cli": "^17.5.1",
    "@commitlint/config-conventional": "^17.4.4",
    "@types/lodash": "^4.14.186",
    "@types/mockjs": "^1.0.7",
    "@types/nprogress": "^0.2.0",
    "@types/sortablejs": "^1.15.0",
    "@typescript-eslint/eslint-plugin": "^5.40.0",
    "@typescript-eslint/parser": "^5.40.0",
    "@vitejs/plugin-vue": "^5.2.1",
    "@vitejs/plugin-vue-jsx": "^2.0.1",
    "@vue/babel-plugin-jsx": "^1.1.1",
    "commitizen": "^4.3.1",
    "commitlint-config-cz": "^0.13.3",
    "consola": "^2.15.3",
    "cross-env": "^7.0.3",
    "cz-conventional-changelog": "^3.3.0",
    "cz-customizable": "^7.3.0",
    "eslint": "^8.25.0",
    "eslint-config-airbnb-base": "^15.0.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-import-resolver-typescript": "^3.5.1",
    "eslint-plugin-import": "^2.26.0",
    "eslint-plugin-prettier": "^4.2.1",
    "eslint-plugin-vue": "^9.6.0",
    "husky": "^8.0.1",
    "less": "^4.2.1",
    "lint-staged": "^13.0.3",
    "mockjs": "^1.1.0",
    "postcss-html": "^1.5.0",
    "prettier": "^2.7.1",
    "rollup": "^4.28.1",
    "rollup-plugin-visualizer": "^5.12.0",
    "stylelint": "^14.13.0",
    "stylelint-config-prettier": "^9.0.3",
    "stylelint-config-rational-order": "^0.1.2",
    "stylelint-config-recommended-vue": "^1.4.0",
    "stylelint-config-standard": "^29.0.0",
    "stylelint-order": "^5.0.0",
    "typescript": "^4.8.4",
    "unplugin-vue-components": "^0.24.1",
    "vite": "^6.0.3",
    "vite-plugin-compression": "^0.5.1",
    "vite-plugin-eslint": "^1.8.1",
    "vite-plugin-imagemin": "^0.6.1",
    "vite-svg-loader": "^3.6.0",
    "vue-tsc": "^1.0.14"
  },
  "engines": {
    "node": ">=14.0.0"
  },
  "resolutions": {
    "bin-wrapper": "npm:bin-wrapper-china",
    "gifsicle": "5.2.0"
  }
}

删除pnpm-lock.yaml

重新执行pnpm i

 rm -rf pnpm-lock.yaml   
 pnpm i

image.png

启动查看

pnpm run dev

image.png

因为rollup已经全新升级至v4+,所以如果执行build部署需要修改vite.conf

image.png

rollup4官网 - 迁移到 Rollup 4

  • 修改/config/vite.config.prod.ts
...
    build: {
      rollupOptions: {
        makeAbsoluteExternalsRelative: true,
        preserveEntrySignatures: 'strict',
        output: {
          esModule: true,
          generatedCode: {
            reservedNamesAsProps: false,
          },
          interop: 'compat',
          systemNullSetters: false,
          manualChunks: {
            arco: ['@arco-design/web-vue'],
            chart: ['echarts', 'vue-echarts'],
            vue: ['vue', 'vue-router', 'pinia', '@vueuse/core', 'vue-i18n'],
          },
        },
      },
      chunkSizeWarningLimit: 2000,
    },
...

使用preview部署预览

npm run preview

image.png

image.png

因为模版原因,ts文件报jsx语法错误

Vue3ArcoPro是默认支持jsx语法的:

  • /config/vite.config.base.ts

image.png

注意查看Vue3官网

JSX 类型推断

与转换类似,Vue 的 JSX 也需要不同的类型定义。

从 Vue 3.4 开始,Vue 不再隐式注册全局 JSX 命名空间。要指示 TypeScript 使用 Vue 的 JSX 类型定义,请确保在你的 tsconfig.json 中包含以下内容:

{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "vue"
    // ...
  }
}

你也可以通过在文件的顶部加入 /* @jsxImportSource vue */ 注释来选择性地开启。

如果仍有代码依赖于全局存在的 JSX 命名空间,你可以在项目中通过显式导入或引用 vue/jsx 来保留 3.4 之前的全局行为,它注册了全局 JSX 命名空间。

修改tsconfig.json

{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "vue",
    "jsxFactory": "h",
    "jsxFragmentFactory": "Fragment",
    // ...
  }
}

检查报错的*.vue文件script标签

....
<script lang="tsx">
<!-- <script lang="tsx" setup> -->
 ...
</script>

再次执行preview部署预览

npm run preview

image.png

根据tsLint检测提示,打开/src/api/interceptor.ts并修改:

import axios from 'axios';
import type { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { Message, Modal } from '@arco-design/web-vue';
import { useUserStore } from '@/store';
import { getToken } from '@/utils/auth';

export interface HttpResponse<T = unknown> {
  status: number;
  msg: string;
  code: number;
  data: T;
}

if (import.meta.env.VITE_API_BASE_URL) {
  axios.defaults.baseURL = import.meta.env.VITE_API_BASE_URL;
}

axios.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // let each request carry token
    // this example using the JWT token
    // Authorization is a custom headers key
    // please modify it according to the actual situation
    const token = getToken();
    if (token) {
      if (!config.headers) {
        // @ts-ignore
        config.headers = {}; // 确保 headers 不为 undefined
      }
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    // do something
    return Promise.reject(error);
  }
);

// add response interceptors
axios.interceptors.response.use(
  (response: AxiosResponse<HttpResponse>) => {
    const res = response.data;
    // if the custom code is not 20000, it is judged as an error.
    if (res.code !== 20000) {
      Message.error({
        content: res.msg || 'Error',
        duration: 5 * 1000,
      });
      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      if (
        [50008, 50012, 50014].includes(res.code) &&
        response.config.url !== '/api/user/info'
      ) {
        Modal.error({
          title: 'Confirm logout',
          content:
            'You have been logged out, you can cancel to stay on this page, or log in again',
          okText: 'Re-Login',
          async onOk() {
            const userStore = useUserStore();

            await userStore.logout();
            window.location.reload();
          },
        });
      }
      return Promise.reject(new Error(res.msg || 'Error'));
    }
    // 返回 response 而不是 res
    return response;
  },
  (error) => {
    Message.error({
      content: error.message || 'Request Error',
      duration: 5 * 1000,
    });
    return Promise.reject(error);
  }
);

再次执行npm run preview

部署成功:

image.png

添加Tailwindcss

安装tailwindcss与相关的依赖

pnpm add -D tailwindcss postcss autoprefixer

初始化tailwindcss

npx tailwindcss init -p

修改配置tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
  darkMode: 'class',
  theme: {
    extend: {},
  },
  plugins: [],
  // corePlugins: {
  //   preflight: false
  // }
};

在全局样式文件/src/assets/style/global.less中添加配置:

@tailwind base;
@tailwind components;
@tailwind utilities;
...

修改/config/vite.config.base.ts

...
import tailwindcss from 'tailwindcss'
import autoprefixer from 'autoprefixer'

...
export default defineConfig({
  ...,
  css: {
   postcss: {
    plugins: [tailwindcss, autoprefixer()]
   }
  }
})

修改任意.vue文件验证tailwindcss样式是否生效

image.png

添加vue3-setup插件vite-plugin-vue-setup-extend

pnpm add vite-plugin-vue-setup-extend -D  

修改/config/vite.config.base.ts

...
import VueSetupExtend from 'vite-plugin-vue-setup-extend';

...
export default defineConfig({
  ...,
  plugins: [
    vue(),
    vueJsx(),
    VueSetupExtend(),
    svgLoader({ svgoConfig: {} }),
    configArcoStyleImportPlugin(),
  ],
  ...
})

最后 - 开箱即用的模版仓库地址

vue3-vite6-arcoPro