vue项目搭建---1.搭建基础的框架

  • 命令工具:pnpm
  • 前端框架:vue3+vite+js
  • vue库:pinia+vue-router
  • 样式库:sass
  • 格式化:eslint+prettier
  • git提交规范化:commitizen
  • 预检查工具:husky+lint-staged
  • 组件库:ant-design-vue
  • 优化库:viteCompression+viteImagemin+terser

1. pnpm下载

选择pnpm主要原因:编译速度pnpm >> yarn >> npm

1.1 安装

npm install pnpm -g

win11系统,pnpm build 的时候遇到了问题set-ExecutionPolicy RemoteSigned解决。如果早一些的版本需要管理员身份运行。

1.2 差异

## 增加包
npm install
pnpm add

## 卸载包
npm uninstall
pnpm remove

## 运行项目
npm run dev
pnpm dev

## 打包项目
npm run build
pnpm build

1.3 镜像源设置

#查看
pnpm get registry 
#临时
pnpm --registry https://registry.npmmirror.com install axios
#永久
pnpm config set registry https://registry.npmmirror.com

2. 项目

2.1 vite创建项目

## 创建项目,根据提示步骤进行
pnpm create vite

## 运行项目
pnpm dev
## 生产打包
pnpm build

2.2 项目配置

复制package.json保留想要用的安装包,pnpm install即可全部安装。

  • scripts部分根据需要保留对应指令。
  • commitizenlint-staged不用可以删除。
  • 格式化等去查看对应部分,增加相应的配置。
{
   
  "name": "vue3-collection",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
   
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "prettier:comment": "自动格式化当前目录下的所有文件",
    "prettier": "prettier --write",
    "eslint:comment": "使用 ESLint 检查并自动修复 src 目录下所有扩展名为 .js 和 .vue 的文件",
    "eslint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
    "commit:comment": "引导设置规范化的提交信息",
    "commit": "git-cz",
    "prepare": "husky install"
  },
  "config": {
   
    "commitizen": {
   
      "path": "./node_modules/cz-customizable"
    }
  },
  "lint-staged": {
   
    "*.{js,ts}": [
      "pnpm eslint",
      "pnpm prettier"
    ]
  },
  "dependencies": {
   
    "ant-design-vue": "^4.0.7",
    "pinia": "^2.1.7",
    "pinia-plugin-persistedstate": "^3.2.0",
    "vite-plugin-compression": "^0.5.1",
    "vue": "^3.3.8",
    "vue-router": "^4.2.5"
  },
  "devDependencies": {
   
    "@commitlint/cli": "^18.4.3",
    "@commitlint/config-conventional": "^18.4.3",
    "@vitejs/plugin-vue": "^4.5.0",
    "commitizen": "^4.3.0",
    "commitlint-config-cz": "^0.13.3",
    "cz-conventional-changelog": "^3.3.0",
    "cz-customizable": "^7.0.0",
    "eslint": "^8.55.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-prettier": "^5.0.1",
    "eslint-plugin-vue": "^9.19.2",
    "husky": "^8.0.3",
    "lint-staged": "^15.2.0",
    "mockjs": "^1.1.0",
    "prettier": "^3.1.0",
    "sass": "^1.69.5",
    "terser": "^5.26.0",
    "vite": "^5.0.7",
    "vite-plugin-imagemin": "^0.6.1",
    "vite-plugin-mock": "^2.9.6"
  }
}

2.3 pinia+vue-router

pinia官网:pinia.web3doc.top/

pnpm i pinia --save
pnpm i vue-router --save
#函数包
pnpm i @vueuse/core
#持久化
pnpm i pinia-plugin-persistedstate --save
main.js
import {
    createPinia } from 'pinia'
import router from './router/index'
app.use(createPinia()).use(router)
store/index.js
import {
    defineStore } from "pinia";
// useStore 可以是 useUser、useCart 之类的任何东西
// 第一个参数是应用程序中 store 的唯一 id
export const useStore = defineStore("main", {
   
  state: () => {
   
    return {
   
      count: 0,
    };
  },
  getters: {
   },
  actions: {
   
    increment() {
   
      this.count++;
    },
  },
//数据默认是存在localStorage
  persist: {
   
    enabled: true,	//开启
    storage:sessionStorage,	//修改存储位置
    key:'userInfo',	//设置存储的key
    paths: ['id'],//指定要长久化的字段
  },
});
router/index.js
import {
    createRouter, createWebHistory } from 'vue-router';
const routes = [
  {
   
    path: '/login',
    name: 'Login',
    meta: {
   
        title: '登录',
        keepAlive: true,
        requireAuth: false
    },
    component: () => import('@/pages/login.vue')
  },
  {
   
      path: '/',
      redirect: '/login'
  }
]

