【学习 vite + vue3 + pinia + ts】框架搭建

一、创建项目

Vite 需要 Node.js 版本 >= 12.0.0

npm init vite@latest 

yarn create vite

// 或者

# npm 6.x

npm init vite@latest 项目名称 --template vue

# npm 7+, 需要额外的双横线:

npm init vite@latest 项目名称 -- --template vue

# yarn

yarn create vite 项目名称 --template vue

以上命令,根据自己需要取一行即可。我用的是yarn create vite,没有yarn的可以安装一下

npm install -g yarn 

【学习 vite + vue3 + pinia + ts】框架搭建_第1张图片

二、给路径配别名

 yarn add @types/node -D  //或者  npm i --save-dev @types/node

修改vue.config.ts

import { resolve } from 'path'
export default defineConfig({
  //...
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src')
    }
  },
  base: `./`, // 设置打包路径
})

修改tsconfig.ts

{
  "compilerOptions": {
    //...
    "baseUrl": "./",
    "paths": {
      "@/*":["./src/*"],
    }
  },
  //...
}

若未生效,关闭vscode重新打开下就好了。 

三、添加Ant Design Vue UI库

yarn add ant-design-vue   //或者    npm i --save ant-design-vue

修改main.ts(下方为全局引入模式)

import { createApp } from 'vue'
import App from './App.vue'

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

const app = createApp(App)
app.use(Antd).mount('#app')

文档地址: https://www.antdv.com/docs/vue/getting-started-cn

四、集成 less 预编译

 yarn add less -D       //     npm install --save-dev less 

 修改vue.config.ts

export default defineConfig({
  //...
  css: {
    preprocessorOptions: {
      less: {
        modifyVars: {
          'primary-color': `#333`, //此处可以写一些自定义变量
        },
        javascriptEnable: true
      }
    }
  }
})

五、定义全局组件 

在src/components下新建index.ts

import * as Icons from '@ant-design/icons-vue'

// import ComponentVue from './ComponentVue.vue' // 替换为想要全局注册的组件

const icons: any = Icons
export default function install(app: any) {

  // app.component('ComponentVue', ComponentVue) //注册
  
  for(const i in icons){
    app.component(i, icons[i])
  }
}

 修改main.ts

import components from '@/components/index'

app
.use(Antd)
.use(components)
.mount('#app')

六、添加router路由

yarn add vue-router -D     //或者     npm install vue-router -D

在src下新建router文件夹,在其中建index.ts 

import { createRouter, createWebHashHistory, createWebHistory, RouteRecordRaw } from 'vue-router'

const routes:Array = [
  {
    path: '/',
    name: 'Home',
    component: () => import(`@components/Home.vue`),
    meta: {title: '首页'},
  },
  ...
]
const router = createRouter({
  history: createWebHashHistory(),//路径带# http://127.0.0.1:5173/#/
  // history: createWebHistory(),//路径带# http://127.0.0.1:5173/
  routes
})

export default router

 修改main.ts

import router from '@/router/index'

app
.use(router)
.use(Antd)
.use(components)
.mount('#app')

七、添加状态管理机pinia

yarn add pinia  // 或者 npm install --save pinia 

 修改main.ts

import { createPinia } from 'pinia'

app
.use(router)
.use(createPinia())
.use(Antd)
.use(components)
.mount('#app')

store的创建及应用,请参考:

【学习vite + vue3 + pinia + ts】pinia应用 store的创建及应用_糖糖246的博客-CSDN博客

官方文档:

定义一个 Store | Pinia 中文文档

八、配置运行环境相关设置 

根目录下新建 .env.development 文件(开发环境)

 VITE_BASE_URL = http://127.0.0.1:8080

 根目录下新建 .env.production文件(生产环境)

  VITE_BASE_URL = http://127.0.0.1:9090

修改 package.json

  "scripts": {

    "dev": "vite --mode development",    //development为之前新建的开发环境env后边名字

    ... 

  },

