{"id":19750485,"url":"https://github.com/57code/vite2-in-action","last_synced_at":"2025-04-13T06:41:54.853Z","repository":{"id":44053578,"uuid":"330351906","full_name":"57code/vite2-in-action","owner":"57code","description":"一个关于vite2+vue3项目开发中常见任务实践的库，很适合作为一个项目的起始点，欢迎大家提pr丰富这个项目。","archived":false,"fork":false,"pushed_at":"2021-03-03T02:16:51.000Z","size":71,"stargazers_count":236,"open_issues_count":4,"forks_count":56,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-26T23:08:55.872Z","etag":null,"topics":["vite","vite2","vue3"],"latest_commit_sha":null,"homepage":"","language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/57code.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-17T09:05:03.000Z","updated_at":"2024-12-26T08:48:52.000Z","dependencies_parsed_at":"2022-09-12T22:20:19.309Z","dependency_job_id":null,"html_url":"https://github.com/57code/vite2-in-action","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/57code%2Fvite2-in-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/57code%2Fvite2-in-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/57code%2Fvite2-in-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/57code%2Fvite2-in-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/57code","download_url":"https://codeload.github.com/57code/vite2-in-action/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248675435,"owners_count":21143763,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["vite","vite2","vue3"],"created_at":"2024-11-12T02:35:31.076Z","updated_at":"2025-04-13T06:41:54.832Z","avatar_url":"https://github.com/57code.png","language":"Vue","readme":"## Vite2项目最佳实践\n\n### 配套视频演示\n\n我专门录了一套视频演示本文所做的所有操作，喜欢看视频学习的小伙伴移步：\n[「备战2021」Vite2 + Vue3项目最佳实践](https://www.bilibili.com/video/BV1vX4y1K7bQ)\n\n制作不易，求`3连`，求`关注`\n\n### vite2来了\n\n`Vite1`还没用上，`Vite2`已经更新了，全新插件架构，丝滑的开发体验，和`Vue3`的完美结合。 2021年第一弹，村长打算以Vite2+Vue3为主题开启大家的前端学习之旅。\n\n### 2021先学学vite准没错\n\n![img](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a26ab28cab8d45a981986b581ae71d04~tplv-k3u1fbpfcp-zoom-1.image)\n\n### 本文目标\n\n- `vite2`变化分析\n- 项目中常见任务`vite2+vue3`实践\n\n\n### 创建Vite2项目\n\n闲言碎语不必说，下面我们表一表好汉`vite2`\n\n使用npm:\n\n```bash\n$ npm init @vitejs/app\n```\n\n\u003e 按提示指定项目名称和模板，或直接指定\n\u003e\n\u003e ```bash\n\u003e $ npm init @vitejs/app my-vue-app --template vue\n\u003e ```\n\n\n\n### Vite2主要变化\n\n对我们之前项目影响较大的我已经都标记出来了：\n\n- 配置选项变化：`vue特有选项`、创建选项、css选项、jsx选项等\n- `别名行为变化`：不再要求`/`开头或结尾\n- `Vue支持`：通过 [@vitejs/plugin-vue](https://github.com/vitejs/vite/tree/main/packages/plugin-vue)插件支持\n- React支持\n- HMR API变化\n- 清单格式变化\n- `插件API重新设计`\n\n\n\n#### Vue支持\n\nVue的整合也通过插件实现，和其他框架一视同仁：\n\n\u003cimg src=\"https://gitee.com/57code/picgo/raw/master/image-20210114183159562.png\" style=\"zoom:80%;\" /\u003e\n\n\n\nSFC定义默认使用`setup script`，语法比较激进，但更简洁，好评！\n\n\u003cimg src=\"https://gitee.com/57code/picgo/raw/master/image-20210116192013356.png\" style=\"zoom:40%;\" /\u003e\n\n#### 别名定义\n\n不再需要像`vite1`一样在别名前后加上`/`，这和`webpack`项目配置可以保持一致便于移植，好评！\n\n```js\nimport path from 'path'\n\nexport default {\n  alias: {\n    \"@\": path.resolve(__dirname, \"src\"),\n    \"comps\": path.resolve(__dirname, \"src/components\"),\n  },\n}\n```\n\n`App.vue`里面用一下试试\n\n```vue\n\u003cscript setup\u003e\nimport HelloWorld from 'comps/HelloWorld.vue'\n\u003c/script\u003e\n```\n\n\n\n#### 插件API重新设计\n\n`Vite2`主要变化在插件体系，这样更标准化、易扩展。`Vite2`插件API扩展自`Rollup`插件体系，因此能兼容现存的`Rollup`插件，编写的Vite插件也可以同时运行于开发和创建，好评！\n\n\u003e 插件编写我会另开专题讨论，欢迎大家关注我。\n\n\n\n##### Vue3 Jsx支持\n\n`vue3`中`jsx`支持需要引入插件：`@vitejs/plugin-vue-jsx`\n\n```bash\n$ npm i @vitejs/plugin-vue-jsx -D\n```\n\n注册插件，`vite.config.js`\n\n```js\nimport vueJsx from \"@vitejs/plugin-vue-jsx\";\n\nexport default {\n  plugins: [vue(), vueJsx()],\n}\n```\n\n用法也有要求，改造一下`App.vue`\n\n```vue\n\u003c!-- 1.标记为jsx --\u003e\n\u003cscript setup lang=\"jsx\"\u003e\nimport { defineComponent } from \"vue\";\nimport HelloWorld from \"comps/HelloWorld.vue\";\nimport logo from \"./assets/logo.png\"\n\n// 2.用defineComponent定义组件且要导出\nexport default defineComponent({\n  render: () =\u003e (\n    \u003c\u003e\n      \u003cimg alt=\"Vue logo\" src={logo} /\u003e\n      \u003cHelloWorld msg=\"Hello Vue 3 + Vite\" /\u003e\n    \u003c/\u003e\n  ),\n});\n\u003c/script\u003e\n```\n\n\n\n##### Mock插件应用\n\n之前给大家介绍的[vite-plugin-mock](https://github.com/vbenjs/vite-plugin-mock)已经重构支持了Vite2。\n\n\n\n安装插件\n\n```bash\nnpm i mockjs -S\n```\n\n```bash\nnpm i vite-plugin-mock cross-env -D\n```\n\n\n\n配置，`vite.config.js`\n\n```js\nimport { viteMockServe } from 'vite-plugin-mock'\n\nexport default {\n  plugins: [ viteMockServe({ supportTs: false }) ]\n}\n```\n\n\n\n设置环境变量，`package.json`\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"cross-env NODE_ENV=development vite\",\n    \"build\": \"vite build\"\n  },\n} \n```\n\n\n\n\n\n### 项目基础架构\n\n#### 路由\n\n安装`vue-router 4.x`\n\n```js\nnpm i vue-router@next -S\n```\n\n\u003cimg src=\"https://gitee.com/57code/picgo/raw/master/image-20210118170758418.png\" style=\"zoom:33%;\" /\u003e\n\n\n\n路由配置，`router/index.js`\n\n```js\nimport { createRouter, createWebHashHistory } from 'vue-router';\n\nconst router = createRouter({\n  history: createWebHashHistory(),\n  routes: [\n    { path: '/', component: () =\u003e import('views/home.vue') }\n  ]\n});\n\nexport default router\n```\n\n\n\n引入，`main.js`\n\n```js\nimport router from \"@/router\";\ncreateApp(App).use(router).mount(\"#app\");\n```\n\n\u003e 别忘了创建`home.vue`并修改`App.vue`\n\u003e\n\u003e 路由用法略有变化，[村长的视频教程](https://www.bilibili.com/video/BV1Wh411X7Xp?p=19)\n\n\n\n#### 状态管理\n\n安装`vuex 4.x`\n\n```bash\nnpm i vuex@next -S\n```\n\n\u003cimg src=\"https://gitee.com/57code/picgo/raw/master/image-20210118181504903.png\" alt=\"image\" style=\"zoom:33%;\" /\u003e\n\n\n\nStore配置，`store/index.js`\n\n```js\nimport {createStore} from 'vuex';\n\nexport default createStore({\n  state: {\n    couter: 0\n  }\n});\n```\n\n\n\n引入，`main.js`\n\n```js\nimport store from \"@/store\";\ncreateApp(App).use(store).mount(\"#app\");\n```\n\n\u003e 用法和以前基本一样，[村长的视频教程](https://www.bilibili.com/video/BV1Wh411X7Xp?p=23)\n\n\n\n\n\n#### 样式组织\n\n 安装sass\n\n```bash\nnpm i sass -D\n```\n\n\n\n`styles`目录保存各种样式\n\n![截屏2020-12-24 上午11.51.30](https://gitee.com/57code/picgo/raw/master/%E6%88%AA%E5%B1%8F2020-12-24%20%E4%B8%8A%E5%8D%8811.51.30.png)\n\n`index.scss`作为出口组织这些样式，同时编写一些全局样式\n\n![image-20201224115414266](https://gitee.com/57code/picgo/raw/master/image-20201224115414266.png)\n\n最后在`main.js`导入\n\n```js\nimport \"styles/index.scss\";\n```\n\n\u003e 注意在`vite.config.js`添加`styles`别名\n\n\n\n#### UI库\n\n就用我们[花果山团队](https://www.yuque.com/hugsun)自家的[element3](https://github.com/hug-sun/element3)。\n\n\u003e [中文文档](https://element3-ui.com/)\n\n\n\n安装\n\n```bash\nnpm i element3 -S\n```\n\n\n\n完整引入，`main.js`\n\n```js\nimport element3 from \"element3\";\nimport \"element3/lib/theme-chalk/index.css\";\n\ncreateApp(App).use(element3)\n```\n\n\n\n按需引入，`main.js`\n\n```js\nimport \"element3/lib/theme-chalk/button.css\";\nimport { ElButton } from \"element3\"\ncreateApp(App).use(ElButton)\n```\n\n\n\n抽取成插件会更好，`plugins/element3.js`\n\n```js\n// 完整引入\nimport element3 from \"element3\";\nimport \"element3/lib/theme-chalk/index.css\";\n\n// 按需引入\n// import { ElButton } from \"element3\";\n// import \"element3/lib/theme-chalk/button.css\";\n\nexport default function (app) {\n  // 完整引入\n  app.use(element3)\n\n  // 按需引入\n  // app.use(ElButton);\n}\n```\n\n\n\n测试\n\n```html\n\u003cel-button\u003emy button\u003c/el-button\u003e\n```\n\n\n\n#### 基础布局\n\n我们应用需要一个基本布局页，类似下图，将来每个页面以布局页为父页面即可：\n\n![image-20201223143247535](https://gitee.com/57code/picgo/raw/master/image-20201223143247535.png)\n\n\n\n布局页面，`layout/index.vue`\n\n```vue\n\u003ctemplate\u003e\n  \u003cdiv class=\"app-wrapper\"\u003e\n    \u003c!-- 侧边栏 --\u003e\n    \u003cdiv class=\"sidebar-container\"\u003e\u003c/div\u003e\n    \u003c!-- 内容容器 --\u003e\n    \u003cdiv class=\"main-container\"\u003e\n      \u003c!-- 顶部导航栏 --\u003e\n      \u003cnavbar /\u003e\n      \u003c!-- 内容区 --\u003e\n      \u003capp-main /\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript setup\u003e\nimport AppMain from \"./components/AppMain.vue\";\nimport Navbar from \"./components/Navbar.vue\";\n\u003c/script\u003e\n\n\u003cstyle lang=\"scss\" scoped\u003e\n@import \"../styles/mixin.scss\";\n\n.app-wrapper {\n  @include clearfix;\n  position: relative;\n  height: 100%;\n  width: 100%;\n}\n\u003c/style\u003e\n```\n\n\u003e 别忘了创建`AppMain.vue`和`Navbar.vue`\n\n\n\n路由配置，`router/index.js`\n\n```js\n{\n  path: \"/\",\n\tcomponent: Layout,\n  children: [\n    {\n      path: \"\",\n      component: () =\u003e import('views/home.vue'),\n      name: \"Home\",\n      meta: { title: \"首页\", icon: \"el-icon-s-home\" },\n    },\n  ],\n},\n```\n\n\n\n#### 动态导航\n\n##### 侧边导航\n\n根据路由表动态生成侧边导航菜单。\n\n![image-20201225180300250](https://gitee.com/57code/picgo/raw/master/image-20201225180300250.png)\n\n\n\n首先创建侧边栏组件，递归输出`routes`中的配置为多级菜单，`layout/Sidebar/index.vue`\n\n```vue\n\u003ctemplate\u003e\n  \u003cel-scrollbar wrap-class=\"scrollbar-wrapper\"\u003e\n    \u003cel-menu\n      :default-active=\"activeMenu\"\n      :background-color=\"variables.menuBg\"\n      :text-color=\"variables.menuText\"\n      :unique-opened=\"false\"\n      :active-text-color=\"variables.menuActiveText\"\n      mode=\"vertical\"\n    \u003e\n      \u003csidebar-item\n        v-for=\"route in routes\"\n        :key=\"route.path\"\n        :item=\"route\"\n        :base-path=\"route.path\"\n      /\u003e\n    \u003c/el-menu\u003e\n  \u003c/el-scrollbar\u003e\n\u003c/template\u003e\n\n\u003cscript setup\u003e\nimport SidebarItem from \"./SidebarItem.vue\";\nimport { computed } from \"vue\";\nimport { useRoute } from \"vue-router\";\nimport { routes } from \"@/router\";\nimport variables from \"styles/variables.module.scss\";\n\nconst activeMenu = computed(() =\u003e {\n  const route = useRoute();\n  const { meta, path } = route;\n  if (meta.activeMenu) {\n    return meta.activeMenu;\n  }\n  return path;\n});\n\u003c/script\u003e\n\n```\n\n\u003e 注意：`sass`文件导出变量解析需要用到`css module`，因此`variables`文件要加上`module`中缀。\n\n\n\n添加相关样式：\n\n- `styles/variables.module.scss`\n- `styles/sidebar.scss`\n- `styles/index.scss`中引入\n\n\n\n创建`SidebarItem.vue`组件，解析当前路由是导航链接还是父菜单：\n\n![image-20201229123955087](https://gitee.com/57code/picgo/raw/master/image-20201229123955087.png)\n\n\n\n##### 面包屑\n\n通过路由匹配数组可以动态生成面包屑。\n\n\n\n面包屑组件，`layouts/components/Breadcrumb.vue`\n\n```vue\n\u003ctemplate\u003e\n  \u003cel-breadcrumb class=\"app-breadcrumb\" separator=\"/\"\u003e\n      \u003cel-breadcrumb-item v-for=\"(item, index) in levelList\" :key=\"item.path\"\u003e\n        \u003cspan\n          v-if=\"item.redirect === 'noRedirect' || index == levelList.length - 1\"\n          class=\"no-redirect\"\n          \u003e{{ item.meta.title }}\u003c/span\u003e\n        \u003ca v-else @click.prevent=\"handleLink(item)\"\u003e{{ item.meta.title }}\u003c/a\u003e\n      \u003c/el-breadcrumb-item\u003e\n  \u003c/el-breadcrumb\u003e\n\u003c/template\u003e\n\n\u003cscript setup\u003e\nimport { compile } from \"path-to-regexp\";\nimport { reactive, ref, watch } from \"vue\";\nimport { useRoute, useRouter } from \"vue-router\";\n\nconst levelList = ref(null);\nconst router = useRouter();\nconst route = useRoute();\n\nconst getBreadcrumb = () =\u003e {\n  let matched = route.matched.filter((item) =\u003e item.meta \u0026\u0026 item.meta.title);\n\n  const first = matched[0];\n  if (first.path !== \"/\") {\n    matched = [{ path: \"/home\", meta: { title: \"首页\" } }].concat(matched);\n  }\n\n  levelList.value = matched.filter(\n    (item) =\u003e item.meta \u0026\u0026 item.meta.title \u0026\u0026 item.meta.breadcrumb !== false\n  );\n}\n\nconst pathCompile = (path) =\u003e {  \n  var toPath = compile(path);\n  return toPath(route.params);\n}\n\nconst handleLink = (item) =\u003e {\n  const { redirect, path } = item;\n  if (redirect) {\n    router.push(redirect);\n    return;\n  }\n  router.push(pathCompile(path));\n}\n\ngetBreadcrumb();\nwatch(route, getBreadcrumb)\n\n\u003c/script\u003e\n\n\u003cstyle lang=\"scss\" scoped\u003e\n.app-breadcrumb.el-breadcrumb {\n  display: inline-block;\n  font-size: 14px;\n  line-height: 50px;\n  margin-left: 8px;\n\n  .no-redirect {\n    color: #97a8be;\n    cursor: text;\n  }\n}\n\u003c/style\u003e\n```\n\n\u003e 别忘了添加依赖：`path-to-regexp`\n\u003e\n\u003e 注意：`vue-router4`已经不再使用`path-to-regexp`解析动态`path`，因此这里后续还需要改进。\n\n\n\n#### 数据封装\n\n统一封装数据请求服务，有利于解决一下问题：\n\n- 统一配置请求\n- 请求、响应统一处理\n\n\n\n准备工作：\n\n- 安装`axios`: \n\n  ```bash\n  npm i axios -S\n  ```\n\n- 添加配置文件：`.env.development`\n\n  ```\n  VITE_BASE_API=/api\n  ```\n\n\n\n请求封装，`utils/request.js`\n\n```js\nimport axios from \"axios\";\nimport { Message, Msgbox } from \"element3\";\n\n// 创建axios实例\nconst service = axios.create({\n  // 在请求地址前面加上baseURL\n  baseURL: import.meta.env.VITE_BASE_API,\n  // 当发送跨域请求时携带cookie\n  // withCredentials: true,\n  timeout: 5000,\n});\n\n// 请求拦截\nservice.interceptors.request.use(\n  (config) =\u003e {\n    // 模拟指定请求令牌\n    config.headers[\"X-Token\"] = \"my token\";\n    return config;\n  },\n  (error) =\u003e {\n    // 请求错误的统一处理\n    console.log(error); // for debug\n    return Promise.reject(error);\n  }\n);\n\n// 响应拦截器\nservice.interceptors.response.use(\n  /**\n   * 通过判断状态码统一处理响应，根据情况修改\n   * 同时也可以通过HTTP状态码判断请求结果\n   */\n  (response) =\u003e {\n    const res = response.data;\n\n    // 如果状态码不是20000则认为有错误\n    if (res.code !== 20000) {\n      Message.error({\n        message: res.message || \"Error\",\n        duration: 5 * 1000,\n      });\n\n      // 50008: 非法令牌; 50012: 其他客户端已登入; 50014: 令牌过期;\n      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {\n        // 重新登录\n        Msgbox.confirm(\"您已登出, 请重新登录\", \"确认\", {\n          confirmButtonText: \"重新登录\",\n          cancelButtonText: \"取消\",\n          type: \"warning\",\n        }).then(() =\u003e {\n          store.dispatch(\"user/resetToken\").then(() =\u003e {\n            location.reload();\n          });\n        });\n      }\n      return Promise.reject(new Error(res.message || \"Error\"));\n    } else {\n      return res;\n    }\n  },\n  (error) =\u003e {\n    console.log(\"err\" + error); // for debug\n    Message({\n      message: error.message,\n      type: \"error\",\n      duration: 5 * 1000,\n    });\n    return Promise.reject(error);\n  }\n);\n\nexport default service;\n\n```\n\n\n\n#### 业务处理\n\n##### 结构化数据展示\n\n使用`el-table`展示结构化数据，配合`el-pagination`做数据分页。\n\n![image-20210201110626262](https://gitee.com/57code/picgo/raw/master/image-20210201110626262.png)\n\n文件组织结构如下：`list.vue`展示列表，`edit.vue`和`create.vue`编辑或创建，内部复用`detail.vue`处理，`model`中负责数据业务处理。\n\n![image-20210201110542893](https://gitee.com/57code/picgo/raw/master/image-20210201110542893.png)\n\n`list.vue`中的数据展示\n\n```vue\n\u003cel-table v-loading=\"loading\" :data=\"list\"\u003e\n  \u003cel-table-column label=\"ID\" prop=\"id\"\u003e\u003c/el-table-column\u003e\n  \u003cel-table-column label=\"账户名\" prop=\"name\"\u003e\u003c/el-table-column\u003e\n  \u003cel-table-column label=\"年龄\" prop=\"age\"\u003e\u003c/el-table-column\u003e\n\u003c/el-table\u003e\n```\n\n\n\n`list`和`loading`数据的获取逻辑，可以使用`compsition-api`提取到`userModel.js`\n\n```js\nexport function useList() {\n  // 列表数据\n  const state = reactive({\n    loading: true, // 加载状态\n    list: [], // 列表数据\n  });\n\n  // 获取列表\n  function getList() {\n    state.loading = true;\n    return request({\n      url: \"/getUsers\",\n      method: \"get\",\n    }).then(({ data, total }) =\u003e {\n      // 设置列表数据\n      state.list = data;\n    }).finally(() =\u003e {\n      state.loading = false;\n    });\n  }\n  \n  // 首次获取数据\n  getList();\n\n  return { state, getList };\n}\n```\n\n\n\n`list.vue`中使用\n\n```js\nimport { useList } from \"./model/userModel\";\n```\n\n```js\nconst { state, getList } = useList();\n```\n\n\n\n分页处理，`list.vue`\n\n```html\n\u003cpagination\n      :total=\"total\"\n      v-model:page=\"listQuery.page\"\n      v-model:limit=\"listQuery.limit\"\n      @pagination=\"getList\"\n    \u003e\u003c/pagination\u003e\n```\n\n数据也在`userModel`中处理\n\n```js\nconst state = reactive({\n  total: 0,   // 总条数\n  listQuery: {// 分页查询参数\n    page: 1,  // 当前页码\n    limit: 5, // 每页条数\n  },\n});\n```\n\n```js\nrequest({\n  url: \"/getUsers\",\n  method: \"get\",\n  params: state.listQuery, // 在查询中加入分页参数\n})\n```\n\n\n\n##### 表单处理\n\n用户数据新增、编辑使用`el-form`处理\n\n\n\n可用一个组件`detail.vue`来处理，区别仅在于初始化时是否获取信息回填到表单。\n\n```html\n\u003cel-form ref=\"form\" :model=\"model\" :rules=\"rules\"\u003e\n  \u003cel-form-item prop=\"name\" label=\"用户名\"\u003e\n    \u003cel-input v-model=\"model.name\"\u003e\u003c/el-input\u003e\n  \u003c/el-form-item\u003e\n  \u003cel-form-item prop=\"age\" label=\"用户年龄\"\u003e\n    \u003cel-input v-model.number=\"model.age\"\u003e\u003c/el-input\u003e\n  \u003c/el-form-item\u003e\n  \u003cel-form-item\u003e\n    \u003cel-button @click=\"submitForm\" type=\"primary\"\u003e提交\u003c/el-button\u003e\n  \u003c/el-form-item\u003e\n\u003c/el-form\u003e\n```\n\n\n\n数据处理同样可以提取到`userModel`中处理。\n\n```js\nexport function useItem(isEdit, id) {\n  const model = ref(Object.assign({}, defaultData));\n\n  // 初始化时，根据isEdit判定是否需要获取详情\n  onMounted(() =\u003e {\n    if (isEdit \u0026\u0026 id) {\n      // 获取详情\n      request({\n        url: \"/getUser\",\n        method: \"get\",\n        params: { id },\n      }).then(({ data }) =\u003e {\n        model.value = data;\n      });\n    }\n  });\n  return { model };\n}\n```\n\n### 配套视频演示\n\n我专门录了一套视频演示本文所做的所有操作，喜欢看视频学习的小伙伴移步：\n[「备战2021」Vite2 + Vue3项目最佳实践](https://www.bilibili.com/video/BV1vX4y1K7bQ)\n\n制作不易，求`3连`，求`关注`\n\n\n### 关注村长\n\n欢迎关注我的公众号「村长学前端」跟我一起学习最新前端知识。\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F57code%2Fvite2-in-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F57code%2Fvite2-in-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F57code%2Fvite2-in-action/lists"}