Skip to content

使用vite创建uni-app开发环境,集成eslint+stylelint+typescript。适用全端。

Notifications You must be signed in to change notification settings

CalmHarbin/uniApp-template

Repository files navigation

uniApp-template

前言

因为每次在开发新项目时,都需要一个开箱即用的基础框架,避免重新开始搭建而浪费时间,遂记录下从零开始搭建一个开箱即用的框架。随着前端的发展,未来版本的更新需要重新搭建框架时也可以作个参考。

实现功能

  • 使用 Vue3 进行开发
  • 构建工具 使用 Vite
  • 使用 Vuex
  • 集成 Typescript
  • 集成 Scss 来编写 css
  • 集成 Eslint + Stylelint + Prettier 来规范和格式化代码
  • 环境区分
  • 封装 uni-request 请求
  • 集成 Mock 辅助开发
  • 集成 uni-ui

项目整体目录

├── dist/                   // 打包文件的目录
├── env/                    // 环境配置目录
|   ├── .env.development    // 开发环境
|   ├── .env.production     // 生产环境
├── mock/                   // mock
|   ├── index.ts
├── src/
|   ├── assets/             // 存放图片
|   ├── components/         // 自定义组件
|   ├── pages/              // 页面
|   ├── store/
|   |   ├── index.ts        // store 配置文件
|   |   ├── index.d.ts      // 声明文件
|   |   └── modules
|   |       └── system.ts   // 自己的业务模块,这里写|个示例
|   ├── styles/             // 样式文件
|   ├── App.vue
|   ├── env.d.ts
|   ├── main.ts
|   ├── manifest.json
|   ├── pages.json
|   ├── shims-vue.d.ts
|   └── uni.scss
├── .eslintignore           // eslint忽略文件
├── .eslintrc.js            // eslint配置文件
├── .gitignore              // git忽略文件
├── .prettierrc             // prettier配置文件
├── .stylelintignore        // stylelint忽略文件
├── index.html
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── README.md
├── stylelint.config.js     // stylelint配置文件
├── tsconfig.json
└── vite.config.ts

生成基本框架

使用官方提供 Vue3/Vite 版本的模板来生成我们的基础项目。

npx degit dcloudio/uni-preset-vue#vite-ts uniApp-template

网络不好可以或者直接从 gitee 上下载。

做一些简单的配置

基础模板中功能比较少,我们对生成的基础框架添加一些自定义的配置。

  1. 规范目录
  2. 配置别名 @ 来表示 src 目录
  3. 配置代理解决开发环境跨域的问题
  4. 打包调整生成规范的文件

修改 vite.config.ts 文件

import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { resolve } from 'path'

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [uni()],
    resolve: {
        // 配置别名
        alias: {
            '@': resolve(__dirname, 'src')
        }
    },
    css: {
        // css预处理器
        preprocessorOptions: {
            scss: {
                // 因为uni.scss可以全局使用,这里根据自己的需求调整
                additionalData: '@import "./src/styles/global.scss";'
            }
        }
    },
    // 开发服务器配置
    server: {
        host: '0.0.0.0',
        port: 8080,
        // 请求代理
        proxy: {
            // 个人习惯,这里就用/dev作为前缀了
            '/dev': {
                target: 'https://xxx.com/api',
                changeOrigin: true,
                // 路径重写,去掉/dev
                rewrite: (path) => path.replace(/^\/dev/, '')
            }
        }
    },
    build: {
        // 禁用 gzip 压缩大小报告,以提升构建性能
        brotliSize: false,
        /** 配置h5打包js,css,img分别在不同文件夹start */
        assetsDir: 'static/img/',
        rollupOptions: {
            output: {
                chunkFileNames: 'static/js/[name]-[hash].js',
                entryFileNames: 'static/js/[name]-[hash].js',
                assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
            }
        }
        /** 配置h5打包js,css,img分别在不同文件夹end */
    }
})

tsconfig.json 中添加配置,使编辑器可以识别我们的别名。

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

