Vue.js 整合传统 HTML 项目:注册页面实战教程

前言

本文以一个完整的用户注册页面为例,详细讲解如何在传统 HTML 项目中整合 Vue.js,实现表单验证、图形验证码、异步数据交互等功能。文章提供完整的代码,读者可直接复制使用。


一、项目结构

首先,明确项目文件的组织结构:

复制代码
project/
├── static/
│   ├── js/
│   │   ├── vue-2.5.16.js          # Vue.js 核心库
│   │   ├── axios-0.18.0.min.js    # HTTP 请求库
│   │   ├── common.js              # 通用工具函数
│   │   └── register.js            # 注册页面 Vue 逻辑
│   ├── css/
│   │   ├── reset.css
│   │   └── main.css
│   └── images/
│       └── logo.png
└── templates/
    └── register.html               # 注册页面模板

二、完整代码

2.1 通用工具函数 common.js

该文件包含 generateUUID() 等通用函数,被 register.js 依赖:

javascript 复制代码
// common.js

// 获取 cookie
function getCookie(name) {
    let r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    return r ? r[1] : undefined;
}

// 提取地址栏中的查询字符串
function get_query_string(name) {
    let reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
    let r = window.location.search.substr(1).match(reg);
    if (r != null) {
        return decodeURI(r[2]);
    }
    return null;
}

// 生成 UUID
function generateUUID() {
    let d = new Date().getTime();
    if (window.performance && typeof window.performance.now === "function") {
        d += performance.now();
    }
    let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        let r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
    return uuid;
}

2.2 Vue 逻辑 register.js

这是注册页面的核心逻辑,包含表单验证、异步校验等功能:

javascript 复制代码
// register.js

// 创建 Vue 实例
let vm = new Vue({
    el: '#app',  // 通过 ID 选择器找到绑定的 HTML 内容
    
    // 修改 Vue 读取变量的语法,解决与后端模板引擎冲突
    // delimiters: ['[[', ']]'],
    
    data() {  // 数据对象
        return {
            // v-model 绑定
            username: '',
            password: '',
            password2: '',
            mobile: '',
            allow: '',
            image_code_url: '',
            uuid: '',

            // v-show 控制显示
            error_name: false,
            error_password: false,
            error_password2: false,
            error_mobile: false,
            error_allow: false,

            // 错误提示信息
            error_name_message: '你好',
            error_mobile_message: '13020577695',
        };
    },
    
    mounted() {  // 页面加载完成后调用
        // 生成图形验证码
        this.generate_image_code();
    },
    
    methods: {  // 定义事件方法
        // 生成图形验证码
        generate_image_code() {
            this.uuid = generateUUID();
            this.image_code_url = '/image_codes/' + this.uuid + '/';
        },
        
        // 校验用户名
        checkUsername() {
            // 用户名是 5-20 个字符,[a-zA-Z0-9_-]
            let re = /^[a-zA-Z0-9_-]{5,20}$/;

            if (re.test(this.username)) {
                this.error_name = false;
            } else {
                this.error_name_message = '请输入5-20个字符的用户名';
                this.error_name = true;
            }

            // 判断用户名是否重复注册(异步请求)
            if (this.error_name == false) {
                let url = '/usernames/' + this.username + '/count/';
                axios.get(url, {
                    responseType: 'json'
                })
                .then(response => {
                    if (response.data.count == 1) {
                        this.error_name_message = '用户名已存在';
                        this.error_name = true;
                    } else {
                        this.error_name = false;
                    }
                })
                .catch(error => {
                    console.log(error.response);
                });
            }
        },

        // 校验密码
        checkPassword() {
            let re = /^[0-9A-Za-z]{8,20}$/;
            if (re.test(this.password)) {
                this.error_password = false;
            } else {
                this.error_password = true;
            }
        },

        // 校验确认密码
        checkPassword2() {
            if (this.password != this.password2) {
                this.error_password2 = true;
            } else {
                this.error_password2 = false;
            }
        },

        // 校验手机号
        checkMobile() {
            let re = /^1[3-9]\d{9}$/;
            if (re.test(this.mobile)) {
                this.error_mobile = false;
            } else {
                this.error_mobile_message = '您输入的手机号格式不正确';
                this.error_mobile = true;
            }
        },

        // 校验是否勾选协议
        checkAllow() {
            if (!this.allow) {
                this.error_allow = true;
            } else {
                this.error_allow = false;
            }
        },

        // 监听表单提交事件
        submitForm(){
            this.checkUsername();
            this.checkPassword();
            this.checkPassword2();
            this.checkMobile();
            this.checkAllow();

            // 只要有错误,就阻止表单提交
            if (this.error_name == true ||
                this.error_password == true ||
                this.error_password2 == true ||
                this.error_mobile == true ||
                this.error_allow == true) {
                window.event.returnValue = false;
            }
        },

        // 刷新图形验证码
        refreshImageCode() {
            this.generate_image_code();
        }
    }
});

