序:
声明式渲染(最基础用法)–>组件系统(更丰富的用法)–>客户端路由(单页面应用,局部更新,历史回退功能)–>集中式管理状态(功能比较大,用了大量业务数据,为了方便管理vuex)–>项目构建(前后端分离,前端 独立开发,测试,部署,上线)
mvc和mvvm
1. mvc(后端思想)

2. mvvm(前端思想)
①、界面(View):HTML + CSS,且HTML还是增强版;
②、数据模型(Model):保存页面所需要的变量和函数 的对象;
③、视图模型(ViewModel):自动保持页面与内存数据同步的特殊对象。
Vue的绑定原理(访问器属性 + 虚拟DOM树):
一切Vue的开始都是从new Vue(),首先创建Vue类型的对象,将模型对象的内容包裹进Vue类型的对象中托管。
a. 将原data隐姓埋名。并为data中每个变量自动请保镖(定义访问器属性get 和 set);
b. 打散引入的methods,让methods中的所有方法,都直接隶属于new Vue();
c. 结果methods中的方法和为data中变量请的保镖就平级了,所以对象中平级的方法,想使用平级的访问器属性,就必须加this
虚拟dom树:
**虚拟dom树:**Vue扫描HTML页面获取仅保存可能发生变化的元素,得到简版的dom树;
**何时创建:**new Vue()创建完对象后,自动扫描el: "#app"所指向的页面区域,在扫描过程中仅找出可能发生变化的元素,保存在一个新创建的虚拟dom树集合中;
优点:
a. 内容少:仅包含可能发生变化的元素,其余元素都不包含;
b. 遍历快: 可快速找到受影响的元素;
c. 渲染效率高: 只更新受影响的元素;
d.已经封装了DOM增删改查操作,避免了打量重复的编码。
修改变量后,Vue变化过程:
修改变量其实是修改变量的访问器属性;
修改访问器属性就会触发set()
触发set(),就会自动执行set中的通知函数
通知发给虚拟DOM树,告知那个变量的值发生了变化
虚拟DOM树遍历自己内部的元素,只找到受本次变化影响的元素
虚拟DOM树用已经封装好的DOM操作,只能更新页面中受影响的元素