最后使用 pnpm 安装依赖,然后运行到 H5 看看是否成功。

# 安装依赖
pnpm i

# 运行
npm run dev:h5

配置 vuex

因为基础模板中已经给我们依赖了 vuex,所以我们这里就不用再安装了,我们需要新建一个 src/store 文件夹来管理我们的 store

└── src/
    ├── store/
        ├── index.ts  // store 配置文件
        ├── index.d.ts  // 声明文件
        ├── modules
            ├── system.ts // 自己的业务模块,这里写一个示例

首先编写我们的声明文件,这里对我们所有的 store 添加一个声明,以便我们在使用的使用编辑器有提示。

src/store/index.d.ts

import { rootStateType } from './index'
import { systemStateType } from './modules/system'

export interface StateType extends rootStateType {
    system: systemStateType
}

然后在配置文件中来实例化 store

src/store/index.ts

import { createStore } from 'vuex'
import { StateType } from './index.d'

// 批量引入其他module,
const files = import.meta.globEager('./modules/*.ts') // vite的写法
const keys = Object.keys(files)

const modules: any = {}

keys.forEach((key) => {
    if (Object.prototype.hasOwnProperty.call(files, key)) {
        // 提取文件的名字作为模块名
        modules[key.replace(/(\.\/modules\/|\.ts)/g, '')] = files[key].default
    }
})

/** 全局的state,这个看自己的需求,如果有用到就在createStore中添加 */
export interface rootStateType {}

export default createStore<StateType>({
    modules
})

在 modules 文件夹中根据自己的业务来创建模块,同时在 index.d.ts 中加入声明。例如:src/store/modules/system.ts

import { Module } from 'vuex'
import { rootStateType } from '@/store'

export interface systemStateType {
    title: string
}

const systemModule: Module<systemStateType, rootStateType> = {
    namespaced: true,
    state: () => ({
        title: '你好,我是uni-app'
    })
}

export default systemModule

main.ts 文件中挂载 vuex

import { createSSRApp } from 'vue'
import App from './App.vue'
import store from './store'

// eslint-disable-next-line import/prefer-default-export
export function createApp() {
    const app = createSSRApp(App).use(store)
    return {
        app
    }
}

最后使用 vuex,常见的两种用法。

// 使用this
this.$store.state.system.title

// 使用useStore
import { useStore } from 'vuex'
const store = useStore()
console.log(store.state.system.title)

到这一步我们已经可以正常使用 vuex 了,但是此时会发现编辑器对 store 没有检测到 ts 类型声明。需要我们扩展下 ts 类型声明。

创建 src/shims-vue.d.ts。名字其实无所谓,只要是在 src 下的.d.ts 文件就行,这里延续 vue 风格的命名。

// import 'vue' // 必须要引入vue,否则就成了覆盖
import { StateType } from '@/store/index.d'
import { InjectionKey } from 'vue'
import { Store } from 'vuex'

/**
 * 这里为什么用vue,而不用@vue/runtime-core,是因为使用pnpm安装依赖是,node_modules中没有@vue/runtime-core,
 * 会导致找不到模块而类型声明失败。
 */
// declare module '@vue/runtime-core' {
declare module 'vue' {
    interface ComponentCustomProperties {
        // 这里扩展this.$store,还可以在这里对this添加其他的声明
        $store: Store<StateType>
    }
}

// 扩展useStore声明
declare module 'vuex' {
    export function useStore<S = StateType>(injectKey?: InjectionKey<Store<S>> | string): Store<S>
}

// 这个导出一个东西也可以,或者上面引入vue
export {}

集成 eslint

安装 eslint

pnpm add eslint -D

生成 eslint 配置文件,这一块参考是参考这篇文件写的。原文地址

