ASCII码 ASCII码

vite+vue3项目最佳起始点(保姆级)

发布于:2022-06-16 10:52:08  栏目:技术文档

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

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

相关推荐
阅读 +