Vue.js

  • Vue.js已关闭评论
  • 178 次浏览
  • A+
所属分类:Web前端
摘要

@Vue是一套用户构建用户界面的渐进式JavaScript框架作者:尤雨溪vue的特点:先用<script>引入,点击下载开发版本

@

目录

1、vue核心基础

Vue是一套用户构建用户界面渐进式JavaScript框架

Vue.js

作者:尤雨溪

vue的特点:

  1. 采用组件化模式,提高代码复用率,且让代码更好维护。
  2. 声明式编码,让编码人员无需直接操作DOM,提高开发效率。
  3. 使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点。

1.1、安装

先用<script>引入,点击下载开发版本

Vue.js

<!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>初识Vue</title>     <script src="../js/vue.js"></script> </head> <body>      </body> </html> 

运行代码,查看控制台,建议我们使用开发者工具

Vue.js

可以去扩展迷中搜索vue选择Vue.js devtools,点击下载,添加到谷歌浏览器的扩展程序中

Vue.js

再次刷新刚才打开的初始vue.html网页,发现终端没有了提示安装开发者工具了,只是还是有提示只需要做一个全局配置就可以

Vue.js

在Vue官方 API中有一个productionTip默认是true

Vue.js

<body>     <script type="text/javascript">         Vue.config.productionTip = false;  //阻止 vue 在启动时生成生产提示。     </script> </body> 

在刷新初识Vue.html打开控制台,就没有提示了

Vue.js

1.2、Hello Vue

<head>     <title>初识Vue</title>     <script type="text/javascript" src="../js/vue.js"></script> </head> <body>      <!-- 准备一个容器 -->     <div id="root">         <h1>Hello {{name}} !</h1>     </div>      <script type="text/javascript">         Vue.config.productionTip = false;;  //阻止 vue 在启动时生成生产提示。                  //创建Vue实例,         new Vue({             el:'#root' ,  //el用户指定当前Vue实例为哪个容器服务,值通常为css选择器字符串,             // el:document.getElementById('root')             data:{    //data中用于存储数据,数据供el所指定的容器使用, 值暂时先写成一个对象                 name:'Vue',             }         })     </script> </body> 