npx eslint --init
  • How would you like to use ESLint? (你想如何使用 ESLint?)

    我们这里选择 To check syntax, find problems, and enforce code style(检查语法、发现问题并强制执行代码风格)

  • What type of modules does your project use?(你的项目使用哪种类型的模块?)

    我们这里选择 JavaScript modules (import/export)

  • Which framework does your project use? (你的项目使用哪种框架?)

    我们这里选择 Vue.js

  • Does your project use TypeScript?(你的项目是否使用 TypeScript?)

    我们这里选择 Yes

  • Where does your code run?(你的代码在哪里运行?)

    我们这里选择 Browser 和 Node(按空格键进行选择,选完按回车键确定)

  • How would you like to define a style for your project?(你想怎样为你的项目定义风格?)

    我们这里选择 Use a popular style guide(使用一种流行的风格指南)

  • Which style guide do you want to follow?(你想遵循哪一种风格指南?)

    我们这里选择 Airbnb(github 上 star 最高)

  • What format do you want your config file to be in?(你希望你的配置文件是什么格式?)

    我们这里选择 JavaScript

  • Would you like to install them now with npm?(你想现在就用 NPM 安装它们吗?)

    我们这里选择 No,根据提示需要安装的依赖包,我们自己使用 pnpm 安装。注意 eslint 的版本,之前我们安装过可以不再安装了。

pnpm add -D eslint-plugin-vue@latest @typescript-eslint/eslint-plugin@latest eslint-config-airbnb-base@latest eslint@^8.2.0 eslint-plugin-import@^2.25.2 @typescript-eslint/parser@latest

修改.eslintrc.js文件

// 因为我们使用的是 vue3,所以使用 vue3 的校验规则
plugin:vue/essential 修改成 plugin:vue/vue3-recommended

// 增加uni的声明
globals: {
    /** 避免uni报错 */
    uni: true,
    UniApp: true
},

增加 eslint 忽略文件 src/.eslintignore

index.html
*.d.ts

集成 stylelint

我们可以使用 stylelint 来规范我们的 css 写法。

pnpm add -D stylelint stylelint-config-rational-order stylelint-config-recommended-scss stylelint-config-recommended-vue stylelint-config-standard-scss stylelint-order

根目录下新增配置文件 stylelint.config.js

module.exports = {
    extends: [
        'stylelint-config-standard-scss',
        'stylelint-config-recommended-vue',
        'stylelint-config-recommended-vue/scss',
        'stylelint-config-rational-order'
    ],
    rules: {
        // 使用4格缩进
        indentation: 4,
        // 可以使用rpx单位
        'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }]
    }
}

增加 stylelint 的忽略文件 src/.stylelintignore

**/*.js
**/*.ts
dist
node_modules
index.html
*.md

集成 prettier

我们使用 prettier 来搭配 eslint 和 stylelint 使用。

安装依赖

pnpm add -D prettier eslint-config-prettier eslint-plugin-prettier stylelint-config-prettier

根目录下添加 prettier 的配置文件 .prettierrc

{
    "trailingComma": "none",
    "printWidth": 100,
    "tabWidth": 4,
    "semi": false,
    "singleQuote": true,
    "endOfLine": "auto"
}

修改 .eslintrc.js 来解决与 eslint 的冲突

extends: [
    // ...
    'plugin:prettier/recommended' // 一定要放在最后一项
],
rules: {
    'prettier/prettier': [
        'error',
        {
            trailingComma: 'none',
            printWidth: 100,
            tabWidth: 4,
            semi: false,
            singleQuote: true,
            endOfLine: 'auto'
        }
    ]
}

修改 stylelint.config.js 来解决与 stylelint 的冲突

extends: [
    'stylelint-config-prettier' // 一定要放在最后一项
]

完成以上步骤,就可以愉快的敲代码了。

你可能遇到的问题:

eslint 报:使用别名报错 import/no-unresolved

解决办法:

安装依赖

pnpm add eslint-import-resolver-alias -D

修改 .eslintrc.js

settings: {
    'import/resolver': {
        alias: {
            map: [['@', './src']],
            extensions: ['.js', '.jsx', '.ts', '.tsx']
        }
    }
},
rules: {
    // 解决vite+airbnb导致eslint报错import/extensions
    'import/extensions': [
        'error',
        'ignorePackages',
        {
            js: 'never',
            jsx: 'never',
            ts: 'never',
            tsx: 'never'
        }
    ]
}
stylelint 报错:Unknown word

