Vue
语法进阶
课程大纲
1、事件操作
2、表单处理
3、
MVVM
数据双向绑定(面试)4、自定义框架
1、事件操作
事件event
对象是脚本开发中一个非常重要的对象,Vue
中对事件和事件对象进行了良好的支持
(1) 事件绑定
通过v-on:事件名称
绑定事件操作,语法上可以优化为@事件名称
完成事件的绑定操作!
(2) 事件对象
事件操作函数不带参数的时候,事件处理函数的第一个形式参数,默认就是事件对象!这里也不需要添加兼容性写法
const vm = new Vue({ ... methods: { handlerMsg(e) { // e = e || window.event // 直接使用e 事件对象 } } })
(3) 带参数的事件对象
事件操作函数如果附带参数的情况下,Vue
提供了显式的事件对象:$event
,用于事件函数传参
<div> <input type="text" @keyup="handleDelEvnet($event, 10)" /> </div> ... <script> const vm = new Vue({ ... methods: { handleDelEvnet(e, index) { // 处理事件对象e 和参数数据index } } }) </script>
(4) 事件修饰符
事件操作过程中,会包含很多事件的附加操作,如阻止默认行为、阻止事件冒泡、事件一次性触发等等,Vue
中提供了对应的事件修饰符,可以在事件绑定时直接完成关联
.stop
:阻止事件冒泡.prevent
:阻止默认行为.capture
:捕获触发.once
:一次性触发.self
:独立触发.passive
:滚动行为
思考;原生
JS
中和事件相关的一些兼容性问题,整理出来!
(6) 案例操作
和事件相关的案例代码:demo02事件操作.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> #app{height:2000px;} .outer{width: 500px; background:orange;padding: 10px;} .inner{width: 300px; background: red; padding: 10px;} </style> </head> <body> <div id="app"> <!-- 1、事件绑定,通过v-on指令进行操作 --> <div> <button @click="handleEvent">1、事件绑定</button> </div> <!-- 2、事件对象,获取鼠标位置--> <div> <button @click="handleEvent2">2、直接操作事件对象</button> </div> <!-- 3、事件对象,传递参数数据--> <div> <h5>3、带参数的情况下传递事件对象</h5> <input type="text" @keyup="handleDelEvent($event, 10)"> </div> <!-- 4、事件触发修饰符 --> <div> <div class="outer" @click="handleOuter"> <div class="inner" @click="handleInner"> <button @click.stop="handleTarget">点击按钮,阻止冒泡: .stop</button> </div> </div> </div> <div> <a href="https://www.baidu.com" @click.prevent="handleMsg">百度一下,阻止默认行为: .prevent</a> </div> <div> <button id="btn" @click.once="handlerOnce">点击我试试,一次性事件: .once</button> </div> <div> <div class="outer" @click.capture="handleOuter"> <div class="inner" @click.capture="handleInner"> <button @click="handleTarget">点击按钮,捕获触发->冒泡触发: .capture</button> </div> </div> </div> <div> <div class="outer" @click="handleOuter"> <div class="inner" @click.self="handleInner"> <button @click="handleTarget">点击按钮,事件独立触发: .self</button> </div> </div> </div> <div> <div class="outer" @click="handleOuter"> <div class="inner" @click="handleInner"> <a href="https://www.baidu.com" @click.prevent.stop="handleMsg">百度一下 如何阻止冒泡的同时阻止默认行为?</a> </div> </div> </div> </div> <button id="btn">点击我试试,一次性事件</button> <script src="./vue.js"></script> <script> // let btn = document.querySelector("#btn") btn.onclick = function() { alert("考试结算") btn.onclick = null; } const vm = new Vue({ el: "#app", methods: { handleEvent() { alert("事件被触发了.") }, handleEvent2(e) { console.log("事件对象", e) console.log(e.clientX, e.clientY, "浏览器窗口") console.log(e.pageX, e.pageY, "网页文档") console.log(e.screenX, e.screenY, "屏幕窗口") }, handleDelEvent(e, index) { console.log("要删除的数据索引:" + index) console.log("要删除的数据值:" + e.target.value) }, handleTarget() { console.log("按钮被点击了") }, handleInner() { console.log("inner div 被点击了") }, handleOuter() { console.log("outer div 被点击了") }, handleMsg() { console.log("该功能正在升级中...") }, handlerOnce() { console.log("用户点击了按钮") } } }) </script> </body> </html>
2、表单操作
网页中有一个非常重要的组件:表单,完成了用户对数据的输入!
Vue
中提供了一个非常重要的指令:v-model
,用于自动接受用户输入的数据!
代码操作:demo02表单操作.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <form action=""> <div> <label for="username">账号</label> <input type="text" v-model="formData.username"> <span>{{ formData.username }}</span> </div> <div> <label for="password">密码</label> <input type="password" v-model="formData.password"> <span>{{ formData.password }}</span> </div> <div> <label for="age">年龄</label> <input type="text" v-model="formData.age"> <span>{{ formData.age }}</span> </div> <div> <label for="gender">性别</label> <input type="radio" name="gender" value="男" v-model="formData.gender">男 <input type="radio" name="gender" value="女" v-model="formData.gender">女 <span>{{ formData.gender }}</span> </div> <div> <label for="fav">爱好</label> <input type="checkbox" name="fav" value="篮球" v-model="formData.fav">篮球 <input type="checkbox" name="fav" value="旅游" v-model="formData.fav">旅游 <input type="checkbox" name="fav" value="电影" v-model="formData.fav">电影 <input type="checkbox" name="fav" value="阅读" v-model="formData.fav">阅读 <input type="checkbox" name="fav" value="游戏" v-model="formData.fav">游戏 <span>{{formData.fav}}</span> </div> <div> <label for="address">地址</label> <select v-model="formData.address"> <option value="郑州">郑州</option> <option value="广州">广州</option> <option value="兰州">兰州</option> <option value="苏州">苏州</option> <option value="杭州">杭州</option> <option value="柳州">柳州</option> </select> <span>{{formData.address}}</span> </div> <div> <label for="introduction">简介</label> <textarea cols="30" rows="3" v-model="formData.introduction"></textarea> <span>{{formData.introduction}}</span> </div> <div> <input type="submit" value="提交" @click.prevent="submit"> </div> </form> </div> <script src="./vue.js"></script> <script> const vm = new Vue({ el: "#app", data: { formData: { username: '', password: '', age: '', gender: '', fav: [], address: '', introduction: '' } }, methods: { submit() { console.log("用户准备提交表单,验证数据,发送ajax请求") console.log(this.formData) // $.ajax({ // url: "http://localhost:3000/api/login", // type: "post", // data: this.formData, // success: res => { // if(res.code === 200) { // } // } // }) } } }) </script> </body> </html>
3、数据双向绑定
(1) 什么是数据双向绑定
所谓数据双向绑定,是出现在前端应用中的一个专业术语,描述了数据在界面和数据处理部分之间的关联关系,数据在视图界面和脚本数据处理部分实现了双向关联,所以称这样的操作模式为数据双向绑定,简称MVVM
代码操作:
<body> <div id="app"> <input type="text" v-model="name"> <p>用户输入的数据:{{name}}</p> </div> <script src="./vue.js"></script> <script> const vm = new Vue({ el: "#app", data: { name: "DAMU" } }) </script> </body>
备注:表单的操作,也可以添加表单修饰符、按键修饰符
表单修饰符:
v-model.trim
:剔除输入数据两侧的空格v-model.number
:将数据转换成数值,如果转换不成功就什么都不做v-model.lazy
:延迟数据同步,失去焦点的时候再完成同步
按键修饰符:
@click.enter
:按下回车键@click.esc
:按下esc
键- ...
(2) 数据双向绑定的问题
编写一个添加用户名称的案例,核心操作通过事件函数向数组中添加数据并完成页面上的数据渲染
① 正确的添加和渲染
this.names.push(this.name) // 添加数据【成功】,渲染数据【成功】
② 出现问题的添加
this.names[this.names.length] = this.name // 添加数据【成功】,渲染数据【失败】
总结:渲染失败的问题
Vue
中存在一种机制,可以监听变量中的数据是否发生变化,一旦数据发生变化就需要通知页面进行更新;假设这样的机制监听了数组的push
函数,所以push()
数据时添加和渲染都没有问题;但是没有监听通过下标进行添加数据的操作,所以下标增加数据成功但是无法在页面上完成渲染!浏览器控制台打开
vm
实例,查看names
属性时,发现原型对象上挂载了数组的部分函数(接收vue
实例进行管理和监听的,当数组中的数据经过这些函数完成数据更新,会自动完成数据渲染);这部分函数以外的其他操作方式可能会改变数组数据但是不会引起页面重新渲染!思考:这样的监听方式(如何监听变量数据的变化),底层是如何实现的?
补充:如果一定要通过索引的方式完成数据的添加
Vue2.x
版本中,提供了一个系统函数$set()
可以完成数据的添加和渲染// 3、使用框架内建函数完成 // this/vm.$set(数组, 索引, 数据) this.$set(this.names, this.names.length, this.name)
(3) 底层实现原理
Vue
中底层对变量进行了 数据劫持(变量的一种声明方式),就可以监控变量中数据的变化,也是Vue
中数据双向绑定的底层实现原理,如图所示:
代码操作:数据劫持完成变量声明
<body> <div id="app"> </div> <script> // 1、声明一个可以被监听的变量 /***************************************************/ let myNameTemp = "DAMU" // 存储数据的初始值 // 数据劫持定义:在window对象上声明变量myName Object.defineProperty(window, "myName", { get() { console.log("get函数被调用了") return myNameTemp }, set(val) { console.log("set函数被调用了") myNameTemp = val // 2、数据双向绑定中,数据的自动渲染(发布订阅--通知机制) render() } }) /***************************************************/ function render() { app.innerText = myName } render() </script> </body>
小总结:
关于数据劫持的代码,不需要熟悉!
- 需要掌握:数据双向绑定底层实现原理,就是数据劫持
- 数据劫持:是一种变量的声明方式,用来声明可以被监听的变量
- 数据劫持通过
Object.defineProperty()
系统函数实现
4、 自定义Vue
框架
① 文件结构初始化
项目中创建myvue.js
/** * 自定义vue框架 * V1.0 */
项目中创建demo06自定义vue.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> </div> <script src="./myvue.js"></script> <script> </script> </body> </html>
② 页面开发
编辑demo06自定义vue.html
,添加Vue
实例的创建、变量数据的声明、函数的声明
<body> <div id="app"> <p>{{ name }}</p> <p>{{age}}</p> <p>{{introduction()}}</p> </div> <script src="./myvue.js"></script> <script> const vm = new Vue({ el: "#app", data: { name: "大牧", age: 18 }, methods: { introduction() { console.log(`姓名:${this.name};年龄:${this.age}`) } } }) </script> </body>
③ 基础类型声明
编辑myvue.js
,添加Vue
类型声明
/** * 自定义vue框架 * V1.0 */ class Vue { constructor(props) { console.log(props) } }
④ 接受并处理数据
编辑myvue.js
,完成el、data、methods
数据的接受
constructor(props) { // 接受el标签 this.$el = document.querySelector(props.el) this._html = this.$el.innerHTML // 接受data数据 this._data = {...props.data} for(let key in this._data) { Object.defineProperty(this, key, { get() { return props.data[key] }, set(val) { this._data[key] = val } }) } // 接受methods函数数据 for(let key in props.methods) { this[key] = props.methods[key] } }
⑤ 渲染页面
编辑myvue.js
,通过字符串操作完成页面数据的替换
// 渲染页面数据的函数 render() { let reg = /\{\{\s*(\w*\(?\)?)\s*\}\}/g; let newHtml = this._html.replace(reg, (a, b) => { console.log(a, b, "正则替换") if(b.endsWith("()")) { // 这是一个函数,需要调用执行 return this[b.substring(0, b.length-2)]() // this['introduction()']() } else { // 这是一个变量,直接获取 return this[b] // this['name'] } }) // console.log(newHtml, "newHtml") this.$el.innerHTML = newHtml }