从0到1构建开源 vue-uniapp-template:使用 UniApp + Vue3 + TypeScript 和 VSCoe、CLI 开发跨平台移动端脚手架
<blockquote><p>🚀 作者主页: 有来技术<br>
🔥 开源项目: youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template<br>
🌺 仓库主页: GitCode︱ Gitee︱ Github<br>
💖 欢迎点赞 👍 收藏 ⭐评论 📝 如有错误敬请纠正!</p>
</blockquote>
<p>开局一张图,先看一下初期登录的效果图,内容和UI正在升级中...<br>
<img src="https://i-blog.csdnimg.cn/direct/2c0eaeaa15134517a77b7a1689388537.gif#pic_center" alt="在这里插入图片描述" loading="lazy"></p>
<h2 id="前言">前言</h2>
<p>本文将带你从零开始,使用 VSCode 和 vue-cli 构建一个基于 Vue3 和 TypeScript 的 UniApp 跨平台移动端开源脚手架模板。通过详尽的步骤讲解,从环境配置到项目部署,全面覆盖移动端开发的关键环节。</p>
<h2 id="环境准备">环境准备</h2>
<p>vue-uniapp-template是一个通过 <code>vue-cli </code> 构建的跨移动端脚手架模板,结合了 <code>uniapp</code>、<code>vue3 </code>和 <code>typescript</code>。在开始之前,需要准备以下环境。如果环境准备OK,请忽略本节。</p>
<h3 id="安装-node">安装 Node</h3>
<blockquote>
<p><code>Node.js</code> 是运行 JavaScript 代码的环境,也是 <code>npm</code> 包管理器的依赖。</p>
</blockquote>
<p>打开 Node.js 官方下载页面,根据你的操作系统选择合适的版本进行下载,<strong>推荐安装 LTS 版本</strong>,这是长期支持版本,适合开发环境,比如这里选择 <code>v20.18.0(LTS) </code> 版本。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024220508312.png" alt="" loading="lazy"></p>
<p>下载之后,双击安装包根据提示安装,通过以下命令检查是否成功安装:</p>
<pre><code class="language-bash">node -v
</code></pre>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024224347056.png" alt="" loading="lazy"></p>
<h3 id="安装-vscode">安装 VSCode</h3>
<blockquote>
<p><code>VSCode</code> 是一款非常流行的代码编辑器,特别适合前端开发。</p>
</blockquote>
<p>访问 Visual Studio Code 官方网站 ,根据你的操作系统下载相应版本的 <code>VSCode</code> ,下载完成后,双击安装程序并按照提示完成安装。</p>
<h3 id="安装-vue-cli">安装 vue-cli</h3>
<blockquote>
<p><code>Vue CLI</code> 是 Vue.js 的命令行工具,能够快速创建、开发、构建 Vue.js 项目。</p>
</blockquote>
<p>打开终端或命令提示符, 使用 <code>npm</code> 全局安装 <code>Vue CLI</code>:</p>
<pre><code>npm install -g @vue/cli
</code></pre>
<p>安装完成后,检查 <code>Vue CLI</code> 是否安装成功:</p>
<pre><code class="language-bash">vue --version
</code></pre>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024213719105.png" alt="" loading="lazy"></p>
<h2 id="创建项目">创建项目</h2>
<h3 id="初始化项目">初始化项目</h3>
<p>按照 uni-app 官方文档 的步骤,通过 <code>vue-cli</code> 创建 <code>uni-app</code> + <code>vue</code> + <code>typescript</code> 脚手架:</p>
<pre><code class="language-bash">npx degit dcloudio/uni-preset-vue#vite-ts vue-uniapp-template
</code></pre>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240917083025350.png" alt="" loading="lazy"></p>
<p>如果使用命令创建失败,可以通过 Gitee 下载 ZIP 包:vite-ts 分支。</p>
<h3 id="配置编译器">配置编译器</h3>
<p>默认生成的 <code>TypeScript </code> 编译器配置文件 <code>tsconfig.json</code> 中继承的 <code>@vue/tsconfig/tsconfig.json</code> 文件不存在。因此,你需要移除此继承配置并添加相应的编译设置。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240923143412332.png" alt="" loading="lazy"></p>
<p>根据 TypeScript 官方配置文档,调整后的完整配置如下:</p>
<pre><code class="language-json">{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "node",
"target": "esnext",
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"lib": ["esnext", "dom"],
"types": ["@dcloudio/types"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules", "dist"]
}
</code></pre>
<ul>
<li><code>"module": "esnext"</code>: 指定模块系统为 <code>ESNext</code>,即最新的 ECMAScript 模块系统,支持 <code>import.meta</code> 和其他最新的特性。</li>
<li><code>"moduleResolution": "node"</code>: 模块解析策略,通常设置为 <code>Node</code> 以支持 Node.js 风格的模块解析。</li>
<li><code>"target": "esnext"</code>: 将目标 JavaScript 版本设置为 <code>ESNext</code>,编译输出现代浏览器能够支持的最新特性代码。</li>
<li><code>"allowJs": true</code>: 允许 TypeScript 编译器处理 <code>.js</code> 文件,混合使用 TypeScript 和 JavaScript 文件。</li>
<li><code>"skipLibCheck": true</code>: 跳过库文件的类型检查,提升编译速度。</li>
<li><code>"strict": true</code> : 启用所有严格类型检查选项。</li>
</ul>
<h3 id="启动项目">启动项目</h3>
<p>创建完成后,使用 <code>VSCode</code> 打开项目并启动:</p>
<pre><code class="language-bash"># 安装依赖
pnpm install
# 启动项目
pnpm run dev:h5
</code></pre>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240917085244593.png" alt="" loading="lazy"></p>
<p>项目启动后,访问 http://localhost:5173 预览效果:</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240917085841605.png" alt="" loading="lazy"></p>
<h2 id="代码规范配置">代码规范配置</h2>
<p>为了保证项目代码的规范性和一致性,可以为项目配置 <code>ESLint</code>、<code>Stylelint</code>、<code>Prettier</code> 以及 <code>Husky</code>,从而确保代码质量和开发流程的一致性。</p>
<h3 id="集成-eslint">集成 ESLint</h3>
<p><code>ESLint</code> 是一款 JavaScript 和 TypeScript 的代码规范工具,能够帮助开发团队保持代码风格一致并减少常见错误。</p>
<p><strong>ESLint 中文网</strong>:https://eslint.nodejs.cn/</p>
<h4 id="安装插件">安装插件</h4>
<p>VSCode 插件市场搜索 ESLint 插件并安装</p>
<p><img src="https://s2.loli.net/2023/04/17/C3mV14KT2vHo6ZA.png" alt="" loading="lazy"></p>
<h4 id="配置-eslint">配置 ESLint</h4>
<p>通过以下命令快速生成 ESLint 配置文件:</p>
<pre><code class="language-bash">npx eslint --init
</code></pre>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240917202516052.png" alt="" loading="lazy"></p>
<p>执行该命令后,ESLint 会通过交互式问题的方式,帮助生成配置文件。针对 9.x 版本,默认会生成基于 Flat Config 格式的 <code>eslint.config.mjs</code> 文件,与之前的 <code>.eslintrc</code> 格式有所不同。</p>
<p>默认生成的 <code>eslint.config.mjs</code> 文件如下所示:</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240917221647303.png" alt="" loading="lazy"></p>
<p>在此基础上,可以根据项目的需求进行一些定制化配置,例如添加忽略规则或自定义的特殊规则。</p>
<pre><code class="language-js">import globals from "globals"; // 全局变量配置
import pluginJs from "@eslint/js"; // JavaScript 的推荐配置
import tseslint from "typescript-eslint"; // TypeScript 配置
import pluginVue from "eslint-plugin-vue"; // Vue 配置
export default [
{files: ["**/*.{js,mjs,cjs,ts,vue}"]}, // 校验的文件类型
{languageOptions: { globals: {...globals.browser , ...globals.node} }}, // 浏览器/Node环境全局变量
pluginJs.configs.recommended, // JavaScript 推荐配置
...tseslint.configs.recommended, // TypeScript 推荐配置
...pluginVue.configs["flat/essential"], // Vue 推荐配置
{ files: ["**/*.vue"], languageOptions: { parserOptions: { parser: tseslint.parser } } }, // 对 .vue 文件使用 TypeScript 解析器
// 添加忽略的文件或目录
{
ignores: [
"/dist",
"/public",
"/node_modules",
"**/*.min.js",
"**/*.config.mjs",
"**/*.tsbuildinfo",
"/src/manifest.json",
]
},
// 自定义规则
{
rules: {
quotes: ["error", "double"], // 强制使用双引号
"quote-props": ["error", "always"], // 强制对象的属性名使用引号
semi: ["error", "always"], // 要求使用分号
indent: ["error", 2], // 使用两个空格进行缩进
"no-multiple-empty-lines": ["error", { max: 1 }], // 不允许多个空行
"no-trailing-spaces": "error", // 不允许行尾有空格
// TypeScript 规则
"@typescript-eslint/no-explicit-any": "off", // 禁用 no-explicit-any 规则,允许使用 any 类型
"@typescript-eslint/explicit-function-return-type": "off", // 不强制要求函数必须明确返回类型
"@typescript-eslint/no-empty-interface": "off", // 禁用 no-empty-interface 规则,允许空接口声明
"@typescript-eslint/no-empty-object-type": "off", // 允许空对象类型
// Vue 规则
"vue/multi-word-component-names": "off", // 关闭多单词组件名称的限制
"vue/html-indent": ["error", 2], // Vue 模板中的 HTML 缩进使用两个空格
"vue/no-v-html": "off", // 允许使用 v-html (根据实际项目需要)
},
},
];
</code></pre>
<h4 id="添加-eslint-脚本">添加 ESLint 脚本</h4>
<p>为了方便使用 ESLint,可以在 <code>package.json</code> 中添加 <code>lint</code> 脚本命令:</p>
<pre><code class="language-json">{
"scripts": {
"lint:eslint": "eslint --fix ./src"
}
}
</code></pre>
<p>此脚本会自动修复符合 ESLint 规则的代码问题,并输出检查结果。</p>
<h4 id="测试效果">测试效果</h4>
<p>在 <code>App.vue</code> 文件中声明一个未使用的变量,并运行 <code>pnpm run lint:eslint</code>,可以看到 ESLint 提示该变量未使用。如下图所示:</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240917235501951.png" alt="" loading="lazy"></p>
<h4 id="推荐配置">推荐配置</h4>
<p>安装 Vue 文件解析器 <code>vue-eslint-parser</code>:</p>
<pre><code class="language-bash">pnpm add -D vue-eslint-parser
</code></pre>
<p>针对不同文件配置插件和解析器:</p>
<pre><code class="language-javascript">// eslint.config.mjs
import globals from "globals";
import js from "@eslint/js";
// ESLint 核心插件
import pluginVue from "eslint-plugin-vue";
import pluginTypeScript from "@typescript-eslint/eslint-plugin";
// Prettier 插件及配置
import configPrettier from "eslint-config-prettier";
import pluginPrettier from "eslint-plugin-prettier";
// 解析器
import * as parserVue from "vue-eslint-parser";
import * as parserTypeScript from "@typescript-eslint/parser";
// 定义 ESLint 配置
export default [
// 通用 JavaScript/TypeScript 配置
{
...js.configs.recommended,
ignores: [
"/dist",
"/public",
"/node_modules",
"**/*.min.js",
"**/*.config.mjs",
"**/*.tsbuildinfo",
"/src/manifest.json",
],
languageOptions: {
globals: {
...globals.browser, // 浏览器变量 (window, document 等)
...globals.node, // Node.js 变量 (process, require 等)
},
},
plugins: {
prettier: pluginPrettier,
},
rules: {
...configPrettier.rules,
...pluginPrettier.configs.recommended.rules,
"no-debug": "off", // 允许使用 debugger
"prettier/prettier": [
"error",
{
endOfLine: "auto", // 解决换行符冲突
},
],
},
},
// TypeScript 配置
{
files: ["**/*.?()ts"],
languageOptions: {
parser: parserTypeScript,
parserOptions: {
sourceType: "module",
},
},
plugins: {
"@typescript-eslint": pluginTypeScript,
},
rules: {
...pluginTypeScript.configs.recommended.rules,
"@typescript-eslint/no-explicit-any": "off", // 允许使用 any
"@typescript-eslint/no-empty-function": "off", // 允许空函数
"@typescript-eslint/no-empty-object-type": "off", // 允许空对象类型
"@typescript-eslint/consistent-type-imports": [
"error",
{ disallowTypeAnnotations: false, fixStyle: "inline-type-imports" },
], // 统一类型导入风格
},
},
// TypeScript 声明文件的特殊配置
{
files: ["**/*.d.ts"],
rules: {
"eslint-comments/no-unlimited-disable": "off", // 关闭 eslint 注释相关规则
"unused-imports/no-unused-vars": "off", // 忽略未使用的导入
},
},
// JavaScript (commonjs) 配置
{
files: ["**/*.?()js"],
rules: {
"@typescript-eslint/no-var-requires": "off", // 允许 require
},
},
// Vue 文件配置
{
files: ["**/*.vue"],
languageOptions: {
parser: parserVue,
parserOptions: {
parser: "@typescript-eslint/parser",
sourceType: "module",
},
},
plugins: {
vue: pluginVue,
},
processor: pluginVue.processors[".vue"],
rules: {
...pluginVue.configs["vue3-recommended"].rules,
"vue/no-v-html": "off", // 允许 v-html
"vue/require-default-prop": "off", // 允许没有默认值的 prop
"vue/multi-word-component-names": "off", // 关闭组件名称多词要求
"vue/html-self-closing": [
"error",
{
html: { void: "always", normal: "always", component: "always" },
svg: "always",
math: "always",
},
], // 自闭合标签
},
},
];
</code></pre>
<h3 id="集成-prettier">集成 Prettier</h3>
<p>Prettier 是一个代码格式化工具,能够和 ESLint 配合使用,确保代码风格统一。</p>
<p><strong>prettier 中文网</strong>:https://prettier.nodejs.cn/</p>
<h4 id="安装插件-1">安装插件</h4>
<p>VSCode 插件市场搜索 <code>Prettier - Code formatter</code> 插件安装</p>
<p><img src="https://s2.loli.net/2023/02/12/cBHDxNGzCaoeqvW.png" alt="" loading="lazy"></p>
<h4 id="安装依赖">安装依赖</h4>
<pre><code class="language-bash">pnpm install -D prettier eslint-config-prettier eslint-plugin-prettier
</code></pre>
<ul>
<li>
<p><strong>prettier</strong>:主要的 Prettier 格式化库。</p>
</li>
<li>
<p><strong>eslint-plugin-prettier</strong>:将 Prettier 的规则作为 ESLint 的规则来运行。</p>
</li>
<li>
<p><strong>eslint-config-prettier</strong>:禁用所有与格式相关的 ESLint 规则,以避免和 Prettier 的冲突。</p>
</li>
</ul>
<h4 id="配置-prettier">配置 Prettier</h4>
<p>项目根目录下新建配置文件 <code>prettier.config.mjs</code>,添加常用规则:</p>
<pre><code class="language-js">export default {
printWidth: 100, // 每行最多字符数量,超出换行(默认80)
tabWidth: 2, // 缩进空格数,默认2个空格
useTabs: false, // 指定缩进方式,空格或tab,默认false,即使用空格
semi: true, // 使用分号
singleQuote: false, // 使用单引号 (true:单引号;false:双引号)
trailingComma: 'all', // 末尾使用逗号
};
</code></pre>
<h4 id="配置忽略文件">配置忽略文件</h4>
<p>项目根目录新建 <code>.prettierignore</code> 文件指定 Prettier 不需要格式化的文件和文件夹</p>
<pre><code class="language-bash"># .prettierignore
node_modules
dist
public
*.min.js
</code></pre>
<h4 id="添加格式化脚本">添加格式化脚本</h4>
<p>在 <code>package.json</code> 文件中添加:</p>
<pre><code class="language-json">{
"scripts": {
"format": "prettier --write ./src"
}
}
</code></pre>
<h4 id="保存自动格式化">保存自动格式化</h4>
<p>打开 VSCode 的 <code>File</code> → <code>Preferences</code> → <code>Settings</code>,然后选择 <code>Open Settings (JSON)</code>,添加以下配置</p>
<pre><code class="language-json">{
"editor.formatOnSave": true, // 保存格式化文件
"editor.defaultFormatter": "esbenp.prettier-vscode" // 指定 prettier 为所有文件默认格式化器
}
</code></pre>
<h4 id="测试">测试</h4>
<p>下图演示了保存时的自动格式化效果,展示了代码中引号和换行的自动调整:</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/PixPin_2024-09-18_15-45-29.gif" alt="" loading="lazy"></p>
<h3 id="集成-stylelint">集成 Stylelint</h3>
<p>Stylelint 一个强大的 CSS linter(检查器),可帮助您避免错误并强制执行约定。</p>
<p><strong>Stylelint 官网</strong>:https://stylelint.io/</p>
<h4 id="安装插件-2">安装插件</h4>
<p>VSCode 插件搜索 <code>Stylelint</code> 并安装</p>
<p><img src="https://s2.loli.net/2023/04/08/azfOTp4K12c3PvQ.png" alt="" loading="lazy"></p>
<h4 id="安装依赖-1">安装依赖</h4>
<pre><code class="language-bash">pnpm install -D postcss postcss-html postcss-scss stylelint stylelint-config-recommended stylelint-config-recommended-scss stylelint-config-recommended-vue stylelint-config-recess-order stylelint-config-html stylelint-prettier
</code></pre>
<table>
<thead>
<tr>
<th>依赖</th>
<th>说明</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>postcss</td>
<td>CSS 解析工具,允许使用现代 CSS 语法并将其转换为兼容的旧语法</td>
<td></td>
</tr>
<tr>
<td>postcss-html</td>
<td>解析 HTML (类似 HTML) 的 PostCSS 语法</td>
<td>postcss-html 文档</td>
</tr>
<tr>
<td>postcss-scss</td>
<td>PostCSS 的 SCSS 解析器</td>
<td>postcss-scss 文档,支持 CSS 行类注释</td>
</tr>
<tr>
<td>stylelint</td>
<td>stylelint 核心库</td>
<td>stylelint </td>
</tr>
<tr>
<td>stylelint-config-standard</td>
<td>Stylelint 标准共享配置</td>
<td>stylelint-config-standard 文档</td>
</tr>
<tr>
<td>stylelint-config-recommended</td>
<td></td>
<td></td>
</tr>
<tr>
<td>stylelint-config-recommended-scss</td>
<td>扩展 stylelint-config-recommended 共享配置并为 SCSS 配置其规则</td>
<td>stylelint-config-recommended-scss 文档</td>
</tr>
<tr>
<td>stylelint-config-recommended-vue</td>
<td>扩展 stylelint-config-recommended 共享配置并为 Vue 配置其规则</td>
<td>stylelint-config-recommended-vue 文档</td>
</tr>
<tr>
<td>stylelint-config-recess-order</td>
<td>提供优化样式顺序的配置</td>
<td>CSS 书写顺序规范</td>
</tr>
<tr>
<td>stylelint-config-html</td>
<td>共享 HTML (类似 HTML) 配置,捆绑 postcss-html 并对其进行配置</td>
<td>stylelint-config-html 文档</td>
</tr>
<tr>
<td>stylelint-prettier</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h4 id="配置-stylelint">配置 Stylelint</h4>
<p>根目录新建 <code>.stylelintrc.cjs</code> 文件,配置如下:</p>
<pre><code class="language-javascript">{
"extends": [
"stylelint-config-recommended",
"stylelint-config-recommended-scss",
"stylelint-config-recommended-vue/scss",
"stylelint-config-html/vue",
"stylelint-config-recess-order"
],
"plugins": ["stylelint-prettier"],
"overrides": [
{
"files": ["**/*.{vue,html}"],
"customSyntax": "postcss-html"
},
{
"files": ["**/*.{css,scss}"],
"customSyntax": "postcss-scss"
}
],
"rules": {
"import-notation": "string",
"selector-class-pattern": null,
"custom-property-pattern": null,
"keyframes-name-pattern": null,
"no-descending-specificity": null,
"no-empty-source": null,
"selector-pseudo-class-no-unknown": [
true,
{
"ignorePseudoClasses": ["global", "export", "deep"]
}
],
"unit-no-unknown": [true, {
"ignoreUnits": ["rpx"]
}]
"property-no-unknown": [
true,
{
"ignoreProperties": []
}
],
"at-rule-no-unknown": [
true,
{
"ignoreAtRules": ["apply", "use", "forward"]
}
]
}
}
</code></pre>
<h4 id="配置忽略文件-1">配置忽略文件</h4>
<p>根目录创建 .stylelintignore 文件,配置忽略文件如下:</p>
<pre><code class="language-basic">*.min.js
dist
public
node_modules
</code></pre>
<h4 id="添加-stylelint-脚本">添加 Stylelint 脚本</h4>
<p>package.json 添加 Stylelint 检测指令:</p>
<pre><code class="language-json">"scripts": {
"lint:stylelint": "stylelint\"**/*.{css,scss,vue,html}\" --fix"
}
</code></pre>
<h4 id="保存自动修复">保存自动修复</h4>
<p>项目根目录下<code>.vscode/settings.json</code> 文件添加配置:</p>
<pre><code class="language-json">{
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": true
},
"stylelint.validate": ["css", "scss", "vue", "html"]
}
</code></pre>
<p>为了验证把尺寸属性 width 放置在定位属性 position 前面,根据 CSS 书写顺序规范 推断是不符合规范的,在保存时 Stylelint 自动将属性重新排序,达到预期。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/PixPin_2024-09-18_20-39-04.gif" alt="" loading="lazy"></p>
<h4 id="测试-1">测试</h4>
<p>执行以下命令进行检测</p>
<pre><code class="language-bash">npm run lint:stylelint
</code></pre>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240918203537608.png" alt="" loading="lazy"></p>
<h2 id="git提交规范配置">Git提交规范配置</h2>
<p>配置 Husky 的 <code>pre-commit</code> 和 <code>commit-msg</code> 钩子,实现代码提交的自动化检查和规范化。</p>
<ul>
<li><strong>pre-commit</strong>: 使用 Husky + Lint-staged,在提交前进行代码规范检测和格式化。确保项目已配置 ESLint、Prettier 和 Stylelint。</li>
<li><strong>commit-msg</strong>: 结合 Husky、Commitlint、Commitizen 和 cz-git,生成规范化且自定义的 Git commit 信息。</li>
</ul>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240919150854229.png" alt="" loading="lazy"></p>
<h3 id="集成-husky">集成 Husky</h3>
<p>Husky 是 Git 钩子工具,可以设置在 git 各个阶段(<code>pre-commit</code>、<code>commit-msg</code> 等)触发。</p>
<p><strong>Husky官网</strong>:https://typicode.github.io</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240919094059492.png" alt="" loading="lazy"></p>
<h4 id="安装依赖-2">安装依赖</h4>
<pre><code class="language-bash">pnpm add -D husky
</code></pre>
<h4 id="初始化">初始化</h4>
<p><code>init</code> 命令简化了项目中的 husky 设置。它会在 <code>.husky/</code> 中创建 <code>pre-commit</code> 脚本,并更新 <code>package.json</code> 中的 <code>prepare</code> 脚本。</p>
<pre><code class="language-bash">pnpm exec husky init
</code></pre>
<h4 id="测试-2">测试</h4>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240919100633078.png" alt="image-20240919100633078" loading="lazy"></p>
<p>通过 <code>pre-commit</code> 钩子,可以自动运行各种代码检查工具,在提交代码前强制执行代码质量和样式检查。常见的工具包括:</p>
<ul>
<li><strong><code>eslint</code></strong>:用于检查和修复 JavaScript/TypeScript 代码中的问题。</li>
<li><strong><code>stylelint</code></strong>:用于检测和修复 CSS/SCSS 样式问题。</li>
</ul>
<p>接下来,集成 <strong><code>lint-staged</code></strong> 和 <strong><code>commitlint</code></strong> 来进一步完善开发体验。</p>
<h3 id="集成-lint-staged">集成 lint-staged</h3>
<p><code>lint-staged</code> 是一个工具,专门用于只对 Git 暂存区的文件运行 lint 或其他任务,确保只检查和修复被修改或新增的代码部分,而不会影响整个代码库。这样可以显著提升效率,尤其是对于大型项目。</p>
<h4 id="安装依赖-3">安装依赖</h4>
<p>使用以下命令安装 <code>lint-staged</code>:</p>
<pre><code class="language-json">pnpm add -D lint-staged
</code></pre>
<h4 id="配置-lint-staged">配置 lint-staged</h4>
<p>在 <code>package.json</code> 中添加 <code>lint-staged</code> 配置,确保在 <code>pre-commit</code> 阶段自动检测暂存的文件:</p>
<pre><code class="language-json">{
"name": "vue-uniapp-template",
"version": "0.0.0",
"lint-staged": {
"*.{js,ts}": [
"eslint --fix",
"prettier --write"
],
"*.{cjs,json}": [
"prettier --write"
],
"*.{vue,html}": [
"eslint --fix",
"prettier --write",
"stylelint --fix"
],
"*.{scss,css}": [
"stylelint --fix",
"prettier --write"
],
"*.md": [
"prettier --write"
]
}
}
</code></pre>
<p>在 <code>package.json</code> 的 <code>scripts</code> 部分中,添加用于运行 <code>lint-staged</code> 的命令:</p>
<pre><code class="language-json">"scripts": {
"lint:lint-staged": "lint-staged"
}
</code></pre>
<h4 id="添加-husky-钩子">添加 Husky 钩子</h4>
<p>在项目根目录的 <code>.husky/pre-commit</code> 中添加以下命令,确保在提交代码前执行 <code>lint-staged</code>:</p>
<pre><code class="language-bash">pnpm run lint:lint-staged
</code></pre>
<h4 id="测试-3">测试</h4>
<p>提交代码时,<code>lint-staged</code> 会自动对暂存的文件运行相应的 lint 任务。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240919112617112.png" alt="" loading="lazy"></p>
<p>通过这种集成方式,确保代码在提交前经过自动格式化和校验,提高代码质量和一致性。</p>
<h3 id="集成-commitlint">集成 Commitlint</h3>
<p><code>commitlint</code> 用于检查 Git 提交信息是否符合特定规范(如 Angular 提交规范),从而保证提交信息的一致性。</p>
<p><strong>Commitlint官网</strong>:https://commitlint.js.org/</p>
<h4 id="安装依赖-4">安装依赖</h4>
<pre><code class="language-bash">pnpm add -D@commitlint/cli @commitlint/config-conventional
</code></pre>
<h4 id="配置-commitlint">配置 Commitlint</h4>
<p>在项目根目录下创建 <code>commitlint.config.cjs</code> 文件,添加以下内容来启用 Angular 规范:</p>
<pre><code class="language-json">module.exports = {
// 继承的规则
extends: ["@commitlint/config-conventional"],
// 自定义规则
rules: {
// 提交类型枚举,git提交type必须是以下类型 @see https://commitlint.js.org/#/reference-rules
"type-enum": [
2,
"always",
[
"feat", // 新增功能
"fix", // 修复缺陷
"docs", // 文档变更
"style", // 代码格式(不影响功能,例如空格、分号等格式修正)
"refactor", // 代码重构(不包括 bug 修复、功能新增)
"perf", // 性能优化
"test", // 添加疏漏测试或已有测试改动
"build", // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
"ci", // 修改 CI 配置、脚本
"revert", // 回滚 commit
"chore", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
],
],
"subject-case": , // subject大小写不做校验
},
};
</code></pre>
<h4 id="添加-husky-钩子-1">添加 Husky 钩子</h4>
<p>将 <code>commitlint</code> 与 Husky 集成,在 <code>.husky/commit-msg</code> 文件中添加以下内容,确保提交信息符合规范:</p>
<pre><code class="language-bash">npx --no-install commitlint --edit $1
</code></pre>
<h4 id="测试-4">测试</h4>
<p>根据 Angular 的提交规范,提交信息由以下部分组成:</p>
<ol>
<li><strong>类型</strong>:表示本次提交的类型,例如 <code>feat</code> (新功能)、<code>fix</code> (修复 bug)、<code>docs</code> (文档更新)。</li>
<li><strong>作用域</strong>(可选):说明本次提交影响的模块,例如 <code>auth</code>、<code>ui</code>。</li>
<li><strong>简短描述</strong>:简洁明了的提交描述,限定在 50 字符以内。</li>
</ol>
<p>当你尝试提交不符合规范的提交信息时,提交会被阻止,并显示相关错误提示。如下图所示:</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240919134921181.png" alt="" loading="lazy"></p>
<h3 id="集成-commitizen--和--cz-git">集成 Commitizen和cz-git</h3>
<ul>
<li>
<p><strong>commitizen</strong>:是一个帮助开发者以标准化格式生成提交信息的工具。--Commitizen 官方文档</p>
</li>
<li>
<p><strong>cz-git</strong>: <code>cz-git</code> 是 <code>Commitizen</code> 的适配器之一,它基于 <code>Commitizen</code>,提供了更多自定义功能和增强的交互体验。--cz-git 官方文档</p>
</li>
</ul>
<h4 id="安装依赖-5">安装依赖</h4>
<pre><code class="language-bash">pnpm add -D commitizen cz-git
</code></pre>
<h4 id="配置-cz-git">配置 cz-git</h4>
<p>在项目中初始化 <code>Commitizen</code>,并配置使用 <code>cz-git</code> 作为适配器。在 <code>package.json</code> 中添加以下配置:</p>
<pre><code class="language-json">"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
}
</code></pre>
<p>在<code>commitlint</code> 的配置文件 <code>commitlint.config.cjs</code> 中添加配置,commitlint 配置模板:https://cz-git.qbb.sh/zh/config/</p>
<pre><code class="language-javascript">module.exports = {
// 继承的规则
extends: ["@commitlint/config-conventional"],
// 自定义规则
rules: {
// ...
},
// cz-git 配置
prompt: {
messages: {
type: "选择你要提交的类型 :",
scope: "选择一个提交范围(可选):",
customScope: "请输入自定义的提交范围 :",
subject: "填写简短精炼的变更描述 :\n",
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
footerPrefixesSelect: "选择关联issue前缀(可选):",
customFooterPrefix: "输入自定义issue前缀 :",
footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
generatingByAI: "正在通过 AI 生成你的提交简短描述...",
generatedSelectByAI: "选择一个 AI 生成的简短描述:",
confirmCommit: "是否提交或修改commit ?",
},
// prettier-ignore
types: [
{ value: "feat", name: "特性: ✨新增功能", emoji: ":sparkles:" },
{ value: "fix", name: "修复: 🐛修复缺陷", emoji: ":bug:" },
{ value: "docs", name: "文档: 📝文档变更", emoji: ":memo:" },
{ value: "style", name: "格式: 💄代码格式(不影响功能,例如空格、分号等格式修正)", emoji: ":lipstick:" },
{ value: "refactor", name: "重构: ♻️代码重构(不包括 bug 修复、功能新增)", emoji: ":recycle:" },
{ value: "perf", name: "性能: ⚡️性能优化", emoji: ":zap:" },
{ value: "test", name: "测试: ✅添加疏漏测试或已有测试改动", emoji: ":white_check_mark:"},
{ value: "build", name: "构建: 📦️构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)", emoji: ":package:"},
{ value: "ci", name: "集成: 🎡修改 CI 配置、脚本",emoji: ":ferris_wheel:"},
{ value: "revert", name: "回退: ⏪️回滚 commit",emoji: ":rewind:"},
{ value: "chore", name: "其他: 🔨对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: ":hammer:"},
],
useEmoji: true,
emojiAlign: "center",
useAI: false,
aiNumber: 1,
themeColorCode: "",
scopes: [],
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlign: "bottom",
customScopesAlias: "custom",
emptyScopesAlias: "empty",
upperCaseSubject: false,
markBreakingChangeMode: false,
allowBreakingChanges: ["feat", "fix"],
breaklineNumber: 100,
breaklineChar: "|",
skipQuestions: [],
issuePrefixes: [{ value: "closed", name: "closed: ISSUES has been processed" }],
customIssuePrefixAlign: "top",
emptyIssuePrefixAlias: "skip",
customIssuePrefixAlias: "custom",
allowCustomIssuePrefix: true,
allowEmptyIssuePrefix: true,
confirmColorize: true,
maxHeaderLength: Infinity,
maxSubjectLength: Infinity,
minSubjectLength: 0,
scopeOverrides: undefined,
defaultBody: "",
defaultIssues: "",
defaultScope: "",
defaultSubject: "",
},
};
</code></pre>
<h4 id="添加-cz-git-脚本">添加 cz-git 脚本</h4>
<p>在<code>package.json</code> 文件中添加 <code>commit</code> 脚本命令</p>
<pre><code class="language-json"> "scripts": {
"commit": "git-cz"
}
</code></pre>
<h4 id="测试-5">测试</h4>
<p>执行 <code>pnpm run commit</code> 命令后,按照提示输入相关信息,最终生成符合规范的提交信息。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240919150854229.png" alt="" loading="lazy"></p>
<h2 id="整合-sass">整合 Sass</h2>
<p><strong>Sass</strong>是帮助开发者编写、管理和维护样式的强大工具,通过 <code><style lang="scss"></code> 使用。它提供变量、嵌套、混合等功能,提升了样式的可维护性和开发效率,尤其在复杂项目中减少重复代码、提高复用性。</p>
<pre><code>pnpm add -D sass sass-loader
</code></pre>
<h2 id="整合-unocss">整合 UnoCSS</h2>
<p>UnoCSS 是一个高性能、灵活且按需生成的原子化 CSS 引擎。</p>
<p><strong>官方网站</strong>:https://unocss.net/</p>
<p>先比较下</p>
<table>
<thead>
<tr>
<th>内部样式</th>
<th>UnoCSS原子样式</th>
</tr>
</thead>
<tbody>
<tr>
<td><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240920174025912.png" alt="" loading="lazy"></td>
<td><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240920174419861.png" alt="image-20240920174419861" loading="lazy"></td>
</tr>
</tbody>
</table>
<h3 id="安装插件-3">安装插件</h3>
<p><code>VSCode</code> 安装 <code>UnoCSS</code> 插件</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240920114534560.png" alt="VSCode安装UnoCSS插件" loading="lazy"></p>
<h3 id="安装依赖-6">安装依赖</h3>
<p>本次整合基于官网提供的社区预设 unocss-preset-weapp。该预设内置了 <code>transformer</code>,用于解决小程序的兼容性问题。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240920093428222.png" alt="UnoCSS 整合" loading="lazy"></p>
<p>进一步参考 unocss-preset-weapp 的 uniapp_vue3 使用与配置指南,使用以下命令安装 UnoCSS 和 <code>unocss-preset-weapp</code>:</p>
<pre><code class="language-bash">pnpm add -D unocss unocss-preset-weapp
</code></pre>
<h3 id="配置-unocss">配置 UnoCSS</h3>
<p>参考 unocss-preset-weapp 的 uniapp_vue3 使用与配置指南,配置如下:</p>
<ul>
<li>
<p><strong>vite.config.ts</strong></p>
<blockquote>
<p>在 <code>vite.config.ts</code> 中添加如下配置,整合 UnoCSS 到 UniApp 项目:</p>
</blockquote>
<pre><code class="language-bash">import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
export default defineConfig(async () => {
const UnoCss = await import('unocss/vite').then(i => i.default)
return {
plugins: [
uni(),
// 配置 UnoCSS
UnoCss(),
],
}
})
</code></pre>
</li>
<li>
<p><strong>unocss.config.ts</strong></p>
<blockquote>
<p>添加unocss.config.ts文件,搭配 unocss vscode 插件,智能提示</p>
</blockquote>
<pre><code class="language-typescript">import presetWeapp from "unocss-preset-weapp";
import { extractorAttributify, transformerClass } from "unocss-preset-weapp/transformer";
const { presetWeappAttributify, transformerAttributify } = extractorAttributify();
export default {
presets: [
// https://github.com/MellowCo/unocss-preset-weapp
presetWeapp(),
// attributify autocomplete
presetWeappAttributify(),
],
shortcuts: [
{
"flex-center": "flex justify-center items-center",
"flex-col": "flex flex-col",
},
],
transformers: [
// https://github.com/MellowCo/unocss-preset-weapp/tree/main/src/transformer/transformerAttributify
transformerAttributify(),
// https://github.com/MellowCo/unocss-preset-weapp/tree/main/src/transformer/transformerClass
transformerClass(),
],
};
</code></pre>
</li>
</ul>
<p> <strong><code>shortcuts</code> 自定义样式组合</strong>:可以在 <code>shortcuts</code> 中定义常用的样式组合,以便简化项目中的样式使用和维护,避免冗余和重复的样式。</p>
<ul>
<li>
<p><strong>main.ts</strong></p>
<pre><code>import 'uno.css'
</code></pre>
</li>
</ul>
<h3 id="测试-6">测试</h3>
<p>下图展示了在 VSCode 中测试 UnoCSS 时,智能提示和样式设置功能已经正常生效。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240920123246893.png" alt="img" loading="lazy"></p>
<p>此外,在 <code>unocss.config.ts</code> 文件中预设的 <code>shortcuts</code> 组合样式也得到了正确应用。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240920175138557.png" alt="image-20240920175138557" loading="lazy"></p>
<h2 id="添加-tabbar">添加 TabBar</h2>
<p>在 APP 开发中,底部导航栏(TabBar)是移动端应用的重要部分,方便用户在不同页面间进行切换。</p>
<h3 id="添加页面">添加页面</h3>
<p>在初始化的模板项目中,<code>src/pages</code> 目录下默认有一个 <code>index/index.vue</code> 页面。为了更好地演示 <code>TabBar</code> 的切换效果,我们在 <code>pages</code> 目录下再新增两个页面:</p>
<ul>
<li>
<p><strong>工作台页面</strong>(<code>src/pages/work/index.vue</code>):</p>
<pre><code class="language-vue"><template>
<view class="flex-center flex-col">
<view>
<text class="text-cyan font-bold text-lg">工作台</text>
</view>
</view>
</template>
</code></pre>
</li>
<li>
<p><strong>我的页面</strong>(<code>src/pages/my/index.vue</code>):</p>
<pre><code class="language-vue"><template>
<view class="flex-center flex-col">
<view>
<text class="text-blue font-bold text-lg">我的</text>
</view>
</view>
</template>
</code></pre>
</li>
</ul>
<h3 id="添加图标">添加图标</h3>
<p>在 <code>src/static</code> 目录下创建一个 <code>tabbar</code> 文件夹,存放从 iconfont 获取的图标。每个图标都需要有未激活和激活两种状态的样式。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240923115902874.png" alt="" loading="lazy"></p>
<h3 id="配置-tabbar">配置 TabBar</h3>
<p>在 UniApp 项目中,底部导航栏(TabBar)通过配置 <code>pages.json</code> 文件来实现。首先,找到并打开项目根目录下的 <code>src/pages.json</code> 文件。在该文件中,可以为每个页面配置导航栏,同时定义 TabBar。</p>
<p>下面是完整的配置示例,注意 <code>tabBar</code> 中的 <code>pagePath</code> 必须对应 <code>pages</code> 目录下的实际页面路径。</p>
<pre><code class="language-json">{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/work/index",
"style": {
"navigationBarTitleText": "工作台"
}
},
{
"path": "pages/mine/index",
"style": {
"navigationBarTitleText": "我的"
}
}
],
"tabBar": {
"color": "#474747",
"selectedColor": "#3B8DFF",
"backgroundColor": "#F8F8F8",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/home-active.png"
},
{
"pagePath": "pages/work/index",
"text": "工作台",
"iconPath": "static/tabbar/work.png",
"selectedIconPath": "static/tabbar/work-active.png"
},
{
"pagePath": "pages/mine/index",
"text": "我的",
"iconPath": "static/tabbar/mine.png",
"selectedIconPath": "static/tabbar/mine-active.png"
}
]
}
}
</code></pre>
<h3 id="测试-7">测试</h3>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/PixPin_2024-09-20_18-36-33.gif" alt="" loading="lazy"></p>
<h2 id="按需自动导入">按需自动导入</h2>
<p>在传统的 Vue 开发中,我们通常需要在每个页面手动导入 Vue 组合式 API(如 <code>ref</code>, <code>reactive</code>, <code>onMounted</code> 等)。随着项目的增大,手动导入会增加代码的冗余度,降低开发体验。</p>
<p>通过对比,来看一下手动导入与按需自动导入的区别:</p>
<table>
<thead>
<tr>
<th>手动导入</th>
<th>按需自动导入</th>
</tr>
</thead>
<tbody>
<tr>
<td><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240921171309833.png" alt="手动导入" loading="lazy"></td>
<td><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240921171422590.png" alt="按需导入" loading="lazy"></td>
</tr>
</tbody>
</table>
<p><strong>手动导入:</strong> 每个页面都需要显式地引入 <code>ref</code>, <code>reactive</code>, <code>onMounted</code> 等组合式 API。<br>
<strong>按需自动导入:</strong> 配置了自动导入插件后,这些 API 无需显式导入即可直接使用,减少了重复代码,提高了开发效率。</p>
<p>由于当前还未整合按需自动导入插件,所以右图的代码仍然报错,提示未找到 <code>ref</code> 和 <code>reactive</code> 的定义。这展示了按需自动导入的重要性:一旦整合插件,这类错误将被消除,代码更加简洁易维护。</p>
<h3 id="安装依赖-7">安装依赖</h3>
<p>首先使用以下命令安装 <code>unplugin-auto-import</code> 插件:</p>
<pre><code class="language-bash">pnpm add -D unplugin-auto-import
</code></pre>
<h3 id="配置自动导入">配置自动导入</h3>
<p>接着,在 <code>vite.config.ts</code> 中配置 <code>unplugin-auto-import</code> 插件,确保 Vue 和 UniApp 的 API 能够自动按需导入。</p>
<pre><code class="language-typescript">// vite.config.ts
import { defineConfig } from "vite";
import AutoImport from "unplugin-auto-import/vite";
export default defineConfig(async () => {
return {
plugins: [
uni(), // 这里是你的 uni-app 插件
AutoImport({
imports: ["vue", "uni-app"], // 自动导入 Vue 和 UniApp 的 API
dts: "src/types/auto-imports.d.ts", // 自动生成类型声明文件
eslintrc: {
enabled: true, // 生成 ESLint 配置文件
filepath: './.eslintrc-auto-import.json', // ESLint 配置文件路径
},
}),
],
};
});
</code></pre>
<h3 id="配置-eslint-规则">配置 ESLint 规则</h3>
<p>为了让 ESLint 能识别这些通过 <code>unplugin-auto-import</code> 自动导入的 API,需要在 ESLint 的配置中引入 <code>unplugin-auto-import</code> 生成的 <code>.eslintrc-auto-import.json</code> 文件。</p>
<p>在 ESLint 9.x 版本中,使用 Flat Config 时不再支持 <code>extends</code> 关键字。因此,<strong>不能</strong>使用以下配置:</p>
<pre><code class="language-typescript">// 错误示例
export default [
{
extends: ["./.eslintrc-auto-import.json"], // 这种扩展方式在 Flat Config 中不再支持
},
];
</code></pre>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240923123316040.png" alt="" loading="lazy"></p>
<p>取而代之的是直接引入 <code>.eslintrc-auto-import.json</code> 文件内容,通过解析文件的方式将自动导入的全局变量配置整合进 ESLint 配置。</p>
<p>在 <code>eslint.config.mjs</code> 中添加如下配置:</p>
<pre><code class="language-typescript">// eslint.config.mjs 正确的配置
import { readFileSync } from "fs";
import { fileURLToPath } from "url";
import { dirname, resolve } from "path";
// 动态读取 .eslintrc-auto-import.json 文件内容
const autoImportConfig = JSON.parse(
readFileSync(
resolve(dirname(fileURLToPath(import.meta.url)), ".eslintrc-auto-import.json"),
"utf-8",
),
);
export default [
{
// 语言选项配置,定义全局变量
languageOptions: {
globals: {
// ...
...autoImportConfig.globals, // 自动导入的全局变量
},
},
},
];
</code></pre>
<p>这样配置后,ESLint 将能够识别通过自动导入的 API,避免例如 <code>'ref' is not defined</code> 这样的错误,从而使项目的开发更加顺畅。</p>
<h3 id="测试-8">测试</h3>
<p>通过上述步骤配置后,原先在未手动导入情况下报错的页面,现在可以正常使用 <code>ref</code>, <code>reactive</code> 等 API,而无需显式导入。</p>
<p>以下是最终的效果:</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240921182221737.png" alt="按需自动导入" loading="lazy"></p>
<p>整合按需自动导入后,你将不再需要在每个页面显式导入 Vue 或 UniApp 的组合式 API,大幅度减少了重复的代码,提升了开发体验。</p>
<h2 id="环境变量">环境变量</h2>
<blockquote>
<p>Vite 环境变量主要是为了区分开发、测试、生产等环境的变量</p>
</blockquote>
<p>下面的整合过程参考 Vite 环境变量配置官方文档 </p>
<h3 id="配置环境变量">配置环境变量</h3>
<p>项目根目录新建 <code>.env.development</code> 、<code>.env.production</code></p>
<ul>
<li>
<p>开发环境变量配置:.env.development</p>
<pre><code class="language-properties"># 变量必须以 VITE_ 为前缀才能暴露给外部读取
# 项目运行的端口号
VITE_APP_PORT = 5173
# API 基础路径,开发环境下的请求前缀
VITE_APP_BASE_API = '/dev-api'
# API 服务器的 URL
VITE_APP_API_URL = https://api.youlai.tech
</code></pre>
</li>
<li>
<p>生产环境变量配置:.env.production</p>
<pre><code class="language-properties"># API 基础路径,生产环境下的请求前缀
VITE_APP_BASE_API = '/prod-api'
# API 服务器的 URL
VITE_APP_API_URL = https://api.youlai.tech
</code></pre>
</li>
</ul>
<h3 id="智能提示">智能提示</h3>
<p>首先,在 <code>src/types/env.d.ts</code> 文件中添加环境变量的类型声明:</p>
<pre><code class="language-typescript">// src/types/env.d.ts
interface ImportMetaEnv {
/**
* 应用端口
*/
VITE_APP_PORT: number;
/**
* API 基础路径
*/
VITE_APP_BASE_API: string;
/**
* API 服务器的 URL
*/
VITE_APP_API_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
</code></pre>
<p>确保 TypeScript 编译器使用的模块系统支持 <code>import.meta</code>。在 <code>tsconfig.json</code> 文件中,你可以指定 <code>es2020</code>、<code>es2022</code> 或 <code>esnext</code> 等模块系统。例如:</p>
<pre><code class="language-json">// tsconfig.json
{
"compilerOptions": {
"module": "esnext",// 支持 import.meta
// ...
}
}
</code></pre>
<p>在任意页面中测试 <code>import.meta</code> 是否能够智能提示环境变量:</p>
<pre><code class="language-typescript">console.log(import.meta.env.VITE_APP_PORT);
</code></pre>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240923150054556.png" alt="" loading="lazy"></p>
<h2 id="整合-http-请求">整合 HTTP 请求</h2>
<p>通过一个登录案例演示如何在Uniapp中整合HTTP请求。这里未使用<strong>axios</strong>和<strong>alova</strong>请求库的原因在于,Uniapp自身提供了跨平台的<strong>uni.request</strong>方法,该方法在不同平台(如小程序、App和H5)下表现一致,且无需额外安装第三方库,从而减少了项目的依赖复杂性和体积。</p>
<h3 id="封装请求工具">封装请求工具</h3>
<p>在 <code>global.d.ts</code> 中定义全局响应数据类型 <code>ResponseData</code>:</p>
<pre><code class="language-typescript">// src/types/global.d.ts
declare global {
/**
* 响应数据结构
*/
interface ResponseData<T = any> {
code: string;// 业务状态码
data: T; // 返回数据
msg: string; // 消息
}
}
export {};
</code></pre>
<p>在 <code>src/utils/request.ts</code> 下创建一个 HTTP 请求工具,用于与 API 服务器进行通信:</p>
<pre><code class="language-typescript">// src/utils/request.ts
export default function request<T>(options: UniApp.RequestOptions): Promise<T> {
const token = uni.getStorageSync("token"); // 从本地缓存获取 token
return new Promise((resolve, reject) => {
uni.request({
...options,
// VITE_APP_API_URL 是在 Vite 项目的 .env.development 文件中配置的环境变量,表示 API 的路径
url: `${import.meta.env.VITE_APP_API_URL}${options.url}`,
header: {
...options.header,
Authorization: token,
},
success: (response) => {
const resData = response.data as ResponseData<T>;
// 业务状态码 00000 表示成功
if (resData.code === "00000") {
resolve(resData.data);
} else {
uni.showToast({
title: resData.msg || "业务处理失败",
icon: "none",
duration: 2000,
});
reject({
message: resData.msg || "业务处理失败",
code: resData.code,
});
}
},
fail: (error) => {
uni.showToast({
title: "网络请求失败",
icon: "none",
duration: 2000,
});
reject({
message: "网络请求失败",
error,
});
},
});
});
}
</code></pre>
<p><strong>注意事项</strong></p>
<p>当首次使用该请求工具类时,可能会出现 <code>'uni' is not defined</code> 的 ESLint 错误,如下图所示:</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240923124237153.png" alt="" loading="lazy"></p>
<p>为解决此问题,需要在 ESLint 配置文件中定义 <code>uni</code> 为全局变量:</p>
<pre><code class="language-javascript">// eslint.config.mjs
export default [
{
// 语言选项配置,定义全局变量
languageOptions: {
globals: {
// ...
...{
uni: "readonly", // uni-app 全局对象
},
},
},
},
];
</code></pre>
<h3 id="登录接口定义">登录接口定义</h3>
<p><strong>请求URL</strong>: https://api.youlai.tech/api/v1/auth/login</p>
<p><strong>请求参数:</strong></p>
<table>
<thead>
<tr>
<th>参数名</th>
<th>参数值</th>
</tr>
</thead>
<tbody>
<tr>
<td>username</td>
<td>admin</td>
</tr>
<tr>
<td>password</td>
<td>123456</td>
</tr>
</tbody>
</table>
<p><strong>返回响应:</strong></p>
<pre><code class="language-json">{
"code": "00000",
"data": {
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIxxx.xxxxxxx",
"tokenType": "Bearer"
},
"msg": "一切ok"
}
</code></pre>
<p>根据上述登录接口信息,创建 <code>AuthAPI</code> 类用于处理登录请求:</p>
<pre><code class="language-typescript">import request from "@/utils/request";
const AuthAPI = {
/**
* 登录接口
*
* @param username 用户名
* @param password 密码
* @returns 返回 token
*/
login(username: string, password: string): Promise<LoginResult> {
return request<LoginResult>({
url: "/api/v1/auth/login",
method: "POST",
data: {
username,
password,
},
header: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
},
/**
* 登出接口
*/
logout(): Promise<void> {
return request({
url: "/api/v1/auth/logout",
method: "DELETE",
});
},
};
export default AuthAPI;
/** 登录响应 */
export interface LoginResult {
/** 访问token */
accessToken?: string;
/** token 类型 */
tokenType?: string;
}
</code></pre>
<h3 id="登录页面">登录页面</h3>
<p>新建 <code>src/pages/login/index.vue</code> 文件,编写登录页面及逻辑:</p>
<pre><code class="language-vue"><template>
<view class="flex-col">
<input v-model="username" placeholder="请输入用户名" />
<input v-model="password" placeholder="请输入密码" type="password" />
<button @click="handleLogin">登录</button>
</view>
</template>
<script lang="ts" setup>
import AuthAPI, { LoginResult } from '@/api/auth';
const username = ref('');
const password = ref('');
const router = useRouter();
const handleLogin = async () => {
try {
const response: LoginResult = await AuthAPI.login(username.value, password.value);
if (response.accessToken) {
uni.setStorageSync('token', response.accessToken);
uni.showToast({ title: '登录成功', icon: 'success' });
} else {
uni.showToast({ title: '登录失败', icon: 'none' });
}
} catch (err) {
uni.showToast({ title: '登录失败', icon: 'none' });
}
};
</script>
<style scoped>
</style>
</code></pre>
<p>在 <code>pages.json</code> 文件中,声明登录页面的路由:</p>
<pre><code class="language-json">// src/pages.json
{
"pages": [
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录"
}
}
]
}
</code></pre>
<h3 id="登录测试">登录测试</h3>
<p>访问登录页面:http://localhost:5173/#/pages/login/index,输入用户名和密码 (<code>admin</code>/<code>123456</code>) 测试登录接口,登录成功后可以看到返回的访问令牌。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/PixPin_2024-09-22_17-21-05.gif" alt="" loading="lazy"></p>
<h3 id="整合源码">整合源码</h3>
<p>整合<code>HTTP请求</code>代码版本:vue-uniapp-template#737f6a。</p>
<h2 id="整合-pinia">整合 Pinia</h2>
<blockquote>
<p>Pinia 是 Vue 的状态管理库,专为跨组件或页面共享状态设计。</p>
</blockquote>
<ul>
<li>
<p><strong>Pinia 官方文档</strong>: https://pinia.vuejs.org/zh/getting-started.html</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20240921102456261.png" alt="" loading="lazy"></p>
</li>
</ul>
<h3 id="安装依赖-8">安装依赖</h3>
<p>首先,安装 <code>pinia</code> 依赖:</p>
<pre><code class="language-bash">pnpm add pinia
</code></pre>
<h3 id="全局注册">全局注册</h3>
<p>在项目的 <code>src</code> 目录下创建 <code>store</code> 文件夹,并新建 <code>index.ts</code> 文件,初始化并注册 Pinia 实例。</p>
<pre><code class="language-typescript">// src/store/index.ts
import type { App } from "vue";
import { createPinia } from "pinia";
const store = createPinia();
// 注册 Pinia
export function setupStore(app: App<Element>) {
app.use(store); // 全局注册 Pinia
}
</code></pre>
<p>接着,将 <code>store</code> 在项目入口文件 <code>main.ts</code> 中引入,并将其作为全局插件传递给应用:</p>
<pre><code class="language-typescript">// src/main.ts
import { createSSRApp } from "vue";
import App from "./App.vue";
import { setupStore } from "@/store";
export function createApp() {
const app = createSSRApp(App);
// 全局注册 store
setupStore(app);
return {
app,
};
}
</code></pre>
<p>接下来,我们通过 Pinia 管理登录状态和用户信息,并在多个页面共享状态。</p>
<h3 id="用户信息接口">用户信息接口</h3>
<p>编写一个 API 来获取当前登录用户的信息:</p>
<pre><code class="language-typescript">import request from "@/utils/request";
const USER_BASE_URL = "/api/v1/users";
const UserAPI = {
/**
* 获取当前登录用户信息
*
* @returns 登录用户昵称、头像信息,包括角色和权限
*/
getUserInfo(): Promise<UserInfo> {
return request<UserInfo>({
url: `${USER_BASE_URL}/me`,
method: "GET",
});
},
};
export default UserAPI;
/** 登录用户信息 */
export interface UserInfo {
/** 用户ID */
userId?: number;
/** 用户名 */
username?: string;
/** 昵称 */
nickname?: string;
/** 头像URL */
avatar?: string;
/** 角色 */
roles: string[];
/** 权限 */
perms: string[];
}
</code></pre>
<h3 id="用户状态管理">用户状态管理</h3>
<p>通过 Pinia 定义 <code>user</code> 模块,管理登录状态、用户信息等。</p>
<pre><code class="language-typescript">//src/store/module/user.ts
import { defineStore } from "pinia";
import AuthAPI from "@/api/auth";
import UserAPI, { UserInfo } from "@/api/user";
export const useUserStore = defineStore("user", () => {
// 确保 token 是响应式的
const token = ref<string>(uni.getStorageSync("token") || "");
const userInfo = ref<UserInfo | null>(null);
// 登录
const login = async (username: string, password: string) => {
const { tokenType, accessToken } = await AuthAPI.login(username, password);
token.value = `${tokenType} ${accessToken}`; // Bearer token
uni.setStorageSync("token", token.value);
};
// 获取用户信息
const getUserInfo = async () => {
const info = await UserAPI.getUserInfo();
userInfo.value = info;
};
// 登出
const logout = async () => {
await AuthAPI.logout();
userInfo.value = null;
token.value = ""; // 清空 token
uni.removeStorageSync("token"); // 从本地缓存移除 token
};
return {
token,
userInfo,
login,
logout,
getUserInfo,
};
});
</code></pre>
<h3 id="个人中心页面">个人中心页面</h3>
<p>个人中心页面展示用户的头像和昵称,未登录时引导用户去登录。</p>
<pre><code class="language-vue"><template>
<view class="flex-center flex-col">
<text class="text-blue font-bold text-lg">我的</text>
<!-- 判断是否已登录 -->
<template v-if="isLoggedIn">
<image :src="userInfo?.avatar" class="w100 h100 mb-5 rounded-full" />
<text class="text-lg font-bold">{{ userInfo?.nickname }}</text>
<button @click="handleLogout" class="mt-5">退出登录</button>
</template>
<!-- 未登录时显示去登录按钮 -->
<template v-else>
<text>您还未登录,请先登录</text>
<button @click="goToLoginPage" class="mt-5">去登录</button>
</template>
</view>
</template>
<script lang="ts" setup>
import { useUserStore } from "@/store/modules/user";
// 使用 pinia
const userStore = useUserStore();
const isLoggedIn = computed(() => userStore.token);
const userInfo = computed(() => userStore.userInfo);
// 跳转到登录页面
const goToLoginPage = () => {
uni.navigateTo({ url: "/pages/login/index" });
};
// 退出登录处理
const handleLogout = async () => {
await userStore.logout();
uni.showToast({ title: "已退出登录", icon: "success" });
};
</script>
</code></pre>
<p>登录页通过 <code>Pinia</code> 实现用户信息的全局状态管理,并在登录成功后跳转到个人中心页面。</p>
<pre><code class="language-vue"><template>
<view class="flex-col items-center">
<input v-model="username" placeholder="请输入用户名" />
<input v-model="password" placeholder="请输入密码" type="password" />
<button class="mt-5" @click="handleLogin">登录</button>
</view>
</template>
<script lang="ts" setup>
import { useUserStore } from "@/store/modules/user";
// 登录表单
const username = ref("admin");
const password = ref("123456");
// 使用 pinia
const userStore = useUserStore();
// 登录处理
const handleLogin = async () => {
await userStore.login(username.value, password.value);
if (!!userStore.token) {
await userStore.getUserInfo(); // 登录成功后获取用户信息
uni.showToast({ title: "登录成功", icon: "success" });
uni.navigateBack(); // 登录成功后返回上一页
} else {
uni.showToast({ title: "登录失败", icon: "none" });
}
};
</script>
<style scoped>
input {
width: 80%;
padding: 10px;
margin-top: 16px;
border: 1px solid #ccc;
}
</style>
</code></pre>
<h3 id="测试效果-1">测试效果</h3>
<p>登录后,个人中心会显示用户的头像和昵称。通过 Pinia 实现了登录状态的共享和跨页面传递。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/PixPin_2024-09-22_17-21-011.gif" alt="" loading="lazy"></p>
<h3 id="整合源码-1">整合源码</h3>
<p>整合<code>Pinia</code>代码版本:vue-uniapp-template#737f6a3。</p>
<h2 id="反向代理">反向代理</h2>
<p>在开发中,若服务端没有启用 CORS(跨域资源共享),浏览器会基于安全策略拦截跨域请求,导致无法访问接口。为了绕过这个问题,我们可以通过 Vite 的反向代理功能,将开发阶段的请求代理到真实的 API 服务器上,伪装成同源请求。</p>
<p>本节将介绍如何配置 Vite 的反向代理来处理跨域请求。</p>
<hr>
<h3 id="环境变量配置">环境变量配置</h3>
<p>我们将通过环境变量来管理项目端口和 API 请求地址,以下是 <code>.env.development</code> 中的相关配置:</p>
<pre><code class="language-bash"># .env.development
# 项目运行的端口号
VITE_APP_PORT=5173
# API 请求的基础路径(开发环境)
VITE_APP_BASE_API=/dev-api
# 真实 API 服务器的 URL
VITE_APP_API_URL=https://api.youlai.tech
</code></pre>
<h3 id="请求工具的调整">请求工具的调整</h3>
<p>为了让请求走代理,我们需要在请求工具中将 <code>VITE_APP_API_URL</code> 替换为 <code>VITE_APP_BASE_API</code>。这样,所有对 <code>API</code> 的请求都会通过代理标识 <code>/dev-api</code> 进行转发。</p>
<pre><code class="language-typescript">export default function request<T>(options: UniApp.RequestOptions): Promise<T> {
return new Promise((resolve, reject) => {
uni.request({
...options,
// 原请求方式: 使用真实 API URL
// url: `${import.meta.env.VITE_APP_API_URL}${options.url}`, // 示例: https://api.youlai.tech/login
// 修改后:使用代理标识,实际转发到真实 API
url: `${import.meta.env.VITE_APP_BASE_API}${options.url}`, // 示例: http://localhost:5173/dev-api/login
});
});
}
</code></pre>
<h3 id="vite-反向代理配置">Vite 反向代理配置</h3>
<p>接下来,在 <code>vite.config.ts</code> 中添加反向代理配置,将 <code>/dev-api</code> 的请求代理到 <code>VITE_APP_API_URL</code>,通过 <code>http-proxy</code> 实现请求的转发。</p>
<pre><code class="language-typescript">// vite.config.ts
import { defineConfig, UserConfig, ConfigEnv, loadEnv } from "vite";
export default defineConfig(async ({ mode }: ConfigEnv): Promise<UserConfig> => {
const env = loadEnv(mode, process.cwd());
return {
server: {
host: "0.0.0.0",
port: +env.VITE_APP_PORT,
open: true,
// 反向代理配置
proxy: {
: {
target: env.VITE_APP_API_URL, // 目标服务器
changeOrigin: true, // 支持跨域
rewrite: (path) => path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""), // 去掉前缀
},
},
},
plugins: [
// 插件配置...
],
};
});
</code></pre>
<h3 id="测试与验证">测试与验证</h3>
<p>在配置好反向代理后,浏览器发出的请求将被 Vite 的代理服务器拦截并转发至真实的 API 地址。例如,浏览器请求 <code>http://localhost:5173/dev-api/api/v1/auth/login</code> 时,Vite 会将该请求代理到 <code>https://api.youlai.tech/api/v1/auth/login</code>。</p>
<p>下图展示了这一过程,浏览器认为请求的 URL 与应用的主机地址一致,因此不会阻止该请求,即便真实请求已通过代理转发到外部服务器。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86%E8%B7%A8%E5%9F%9F.gif" alt="反向代理跨域" loading="lazy"></p>
<p>需要注意,反向代理的目标是伪装请求来源,虽然它绕过了浏览器的同源策略,但有时也会让开发者误以为请求地址错误。实际上,这是由于代理转发过程造成的表面请求地址与真实请求地址的差异。</p>
<h3 id="整合源码-2">整合源码</h3>
<p>整合<code>反向代理</code>和<code>环境变量</code>代码版本:vue-uniapp-template#272d643。</p>
<h2 id="整合-wot-design-uni">整合 <code>wot-design-uni</code></h2>
<p><code>wot-design-uni</code> 是基于 <code>Vue 3</code> 和 <code>TypeScript</code> 构建的高质量组件库。组件库遵循 <code>Wot Design</code> 的设计规范,提供 70 多个组件,支持暗黑模式、国际化和自定义主题,旨在为开发者提供一致的 UI 交互,同时提高开发效率。</p>
<blockquote>
<p><strong>说明:</strong> 本文档整合步骤基于 <code>wot-design-uni</code> 官方文档编写,建议开发者参考 官方文档 进行安装和配置,以确保组件库的正确使用。</p>
</blockquote>
<hr>
<h3 id="安装依赖-9">安装依赖</h3>
<p>根据官方文档,使用 <code>pnpm</code> 安装组件库的依赖:</p>
<pre><code class="language-bash">pnpm add wot-design-uni
</code></pre>
<h3 id="配置自动引入组件">配置自动引入组件</h3>
<p>在传统的 <code>Vue</code> 项目中,使用组件需要手动安装、引用、注册。而使用 <code>easycom</code> 可以简化这些操作。只要组件路径符合规范,就可以直接在页面中使用,无需手动导入和注册。</p>
<p>在 <code>pages.json</code> 文件中配置 <code>easycom</code> 自动引入:</p>
<pre><code class="language-json">// pages.json
{
"easycom": {
"autoscan": true,
"custom": {
"^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue"
}
},
"pages": [
// 这里是项目已有的内容
]
}
</code></pre>
<p><strong>关于 <code>easycom</code>:</strong><br>
<code>easycom</code> 是 <code>uni-app</code> 提供的自动化引入功能,更多细节请参考 easycom 官方文档。</p>
<h3 id="volar-支持">Volar 支持</h3>
<p>为了让 <code>Volar</code> 正确识别和提示全局组件,你需要在项目的 <code>tsconfig.json</code> 文件中配置全局组件类型支持:</p>
<pre><code class="language-json">// tsconfig.json
{
"compilerOptions": {
"types": ["wot-design-uni/global"]
}
}
</code></pre>
<p>这将确保你在 <code>TypeScript</code> 项目中编写代码时,Volar 能提供完整的类型支持和代码提示。</p>
<h3 id="测试组件">测试组件</h3>
<p>安装和配置完成后,你可以开始使用 <code>wot-design-uni</code> 的组件。在页面中,直接写组件标签即可,无需手动导入和注册:</p>
<pre><code class="language-vue"><template>
<view>
<wd-button size="small">主要按钮</wd-button>
<wd-button type="success" size="small">成功按钮</wd-button>
<wd-button type="info" size="small">信息按钮</wd-button>
<wd-button type="warning" size="small">警告按钮</wd-button>
<wd-button type="error" size="small">危险按钮</wd-button>
</view>
</template>
</code></pre>
<p>你将看到如下按钮效果:</p>
<p><img src="https://raw.gitmirror.com/youlaitech/images/main/docs/image-20240925122808028.png" alt="" loading="lazy"></p>
<h3 id="整合源码-3">整合源码</h3>
<p>整合<code>wot-design-uni</code> 代码版本:vue-uniapp-template#a775721。</p>
<h2 id="项目部署">项目部署</h2>
<h3 id="h5-部署">H5 部署</h3>
<p>执行 <code>pnpm run build:h5</code> 命令来完成项目的打包:</p>
<pre><code class="language-bash">pnpm run build:h5
</code></pre>
<p>打包后生成的静态文件位于 <code>dist/build/h5</code> 目录下。将该目录下的文件复制到服务器的 <code>/usr/share/nginx/html/vue-uniapp-template</code> 目录。</p>
<p>接下来,配置 nginx:</p>
<pre><code class="language-nginx"># nginx.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html/vue-uniapp-template;
index index.html index.htm;
}
# 反向代理配置
location /prod-api/ {
# 将 api.youlai.tech 替换为后端 API 地址,注意保留后面的斜杠 /
proxy_pass http://api.youlai.tech/;
}
}
</code></pre>
<p>这样配置完成后,就可以通过 <code>nginx </code>服务器来访问你的项目了。</p>
<h3 id="小程序发布">小程序发布</h3>
<h3 id="下载工具">下载工具</h3>
<p>下载HBuilder X编辑器</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024232438606.png" alt="" loading="lazy"></p>
<p>下载 微信开发者工具</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024232328575.png" alt="" loading="lazy"></p>
<h3 id="获取小程序-appid">获取小程序 AppID</h3>
<p>访问 微信公众平台申请小程序,获取 <code>AppID</code>。如果已申请,可在 <code>首页</code> → <code>小程序信息</code> → <code>查看详情</code> 查看 AppID</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024231827075.png" alt="" loading="lazy"></p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024232218176.png" alt="" loading="lazy"></p>
<h3 id="配置项目">配置项目</h3>
<p>使用 HBuilder X 打开项目,修改 <code>manifest.json</code> 文件中的小程序配置,并填写获取的 AppID。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024232920676.png" alt="" loading="lazy"></p>
<h3 id="设置微信开发者工具">设置微信开发者工具</h3>
<p>使用微信扫码登录微信开发者工具,开启服务端口:点击工具栏<code>设置</code>→<code>安全设置</code>→<code>安全</code>→<code>服务端口</code>,选择打开。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024233058539.png" alt="" loading="lazy"></p>
<h3 id="运行项目">运行项目</h3>
<p>在 HBuilder X 中,点击 <code>运行</code>→<code>运行到小程序模拟器</code>→<code>微信开发者工具</code>。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024233326086.png" alt="image-20241024233326086" loading="lazy"></p>
<p>项目编译完成后,微信开发者工具会自动启动并呈现页面。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024233741449.png" alt="" loading="lazy"></p>
<h3 id="上传发布">上传发布</h3>
<p>在微信开发者工具中,点击 <code>上传</code> 将应用发布到小程序平台。</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024234052335.png" alt="" loading="lazy"></p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/image-20241024234313807.png" alt="" loading="lazy"></p>
<h3 id="查看效果">查看效果</h3>
<p>最后,使用手机打开小程序查看效果:</p>
<p><img src="https://raw.gitmirror.com/youlaitech/image/main/blog/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20241024234523.jpg" alt="" loading="lazy"></p>
<h2 id="结语">结语</h2>
<p>通过本文的讲解,相信你已经了解了如何从零搭建一个基于 Vue3 和 TypeScript 的 UniApp 跨端脚手架模板 —— vue-uniapp-template。本文涵盖了代码规范、状态管理、样式配置、Git 提交规范和项目部署等关键内容,旨在帮助开发者在实际项目中提升开发效率和代码质量。</p>
<p>源码已开源,项目地址:https://gitee.com/youlaiorg/vue-uniapp-template。目前项目仍在持续完善中,非常欢迎对该项目感兴趣的开发者参与共建,有意者可随时联系我(haoxianrui)。期待与你一起将这个项目做得更好,为开发者提供一个高效、实用的跨端解决方案。</p>
<p>感谢你的阅读和支持!</p><br><br>
来源:https://www.cnblogs.com/haoxianrui/p/18684753
頁:
[1]