新城星玛百货 發表於 2024-1-8 11:29:00

uni-app+vue3+ts项目搭建完整流程

<blockquote>
<p>项目代码同步更新至码云 uni-vue3-ts-template</p>
</blockquote>
<h2 id="开发前准备">开发前准备</h2>
<p>利用 <code>uni-app</code> 开发,有两种方法:</p>
<ol>
<li>通过 <code>HBuilderX</code> 创建(需安装 <code>HBuilderX</code> 编辑器)</li>
<li>通过命令行创建(需安装 <code>NodeJS</code> 环境),推荐使用 <code>vscode</code> 编辑器</li>
</ol>
<p>这里我们使用第2种方法,这两种方法官方都有详细介绍 点击查看官方文档</p>
<h3 id="vscode-安装插件"><code>vscode</code> 安装插件</h3>
<ol>
<li>安装 Vue3 插件,点击查看官方文档</li>
</ol>
<ul>
<li>安装 <code>Vue Language Features (Volar)</code> :Vue3 语法提示插件</li>
<li>安装 <code>TypeScript Vue Plugin (Volar)</code> :Vue3+TS 插件</li>
<li><strong>工作区禁用</strong> Vue2 的 Vetur 插件(Vue3 插件和 Vue2 冲突)</li>
<li><strong>工作区禁用</strong> @builtin typescript 插件(禁用后开启 Vue3 的 TS 托管模式)</li>
</ul>
<ol start="2">
<li>安装 uni-app 开发插件</li>
</ol>
<ul>
<li><code>uni-create-view</code> :快速创建 uni-app 页面</li>
<li><code>uni-helper</code>(插件套装,安装一个后会有多个插件) :代码提示</li>
<li><code>uniapp小程序扩展</code> :鼠标悬停查文档</li>
</ul>
<h3 id="uni-create-view-插件使用"><code>uni-create-view</code> 插件使用</h3>
<p><code>uni-create-view</code> 安装后,需要修改配置,进入 <code>文件</code> -&gt; <code>首选项</code> -&gt; <code>设置</code>,按以下选项修改即可<br>
<img src="https://img2024.cnblogs.com/blog/1086129/202401/1086129-20240108112010492-1130455097.png" alt="image" loading="lazy"></p>
<p><code>uni-create-view</code> 使用方法:<br>
在 <code>src/pages</code> 下右键,选择 <code>新建uni-app页面</code>,弹出输入框,输入 <code>文件夹名称 页面名称</code>,然后回车<br>
<img src="https://img2024.cnblogs.com/blog/1086129/202401/1086129-20240108112021263-182547330.png" alt="image" loading="lazy"></p>
<p>生成如下目录文件<br>
<img src="https://img2024.cnblogs.com/blog/1086129/202401/1086129-20240108112028874-1862871325.png" alt="image" loading="lazy"></p>
<p>并且在 <code>src/pages.json</code> 目录下,已将新界面配置进去<br>
<img src="https://img2024.cnblogs.com/blog/1086129/202401/1086129-20240108112036371-60894021.png" alt="image" loading="lazy"></p>
<h3 id="vscode-项目配置"><code>vscode</code> 项目配置</h3>
<p><strong>项目生成后,在项目的根目录进行</strong><br>
新建 <code>.vscode</code> 文件夹,并创建 <code>settings.json</code> 文件:</p>
<pre><code class="language-json">{
// 在保存时格式化文件
"editor.formatOnSave": true,
// 文件格式化配置
"": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
},
// 配置语言的文件关联
"files.associations": {
    "pages.json": "jsonc", // pages.json 可以写注释
    "manifest.json": "jsonc" // manifest.json 可以写注释
}
}
</code></pre>
<p>同样,在 <code>.vscode</code> 文件夹内,创建 vue3 模板文件,命名为 <code>vue3-uniapp.code-snippets</code>:</p>
<pre><code class="language-txt">{
"vue3+uniapp快速生成模板": {
    "scope": "vue",
    "prefix": "Vue3",
    "body": [
      "&lt;script setup lang=\"ts\"&gt;",
      "$2",
      "&lt;/script&gt;\n",
      "&lt;template&gt;",
      "\t&lt;view class=\"\"&gt;test&lt;/view&gt;",
      "&lt;/template&gt;\n",
      "&lt;style lang=\"scss\"&gt;&lt;/style&gt;",
      "$2"
    ],
    "description": "vue3+uniapp快速生成模板"
}
}
</code></pre>
<p>然后,在空白vue文件中,输入vue3,选择此模板,即可快速生成代码<br>
<img src="https://img2024.cnblogs.com/blog/1086129/202401/1086129-20240108112102666-1986861080.png" alt="image" loading="lazy"></p>
<h2 id="项目初始化">项目初始化</h2>
<h3 id="项目创建">项目创建</h3>
<p>拉取 <code>uni-app</code> 官方项目基础架构代码 https://uniapp.dcloud.net.cn/quickstart-cli.html</p>
<pre><code class="language-bash">npx degit dcloudio/uni-preset-vue#vite-ts uni-vue3-ts-shop

