Vite-Vue3.x-TS-Eslint项目初始化(2022-05-02)

Vite-Vue3.x-TS-Eslint项目初始化

Lzq811/Vue3.x-vite-ts-eslint-prettier: vue3大屏基础框架 (github.com)

1. init

yarn create vite demo-product --template vue-ts
cd ./demo-product
yarn
yarn dev
# 能正常方法 localhost:3000 页面,说明 init 成功

2. 使用 element-plus UI 库

yarn add element-plus
  • 推荐使用 按需引入 文档地址

    # 需要安装下面两个依赖
    yarn add unplugin-vue-component unplugin-auto-import
    
  • 然后再 vite.config.ts 文件中修改

    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import AutoImport from 'unplugin-auto-import/vite'
    import Component from 'unplugin-vue-components/vite'
    import {ElementPlusResolver } from 'unplugin-vue-components/resolvers'
    
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        vue(),
        AutoImport({
          resolvers: [ElementPlusResolver()]
        }),
        Component({
          resolvers: [ElementPlusResolver()]
        })
      ]
    })
    
    
  • 然后在使用的组件中直接调用

    <el-button type="primary">hello element btn</el-button>
    
  • 自定义主题 文档地址

  • 使用 Less

    # 安装依赖
    yarn add less less-loader -D
    # 然后vite.config.js, 要require('path'), 如果require 和 __dirname报ts错, 使用 @ts-ignore 或者安装 @types/node 依赖
    css: {
        preprocessorOptions: {
          less: {
            modifyVars: {
              hack: `true; @import (reference) "${path.resolve(__dirname, 'src/assets/base.less')}"` // 全局定义的less文件
            },
            javascriptEnabled: true
          }
        }
      }
      
     # 在组件内使用
     <style lang="less" scoped></style>
    