简单指令
1.v-cloak
<div v-cloak>{{ msg }}</div>
解决插值表达式的闪烁问题(页面刷新加载时出现的{{}})
**注:**配合样式[v-cloak]display: none
- 不解析标签
- 只替换占位符,不覆盖原本内容
2. v-text
<div v-text = "msg"></div>
- 默认没有闪烁问题
- 覆盖元素中的内容
- 不解析标签
3.v-html
<div v-html= "msg"></div>
- 覆盖元素中内容
- 会解析标签
4.v-bind:(简写为‘:’)
<input type='button' v-bind:title='mytitle'></div>
绑定属性,数据单向绑定
不加v-bind时,则认为’mytitle’为字符串,加上后则为变量或者合法js表达式
三种绑定方法:
- 对象型 ‘{red:isred}’
- 三元型 ‘isred?“red”:“blue”’
- 数组型 '[{red:“isred”},{blue:“isblue”}]
5.v-on:(简写‘@’)
绑定事件
点击:v-on:click = ‘方法名’ -------@click
悬浮:v-on:mouseover = ‘方法名’ -------@mouseover
离开:v-on:mouseout = ‘方法名’ -------@mouseout
移动端 touchstart、touchend、touchmove…
6.事件修饰符
写在绑定事件名后:如 @click.stop
- .stop:阻止冒泡
- .prevent:阻止默认行为,提交事件不再重载页面
- .capture:改为事件捕获
- .self:事件在该元素本身触发时 触发回调
- .once:只触发一次
7.数据双向绑定指令 v-model
<input type ='text' v-model = '属性名'>
**注:**仅适用于表单元素
8.v-for循环遍历
<p v-for = '(值, 索引) in 数组名/对象名' :key=''></p>
- key的值只能为 srting 或者 number类型
- 使用绑定属性的形式指定key的值
9.v-if、v-show
<p v-if = '条件'></p>
<p v-show = '条件'></p>
- v-if 删除或创建元素
- 有较高切换性能消耗,若元素需频繁切换,不适用v-if
- v-show 隐藏或显示元素,等同切换 display值 => ‘none’
- 有较高初始渲染消耗,若元素并不需要显示,不适用v-show
- v-show 只编译一次,后面其实就是控制 css,而 v-if 不停的销毁和创建,故 v-show 性能更好一
附:为什么避免 v-if 和 v-for 在一起用
当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,通过 v-if 移动到容器元素,不会 再重复遍历列表中的每个值。取而代之的是,我们只检查它一次,且不会在 v-if 为否的时候运 算 v-f
过滤器
用作常见的文本格式化,可使用在:mustache插值 和 v-bind表达式;
{{ msg | 过滤器名称(传参)}} ===> 先通过过滤器对数据进行处理后,在返回给name显示;
‘|’ => 管道符;
过滤器调用 采用就近原则,当全局过滤器与私有过滤器重名,则优先采用私有过滤器;
1.全局过滤器
所有的vm实例都共享
定义:Vue.filter(‘过滤器名称’, function(msg,arg){ //数据处理 })
定义位置:在vm实例外面定义
参数:
- msg:管道符前面的数据
- arg:接收的参数
2.私有过滤器(局部)
定义位置:vm内部,filters: {},与methods同级
定义:filters:{ 过滤器名称(管道符前数据,接受的参数) {}}
《=========》
(小知识):string.padStart(2, ‘0’) 用来给个位数补零
自定义指令
自定义指令名以 ‘v-’ 开头
1.全局
定义:Vue.directive(‘自定义指令吗’, {})
参数1:指令名称 定义时,自定义指令名不加 ‘v-’ 前缀,调用时需加上
参数2:对象,对象中有一些指令相关函数,在特定阶段执行相应操作
例:焦点事件
<input type='text' v-focus>
Vue.directive('focus', {
每个函数第一个参数为 el,表示被绑定指令的元素,此时可使用原生的方法
bind:function(el){ //指令绑定到元素上时执行,仅一次
el.focus() //元素还未插入到DOM 中,不生效
//多为与样式相关的操作
}
inserted:function(el){ //元素插入到 DOM 中的时候执行,仅一次
el.focus() //元素已经插入到DOM 中,生效
//多为与js相关的操作
}
updated:function(){ //VNode 更新时执行,可能触发多次
}
})
函数相关参数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v1uT6JV9-1659282675374)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220523152632331.png)]](https://i-blog.csdnimg.cn/blog_migrate/59cb9c10fb22f627834b389202b7e77c.png)
2.私有
定义位置:在vm实例内定义,与 methods 同级
定义:directives: {'自定义指令名': {相关函数}}
函数简写(全局/私有):
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8VNnPiQj-1659282675375)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220523152736818.png)]](https://i-blog.csdnimg.cn/blog_migrate/329dde8df637e1eae91ae904b0f2426e.png)
实例生命周期
生命周期函数与 methods 同级
**html:**
<div id='app'>
<input type='button' value='改变msg' @click="msg='No'">
<p id='p'>{{ msg }}</p>
</div>
**js:**
var vm = new Vue({
el: '#app',
data: {
msg: 'ok'
},
methods: {
show() { console.log('执行了show方法') }
},
})
***创建期间的生命周期函数***
beforCreate(){ //实例被完全创建之前执行
//在这个生命周期内,data 和 methods 中的数据未初始化
console.log(this.msg); //undefined
this.show() //报错,this.show() is not a function
}
created() {
//在这个生命周期内,data 和 methods 中的数据初始化完成
//可调用 methods 中的方法和操作 data 中的数据
console.log(msg) //ok
this.show() //执行了show方法
}
beforeMount() { //模板在内存中编译完成,还未渲染到页面中
//在这个生命周期内,页面中的元素未被替换,还是原来写好的模板字符串
console.log(document.getElementById('p').innerHtml) //{{ msg }}
}
mounted() { 实例创建期间的最后一个生命周期函数
//将编译好的内存中的模板挂在到页面中
console.log(document.getElementById('p').innerHtml) //ok
}
***组件运行阶段的生命周期函数***
beforeUpdate() { //界面还未被更新,但数据已被更新
//组件/数据被改变时触发
//例:当点击input按钮时:
console.log(document.getElementById('p').innerHtml) // ok ===>页面未更新
console.log(this.msg) // No ===>数据已经改变
}
updated(){ //页面与数据都已更新
//组件/数据被改变时触发
//例:当点击input按钮时:
console.log(document.getElementById('p').innerHtml) // No ===>页面已更新
console.log(this.msg) // No ===>数据已经改变
}
***组件销毁阶段的生命周期函数***
beforeDestroy() {
//还未执行销毁过程
}
destroyed() {
//组件被完全销毁,此时组件中所有的数据、方法、指令、过滤器等 都不可用
}
动画
将需要动画的元素使用 标签包裹
样式:
v-enter, v-leave-to {} ——>进入和离开
v-enter-active, v-leave-active {} ——>中间过程动画
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ZPkq5Xs-1659282675376)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220523152906898.png)]](https://i-blog.csdnimg.cn/blog_migrate/f8c7f9956085206128b03177287fb76d.png)
点击查看:vue动画详细介绍
组件
定义:拆分vue实例的代码量,不同组件划分不同功能模块,需要时调用
1.全局创建
<div id='app'>
<my-com1></my-com1>//引用组件
</div>
<template id='tmp1'>
<div> //根元素
<p>创建的组件</p>
</div>
</template>
****Vue.component('组件名称', { //创建的组件模板对象 })****
Vue.component('myCom1', {
template: '#tmp1'
})
注:
1.若组件名称使用驼峰命名时,调用时用小写,并且单词之间‘-’连接,若未使用驼峰命名,则直接引用组件名;
2.常见的模板对象必须有一个根元素包裹
2.局部创建(私有)
实例内部,与methods同级
components: { //定义实例内部私有组件
组件名: {
template: ''
}
}
3.组件中 data、methods
Vue.compontent('com1', {
template: '',
data: function() {
return {}
//组件中的 data 为一个函数,且必须 return 一个对象
},
methods: {}
})
4.组件切换
<component :is="'组件名'"></component>
5.组件传值
5.1.父传子
<div id='#app'>
<com1 :parentmsg='msg' v-on:func='show'></com1>
</div>
<template id='son'>
<div>
<input type='button' value='点击调用父组件传递的方法' @click=‘parentfunc’>
<p>子组件---{{ parentmag }}</p>
</div>
</template>
var vm = new Vue({
el: '#app',
data: {
msg: '父组件数据'
},
methods: {
show() {
console.log('调用了父组件方法')
}
},
//定义子组件:
components: {
com1: {
template: '#son',
props: ['parentmsg'], //将父组件传递的parentmsg属性在props数组中定义,然后才能使用,但是不做更改
methods: {
parentfunc() {
this.$emit(‘func’)
}
}
}
}
})
总结:
(1)在父组件调用子组件的地方,绑定一个自定义属性,属性值为要传递的数据,在子组件定义的地方通过props数组接收
(2)若调用父组件的方法,在父组件调用子组件的地方,使用事件绑定方式,自定义事件名,在子组件的方法中,通过this.$emit('方法名')调用父组件方法
5.2.子传父
在子组件调用的地方,添加自定义事件,这个事件由父组件执行,在子组件中,使用this.$emit('自定义事件名称','需要传递的参数')来讲数据传递给父组件
获取DOM元素及组件
使用ref
<div id='app'>
<input type='button' @click='getElement' value='获取元素'>
<p ref='content'>来找我啊</p>
<com ref=‘sonContent’></com>
</div>
<template id='son'>
<div>
<p>哈哈哈</p>
</div>
</template>
var com = {
template: '#son',
data() {
return {
msg: 'son msg'
}
},
methods: {
show() {
console.log('这里是子组件的方法')
}
}
}
var vm = new Vue({
el: '#app',
data: {},
methods: {
getElement() {
console.log(this.$refs.content.innerText); //来找我啊
console.log(this.$refs.sonContent.msg); //son msg
this.$refs.sonContent.show(); //这里是子组件的方法
}
},
components: {
com
}
})
在这里,通过给元素添加 ref 属性,使用this.$refs.ref属性名.innerText去获取元素的内容,避免了DOM的操作,也可以给子组件添加 ref 属性,同样使用 this.$refs.ref属性名.变量名/方法名 去获取子组件中的变量或者方法。
路由
1、vue-router
var routerObj = new VueRouter({ //创建路由实例
routes: [ //路由匹配规则
{
path: '/...', //路径
component: 组件模板对象, //组件模板对象,不能为组件引用名称
},
{path: '', component: },
...
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj, //将路由规则对象注册到vm实例上
})
在页面中使用`<router-view></router-view>`来显示组件内容
2、router-link
(1)、默认渲染为一个 a 标签
(2)、tag属性:将router-link转为指定的元素
<router-link to='/login' tag='span'>登录</router-link> //此时渲染为 span 标签
<router-link to='/register'>注册</router-link> //此时为默认渲染 a 标签
3、路由 redirect 重定向
routes: [
{path: '/', redirect: '/login'} //如果为根路径,则重定向到登录页路径
]
注: 此处重定向不同于 node 中的重定向
跳转按钮的样式修改
- 在使用
<router-link>时,标签会有一个默认的类名:router-link-active,可以通过这个类名在修改被选中的标签的样式 - 可以在router的构造函数中,通过linkActiveClass属性来修改默认的类名
4、路由传参之 — query
4.1、获取参数: this.$route.query
4.2、传参:
在跳转按钮处定义点击事件,在点击事件中:
this.$router.push({
path: '跳转的路径',
query: {
//传递的参数
id: '01',
neme: '哈哈哈'
}
})
**注:**在<router-link>标签中,添加点击事件时,需要写成 @click.native
4.3.使用params时,在路由实例中的匹配规则内,需要加上 ‘name’ 属性,值为路径名称(不加‘/’)
**注:**params必须使用name来引入
4.4、路由嵌套 — children
在某个路由跳转的组件中,内部需要使用路由时,在路由匹配规则中格式应为:
<router-link to='/account/login'>登录</router-link>
routes: [
{
path: '/account',
component: account,
children: [
path: 'login', //此时路径不加'/'
component: login
]
} //如果为根路径,则重定向到登录页路径
]
5、路由命名视图
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IEiVCFHN-1659282675377)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220523154346943.png)]](https://i-blog.csdnimg.cn/blog_migrate/c0c90088ca1c757689945413270f9a1d.png)
图中布局分为头部(header),左侧边栏(leftBox),内容(mainBox)三个组件组成
<div id="app">
<router-view></router-view>
<div class="content">
<router-view name='left'></router-view>
<router-view name='main'></router-view>
</div>
</div>
路由匹配规则
routes: [
{
path: '/',
components: {
'default': header,
'left': leftBox,
'main': mainBox
}
},
]
watch 监听
1、监听文本框内容变化
watch:{} :与 methods 同级,可以监视 data 中指定的数据的变化,然后触发 watch 中对应的 function 函数
<div id='app'>
<input type='text' v-model='txtValue'>
</div>
data:{
txtValue: ''
},
watch: {
txtValue: function(newVal,oldVal) {
//当输入框中值改变时,触发该事件
//方法中可以包含newVal、oldVal两个参数,代表改变之前的值后改变之后的值
}
}
2、监听路由变化
watch: {
'$route.path': function(newVal,oldVal) {
//当输入框中值改变时,触发该事件
//方法中可以包含newVal、oldVal两个参数,代表改变之前的值后改变之后的值
}
}
3、computed 计算属性
computed :{} :与 methods 同级,可以定义一些属性,为计算属性,其本质是一个方法,只不过在使用这些计算属性的时候,是将其名称直接当作属性来使用,并不会把计算属性当作方法去调用
<div id='app'>
<input type='text' v-model='firstName'>
<input type='text' v-model='lastName'>
</div>
data: {
firstName: ''
},
computed: {
lastName: function() {
return this.firstName
}
}
【注】:
1、计算属性在调用的时候不加 () 调用,直接当作普通属性使用
2、计算属性的 function 内部所用到的任何 data 中的数据发生变化,就会重新计算该计算属性中的值
3、计算属性的求值结果会被缓存,方便下次直接使用,如果计算属性的方法中所依赖的任何数据都没有发生变化,则不会重新对计算属性求值
修饰符
1.Lazy
Lazy 修饰符的作用是,改变输入框的值时value不会改变,当光标离开输入框时,v-model绑定的值value才会改变。
<input type="text" v-model.lazy="value">
<div>{{value}}</div>
data() {
return {
value: '222'
}
}
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lbc8IEhs-1659282675378)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220706001452133.png)]](https://i-blog.csdnimg.cn/blog_migrate/01abdf2c60d1efd8f5733781d69680c7.png)
2. Trim
Trim 修饰符的作用类似于javaScript中的 Trim()方法,作用是把v-model 绑定的值的首尾空格给过滤掉。
<input type="text" v-model.trim="value">
<div>{{value}}</div>
data() {
return {
value: '123123'
}
}
失去光标前
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DcPCcsQj-1659282675379)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220706001718926.png)]](https://i-blog.csdnimg.cn/blog_migrate/100f669af1fd44c4a0becfa2b6989a6c.png)
失去光标后
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qcMnFUP7-1659282675380)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220706001700068.png)]](https://i-blog.csdnimg.cn/blog_migrate/c6215030f076589542e86c0dbe5a892b.png)
3. Number
Number 修饰符的作用是将数值转成数字,但是先输入字符串与先输入数字是两种情况。
<input type="text" v-model.number="value">
<div>{{value}}</div>
data() {
return {
value: '123123'
}
}
先输入数字字符串时,只取前面数字部分
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y6YYrXDh-1659282675381)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220706001815094.png)]](https://i-blog.csdnimg.cn/blog_migrate/e30e982eaeb210a1c3e539655afbdb4f.png)
当开始输入非数字的字符串时,由于Vue无法将字符串转换为数值,所以属性值将实时更新成相同的字符串,即使后面输入数字,也将被视作字符串。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZuLhYdl0-1659282675381)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220706001841004.png)]](https://i-blog.csdnimg.cn/blog_migrate/e6f56d8945b1c374dab611b0334a2b9f.png)
4. Stop
Stop修饰符的作用是阻止冒泡
<div @click="clickEvent(2)" style="width:300px;height:100px;background:red">
<button @click.stop="clickEvent(1)">点击</button>
</div>
methods: {
clickEvent(num) {
console.log(num)
}
}
// 不加 stop 点击按钮输出 1 2
// 加了 stop 点击按钮输出 1
5. Capture
事件默认是由里往外冒泡,capture 修饰符的作用是反过来,由外往内捕获
<div @click.capture="clickEvent(2)" style="width:300px;height:100px;background:red">
<button @click="clickEvent(1)">点击</button>
</div>
methods: {
clickEvent(num) {
console.log(num)
}
}
// 不加 capture 点击按钮输出 1 2
// 加了 capture 点击按钮输出 2 1
6. Self
只要学过 python 的都有感觉,这个就是他自身,只有当点击他的时候才能显示,别想从冒泡获取。比如,如果在最外层加上 .self ,当点击button的时候,只有点击最外层才能被触发,如果不加上 .self 会被冒泡
<div @click.self="clickEvent(2)" style="width:300px;height:100px;background:red">
<button @click="clickEvent(1)">点击</button>
</div>
methods: {
clickEvent(num) {
console.log(num)
}
}
// 不加 self 点击按钮输出 1 2
// 加了 self 点击按钮输出 1 点击div才会输出 2
7. Once
这个更好理解,只能触发一次,你再点击对我没用了
<div @click.once="clickEvent(1)" style="width:300px;height:100px;background:red">
<button @click="clickEvent(2)">点击</button>
</div>
methods: {
clickEvent(num) {
console.log(num)
}
}
// 不加 once 多次点击按钮输出 1
// 加了 once 多次点击按钮只会输出一次 1
8. Prevent
作用是阻止默认事件,什么是默认事件呢?比如我写一个 a 标签,他默认是跳转的,如果加上 .prevent 就可以在跳转之间加上自己的事件,保证显示咱们自己的行为
<a href="#" @click.prevent="clickEvent(1)">点我</a>
methods: {
clickEvent(num) {
console.log(num)
}
}
// 不加 prevent 点击a标签 先跳转然后输出 1
// 加了 prevent 点击a标签 不会跳转只会输出 1
9. Native
在项目开发中如果你用到了自己自定义的组件,并且想给他绑定一个点击事件,这时候就是 .native 发挥作用的时候了,他可以将事件绑定到根组件上,这样 click 事件就会生效了
// 执行不了
<My-component @click="shout(3)"></My-component>
// 可以执行
<My-component @click.native="shout(3)"></My-component>
10. Left,Right,Middle
这三个修饰符是鼠标的左中右键触发的事件
<button @click.middle="clickEvent(1)" @click.left="clickEvent(2)" @click.right="clickEvent(3)">点我</button>
methods: {
clickEvent(num) {
console.log(num)
}
}
// 点击中键输出1
// 点击左键输出2
// 点击右键输出3
11. Passive
当我们在监听元素滚动事件的时候,会一直触发 onscroll 事件,在pc 端是没什么问题的,但是在移动端,会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给 onscroll 事件整了一个 .lazy 修饰符
<div @scroll.passive="onScroll">...</div>
12. Sync
当父组件传值进子组件,子组件想要改变这个值时,可以这么做
// 父组件里
<children :foo="bar" @update:foo="val => bar = val"></children>
// 子组件里
this.$emit('update:foo', newValue)
.Sync 的作用就是可以简写
// 父组件里
<children :foo.sync="bar"></children>
// 子组件里
this.$emit('update:foo', newValue)
13. KeyCode
当我们这么写事件的时候,无论按什么按键都会触发事件
<input type="text" @keyup="shout(4)">
那么想要限制某一个按钮才能触发事件要怎么办?这个时候 keyCode 就派上用场了
<input type="text" @keyup.keyCode="shout(4)">
Vue 提供的 keyCode
//普通键
.enter
.tab
.delete //(捕获“删除”和“退格”键)
.space
.esc
.up
.down
.left
.right
//系统修饰键
.ctrl
.alt
.meta
.shift
eg:
// 按 ctrl 才会触发
<input type="text" @keyup.ctrl="shout(4)">
// 也可以鼠标事件+按键
<input type="text" @mousedown.ctrl="shout(4)">
// 可以多按键触发 例如 ctrl + 67
<input type="text" @keyup.ctrl.67="shout(4)">
Vue双向绑定的实现
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bmr1Zkj2-1659282675382)(C:\Users\huawei\Videos\GIF\1658933955026.gif)]](https://i-blog.csdnimg.cn/blog_migrate/a8eb75aa7e72f051ea41f2db8a2253ac.gif)
vue2
<!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 style="position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);">
<input type="text">
<h1></h1>
<button>点击</button>
</div>
</body>
<script>
const input = document.getElementsByTagName('input')[0]
const h1 = document.getElementsByTagName('h1')[0]
const btn = document.getElementsByTagName('button')[0]
let data = { text: '' }
input.addEventListener('input', function(e) {
data.text = e.target.value
})
btn.onclick = function() {
data.text = '晚上好,小龙虾!'
}
Object.defineProperty(data, 'text', {
get: function() {
return data['text']
},
set: function(newValue) {
h1.innerText = newValue
input.value = newValue
}
})
</script>
</html>
vue3
<!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" style="position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);">
<input type="text">
<h1></h1>
<button>点击</button>
</div>
</body>
<script>
const h1 = document.getElementsByTagName('h1')[0]
const btn = document.getElementsByTagName('button')[0]
let data = { text: '' }
function effect() {
h1.innerText = data.text
}
const obj = new Proxy(data, {
// target当前对象 key属性
get(target, key) {
return target[key]
},
// target当前对象 key属性 newValue它的值
set(target, key, newValue) {
target[key] = newValue
effect()
return true
}
})
btn.onclick = function() {
obj.text = '晚安!'
}
</script>
</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" style="position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);">
<input type="text">
<h1></h1>
<button>点击</button>
</div>
</body>
<script>
const h1 = document.getElementsByTagName('h1')[0]
const btn = document.getElementsByTagName('button')[0]
let data = { text: '' }
function effect() {
h1.innerText = data.text
}
const obj = new Proxy(data, {
// target当前对象 key属性
get(target, key) {
return target[key]
},
// target当前对象 key属性 newValue它的值
set(target, key, newValue) {
target[key] = newValue
effect()
return true
}
})
btn.onclick = function() {
obj.text = '晚安!'
}
</script>
</html>
775





