Blogger Information
Blog 291
fans 0
comment 0
visits 350700
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
vite+vue3项目最佳起始点(保姆级)
Original
785 people have browsed it

一 、通过云开发平台快速创建初始化应用

1.创建相关应用模版请参考链接:去中心化的前端构建工具 — Vite

2.完成创建后就可以在github中查看到新增的Vite仓库

file

二 、 本地编写 Vite后台项目最佳起始点

1.将应用模版克隆到本地

  • 首先假定你已经安装了Git、node,没有安装请移步node官网进行安装。克隆项目:
  1. git clone + 项目地址
  • 进入项目文件
  1. cd Vite
  • 切换到feature/1.0.0 分支上
  1. git checkout feature/1.0.0
  • 安装依赖包
  1. npm install
  • 启动服务
  1. npm run dev

这里打开浏览器3000端口,并出现默认页面。

2.路由

  • 安装 vue-router 4.x
  1. npm i vue-router@next -S

file

  • 路由配置 router/index.js
  1. import { createRouter, createWebHashHistory } from 'vue-router';
  2. const router = createRouter({
  3. history: createWebHashHistory(),
  4. routes: [
  5. { path: '/', component: () => import('views/home.vue') }
  6. ]
  7. });
  8. export default router
  • 引入 main.js
  1. import router from "@/router";
  2. createApp(App).use(router).mount("#app");

3.状态管理

  • 安装 vuex 4.x
  1. npm i vuex@next -S

file

  • Store配置 store/index.js
  1. import {createStore} from 'vuex';
  2. export default createStore({
  3. state: {
  4. couter: 0
  5. }
  6. });
  • 引入 main.js
  1. import store from "@/store";
  2. createApp(App).use(store).mount("#app");

4.样式组织

  • 安装 sass
  1. npm i sass -D

styles 目录保存各种样式

file

index.scss 作为出口组织这些样式,同时编写一些全局样式

file

最后在main.js导入

5.UI库

  • 安装
  1. npm i element3 -S
  • 完整引入 main.js
  1. import element3 from "element3";
  2. import "element3/lib/theme-chalk/index.css";
  3. createApp(App).use(element3)
  • 按需引入 main.js
  1. import "element3/lib/theme-chalk/button.css";
  2. import { ElButton } from "element3"
  3. createApp(App).use(ElButton)

抽取成插件会更好 plugins/element3.js

  1. // 完整引入
  2. import element3 from "element3";
  3. import "element3/lib/theme-chalk/index.css";
  4. // 按需引入
  5. // import { ElButton } from "element3";
  6. // import "element3/lib/theme-chalk/button.css";
  7. export default function (app) {
  8. // 完整引入
  9. app.use(element3)
  10. // 按需引入
  11. // app.use(ElButton);
  12. }
  • 测试
  1. <el-button>my button</el-button>

6.基础布局

我们应用需要一个基本布局页,类似下图,将来每个页面以布局页为父页面即可:

file

  • 布局页面 layout/index.vue
  1. <template>
  2. <div class="app-wrapper">
  3. <!-- 侧边栏 -->
  4. <div class="sidebar-container"></div>
  5. <!-- 内容容器 -->
  6. <div class="main-container">
  7. <!-- 顶部导航栏 -->
  8. <navbar />
  9. <!-- 内容区 -->
  10. <app-main />
  11. </div>
  12. </div>
  13. </template>
  14. <script setup>
  15. import AppMain from "./components/AppMain.vue";
  16. import Navbar from "./components/Navbar.vue";
  17. </script>
  18. <style lang="scss" scoped>
  19. @import "../styles/mixin.scss";
  20. .app-wrapper {
  21. @include clearfix;
  22. position: relative;
  23. height: 100%;
  24. width: 100%;
  25. }
  26. </style>
  • 路由配置 router/index.js
  1. {
  2. path: "/",
  3. component: Layout,
  4. children: [
  5. {
  6. path: "",
  7. component: () => import('views/home.vue'),
  8. name: "Home",
  9. meta: { title: "首页", icon: "el-icon-s-home" },
  10. },
  11. ],
  12. },

7.动态导航

  • 侧边导航

根据路由表动态生成侧边导航菜单。

file

首先创建侧边栏组件,递归输出routes中的配置为多级菜单,layout/Sidebar/index.vue

  1. <template>
  2. <el-scrollbar wrap-class="scrollbar-wrapper">
  3. <el-menu
  4. :default-active="activeMenu"
  5. :background-color="variables.menuBg"
  6. :text-color="variables.menuText"
  7. :unique-opened="false"
  8. :active-text-color="variables.menuActiveText"
  9. mode="vertical"
  10. >
  11. <sidebar-item
  12. v-for="route in routes"
  13. :key="route.path"
  14. :item="route"
  15. :base-path="route.path"
  16. />
  17. </el-menu>
  18. </el-scrollbar>
  19. </template>
  20. <script setup>
  21. import SidebarItem from "./SidebarItem.vue";
  22. import { computed } from "vue";
  23. import { useRoute } from "vue-router";
  24. import { routes } from "@/router";
  25. import variables from "styles/variables.module.scss";
  26. const activeMenu = computed(() => {
  27. const route = useRoute();
  28. const { meta, path } = route;
  29. if (meta.activeMenu) {
  30. return meta.activeMenu;
  31. }
  32. return path;
  33. });
  34. </script>
  • 添加相关样式:

    ○ styles/variables.module.scss

    ○ styles/sidebar.scss

    ○ styles/index.scss中引入