cd uni-vue3-ts-shop
</code></pre>
<p>或者直接直接克隆国内 gitee 地址,然后修改项目名称,并进入项目根目标</p>
<pre><code class="language-bash">git clone -b vite-ts https://gitee.com/dcloud/uni-preset-vue.git
</code></pre>
<h3 id="安装ts扩展">安装ts扩展</h3>
<p>主要是为了增加 <code>uni-app</code> 、<code>微信小程序</code>、<code>nodejs</code> 对ts的支持</p>
<pre><code class="language-bash">npm i -D @uni-helper/uni-app-types miniprogram-api-typings @types/node
</code></pre>
<p>修改 <code>tsconfig.json</code></p>
<pre><code class="language-json">{
"compilerOptions": {
    "ignoreDeprecations": "5.0",
    "allowJs": true,
    },
    "types": ["@dcloudio/types", "miniprogram-api-typings", "@uni-helper/uni-app-types"]
},
"vueCompilerOptions": {
    // experimentalRuntimeMode 已废弃,现调整为 nativeTags,请升级 Volar 插件至最新版本
    "nativeTags": ["block", "component", "template", "slot"]
}
}
</code></pre>
<h2 id="配置环境变量">配置环境变量</h2>
<p>点我查看官方文档</p>
<h3 id="新增env文件">新增env文件</h3>
<p>根目录下新建 <code>.env</code> 文件</p>
<pre><code class="language-txt">VITE_HTTP = https://mock.mengxuegu.com/mock/6598258423a3c638568501db/uniapp_template
</code></pre>
<h3 id="使用">使用</h3>
<p>获取环境变量</p>
<pre><code class="language-txt">process.env.NODE_ENV          // 应用运行的模式,比如vite.config.ts里
import.meta.env.VITE_HTTP   // src下的vue文件或其他ts文件里
</code></pre>
<h3 id="开启-sourcemap">开启 <code>sourcemap</code></h3>
<p>点我查看官方文档<br>
修改 <code>vite.config.ts</code> 文件:</p>
<pre><code class="language-ts">export default defineConfig({
build: {
    // 开发阶段启用源码映射:https://uniapp.dcloud.net.cn/tutorial/migration-to-vue3.html#需主动开启-sourcemap
    sourcemap: process.env.NODE_ENV === 'development',
},
plugins: ,
})
</code></pre>
<h2 id="统一代码规范">统一代码规范</h2>
<h3 id="安装-prettier">安装 <code>prettier</code></h3>
<pre><code class="language-shell">npm i -D prettier
</code></pre>
<p>根目录下新建 <code>.prettierrc.json</code></p>
<pre><code class="language-json">{
"singleQuote": true,
"semi": false,
"printWidth": 120,
"trailingComma": "all",
"endOfLine": "auto"
}
</code></pre>
<h3 id="安装-eslint">安装 <code>eslint</code></h3>
<pre><code class="language-shell">npm i -D eslint eslint-plugin-vue @rushstack/eslint-patch @vue/eslint-config-typescript@vue/eslint-config-prettier
</code></pre>
<p>根目录下新建 <code>.eslintrc.js</code></p>
<pre><code class="language-js">/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