2.3 注册页面模板 register.html

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用户注册</title>
    <link rel="stylesheet" href="styles.css">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js"></script>

</head>
<body>
    <div id="app">
        <div class="register-container">
            <h1>用户注册</h1>
            <form @submit.prevent="submitForm">
                <!-- 用户名 -->
                <div>
                    <label for="username">用户名:</label>
                    <input type="text" id="username" v-model="username" @blur="checkUsername">
                    <p v-show="error_name" class="error">{{ error_name_message }}</p>
                </div>

                <!-- 密码 -->
                <div>
                    <label for="password">密码:</label>
                    <input type="password" id="password" v-model="password" @blur="checkPassword">
                    <p v-show="error_password" class="error">密码格式不正确</p>
                </div>

                <!-- 确认密码 -->
                <div>
                    <label for="password2">确认密码:</label>
                    <input type="password" id="password2" v-model="password2" @blur="checkPassword2">
                    <p v-show="error_password2" class="error">两次输入的密码不一致</p>
                </div>

                <!-- 手机号 -->
                <div>
                    <label for="mobile">手机号:</label>
                    <input type="text" id="mobile" v-model="mobile" @blur="checkMobile">
                    <p v-show="error_mobile" class="error">{{ error_mobile_message }}</p>
                </div>

                <!-- 验证码 -->
                <div>
                    <label for="image_code">验证码:</label>
                    <input type="text" id="image_code" v-model="image_code">
                    <img :src="image_code_url" @click="refreshImageCode" alt="验证码">
                </div>

                <!-- 同意协议 -->
                <div>
                    <input type="checkbox" id="allow" v-model="allow">
                    <label for="allow">我已阅读并同意<a href="#">《用户协议》</a></label>
                    <p v-show="error_allow" class="error">请勾选同意协议</p>
                </div>

                <!-- 提交按钮 -->
                <div>
                    <button type="submit">注册</button>
                </div>
            </form>
        </div>

        <div class="footer no-mp">
            <div class="foot_link">
                <a href="#">关于我们</a>
                <span>|</span>
                <a href="#">联系我们</a>
                <span>|</span>
                <a href="#">招聘人才</a>
                <span>|</span>
                <a href="#">联系我们</a>
            </div>
            <p>CopyRight © 2016 北京美多商业股份有限公司 All Rights Reserved</p>
            <p>电话:010-****888 京ICP备*******8号</p>
        </div>
    </div>

    <!-- 引入通用函数库 -->
    <script src="js/common.js"></script>
    <!-- 引入 Vue 逻辑 -->
    <script src="js/register.js"></script>
</body>
</html>

三、测试流程