在组件中使用环境变量

console.log(import.meta.env)

【学习 vite + vue3 + pinia + ts】框架搭建_第2张图片

在vue.config.ts中使用环境变量 

import { defineConfig, loadEnv } from 'vite'

export default ({mode}:any) => {
  const obj = loadEnv(mode, process.cwd())
  console.log(obj)
  return defineConfig({ ..... })
}

九、集成axios,配置请求拦截

yarn add axios    //或者 npm install --save axios

封装axios  src>>server>>axios.ts

import axios from 'axios'
import { notification } from "ant-design-vue"

interface CustomObj {
  [propName: string]: any
}

interface ErrorObj {
  code: string,
  message: string,
}

//开发环境下解决不能添加cookie
if (import.meta.env.MODE === `development`) {
  axios.defaults.headers.post[`User`] = `admin`  //与后台配合识别登录用户
  axios.defaults.headers.post[`Uid`] = `12345678913` //与后台配合识别登录用户
}
axios.defaults.headers.post[`Content-Type`] = `application/json;charset=UTF-8`

const $http = axios.create({ baseURL: import.meta.env.VITE_BASE_URL })

//定义错误情况的处理函数
const errorHandler = (error: ErrorObj) => {
  notification.warning({
    message: error.code,
    description: error.message
  })
}

// 请求拦截器
$http.interceptors.request.use(
  config => {
    return config
  },
  error => {return Promise.reject(error)}
)

//定义记录请求取消的相关函数与变量
let source: CustomObj = {}
let requestList:string[] = []
function cancelRequest(path:string, allCancel?:boolean):void {
  if (requestList.includes(path) && typeof source[path]==='function'){
    source[path]('终止请求')
  } 
  else if(allCancel) {
    requestList.forEach(el =>{
      source[el]('批量终止请求')
    })
  }
}

// 响应拦截器
$http.interceptors.response.use(
  (res: any) => {
    //从记录的可能取消的路径数据中剔除已经完成请求的路径
    const path = JSON.stringify(res.config.url)
    requestList = requestList.filter(item => !path.includes(item))
    //根据返回的code,对响应结果进行处理
    if (String(res.data.code) === '000000') {
      return Promise.resolve(res.data.data)
    }
    else {
      return errorHandler(res.data as ErrorObj)
    }
  },
  error => {
    return Promise.reject(error)
  },
)

//封装请求
const get = (url:string = ``, config?:CustomObj):Promise => {
  return $http.get(url, config)
}

const post = (url = ``, params:CustomObj = {}, config?:CustomObj):Promise => {
  //取消未结束的上次同路径请求
  if(requestList.length) {
    cancelRequest(url)
  }
  //根据配置情况,决定是否将该路径加到取消请求路径数组中
  if(config?.isCancelRequest){
    requestList.push(url)
  }
  return $http.post(url, params, {
    ...config,
    cancelToken: new axios.CancelToken(c=>{
      source[url] = c
    }) //取消请求设置
  }).catch(error => {
    throw new Error(error)
  })
}
const ssoPost = (url = ``, params:CustomObj = {}, config?:CustomObj):Promise => {
  const formatDate = new FormData()
  for (const key in params) {
    formatDate.append(key, params[key])
  }
  return $http.post(url, formatDate, {
    ...config, 
    baseURL: import.meta.env.VITE_BASE_URL_SSO,
  }).catch(error => {
    throw new Error(error)
  })
}

export {
  get, post, ssoPost,
}

封装请求函数  src>>server>>login.ts

import { post, ssoPost } from '@/servers/axios'

enum Url {
  login = '/login', //登录接口
  list = '/list', //列表接口
}
//params:any  config?:any 根据自己需求写类型接口
export function login(params:any,config?:any){
  return ssoPost(Url.login, params, config)
}
export function getList(params:any,config?:any){
  return post(Url.list, params, {...config, isCancelRequest: true})
}

 组件中发送请求