module.exports = {
root: true,
extends: [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/eslint-config-typescript',
    '@vue/eslint-config-prettier',
],
// 小程序全局变量
globals: {
    uni: true,
    wx: true,
    WechatMiniprogram: true,
    getCurrentPages: true,
    getApp: true,
    UniApp: true,
    UniHelper: true,
    App: true,
    Page: true,
    Component: true,
    AnyObject: true,
},
parserOptions: {
    ecmaVersion: 'latest',
},
rules: {
    'prettier/prettier': [
      'warn',
      {
      singleQuote: true,
      semi: false,
      printWidth: 120,
      trailingComma: 'all',
      endOfLine: 'auto',
      },
    ],
    'vue/multi-word-component-names': ['off'],
    'vue/no-setup-props-destructure': ['off'],
    'vue/no-deprecated-html-element-is': ['off'],
    '@typescript-eslint/no-unused-vars': ['off'],
},
}
</code></pre>
<p><code>package.json</code> 中新增命令 <code>lint</code></p>
<pre><code class="language-json">{
"scripts": {
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
}
}
</code></pre>
<p>然后运行 <code>npm run lint</code>,将项目内的文件格式化为 <code>eslint</code> 规定的格式(这个命令可随时运行,以便有新页面/插件加入时,解决代码风格的问题)</p>
<h2 id="规范git提交">规范git提交</h2>
<p><strong><code>非必需,适合多人开发</code></strong></p>
<h3 id="安装-husky">安装 <code>husky</code></h3>
<p>安装并初始化 <code>husky</code></p>
<pre><code class="language-bash">npx husky-init
</code></pre>
<p>如果是首次安装,会有以下提示,输入 <code>y</code> 回车即可</p>
<pre><code class="language-bash">Need to install the following packages:
husky-init@8.0.0
Ok to proceed? (y)
</code></pre>
<p>安装完成后,会多出 <code>.husky</code> 文件夹和 <code>pre-commit</code> 文件</p>
<h3 id="安装-lint-staged">安装 <code>lint-staged</code></h3>
<pre><code class="language-bash">npm i -D lint-staged
</code></pre>
<p>安装完成后配置 <code>package.json</code></p>
<pre><code class="language-json">{
"script": {
    // ... 省略 ...
    "lint-staged": "lint-staged"
},
"lint-staged": {
    "*.{vue,ts,js}": ["eslint --fix"]
}
}
</code></pre>
<p>修改 <code>pre-commit</code> 文件</p>
<pre><code class="language-diff">- npm test
+ npm run lint-staged
</code></pre>
<h3 id="提交规范">提交规范</h3>
<p>至此,已完成 <code>husky</code> + <code>lint-staged</code> 的配置。之后,每次提交代码,在提交信息前都要加入以下提交类型之一,譬如:<code>feat: 首页新增轮播图</code></p>
<table>
<thead>
<tr>
<th>提交字段</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>feat:</td>
<td>增加新功能</td>
</tr>
<tr>
<td>fix:</td>
<td>修复问题/BUG</td>
</tr>
<tr>
<td>style:</td>
<td>代码风格相关无影响运行结果的</td>
</tr>
<tr>
<td>perf:</td>
<td>优化/性能提升</td>
</tr>
<tr>
<td>refactor:</td>
<td>重构</td>
</tr>
<tr>
<td>revert:</td>
<td>撤销修改</td>
</tr>
<tr>
<td>test:</td>
<td>测试相关</td>
</tr>
<tr>
<td>docs:</td>
<td>文档/注释</td>
</tr>
<tr>
<td>chore:</td>
<td>依赖更新/脚手架配置修改等</td>
</tr>
<tr>
<td>workflow:</td>
<td>工作流改进</td>
</tr>
<tr>
<td>ci:</td>
<td>持续集成</td>
</tr>
<tr>
<td>types:</td>
<td>类型定义文件更改</td>
</tr>
<tr>
<td>wip:</td>
<td>开发中</td>
</tr>
</tbody>
</table>
<h2 id="安装-uni-ui-组件库">安装 <code>uni-ui</code> 组件库</h2>
<p><strong><code>非必需,也可使用其他组件库</code></strong></p>
<p><code>uni-ui</code> 是DCloud提供的一个跨端ui库,它是基于vue组件的、flex布局的、无dom的跨全端ui框架。查看官方文档</p>
<h3 id="安装-uni-ui-及相关插件">安装 <code>uni-ui</code> 及相关插件</h3>
<p><code>sass sass-loader</code> 是 <code>uni-ui</code> 的依赖库,<code>@uni-helper/uni-ui-types</code> 是类型声明文件</p>
<pre><code class="language-bash">npm i-D sass sass-loader
npm i @dcloudio/uni-ui
npm i -D @uni-helper/uni-ui-types
</code></pre>
<h3 id="修改配置">修改配置</h3>
<p>修改 <code>tsconfig.json</code>,配置类型声明文件</p>
<pre><code class="language-json">{
"compilerOptions": {
    "types": ["@dcloudio/types", "miniprogram-api-typings", "@uni-helper/uni-app-types", "@uni-helper/uni-ui-types"]
}
}
</code></pre>
<p>修改 <code>src/pages.json</code>,配置自动导入组件</p>
<pre><code class="language-json">{
"easycom": {
    "autoscan": true,
    "custom": {
      // uni-ui 规则如下配置
      "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
    }
},
"pages": [
    // ……
]
}
</code></pre>
<h2 id="安装配置-pina">安装配置 <code>pina</code></h2>
<h3 id="安装">安装</h3>
<p><code>pinia-plugin-persistedstate</code> 是持久化 <code>pina</code> 插件</p>
<pre><code class="language-bash">npm i pinia pinia-plugin-persistedstate
</code></pre>
<h3 id="使用-1">使用</h3>
<p>在 <code>src</code> 下新增以下目录和文件</p>
<pre><code class="language-txt">src
├─stores
│├─modules
││ └─user.ts
|└─index.ts
</code></pre>
<p><code>user.ts</code></p>

