1.创建项目
npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project
命令最后空格加 项目名
创建可能会出现报错情况
这种直接到gitee 下载源码就行
TS 类型校验
- 安装 类型声明文件
npm i -D miniprogram-api-typings @uni-helper/uni-app-types
- 配置 tsconfig.json 参考
// tsconfig.json
{
"extends": "@vue/tsconfig/tsconfig.json",
"compilerOptions": {
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"lib": ["esnext", "dom"],
// 类型声明文件
"types": [
"@dcloudio/types", // uni-app API 类型
"miniprogram-api-typings", // 原生微信小程序类型
"@uni-helper/uni-app-types" // uni-app 组件类型
]
},
// vue 编译器类型,校验标签类型
"vueCompilerOptions": {
// 原配置 `experimentalRuntimeMode` 现调整为 `nativeTags`
"nativeTags": ["block", "component", "template", "slot"],
"experimentalRuntimeMode": "runtime-uni-app"
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
打开代码会发现 manifest.json 和 pages.json 会出现报错 原因是不能有注释
解决办法: 在 settings.json 中 添加以下代码
"files.associations": {
"pages.json": "jsonc",
"manifest.json": "jsonc",
},
2. ui组件库(vant-ui)引用(官网)
安装vant4
组件按需引入配置
1.安装插件
# 通过 npm 安装
npm i @vant/auto-import-resolver unplugin-vue-components -D
# 通过 yarn 安装
yarn add @vant/auto-import-resolver unplugin-vue-components -D
# 通过 pnpm 安装
pnpm add @vant/auto-import-resolver unplugin-vue-components -D
# 通过 bun 安装
bun add @vant/auto-import-resolver unplugin-vue-components -D
2.配置插件
如果是基于 vite 的项目,在 vite.config.js 文件中配置插件:
import vue from '@vitejs/plugin-vue';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from '@vant/auto-import-resolver';
export default {
plugins: [
vue(),
Components({
resolvers: [VantResolver()],
}),
],
};
如果是基于 vue-cli 的项目,在 vue.config.js 文件中配置插件:
const { VantResolver } = require('@vant/auto-import-resolver');
const ComponentsPlugin = require('unplugin-vue-components/webpack');
module.exports = {
configureWebpack: {
plugins: [
ComponentsPlugin({
resolvers: [VantResolver()],
}),
],
},
};
如果是基于 webpack 的项目,在 webpack.config.js 文件中配置插件:
const { VantResolver } = require('@vant/auto-import-resolver');
const ComponentsPlugin = require('unplugin-vue-components/webpack');
module.exports = {
plugins: [
ComponentsPlugin({
resolvers: [VantResolver()],
}),
],
};
3.使用组件
<template>
<van-button type="primary" >按钮 </van-button>
</template>
有vant4 效果说明成功
3. Pinia 持久化 (官网)
使用方法
- List itemdefineStore( ) 方法的第一个参数:相当于为容器起一个名字。注意:这里的名字必须唯一,不能重复。
- defineStore( ) 方法的第二个参数:可以简单理解为一个配置对象,里边是对容器仓库的配置说明。当然这种说明是以对象的形式。
- state 属性: 用来存储全局的状态的,这里边定义的,就可以是为SPA里全局的状态了。
- getters属性: 用来监视或者说是计算状态的变化的,有缓存的功能。
- actions属性: 对state里数据变化的业务逻辑,需求不同,编写逻辑不同。说白了就是修改state全局状态数据的。
import { defineStore } from 'pinia'
export const mainStore = defineStore("名称(唯一)", {
state:() => {
return {
}
},
getters: {
},
actions: {
}
})
1.安装
npm i pinia
2.配置mian.ts
import { createPinia } from 'pinia'
app.use(createPinia())
3.在src中创建stores 文件夹 和index.ts
import useMemberStore from './modules/user'
export default function useStore() {
return {
user: useMemberStore(),
}
}
4.在stores中创建modules文件夹 创建user.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
// 定义 Store
const useMemberStore = defineStore(
'user',
() => {
// 用户信息
const userfile = ref<any>()
// 保存用户信息,登录时使用
const setProfile = (val: any) => {
userfile.value = val
}
// 清理用户信息,退出时使用
const clearProfile = () => {
userfile.value = undefined
}
// 记得 return
return {
userfile,
setProfile,
clearProfile,
}
}
)
export default useMemberStore
遇到问题:
这种情况需要pinia 降级 降为:2.0.36 有效 vue 版本:3.3.4
测试:
4.Router(插件说明)路由配置
1.安装(需要跨平台可以用uni-app自带路由或者安装router跨平台插件,这里只做H5配置)
npm install uni-mini-router --save
npm install uni-parse-pages --save
2.配置
项目src目录下(HbuilderX创建的项目可以在根目录下)创建router文件夹,并在该文件夹创建index.ts
//index.ts
import { createRouter } from 'uni-mini-router'
// 导入pages.json
import pagesJson from '../pages.json'
// 引入uni-parse-pages
import pagesJsonToRoutes from 'uni-parse-pages'
// 生成路由表
const routes = pagesJsonToRoutes(pagesJson)
const router = createRouter({
routes: [...routes] // 路由表信息
})
export default router
配置main.ts
import { createSSRApp } from "vue";
import pinia from './stores'
import App from "./App.vue";
import router from './router'
export function createApp() {
const app = createSSRApp(App);
app.use(pinia)
app.use(router)
return {
app,
};
}
配置pages.json
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"name": "home",
"style": {
"navigationBarTitleText": "首页",
"navigationBarBackgroundColor": "#E7F0FF"
}
},
{
"path": "pages/login/login",
"name": "login",
"style": {
"navigationBarTitleText": "登录",
"navigationBarBackgroundColor": "#E7F0FF"
}
},
{
"path": "pages/mine/mine",
"name": "mine",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#E7F0FF"
}
}
],
"tabBar": {
"color": "#333",
"selectedColor": "#27ba9b",
"backgroundColor": "#fff",
"borderStyle": "white",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "static/tabs/home_default.png",
"selectedIconPath": "static/tabs/home_selected.png",
"text": "首页"
},
{
"pagePath": "pages/mine/mine",
"iconPath": "static/tabs/user_default.png",
"selectedIconPath": "static/tabs/user_selected.png",
"text": "我的"
}
]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}
页面测试:
// pages/index/index.vue
<template>
<van-button type="primary" @click="login"> 登录按钮 </van-button>
</template>
<script setup lang="ts">
import router from '@/router'
const login = () => {
router.replaceAll({ name: 'mine' })
}
显示mine 则成功
5.axios(官网)请求封装
1.axios 安装
npm install axios
2.在src文件夹下创建utils文件夹, utils文件夹下创建http.ts
// http.ts
import axios from 'axios'
const instance = axios.create({
baseURL :'http://localhost:5173/src/styles',
timeout :60000
})
instance.interceptors.request.use(
(request) => {
return request
},
(error) => {
return Promise.reject(error)
}
)
instance.interceptors.response.use(
(response) => {
return response.data
},
(error) => {
return Promise.reject(error)
}
)
export default instance
3.在src/styles下 创建虚拟text.json数据
{
"code":"200",
"message":"请求成功",
"lists":[
{
"date": "2016-05-02",
"name": "王小虎",
"address": "上海市普陀区金沙江路 1518 弄",
"token":"bug666"
}
]
}
4.在src文件夹下创建api ,创建login.ts
import request from '@/utils/http'
export function text(data:any){
return request({
//访问到styles下的text.json文件
url:'/text.json',
method:'post',
data
})
}
5.在pages/index/index.vue 中 写入测试接口测试是否成功响应
<template>
<van-button type="primary" @click="login"> 登录按钮 </van-button>
</template>
<script setup lang="ts">
import { text } from "@/api/login"
//login()
const login = () => {
text('').then((res: any) => {
console.log(res);
});
};
</script>
<style>
</style>
点击按钮 出现如图则成功
6.Eslint(官网)
1.安装
npm i eslint -D
npm i eslint-plugin-vue -D
2.在根目录创建.eslintrc.js
module.exports = {
//此项是用来告诉eslint找当前配置文件不能往父级查找
root: true,
//指定eslint继承的模板
extends: ["plugin:vue/essential", "@vue/standard"],
//此项指定环境的全局变量,下面的配置指定为浏览器环境
env: {
browser: true
},
// 此项是用来提供插件的,插件名称省略了eslint-plugin-,下面这个配置是用来规范html的
plugins: ["vue"],
//指定javaScript语言类型和风格
parserOptions: {
parser: "babel-eslint"
},
//规则https://www.wenjiangs.com/docs/eslint,vue规则:https://eslint.vuejs.org/rules/
// 主要有如下的设置规则,可以设置字符串也可以设置数字,两者效果一致
// "off" -> 0 关闭规则
// "warn" -> 1 开启警告规则
//"error" -> 2 开启错误规则
rules: {
// 使用 === 替代 == allow-null允许null和undefined== [2, "allow-null"]
eqeqeq: 0,
// 双峰驼命名格式
camelcase: 0,
//要求或者禁止Yoda条件
yoda: 2,
// 行尾不使用分号
semi: 0,
//强制一致地使用反引号、双引号或单引号。
quotes: 2,
//强制函数中的变量在一起声明或分开声明
"one-var": 2,
// 禁用 console
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
// 强制 generator 函数中 * 号周围使用一致的空格
"generator-star-spacing": "off",
// 禁用 debugger
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
// 禁止对象字面量中出现重复的 key
"no-dupe-keys": 2,
// 函数参数不能重复
"no-dupe-args": 2,
// 禁止重复的函数声明
"no-func-assign": 2,
// 禁止重复的 case 标签
"no-duplicate-case": 2,
// 禁用未声明的变量
"no-undef": 1,
//禁止出现多个空格
"no-multi-spaces": 2,
// 不允许标签与变量同名
"no-label-var": 2,
//禁止tab
"no-tabs": 1,
// 禁止 var 声明 与外层作用域的变量同名
"no-shadow": 0,
// 禁止 if 语句中有 return 之后有 else
"no-else-return": 0,
// 禁止出现空函数.如果一个函数包含了一条注释,它将不会被认为有问题。
"no-empty-function": 1,
// 禁止出现未使用过的变量
"no-unused-vars": 1,
//禁止在返回语句中赋值
"no-return-assign": 0,
// 禁用行尾空格
"no-trailing-spaces": 2,
// 禁止修改 const 声明的变量
"no-const-assign": 2,
// 禁止类成员中出现重复的名称
"no-dupe-class-members": 2,
//禁止使用alert confirm promp
"no-alert": process.env.NODE_ENV === "production" ? "error" : "off",
//禁止多余的冒号
"no-extra-semi": 2,
//禁止在条件中使用常量表达式
"no-constant-condition": 2,
//空行最多不能超过2行
"no-multiple-empty-lines": [1, { max: 2 }],
//禁止无用的表达式
"no-unused-expressions": 1,
//禁用不必要的嵌套块
"no-lone-blocks": 2,
//不允许使用逗号操作符
"no-sequences": 2,
//禁止不规则的空白
"no-irregular-whitespace": 2,
//函数括号前的空格
"space-before-function-paren": 0,
//处理回调错误
"handle-callback-err": 1,
//首选承诺拒绝错误
"prefer-promise-reject-errors": 0,
//要求或禁止在注释前有空白 (space 或 tab)
"spaced-comment": 1,
//强制关键字周围空格的一致性
"keyword-spacing": 1,
//强制在花括号中使用一致的空格
"object-curly-spacing": 1,
// 控制逗号前后的空格
"comma-spacing": [
2,
{
before: false,
after: true
}
],
// 要求或禁止 var 声明语句后有一行空行
"newline-after-var": 0,
//强制使用一致的缩进
indent: 0,
// html 内 缩进
"vue/html-indent": 0,
// 插值两端必须留一个空格
"vue/mustache-interpolation-spacing": 0,
//强制每行的最大属性数
"vue/max-attributes-per-line": 0,
//vue/属性顺序
"vue/attributes-order": 0,
// 强制要求在对象字面量的属性中键和值之间使用一致的间距 "var obj = { "foo": 42 };"
"key-spacing": 0,
// 禁止末尾逗号
"comma-dangle": 0,
// 强制在块之前使用一致的空格 "function a() {}"
"space-before-blocks": 0,
// 要求操作符周围有空格 "a ? b : c"
"space-infix-ops": 2,
// "() => {};" // 强制箭头函数前后使用一致的空格
"arrow-spacing": 2,
//插值中强制统一间距
//强制组件中的属性顺序
"vue/order-in-components": 0,
//不允许字段名称重复
"vue/no-dupe-keys": 2,
//多次引用同个包
"import/no-duplicates": 2,
//执行有效v-for指令
"vue/valid-v-for": 2,
//V-bind:key使用v-for指令要求
"vue/require-v-for-key": 2,
//不允许解析错误<template>
"vue/no-parsing-error": [2, { "x-invalid-end-tag": false }],
//强制执行自闭式
"vue/html-self-closing": "off",
//不允许计算属性中的副作用
"vue/no-side-effects-in-computed-properties": 0,
//禁止 v-for 指令或范围属性的未使用变量定义
"vue/no-unused-vars": 1,
//执行有效v-model指令
"vue/valid-v-model": 2,
//强制执行有效的模板根
"vue/valid-template-root": 2
}
};
3.在setting.json 配置
"[vue]": {
"editor.defaultFormatter": "Vue.volar"
},
// vscode默认启用了根据文件类型自动设置tabsize的选项
"editor.detectIndentation": false,
// 重新设定tabsize
"editor.tabSize": 2,
// #每次保存的时候自动格式化
"editor.formatOnSave": true,
// eslint配置项,保存时自动修复错误
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
// 添加 vue 支持
"eslint.validate": [
"javascript",
"javascriptreact",
"html",
"vue"
],
// #让函数(名)和后面的括号之间加个空格
"javascript.format.insertSpaceBeforeFunctionParenthesis": true,
//打开文件不覆盖
"workbench.editor.enablePreview": false,
// 设置不同文件使用的格式化配置
"[html]": {
// "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"vetur.format.defaultFormatter.css": "prettier",
"vetur.format.defaultFormatter.postcss": "prettier",
"vetur.format.defaultFormatter.scss": "prettier",
"vetur.format.defaultFormatter.less": "prettier",
"vetur.format.defaultFormatter.stylus": "stylus-supremacy",
"vetur.format.defaultFormatter.ts": "prettier",
// vetur 缩进是4
"vetur.format.options.tabSize": 2,
"vetur.format.options.useTabs": false,
// #vue组件中html代码格式化样式
"prettier.vueIndentScriptAndStyle": true,
"vetur.format.defaultFormatterOptions": {
"prettier": {
// Prettier option here
"trailingComma": "es5", // 多行时,尽可能打印尾随的逗号
"tabWidth": 2, // 会忽略vetur的tabSize配置
"useTabs": false, // 是否利用tab替代空格
"semi": true, // 句尾是否加;
"singleQuote": false, // 使用单引号而不是双引号
"arrowParens": "avoid", // allow paren-less arrow functions 箭头函数的参数使用圆括号
},
"js-beautify-html": {
//属性强制折行对齐
"wrap_attributes": "force-aligned"
}
},
"eslint.options": {
"configFile": "./`.eslintrc`.js"
},
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
}
7.权限控制
1.在src/enums中 添加user.ts
export default class UserInfo {
// 帐号
userId: string | null = null;
// token
token: string | null = null;
//名称
name: string | null = null;
//用户类型
UserType: string | null = null;
}
2.在src/stores/modules/user.ts中配置状态管理
import { defineStore } from "pinia";
import type UserInfo from "@/enums/user";
interface AuthStore {
userInfo: UserInfo | null;
}
// 定义 Store
const useMemberStore = defineStore("user", {
state: (): AuthStore => ({
userInfo: null,
}),
getters: {},
actions: {
logout() {
sessionStorage.removeItem("userInfo");
localStorage.removeItem("user");
},
},
});
export default useMemberStore;
3.在src/style/text.json
{
"code": "200",
"message": "请求成功",
"lists": [
{
"userId": "123456",
"token": "abc123",
"name": "xpf",
"UserType": "bug666"
}
]
}
4.src/api/login.ts
import request from "@/utils/http";
export function text(data: any) {
return request({
//访问到styles下的text.json文件
url: "/text.json",
method: "post",
data,
});
}
5.login.ts代码:
<template>
<van-form @submit="onSubmit">
<van-cell-group inset>
<van-field v-model="username" name="用户名" label="用户名" placeholder="用户名"
:rules="[{ required: true, message: '请填写用户名' }]" />
<van-field v-model="password" type="password" name="密码" label="密码" placeholder="密码"
:rules="[{ required: true, message: '请填写密码' }]" />
</van-cell-group>
<div style="margin: 16px;">
<van-button round block type="primary" native-type="submit">
登录
</van-button>
</div>
</van-form>
</template>
<script lang="ts" setup>
import router from "@/router";
import { ref } from "vue";
import { text } from "@/api/login"
import PiniaInfo from '@/stores/index';
import { storeToRefs } from "pinia"
const username = ref("");
const password = ref("");
debugger
if (sessionStorage["userInfo"]) {
router.replaceAll({ name: "home" });
}
const onSubmit = () => {
text(null).then((res: any) => {
const user = storeToRefs(PiniaInfo().user)
user.userInfo.value = res.lists[0];
sessionStorage["userInfo"] = JSON.stringify(res.lists[0]);
router.replaceAll({ name: "home" });
});
};
</script>
<style></style>
6.src/pages/index/index.ts代码
<template>
<van-button type="primary" @click="loginHtml"> 登录页 </van-button>
</template>
<script setup lang="ts">
import router from "@/router";
const loginHtml = () => {
router.replaceAll({ name: "login" });
}
</script>
<style></style>
7.src/pages/mine/mine.ts代码
<template>
<van-button type="primary" @click="mine"> 退出登录 </van-button>
</template>
<script lang="ts" setup>
import router from "@/router";
import userInfo from "@/stores/index"
const mine = () => {
const out = userInfo()
out.user.logout()
router.replaceAll({ name: "login" })
}
</script>
<style></style>
测试…