const router = createRouter({
   
  history: createWebHistory(),
  routes
});
export default router;
vue文件里使用示例

这时候store不同于vuex,他是一个ref变量,在标签里直接使用{ {store.xx}}

<template>
	<div>{
  {store.count}}</div>
	<button @click="go('/login')">go link</button>
</template>
<script setup>
import {
      useStore } from '@/store/index'
import {
      storeToRefs } from 'pinia';
import {
     useRouter} from 'vue-router'
const store = useStore();
const router = useRouter();
//不能解构而是用storeToRefs保持响应性
const {
     count} = storeToRefs(store)
//更新
store.increment();

function go(path){
     
	router.push({
     path:path})
}
</script>

2.4 eslint + prettier

## 安装
pnpm i eslint eslint-plugin-vue --save-dev
pnpm i prettier eslint-config-prettier eslint-plugin-prettier --save-dev
.eslintrc.js
  • semi设置0,关闭了对逗号检查
  • comma-dangle设置0,关闭了分号检查
  • space-before-function-paren设置0,关闭了方法名括号前空格检查
  • vue/html-self-closinghtml-void改为always规避img等单标签报错
  • 删除了parser: 'babel-eslint'解决了缺包提示
  • 最外层增加parser: 'vue-eslint-parser',编译es6等不出错
// 'off' or 0 - 关闭这个规则校验
// 'warn' or 1 - 开启这个规则校验,但只是提醒,不会退出
// 'error' or 2 - 开启这个规则校验,并退出

module.exports = {
   
  root: true,
  parserOptions: {
   
    ecmaVersion: 7,
    ecmaFeatures: {
   
      jsx: true,
      modules: true,
    },
  },
  parser: 'vue-eslint-parser',
  env: {
   
    es6: true,
    node: true,
  },

  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-recommended',
    'prettier',
    'plugin:prettier/recommended',
  ],

  plugins: ['vue'],

  globals: {
   
    document: false,
    navigator: false,
    window: false,
  },

  rules: {
   
    'accessor-pairs': 2,
    'arrow-spacing': [
      2,
      {
   
        before: true,
        after: true,
      },
    ],
    'block-spacing': [2, 'always'],
    'comma-dangle': [0, 'never'],
    'comma-spacing': [
      2,
      {
   
        before: false,
        after: true,
      },
    ],
    'comma-style': [2, 'last'],
    'constructor-super': 2,
    curly: [2, 'multi-line'],
    'dot-location': [2, 'property'],
    'eol-last': 2,
    eqeqeq: [2, 'allow-null'],
    'generator-star-spacing': [
      2,
      {
   
        before: true,
        after: true,
      },
    ],
    'handle-callback-err': [2, '^(err|error)$'],
    indent: [
      2,
      2,
      {
   
        SwitchCase: 1,
      },
    ],
    'jsx-quotes': [2, 'prefer-single'],
    'key-spacing': [
      2,
      {
   
        beforeColon: false,
        afterColon: true,
      },
    ],
    'keyword-spacing': [
      2,
      {
   
        before: true,
        after: true,
      },
    ],
    'new-cap': [
      2,
      {
   
        newIsCap: true,
        capIsNew: false,
      },
    ],
    'new-parens': 2,
    'no-array-constructor': 2,
    'no-console': 0,
    'no-caller': 2,
    'no-class-assign': 2,
    'no-cond-assign': 2,
    'no-const-assign': 2,
    'no-control-regex': 2,
    'no-delete-var': 2,
    'no-dupe-args': 2,
    'no-dupe-class-members': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-empty-character-class': 2,
    'no-empty-pattern': 2,
    'no-eval': 2,
    'no-ex-assign': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-boolean-cast': 2,
    'no-extra-parens': [2, 'functions'],
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-func-assign': 2,
    'no-implied-eval': 2,
    'no-inner-declarations': [2, 'functions'],
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-iterator': 2,
    'no-label-var': 2,
    'no-labels': [
      2,
      {
   
        allowLoop: false,
        allowSwitch: false,
      },
    ],
    'no-lone-blocks': 2,
    'no-mixed-spaces-and-tabs': 2,
    'no-multi-spaces': 2,
    'no-multi-str': 2,
    'no-multiple-empty-lines': [
      2,
      {
   
        max: 1,
      },
    ],
    'no-native-reassign': 2,
    'no-negated-in-lhs': 2,
    'no-new-object': 2,
    'no-new-require': 2,
    'no-new-symbol': 2,
    'no-new-wrappers': 2,
    'no-obj-calls': 2,
    'no-octal': 2,
    'no-octal-escape': 2,
    'no-path-concat': 2,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-regex-spaces': 2,
    'no-return-assign': [2, 'except-parens'],
    'no-self-assign': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow-restricted-names': 2,
    'no-spaced-func': 2,
    'no-sparse-arrays': 2,
    'no-this-before-super': 2,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-undef': 0,
    'no-undef-init': 2,
    'no-unexpected-multiline': 2,
    'no-unmodified-loop-condition': 2,
    'no-unneeded-ternary': [
      2,
      {
   
        defaultAssignment: false,
      },
    ],
    'no-unreachable': 2,
    'no-unsafe-finally': 2,
    'no-unused-vars': [
      2,
      {
   
        vars: 'all',
        args: 'none',
      },
    ],
    'no-useless-call': 2,
    'no-useless-computed-key': 2,
    'no-useless-constructor': 2,
    'no-useless-escape': 0,
    'no-whitespace-before-property': 2,
    'no-with': 2,
    'one-var': [
      2,
      {
   
        initialized: 'never',
      },
    ],
    'operator-linebreak': [
      2,
      'after',
      {
   
        overrides: {
   
          '?': 'before',
          ':': 'before',
        },
      },
    ],
    'padded-blocks': [2, 'never'],
    quotes: [
      2,
      'single',
      {
   
        avoidEscape: true,
        allowTemplateLiterals: true,
      },
    ],
    semi: [0, 'never'],
    'semi-spacing': [
      2,
      {
   
        before: false,
        after: true,
      },
    ],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [0, 'always'],
    'space-in-parens': [2, 'never'],
    'space-infix-ops': 2,
    'space-unary-ops': [
      2,
      {
   
        words: true,
        nonwords: false,
      },
    ],
    'spaced-comment': [
      2,
      'always',
      {
   
        markers: [
          'global',
          'globals',
          'eslint',
          'eslint-disable',
          '*package',
          '!',
          ',',
        ],
      },
    ],
    'template-curly-spacing': [2, 'never'],
    'use-isnan': 2,
    'valid-typeof': 2,
    'wrap-iife': [2, 'any'],
    'yield-star-spacing': [2, 'both'],
    yoda: [2, 'never'],
    'prefer-const': 2,
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
    'object-curly-spacing': [
      2,
      'always',
      {
   
        objectsInObjects: false,
      },
    ],
    'array-bracket-spacing': [2, 'never'],
    'vue/jsx-uses-vars': 2,
    'vue/max-attributes-per-line': [
      'error',
      {
   
        singleline: 3,
        multiline: {
   
          max: 13,
        },
      },
    ],
    'vue/html-self-closing': [
      'error',
      {
   
        html: {
   
          void: 'always',
          normal: 'any',
          component: 'always',
        },
        svg: 'always',
        math: 'always',
      },
    ],
  },
};
.eslintignore

