tina-ui
v1.0.0
Published
因为每次在开发新项目时,都需要一个开箱即用的基础框架,避免重新开始搭建而浪费时间,遂记录下从零开始搭建一个开箱即用的框架。随着前端的发展,未来版本的更新需要重新搭建框架时也可以作个参考。
Downloads
1
Readme
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 上下载。
做一些简单的配置
基础模板中功能比较少,我们对生成的基础框架添加一些自定义的配置。
- 规范目录
- 配置别名
@
来表示src
目录 - 配置代理解决开发环境跨域的问题
- 打包调整生成规范的文件
修改 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
模板