<pre><code class="language-ts">import { defineStore } from 'pinia'
import { ref } from 'vue'

// 定义 Store
export const useMemberStore = defineStore(
'user',
() =&gt; {
    // 用户信息
    const userInfo = ref&lt;any&gt;()

    // 保存用户信息,登录时使用
    const setUserInfo = (val: any) =&gt; {
      userInfo.value = val
    }

    // 清理用户信息,退出时使用
    const clearUserInfo = () =&gt; {
      userInfo.value = undefined
    }

    return {
      userInfo,
      setUserInfo,
      clearUserInfo,
    }
},
// TODO: 持久化
{
    persist: true,
},
)
</code></pre>

<p><code>index.ts</code></p>

<pre><code class="language-ts">import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'

// 创建 pinia 实例
const pinia = createPinia()
// 使用持久化存储插件
pinia.use(persist)

// 默认导出,给 main.ts 使用
export default pinia

// 模块统一导出
export * from './modules/user'
</code></pre>

<p><code>main.ts</code></p>

<pre><code class="language-ts">import { createSSRApp } from 'vue'
import pinia from './stores'

import App from './App.vue'
export function createApp() {
const app = createSSRApp(App)

app.use(pinia)
return {
    app,
}
}

</code></pre>

<h3 id="持久化">持久化</h3>
<p>插件默认使用 <code>localStorage</code> 实现持久化,小程序端不兼容,需要替换持久化 API。<br>
修改 <code>stores/modules/user.ts</code></p>
<pre><code class="language-ts">export const useUserStore = defineStore(
'member',
() =&gt; {
    //…省略
},
{
    // 配置持久化
    persist: {
      // 调整为兼容多端的API
      storage: {
      setItem(key, value) {
          uni.setStorageSync(key, value)
      },
      getItem(key) {
          return uni.getStorageSync(key)
      },
      },
    },
},
)
</code></pre>
<h2 id="封装请求">封装请求</h2>
<h3 id="uniapp-拦截器"><code>uniapp</code> 拦截器</h3>
<p><code>uni.addInterceptor</code> 的使用参考 官方文档</p>
<p><code>src</code> 目录下新建 <code>utils</code> 文件夹,并新建 <code>http.ts</code> 文件</p>
<pre><code class="language-ts">import { useUserStore } from '@/stores'

