vue3项目---初始化

创建项目

基于webpack
vue create xxx
基于vite
npm init vue@latest

配置项目环境(TS专属)

安装插件
vue language features
typescript vue plugin

vue文件类型声明

//env.d.ts
<reference type="vite/client"/>
//declare 声明文件
declare module '*.vue'{
import {DefineComponent} from 'vue'
const component:DefineComponent
export default component
}

配置项目的icon

复制.icon文件到public文件夹

配置项目的标题

index.html中修改title

配置项目别名

//tsconfig.json
{
  "extends": "@vue/tsconfig/tsconfig.web.json",
  "include": ["src/**/*", "src/**/*.vue", "env.d.ts", "auto-imports.d.ts", "components.d.ts"],
  "exclude": ["commitlint.config.js"],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    // "types": ["element-plus/global"]
  },

  "references": [
    {
      "path": "./tsconfig.config.json"
    }
  ]
}

//jsconfig.json
//复制粘贴:更好的代码提示
{
  "compilerOptions": {
    // "target": "es5",
    "module": "esnext",
    "baseUrl": "./",
    "moduleResolution": "node",
    "jsx":"preserve",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
 // "vueCompilerOptions": {
    "experimentalDisableTemplateSupport": true
//  }
}

editorconfig配置

为不同 IDE 编辑器上处理同一项目的多个开发人员维护一致的编码风格
VSCode需要安装一个插件:EditorConfig for VS Code

//.editorconfig
# http://editorconfig.org

root = true

[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space)
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行尾的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行

[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off
trim_trailing_whitespace = false

prettier工具

npm install prettier -D
代码格式化工具
VSCode需要安装prettier-code formatter的插件
VSCod中的配置

  • settings =>format on save => 勾选上
  • settings => editor default formatter => 选择 prettier

在package.json中配置一个scripts
“prettier”: “prettier --write .”
npm run prettier

{
  "useTabs": false,//使用tab缩进还是空格缩进
  "tabWidth": 2,//tab是空格的情况下,是几个空格
  "printWidth": 80,//当行字符的长度
  "singleQuote": true,//使用单引号还是双引号
  "trailingComma": "none",//在多行输入的尾逗号是否添加,
  "semi": false//语句末尾是否要加分号
}

.prettierignore忽略文件

/dist/*
.local
.output.js
/node_modules/**

**/*.svg
**/*.sh

/public/*

ESLint检测

对代码进行检测并报错
VSCode需要安装ESLint插件

解决eslint和prettier冲突的问题

安装插件:(vue在创建项目时,如果选择prettier,那么这两个插件会自动安装)
npm install eslint-plugin-prettier eslint-config-prettier -D

//添加prettier插件
 // .eslintrc.cjs
extends: [
    "plugin:vue/vue3-essential",
    "eslint:recommended",
    "@vue/typescript/recommended",
    "@vue/prettier",
    "@vue/prettier/@typescript-eslint",
    'plugin:prettier/recommended'
  ],
  rules:{}

目录结构

├── src # 源代码目录
│ ├── assets # 主题 字体,CSS等静态资源
│ ├── components # 全局公用组件
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── service # 网络请求
│ ├── utils # 全局公用方法
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ ├── main.js # 入口文件 加载组件 初始化等
│ └── permission.js # 权限管理
│ └── settings.js # 配置文件

css样式重置

npm install normalize.css
npm install less -D

normalize.css

//main.ts

import "normalize.css"
import './assets/css/index.less'

reset.less

/* reset.css样式重置文件 */
/* margin/padding重置 */
body, h1, h2, h3, ul, ol, li, p, dl, dt, dd {
  padding: 0;
  margin: 0;
}

/* a元素重置 */
a {
  text-decoration: none;
  color: #333;
}

/* img的vertical-align重置 */
img {
  vertical-align: top;
}

/* ul, ol, li重置 */
ul, ol, li {
  list-style: none;
}

/* 对斜体元素重置 */
i, em {
  font-style: normal;
}

common.less

:root {
  // --el-button-size: 50px !important;
}
body{
font-size:14px;
}

index.css

//src/assets/css/index.less
@import './reset.less';
@import './common.less';
//main.js
import "./assets/css/index.css"
}

宽高铺满

//App.vue
.app{
height:100vh;
width:100vw;
background-color:blue;
}

配置路由

npm install vue-router
ctrl+t打开某个文件

初始化

// router/index.ts
import { createRouter, createWebHashHistory } from 'vue-router'

const router = createRouter({
  history: createWebHashHistory(),
  // 映射关系: path => component
  routes: []
})

export default router
//main.ts
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')

新建页面

//views/main/Main.vue
<template>
    <div class="main">
        <h2>
            Main
        </h2>
    </div>
</template>
<script setup lang="ts"></script>
<style lang="less" scoped>
    .main{}
</style>

新建NotFound页面

<template>
  <div class="not-found">
    <h2>您输入的路径地址不正确, 请联系管理员~</h2>
  </div>
</template>

<script setup lang="ts"></script>

<style lang="less" scoped>
.not-found {
  color: purple;
}
</style>

配置路由关系

routes:[
{
  path:'/',
  redirect:'/main'
},
{
  path:'/login',
  components:()=>import('../views/login/Login.vue')
},
{
  path:'/main',
  components:()=>import('../views/main/Main.vue')
},
 {
      path: '/:pathMatch(.*)',
      component: () => import('../views/not-found/NotFound.vue')
    }
]

测试

//App.vue
<template>
    <div class="app">
        <h2>App</h2>
        <router-link to="/main">主页</router-link>
        <router-link to="/login">登录</router-link>
        <router-view/>
    </div>
</template>

配置pinia

npm install pinia

初始化

// store/index.ts
import { createPinia } from 'pinia'
const pinia = createPinia()
export default pinia
//main.ts
import pinia from './store'
app.use(pinia)

新建store

//store/login/login.ts
import { defineStore } from 'pinia'

const useCounterStore = defineStore('counter', {
  state: () => ({
    counter: 100
  }),
  getters: {
    doubleCounter(state) {
      return state.counter * 2
    }
  },
  actions: {
    changeCounterAction(newCounter: number) {
      this.counter = newCounter
    }
  }
})

export default useCounterStore

适用store

//Main.vue
<h2>main:{{counterStore.counter}}-{{counterStore.doubleCounter}}</h2>
<button @click="changeCounter">修改counter</button>
import useCounterStore from '@/store/counter'
const counterStore = useCounterStore()

function changeCounter(){
counterStore.changeCounterAction(999)
}

配置axios(JS)

新建目录结构

├── request
│ ├── ├── index.js
│ ├── ├── config.js
├── module
│ ├── ├── city.js

config.js

//services/request/config.js

export const BASE_URL = "http://123.207.32.32:1888/api"
// export const BASE_URL = "http://codercba.com:1888/api"
export const TIMEOUT = 10000 

index.js

// services/request/index.js

import axios from 'axios'
import { BASE_URL, TIMEOUT } from './config'

class HYRequest {
  constructor(baseURL, timeout=10000) {
    this.instance = axios.create({
      baseURL,
      timeout
    })
  }

  request(config) {
    return new Promise((resolve, reject) => {
      this.instance.request(config).then(res => {
        resolve(res.data)
      }).catch(err => {
        reject(err)
      })
    })
  }

  get(config) {
    return this.request({ ...config, method: "get" })
  }

  post(config) {
    return this.request({ ...config, method: "post" })
  }
}

export default new HYRequest(BASE_URL, TIMEOUT)

city.js

services/modules/city.js
import hyRequest from '../request'

export function getCityAll() {
  return hyRequest.get({
    url: "/city/all"
  })
}

使用

import { getCityAll } from "@/services/modules/city"
// 网络请求: 请求城市的数据
 const allCity = ref({})
 getCityAll().then(res => {
   allCity.value = res.data
    console.log(res)
 })

配置axios(TS)

npm install axios

新建目录结构

├── service
│ ├── config
│ ├── ├── index.ts
│ ├── login
│ ├── main
│ ├── request
│ ├── ├── index.ts
│ ├── ├── type.ts
│ ├── index.ts

config文件

export const BASE_URL = "http://codercba.com:8000"
export const TIME_OUT = 10000

request文件

//index.ts
import axios from "axios"
import type { AxiosInstance, } from "axios"
import type { HYRequestConfig } from "./type"

/**
 * 两个难点:
 *  1.拦截器进行精细控制
 *    > 全局拦截器
 *    > 实例拦截器
 *    > 单次请求拦截器
 * 
 *  2.响应结果的类型处理(泛型)
 */

class HYRequest {
  instance: AxiosInstance

  // request实例 => axios的实例
  constructor(config: HYRequestConfig) {
    this.instance = axios.create(config)

    // 每个instance实例都添加拦截器
    this.instance.interceptors.request.use(config => {
      // loading/token
      return config
    }, err => {
      return err
    })
    this.instance.interceptors.response.use(res => {
      return res.data
    }, err => {
      return err
    })
 
    // 针对特定的hyRequest实例添加拦截器
    this.instance.interceptors.request.use(
      config.interceptors?.requestSuccessFn,
      config.interceptors?.requestFailureFn
    )
    this.instance.interceptors.response.use(
      config.interceptors?.responseSuccessFn,
      config.interceptors?.responseFailureFn
    )
  }

  // 封装网络请求的方法
  // T => IHomeData axios->AxiosResponse->IHomeData
  request<T = any>(config: HYRequestConfig<T>) {
    // 单次请求的成功拦截处理
    if (config.interceptors?.requestSuccessFn) {
      config = config.interceptors.requestSuccessFn(config)
    }

    // 返回Promise
    return new Promise<T>((resolve, reject) => {
      this.instance.request<any, T>(config).then(res => {
        // 单词响应的成功拦截处理
        if (config.interceptors?.responseSuccessFn) {
          res = config.interceptors.responseSuccessFn(res)
        }
      //resolve(res as any as T)
        resolve(res)
      }).catch(err => {
        reject(err)
      })
    })
  }

  get<T = any>(config: HYRequestConfig<T>) {
    return this.request({ ...config, method: "GET" })
  }
  post<T = any>(config: HYRequestConfig<T>) {
    return this.request({ ...config, method: "POST" })
  }
  delete<T = any>(config: HYRequestConfig<T>) {
    return this.request({ ...config, method: "DELETE" })
  }
  patch<T = any>(config: HYRequestConfig<T>) {
    return this.request({ ...config, method: "PATCH" })
  }
}


export default HYRequest

//type.ts
import type { AxiosRequestConfig, AxiosResponse } from "axios"

// 针对AxiosRequestConfig配置进行扩展
export interface HYInterceptors<T = AxiosResponse> {
  requestSuccessFn?: (config: AxiosRequestConfig) => AxiosRequestConfig 
  requestFailureFn?: (err: any) => any 
  responseSuccessFn?: (res: T) => T
  responseFailureFn?: (err: any) => any 
}

export interface HYRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
  interceptors?: HYInterceptors<T>
}