3.1 环境准备

  1. 创建项目文件夹 (如 test-vue/
  2. 下载依赖文件(或使用 CDN)
bash 复制代码
# 目录结构
test-vue/
├── css/
│   ├── reset.css
│   └── main.css
├── images/
│   └── logo.png
├── js/
│   ├── vue-2.5.16.js
│   ├── axios-0.18.0.min.js
│   ├── common.js
│   └── register.js
└── register.html

3.2 获取必要资源

方式一:下载文件

文件 下载地址
Vue.js https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js
Axios https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js

方式二:直接使用 CDN(推荐测试)

register.html 中的引入改为:

html 复制代码
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js"></script>

3.3 简化版 HTML(纯前端测试)

如果暂时没有后端 API,可以创建一个简化版用于测试 Vue 是否正常工作:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Vue 注册页面测试</title>
    <style>
        body { font-family: Arial, sans-serif; padding: 20px; }
        .form-group { margin-bottom: 15px; }
        .form-group label { display: inline-block; width: 100px; }
        .error_tip { color: red; font-size: 12px; margin-left: 10px; }
        .has-error input { border-color: red; }
    </style>
</head>
<body>
    <div id="app">
        <h1>用户注册</h1>
        <form @submit="on_submit">
            <div class="form-group">
                <label>用户名:</label>
                <input type="text" v-model="username" @blur="check_username">
                <span class="error_tip" v-show="error_name">[[ error_name_message ]]</span>
            </div>
            
            <div class="form-group">
                <label>密码:</label>
                <input type="password" v-model="password" @blur="check_password">
                <span class="error_tip" v-show="error_password">请输入8-20位的密码</span>
            </div>
            
            <div class="form-group">
                <label>确认密码:</label>
                <input type="password" v-model="password2" @blur="check_password2">
                <span class="error_tip" v-show="error_password2">两次输入的密码不一致</span>
            </div>
            
            <div class="form-group">
                <label>手机号:</label>
                <input type="text" v-model="mobile" @blur="check_mobile">
                <span class="error_tip" v-show="error_mobile">[[ error_mobile_message ]]</span>
            </div>
            
            <div class="form-group">
                <label>图形验证码:</label>
                <!-- 简化:只显示UUID,实际项目应请求后端 -->
                <input type="text" v-model="image_code">
                <button type="button" @click="generate_image_code">刷新验证码</button>
                <span> 当前UUID: [[ uuid ]]</span>
            </div>
            
            <div class="form-group">
                <input type="checkbox" v-model="allow" @change="check_allow">
                <label>同意用户协议</label>
                <span class="error_tip" v-show="error_allow">请勾选用户协议</span>
            </div>
            
            <div class="form-group">
                <input type="submit" value="注 册">
            </div>
        </form>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script>
        // 生成 UUID 函数
        function generateUUID() {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        }

        let vm = new Vue({
            el: '#app',
            delimiters: ['[[', ']]'],
            data: {
                username: '',
                password: '',
                password2: '',
                mobile: '',
                image_code: '',
                uuid: '',
                allow: false,
                
                error_name: false,
                error_password: false,
                error_password2: false,
                error_mobile: false,
                error_allow: false,
                
                error_name_message: '',
                error_mobile_message: '',
            },
            mounted() {
                this.generate_image_code();
            },
            methods: {
                generate_image_code() {
                    this.uuid = generateUUID();
                },
                check_username() {
                    let re = /^[a-zA-Z0-9_-]{5,20}$/;
                    if (re.test(this.username)) {
                        this.error_name = false;
                    } else {
                        this.error_name_message = '请输入5-20个字符的用户名';
                        this.error_name = true;
                    }
                },
                check_password() {
                    let re = /^[0-9A-Za-z]{8,20}$/;
                    this.error_password = !re.test(this.password);
                },
                check_password2() {
                    this.error_password2 = (this.password !== this.password2);
                },
                check_mobile() {
                    let re = /^1[3-9]\d{9}$/;
                    if (re.test(this.mobile)) {
                        this.error_mobile = false;
                    } else {
                        this.error_mobile_message = '手机号格式不正确';
                        this.error_mobile = true;
                    }
                },
                check_allow() {
                    this.error_allow = !this.allow;
                },
                on_submit() {
                    this.check_username();
                    this.check_password();
                    this.check_password2();
                    this.check_mobile();
                    this.check_allow();

                    if (this.error_name || this.error_password || 
                        this.error_password2 || this.error_mobile || this.error_allow) {
                        window.event.returnValue = false;
                        alert('表单验证失败,请检查输入');
                    } else {
                        alert('表单验证通过!\n用户名: ' + this.username);
                    }
                }
            }
        });
    </script>
</body>
</html>

四、关键点总结

4.1 引入顺序

正确的脚本引入顺序:

html 复制代码
<!-- 1. 引入 Vue 核心库 -->
<script src="js/vue-2.5.16.js"></script>

<!-- 2. 引入 Axios(如果需要异步请求)-->
<script src="js/axios-0.18.0.min.js"></script>

<!-- 3. 引入被依赖的工具函数(必须在 register.js 之前)-->
<script src="js/common.js"></script>

<!-- 4. 最后引入 Vue 逻辑脚本 -->
<script src="js/register.js"></script>

4.2 自定义分隔符

解决 Vue 与后端模板冲突:

javascript 复制代码
delimiters: ['[[', ']]']
html 复制代码
<!-- Vue 变量 -->
<span>[[ username ]]</span>

<!-- 后端变量 -->
<span>{{ user.name }}</span>

4.3 常用指令对应关系

Vue 指令 作用 传统 JS 等价
v-model 双向绑定 input.value
@blur 失去焦点 input.onblur
@click 点击事件 element.onclick
v-show 条件显示 element.style.display
v-if 条件渲染 if (cond) append()

五、常见问题

Q1:Vue 实例不生效?

检查以下几点:

  1. <div id="app"> 是否存在且正确闭合
  2. Vue.js 是否成功加载(打开浏览器控制台查看)
  3. el: '#app' 选择器是否与 HTML 中的 ID 匹配

Q2:变量不显示?

  1. 检查是否使用了正确的分隔符 [[ ]]
  2. 确认变量名在 data() 中正确定义
  3. 检查是否有 CSS 隐藏了元素(如 v-cloak 相关样式)

Q3:异步请求失败?

  1. 确认后端 API 路径正确
  2. 检查是否有跨域问题
  3. 查看浏览器控制台 Network 面板的请求详情

结语

通过本文的完整代码和测试流程,你应该能够:

  • ✅ 理解 Vue.js 在传统 HTML 项目中的整合方式
  • ✅ 掌握 v-model@blurv-show 等常用指令
  • ✅ 解决 Vue 与后端模板引擎的冲突问题
  • ✅ 实现表单验证和异步数据交互

这些知识可以复用到任何需要渐进式增强的传统 Web 项目中。

相关推荐
XXYBMOOO2 小时前
Flarum 主题定制:从零打造你的赛博朋克/JOJO 风格社区(含全套 CSS 源码)
前端·css
升鲜宝供应链及收银系统源代码服务2 小时前
升鲜宝生鲜配送供应链管理系统生产加工子模块的详细表设计说明
java·大数据·前端·数据库·bootstrap·供应链系统·生鲜配送
行者-全栈开发2 小时前
43 篇系统实战:uni-app 从入门到架构师成长之路
前端·typescript·uni-app·vue3·最佳实践·企业级架构
泉城老铁2 小时前
一分钟搞定SpringBoot+Vue3 整合 SSE 实现实时消息推送
前端·vue.js·后端
踩着两条虫2 小时前
AI 驱动的 Vue3 应用开发平台 深入探究(五):核心概念之项目结构与文件组织
前端·vue.js·ai编程
HelloReader2 小时前
Flutter 隐式动画两行代码让方块丝滑变色(七)
前端
木斯佳2 小时前
前端八股文面经大全:X transfer前端一面(2026-03-10)·面经深度解析
前端·状态模式
Pu_Nine_92 小时前
深入理解 ES6 Map 数据结构:从理论到实战应用
前端·javascript·数据结构·es6
豆芽包2 小时前
Git 指令大全
前端·面试