目录
1、修改数组的内容出现的问题
点击第一个按钮,可以切换数组people的第0项的name属性,但是点击第二个按钮,修改people的整个第2项,发现页面上数据没变,我门在控制台输出people的第2项发现,数据是被修改了,但是页面没有渲染,说明Vue没有检测到数组数据的改变。
解决:看本节3和4
<div id="block"> <div v-for="(item,index) in people" :key="item.id"> {{item.name}}<br> {{item.age}}<br> ----------- </div> <button type="button" @click="change001Name">点击切换001的名字</button> <button type="button" @click="change003">点击切换003整体</button> </div> <script type="text/javascript"> Vue.config.productionTip = false; const vm = new Vue({ el: '#block', data: { people: [ {id: 0,name: '001',age: 30}, {id: 1,name: '002',age: 20}, {id: 2,name: '003',age: 40}] }, methods:{ change001Name(){ this.people[0].name = "000" }, change003(){ this.people[2] = {id:3,name:"004",age:80} } } }) </script>
2、手写Vue检测数据的原理
/*这里的缺点是:
1:没有做数据代理
2:没有做深度的检测,如果对象里面有对象,或者对象里面的数组有对象
就无法添加getter和setter
*/
var people = {
name:"potkiss",
height:90,
age:80,
friend:{
name:'kk'
},
family:[{name:'father'},{name:'mother'}]
};
let a = new Observe(people);
let vm = {}
vm._data = data = a
console.log(vm)
function Observe(re){
//遍历构造函数的属性
Object.keys(re).forEach((item,index)=>{
//this指向的是实例对象,给这个this添加属性
Object.defineProperty(this,item,{
get(){
//这里可以像数组一样获取对象的值
return re[item]
},
set(val){
console.log(`${val}被改了,开始解析模版,生成虚拟dom,进行比较`)
re[item] = val
}
})
})
}
3、Vue.set()和vue实例的$set()
这样操作后的数据是可以被vue检测到的,是有getter和setter的,能后引发后面的一系列动作,
缺点:不能在Vue下或者Vm根下添加数据
/*这样操作后的数据是可以被vue检测到的,
是有getter和setter的,能后引发后面的一系列动作,
缺点:不能在Vue下或者Vm根下添加数据
*/
<div id="block">
{{name}}<br>
{{height}}<br>
年龄:{{hobb.age}}<br>
爱好<br>
{{hobb.name}}<br>
家人<br>
<ul v-for="(item,index) in family" :key="index">
<li>{{item.name}}</li>
</ul>
<button type="button" @click="change001">点击添加年龄值</button>
<button type="button" @click="change002">点击添加家庭成员</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vm = new Vue({
el:'#block',
data:{
name:"potkiss",
height:190,
hobb:{
name:'写代码',
},
family:[{name:'father'},{name:'mother'}]
},
methods:{
change001(){
/*注意:Vue.set()和vm.$set()方法,
都无法在data或者Vm上直接添加数据,
只能在data或者vm上面的某一个对象上追加,
只有用set()方法追加的数据才可以被VUe检测到
才可以进行后面的解析模版,等一系列操作。
*/
Vue.set(this.hobb,'age','99') //属于Vue上的方法
this.$set(this.hobb,'age','99') //属于Vm上的方法
},
change002(){
/*
第一个在数组的第二位置追加了一个元素
第二个吧数字的第一位替换了,
这样操作后的数组里面的对象是有getter和setter的
但是不建议这样做,操作数组我们可以用后面的7个常用
操作数字的方法。
*/
Vue.set(this.family,2,{name:'potkiss'}) //属于Vue上的方法
this.$set(this.family,0,{name:'potkiss'}) //属于Vm上的方法
}
}
})
</script>
4、操作数组的数据发生改变
(1)注意只有下面的这7个可以改变数组的顺序的方法才可以,像filter就不行,如果想用filter就吧过滤好的数组用7个方法替换即可。
(2)这七个方法其实已经不是原本的Array上的方法了,而是Vue给重新打包分装好的,当我们使用这5个方法时,(你可以在控制台输入这两条语句let a =[];vm.family.shift === b.shift;然后看结果是不是false)
1、调用原本Array上的原本的方法,
2、解析模版,生成虚拟DOM,diff对比,渲染。。。
/*
(1)注意只有下面的这7个可以改变数组的顺序的方法才可以,
像filter就不行,如果想用filter就吧过滤好的数组用
下面的7个方法替换即可。
(2)这七个方法其实已经不是原本的Array上的方法了,
(你可以在控制台输入这两条语句let a =[];vm.family.shift === b.shift;然后看结果是不是false)
而是Vue给重新打包分装好的,当我们使用这5个方法时,
1、调用原本Array上的原本的方法,
2、解析模版,生成虚拟DOM,diff对比,渲染。。。
*/
<div id="block">
家人<br>
<ul v-for="(item,index) in family" :key="index">
<li>{{item.name}}</li>
</ul>
<button type="button" @click="change001">点击添加年龄值</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
let vm = new Vue({
el:'#block',
data:{
family:[{name:'mother',age:20},{name:'father',age:80}]
},
methods:{
change001(){
this.family.splice(0,2,{name:'potkiss'}) //替换数组的某长度元素,{第几位开始,到第几位,换成啥}
this.family.pop() //移除最后一个
this.family.push({name:'potkiss'}) //后面追加一个
this.family.shift() //删除第一个
this.family.unshift({name:'potkiss'}) //前面追加一个
this.family.sort(function(a,b){return b.age-a.age}) //排序,参数必须是一个函数,这里是由大到小排列
this.family.reverse() //翻转数组
}
}
})
</script>
5、v-model的表单获取,(细节满满)
<!-- 这里给submit事件添加了一个阻止默认行为的属性 -->
<form id="block" @submit.prevent="sub">
<!-- 这里的trim是删除输入字符前后的空格 -->
姓名:<input type="text" v-model.trim="UserInfo.name"><br>
<!-- 这里的trim是删除输入字符前后的空格 -->
密码:<input type="password" v-model.trim="UserInfo.password"><br>
<!-- 这里type为number是为了让用户只可以输入数字,v-model后面的number是让用户输入的内容强制变为数字类型的数据 -->
年龄:<input type="number" v-model.number="UserInfo.age"><br>
性别:
<!-- 这里的name可以让多个单选框只可以选中一个,但是要注意还要写一个value的值,才可以获取到,不然获取到的就是空字符串 -->
男<input type="radio" name="sex" value="male" v-model="UserInfo.sex">
女<input type="radio" name="sex" value="female" v-model="UserInfo.sex"><br>
爱好:
<!-- 这里也是,一定要写value,不然v-model获取到的就是checked这个属性,又因为三个绑定了同一个字段,所以会同时操作三个 -->
<!-- 还要注意下面的数据要是数组,不然就还是获取的checked这个属性的值 -->
吃饭:<input type="checkbox" v-model="UserInfo.hobby" value="eat">
喝酒:<input type="checkbox" v-model="UserInfo.hobby" value="drink">
抽烟:<input type="checkbox" v-model="UserInfo.hobby" value="smoke"><br>
地址:
<!-- 注意这里要绑定在select上 -->
<select v-model="UserInfo.address">
<option value="" >选一个</option>
<option value="xj">新疆</option>
<option value="bj">北京</option>
<option value="lz">兰州</option>
</select><br>
<!-- 这里的lazy是当用户失去焦点后获取输入的值 -->
<textarea cols="30" rows="10" v-model.lazy="UserInfo.text"></textarea><br>
<!-- 这里直接获取的checked的值 -->
<input type="checkbox" v-model="UserInfo.check" >协议<br>
<button>提交</button>
</form>
<script type="text/javascript">
Vue.config.productionTip = false
let vm = new Vue({
el:'#block',
data:{
UserInfo:{
name:'',
password:'',
age:null,
sex:'',
hobby:[], //注意这个数据类型
address:'',
text:'',
check:true
}
},
methods:{
sub(){
//把所有数据放在一个对象里面就可以一次性获取,并且方便后面添加属性,因为Vue.set()不能再根上添加
console.log(this.UserInfo);
}
}
})
</script>
6、filters过滤器的使用
注意这里用了dayjs这个库
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.1/dayjs.min.js"></script>
<body>
<div id="block">
1: {{comTime}}<br>
2: {{getTime()}}<br>
3 {{time | filterTime("abc") | cutString | CutString}}<br>
<div id="block2">
<!-- 这里的cutString也可以用是因为block2包含在block1中的 -->
4: {{time | filterTime("abc") | cutString }}<br>
5: {{time | filterTime("abc") | CutString}}<br>
</div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//全局的filter,别的组件也可以用
Vue.filter('CutString',function(value){
return value.slice(0,1);
})
let vm = new Vue({
el:'#block',
data:{
time:'1650216828381'
},
methods:{
getTime(){
return dayjs(Date.now()).format('YY-MM-DD HH-MM-SS')
}
},
computed:{
comTime(){
return dayjs(Date.now()).format('YY-MM-DD HH-MM-SS')
}
},
filters:{
filterTime(value,can){
console.log(value,can)
return dayjs(Date.now()).format('YY-MM-DD HH-MM-SS')
},
cutString(value){
/*
功能:只显示前4位,
问题:别的Vue实例(组件)不能用
解决:在实例前面弄全局的过滤器,注意,一次只能弄一个
*/
return value.slice(0,4);
}
}
})
let vm2 = new Vue({
el:'#block2',
})
</script>
7、5个不常用的内部指令
v-text;v-html;v-cloak;v-once;v-pre
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>内部指令</title>
</head>
<style>
[v-cloak]{
display: none;
}
</style>
<body>
<div id="block">
<div>{{data}},world</div>
<!-- v-text的特别的地方是会替换这个元素的全部文本,不能拼接 -->
<div v-text="data">world</div>
<!-- v-html可以解析有标签的结构,
但是有危险,可能会被xss攻击,一般不建议用!!!,
比如我写一个跳转的链接,通过这种可以解析
html的地方输入出去,比如评论,当有人点击了我的评论
然后会把点击的用户的本地没有HTTP-ONLY的cookie
给截取到发给劫持者的服务器,然后劫持者接可以为所欲为了
-->
<div v-html="data2"></div>
<!--v-cloak这个属性是如果页面引入的js加载太慢了,
没有渲染VUE时会出现,页面开始渲染了就消失了,
可以配合css,实现在页面还没渲染之前隐藏动态元素
-->
<div v-cloak>{{data3}}</div>
<!-- v-once指的是这个动态数据值渲染一次,渲染完就是静态数据了 -->
<div v-once >初始值{{n}}</div>
<div >{{n}}</div>
<button @click="n++">点击++</button>
<!-- 有v-pre的元素,vue在渲染页面时会跳过,加快渲染进度, -->
<div v-pre>静态的数据</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
let vm = new Vue({
el:'#block',
data:{
data:'hello',
data2:'<h2>hello</h2>',
data3:'potkiss',
n:0
}
})
</script>
</html>
8、自定义指令
注意:自定义指令在绑定时写的是v-xx;在自定义指令定义的地方写的xx;自定义指令的名字不能用驼峰命名,因为用了Vue就会把大写变成小写,也可以用短横杠连接如v-name-big;如果用了短杠,需要在directives里面的名字加引号如'name-big'
<body>
<div id="block">
<div v-text="n"></div>
<input type="text" v-f-bind="n">
<div v-big="n"></div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
//全局的自定义指令
//Vue.directive('name',{}或者function(){})
let vm = new Vue({
el:'#block',
data:{
n:1,
},
directives:{
//想实习一上来就获取焦点,所以要写出对象,只可以操作inserted方法
"f-bind":{
bind(element,binding){
//当自定义指令和元素成功绑定时调用
console.log(1);
element.value = binding.value;
},
inserted(element,binding){
//当绑定的元素被插入页面时调用
console.log(2);
element.focus();
},
update(element,binding){
//指令所在的模版被重新解析时
console.log(3);
element.value = binding.value;
}
},
big(element,binding){
//写出函数的形式相当于没有调用inserted这个方法
element.innerText = binding.value * 10
}
}
})
</script>