创建SidebarItem.vue组件,解析当前路由是导航链接还是父菜单:

file

8.面包屑

通过路由匹配数组可以动态生成面包屑。

面包屑组件,layouts/components/Breadcrumb.vue

  1. <template>
  2. <el-breadcrumb class="app-breadcrumb" separator="/">
  3. <el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
  4. <span
  5. v-if="item.redirect === 'noRedirect' || index == levelList.length - 1"
  6. class="no-redirect"
  7. >{{ item.meta.title }}</span>
  8. <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
  9. </el-breadcrumb-item>
  10. </el-breadcrumb>
  11. </template>
  12. <script setup>
  13. import { compile } from "path-to-regexp";
  14. import { reactive, ref, watch } from "vue";
  15. import { useRoute, useRouter } from "vue-router";
  16. const levelList = ref(null);
  17. const router = useRouter();
  18. const route = useRoute();
  19. const getBreadcrumb = () => {
  20. let matched = route.matched.filter((item) => item.meta && item.meta.title);
  21. const first = matched[0];
  22. if (first.path !== "/") {
  23. matched = [{ path: "/home", meta: { title: "首页" } }].concat(matched);
  24. }
  25. levelList.value = matched.filter(
  26. (item) => item.meta && item.meta.title && item.meta.breadcrumb !== false
  27. );
  28. }
  29. const pathCompile = (path) => {
  30. var toPath = compile(path);
  31. return toPath(route.params);
  32. }
  33. const handleLink = (item) => {
  34. const { redirect, path } = item;
  35. if (redirect) {
  36. router.push(redirect);
  37. return;
  38. }
  39. router.push(pathCompile(path));
  40. }
  41. getBreadcrumb();
  42. watch(route, getBreadcrumb)
  43. </script>
  44. <style lang="scss" scoped>
  45. .app-breadcrumb.el-breadcrumb {
  46. display: inline-block;
  47. font-size: 14px;
  48. line-height: 50px;
  49. margin-left: 8px;
  50. .no-redirect {
  51. color: #97a8be;
  52. cursor: text;
  53. }
  54. }
  55. </style>

9.数据封装

统一封装数据请求服务,有利于解决一下问题:

  • 统一配置请求
  • 请求、响应统一处理

准备工作:

  • 安装axios:
  1. npm i axios -S

添加配置文件:.env.development

  1. VITE_BASE_API=/api

请求封装 utils/request.js

  1. import axios from "axios";
  2. import { Message, Msgbox } from "element3";
  3. // 创建axios实例
  4. const service = axios.create({
  5. // 在请求地址前面加上baseURL
  6. baseURL: import.meta.env.VITE_BASE_API,
  7. // 当发送跨域请求时携带cookie
  8. // withCredentials: true,
  9. timeout: 5000,
  10. });
  11. // 请求拦截
  12. service.interceptors.request.use(
  13. (config) => {
  14. // 模拟指定请求令牌
  15. config.headers["X-Token"] = "my token";
  16. return config;
  17. },
  18. (error) => {
  19. // 请求错误的统一处理
  20. console.log(error); // for debug
  21. return Promise.reject(error);
  22. }
  23. );
  24. // 响应拦截器
  25. service.interceptors.response.use(
  26. /**
  27. * 通过判断状态码统一处理响应,根据情况修改
  28. * 同时也可以通过HTTP状态码判断请求结果
  29. */
  30. (response) => {
  31. const res = response.data;
  32. // 如果状态码不是20000则认为有错误
  33. if (res.code !== 20000) {
  34. Message.error({
  35. message: res.message || "Error",
  36. duration: 5 * 1000,
  37. });
  38. // 50008: 非法令牌; 50012: 其他客户端已登入; 50014: 令牌过期;
  39. if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
  40. // 重新登录
  41. Msgbox.confirm("您已登出, 请重新登录", "确认", {
  42. confirmButtonText: "重新登录",
  43. cancelButtonText: "取消",
  44. type: "warning",
  45. }).then(() => {
  46. store.dispatch("user/resetToken").then(() => {
  47. location.reload();
  48. });
  49. });
  50. }
  51. return Promise.reject(new Error(res.message || "Error"));
  52. } else {
  53. return res;
  54. }
  55. },
  56. (error) => {
  57. console.log("err" + error); // for debug
  58. Message({
  59. message: error.message,
  60. type: "error",
  61. duration: 5 * 1000,
  62. });
  63. return Promise.reject(error);
  64. }
  65. );
  66. export default service;

