uniapp-vue3-vite 搭建小程序、H5 项目模板

avatar
作者
猴君
阅读量:3

uniapp-vue3-vite 搭建小程序、H5 项目模板

1

作者GitHub:https://github.com/gitboyzcf 有兴趣可关注!!!

特色

准备

Vue3/Vite版要求 node 版本^14.18.0 || >=16.0.0

拉取默认UniApp模板

点击下载 默认模板,或者通过下面命令行拉取

npx degit dcloudio/uni-preset-vue#vite my-vue3-project 

得到下面目录结构👇

1

安装依赖

我这里使用pnpm ,可以使用如npm、yarn、…

pnpm install 

如有报下面错误👇

This modules directory was created using the following registries configuration:{“default”:“https://registry.npmjs.org/”}. The current configuration is {“default”:“https://registry.npm.taobao.org/”}. To recreate the modules directory using the new settings, run “pnpm install -g”.

解决方案 下载源切换

pnpm config set registry https://registry.npmjs.org 

启动项目测试

执行该命令 会将此项目编译成微信小程序项目,该命令会持续监听修改并进行热更新

pnpm dev:mp-weixin 

执行后会出现 dist\dev\mp-weixin文件夹结构

2

将此目录下的mp-weixin微信开发者工具进行打开

如未安装点击下面链接下载安装即可👇
https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

结果

结果

配置自动化导入

安装依赖

pnpm i unplugin-auto-import -D 

在vite.config.js中配置

配置

import AutoImport from 'unplugin-auto-import/vite'  AutoImport({  imports: ["vue", "uni-app", "pinia"],  dts: true, }) 

配置完后 重新执行pnpm dev:mp-weixin此时会生成auto-imports.d.ts文件

此时在pages/index/index.vue中不用引入直接可以使用vue的api

<script setup> const title = ref('Hello World!') </script> 

引入 prerttier + eslint + stylelint

安装相关依赖包👇

pnpm add -D eslint @babel/eslint-parser eslint-config-airbnb-base eslint-config-prettier eslint-plugin-import eslint-plugin-prettier eslint-plugin-vue vue-global-api stylelint stylelint-scss stylelint-config-standard-scss stylelint-config-prettier 

.editorconfig

# .editorconfig 文件 root = true  [*] # 表示所有文件适用 charset = utf-8 # 设置文件字符集为 utf-8 indent_style = space # 缩进风格(tab | space) indent_size = 2 # 缩进大小 end_of_line = lf # 控制换行类型(lf | cr | crlf) trim_trailing_whitespace = true # 去除行首的任意空白字符 insert_final_newline = true # 始终在文件末尾插入一个新行  [*.md] # 表示仅 md 文件适用以下规则 max_line_length = off # 关闭最大行长度限制 trim_trailing_whitespace = false # 关闭末尾空格修剪  

.prettierrc.cjs

module.exports = {   // 一行的字符数,如果超过会进行换行,默认为80,官方建议设100-120其中一个数   printWidth: 100,   // 一个tab代表几个空格数,默认就是2   tabWidth: 2,   // 启用tab取代空格符缩进,默认为false   useTabs: false,   // 行尾是否使用分号,默认为true(添加理由:更加容易复制添加数据,不用去管理尾行)   semi: false,   vueIndentScriptAndStyle: true,   // 字符串是否使用单引号,默认为false,即使用双引号,建议设true,即单引号   singleQuote: true,   // 给对象里的属性名是否要加上引号,默认为as-needed,即根据需要决定,如果不加引号会报错则加,否则不加   quoteProps: 'as-needed',   // 是否使用尾逗号,有三个可选值"<none|es5|all>"   trailingComma: 'none',   // 在jsx里是否使用单引号,你看着办   jsxSingleQuote: true,   // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }   bracketSpacing: true,   proseWrap: 'never',   htmlWhitespaceSensitivity: 'strict',   endOfLine: 'auto', }  

.eslintrc.cjs

// .eslintrc.cjs 文件 module.exports = {   env: {     browser: true,     es2021: true,     node: true,   },   extends: [     'eslint:recommended',     'plugin:vue/vue3-essential',     // eslint-plugin-import 插件, @see https://www.npmjs.com/package/eslint-plugin-import     'plugin:import/recommended',     // eslint-config-airbnb-base 插件, tips: 本插件也可以替换成 eslint-config-standard     'airbnb-base',     // 1. 接入 prettier 的规则     'prettier',     'plugin:prettier/recommended',     'vue-global-api',   ],   overrides: [     {       env: {         node: true,       },       files: ['.eslintrc.{js}'],       parserOptions: {         sourceType: 'script',       },     },   ],   parserOptions: {     ecmaVersion: 'latest',     parser: '@babel/eslint-parser',     sourceType: 'module',   },   plugins: [     '@babel/eslint-parser',     'vue',     // 2. 加入 prettier 的 eslint 插件     'prettier',     // eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript     'import',   ],   rules: {     // 3. 注意要加上这一句,开启 prettier 自动修复的功能     'prettier/prettier': 'error',     // turn on errors for missing imports     'import/no-unresolved': 'off',     // 对后缀的检测,否则 import 一个ts文件也会报错,需要手动添加'.ts', 增加了下面的配置后就不用了     'import/extensions': [       'error',       'ignorePackages',       { js: 'never', jsx: 'never', ts: 'never', tsx: 'never' },     ],     // 只允许1个默认导出,关闭,否则不能随意export xxx     'import/prefer-default-export': ['off'],     'no-console': ['off'],     // 'no-unused-vars': ['off'],     // '@typescript-eslint/no-unused-vars': ['off'],     // 解决vite.config.ts报错问题     'import/no-extraneous-dependencies': 'off',     'no-plusplus': 'off',     'no-shadow': 'off',     'vue/multi-word-component-names': 'off',     '@typescript-eslint/no-explicit-any': 'off',   },   // eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript   settings: {     'import/parsers': {       '@typescript-eslint/parser': ['.ts', '.tsx'],     },     'import/resolver': {       typescript: {},     },   },   globals: {     uni: true,     UniApp: true,     wx: true,     WechatMiniprogram: true,     getCurrentPages: true,     UniHelper: true,     Page: true,     App: true,   }, }  

.stylelintrc.cjs

// .stylelintrc.cjs 文件 module.exports = {   root: true,   extends: [     'stylelint-config-standard',     'stylelint-config-standard-scss', // tips: 本插件也可以替换成 stylelint-config-recommended-scss     'stylelint-config-recommended-vue/scss',     'stylelint-config-html/vue',     'stylelint-config-recess-order',   ],   overrides: [     // 扫描 .vue/html 文件中的<style>标签内的样式     {       files: ['**/*.{vue,html}'],       customSyntax: 'postcss-html',     },     {       files: ['**/*.{css,scss}'],       customSyntax: 'postcss-scss',     },   ],   // 自定义规则   rules: {     // 允许 global 、export 、v-deep等伪类     'selector-pseudo-class-no-unknown': [       true,       {         ignorePseudoClasses: ['global', 'export', 'v-deep', 'deep'],       },     ],     'unit-no-unknown': [       true,       {         ignoreUnits: ['rpx'],       },     ],     // 处理小程序page标签不认识的问题     'selector-type-no-unknown': [       true,       {         ignoreTypes: ['page'],       },     ],     'comment-empty-line-before': 'never',   }, } 

引入 husky + lint-staged + commitlint

说明
husky 用于git提交的钩子
lint-staged 一个在 git 暂存文件上(也就是被git add后的文件)运行已配置的格式工具;比如eslint、stylelintrc、…
commitlint 检查您的提交消息是否符合 常规提交格式 (Conventional commit format)
正确的提交格式:(): ,type 和 subject 默认必填
在这里插入图片描述

安装相关依赖包👇

pnpm i -D husky@6 lint-staged commitlint @commitlint/cli @commitlint/config-conventional 

执行 npx husky install
并且在 package.json的scripts里面增加 "prepare": "husky install",(其他人安装后会自动执行) 根目录会生成 .hushy 文件夹。

package.josn 增加如下属性👇:

... "scripts": { 	... 	"prepare": "husky install", }, "lint-staged": {   "**/*.{html,vue,ts,cjs,json,md}": [     "prettier --write"   ],   "**/*.{vue,js,ts,jsx,tsx}": [     "eslint --fix"   ],   "**/*.{vue,css,scss,html}": [     "stylelint --fix"   ] } 

根目录新增 .commitlintrc.cjs,内容如下👇

module.exports = {   extends: ['@commitlint/config-conventional'],   rules: {     'type-enum': [       2,       'always',       [         'feat',         'fix',         'perf',         'style',         'docs',         'test',         'refactor',         'build',         'ci',         'init',         'chore',         'revert',         'wip',         'workflow',         'types',         'release',       ],     ],     'subject-case': [0],   }, } 

通过下面命令在钩子文件中添加内容👇

npx husky add .husky/pre-commit "npx --no-install -- lint-staged" npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1" 

Commitizen & cz-git

说明
commitizen 基于Node.js的 git commit 命令行工具,辅助生成标准化规范化的 commit message
cz-customizable 标准输出格式的 commitizen 适配器

安装依赖包👇

pnpm add -D commitizen cz-customizable 

修改 package.json指定使用的适配器

... "scripts": { 	... 	"cz": "git-cz" },  "config": {     "commitizen": {       "path": "node_modules/cz-customizable"     }   } 

更改提示消息模板 .cz-config.js

.cz-config.js

module.exports = {   types: [     { value: 'feat', name: '✨ feat:     新功能' },     { value: 'fix', name: '🐛 fix:      修复' },     { value: 'init', name: '🎉 Init:     初始化' },     { value: 'docs', name: '📝 docs:     文档变更' },     { value: 'style', name: '💄 style:    代码格式(不影响代码运行的变动)' },     {       value: 'refactor',       name: '♻️  refactor: 重构(既不是增加feature,也不是修复bug)',     },     { value: 'perf', name: '⚡️ perf:     性能优化' },     { value: 'test', name: '✅ test:     增加测试' },     { value: 'revert', name: '⏪️ Revert:   回退' },     { value: 'build', name: '🚀‍ build:    构建过程或辅助工具的变动' },     { value: 'ci', name: '👷 ci:       CI 配置' },   ],   // 消息步骤   messages: {     type: '请选择提交类型:',     subject: '请简要描述提交(必填):',     customScope: '请输入修改范围(可选):',     body: '请输入详细描述(可选):',     breaking: '列出任何BREAKING CHANGES(可选)',     footer: '请输入要关闭的issue(可选):',     confirmCommit: '确认使用以上信息提交吗?',   },   allowBreakingChanges: ['feat', 'fix'],   skipQuestions: ['customScope'],   subjectLimit: 72, } 

检测

在命令行中输入👇

git add . pnpm cz 

然后会出现本次提交选项
1

选择本次提交的类型,按要求写入然后回车即可

配置UnoCss

安装依赖👇

pnpm add -D unocss @unocss/preset-uno unocss-applet @unocss/preset-legacy-compat pnpm add @uni-helper/unocss-preset-uni 

然后配置 unocss.config.js

// uno.config.js import {   Preset,   defineConfig,   presetAttributify,   presetIcons,   transformerDirectives,   transformerVariantGroup, } from 'unocss'  import {   presetApplet,   presetRemRpx,   transformerApplet,   transformerAttributify, } from 'unocss-applet' import { presetUni } from '@uni-helper/unocss-preset-uni'  // @see https://unocss.dev/presets/legacy-compat import presetLegacyCompat from '@unocss/preset-legacy-compat'  const isH5 = process.env?.UNI_PLATFORM === 'h5' const isMp = process.env?.UNI_PLATFORM?.startsWith('mp') ?? false  const presets = [] if (!isMp) {   /**    * you can add `presetAttributify()` here to enable unocss attributify mode prompt    * although preset is not working for applet, but will generate useless css    * 为了不生产无用的css,要过滤掉 applet    */   // 支持css class属性化,eg: `<button bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600" text="sm white">attributify Button</button>`   presets.push(presetAttributify()) } if (!isH5) {   presets.push(presetRemRpx()) } export default defineConfig({   presets: [     presetApplet({ enable: !isH5 }),     ...presets,     // 支持图标,需要搭配图标库,eg: @iconify-json/carbon, 使用 `<button class="i-carbon-sun dark:i-carbon-moon" />`     presetIcons({       scale: 1.2,       warn: true,       extraProperties: {         display: 'inline-block',         'vertical-align': 'middle',       },     }),     // 将颜色函数 (rgb()和hsl()) 从空格分隔转换为逗号分隔,更好的兼容性app端,example:     // `rgb(255 0 0)` -> `rgb(255, 0, 0)`     // `rgba(255 0 0 / 0.5)` -> `rgba(255, 0, 0, 0.5)`     presetLegacyCompat({       commaStyleColorFunction: true,     }),   ],   /**    * 自定义快捷语句    * @see https://github.com/unocss/unocss#shortcuts    */   shortcuts: [['center', 'flex justify-center items-center']],   transformers: [     // 启用 @apply 功能     transformerDirectives(),     // 启用 () 分组功能     // 支持css class组合,eg: `<div class="hover:(bg-gray-400 font-medium) font-(light mono)">测试 unocss</div>`     transformerVariantGroup(),     // Don't change the following order     transformerAttributify({       // 解决与第三方框架样式冲突问题       prefixedOnly: true,       prefix: 'fg',     }),     transformerApplet(),   ],   rules: [     [       'p-safe',       {         padding:           'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',       },     ],     ['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],     ['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],   ], }) 

src/main.js增加 import 'virtual:uno.css'

vite.config.js文件写入:

import UnoCSS from 'unocss/vite'  // 其他配置省略 plugins: [UnoCSS()], 

检测

<view class="text-area b-1px b-solid b-color-red">   <text class="title mt-4">{{ title }}</text> </view> 

结果👇
res

配置pinia 添加持久化

首先安装依赖包:

pnpm add pinia pinia-plugin-persistedstate -S 

然后写入文件:

// src/store/count.js import { piniaStore } from '@/store' export const useCountStore = defineStore('count', {   state: () => {     return {       count: 0     }   },   actions: {     increment() {       this.count++     }   },   persist: true // 配置持久化 })  export function useOutsideCountStore(){   return useCountStore(piniaStore) } 

注意下面👇这个文件对持久化的处理,否则非h5环境不能正确持久化

// src/store/index.js import { createPinia } from 'pinia' import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化  const store = createPinia() store.use(   createPersistedState({     storage: {       getItem: uni.getStorageSync,       setItem: uni.setStorageSync,     },   }), ) export default store  
// src/main.js import { createSSRApp } from 'vue' import App from './App.vue' import { setupStore } from './store' import 'virtual:uno.css' export function createApp() {   const app = createSSRApp(App)   setupStore(app)   return {     app   } } 

如有报下面错误👇
vue pinia版本冲突问题

Not able to use pinia: No matching export in “node_modules/pinia/node_modules/vue-demi/lib/index.mjs” for import “hasInjectionContext”

解决方案

将vue3 和pinia更换成下面版本重新下载对应npm包
“pinia”: “2.0.32”,
“vue”: “3.2.47”,

pnpm install vue@3.2.47 pinia@2.0.32 -S 

具体请看:https://github.com/vuejs/pinia/issues/2210

检测

<view>  <button type="primary" @click="useCountStore.count++">点击 {{ count }}</button> </view>  <script setup>   import { useOutsideCountStore } from '@/store/count'   const useCountStore = useOutsideCountStore()   const count = computed(() => useCountStore.count) </script> 

res

Axios接入配置

安装相关依赖包👇

pnpm add @uni-helper/axios-adapter axios 

以下步骤创建对应文件粘贴代码即可

  1. src》api》index.js

    /**  * 模块化方式处理 默认处理 modules文件夹下的所有js文件 内容以export default导出的文件  * @param { 模块内容集合 } moduleContext  * @returns modules集合  */ const modulesHandle = (moduleContext = {}) => {   if (!Object.keys(moduleContext).length) return   const modules = {}   Object.keys(moduleContext).forEach((v) => {     for (let key in moduleContext[v].default) {       modules[key] = moduleContext[v].default[key]     }   })   return modules }  const apis = modulesHandle(import.meta.glob('./modules/**/*.js', { eager: true })) export const useRequest = () => apis 
  2. src》api》utils.js

    // 请求状态错误 export const httpLogError = (error, msg) => {   error.message = msg   uni.showToast({     title: msg,     icon: 'error',     duration: 2000   }) }  // api请求错误 export const requestError = (response) => {   return new Promise((_, reject) => {     const { data } = response     const msg = `api请求出错 ${response.config.url}${data.message}`     uni.showToast({       title: msg,       icon: 'error',       duration: 2000     })     reject(data)   }) }  // 登录失效 export const throttleToLogin = () => {   // uni.navigateTo({ url: '/pages/login/login' }) } 
  3. src》api》service.js

    import axios from 'axios' import { createUniAppAxiosAdapter } from '@uni-helper/axios-adapter' import { httpLogError, requestError, throttleToLogin } from './utils' export function createService() {   const request = axios.create({ adapter: createUniAppAxiosAdapter() })   request.interceptors.request.use(     (request) => {       return request     },     (err) => {       return Promise.reject(err)     }   )    request.interceptors.response.use(     (response) => {       const dataAxios = response.data       // 这个状态码是和后端约定的       const { code, data } = dataAxios       console.log(dataAxios);       // 根据 code 进行判断       if (code === undefined) {         return dataAxios       } else {         // 目前和公司后端口头约定是字符串,以防万一强制转字符串         switch (`${code}`) {           // code === 200 | 2 代表没有错误           case '200':             return data           // code === 400001004 代表token 过期打回登录页           case '400001004':             throttleToLogin()             break           case '400':             // 不是正确的 code             return requestError(response)           case '401':             // 错误登录             return throttleToLogin()           default:             // 不是正确的 code             return requestError(response)         }       }     },      (error) => {       console.log(error);       const status = error.response?.status       switch (status) {         // TODO 再考虑下怎么判断是跨域问题         case undefined:         case null:           httpLogError(error, '网路错误或跨域')           break         case 400:           httpLogError(error, '请求错误')           break         case 401:           httpLogError(error, '未授权,请登录')           break         case 403:           httpLogError(error, '拒绝访问')           break         case 404:           httpLogError(error, `请求地址出错: ${error.response.config.url}`)           break         case 408:           httpLogError(error, '请求超时')           break         case 500:           httpLogError(error, '服务器内部错误')           break         case 501:           httpLogError(error, '服务未实现')           break         case 502:           httpLogError(error, '网关错误')           break         case 503:           httpLogError(error, '服务不可用')           break         case 504:           httpLogError(error, '网关超时')           break         case 505:           httpLogError(error, 'HTTP版本不受支持')           break         default:           httpLogError(error, '请求错误')           break       }       return Promise.reject(error)     }   )   return request }  export const service = createService() 
  4. src》api》request.js

    import { service } from './service' function createRequest(service) {   function request(config) {   	// config 自定义配置     // axios默认配置     const configDefault = {       baseURL: import.meta.env.VITE_APP_API_BASEURL, // 所有通过此配置的基础地址 在.env文件配置       timeout: 15000, // 请求超时时间       responseType: 'json', // 响应类型       headers: {         // 请求头配置...       }     }     const requestConfig = Object.assign(configDefault, config)     return service(requestConfig)   }   return request }  export const request = createRequest(service) 
  5. src》modules》demo.js

    /**  * 命名=》API_xxx_methodName  */  import { request } from '@/api/request.js' export default {   API_DEMO_POST(data = {}) {     return request({       url: 'demo/mock',       method: 'post',       data     })   },    API_DEMO_GET(params = {}) {     return request({       url: '/demo/get',       method: 'get',       params     })   } }  

在vite.config.js中配置自动化导入

在原有的配置中添加

import { defineConfig } from 'vite' import AutoImport from 'unplugin-auto-import/vite' // ... // https://vitejs.dev/config/ export default defineConfig({   plugins: [   	//...     AutoImport({       imports: [        //...         {           '@/api': ['useRequest']         }       ],       //...     }),     //...   ],   //... })  

检测

<template>   <view class="content">     <view><button type="primary" @click="getList">点击请求数据</button></view>   </view>   <view class="p-2">     <view       class="my-15rpx b-b-solid b-b-1rpx b-b-color-[#cccccc]"       v-for="item in list"       :key="item.id"     >       <text class="text-18rpx font-bold">{{ item.title }}</text>       <view class="mt-4 text-12rpx color-[#9999]">{{ item.body }}</view>     </view>   </view> </template>  <script setup>   const { API_DEMO_GET } = useRequest()   const list = ref([])      const getList = () => {     uni.showLoading({       title: '加载中...'     })     API_DEMO_GET()       .then((res) => {         console.log(res)         list.value = res         uni.hideLoading()       })       .catch(() => {         uni.hideLoading()       })   } </script> 

api

图标库

  1. 前往图标库查找相关图标iconify.design

  2. 安装图标所在的图标库

    # 格式:pnpm add @iconify-json/[the-collection-you-want] -D  # 例如 pnpm add @iconify-json/ep -D 

    安装完后可以使用当前库下的所有图标
    https://icon-sets.iconify.design/ep/

  3. 项目中使用
    使用unocss的方式https://iconify.design/docs/usage/css/unocss/

    <text class="i-ep-apple w-40rpx h-40rpx color-red"></text> 

UI 方案

自行使用相关ui接入项目
uni-ui
https://www.uvui.cn/
https://uviewui.com/

模板

拉取后 开箱即用
模板地址👉 https://github.com/gitboyzcf/uni-preset-vue3-vite

敬请期待

后续还会有新功能接入当前模板,敬请期待…






到这里就结束了,后续还会更新 前端 系列相关,还请持续关注!
感谢阅读,若有错误可以在下方评论区留言哦!!!

111


推荐文章👇

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!