《基础篇第2章:vue2基础》包含Vue2知识点、个人总结的使用注意点及碰到的问题总结

72 阅读13分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

在这里插入图片描述 @[toc]

第2章:vue2基础 - 指令

2.1如何使用vue

如何使用Vue: 1、引入Vue的核心JS文件 2、准备Dom结构 3、实例化组件 通过el属性,挂载元素,绑定id为app的html元素 通过data属性,定义数据,可以在html代码段中显示的数据 4、获取数据 数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值

2.2vue模板语法

在这里插入图片描述

<div id="app">
        <h1>插值语法</h1>
        <h1>{{name}}-{{address}}</h1>
        <hr>
        <h1>指令语法</h1>
        <a v-bind:href="url">点我跳转百度</a>
        <a :href="url"></a>
</div>

2.3文本渲染 (都是针对标签属性)

2.3.1 v-text

在这里插入图片描述

注意点1:v-text=’msg’会拿msg的值替换标签体的值,换句话说如果设置了v-text标签且无论msg是否为空,那么标签体的内容“你好”就失效了,不会显示

比如data中msg值为Hello World,那么页面会显示Hello World,如果msg=’ ’值为空,那么h3整个标签都不会显示,更不会显示标签体内容“你好”
<h3 v-text=’msg’>你好</h3>

==在 html 中插入数据,跟{{}}功能一致:== 在这里插入图片描述

2.3.2 v-once

注意点1:切记不要弄混了文本渲染v-once和事件修饰符的.once 文本渲染v-once:指页面只渲染一次 事件修饰符的.once:指的是方法只调用一次有效 在这里插入图片描述

2.3.3 v-html

在这里插入图片描述

注意点1(重点关注):使用v-html存在安全隐患,比如下面图片举例说明,str2是一个黄色钓鱼网址链接,如果使用v-html,那么会解析标签内容结构到页面上变成一个链接,如果用户点击了该链接,那么恶意网站就能获取用户登录cookie(cookie包含了你的用户名密码),那么恶意网站就会做坏事了,比如盗号、恶意转账盗钱等。所以为了避免安全隐患,请谨慎使用v-html标签,如果你确定代码部分是安全的,不会有乱七八糟的东西,那么使用v-html标签也没事。而v-text就不会解析html元素,就不会把str2变成链接地址,这样因为跳转不了,恶意软件也就无法获取用户cookie 了,也就做到了避免安全隐患。 在这里插入图片描述

注意点2:把文本内容进行解析,生成 html 结构并渲染。 在这里插入图片描述 在这里插入图片描述

2.4数据绑定

v-bind 指令可以绑定元素的属性,动态给属性赋值。比如: v-bind:class、 v-bind:style、 v-bind:href 形式。 注意点1:v-bind标签不仅可以传属性值,还可以传方法

v-bind:是单项绑定,下图中黄线存在,粉线不存在 注意点2:总结:v-bind单向绑定 v-model双向绑定 在这里插入图片描述

v-model只能用于表单元素,换句话说只作用于有value属性的标签(表单元素) 在这里插入图片描述

在这里插入图片描述

2.5el和data两种写法

在这里插入图片描述

el两种方法 在这里插入图片描述

data两种写法 在这里插入图片描述

说明点1:el两种用哪个都可以,但是data两种方式分情况,后续使用组件,data使用方式必须使用“函数式”,不然会报错。

data写法函数式的普通方式:data:function () {}
 data:function () {
console.log('@@@',this)  //指代vue对象
        return{
           name:"尚硅谷",
           address:"哈尔滨",
             }
}
data写法函数式的简化普通方式:data() {}
data() {
console.log('@@@',this)  //指代vue对象
    return{
        name:"尚硅谷",
        address:"哈尔滨",
        }
}
data的第二种写法箭头函数式:data:()=> {}
data:()=> {
      console.log('@@@',this)  //指代window对象
      return{
           name:"尚硅谷",
           address:"哈尔滨",
      }
}

针对函数式,箭头函数方式this指代window对象,普通方式this指代vue对象 在这里插入图片描述 在这里插入图片描述

2.6MVVM模型

在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

2.7数据代理

锚点

2.7.1回顾Object.defineProperty()

回顾Object.defineProperty()方法,该方法可动态设置属性。 defineProperty(目标对象, key, {config}) 注意点1:defineProperty中如果定义使用get()和set(),那么属性value和writable:true必须注释掉,不然报错 注意点2:如果defineProperty中只定义value属性,那么控制台赋值无效,即必须设置writable:true才能对person属性实现更新

let number = 18
let person = {
        name:"张三",
        sex:"男"
}