10.常见业务处理

  • 结构化数据展

使用el-table展示结构化数据,配合el-pagination做数据分页。

file

文件组织结构如下:list.vue展示列表,edit.vue和create.vue编辑或创建,内部复用detail.vue处理,model中负责数据业务处理。

file

list.vue中的数据展示

  1. <el-table v-loading="loading" :data="list">
  2. <el-table-column label="ID" prop="id"></el-table-column>
  3. <el-table-column label="账户名" prop="name"></el-table-column>
  4. <el-table-column label="年龄" prop="age"></el-table-column>
  5. </el-table>

list和loading数据的获取逻辑,可以使用compsition-api提取到userModel.js

  1. export function useList() {
  2. // 列表数据
  3. const state = reactive({
  4. loading: true, // 加载状态
  5. list: [], // 列表数据
  6. });
  7. // 获取列表
  8. function getList() {
  9. state.loading = true;
  10. return request({
  11. url: "/getUsers",
  12. method: "get",
  13. }).then(({ data, total }) => {
  14. // 设置列表数据
  15. state.list = data;
  16. }).finally(() => {
  17. state.loading = false;
  18. });
  19. }
  20. // 首次获取数据
  21. getList();
  22. return { state, getList };
  23. }

list.vue中使用

  1. import { useList } from "./model/userModel";
  1. const { state, getList } = useList();

分页处理 list.vue

  1. <pagination
  2. :total="total"
  3. v-model:page="listQuery.page"
  4. v-model:limit="listQuery.limit"
  5. @pagination="getList"
  6. ></pagination>

数据也在userModel中处理

  1. const state = reactive({
  2. total: 0, // 总条数
  3. listQuery: {// 分页查询参数
  4. page: 1, // 当前页码
  5. limit: 5, // 每页条数
  6. },
  7. });
  1. request({
  2. url: "/getUsers",
  3. method: "get",
  4. params: state.listQuery, // 在查询中加入分页参数
  5. })

11.表单处理

用户数据新增、编辑使用el-form处理

可用一个组件detail.vue来处理,区别仅在于初始化时是否获取信息回填到表单。

  1. <el-form ref="form" :model="model" :rules="rules">
  2. <el-form-item prop="name" label="用户名">
  3. <el-input v-model="model.name"></el-input>
  4. </el-form-item>
  5. <el-form-item prop="age" label="用户年龄">
  6. <el-input v-model.number="model.age"></el-input>
  7. </el-form-item>
  8. <el-form-item>
  9. <el-button @click="submitForm" type="primary">提交</el-button>
  10. </el-form-item>
  11. </el-form>

数据处理同样可以提取到userModel中处理。

  1. export function useItem(isEdit, id) {
  2. const model = ref(Object.assign({}, defaultData));
  3. // 初始化时,根据isEdit判定是否需要获取详情
  4. onMounted(() => {
  5. if (isEdit && id) {
  6. // 获取详情
  7. request({
  8. url: "/getUser",
  9. method: "get",
  10. params: { id },
  11. }).then(({ data }) => {
  12. model.value = data;
  13. });
  14. }
  15. });
  16. return { model };
  17. }

三 、 云端一键部署上线应用

1.上传代码

  1. git add .
  2. git commit -m '添加你的注释'
  3. git push

2.在日常环境部署

一键进行应用部署。在应用详情页面点击日常环境的「部署」按钮进行一键部署,部署状态变成绿色已部署以后可以点击访问部署网站查看效果。

file

3.配置自定义域名在线上环境上线

  • 配置线上环境自定义域名。在功能开发验证完成后要在线上环境进行部署,在线上环境的「部署配置」-「自定义域名」中填写自己的域名。例如我们添加一个二级域名 company.workbench.fun 来绑定我们部署的前端应用。然后复制自定义域名下方的API网关地址对添加的二级域名进行CNAME配置。

file

  • 配置CNAME地址。复制好 API网关域名地址后,来到你自己的域名管理平台(此示例中的域名管理是阿里云的域名管理控制台,请去自己的域名控制台操作)。添加记录的「记录类型」选择「CNAME」,在「主机记录」中输入你要创建的二级域名,这里我们输入「company」,在「记录值」中粘贴我们之前复制的 API网关域名地址,「TTL」保留默认值或者设置一个你认为合适的值即可。

file

  • 在线上环境部署上线。回到云开发平台的应用详情页面,按照部署的操作,点击线上环境的「部署按钮」,部署完成以后就在你自定义的域名进行了上线。CNAME 生效之后,我们输入 company.workbench.fun(示例网址) 可以打开部署的页面。至此,如何部署一个应用到线上环境,如何绑定自己的域名来访问一个线上的应用就完成了,赶紧部署自己的应用到线上环境,用自己的域名玩起来吧 ;)

file

4.项目预览效果

file

一键创建Vite应用模版链接 :https://workbench.aliyun.com/application/front/create?fromConfig=27&fromRepo=sol_github_27

参考文献:https://juejin.cn/post/6926822933721513998#heading-22

Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post