3. 使用 Eslint 、 Prettier 做代码校验和自动格式化

  • 安装 eslint 以及相关依赖

    yarn add eslint eslint-plugin-vue vue-eslint-parser @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-airbnb-base eslint-plugin-import -D
    
  • vscode 编辑器 也要安装 Eslint 和 Prettier 拓展

  • 根目录添加 .eslintrc.js文件,并添加下面代码

    module.exports = {
      root: true,
      globals: {
          defineEmits: 'readonly',
          defineProps: 'readonly'
      },
      extends: [
          'plugin:@typescript-eslint/recommended',
          'plugin:vue/vue3-recommended',
          'airbnb-base'
      ],
      parserOptions: {
          parser: '@typescript-eslint/parser',
          ecmaVersion: 2020
      },
      rules: {
          'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 禁用 debugger
          'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 禁用 console
          'no-bitwise': 'off', // 禁用按位运算符
          'no-tabs': 'off', // 禁用 tab
          'array-element-newline': ['error', 'consistent'], // 强制数组元素间出现换行
          indent: [
              'error',
              2,
              { MemberExpression: 0, SwitchCase: 1, ignoredNodes: ['TemplateLiteral'] }
          ], // 强制使用一致的缩进
          quotes: ['error', 'single'], // 强制使用一致的反勾号、双引号或单引号
          'comma-dangle': ['error', 'always-multiline'], // 要求或禁止末尾逗号
          'object-curly-spacing': ['error', 'always'], // 强制在大括号中使用一致的空格
          'max-len': ['error', 120], // 强制一行的最大长度
          'no-new': 'off', // 禁止使用 new 以避免产生副作用
          'linebreak-style': 'off', // 强制使用一致的换行风格
          'import/extensions': 'off', // 确保在导入路径中统一使用文件扩展名
          'eol-last': 'off', // 要求或禁止文件末尾存在空行
          'no-shadow': 'off', // 禁止变量声明与外层作用域的变量同名
          'no-unused-vars': 'warn', // 禁止出现未使用过的变量
          'import/no-cycle': 'off', // 禁止一个模块导入一个有依赖路径的模块回到自己身上
          'arrow-parens': 'off', // 要求箭头函数的参数使用圆括号
          semi: ['error', 'never'], // 要求或禁止使用分号代替 ASI
          eqeqeq: 'on', // 要求使用 === 和 !==
          'no-param-reassign': 'off', // 禁止对 function 的参数进行重新赋值
          'import/prefer-default-export': 'off', // 如果模块只输入一个名字,则倾向于默认输出
          'no-use-before-define': 'on', // 禁止在变量定义之前使用它们,则倾向于默认输出
          'no-continue': 'off', // 禁用 continue 语句
          'prefer-destructuring': 'off', // 优先使用数组和对象解构
          'no-plusplus': 'off', // 禁用一元操作符 ++ 和 --
          'prefer-const': 'warn', // 要求使用 const 声明那些声明后不再被修改的变量
          'global-require': 'on', // 要求 require() 出现在顶层模块作用域中
          'no-prototype-builtins': 'off', // 禁止直接调用 Object.prototypes 的内置属性
          'consistent-return': 'off', // 要求 return 语句要么总是指定返回的值,要么不指定
          'one-var-declaration-per-line': 'off', // 要求或禁止在变量声明周围换行
          'one-var': 'off', // 强制函数中的变量要么一起声明要么分开声明
          'import/named': 'off', // 确保命名导入与远程文件中的命名导出相对应
          'object-curly-newline': 'off', // 强制大括号内换行符的一致性
          'default-case': 'off', // 要求 switch 语句中有 default 分支
          'no-trailing-spaces': 'on', // 禁用行尾空格
          'func-names': 'off', // 要求或禁止使用命名的 function 表达式
          radix: 'off', // 强制在 parseInt() 使用基数参数
          'no-unused-expressions': 'off', // 禁止出现未使用过的表达式
          'no-underscore-dangle': 'off', // 禁止标识符中有悬空下划线
          'no-nested-ternary': 'off', // 禁用嵌套的三元表达式
          'no-restricted-syntax': 'off', // 禁用特定的语法
          'no-await-in-loop': 'off', // 禁止在循环中出现 await
          'import/no-extraneous-dependencies': 'off', // 禁止使用外部包
          'import/no-unresolved': 'off', // 确保导入指向一个可以解析的文件/模块
          'template-curly-spacing': ['error', 'always'], // 要求或禁止模板字符串中的嵌入表达式周围空格的使用
          '@typescript-eslint/no-var-requires': 'off', // 除import语句外,禁止使用require语句
          '@typescript-eslint/no-empty-function': 'off', // 不允许空函数
          '@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
          'guard-for-in': 'off', // 要求 for-in 循环中有一个 if 语句
          'class-methods-use-this': 'off', // 强制类方法使用 this
          'vue/html-indent': ['error', 2], // 在<template>中强制一致缩进
          'vue/html-self-closing': 'off', // 执行自闭合的风格
          'vue/max-attributes-per-line': [
              // 强制每行属性的最大数量
              'warn',
              {
                  singleline: {
                      max: 3,
                      allowFirstLine: true
                  },
                  multiline: {
                      max: 1,
                      allowFirstLine: false
                  }
              }
          ],
          'vue/singleline-html-element-content-newline': 'off' // 要求单行元素的内容前后有一个换行符
      }
    }
    
    
  • 根目录添加 prettier.config.js文件,并添加下面代码

    // prettier.config.js or .prettierrc.js
    module.exports = {
      // 一行最多 100 字符
      printWidth: 100,
      // 使用 2 个空格缩进
      tabWidth: 2,
      // 不使用缩进符,而使用空格
      useTabs: false,
      // 行尾需要有分号
      semi: false,
      // 使用单引号
      singleQuote: true,
      // 对象的 key 仅在必要时用引号
      quoteProps: 'as-needed',
      // jsx 不使用单引号,而使用双引号
      jsxSingleQuote: false,
      // 末尾不需要逗号
      trailingComma: 'none',
      // 大括号内的首尾需要空格
      bracketSpacing: true,
      // jsx 标签的反尖括号需要换行
      jsxBracketSameLine: false,
      // 箭头函数,只有一个参数的时候,也需要括号
      arrowParens: 'always',
      // 每个文件格式化的范围是文件的全部内容
      rangeStart: 0,
      rangeEnd: Infinity,
      // 不需要写文件开头的 @prettier
      requirePragma: false,
      // 不需要自动在文件开头插入 @prettier
      insertPragma: false,
      // 使用默认的折行标准
      proseWrap: 'preserve',
      // 根据显示样式决定 html 要不要折行
      htmlWhitespaceSensitivity: 'css',
      // 换行符使用 lf
      endOfLine: 'lf'
    }
    
    
  • 根目录 添加 .eslintignore 并写入下面代码

    /build/
    /config/
    /dist/
    /*.js
    /*.zip
    /*.rar
    
    

4. 配置环境变量

  • 在根目录新建文件夹 env-config,然后在文件夹下新增文件并录入内容

    所有的环境变量必须以 VITE_ 开头

    # 新增 .env 文件 写入下面内容
      VITE_ENV = DEV
      VITE_APP_BASE_URL = 'http://DEV.com'
      
    # 新增 .env.test 文件并写入内容
      VITE_ENV = TEST
      VITE_APP_BASE_URL = 'http://TEST.com'
      
    # 新增 .env.prod 文件并写入内容
      VITE_ENV = PROD
      VITE_APP_BASE_URL = 'http://PROD.com'
    
  • vite.config.js文件夹中配置 envDir

    // @ts-ignore 如果提示错误 使用 @ts-ignore 或者 yarn add @types/node 只有node里面才有 require 和 __dirname
    const path = require('path')
    function _resolve(dir) {
      // @ts-ignore
      return path.resolve(__dirname, dir)
    }
    
    // https://vitejs.dev/config/
    export default defineConfig({
      envDir: _resolve('env-config'),
      ...
    })
    
  • 修改 package.json 的命令(我比较喜欢用 start 代替 dev)

      "scripts": {
        "dev": "vite",
        "start": "vite --mode env",
        "start-test": "vite --mode test",
        "start-prod": "vite --mode prod",
        "build": "vue-tsc --noEmit && vite build",
        "build-test": "vue-tsc --noEmit && vite build --mode test",
        "build-prod": "vue-tsc --noEmit && vite build --mode prod",
        "preview": "vite preview"
      },
    
  • 使用 import.meta.env 来获取环境变量信息

    # 组件里面调用测试
    onMounted(() => {console.log(import.meta.env)})
    
  • 执行start命令

    yarn start # 执行 dev 环境
    # OR
    yarn start-test # 执行 test 环境
    # OR
    yarn start-prod # 执行 orod 环境
    
    #demo
    yarn start-test
    # 调取 import.meta.env 的结果
      BASE_URL: "/"
      DEV: true
      MODE: "test"
      PROD: false
      VITE_APP_BASE_URL: "http://TEST.com"
      VITE_ENV: "TEST"
    
  • 执行build命令

    #demo
    yarn build-test
    # 调取 import.meta.env 的结果
      BASE_URL: "/"
      DEV: true
      MODE: "test"
      PROD: false
      VITE_APP_BASE_URL: "http://TEST.com"
      VITE_ENV: "TEST"
    

5. 使用 axios 封装 ajax 请求

  • 安装 axios

    yarn add axios
    
  • 配置封装

    1. 新建文件src/api/index.tsx
    // index.tsx 文件
    import ajax from './ajax'
    /*
     * ajax 从 ajax.js 引入 需要
     * 第一个 参数 是 url 必填
     * 第二个 参数 是 params对象 默认 {} 非必填
     * 第三个 参数 是 GET、POST 请求方式, 默认 POST, 非必填
     */
    // 后台地址
    const BASE_URL: string = import.meta.env.VITE_APP_BASE_URL || '' // 环境变量后台地址
    // const BASE_URL: string = `http:xxxx/api`
    // const BASE_URL_OTHER:string = `http:xxx2.api` // 多个后台地址
    
    interface IParams {}
    
    // 登陆接口
    export const ReqLogin = (params: IParams) => ajax(`${BASE_URL}login`, params, 'POST') // POST 是默认值,可以不写
    
    // export const ReqOther = (params:IParams) => ajax(`${BASE_URL_OTHER}login`, params)
    
    
  1. 新建文件 src/api/ajax.tsx

    // ajax.tsx 文件
    import axios from 'axios'
    
    // 可以在这里做一下请求拦截,设置公共请求头等
    
    export default function ajax(url: string, data: any = {}, type: string = 'POST') {
      // 判断 url 地址, 在多个后台地址时候使用, 可以在这里拦截使用不同的请求头,传入不同token等操作
    
      return new Promise((resolve, reject) => {
        let promise: any // 返回一个 promise 对象
        if (type === 'GET') {
          promise = axios.get(url, { params: data })
    
          // 多个后台地址时候,传入不同的token值
          promise = axios.get(url, {
            params: data,
            headers: { access_totken: sessionStorage.getItem('token_other') || '' }
          })
        } else if (type === 'POST') {
          promise = axios.post(url, data)
    
          // 多个后台地址时候,传入不同的token值
          promise = axios.get(url, data)
        }
        // 统一处理 response
        promise
          .then((response: any) => {
            response && response.data ? resolve(response.data) : reject(response)
          })
          .catch((error: any) => {
            console.log(error)
          })
      })
    }
    
    
  2. 组件内调用

 ```tsx
 import {ReqLogin} from './src/api'
 
 const ajaxDemo = async () => {
    const res:any = await ReqLogin({}) // res就是ajax返回的结果
 }
 ```