index.ts

import HYRequest from "./request";
import { BASE_URL, TIME_OUT } from "./config";

const hyRequest = new HYRequest({
  baseURL: BASE_URL,
  timeout: TIME_OUT
})

export default hyRequest

使用

//Login.vue
hyRequest.get({
url:'/home/multidata'
})
.then((res)=>{
console.log(res)
})

开发和生产环境

第一种

// config/index.ts
let BASE_URL = ''
if (import.meta.env.PROD) {//应用是否运行在生产环境
  // 生产环境
  BASE_URL = 'http://152.136.185.210:4000'
} else {
  // 开发环境
  BASE_URL = 'http://152.136.185.210:5000'
}
export {BASE_URL}

第二种

│ ├── .env
│ ├── .env.development
│ ├── .env.production

//.env.development
VITE_BASE_URL = "http://codercba.com/dev:1888"
VITE_TIME_OUT = 10000

引入element-plus

npm install element-plus

按需引入

npm install -D unplugin-vue-components unplugin-auto-import

vite

// vite.config.ts
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  // ...
  plugins: [
    // ...
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
})

Webpack

// webpack.config.js
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = {
  // ...
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}
//tsconfig.json
{
  "extends": "@vue/tsconfig/tsconfig.web.json",
  "include": ["src/**/*", "src/**/*.vue", "env.d.ts", "auto-imports.d.ts", "components.d.ts"],
  "exclude": ["commitlint.config.js"],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    // "types": ["element-plus/global"]
  },

  "references": [
    {
      "path": "./tsconfig.config.json"
    }
  ]
}