Object.defineProperty(person, "age", {
        value:18,
}

在这里插入图片描述 如果defineProperty()方法设置属性enumerable:true、writable:true、configurable:true就可实现跟局部定义变量属性一样的效果,可增删改

Object.defineProperty(person, "age", {
        value:18,
        enumerable:true,    //控制属性是否可以枚举(也就是是否可以遍历访问到),默认值是false
        writable:true,      //控制属性是否可以被修改,默认值是false
        configurable:true,   //控制属性是否可以被删除,默认值是false
}

在这里插入图片描述 defineProperty()方法定义get()和set()

Object.defineProperty(person, "age", {       
        //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
        get() {
             console.log('有人读取age属性了')
             return number
         },
        // //当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
         set(value) {
             console.log('有人修改age属性了')
            number = value
        }
    })

2.7.2何为数据代理

数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)

 let obj = {x:100}
    let obj2 = {y:200}

    Object.defineProperty(obj2, "x", {
        get() {
            return obj.x
        },
        set(value) {
            obj.x = value
        }
})

在这里插入图片描述

2.7.3vue中的数据代理

问题:数据代理干了啥?

答案: 简单说:通过Object.defineProperty()方法把data对象所有属性添加到vm(Vue原型)对象上,且为每个属性指定一个getter/setter。 复杂说:首先vue把data对象所有属性添加到命名为_data的对象中,通过Object.defineProperty()方法把_data对象所有属性添加到vm(Vue原型)对象上,并为每一个添加到vm上的属性,都指定一个getter/setter,这样vm对象就有了属性name和address,不然你想操作属性只能使用_data.name_或者data.address,这样很不方便。 在这里插入图片描述

在这里插入图片描述

2.8事件处理

2.8.1事件的基本使用

注意点1:定义事件函数时,普通函数this指代vue对象,而箭头函数中this指代Window对象。 注意点2:定义事件没参数小括号()可省略,但是用在插值表达式使用一个函数返回值就必须带小括号(),比如 全名:{{fullName()}}。 注意点3:不被vue所管理的函数那么请使用箭头函数。 在这里插入图片描述

代码:

<button v-on:click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2($event, 66)">点我提示信息2(不传参)</button>

var vm = new Vue({
        el: "#app",
        data: {},
        methods:{
            showInfo1(event) {
                console.log(event);
            },
            showInfo2(event, number) {
                console.log(event);  //指代事件对象
                console.log(number);
                console.log(this);  //箭头函数中指代Window对象,普通方法指代vue对象
            }
        }
    })

2.8.2 $event 对象

在事件处理函数中访问 DOM 原生事件 event 对象,可以使用特殊变量$event 对象传 入。

2.8.3事件的修饰符

在这里插入图片描述 在这里插入图片描述

事件修饰符举例说明:

1.默认<a>标签会跳转,想实现点击不跳转就设置@click.prevent=”事件函数”或者<a href="javascript:void(0);" >
2.阻止事件冒泡
捕获阶段:指由外往内
冒泡阶段:指由内到外
举例:2个div1,div2,都有点击事件,如果不设置事件修饰符stop,那么点击div2触发事件完成之后,还会触发div1事件,而设置stop修饰符后,不会触发冒泡阶段。

在这里插入图片描述

3.once:事件只触发一次(常用),即无论点击多少次按钮,按钮只触发一次。
4.capture:设置使用事件的捕获模式,不设置使用冒泡模式(由内向外触发事件函数);

使用事件的捕获模式 :不设置.capture,先执行showMsg(2),再执行showMsg(1),设置之后先执行showMsg(1),再执行showMsg(2)

 <div class="box1" @click.capture="showMsg(1)">
        div1
        <div class="box2" @click="showMsg(2)">
            div2
        </div>
</div>

showInfo(e){
   console.log(e.target)
}
5.self:只有event.target是当前操作的元素时才触发事件
举例:div和button都设置事件函数,如果div标签不设置.self,那么点击button按钮会调用2次函数,且输出event.target为:<button>点我提示信息</button>,如果div标签设置了.self那么只有点击div才会触发事件且输出event.target为:<div class="demo1"><button>点我提示信息</button></div>,而只点击button只会触发button的事件且输出event.target为:<button>点我提示信息</button><div class="demo1" @click.self="showInfo">
        <button @click="showInfo">点我提示信息</button>
</div>
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕
举例:定一个事件会计算10W次,如果不设置.passive,那么会等事件全部结束后,滚轮才会向下移动一个位置,而设置了.passive,那么滚轮会先移动而不会去等待事件执行是否结束。

在这里插入图片描述 事件的默认行为立即执行,无需等待事件回调执行完毕

<ul @wheel.passive="demo" class="list">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
</ul>

demo(){
   for (let i = 0; i < 100000; i++) {
          console.log('#')
   }
   console.log('累坏了')
}

2.8.4键盘事件(键值修饰符)

在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 两个隐藏小知识点: 1.事件修饰符和串行,比如:@click.stop.once 2.对于键盘按键想实现按Ctrl+y才调用,则使用@keyup.ctrl.y=”showInfo”

2.9条件渲染

锚点 在这里插入图片描述 在这里插入图片描述

注意点1:使用v-if和v-else-if和v-else时中间不可以中断,否则无效,比如: 在这里插入图片描述 注意点2:

假设我有3个<h2>标签想实现条件渲染,有3种方式可以实现并说明不同优缺点
方式1:<h2>标签加条件,缺点:太重复了

在这里插入图片描述

方式2:3个<h2>标签外绑定div条件控制,缺点:div也得知晓并设置样式,否则<h2>有样式而被div包裹后样式没了,即会改变结构

在这里插入图片描述

方式3(推荐):使用<template>标签,元素加载解析时会自动去掉<template>标签只保留<h2>标签,即不会改变结构,注意:<template>标签中只可以使用v-if,不能使用v-show,否则报错

在这里插入图片描述

2.10列表渲染

锚点

2.10.1 v-for

<li v-for="item in items">{{item.name}}</li>
列表渲染
    v-for
        可以把一组值进行列表渲染,语法形式: item  in items,
        items 是源数据数组并且 item 是数组元素迭代的别名。
        在 v-for 块中,对父作用域属性的完全访问权限。v-for 还支持一个可选的第二个参数为当前项的索引。
        v-for也可以遍历对象,可以指定三个形参:
            形式: v-for="(value, key, index) in object"
            value: 对象的值
            key: 对象的键
            index: 遍历的索引

在这里插入图片描述

2.10.2 key 属性(非常重要)

用 v-for 渲染列表时, 使用 key 属性给每个指定一个唯一的 ID 表示,那么可以在 下次数据渲染时,提高渲染速度。

<li v-for="item in items" :key="item.id">{{item.name}}</li>

注意:在 v-for 中:key非常重要,推荐每次都写:key 在这里插入图片描述

在这里插入图片描述

举例说明:默认存在3个标签且有值,有一个按钮效果是在最上面新增一行标签,此时会容易发生问题 点击按钮前: 在这里插入图片描述 点击按钮后: 在这里插入图片描述 结果:顺序乱了?详细原因如下图,对比相同就复用,对比不同就重新生成DOM 在这里插入图片描述

注意点1:默认不写:key时,解析DOM会自动加上数组index 注意点2::key最好绑定数据唯一标识,身份证、手机号、库表id等,这样无论在数组什么位置插入,哪怕是数组开头或者数组结尾,都不会影响结构。 注意点3:虚拟DOM存在于内存中,而用户操作的页面标签属于真实DOM

2.10.3 取值范围

v-for 也可以指定整数,用来重复多次使用模板。

<li v-for="i in 5">第 {{i}} 次</li>

2.10.4列表过滤

举例:针对

  • 内容进行过滤,根据用户输入筛选内容,要求使用computed和watch两种方式实现 在这里插入图片描述

  • 注意点1:列表过滤使用.filter(),它和自定义过滤器不是同一个东西。 列表过滤器.filter()用在方法中,而自定义过滤器用在插值表达式和 v-bind 表达式中。 注意点2:.filter会生成新数据,不会修改原数据结构。 注意点3:如果computed和watch两种方式都可以实现,推荐优先使用计算属性computed

    方式一:computed

    <div id="root">
    			<h2>人员列表</h2>
    			<input type="text" placeholder="请输入名字" v-model="keyWord">
    			<ul>
    				<li v-for="(p,index) of filPerons" :key="index">
    					{{p.name}}-{{p.age}}-{{p.sex}}
    				</li>
    			</ul>
    		</div>
    
    new Vue({
    				el:'#root',
    				data:{
    					keyWord:'',
    					persons:[
    						{id:'001',name:'马冬梅',age:19,sex:'女'},
    						{id:'002',name:'周冬雨',age:20,sex:'女'},
    						{id:'003',name:'周杰伦',age:21,sex:'男'},
    						{id:'004',name:'温兆伦',age:22,sex:'男'}
    					]
    				},
    				computed:{
    					filPerons(){
    						return this.persons.filter((p)=>{
    							return p.name.indexOf(this.keyWord) !== -1
    						})
    					}
    				}
    			}) 
    

    方式二:watch

    new Vue({
    				el:'#root',
    				data:{
    					keyWord:'',
    					persons:[
    						{id:'001',name:'马冬梅',age:19,sex:'女'},
    						{id:'002',name:'周冬雨',age:20,sex:'女'},
    						{id:'003',name:'周杰伦',age:21,sex:'男'},
    						{id:'004',name:'温兆伦',age:22,sex:'男'}
    					],
    					filPerons:[]
    				},
    				watch:{
    					keyWord:{
    						immediate:true,
    						handler(val){
    							 this.filPerons = this.persons.filter((p)=>{
    								return p.name.indexOf(val) !== -1
    							})
    						}
    					}
    				}
    			})
    

    2.10.5列表排序

    注意点1:列表排序使用.sort()方法,其中sort(参数1,参数2)有两个参数,参数2-参数1为降序,参数1-参数2为升序,具体查看。 blog.csdn.net/weixin_4666… 注意点2:会引起自身数据的改变。 举例:对列表过滤的数据进行升降序 注意点3:.sort()方法会更改原数据结构,比如更改原数组顺序 在这里插入图片描述

    <div id="root">
    			<h2>人员列表</h2>
    			<input type="text" placeholder="请输入名字" v-model="keyWord">
    			<button @click="sortType = 2">年龄升序</button>
    			<button @click="sortType = 1">年龄降序</button>
    			<button @click="sortType = 0">原顺序</button>
    			<ul>
    				<li v-for="(p,index) of filPerons" :key="p.id">
    					{{p.name}}-{{p.age}}-{{p.sex}}
    					<input type="text">
    				</li>
    			</ul>
    		</div>
    
    new Vue({
    				el:'#root',
    				data:{
    					keyWord:'',
    					sortType:0, //0原顺序 1降序 2升序
    					persons:[
    						{id:'001',name:'马冬梅',age:30,sex:'女'},
    						{id:'002',name:'周冬雨',age:31,sex:'女'},
    						{id:'003',name:'周杰伦',age:18,sex:'男'},
    						{id:'004',name:'温兆伦',age:19,sex:'男'}
    					]
    				},
    				computed:{
    					filPerons(){
    						const arr = this.persons.filter((p)=>{
    							return p.name.indexOf(this.keyWord) !== -1
    						})
    						//判断一下是否需要排序
    						if(this.sortType){
    							arr.sort((p1,p2)=>{
    								return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age
    							})
    						}
    						return arr
    					}
    				}
    			}) 
    

    2.10.6 Vue监测“对象”数据改变的原理

    注意点1:vue监测数据原理就是,给每个data属性添加get()和 set()方法,即data属性改变 =》调用set() =》 重新渲染页面值改变。

    注意点2:加载流程: 1)加载data属性,加工封装get()和 set()方法 2)vue._data=data,这样控制台点开vue实例就会看到一堆属性及方法了

    举例:模拟data对象属性值改变了,页面值也跟着改变,即实现vue监测数据改变效果

    let data = {
    				name:'尚硅谷',
    				address:'北京',
    			}
    
    			//第1步:创建一个监视的实例对象,用于监视data中属性的变化
    			const obs = new Observer(data)		
    			console.log(obs)	
    
    			//第2步:准备一个vm实例对象
    			let vm = {}
    			vm._data = data = obs
                
    //第3步:定义观察者函数
    			function Observer(obj){
    				//汇总对象中所有的属性形成一个数组
    				const keys = Object.keys(obj)
    				//遍历
    				keys.forEach((k)=>{
    					Object.defineProperty(this,k,{  //this指代观察者对象,而不是vue实例
    						get(){
    							return obj[k]
    						},
    						set(val){
    							console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`)
    							obj[k] = val
    						}
    					})
    				})
    			}
    

    2.10.7 Vue.set()或者vm.$set()动态新增标签

    锚点 Vue.set()和vm.$set() =》 相同效果。 主要讲解Vue.set()方法是什么? 在这里插入图片描述

    注意点 1:Vue.set()方法有局限性,不能给vue实例或者data的直接属性进行添加,只能作用于data下面的某个属性对象。

    举例:比如动态添加“校长属性”,即动态给data属性下面的school对象添加“校长属性” 在这里插入图片描述

    <body>
    		<!-- 准备好一个容器-->
    		<div id="root">
    			<h1>学校信息</h1>
    			<h2>学校名称:{{school.name}}</h2>
    			<h2>学校地址:{{school.address}}</h2>
    			<h2>校长是:{{school.leader}}</h2>
    			<hr/>
    			<h1>学生信息</h1>
    			<button @click="addSex">添加一个性别属性,默认值是男</button>
    			<h2>姓名:{{student.name}}</h2>
    			<h2 v-if="student.sex">性别:{{student.sex}}</h2>
    			<h2>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2>
    			<h2>朋友们</h2>
    			<ul>
    				<li v-for="(f,index) in student.friends" :key="index">
    					{{f.name}}--{{f.age}}
    				</li>
    			</ul>
    		</div>
    	</body>
    
    	<script type="text/javascript">
    		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
    
    		const vm = new Vue({
    			el:'#root',
    			data:{
    				school:{
    					name:'尚硅谷',
    					address:'北京',
    				},
    				student:{
    					name:'tom',
    					age:{
    						rAge:40,
    						sAge:29,
    					},
    					friends:[
    						{name:'jerry',age:35},
    						{name:'tony',age:36}
    					]
    				}
    			},
    			methods: {
    				addSex(){
    					// Vue.set(this.student,'sex','男')
    					this.$set(this.student,'sex','男')
    				}
    			}
    		})
    	</script>
    

    2.10.8Vue监测“数组”数据改变的原理

    问题1:vue实例对象针对数组元素并没有提供类似get和set方法,为啥也会实现页面响应式监听?

    在这里插入图片描述 答案:因为vue对数组的部分基本方法进行了二次封装,在继承方法的使用效果同时,也进行了响应式更新DOM。 在这里插入图片描述

    举例:爱好列表 在这里插入图片描述

    <div id="root">
        <h2>爱好</h2>
        <ul>
            <li v-for="(h,index) in student.hobby" :key="index">
                {{h}}
            </li>
        </ul>
    </div>
    
    const vm = new Vue({
            el:'#root',
            data:{
                student:{
                    hobby:['抽烟','喝酒','烫头']
                }
            }
        })
    

    2.10.9总结vue数据监测

    在这里插入图片描述

    在这里插入图片描述

    问题:什么叫数据劫持?

    定义:指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。比如:拦截data各属性,添加额外的get和set方法到vue实例的_data对象中。

    2.11 表单输入绑定

    锚点 用 v-model 指令在表单控件元素上创建双向数据绑定。它会根据控件类型自动选取 正确的方法来更新元素。 在这里插入图片描述 在这里插入图片描述

    小技巧:

    1.针对input输入框想实现点击名字,光标自动定位到输入框,label设置for属性和input设置id属性即可

    <label for="account">账号:</label>
    <input type="text" id="account" v-model.trim="userInfo.account"> <br/><br/>
    

    2.针对radio想实现二选一,需要设置相同name属性值,同时配置value值 3.针对radio或者select标签想实现“默认选中”,不用设置checked,只需要设置data中属性值即可 4.表单提交方式

    方式1:button绑定事件
    <button @click=”xx”></button>
    --------------------------------------------------------------------------------------------
    方式2:<form>标签设置<form @submit.prevent="demo">
    

    5.阻止表单提交后刷新页面操作,使用事件修饰符.prevent 6.form表单提交中如何一口气获取全部数据打印输出?

    方式1:定义对象接收全部参数
    new Vue({
            el:'#root',
            data:{
                userInfo:{
                    account:'',
                    password:'',
                    age:18,
                    sex:'female',
                    hobby:[],
                    city:'beijing',
                    other:'',
                    agree:''
                }
            }
    
    console.log(JSON.stringify(this.userInfo))
    --------------------------------------------------------------------------------------------
    方式2:使用this._data,虽说this._data除了属性还有get和set方法,但是打印的时候不会输出这些,并不影响。
    console.log(JSON.stringify(this._data))
    

    7.针对年龄标签,防止输入字母,或者防止默认是数字但重新输入后传值为字符串,需同时设置type="number"和v-model.number

    <input type="number" v-model.number="userInfo.age">
    

    8.针对checkbox注意点1,考虑是否需要配置input标签的value值,比如:如果是爱好等多选就需要设置;如果是是否同意协议等check标签就不需要设置 9.针对checkbox注意点2,data属性值默认设置字符串和数组效果时不一样的

    2.11.1 文本 text

    2.11.2 复选框

    单个使用时 v-model 是逻辑值: true 和 false,多个一起使用需要指定 value 值, 选中结果绑定 v-model 的数组。

    2.11.3 单选框

    2.11.4选择列表

    2.11.5修饰符

    在这里插入图片描述

    2.12计算属性与监听属性

    2.12.1计算属性computed

    在这里插入图片描述

    注意点1:只要data任意值发生改变,vue肯定会重新解析模板。比如{{fullName()}},插值表达式使用fullName()函数,那么当vue重新解析模板时,就会重新执行一遍fullName()函数,因为data任意值改变了,vue也不知道fullName()函数中是否引用了data中改变的属性,所以会选择直接重新执行一遍fullName()函数。 注意点2:默认提供了缓存机制,实际上计算属性就是调用了fullName的get()方法,get什么时候调用?1.初次读取fullName时 2.所依赖的数据发生变化时。 注意点3:既然fullName是个对象,那我插值表达式可以使用{{fullName.get()}}? 答案:不能这样使用,只有data配置和methods配置可以调用,计算配置不能这么调用,会报错fullName中get()方法不存在。 注意点4:methods定义的方法,在插值表达式使用要加(),{{fullName()}},如果是计算属性中监听的属性,则写成{{fullName}}

    举例:计算“全名”标签,由“姓-名”构成 在这里插入图片描述 思路: 方式1:用插值表达式拼,(缺点:万一对字符串进行分割、转换大小写、截取长度等等操作显得表达式太长,不易解读) 全名:{{firstName}}-{{lastName}}


    方式2:定义函数写在methods配置中(缺点:不提供缓存,有几个fullName()调用就会执行几次) 全名:{{fullName()}} 全名:{{fullName()}}
    方式3:定义计算属性(推荐使用,有缓存),代吗如下↓

    全名:<span>{{fullName}}</span> <br/><br/>
    
    computed:{
    				fullName:{
    					//get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
    					//get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。
    					get(){
    						console.log('get被调用了')
    						// console.log(this) //此处的this是vm
    						return this.firstName + '-' + this.lastName
    					},
    					//set什么时候调用? 当fullName被修改时。
    					set(value){
    						console.log('set',value)
    						const arr = value.split('-')
    						this.firstName = arr[0]
    						this.lastName = arr[1]
    					}
    				}
    			}
    

    2.12.2(简写)计算属性

    注意点1:计算属性只有在没有set方法时才能使用简写 注意点2:

    问题:插值表达式中使用data配置、methods配置和计算配置时,区分何时带()?何时不能带()?

    答案: 针对data配置都不带() 针对methods配置有形参带(),没有形参()可省略 针对计算配置也不带(),带()会报错

    全名:<span>{{fullName}}</span> <br/><br/>
    
    computed:{
    				//完整写法
    				 fullName:{
    					get(){
    						console.log('get被调用了')
    						return this.firstName + '-' + this.lastName
    					},
    					set(value){
    						console.log('set',value)
    						const arr = value.split('-')
    						this.firstName = arr[0]
    						this.lastName = arr[1]
    					}
    				} 
    				//简写
    				fullName(){
    					console.log('get被调用了')
    					return this.firstName + '-' + this.lastName
    				}
    			}
    

    2.12.3计算属性-天气案例

    该案例为了讲述1个坑,1个技巧

    案例说明:今天天气很【炎热/凉爽】,点击按钮进行切换 源代码如下: 在这里插入图片描述 在这里插入图片描述

    <div id="root">
    			<h2>今天天气很{{info}}</h2>
    			<!-- 绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句 -->
    			<!-- <button @click="isHot = !isHot">切换天气</button> -->
    			<button @click="changeWeather">切换天气</button>
    </div>
    
    const vm = new Vue({
    			el:'#root',
    			data:{
    				isHot:true,
    			},
    			computed:{
    				info(){
    					return this.isHot ? '炎热' : '凉爽'
    				}
    			},
    			methods: {
    				changeWeather(){
    					this.isHot = !this.isHot
    				}
    			},
    		})
    

    注意点1:(1个坑指的是),假设页面显示的是“今天天气很好”,也就是页面压根没用到info属性,而代码中却定义了计算属性及方法,那么会发生的坑是,点击按钮后Vue插件显示结果没变动,但是实际控制台info结果已经改变了,也就是说因为页面没用到属性所以插件就不更新值了,但是实际info值已经改变了。 注意点2:(1个技巧指的是),假设定义方法逻辑很简单,可以直接这样写@click="isHot = !isHot",也是生效的。 在这里插入图片描述

    在这里插入图片描述

    2.12.4监听属性watch

    配置watch关键字和handler()方法实现监听 在这里插入图片描述

    写法1:.new Vue是配置

    const vm = new Vue({
    watch:{
    				isHot:{
    					immediate:true, //初始化时让handler调用一下
    					//handler什么时候调用?当isHot发生改变时。
    					handler(newValue,oldValue){
    						console.log('isHot被修改了',newValue,oldValue)
    					}
    			}
    }
    

    写法2:首先保证vue实例已经创建完了,在vue实例下面,通过vm.$watch配置

    vm.$watch('isHot',{
    			immediate:true, //初始化时让handler调用一下
    			//handler什么时候调用?当isHot发生改变时。
    			handler(newValue,oldValue){
    				console.log('isHot被修改了',newValue,oldValue)
    			}
    		})
    
    注意点1:什么场景下使用写法1 和写法2呢?
    答案:创建vue实例时已经明确知道监听什么属性时就使用写法1,而如果创建vue实例时不知道监视谁,而是后续根据用户业务行为才知道要监视谁就使用写法2
    
    注意点2:vm.$watch(参数1,参数2)有两个参数,参数2为{}配置对象,参数1表示你要监视的属性名,注意名字要加引号“引号”标识,如果直接写vm.$watch(isHot,参数2)这是错误的,会报如下错误,原因在于写法1中的isHot不会触发“读取变量”,而写法2会触发“读取变量”,所以写法2中属性名必须加“引号”。
    

    在这里插入图片描述

    2.12.5监听属性watch-深度监听

    在这里插入图片描述

    举例:data配置numbers对象,其中有属性a和b,我想监听numbers,只要numbers中任意属性值改变了,我就打印一条语句。

    <h3>a的值是:{{numbers.a}}</h3>
    <button @click="numbers.a++">点我让a+1</button>
    <h3>b的值是:{{numbers.b}}</h3>
    <button @click="numbers.b++">点我让b+1</button>
    
    const vm = new Vue({
    			el:'#root',
    			data:{
    				numbers:{
    					a:1,
    					b:1
    				}
    			},
    			watch:{
    				numbers:{
    						deep:true, //开启深度监视,监视多级结构中所有属性的变化
    						handler(){
    						  console.log('numbers改变了')
    					   }
    				}
    			}
    })
    

    注意点1:默认deep为false,不进行深度监视,这样效率快 注意点2:如果如图1这么写,不配置deep=true的话,那么watch配置会让vue实例监视的是整个numbers的地址,而不是咱们想实现的监视numbers对象内部任意属性值a或者b的变化,因为如图2,watch配置会让vue实例默认监视的是“粉色框”的地址值是否改变,而不是监视“绿色框”中属性a和b的值是否改变,哪怕属性a或者b值改变了,但是numbers对象整体地址没改变,那么被watch配置的vue实例就会觉得监视值未改变,所以如果想实现深度结构监控,请设置deep=true 记住:配置watch是为了作用于vue实例怎么怎么怎么地。 在这里插入图片描述 在这里插入图片描述

    2.12.6监听属性watch-(简写)深度监听

    watch和 vm.$watch都有2种写法:

    问题:什么情况下使用简写方式?

    答案:当监听属性中只用到handler()方法,而没有其他配置(比如immediate或者deep)时,就可以使用简写方式。

    watch:{
    	//正常写法
    	isHot:{
    		// immediate:true, //初始化时让handler调用一下
    		// deep:true,//深度监视
    		handler(newValue,oldValue){
    			console.log('isHot被修改了',newValue,oldValue)
    		}
    	}, 
    	//简写
    	isHot(newValue,oldValue){
    		console.log('isHot被修改了',newValue,oldValue,this)
    	} 
    }
    -------------------------------------------------------------------------------------------------
    //正常写法
    vm.$watch('isHot',{
    	immediate:true, //初始化时让handler调用一下
    	deep:true,//深度监视
    	handler(newValue,oldValue){
    		console.log('isHot被修改了',newValue,oldValue)
    	}
    }) 
    -------------------------------------------------------------------------------------------------
    //简写
    vm.$watch('isHot',function(newValue,oldValue){
    	console.log('isHot被修改了',newValue,oldValue,this)
    }) 
    

    2.12.7计算属性和监视属性区别

    举例说明:天气案例

    计算属性

    data:{
    	firstName:'张',
    		lastName:'三'
    	},
    	computed:{
    		info(){
    			return this.firstName + “-” + this.lastName
    		}
    	},
    

    监视属性

    data:{
    	firstName:'张',
    			lastName:'三',
    			fullName:'张-三'
    		},
    		watch:{
    			firstName(val){
    					this.fullName = val + '-' + this.lastName
    			},
    			lastName(val){
    				this.fullName = this.firstName + '-' + val
    			}
    		}
    

    区别:

    注意点1:如果计算属性computed和监视属性watch都可以实现,推荐使用计算属性,因为代码最少 注意点2:计算属性computed不能使用异步操作,比如睡一秒再执行操作等,而监视属性watch可以进行异步操作 注意点3:为了使用this指代vue对象,那么使用计算属性computed的同步操作,必须写普通函数(因为计算属性computed被vue管理),如果使用监视属性watch的异步操作等(不被vue所管理的函数)那么请使用箭头函数, (即: 计算属性使用同步操作的普通函数,this => vue, 计算属性使用同步操作的箭头函数,this => window

    监视属性使用同步操作的普通函数,this => vue, 监视属性使用同步操作的箭头函数,this => window, 监视属性使用异步操作的普通函数,this => window, 监视属性使用异步操作的箭头函数,this => vue,具体原因看下面图 )。 在这里插入图片描述

    2.13绑定样式

    2.13.1绑定class

    在这里插入图片描述

    data:{
    	name:'尚硅谷',
    		mood:'normal',
    		classArr:['atguigu1','atguigu2','atguigu3'],
    		classObj:{
    			atguigu1:false,
    			atguigu2:false,
    		}
    }
    

    2.13.2绑定style

    在这里插入图片描述

    data:{
    	styleObj:{
    			fontSize: '40px',
    				color:'red',
    			},
    	styleObj2:{
    				backgroundColor:'orange'
    			},
    	styleArr:[
    				{
    					fontSize: '40px',
    					color:'blue',
    				},
    				{
    					backgroundColor:'gray'
    				}
    			]
    }
    

    2.14自定义过滤器

    在这里插入图片描述

    Vue允许自定义过滤器,可被用作一些常见的文本格式化。 过滤器可以用在两个地方:mustache 插值表达式和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。 注意点1:过滤器无形参时,默认也会把前面参数传给过滤器,举例:{{time | timeFormater}},默认会把time当作第一个参数传给timeFormater 注意点2:过滤器有形参时,前面的参数还是当作第一个参数,而形参默认当作第二个参数,举例:{{time | timeFormater('YYYY_MM_DD')}},time 为形参1,'YYYY_MM_DD'为形参2 注意点3:过滤器可串行使用:{{ message | filterA | filterB }} 注意点4:过滤器并没有改变原本数据 注意点5:“ES6默认参数”机制(可用在方法中) =》举例:timeFormater方法的第二个参数str,如果有传值就用传递过来的值,如果str为空,就用默认的值YYYY年MM月DD日 HH:mm:ss

    filters:{
    	timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){
    			return dayjs(value).format(str)
    				}
    			}
    

    注意点6:过滤器应该被添加在 JavaScript 表达式的尾部,由“管道符”指示:

    <!-- in mustaches -->
    {{ message | capitalize }}
    <!-- in v-bind -->
    <div v-bind:id="name| upperCase ">hei</div>
    

    注意点7:使用Vue.filter定义全局过滤器,使用filters在组件内指定局部过滤器。

    <div id="root">
        <h4>{{name | upperCase | length | test('A-','-B')}}</h4>
    </div>
    <script>
        /***
         * filter:过滤器
         * */
        var vm = new Vue({
            el: '#root',
            data: {
                name: 'hello'
            },
            filters: {
                upperCase: function (value) {
                    return value.toUpperCase();
                },
                length: function (value) {
                    return value+value.length;
                },
                //在value两边加点东西
                test: function (value, begin, end) {
                    console.log(value, begin, end);
                    return begin+value+end;
                }
            }
        });
    </script>
    

    在这里插入图片描述

    2.15内置指令

    就是介绍下vue自带的但是我们不常用到的指令

    2.15.1 v-cloak指令

    在这里插入图片描述

    举例说明:比如网页上方有个{{name}}属性,在<div>标签下面</body>的上面写个js引入文件,如果js引入出现网络延迟,那么出现的现象是页面只显示{{name}},而没有走到实例化vue去解析页面内容,这样用户体验极差,正常效果是要么不显示name,要么解析好了直接显示name属性值。
    
    解决方案:标签设置 v-cloak特殊属性,并配合css样式display:none进行隐藏内容,默认网页元素加载时隐藏{{name}}属性所在的标签,当实例化vue完毕后会自动删除v-cloak特殊属性,从而达到显示name属性值的作用。
    <head>
    		<meta charset="UTF-8" />
    		<title>v-cloak指令</title>
    		<style>
                //[v-cloak]代表选中所有带v-cloak的标签
    			[v-cloak]{
    				display:none;
    			}
    		</style>
    		<!-- 引入Vue -->
    	</head>
    	<body>		
    		<!-- 准备好一个容器-->
    		<div id="root">
    			<h2 v-cloak>{{name}}</h2>
    		</div>
    		<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
    	</body>
    	
    	<script type="text/javascript">
    		console.log(1)
    		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
    		
    		new Vue({
    			el:'#root',
    			data:{
    				name:'尚硅谷'
    			}
    		})
    	</script>
    

    2.15.2 v-pre指令

    在这里插入图片描述

    注意点1:加了v-pre后,vue解析到红色框时,就会跳过该行了,不会去判断当前行是否有指令和插值表达式之类的。

    2.16.自定义指令

    除了默认设置的核心指令 (v-model 和 v-show),Vue 也允许注册自定义指令。 在这里插入图片描述

    2.16.1基本使用

    1)定义

    注意点1:定义全局指令使用Vue.directive,定义局部指令使用directives。 注意点2:自定义指令时不需要加v-,而标签使用时需要加v-自定义指令名 注意点3:只定义指令可以用在很多地方,包括:标签属性、标签体内容、绑定事件..... 注意点4:

    问题:自定义函数何时被调用?

    答案: 1)指令与元素成功绑定时(一上来) 2)指令所在的模板被重新解析时,比如下方图片,无论data中属性name或者属性n只要其中任意一个发生值改变,上面的红框div模板就会进行重新解析 在这里插入图片描述

    注意点5:自定义指令有两个形参,形参1代表:自定义指令所在的标签,如图1,形参2:代表绑定的所有信息封装一个成对象给你,如图2,既然形参1能获取自定义指令所在的标签,那么指令用在标签属性、标签体内容、绑定事件上,就都可以实现了。 在这里插入图片描述 图1:形参1 在这里插入图片描述

    图2:形参2

    //自定义全局指令v-focus
    Vue.directive('focus',{
        //当绑定元素插入到DOM调用
        inserted: function (el) {
            //元素获取焦点
            el.focus();
        }
    });
    

    使用directive定义,第一个参数为指令名,使用时加上v-前缀才能生效。inserted属性指当绑定元素插入到DOM时调用。 定义局部指令使用directives:

    //定义局部指令使用directives
    var app = new Vue({
        el:'#app',
        directives:{
            focus:{
                inserted: function (el) {
                    //元素获取焦点
                    el.focus();
                }
            }
        }
    });
    

    2)使用

    <div id="app">
        <input type="text" v-focus>
    </div>
    

    2.16.2钩子函数

    指令定义函数提供了几个钩子函数 (可选):

    • bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
    • inserted:被绑定元素插入父节点时调用 (父节点存在即可调用,不必存在于 document 中)。
    • update:所在组件的 VNode 更新时调用,但是可能发生在其孩子的 VNode 更新之前。指令的值可能发生了改变也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。
    • componentUpdated:所在组件的 VNode 及其孩子的 VNode 全部更新时调用。
    • unbind:只调用一次,指令与元素解绑时调用。

    钩子函数的参数有4个: 1)el:当前指令绑定元素 2)binding:当前指令绑定的所有信息对象,有以下属性:

    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1", value 的值是 2。
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:绑定值的字符串形式。例如 v-my-directive="1 + 1" ,expression 的值是 "1 + 1"。
    • arg:传给指令的参数。例如 v-my-directive:foo,arg 的值是 "foo"。
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }。

    3)vnode:Vue 编译生成的虚拟节点 4)oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

    <div id="app">
        <input type="text" v-demo:arg.a.b="1+1">
    </div>
    <script>
        Vue.directive('demo',{
            bind: function (el,binding) {
                console.log(el);
                console.log(binding);
            }
        });
        var app = new Vue({
            el:'#app'
        });
    </script>
    

    在这里插入图片描述

    2.16.3实例图片懒加载

    谷歌图片的加载做得非常优雅,在图片未完成加载前,用随机的背景色占位,图片加载完成后才直接渲染出来,用自定义指令可以非常方便的实现这个功能。

    1)样式

    <style>
        .item, .item img{
            width: 200px;
            height: 120px;
            float: left;
        }
    </style>
    

    2)自定义v-img指令

    //定义全局自定义指令v-img
    Vue.directive('img',{
        bind: function (el,binding) {
            //生成随机颜色
            var color = parseInt(Math.random()*0xFFFFFF).toString(16);
            //设置当前元素的背景,提前进行占位等待图片加载
            el.style.background = '#'+color;
            //setTimeout模拟图片加载的延时情况
            setTimeout(function () {
                //创建图片对象
                var img = new Image();
                //通过binding对象获取真实的图片url
                img.src = binding.value;
                //将图片元素插入DOM结构
                el.appendChild(img);
                //随机延时
            },Math.random()*3000+500);
        }
    });
    

    3)模拟数据

    var app = new Vue({
        el:'#app',
        data:{
            //定义模拟数据
            imgs:[
                {url:'img/01.jpg'},
                {url:'img/02.jpg'},
                {url:'img/03.jpg'},
                {url:'img/04.jpg'}
            ]
        }
    });
    

    4)使用

    <div id="app">
        <div v-img="item.url" v-for="item in imgs" class="item"></div>
    </div>
    

    5)效果

    在这里插入图片描述

    本人其他相关文章链接

    1.《基础篇第1章:vue2简介》包含Vue2知识点、个人总结的使用注意点及碰到的问题总结

    2.《基础篇第2章:vue2基础》包含Vue2知识点、个人总结的使用注意点及碰到的问题总结

    3.《进阶篇第3章:vue进阶-组件》包含组件、自定义事件、插槽、路由等等扩展知识点

    4.《基础篇第4章》:使用vue脚手架创建项目

    5.vue2知识点:数据代理

    6.vue2知识点:事件处理

    7.vue2知识点:列表渲染(包含:v-for、key、取值范围、列表过滤、列表排序、vue监视对象或数组的数据改变原理、总结vue数据监测)

    8.vue2知识点:计算属性与监听属性

    9.vue2知识点:生命周期(包含:生命周期介绍、生命周期钩子、整体流程图详解)

    10.vue2知识点:非单文件组件和单文件组件

    11.vue2知识点:组件is属性

    12.vue2知识点:组件模板定义

    13.vue2知识点:组件的props属性、非props属性、props属性校验

    14.vue2知识点:组件自定义事件

    15.vue2知识点:组件插槽分发

    16.vue2知识点:动态组件

    17.vue2知识点:混入

    18.vue2知识点:浏览器本地缓存

    19.vue2知识点:全局事件总线(GlobalEventBus)

    20.vue2知识点:消息订阅与发布

    21.vue2知识点:nextTick语法

    22.vue2知识点:Vue封装的过度与动画

    23.vue2知识点:路由

    24.vue2知识点:vm调用待$命令介绍

    25.vue组件通信案例练习(包含:父子组件通信及平行组件通信)

    26.vue表单案例练习:vue表单创建一行数据及删除数据的实现与理解

    27.vue2基础组件通信案例练习:待办事项Todo-list案例练习

    28.vue2基础组件通信案例练习:把案例Todo-list改写成本地缓存

    29.vue2基础组件通信案例练习:把案例Todo-list改成使用自定义事件

    30.vue2基础组件通信案例练习:把案例Todo-list改成使用全局事件总线

    31.vue2基础组件通信案例练习:把案例Todo-list改成使用消息订阅与发布

    32.vue2基础组件通信案例练习:把案例Todo-list新增编辑按钮

    33.vue2基础组件通信案例练习:把案例Todo-list改成使用动画与过度

    34.学习vue2遇到过的问题及个人总结