6. 页面弹性布局

  • 新建 src/utils/comfort_page.tsx

    /*
     * 做页面自适应
     * 0. 项目跟元素 id = root
     * 1. width 为适应基准
     * 2. 无论 width height 如何变化,都要输出 16 / 9 比例的页面
     * 3. 客户的设备分辨率 1920 * 1080
     */
    
    // @ts-nocheck
    
    const W = 1920
    const H = 1080
    
    export default () => {
      const root = document.getElementById('root')?.style
      window.onresize = () => {
        root.transform = `scale(${document.body.offsetWidth / W})`
        root.transformOrigin = `left top 0px`
        root.width = `${W}px`
        root.height = `${H}px`
      }
      root.transform = `scale(${document.body.offsetWidth / W})`
      root.transformOrigin = `left top 0px`
      root.width = `${W}px`
      root.height = `${H}px`
    }
    
    
  • App.vue 组件引入

    import ComfortPage from './utils/comfort_page'
    onMounted(() => {
      ComfortPage()
    })
    

7. 路由

  • 路由要用 4 版本的,才对应 vue3版本

    yarn add vue-router@4
    
  • 新增 src/routes/index.tsx

    import { createRouter, createWebHistory } from 'vue-router'
    
    const routes: any = [
      {
        path: '/',
        redirect: 'home'
      },
      {
        path: '/home',
        name: 'home',
        component: () => import('../Home.vue') // 要提前注册号该组件
      },
      {
        path: '/first',
        name: 'first',
        component: () => import('../First.vue') // 要提前注册号该组件
      }
    ]
    
    const router = createRouter({
      history: createWebHistory(),
      routes: routes
    })
    
    router.beforeEach((to, from) => {
      const { path: toPath } = to
      const { path: fromPath } = from
      if (toPath === fromPath) {
        return false
      }
    })
    
    export default router
    
    
  • 修改 main.ts 文件

    import router from './routes'
    
    const app = createApp(App)
    app.use(router)
    app.mount('#app')
    
  • 修改 App.vue 文件

    <template>
        <router-view></router-view>
    </template>
    
  • 跳转

    import {useRouter} from 'vue-router'
    
    const router = useRouter()
    
    router.push('./first')
    

8. 路径别名

  • 修改 vite.config.js 内容

    export default defineConfig({
      envDir: path.resolve(__dirname, 'env-config'),
      plugins: [
        vue(),
        AutoImport({
          resolvers: [ElementPlusResolver()]
        }),
        Component({
          resolvers: [ElementPlusResolver()]
        })
      ],
      css: {
        preprocessorOptions: {
          less: {
            modifyVars: {
              hack: `true; @import (reference) "${path.resolve(__dirname, 'src/assets/base.less')}"` // 全局定义的less文件
            },
            javascriptEnabled: true
          }
        }
      },
      resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
          '@api': _resolve('src/api'),
          '@components': _resolve('src/components'),
          '@': _resolve('src')
        }
      }
    })
    
  • 新增 src/ts.extends.json 文件

    {
      "compilerOptions": {
        "baseUrl": ".",
        "paths": {
          "@/*": [
            "*"
          ],
          "@api/*": [
            "src/api/*"
          ]
        }
      }
    }
    
  • 修改 tsconfig.json 文件

    {
      ...
      "extends": "./ts.extends.json" # 添加该属性
    }
    
  • 使用

    import { ReqLogin } from '@api/index'
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容