Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南

马肤

温馨提示:这篇文章已超过402天没有更新,请注意相关的内容是否还可用!

Vue3实战教程,专为初学者设计,帮助快速入门Vue框架。教程内容涵盖Vue3基础概念、安装配置、核心组件、实战案例等。通过简洁明了的语言和详细的步骤,让读者轻松掌握Vue3开发技能。适合前端开发者、UI设计师及任何对Vue3感兴趣的人群。跟随教程,快速构建现代Web应用程序。

Vue3实战教程(快速入门)

  • 前言
  • 1.搭建脚手架
    • 1.1 创建项目
    • 1.2 清除多余文件,创建干净项目
    • 1.3 创建登录页面
    • 2.创建404页面(引入sass)
      • 2.1 引入sass
      • 2.2 创建404页面
      • 3.构建登录注册页面(引入element-plus)
        • 3.1 实现布局左右切换动画
        • 3.2 引入element-plus
        • 3.3 使用element-plus表单组件
        • 3.4 表单验证
        • 4.类型匹配和代码抽离
          • 4.1 代码抽离
          • 4.2 类型匹配
            • 4.2.1 ts文件类型匹配
            • 4.2.2 vue文件中的ts类型匹配
            • 5.抽离登录组件
            • 6. 实现注册表单
              • 6.1 创建注册表单ts——存放注册表单及其验证规则
              • 6.2 创建注册表单组件
              • 6.3 在LoginRegister.vue中引入使用
              • 7. 封装axios
                • 7.1 下载axios
                • 7.2 封装axios
                • 7.3 解决跨域问题(配置vue.config.js,设置代理)
                • 7.4 使用axios发起请求
                  • 7.4.1 创建api文件夹,规范使用api(推荐)
                  • 7.4.2 全局注册axios(不推荐,也没必要)
                  • 8.总结

                    前言

                    本教程通过搭建一个简单项目,帮助读者快速入门Vue3项目实战,掌握Vue3、TS、Element Plus、axios等技术栈。

                    1.搭建脚手架

                    vue -V查看vue版本,需要在4.5.1版本之后,即可进行以下操作

                    1.1 创建项目

                    (1)使用命令 vue create vue3-elementplus-demo 创建Vue项目。

                    (2)进入选项配置,选择 Manually select features,进行手动配置

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第1张

                    (3)配置项如下

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第2张

                    都选择完毕后,回车,项目即可创建完毕,使用VsCode或者按照提示进入和启动项目

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第3张

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第4张

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第5张

                    1.2 清除多余文件,创建干净项目

                    (1)删除以下文件

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第6张

                    (2)在views目录下创建Index.vue文件(后面处于方便,又将Index.vue修改成了Home.vue),内容如下:

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第7张

                      
                    首页
                    export default { name: 'Index' }

                    (3)修改router/index.ts路由文件:

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第8张

                    import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
                    import Home from '../views/Home.vue'
                    const routes: Array = [
                      {
                        path: '/',
                        name: 'Index',
                        component: () => import('../views/Index.vue')
                      },
                    ]
                    const router = createRouter({
                      history: createWebHistory(process.env.BASE_URL),
                      routes
                    })
                    export default router
                    

                    (4)修改App文件:

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第9张

                      
                    html, body, #app { width: 100%; height: 100%; }

                    修改完毕后,查看效果

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第10张

                    (5)新建css/resset.css文件(上网搜关键词reset.css就有),并在index.html文件中引入,初始化样式

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第11张

                    /**
                     * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/)
                     * http://cssreset.com
                     */
                     
                     html, body, div, span, applet, object, iframe,
                     h1, h2, h3, h4, h5, h6, p, blockquote, pre,
                     a, abbr, acronym, address, big, cite, code,
                     del, dfn, em, img, ins, kbd, q, s, samp,
                     small, strike, strong, sub, sup, tt, var,
                     b, u, i, center,
                     dl, dt, dd, ol, ul, li,
                     fieldset, form, label, legend,
                     table, caption, tbody, tfoot, thead, tr, th, td,
                     article, aside, canvas, details, embed, 
                     figure, figcaption, footer, header, hgroup, 
                     menu, nav, output, ruby, section, summary,
                     time, mark, audio, video{
                       margin: 0;
                       padding: 0;
                       border: 0;
                       font-size: 100%;
                       font: inherit;
                       font-weight: normal;
                       vertical-align: baseline;
                     }
                     /* HTML5 display-role reset for older browsers */
                     article, aside, details, figcaption, figure, 
                     footer, header, hgroup, menu, nav, section{
                       display: block;
                     }
                     ol, ul, li{
                       list-style: none;
                     }
                     blockquote, q{
                       quotes: none;
                     }
                     blockquote:before, blockquote:after,
                     q:before, q:after{
                       content: '';
                       content: none;
                     }
                     table{
                       border-collapse: collapse;
                       border-spacing: 0;
                     }
                      
                     /* custom */
                     a{
                       color: #7e8c8d;
                       text-decoration: none;
                       -webkit-backface-visibility: hidden;
                     }
                     ::-webkit-scrollbar{
                       width: 5px;
                       height: 5px;
                     }
                     ::-webkit-scrollbar-track-piece{
                       background-color: rgba(0, 0, 0, 0.2);
                       -webkit-border-radius: 6px;
                     }
                     ::-webkit-scrollbar-thumb:vertical{
                       height: 5px;
                       background-color: rgba(125, 125, 125, 0.7);
                       -webkit-border-radius: 6px;
                     }
                     ::-webkit-scrollbar-thumb:horizontal{
                       width: 5px;
                       background-color: rgba(125, 125, 125, 0.7);
                       -webkit-border-radius: 6px;
                     }
                     html, body{
                       width: 100%;
                       font-family: "Arial", "Microsoft YaHei", "黑体", "宋体", "微软雅黑", sans-serif;
                     }
                     body{
                       line-height: 1;
                       -webkit-text-size-adjust: none;
                       -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
                     }
                     html{
                       overflow-y: scroll;
                     }
                      
                     /*清除浮动*/
                     .clearfix:before,
                     .clearfix:after{
                       content: " ";
                       display: inline-block;
                       height: 0;
                       clear: both;
                       visibility: hidden;
                     }
                     .clearfix{
                       *zoom: 1;
                     }
                      
                     /*隐藏*/
                     .dn{
                       display: none;
                     }
                    

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第12张

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第13张

                    1.3 创建登录页面

                    创建LoginRegister.vue文件:

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第14张

                      
                    export default { name: 'LoginRegister' } .container { position: relative; width: 100%; min-height: 100vh; background-color: #fff; overflow: hidden; } .form-container { position: absolute; left: 0; top: 0; width: 100%; height: 100%; } .signin-signup { position: relative; top: 50%; left: 75%; transform: translate(-50%, -50%); width: 44%; transition: 1s 0.7s ease-in-out; display: grid; grid-template-columns: 1fr; z-index: 5; }

                    在路由中引入

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第15张

                    查看效果:

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第16张

                    2.创建404页面(引入sass)

                    2.1 引入sass

                    (1)查看当前node版本

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第17张

                    (2)引入对应版本的node-sass和sass-load

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第18张

                    当前已知 node-sass 与 node 版本对应如下:https://github.com/sass/node-sass

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第19张

                    node-sass 和 sass-loader 的常见版本对应关系如下:

                    node-sasssass-loader
                    4.3.04.1.1
                    4.7.2.0.3/7.3.1
                    6.0.110.0.1

                    (3)如果引入出现了问题,基本上就是node版本和sass版本不一致导致。此时需要创建一个新项目,将新项目中的package.json和package-lock.json复制到当前项目中,然后重新 npm i 即可。

                    2.2 创建404页面

                    (1)assets下创建img文件夹,加入404.gif

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第20张

                    (2)创建404.vue

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第21张

                      
                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第22张
                    export default { name: '404' } .not-found { width: 100%; height: 100%; overflow: hidden; img { width: 100%; height: 100%; } }

                    (3)router/index.ts中通过正则表达式匹配匹配失败的路由为404页面

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第23张

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第24张

                    3.构建登录注册页面(引入element-plus)

                    3.1 实现布局左右切换动画

                    因为本篇文章主要讲解的是Vue3和element-plus的用法,css部分就省略说明,有兴趣的同学可以自行研究。

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第25张

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第26张

                      

                    Row,row,row your boat

                    Gentlely down the stream

                    注册

                    Merrily,merrily,merrily,merrily,

                    Life is but a dream

                    登录
                    import { ref } from 'vue' export default { name: 'LoginRegister', components: {}, // Vue3语法糖 // Vue2是通过data和methods传递数据和方法 // Vue3将data和methods直接耦合在了一起 setup() { // 登录/注册模式 const signUpMode = ref(false) return { signUpMode } } } .container { position: relative; width: 100%; min-height: 100vh; background-color: #fff; overflow: hidden; } .form-container { position: absolute; left: 0; top: 0; width: 100%; height: 100%; } .signin-signup { position: relative; top: 50%; left: 75%; transform: translate(-50%, -50%); width: 44%; transition: 1s 0.7s ease-in-out; display: grid; grid-template-columns: 1fr; z-index: 5; } /* 左右切换动画 */ .social-text { padding: 0.7rem 0; font-size: 1rem; } .social-media { display: flex; justify-content: center; } .social-icon { height: 46px; width: 46px; display: flex; justify-content: center; align-items: center; margin: 0 0.45rem; color: #333; border-radius: 50%; border: 1px solid #333; text-decoration: none; font-size: 1.1rem; transition: 0.3s; } .social-icon:hover { color: #4481eb; border-color: #4481eb; } .btn { width: 150px; background-color: #5995fd; border: none; outline: none; height: 49px; border-radius: 49px; color: #fff; text-transform: uppercase; font-weight: 600; margin: 10px 0; cursor: pointer; transition: 0.5s; } .btn:hover { background-color: #4d84e2; } .panels-container { position: absolute; height: 100%; width: 100%; top: 0; left: 0; display: grid; grid-template-columns: repeat(2, 1fr); } .container:before { content: ''; position: absolute; height: 2000px; width: 2000px; top: -10%; right: 48%; transform: translateY(-50%); background-image: linear-gradient(-45deg, #4481eb 0%, #04befe 100%); transition: 1.8s ease-in-out; border-radius: 50%; z-index: 6; } .image { width: 100%; transition: transform 1.1s ease-in-out; transition-delay: 0.4s; } .panel { display: flex; flex-direction: column; align-items: flex-end; justify-content: space-around; text-align: center; z-index: 6; } .left-panel { pointer-events: all; padding: 3rem 17% 2rem 12%; } .right-panel { pointer-events: none; padding: 3rem 12% 2rem 17%; } .panel .content { color: #fff; transition: transform 0.9s ease-in-out; transition-delay: 0.6s; } .panel h3 { font-weight: 600; line-height: 1; font-size: 1.5rem; } .panel p { font-size: 0.95rem; padding: 0.7rem 0; } .btn.transparent { margin: 0; background: none; border: 2px solid #fff; width: 130px; height: 41px; font-weight: 600; font-size: 0.8rem; } .right-panel .image, .right-panel .content { transform: translateX(800px); } /* ANIMATION */ .container.sign-up-mode:before { transform: translate(100%, -50%); right: 52%; } .container.sign-up-mode .left-panel .image, .container.sign-up-mode .left-panel .content { transform: translateX(-800px); } .container.sign-up-mode .signin-signup { left: 25%; } .container.sign-up-mode form.sign-up-form { opacity: 1; z-index: 2; } .container.sign-up-mode form.sign-in-form { opacity: 0; z-index: 1; } .container.sign-up-mode .right-panel .image, .container.sign-up-mode .right-panel .content { transform: translateX(0%); } .container.sign-up-mode .left-panel { pointer-events: none; } .container.sign-up-mode .right-panel { pointer-events: all; } @media (max-width: 870px) { .container { min-height: 800px; height: 100vh; } .signin-signup { width: 100%; top: 95%; transform: translate(-50%, -100%); transition: 1s 0.8s ease-in-out; } .signin-signup, .container.sign-up-mode .signin-signup { left: 50%; } .panels-container { grid-template-columns: 1fr; grid-template-rows: 1fr 2fr 1fr; } .panel { flex-direction: row; justify-content: space-around; align-items: center; padding: 2.5rem 8%; grid-column: 1 / 2; } .right-panel { grid-row: 3 / 4; } .left-panel { grid-row: 1 / 2; } .image { width: 200px; transition: transform 0.9s ease-in-out; transition-delay: 0.6s; } .panel .content { padding-right: 15%; transition: transform 0.9s ease-in-out; transition-delay: 0.8s; } .panel h3 { font-size: 1.2rem; } .panel p { font-size: 0.7rem; padding: 0.5rem 0; } .btn.transparent { width: 110px; height: 35px; font-size: 0.7rem; } .container:before { width: 1500px; height: 1500px; transform: translateX(-50%); left: 30%; bottom: 68%; right: initial; top: initial; transition: 2s ease-in-out; } .container.sign-up-mode:before { transform: translate(-50%, 100%); bottom: 32%; right: initial; } .container.sign-up-mode .left-panel .image, .container.sign-up-mode .left-panel .content { transform: translateY(-300px); } .container.sign-up-mode .right-panel .image, .container.sign-up-mode .right-panel .content { transform: translateY(0px); } .right-panel .image, .right-panel .content { transform: translateY(300px); } .container.sign-up-mode .signin-signup { top: 5%; transform: translate(-50%, 0); } } @media (max-width: 570px) { form { padding: 0 1.5rem; } .image { display: none; } .panel .content { padding: 0.5rem 1rem; } .container { padding: 1.5rem; } .container:before { bottom: 72%; left: 50%; } .container.sign-up-mode:before { bottom: 28%; left: 50%; } } /* 控制login & register显示 */ form { padding: 0rem 5rem; transition: all 0.2s 0.7s; overflow: hidden; } form.sign-in-form { z-index: 2; } form.sign-up-form { opacity: 0; z-index: 1; } /* register */ .loginForm, .registerForm { margin-top: 20px; background-color: #fff; padding: 20px 40px 20px 20px; border-radius: 5px; box-shadow: 0px 5px 10px #cccc; } .submit-btn { width: 100%; } .tiparea { text-align: right; font-size: 12px; color: #333; width: 100%; } .tiparea a { color: #409eff; }

                    3.2 引入element-plus

                    (1)下载element-plus包:

                    npm i element-plus
                    

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第27张

                    (2)在main.ts中引入

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第28张

                    3.3 使用element-plus表单组件

                    (1)setup中加入登录表单loginUser

                    setup() {
                        // 登录/注册模式
                        const signUpMode = ref(false)
                        // 登录表单
                        const loginUser = reactive({
                          email: '',
                          password: ''
                        })
                        return { signUpMode, loginUser }
                      }
                    

                    (2)使用表单组件

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第29张

                    
                      
                        
                      
                      
                        
                      
                      
                        提交
                      
                      
                      
                        

                    忘记密码立即找回

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第30张

                    3.4 表单验证

                    (1)表单验证

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第31张

                    关键代码:

                    // 校验规则
                    const rules = reactive({
                      email: [
                        {
                          required: true,
                          type: 'email',
                          message: 'email格式错误',
                          trigger: 'blur'
                        }
                      ],
                      password: [
                        { required: true, message: '密码不得为空', trigger: 'blur' },
                        { min: 6, max: 30, message: '密码长度必须在6到30之间', trigger: 'blur' }
                      ]
                    })
                    

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第32张

                    (2)点击提交触发表单验证

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第33张

                    关键代码:

                      

                    Row,row,row your boat

                    Gentlely down the stream

                    注册

                    Merrily,merrily,merrily,merrily,

                    Life is but a dream

                    登录
                    import { ref, reactive, getCurrentInstance } from 'vue' export default { name: 'LoginRegister', components: {}, // Vue3语法糖 // Vue2是通过data和methods传递数据和方法 // Vue3将data和methods直接耦合在了一起 setup() { // 通过解构getCurrentInstance,获取this,这里的this就是ctx const { ctx } = getCurrentInstance() // 登录/注册模式 const signUpMode = ref(false) // 登录表单 const loginUser = reactive({ email: '', password: '' }) // 校验规则 const rules = reactive({ email: [ { required: true, type: 'email', message: 'email格式错误', trigger: 'blur' } ], password: [ { required: true, message: '密码不得为空', trigger: 'blur' }, { min: 6, max: 30, message: '密码长度必须在6到30之间', trigger: 'blur' } ] }) // 触发登录方法 const handleLogin = (formName) => { console.log(ctx) ctx.$refs[formName].validate((valid) => { if (valid) { console.log('submit!') } else { console.log('error submit!') return false } }) } return { signUpMode, loginUser, rules, handleLogin } } }

                    点击提交,完成验证:

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第34张

                    4.类型匹配和代码抽离

                    4.1 代码抽离

                    创建utils文件夹和loginValidators.ts文件,将LoginRegister.vue中的loginUser和rules剪切抽离到该文件中,LoginRegister.vue再通过import导入。测试正常运行。

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第35张

                    loginValidators.ts:

                    import { ref, reactive  } from 'vue'
                    // 登录表单
                    export const loginUser = reactive({
                      email: '',
                      password: ''
                    })
                    // 校验规则
                    export const rules = reactive({
                      email: [
                        {
                          required: true,
                          type: 'email',
                          message: 'email格式错误',
                          trigger: 'blur'
                        }
                      ],
                      password: [
                        { required: true, message: '密码不得为空', trigger: 'blur' },
                        { min: 6, max: 30, message: '密码长度必须在6到30之间', trigger: 'blur' }
                      ]
                    })
                    

                    4.2 类型匹配

                    4.2.1 ts文件类型匹配

                    将鼠标悬浮在 loginUser 的 reactive 上,可以看到对应的类型提示,写入interface User 并复制提示。rules重复操作。

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第36张

                    import { ref, reactive  } from 'vue'
                    interface User{
                      email: string;
                      password: string;
                    }
                    // 登录表单
                    export const loginUser = reactive({
                      email: '',
                      password: ''
                    })
                    interface Rules{
                      email: {
                          required: boolean;
                          type: string;
                          message: string;
                          trigger: string;
                      }[];
                      password: ({
                          required: boolean;
                          message: string;
                          trigger: string;
                          min?: undefined;
                          max?: undefined;
                      } | {
                          min: number;
                          max: number;
                          message: string;
                          trigger: string;
                          required?: undefined;
                      })[];
                    }
                    // 校验规则
                    export const rules = reactive({
                      email: [
                        {
                          required: true,
                          type: 'email',
                          message: 'email格式错误',
                          trigger: 'blur'
                        }
                      ],
                      password: [
                        { required: true, message: '密码不得为空', trigger: 'blur' },
                        { min: 6, max: 30, message: '密码长度必须在6到30之间', trigger: 'blur' }
                      ]
                    })
                    

                    此时如果将字段修改为不符合规范的类型,则会有报错提示

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第37张

                    这对于初次使用ts的同学可能不太适应,认为完全没有必要。但是这其实对于大型项目的维护来说,相对比js友好许多。

                    4.2.2 vue文件中的ts类型匹配

                    (1)在script标签中加入lang="ts"标识,发现参数都没有类型匹配。

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第38张

                    (2)类型匹配

                    1、对于any类型的变量——ctx,使用// @ts-ignore进行类型忽略

                    2、对于其他类型的变量,在变量后加入:类型即可,比如formName:string和valid:boolean

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第39张

                    5.抽离登录组件

                    (1)创建组件LoginForm.vue,将LoginRegister.vue中的表单内容(包括组件、数据、方法和样式,顺便对样式做了点优化)复制到该文件中。

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第40张

                    LoginForm:

                      
                      
                        
                          
                        
                        
                          
                        
                        
                          提交
                        
                        
                        
                          

                    忘记密码立即找回

                    import { getCurrentInstance } from 'vue' export default { name: 'LoginForm', props: { loginUser: { type: Object, required: true }, rules: { type: Object, required: true } }, setup() { // 通过解构getCurrentInstance,获取this,这里的this就是ctx // @ts-ignore const { ctx } = getCurrentInstance() // 触发登录方法 const handleLogin = (formName: string) => { console.log(ctx) ctx.$refs[formName].validate((valid: boolean) => { if (valid) { console.log('submit!') } else { console.log('error submit!') return false } }) } return { handleLogin } } } /* register */ .login-form, .register-form { background-color: #fff; padding: 50px 80px 20px 20px; border-radius: 5px; box-shadow: 0px 5px 10px #cccc; } .submit-btn { width: 100%; } .tiparea { text-align: right; font-size: 12px; color: #333; width: 100%; } .tiparea a { color: #409eff; }

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第41张

                    6. 实现注册表单

                    其实完全可以仿照登录组件的写法,先在LoginRegister.vue中写出form组件,填充form数据,实现表单验证,最后再抽离出来。但是如果已经熟练的话,建议可以直接分为3个部分写完(ts——对应的数据和验证规则,component——组件,vue——引入到父组件中)

                    6.1 创建注册表单ts——存放注册表单及其验证规则

                    创建registerValidator.ts文件,用于存放注册表单及其验证规则。

                    可以先复制之前的loginValidator.ts文件的内容,然后进行修改(注意确认密码password2的验证规则写法)。

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第42张

                    registerValidator.ts:

                    import { reactive  } from 'vue'
                    interface RegisterUser{
                      name:string;
                      email: string;
                      password: string;
                      password2: string;
                      role:string
                    }
                    // 登录表单
                    export const registerUser = reactive({
                      name:'',
                      email: '',
                      password: '',
                      password2: '',
                      role:''
                    })
                    interface RegisterRules{
                      name: {
                        required: boolean;
                        message: string;
                        trigger: string;
                    }[];
                      email: {
                          required: boolean;
                          type: string;
                          message: string;
                          trigger: string;
                      }[];
                      password: ({
                          required: boolean;
                          message: string;
                          trigger: string;
                      } | {
                          min: number;
                          max: number;
                          message: string;
                          trigger: string;
                      })[];
                      password2: ({
                          required: boolean;
                          message: string;
                          trigger: string;
                      } | {
                          min: number;
                          max: number;
                          message: string;
                          trigger: string;
                      } | {
                        validator:(rule: RegisterRules, value: string, callback: any)=>any;
                        trigger:string
                      })[];
                      role: {
                        required: boolean;
                        message: string;
                        trigger: string;
                    }[];
                    }
                    const validatePass2 = (rule: RegisterRules, value: string, callback: any) => {
                      if (value === '') {
                        callback(new Error('请再次输入密码'))
                      } else if (value !== registerUser.password) {
                        callback(new Error("两次输入的密码不一致!"))
                      } else {
                        callback()
                      }
                    }
                    // 校验规则
                    export const registerRules = reactive({
                      name: [
                        {
                          required: true,
                          message: '用户名不得为空',
                          trigger: 'blur'
                        }
                      ],
                      email: [
                        {
                          required: true,
                          type: 'email',
                          message: 'email格式错误',
                          trigger: 'blur'
                        }
                      ],
                      password: [
                        { required: true, message: '密码不得为空', trigger: 'blur' },
                        { min: 6, max: 30, message: '密码长度必须在6到30之间', trigger: 'blur' }
                      ],
                      password2: [
                        { required: true, message: '确认密码不得为空', trigger: 'blur' },
                        { min: 6, max: 30, message: '密码长度必须在6到30之间', trigger: 'blur' },
                        { validator: validatePass2, trigger: 'blur' }
                      ],
                      role: [
                        {
                          required: true,
                          message: '角色不得为空',
                          trigger: 'blur'
                        }
                      ],
                    })
                    

                    完成后将 registerUser 和 registerRules 引入到LoginRegister.vue中备用。

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第43张

                    6.2 创建注册表单组件

                    创建注册表单组件RegisterForm.vue,模仿或者直接复制LoginForm.vue组件,稍作修改(包括样式、数据和方法)

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第44张

                      
                      
                        
                          
                        
                        
                          
                        
                        
                          
                        
                        
                          
                        
                        
                          
                            
                            
                            
                          
                        
                        
                          提交
                        
                      
                    
                    
                    import { getCurrentInstance } from 'vue'
                    export default {
                      name: 'registerForm',
                      props: {
                        registerUser: {
                          type: Object,
                          required: true
                        },
                        registerRules: {
                          type: Object,
                          required: true
                        }
                      },
                      setup() {
                        // 通过解构getCurrentInstance,获取this,这里的this就是ctx
                        // @ts-ignore
                        const { ctx } = getCurrentInstance()
                        // 触发登录方法
                        const handleRegister = (formName: string) => {
                          console.log(ctx)
                          ctx.$refs[formName].validate((valid: boolean) => {
                            if (valid) {
                              console.log('submit!')
                            } else {
                              console.log('error submit!')
                              return false
                            }
                          })
                        }
                        return { handleRegister }
                      }
                    }
                    
                    
                    /* register */
                    .login-form,
                    .register-form {
                      background-color: #fff;
                      padding: 50px 80px 20px 20px;
                      border-radius: 5px;
                      box-shadow: 0px 5px 10px #cccc;
                    }
                    .submit-btn {
                      width: 100%;
                    }
                    .tiparea {
                      text-align: right;
                      font-size: 12px;
                      color: #333;
                      width: 100%;
                    }
                    .tiparea a {
                      color: #409eff;
                    }
                    
                    

                    6.3 在LoginRegister.vue中引入使用

                    最后在LoginRegister.vue中引入使用即可

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第45张

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第46张

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第47张

                    7. 封装axios

                    7.1 下载axios

                    (1)使用命令npm i axios下载axios

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第48张

                    7.2 封装axios

                    创建utils/http.ts文件,用于封装axios请求(为了避免混乱,所以取名http.ts,不过叫做axios也无不可)。

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第49张

                    http.ts:

                    import axios,{AxiosRequestConfig,AxiosResponse} from 'axios'
                    import { ElLoading } from 'element-plus'
                    import { ElMessage } from 'element-plus'
                    let loading:any;
                    const startLoading = () =>{
                      interface Options{
                        lock: boolean;
                        text: string;
                        background: string;
                    }
                      const options:Options = {
                        lock: true,
                        text: 'Loading',
                        background: 'rgba(0, 0, 0, 0.7)'
                      }
                      loading = ElLoading.service(options)
                    }
                    const endLoading = ()=>{
                      loading.close()
                    }
                    // 请求拦截
                    axios.interceptors.request.use((config:AxiosRequestConfig)=>{
                      // 开始Loading
                      startLoading()
                      return config
                    })
                    // 响应拦截
                    axios.interceptors.response.use((res:AxiosResponse)=>{
                      // 结束Loading
                      endLoading()
                      // console.log('成功响应拦截',res)
                      // 如果请求成功直接返回响应数据
                      if(res.status === 200){
                        return res.data
                      }
                    },error=>{
                      // 结束Loading
                      endLoading()
                      // console.log('失败响应拦截',error)
                      const { response: res } = error
                      const msg = typeof res.data === 'string' ? res.data: res.data.error || '请求失败,请稍后重试'
                      ElMessage.error(msg)
                      // 错误提醒
                      return Promise.reject(error)
                    })
                    export default axios
                    

                    7.3 解决跨域问题(配置vue.config.js,设置代理)

                    创建vue.config.js文件,配置如下:

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第50张

                    module.exports = {
                      devServer: {
                        open: true,
                        host: 'localhost',
                        port: 8080,
                        https: false,
                        hotOnly: false,
                        // 设置跨域
                        proxy: {
                          '/api': {
                            target: 'http://imissu.herokuapp.com',
                            ws: true,
                            changeOrigin: true,
                            pathRewrite: {
                              '^api': ''
                            }
                          }
                        },
                        before: (app) => {}
                      }
                    }
                    

                    7.4 使用axios发起请求

                    API地址:http://imissu.herokuapp.com/

                    7.4.1 创建api文件夹,规范使用api(推荐)

                    (1)创建api/loginRegister.ts,引入axios,规范export注册接口

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第51张

                    import axios from '@/utils/http'
                    // 注册接口
                    export function register(params: any) {
                      return axios({
                        url: '/api/v1/auth/register',
                        method: 'post',
                        headers: {
                          'Content-Type': 'application/json;charset=UTF-8'
                        },
                        data: params
                      })
                    }
                    

                    (2)在注册组件中使用

                    Vue3实战教程(快速入门),Vue 3实战教程,快速入门指南 第52张

                      
                      
                        
                          
                        
                        
                          
                        
                        
                          
                        
                        
                          
                        
                        
                          
                            
                            
                            
                          
                        
                        
                          提交
                        
                      
                    
                    
                    import { getCurrentInstance } from 'vue'
                    import { useRouter } from 'vue-router'
                    import { register } from '@/api/loginRegister'
                    // import { ElMessage } from 'element-plus'
                    export default {
                      name: 'registerForm',
                      props: {
                        registerUser: {
                          type: Object,
                          required: true
                        },
                        registerRules: {
                          type: Object,
                          required: true
                        }
                      },
                      setup(props: any) {
                        // 通过解构getCurrentInstance,获取this,这里的this就是ctx
                        // @ts-ignore
                        const { ctx, proxy } = getCurrentInstance()
                        const router = useRouter()
                        // 触发登录方法
                        const handleRegister = (formName: string) => {
                          console.log(ctx)
                          ctx.$refs[formName].validate(async (valid: boolean) => {
                            if (valid) {
                              // const res = await proxy.$http({
                              //   url: '/api/v1/auth/register',
                              //   method: 'post',
                              //   headers: {
                              //     'Content-Type': 'application/json;charset=UTF-8'
                              //   },
                              //   data: props.registerUser
                              // })
                              const res = await register(props.registerUser)
                              proxy.$message.success(res.data)
                              router.push('/')
                            } else {
                              return false
                            }
                          })
                        }
                        return { handleRegister }
                      }
                    }
                    
                    
                    /* register */
                    .login-form,
                    .register-form {
                      background-color: #fff;
                      padding: 50px 80px 20px 20px;
                      border-radius: 5px;
                      box-shadow: 0px 5px 10px #cccc;
                    }
                    .submit-btn {
                      width: 100%;
                    }
                    .tiparea {
                      text-align: right;
                      font-size: 12px;
                      color: #333;
                      width: 100%;
                    }
                    .tiparea a {
                      color: #409eff;
                    }
                    
                    

                    登录功能模仿注册功能即可。

                    7.4.2 全局注册axios(不推荐,也没必要)

                    (1)在main.ts中全局挂载axios

                    import axios from '@/utils/http'
                    app.config.globalProperties.$http = axios
                    

                    (2)在注册组件中使用,关键代码:

                    // @ts-ignore
                    const { ctx, proxy } = getCurrentInstance()
                    const res = await proxy.$http({
                      url: '/api/v1/auth/register',
                      method: 'post',
                      headers: {
                        'Content-Type': 'application/json;charset=UTF-8'
                      },
                      data: props.registerUser
                    })
                    

                    (3)不推荐的原因是,随着版本的更替,貌似axios全局挂载的位置也发生了变化,之前是在ctx,之后的版本又换成了proxy;另外一个原因是,这种方式不便于一个接口多处使用,复用性较差。

                    8.总结

                    Vue3 和 Vue2 的几个明显的区别:

                    (1)Vue2使用的选项式API,Vue3使用的是组合式API。前者随着项目页面体积的增大,对于代码的管理会给使用者带来更大的心智负担;后者组合式的写法,将data和methods等组合在一起,更容易理解和使用。不过对于初学者而言,可能会有些不太适应。

                    (2)Vue2使用的js构建的源码和使用方式,Vue3使用ts构建的源码,使用方式也支持ts,对于大型项目而言,更加友好,不过对于小型项目而言,往往使用者无法一下子看出ts对于强类型支持带来的好处,相反会觉得麻烦和没有必要。

                    (3)由于该项目是为了简要说明Vue3和Vue2在页面中的区别,方便急于使用Vue3的同学构建项目和页面,所以没有将Vue3更多的特性展示出来。在下一篇文章中,将会通过一个更加完整的项目,对Vue3的更多其他特性以及和Vue2的区别进行深入的解析和说明,敬请期待。


0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码