前言
本文以一个完整的用户注册页面为例,详细讲解如何在传统 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 环境准备
- 创建项目文件夹 (如
test-vue/) - 下载依赖文件(或使用 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 实例不生效?
检查以下几点:
<div id="app">是否存在且正确闭合- Vue.js 是否成功加载(打开浏览器控制台查看)
el: '#app'选择器是否与 HTML 中的 ID 匹配
Q2:变量不显示?
- 检查是否使用了正确的分隔符
[[ ]] - 确认变量名在
data()中正确定义 - 检查是否有 CSS 隐藏了元素(如
v-cloak相关样式)
Q3:异步请求失败?
- 确认后端 API 路径正确
- 检查是否有跨域问题
- 查看浏览器控制台 Network 面板的请求详情
结语
通过本文的完整代码和测试流程,你应该能够:
- ✅ 理解 Vue.js 在传统 HTML 项目中的整合方式
- ✅ 掌握
v-model、@blur、v-show等常用指令 - ✅ 解决 Vue 与后端模板引擎的冲突问题
- ✅ 实现表单验证和异步数据交互
这些知识可以复用到任何需要渐进式增强的传统 Web 项目中。