如果遇到报错可对应增加!xx解决配置类文件被检测报错的问题。

node_modules/
dist/
index.html
!.eslintrc.js
!.prettierrc.js
!.gitignore
.prettierrc
{
   
    "printWidth": 80,
    "tabWidth": 2,
    "useTabs": false,
    "semi": true,
    "singleQuote": true,
    "quoteProps": "as-needed",
    "jsxSingleQuote": false,
    "trailingComma": "all",
    "bracketSpacing": true,
    "jsxBracketSameLine": false,
    "arrowParens": "always",
    "rangeStart": 0,
    "rangeEnd": 9999999,
    "requirePragma": false,
    "insertPragma": false,
    "proseWrap": "preserve",
    "htmlWhitespaceSensitivity": "css",
    "endOfLine": "auto"
}

2.5 样式

#scss
pnpm add -D sass

全局变量

css: {
   
    preprocessorOptions: {
   
      scss: {
   
        /** 如果引入多个文件,可以使用
         * '@import "@/assets/scss/globalVariable1.scss";
         * @import"@/assets/scss/globalVariable2.scss";'
         **/
        additionalData: '@import "@/style/var.scss";',
      },
    },
  },

2.6 commitizen

#安装 commitizen (交互式提交 + 自定义提示文案 + Commit规范)
pnpm install -D commitizen cz-conventional-changelog @commitlint/config-conventional @commitlint/cli commitlint-config-cz cz-customizable
package.json

package.json scripts添加,使用pnpm commit命令替代git commit

scripts:{
   
	"commit:comment": "引导设置规范化的提交信息",
	"commit": "git-cz",
}
"config": {
   
      "commitizen": {
   
        "path": "./node_modules/cz-customizable"
      }
  },
.cz-config.js

types为自定义内容,valuecommitlint.config.js对应

