├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .husky ├── .gitignore └── pre-commit ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── .vscode └── extensions.json ├── README.md ├── components.d.ts ├── index.html ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── public └── vite.svg ├── src ├── App.vue ├── assets │ ├── iconfont │ │ ├── demo.css │ │ ├── demo_index.html │ │ ├── font │ │ │ ├── d9eH2sUIxqPl.woff │ │ │ ├── d9eH2sUIxqPl.woff2 │ │ │ └── font.css │ │ ├── iconfont.css │ │ ├── iconfont.js │ │ ├── iconfont.json │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── iconfont.woff2 │ ├── images │ │ ├── loginbg.jpg │ │ ├── mypic.jpg │ │ ├── 个人中心.png │ │ ├── 版本介绍.png │ │ └── 首页.png │ ├── scss │ │ ├── index.scss │ │ ├── reset.scss │ │ └── variables.scss │ └── vue.svg ├── components │ └── TabBar.vue ├── global │ └── env.ts ├── hooks │ └── user.ts ├── layout │ └── index.vue ├── main.ts ├── polyfill │ └── polyfill.ts ├── router │ └── index.ts ├── service │ ├── apiList.ts │ ├── error.ts │ ├── handleError.ts │ ├── request.ts │ ├── requestList.ts │ └── webRequest.ts ├── store │ ├── index.ts │ └── modules │ │ ├── index.ts │ │ └── user.ts ├── typings │ └── index.ts ├── utils │ ├── commonTools.ts │ └── vconsole.ts ├── views │ ├── account.vue │ ├── category │ │ └── index.vue │ ├── home │ │ └── index.vue │ ├── login │ │ └── index.vue │ ├── mycenter │ │ └── index.vue │ ├── shopcart │ │ └── index.vue │ └── testPage │ │ └── one.vue └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | tsconfig.json 4 | *.svg 5 | *.png 6 | *.jpg 7 | *.jpeg 8 | *.scss 9 | *.gif 10 | *.webp 11 | *.ttf 12 | index.html 13 | *.md -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: "vue-eslint-parser", 4 | parserOptions: { 5 | parser: "@typescript-eslint/parser", 6 | }, 7 | extends: ["plugin:vue/vue3-recommended", "plugin:prettier/recommended"], 8 | rules: { 9 | "vue/no-v-html": 0, 10 | "vue/v-on-event-hyphenation": 0, 11 | "vue/no-template-shadow": 0, 12 | "vue/multi-word-component-names": 0, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | .eslintcache -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v16 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | /deploy 4 | *.yml 5 | *.yaml 6 | tsconfig.json 7 | *.svg 8 | *.png 9 | *.jpg 10 | *.jpeg 11 | *.gif 12 | *.webp 13 | *.ttf 14 | index.html 15 | *.md -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, // 使用单引号代替双引号 3 | printWidth: 200, // 超过最大值换行 4 | semi: false, // 结尾不用分号 5 | useTabs: true, // 缩进使用tab, 不使用空格 6 | tabWidth: 4, // tab 样式宽度 7 | bracketSpacing: true, // 对象数组, 文字间加空格 {a: 1} => { a: 1 } 8 | arrowParens: "avoid", // 如果可以, 自动去除括号 (x) => x 变为 x => x 9 | proseWrap: "preserve", 10 | htmlWhitespaceSensitivity: "ignore", 11 | trailingComma: "all", 12 | overrides: [ 13 | { 14 | files: '*.scss', 15 | options: { 16 | parser: 'scss', 17 | }, 18 | }, 19 | ], 20 | }; 21 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **[vue3.3-Mobile-template](https://github.com/HSg666/vue3.3-Mobile-template)** 2 | 3 | 基于Vue3.3 + TS + Vant4 + Vite5 + Pinia + tailwindcss + ViewPort适配 + Sass + Axios封装 + vconsole调试工具,搭建的H5移动端开发模板,开箱即用的。 4 | 5 | ### 环境要求: 6 | 7 | Node:18.20.2 pnpm:10.5.2 8 | 9 | 必须装上安装pnpm,没装的看这篇文章 https://blog.csdn.net/Steven_Son/article/details/135151622 10 | 11 | 代码管理工具推荐用:sourceTree 12 | 13 | ### 项目预览 14 | ![](https://img-blog.csdnimg.cn/direct/5d6fdf21951c40efa2ddd1b5abe89b1d.png) 15 | 16 | 17 | ![](https://img-blog.csdnimg.cn/direct/58786e2f1f1c4ff3b57adfa64ece87d1.png) 18 | 19 | ![](https://img-blog.csdnimg.cn/direct/2462980ba4fa44c1ac591aede2895971.png) 20 | 21 | ### 项目结构 22 | 23 | ```js 24 | learn-vite -- UI 主目录 25 | ├── dist 打包后自动生成的文件夹 26 | ├── public -- 静态资源 27 | ├ ├── favicon.ico -- 图标 28 | ├── src -- 源码目录 29 | ├ ├── assets -- 全局静态资源 30 | ├ ├ ├── iconfont -- 字体和字体图标 31 | ├ ├ ├── images -- 图片存放路径 32 | ├ ├ ├── json -- 静态json 33 | ├ ├ └── scss -- index.scss 全局样式,reset.scss初始化样式 34 | ├ ├── components -- 封装的组件 35 | ├ ├── global 配置全局URL环境变量 36 | ├ ├── hooks -- vue3 Hooks 37 | ├ ├── layout -- 全局Tabbar配置、keep-alive可配置需长缓存的路由 38 | ├ ├── polyfill 解决浏览器兼容性的文件 39 | ├ ├── router -- VUE 路由 40 | ├ ├ ├── index -- 路由入口 41 | ├ ├── service 42 | ├ ├ ├── apiList.ts -- 接口列表 43 | ├ ├ ├── error.ts -- 封装的接口错误提示 44 | ├ ├ ├── handleError.ts -- 处理接口请求错误 45 | ├ ├ ├── requestList.ts -- 请求函数列表 46 | ├ ├ └── webRequest.ts -- 封装Axios请求函数 47 | ├ ├── store -- Pinia 48 | ├ ├ ├── index -- 统一导出整个pinia和store 49 | ├ ├ └── modules.ts store模块化 50 | ├ ├── typings -- 存储TS类型别名 51 | ├ ├── utils -- 工具包 52 | ├ ├── views -- 业务上的 vue 页面 53 | ├ ├── App.vue -- 根组件 54 | ├ └── main.ts -- 入口 ts 55 | ├── components.d.ts -- 自动注册组件文件 56 | ├── .eslintrc.js -- ESLint 配置 57 | ├── .gitignore -- git 忽略 58 | ├── tsconfig.json -- vscode 路径引入配置 59 | ├── tailwind.config.js -- tailwindcss 配置文件 60 | ├── index.html -- 首页 61 | ├── package.json -- 依赖管理 62 | ├── vite.config.ts -- vite5的相关配置 63 | └── windi.config.ts -- WindiCSS的配置文件 64 | ``` 65 | 66 | ## 命令 67 | 68 | ```js 69 | git clone https://github.com/HSg666/vue3.3-Mobile-template 70 | // 或 git clone git@github.com:HSg666/vue3.3-Mobile-template 71 | pnpm i // 装依赖 72 | pnpm start // 启动 73 | pnpm run build // 打包 74 | rm -rf node_modules // 强行删除依赖包 75 | ``` 76 | 准备打包上线时请看 配置全局URL环境变量,检查完配置后再执行pnpm run build 打包 77 | 78 | 部署上线后如果出现页面刷新报Nginx404,请看这篇文章并对照检查你的router/index.ts中的mode模式,更改配置后再试试就OK了。 79 | 80 | https://blog.csdn.net/Steven_Son/article/details/135414494 81 | 82 | ## 目录 83 | 84 | - [1、封装Router](#router) 85 | - [2、Vant4自动按需导入](#vant4) 86 | - [3、封装Axios请求函数、接口列表、请求错误处理](#axios) 87 | - [4、配置全局URL环境变量](#globalUrl) 88 | - [5、配置alias路径别名](#alias) 89 | - [6、封装Pinia、模块化、长缓存](#pinia) 90 | - [7、postcss-px-to-viewport移动端适配](#postcss-px-to-viewport) 91 | - [8、自动导入组件](#unplugin-vue-components) 92 | - [9、封装TabBar布局容器](#tabbar) 93 | - [10、tailwindcss样式库](#tailwindcss) 94 | - [11、初始化全局CSS和防止页面文本被用户选中](#resetcss) 95 | - [12、字体与字体图标](#iconfont) 96 | - [13、性能优化](#xnyh) 97 | - [14、代码规范](#pretter) 98 | - [15、配置兼容性](#jrx) 99 | - [16、已配置第三方工具库](#threeTool) 100 | - [17、拓展](#tuozhan) 101 | 102 | ## 1、封装Router 103 | 104 | 路径:src/router/index.ts 105 | 106 | ```js 107 | // 需要Tabbar的组件在layoutRoutes中添加路由,Tabbar就是页面底部的 精选、分类、购物车、我的 108 | export const layoutRoutes: Array = [ 109 | { 110 | path: '/', 111 | name: 'home', 112 | meta: { 113 | title: 'home', 114 | keepAlive: true, 115 | }, 116 | component: () => import('@/views/home/index.vue'), 117 | }, 118 | { 119 | path: '/category', 120 | name: 'category', 121 | meta: { 122 | title: 'category', 123 | // keepAlive: true, 124 | }, 125 | component: () => import('@/views/category/index.vue'), 126 | }, 127 | ] 128 | ``` 129 | 130 | ```js 131 | // 不需要Tabbar的组件在routes中添加路由,即页面底部空空如也的组件。 132 | export const routes: Array = [ 133 | // 这个是布局,不用改 134 | { 135 | path: '/', 136 | component: () => import('@/layout/index.vue'), 137 | redirect: '/index', 138 | // 需要layout的页面 139 | children: layoutRoutes, 140 | }, 141 | // 注册的路由类似登录页 142 | { 143 | path: '/login', 144 | name: 'login', 145 | component: () => import('@/views/login/index.vue'), 146 | }, 147 | ] 148 | 149 | ``` 150 | ## 2、移动端UI库采用Vant4 151 | 项目已经配置好按需导入和组件自动注册了,页面直接使用即可,无需手动注册。 152 | 153 | 除了Toast轻提示使用时需要手动引入,其他都无需手动引入。示例如下: 154 | 155 | 在vant4 Toast的函数名都改了,大家看官方文档就知道。 156 | 157 | ```js 158 | // 示例: 159 | 166 | 169 | ``` 170 | 171 | 自动注册的组件都保存在项目根目录的 components.d.ts中,可自行查看。 172 | 173 | 配置详情:https://blog.csdn.net/Steven_Son/article/details/135544198?spm=1001.2014.3001.5501 174 | 175 | UI库官网地址:https://vant-ui.github.io/vant/#/zh-CN/button 176 | 177 | ## 3、封装Axios 178 | 179 | ### 1、新增axios并封装,还新增了自定义请求错误处理函数,请求类 180 | 181 | ### 2、封装api列表 apiList 182 | 183 | 封装的axios配合api接口使用模板 184 | 185 | (1)、先把接口添加进接口列表 186 | ```js 187 | export const APIs = { 188 | GET_SHOPLIST: '/h5/getShopList', // 获取商品列表 189 | } 190 | ``` 191 | (2)、页面使用 192 | ```js 193 | // account.vue 194 | import AxiosRequestError from '@/service/error' // 引入自定义错误处理函数 195 | import $api from '@/service/webRequest' // 封装好的axios请求函数 196 | import { APIs } from '@/service/apiList' // 接口列表 197 | 198 | // 二选一即可 199 | 200 | // async await 写法 201 | const getShop = async () => { 202 | try { 203 | const res = await $api.getShopList() 204 | console.dir(res, 'res') 205 | } catch (error: AxiosRequestError) { 206 | console.dir(error, 'error') 207 | } 208 | } 209 | 210 | // 原生Primise .then .catch 211 | const params = { user:'', password:'' } // 传参将需要传的值放入即可,跟vue2一样 212 | $api.get(APIs.GET_SHOPLIST, params) 213 | .then(() => {}) 214 | .catch((err: AxiosRequestError) => { 215 | console.dir(err, 'err') 216 | }) 217 | 218 | ``` 219 | (3)、用console.dir可以捕获到详细的错误信息,还能看到我们封装的错误处理函数 220 | 221 | data: undefined, // 接口返回值为undefined 222 | 223 | isServerError: false, // 是否为服务器出错 224 | 225 | isUnAuthorized: false, // 是否已通过鉴权,也就是常见的登录状态 226 | 227 | (4)、如果要添加或使用自定义请求函数,请在src/service/requestList.ts中添加,类似于已经存在的上传图片接口 228 | ### 3、自定义封装请求函数 229 | 1、先到service/apiList.ts中添加接口 230 | ```js 231 | export const APIs = { 232 | GET_PRDDETAIL: '/m/CcbLifeGoods/getyById', // 获取商品详情 233 | } 234 | ``` 235 | 2、在service/requestList.ts中添加,用模板字符串自行拼接 236 | 注:由于get如果传多个参数需要自己拼接的,不像post直接传整个json对象给后端,所以你打算用get传多个参数,请看一下案例(采用模板字符串和&拼接方式),见getOnlyId和getShopListFN;想用post单独传参也是ok的,见postOnlyId。 237 | ```js 238 | import { APIs } from './apiList' 239 | class API { 240 | // 获取单个id数据 241 | async getOnlyId(key, id) { 242 | return this.get(`${key}?id=${id}`) 243 | } 244 | // 获取商品列表 以及搜索 245 | // key:接口名称 data: 所有参数组成的对象 246 | async getShopListFN(key, data) { 247 | // 写法1 传统 248 | // return this.get(`${key}?id=${data.id}&name=${data.name}&price=${data.price}&sale=${data.sale}&xp=${data.xp}`) 249 | // 写法2 解构 250 | const { id,name,price,sale,xp } = data 251 | // return this.get(`${key}?id=${id}&name=${name}&price=${price}&sale=${sale}&xp=${xp}`) 252 | } 253 | // 传递单个id数据 254 | async postOnlyId(key, data) { 255 | return this.post(`${key}?goodsId=${data.goodsId}`) 256 | } 257 | 258 | } 259 | ``` 260 | 3、页面使用 261 | 注:自定义的方法都是绑定在$api this上的,所以直接在它身上取就可以 262 | ```js 263 | import $api from '@/service/webRequest' // 封装好的axios请求函数 264 | import { APIs } from '@/service/apiList' // 接口列表 265 | 266 | const getData = async () => { 267 | // 单一传 268 | let id = route.query.id 269 | const { data: res } = await $api.getOnlyId(APIs.GET_PRDDETAIL, id) 270 | 271 | // 多个参数组装为一个对象传入 272 | let params = { 273 | id:1, 274 | name:'HSg', 275 | price:99, 276 | sale:3, 277 | xp:11 278 | } 279 | const { data: res } = await $api.getShopListFN(APIs.GET_PRDDETAIL, params) 280 | } 281 | ``` 282 | ## 4、配置全局URL环境变量 283 | 开发和正式环境地址在 global/env.ts 中配置 284 | ```js 285 | // 静态图片前缀 286 | export const fileServerAddress = 'http://192.168.1.179:8081/' // 客户端地址(某后端接口地址) 287 | // const fileServerAddress = 'http://192.168.1.179:8081/' ; // 客户端地址(线上) 288 | 289 | // 正式环境 290 | export const PROD_ENV = { 291 | SERVER_URL: 'http://192.168.1.193:8090/', // 服务器地址 292 | IS_DEV: 'false', // 是否为开发环境 293 | } 294 | 295 | // 开发环境 296 | export const DEV_ENV = { 297 | SERVER_URL: 'http://192.168.1.193:8099/', 298 | IS_DEV: 'true', 299 | } 300 | 301 | /* 302 | isDEV:true为生产环境,false为开发环境 303 | 假设开发环境的域名是 http://127.0.0.1:8099/api 或 https://xxx-test.com 304 | 提示: 305 | 本地如果要将请求地址切换为生产服务器,则将isDEV设置为false,注释掉判断开发环境的代码。代码如下 306 | const isDEV = false 307 | // if (typeof window !== 'undefined') { 308 | // isDEV = process.env.NODE_ENV === 'development' || ['http://192.168.1.193:8099'].includes(window.location.host) 309 | // } 310 | 311 | 准备打包上线,将代码改回来。(开发环境也是这个代码)代码如下 312 | let isDEV = true // 默认为开发环境 313 | if (typeof window !== 'undefined') { 314 | isDEV = process.env.NODE_ENV === 'development' || ['http://192.168.1.193:8099'].includes(window.location.host) 315 | } 316 | 317 | */ 318 | 319 | let isDEV = true // 默认为开发环境,但会根据当前环境动态更换开发或生产 320 | if (typeof window !== 'undefined') { 321 | isDEV = process.env.NODE_ENV === 'development' || [fileServerAddress].includes(window.location.host) 322 | } 323 | ``` 324 | 325 | 静态图片前缀页面使用案例 326 | ```js 327 | home.vue 328 | // 图片前缀 329 | import { fileServerAddress } from '@/global/env' 330 | // 1、标签中 331 | 332 | 333 | // 2、函数中 334 | 直接使用fileServerAddress 335 | ``` 336 | 337 | 338 | 339 | 340 | ## 5、配置路径别名 alias 341 | 示例:@/store 只要在src下的都能这样简写 342 | 343 | 总共分为4步: 344 | 345 | 1、vite.config.ts 346 | 347 | ```js 348 | import path from 'path' 349 | export default defineConfig({ 350 | //新增 351 | resolve: { 352 | alias: { 353 | '@/assets': path.resolve(__dirname, './src/assets'), 354 | }, 355 | }, 356 | }) 357 | ``` 358 | 2、tsconfig.json 359 | ```js 360 | "paths": { 361 | "@/assets/*": ["src/assets/*"], 362 | } 363 | ``` 364 | 3、配置好全局使用 365 | 例如main.ts引入 366 | ```js 367 | // 引入全局样式 368 | import '@/assets/scss/index.scss' 369 | ``` 370 | 371 | 4、更改完vite.config.ts和tsconfig.json记得重启项目。 372 | 373 | ## 6、封装Pinia、模块化、长缓存 374 | 375 | 使用方式: 376 | 377 | 1、在store/modules下创建user.ts 378 | 379 | ````js 380 | import { defineStore, acceptHMRUpdate } from 'pinia' 381 | 382 | // 1、声明导出store名称 383 | export const userStore = defineStore({ 384 | id: 'user', // 2、声明store名称 385 | state: () => ({ 386 | name: '很老很老的值', 387 | }), 388 | getters: { 389 | myName: state => { 390 | return `getters ${state.name}` 391 | }, 392 | }, 393 | actions: { 394 | changeName(name: string) { 395 | this.name = name 396 | }, 397 | }, 398 | 399 | }) 400 | 401 | // 这行代码是用于支持热模块替换(HMR)的。在Pinia中,它允许接受热更新并应用到使用了userStore的地方。 402 | // 3、为了让当前store接收热更新为它配置一下 403 | if (import.meta.hot) { 404 | import.meta.hot.accept(acceptHMRUpdate(userStore, import.meta.hot)) 405 | } 406 | ```` 407 | 408 | 2、导出user.ts中整个userStore给其他组件使用 409 | 410 | store/modules/index.ts 411 | 412 | ```js 413 | export * from './user' 414 | ``` 415 | 416 | 3、store在组件中的使用方式 417 | 418 | ```js 419 | // 1、引入 420 | import { userStore } from '@/store' // 由于项目已配置路径别名,所以就用@/,它代表的是src 421 | 422 | // 2、实例化 423 | const useUserStore = userStore() 424 | 425 | // 3、如何使用userStore中的变量和函数 看下面template中的p标签就知道,解不解构2选1 426 | // 3.1.1 变量可用解构 例如取出name后直接使用即可 427 | const { name } = useUserStore 428 | // 3.1.2 变量不解构 需要加上useUserStore.name 429 | console.log(useUserStore.name) 430 | 431 | // 3.2 使用userStore中的函数 432 | const handleLogin = () => { 433 | useUserStore.changeName('张三') 434 | } 435 | 436 | // 页面 437 | 441 | ``` 442 | 443 | 完整代码 444 | 445 | ```js 446 | import { userStore } from '@/store' // 1、引入 447 | const useUserStore = userStore() // 2、实例化 448 | 449 | const { name } = useUserStore // 3、解构变量 450 | 451 | // 4、使用 452 | const handleLogin = () => { 453 | useUserStore.changeName('张三') 454 | } 455 | 456 | 459 | ``` 460 | 461 | 462 | 463 | 4、引入的store存储的数据默认是没有响应式的,可以用 storeToRefs 将其变为响应式。 464 | 465 | ```js 466 | // 引入 467 | import { storeToRefs } from "pinia"; 468 | 469 | // 将我们实例化的useAppstore放进去然后解构,解构出的state数据即为响应式 470 | const { name } = storeToRefs(useAppstore); 471 | ``` 472 | 473 | 需要storeToRefs的完整代码 474 | 475 | ```js 476 | import { userStore } from '@/store' // 引入userStore 477 | import { storeToRefs } from "pinia"; // 取出响应式方法 478 | 479 | const useUserStore = userStore() // 实例化 480 | const { name } = storeToRefs(useUserStore); // 将实例化对象的数据更改为响应式并解构出来 481 | 482 | // 使用userStore中的函数 483 | const handleLogin = () => { 484 | useUserStore.changeName('张三') 485 | } 486 | 487 | // 页面 488 | 491 | ``` 492 | 493 | 如何证明数据是否为响应式,请看这篇文章 https://blog.csdn.net/Steven_Son/article/details/128440811 494 | 495 | 封装+模块化:https://blog.csdn.net/Steven_Son/article/details/135553816?spm=1001.2014.3001.5501 496 | 497 | 长缓存:https://blog.csdn.net/Steven_Son/article/details/135551314?spm=1001.2014.3001.5501 498 | 499 | Pinia官网文章:https://pinia.web3doc.top/introduction.html 500 | 501 | ## 7、自适应采用的是postcss-px-to-viewport 502 | 503 | 详细配置说明看这篇文章:https://blog.csdn.net/Steven_Son/article/details/135554296?spm=1001.2014.3001.5501 504 | 505 | ## 8、自动导入组件 506 | 507 | 使用components下的组件时自动注册的插件 unplugin-vue-components 508 | 509 | 作用:哪个页面要用到components下的组件无需import手动导入,直接使用即可。 510 | 511 | 所用的组件都自动保存在项目根目录的 components.d.ts 中。 512 | 513 | ## 9、封装TabBar布局容器 514 | 515 | 1、路径:src/layout/index.vue 516 | 517 | 2、作用:页面整体的布局结构,如需增加/减少tabbar数量,增加时记得给新tabbar配置正确的路由,才能正常跳转。 518 | 519 | ## 10、tailwindcss库的用法 520 | 521 | 库已经配置好了,你直接使用即可。 522 | 523 | 524 | 525 | ```html 526 |

橙色

527 | ``` 528 | ## 11、fontawesome-free字体图标库 529 | 530 | 免费可商用的,这个库主要是配合tailwindcss的。因为用莫高生成的静态代码用的icon就是这个,为了方便直接使用莫高的生成静态代码到页面演示完美展示,所以直接帮你引入了。 你就无需再次引入。 531 | ```html 532 | 533 | ``` 534 | 535 | 536 | 537 | ## 12、初始化全局CSS和防止页面文本被用户选中 538 | 539 | src/assets/scss/reset.scss 和 src/assets/scss/index.scss 540 | 541 | ## 13、字体和字体图标 542 | 543 | 项目使用的字体和字体图标是阿里巴巴免费可商用的iconfont,无需担心是否侵权的问题。 544 | 545 | 路径:src/assets/iconfont 546 | 547 | 1、iconfont 阿里巴巴字体图标 548 | 549 | 配置文章链接: https://blog.csdn.net/Steven_Son/article/details/128149868?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22128149868%22%2C%22source%22%3A%22Steven_Son%22%7D 550 | 551 | 2、引入免费的阿里巴巴思源黑体字体 552 | 553 | 配置文章链接:https://www.iconfont.cn/fonts/detail?spm=a313x.fonts_index.i1.d9df05512.7ccd3a81uTg3IB&cnid=nsKKStjV4gdI 554 | 555 | ## 14、性能优化 556 | 557 | ### 1、需要keep-alive长缓存的组件在此配置 558 | 559 | 1、路由设置keepAlive属性 560 | 561 | src/router/index.ts 562 | 563 | ```js 564 | { 565 | path: '/category', 566 | name: 'category', 567 | meta: { 568 | title: 'category', 569 | keepAlive: true, // 加这一行 570 | }, 571 | component: () => import('@/views/category/index.vue'), 572 | }, 573 | ``` 574 | 575 | 2、到布局结构页面手动添加要keep-alive的组件名称 576 | 577 | src/layout/index.vue 578 | 579 | ```js 580 | const routerStrArr = ['home'] 581 | ``` 582 | 583 | 浏览器可以搭配插件vue.js Devtools 查看以及控制台网络降速测试 584 | 585 | 注意:最多缓存10个,缓存太多影响性能。 586 | 587 | ### 2、为每次打包的文件后缀添加打包时的时间戳,防止打包上线页面缓存的问题 588 | 589 | vite.config.ts timeStamp 590 | 591 | ### 3、为index.html增加防盗链,解决图片403 592 | 593 | ### 4、PC端时自动生成iframe框架嵌套项目并网页自动居中
594 | 595 | 具体代码逻辑在 src/App.vue onMounted中 596 | 597 | ### 5、vite.config.ts已配置诸多优化,具体请自行查看。 598 | 599 | ## 15、代码规范 600 | 601 | ### 1、prettier + eslint 配置了代码规范插件 602 | 603 | ### 2、husky + lint-staged git提交规范 604 | 605 | - feat:新功能(feature) 606 | - fix/to:修复 bug,可以是 QA 发现的 BUG,也可以是研发自己发现的 607 | - fix:产生 diff 并自动修复此问题。适合于一次提交直接修复问题 608 | - to:只产生 diff 不自动修复此问题。适合于多次提交。最终修复问题提交时使用 fix 609 | - docs:文档(documentation)。 610 | - style:格式(不影响代码运行的变动)【比如说加注释就是这个?】 611 | - refactor:重构(即不是新增功能,也不是修改 bug 的代码变动)。 612 | - perf:优化相关,比如提升性能、体验。 613 | - test:增加测试。 614 | - chore:构建过程或辅助工具的变动。 615 | - revert:回滚到上一个版本。 616 | - merge:代码合并。 617 | - sync:同步主线或分支的 Bug。 618 | 619 | ## 16、配置兼容性 620 | 621 | ### 1、browserslist 配置了浏览器兼容性 622 | 623 | ### 2、polyfill web项目兼容低版本浏览器插件 624 | 625 | core-js 和 @vitejs/plugin-legacy 626 | 627 | ## 17、已配置第三方工具库 628 | 629 | ### 1、lodash 630 | 631 | 防抖和节流的使用方法,节流用到时再去查 632 | 633 | ```js 634 | import { debounce,throttle } from 'lodash-es' 635 | 636 | // 它返回一个带防抖的新函数 637 | const debounceLogin = debounce(toLogin, 500) 638 | function toLogin() { 639 | console.log(111) 640 | } 641 | ``` 642 | 643 | ### 2、vConsole移动端调试工具 644 | 645 | 详细文章看这篇:https://blog.csdn.net/Steven_Son/article/details/135555570?spm=1001.2014.3001.5501 646 | 647 | 648 | ## 18、拓展: 649 | 650 | 651 | 652 | ### 1、如果不知道怎么用Nginx部署前端打包后的dist,可以看这篇文章 653 | 654 | https://blog.csdn.net/Steven_Son/article/details/135414494?spm=1001.2014.3001.5501 655 | 656 | ### 2、如果要做JWT免登,请根据你的需求对以下几个文件进行更改 657 | 658 | 1、src/service/webRequest.ts 设置token的地方 659 | 2、src/service/error.ts 错误报错页 660 | 3、src/login/index.vue 登录页,登录后可能就要保存token了 661 | 662 | ### 3、本地开发的项目到手机端演示 663 | 664 | 1、修改package.json配置,更改为你电脑的IP地址,同时电脑和手机要在同个网络。 665 | 说明:连的同个WIFI、同个网线。 666 | 667 | ```js 668 | "scripts": { 669 | "testMobile": "vite --host 192.168.1.193" 670 | } 671 | ``` 672 | 673 | 2、电脑(windows)关闭防火墙,这三个都要关闭:域网络、专用网络、公用网络。 674 | 位置:安全中心 —— 防火墙和网络保护 675 | 676 | 3、pnpm testMobild 启动项目,手机访问启动后的项目链接。 677 | 678 | ### 4、解决main.ts 文件引入路径的问题 679 | 680 | 1、如果引入路径正确,但是提示找不到文件,则删除'XX',重新引入 681 | 682 | 2、检查vite.config.ts的路径别名配置是否正确,正确代码如下 683 | 684 | ```js 685 | //新增 686 | resolve: { 687 | alias: { 688 | '@': path.resolve(__dirname, './src'), //把 src 的别名设置为 @ 689 | }, 690 | extensions: ['.js', '.json', '.ts'], // 这些类型的文件后缀的不需要写 691 | }, 692 | ``` 693 | 694 | 3、检查tsconfig.json的部分属性配置 695 | 696 | ```js 697 | "baseUrl": ".", 698 | "paths": {"@/*": ["src/*"]}, 699 | "target": "ES2020", 700 | "module": "ES2020", 701 | "lib": [ 702 | "es2020", 703 | "es5", 704 | "es6", 705 | "DOM", 706 | "DOM.Iterable" 707 | ], 708 | "moduleResolution": "node", 709 | "include": [ 710 | "src/**/*.ts", 711 | "src/**/*.d.ts", 712 | "src/**/*.tsx", 713 | "src/**/*.vue", 714 | ], 715 | ``` 716 | 717 | 4、检查tsconfig.node.json的部分属性配置 718 | 719 | ```js 720 | "compilerOptions": { 721 | "module": "ES2020", 722 | "moduleResolution": "node", 723 | "allowSyntheticDefaultImports": true 724 | }, 725 | "include": ["vite.config.ts", "src/**/*.ts", "global/*.ts"] 726 | ``` 727 | 728 | 5、在src下新建vite-env.d.ts ,解决ts无法识别引入.vue后缀的文件夹 729 | 730 | ```js 731 | /// 732 | declare module '*.vue' { 733 | import type { DefineComponent } from 'vue' 734 | 735 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 736 | 737 | const component: DefineComponent<{}, {}, any> 738 | 739 | export default component 740 | } 741 | 742 | ``` 743 | 744 | 每次修改完都要重启项目,或者关闭项目重启VSCode、重启项目。 745 | 746 | ### 5、使用van-nav-bar时看这里 747 | 748 | 直接使用,但由于它会盖住外部包裹层,所以你使用van-nav-bar时需要给外层container添加padding-top:92px; 也就是vant-nav-bar的2倍高度(1倍是46),因为我们设计稿是750的。 749 | 750 | 这样在van-nav-bar下的内容就不会被它盖住了。 751 | 752 | 不需要van-nav-bar的无需加此样式。 753 | 754 | ```html 755 | 760 | 765 | ``` 766 | ### 6、如果打开某些组件正确引入vue的api了,但还是报未找到vue文件,此时项目有缓存,关闭整个VScode重启项目即可解决。 767 | 768 | ```js 769 | import { ref } from 'vue' // 正确引了,但提示报未找到文件 770 | ``` 771 | ### 7、souceTree 如果提交代码失败请检查当前node 版本是否为16 772 | ```js 773 | node -v 如果不是则切换为16 774 | nvm use 16 切换后souceTree重新提交代码 775 | ``` 776 | ### 8、如何深层次修改vant-ui组件样式 777 | 778 | ```js 779 | ::v-deep(.van-search) { 780 | background-color: #f5f5f5; 781 | } 782 | ``` 783 | ### 9、如何定义SCSS全局公共样式并使用 784 | 1、在src/assets/scss/variables.scss中定义 785 | ```js 786 | // 主题色 787 | $theme-color: #af1d36; 788 | ``` 789 | 2、记得在vite.config.ts配置一下这个文件路径,否则页面用了找不着 790 | vite.config.ts 791 | ```js 792 | export default defineConfig({ 793 | css:{ 794 | // css预处理器 795 | preprocessorOptions: { 796 | scss: { 797 | // 引入 variables.scss 这样就可以在全局中使用 variables.scss中预定义的变量了 798 | // 给导入的路径最后加上 ; 799 | additionalData: '@use "@/assets/scss/variables.scss" as *;', 800 | api: 'modern-compiler', // or "modern" modern-compiler 最新的sass编译方式 801 | }, 802 | }, 803 | } 804 | }) 805 | ``` 806 | 3、页面如何使用 807 | views/home/index.vue 808 | ```html 809 | 812 | 813 | 818 | ``` 819 | 820 | 821 | ### 10、assets/images的图片可以用变量形式引入并使用 822 | 1、以home.vue为例 823 | 824 | 项目中已为assets配置路径别名,所以无需../ 825 | ```js 826 | import prodImg from '@/assets/images/icons/商品1.png' 827 | 828 |