总结:

  1. 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
  2. root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
  3. root容器里的代码被成为【Vue模板】
  4. Vue实例和容器是一一对应的
  5. 真实开发中只有一个Vue实例,并且会配合着组件一起使用
  6. {{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
  7. 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新。

注意区分:js表达式和js代码(语句):

1. js表达式:一个表达式会生成一个值,可以放在任何一个需要 值的地方; * a  * a+b * x === y ? 'a'  : 'b'  2. js代码(语句)  * if(){}  * for(){} 

1.3、模板语法

<body>      <!-- 准备一个容器 -->     <div id="root">         <h1>插值语法</h1>         <h3>你好  {{ name }}</h3>         <hr>         <h1>指令语法</h1>         <a v-bind:href="url">点我去百度</a>         <a v-bind:href="url.toUpperCase()">点我去百度</a>         <a :href="Date.now()">点我去百度</a>      </div>      <script type="text/javascript">         Vue.config.productionTip = false;;  //阻止 vue 在启动时生成生产提示。                  //创建Vue实例,         new Vue({             el:'#root' ,  //el用户指定当前Vue实例为哪个容器服务,值通常为css选择器字符串,             // el:document.getElementById('root')             data:{    //data中用于存储数据,数据供el所指定的容器使用, 值暂时先写成一个对象                 name:'张三',                 url:'http://www.baidu.com'             }         })     </script> </body> 
<body>      <!-- 准备一个容器 -->     <div id="root">         <h1>插值语法</h1>         <h3>你好  {{ name }}</h3>         <hr>         <h1>指令语法</h1>         <a v-bind:href="school.url">点我去{{school.name}}</a>         <a :href="Date.now()">点我去{{school.name}}</a>     </div>      <script type="text/javascript">         Vue.config.productionTip = false;;  //阻止 vue 在启动时生成生产提示。                  //创建Vue实例,         new Vue({             el:'#root' ,  //el用户指定当前Vue实例为哪个容器服务,值通常为css选择器字符串,             // el:document.getElementById('root')             data:{    //data中用于存储数据,数据供el所指定的容器使用, 值暂时先写成一个对象                 name:'张三',                 school:{                     url:'http://www.jingdong.com',                     name:'京东'                 }             }         })     </script> </body> 

总结:

Vue模板语法有2大类:

  1. 插值语法
    • 功能:用于解析标签体内容太
    • 写法:{{xxx}} xxx是js表达式,且可以直接读取到data中的所有属性
  2. 指令语法
    • 功能:用户解析标签(包括:标签属性,标签体内容,绑定事件...)
    • 举例: v-bind:href="xxx" 或 简写为 :href="xxx" ,xxx同样要写js表达式,且可以直接读取到data中的所有属性
    • 注意:Vue中有很多指令,且形式都是 : v- ???:此处只是拿v-bind举了个例子。

1.4、数据绑定

<body>     <!--          Vue中有2种数据绑定的方式:         1.单向绑定(v-bind):数据只能从data流向页面         2.双向绑定(v-model):数据不仅仅能从data流向页面,还可以从页面流向data         备注:             1.双向绑定一般都应用在表单类元素上(如:input,select)             2.v-model:value 可以简写为v-model,因为v-model默认收集的就是value值     -->      <!-- 准备一个容器 -->     <div id="root">        <!-- 普通写法 -->        <!-- 单向数据绑定 :<input type="text" v-bind:value="name">        双向数据绑定 :<input type="text" v-model:value="name"> -->                <!-- 简写 -->        单向数据绑定 :<input type="text" :value="name">        双向数据绑定 :<input type="text" v-model="name">                <!-- 如下代码是错误,因为v-model只能用在表单类元素(输入类元素)上 -->        <!-- <h2 v-model:x="name">你好</h2> -->     </div>     <script type="text/javascript">         Vue.config.productionTip = false;;  //阻止 vue 在启动时生成生产提示。         new Vue({             el:'#root',             data:{                 name:'测试'             }         })      </script> </body> 

1.5、el与data的两种写法

el两种写法:

<body>     <!-- 准备一个容器 -->     <div id="root">       <h1>你好,{{name}} </h1>     </div>      <script type="text/javascript">         Vue.config.productionTip = false;;  //阻止 vue 在启动时生成生产提示。                  el的两种写法         const v=new Vue({             // el:'#root',    //第一种写法             data:{                 name:'测试'             }         })         // console.log(v)         //setTimeout(()=>{          //   v.$mount('#root')         //},1000);                  v.$mount('#root')   //第二种写法     </script> </body> 

data的两种写法:

<body>     <!-- 准备一个容器 -->     <div id="root">       <h1>你好,{{name}} </h1>     </div>      <script type="text/javascript">         Vue.config.productionTip = false;;  //阻止 vue 在启动时生成生产提示。                    new Vue({             el:'#root',             // data:{      //第一种写法,对象式             //     name:'测试'             // }              //第一种写法,函数式             data:function(){                   console.log('@@@',this); //此处的this指的是Vue实例对象                 return {                     name:'Vue'                 }             }         })     </script> </body> 

总结:

 data与el的2种写法:         1.el有两种写法:             (1)new Vue时候配置el属性             (2)先创建Vue实例,随后再通过vm.$mount('#root')指定el的值         2.data有两种写法:             (1)对象式             (2)函数式             如何选择:目前那种写法都可以,以后学习到组件的时候,data必须使用函数式,否则会报错         3.一个重要原则:             由Vue管理的函数,一定不要写成箭头函数,一旦写了箭头函数,this就不再是Vue实例了 

1.6、理解MVVM模型

M:模型(Model):对应data种的数据

V:视图(View):模板

VM:视图模型(ViewModel):Vue实例对象

Vue.js

1.7、Object.defineProperty方法

<body>         <script>             let number = 18             let person={                 name:'zs',                 sex:'男',                 // age:18,             }              Object.defineProperty(person,'age',{                 // value:18,                 // enumerable:true,//控制属性是否可以枚举,默认是false                 // writable:true,// 控制属性是否可以被修改,默认值false                 // configurable:true, //控制属性是否可以被删除,默认值是false                  //当有人读取person的age属性,get函数(getter)就会被调用,且返回值就是age的值                 get:function(){                     return number                 },                  //当有人修改person的age属性,set函数(setter)就会被调用,且会收到被修改的值                 set(value){                     number=value                 }             })              console.log(person);         </script> </body> 

1.8、数据代理

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

<script>     let obj={x:100}     let obj2={y:200}     Object.defineProperty(obj2,'x',{         get(){             return obj.x         },         set(value){             obj.x=value          }     }) </script> 
<body>     <!-- 准备一个容器 -->     <div id="root">         <h2>学校名称:{{name}}</h2>         <h2>学校地址:{{address}}</h2>              </div>     <script>         Vue.config.productionTip = false;         const vm = new Vue({             el:'#root',             data:{                 name:'北大',                 address:'北京'             }         })     </script> </body> 

Vue.js

  1. Vue中的数据代理:
    • 通过vm对象来代理data对象 中属性的操作(读/写)
  2. Vue中数据代理的好处:
    • 更加方便的操作data中的数据
  3. 基本原理:
    • 通过Object.definProperty()把data对象中所有的属性添加到vm上
    • 为每一个添加到vm上的属性,都指定了一个getter/setter
    • 在getter/setter内部去操作(读/写)data中对应的属性

1.9、事件处理

事件基本使用:

1. 基本v-on:xxx  或 @xxx 绑定事件,其中xxx是事件名; 2. 使用的回调需要配置在methods对象中,最终会在vm上; 3. methods中配置的函数,不需要使用箭头函数,!!否则this指的就不是vm了 4. methods中配置的函数,都是被Vue所管理的函数,this的指向是 vm 或 组件实例对象; 5. @click="demo"  和 @click="demo($event)"效果一致,但后者可以传参 
<body>     <div id="root">         <h2>Hello {{name}} !!!</h2>         <button v-on:click="showInfo1">Click Me1!</button>         <button @click="showInfo2(66,$event)">Click Me2!</button>      </div>      <script>         Vue.config.productionTip = false;         new Vue({             el:'#root',             data:{                 name:'Vue'             },             methods:{                 showInfo1(event){                     alert('Hello Vue1!!!')                     // console.log(event.target.innerText);                     // console.log(this); //此处是vm,                 },                 showInfo2(number,event){                     alert('Hello Vue2!!!')                     console.log(number,event);                 }             }         })     </script> </body> 

2.0、事件修饰符

prevent:阻止默认事件(常用) stop:阻止事件冒泡(常用) once:事件只触发一次(常用) capture:使用事件的捕获模式 self:只有event.target是当前操作的元素时,才触发事件 passive:事件的默认行为立即执行,无需等待事件回调执行完毕 
<!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">     <meta name="viewport" content="width=device-width, initial-scale=2.0">     <meta name="viewport" content="width=device-width, initial-scale=3.0">     <meta name="viewport" content="width=device-width, initial-scale=4.0">     <title>Document</title>     <script src="../js/vue.js"></script>     <style>         *{             margin-top: 20px;         }         .demo1{             height: 50px;             background-color: aqua;         }         .box1{             padding: 5px;             background-color: orange;         }         .box2{             padding: 5px;             background-color: blueviolet;         }         .list{             width: 200px;             height: 200px;             background-color: aquamarine;             overflow: auto;          }         li{             height: 100px;         }     </style> </head> <body>     <div id="root">         <h2>Hello {{name}} !!!</h2>         <!-- 阻止默认事件 -->         <a href="http://www.baidu.com" @click.prevent="showInfo">百度</a>         <!-- 阻止冒泡 -->         <div class="demo1" @click="showInfo">             <button @click.stop="showInfo">点我 提示信息</button>         </div>         <!-- 事件只触发一次 -->         <button @click.once="showInfo">点我 提示信息</button>         <!-- 使用事件的捕获模式 -->         <div class="box1" @click.capture="showMsg(1)">             div1             <div class="box2" @click="showMsg(2)">div2</div>         </div>         <!-- 只有event.target是当前操作的元素时,才触发事件 -->         <div class="demo1" @click.self="showInfo">             <button @click="showInfo">点我 提示信息</button>         </div>          <!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->         <!-- <ul @scroll="demo" class="list">    滚动条-->            <!-- <ul @wheel="demo" class="list"> 鼠标滚轮滚动 -->         <ul @wheel.passive="demo" class="list">              <li>1</li>             <li>2</li>             <li>3</li>             <li>4</li>         </ul>     </div>      <script>         Vue.config.productionTip = false;         new Vue({             el:'#root',             data:{                 name:'Vue'             },             methods:{                 showInfo(e){                     alert('Hello Vue1!!!')                 },                    showMsg(msg){                     console.log(msg);                 },                  demo(){                     for (let i = 0; i < 10000; i++) {                         console.log('#')                     }                     console.log('累坏了')                 }              }         })     </script> </body> </html> 

2.1、键盘事件

1.Vue中常用的按键别名     回车 =》enter     删除 =》delete (捕获 “删除” 和“退格”键)     退出 =》 esc     空格 =》 space     换行 =》tab     上 =》 up     下 =》down     左 =》 left     右 =》 right      2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)  3.系统修饰键(用户特殊):crtl alt shift  meta 	(1)配置keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才能触发     (2)配合keydown使用:正常触发事件      4.也可以使用keyCode去指定具体的按键(不推荐)  5.Vue.config.keyCodes.自定义键名 = 键码 ,可以去定制按键别名 
<body>     <div id="root">         <h2>欢迎学习 {{name}}</h2>         <input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo">     </div> </body>  <script>     Vue.config.productionTip = false;     new Vue({         el:'#root',         data:{             name:'Vue',         },         methods:{             showInfo(event){                 console.log(event.target.value);                          }         }     }) </script> 

2.2、计算属性

插值语法:

<body>     <div id="root">             姓:<input type="text" v-model="firstName"><br/><br/>             名: <input type="text" v-model="lastName"><br/><br/>             全名: <span>{{firstName}}-{{lastName}}</span>     </div> </body> <script>     Vue.config.productionTip = false;     new Vue({         el:'#root',         data:{             firstName:'张',             lastName:'三',         }     })  </script> 

methods实现:

<body>     <div id="root">             姓:<input type="text" v-model="firstName"><br/><br/>             名: <input type="text" v-model="lastName"><br/><br/>             全名: <span>{{fullName()}}</span>     </div> </body> <script>     Vue.config.productionTip = false;     new Vue({         el:'#root',         data:{             firstName:'张',             lastName:'三',         },         methods:{             fullName(){               return this.firstName  + '-' +this.lastName             }         }     })  </script> 

计算属性:

1.定义:要用的 属性不存在,要通过已有的属性计算得来 2.原理:底层借助了Object.defineproperty方法提供的getter和setter 3.get函数什么时候执行? 	(1)初次读取时会执行一次     (2)当依赖的数据发生变化时会再次调用 4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调式方便 5.备注: 	(1)计算属性最终会出现在vm上,直接读取使用即可     (2)如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变 
<body>     <div id="root">             姓:<input type="text" v-model="firstName"><br/><br/>             名: <input type="text" v-model="lastName"><br/><br/>             全名: <span>{{fullName}}</span>     </div> </body> <script>     Vue.config.productionTip = false;     new Vue({         el:'#root',         data:{             firstName:'张',             lastName:'三',         },         computed:{             fullName:{                 //当有人读取fullName时,get就会调用,且返回值就作为fullName的值                 get(){                     return this.firstName + '-' + this.lastName                 },                 //当fullName被修改时,set就会调用                 set(value){                     const arr=value.split('-')                     this.firstName=arr[0]                     this.lastName=arr[1]                   }             }         }     })  </script> 

简写

<body>     <div id="root">             姓:<input type="text" v-model="firstName"><br/><br/>             名: <input type="text" v-model="lastName"><br/><br/>             全名: <span>{{fullName}}</span>     </div> </body> <script>     Vue.config.productionTip = false;     new Vue({         el:'#root',         data:{             firstName:'张',             lastName:'三',         },         computed:{             fullName:function(){                 return this.firstName + '-' + this.lastName             }         }     })  </script> 

2.3、监视属性

天气案例

<body>     <div id="root">         <h2>天气很{{info}}</h2>         <button @click="changeWeather">切换天气</button>         <!-- <button @click="isHot = !isHot">切换天气</button> -->      </div> </body> <script>     Vue.config.productionTip = false;     new Vue({         el:'#root',         data:{             isHot:true         },         computed:{             info(){                 return this.isHot ? '炎热' : '凉爽'             }         },         methods: {             changeWeather(){                 this.isHot = !this.isHot             }         },          }) </script> 

监视属性

1.当被监视的属性变化时,回调函数自动调用,进行相关操作 2.监视的属性必须存在,才能进行监视 3监视的两种写法: 	(1)new Vue时传入watch配置    	(2)通过vm.$watch监视 
<body>     <div id="root">         <h2>天气很{{info}}</h2>         <button @click="changeWeather">切换天气</button>         <!-- <button @click="isHot = !isHot">切换天气</button> -->      </div> </body> <script>     Vue.config.productionTip = false;     new Vue({         el:'#root',         data:{             isHot:true         },         computed:{             info(){                 return this.isHot ? '炎热' : '凉爽'             }         },         methods: {              changeWeather(){                 this.isHot = !this.isHot             }         },         watch:{             isHot:{                 immediate:true, //初始化时让handler调用以下                 //当isHot发生改变时,调用handler()                 handler(newValue,oldValue){                     console.log('isHos被修改了',newValue,oldValue);                  }             }         }     }) </script> 
</body> <script>     Vue.config.productionTip = false;     const nw = new Vue({         el:'#root',         data:{             isHot:true         },         computed:{             info(){                 return this.isHot ? '炎热' : '凉爽'             }         },         methods: {              changeWeather(){                 this.isHot = !this.isHot             }         },     })     vm.$watch('isHot',{         immediate:true,         handler(newValue,oldValue){             console.log('isHot被修改了 ',newValue,oldValue);         }     }) </script> 

深度监视

深度监视: 	(1)Vue中的watch默认不监视对象内部值的改变(一层)     (2)配置deep:true可以监视对象内部值的改变(多层) 备注: 	(1)Vue自身可以配置对象内部值的改变,但Vue提供的watch默认不可以     (2)使用watch时根据数据的具体结构,决定是否采用深度监视 
<body>     <div id="root">         <h2>天气很{{info}}</h2>         <button @click="changeWeather">切换天气</button>         <!-- <button @click="isHot = !isHot">切换天气</button> -->         <hr>         <h3>a的值是{{numbers.a}}</h3>         <button @click="numbers.a++">点我让a+1</button>         <h3>b的值是{{numbers.b}}</h3>         <button @click="numbers.b++">点我让b+1</button>     </div> </body> <script>     Vue.config.productionTip = false;     const nw = new Vue({         el:'#root',         data:{             isHot:true,             numbers:{                 a:1,                 b:1             }         },         computed:{             info(){                 return this.isHot ? '炎热' : '凉爽'             }         },         methods: {              changeWeather(){                 this.isHot = !this.isHot             }         },         watch:{             //正常写法             // isHot:{             //     immediate:true, //初始化时让handler调用以下             //     deep:true, //深度监视             //     //当isHot发生改变时,调用handler()             //     handler(newValue,oldValue){             //         console.log('isHos被修改了',newValue,oldValue);              //     }             // },              //简写             isHot(newValue,oldValue){                 console.log('isHos被修改了',newValue,oldValue);             }         }     })  </script> 

watch对比computed区别

computed与watch之间的区别 	1.computed能完成的功能,watch都可以完成 	2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步惭怍 两个重要的小原则: 	1.所被Vue管理的函数,最好写成普通函数这样this的指向才是vm或者组件实例对象     2.所有不被Vue所管理的函数和(定时器的回调函数、Ajax的回调函数),最好写成箭头函数     这样this的指向才是vm  或组件实例对象 

watch

<body>     <div id="root">         姓: <input type="text" v-model="firstName"><br><br>         名:<input type="text" v-model="lastName"><br><br>         全名: <span>{{ fullName }}</span>     </div> </body> <script>     Vue.config.productionTip = false;      new Vue({         el:'#root',         data:{             firstName:'张',             lastName:'三',             fullName:'张-三',         },         watch:{             firstName(val){                 this.firstName=val + '-' + this.lastName             },             lastName(val){                 this.fullName= this.firstName + '-' + val             }         }     }) </script> 

computed

<body>     <div id="root">             姓:<input type="text" v-model="firstName"><br/><br/>             名: <input type="text" v-model="lastName"><br/><br/>             全名: <span>{{fullName}}</span>     </div> </body> <script>     Vue.config.productionTip = false;     new Vue({         el:'#root',         data:{             firstName:'张',             lastName:'三',         },         computed:{             fullName:{                 //当有人读取fullName时,get就会调用,且返回值就作为fullName的值                 get(){                     return this.firstName + '-' + this.lastName                 },                 //当fullName被修改时,set就会调用                 set(value){                     const arr=value.split('-')                     this.firstName=arr[0]                     this.lastName=arr[1]                   }             }         }     })  </script> 

2.4、绑定样式

  • 在应用界面中,某些(个)元素的样式时变化的
  • class/style绑定就是专门用来实现动态样式效果的技术
绑定样式:     1. class样式         写法:class="xxx" xxx可以是字符串、对象、数组。         字符串写法适用于:类名不确定,要动态获取。         对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。         数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。     2. style样式         :style="{fontSize: xxx}"其中xxx是动态值。         :style="[a,b]"其中a、b是样式对象。 	 

class绑定:

<!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>     <script src="../js/vue.js"></script>     <style>         .basic{             width: 400px;             height: 100px;             border: 1px solid black;         }                  .happy{             border: 4px solid red;;             background-color: rgba(255, 255, 0, 0.644);             background: linear-gradient(30deg,yellow,pink,orange,yellow);         }         .sad{             border: 4px dashed rgb(2, 197, 2);             background-color: gray;         }         .normal{             background-color: skyblue;         }          .bj1{             background-color: yellowgreen;         }         .bj2{             font-size: 30px;             text-shadow:2px 2px 10px red;         }         .bj3{             border-radius: 20px;         }     </style> </head> <body>     <div id="root">         <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定需要动态指定 -->         <div class="basic " :class="mood" @click="changeMood">{{name}}</div>         <br>         <br>         <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定,名字也不确定 -->         <div class="basic " :class="classArr" >{{name}}</div>         <br>         <br>         <!-- 绑定class样式--对象写法 ,适用于:要绑定的样式个数确定,名字确定 ,但是要动态决定用不用 -->         <div class="basic " :class="classObj" >{{name}}</div>      </div> </body> <script>     Vue.config.productionTip = false;     new Vue({         el:'#root',         data:{             name:'北京',             mood:'normal',             classArr:['bj1','bj2','bj3'],             classObj:{                 bj1:true,                 bj2:true,             }         },         methods: {             changeMood(){                 // this.mood  = 'happy'                 const arr=['happy','sad','normal']                 const index=Math.floor(Math.random()*3)                 this.mood =arr[index]             }         },     }) </script> </html> 

style绑定:

<body> 		 		<!-- 准备好一个容器--> 		<div id="root"> 			<!-- 绑定style样式--对象写法 --> 			<div class="basic" :style="styleObj ">{{name}}</div> <br/><br/> 			<!-- 绑定style样式--数组写法 -->             <div class="basic" :style="[styleObj,styleObj2]">{{name}}</div> 			<div class="basic" :style="styleArr">{{name}}</div> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false 		 		const vm = new Vue({ 			el:'#root', 			data:{ 				name:'北京',	 				styleObj:{ 					fontSize: '40px', 					color:'red', 				}, 				styleObj2:{ 					backgroundColor:'orange' 				}, 				styleArr:[ 					{ 						fontSize: '40px', 						color:'blue', 					}, 					{ 						backgroundColor:'gray' 					} 				] 			}, 			 		}) 	</script> 

2.5、条件渲染

条件渲染:     1.v-if         写法:             (1).v-if="表达式"              (2).v-else-if="表达式"             (3).v-else="表达式"         适用于:切换频率较低的场景。         特点:不展示的DOM元素直接被移除。         注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。      2.v-show         写法:v-show="表达式"         适用于:切换频率较高的场景。         特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉      3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。 
	<body> 	 		<!-- 准备好一个容器--> 		<div id="root"> 			<h2>当前的n值是:{{n}}</h2> 			<button @click="n++">点我n+1</button> 			<!-- 使用v-show做条件渲染 --> 			<!-- <h2 v-show="false">欢迎来到{{name}}</h2> --> 			<!-- <h2 v-show="1 === 1">欢迎来到{{name}}</h2> -->  			<!-- 使用v-if做条件渲染 --> 			<!-- <h2 v-if="false">欢迎来到{{name}}</h2> --> 			<!-- <h2 v-if="1 === 1">欢迎来到{{name}}</h2> -->  			<!-- v-else和v-else-if --> 			<!-- <div v-if="n === 1">Angular</div> 			<div v-else-if="n === 2">React</div> 			<div v-else-if="n === 3">Vue</div> 			<div v-else>哈哈</div> -->  			<!-- v-if与template的配合使用 --> 			<template v-if="n === 1"> 				<h2>你好</h2> 				<h2>Vue</h2> 				<h2>北京</h2> 			</template>  		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false  		const vm = new Vue({ 			el:'#root', 			data:{ 				name:'Vue', 				n:0 			} 		}) 	</script> 

2.6、列表渲染

基本列表

v-for指令:     1.用于展示列表数据     2.语法:v-for="(item, index) in xxx" :key="yyy"     3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少) 
<!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> 	<script src="../js/vue.js"></script> </head> <body> 	<div id="root"> 		<!-- 遍历数组 --> 		<h2>人员列表</h2> 		<ul> 			<li v-for="(p,index) in persons" :key="p.id"> 				{{p.name}}-{{p.age}} 			</li> 		</ul>  		<!-- 遍历对象 --> 		<h2>汽车信息</h2> 		<ul> 			<li v-for="(value,k) of car" :key="k"> 				{{k}}-{{value}}  			</li> 		</ul>  		<!-- 遍历字符串 --> 		<h3>遍历字符串</h3> 		<ul> 			<li v-for="(char,index) of str" ::key="index"> 				{{char}}-{{index}} 			</li> 		</ul> 		 		<!-- 遍历指定次数 --> 		<h3>遍历指定次数</h3> 		<ul> 			<li v-for="(number,index) of 5" :key="index"> 				{{index}}-{{number}} 			</li> 		</ul> 	</div> </body> <script> 	Vue.config.productionTip = false; 	new Vue({ 		el:'#root', 		data:{ 			persons:[ 				{id:'001',name:'张三',age:18}, 				{id:'002',name:'李四',age:19}, 				{id:'003',name:'王五',age:20} 			], 			car:{ 				name:'奥迪A8', 				price:'78万', 				color:'黑色', 			}, 			str:'helloVue' 		}  	}) </script> </html> 

key的工作原理

面试题:react、vue中的key有什么作用?(key的内部原理)  1. 虚拟DOM中key的作用:     key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,      随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:  2.对比规则:     (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:     ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!     ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。      (2).旧虚拟DOM中未找到与新虚拟DOM相同的key     创建新的真实DOM,随后渲染到到页面。  3. 用index作为key可能会引发的问题:     1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:         会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。      2. 如果结构中还包含输入类的DOM:     会产生错误DOM更新 ==> 界面有问题。  4. 开发中如何选择key?:     1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。     2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,     使用index作为key是没有问题的。 

Vue.js

Vue.js

<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<!-- 遍历数组 --> 			<h2>人员列表(遍历数组)</h2> 			<button @click.once="add">添加一个老刘</button> 			<ul> 				<li v-for="(p,index) of persons" :key="index"> 					{{p.name}}-{{p.age}} 					<input type="text"> 				</li> 			</ul> 		</div>  		<script type="text/javascript"> 			Vue.config.productionTip = false 			 			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} 						this.persons.unshift(p) 					} 				}, 			}) 		</script> 

列表过滤

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>列表过滤</title> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<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>  		<script type="text/javascript"> 			Vue.config.productionTip = false 			 			//用watch实现 			//#region  			/* 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 							}) 						} 					} 				} 			}) */ 			//#endregion 			 			//用computed实现 			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 						}) 					} 				} 			})  		</script> </html> 

列表排序

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>列表过滤</title> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<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}} 				</li> 			</ul> 		</div>  		<script type="text/javascript"> 			Vue.config.productionTip = false 			 			//用computed实现 			new Vue({ 				el:'#root', 				data:{ 					keyWord:'', 					sortType:0,  //0代表原顺序,1降序,2升序 					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(){ 						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 					} 				} 			})  		</script> </html> 

列表更新

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>更新时的一个问题</title> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<h2>人员列表</h2> 			<button @click="updateMei">更新马冬梅的信息</button> 			<ul> 				<li v-for="(p,index) of persons" :key="p.id"> 					{{p.name}}-{{p.age}}-{{p.sex}} 				</li> 			</ul> 		</div>  		<script type="text/javascript"> 			Vue.config.productionTip = false 			 			const vm = new Vue({ 				el:'#root', 				data:{ 					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:'男'} 					] 				}, 				methods: { 					updateMei(){ 						// this.persons[0].name = '马老师' //奏效 						// this.persons[0].age = 50 //奏效 						// this.persons[0].sex = '男' //奏效 						// this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'} //不奏效 						this.persons.splice(0,1,{id:'001',name:'马老师',age:50,sex:'男'}) 					} 				} 			})   		</script> </html> 

Vue监测数据改变的原理_对象

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>Vue监测数据改变的原理</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<h2>学校名称:{{name}}</h2> 			<h2>学校地址:{{address}}</h2> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。  		const vm = new Vue({ 			el:'#root', 			data:{ 				name:'北大', 				address:'北京', 				student:{ 					name:'tom', 					age:{ 						rAge:40, 						sAge:29, 					}, 					friends:[ 						{name:'jerry',age:35} 					] 				} 			} 		}) 	</script> </html> 

Vue.set使用

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>Vue.set的使用</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<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> </html> 

Vue监测数据改变的原理_数组

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>Vue监测数据改变的原理_数组</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<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="(h,index) in student.hobby" :key="index"> 					{{h}} 				</li> 			</ul> 			<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, 					}, 					hobby:['抽烟','喝酒','烫头'], 					friends:[ 						{name:'jerry',age:35}, 						{name:'tony',age:36} 					] 				} 			}, 			methods: { 				addSex(){ 					// Vue.set(this.student,'sex','男') 					this.$set(this.student,'sex','男') 				} 			} 		}) 	</script> </html> 

Vue.js

总结

Vue监视数据的原理:     1. vue会监视data中所有层次的数据。      2. 如何监测对象中的数据?         通过setter实现监视,且要在new Vue时就传入要监测的数据。         (1).对象中后追加的属性,Vue默认不做响应式处理         (2).如需给后添加的属性做响应式,请使用如下API:         Vue.set(target,propertyName/index,value) 或          vm.$set(target,propertyName/index,value)      3. 如何监测数组中的数据?     	通过包裹数组更新元素的方法实现,本质就是做了两件事:         (1).调用原生对应的方法对数组进行更新。         (2).重新解析模板,进而更新页面。      4.在Vue修改数组中的某个元素一定要用如下方法:         1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()         2.Vue.set() 或 vm.$set()      特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!  
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>总结数据监视</title> 		<style> 			button{ 				margin-top: 10px; 			} 		</style> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		 		<!-- 准备好一个容器--> 		<div id="root"> 			<h1>学生信息</h1> 			<button @click="student.age++">年龄+1</button><br> 			<button @click="addSex">添加性别属性,默认值:男</button><br> 			<button @click="student.sex= '未知'">修改性别属性</button><br> 			<button @click="addFriend">在列表首位添加一个朋友</button><br> 			<button @click="updateFirstFriendName">修改第一个朋友的名字为张三</button><br> 			<button @click="addHobby">添加一个爱好</button><br> 			<button @click="updateFirstHobby">修改第一个 爱好为 开车</button><br> 			<button @click="removeSmoke">过滤掉爱好中的 抽烟</button><br> 			<h3>姓名:{{student.name}}</h3> 			<h3>年龄:{{student.age}}</h3> 			<h3 v-if="student.sex">性别:{{student.sex}}</h3> 			<h3>爱好:</h3> 			<ul> 				<li v-for="(h,index) in student.hobby" :key="index"> 					{{h}} 				</li> 			</ul> 			<h3>朋友们:</h3> 			<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:{ 				student:{ 					name:'tom', 					age:18, 					hobby:['抽烟','喝酒','烫头'], 					friends:[ 						{name:'jerry',age:35}, 						{name:'tony',age:36} 					] 				} 			}, 			methods: { 				addSex(){ 					// Vue.set(this.student,'sex','男') 					vm.$set(this.student,'sex','男') 				}, 				addFriend(){ 					this.student.friends.unshift({ 						name:'莎莎', 						age:70, 					}) 				}, 				updateFirstFriendName(){ 					this.student.friends[0].name='张三' 					// this.student.friends[0].age=18 				}, 				addHobby(){ 					this.student.hobby.push('学习') 				}, 				updateFirstHobby(){ 					// this.student.hobby.splice(0,1,'开车') 						// Vue.set(this.student.hobby,0,'开车') 						this.$set(this.student.hobby,0,'开车') 				}, 				removeSmoke(){ 					this.student.hobby = this.student.hobby.filter((h)=>{ 						return h!== '抽烟' 					}) 				}   			}, 			 		}) 	</script> </html> 

2.7、收集表单数据

收集表单数据:     若:<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组成的数组     备注:v-model的三个修饰符:         lazy:失去焦点再收集数据         number:输入字符串转为有效的数字         trim:输入首尾空格过滤  
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>收集表单数据</title> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		 		<!-- 准备好一个容器--> 		<div id="root"> 			<form @submit.prevent="demo"> 				账号: <input type="text" v-model.trim="userInfo.account"><br><br> 				密码: <input type="password" v-model="userInfo.password"><br><br> 				年龄 <input type="number" v-model.number="userInfo.age"><br><br> 				性别:  				男:<input type="radio" name="sex" value="male" v-model="userInfo.sex"> 				女:<input type="radio" name="sex" value="female" v-model="userInfo.sex"><br><br> 				爱好: 				学习:<input type="checkbox" value="study" v-model="userInfo.hobby"> 				打游戏<input type="checkbox" value="game" v-model="userInfo.hobby"> 				吃饭:<input type="checkbox" value="eat" v-model="userInfo.hobby">  				<br><br> 				所属校区: 				<select v-model="userInfo.city"> 					<option value="">请选择校区</option> 					<option value="beijing">北京</option> 					<option value="shanghai">上海</option> 					<option value="guangdong">广东</option> 					<option value="wuhan">武汉</option> 				</select> 				<br><br> 				其他信息: 				<textarea v-model.lazy="userInfo.other"></textarea> 				<br><br> 				<input type="checkbox" v-model="userInfo.agree"> 阅读并接受<a href="http://www.baidu.com">《用户协议》</a> 				<br><br> 				<button>提交</button> 			</form> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false 		new Vue({ 			el:'#root', 			data:{ 				userInfo:{ 					account:'', 					password:'', 					sex:'female', 					hobby:[], 					city:'beijing', 					other:'', 					agree:'', 				} 			}, 			methods: { 				demo(){ 				 				} 			}, 		}) 		 	</script> </html> 

2.8、过滤器

过滤器:     定义:     	对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。     语法:         1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}         2.使用过滤器:{{ xxx | 过滤器名}}  或  v-bind:属性 = "xxx | 过滤器名"     备注:     	1.过滤器也可以接收额外参数、多个过滤器也可以串联  
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>过滤器</title> 		<script type="text/javascript" src="../js/vue.js"></script> 		<script type="text/javascript" src="../js/dayjs.min.js"></script> 	</head> 	<body> 		 		<!-- 准备好一个容器--> 		<div id="root"> 			<h2>显示格式化后的时间</h2> 			<!-- 计算属性实现 --> 			<h3>现在是:{{fmtTime}}</h3> 			<!-- methods实现 --> 			<h3>现在是:{{getFmtTime()}}</h3> 			<!-- 过滤器实现 --> 			<h3>现在是:{{time | timeFormater1 }}</h3> 			<!-- 过滤器实现(传参) --> 			<h3>现在是:{{time | timeFormater2('YYYY_MM_DD')}}</h3> 			<h3>现在是:{{time | timeFormater3('YYYY_MM_DD') | mySlice}}</h3> 			 			 		</div>  		<div id="root2"> 			<h3>{{msg | mySlice}}</h3> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false         //全局过滤器  		Vue.filter('mySlice',function(value){ 			return value.slice(0,4) 		}) 		 		new Vue({ 			el:'#root', 			data:{ 				time:Date.now() 			}, 			computed:{ 				fmtTime(){ 					return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss') 				} 			}, 			methods: { 				getFmtTime(){ 					return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss') 				} 			}, 			filters:{ 				timeFormater1(value){ 					return dayjs(value).format('YYYY-MM-DD HH:mm:ss') 				}, 				timeFormater2(value,str){ 					return dayjs(value).format(str) 				}, 				timeFormater3(value,str='YYY年MM月DD日 HH:mm:ss'){ 					return dayjs(value).format(str) 				}, 				mySlice(value){ 					return value.slice(0,4) 				} 			}, 			 		}) 		new Vue({ 			el:'#root2', 			data:{ 				msg:'Hello Vue' 			} 		}) 	 	</script> </html> 

2.9、内置指令

v-text

我们学过的指令:     v-bind	: 单向绑定解析表达式, 可简写为 :xxx     v-model	: 双向数据绑定     v-for  	: 遍历数组/对象/字符串     v-on   	: 绑定事件监听, 可简写为@     v-if 	 	: 条件渲染(动态控制节点是否存存在)     v-else 	: 条件渲染(动态控制节点是否存存在)     v-show 	: 条件渲染 (动态控制节点是否展示) v-text指令:     1.作用:向其所在的节点中渲染文本内容。     2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。  
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>v-text指令</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<div>你好,{{name}}</div> 			<div v-text="name"></div> 			<div v-text="str"></div> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 		 		new Vue({ 			el:'#root', 			data:{ 				name:'Vue', 				str:'<h3>你好啊!</h3>' 			} 		}) 	</script> </html> 

v-html

v-html指令:     1.作用:向指定节点中渲染包含html结构的内容。     2.与插值语法的区别:         (1).v-html会替换掉节点中所有的内容,{{xx}}则不会。         (2).v-html可以识别html结构。     3.严重注意:v-html有安全性问题!!!!         (1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。         (2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上! 
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>v-html指令</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<div>你好,{{name}}</div> 			<div v-html="str"></div> 			<div v-html="str2"></div> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。  		new Vue({ 			el:'#root', 			data:{ 				name:'尚硅谷', 				str:'<h3>你好啊!</h3>', 				str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>', 			} 		}) 	</script> </html> 

v-cloak

v-cloak指令(没有值):     1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。     2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。 
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>v-cloak指令</title> 		<style> 			[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> </html> 

v-once

v-once指令:     1.v-once所在节点在初次动态渲染后,就视为静态内容了。     2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。 
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>v-once指令</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		 		<!-- 准备好一个容器--> 		<div id="root"> 			<h2 v-once>初始化的n值是:{{n}}</h2> 			<h2>当前的n值是:{{n}}</h2> 			<button @click="n++">点我n+1</button> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 		 		new Vue({ 			el:'#root', 			data:{ 				n:1 			} 		}) 	</script> </html> 

v-pre

v-pre指令:     1.跳过其所在节点的编译过程。     2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。 
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>v-pre指令</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		 		<!-- 准备好一个容器--> 		<div id="root"> 			<h2 v-pre>Vue其实很简单</h2> 			<h2 >当前的n值是:{{n}}</h2> 			<button @click="n++">点我n+1</button> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。  		new Vue({ 			el:'#root', 			data:{ 				n:1 			} 		}) 	</script> </html> 

3.0、自定义指令

需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。

自定义指令总结:     一、定义语法:         (1).局部指令:         new Vue({															new Vue({             directives:{指令名:配置对象}   或   		directives{指令名:回调函数}         }) 																		})         (2).全局指令:         Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)      二、配置对象中常用的3个回调:         (1).bind:指令与元素成功绑定时调用。         (2).inserted:指令所在元素被插入页面时调用。         (3).update:指令所在模板结构被重新解析时调用。      三、备注:         1.指令定义时不加v-,但使用时要加v-;         2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。  
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>自定义指令</title> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<h2>{{name}}</h2> 			<h2>当前的n值是:<span v-text="n"></span> </h2> 			<!-- <h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2> --> 			<h2>放大10倍后的n值是:<span v-big="n"></span> </h2> 			<button @click="n++">点我n+1</button> 			<hr/> 			<input type="text" v-fbind:value="n"> 		</div> 	</body> 	 	<script type="text/javascript"> 		Vue.config.productionTip = false  		//定义全局指令 		/* Vue.directive('fbind',{ 			//指令与元素成功绑定时(一上来) 			bind(element,binding){ 				element.value = binding.value 			}, 			//指令所在元素被插入页面时 			inserted(element,binding){ 				element.focus() 			}, 			//指令所在的模板被重新解析时 			update(element,binding){ 				element.value = binding.value 			} 		}) */  		new Vue({ 			el:'#root', 			data:{ 				name:'北大', 				n:1 			}, 			directives:{ 				//big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。 				/* 'big-number'(element,binding){ 					// console.log('big') 					element.innerText = binding.value * 10 				}, */ 				big(element,binding){ 					console.log('big',this) //注意此处的this是window 					// console.log('big') 					element.innerText = binding.value * 10 				}, 				fbind:{ 					//指令与元素成功绑定时(一上来) 					bind(element,binding){ 						element.value = binding.value 					}, 					//指令所在元素被插入页面时 					inserted(element,binding){ 						element.focus() 					}, 					//指令所在的模板被重新解析时 					update(element,binding){ 						element.value = binding.value 					} 				} 			} 		}) 		 	</script> </html> 

3.1、生命周期

生命周期:     1.又名:生命周期回调函数、生命周期函数、生命周期钩子。     2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。     3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。     4.生命周期函数中的this指向是vm 或 组件实例对象。		 
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>引出生命周期</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<h2 v-if="a">你好啊</h2> 			<h2 :style="{opacity}">欢迎学习Vue</h2> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 		 		 new Vue({ 			el:'#root', 			data:{ 				a:false, 				opacity:1 			}, 			methods: { 				 			}, 			//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted 			mounted(){ 				console.log('mounted',this) 				setInterval(() => { 					this.opacity -= 0.01 					if(this.opacity <= 0) this.opacity = 1 				},16) 			}, 		})   		//通过外部的定时器实现(不推荐) 		/* setInterval(() => { 			vm.opacity -= 0.01 			if(vm.opacity <= 0) vm.opacity = 1 		},16) */ 	</script> </html> 

Vue.js

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>分析生命周期</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root" :x="n"> 			<h2 v-text="n"></h2> 			<h2>当前的n值是:{{n}}</h2> 			<button @click="add">点我n+1</button> 			<button @click="bye">点我销毁vm</button> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。  		new Vue({ 			el:'#root', 			// template:` 			// 	<div> 			// 		<h2>当前的n值是:{{n}}</h2> 			// 		<button @click="add">点我n+1</button> 			// 	</div> 			// `, 			data:{ 				n:1 			}, 			methods: { 				add(){ 					console.log('add') 					this.n++ 				}, 				bye(){ 					console.log('bye') 					this.$destroy() 				} 			}, 			watch:{ 				n(){ 					console.log('n变了') 				} 			}, 			beforeCreate() { 				console.log('beforeCreate') 			}, 			created() { 				console.log('created') 			}, 			beforeMount() { 				console.log('beforeMount') 			}, 			mounted() { 				console.log('mounted') 			}, 			beforeUpdate() { 				console.log('beforeUpdate') 			}, 			updated() { 				console.log('updated') 			}, 			beforeDestroy() { 				console.log('beforeDestroy') 			}, 			destroyed() { 				console.log('destroyed') 			}, 		}) 	</script> </html> 

总结:

常用的生命周期钩子:     1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。     2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。  关于销毁Vue实例     1.销毁后借助Vue开发者工具看不到任何信息。     2.销毁后自定义事件会失效,但原生DOM事件依然有效。     3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。 
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>引出生命周期</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		 		<!-- 准备好一个容器--> 		<div id="root"> 			<h2 :style="{opacity}">欢迎学习Vue</h2> 			<button @click="opacity = 1">透明度设置为1</button> 			<button @click="stop">点我停止变换</button> 		</div> 	</body>  	<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> </html> 

2、组件化编程

1.1、模块与组件、模块化与组件化

模块的定义:向外提供特定功能的 js 程序, 一般就是一个 js 文件=

组件的定义:实现应用中局部功能代码资源集合

模块化的定义:当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。

组件化的定义:当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用,。

1.2、非单文件组件

一个文件中包含有n个组件

基本使用

Vue中使用组件的三大步骤:     一、定义组件(创建组件)     二、注册组件     三、使用组件(写组件标签)  一、如何定义一个组件?     使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;     区别如下:         1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。         2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。     备注:使用template可以配置组件结构。  二、如何注册组件?     1.局部注册:靠new Vue的时候传入components选项     2.全局注册:靠Vue.component('组件名',组件)  三、编写组件标签:     <school></school>  
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>基本使用</title> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		 		<!-- 准备好一个容器--> 		<div id="root"> 			<h2>{{msg}}</h2> 			<hr> 			<hello></hello>   <!---全局使用组件---> 			<hr> 			<!-- 第三步,编写组件标签 --> 			<xuexiao></xuexiao> 			<hr> 			<!-- 第三步,编写组件标签 --> 			<xuesheng></xuesheng> 	 		</div>  		<div id="root2"> 			<hello></hello>	 		</div>  		 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false  		//第一步 创建school组件 		const school =Vue.extend({ 			template:` 			<div> 				<h2>学校名称:{{schoolName}}</h2> 			    <h2>学校地址:{{address}}</h2>	 				<button @click="showName">点我提示学校</button> 			</div> 			`, 			data(){ 				return { 					schoolName:'清华', 					address:'北京', 				 				} 			}, 			methods: { 				showName(){ 					alert(this.schoolName) 				} 			}, 		}) 		//第一步 创建student组件 		const student =Vue.extend({ 			template:` 			<div> 				<h2>学生名称:{{studentName}}</h2> 			    <h2>学校年龄:{{age}}</h2> 			</div> 			`, 			data(){ 				return { 					studentName:'张三', 					age:18, 				} 			} 		}) 		//第一步 创建hello 组件 		const hello = Vue.extend({ 			template:` 			<div> 				<h2>你好  {{name}}</h2>	 			</div>	 			`, 			data(){ 				return { 					name:'秋刀鱼' 				}  			} 		}) 		 		//第二部全局注册组件 		Vue.component('hello',hello)  		// 创建vm 		new Vue({ 			el:'#root', 			data:{ 				msg:'你好啊' 			}, 			// 第二部 注册组件(局部注册) 			components:{ 				xuexiao:school, 				xuesheng:student, 			} 		}) 		new Vue({ 			el:'#root2', 		}) 	</script> </html> 

几个注意点

几个注意点:     1.关于组件名:         一个单词组成:             第一种写法(首字母小写):school             第二种写法(首字母大写):School         多个单词组成:             第一种写法(kebab-case命名):my-school             第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)         备注:             (1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。             (2).可以使用name配置项指定组件在开发者工具中呈现的名字。      2.关于组件标签:         第一种写法:<school></school>         第二种写法:<school/>         备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。      3.一个简写方式:         const school = Vue.extend(options) 可简写为:const school = options  
<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>几个注意点</title> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<h1>{{msg}}</h1> 			<!-- 使用组件 --> 			<school></school> 			<!-- <school/> --> 			 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false 		//定义组件 		const school =Vue.extend({             name:'School' 			template:` 			<div> 				<h2>学校名称:{{name}}</h2> 			    <h2>学校地址:{{address}}</h2>	 			</div> 			`, 			data(){ 				return { 					name:'清华', 					address:'北京' 				} 			} 		}) 		  		new Vue({ 			el:'#root', 			data:{ 				msg:'欢迎学习Vue!' 			}, 			//注册组件 			components:{ 				// school:school 				school   //简单写法 			} 			 		}) 	</script> </html> 

组件的嵌套

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>组件的嵌套</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<school></school> 			<hello></hello> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 		   		//定义学生组件 		const student = Vue.extend({ 			name:'Student', 			template:` 			<div> 				<h2>学生名称:{{name}}</h2> 				<h2>学生年龄:{{age}}</h2> 			</div> 			`, 			data(){ 				return { 					name:'王五', 					age:18 				} 			}  		}) 		 		//定义school组件 		const school = Vue.extend({ 			name:'School', 			template:` 			<div> 				<h2>学校名称:{{name}}</h2> 				<h2>学校地址:{{address}}</h2> 				<student></student> 			</div> 			`, 			data(){ 				return { 					name:'清华', 					address:'北京' 				} 			}, 			//注册组件(局部) 			components:{ 				student 			} 		})        //定义hello组件 		const hello = Vue.extend({ 			template:` 			<h2>{{msg}}</h2> 			`, 			data(){ 				return { 					msg:'Hello Vue!!' 				} 			} 		}) 	 		new Vue({ 			el:'#root', 			//注册组件(局部) 			components:{school,hello} 		}) 	</script> </html> 

Vue.js

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>组件的嵌套</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<!-- <app></app> --> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 		   		//定义学生组件 		const student = Vue.extend({ 			name:'Student', 			template:` 			<div> 				<h2>学生名称:{{name}}</h2> 				<h2>学生年龄:{{age}}</h2> 			</div> 			`, 			data(){ 				return { 					name:'王五', 					age:18 				} 			}  		}) 		 		//定义school组件 		const school = Vue.extend({ 			name:'School', 			template:` 			<div> 				<h2>学校名称:{{name}}</h2> 				<h2>学校地址:{{address}}</h2> 				<student></student> 			</div> 			`, 			data(){ 				return { 					name:'清华', 					address:'北京' 				} 			}, 			//注册组件(局部) 			components:{ 				student 			} 		})        //定义hello组件 		const hello = Vue.extend({ 			template:` 			<h2>{{msg}}</h2> 			`, 			data(){ 				return { 					msg:'Hello Vue!!' 				} 			} 		}) 	 		//定义app组件 		const app =Vue.extend({ 			template:` 				<div> 					<hello></hello> 					<school></school>	 				</div>  			`,   			components:{ 				school, 			hello 			} 		}) 		new Vue({ 			template:'<app></app>', 			el:'#root', 			//注册组件(局部) 			components:{app} 		}) 	</script> </html> 

Vue.js

VueComponent构造函数

关于VueComponent:     1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。      2.我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象,      即Vue帮我们执行的:new VueComponent(options)。      3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!      4.关于this指向:     (1).组件配置中:     	data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。     (2).new Vue(options)配置中:     	data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。      5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。     Vue的实例对象,以后简称vm。  

Vue.js

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>VueComponent</title> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!-- 准备好一个容器--> 		<div id="root"> 			<school></school> 			<hello></hello> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false 		 		const school=Vue.extend({ 			name:'School', 			template:` 			<div> 				<h2>学校名称:{{name}}</h2> 			    <h2>学校地址:{{address}}</h2>	 			</div> 			`, 			data(){ 				return { 					name:'北大', 					address:'北京' 				} 			}  		}) 		  		const test =Vue.extend({ 			template:` 			<span>{{msg}}</span> 			`, 			data(){ 				return { 					msg:'测试' 				} 			} 		}) 		const hello =Vue.extend({ 			template:` 			<div> 				<h2>{{msg}}</h2> 			    <test></test>	 				 			</div> 			`, 			data(){ 				return { 					msg:'你好Vue' 				} 			}, 			components:{ 				test 			} 		}) 		// console.log('@@@@@',school);  		const vm =new Vue({ 			el:'#root', 			components:{ 				school,hello 			} 		}) 	 	</script> </html> 

Vue.js

内置关系

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>一个重要的内置关系</title> 		<!-- 引入Vue --> 		<script type="text/javascript" src="../js/vue.js"></script> 	</head> 	<body> 		<!--  				1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype 				2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。 		--> 		<!-- 准备好一个容器--> 		<div id="root"> 			<school></school> 		</div> 	</body>  	<script type="text/javascript"> 		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 		Vue.prototype.x = 99  		//定义school组件 		const school = Vue.extend({ 			name:'school', 			template:` 				<div> 					<h2>学校名称:{{name}}</h2>	 					<h2>学校地址:{{address}}</h2>	 					<button @click="showX">点我输出x</button> 				</div> 			`, 			data(){ 				return { 					name:'尚硅谷', 					address:'北京' 				} 			}, 			methods: { 				showX(){ 					console.log(this.x) 				} 			}, 		})  		//创建一个vm 		const vm = new Vue({ 			el:'#root', 			data:{ 				msg:'你好' 			}, 			components:{school} 		})  		 		//定义一个构造函数 		/* function Demo(){ 			this.a = 1 			this.b = 2 		} 		//创建一个Demo的实例对象 		const d = new Demo()  		console.log(Demo.prototype) //显示原型属性  		console.log(d.__proto__) //隐式原型属性  		console.log(Demo.prototype === d.__proto__)  		//程序员通过显示原型属性操作原型对象,追加一个x属性,值为99 		Demo.prototype.x = 99  		console.log('@',d) */  	</script> </html> 

1.3、单文件组件

安装插件:Vetur

Vue.js

安装插件后,快速生成组件模板
Vue.js

一个文件中只包含1个组件

步骤一:创建一个Student.vue文件

template里写结构,script中写脚本,脚本里包含给组件命名,配置数据,配置计算属性,等等。style中配置样式

<template>   <div>     <h2>姓名:{{ name }}</h2>     <h2>年龄:{{ age }}</h2>   </div> </template>   <script> export default {   name: "Student",   data() {     return {       name: "李四",       age: 18,     };   }, }; </script> 

步骤二:创建一个School.vue文件

<template> <!-- 组件的结构 --> <div class="demo">     <h2>学校名称:{{schoolName}}</h2>     <h2>学校地址:{{address}}</h2>     <button @click="showName">点我提示学校名称</button> </div>  </template>   <script> //组件交互相关的代码(数据、方法) export default{     name:'School',     data(){         return {             schoolName:'北大',             address:'北京',         }     },     methods:{         showName(){             alert(this.schoolName)         }     } } </script>     <style> /* 组件的样式 */     .demo{         background-color: aqua;     } </style> 

步骤三:创建一个App.vue文件,汇总所有组件

<template>   <div>     <School></School>     <Student></Student>        </div> </template>  <script>     //引入组件     import School from './School.vue'     import Student from  './Student.vue'     export default {         name:'App',         //注册         components:{             School,             Student         }           } </script>  <style>  </style> 

步骤四:借助main.js创建Vue实例,指定为哪个容器服务

import App  from './App.vue '   new Vue({     el:"#root",     template:`<App></App>`,     components:{App} }) 

步骤五:创建一个index.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>单文件语法使用</title> </head> <body>     <div id="root">         <!-- <App></App> -->     </div>     <script src="../js/vue.js"></script>     <script src="./main.js"></script> </body> </html> 

以上步骤不能运行,需要放到脚手架中

3、使用Vue脚手架

Vue CLI: Vue command line interface

Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)

文档

1.1、具体步骤

  1. 全局安装@vue/cli
npm install -g @vue/cli 
  1. 切换到要创建项目的目录,然后使用命令创建项目
vue create xxxx 
  1. 启动项目
npm run serve 

如果出现下载速度缓慢需要配置npm淘宝镜像:

npm config set registry https://registry.npm.taobao.org 

Vue.js

1.2、分析脚手架结构

├── node_modules ├── public │ ├── favicon.ico: 页签图标 │ └── index.html: 主页面 ├── src │ ├── assets: 存放静态资源 │ │ └── logo.png │ │── component: 存放组件 │ │ └── HelloWorld.vue │ │── App.vue: 汇总所有组件 │ │── main.js: 入口文件 ├── .gitignore: git 版本管制忽略的配置 ├── babel.config.js: babel 的配置文件 ├── package.json: 应用包配置文件 ├── README.md: 应用描述文件 ├── package-lock.json:包版本控制文件 
当执行了npm run serve 命令之后,直接找src里的main.js 
/* 该文件是整个项目的入口文件  */  //引入Vue import Vue from 'vue' //引入App组件,它是所有组件的父组件 import App from './App.vue' //关闭Vue的生产提示 Vue.config.productionTip = false  //创建Vue实例对 --vm  // new Vue({ //   //将App组件放入容器中 //   render: h => h(App), // }).$mount('#app')  new Vue({   el:"#app",   //将App组件放入容器中   render: h => h(App), })  
找到src中的App.vue ,引入组件,进行组件注册 
<template>   <div>     <img src="./assets/logo.png" alt="Vue.js" alt="logo">     <School></School>     <Student></Student>        </div> </template>  <script>     //引入组件     import School from './components/School.vue'     import Student from  './components/Student.vue'     export default {         name:'App',         //注册         components:{             School,             Student         }           } </script>  <style>  </style> 
最后找到容器public下的index.html 
<!DOCTYPE html> <html lang="">   <head>     <meta charset="utf-8">     <!-- 针对IE浏览器的特殊配置,让IE浏览器以最高的渲染级别渲染页面 -->     <meta http-equiv="X-UA-Compatible" content="IE=edge">     <!-- 开启移动端的理想视口 -->     <meta name="viewport" content="width=device-width,initial-scale=1.0">     <!-- 配置页签图标 -->     <link rel="icon" href="<%= BASE_URL %>favicon.ico">     <!-- 配置网页标题 -->     <title><%= htmlWebpackPlugin.options.title %></title>   </head>   <body>     <!-- 当浏览器不支持js时,noscript中的元素就会被渲染 -->     <noscript>       <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>     </noscript>     <!-- 容器 -->     <div id="app"></div>     <!-- built files will be auto injected -->   </body> </html>  

终端运行npm run serve 报错,可以在vue.config.js中设置如下代码:

Vue.js

const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({   transpileDependencies: true,   lintOnSave:false /*关闭语法检查*/ })  

Vue.js

1.3、render函数

关于不同版本的Vue:     1.vue.js与vue.runtime.xxx.js的区别:         (1).vue.js是完整版的Vue,包含:核心功能+模板解析器。         (2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。      2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用 

1.4、vue.config.js配置文件

Vue.js

Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的webpakc 配置,请在项目终端执行:

vue inspect > output.j以查看到Vue脚手架的默认配置 

使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh

下面代码的路径都可以修改

module.exports = {   pages: {     index: {       // page 的入口       entry: 'src/index/main.js',       // 模板来源       template: 'public/index.html',       // 在 dist/index.html 的输出       filename: 'index.html',       // 当使用 title 选项时,       // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>       title: 'Index Page',       // 在这个页面中包含的块,默认情况下会包含       // 提取出来的通用 chunk 和 vendor chunk。       chunks: ['chunk-vendors', 'chunk-common', 'index']     },     // 当使用只有入口的字符串格式时,     // 模板会被推导为 `public/subpage.html`     // 并且如果找不到的话,就回退到 `public/index.html`。     // 输出文件名会被推导为 `subpage.html`。     subpage: 'src/subpage/main.js'   } } 

vue.config.js文件中配置关闭语法检查

module.exports = defineConfig({        lintOnSave:false /*关闭语法检查*/ })  

1.5、ref属性

  1. 被用来给元素或子组件注册引用信息(id的替代者)
  2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
  3. 使用方式:
    1. 打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>
    2. 获取:this.$refs.xxx
  • main.js
//引入vue import Vue from 'vue' //引入App import App from './App.vue' //关闭Vue的身长提示 Vue.config.productionTip=false  //创建Vue实例---vm new Vue({     el:"#app",     render:h =>h(App) }) 
  • School.vue
<template>   <div class="school">     <h2>学校名称:{{name}}</h2>     <h2>学校地址:{{address}}</h2>   </div> </template>  <script> export default {     name:'School',     data(){         return {             name:'北大',             address:'北京'         }     } } </script>  <style>     .school{         background-color: bisque;     } </style> 
  • App.vue
<template>   <div>     <h1 v-text="msg" ref="title"></h1>     <button ref="btn" @click="showDOM">点我输出上面的DOM元素</button>     <School ref="sch"/>   </div> </template>  <script> //引入School组件 import School from './components/School.vue' export default {     name:'App',     data(){         return {             msg:'学习Vue中'         }     },     components:{         School     },     methods:{         showDOM(){             console.log(this.$refs.title);  //真实DOM元素             console.log(this.$refs.btn);  //真实DOM元素             console.log(this.$refs.sch);  //School组件的实例对象(vc)         }     } } </script>  

Vue.js

1.6、props配置

  1. 功能:让组件接收外部传过来的数据

  2. 传递数据:<Demo name="xxx"/>

  3. 接收数据:

    1. 第一种方式(只接收):props:['name']

    2. 第二种方式(限制类型):props:{name:String}

    3. 第三种方式(限制类型、限制必要性、指定默认值):

      props:{ 	name:{ 	type:String, //类型 	required:true, //必要性 	default:'老王' //默认值 	} } 

    备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

  • Student.vue
<template>   <div>     <h1>{{msg}}</h1>     <h2>学生姓名:{{name}}</h2>     <h2>学生性别:{{sex}}</h2>     <h2>学生年龄:{{age+1}}</h2>   </div> </template>  <script> export default {     name:'School',     data(){         return {             msg:'我是一名学生',         }     },     // props:['name','sex','age']  //简单声明接收      //接收的同时,对数据进行类型限制     // props:{     //     name:String,     //     age:Number,     //     sex:String     // }      //接收的同时对数据类型进行限制,默认值的指定 + 必要性的限制     props:{         name:{             type:String,  //name的类型             require:true  //name是必传的         },         age:{             type:Number,             default:99   //默认值         },         sex:{             type:String,             require:true,         }     } } </script>   
  • App.vue
<template>   <div>     <Student name="李四" sex="女" :age="18"/>   </div> </template>  <script> //引入School组件 import Student  from './components/Student.vue' export default {     name:'App',     components:{         Student     },     } </script>  

1.7、mixin混入

  1. 功能:可以把多个组件共用的配置提取成一个混入对象

  2. 使用方式:

    第一步定义混合:

    {     data(){....},     methods:{....}     .... } 

    第二步使用混入:

    ​ 全局混入:Vue.mixin(xxx)
    ​ 局部混入:mixins:['xxx']

    Vue.js

局部混入

  • Student.vue
<template>   <div>      <h2 @click="showName">学生姓名:{{ name }}</h2>     <h2>学生性别:{{ sex }}</h2>   </div> </template>  <script> //引入mixin import {mixin,mixin2} from '../mixin' export default {   name: "School",   data() {     return {              name: "张三",       sex: "男",     };   },   //使用   mixins:[mixin,mixin2] }; </script>   
  • School.vue
<template>   <div>      <h2 @click="showName">学校名称:{{ name }}</h2>     <h2>学校地址:{{ address }}</h2>   </div> </template>  <script> import {mixin,mixin2} from '../mixin' export default {   name: "School",   data() {     return {          name: "北大",       address: "北京",     };   },   mixins:[mixin,mixin2]  }; </script>   
  • src/mixin.js
export const mixin ={     methods:{         showName(){             alert(this.name)         }       },       mounted() {         console.log('你好啊!!');       }, } export const mixin2={     data(){         return {             x:100,             y:200         }     } } 

全局混入

//main.js import {mixin,mixin2} from './mixin' Vue.mixin(mixin) Vue.mixin(mixin2) 

1.8、插件

  1. 功能:用于增强Vue

  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

  3. 定义插件:

    对象.install = function (Vue, options) {     // 1. 添加全局过滤器     Vue.filter(....)      // 2. 添加全局指令     Vue.directive(....)      // 3. 配置全局混入(合)     Vue.mixin(....)      // 4. 添加实例方法     Vue.prototype.$myMethod = function () {...}     Vue.prototype.$myProperty = xxxx } 
  4. 使用插件:Vue.use()

  • Student.vue
<template>   <div>      <h2>学生姓名:{{ name | mySlice}}</h2>     <h2>学生性别:{{ sex }}</h2>     <button @click="test">点我测试hello方法</button>   </div> </template>  <script> //引入mixin  export default {   name: "School",   data() {     return {              name: "张三12313123",       sex: "男",     };   },   methods:{     test(){       this.hello()     }   }  }; </script>   
  • src/plugins.js
export default{     install(Vue,x,y,z){         console.log(x,y,z)         //全局过滤器 		Vue.filter('mySlice',function(value){ 			return value.slice(0,4) 		})         //全局混入         Vue.mixin({             data(){                 return {                     x:100,                     y:200                 }             }         })         //给Vue原型上添加上方法         Vue.prototype.hello=()=>{alert('你好')} 		     } }  
  • main.js
//引入插件 import  plugins from './plugins'   //使用插件 //Vue.use(plugins)  //不带参数 Vue.use(plugins,1,2,3)  //带参数 

1.9、scoped样式

  1. 作用:让样式在局部生效,防止冲突。
  2. 写法:<style scoped>
<style scoped> .demo {   background-color: yellow; } </style> 
<template>   <div class="demo">     <h2 class="que">学生性别:{{ sex }}</h2>   </div> </template>  <style lang="less"> .demo {   background-color: yellow;   .que{     font-size: 40px;   } } 

2.0、Todo-list案例

  1. 组件化编码流程:

    ​ (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

    ​ (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​ 1).一个组件在用:放在组件自身即可。

    ​ 2). 一些组件在用:放在他们共同的父组件上(状态提升)。

    ​ (3).实现交互:从绑定事件开始。

  2. props适用于:

    ​ (1).父组件 ==> 子组件 通信

    ​ (2).子组件 ==> 父组件 通信(要求父先给子一个函数)

  3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

  4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

  • Top.vue
<template>   <div class="todo-header">     <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>   </div> </template>  <script> import  {nanoid} from 'nanoid' export default {   name: "Header",   props:["addTodo"],   data(){    return {      title:''    }   },   methods:{     add(){       //校验数据       if(!this.title.trim) return alert('输入不能为空')       //将用户的输入包装成一个todo对象       const todoObj={id:nanoid(),title:this.title,done:false}       //通知App组件,添加一个todo对象       this.addTodo(todoObj)       //清空输入       this.title=''     }   },    } </script>  <style scoped> /*header*/ .todo-header input {   width: 560px;   height: 28px;   font-size: 14px;   border: 1px solid #ccc;   border-radius: 4px;   padding: 4px 7px; }  .todo-header input:focus {   outline: none;   border-color: rgba(82, 168, 236, 0.8);   box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),     0 0 8px rgba(82, 168, 236, 0.6); } </style> 
  • List.vue
<template>   <ul class="todo-main">     <Item v-for="todoObj in todos"      :key="todoObj.id"       :todo="todoObj"      :checkTodo="checkTodo"     :deleteTodo="deleteTodo"/>   </ul> </template>  <script> import Item from "./Item"; export default {   name: "List",   components: {     Item,   },   props:['todos','checkTodo','deleteTodo']  }; </script>  <style scoped> .todo-main {   margin-left: 0px;   border: 1px solid #ddd;   border-radius: 2px;   padding: 0px; }  .todo-empty {   height: 40px;   line-height: 40px;   border: 1px solid #ddd;   border-radius: 2px;   padding-left: 5px;   margin-top: 10px; } </style> 
  • Item.vue
<template>   <li>     <label>       <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>       <span>{{todo.title}}</span>     </label>     <button class="btn btn-danger" @click="handelDelete(todo.id)">删除</button>   </li> </template>  <script>   export default {     name: "Item",     //声明接收todo对象     props:['todo','checkTodo','deleteTodo'],     methods:{       // 勾选or取消勾选       handleCheck(id){         //通知App组件将对应的todo对象的done值取反         this.checkTodo(id)       },       //删除       handelDelete(id){         if(confirm('确定删除吗?')){           this.deleteTodo(id)         }       }     }   } </script>  <style scoped>  li {   list-style: none;   height: 36px;   line-height: 36px;   padding: 0 5px;   border-bottom: 1px solid #ddd; }  li label {   float: left;   cursor: pointer; }  li label li input {   vertical-align: middle;   margin-right: 6px;   position: relative;   top: -1px; }  li button {   float: right;   display: none;   margin-top: 3px; }  li:before {   content: initial; }  li:last-child {   border-bottom: none; }  li:hover{   background-color: #ddd; }  li:hover button {   display: block; } </style> 
  • Footer.vue
<template>   <div class="todo-footer" v-show="total">     <label>       <input type="checkbox" :checked="isAll" @change="checkAll"/>     </label>     <span> <span>已完成{{doneTotal}}</span> / 全部{{total}}</span>     <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>   </div> </template>  <script> export default {   name: "Footer",   props:['todos','checkAllTodo','clearAllTodo'],   computed:{     doneTotal(){       return  this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 :0),0)     },     total(){       return this.todos.length     },     isAll(){       return this.doneTotal == this.total  && this.total >0     }   },   methods:{     checkAll(e){             this.checkAllTodo(e.target.checked)     },     clearAll(){       this.clearAllTodo()     }   } }; </script>  <style scoped> /*footer*/ .todo-footer {   height: 40px;   line-height: 40px;   padding-left: 6px;   margin-top: 5px; }  .todo-footer label {   display: inline-block;   margin-right: 20px;   cursor: pointer; }  .todo-footer label input {   position: relative;   top: -1px;   vertical-align: middle;   margin-right: 5px; }  .todo-footer button {   float: right;   margin-top: 5px; } </style> 
  • App.vue
<template>   <div id="root">     <div class="todo-container">       <div class="todo-wrap">         <Top :addTodo="addTodo"/>         <List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>         <Footer :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>       </div>     </div>   </div> </template>  <script> //引入School组件 import Top from "./components/Top.vue"; import List from "./components/List.vue"; import Footer from "./components/Footer.vue";  export default {   name: "App",   components: {     Top,     List,     Footer,   },   data() {     return {       todos: [         { id: "001", title: "喝酒", done: true },         { id: "002", title: "打游戏", done: false },         { id: "003", title: "抽烟", done: true },       ],     };   },   methods:{     //添加todo     addTodo(todoObj){       this.todos.unshift(todoObj)     },     //勾选or取消勾选一个todo     checkTodo(id){       this.todos.forEach((todo)=>{         if(todo.id ===id) todo.done = !todo.done       })     },     //删除一个todo     deleteTodo(id){       this.todos=this.todos.filter((todo)=>{         return todo.id !== id       })     },     //全选or全不选     checkAllTodo(done){       this.todos.forEach((todo)=>{         todo.done =done       })     },     //清除所有已经完成的todo     clearAllTodo(){       this.todos=this.todos.filter((todo)=>{         return !todo.done       })     }    } } </script>  <style scoped> body {   background: #fff; }  .btn {   display: inline-block;   padding: 4px 12px;   margin-bottom: 0;   font-size: 14px;   line-height: 20px;   text-align: center;   vertical-align: middle;   cursor: pointer;   box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),     0 1px 2px rgba(0, 0, 0, 0.05);   border-radius: 4px; }  .btn-danger {   color: #fff;   background-color: #da4f49;   border: 1px solid #bd362f; }  .btn-danger:hover {   color: #fff;   background-color: #bd362f; }  .btn:focus {   outline: none; }  .todo-container {   width: 600px;   margin: 0 auto; } .todo-container .todo-wrap {   padding: 10px;   border: 1px solid #ddd;   border-radius: 5px; } </style>  
  • main.js
//引入vue import Vue from 'vue' //引入App import App from './App.vue'  //关闭Vue的身长提示 Vue.config.productionTip=false    //创建Vue实例---vm new Vue({     el:"#app",     render:h =>h(App) }) 

2.1、浏览器本地存储

localStorage

<!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>localStorage</title> </head> <body>     <h2>localStorage</h2>     <button onclick="saveData()">点我保存数据</button>     <button onclick="readData()">点我读取数据</button>     <button onclick="deleteData()">点我删除数据</button>     <button onclick="clearData()">点我清空数据</button> </body> <script>     let p={name:'张三',age:18}     function saveData(){            window.localStorage.setItem('msg','hello')         localStorage.setItem('msg2',666)         localStorage.setItem('person',JSON.stringify(p))     }     function readData(){            console.log(localStorage.getItem('msg'));         console.log(localStorage.getItem('msg2'));          const result =localStorage.getItem('person')         console.log(JSON.stringify(result))     }     function deleteData(){           // localStorage.removeItem('msg')          localStorage.removeItem('msg2')              }     function clearData(){           // localStorage.removeItem('msg')          localStorage.clear()              } </script> </html> 

Vue.js

sessionStorage

<!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>sessionStorage</title> </head> <body>     <h2>sessionStorage</h2>     <button onclick="saveData()">点我保存数据</button>     <button onclick="readData()">点我读取数据</button>     <button onclick="deleteData()">点我删除数据</button>     <button onclick="clearData()">点我清空数据</button> </body> <script>     let p={name:'张三',age:18}     function saveData(){            window.sessionStorage.setItem('msg','hello')         sessionStorage.setItem('msg2',666)         sessionStorage.setItem('person',JSON.stringify(p))     }     function readData(){            console.log(sessionStorage.getItem('msg'));         console.log(sessionStorage.getItem('msg2'));          const result =sessionStorage.getItem('person')         console.log(JSON.stringify(result))     }     function deleteData(){           // sessionStorage.removeItem('msg')          sessionStorage.removeItem('msg2')              }     function clearData(){           // sessionStorage.removeItem('msg')          sessionStorage.clear()              } </script> </html> 

Vue.js

2.2、组件的自定义事件

绑定,解绑

  • Schoole.vue
<template>   <div class="school">     <h2>学校名称:{{ name }}</h2>     <h2>学校地址:{{ address }}</h2>     <button @click="sendSchoolName">把学校名给App</button>   </div> </template>  <script>  export default {   name: "School",   data() {     return {       name: "北大",       address: "北京",     }   },   props:['getSchoolName'],   methods:{     sendSchoolName(){       this.getSchoolName(this.name)     }   }   }; </script>  <style>   .school{     background-color: orange;     padding:5px;     margin-top:30px ;   } </style>   
  • Student.vue
<template>   <div class="student">      <h2>学生姓名:{{ name }}</h2>     <h2>学生性别:{{ sex }}</h2>     <h2>当前求和为:{{number}}</h2>     <button @click="add">点我number++</button>     <button @click="sendStudentName">把学生名给App</button>     <button @click="unbind">解绑atguigu事件</button>     <button @click="death">销毁当前Student组件的实例</button>    </div> </template>  <script>  export default {   name: "School",   data() {     return {              name: "张三",       sex: "男",       number:0     };   },   methods:{     add(){       console.log('add被调用了');       this.number++     },     sendStudentName(){       //触发Student组件实例身上的atguigu事件       this.$emit('atguigu',this.name,666,888,999)       this.$emit('demo')     },     unbind(){       // this.$off('atguigu');  //解绑一个自定义事件       // this.$off(['atguigu','demo']);  //解绑多个自定义事件       this.$off();  //解绑所有自定义事件     },     death(){       this.$destroy() //销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全都不会奏效     }   }  }; </script>  <style>   .student{     background-color: skyblue;      padding:5px;   } </style>  
  • App.vue
<template>   <div class="app">     <h2>{{ msg }}</h2>     <!-- 通过父组件给子组件传递函数类型的Props实现:子给父传递数据 -->     <School :getSchoolName="getSchoolName" />     <!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据 -->     <!-- <Student v-on:atguigu="getStudentName" /> -->     <Student @atguigu="getStudentName" @demo="m1" />     <!-- <Student ref="student" /> -->   </div> </template>  <script> //引入School组件 import Student from "./components/Student.vue"; import School from "./components/School.vue"; export default {   name: "App",   components: {     Student,     School,   },   data() {     return {       msg: "你好Vue~~",     };   },   methods: {     getSchoolName(name) {       console.log("App收到了学校名:", name);     },     getStudentName(name,...params){       console.log('App收到了学生名:',name,params)     },     m1(){       console.log('demo事件被触发')     }   },   mounted(){     setTimeout(()=>{       this.$refs.student.$on('atguigu',this.getStudentName)     },3000)   } } </script> <style> .app {   background-color: gray;   padding: 5px; } </style>  

2.3、全局事件总线

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 安装全局事件总线:

    new Vue({ 	...... 	beforeCreate() { 		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm 	},     ...... })  
  3. 使用事件总线:

    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

      methods(){   demo(data){......} } ...... mounted() {   this.$bus.$on('xxxx',this.demo) } 
    2. 提供数据:this.$bus.$emit('xxxx',数据)

  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

  • main.js
//引入vue import Vue from 'vue' //引入App import App from './App.vue' //关闭Vue的身长提示 Vue.config.productionTip=false  // const Demo =Vue.extend({}) // const d = new Demo()    //创建Vue实例---vm new Vue({     el:"#app",     render:h =>h(App),     beforeCreate(){         Vue.prototype.$bus= this  //安装全局事件总线     } })  
  • Student.vue
<template>   <div class="student">     <h2>学生姓名:{{ name }}</h2>     <h2>学生性别:{{ sex }}</h2>     <button @click="sendStudentName">把学生名给School组件</button>   </div> </template>  <script> export default {   name: "School",   data() {     return {       name: "张三",       sex: "男",     };   },   methods: {     sendStudentName() {       this.$bus.$emit('hello', 666);     },   },   mounted() {     // console.log('Student',this.x)   }, }; </script>  <style> .student {   background-color: skyblue;   padding: 5px; } </style>  
  • School.vue
<template>   <div class="school">     <h2>学校名称:{{ name }}</h2>     <h2>学校地址:{{ address }}</h2>       </div> </template>  <script>  export default {   name: "School",   data() {     return {       name: "北大",       address: "北京",     }   },   mounted(){     // console.log('School',this)     this.$bus.$on('hello',(data)=>{       console.log('我是School组件,收到了数据 》',data)     })   },   beforeDestroy(){     this.$bus.$off('hello')   } }; </script>  <style>   .school{     background-color: orange;     padding:5px;     margin-top:30px ;   } </style>   

2.4、消息订阅与发布pubsub-js

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

      methods(){   demo(data){......} } ...... mounted() {   this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息 } 
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)取消订阅。

    • Student.vue
<template>   <div class="student">     <h2>学生姓名:{{ name }}</h2>     <h2>学生性别:{{ sex }}</h2>     <button @click="sendStudentName">把学生名给School组件</button>   </div> </template>  <script> import pubsub from 'pubsub-js' export default {   name: "School",   data() {     return {       name: "张三",       sex: "男",     };   },   methods: {     sendStudentName() {       // this.$bus.$emit('hello', 666);       pubsub.publish('hello',666)     },   },   mounted() {     // console.log('Student',this.x)   }, }; </script>  <style> .student {   background-color: skyblue;   padding: 5px; } </style>  
  • School.vue
<template>   <div class="school">     <h2>学校名称:{{ name }}</h2>     <h2>学校地址:{{ address }}</h2>   </div> </template>  <script> import pubsub from 'pubsub-js'  export default {   name: "School",   data() {     return {       name: "北大",       address: "北京",     }   },   methods:{     demo(message,data){       console.log('有人发布了hello消息,hello消息的回调执行了',message,data)     }   },   mounted(){     // console.log('School',this)     // this.$bus.$on('hello',(data)=>{     //   console.log('我是School组件,收到了数据 》',data)     // })     this.pubId=pubsub.subscribe('hello',this.demo)     },   beforeDestroy(){     // this.$bus.$off('hello')     pubsub.unsubscribe(this.pubId)   } }; </script>  <style>   .school{     background-color: orange;     padding:5px;     margin-top:30px ;   } </style>   

2.5、nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

2.6、动画与过度效果

  1. 作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。

  2. 图示:

Vue.js

  1. 写法:

    1. 准备好样式:

      • 元素进入的样式:
        1. v-enter:进入的起点
        2. v-enter-active:进入过程中
        3. v-enter-to:进入的终点
      • 元素离开的样式:
        1. v-leave:离开的起点
        2. v-leave-active:离开过程中
        3. v-leave-to:离开的终点
    2. 使用<transition>包裹要过度的元素,并配置name属性:

      <transition name="hello"> 	<h1 v-show="isShow">你好啊!</h1> </transition> 
    3. 备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值。


    动画

    <template> 	<div> 		<button @click="isShow = !isShow">显示/隐藏</button> 		<transition name="hello" appear> 			<h1 v-show="isShow">你好啊!</h1> 		</transition> 	</div> </template>  <script> 	export default { 		name:'Test', 		data() { 			return { 				isShow:true 			} 		}, 	} </script>  <style scoped> 	h1{ 		background-color: orange; 	}  	.hello-enter-active{ 		animation: atguigu 0.5s linear; 	}  	.hello-leave-active{ 		animation: atguigu 0.5s linear reverse; 	}  	@keyframes atguigu { 		from{ 			transform: translateX(-100%); 		} 		to{ 			transform: translateX(0px); 		} 	} </style> 

    过度

    <template> 	<div> 		<button @click="isShow = !isShow">显示/隐藏</button> 		<transition-group name="hello" appear> 			<h1 v-show="!isShow" key="1">你好啊!</h1> 			<h1 v-show="isShow" key="2">尚硅谷!</h1> 		</transition-group> 	</div> </template>  <script> 	export default { 		name:'Test', 		data() { 			return { 				isShow:true 			} 		}, 	} </script>  <style scoped> 	h1{ 		background-color: orange; 	} 	/* 进入的起点、离开的终点 */ 	.hello-enter,.hello-leave-to{ 		transform: translateX(-100%); 	} 	.hello-enter-active,.hello-leave-active{ 		transition: 0.5s linear; 	} 	/* 进入的终点、离开的起点 */ 	.hello-enter-to,.hello-leave{ 		transform: translateX(0); 	}  </style> 

    第三方动画库

    <template> 	<div> 		<button @click="isShow = !isShow">显示/隐藏</button> 		<transition-group  			appear 			name="animate__animated animate__bounce"  			enter-active-class="animate__swing" 			leave-active-class="animate__backOutUp" 		> 			<h1 v-show="!isShow" key="1">你好啊!</h1> 			<h1 v-show="isShow" key="2">尚硅谷!</h1> 		</transition-group> 	</div> </template>  <script> 	import 'animate.css' 	export default { 		name:'Test', 		data() { 			return { 				isShow:true 			} 		}, 	} </script>  <style scoped> 	h1{ 		background-color: orange; 	} 	  </style> 

4、Vue中的ajax

1.1、解决Ajax跨域问题

方法一

​ 在vue.config.js中添加如下配置:

devServer:{   proxy:"http://localhost:5000" } 

说明:

  1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

方法二

​ 编写vue.config.js配置具体代理规则:

module.exports = { 	devServer: {       proxy: {       '/api1': {// 匹配所有以 '/api1'开头的请求路径         target: 'http://localhost:5000',// 代理目标的基础路径         changeOrigin: true,         pathRewrite: {'^/api1': ''}       },       '/api2': {// 匹配所有以 '/api2'开头的请求路径         target: 'http://localhost:5001',// 代理目标的基础路径         changeOrigin: true,         pathRewrite: {'^/api2': ''}       }     }   } } /*    changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000    changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080    changeOrigin默认值为true */ 

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
  2. 缺点:配置略微繁琐,请求资源时必须加前缀。

代码示例:

const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({   transpileDependencies: true,   lintOnSave:false ,/*关闭语法检查*/    //开启代理服务器 (方式一)   // devServer: {   //   proxy: 'http://localhost:5000'   // }     //开启代理服务器 (方式二)     devServer: {       proxy: {         '/api': {           target: 'http://localhost:5000',           pathRewrite:{'^/api':''},           ws: true,  //用户支持websocket           changeOrigin: true   //用于控制请求头中的host值         },         '/demo': {           target: 'http://localhost:5001',           pathRewrite:{'^/demo':''},           ws: true,  //用户支持websocket           changeOrigin: true   //用于控制请求头中的host值         },               }     } })  
<template> 	<div > 		<button @click="getStudents">获取学生信息</button> 		<button @click="getCars">获取学生信息</button> 	</div> </template>  <script> 	import axios from 'axios'  	export default { 		name:'App', 		methods:{ 			getStudents(){ 				axios.get('http://localhost:8081/api/students').then( 					response=>{ 						console.log('请求成功了',response.data) 					}, 					error=>{ 						console.log('请求失败了',error.message) 					} 				) 			}, 			getCars(){ 				axios.get('http://localhost:8081/demo/cars').then( 					response=>{ 						console.log('请求成功了',response.data) 					}, 					error=>{ 						console.log('请求失败了',error.message) 					} 				) 			} 		} 	} </script>   

1.2、插槽

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件

  2. 分类:默认插槽、具名插槽、作用域插槽

  3. 使用方式:

    1. 默认插槽:

      父组件中:         <Category>            <div>html结构1</div>         </Category> 子组件中:         <template>             <div>                <!-- 定义插槽 -->                <slot>插槽默认内容...</slot>             </div>         </template> 
    2. 具名插槽:

      父组件中:         <Category>             <template slot="center">               <div>html结构1</div>             </template>              <template v-slot:footer>                <div>html结构2</div>             </template>         </Category> 子组件中:         <template>             <div>                <!-- 定义插槽 -->                <slot name="center">插槽默认内容...</slot>                <slot name="footer">插槽默认内容...</slot>             </div>         </template> 
    3. 作用域插槽:

      1. 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)

      2. 具体编码:

        父组件中: 		<Category> 			<template scope="scopeData"> 				<!-- 生成的是ul列表 --> 				<ul> 					<li v-for="g in scopeData.games" :key="g">{{g}}</li> 				</ul> 			</template> 		</Category>  		<Category> 			<template slot-scope="scopeData"> 				<!-- 生成的是h4标题 --> 				<h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> 			</template> 		</Category> 子组件中:         <template>             <div>                 <slot :games="games"></slot>             </div>         </template> 		         <script>             export default {                 name:'Category',                 props:['title'],                 //数据在子组件自身                 data() {                     return {                         games:['红色警戒','穿越火线','劲舞团','超级玛丽']                     }                 },             }         </script> 
默认插槽
  • App.vue
<template> 	<div class="container"> 		<Category title="美食"> 			<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="Vue.js" alt=""> 		</Category> 		<Category title="游戏"> 			<ul> 				<li v-for="(g,index) in games" :key="index">{{g}}</li> 			</ul> 		</Category> 		<Category title="电影"> 			<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video> 		</Category> 	</div> </template>  <script> 	import Category from './components/Category.vue' 	export default { 		name:'App', 		components:{Category}, 		data() { 			return { 				foods:['火锅','烧烤','小龙虾','牛排'], 				games:['红色警戒','穿越火线','劲舞团','超级玛丽'], 				films:['《教父》','《拆弹专家》','《你好,李焕英》','《尚硅谷》'] 			} 		}, 	} </script> <style>  .container{ 	display: flex; 	justify-content: space-around;  }  h3{ 	text-align: center; 	background-color: orange;  }  </style> 
  • Category.vue
<template>   <div class="category">     <h3>{{title}}分类</h3>     <slot>我是一些默认值,当使用者灭有传递具体结构时,我会出现</slot>   </div> </template>  <script> export default {     name:'Category',     props:['title'] } </script>  <style  scoped>   .category{     background-color: skyblue;     width:200px;     height: 300px;  }   video{ 	width: 100%;  }  img{     width: 100%;  }  </style> 
具名插槽
  • Category.vue
<template>   <div class="category">     <h3>{{title}}分类</h3>     <slot name="center">我是一些默认值,当使用者灭有传递具体结构时,我会出现</slot>     <slot name="footer">我是一些默认值,当使用者灭有传递具体结构时,我会出现</slot>   </div> </template>  <script> export default {     name:'Category',     props:['title'] } </script>  <style  scoped>   .category{     background-color: skyblue;     width:200px;     height: 300px;  }   video{ 	width: 100%;  }  img{     width: 100%;  }  </style> 
  • App.vue
<template>   <div class="container">     <Category title="美食">       <img         slot="center"         src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="Vue.js"         alt=""       />       <a slot="footer" href="http://www.atguigu.com">更多美食</a>     </Category>     <Category title="游戏">       <ul slot="center">         <li v-for="(g, index) in games" :key="index">{{ g }}</li>       </ul>       <div class="foot" slot="footer">         <a href="http://www.atguigu.com">单机游戏</a>         <a href="http://www.atguigu.com">网络游戏</a>       </div>     </Category>     <Category title="电影">       <video         slot="center"         controls         src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"       ></video>       <template v-slot:footer>         <div class="foot">           <a href="http://www.atguigu.com">经典</a>           <a href="http://www.atguigu.com">热门</a>           <a href="http://www.atguigu.com">推荐</a>         </div>         <h4>欢迎前来观影</h4>       </template>     </Category>   </div> </template>  <script> import Category from "./components/Category.vue"; export default {   name: "App",   components: { Category },   data() {     return {       foods: ["火锅", "烧烤", "小龙虾", "牛排"],       games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],       films: ["《教父》", "《拆弹专家》", "《你好,李焕英》", "《尚硅谷》"],     };   }, }; </script> <style> .container, .foot {   display: flex;   justify-content: space-around; } h3 {   text-align: center;   background-color: orange; } h4 {   text-align: center; } </style> 
作用域插槽
  • Category.vue
<template>   <div class="category">     <h3>{{title}}分类</h3>     <slot :games="games">我是默认的一些内容</slot>   </div> </template>  <script> export default {     name:'Category',     props:['title'],     data() {     return {       games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],     };   }, } </script>  <style  scoped>   .category{     background-color: skyblue;     width:200px;     height: 300px;  }   video{ 	width: 100%;  }  img{     width: 100%;  }  </style> 
  • App.vue
<template>   <div class="container">     <Category title="游戏">       <template scope="atguigu">         <ul>           <li v-for="(g, index) in atguigu.games" :key="index">{{ g }}</li>         </ul>       </template>     </Category>     <Category title="游戏">       <template scope="{games}">         <ol>           <li style="color: red" v-for="(g, index) in games" :key="index">             {{ g }}           </li>         </ol>       </template>     </Category>      <Category title="游戏">       <template slot-scope="{games}">         <h4 style="color: blue" v-for="(g, index) in games" :key="index">           {{ g }}         </h4>       </template>     </Category>   </div> </template>  <script> import Category from "./components/Category.vue"; export default {   name: "App",   components: { Category }, }; </script> <style> .container, .foot {   display: flex;   justify-content: space-around; } h3 {   text-align: center;   background-color: orange; } h4 {   text-align: center; } </style> 

5、vuex

在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

何时用vuex

多个组件需要共享数据时

  1. 多个组件依赖于同一状态
  2. 来自不同组件的行为需要变更同一状态

求和案例纯Vue版

  • Count.vue
<template> 	<div> 		<h1>当前求和为:{{sum}}</h1> 		<select v-model.number="n"> 			<option value="1">1</option> 			<option value="2">2</option> 			<option value="3">3</option> 		</select> 		<button @click="increment">+</button> 		<button @click="decrement">-</button> 		<button @click="incrementOdd">当前求和为奇数再加</button> 		<button @click="incrementWait">等一等再加</button> 	</div> </template>  <script> 	export default { 		name:'Count', 		data() { 			return { 				n:1, //用户选择的数字 				sum:0 //当前的和 			} 		}, 		methods: { 			increment(){ 				this.sum += this.n 			}, 			decrement(){ 				this.sum -= this.n 			}, 			incrementOdd(){ 				if(this.sum % 2){ 					this.sum += this.n 				} 			}, 			incrementWait(){ 				setTimeout(()=>{ 					this.sum += this.n 				},500) 			}, 		}, 	} </script>  <style lang="css"> 	button{ 		margin-left: 5px; 	} </style>  
  • App.vue
<template> 	<div> 		<Count/> 	</div> </template>  <script> 	import Count from './components/Count' 	export default { 		name:'App', 		components:{Count}, 	} </script>  
  • main.js
//引入Vue import Vue from 'vue' //引入App import App from './App.vue' //引入插件 import vueResource from 'vue-resource' //关闭Vue的生产提示 Vue.config.productionTip = false //使用插件 Vue.use(vueResource)  //创建vm new Vue({ 	el:'#app', 	render: h => h(App), 	beforeCreate() { 		Vue.prototype.$bus = this 	} }) 

1.1、Vuex的工作原理

Vue.js

1.2、搭建vuex环境

  1. 创建文件:src/store/index.js

    //引入Vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex插件 Vue.use(Vuex)  //准备actions对象——响应组件中用户的动作 const actions = {} //准备mutations对象——修改state中的数据 const mutations = {} //准备state对象——保存具体的数据 const state = {}  //创建并暴露store export default new Vuex.Store({ 	actions, 	mutations, 	state }) 
  2. main.js中创建vm时传入store配置项

    ...... //引入store import store from './store' ......  //创建vm new Vue({ 	el:'#app', 	render: h => h(App), 	store }) 

1.3、基本使用

  1. 初始化数据、配置actions、配置mutations,操作文件store/index.js

    //引入Vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //引用Vuex Vue.use(Vuex)  const actions = {     //响应组件中加的动作 	jia(context,value){ 		// console.log('actions中的jia被调用了',miniStore,value) 		context.commit('JIA',value) 	}, }  const mutations = {     //执行加 	JIA(state,value){ 		// console.log('mutations中的JIA被调用了',state,value) 		state.sum += value 	} }  //初始化数据 const state = {    sum:0 }  //创建并暴露store export default new Vuex.Store({ 	actions, 	mutations, 	state, }) 
  2. 组件中读取vuex中的数据:$store.state.sum

  3. 组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

    备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

求和案例

  • store/index.js
//该文件用于创建Vuex中最为核心的store import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex插件 Vue.use(Vuex)  //准备actions——用于响应组件中的动作 const actions = { 	/* jia(context,value){ 		console.log('actions中的jia被调用了') 		context.commit('JIA',value) 	}, 	jian(context,value){ 		console.log('actions中的jian被调用了') 		context.commit('JIAN',value) 	}, */ 	jiaOdd(context,value){ 		console.log('actions中的jiaOdd被调用了') 		if(context.state.sum % 2){ 			context.commit('JIA',value) 		} 	}, 	jiaWait(context,value){ 		console.log('actions中的jiaWait被调用了') 		setTimeout(()=>{ 			context.commit('JIA',value) 		},500) 	} } //准备mutations——用于操作数据(state) const mutations = { 	JIA(state,value){ 		console.log('mutations中的JIA被调用了') 		state.sum += value 	}, 	JIAN(state,value){ 		console.log('mutations中的JIAN被调用了') 		state.sum -= value 	} } //准备state——用于存储数据 const state = { 	sum:0 //当前的和 }  //创建并暴露store export default new Vuex.Store({ 	actions, 	mutations, 	state, }) 
  • Count.vue
<template> 	<div> 		<h1>当前求和为:{{$store.state.sum}}</h1> 		<select v-model.number="n"> 			<option value="1">1</option> 			<option value="2">2</option> 			<option value="3">3</option> 		</select> 		<button @click="increment">+</button> 		<button @click="decrement">-</button> 		<button @click="incrementOdd">当前求和为奇数再加</button> 		<button @click="incrementWait">等一等再加</button> 	</div> </template>  <script> 	export default { 		name:'Count', 		data() { 			return { 				n:1, //用户选择的数字 			} 		}, 		methods: { 			increment(){ 				this.$store.commit('JIA',this.n) 			}, 			decrement(){ 				this.$store.commit('JIAN',this.n) 			}, 			incrementOdd(){ 				this.$store.dispatch('jiaOdd',this.n) 			}, 			incrementWait(){ 				this.$store.dispatch('jiaWait',this.n) 			}, 		}, 		mounted() { 			console.log('Count',this) 		}, 	} </script>  <style lang="css"> 	button{ 		margin-left: 5px; 	} </style>  
  • main.js
//引入Vue import Vue from 'vue' //引入App import App from './App.vue' //引入插件 import vueResource from 'vue-resource' //引入store import store from './store'  //关闭Vue的生产提示 Vue.config.productionTip = false //使用插件 Vue.use(vueResource)  //创建vm new Vue({ 	el:'#app', 	render: h => h(App), 	store, 	beforeCreate() { 		Vue.prototype.$bus = this 	} })  

1.4、getters的使用

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。

  2. store.js中追加getters配置

    ......  const getters = { 	bigSum(state){ 		return state.sum * 10 	} }  //创建并暴露store export default new Vuex.Store({ 	...... 	getters }) 
  3. 组件中读取数据:$store.getters.bigSum

求和案例

  • Count.vue
<template> 	<div> 		<h1>当前求和为:{{$store.state.sum}}</h1> 		<h3>当前求和放大10倍为:{{$store.getters.bigSum}}</h3> 		<select v-model.number="n"> 			<option value="1">1</option> 			<option value="2">2</option> 			<option value="3">3</option> 		</select> 		<button @click="increment">+</button> 		<button @click="decrement">-</button> 		<button @click="incrementOdd">当前求和为奇数再加</button> 		<button @click="incrementWait">等一等再加</button> 	</div> </template>  <script> 	export default { 		name:'Count', 		data() { 			return { 				n:1, //用户选择的数字 			} 		}, 		methods: { 			increment(){ 				this.$store.commit('JIA',this.n) 			}, 			decrement(){ 				this.$store.commit('JIAN',this.n) 			}, 			incrementOdd(){ 				this.$store.dispatch('jiaOdd',this.n) 			}, 			incrementWait(){ 				this.$store.dispatch('jiaWait',this.n) 			}, 		}, 		mounted() { 			console.log('Count',this.$store) 		}, 	} </script>  <style lang="css"> 	button{ 		margin-left: 5px; 	} </style>  
  • store/index.js
//该文件用于创建Vuex中最为核心的store import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex插件 Vue.use(Vuex)  //准备actions——用于响应组件中的动作 const actions = { 	/* jia(context,value){ 		console.log('actions中的jia被调用了') 		context.commit('JIA',value) 	}, 	jian(context,value){ 		console.log('actions中的jian被调用了') 		context.commit('JIAN',value) 	}, */ 	jiaOdd(context,value){ 		console.log('actions中的jiaOdd被调用了') 		if(context.state.sum % 2){ 			context.commit('JIA',value) 		} 	}, 	jiaWait(context,value){ 		console.log('actions中的jiaWait被调用了') 		setTimeout(()=>{ 			context.commit('JIA',value) 		},500) 	} } //准备mutations——用于操作数据(state) const mutations = { 	JIA(state,value){ 		console.log('mutations中的JIA被调用了') 		state.sum += value 	}, 	JIAN(state,value){ 		console.log('mutations中的JIAN被调用了') 		state.sum -= value 	} } //准备state——用于存储数据 const state = { 	sum:0 //当前的和 } //准备getters——用于将state中的数据进行加工 const getters = { 	bigSum(state){ 		return state.sum*10 	} }  //创建并暴露store export default new Vuex.Store({ 	actions, 	mutations, 	state, 	getters }) 
  • App.vue
<template> 	<div> 		<Count/> 	</div> </template>  <script> 	import Count from './components/Count' 	export default { 		name:'App', 		components:{Count}, 		mounted() { 			// console.log('App',this) 		}, 	} </script>  
  • main.js
//引入Vue import Vue from 'vue' //引入App import App from './App.vue' //引入插件 import vueResource from 'vue-resource' //引入store import store from './store'  //关闭Vue的生产提示 Vue.config.productionTip = false //使用插件 Vue.use(vueResource)  //创建vm new Vue({ 	el:'#app', 	render: h => h(App), 	store, 	beforeCreate() { 		Vue.prototype.$bus = this 	} })  

1.5、四个map方法的使用

  1. mapState方法:用于帮助我们映射state中的数据为计算属性

    computed: {     //借助mapState生成计算属性:sum、school、subject(对象写法)      ...mapState({sum:'sum',school:'school',subject:'subject'}),               //借助mapState生成计算属性:sum、school、subject(数组写法)     ...mapState(['sum','school','subject']), }, 
  2. mapGetters方法:用于帮助我们映射getters中的数据为计算属性

    computed: {     //借助mapGetters生成计算属性:bigSum(对象写法)     ...mapGetters({bigSum:'bigSum'}),      //借助mapGetters生成计算属性:bigSum(数组写法)     ...mapGetters(['bigSum']) }, 
  3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    methods:{     //靠mapActions生成:incrementOdd、incrementWait(对象形式)     ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})      //靠mapActions生成:incrementOdd、incrementWait(数组形式)     ...mapActions(['jiaOdd','jiaWait']) } 
  4. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

    methods:{     //靠mapActions生成:increment、decrement(对象形式)     ...mapMutations({increment:'JIA',decrement:'JIAN'}),          //靠mapMutations生成:JIA、JIAN(对象形式)     ...mapMutations(['JIA','JIAN']), } 

备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

求和案例_mapState与mapGetters

  • Count.vue
<template> 	<div> 		<h1>当前求和为:{{sum}}</h1> 		<h3>当前求和放大10倍为:{{bigSum}}</h3> 		<h3>我在{{school}},学习{{subject}}</h3> 		<select v-model.number="n"> 			<option value="1">1</option> 			<option value="2">2</option> 			<option value="3">3</option> 		</select> 		<button @click="increment">+</button> 		<button @click="decrement">-</button> 		<button @click="incrementOdd">当前求和为奇数再加</button> 		<button @click="incrementWait">等一等再加</button> 	</div> </template>  <script> 	import {mapState,mapGetters} from 'vuex' 	export default { 		name:'Count', 		data() { 			return { 				n:1, //用户选择的数字 			} 		}, 		computed:{ 			//靠程序员自己亲自去写计算属性 			/* sum(){ 				return this.$store.state.sum 			}, 			school(){ 				return this.$store.state.school 			}, 			subject(){ 				return this.$store.state.subject 			}, */  			//借助mapState生成计算属性,从state中读取数据。(对象写法) 			// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),  			//借助mapState生成计算属性,从state中读取数据。(数组写法) 			...mapState(['sum','school','subject']),  			/* ******************************************************************** */  			/* bigSum(){ 				return this.$store.getters.bigSum 			}, */  			//借助mapGetters生成计算属性,从getters中读取数据。(对象写法) 			// ...mapGetters({bigSum:'bigSum'}) 			 			//借助mapGetters生成计算属性,从getters中读取数据。(数组写法) 			...mapGetters(['bigSum'])  		}, 		methods: { 			increment(){ 				this.$store.commit('JIA',this.n) 			}, 			decrement(){ 				this.$store.commit('JIAN',this.n) 			}, 			incrementOdd(){ 				this.$store.dispatch('jiaOdd',this.n) 			}, 			incrementWait(){ 				this.$store.dispatch('jiaWait',this.n) 			}, 		}, 		mounted() { 			const x = mapState({he:'sum',xuexiao:'school',xueke:'subject'}) 			console.log(x) 		}, 	} </script>  <style lang="css"> 	button{ 		margin-left: 5px; 	} </style>  
  • store/index.js
//该文件用于创建Vuex中最为核心的store import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex插件 Vue.use(Vuex)  //准备actions——用于响应组件中的动作 const actions = { 	/* jia(context,value){ 		console.log('actions中的jia被调用了') 		context.commit('JIA',value) 	}, 	jian(context,value){ 		console.log('actions中的jian被调用了') 		context.commit('JIAN',value) 	}, */ 	jiaOdd(context,value){ 		console.log('actions中的jiaOdd被调用了') 		if(context.state.sum % 2){ 			context.commit('JIA',value) 		} 	}, 	jiaWait(context,value){ 		console.log('actions中的jiaWait被调用了') 		setTimeout(()=>{ 			context.commit('JIA',value) 		},500) 	} } //准备mutations——用于操作数据(state) const mutations = { 	JIA(state,value){ 		console.log('mutations中的JIA被调用了') 		state.sum += value 	}, 	JIAN(state,value){ 		console.log('mutations中的JIAN被调用了') 		state.sum -= value 	} } //准备state——用于存储数据 const state = { 	sum:0, //当前的和 	school:'尚硅谷', 	subject:'前端' } //准备getters——用于将state中的数据进行加工 const getters = { 	bigSum(state){ 		return state.sum*10 	} }  //创建并暴露store export default new Vuex.Store({ 	actions, 	mutations, 	state, 	getters }) 
  • App.vue
<template> 	<div> 		<Count/> 	</div> </template>  <script> 	import Count from './components/Count' 	export default { 		name:'App', 		components:{Count}, 		mounted() { 			// console.log('App',this) 		}, 	} </script>  
  • main.js
//引入Vue import Vue from 'vue' //引入App import App from './App.vue' //引入插件 import vueResource from 'vue-resource' //引入store import store from './store'  //关闭Vue的生产提示 Vue.config.productionTip = false //使用插件 Vue.use(vueResource)  //创建vm new Vue({ 	el:'#app', 	render: h => h(App), 	store, 	beforeCreate() { 		Vue.prototype.$bus = this 	} })  

求和案例_mapMutations与mapActions

  • Count.vue
<template> 	<div> 		<h1>当前求和为:{{sum}}</h1> 		<h3>当前求和放大10倍为:{{bigSum}}</h3> 		<h3>我在{{school}},学习{{subject}}</h3> 		<select v-model.number="n"> 			<option value="1">1</option> 			<option value="2">2</option> 			<option value="3">3</option> 		</select> 		<button @click="increment(n)">+</button> 		<button @click="decrement(n)">-</button> 		<button @click="incrementOdd(n)">当前求和为奇数再加</button> 		<button @click="incrementWait(n)">等一等再加</button> 	</div> </template>  <script> 	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex' 	export default { 		name:'Count', 		data() { 			return { 				n:1, //用户选择的数字 			} 		}, 		computed:{ 			//借助mapState生成计算属性,从state中读取数据。(对象写法) 			// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),  			//借助mapState生成计算属性,从state中读取数据。(数组写法) 			...mapState(['sum','school','subject']),  			/* ******************************************************************** */  			//借助mapGetters生成计算属性,从getters中读取数据。(对象写法) 			// ...mapGetters({bigSum:'bigSum'}) 			 			//借助mapGetters生成计算属性,从getters中读取数据。(数组写法) 			...mapGetters(['bigSum'])  		}, 		methods: { 			//程序员亲自写方法 			/* increment(){ 				this.$store.commit('JIA',this.n) 			}, 			decrement(){ 				this.$store.commit('JIAN',this.n) 			}, */  			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) 			...mapMutations({increment:'JIA',decrement:'JIAN'}),  			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法) 			// ...mapMutations(['JIA','JIAN']),  			/* ************************************************* */  			//程序员亲自写方法 			/* incrementOdd(){ 				this.$store.dispatch('jiaOdd',this.n) 			}, 			incrementWait(){ 				this.$store.dispatch('jiaWait',this.n) 			}, */  			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法) 			...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})  			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法) 			// ...mapActions(['jiaOdd','jiaWait']) 		}, 		mounted() { 			const x = mapState({he:'sum',xuexiao:'school',xueke:'subject'}) 			console.log(x) 		}, 	} </script>  <style lang="css"> 	button{ 		margin-left: 5px; 	} </style>  
  • store/index.js
//该文件用于创建Vuex中最为核心的store import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex插件 Vue.use(Vuex)  //准备actions——用于响应组件中的动作 const actions = { 	/* jia(context,value){ 		console.log('actions中的jia被调用了') 		context.commit('JIA',value) 	}, 	jian(context,value){ 		console.log('actions中的jian被调用了') 		context.commit('JIAN',value) 	}, */ 	jiaOdd(context,value){ 		console.log('actions中的jiaOdd被调用了') 		if(context.state.sum % 2){ 			context.commit('JIA',value) 		} 	}, 	jiaWait(context,value){ 		console.log('actions中的jiaWait被调用了') 		setTimeout(()=>{ 			context.commit('JIA',value) 		},500) 	} } //准备mutations——用于操作数据(state) const mutations = { 	JIA(state,value){ 		console.log('mutations中的JIA被调用了') 		state.sum += value 	}, 	JIAN(state,value){ 		console.log('mutations中的JIAN被调用了') 		state.sum -= value 	} } //准备state——用于存储数据 const state = { 	sum:0, //当前的和 	school:'尚硅谷', 	subject:'前端' } //准备getters——用于将state中的数据进行加工 const getters = { 	bigSum(state){ 		return state.sum*10 	} }  //创建并暴露store export default new Vuex.Store({ 	actions, 	mutations, 	state, 	getters }) 
  • App.vue
<template> 	<div> 		<Count/> 	</div> </template>  <script> 	import Count from './components/Count' 	export default { 		name:'App', 		components:{Count}, 		mounted() { 			// console.log('App',this) 		}, 	} </script>  
  • main.js
//引入Vue import Vue from 'vue' //引入App import App from './App.vue' //引入插件 import vueResource from 'vue-resource' //引入store import store from './store'  //关闭Vue的生产提示 Vue.config.productionTip = false //使用插件 Vue.use(vueResource)  //创建vm new Vue({ 	el:'#app', 	render: h => h(App), 	store, 	beforeCreate() { 		Vue.prototype.$bus = this 	} })  

1.6、多组件共享数据

  • Count.vue
<template> 	<div> 		<h1>当前求和为:{{sum}}</h1> 		<h3>当前求和放大10倍为:{{bigSum}}</h3> 		<h3>我在{{school}},学习{{subject}}</h3> 		<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3> 		<select v-model.number="n"> 			<option value="1">1</option> 			<option value="2">2</option> 			<option value="3">3</option> 		</select> 		<button @click="increment(n)">+</button> 		<button @click="decrement(n)">-</button> 		<button @click="incrementOdd(n)">当前求和为奇数再加</button> 		<button @click="incrementWait(n)">等一等再加</button> 	</div> </template>  <script> 	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex' 	export default { 		name:'Count', 		data() { 			return { 				n:1, //用户选择的数字 			} 		}, 		computed:{ 			//借助mapState生成计算属性,从state中读取数据。(数组写法) 			...mapState(['sum','school','subject','personList']), 			//借助mapGetters生成计算属性,从getters中读取数据。(数组写法) 			...mapGetters(['bigSum']) 		}, 		methods: { 			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) 			...mapMutations({increment:'JIA',decrement:'JIAN'}), 			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法) 			...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) 		}, 		mounted() { 			// const x = mapState({he:'sum',xuexiao:'school',xueke:'subject'}) 			// console.log(x) 		}, 	} </script>  <style lang="css"> 	button{ 		margin-left: 5px; 	} </style>  
  • Persion.vue
<template> 	<div> 		<h1>人员列表</h1> 		<h3 style="color:red">Count组件求和为:{{sum}}</h3> 		<input type="text" placeholder="请输入名字" v-model="name"> 		<button @click="add">添加</button> 		<ul> 			<li v-for="p in personList" :key="p.id">{{p.name}}</li> 		</ul> 	</div> </template>  <script> 	import {nanoid} from 'nanoid' 	export default { 		name:'Person', 		data() { 			return { 				name:'' 			} 		}, 		computed:{ 			personList(){ 				return this.$store.state.personList 			}, 			sum(){ 				return this.$store.state.sum 			} 		}, 		methods: { 			add(){ 				const personObj = {id:nanoid(),name:this.name} 				this.$store.commit('ADD_PERSON',personObj) 				this.name = '' 			} 		}, 	} </script>  
  • App.vue
<template> 	<div> 		<Count/> 		<hr> 		<Person/> 	</div> </template>  <script> 	import Count from './components/Count' 	import Person from './components/Person'  	export default { 		name:'App', 		components:{Count,Person}, 		mounted() { 			// console.log('App',this) 		}, 	} </script>  
  • store/index.js
//该文件用于创建Vuex中最为核心的store import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex插件 Vue.use(Vuex)  //准备actions——用于响应组件中的动作 const actions = { 	/* jia(context,value){ 		console.log('actions中的jia被调用了') 		context.commit('JIA',value) 	}, 	jian(context,value){ 		console.log('actions中的jian被调用了') 		context.commit('JIAN',value) 	}, */ 	jiaOdd(context,value){ 		console.log('actions中的jiaOdd被调用了') 		if(context.state.sum % 2){ 			context.commit('JIA',value) 		} 	}, 	jiaWait(context,value){ 		console.log('actions中的jiaWait被调用了') 		setTimeout(()=>{ 			context.commit('JIA',value) 		},500) 	} } //准备mutations——用于操作数据(state) const mutations = { 	JIA(state,value){ 		console.log('mutations中的JIA被调用了') 		state.sum += value 	}, 	JIAN(state,value){ 		console.log('mutations中的JIAN被调用了') 		state.sum -= value 	}, 	ADD_PERSON(state,value){ 		console.log('mutations中的ADD_PERSON被调用了') 		state.personList.unshift(value) 	} } //准备state——用于存储数据 const state = { 	sum:0, //当前的和 	school:'尚硅谷', 	subject:'前端', 	personList:[ 		{id:'001',name:'张三'} 	] } //准备getters——用于将state中的数据进行加工 const getters = { 	bigSum(state){ 		return state.sum*10 	} }  //创建并暴露store export default new Vuex.Store({ 	actions, 	mutations, 	state, 	getters }) 
  • main.js
//引入Vue import Vue from 'vue' //引入App import App from './App.vue' //引入插件 import vueResource from 'vue-resource' //引入store import store from './store'  //关闭Vue的生产提示 Vue.config.productionTip = false //使用插件 Vue.use(vueResource)  //创建vm new Vue({ 	el:'#app', 	render: h => h(App), 	store, 	beforeCreate() { 		Vue.prototype.$bus = this 	} })  

1.7、vuex模块化

  1. 目的:让代码更好维护,让多种数据分类更加明确。

  2. 修改store.js

    const countAbout = {   namespaced:true,//开启命名空间   state:{x:1},   mutations: { ... },   actions: { ... },   getters: {     bigSum(state){        return state.sum * 10     }   } }  const personAbout = {   namespaced:true,//开启命名空间   state:{ ... },   mutations: { ... },   actions: { ... } }  const store = new Vuex.Store({   modules: {     countAbout,     personAbout   } }) 
  3. 开启命名空间后,组件中读取state数据:

    //方式一:自己直接读取 this.$store.state.personAbout.list //方式二:借助mapState读取: ...mapState('countAbout',['sum','school','subject']), 
  4. 开启命名空间后,组件中读取getters数据:

    //方式一:自己直接读取 this.$store.getters['personAbout/firstPersonName'] //方式二:借助mapGetters读取: ...mapGetters('countAbout',['bigSum']) 
  5. 开启命名空间后,组件中调用dispatch

    //方式一:自己直接dispatch this.$store.dispatch('personAbout/addPersonWang',person) //方式二:借助mapActions: ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) 
  6. 开启命名空间后,组件中调用commit

    //方式一:自己直接commit this.$store.commit('personAbout/ADD_PERSON',person) //方式二:借助mapMutations: ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}), 

6、路由

  1. 理解: 一个路由(route)就是一组映射关系(key - value)多个路由需要路由器(router)进行管理。
  2. 前端路由:key是路径,value是组件。

1.基本使用

  1. 安装vue-router,命令:npm i vue-router

  2. 应用插件:Vue.use(VueRouter)

  3. 编写router配置项:

    //引入VueRouter import VueRouter from 'vue-router' //引入Luyou 组件 import About from '../components/About' import Home from '../components/Home'  //创建router实例对象,去管理一组一组的路由规则 const router = new VueRouter({ 	routes:[ 		{ 			path:'/about', 			component:About 		}, 		{ 			path:'/home', 			component:Home 		} 	] })  //暴露router export default router 
  4. 实现切换(active-class可配置高亮样式)

    <router-link active-class="active" to="/about">About</router-link> 
  5. 指定展示位置

    <router-view></router-view> 

2.几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

3.多级路由(多级路由)

  1. 配置路由规则,使用children配置项:

    routes:[ 	{ 		path:'/about', 		component:About, 	}, 	{ 		path:'/home', 		component:Home, 		children:[ //通过children配置子级路由 			{ 				path:'news', //此处一定不要写:/news 				component:News 			}, 			{ 				path:'message',//此处一定不要写:/message 				component:Message 			} 		] 	} ] 
  2. 跳转(要写完整路径):

    <router-link to="/home/news">News</router-link> 
  • componets/Banner.vue
<template>   <div class="col-xs-offset-2 col-xs-8">         <div class="page-header"><h2>Vue Router Demo</h2></div>       </div> </template>  <script> export default {  } </script>   
  • pages/About.vue
<template> 	<h2>我是About的内容</h2> </template>  <script> 	export default { 		name:'About', 		beforeDestroy(){ 			console.log('About组件即将销毁'); 		}, 		mounted(){ 			console.log('About组件挂载完毕了',this); 		} 	} </script> 
  • pages/Home.vue
<template>   <div>     <h2>Home组件内容</h2>     <div>       <ul class="nav nav-tabs">         <li>           <router-link             class="list-group-item"             active-class="active"             to="/home/news"             >News</router-link           >         </li>         <li>           <router-link             class="list-group-item"             active-class="active"             to="/home/message"             >Message</router-link           >         </li>       </ul>       <router-view></router-view>     </div>   </div> </template>  <script> export default {   name: "Home",   beforeDestroy() {     console.log("Home组件即将销毁");   },   mounted() {     console.log("Home组件挂载完毕了", this);   }, }; </script> 
  • pages/Message.vue
<template>   <div>     <ul>       <li><a href="/message1">message001</a>&nbsp;&nbsp;</li>       <li><a href="/message2">message002</a>&nbsp;&nbsp;</li>       <li><a href="/message/3">message003</a>&nbsp;&nbsp;</li>     </ul>   </div> </template>  <script> export default {   name: "Message", }; </script>  <style> </style> 
  • componets/News.vue
<template>   <div>     <ul>       <li>news001</li>       <li>news002</li>       <li>news003</li>     </ul>   </div> </template>  <script> export default {   name: "News", }; </script>  <style> </style> 
  • router/index.js
// 该文件专门用于创建整个应用的路由器 import VueRouter from 'vue-router' //引入组件 import About from '../pages/About' import Home from '../pages/Home' import Message from '../pages/Message' import News from '../pages/News'  //创建并暴露一个路由器 export default new VueRouter({ 	routes: [ 		{ 			path: '/about', 			component: About 		}, 		{ 			path: '/home', 			component: Home, 			children: [ 				{ 					path: 'news', 					component: News, 				}, 				{ 					path:'message', 					component:Message 				} 			] 		} 	] })  
  • App.vue
<template>   <div>     <div class="row">       <Banner/>     </div>     <div class="row">       <div class="col-xs-2 col-xs-offset-2">         <div class="list-group"> 					<!-- 原始html中我们使用a标签实现页面的跳转 -->           <!-- <a class="list-group-item active" href="./about.html">About</a> -->           <!-- <a class="list-group-item" href="./home.html">Home</a> -->  					<!-- Vue中借助router-link标签实现路由的切换 --> 					<router-link class="list-group-item" active-class="active" to="/about">About</router-link>           <router-link class="list-group-item" active-class="active" to="/home">Home</router-link>         </div>       </div>       <div class="col-xs-6">         <div class="panel">           <div class="panel-body"> 						<!-- 指定组件的呈现位置 -->             <router-view></router-view>           </div>         </div>       </div>     </div>   </div> </template>  <script> import Banner from './components/Banner.vue' 	export default { 		name:'App',     components:{Banner}, 	} </script>  
  • main.js
//引入Vue import Vue from 'vue' //引入App import App from './App.vue' //引入VueRouter import VueRouter from 'vue-router' //引入路由器 import router from './router'  //关闭Vue的生产提示 Vue.config.productionTip = false //应用插件 Vue.use(VueRouter)  //创建vm new Vue({ 	el:'#app', 	render: h => h(App), 	router:router })  

4.路由的query参数

  1. 传递参数

    <!-- 跳转并携带query参数,to的字符串写法 --> <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link> 				 <!-- 跳转并携带query参数,to的对象写法 --> <router-link  	:to="{ 		path:'/home/message/detail', 		query:{ 		   id:666,             title:'你好' 		} 	}" >跳转</router-link> 
  2. 接收参数:

    $route.query.id $route.query.title 

5.命名路由

  1. 作用:可以简化路由的跳转。

  2. 如何使用

    1. 给路由命名:

      { 	path:'/demo', 	component:Demo, 	children:[ 		{ 			path:'test', 			component:Test, 			children:[ 				{                      name:'hello' //给路由命名 					path:'welcome', 					component:Hello, 				} 			] 		} 	] } 
    2. 简化跳转:

      <!--简化前,需要写完整的路径 --> <router-link to="/demo/test/welcome">跳转</router-link>  <!--简化后,直接通过名字跳转 --> <router-link :to="{name:'hello'}">跳转</router-link>  <!--简化写法配合传递参数 --> <router-link  	:to="{ 		name:'hello', 		query:{ 		   id:666,             title:'你好' 		} 	}" >跳转</router-link> 

6.路由的params参数

  1. 配置路由,声明接收params参数

    { 	path:'/home', 	component:Home, 	children:[ 		{ 			path:'news', 			component:News 		}, 		{ 			component:Message, 			children:[ 				{ 					name:'xiangqing', 					path:'detail/:id/:title', //使用占位符声明接收params参数 					component:Detail 				} 			] 		} 	] } 
  2. 传递参数

    <!-- 跳转并携带params参数,to的字符串写法 --> <router-link :to="/home/message/detail/666/你好">跳转</router-link> 				 <!-- 跳转并携带params参数,to的对象写法 --> <router-link  	:to="{ 		name:'xiangqing', 		params:{ 		   id:666,             title:'你好' 		} 	}" >跳转</router-link> 

    特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  3. 接收参数:

    $route.params.id $route.params.title 

7.路由的props配置

​ 作用:让路由组件更方便的收到参数

{ 	name:'xiangqing', 	path:'detail/:id', 	component:Detail,  	//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件 	props:{a:900}  	//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件 	props:true 	 	//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件 	props(route){ 		return { 			id:route.query.id, 			title:route.query.title 		} 	} } 

8.<router-link>的replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace .......>News</router-link>

9.编程式路由导航

  1. 作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    //$router的两个API this.$router.push({ 	name:'xiangqing', 		params:{ 			id:xxx, 			title:xxx 		} })  this.$router.replace({ 	name:'xiangqing', 		params:{ 			id:xxx, 			title:xxx 		} })  this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退 

10.缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。

  2. 具体编码:

    <keep-alive include="News">      <router-view></router-view> </keep-alive> 

11.两个新的生命周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  2. 具体名字:
    1. activated路由组件被激活时触发。
    2. deactivated路由组件失活时触发。

12.路由守卫

  1. 作用:对路由进行权限控制

  2. 分类:全局守卫、独享守卫、组件内守卫

  3. 全局守卫:

    //全局前置守卫:初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ 	console.log('beforeEach',to,from) 	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 		if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则 			next() //放行 		}else{ 			alert('暂无权限查看') 			// next({name:'guanyu'}) 		} 	}else{ 		next() //放行 	} })  //全局后置守卫:初始化时执行、每次路由切换后执行 router.afterEach((to,from)=>{ 	console.log('afterEach',to,from) 	if(to.meta.title){  		document.title = to.meta.title //修改网页的title 	}else{ 		document.title = 'vue_test' 	} }) 
    • router/index.js
    // 该文件专门用于创建整个应用的路由器 import VueRouter from 'vue-router' //引入组件 import About from '../pages/About' import Home from '../pages/Home' import News from '../pages/News' import Message from '../pages/Message' import Detail from '../pages/Detail'  //创建并暴露一个路由器 const router =  new VueRouter({ 	routes:[ 		{ 			name:'guanyu', 			path:'/about', 			component:About, 			meta:{title:'关于'} 		}, 		{ 			name:'zhuye', 			path:'/home', 			component:Home, 			meta:{title:'主页'}, 			children:[ 				{ 					name:'xinwen', 					path:'news', 					component:News, 					meta:{isAuth:true,title:'新闻'} 				}, 				{ 					name:'xiaoxi', 					path:'message', 					component:Message, 					meta:{isAuth:true,title:'消息'}, 					children:[ 						{ 							name:'xiangqing', 							path:'detail', 							component:Detail, 							meta:{isAuth:true,title:'详情'},  							//props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件。 							// props:{a:1,b:'hello'}  							//props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件。 							// props:true  							//props的第三种写法,值为函数 							props($route){ 								return { 									id:$route.query.id, 									title:$route.query.title, 									a:1, 									b:'hello' 								} 							}  						} 					] 				} 			] 		} 	] })  //全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用 router.beforeEach((to,from,next)=>{ 	console.log('前置路由守卫',to,from) 	if(to.meta.isAuth){ //判断是否需要鉴权 		if(localStorage.getItem('school')==='atguigu'){ 			next() 		}else{ 			alert('学校名不对,无权限查看!') 		} 	}else{ 		next() 	} })  //全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用 router.afterEach((to,from)=>{ 	console.log('后置路由守卫',to,from) 	document.title = to.meta.title || '硅谷系统' })  export default router  
  4. 独享守卫:

    beforeEnter(to,from,next){ 	console.log('beforeEnter',to,from) 	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 		if(localStorage.getItem('school') === 'atguigu'){ 			next() 		}else{ 			alert('暂无权限查看') 			// next({name:'guanyu'}) 		} 	}else{ 		next() 	} } 
  5. 组件内守卫:

    //进入守卫:通过路由规则,进入该组件时被调用 beforeRouteEnter (to, from, next) { }, //离开守卫:通过路由规则,离开该组件时被调用 beforeRouteLeave (to, from, next) { } 
  • About.vue
<template> 	<h2>我是About的内容</h2> </template>  <script> 	export default { 		name:'About', 		/* beforeDestroy() { 			console.log('About组件即将被销毁了') 		},*/ 		/* mounted() { 			console.log('About组件挂载完毕了',this) 			window.aboutRoute = this.$route 			window.aboutRouter = this.$router 		},  */ 		mounted() { 			// console.log('%%%',this.$route) 		},  		//通过路由规则,进入该组件时被调用 		beforeRouteEnter (to, from, next) { 			console.log('About--beforeRouteEnter',to,from) 			if(to.meta.isAuth){ //判断是否需要鉴权 				if(localStorage.getItem('school')==='atguigu'){ 					next() 				}else{ 					alert('学校名不对,无权限查看!') 				} 			}else{ 				next() 			} 		},  		//通过路由规则,离开该组件时被调用 		beforeRouteLeave (to, from, next) { 			console.log('About--beforeRouteLeave',to,from) 			next() 		} 	} </script> 

13.路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。

  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。

  3. hash模式:

    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  4. history模式:

    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
  5. 项目打包

打包后不能查看页面,需要部署在服务器上才能看页面内容

 npm run build 

7、element-ui

网站快速成型工具

安装element-ui

npm i element-ui 

完整引入

在 main.js 中写入以下内容:

//引入Vue import Vue from 'vue' //引入App import App from './App.vue' //引入elementUi组件库 import ElementUI from 'element-ui'; //引入elementUi全部样式 import 'element-ui/lib/theme-chalk/index.css';   //关闭Vue的生产提示 Vue.config.productionTip = false  //应用element UI组件 Vue.use(ElementUI);  //创建vm new Vue({ 	el:'#app', 	render: h => h(App),  })  

按需引入

npm install babel-plugin-component -D 

babel.config.js文件中追加以下代码

module.exports = {   presets: [     ["es2015", { "modules": false }],     ],   plugins: [     [       "component",       {         "libraryName": "element-ui",         "styleLibraryName": "theme-chalk"       }     ]   ] }  

main.js中写入以下代码

import Vue from 'vue'; import { Button, Select } from 'element-ui'; import App from './App.vue';  Vue.component(Button.name, Button); Vue.component(Select.name, Select); /* 或写为  * Vue.use(Button)  * Vue.use(Select)  */  new Vue({   el: '#app',   render: h => h(App) }); 

具体查看官网

8、Vue3快速上手

Vue.js

1.Vue3简介

2.Vue3带来了什么

1.性能的提升

  • 打包大小减少41%

  • 初次渲染快55%, 更新渲染快133%

  • 内存减少54%

    ......

2.源码的升级

  • 使用Proxy代替defineProperty实现响应式

  • 重写虚拟DOM的实现和Tree-Shaking

    ......

3.拥抱TypeScript

  • Vue3可以更好的支持TypeScript

4.新的特性

  1. Composition API(组合API)

    • setup配置
    • ref与reactive
    • watch与watchEffect
    • provide与inject
    • ......
  2. 新的内置组件

    • Fragment
    • Teleport
    • Suspense
  3. 其他改变

    • 新的生命周期钩子
    • data 选项应始终被声明为一个函数
    • 移除keyCode支持作为 v-on 的修饰符
    • ......

一、创建Vue3.0工程

1.使用 vue-cli 创建

官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上 vue --version ## 安装或者升级你的@vue/cli npm install -g @vue/cli ## 创建 vue create vue_test ## 启动 cd vue_test npm run serve 

2.使用 vite 创建

官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite

vite官网:https://vitejs.cn

  • 什么是vite?—— 下一代前端构建工具。
  • 优势如下:
    • 开发环境中,无需打包操作,可快速的冷启动。
    • 轻量快速的热重载(HMR)。
    • 真正的按需编译,不再等待整个应用编译完成。
## 创建工程 npm init vite-app <project-name> ## 进入工程目录 cd <project-name> ## 安装依赖 npm install ## 运行 npm run dev 

二、常用 Composition API

官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html

1.拉开序幕的setup

  1. 理解:Vue3.0中一个新的配置项,值为一个函数。
  2. setup是所有Composition API(组合API)“ 表演的舞台 ”
  3. 组件中所用到的:数据、方法等等,均要配置在setup中。
  4. setup函数的两种返回值:
    1. 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
    2. 若返回一个渲染函数:则可以自定义渲染内容。(了解)
  5. 注意点:
    1. 尽量不要与Vue2.x配置混用
      • Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。
      • 但在setup中不能访问到Vue2.x配置(data、methos、computed...)。
      • 如果有重名, setup优先。
    2. setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
<template>   <!-- Vue3中的模板结构可以没有div标签 -->   <h1>一个人的信息</h1>   <h2>姓名:{{ name }}</h2>   <h2>年龄:{{ age }}</h2>   <h2>性别:{{ sex }}</h2>   <button @click="sayHello">你好(vue3配置的-sayHello)</button>   <br />   <br />   <button @click="sayWelcome">欢迎(vue2配置的-sayWelcome)</button>   <br />   <br />   <button @click="test1">测试一下在Vue2的配置中去读取Vue3中的数据、方法</button>   <br />   <br />   <button @click="test2">测试以下在Vue的sutup中读取Vue2的数据、方法</button> </template>  <script> // import {h} from 'vue' export default {   name: "App",   data() {     return {       sex: "男",     };   },   methods: {     sayWelcome() {       alert("欢迎学习Vue3");     },     test1() {       console.log(this.sex);       console.log(this.name);       console.log(this.age);       console.log(this.sayHello);       this.sayHello();     },   },   setup() {     //此处只是测试一个setup,     //数据     let name = "张三";     let age = 18;      //方法     function sayHello() {       alert(`我叫${name},我今年${age}岁了`);     }     function test2() {         console.log(name);       console.log(age);       console.log(sayWelcome);       console.log(this.sex);            this.sayWelcome();     }      //返回一个对象(常用)     return {       name,       age,       sayHello,       test2     };      //返回一个函数(渲染函数)     // return ()=> h('h1','北京')   }, }; </script>   

2.ref函数

  • 作用: 定义一个响应式的数据
  • 语法: const xxx = ref(initValue)
    • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
    • JS中操作数据: xxx.value
    • 模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>
  • 备注:
    • 接收的数据可以是:基本类型、也可以是对象类型。
    • 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的。
    • 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。
<template>   <!-- Vue3中的模板结构可以没有div标签 -->   <h1>一个人的信息</h1>   <h2>姓名:{{ name }}</h2>   <h2>年龄:{{ age }}</h2>   <h3>工作种类:{{job.type}}</h3>   <h3>工作薪水:{{job.salary}}</h3>   <button @click="changeInfo">修改人的信息</button> </template>  <script> import {ref} from 'vue' export default {   name: "App",   setup() {     let name = ref("张三");     let age = ref(18);     let job = ref({       type:'前端工程师',       salary:'30k'     })     function changeInfo(){       name.value='李四'       age.value=48       job.value.type= 'UI设计师'        job.value.salary= '60k'      }      return {       name,       age,       changeInfo,       job     };       }, }; </script>   

3.reactive函数

  • 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reactive定义的响应式数据是“深层次的”。
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
<template>   <!-- Vue3中的模板结构可以没有div标签 -->   <h1>一个人的信息</h1>   <h2>姓名:{{ person.name }}</h2>   <h2>年龄:{{ person.age }}</h2>   <h3>工作种类:{{ person.job.type }}</h3>   <h3>工作薪水:{{ person.job.salary }}</h3>   <h3>爱好:{{ person.hobby }}</h3>   <h3>测试的数据:{{ person.job.a.b.c }}</h3>   <button @click="changeInfo">修改人的信息</button> </template>  <script> import {reactive } from "vue"; export default {   name: "App",   setup() {     let person = reactive({       name: "张三",       age: 18,       job: {         type: "前端工程师",         salary: "30k",         a: {           b: {             c: 666,           },         },       },       hobby:['抽烟','喝酒','烫头']     });      function changeInfo() {       person.name = "李四";       person.age = 48;       person.job.type = "UI设计师";       person.job.salary = "60k";       person.job.a.b.c = 999;       person.hobby[0] = "Python";     }      return {       changeInfo,       person,     };   }, }; </script>   

4.Vue3.0中的响应式原理

vue2.x的响应式

  • 实现原理:

    • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。

    • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

      Object.defineProperty(data, 'count', {     get () {},      set () {} }) 
  • 存在问题:

    • 新增属性、删除属性, 界面不会更新。
    • 直接通过下标修改数组, 界面不会自动更新。

Vue3.0的响应式

<!DOCTYPE html> <html> 	<head> 		<meta charset="UTF-8" /> 		<title>Document</title> 	</head> 	<body> 		<script type="text/javascript" > 			//源数据 			let person = { 				name:'张三', 				age:18 			}  			//模拟Vue2中实现响应式 			//#region  			/* let p = {} 			Object.defineProperty(p,'name',{ 				configurable:true, 				get(){ //有人读取name时调用 					return person.name 				}, 				set(value){ //有人修改name时调用 					console.log('有人修改了name属性,我发现了,我要去更新界面!') 					person.name = value 				} 			}) 			Object.defineProperty(p,'age',{ 				get(){ //有人读取age时调用 					return person.age 				}, 				set(value){ //有人修改age时调用 					console.log('有人修改了age属性,我发现了,我要去更新界面!') 					person.age = value 				} 			}) */ 			//#endregion 			 			//模拟Vue3中实现响应式 			//#region  			const p = new Proxy(person,{ 				//有人读取p的某个属性时调用 				get(target,propName){ 					console.log(`有人读取了p身上的${propName}属性`) 					return Reflect.get(target,propName) 				}, 				//有人修改p的某个属性、或给p追加某个属性时调用 				set(target,propName,value){ 					console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`) 					Reflect.set(target,propName,value) 				}, 				//有人删除p的某个属性时调用 				deleteProperty(target,propName){ 					console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`) 					return Reflect.deleteProperty(target,propName) 				} 			}) 			//#endregion  			let obj = {a:1,b:2} 			//通过Object.defineProperty去操作 			//#region  			/* try { 				Object.defineProperty(obj,'c',{ 					get(){ 						return 3 					} 				}) 				Object.defineProperty(obj,'c',{ 					get(){ 						return 4 					} 				}) 			} catch (error) { 				console.log(error) 			} */ 			//#endregion  			//通过Reflect.defineProperty去操作 			//#region  			/* const x1 = Reflect.defineProperty(obj,'c',{ 				get(){ 					return 3 				} 			}) 			console.log(x1) 			 			const x2 = Reflect.defineProperty(obj,'c',{ 				get(){ 					return 4 				} 			})  			if(x2){ 				console.log('某某某操作成功了!') 			}else{ 				console.log('某某某操作失败了!') 			} */ 			//#endregion  			// console.log('@@@')  		</script> 	</body> </html> 

5.reactive对比ref

  • 从定义数据角度对比:
    • ref用来定义:基本类型数据
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
  • 从原理角度对比:
    • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
    • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:
    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据:均不需要.value

6.setup的两个注意点

  • setup执行的时机

    • 在beforeCreate之前执行一次,this是undefined。
  • setup的参数

    • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
    • context:上下文对象
      • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs
      • slots: 收到的插槽内容, 相当于 this.$slots
      • emit: 分发自定义事件的函数, 相当于 this.$emit
  • Demo.vue
<template> 	<h1>一个人的信息</h1> 	<h2>姓名:{{person.name}}</h2> 	<h2>年龄:{{person.age}}</h2> 	<button @click="test">测试触发一下Demo组件的Hello事件</button> </template>  <script> 	import {reactive} from 'vue' 	export default { 		name: 'Demo', 		props:['msg','school'], 		emits:['hello'], 		setup(props,context){ 			// console.log('---setup---',props) 			// console.log('---setup---',context) 			// console.log('---setup---',context.attrs) //相当与Vue2中的$attrs 			// console.log('---setup---',context.emit) //触发自定义事件的。 			console.log('---setup---',context.slots) //插槽 			//数据 			let person = reactive({ 				name:'张三', 				age:18 			})  			//方法 			function test(){ 				context.emit('hello',666) 			}  			//返回一个对象(常用) 			return { 				person, 				test 			} 		} 	} </script>   
  • App.vue
<template> 	<Demo @hello="showHelloMsg" msg="你好啊" school="北京"> 		<template v-slot:qwe> 			<span>北京</span> 		</template> 		<template v-slot:asd> 			<span>北京</span> 		</template> 	</Demo> </template>  <script> 	import Demo from './components/Demo' 	export default { 		name: 'App', 		components:{Demo}, 		setup(){ 			function showHelloMsg(value){ 				alert(`你好啊,你触发了hello事件,我收到的参数是:${value}!`) 			} 			return { 				showHelloMsg 			} 		} 	} </script>   

7.计算属性与监视

1.computed函数

  • 与Vue2.x中computed配置功能一致

  • 写法

    <template>   <!-- Vue3中的模板结构可以没有div标签 -->   <h1>一个人的信息</h1>   姓:<input type="text" v-model="person.firstName" />   <br />   名:<input type="text" v-model="person.lastName" />   <br />   <span>{{ person.fullName }}</span>   全名: <input type="text" v-model="person.fullName" /> </template> <script> import { reactive, computed } from "vue"; export default {   name: "Demo",   setup() {     //数据     let person = reactive({       firstName: "张",       lastName: "三",     });            //计算属性 --简写 ,灭有考虑计算属性被修改的情况0     //  person.fullName =computed(()=>{     //   return person.firstName + '-'+ person.lastName     //  })      //计算属性 --完整写法 (考虑读写)     person.fullName = computed({       get() {         return person.firstName + "-" + person.lastName;       },       set(value) {         const nameArr = value.split("-");         person.firstName = nameArr[0];         person.lastName = nameArr[1];       },     });      return {       person,     };   }, }; </script>   

2.watch函数

  • 与Vue2.x中watch配置功能一致

  • 两个小“坑”:

    • 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
    • 监视reactive定义的响应式数据中某个属性时:deep配置有效。
    //情况一:监视ref定义的响应式数据 watch(sum,(newValue,oldValue)=>{ 	console.log('sum变化了',newValue,oldValue) },{immediate:true})  //情况二:监视多个ref定义的响应式数据 watch([sum,msg],(newValue,oldValue)=>{ 	console.log('sum或msg变化了',newValue,oldValue) })   /* 情况三:监视reactive定义的响应式数据 			若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!! 			若watch监视的是reactive定义的响应式数据,则强制开启了深度监视  */ watch(person,(newValue,oldValue)=>{ 	console.log('person变化了',newValue,oldValue) },{immediate:true,deep:false}) //此处的deep配置不再奏效  //情况四:监视reactive定义的响应式数据中的某个属性 watch(()=>person.job,(newValue,oldValue)=>{ 	console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true})   //情况五:监视reactive定义的响应式数据中的某些属性 watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{ 	console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true})  //特殊情况 watch(()=>person.job,(newValue,oldValue)=>{     console.log('person的job变化了',newValue,oldValue) },{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效 
<template>   <h1>当前求和为:{{ sum }}</h1>   <button @click="sum++">点我加1</button>   <hr />   <h2>当前的信息为:{{ msg }}</h2>   <button @click="msg += '!'">修改信息</button>   <hr />   <h2>姓名:{{ person.name }}</h2>   <h2>年龄:{{ person.age }}</h2>   <h2>薪资:{{ person.job.j1.salary }}</h2>   <button @click="person.name += '~'">修改姓名</button>   <button @click="person.age++">增长年龄</button>   <button @click="person.job.j1.salary++">增长薪资</button> </template> <script> import { ref, reactive, watch } from "vue"; export default {   name: "Demo",    setup() {     //数据     let sum = ref(0);     let msg = ref("你好啊");     let person = reactive({       name: "张三",       age: 18,       job: {         j1: {           salary: 20,         },       },     });     // //情况一,监视ref所定义的响应式数据     // watch(sum,(newvalue,oldvalue)=>{     //   console.log('sum的值变化了',newvalue,oldvalue)     // })      //情况二,监视ref所定义的多个响应式数据     // watch([sum.msg],(newvlaue,oldvalue)=>{     //   console.log('sum&msg的值变了,',newvlaue,oldvalue)     // },{immediate:true,deep:true})      //情况三,监视reactive所定义的响应式数据的全部属性,     // 注意:1. 此后无法正确的获得oldvalue     // 注意:2. 强制开启深度监视(deep配置无效)     // 注意:3.     // watch(person,(newvalue,oldvalue)=>{     //   console.log('person变化了',newvalue,oldvalue)     // },{deep:false})  //此处的deep配置无效      //情况四:监视reactive所定义的响应式数据的某个属性     //  watch(()=>person.age,(newvalue,oldvalue)=>{     //   console.log('person的age变化了',newvalue,oldvalue)     // })      //情况五:监视reactive所定义的响应式数据的某些属性     // watch(     //   [() => person.age,() => person.name],     //   (newvalue, oldvalue) => {     //     console.log("person的age或name变化了", newvalue, oldvalue);     //   }     // );    // //特殊情况   //   watch(()=>person.job,(newvalue,oldvalue)=>{   //     console.log('person的job变化了',newvalue,oldvalue)   //   },{deep:true})  //此处由于监视的是reactive定义的对象中的某个属性,所以deep配置有效      return {       sum,       msg,       person,     };   }, }; </script>   

3.watchEffect函数

  • watch的套路是:既要指明监视的属性,也要指明监视的回调。

  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

  • watchEffect有点像computed:

    • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
    //watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。 watchEffect(()=>{     const x1 = sum.value     const x2 = person.age     console.log('watchEffect配置的回调执行了') }) 

8.生命周期

Vue.js

  • Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
    • beforeDestroy改名为 beforeUnmount
    • destroyed改名为 unmounted
  • Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
    • beforeCreate===>setup()
    • created=======>setup()
    • beforeMount ===>onBeforeMount
    • mounted=======>onMounted
    • beforeUpdate===>onBeforeUpdate
    • updated =======>onUpdated
    • beforeUnmount ==>onBeforeUnmount
    • unmounted =====>onUnmounted
<template>   <h1>当前求和为:{{ sum }}</h1>   <button @click="sum++">点我加1</button> </template> <script> import {   ref,   onBeforeMount,   onMounted,   onBeforeUpdate,   onUpdated,   onBeforeUnmount,   onUnmounted, } from "vue"; export default {   name: "Demo",   setup() {     console.log('----setup----')     //数据     let sum = ref(0);     //通过组合式API的形式使用 声明周期钩子     onBeforeMount(() => {       console.log("----onBeforeMount----");     });     onMounted(() => {       console.log("----onMounted----");     });     onBeforeUpdate(() => {       console.log("----onBeforeUpdate----");     });     onUpdated(() => {       console.log("----onUpdated----");     });     onBeforeUnmount(() => {       console.log("----onBeforeUnmount----");     });     onUnmounted(() => {       console.log("----onUnmounted----");     });     return { sum };   },    //通过配置项的方式使用声明周期钩子   //#region   // beforeCreate() {   //   console.log("-----beforeCreate-----");   // },   // created() {   //   console.log("-----created-----");   // },   // beforeMount() {   //   console.log("-----beforeMount-----");   // },   // mounted() {   //   console.log("-----mounted-----");   // },   // beforeUpdate() {   //   console.log("-----beforeUpdate-----");   // },   // updated() {   //   console.log("-----updated-----");   // },   // destroyed() {   //   console.log("-----destroyed-----");   // },    //#endregion }; </script>   

9.自定义hook函数

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。

  • 类似于vue2.x中的mixin。

  • 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。

  • src/hooks/userPoint.js
import { reactive,onMounted, onBeforeUnmount } from "vue";  export default function (){     let point = reactive({         x: 0,         y: 0,       });          function savePoint(event) {         point.x = event.pageX;         point.y = event.pageY;         console.log(event.pageX, event.pageY);       }       onMounted(() => {         window.addEventListener("click",savePoint );       });       onBeforeUnmount(() => {         window.removeEventListener("click",savePoint);       });       return  point } 
  • Demo.vue
<template>   <h1>当前求和为:{{ sum }}</h1>   <button @click="sum++">点我加1</button>   <hr />   <h2>当前点击时鼠标的坐标为:x:{{ point.x }} y:{{ point.y }}</h2> </template> <script> import {ref} from "vue"; import usePoint from '../hooks/usePoint' export default {   name: "Demo",   setup() {     console.log("----setup----");     //数据     let sum = ref(0);     let point =usePoint()          return { sum,point};   }, }; </script>   
  • App.vue
<template>   <button @click="isShowDemo = !isShowDemo">切换隐藏/显示</button>   <Demo v-if="isShowDemo" /> </template>  <script> import { ref } from "vue"; import Demo from "./components/Demo.vue"; export default {   name: "App",   components: { Demo },   setup() {     let isShowDemo = ref(true)     return {isShowDemo}   } } </script> 

10.toRef

  • 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。

  • 语法:const name = toRef(person,'name')

  • 应用: 要将响应式对象中的某个属性单独提供给外部使用时。

  • 扩展:toRefstoRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)

<template> 	<h4>{{person}}</h4> 	<h2>姓名:{{name}}</h2> 	<h2>年龄:{{age}}</h2> 	<h2>薪资:{{job.j1.salary}}K</h2> 	<button @click="name+='~'">修改姓名</button> 	<button @click="age++">增长年龄</button> 	<button @click="job.j1.salary++">涨薪</button> </template>  <script> 	import {ref,reactive,toRef,toRefs} from 'vue' 	export default { 		name: 'Demo', 		setup(){ 			//数据 			let person = reactive({ 				name:'张三', 				age:18, 				job:{ 					j1:{ 						salary:20 					} 				} 			})  			// const name1 = person.name 			// console.log('%%%',name1)  			// const name2 = toRef(person,'name') 			// console.log('####',name2)  			const x = toRefs(person) 			console.log('******',x)  			//返回一个对象(常用) 			return { 				person, 				// name:toRef(person,'name'), 				// age:toRef(person,'age'), 				// salary:toRef(person.job.j1,'salary'), 				...toRefs(person) 			} 		} 	} </script>   

三、其它 Composition API

1.shallowReactive 与 shallowRef

  • shallowReactive:只处理对象最外层属性的响应式(浅响应式)。

  • shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。

  • 什么时候使用?

    • 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
    • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
<template> 	<h4>当前的x.y值是:{{x.y}}</h4> 	<button @click="x={y:888}">点我替换x</button> 	<button @click="x.y++">点我x.y++</button> 	<hr> 	<h4>{{person}}</h4> 	<h2>姓名:{{name}}</h2> 	<h2>年龄:{{age}}</h2> 	<h2>薪资:{{job.j1.salary}}K</h2> 	<button @click="name+='~'">修改姓名</button> 	<button @click="age++">增长年龄</button> 	<button @click="job.j1.salary++">涨薪</button> </template>  <script> 	import {ref,reactive,toRef,toRefs,shallowReactive,shallowRef} from 'vue' 	export default { 		name: 'Demo', 		setup(){ 			//数据 			// let person = shallowReactive({ //只考虑第一层数据的响应式 			let person = reactive({ 				name:'张三', 				age:18, 				job:{ 					j1:{ 						salary:20 					} 				} 			}) 			let x = shallowRef({ 				y:0 			}) 			console.log('******',x)  			//返回一个对象(常用) 			return { 				x, 				person, 				...toRefs(person) 			} 		} 	} </script> 

2.readonly 与 shallowReadonly

  • readonly: 让一个响应式数据变为只读的(深只读)。
  • shallowReadonly:让一个响应式数据变为只读的(浅只读)。
  • 应用场景: 不希望数据被修改时。
<template> 	<h4>当前求和为:{{sum}}</h4> 	<button @click="sum++">点我++</button> 	<hr> 	<h2>姓名:{{name}}</h2> 	<h2>年龄:{{age}}</h2> 	<h2>薪资:{{job.j1.salary}}K</h2> 	<button @click="name+='~'">修改姓名</button> 	<button @click="age++">增长年龄</button> 	<button @click="job.j1.salary++">涨薪</button> </template>  <script> 	import {ref,reactive,toRefs,readonly,shallowReadonly} from 'vue' 	export default { 		name: 'Demo', 		setup(){ 			//数据 			let sum = ref(0) 			let person = reactive({ 				name:'张三', 				age:18, 				job:{ 					j1:{ 						salary:20 					} 				} 			}) 			person = readonly(person) 			// person = shallowReadonly(person) 			// sum = readonly(sum) 			// sum = shallowReadonly(sum)  			//返回一个对象(常用) 			return { 				sum, 				...toRefs(person) 			} 		} 	} </script> 

3.toRaw 与 markRaw

  • toRaw:
    • 作用:将一个由reactive生成的响应式对象转为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
  • markRaw:
    • 作用:标记一个对象,使其永远不会再成为响应式对象。
    • 应用场景:
      1. 有些值不应被设置为响应式的,例如复杂的第三方类库等。
      2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
<template> 	<h4>当前求和为:{{sum}}</h4> 	<button @click="sum++">点我++</button> 	<hr> 	<h2>姓名:{{name}}</h2> 	<h2>年龄:{{age}}</h2> 	<h2>薪资:{{job.j1.salary}}K</h2> 	<h3 v-show="person.car">座驾信息:{{person.car}}</h3> 	<button @click="name+='~'">修改姓名</button> 	<button @click="age++">增长年龄</button> 	<button @click="job.j1.salary++">涨薪</button> 	<button @click="showRawPerson">输出最原始的person</button> 	<button @click="addCar">给人添加一台车</button> 	<button @click="person.car.name+='!'">换车名</button> 	<button @click="changePrice">换价格</button> </template>  <script> 	import {ref,reactive,toRefs,toRaw,markRaw} from 'vue' 	export default { 		name: 'Demo', 		setup(){ 			//数据 			let sum = ref(0) 			let person = reactive({ 				name:'张三', 				age:18, 				job:{ 					j1:{ 						salary:20 					} 				} 			})  			function showRawPerson(){ 				const p = toRaw(person) 				p.age++ 				console.log(p) 			}  			function addCar(){ 				let car = {name:'奔驰',price:40} 				person.car = markRaw(car) 			}  			function changePrice(){ 				person.car.price++ 				console.log(person.car.price) 			}  			//返回一个对象(常用) 			return { 				sum, 				person, 				...toRefs(person), 				showRawPerson, 				addCar, 				changePrice 			} 		} 	} </script>   

4.customRef

  • 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。

  • 实现防抖效果:

    <template> 	<input type="text" v-model="keyword"> 	<h3>{{keyword}}</h3> </template>  <script> 	import {ref,customRef} from 'vue' 	export default { 		name:'Demo', 		setup(){ 			// let keyword = ref('hello') //使用Vue准备好的内置ref 			//自定义一个myRef 			function myRef(value,delay){ 				let timer 				//通过customRef去实现自定义 				return customRef((track,trigger)=>{ 					return{ 						get(){ 							track() //告诉Vue这个value值是需要被“追踪”的 							return value 						}, 						set(newValue){ 							clearTimeout(timer) 							timer = setTimeout(()=>{ 								value = newValue 								trigger() //告诉Vue去更新界面 							},delay) 						} 					} 				}) 			} 			let keyword = myRef('hello',500) //使用程序员自定义的ref 			return { 				keyword 			} 		} 	} </script> 

5.provide 与 inject

  • 作用:实现祖与后代组件间通信

  • 套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据

  • 具体写法:

    1. 祖组件中:

      setup(){ 	......     let car = reactive({name:'奔驰',price:'40万'})     provide('car',car)     ...... } 
    2. 后代组件中:

      setup(props,context){ 	......     const car = inject('car')     return {car} 	...... } 
  • App.vue
<template> 	<div class="app"> 		<h3>我是App组件(祖),{{name}}--{{price}}</h3> 		<Child/> 	</div> </template>  <script> 	import { reactive,toRefs,provide } from 'vue' 	import Child from './components/Child.vue' 	export default { 		name:'App', 		components:{Child}, 		setup(){ 			let car = reactive({name:'奔驰',price:'40W'}) 			provide('car',car) //给自己的后代组件传递数据 			return {...toRefs(car)} 		} 	} </script>  <style> 	.app{ 		background-color: gray; 		padding: 10px; 	} </style>0 
  • Child.vue
<template> 	<div class="child"> 		<h3>我是Child组件(子)</h3> 		<Son/> 	</div> </template>  <script> 	import {inject} from 'vue' 	import Son from './Son.vue' 	export default { 		name:'Child', 		components:{Son}, 		/* setup(){ 			let x = inject('car') 			console.log(x,'Child-----') 		} */ 	} </script>  <style> 	.child{ 		background-color: skyblue; 		padding: 10px; 	} </style> 
  • Son.vue
<template> 	<div class="son"> 		<h3>我是Son组件(孙),{{car.name}}--{{car.price}}</h3> 	</div> </template>  <script> 	import {inject} from 'vue' 	export default { 		name:'Son', 		setup(){ 			let car = inject('car') 			return {car} 		} 	} </script>  <style> 	.son{ 		background-color: orange; 		padding: 10px; 	} </style> 

6.响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
<template> 	<h3>我是App组件</h3> </template>  <script> 	import {ref, reactive,toRefs,readonly,isRef,isReactive,isReadonly,isProxy } from 'vue' 	export default { 		name:'App', 		setup(){ 			let car = reactive({name:'奔驰',price:'40W'}) 			let sum = ref(0) 			let car2 = readonly(car)  			console.log(isRef(sum)) 			console.log(isReactive(car)) 			console.log(isReadonly(car2)) 			console.log(isProxy(car)) 			console.log(isProxy(sum)) 			return {...toRefs(car)} 		} 	} </script>  <style> 	.app{ 		background-color: gray; 		padding: 10px; 	} </style> 

四、Composition API 的优势

1.Options API 存在的问题

使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。

Vue.js

<br

Vue.js

2.Composition API 的优势

我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。

Vue.js
Vue.js

五、新的组件

1.Fragment

  • 在Vue2中: 组件必须有一个根标签
  • 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处: 减少标签层级, 减小内存占用

2.Teleport

  • 什么是Teleport?—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。

    <teleport to="移动位置"> 	<div v-if="isShow" class="mask"> 		<div class="dialog"> 			<h3>我是一个弹窗</h3> 			<button @click="isShow = false">关闭弹窗</button> 		</div> 	</div> </teleport> 

3.Suspense

  • 等待异步组件时渲染一些额外内容,让应用有更好的用户体验

  • 使用步骤:

    • 异步引入组件

      import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue')) 
    • 使用Suspense包裹组件,并配置好defaultfallback

      <template> 	<div class="app"> 		<h3>我是App组件</h3> 		<Suspense> 			<template v-slot:default> 				<Child/> 			</template> 			<template v-slot:fallback> 				<h3>加载中.....</h3> 			</template> 		</Suspense> 	</div> </template> 

六、其他

1.全局API的转移

  • Vue 2.x 有许多全局 API 和配置。

    • 例如:注册全局组件、注册全局指令等。

      //注册全局组件 Vue.component('MyButton', {   data: () => ({     count: 0   }),   template: '<button @click="count++">Clicked {{ count }} times.</button>' })  //注册全局指令 Vue.directive('focus', {   inserted: el => el.focus() } 
  • Vue3.0中对这些API做出了调整:

    • 将全局的API,即:Vue.xxx调整到应用实例(app)上

      2.x 全局 API(Vue 3.x 实例 API (app)
      Vue.config.xxxx app.config.xxxx
      Vue.config.productionTip 移除
      Vue.component app.component
      Vue.directive app.directive
      Vue.mixin app.mixin
      Vue.use app.use
      Vue.prototype app.config.globalProperties

2.其他改变

  • data选项应始终被声明为一个函数。

  • 过度类名的更改:

    • Vue2.x写法

      .v-enter, .v-leave-to {   opacity: 0; } .v-leave, .v-enter-to {   opacity: 1; } 
    • Vue3.x写法

      .v-enter-from, .v-leave-to {   opacity: 0; }  .v-leave-from, .v-enter-to {   opacity: 1; } 
  • 移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes

  • 移除v-on.native修饰符

    • 父组件中绑定事件

      <my-component   v-on:close="handleComponentEvent"   v-on:click="handleNativeClickEvent" /> 
    • 子组件中声明自定义事件

      <script>   export default {     emits: ['close']   } </script> 
  • 移除过滤器(filter)

    过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。

  • ......

  • 版权声明:本站原创文章,于2023年1月4日00:52:59,由 发表,共 120664 字。
  • 转载请注明:Vue.js - 张拓的天空