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、@blur、v-show 等常用指令
  • ✅ 解决 Vue 与后端模板引擎的冲突问题
  • ✅ 实现表单验证和异步数据交互

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

相关推荐
卸任5 小时前
Vibe coding工具使用心得
前端·ai编程
brien96 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance6 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M6 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写6 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李7 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易7 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马7 小时前
前沿视界
前端
kyriewen7 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易7 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员