针对ElMessage和ElLoading等反馈组件样式引入

npm i -D vite-plugin-style-import
npm install consola -D

//vite.config.ts
import {
  createStyleImportPlugin,
  ElementPlusResolve
} from 'vite-plugin-style-import'
plugins: [
...
 createStyleImportPlugin({
      resolves: [ElementPlusResolve()],
      libs: [
        {
          libraryName: 'element-plus',
          esModule: true,
          resolveStyle: (name: string) => {
            return `element-plus/theme-chalk/${name}.css`
          }
        }
      ]
    })
]

icon 图标

npm install @element-plus/icons-vue

//global/register-icons.ts
import type { App } from 'vue'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

function registerIcons(app: App<Element>) {
  for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
  }
}

export default registerIcons
//main.ts
import icons from './global/register-icons'
app.use(icons)

引入Vant

npm i unplugin-vue-components -D

vite

//vite.config.js
   import vue from '@vitejs/plugin-vue';
+  import Components from 'unplugin-vue-components/vite';
+   import { VantResolver } from 'unplugin-vue-components/resolvers';

export default {
  plugins: [
    vue(),
+    Components({
      resolvers: [VantResolver()],
    }),
  ],
};

webpack

//vue.config.js
const { VantResolver } = require('unplugin-vue-components/resolvers');
const ComponentsPlugin = require('unplugin-vue-components/webpack');

module.exports = {
  configureWebpack: {
    plugins: [
      ComponentsPlugin({
        resolvers: [VantResolver()],
      }),
    ],
  },
};

git 提交

Husky

对git commit进行校验,符合eslint规范
npx husky-init ‘&&’ npm install(有git仓库 git init)

//  .husky/pre-commit

...

npm run lint

Commitizen

编写规范commit message的工具,不再使用git commit -m “”
使用npx cz
npm install commitizen -D
npx commitizen init cz-conventional-changelog --save-dev --save-exact

阻止commit提交

npm i @commitlint/config-conventional @commitlint/cli -D
npx husky add .husky/commit-msg “npx --no-install commitlint --edit $1”

//commitlint.config.js
module.exports={
extends:['@commitlint/config-conventional']
}

命令重置

//package.json
"script":{
"commit":'cz'
}
npm run commit
  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值