十、添加代码检查工具

1.安装依赖

yarn add -D eslint eslint-plugin-import eslint-plugin-vue

yarn add -D prettier eslint-plugin-prettier eslint-config-prettier

yarn add -D stylelint stylelint-config-standard stylelint-order

yarn add -D @typescript-eslint/parser postcss-html

2.根目录新建 .eslintrc.cjs文件 (如果运行有错,将文件名改为 .eslintrc.js)

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [`plugin:vue/vue3-recommended`, `prettier`],
  parserOptions: {
    ecmaVersion: 12,
    parser: `@typescript-eslint/parser`,
    sourceType: `module`,
  },
  plugins: [`vue`, `@typescript-eslint`, `prettier`],
  rules: {
    'block-scoped-var': `error`, // 强制变量在其作用域内使用
    'class-methods-use-this': [
      // 强制class使用this
      `error`,
      { exceptMethods: [] },
    ],
    'no-debugger': `warn`,
    'no-console': `warn`,
    'no-undef': `off`,
    'no-unused-vars': `off`,
    'no-alert': `error`, // 禁用alert confirm
    'no-eval': `error`, // 金庸eval
    'no-multi-spaces': `error`, // 禁止使用多个空格
    'no-multi-str': `error`, // 禁止使用多行字符串
    'no-new-func': `error`, // 禁止使用new Function
    'no-new-wrappers': `error`, // 禁止对 String,Number 和 Boolean 使用 new 操作符
    'no-return-assign': `error`, // 禁止在return 中使用赋值语句
    'no-return-await': `error`, // 禁止不必要的return await
    radix: `error`, // 强制paserInt使用基数参数

    // 以下是代码风格
    semi: [`error`, `never`], //没有分号
    'no-extra-parens': [`error`, `all`], // 禁止任何地方出现不必要的圆括号
    'array-bracket-spacing': [`error`, `never`], // 中括号内禁用多余的空格或空行
    'array-bracket-newline': [`error`, `consistent`], // 中括号使用一致的换行规则
    'array-element-newline': [`error`, `consistent`], // 数组间使用一致的换行规则
    'block-spacing': `error`, // 左花括号前后必须有一个空格
    camelcase: [`error`, { properties: `never` }], // 强制使用驼峰命名法
    'comma-dangle': [`error`, `always-multiline`], // 当最后一个元素或属性与闭括号 ] 或 } 在 不同的行时,要求使用拖尾逗号;当在 同一行时,禁止使用拖尾逗号
    'comma-spacing': [`error`, { before: false, after: true }], // 逗号前禁止使用空格,逗号后使用必须一个或多个空额
    'comma-style': [`error`, `last`], //要求逗号放在数组元素、对象属性或变量声明之后,且在同一行
    'computed-property-spacing': [`error`, `never`], // 禁止在计算属性中括号中使用空格
    'eol-last': [`error`, `always`], // 禁止文件末尾存在空行
    'func-call-spacing': [`error`, `never`], // 禁止在函数调用的函数名和开括号之间有空格
    'function-paren-newline': [`error`, `multiline`], // 如果函数的任一参数有换行,则要求在函数括号内换行。否则禁止换行
    'implicit-arrow-linebreak': [`error`, `beside`], // 禁止在箭头函数体之前出现换行
    indent: [`error`, 2], // 强制使用两个空格换行
    // 'id-length': ["error", { min: 3, max: 30 }], // 强制标识符的最大为20和最小长度为3
    'jsx-quotes': [`error`, `prefer-single`], // 强制所有不包含单引号的 JSX 属性值使用单引号
    'key-spacing': [`error`, { beforeColon: false, afterColon: true }], // 要求对象字面量冒号前无空格,冒号与值之间有一个空格
    'keyword-spacing': [`error`, { before: true, after: true }], // 强制关键字前后必须有一个空格
    'new-cap': `error`, //要求构造函数首字母大写
    'newline-per-chained-call': [`error`, { ignoreChainWithDepth: 2 }], // 要求链式调用多于两层时换行
    'no-array-constructor': `error`, // 禁止使用Array构造函数
    'no-new-object': `error`, // 禁用Object构造函数
    'no-lonely-if': `error`, // 禁止 if 语句作为唯一语句出现在 else 语句块中
    'no-multi-assign': `error`, // 禁止连续赋值
    'no-multiple-empty-lines': [`error`, { max: 2 }], // 强制连续换行不得大于两行
    'no-trailing-spaces': [`error`, { skipBlankLines: false, ignoreComments: false }], // 禁止在空行和块注释中使用空白符
    'no-unneeded-ternary': `error`, // 禁止在有更简单的表达式时使用三元操作符
    'no-whitespace-before-property': `error`, // 禁止点操作符前后有空白
    'nonblock-statement-body-position': [`error`, `beside`], // 禁止单行语句前有换行
    'object-curly-newline': [`error`, { multiline: true }], // 要求花括号间有一致的换行操作
    'object-curly-spacing': [`error`, `always`], // 要求花括号内有空格
    quotes: [`error`, `backtick`], // 要求尽可能地使用反引号
    'semi-spacing': [`error`, { before: false, after: true }], // 强制分号之后有空格,禁止分号之前有空格
    'space-before-blocks': `error`, // 要求块之前有空格
    'space-before-function-paren': `off`, // 要求function函数左圆括号之前有一个空格
    'space-in-parens': [`error`, `never`], // 强制圆括号内不能有空格
    'space-unary-ops': `error`, // 强制 words 一元操作符后空格和 nonwords 一元操作符之前或之后的空格的一致性

    // es6
    'arrow-spacing': `error`, // 强制箭头函数箭头左右有一个空格
    'no-var': `error`, // 禁用var
    'prefer-template': `error`, // 禁用字符串连接,使用模板字符串
    'prefer-const': `error`, // 不变量使用const
    'no-console': `off`,

    'generator-star-spacing': `warn`,
    'no-global-assign': [`error`],
    'vue/no-unused-components': `off`,
    'vue/multi-word-component-names': `off`,
    'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
    indent: `off`,
    'vue/script-indent': [
      `error`,
      2,
      {
        baseIndent: 1,
        switchCase: 0,
      },
    ],
    'vue/html-self-closing': [
      `error`,
      {
        html: {
          void: `always`,
          normal: `always`,
          component: `always`,
        },
        svg: `always`,
        math: `always`,
      },
    ],
  },
}

 3. 根目录新建 .eslintignore 文件

