Vue 学习笔记 学习测试环境可通过 CDN 地址引入 Vue(项目开发和线上环境一般使用 vue-cli、webpack 打包)。
1 <script src ="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js" > </script >
可使用构造函数创建 Vue 实例,即应用的入口:
1 2 3 4 5 6 7 8 9 10 11 12 13 <div id ="app" > {{ msg }} </div > <script > let app = new Vue({ el : '#app' , data : { msg : 'Hello World!' } }) console .log(app.$el) console .log(app.msg) </script >
与 jQuery 对比 使用 jQuery 实现 todo-list:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <div > <ul id ="ul-list" > </ul > </div > <script type ="text/javascript" > var $txtTitle = $('#txt-title' ) $('#btn-submit' ).click(function ( ) { var title = $txtTitle.val() if (!title) return var $li = $('<li>' + title + '</li>' ) $('#ul-list' ).append($li) $txtTitle.val('' ) }) </script >
使用 Vue 实现 todo-list:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <div id ="app" > <div > <input v-model ="title" > <button v-on:click ="add" > submit</button > </div > <div > <ul > <li v-for ="item in list" > {{ item }}</li > </ul > </div > </div > <script type ="text/javascript" > var data = { title : '' , list : [] } new Vue({ el : '#app' , data : data, methods : { add : function ( ) { this .list.push(this .title) this .title = '' } } }) </script >
区别在于:jQuery 以选择器获取并修改 DOM,Vue 以数据与 DOM 元素绑定、DOM 响应数据变化;
即数据和视图分离(开放封闭原则)、以数据驱动视图(DOM 操作被封装)。
生命周期 一些生命周期钩子:
created:实例创建完成后调用,完成了数据的观测等而尚未挂载($el
还不可用)需要初始化处理一些数据时会用到;
mounted:el挂载到实例上后调用,一般第一个业务逻辑会在这里开始。相当于$(document).ready()
;
beforeDestroy:实例销毁之前调用。主要解绑一些使用 addEventListener
监听的事件等;
…
文本插值,表达式 使用双大括号( Mustache 语法)是最基本的文本插值方法,可以将双向绑定的数据实时显示,还可以使用单行 JS 表达式:
1 2 3 4 {{ 1 + 1 }} {{ 6 > 5 ? msg1: msg2 }} {{ var a = 6 }} {{ if (6 > 3 ){} }}
实例:自动刷新的计时器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <div id ="dateApp" > {{ date }} </div > <script > let app = new Vue({ el : '#dateApp' , data : { date : new Date () }, mounted : function ( ) { let _this = this ; this .timer = setInterval ( () => { _this.date = new Date (); }, 1000 ) }, beforeDestroy : function ( ) { if (this .timer) { clearInterval (this .timer) } } }) </script >
过滤器 尾部添加一或多个管道符 |
可对数据进行过滤,常用于格式化文本(字母全部大写、货币千位使用逗号分隔等):
以刚才的计时器为例
在 Vue 实例中定义过滤器函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let plusDate = function (value ) { return value < 10 ? '0' + value: value } let app = new Vue({ el : '#dateApp' , data : { date : new Date () }, filters : { formatDate : function (value ) { let date = new Date (value); let year = date.getFullYear(); let month = plusDate(date.getMonth() + 1 ); let day = plusDate(date.getDate()); return `{year}-{month}-{day}` } }
过滤器中也可以传参数,如 date | formatDate(66,99)
中 formatDate
的第一个和第二个参数,分别对应过滤器函数的第二个和 第三个参数(第一个参数为 date
)
计算属性
在一个计算属性里可以完成复杂的逻辑(运算、函数调用),最终返回一个结果;
计算属性还可以依赖多个 Vue 实例的数据,只要其中任一数据变化,计算属性就会重新执行并更新视图;
可以为计算属性(默认为 getter )定义 getter 和 setter 方法对属性进行读写;
缓存: 不管渲染不渲染,只要计算属性依赖的数据未发生变化,就永远不变(使用计算属性还是 methods
取决于是否需要缓存,当遍历大数组和做大量计算时应当使用计算属性做缓存)。
实例:计算购物车总价
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <div id='app' > {{ price }} </div> <script > let app = new Vue({ el : '#app' , data : { package1 : [ {name : 'x' , price : 6999 , count : 2 }, {name : 'y' , price : 7999 , count : 3 } ], package2 : [ {name : 'x' , price : 2999 , count : 5 }, {name : 'y' , price : 3999 , count : 6 } ] }, computed : { price : function ( ) { let price = 0 for (let i = 0 ; i < this .package1.length; i++) { price += this .package1[i].price * this .package1[i].count; } for (let j = 0 ; j < this .package2.length; j++) { price += this .package2[j].price * this .package2[j].count; } return price } } }) </script >
监听属性变化 除了使用计算属性 computed
以外,还可以使用 watch
监听单个属性的变化,两者对比:
computed:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <div id ="app" > firstName: <input type ="text" v-model ="firstName" > lastName: <input type ="text" v-model ="lastName" > <br > {{ fullName }} </div > <script > let app = new Vue({ el : "#app" , data : { firstName : "" , lastName : "" }, computed : { fullName ( ) { return `${this .firstName} ${this .lastName} ` } } }) </script >
watch:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <div id ="app" > firstName: <input type ="text" v-model ="firstName" > lastName: <input type ="text" v-model ="lastName" > <br > {{ fullName }} </div > <script > let app = new Vue({ el : "#app" , data : { firstName : "" , lastName : "" , fullName : "" }, watch : { firstName (val ) { this .fullName = `${val} ${this .lastName} ` }, lastName (val ) { this .fullName = `${this .firstName} ${val} ` } } }) </script >
基本指令 指令是 Vue 模板中最常用的一项功能,在 HTML 元素中带有前缀 v-
表示,有助于快速完成 DOM 操作,最常用的几个指令:
v-text
:解析文本,和双花括号作用一样
v-html
:解析html元素
v-bind
:动态更新元素上的属性,如 id、class 等,当数据变化时就会重新渲染。
v-on
:绑定事件监听器1 2 <div v-bind:class ='className' > </div > <button v-on:dbclick ='count' > </button >
语法糖:指在不影响功能的情况下,添加某种简洁方法实现同样的效果,从而更加方便程序开发:
v-bind
=》 :
v-on
=》 @
v-bind 绑定值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <div id ='app' > <a v-bind:href ="url" > baidu</a > <img :src ="imgUrl" alt ="" > </div > <script > new Vue({ el : "#app" , data : { url : "http://www.baidu.com" imgUrl : "https://www.baidu.com/img/bd_logo1.png?where=super" } }) </script >
绑定Class(绑定Style同理):
给 vbind:class
设置一个对象/数组可以动态地切换 class ,条件复杂时可以使用计算属性
Vue 中驼峰式命名的大写字母都会被转换成中划线分隔的小写字母(建议统一写驼峰);1 2 <div :class ="{divStyle: isActive, borderStyle: isBorder}" > </div > <div :class ="[activeClass,errorClass]" > </div >
v-cloak 解决初始化慢导致页面闪动的问题,一般与 display: none
结合使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <style > [v-cloak] : { display: none } </style > v-cloak: <p v-cloak: > { { msg } }</p > <script > while (true ) { } new Vue({ el : "#app" , data : { msg : "Hello World!" } }) </script >
v-once 只渲染一次,后续修改不会重新渲染
条件渲染指令:v-if,v-else-if,v-else
v-if
、v-else-if
后接等号和必须返回布尔值的条件,满足条件(结果为 True)才渲染,否则会从 DOM 中移除;
v-if
在渲染元素时出于效率考虑,会尽可能复用已有元素而非重新渲染,所以可能出错(只渲染变化的元素,实例:用户名与密码的 input 框);
上述问题的解决方法:为元素加上 key=keyName
(使元素有差异而重新渲染)。
条件渲染指令:v-show
与 v-if
类似,显现与否取决于布尔值;
与 v-if
的区别在于 v-show
值为 false 时相当于 display: none
,页面上仍然存在该元素。
列表渲染指令:v-for 数组遍历或枚举对象属性时可使用 v-for
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <ul > <li v-for ="item in arr" > {{ item.name }} </li > <li v-for ="(item, index) in arr" > {{ index }}-{{ i.name }} </li > <li v-for ="(value, key, index) in obj" > {{ value }}-{{ key }}-{{ index }} <li > <ul >
方法与事件 当方法有参数,但 @click
指定方法时没有加括号 @click="handle"
会默认传原生事件对象(object MouseEvent)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <div id ="app" > {{ count }} <button @click ="handle()" > 加1</button > <button @click ="handle(10)" > 加10</button > </div > <script > let app = new Vue({ el : "#app" , data : {count : 0 } methods : { hadnle : function (count ) { count = count || 1 ; this .count += count } } }) </script >
数组更新 涉及数组的操作中,可使用js更改数组的方法:
push
pop
shift
unshift
splice
sort
reverse
…
但其中两种情况下数组变动 Vue 会检测不到(不会重新渲染):
改变数组指定项:this.arr[0] = "car"
改变数组长度:this.arr.length = 1
解决方法:
1 2 Vue.set(app.arr, 0 , "car" ) app.arr.splice(0 )
数组过滤:
1 2 3 4 5 6 7 8 9 10 datra: { arr : ["book" , "pen" , "pencil" ] }, computed : { matchOO : function ( ) { return this .arr.filter(function (value ) { return value.match(/oo/ ) }) } }
修饰符 在 Vue 中传入Event对象用 $event
stop:定义在内层元素,阻止事件向上冒泡(<button @click.stop='btnClick()'>
)
prevent:提交事件并阻止页面重载(<form action="" @submit.prevent="handle">...
,handle 为触发事件执行函数)
self:定义在外层元素,只作用在元素本身而非子元素时调用(与 stop 相反,如 button 的外层 div)
once:只执行一次的方法(<button @click.once='btnClick()'>
)
键盘事件 1 <input @keyup.13 = "submit" >
其中Vue提供了键盘事件的简写:
.enter
.tab
.delete
…
表单与 v-model 输入框和编辑框
使用 v-model
指令用于在表单类元素(可以用于 input 框,以及 textarea 等)上双向绑定事件;
框内所显示的值只依赖于所绑定的数据,不再关心初始化时插入的 value。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <div id ="app" > <button v-on:click ="reverse()" > reverse</button > <input v-model ="content" > </div > <script > let app = new Vue({ el : "#app" , data : { content : "hello World" , }, methods : { reverse ( ) { this .content = this .content.split("" ).reverse().join("" ) } } }) </script >
单选按钮
单个单选按钮,直接用 v-bind
绑定一个布尔值,用 v-model
是不可以的;
多个单选按钮组合使用时需要 v-model
来配合 value 使用,绑定选中的单选框的 value 值,此处所绑定的初始值可以随意。1 2 3 4 5 6 7 8 9 10 11 12 13 14 <div id ="app" > <input type ="radio" value ="apple" v-model ="checks" > apple <input type ="radio" value ="banana" v-model ="checks" > banana <input type ="radio" value ="orange" v-model ="checks" > orange 选中:{{ checks }} </div > <script > let app = new Vue({ el : "#app" , data : { checks : "" } }) </script >
复选框
单个复选框,直接用定一个布尔值,可以用 v-model
或 v-bind
;
多个复选框组合使用时需要 v-model
来配合 value
使用,v-model
绑定一个数组;如果绑定的是字符串则会转化为布尔值,与所有绑定的复选框的 checked
属性相对应。1 2 3 4 5 6 7 8 9 10 11 12 13 14 <div id ="app" > <input type ="checkbox" value ="apple" v-model ="checks" > apple <input type ="checkbox" value ="banana" v-model ="checks" > banana <input type ="checkbox" value ="orange" v-model ="checks" > orange 选中:{{ checks }} </div > <script > let app = new Vue({ el : "#app" , data : { checks : [] } }) </script >
下拉框
单选下拉框,所绑定的 value
值初始化可以为数组或字符串,有 value
直接优先匹配一个 value
值,没有则匹配一个 text
值;
多选下拉框需要 v-model
来配合value使用,v-model
绑定一个数组,与复选框类似。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <input type ="text" v-model ="value" > {{ value }} <textarea v-model ="msg" > xxx</textarea > {{ msg }}<input type ="radio" v-bind:checked ="singleRadio" > <input type ="radio" name ="checks" v-model ="checkName" value ="apple" > apple<input type ="radio" name ="checks" v-model ="checkName" value ="orange" > orange <input type ="radio" name ="checks" v-model ="checkName" value ="banana" > banana选中:{{ checkName }} <input type ="checkbox" value ="apple" v-model ="checks" > apple <input type ="checkbox" value ="banana" v-model ="checks" > banana<input type ="checkbox" value ="orange" v-model ="checks" > orange选中:{{ checks }} <select v-model ="seleted" > <option value ="apple" > apple</option > <option value ="banaba" > banana</option > <option value ="orange" > orange</option > </select > 选中:{{ seleted }} <select v-model ="seleted" multiple > <option value ="apple" > apple</option > <option value ="banaba" > banana</option > <option value ="orange" > orange</option > </select > 选中:{{ selectedmul }} <script > let app = new Vue({ el : "#app" , data : { value : "Hello World!" , msg : "Hello" , singleRadio : true , checkName : "apple" , checks : "xyxyxyx" , seleted : [], selectedmul : [] } }) </script >
总结:
绑定值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <input type ="radio" v-model ="picked" v-bind:value ="value" > {{ picked }}<input type ="checkbox" v-model ="toggle" , :true-value ="value1" :false-value ="value2" > {{ toggle == value1 }} {{ toggle == value2 }} <select v-model ="valueSelect" :value ="{num: 1}" > <option value ="apple" > apple</option > <option value ="banaba" > banana</option > <option value ="orange" > orange</option > </select > <script > let app = new Vue({ el : "#app" , data : { picked : true , value : "Hello World!" , toggle : true , value1 : "选中" , value2 : "不选中" , valueSelect : {} } }) </script >
组件 使用组件可提高代码的复用性。
命名规则 Vue 组件中 camelCased(驼峰式)命名与 kebabcase(短横线命名):
在html中,myMessage
和 mymessage
是一致的,因此在组件中的 HTML 中使用必须使用 kebabcase
(短横线)命名,不允许使用驼峰!
在组件中, 父组件给子组件传递数据必须用短横线。在 template 中,必须使用驼峰命名方式,若为短横线命名则会报错;
在组件的 data 中,用 this.XXX
引用时只能是驼峰命名方式,若为短横线命名则会报错。
组件注册 全局注册(所有 Vue 对象都可以使用,但权限太大,容错率低)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <div id ="app" > <my-component > </my-component > </div > <script > Vue.component( 'my-component' , { template :'<div>我是组件的内容</div>' } ) var app = new Vue({ el : "#app" , data : { } }) </script >
局部注册(Vue 对象中可用)
1 2 3 4 5 6 7 8 var app = new Vue({ el : '#app' , components :{ 'my-component' :{ template : '<div>我是组件的内容</div>' } } })
Vue 组件模板在某些情况下会受到 HTML 标签的限制:此时可以使用 is
属性来挂载组件:
1 2 3 4 5 <table > <tbody is ="my-component" > </tbody > </table >
必须使用小写字母加“”命名组件(child、mycomponnet);
template 中的内容必须被一个 DOM 元素包括,也可以嵌套;
在组件的定义中,可以使用除了 template 之外的其他选项:data
、computed
、methods
…
data 必须是一个方法:
1 2 3 4 5 6 7 8 9 10 'btn-component' : { template : '<button @click="count++">' {{ count }}</button>, data: function ( ) { return { count : 0 } } }
数组验证 Vue 中验证的数据类型有:
String
Number
Boolean
Object
Array
Function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <div id="app" > <type-component :a ="a" :b ="b" :d ="d" :f ="f" :g ="g" > </type-component > </div> <script > Vue.component('typeComponent' , { props : { a : Number , b : [String , Number ], c : { type : Boolean , default : true }, d : { type : Number , required : true }, e : { type : Array , default : function ( ) { return []; } }, f : { validator : function (value ) { return value > 10 ; } }, g : { type : Function } }, template : '<div>{{ a }} - {{ b }} - {{ d }} - {{ f }}</div>' , }) let app = new Vue({ el : "#app" , data : { a : 1 , b : 567 , d : 789 , f : 99 , g : 1111 } }) </script >
组件通信 父组件向子组件传递数据
在组件中使用 props
来从父亲组件接收参数,在 props
中定义的属性,都可以在组件中直接使用;
组件中 propps
来自父级,而 data
return 的数据就是组件自己的数据,两种情况作用域就是组件本身,可以在 template
,computed
,methods
中直接使用;
props
可以设置字符串数组或对象,以使用 v-bind
动态绑定父组件来的内容。
父组件data: {parentMsg: "Hello World!"}
-> 子组件<bind-component v-bind:msg="parentMsg"></bind-component>
-> 子组件"bind-component": {props: ["msg"]}
-> 子组件"child-component": {template: "<div>{{ msg }}</div>"}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <div id ="father" style ="border: 2px solid chartreuse; height: 160px" > <h5 style ="text-align: center" > 父组件</h5 > <child-component msg ="来自父组件的内容" > </child-component > <input type ="text" v-model ="parentMsg" > <bind-component v-bind:msg ="parentMsg" > </bind-component > </div > <script > let app = new Vue({ el : "#father" , data : { parentMsg : "Hello World!" }, components : { "child-component" : { template : "<div>{{ msg }}</div>" , props : ["msg" ], }, "bind-component" : { template : "<div>{{ msg }}</div>" , props : ["msg" ], } } }) </script >
单向数据流:
通过 props
传递数据是单向的,父组件数据变化时会传递给子组件,但不能反过来;
单向传递的目的是尽可能将父子组件解稿,避免子组件无意中修改了父组件的状态。
当父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域下可以随意使用和修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <div id ="app" > <my-component msg ="父组件传递到子组件的数据" > </my-component > </div > <script > Vue.component('my-component' , { props : ['msg' ], template : '<div>子组件</div>' , data : function ( ) { return { count : this .msg } } }) let app = new Vue({ el : "#father" , data : {} }) </script >
prop
作为需要被转变的原始值传入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <div id ="app" > <input type ="text" v-model ="width" > <my-comp :width ="width" > </my-comp > </div > <script > var app = new Vue({ el : '#app' , data : { width : '100' }, components : { 'my-comp' : { props : ['width' ], template : '<div :style="style">{{ width }}px</div>' , computed : { style : function ( ) { return { width : this .width + 'px' , background : 'red' } } } } } }) </script >
子组件向父组件传递数据 子组件 <button @click='increase'>+1</button>
-> 子组件 this.$emit('change', this.count)
-> 父组件 <child @change="handldTotal"></child>
-> 父组件 handldTotal: function(value) {this.total = value}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <div id ="app" > {{ total }} <child @change ="handldTotal" > </child > </div > <script > Vue.component('child' , { template : "<div><button @click='increase'>+1</button></div>" , methods : { increase : function ( ) { this .count += 1 this .$emit('change' , this .count) } }, data : function ( ) { return { count : 200 } } }) let app = new Vue({ el : "#app" , data : { total : 200 }, methods : { handldTotal : function (value ) { this .total = value } } }) </script >
$emit
实际上会触发一个 input
事件, 其后的参数就是传递给 v-model
绑定的属性的值
v-model
其实是一个语法糖,这背后做了两个操作: i. v-bind
绑定一个 value
属性 ii. v-on
指令给当前元素绑定 input
事件
非父组件之间的通信 可以使用一个空的 Vue 实例作为中央事件总线:
1 2 3 4 5 6 7 8 9 +-------------+ | 根组件 | | +---------+ | | | 子组件 A | | | +---------+ | | +---------+ | | | 子组件 B | | | +---------+ | +-------------+
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 <div id ="app" > <a-component > </a-component > <b-component > </b-component > <br > <child-component > </child-component > {{ msg }} </div > <script > Vue.component('a-component' , { template : '<div style="width: 100px; height: 100px; border: 1px solid black"><button @click="handle">点击向B组件传递数据</button></div>' , data : function ( ) { return { a : '来自A组件的内容' } }, methods : { handle : function ( ) { this .$root.bus.$emit('send' , this .a) } } }) Vue.component('b-component' , { template :'<div style="width: 100px; height: 100px; border: 1px solid black">{{ b }}</div>' , data : function ( ) { return { b : "" } }, created : function ( ) { let self = this this .$root.bus.$on('send' , function (value ) { self.b = value }) } }) Vue.component('child-component' , { template : '<button @click="setFatherData">通过点击修改父亲的数据</button>' , methods : { setFatherData : function ( ) { this .$parent.msg = '数据已修改' } } }) let app = new Vue({ el : "#app" , data : { bus : new Vue(), msg : '数据未修改' , formchild : '未取得数据' }, methods : { getChildData : function ( ) { this .formchild = this .$refs.c.msg; } } }) </script >
使用 Slot 分发内容 让组件可以组合的过程被称为内容分发,使用特殊的 slot
元素作为原始内容的插槽。
编译的作用域 1 2 3 <child-component > {{ message }} </child-component >
其中 message
应该绑定到父组件的数据,组件作用域简单地说是:
父组件模板的内容在父组件作用域内编译;
子组件模板的内容在子组件作用域内编译。
插槽的用法 将父组件的内容与子组件相混合,从而弥补了视图的不足:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <div id ="app" > <my-component > <p > 我是父组件的内容</p > </my-component > </div > <script > Vue.component('my-component' , { template : `<div> <slot>如果父组件没有插入内容,我就作为默认出现</slot> </div>` }) new Vue({ el : "#app" }) </script >
其中 <slot>
标签中可以指定 name
,称为“具名插槽”,对应 DOM 标签的 slot
属性:
1 2 3 4 5 6 7 <p slot ="footer" > 底部</p > // ... template: ` <div class ="footer" > <slot name ="footer" > </slot > </div > `
作用域插槽 使用一个可以复用的模板来替换已经渲染的元素。
从子组件获取数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <div id ="app" > <my-component > <template slot ="abc" slot-scope ="prop" > {{ prop.text }} </template > </my-component > </div > <script > Vue.component('my-component', { template: ` <div > <slot text ="来自子组件的数据" name ="abc" > </slot > </div > ` }) new Vue({ el: "#app" }) </script >
访问插槽 通过 this.$slots.name
可以访问名称为 name
的插槽。
1 2 3 4 5 6 7 8 9 mounted:function () { // 访问插槽 var header = this.$slots.header; var text = header[0].elm.innerText; var html = header[0].elm.innerHTML; console.log(header) console.log(text) console.log(html) }
动态组件 Vue 提供了 component 元素用来动态的挂载不同的组件,使用 is
特性实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <div id ="app" > <component :is ="thisView" > </component > <button @click ="handleView('A')" > 1</button > <button @click ="handleView('B')" > 2</button > <button @click ="handleView('C')" > 3</button > </div > <script > Vue.component('compA' , { template : '<div>JavaScript</div>' }) Vue.component('compB' , { template : '<div>CSS</div>' }) Vue.component('compC' , { template : '<div>HTML</div>' }) let app = new Vue({ el : "#app" , data : { thisView : "compA" }, methods : { handleView : function (tag ) { this .thisView = "comp" + tag; } } }) </script >
自定义指令 类似组件可以全局注册和局部注册,使用 derective 注册。
钩子函数 指令定义函数提供了几个钩子函数(可选):
bind:只调用一次,指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作;
inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document中);
update:被绑定元素所在的模板更新时调用,而不论绑定值是否变化;通过比较更新前后的绑定值,可以忽略不必要的模板更新;
componentUpdated:被绑定元素所在模板完成一次更新周期时调用;
unbind:只调用一次, 指令与元素解绑时调用。
钩子函数有以下参数:
el:指令所绑定的元素,可以用来直接操作 DOM 。
binding:一个对象,包含以下属性:
name:指令名,不包括 “v” 前缀;
value:指令的绑定值,例如 ` vmydirective=”1 + 1”,value 的值是2;
oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用,无论值是否改变都可用;
expression:绑定值的字符串形式,例如 vmydirective="1 + 1"
,expression 的值是“1 + 1”;
arg:传给指令的参数。例如 vmydirective:foo
, arg 的值是 “foo”。
modifiers:一个包含修饰符的对象, 例如vmydirective.foo.bar
,修饰符对 象 modifiers 的值是{ foo: true, bar: true }
;
vnode:Vue 编译生成的虚拟节点。
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <div id ="app" > <input type ="text" v-focus > 获取焦点 <br > <input type ="text" > <hr > <div v-command:expression.a.b.c ="value" > </div > </div > <script > Vue.directive('focus' , { inserted : function (el ) { el.focus() } }) Vue.directive('command' , { bind : function (el, binding, vnode ) { let keys = [] for (let key in vnode) { keys.push(key) } console .log(keys) el.innerHTML = "name: " + binding.name + "<br>" + "name: " + binding.value + "<br>" + "expression: " + binding.expression + "<br>" + "argument: " + binding.arg + "<br>" + "modifiers: " + JSON .stringify(binding.modifiers) + "<br>" + "vnode keys: " + keys.join(", " ) + "<br>" } }) let app = new Vue({ el : "#app" , data : { value : "自定义指令所绑定的值" } }) </script >
render函数 在 render 函数中使用 this.$slots
和 props
传递数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <div id ="app" > <child :level ="level" > 标题 </child > <button v-on:click ="add1()" > +1</button > <button v-on:click ="minus1()" > -1</button > </div > <script > Vue.component('child' , { render : function (createElement ) { return createElement( 'h' + this .level, this .$slots.default ); }, props : ['level' ] }) let app = new Vue({ el : "#app" , data : { level : 1 }, methods : { add1 : function ( ) { if (this .level < 6 ) this .level += 1 ; }, minus1 : function ( ) { if (this .level > 1 ) this .level -= 1 ; } } }) </script >
在render函数中使用v-model
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <div id ="app" > <my-component :name ="name" v-model ="name" > </my-component > <br > {{ name }} </div > <script > Vue.component("my-component" , { render : function (createElement ) { let self = this return createElement("input" , { domProps : { value : self.name }, on : { input : function (event ) { console .log(this ) self.$emit("input" , event.target.value) } } }) } }) let app = new Vue({ el : "#app" , data : { name : "ywh" }, }) </script >
在 render 函数中使用作用域插槽:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <div id ="app" > <my-component > <template scope ="prop" > {{ prop.text }} </template > </my-component > </div > <script > Vue.component('my-component' , { render : function (createElement ) { return createElement( 'div' , this .$scopedSlots.default({ text : '子组件传递过来的数据' , }) ) } }) let app = new Vue({ el : "#app" }) </script >
函数化组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <div id ="app" > <my-component value ="子组件信息" > </my-component > </div > <script > Vue.component('my-component' , { functional : true , render : function (createElement, context ) { return createElement("button" , { on : { click : function ( ) { console .log(context) console .log(context.parent) alert(context.parent.parentMsg) console .log(context.props.value) } } }, "click" ) }, prope : ["value" ] }) let app = new Vue({ el : "#app" , data : { parentMsg : "父组件信息" } }) </script >
vue-router 使用 vue-router 可实现前端路由。
安装 vue-router:
1 cnpm install --save vue-router
配置路由文件,并在 Vue 实例中导入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import router from 'vue-router' import HelloWorld from './components/HelloWorld' Vue.use(router) var rt = new router({ routes :[{ path : '/' , component : HelloWorld }] }) new Vue({ el : '#app' , router : router, components : { App }, template : '<App/>' })
设置视图(组件)加载的位置
1 <router-view > </router-view >
路由跳转 1 2 3 4 5 6 7 8 9 10 11 <router-link to="/" ></router-link> <template > <ul > <li > <router-link to ="/helloworld" > HELLO WORLD</router-link > </li > <li > <router-link to ="/helloearth" > HELLO EARTH</router-link > </li > </ul > </template >
路由参数传递
必须在路由内加入路由的 name
必须在path后加 /:
加上传递的参数
路由:
1 2 3 4 5 6 7 8 9 10 const router = new VueRouter({ routes : [ { name : "helloworld" , path : '/helloworld/:msg' , component : HelloWorld, }] })
使用 params
在跳转链接带上参数:
1 2 3 4 5 6 7 <router-link :to ="{name: helloearth, params: {msg: '你好世界'}}" > HELLO WORLD </router-link >
使用 query 在跳转链接带上参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <router-link :to ="{name: helloearth, query: {msg: '你好世界'}}" > HELLO WORLD </router-link >
发送 HTTP 请求 axios 是一个基于 Promise、用于浏览器和 Node.js Server 的HTTP客户端:
从浏览器中创建 XMLHttpRequest
从 Node.js Server 发出 http 请求
支持 Promise API
拦截请求和响应
转换请求和响应数据
取消请求
自动转换JSON数据
防止CSRF/ XSRF
安装 axios
导入并挂载到 Vue 原型中:
1 2 import axios from 'axios' Vue.prototype.$http = axios;
发送 Get 请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 getData ( ) { var self = this ; this .$http.get('https://cnodejs.org/api/v1/topics' ) .then(function (res ) { self.items = res.data.data }) .catch(function (err ) { console .log(err) }) } axios.get('/user' , {params : {ID : 12345 }} ) axios.get('/user' , { ID : 12345 }) axios.get('https://cnodejs.org/api/v1/topics?page=1&limit=15' )
发送 Post 请求(有两种格式):
form-data:?page=1&limit=48
x-www-form-urlencoded:{page: 1, limit: 10}
在 axios 中,Post 请求接收的参数必须是 form-data,因此需要安装 qs 插件
1 2 3 4 5 6 7 8 9 10 11 12 13 postData ( ) { var self = this ; this .$http.post(url, qs.stringify({ page :1 , limit :10 })) .then(function (res ) { self.items = res.data.data }) .catch(function (err ) { console .log(err) }) }
状态管理 使用 vuex 可实现状态管理(在各个组件之间管理外部状态),共享数据。
安装 vuex:
父子组件之间数据传递 child.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <template > <div > <span > 子组件</span > :{{ fromParentMsg }} <button @click ="sendMsgToParent" > 向父组件传递消息</button > </div > </template > <script > export default { name : "child" , props : { fromParentMsg : { type : String , default : "" } }, data : function ( ) { return {toParentMsg : "子组件向父组件传递消息" } } methods : { sendMsgToParent : function ( ) { this .$emit('handle' , this .toParentMsg) } } } </script >
parent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <template > <div > <span > 父组件</span > :{{ fromChildMsg }} <hr > <child :fromParentMsg ="toChildMsg" @handle ="getMsgFromChild" > </child > </div > </template > <script > import child from './child' export default { name : "parent" , data : function ( ) { return {toChildMsg : "父组件向子组件传递消息" , fromChildMsg : "" } }, components : {child}, methods : { getMsgFromChild : function (value ) { this .fromChildMsg = value } } } </script >
多个组件之间共享数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import Vuex from 'vuex' Vue.use(Vuex) var store = new Vuex.Store({ state : { name : ywh } }) new Vue({ el : "#app" , router, store, components : {APP}, template : "<App/>" })
vuex 操作 vuex 状态管理的流程:
view
actions
mutations
state
view
实现状态修改:
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var store = new Vuex.Store({ state : { name : ywh }, mutations : { reverse (state ) { state.name.split("" ).reverse().join("" ) } }, actions : { reverseAction (contenxt ) { setTimeout (function ( ) { context.commit("reverse" ); }, 1000 ) } }, getters : { getName (state ) { return state.name.length > 2 ? state.name : "" } } })
parent.vue
1 2 3 4 5 6 7 8 9 <button @click ="reverse" > </button > // ... methods: { reverse() { this.$store.commit('reverse') // this.$store.dispatch('reverse') 通过actions调用 } }