const baseURL = import.meta.env.VITE_HTTP

// 拦截器配置
const httpInterceptor = {
// 拦截前触发
invoke(options: UniApp.RequestOptions) {
    // 非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      options.url = baseURL + options.url
    }
    options.timeout = 10000
    // 添加请求头标识
    options.header = {
      'request-client': 'wechart-app',
      ...options.header,
    }
    // 添加 token 请求头标识
    const memberStore = useUserStore()
    const token = memberStore.userInfo?.token
    if (token) {
      options.header.Authorization = token
    }
},
}

// 拦截 request 请求
uni.addInterceptor('request', httpInterceptor)
// 拦截 uploadFile 文件上传
uni.addInterceptor('uploadFile', httpInterceptor)
</code></pre>
<p>由于 <code>uni-app</code> 的响应拦截器对类型支持并不友好,所以我们自行封装响应拦截器,同一个文件,继续</p>
<pre><code class="language-ts">/**
* 请求函数
* @paramUniApp.RequestOptions
* @returns Promise
*/
// Data类型根据后台返回数据去定义
type Data&lt;T&gt; = {
code: string
msg: string
result: T
}
export const http = &lt;T&gt;(options: UniApp.RequestOptions) =&gt; {
return new Promise&lt;Data&lt;T&gt;&gt;((resolve, reject) =&gt; {
    uni.request({
      ...options,
      // 响应成功
      success(res) {
      if (res.statusCode &gt;= 200 &amp;&amp; res.statusCode &lt; 300) {
          resolve(res.data as Data&lt;T&gt;)
      } else if (res.statusCode === 401) {
          // 401错误-&gt; 清理用户信息,跳转到登录页
          const userStore = useUserStore()
          userStore.clearUserInfo()
          uni.navigateTo({ url: '/pages/login' })
          reject(res)
      } else {
          // 其他错误 -&gt; 根据后端错误信息轻提示
          uni.showToast({
            icon: 'none',
            title: (res.data as Data&lt;T&gt;).msg || '请求错误',
          })
          reject(res)
      }
      },
      // 响应失败
      fail(err) {
      uni.showToast({
          icon: 'none',
          title: '网络错误,换个网络试试',
      })
      reject(err)
      },
    })
})
}
</code></pre>
<h3 id="使用-2">使用</h3>
<p>为了统一API文件,我们在 src 目录下新建 <code>api</code> 文件夹,并新建 <code>user.ts</code></p>
<pre><code class="language-ts">import { http } from '@/utils/http'

export const getUserInfoAPI = (data: any) =&gt; {
return http({
    url: '/user/info',
    method: 'POST',
    data,
})
}
</code></pre>
<p>然后在需要的地方调用,比如在 <code>page/my/index.vue</code> 里调用:</p>
<pre><code class="language-html">&lt;script setup lang="ts"&gt;
import { useUserStore } from '@/stores'
import { getUserInfoAPI } from '@/api/user'