# Created by .ignore support plugin (hsz.mobi)
### Example user template template
### Example user template
codebase
# IntelliJ project files
.idea
.vscode
.git
*.iml
out
gen
dist
*.d.ts

4.根目录新建 .prettierignore 文件

# Created by .ignore support plugin (hsz.mobi)
### Example user template template
### Example user template
codebase
# IntelliJ project files
.idea
.vscode
.git
*.iml
out
gen
dist
*.d.ts
node_modules
public
src/assets

5. 根目录新建 .prettierrc 文件

{
  "tabWidth": 2,
  "jsxSingleQuote": true,
  "bracketSameLine": false,
  "printWidth": 100,
  "singleQuote": true,
  "semi": false,
  "overrides": [
    {
      "files": "*.json",
      "options": {
        "printWidth": 200
      }
    }
  ],
  "arrowParens": "avoid",
  "htmlWhitespaceSensitivity": "ignore",
  "vueIndentScriptAndStyle": true
}

6.根目录新建 .stylelintrc.json 文件

{
  "extends": ["stylelint-config-standard"],
  "overrides": [
    {
      "files": ["*.vue", "**/*.vue"],
      "customSyntax": "postcss-html"
    },
    {
      "files": ["*.html", "**/*.html"],
      "customSyntax": "postcss-html"
    }
  ],
  "plugins": ["stylelint-order"],
  "rules": {
    "order/order": ["declarations", "custom-properties", "dollar-variables", "rules", "at-rules"],
    "order/properties-order": [
      "position",
      "z-index",
      "top",
      "bottom",
      "left",
      "right",
      "float",
      "clear",
      "columns",
      "columns-width",
      "columns-count",
      "column-rule",
      "column-rule-width",
      "column-rule-style",
      "column-rule-color",
      "column-fill",
      "column-span",
      "column-gap",
      "display",
      "grid",
      "grid-template-rows",
      "grid-template-columns",
      "grid-template-areas",
      "grid-auto-rows",
      "grid-auto-columns",
      "grid-auto-flow",
      "grid-column-gap",
      "grid-row-gap",
      "grid-template",
      "grid-template-rows",
      "grid-template-columns",
      "grid-template-areas",
      "grid-gap",
      "grid-row-gap",
      "grid-column-gap",
      "grid-area",
      "grid-row-start",
      "grid-row-end",
      "grid-column-start",
      "grid-column-end",
      "grid-column",
      "grid-column-start",
      "grid-column-end",
      "grid-row",
      "grid-row-start",
      "grid-row-end",
      "flex",
      "flex-grow",
      "flex-shrink",
      "flex-basis",
      "flex-flow",
      "flex-direction",
      "flex-wrap",
      "justify-content",
      "align-content",
      "align-items",
      "align-self",
      "order",
      "table-layout",
      "empty-cells",
      "caption-side",
      "border-collapse",
      "border-spacing",
      "list-style",
      "list-style-type",
      "list-style-position",
      "list-style-image",
      "ruby-align",
      "ruby-merge",
      "ruby-position",
      "box-sizing",
      "width",
      "min-width",
      "max-width",
      "height",
      "min-height",
      "max-height",
      "padding",
      "padding-top",
      "padding-right",
      "padding-bottom",
      "padding-left",
      "margin",
      "margin-top",
      "margin-right",
      "margin-bottom",
      "margin-left",
      "border",
      "border-width",
      "border-top-width",
      "border-right-width",
      "border-bottom-width",
      "border-left-width",
      "border-style",
      "border-top-style",
      "border-right-style",
      "border-bottom-style",
      "border-left-style",
      "border-color",
      "border-top-color",
      "border-right-color",
      "border-bottom-color",
      "border-left-color",
      "border-image",
      "border-image-source",
      "border-image-slice",
      "border-image-width",
      "border-image-outset",
      "border-image-repeat",
      "border-top",
      "border-top-width",
      "border-top-style",
      "border-top-color",
      "border-top",
      "border-right-width",
      "border-right-style",
      "border-right-color",
      "border-bottom",
      "border-bottom-width",
      "border-bottom-style",
      "border-bottom-color",
      "border-left",
      "border-left-width",
      "border-left-style",
      "border-left-color",
      "border-radius",
      "border-top-right-radius",
      "border-bottom-right-radius",
      "border-bottom-left-radius",
      "border-top-left-radius",
      "outline",
      "outline-width",
      "outline-color",
      "outline-style",
      "outline-offset",
      "overflow",
      "overflow-x",
      "overflow-y",
      "resize",
      "visibility",
      "font",
      "font-style",
      "font-variant",
      "font-weight",
      "font-stretch",
      "font-size",
      "font-family",
      "font-synthesis",
      "font-size-adjust",
      "font-kerning",
      "line-height",
      "text-align",
      "text-align-last",
      "vertical-align",
      "text-overflow",
      "text-justify",
      "text-transform",
      "text-indent",
      "text-emphasis",
      "text-emphasis-style",
      "text-emphasis-color",
      "text-emphasis-position",
      "text-decoration",
      "text-decoration-color",
      "text-decoration-style",
      "text-decoration-line",
      "text-underline-position",
      "text-shadow",
      "white-space",
      "overflow-wrap",
      "word-wrap",
      "word-break",
      "line-break",
      "hyphens",
      "letter-spacing",
      "word-spacing",
      "quotes",
      "tab-size",
      "orphans",
      "writing-mode",
      "text-combine-upright",
      "unicode-bidi",
      "text-orientation",
      "direction",
      "text-rendering",
      "font-feature-settings",
      "font-language-override",
      "image-rendering",
      "image-orientation",
      "image-resolution",
      "shape-image-threshold",
      "shape-outside",
      "shape-margin",
      "color",
      "background",
      "background-image",
      "background-position",
      "background-size",
      "background-repeat",
      "background-origin",
      "background-clip",
      "background-attachment",
      "background-color",
      "background-blend-mode",
      "isolation",
      "clip-path",
      "mask",
      "mask-image",
      "mask-mode",
      "mask-position",
      "mask-size",
      "mask-repeat",
      "mask-origin",
      "mask-clip",
      "mask-composite",
      "mask-type",
      "filter",
      "box-shadow",
      "opacity",
      "transform-style",
      "transform",
      "transform-box",
      "transform-origin",
      "perspective",
      "perspective-origin",
      "backface-visibility",
      "transition",
      "transition-property",
      "transition-duration",
      "transition-timing-function",
      "transition-delay",
      "animation",
      "animation-name",
      "animation-duration",
      "animation-timing-function",
      "animation-delay",
      "animation-iteration-count",
      "animation-direction",
      "animation-fill-mode",
      "animation-play-state",
      "scroll-behavior",
      "scroll-snap-type",
      "scroll-snap-destination",
      "scroll-snap-coordinate",
      "cursor",
      "touch-action",
      "caret-color",
      "ime-mode",
      "object-fit",
      "object-position",
      "content",
      "counter-reset",
      "counter-increment",
      "will-change",
      "pointer-events",
      "all",
      "page-break-before",
      "page-break-after",
      "page-break-inside",
      "widows"
    ],
    "no-empty-source": null,
    "property-no-vendor-prefix": [true, { "ignoreProperties": ["background-clip"] }],
    "number-no-trailing-zeros": true,
    "length-zero-no-unit": true,
    "value-list-comma-space-after": "always",
    "declaration-colon-space-after": "always",
    "value-list-max-empty-lines": 0,
    "shorthand-property-no-redundant-values": true,
    "declaration-block-no-duplicate-properties": true,
    "declaration-block-no-redundant-longhand-properties": true,
    "declaration-block-semicolon-newline-after": "always",
    "block-closing-brace-newline-after": "always",
    "media-feature-colon-space-after": "always",
    "media-feature-range-operator-space-after": "always",
    "at-rule-name-space-after": "always",
    "indentation": 2,
    "no-eol-whitespace": true,
    "string-no-newline": null
  }
}

7. 修改package.json

"scripts": {
  ...
  "lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx",
  "prettier": "prettier --write .",
  "lintless": "stylelint src/**/* --fix"
},

8. 配合vscode,保存时自动修改

修改vscode的setting.json

"editor.codeActionsOnSave": {
  "source.fixAll.eslint": true
},

9.husky 和 lint-staged

根据以下命令执行

npm install husky --save-dev

npx husky install

npm set-script prepare "husky install"

npx husky add .husky/pre-commit "npm run lint"

git add .husky/pre-commit

npm install -D lint-staged

修改package.json

{
  "lint-staged": {
    "*.{ts,js,vue}": [
      "eslint --fix",
      "prettier --write ."
    ],
    "*.{css,less,vue}": [
      "stylelint --fix"
    ]
  }
}

npx husky add .husky/pre-commit "npx lint-staged"


 giteed地址:vue3_antd: vite_vue3_pinia_ts_antd_less打包构建工具是vite,技术库是vue3+ts+pinia+vue-router@4,ui库是ant design for vue,css预编译器是less,

你可能感兴趣的:(vue3,前端,vue.js,typescript,学习)