Vue基础(复习)

序:

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

mvc和mvvm

1. mvc(后端思想)

img

2. mvvm(前端思想)

①、界面(View):HTML + CSS,且HTML还是增强版;

②、数据模型(Model):保存页面所需要的变量和函数 的对象;

③、视图模型(ViewModel):自动保持页面与内存数据同步的特殊对象。

Vue的绑定原理(访问器属性 + 虚拟DOM树):

一切Vue的开始都是从new Vue(),首先创建Vue类型的对象,将模型对象的内容包裹进Vue类型的对象中托管。

a. 将原data隐姓埋名。并为data中每个变量自动请保镖(定义访问器属性getset);

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操作,只能更新页面中受影响的元素

img

简单指令

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表达式

三种绑定方法:

  1. 对象型 ‘{red:isred}’
  2. 三元型 ‘isred?“red”:“blue”’
  3. 数组型 '[{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)]

2.私有

定义位置:在vm实例内定义,与 methods 同级
定义:directives: {'自定义指令名': {相关函数}}
函数简写(全局/私有):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8VNnPiQj-1659282675375)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220523152736818.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)]

点击查看: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)]

图中布局分为头部(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)]

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)]

失去光标后

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qcMnFUP7-1659282675380)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220706001700068.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)]

当开始输入非数字的字符串时,由于Vue无法将字符串转换为数值,所以属性值将实时更新成相同的字符串,即使后面输入数字,也将被视作字符串。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZuLhYdl0-1659282675381)(C:\Users\huawei\AppData\Roaming\Typora\typora-user-images\image-20220706001841004.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)]

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>

参考

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值