目录:
内容:
一、Vue内部指令:
1、v-if v-else&v-show
v-if与v-show都是选择性显示内容的指令,但是二者之间有区别:
1、v-if:判断是否加载,在需要的时候加载,减少服务器压力
2、v-show:调整css display属性,使客户端操作更加流畅
v-if:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <div v-if="islog">加载</div> <!--<div v-else-if="iselse">可以加载</div>--> <div v-else="islog">不加载</div> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ islog: false, iselse: true //如果islog=true时,后面两个不显示 //当islog为false v-else-if为true 显示第二个内容 ,当第二个为alse显示最后一个 } }) } </script> </body> </html>
v-show:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <div v-show="islog">加载</div> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ islog: true } }) } </script> </body> </html>
2、v-for 循环模式
- 普通循环模式:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <ul> <li v-for="item in items"> {{item}} </li> </ul> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ items: [45,32,77,36,8,54] } }) } </script> </body> </html>
2. 数组排序循环:普通排序有个问题就是只能按照一位一位来进行,所以需要下面的方法来修正
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <ul> <li v-for="item in sortItems"> {{item}} </li> </ul> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ items: [45,32,77,36,8,54] }, computed:{ //使用computed对items进行排序 sortItems:function () {//在排序时,computed中的变量名必须和原来不同 return this.items.sort(); } } }) } </script> </body> </html>
3. 增强型数字数组排序:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <ul> <li v-for="item in sortItems"> {{item}} </li> </ul> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ items: [45,32,77,36,8,54] }, computed:{ //使用computed对items进行排序 sortItems:function () {//在排序时,computed中的变量名必须和原来不同 return this.items.sort(sortNumber);//引入排序函数 } } }); function sortNumber(a,b) {//添加排序函数 return a-b; } }; </script> </body> </html>
4. 数组中增加对象排序:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <ul> <li v-for="item in sortItems"> {{item}} </li> </ul> <ul> <li v-for="(student,index) in sortStudent"> <!-- Vue2.0中排序时,index在value后面 --> {{index}}: {{student.name}}-{{student.age}} </li> </ul> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ items: [45,32,77,36,8,54], students:[ {name:'JSPang',age:33}, {name:'JS',age:22}, {name:'Pang',age:14}, {name:'keke',age:50} ] }, computed:{ //使用computed对items进行排序 sortItems:function () {//在排序时,computed中的变量名必须和原来不同 return this.items.sort(sortNumber);//引入排序函数 }, sortStudent: function () { return sortByKey(this.students, 'age'); } } }); function sortNumber(a,b) {//添加排序函数 return a-b; }; //数组对象方法排序: function sortByKey(arrary,key) { return arrary.sort(function (a,b) { var x=a[key]; var y=b[key]; return ((x<y)?-1:((x>y)?1:0)); }); } }; </script> </body> </html>
3、v-text v-html
其中v-html不推荐使用,因为会造成代码泄漏引发安全问题,v-text可以改善由于各种问题而导致模板{{}}没有渲染成功的问题,如果没有渲染成功v-text不显示内容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app" v-text="message"> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ message: 'hello world' } }); }; </script> </body> </html>
4、v-on
v-on对应的是绑定事件并用来监听
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> 本厂比赛得分: <span v-text="record"></span><br> <button v-on:click="addscore">加分</button> <button @click="reducescore">减分</button> <br> <input type="text" @keyup.enter="onEnter" v-model="incrscore"> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ record: 0, incrscore:0 }, methods:{ addscore:function () { this.record++ }, reducescore:function () { if(this.record <= 0){ this.record = 0 }else{ this.record-- } }, onEnter:function () { this.record=this.record+parseInt(this.incrscore);//有坑 需要将后面转换为数字形式 } } }); }; </script> </body> </html>
5、v-model
双向数据绑定
v-model分为几个不同类型:
1、文本框类型:
1.v-model.lazy表示在光标离开后才生效
2.v-model.number表示只可以输入数字
3.v-model.trim表示去空格
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <p> <h3>文本框</h3> <p >你想表达的文字是: <span v-text="message"></span></p> <p>普通类型:<input type="text" v-model="message"></p> <p>lazy:<input type="text" v-model.lazy="message"></p> <p>number:<input type="text" v-model.number="message"></p> <p>trim:<input type="text" v-model.trim="message"></p> </p> <p> <h3>多区域框</h3> <p >你想表达的文字是: <span v-text="message"></span></p> <p></p> </p> </div> <script type="text/javascript"> window.onload = function () { var app = new Vue({ el: '#app', data: { message: '' } }) } </script> </body> </html>
2、多区域框
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <p> <h3>文本框</h3> <p >你想表达的文字是: <span v-text="message"></span></p> <p>普通类型:<input type="text" v-model="message"></p> <p>lazy:<input type="text" v-model.lazy="message"></p> <p>number:<input type="text" v-model.number="message"></p> <p>trim:<input type="text" v-model.trim="message"></p> </p> <p> <h3>多区域框</h3> <p >你想表达的文字是: <span v-text="message"></span></p> <p><textarea cols="30" rows="10" v-model="message"></textarea></p> </p> </div> <script type="text/javascript"> window.onload = function () { var app = new Vue({ el: '#app', data: { message: '' } }) } </script> </body> </html>
3、多选框选中一个值
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <p> <h3>文本框</h3> <p >你想表达的文字是: <span v-text="message"></span></p> <p>普通类型:<input type="text" v-model="message"></p> <p>lazy:<input type="text" v-model.lazy="message"></p> <p>number:<input type="text" v-model.number="message"></p> <p>trim:<input type="text" v-model.trim="message"></p> </p> <p> <h3>多区域框</h3> <p >你想表达的文字是: <span v-text="message"></span></p> <p><textarea cols="30" rows="10" v-model="message"></textarea></p> </p> <p> <h3>多选框绑定一个值</h3> <input type="checkbox" id="isTrue" v-model="isTrue"> <label for="isTrue"><span v-text="isTrue"></span></label> </p> </div> <script type="text/javascript"> window.onload = function () { var app = new Vue({ el: '#app', data: { message: '', isTrue:true } }) } </script> </body> </html>
4、多选框选中数组
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <p> <h3>文本框</h3> <p >你想表达的文字是: <span v-text="message"></span></p> <p>普通类型:<input type="text" v-model="message"></p> <p>lazy:<input type="text" v-model.lazy="message"></p> <p>number:<input type="text" v-model.number="message"></p> <p>trim:<input type="text" v-model.trim="message"></p> </p> <p> <h3>多区域框</h3> <p >你想表达的文字是: <span v-text="message"></span></p> <p><textarea cols="30" rows="10" v-model="message"></textarea></p> </p> <p> <h3>多选框绑定一个值</h3> <input type="checkbox" id="isTrue" v-model="isTrue"> <label for="isTrue"><span v-text="isTrue"></span></label> </p> <p> <h3>多选框绑定数组</h3> <input type="checkbox" id="jspang" value="jspang" v-model="web_names"> <label for="jspang">jspang</label> <input type="checkbox" id="panda" value="panda" v-model="web_names"> <label for="panda">panda</label> <input type="checkbox" id="pangzi" value="pangzi" v-model="web_names"> <label for="pangzi">pangzi</label> <p v-text="web_names"></p> </p> </div> <script type="text/javascript"> window.onload = function () { var app = new Vue({ el: '#app', data: { message: '', isTrue:true, web_names:[] } }) } </script> </body> </html>
5、单选框
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <p> <h3>文本框</h3> <p >你想表达的文字是: <span v-text="message"></span></p> <p>普通类型:<input type="text" v-model="message"></p> <p>lazy:<input type="text" v-model.lazy="message"></p> <p>number:<input type="text" v-model.number="message"></p> <p>trim:<input type="text" v-model.trim="message"></p> </p> <p> <h3>多区域框</h3> <p >你想表达的文字是: <span v-text="message"></span></p> <p><textarea cols="30" rows="10" v-model="message"></textarea></p> </p> <p> <h3>多选框绑定一个值</h3> <input type="checkbox" id="isTrue" v-model="isTrue"> <label for="isTrue"><span v-text="isTrue"></span></label> </p> <p> <h3>多选框绑定数组</h3> <input type="checkbox" id="jspang" value="jspang" v-model="web_names"> <label for="jspang">jspang</label> <input type="checkbox" id="panda" value="panda" v-model="web_names"> <label for="panda">panda</label> <input type="checkbox" id="pangzi" value="pangzi" v-model="web_names"> <label for="pangzi">pangzi</label> <p v-text="web_names"></p> </p> <p> <h3>单选框绑定</h3> <input type="radio" id="sex1" value="male" v-model="gender"> <label for="sex1">男</label> <input type="radio" id="sex2" value="female" v-model="gender"> <label for="sex2">女</label> <p>你选中的性别是:<span v-text="gender"></span></p> </p> <p> </div> <script type="text/javascript"> window.onload = function () { var app = new Vue({ el: '#app', data: { message: '', isTrue:true, web_names:[], gender:'male' } }) } </script> </body> </html>
6、v-bind
v-bind可以绑定资源也可以绑定类
1、资源绑定
img操作:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <p> <h3>资源绑定</h3> <p><img :src="srcUrl" alt="" width="200px"/></p> </p> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ srcUrl: 'https://www.baidu.com/img/bd_logo1.png' } }) } </script> </body> </html>
a操作:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <p> <h3>资源绑定</h3> <p><img :src="srcUrl" alt="" width="200px"/></p> <p><a :href="aurl">点击</a></p> </p> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ srcUrl: 'https://www.baidu.com/img/bd_logo1.png', aurl: 'https://www.baidu.com' } }) } </script> </body> </html>
2、类操作
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .classA{ color: red; } .classB{ font-size: 30px; } </style> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <p> <h3>资源绑定</h3> <p><img :src="srcUrl" alt="" width="200px"/></p> <p><a :href="aurl">点击</a></p> </p> <p> <h3>类操作</h3> <p :class="classname">1.绑定class</p> <p :class="{classA:isok}">2.绑定class判断</p> <p :class="[classA,classB]">3.绑定class数组</p> <p :class="isok?classA:classB">4.绑定class数组三元运算</p> <p :style="{color:red,fontSize:font}">5.style绑定</p> <p :style="styeobj">6.style绑定对象</p> <hr> <div> <input type="checkbox" id="isok" v-model="isok"> <label for="isok">绑定->{{isok}}</label> </div> </p> </div> <script type="text/javascript"> window.onload=function () { var app=new Vue({ el:'#app', data:{ srcUrl: 'https://www.baidu.com/img/bd_logo1.png', aurl: 'https://www.baidu.com', classname: 'classA', isok:false, classA:'classA', classB:'classB', red:'red', font:'40px', styeobj:{ color:'green', fontSize:'50px' } } }) } </script> </body> </html>
7、v-pre v-cloak v-once
v-pre:不渲染使用v-pre
v-cloak:全部渲染完毕才显示
v-once:只渲染一次(v-model操作不对此生效)
二、全局API
全局API并不在构造器里,而是先声明全局变量或者直接在Vue上定义一些新功能,Vue内置了一些全局API,比如我们今天要学习的指令Vue.directive。说的简单些就是,在构造器外部用Vue提供给我们的API函数来定义新的功能。
1、Vue.directive自定义指令
Vue.directive可以创建自定义指令完成某些特定任务,比如可以定义一个v-gavin的指令,作用是让文字变成绿色
为了实现这个功能,可以写一个简单的demo:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <div v-text="message" v-gavin="color"></div> <button @click="add">增加</button> </div> <script type="text/javascript"> window.onload = function () { Vue.directive('gavin',function (el,binding,vnode) { console.log(el); console.log(binding); el.style='color:'+binding.value; }) var app = new Vue({ el: '#app', data: { message: 0, color: 'red' }, methods: { add: function () { this.message++ } } }) } </script> </body> </html>
fucntion函数中三个参数:
el:指令所绑定的元素,可以用来直接操作DOM
binding:一个对象,包含指令的很多信息
vnode:Vue编译生成的虚拟节点
在自定义指令存在生命周期的概念,它包含五个生命周期也叫做五个钩子函数,分别是:
- bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个绑定时执行一次的初始化动作。
- inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。
- update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。
- componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
- unbind:只调用一次,指令与元素解绑时调用。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <div v-text="message" v-gavin="color"></div> <button @click="add">增加</button> <button onclick="unbind()">解绑</button> </div> <script type="text/javascript"> window.onload = function () { function unbind() { app.$destroy();//Vue提供的销毁绑定的函数 $destory() }; Vue.directive('gavin',{ bind:function () { console.log('1. bind'); }, inserted:function(){ console.log('2. inserted'); }, update:function(el,binding){ console.log('3. update'); el.style='color:'+binding.value }, componentUpdated:function(){ console.log('4. componentUpdated'); }, unbind:function(){ console.log('5. unbind'); }}) var app = new Vue({ el: '#app', data: { message: 0, color: 'red' }, methods: { add: function () { this.message++ } } }) } </script> </body> </html>
2、Vue.extend构造器的延伸
Vue.extend 返回的是一个“扩展实例构造器”,也就是预设了部分选项的Vue实例构造器。经常服务于Vue.component用来生成组件,可以简单理解为当在模板中遇到该组件名称作为标签的自定义元素时,会自动调用“扩展实例构造器”来生产组件实例,并挂载到自定义元素上。
可以构建一个小demo来演示一下无参数标签的实例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"></div> <author></author> <script type="text/javascript"> var authorExtend = Vue.extend({ template:"<p><a :href='authorUrl'><span v-text='authorName'></span></a></p>", data:function () { return{ authorName:'Gavin', authorUrl:'https://www.baidu.com' } } }) new authorExtend().$mount('author')//mount绑定,可以代替new Vue({el: 'app方式'}) new authorExtend().$mount('#app')//可以类似于jquery方式绑定 </script> </body> </html>
3、Vue.set
Vue.set 的作用就是在构造器外部操作构造器内部的数据、属性或者方法。比如在vue构造器内部定义了一个count为1的数据,我们在构造器外部定义了一个方法,要每次点击按钮给值加1.就需要用到Vue.set。
什么是外部数据,就是不在Vue构造器里里的data处声明,而是在构造器外部声明,然后在data处引用就可以了。外部数据的加入让程序更加灵活,我们可以在外部获取任何想要的数据形式,然后让data引用。
demo如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> {{count}} {{name}} {{arr}} </div> <script type="text/javascript"> window.onload=function () { var outData ={ count:1, name: 'Gain', arr: ['aaa','bbb','ccc'] } var app=new Vue({ el:'#app', data: outData//引用外部数据 }) } </script> </body> </html>
修改外部数据的三种方式:
1、outData++
2、app.count++
3、Vue.set(outData,'count',4); //outData我、是外部数据 count是它里面的一项 4表示需要改变为4
由于Javascript的限制,Vue不能自动检测以下变动的数组。
*当你利用索引直接设置一个项时,vue不会为我们自动更新。
*当你修改数组的长度时,vue不会为我们自动更新。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script type="text/javascript" src="vue.js"></script> <title>Vue.set 全局操作</title> </head> <body> <h1>Vue.set 全局操作</h1> <hr> <div id="app"> <ul> <li v-for=" aa in arr">{{aa}}</li> </ul> </div> <button onclick="add()">外部添加</button> <script type="text/javascript"> function add(){ console.log("我已经执行了"); // app.arr[1]='ddd';//使用该方式没法改变数组内数据 Vue.set(app.arr,1,'d'); } var outData={ arr:['aaa','bbb','ccc'] }; var app=new Vue({ el:'#app', data:outData }) </script> </body> </html>
这时我们的界面是不会自动跟新数组的,我们需要用Vue.set(app.arr,1,’d’)来设置改变,vue才会给我们自动更新,这就是Vue.set存在的意义。
4、Vue生命周期
Vue一共有10个生命周期函数,我们可以利用这些函数在vue的每个阶段都进行操作数据或者改变内容。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> {{message}} <button @click="add">add</button></div> <button onclick="des()">des</button> <script type="text/javascript"> function des() { app.$destroy(); } var app=new Vue({ el: '#app', data:{ message:0 }, methods:{ add:function () { this.message++; } }, beforeCreate:function(){ console.log('1-beforeCreate 初始化之后'); }, created:function(){ console.log('2-created 创建完成'); }, beforeMount:function(){ console.log('3-beforeMount 挂载之前'); }, mounted:function(){ console.log('4-mounted 被创建'); }, beforeUpdate:function(){ console.log('5-beforeUpdate 数据更新前'); }, updated:function(){ console.log('6-updated 被更新后'); }, activated:function(){//vuerouter中生效 console.log('7-activated'); }, deactivated:function(){//vuerouter中生效 console.log('8-deactivated'); }, beforeDestroy:function(){ console.log('9-beforeDestroy 销毁之前'); }, destroyed:function(){ console.log('10-destroyed 销毁之后') } }) </script> </body> </html>
5、template
template有三种写法:
1、直接在Vue中写模板:直接在构造器里的template选项后边编写。这种写法比较直观,但是如果模板html代码太多,不建议这么写。
<body> <div id="app"> </div> <script type="text/javascript"> var app=new Vue({ el: '#app', data:{ message: 'hello world!' }, template:`<h2 style="color:red">我是选项模板</h2>` }) </script> </body>
2、写在template标签中的模板
<body> <div id="app"> </div> <template id="dd"> <h2 style="color:red">我是template中的模板</h2> </template> <script type="text/javascript"> var app=new Vue({ el: '#app', data:{ message: 'hello world!' }, template:'#dd' // template:`<h2 style="color:red">我是选项模板</h2>` }) </script> </body>
3、写在script中的模板
<body> <div id="app"> </div> <template id="dd"> <h2 style="color:red">我是template中的模板</h2> </template> <script type="x-template" id="dd1"> <h2 style="color:red">我是script中的模板</h2> </script> <script type="text/javascript"> var app=new Vue({ el: '#app', data:{ message: 'hello world!' }, template:'#dd1' // template:`<h2 style="color:red">我是选项模板</h2>` }) </script> </body>
6、Component
组件就是制作自定义的标签,这些标签在HTML中是没有的。比如:<j></j>
component分为两种情况:
1、全局组件
2、局部组件
其中全局组件在整个Vue生命周期内都可以调用,而局部组件只在对应的Vue下可以调用。
demo如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <gavin></gavin> </div> <div id="app1"> <gavin></gavin> </div> <template id="t1"> <h2>我是全局组件</h2> </template> <script type="text/javascript"> Vue.component('gavin',{ template: '#t1' }); var app=new Vue({ el:'#app' }); var app1=new Vue({ el: '#app1' }) </script> </body> </html>
demo局部如下,可以看到ga组件只能在app上调用,在app1上调用会报错
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <gavin></gavin> <ga></ga> </div> <div id="app1"> <gavin></gavin> <ga></ga> </div> <template id="t1"> <h2>我是全局组件</h2> </template> <template id="t2"> <h2>我是局部组件</h2> </template> <script type="text/javascript"> Vue.component('gavin',{ template: '#t1' }); var app=new Vue({ el:'#app', components:{ 'ga': { template: '#t2' } } }); var app1=new Vue({ el: '#app1' }) </script> </body> </html>
组件注册的是一个标签,而指令注册的是已有标签里的一个属性。在实际开发中我们还是用组件比较多,指令用的比较少。因为指令看起来封装的没那么好
props选项就是设置和获取标签上的属性值的,例如我们有一个自定义的组件<panda></panda>,这时我们想给他加个标签属性写成<panda here=’China’></panda> 意思就是熊猫来自中国,当然这里的China可以换成任何值。定义属性的选项是props。
1、定义属性并获取值
定义属性我们需要用props选项,加上数组形式的属性名称,例如:props:[‘here’]。在组件的模板里读出属性值只需要用插值的形式,例如{{ here }}.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <gavin here="china"></gavin> </div> <template id="t1"> <h2>组件传参: {{here}}</h2> </template> <script type="text/javascript"> var app=new Vue({ el:'#app', data:{ message: 'china' }, components:{ 'gavin':{ template:'#t1', props:['here'] } } }) </script> </body> </html>
2、属性如果中间带‘-’,需要小驼峰式写法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <gavin from-here="china"></gavin> </div> <template id="t1"> <h2>组件传参: {{fromHere}}</h2> </template> <script type="text/javascript"> var app=new Vue({ el:'#app', data:{ message: 'china' }, components:{ 'gavin':{ template:'#t1', props:['fromHere'] } } }) </script> </body> </html>
3、向组件中传参- 需要通过v-bind 或者:方式进行
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <gavin :from-here="message"></gavin> </div> <template id="t1"> <h2>组件传参: {{fromHere}}</h2> </template> <script type="text/javascript"> var app=new Vue({ el:'#app', data:{ message: 'china' }, components:{ 'gavin':{ template:'#t1', props:['fromHere'] } } }) </script> </body> </html>
父子组件:
注意:需要使用div包裹起来才能使用子组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <gavin></gavin> </div> <template id="t2"> <h3>我是子组件</h3> </template> <template id="t1"> <!--需要使用div包裹起来才能使用子组件--> <div> <h2>我是父组件</h2> <sun></sun> </div> </template> <script type="text/javascript"> var sun = { template: '#t2' }; var father = { template: '#t1', components:{ 'sun': sun } }; var app=new Vue({ el:'#app', components:{ 'gavin': father } }) </script> </body> </html>
component标签:
<component></component>标签是Vue框架自定义的标签,它的用途就是可以动态绑定我们的组件,根据数据的不同更换不同的组件。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <component :is="who"></component> <button @click="changeComponent">Change</button> </div> <template id="t1"> <div style="color:red">I'm Component A</div> </template> <template id="t2"> <div style="color:green">I'm Component B</div> </template> <template id="t3"> <div style="color:blue">I'm Component C</div> </template> <script type="text/javascript"> var componentA = { template: '#t1' }; var componentB = { template: '#t2' }; var componentC = { template: '#t3' }; var app=new Vue({ el:'#app', data:{ who: 'componentA' }, components:{ 'componentA': componentA, 'componentB': componentB, 'componentC': componentC }, methods:{ changeComponent:function () { if(this.who==='componentA'){ this.who = 'componentB'; }else if(this.who==='componentB'){ this.who = 'componentC'; }else{ this.who = 'componentA'; } } } }) </script> </body> </html>
三、选项
1、propsData选项
propsData 不是和属性有关,他用在全局扩展时进行传递数据。先回顾一下全局扩展的知识,作一个<header></header>的扩展标签出来。实际我们并比推荐用全局扩展的方式作自定义标签,我们学了组件,完全可以使用组件来做,这里只是为了演示propsData的用法。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <header></header> <script type="text/javascript"> var header_a = Vue.extend({ template: `<p>{{message}}-{{a}}</p>`, data:function(){ return{ message:'hello world' } }, props:['a'] }); new header_a({propsData:{a:'扩展传值'}}).$mount('header') </script> </body> </html>
扩展标签已经做好了,这时我们要在挂载时传递一个数字过去,我们就用到了propsData。
我们用propsData三步解决传值:
1、在全局扩展里加入props进行接收。propsData:{a:1}
2、传递时用propsData进行传递。props:[‘a’]
3、用插值的形式写入模板。{{ a }}
总结:propsData在实际开发中我们使用的并不多,我们在后边会学到Vuex的应用,他的作用就是在单页应用中保持状态和数据的。
2、computed 选项
computed 的作用主要是对原数据进行改造输出。改造输出:包括格式的编辑,大小写转换,顺序重排,添加符号……。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <div v-text="newPrice"></div> <ul> <li v-for="item in newslists"><span v-text="item.title"></span>-<span v-text="item.date"></span></li> </ul> </div> <script type="text/javascript"> var app=new Vue({ el:'#app', data:{ price: 100, news:[ {title:'香港或就“装甲车被扣”事件追责 起诉涉事运输公司',date:'2017/12/10'}, {title:'日本第二大准航母服役 外媒:针对中国潜艇',date:'2017/12/12'}, {title:'中国北方将有明显雨雪降温天气 南方阴雨持续',date:'2017/12/13'}, {title:'起底“最短命副市长”:不到40天落马,全家被查',date:'2017/12/23'} ] }, computed:{ newPrice:function () { return this.price = "¥"+this.price+'元' }, newslists:function () { return this.news.reverse(); } } }) </script> </body> </html>
3、methods选项
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <div v-text="num"></div> <p><button @click="add(money)">add</button></p> <p><input type="text" v-model="money"></p> </div> <script type="text/javascript"> var app=new Vue({ el:'#app', data:{ num:0, money:'' }, methods:{ add:function (mon) { if(mon!=0){ this.num+=parseInt(mon); }else { this.num++; } } } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <div v-text="num"></div> <p><button @click="add(money,$event)">add</button></p> <p><input type="text" v-model="money"></p> </div> <script type="text/javascript"> var app=new Vue({ el:'#app', data:{ num:0, money:'' }, methods:{ add:function (mon,event) { if(mon!=0){ this.num+=parseInt(mon); }else { this.num++; }; console.log(event); } } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <div v-text="num"></div> <!--<p><button @click="add(money,$event)">add</button></p>--> <p><button @click="add(money)">add</button></p> <p><input type="text" v-model="money"></p> <btn @click.native="add(money)"></btn> </div> <template id="t1"> <div> <button >add</button> </div> </template> <script type="text/javascript"> var btn={ template:'#t1' }; var app=new Vue({ el:'#app', data:{ num:0, money:'' }, components:{ 'btn': btn }, methods:{ // add:function (mon,event) { add:function (mon) { if(mon!=0){ this.num+=parseInt(mon); }else { this.num++; }; // console.log(event); } } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <div v-text="num"></div> <!--<p><button @click="add(money,$event)">add</button></p>--> <p><button @click="add(money)">add</button></p> <p><input type="text" v-model="money"></p> <btn @click.native="add(money)"></btn> </div> <div><button onclick="app.add(2)">外部add</button></div> <template id="t1"> <div> <button >add</button> </div> </template> <script type="text/javascript"> var btn={ template:'#t1' }; var app=new Vue({ el:'#app', data:{ num:0, money:'' }, components:{ 'btn': btn }, methods:{ // add:function (mon,event) { add:function (mon) { if(mon!=0){ this.num+=parseInt(mon); }else { this.num++; }; // console.log(event); } } }) </script> </body> </html>
使用方法和正常的javascript传递参数的方法一样,分为两部:
1、在methods的方法中进行声明,比如我们给add方法加上一个num参数,就要写出add:function(num){}.
2、调用方法时直接传递,比如我们要传递2这个参数,我们在button上就直接可以写。<button @click=”add(2)”></button>.
3、在add()里面出来可以传递2以外还可以传递$event这个参数,这个参数包括很多mouseEvent操作
4、如果自定义组件时想要使用构造器里方法,必须使用@click.native=add()方法,native选项可以保证自定义组件可以使用构造器内方法
5、如果外部标签想要使用构造器方法,可以直接采用onclick='app.add()'方法直接调用
4、watch选项
数据变化的监控经常使用,我们可以先来看一个简单的数据变化监控的例子。例如天气预报的穿衣指数,它主要是根据温度来进行提示的
demo如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <p>温度:<span v-text="temp"></span></p> <p>穿衣建议:<span v-text="sugg"></span></p> <div><button @click="incrtemp">升高温度</button><button @click="redutemp">降低温度</button></div> </div> <script type="text/javascript"> var suggs=['棉衣','夏装','春装','秋装'] var app=new Vue({ el:'#app', data:{ temp:0, sugg:suggs[0] }, methods:{ incrtemp:function () { this.temp+=5; }, redutemp:function () { this.temp-=5; } }, watch:{ temp:function (newVal,oldVal) {//newVal是temp新数值,oldVal是temp原值 if(newVal <=0){ this.sugg=suggs[0]; }else if(newVal>0 && newVal<=16){ this.sugg=suggs[2]; }else if(newVal>16 && newVal<26){ this.sugg=suggs[3] }else{ this.sugg=suggs[1] } } } }) </script> </body> </html>
其中watch:{
temp:function(newVal,oldVal){} //newVal表示temp这个变量的新变化的值,而oldVal表示temp这个变量原来的值
}
为了降低耦合度,还可以将watch放在构造器外部书写:
app.$watch('temp',function (newVal,oldVal) { if(newVal <=0){ this.sugg=suggs[0]; }else if(newVal>0 && newVal<=16){ this.sugg=suggs[2]; }else if(newVal>16 && newVal<26){ this.sugg=suggs[3] }else{ this.sugg=suggs[1] }})
5、mixins混入选项
Mixins一般有两种用途:
1、在你已经写好了构造器后,需要增加方法或者临时的活动时使用的方法,这时用混入会减少源代码的污染。
2、很多地方都会用到的公用方法,用混入的方法可以减少代码量,实现代码重用。
在mixins插入后,mixin插入优先于构造器的updated,但是同时全局的mixin优先于普通的mixin
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> {{num}} <p><button @click="add(4)">add</button></p> </div> <script type="text/javascript"> var addCon = { updated:function () { console.log('混入方式:'+this.num); } }; Vue.mixin({ updated:function(){ console.log('我是全局被混入的'); } }); var app=new Vue({ el:'#app', data:{ num: 0 }, methods:{ add:function (val) { console.log('原生的方式执行') this.num+=parseInt(val) } }, updated:function () { console.log('原生的更新执行') }, mixins:[addCon] }) </script> </body> </html>
6、extends选项
通过外部增加对象的形式,对构造器进行扩展
extends:后面只能跟一个扩展选项名
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> {{num}} <p><button @click="add(4)">add</button></p> </div> <script type="text/javascript"> var extendobj = { updated:function () { console.log('extend选项插入') } }; var app=new Vue({ el:'#app', data:{ num: 0 }, methods:{ add:function (val) { console.log('原生的方式执行'); this.num+=parseInt(val) } }, updated:function () { console.log('原生的更新执行') }, extends: extendobj }) </script> </body> </html>
7、delimiters
delimiters的作用是改变我们插值的符号。Vue默认的插值是双大括号{{}}。但有时我们会有需求更改这个插值的形式
delimiters:['${','}']
四、实例与内部组件
实例就是在构造器外部操作构造器内部的属性选项或者方法,就叫做实例?实例的作用就是给原生的或者其他javascript框架一个融合的接口或者说是机会,让Vue和其他框架一起使用
1、Vue与Jquery配合
注意:在调用时必须通过钩子函数mounted或者updated调用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> <script type="text/javascript" src="jquery-3.2.1.min.js"></script> </head> <body> <div id="app"> {{message}} </div> <script type="text/javascript"> var app=new Vue({ el:'#app', data:{ message: 'hello world!!!' }, mounted:function () { $('#app').html('这是jquery!') } }) </script> </body> </html>
对于内部方法可以通过外部引用方式调用
app.add();//外部可以通过实例方式调用
2、$mount() $destroy()$forceUpdate() $nextTick()方法
mount():
$mount方法是用来挂载我们的扩展的
$mount需要通过Vue.extend来完成扩展
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> </div> <template id="t1"> <div> <span v-text="message"></span> </div> </template> <script type="text/javascript"> var mountExt = Vue.extend({ template: '#t1', data:function () { return{ message: 'hello world!' } } }); var vm = new mountExt().$mount('#app') </script> </body> </html>
$destroy()
用$destroy()进行卸载
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> </div> <p><button onclick="destroy()">卸载</button></p> <template id="t1"> <div> <span v-text="message"></span> </div> </template> <script type="text/javascript"> var mountExt = Vue.extend({ template: '#t1', data:function () { return{ message: 'hello world!' } }, mounted:function () { console.log('绑定') }, destroyed:function () { console.log('卸载完毕'); } }); var vm = new mountExt().$mount('#app'); function destroy() { vm.$destroy(); } </script> </body> </html>
$forceUpdate()
vm.$forceUpdate()
$nextTick()
修改数据会自动触发
function tick() {
vm.message='修改数据'
vm.$nextTick(function(){
console.log('message更新完后我被调用了');
})
}
完整demo如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"></div> <p><button onclick="destroy()">卸载</button></p> <p><button onclick="reload()">更新</button></p> <p><button onclick="tick()">修改数据</button></p> <template id="t1"> <div> <span v-text="message"></span> </div> </template> <script type="text/javascript"> var mountExt = Vue.extend({ template: '#t1', data:function () { return{ message: 'hello world!' } }, mounted:function () { console.log('绑定') }, updated:function () { console.log('数据更新') }, destroyed:function () { console.log('卸载完毕'); } }); var vm = new mountExt().$mount('#app'); function destroy() { vm.$destroy(); } function reload() { vm.$forceUpdate(); } function tick() { vm.message='修改数据' vm.$nextTick(function(){ console.log('message更新完后我被调用了'); }) } </script> </body> </html>
3、实例事件
实例事件就是在构造器外部写一个调用构造器内部的方法。这样写的好处是可以通过这种写法在构造器外部调用构造器内部的数据。
我们还是写一个点击按钮,持续加1的例子
1、$on
在构造器外部添加事件, $on接收两个参数,第一个参数是调用时的事件名称,第二个参数是一个匿名方法。如果按钮在作用域外部,可以利用$emit来执行
app.$on('reduce',function (num) {
this.message -=num;
});
function reduce(num) {
app.$emit('reduce',num);
};
2、$once
执行一次的事件
app.$once('reduceOnce',function (num) {
console.log('执行了reduceOnce()');
this.message -=num;
});
function reduceOnce(num) {
app.$emit('reduceOnce',num);
}
3、$off
关闭事件
function off() {
app.$off('reduce')
}
完整代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> {{message}} <div> <button @click="add(2)">add</button> </div> </div> <div><button onclick="reduce(2)">reduce</button></div> <div><button onclick="reduceOnce(2)">reduceOnce</button></div> <div><button onclick="off()">off</button></div> <script type="text/javascript"> var app=new Vue({ el:'#app', data:{ message: 0 }, methods:{ add:function (num) { console.log('执行了add()'); this.message+=parseInt(num); } } }); app.$on('reduce',function (num) { console.log('执行了reduce()'); this.message -=num; }); function reduce(num) { app.$emit('reduce',num); }; app.$once('reduceOnce',function (num) { console.log('执行了reduceOnce()'); this.message -=num; }); function reduceOnce(num) { app.$emit('reduceOnce',num); }; function off() { app.$off('reduce') } </script> </body> </html>
4、内置组件-slot
slot是标签的内容扩展,也就是说你用slot就可以在自定义组件时传递给组件内容,组件接收内容并输出
slot使用分两步:
1、在html组件中用slot属性传递值:
<gavin> <span slot="Url">{{Dataobj.address}}</span> <span slot="netname">{{Dataobj.username}}</span> <span slot="skill">{{Dataobj.skill}}</span> </gavin>
2、在组件模板中使用slot标签接收属性值
<template id="t1"> <div> <p>地址:<slot name="Url"></slot></p> <p>网名:<slot name="netname"></slot></p> <p>技能:<slot name="skill"></slot></p> </div> </template>
完整demo:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="vue.js"></script> </head> <body> <div id="app"> <gavin> <span slot="Url">{{Dataobj.address}}</span> <span slot="netname">{{Dataobj.username}}</span> <span slot="skill">{{Dataobj.skill}}</span> </gavin> </div> <template id="t1"> <div> <p>地址:<slot name="Url"></slot></p> <p>网名:<slot name="netname"></slot></p> <p>技能:<slot name="skill"></slot></p> </div> </template> <script type="text/javascript"> var gavin={ template:'#t1' }; var app=new Vue({ el:'#app', data:{ Dataobj:{ 'address': 'https://www.baidu.com', 'username': 'gavin', 'skill': 'python' } }, components:{ 'gavin':gavin } }) </script> </body> </html>
五、vue-cli
安装vue-cli的前提是你已经安装了npm,安装npm你可以直接下载node的安装包进行安装。你可以在命令行工具里输入npm -v 检测你是否安装了npm和版本情况。出现版本号说明你已经安装了npm和node,如果该命令不可以使用,需要安装node软件包,根据你的系统版本选择下载安装就可以了。
下载地址:http://nodejs.cn/download/
npm没有问题,接下来我们可以用npm 命令安装vue-cli了:
npm install vue-cli -g
-g :代表全局安装。如果你安装时报错,一般是网络问题,你可以尝试用cnpm来进行安装。安装完成后,可以用vue -V来进行查看 vue-cli的版本号。注意这里的V是大写的,如果vue -V的命令管用了,说明已经顺利的把vue-cli安装到我们的计算机里了
我们用vue init命令来初始化项目,具体看一下这条命令的使用方法。
vue init <template-name> <project-name>
vue init webpack vuecliTest
init:表示我要用vue-cli来初始化项目
<template-name>:表示模板名称,vue-cli官方为我们提供了5种模板,
webpack-一个全面的webpack+vue-loader的模板,功能包括热加载,linting,检测和CSS扩展。
webpack-simple-一个简单webpack+vue-loader的模板,不包含其他功能,让你快速的搭建vue的开发环境。
browserify-一个全面的Browserify+vueify 的模板,功能包括热加载,linting,单元检测。
browserify-simple-一个简单Browserify+vueify的模板,不包含其他功能,让你快速的搭建vue的开发环境。
simple-一个最简单的单页应用模板。
<project-name>:标识项目名称,这个你可以根据自己的项目来起名字。
在实际开发中,一般我们都会使用webpack这个模板,那我们这里也安装这个模板,在命令行输入以下命令:
输入命令后,会询问我们几个简单的选项,我们根据自己的需要进行填写就可以了。
- Project name :项目名称 ,如果不需要更改直接回车就可以了。注意:这里不能使用大写,所以我把名称改成了vueclitest
- Project description:项目描述,默认为A Vue.js project,直接回车,不用编写。
- Author:作者,如果你有配置git的作者,他会读取。
- Install vue-router? 是否安装vue的路由插件,我们这里需要安装,所以选择Y
- Use ESLint to lint your code? 是否用ESLint来限制你的代码错误和风格。我们这里不需要输入n,如果你是大型团队开发,最好是进行配置。
- setup unit tests with Karma + Mocha? 是否需要安装单元测试工具Karma+Mocha,我们这里不需要,所以输入n。
- Setup e2e tests with Nightwatch?是否安装e2e来进行用户行为模拟测试,我们这里不需要,所以输入n。
命令行出现上面的文字,说明我们已经初始化好了第一步。命令行提示我们现在可以作的三件事情。
1、cd vuecliTest 进入我们的vue项目目录。
2、npm install 安装我们的项目依赖包,也就是安装package.json里的包,如果你网速不好,你也可以使用cnpm来安装。
3、npm run dev 开发模式下运行我们的程序。给我们自动构建了开发用的服务器环境和在浏览器中打开,并实时监视我们的代码更改,即时呈现给我们。
vue-cli文件解释:
vue-cli脚手架工具就是为我们搭建了开发所需要的环境,为我们省去了很多精力。有必要对这个环境进行熟悉,我们就从项目的结构讲起。
Ps:由于版本实时更新和你选择安装的不同(这里列出的是模板为webpack的目录结构),所以你看到的有可能和下边的有所差别。
.
|-- build // 项目构建(webpack)相关代码
| |-- build.js // 生产环境构建代码
| |-- check-version.js // 检查node、npm等版本
| |-- dev-client.js // 热重载相关
| |-- dev-server.js // 构建本地服务器
| |-- utils.js // 构建工具相关
| |-- webpack.base.conf.js // webpack基础配置
| |-- webpack.dev.conf.js // webpack开发环境配置
| |-- webpack.prod.conf.js // webpack生产环境配置
|-- config // 项目开发环境配置
| |-- dev.env.js // 开发环境变量
| |-- index.js // 项目一些配置变量
| |-- prod.env.js // 生产环境变量
| |-- test.env.js // 测试环境变量
|-- src // 源码目录
| |-- components // vue公共组件
| |-- store // vuex的状态管理
| |-- App.vue // 页面入口文件
| |-- main.js // 程序入口文件,加载各种公共组件
|-- static // 静态文件,比如一些图片,json数据等
| |-- data // 群聊分析得到的数据用于数据可视化
|-- .babelrc // ES6语法编译配置
|-- .editorconfig // 定义代码格式
|-- .gitignore // git上传需要忽略的文件格式
|-- README.md // 项目说明
|-- favicon.ico
|-- index.html // 入口页面
|-- package.json // 项目基本信息
重要文件讲解:
package.json
package.json文件是项目根目录下的一个文件,定义该项目开发所需要的各种模块以及一些项目配置信息(如项目名称、版本、描述、作者等)。
package.json 里的scripts字段,这个字段定义了你可以用npm运行的命令。在开发环境下,在命令行工具中运行npm run dev 就相当于执行 node build/dev-server.js .也就是开启了一个node写的开发行建议服务器。由此可以看出script字段是用来指定npm相关命令的缩写。
"scripts": {
"dev": "node build/dev-server.js",
"build": "node build/build.js"
},
在执行完npm run build命令后,在你的项目根目录生成了dist文件夹,这个文件夹里边就是我们要传到服务器上的文件。
dist文件夹下目录包括:
- index.html 主页文件:因为我们开发的是单页web应用,所以说一般只有一个html文件。
- static 静态资源文件夹:里边js、CSS和一些图片。
main.js是整个项目的入口文件,在src文件夹下:
import Vue from 'vue' import App from './App' import router from './router' Vue.config.productionTip = false //生产环境提示,这里设置成了false /* eslint-disable no-new */ new Vue({ el: '#app', router, template: '<App/>', components: { App } })
通过代码可以看出这里引进了App的组件和<App/>的模板,它是通过 import App from ‘./App’这句代码引入的。 我们找到App.vue文件,打开查看
App.vue文件
<template> <div id="app"> <img src="./assets/logo.png"> <router-view></router-view> </div> </template> <script> export default { name: 'app' } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
app.vue文件我们可以分成三部分解读,
- <template></template>标签包裹的内容:这是模板的HTMLDom结构,里边引入了一张图片和<router-view></router-view>标签,<router-view>标签说明使用了路由机制。我们会在以后专门拿出一篇文章讲Vue-router。
- <script></script>标签包括的js内容:你可以在这里些一些页面的动态效果和Vue的逻辑代码。
- <style></style>标签包裹的css内容:这里就是你平时写的CSS样式,对页面样子进行装饰用的,需要特别说明的是你可以用<style scoped></style>来声明这些css样式只在本模板中起作用。
router/index.js 路由文件
引文在app.vue中我们看到了路由文件,虽然router的内容比较多,但是我们先简单的看一下。下篇文章我们就开始讲Vue-router
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '@/components/Hello'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello
}
]
})
我们可以看到 import Hello from ‘@/components/Hello’这句话, 文件引入了/components/Hello.vue文件。这个文件里就配置了一个路由,就是当我们访问网站时给我们显示Hello.vue的内容
Hello.vue文件解读:
这个文件就是我们在第一节课看到的页面文件了。也是分为<template><script><style>三个部分,以后我们大部分的工作都是写这些.vue结尾的文件。现在我们可以试着改一些内容,然后预览一下。
<template> <div class="hello"> <h1>{{ msg }}</h1> <h2>Essential Links</h2> <ul> <li><a href="https://vuejs.org" target="_blank">Core Docs</a></li> <li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li> <li><a href="https://gitter.im/vuejs/vue" target="_blank">Gitter Chat</a></li> <li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li> <br> <li><a href="http://vuejs-templates.github.io/webpack/" target="_blank">Docs for This Template</a></li> </ul> <h2>Ecosystem</h2> <ul> <li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li> <li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li> <li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li> <li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li> </ul> </div> </template> <script> export default { name: 'hello', data () { return { msg: 'Welcome to Your Vue.js App' } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h1, h2 { font-weight: normal; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
六、vue-router
由于Vue在开发时对路由支持的不足,后来官方补充了vue-router插件,它在Vue的生态环境中非常重要,在实际开发中只要编写一个页面就会操作vue-router。要学习vue-router就要先知道这里的路由是什么?这里的路由并不是指我们平时所说的硬件路由器,这里的路由就是SPA(单页应用)的路径管理器。再通俗的说,vue-router就是我们WebApp的链接路径管理系统
有的小伙伴会有疑虑,为什么我们不能像原来一样直接用<a></a>标签编写链接哪?因为我们用Vue作的都是单页应用,就相当于只有一个主的index.html页面,所以你写的<a></a>标签是不起作用的,你必须使用vue-router来进行管理
安装vue-router
vue-router是一个插件包,所以我们还是需要用npm来进行安装的。打开命令行工具,进入你的项目目录,输入下面命令。
npm install vue-router --save-dev
如果你安装很慢,也可以用cnpm进行安装,如果你在使用vue-cli中已经选择安装了vue-router,那这里不需要重复安装了
解读router/index.js文件
我们用vue-cli生产了我们的项目结构,你可以在src/router/index.js文件,这个文件就是路由的核心文件
import Vue from 'vue' //引入Vue import Router from 'vue-router' //引入vue-router import Hello from '@/components/Hello' //引入根目录下的Hello.vue组件 Vue.use(Router) //Vue全局使用Router export default new Router({ routes: [ //配置路由,这里是个数组 { //每一个链接都是一个对象 path: '/', //链接路径 name: 'Hello', //路由名称, component: Hello //对应的组件模板 } ] })
上边的代码中已经对每行都进行了注释,其实在这个路由文件里只配置了一个功能,就是在进入项目时,显示Hello.vue里边的内容代码。
增加一个Hi的路由和页面
对路由的核心文件熟悉后,我们试着增加一个路由配置,我们希望在地址栏输入 http://localhost:8080/#/hi 的时候出现一个新的页面,先来看一下我们希望得到的效果。
我们看一下具体的操作步骤:
- 在src/components目录下,新建 Hi.vue 文件。
- 编写文件内容,和我们之前讲过的一样,文件要包括三个部分<template><script>和<style>。文件很简单,只是打印一句话。
<template> <div class="hello"> <h1>{{ msg }}</h1> </div> </template> <script> export default { name: 'hi', data () { return { msg: 'Hi, I am JSPang' } } } </script> <style scoped> </style>
- 引入 Hi组件:我们在router/index.js文件的上边引入Hi组件
import Hi from '@/components/Hi'
- 增加路由配置:在router/index.js文件的routes[]数组中,新增加一个对象,代码如下。
{ path:'/hi', name:'Hi', component:Hi }
通过上面的配置已经可以增加一个新的页面了。是不是觉的自己的Vue功力一下子就提升了一个档次。为了方便小伙伴查看,贴出现在的路由配置文件:
import Vue from 'vue' //引入Vue import Router from 'vue-router' //引入vue-router import Hello from '@/components/Hello' //引入根目录下的Hello.vue组件 import Hi from '@/components/Hi' Vue.use(Router) //Vue全局使用Router export default new Router({ routes: [ //配置路由,这里是个数组 { //每一个链接都是一个对象 path: '/', //链接路径 name: 'Hello', //路由名称, component: Hello //对应的组件模板 },{ path:'/hi', name:'Hi', component:Hi } ] })
router-link制作导航
现在通过在地址栏改变字符串地址,已经可以实现页面内容的变化了。这并不满足需求,我们需要的是在页面上有个像样的导航链接,我们只要点击就可以实现页面内容的变化。制作链接需要<router-link>标签,我们先来看一下它的语法。
<router-link to="/">[显示字段]</router-link>
- to:是我们的导航路径,要填写的是你在router/index.js文件里配置的path值,如果要导航到默认首页,只需要写成 to=”/” ,
- [显示字段] :就是我们要显示给用户的导航名称,比如首页 新闻页。
明白了router-link的基本语法,我们在 src/App.vue文件中的template里加入下面代码,实现导航。
<p>导航 :
<router-link to="/">首页</router-link>
<router-link to="/hi">Hi页面</router-link>
</p>
配置路由子系统
子路由的情况一般用在一个页面有他的基础模版,然后它下面的页面都隶属于这个模版,只是部分改变样式
步骤:
1、改造App.vue的导航代码
App.vue代码
<p>导航 :
<router-link to="/">首页</router-link> |
<router-link to="/hi">Hi页面</router-link> |
<router-link to="/hi/hi1">-Hi页面1</router-link> |
<router-link to="/hi/hi2">-Hi页面2</router-link>
</p>
2、改写components/Hi.vue页面
把Hi.vue改成一个通用的模板,加入<router-view>标签,给子模板提供插入位置。“Hi页面1” 和 “Hi页面2” 都相当于“Hi页面”的子页面,有点想继承关系。我们在“Hi页面”里加入<router-view>标签。
components/Hi.vue,就是第5行的代码,其他代码不变
<template> <div class="hello"> <h1>{{ msg }}</h1> <router-view class="aaa"></router-view> </div> </template> <script> export default { name: 'hi', data () { return { msg: 'Hi, I am JSPang' } } } </script> <style scoped> </style>
3、在components目录下新建两个组件模板 Hi1.vue 和 Hi2.vue
新建的模板和Hi.vue没有太多的差别,知识改变了data中message的值,也就是输出的结果不太一样了。
Hi1.vue
<template> <div class="hello"> <h1>{{ msg }}</h1> </div> </template> <script> export default { name: 'hi', data () { return { msg: 'Hi, I am Hi1!' } } } </script> <style scoped> </style>
Hi2.vue
<template> <div class="hello"> <h1>{{ msg }}</h1> </div> </template> <script> export default { name: 'hi', data () { return { msg: 'Hi, I am Hi2' } } } </script> <style scoped> </style>
4、修改router/index.js代码
我们现在导航有了,母模板和子模板也有了,只要改变我们的路由配置文件就可以了。子路由的写法是在原有的路由配置下加入children字段。
children:[ {path:'/',component:xxx}, {path:'xx',component:xxx}, ]
children字段后边跟的是个数组,数组里和其他配置路由基本相同,需要配置path和component。具体看一下这个子路由的配置写法。
import Vue from 'vue' import Router from 'vue-router' import Hello from '@/components/Hello' import Hi from '@/components/Hi' import Hi1 from '@/components/Hi1' import Hi2 from '@/components/Hi2' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Hello', component: Hello },{ path:'/hi', component:Hi, children:[ {path:'/',component:Hi}, {path:'hi1',component:Hi1}, {path:'hi2',component:Hi2}, ] } ] })
vue-router如何传递参数
开发中,参数的传递是个最基本的业务需求。通过URL地址来传递参数是一个形式,我们先想象一个基本需求,就是在我们点击导航菜单时,跳转页面上能显示出当前页面的路径,来告诉用户你想在所看的页面位置(类似于面包屑导航)。
1、用name传递参数
两步完成用name传值并显示在模板里:
a、在路由文件src/router/index.js里配置name属性。
{ path: 'hi1', name: 'Hi1', component: Hi1, },
b、模板里(src/App.vue)用$router.name的形势接收,比如直接在模板中显示:
<p>{{ $route.name }}</p>
2、通过<router-link> 标签中的to传参
我们用<router-link>标签中的to属性进行传参,需要您注意的是这里的to要进行一个绑定,写成:to。
<router-link :to="{name:xxx,params:{key:value}}">valueString</router-link>
这里的to前边是带冒号的,然后后边跟的是一个对象形势的字符串.
- name:就是我们在路由配置文件中起的name值。
- params:就是我们要传的参数,它也是对象形势,在对象里可以传递多个值。
了解基本的语法后,我们改造一下我们的src/App.vue里的<router-link>标签,我们把hi1页面的<router-link>进行修改。
<router-link :to="{name:'hi1',params:{username:'gavin'}}">Hi页面1</router-link>
把src/reouter/index.js文件里给hi1配置的路由起个name,就叫hi1.
{path:'/hi1',name:'hi1',component:Hi1},
最后在模板里(src/components/Hi1.vue)用$route.params.username进行接收.
{{$route.params.username}}
4、单页面多路由操作
实际需求是这样的,在一个页面里我们有2个以上<router-view>区域,我们通过配置路由的js文件,来操作这些区域的内容。例如我们在src/App.vue里加上两个<router-view>标签。我们用vue-cli建立了新的项目,并打开了src目录下的App.vue文件,在<router-view>下面新写了两行<router-view>标签,并加入了些CSS样式。
<router-view ></router-view> <router-view name="left" style="float:left;50%;background-color:#ccc;height:300px;"></router-view> <router-view name="right" style="float:right;50%;background-color:#c0c;height:300px;"></router-view>
现在的页面中有了三个<router-view>标签,也就是说我们需要在路由里配置这三个区域,配置主要是在components字段里进行。
import Vue from 'vue' import Router from 'vue-router' import Hello from '@/components/Hello' import Hi1 from '@/components/Hi1' import Hi2 from '@/components/Hi2' Vue.use(Router) export default new Router({ routes: [ { path: '/', components: { default:Hello, left:Hi1, right:Hi2 } },{ path: '/Hi', components: { default:Hello, left:Hi2, right:Hi1 } } ] })
上边的代码我们编写了两个路径,一个是默认的‘/’,另一个是’/Hi’.在两个路径下的components里面,我们对三个区域都定义了显示内容。
定义好后,我们需要在component文件夹下,新建Hi1.vue和Hi2.vue页面就可以了。
Hi1.vue
<template> <div> <h2>{{ msg }}</h2> </div> </template> <script> export default { name: 'hi1', data () { return { msg: 'I am Hi1 page.' } } } </script>
H2.vue
<template> <div> <h2>{{ msg }}</h2> </div> </template> <script> export default { name: 'hi2', data () { return { msg: 'I am Hi2 page.' } } } </script>
最后在App.vue中配置我们的<router-link>就可以了
<router-link to="/">首页</router-link> | <router-link to="/hi">Hi页面</router-link> |
5、利用url传参
在实际开发也是有很多用URL传值的需求,比如我们在新闻列表中有很多新闻标题整齐的排列,我们需要点击每个新闻标题打开不同的新闻内容,这时在跳转路由时跟上新闻编号就十分实用。
:冒号的形式传递参数
我们可以在路由配置文件里以:冒号的形式传递参数,这就是对参数的绑定。
- 在配置文件里以冒号的形式设置参数。我们在/src/router/index.js文件里配置路由。
{ path:'/params/:newsId/:newsTitle', component:Params }
我们需要传递参数是新闻ID(newsId)和新闻标题(newsTitle).所以我们在路由配置文件里制定了这两个值。
2. 在src/components目录下建立我们params.vue组件,也可以说是页面。我们在页面里输出了url传递的的新闻ID和新闻标题。
<template> <div> <h2>{{ msg }}</h2> <p>新闻ID:{{ $route.params.newsId}}</p> <p>新闻标题:{{ $route.params.newsTitle}}</p> </div> </template> <script> export default { name: 'params', data () { return { msg: 'params page' } } } </script>
3. 在App.vue文件里加入我们的<router-view>标签。这时候我们可以直接利用url传值了。
<router-link to="/params/123/gavin website is very good">params</router-link> |
正则表达式在URL传值中的应用
上边的例子,我们传递了新闻编号,现在需求升级了,我们希望我们传递的新闻ID只能是数字的形式,这时候我们就需要在传递时有个基本的类型判断,vue是支持正则的。
加入正则需要在路由配置文件里(/src/router/index.js)以圆括号的形式加入。
path:'/params/:newsId(\d+)/:newsTitle',
6、vue-router的redirect
开发中有时候我们虽然设置的路径不一致,但是我们希望跳转到同一个页面,或者说是打开同一个组件。这时候我们就用到了路由的重新定向redirect参数。
redirect基本重定向
我们只要在路由配置文件中(/src/router/index.js)把原来的component换成redirect参数就可以了。我们来看一个简单的配置export default new Router({ routes: [ {
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
components: {
HelloWorld : HelloWorld
}
},
{
path: '/params/:newsId(\d{1,4})/:newsTitle',
name: 'Params',
component: Params
},
{
path: '/gohome',
name: 'GOHOME',
redirect: '/' //重定向使用redirect参数
},
{
path: '/goparams/:newsId(\d{1,4})/:newsTitle',//带参数的重定向只要照搬初始值即可
name: 'GoParams',
redirect: '/params/:newsId(\d{1,4})/:newsTitle'//带参数重定向照搬初始值即可
}
]
})
这里我们设置了goback路由,但是它并没有配置任何component(组件),而是直接redirect到path:’/’下了,这就是一个简单的重新定向。
重定向时传递参数
我们已经学会了通过url来传递参数,那我们重定向时如果也需要传递参数怎么办?其实vue也已经为我们设置好了,我们只需要在ridirect后边的参数里复制重定向路径的path参数就可以了。可能你看的有点晕,我们来看一段代码:
{ path:'/params/:newsId(\d+)/:newsTitle', component:Params },{ path:'/goParams/:newsId(\d+)/:newsTitle', redirect:'/params/:newsId(\d+)/:newsTitle' }
已经有了一个params路由配置,我们在设置一个goParams的路由重定向,并传递了参数。这时候我们的路由参数就可以传递给params.vue组件了。参数接收方法和正常的路由接收方法一样。
7、alias别名的使用
alias也可以实现类似重定向的效果
1.首先我们在路由配置文件里(/src/router/index.js),给上节课的Home路径起一个别名
{ path: '/hi1', component: Hi1, alias:'/gavin' }
2.配置我们的<router-link>,起过别名之后,可以直接使用<router-link>标签里的to属性,进行重新定向。
<router-link to="/gavin">gavin</router-link>
redirect和alias的区别
- redirect:仔细观察URL,redirect是直接改变了url的值,把url变成了真实的path路径。
- alias:URL路径没有别改变,这种情况更友好,让用户知道自己访问的路径,只是改变了<router-view>中的内容。
填个小坑:
别名请不要用在path为’/’中,如下代码的别名是不起作用的。
{ path: '/', component: Hello, alias:'/home' }
8、路由的过渡动画
<transition>标签
想让路由有过渡动画,需要在<router-view>标签的外部添加<transition>标签,标签还需要一个name属性。
<transition name="fade"> <router-view ></router-view> </transition>
我们在/src/App.vue文件里添加了<transition>标签,并给标签起了一个名字叫fade。
css过渡类名:
组件过渡过程中,会有四个CSS类名进行切换,这四个类名与transition的name属性有关,比如name=”fade”,会有如下四个CSS类名:
- fade-enter:进入过渡的开始状态,元素被插入时生效,只应用一帧后立刻删除。
- fade-enter-active:进入过渡的结束状态,元素被插入时就生效,在过渡过程完成后移除。
- fade-leave:离开过渡的开始状态,元素被删除时触发,只应用一帧后立刻删除。
- fade-leave-active:离开过渡的结束状态,元素被删除时生效,离开过渡完成后被删除。
从上面四个类名可以看出,fade-enter-active和fade-leave-active在整个进入或离开过程中都有效,所以CSS的transition属性在这两个类下进行设置。
那我们就在App.vue页面里加入四种CSS样式效果,并利用CSS3的transition属性控制动画的具体效果。代码如下:
.fade-enter { opacity:0; } .fade-leave{ opacity:1; } .fade-enter-active{ transition:opacity .5s; } .fade-leave-active{ opacity:0; transition:opacity .5s;
上边的代码设置了改变透明度的动画过渡效果,但是默认的mode模式in-out模式,这并不是我们想要的。下面我们学一下mode模式。
过渡模式mode:
- in-out:新元素先进入过渡,完成之后当前元素过渡离开。
- out-in:当前元素先进行过渡离开,离开完成后新元素过渡进入。
<transition name="fade" mode="out-in"> <router-view/> </transition>
9、mode模式与404页面处理
mode的两个值
- histroy:当你使用 history 模式时,URL 就像正常的 url,例如 http://jsapng.com/lms/,也好看!
- hash:默认’hash’值,但是hash看起来就像无意义的字符排列,不太好看也不符合我们一般的网址浏览习惯。
export default new Router({ mode: 'history', routes: [ { path: '/', name: 'HelloWorld', component: HelloWorld }, { path: '/params/:newsId(\d{1,4})/:newsTitle', name: 'Params', component: Params }, { path: '/gohome', name: 'GOHOME', redirect: '/' //重定向使用redirect参数 }, { path: '/goparams/:newsId(\d{1,4})/:newsTitle',//带参数的重定向只要照搬初始值即可 name: 'GoParams', redirect: '/params/:newsId(\d{1,4})/:newsTitle'//带参数重定向照搬初始值即可 }, { path: '/hi1', name: 'Hi1', component: Hi1, alias: '/gavin' } ] })
404页面的设置:
用户会经常输错页面,当用户输错页面时,我们希望给他一个友好的提示,为此美工都会设计一个漂亮的页面,这个页面就是我们常说的404页面。vue-router也为我们提供了这样的机制.
1.设置我们的路由配置文件(/src/router/index.js):
{ path:'*', component:Error }
这里的path:’*’就是找不到页面时的配置,component是我们新建的一个Error.vue的文件。
2.新建404页面:
在/src/components/文件夹下新建一个Error.vue的文件。简单输入一些有关错误页面的内容。
<template> <div> <h2>{{ msg }}</h2> </div> </template> <script> export default { data () { return { msg: 'Error:404' } } } </script>
10、路由的钩子函数
一个组件从进入到销毁有很多的钩子函数,同样在路由中也设置了钩子函数。路由的钩子选项可以写在路由配置文件中,也可以写在我们的组件模板中。我们这节课就介绍这两种钩子函数的写法。
路由配置文件中的钩子函数
我们可以直接在路由配置文件(/src/router/index.js)中写钩子函数。但是在路由文件中我们只能写一个beforeEnter,就是在进入此路由配置时。先来看一段具体的代码:
{ path:'/params/:newsId(\d+)/:newsTitle', component:Params, beforeEnter:(to,from,next)=>{ console.log('我进入了params模板'); console.log(to); console.log(from); next(); },
我们在params路由里配置了bdforeEnter得钩子函数,函数我们采用了ES6的箭头函数,需要传递三个参数。我们并在箭头函数中打印了to和from函数。具体打印内容可以在控制台查看object。
三个参数:
- to:路由将要跳转的路径信息,信息是包含在对像里边的。
- from:路径跳转前的路径信息,也是一个对象的形式。
- next:路由的控制参数,常用的有next(true)和next(false),最重要的next({path ,'XXX'}) , XXX代表要重定向的url,这表示我们如果想重定向除了refdirect和alias还有一种选择
写在模板中的钩子函数
在配置文件中的钩子函数,只有一个钩子-beforeEnter,如果我们写在模板中就可以有两个钩子函数可以使用:
- beforeRouteEnter:在路由进入前的钩子函数。
- beforeRouteLeave:在路由离开前的钩子函数。
export default { name: 'params', data () { return { msg: 'params page' } }, beforeRouteEnter:(to,from,next)=>{ console.log("准备进入路由模板"); next(); }, beforeRouteLeave: (to, from, next) => { console.log("准备离开路由模板"); next(); } } </script>
这是我们写在params.vue模板里的路由钩子函数。它可以监控到路由的进入和路由的离开,也可以轻易的读出to和from的值。
11、编程式导航
this.$router.go(-1) 和 this.$router.go(1)
<button @click="goback">后退</button>
2.在我们的script模块中写入goback()方法,并使用this.$router.go(-1),进行后退操作。
<script> export default { name: 'app', methods:{ goback(){ this.$router.go(-1); } } } </script>
打开浏览器进行预览,这时我们的后退按钮就可以向以前的网页一样后退了。
router.go(1):代表着前进,用法和后退一样
this.$router.push(‘/xxx ‘):
这个编程式导航都作用就是跳转,比如我们判断用户名和密码正确时,需要跳转到用户中心页面或者首页,都用到这个编程的方法来操作路由。
我们设置一个按钮,点击按钮后回到站点首页。
1.先编写一个按钮,在按钮上绑定goHome( )方法。
<button @click="goHome">回到首页</button>
2.在<script>模块里加入goHome方法,并用this.$router.push(‘/’)导航到首页
export default { name: 'app', methods:{ goback(){ this.$router.go(-1); }, goHome(){ this.$router.push('/'); } } }
七、vuex
vuex是一个专门为vue.js设计的集中式状态管理架构。状态?我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态。简单的说就是data中需要共用的属性。比如:我们有几个页面要显示用户名称和用户等级,或者显示用户的地理位置。如果我们不把这些属性设置为状态,那每个页面遇到后,都会到服务器进行查找计算,返回后再显示。在中大型项目中会有很多共用的数据,所以尤大神给我们提供了vuex。
1、入门小实验:
首先安装vuex:
cnpm install vuex --save
新建一个vuex文件夹(这个不是必须的),并在文件夹下新建store.js文件,文件中引入我们的vue和vuex。
import Vue from 'vue';
import Vuex from 'vuex';
使用我们vuex,引入之后用Vue.use进行引用。
Vue.use(Vuex);
通过这三步的操作,vuex就算引用成功了
现在我们store.js文件里增加一个常量对象。store.js文件就是我们在引入vuex时的那个文件。
const state={ count:1 }
用export default 封装代码,让外部可以引用。
export default new Vuex.Store({ state })
新建一个vue的模板,位置在components文件夹下,名字叫count.vue。在模板中我们引入我们刚建的store.js文件,并在模板中用{{$store.state.count}}输出count 的值。
<template> <div> <h2>{{msg}}</h2> <hr/> <h3>{{$store.state.count}}</h3> </div> </template> <script> import store from '@/vuex/store' export default{ data(){ return{ msg:'Hello Vuex', } }, store } </script>
在store.js文件中加入两个改变state的方法
const mutations={ add(state){ state.count++; }, reduce(state){ state.count--; } }
这里的mutations是固定的写法,意思是改变的,我们到时候会用一节课专门讲这个mutations,所以你先不用着急,只知道我们要改变state的数值的方法,必须写在mutations里就可以了。
在count.vue模板中加入两个按钮,并调用mutations中的方法。
<div> <button @click="$store.commit('add')">+</button> <button @click="$store.commit('reduce')">-</button> </div>
这样进行预览就可以实现对vuex中的count进行加减了。
2、state 状态对象
在第1节我们已经写了一个 const state ,这个就是我们说的访问状态对象,它就是我们SPA(单页应用程序)中的共享值。今天我们主要学习状态对象赋值给内部对象,也就是把stroe.js中的值,赋值给我们模板里data中的值。我们有三种赋值方式,我们一个一个来学习一下。
1、computed属性可以在输出前,对data中的值进行改变,我们就利用这种特性把store.js中的state值赋值给我们模板中的data值。
computed:{ count(){ return this.$store.state.count; } }
这里需要注意的是return this.$store.state.count这一句,一定要写this,要不你会找不到$store的。这种写法很好理解,但是写起来是比较麻烦的,那我们来看看第二种写法。
2、mapState的对象赋值
我们首先要用import引入mapState。
import {mapState} from 'vuex';
然后还在computed计算属性里写如下代码:
computed:mapState({ count:state=>state.count })
3、通过mapState数组赋值
computed:mapState(["count"])
这个算是最简单的写法了,在实际项目开发当中也经常这样使用。
3、Mutations状态修改
$store.commit( )
Vuex提供了commit方法来修改状态,我们粘贴出第一节课的代码内容,简单回顾一下,我们在button上的修改方法。
<button @click="$store.commit('add')">+</button> <button @click="$store.commit('reduce')">-</button>
store.js文件:
onst mutations={ add(state){ state.count++; }, reduce(state){ state.count--; } }
值:
这只是一个最简单的修改状态的操作,在实际项目中我们常常需要在修改状态时传值。比如上边的例子,是我们每次只加1,而现在我们要通过所传的值进行相加。其实我们只需要在Mutations里再加上一个参数,并在commit的时候传递就就可以了。我们来看具体代码:
现在store.js文件里给add方法加上一个参数n。添加的地方我已经标黄了。
const mutations={ add(state,n){ state.count+=n; }, reduce(state){ state.count--; } }
在Count.vue里修改按钮的commit( )方法传递的参数,我们传递10,意思就是每次加10.
<p> <button @click="$store.commit('add',10)">+</button> <button @click="$store.commit('reduce')">-</button> </p>
这样两个简单的修改我们就完成了传值,我们可以在浏览器中实验一下了。
模板获取Mutations方法
实际开发中我们也不喜欢看到$store.commit( )这样的方法出现,我们希望跟调用模板里的方法一样调用。
例如:@click=”reduce” 就和没引用vuex插件一样。
要达到这种写法,只需要简单的两部就可以了:
- 在模板count.vue里用import 引入我们的mapMutations:
import { mapState,mapMutations } from 'vuex';
2. 在模板的<script>标签里添加methods属性,并加入mapMutations
methods:mapMutations([ 'add','reduce' ]),
通过上边两步,我们已经可以在模板中直接使用我们的reduce或者add方法了,就像下面这样。
<button @click="reduce">-</button>
备注:
如果想将编写完毕的vue项目上传到服务器上发布,需要在config目录下的index.js文件下找到:
assetsPublicPath: '/', 把它变为assetsPublicPath: './',
然后运行npm run build进行项目打包
4、getters计算过滤操作
getters从表面是获得的意思,可以把他看作在获取数据之前进行的一种再编辑,相当于对数据的一个过滤和加工。你可以把它看作store.js的计算属性。
getters基本用法:
比如我们现在要对store.js文件中的count进行一个计算属性的操作,就是在它输出前,给它加上100.
我们首先要在store.js里用const声明我们的getters属性。
const getters = { count:function(state){ return state.count +=100; } }
写好了gettters之后,我们还需要在Vuex.Store()里引入,由于之前我们已经引入了state盒mutations,所以引入里有三个引入属性。代码如下,
export default new Vuex.Store({ state,mutations,getters })
在store.js里的配置算是完成了,我们需要到模板页对computed进行配置。在vue 的构造器里边只能有一个computed属性,如果你写多个,只有最后一个computed属性可用,所以要对上节课写的computed属性进行一个改造。改造时我们使用ES6中的展开运算符”…”。
computed:{ ...mapState(["count"]), count(){ return this.$store.getters.count; } },
需要注意的是,你写了这个配置后,在每次count 的值发生变化的时候,都会进行加100的操作。
用mapGetters简化模板写法:
我们都知道state和mutations都有map的引用方法把我们模板中的编码进行简化,我们的getters也是有的,我们来看一下代码。
首先用import引入我们的mapGetters
import { mapState,mapMutations,mapGetters } from 'vuex';
在computed属性中加入mapGetters
...mapGetters(["count"])
5、actoin异步修改状态
actions和之前讲的Mutations功能基本一样,不同点是,actions是异步的改变state状态,而Mutations是同步改变状态
在store.js中声明actions
actions是可以调用Mutations里的方法的,我们还是继续在上节课的代码基础上进行学习,在actions里调用add和reduce两个方法。
const actions={
addAction(context,n){
context.commit('add',n);
},
reduceAction({commit},n){
commit('reduce',n)
}
}
在actions里写了两个方法addAction和reduceAction,在方法体里,我们都用commit调用了Mutations里边的方法。细心的小伙伴会发现这两个方法传递的参数也不一样。
- context:上下文对象,这里你可以理解称store本身。
- {commit}:直接把commit对象传递过来,可以让方法体逻辑和代码更清晰明了。
模板中的使用
我们需要在count.vue模板中编写代码,让actions生效。我们先复制两个以前有的按钮,并改成我们的actions里的方法名,分别是:addAction和reduceAction。
<p>
<button @click="addAction(50)">+</button>
<button @click="reduceAction(50)">-</button>
</p>
改造一下我们的methods方法,首先还是用扩展运算符把mapMutations和mapActions加入。
methods:{ ...mapMutations([ 'add','reduce' ]), ...mapActions(['addAction','reduceAction']) },
你还要记得用import把我们的mapActions引入才可以使用。
增加异步检验
我们现在看的效果和我们用Mutations作的一模一样,肯定有的小伙伴会好奇,那actions有什么用,我们为了演示actions的异步功能,我们增加一个计时器(setTimeOut)延迟执行。在addAction里使用setTimeOut进行延迟执行
const actions={
addAction(context,n){
context.commit('add',n);
setTimeout(()=>context.commit('reduce',n),3000);
console.log('我比reduce先执行')
},
reduceAction({commit},n){
commit('reduce',n)
}
}
6、modules模块组
随着项目的复杂性增加,我们共享的状态越来越多,这时候我们就需要把我们状态的各种操作进行一个分组,分组后再进行按组编写。那今天我们就学习一下module:状态管理器的模块组操作。
声明模块组:
在vuex/store.js中声明模块组,我们还是用我们的const常量的方法声明模块组。代码如下:
const moduleA={
state,mutations,getters,actions
}
声明好后,我们需要修改原来 Vuex.Stroe里的值:
export default new Vuex.Store({ modules:{a:moduleA} })
在模板中使用
现在我们要在模板中使用count状态,要用插值的形式写入。
<h3>{{$store.state.a.count}}</h3>
如果想用简单的方法引入,还是要在我们的计算属性中rutrun我们的状态。写法如下:
computed:{ count(){ return this.$store.state.a.count; } },