const userStore = useUserStore()

const getUserInfo = async () =&gt; {
const res = await getUserInfoAPI({ id: 'weizwz' })
console.log(res)
const { result } = res
userStore.setUserInfo(result)
}
&lt;/script&gt;

&lt;template&gt;
&lt;view class=""&gt;
    &lt;view&gt;用户信息: {{ userStore.userInfo }}&lt;/view&gt;
    &lt;button
      @tap="
      userStore.setUserInfo({
          userName: 'weizwz',
      })
      "
      size="mini"
      plain
      type="primary"
    &gt;
      保存用户信息
    &lt;/button&gt;
    &lt;button @tap="userStore.clearUserInfo()" size="mini" plain type="primary"&gt;清理用户信息&lt;/button&gt;
    &lt;button @tap="getUserInfo()" size="mini" plain type="primary"&gt;发送请求&lt;/button&gt;
&lt;/view&gt;
&lt;/template&gt;

&lt;style lang="scss"&gt;&lt;/style&gt;
</code></pre>
<p>效果如下,可以看到已经调用成功:<br>
<img src="https://img2024.cnblogs.com/blog/1086129/202401/1086129-20240108112431800-543536708.png" alt="image" loading="lazy"></p>
<p><strong>如果调用被拦截的话,请检查微信小程序里的项目设置,然后选中 <code>不检验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书</code> 选项</strong></p>
<h2 id="注意事项">注意事项</h2>
<h3 id="开发区别">开发区别</h3>
<p><code>uni-app</code> 项目每个页面是一个 <code>.vue</code> 文件,数据绑定及事件处理同 <code>Vue.js</code> 规范:</p>
<ol>
<li>vue文件中的 <code>div</code> 标签需替换为 <code>view</code> 标签</li>
<li>属性绑定 <code>src="{ { url }}"</code> 升级成 <code>:src="url"</code></li>
<li>事件绑定 <code>bindtap="eventName"</code> 升级成 <code>@tap="eventName"</code>,<strong>支持()传参</strong></li>
<li>支持 Vue 常用<strong>指令</strong> <code>v-for</code>、<code>v-if</code>、<code>v-show</code>、<code>v-model</code> 等</li>
</ol>
<h3 id="其他补充">其他补充</h3>
<ol>
<li>调用接口能力,<strong>建议前缀</strong> <code>wx</code> 替换为 <code>uni</code> ,养成好习惯,<strong>支持多端开发</strong>。</li>
<li><code>&lt;style&gt;</code> 页面样式不需要写 <code>scoped</code>,小程序是多页面应用,<strong>页面样式自动隔离</strong>。</li>
<li><strong>生命周期分三部分</strong>:应用生命周期(小程序),页面生命周期(小程序),组件生命周期(Vue)</li>
<li>其他参考 uniapp-vue语法 官方文档</li>
</ol>
<p><strong>文章部分内容来自 小兔鲜儿项目 ,本文主要是在此基础上补全了完整创建此项目的流程和所需的依赖</strong></p>


</div>
<div id="MySignature" role="contentinfo">
    <div id="copyrightWeizwz">
    <p><span class="post-copyright-meta">文章作者:</span><span class="post-copyright-info">唯知为之</span></p>
    <p><span class="post-copyright-meta">文章出处:</span><span class="post-copyright-info">https://www.cnblogs.com/weizwz/p/17952042</span></p>
    <p><span class="post-copyright-meta">版权声明:</span><span class="post-copyright-info">本博客所有文章除特别声明外,均采用 「CC BY-NC-SA 4.0 DEED」 国际许可协议,转载请注明出处!</span></p>
    <p>内容粗浅,如有错误,欢迎大佬批评指正</p>
</div><br><br>
来源:https://www.cnblogs.com/weizwz/p/17952042
頁: [1]
查看完整版本: uni-app+vue3+ts项目搭建完整流程