解决办法:将报错的文件添加到忽略文件(.stylelintignore)即可

vite.config.ts 文件报错

解决办法:配置 eslint 规则

rules: {
    'import/no-extraneous-dependencies': ['error', { devDependencies: true }]
}

编译器宏,如 defineProps 和 defineEmits 生成 no-undef 警告

修改 .eslintrc.js

env: {
    'vue/setup-compiler-macros': true
}

环境区分

实现功能:

  • 可以直接区分开发环境和生产环境
  • 自定义环境变量增加 typescript 提示

在根目录下新建 env 文件夹用来存放环境变量配置文件,同时修改 vite 配置(环境变量的根目录)。

因为 vite 默认是将项目根目录作为环境变量配置的目录,所以我们需要修改下 vite 的配置指向 env 文件夹

修改 vite.config.js

export default defineConfig({
    envDir: resolve(__dirname, 'env')
})

同时新增 env 文件夹

├── env/
    ├── .env.development        // 开发环境
    ├── .env.production         // 生产环境
    ├── index.d.ts              // 声明文件

需要检查下 tsconfig.json 文件是否包含了 env/index.d.ts,如果没有需要我们添加一下。

"include": ["env/index.d.ts"]

编辑 src/.env.d.ts 文件增加自定义变量的声明

/** 扩展环境变量import.meta.env */
interface ImportMetaEnv {
    /** 这里增加自定义的声明 */
    VITE_REQUEST_BASE_URL: string
}

封装 uni-request 请求

实现功能:

  • 统一配置接口地址
  • 统一设置超时时间/报文格式/报文加密
  • 统一身份认证
  • 统一处理登录超时/接口异常提示
  • 统一返回接口格式

新建 src/utils/request/index.ts 用来存放我们的代码。

/**
 * uni-request请求封装
 * 1. 统一配置接口地址
 * 2. 统一设置超时时间/报文格式/报文加密
 * 3. 统一身份认证
 * 4. 统一处理登录超时/接口异常提示
 * 5. 统一返回接口格式
 */

type responseType = {
    code: number
    success: boolean
    msg: string
    result: any
}

const request = (config: UniApp.RequestOptions) => {
    let url: string
    if (/^(http|https):\/\/.*/.test(config.url)) {
        // 如果是以http/https开头的则不添加VITE_REQUEST_BASE_URL
        url = config.url
    } else {
        url = import.meta.env.VITE_REQUEST_BASE_URL + config.url
    }
    return new Promise<responseType>((resolve, reject) => {
        uni.request({
            ...config,
            url,
            /** 统一设置超时时间 */
            timeout: config.timeout || 60000,
            header: {
                ...config.header,
                /** 统一报文格式 */
                'Content-Type': 'application/json;charset=UTF-8'
                /** 统一身份认证 */
                // Authorization: Token
            },
            success(res) {
                // 200状态码表示成功
                if (res.statusCode === 200) {
                    resolve(res.data as any)
                    return
                }
                /**
                 * 这里可以做一些登录超时/接口异常提示等处理
                 */
                reject(res.data)
            },
            fail(result) {
                reject(result)
            }
        })
    })
}

export default {
    /**
     * get请求
     * @param url 请求地址
     * @param data 请求的参数
     * @param options 其他请求配置
     */
    get: (url: string, data?: UniApp.RequestOptions['data'], options?: UniApp.RequestOptions) => {
        return request({
            ...options,
            url,
            data,
            method: 'GET'
        })
    },
    /**
     * post请求
     * @param url 请求地址
     * @param data 请求的参数
     * @param options 其他请求配置
     */
    post: (url: string, data?: UniApp.RequestOptions['data'], options?: UniApp.RequestOptions) => {
        return request({
            ...options,
            url,
            data,
            method: 'POST'
        })
    }
}

使用方式

import request from '@/utils/request'

