Vue学习笔记
本次Vue学习基于尚硅谷天禹老师的视频课程。
官方文档:https://cn.vuejs.org/v2/guide/
1、初识Vue
Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
Vue的特点:
1、采用组件化模式,提高代码的复用率,让代码更好维护;
2、声明式编码,让编码人员无需直接操作DOM,提高开发效率;
3、使用虚拟DOM和优秀的Diff算法,尽量复用DOM节点;
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>初识Vuetitle> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 <link rel="shortcut icon" type="image/x-icon" href="favicon.ico"/> 9 head> 10 <body> 11 35 36 37 <div id="app"> 38 <h1>Hello,{{name.toUpperCase()}},{{address}}h1> 39 div> 40 41 <script type="text/javascript" > 42 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 43 44 //创建Vue实例 45 //new不可少,Vue本身是个构造函数,不能直接调用,必须通过实例来调用 46 new Vue({ //通过构造函数创建实例 47 el:'#app', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。 48 data:{ //data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象。 49 name:'waylon', 50 address:'上海' 51 } 52 }) 53 54 script> 55 body> 56 html>
2、模板语法
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>模板语法title> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 10 22 23 <div id="root"> 24 <h1>插值语法h1> 25 <h3>你好,{{name}}h3> 26 <hr/> 27 <h1>指令语法h1> 28 29 <a v-bind:href="school.url.toUpperCase()" x="hello">点我去{{school.name}}学习1a> 30 <a :href="school.url" :x="hello">点我去{{school.name}}学习2a> 31 div> 32 body> 33 34 <script type="text/javascript"> 35 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 36 37 new Vue({ 38 el:'#root', 39 data:{ 40 hello:'嘻嘻', 41 name:'jack', 42 school:{ 43 name:'尚硅谷', //虽然同名,但不同级 44 url:'http://www.atguigu.com', 45 } 46 } 47 }) 48 script> 49 html>
3、数据绑定
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>数据绑定title> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 10 18 19 <div id="root"> 20 21 25 26 27 单向数据绑定:<input type="text" :value="name"><br/> 28 双向数据绑定:<input type="text" v-model="name"><br/> 29 30 31 32 div> 33 body> 34 35 <script type="text/javascript"> 36 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 37 38 new Vue({ 39 el:'#root', 40 data:{ 41 name:'尚硅谷' 42 } 43 }) 44 script> 45 html>
4、el与data的两种写法
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>el与data的两种写法title> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 10 24 25 <div id="root"> 26 <h1>你好,{{name}}h1> 27 div> 28 body> 29 30 <script type="text/javascript"> 31 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 32 33 //el的两种写法 34 /* 35 const vm = new Vue({ 36 // el:'#root', //第一种写法 37 data:{ 38 name:'尚硅谷' 39 } 40 }) 41 console.log(vm) 42 vm.$mount('#root') //第二种写法,mount意为:挂载 43 */ 44 45 // data的两种写法 46 const vm = new Vue({ 47 el:'#root', 48 //data的第一种写法:对象式 49 /* data:{ 50 name:'尚硅谷' 51 } */ 52 53 //data的第二种写法:函数式 54 data(){ //即data:function(){}的简写 55 console.log('@@@',this) //此处的this是Vue实例对象 56 return{ 57 name:'尚硅谷' 58 } 59 } 60 }) 61 script> 62 html>
5、Vue中的MVVM模型
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>理解MVVMtitle> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 10 19 <div id="root"> 20 <h1>学校名称:{{name}}h1> 21 <h1>学校地址:{{address}}h1> 22 26 div> 27 body> 28 29 <script type="text/javascript"> 30 31 const vm = new Vue({ 32 el:'#root', 33 data:{ 34 name:'尚硅谷', 35 address:'北京', 36 } 37 }) 38 console.log(vm) 39 script> 40 html>
Vue的实例对象vm:
MVVM在vue中的体现:
6、数据代理
1、关于Object.defineproperty方法
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>关于Object.defineproperty方法title> 6 head> 7 <body> 8 <script type="text/javascript"> 9 let number = 18 10 let person = { 11 name:'张三', 12 sex:'男', 13 } 14 15 Object.defineProperty(person,'age',{ 16 // value:18, 17 // enumerable:true, //控制属性是否可以枚举,默认值是false 18 // writable:true, //控制属性是否可以被修改,默认值是false 19 // configurable:true //控制属性是否可以被删除,默认值是false 20 21 //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值 22 get(){ 23 console.log('有人读取age属性了') 24 return number 25 }, 26 27 //当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值 28 set(value){ 29 console.log('有人修改了age属性,且值是',value) 30 number = value 31 } 32 //getter和setter将person和number两者产生了关联 33 }) 34 // console.log(Object.keys(person)) //将对象的属性名提取出来组成一个数组 35 36 console.log(person) 37 52 script> 53 body> 54 html>
2、何为数据代理?
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>何为数据代理title> 6 head> 7 <body> 8 9 10 <script type="text/javascript" > 11 let obj = {x:100} 12 let obj2 = {y:200} 13 14 Object.defineProperty(obj2,'x',{ 15 get(){ 16 return obj.x 17 }, 18 set(value){ 19 obj.x = value 20 } 21 }) 22 script> 23 body> 24 html>
3、在Vue中的数据代理如何实现?
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Vue中的数据代理title> 6 <script type="text/javascript" src="js/vue.js">script> 7 head> 8 <body> 9 <div id="app"> 10 <h1>个人姓名:{{name}}h1> 11 <h1>个人年龄:{{age}}h1> 12 div> 13 <script type="text/javascript"> 14 const vm = new Vue({ 15 el:"#app", 16 data:{ 17 name:'天青色', 18 age:18 19 } 20 }) 21 script> 22 body> 23 html>
在上面这段代码中,data属性是个对象,可以通过实例对象vm去操作data中的键值对,如下:
这不就是数据代理吗?通过一个对象实现对另一个对象中属性的(读/写)操作 。这点可以从vm实例对象下面的name属性看出来,将鼠标光标移到上面显示 Invoke property getter,表示读取属性值时要通过getter实现。
name和age都是有自己的getter和setter:
其实在vm实例对象下面有个_data属性,里面保存了data里的键值对,可以通过vm._data来进行读写操作:
vm下面的name和age就是通过数据代理与_data中的name和age建立了联系,如果没有这层联系,那模板中{{name}}就不能直接拿到_data中的name了,而应该是{{ _data.name }}才可以。而_data数据又来自于data,所以vm就很方便的代理操作了data。
尚硅谷老师的图也讲得很清楚:
补充一点,_data里还实现了数据劫持。当我们通过vm.name更改值时,页面上用到name的地方也会自动响应式更新了。这就是_data里数据劫持的作用。
总结:
1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
7、事件处理
8、计算属性
先实现一个小案例:
输入框默认有张和三,手动输入后全名也会跟着改变。
1、通过插值语法实现案例
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>姓名案例_插值语法实现title> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 10 11 <div id="root"> 12 姓:<input type="text" v-model="firstName"> <br/><br/> 13 名:<input type="text" v-model="lastName"> <br/><br/> 14 全名:<span>{{firstName}}-{{lastName}}span> 15 div> 16 body> 17 18 <script type="text/javascript"> 19 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 20 21 new Vue({ 22 el:'#root', 23 data:{ 24 firstName:'张', 25 lastName:'三' 26 } 27 }) 28 script> 29 html>
2、通过methods实现案例
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>姓名案例_methods实现title> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 10 11 <div id="root"> 12 姓:<input type="text" v-model="firstName"> <br/><br/> 13 名:<input type="text" v-model="lastName"> <br/><br/> 14 全名:<span>{{fullName()}}span> 15 div> 16 body> 17 18 <script type="text/javascript"> 19 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 20 21 new Vue({ 22 el:'#root', 23 data:{ 24 firstName:'张', 25 lastName:'三' 26 }, 27 methods: { 28 fullName(){ 29 console.log('@---fullName') 30 return this.firstName + '-' + this.lastName 31 } 32 }, 33 }) 34 script> 35 html>
注意14行的{{fullName()}}是要加()的。
另外,由于是双向数据绑定,每次输入都会导致data对象里面的属性值发生改变,从而会使得模板代码被Vue重新渲染,fullName方法也会被重新调用。如下:
3、通过计算属性实现案例
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>姓名案例_计算属性实现title> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 21 22 <div id="root"> 23 姓:<input type="text" v-model="firstName"> <br/><br/> 24 名:<input type="text" v-model="lastName"> <br/><br/> 25 测试:<input type="text" v-model="x"> <br/><br/> 26 全名:<span>{{fullName}}span> <br/><br/> 27 30 div> 31 body> 32 33 <script type="text/javascript"> 34 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 35 36 const vm = new Vue({ 37 el:'#root', 38 data:{ 39 firstName:'张', 40 lastName:'三', 41 x:'你好' 42 }, 43 methods: { 44 demo(){ 45 46 } 47 }, 48 computed:{ 49 fullName:{ 50 //get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值 51 //get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。 52 get(){ 53 console.log('get被调用了') 54 // console.log(this) //经过Vue的处理后此处的this是vm 55 return this.firstName + '-' + this.lastName 56 }, 57 //set什么时候调用? 当fullName被修改时。 58 set(value){ 59 console.log('set',value) 60 const arr = value.split('-') 61 this.firstName = arr[0] 62 this.lastName = arr[1] 63 } 64 } 65 } 66 }) 67 script> 68 html>
总结:
1.定义:要用的属性不存在,要通过已有属性(如data对象里的属性)计算得来。它也是vm下面的一个属性。
2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
5.计算属性最终会出现在vm上,直接插值法读取使用即可。
6.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
4、计算属性的简写
当只考虑计算属性的读取不考虑修改的时候,就可以简写:
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>姓名案例_计算属性实现title> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 10 11 <div id="root"> 12 姓:<input type="text" v-model="firstName"> <br/><br/> 13 名:<input type="text" v-model="lastName"> <br/><br/> 14 全名:<span>{{fullName}}span> <br/><br/> 15 div> 16 body> 17 18 <script type="text/javascript"> 19 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 20 21 const vm = new Vue({ 22 el:'#root', 23 data:{ 24 firstName:'张', 25 lastName:'三', 26 }, 27 computed:{ 28 //完整写法 29 /* fullName:{ 30 get(){ 31 console.log('get被调用了') 32 return this.firstName + '-' + this.lastName 33 }, 34 set(value){ 35 console.log('set',value) 36 const arr = value.split('-') 37 this.firstName = arr[0] 38 this.lastName = arr[1] 39 } 40 } */ 41 //简写 42 fullName(){ 43 console.log('get被调用了') 44 return this.firstName + '-' + this.lastName 45 } 46 } 47 }) 48 script> 49 html>
读取的时候依然是 {{计算属性}} ,不可加() 。
9、监视属性
实现天气小案例:
默认炎热,点击后切换为凉爽,再点击切换为炎热。
1、实现案例
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>title> 6 <script type="text/javascript" src="js/vue.js">script> 7 head> 8 <body> 9 <div id="app"> 10 <h1>今天的天气很{{weather}}h1> 11 <button type="button" @click="changeWeather">点我切换天气button> 12 13 14 15 div> 16 <script type="text/javascript"> 17 const vm = new Vue({ 18 el:'#app', 19 data:{ 20 ishot:true, 21 // window 点击事件里的window在vm里找不到,所以要在这添加window,不然会报错 22 }, 23 methods:{ 24 changeWeather(){ 25 this.ishot = !this.ishot 26 } 27 }, 28 computed:{ 29 weather(){ 30 return this.ishot ? '炎热' : '凉爽' 31 } 32 } 33 }) 34 script> 35 body> 36 html>
2、监视属性watch
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>天气案例_监视属性title> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 10 11 <div id="root"> 12 <h2>今天天气很{{info}}h2> 13 <button @click="changeWeather">切换天气button> 14 div> 15 body> 16 17 <script type="text/javascript"> 18 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 19 20 const vm = new Vue({ 21 el:'#root', 22 data:{ 23 isHot:true, 24 }, 25 computed:{ 26 info(){ 27 return this.isHot ? '炎热' : '凉爽' 28 } 29 }, 30 methods: { 31 changeWeather(){ 32 this.isHot = !this.isHot 33 } 34 }, 35 /* watch:{ 36 isHot:{ 37 immediate:true, //初始化时让handler调用一下 38 //handler什么时候调用?当isHot发生改变时。 39 handler(newValue,oldValue){ 40 console.log('isHot被修改了',newValue,oldValue) 41 } 42 } 43 } */ 44 }) 45 46 vm.$watch('isHot',{ 47 immediate:true, //初始化时让handler调用一下 48 //handler什么时候调用?当isHot发生改变时。 49 handler(newValue,oldValue){ 50 console.log('isHot被修改了',newValue,oldValue) 51 } 52 }) 53 script> 54 html>
总结:
监视属性watch:
1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作
2.监视的属性必须存在,才能进行监视!可以使data里的属性,也可以是计算属性
3.监视的两种写法:
(1).new Vue时传入watch配置(初期明确知道需要监视)
(2).通过vm.$watch监视(后期需求添加的监视)
3、深度监视deep
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>天气案例_深度监视title> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 10 11 <div id="root"> 12 <h2>今天天气很{{info}}h2> 13 <button @click="changeWeather">切换天气button> 14 <hr/> 15 <h3>a的值是:{{numbers.a}}h3> 16 <button @click="numbers.a++">点我让a+1button> 17 <h3>b的值是:{{numbers.b}}h3> 18 <button @click="numbers.b++">点我让b+1button> 19 <button @click="numbers = {a:666,b:888}">彻底替换掉numbersbutton> 20 21 div> 22 body> 23 24 <script type="text/javascript"> 25 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 26 27 const vm = new Vue({ 28 el:'#root', 29 data:{ 30 isHot:true, 31 numbers:{ 32 a:1, 33 b:1, 34 c:{ 35 d:{ 36 e:100 37 } 38 } 39 } 40 }, 41 computed:{ 42 info(){ 43 return this.isHot ? '炎热' : '凉爽' 44 } 45 }, 46 methods: { 47 changeWeather(){ 48 this.isHot = !this.isHot 49 } 50 }, 51 watch:{ 52 isHot:{ 53 // immediate:true, //初始化时让handler调用一下 54 //handler什么时候调用?当isHot发生改变时。 55 handler(newValue,oldValue){ 56 console.log('isHot被修改了',newValue,oldValue) 57 } 58 }, 59 //监视多级结构中某个属性的变化,注意要用引号,之前isHot是简写 60 /* 'numbers.a':{ 61 handler(){ 62 console.log('a被改变了') 63 } 64 } */ 65 //监视多级结构中所有属性的变化 66 numbers:{ 67 deep:true, //默认为false,开启深度监视 68 handler(){ 69 console.log('numbers改变了') 70 } 71 } 72 } 73 }) 74 75 script> 76 html>
总结:
深度监视:
(1).Vue中的watch默认不监测对象内部值的改变(一层)。
(2).配置deep:true可以监测对象内部值改变(多层)。
(3).Vue自身可以监测对象内部值的改变,无论有多少层,如numbers.c.d.e,但Vue提供的watch默认不可以!
(4).考虑到效率性,使用watch时根据数据的具体结构,决定是否采用深度监视。
4、监视属性简写
当监视某个属性而不需要配置immediate和deep时,可以使用简写形式,类似于计算属性的简写:
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>天气案例_监视属性_简写title> 6 7 <script type="text/javascript" src="js/vue.js">script> 8 head> 9 <body> 10 11 <div id="root"> 12 <h2>今天天气很{{info}}h2> 13 <button @click="changeWeather">切换天气button> 14 div> 15 body> 16 17 <script type="text/javascript"> 18 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 19 20 const vm = new Vue({ 21 el:'#root', 22 data:{ 23 isHot:true, 24 }, 25 computed:{ 26 info(){ 27 return this.isHot ? '炎热' : '凉爽' 28 } 29 }, 30 methods: { 31 changeWeather(){ 32 this.isHot = !this.isHot 33 } 34 }, 35 watch:{ 36 //正常写法 37 /* isHot:{ 38 // immediate:true, //初始化时让handler调用一下 39 // deep:true,//深度监视 40 handler(newValue,oldValue){ 41 console.log('isHot被修改了',newValue,oldValue) 42 } 43 }, */ 44 //简写 45 /* isHot(newValue,oldValue){ 46 console.log('isHot被修改了',newValue,oldValue,this) 47 } */ 48 } 49 }) 50 51 //正常写法 52 /* vm.$watch('isHot',{ 53 immediate:true, //初始化时让handler调用一下 54 deep:true,//深度监视 55 handler(newValue,oldValue){ 56 console.log('isHot被修改了',newValue,oldValue) 57 } 58 }) */ 59 60 //简写 61 /* vm.$watch('isHot',(newValue,oldValue)=>{ 62 console.log('isHot被修改了',newValue,oldValue,this) 63 }) */ 64 65 script> 66 html>
5、watch实现天气案例
1 DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 <title>姓名案例_watch实现title> 6 7 <script type="text/javascript" src="../js/vue.js">script> 8 head> 9 <body> 10 11 <div id="root"> 12 姓:<input type="text" v-model="firstName"> <br/><br/> 13 名:<input type="text" v-model="lastName"> <br/><br/> 14 全名:<span>{{fullName}}span> <br/><br/> 15 div> 16 body> 17 18 <script type="text/javascript"> 19 Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 20 21 const vm = new Vue({ 22 el:'#root', 23 data:{ 24 firstName:'张', 25 lastName:'三', 26 fullName:'张-三' 27 }, 28 watch:{ 29 firstName(val){ 30 setTimeout(()=>{ //此处如果写普通函数则this为window 31 console.log(this) 32 this.fullName = val + '-' + this.lastName 33 },1000); 34 }, 35 lastName(val){ 36 this.fullName = this.firstName + '-' + val 37 } 38 } 39 }) 40 script> 41 html>
总结:
computed和watch之间的区别:
1.computed能完成的功能,watch都可以完成。谁简单用谁。
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作,比如延迟,而计算属性却不能延迟得到返回值
两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,自身无this,向外找,这样this的指向才是vm 或 组件实例对象。
11