module.exports = {
   
    types: [{
   
        value: 'feature',
        name: '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:    打包'
      }
    ],
    // 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
  };
  
commitlint.config.js
module.exports = {
   
  extends: ['@commitlint/config-conventional', 'cz'],
  rules: {
   
    'type-enum': [
      2,
      'always',
      [
        'feature', // 新功能(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> 不能为空
    'type-empty': [2, 'never'],
    // <scope> 范围不能为空
    'scope-empty': [2, '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],
  },
};

2.7 husky+lint-staged

根据步骤完成前 5 步的设置,git提交代码commit时就会先执行husky进行检查。检查通过方可提交。

# 1.安装
pnpm i husky lint-staged -D
# 2.生成 .husky 的文件夹
npx husky install
# 3.添加 hooks,会在 .husky 目录下生成一个 pre-commit 脚本文件
npx husky add .husky/pre-commit "npx --no-install lint-staged"
# 4.添加 commit-msg
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
# 5.package.json中添加以下配置

"lint-staged": {
   
    "*.{js,ts}": [
        "pnpm eslint",
        "pnpm prettier"
    ]
  }
# 6.提交代码 `git commit -m "message"` 就会看到 hook 生效了。  

项目优化

vite官网中mock相关配置:vite-plugin-mock

vite.config.js

import {
    defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// import { visualizer } from 'rollup-plugin-visualizer';
import viteCompression from 'vite-plugin-compression';
import viteImagemin from 'vite-plugin-imagemin';
import {
    viteMockServe } from 'vite-plugin-mock';
import path from 'path';

// https://vitejs.dev/config/
export default defineConfig({
   
  base: './',//解决刷新404问题
  resolve: {
   
    alias: {
    '@': path.resolve('src') },//根路径
    extensions: ['.mjs', '.mts', '.js', '.tsx', '.jsx', '.json'],//可省略后缀的文件
  },
  plugins: [
    vue(),
    //mock数据:injectCode在生产环境启用的话需要。
    viteMockServe({
   
      mockPath:'./mock',
      localEnabled: true,
      prodEnabled: false,
      // injectCode: `import {mockServer} from 'mockServer';mockServer()`,
    }),
    //代码压缩
    viteCompression({
    threshold: 10240, algorithm: 'gzip' }),
    //图片压缩
    viteImagemin({
   
      gifsicle: {
   
        optimizationLevel: 7,
        interlaced: false,
      },
      optipng: {
   
        optimizationLevel: 7,
      },
      mozjpeg: {
   
        quality: 20,
      },
      pngquant: {
   
        quality: [0.8, 0.9],
        speed: 4,
      },
      svgo: {
   
        plugins: [
          {
   
            name: 'removeViewBox',
          },
          {
   
            name: 'removeEmptyAttrs',
            active: false,
          },
        ],
      },
    }),
  ],
  // 全局变量设置
  css: {
   
    preprocessorOptions: {
   
      scss: {
   
        /** 如果引入多个文件,可以使用
         * '@import "@/assets/scss/globalVariable1.scss";
         * @import"@/assets/scss/globalVariable2.scss";'
         **/
        additionalData: '@import "@/style/var.scss";',
      },
    },
  },
  build: {
   
    minify: 'terser',
    terserOptions: {
   
      compress: {
   
        drop_console: true,
        drop_debugger: true,
      },
    },
    rollupOptions: {
   
      output: {
   
        manualChunks(id) {
   
          if (id.includes('node_modules')) {
   
            // 让每个插件都打包成独立的文件
            return id
              .toString()
              .split('node_modules/')[1]
              .split('/')[0]
              .toString();
          }
        },
      },
    },
  },
  server: {
   
    host: '0.0.0.0', // ip
    port: 5001, // 端口号
    open: false, // 是否自动在浏览器打开
    https: false, // 是否开启 https
    // 跨域代理配置
    proxy: {
   
      '/api': {
   
        target: 'http:192.168.1.100:9094',
        changeOrigin: false,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },
});

mockServer.js

import {
    createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';

//mock在最外层根目录下
import routes from '../mock/index';

export function mockServer() {
   
  createProdMockServer([...routes]);
}

相关推荐

  1. vue项目---1.基础框架

    2023-12-11 08:12:05       28 阅读
  2. vue如何项目

    2023-12-11 08:12:05       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-11 08:12:05       14 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-11 08:12:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-11 08:12:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-11 08:12:05       18 阅读

热门阅读

  1. 【面试常考150题】1、88合并两个有序数组

    2023-12-11 08:12:05       33 阅读
  2. MyBatis动态SQL(Dynamic SQL)

    2023-12-11 08:12:05       31 阅读
  3. python tkinter 使用(三)

    2023-12-11 08:12:05       36 阅读