request
    .get('/api/getList', {
        page: 1,
        size: 20
    })
    .then((res) => {
        console.log(res)
    })

集成 Mock 辅助开发

因为 mockjs 只适用于 h5,故小程序部分自行考虑。

实现功能:

  • 统一管理我们想要 mock 的接口
  • 便捷切换是否 mock
  • 自由控制哪些接口 mock,哪些接口真实请求
  • 对于调用接口的地方是否 mock 是无感知的

比如:

import request from '@/utils/request'
/**
 * 这样写,既可以是mock数据,也可以是调用接口。
 */
request.get('/getUserInfo')

安装 mock

pnpm add mockjs
pnpm add -D @types/mockjs

前面我们在环境变量里添加了统一接口地址。我们先制定如下规则:

# 请求接口地址
VITE_REQUEST_BASE_URL = /dev # 这样可以直接使用代理请求
VITE_REQUEST_BASE_URL = /dev/mock # 这样就可以开启mock
VITE_REQUEST_BASE_URL = https://xxx.com/api # 这样就是直接请求接口

根目录下创建 mock/index.ts 文件来存放我们的 Mock 规则。

import Mock from 'mockjs'

// 基于我们制定的规则,这里必须做下判断,这个很重要。
if (/\/mock$/.test(import.meta.env.VITE_REQUEST_BASE_URL)) {
    // 这里添加 /getUserInfo 这个接口mock数据
    Mock.mock(`${import.meta.env.VITE_REQUEST_BASE_URL}/getUserInfo`, {
        code: 200,
        success: true,
        msg: '',
        result: {
            name: Mock.Random.cname()
        }
    })
}

在 main.js 中引入下。(为什么要引入?不引入代码怎么执行啊)

import '../mock'

接下来改造我们封装的请求。

修改 src/utils/request/index.ts

import Mock from 'mockjs'

if (/^(http|https):\/\/.*/.test(config.url)) {
    // 如果是以http/https开头的则不添加VITE_REQUEST_BASE_URL
    url = config.url
    // eslint-disable-next-line no-underscore-dangle
} else if (Mock._mocked[import.meta.env.VITE_REQUEST_BASE_URL + config.url]) {
    // 如果是mock数据,Mock._mocked上记录有所有已设置的mock规则。
    url = import.meta.env.VITE_REQUEST_BASE_URL + config.url
} else {
    /**
     * 开启mock时需要去掉mock路径,不能影响正常接口了。
     * 如果碰巧你接口是 /api/mock/xxx这种,那VITE_REQUEST_BASE_URL就配置/api/mock/mock吧
     */
    url = import.meta.env.VITE_REQUEST_BASE_URL.replace(/\/mock$/, '') + config.url
}

如果 Mock._mocked类型“typeof mockjs”上不存在属性“_mocked”,需要我们扩展下声明

// src/shims-vue.d.ts

// 扩展mock
declare module 'mockjs' {
    /** 所有已注册的mock规则  */
    const _mocked: Record<string, any>
}

集成 uni-ui

因为我们使用的是 vite 开发的,所以我们只能使用 npm+easycom 的方式集成

安装 uni-ui

pnpm add @dcloudio/uni-ui

打开项目根目录下的 pages.json 并添加 easycom 节点:

// pages.json
{
    "easycom": {
        "autoscan": true,
        "custom": {
            // uni-ui 规则如下配置
            "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
        }
    },

    // 其他内容
    "pages": [
        // ...
    ]
}

然后就可以直接使用了

注意无需再次引入

<uni-rate :size="18" :value="5" />

H5 和小程序效果图

写在最后

如果你想快速的体验 vue3 开发,你可以拉取我的源码直接开发。

github: https://github.com/CalmHarbin/uniApp-template

创作不易、欢迎 ⭐

TODO:

  • 使用 Hbuilder 创建 uni-app 模板
  • 搭建一个 Taro+vue3 模板

About

使用vite创建uni-app开发环境,集成eslint+stylelint+typescript。适用全端。

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published