1.数据驱动(mvvm模型,只要改变model的数据,视图层就会自动更新)
2.视图组件: 把整一个网页的拆分成一个个区块,每个区块我们可以看作成一个组件。网页由多个组件拼接或者嵌套组成。
1.引入vue之后就可以进行下面的步骤
2.创建语法(创建 ,设置数据,挂载,渲染)
var vm = new Vue({})//参数是一个对象
1.过滤器 filter属性:
例如我们从后台取出来的数据都是带有小数位的,但是需求上面要求的是只展示整数部分,这个时候我们就可以用过滤器属性了。我们先创建一个实例,数据中的格式都是带有小数点的,此时我们创建filters属性(记住此属性的值是一个对象,所以":"之后要带上大括号“{}”,在filters里面我们写一个转换函数,这个函数的功能就是将传参转换为整数后返回,此处用的是es6的语法),写好函数后要怎么使用呢?我们在div里面,将参数用管道符号"|"传给toInt函数,这样就可以了!
数字1:{{num1 | toInt}}
数字2:{{num2 | toInt}}
数字3:{{num3 | toInt}}
let app6 = new Vue({
el:'#app6',
data:{
num1:123.4,
num2:520.1314,
num3:1314.520
},
filters:{
toInt(value){
return parseInt(value);
}
}
});
4.计算属性computed
有时候,我们拿到一些数据,需要经过处理计算后得到的结果,才是我们要展示的内容。
比如:我们有三个数,但是需要展示的是三个数字之和。这种情况,就适合用我们的计算属性computed。(可以直接调用计算属性里面的值。)
let app6 = new Vue({
el:'#app6',
data:{
num1:123.4,
num2:520.1314,
num3:1314.520
},
filters:{
toInt(value){
return parseInt(value);
}
},
computed:{
sum(){
return parseInt(this.num1+this.num2+this.num3);
}
}
});
计算属性computed的定义语法和过滤器filters类似,但是用法不一,如下:
总和是{{sum}}
我们提供的函数将用作属性app6.sum 的 getter 函数:
计算属性拥有getter和setter两个属性,也就是我们可以设置这两个属性,例如
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
那么每次运行fullname的时候,就是使用getter,返回一个值,而每次执行fullname = ""的时候,就会执行set里面的函数,将firstname和lastname都进行改变。
需要注意的是,sum的值是依赖data中的数据num1,num2,num3的,一旦它们发生了变化,sum的值也会自动更新
5.methods 方法
顾名思义,在methods中,我们可以定义一些方法,供组件使用。
比如,我们定义一个数据a,当用户点击按钮的时候,a的值加1。这种情况,我们可以利用methods来实现。
定义一个plus( )的方法在methods中,下面我们把plus( ) 绑定在按钮的点击事件上
let vm = new Vue({
//挂载元素
el:'#app',
//实例vm的数据
data:{
a:0
},
//方法
methods:{
plus(){
return this.a++;
}
}
});
{{ a }}
1. 计算属性computed和方法methods都能用于计算,说说两者的区别?
答: 假设我们计算一个值sum,它是经过a和b相加得到的结果。
如果我们使用计算属性的方式来实现得到sum,由于计算属性computed是基于它的依赖进行缓存的,也就是sum的值只有在a或者b的值发生变化的时候才会重新计算求值。这就意味着,倘若a和b的值不发生改变,即使重新渲染或者多次访问计算属性中的sum,都无需重新计算,节省运算开销。
反观,如果我们用methods方法来定义一个求和函数sum( )来计算a和b的求和,如果多个地方需要渲染a和b的和,我们则需要多此调用sum( )方法,这样的后果就是多次重复执行函数,造成不必要的运算开销。
6.watch 观察
watch选项是Vue提供的用于检测指定的数据发生改变的api,也就是说,当这个数据发生变化的时候会触发对应的函数。
上面点击按钮a的值加1的例子,不就是数据发生变化了吗?我们就用watch选项来监听数字a是否发生了变化,如果了监听到了变化,我们就在控制台输入以下a的最新值。
在上个例子的代码基础上,我们加上watch部分的代码
let vm = new Vue({
//挂载元素
el:'#app',
//实例vm的数据
data:{
a:0
},
//方法
methods:{
plus(){
return this.a++;
}
},
//观察
watch:{
a(){
console.log(`有变化了,最新值:`);
console.log(this.a);
}
}
});
最后一部分watch就是我们新加的代码,a( ) 表示我们要观察监听的就是数据a,
不要在选项属性或者回调函数上面使用箭头函数,因为箭头函数是和父级上下文绑定在一起的。
1. beforeCreate(还没创建实例)
此阶段为实例初始化之后,此时的数据观察和事件配置都没好准备好。
我们试着console一下实例的数据data和挂载元素el,代码如下:
{{name}}
得到的结果是:
此时的实例中的data和el还是undefined,不可用的。
2. created(创建实例)
beforeCreate之后紧接着的钩子就是创建完毕created,我们同样打印一下数据data和挂载元素el,看会得到什么?
在上一段代码的基础上,加上下面这段代码:
created(){
console.log('创建完毕');
console.log(this.$data);
console.log(this.$el);
}
我们看到打印的结果:
此时,我们能读取到数据data的值,但是DOM还没生成,所以属性el还不存在,输出$data为一个Object对象,而$el的值为undefined。
3. beforeMount(生成dom)
上一个阶段我们知道DOM还没生成,属性el还为undefined,那么,此阶段为即将挂载,我们打印一下此时的$el是什么?
增加一下代码:
beforeMount(){
console.log('即将挂载');
console.log(this.$el);
}
我们看到打印结果:
此时的$el不再是undefined,而是成功关联到我们指定的dom节点
,但此时{{ name }}还没有被成功地渲染成我们data中的数据。没事,我们接着往下看。4. mounted(将实例与dom相连)
mounted也就是挂载完毕阶段,到了这个阶段,数据就会被成功渲染出来,我们编写mounted的钩子,打印$el 看看:
mounted(){
console.log('挂载完毕');
console.log(this.$el);
}
打印结果
如我们所愿,此时打印属性el,我们看到{{ name }}已经成功渲染成我们data.name的值:“krys”。
5. beforeUpdate(实例更新前)
我们知道,当修改vue实例的data时,vue就会自动帮我们更新渲染视图,在这个过程中,vue提供了beforeUpdate的钩子给我们,在检测到我们要修改数据的时候,更新渲染视图之前就会触发钩子beforeUpdate。
html片段代码我们加上ref属性,用于获取DOM元素。
{{name}}
Vue实例代码加上beforeUpdate钩子代码:
beforeUpdate(){
console.log('=即将更新渲染=');
let name = this.$refs.app7.innerHTML;
console.log('name:'+name);
},
我们试一下,在控制台修改一下实例的数据name,在更新渲染之前,我们打印视图中文本innerHTML的内容会是多少:
我们在控制台把app.name的值从原来的 “krys” 修改成 “斑马”,在更新视图之前beforeUpdate打印视图上的值,结果依然为原来的值:“krys”,表明在此阶段,视图并未重新渲染更新。
6. updated(实例更新完成之后)
此阶段为更新渲染视图之后,此时再读取视图上的内容,已经是最新的内容,接着上面的案例,我们添加钩子updated的代码,如下:
updated(){
console.log('==更新成功==');
let name = this.$refs.app7.innerHTML;
console.log('name:'+name);
}
结果如下:
大家注意两个不同阶段打印的name的值是不一样,updated阶段打印出来的name已经是最新的值:“斑马”,说明此刻视图已经更新了。
7. beforeDestroy
调用实例的destroy( )方法可以销毁当前的组件,在销毁前,会触发beforeDestroy钩子。
8. destroyed
成功销毁之后,会触发destroyed钩子,此时该实例与其他实例的关联已经被清除,它与视图之间也被解绑。
案例:我们通过在销毁前通过控制台修改实例的name,和销毁之后再次修改,看看情况。
beforeDestroy(){
console.log('销毁之前');
},
destroyed(){
console.log('销毁成功');
}
效果如下图:
销毁之前,修改name的值,可以成功修改视图显示的内容为:“更新视图”,一旦效用实例的$destroy( )方法销毁之后,实例与视图的关系解绑,再修改name的值,已于事无补,视图再也不会更新了,说明实例成功被销毁了。
9. actived
keep-alive组件被激活的时候调用。
10. deactivated
keep-alive 组件停用时调用。
关于keep-alive组件的激活和停用
以后最为常用的钩子是:created 成功创建。
五.在html中绑定数据
1.Mustache语法:{{}}的写法,在里面写入需要渲染的数据
{{name}}
let app7 = new Vue({
el:"#app7",
data:{
name:"krys"
}
}
2.绑定纯html
如果上面的例子中,name的值是一些html语句,如果单纯的是像上面这样写的话,会原样输出,不会转化为html语句,例如
{{name}}
并不能得到我们想要的效果,这时候需要用到vue提供的v-html指令,
{{name}}
3.绑定属性:attr或者v-bind:attr,其中attr是属性名, 需要注意的是:当渲染的属性值是布尔值的时候(true和false),效果就不一样了,并不是简单地将true或者false渲染出来,而是当值为false的时候,属性会被移除。
let app9 = new Vue({
'el':"#app9",
data:{
link:"https://www.baidu.com"
}
})
4.支持javascript表达式
值得注意的是,只能包含单个表达式,多个表达式组成的不会生效!
上面讲到的都是将数据简单地绑定在视图上,但事实上,vue还支持我们对数据进行简单的运算:javascript表达式支持。
{{ num+3 }}
六,必须要掌握的指令
1.v-text指令
v-text指令用于更新标签包含的文本,他的作用跟双大括号{{}}一样,
2.v-html指令(执行html)
这个指令帮助我们绑定一些包含html代码的数据在视图上。
3.v-show指令
控制元素的css中display属性,v-show指令的取值为true/false,分别对应着显示/隐藏。
我是true
我是false
4.v-if(其实就相当于if)
v-if指令的取值也是true或者false,它控制元素是否需要被渲染出来,设置为true的
标签,成功渲染出来,而设置为false的
标签,直接被一行注释代替了,并没有被解析渲染出来。
那么如何区别v-show和v-if这两个指令呢?就是一个会被渲染出来,一个不会。记住一个很重要的点:如果需要频繁切换显示/隐藏的,就用 v-show ;如果运行后不太可能切换显示/隐藏的,就用 v-if 。
我是true
我是true
5.v-else指令(相当于else)
1) if和else指令,在一般的变成语言都是结对出现,在vue里也不例外,它没有对应的值,但是要求前一个兄弟节点必须使用v-if指令。(没有if哪来的else),如果v-if的值为true那么else里面的东西将不会渲染出来
我是if
我是else
3)v-else-if
A
B
C
Not A/B/C
v-else,v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。
4)使用key来管理可复用的元素
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:
new Vue({
el: '#app',
data: {
loginType:"username"
},
methods:{
change:function () {
this.loginType == "username" ? this.loginType = "email":this.loginType = "username";
}
}
});
那么在上面的代码中切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder。
这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可。
new Vue({
el: '#app',
data: {
loginType:"username"
},
methods:{
change:function () {
this.loginType == "username" ? this.loginType = "email":this.loginType = "username";
}
}
});
现在,每次切换时,输入框都将被重新渲染。
6.v-for指令
v-for指令用来进行循环,不但可以迭代数组,还可以迭代对象和整数,下面例子中,index是索引,item是值(有先后顺序,第一个是值,第二个是索引)。可以看到生成了4个div
{{index}}.我们的成员有:{{item}}
用for in或者for of遍历对象的时候,第一个参数是值,第二个参数是键名,第三个参数是索引。在遍历对象时,是按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下是一致的。
-
{{key}}-- {{value}}
new Vue({
el:"#v-for-object",
data:{
object:{
firstName:"liang",
lastName:"krys",
fullName:"krys liang",
age:23
}
}
});
尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。因为它是 Vue 识别节点的一个通用机制。
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index" 。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有的唯一 id。这个特殊的属性相当于 Vue 1.x 的 track-by ,但它的工作方式类似于一个属性,所以你需要用 v-bind 来绑定动态值 (在这里使用简写):
当对数组进行下面两个操作的时候,数组不会进行响应式的变化,也就是说,即使进行了下面的两个操作,数据也不会重新渲染
1)利用索引直接设置一个值
2)修改数组的长度
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
为了解决第一类问题,改用下面两个方法来实现同样的效果
Vue.set(vm.items, indexOfItem, newValue)
vm.items.splice(indexOfItem, 1, newValue)
vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,可以改用下面的方法
vm.items.splice(newLength)
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 现在是响应式的
vm.b = 2
// `vm.b` 不是响应式的
对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})
你可以添加一个新的 age 属性到嵌套的 userProfile 对象:
Vue.set(vm.userProfile, 'age', 27)
你还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:
vm.$set(vm.userProfile, 'age', 27)
有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
当v-for和v-if处于同一个节点的时候,v-for的优先级比v-if要高,这就是说v-if将分别运行于每个v-for循环中。
{{ todo }}
当在自定义组件中使用 v-for 时,key 现在是必须的。然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要用 props
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id">
7.v-bind指令
v-bind指令用于动态绑定DOM元素的属性(例如href,src等),其中:href等价于v-bind:href
8.v-on指令
v-on指令相当于绑定事件的监听器,绑定的事件触发了,可以指定事件的处理函数。
在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
.stop ==stopPropagation->阻止事件继续传播
.prevent == preventDefault ->去除默认行为(form提交不再重载页面)
.capture ->添加事件监听器时使用事件捕获模式,也就是元素自身出发的事件先在此处理,然后才交给内部元素进行处理
.self ->只在event.target是当前元素自身时才触发处理函数
.once->点击事件只会触发一次
.passive->passive是不拦截默认事件
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
Vue 还对应 addEventListener 中的 passive 选项提供了 .passive 修饰符。
这个 .passive 修饰符尤其能够提升移动端的性能。
不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。
按键修饰符
在监听键盘事件的时候,经常需要检查常见的键值。
上面的keycode是按键的编码,等同于下面
所有的按键别名
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
9、v-model指令(在input、textarea、select这三个元素上创建双向数据绑定。)
v-model会忽略所有表单元素的value、checked、selected特性的初始值而总是将vue实例的数据作为数据来源。应该在data中声明初始值,
这个指令是非常重要且非常常用,一般用在表单输入,它帮助我们轻易的实现表单控件和数据的双向绑定,相对于之前的手动更新DOM,简直就是我们的天使。要注意的是v-model中的值,data中变量的名字,p标签中的{{}}里面的值,三者要一致。因为就是iniput输入的值被绑定到了data中,然后输出到p中。
上面输入的值是:{{msg}}
10.v-once指令
这个指令的特点是只渲染一次,后面元素中的数据再更新变化,都不会重新渲染。
你输入:{{ msg }}
select元素中,如果表达式的初始值没有匹配任何选项(也就是没有指定默认值),select元素将会被渲染为未被选中的状态。这个状况下面再ios就会引发一些bug。所以,在option中提空一个空的禁用的选项。
Selected: {{ selected }}
new Vue({
el: '#example-5',
data: {
selected: ''
}
})
多选的时候,象下面那样绑定到一个数组
Selected: {{ selected }}
new Vue({
el: '#example-6',
data: {
selected: []
}
})
Selected: {{ selected }}
new Vue({
el: '...',
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
})
data: {
picked:this.value,
toggle:"false",
selected:this._value
},
修饰符
1).lazy,只有当值改变的时候才进行同步
2).number
将用户的输入值自动转换为数值类型
3).trim
自动过滤用户输入的首尾空白字符,可以给v-model添加.trim修饰符
七.vue中的组件
1)一个组件的data选项必须是一个函数,所有的数据放在return里面。只有这样,每个实例才可以维护一份被返回对象的独立的拷贝。
data: function () {
return {
count: 0
}
}
2)注册组件
全局注册:Vue.component.这样全局注册之后,可以用在任何新创建的vue实例中
局部注册:先声明组件,然后再实例中的components中使用组件。
局部注册中可以通过一个不同的JavaScript对象来定义组件
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
然后再components选项中定义想要使用的组件
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
对于 components 对象中的每个属性来说,其属性名就是自定义元素的名字,其属性值就是这个组件的选项对象。
注意局部注册的组件在其子组件中不可用。
例如,如果你希望 ComponentA 在 ComponentB 中可用,则你需要这样写:
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
}
在模块系统中局部注册
在这些情况下,我们推荐创建一个 components 目录,并将每个组件放置在其各自的文件中。
然后你需要在局部注册之前导入每个你想使用的组件。例如,在一个假设的 ComponentB.js 或 ComponentB.vue 文件中:
import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
components: {
ComponentA,
ComponentC
},
// ...
}
这样就可以在conponentB中的模板使用componentA和c了。
在模块中,基础组件的自动化全局注册
1)什么是基础组件
许多组件只是包裹了一个输入框或者按钮这类的元素,是相对通用的,把它们称之为基础组件,它们会在各个组件中被频繁的使用。
这样就会导致很多包含基础组件的长列表
import BaseButton from './BaseButton.vue'
import BaseIcon from './BaseIcon.vue'
import BaseInput from './BaseInput.vue'
export default {
components: {
BaseButton,
BaseIcon,
BaseInput
}
}
所以在webpack中,使用require.context只在全局注册这些通用的基础组件。
在入口文件中(src/main.js中全局导入基础组件的实例代码)
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
// 其组件目录的相对路径
'./components',
// 是否查询其子目录
false,
// 匹配基础组件文件名的正则表达式
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
// 获取组件配置
const componentConfig = requireComponent(fileName)
// 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 剥去文件名开头的 `./` 和结尾的扩展名
fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
)
)
// 全局注册组件
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
)
})
记住全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生。
3)通过props向子组件传递数据
HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '{{ postTitle }}
'
})
如果你使用字符串模板,这个限制就不存在。
Prop 是你可以在组件上注册的一些自定义特性。当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样
Vue.component('blog-post', {
props: ['title'],
template: '{{ title }}
'
})
一个 prop 被注册之后,你就可以像这样把数据作为一个自定义特性传递进来:
此外还可以定义传进来的prop数据的类型
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object
}
当子组件需要对prop传入的数据进行处理的时候,最好要有一个本地的数据来进行接收,而不要直接改变prop传进来的数据。
我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。
为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 匹配任何类型)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 在 default 或 validator 函数中是不可用的(这些是在实例创建之后才可以使用)。
非prop的特性
一个非prop特性是值传向一个组件,但是该组件并没有相应prop定义的特性。但是组件可以接收这样的特性,并且添加到这个组件的跟元素上。
替换/合并已有的特性
如果组件内部已经设置好的属性,再从组件的父级传入进来,就会替换掉组件内部设置好的值。但是,class和style这两个特性会只能一些,会把组件内部和外部的值合并起来,得到最终的值。
禁用特性继承(就是不要让组件的根元素,继承父级的所有属性,将父元素的元素拆开来,赋予给不同的组件元素)
v-model="username"
class="username-input"
placeholder="Enter your username"
label = "123"
>
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
{{ label }}
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
new Vue({
el: '#todo-list-example',
data: {
username:""
}
})
可以看到父元素的label属性赋予了组件中的label标签,而其他的属性李如玉placeholder、class等则绑定在了$attrs上面,赋予给了input元素
4)通过事件向父级组件发送消息
在下面的例子中blog-post是父组件,父组件中的postFontSize是用来控制博文的字号
new Vue({
el: '#blog-posts-events-demo',
data: {
posts: [/* ... */],
postFontSize: 1
}
})
然后在模板中添加一个按钮用来放大字号(利用自定义事件,将enlarge-text事件传给父组件)
Vue.component('blog-post', {
props: ['post'],
template: `
Enlarge text {{ post.title }}
`
})
然后再父组件中的模板上面监听这个事件
v-for="post in posts" v-bind:key="post.id" v-bind:post="post" v-on:enlarge-text="postFontSize += 0.1" >
此外,还可以通过这个自定义的事件给父组件传数据
在$emit的第二个参数中填写的是这个数据
例如在上面的例子中,我们由子组件来决定放大多少倍,在模板中的button改成下面
Enlarge text
可以看到,这里的参树为0.1,也就是每次点击都放大0.1em
然后在父级组件监听这个事件的时候,访问这个数据
...
v-on:enlarge-text="postFontSize += $event"
>
如果这个处理函数是一个方法,那么这个值将作为参数被传入
methods: {
onEnlargeText: function (enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
自定义事件
事件名
不同于组件和prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。
v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。
因此,我们推荐你始终使用 kebab-case 的事件名。
自定义组件的v-model
一个组件上的v-model会利用value的prop和input的事件,但是像单选框,复选框等类型的输入控件可能会将value特性用在不同的地方(亲测,当勾选单选框或者复选框的时候,值都为undefined)。
所以如果我们想要获得这个值(就是究竟有没有被选中的时候,就可以利用组件的这个model属性)
首先给组件添加model属性,可以看到下面的model含有checked这个属性以及change的这个事件,当组件被改变状态的时候,就会触发这个change事件,并且将自己的checked的值传给父元素,也就是base-checkbox里面的lovingVue这个值绑定了组件中的chencked所对应的值,当base-checkbox触发一个change事件并且附带一个新的值的时候,lovingVue就会被更新。需要注意的是,仍然需要在组件的props里面声明checked这个属性。
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
将原生事件绑定到组件(.native)
组件根元素直接监听事件(亲测,不添加.native是不生效的),
Vue.component("myinput",{
template:""
})
let vm = new Vue({
el:"#test",
data:{
},
methods:{
console:function () {
console.log("focus");
}
}
});
但有的时候组件的根元素不具有这些原生事件的时候,这个事件触发函数就不会生效,例如
{{ label }}
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
于是vue提供了一个$listeners 属性,里面包含了所有作用在这个组件的监听器
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
于是上面修改为
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 将所有的对象合并为一个新对象
return Object.assign({},
// 我们从父级添加所有的监听器
this.$listeners,
// 然后我们添加自定义监听器,
// 或覆写一些监听器的行为
{
// 这里确保组件配合 `v-model` 的工作
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
{{ label }}
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
`
})
5)在自定义组件里面使用v-model,一定要做一下两件事
将其value属性绑定在一个value的props上
在其input事件被触发的时候,将新的值通过自定义的input事件抛出
Vue.component('custom-input', {
props: ['value'],
template: `
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
6)对于特殊的html元素中,其内部元素为指定的元素,所以当我们自定义的元素出现在这里的时候会被视为无效的内容。
此时使用is属性
7)组件名
全局注册的时候第一个参数就是组件名
Vue.component('my-component-name', { /* ... */ })
定义组件名的方式有两种:
使用 kebab-case
Vue.component('my-component-name', { /* ... */ })
使用 PascalCase
Vue.component('MyComponentName', { /* ... */ })
当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说
因为HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
{{searchText}}
Vue.component('CustomInput', {
props: ['value'],
template: `
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
new Vue({
el: '#todo-list-example',
data: {
searchText:""
}
})
但是,使用首字母大写的情况下就会出错
{{searchText}}
8)插槽
所谓插槽,就是在自定义组件中包含的内容,例如
那么上面的something..就是插槽
官网中写道,如果不在自定义组件中添加
例如上面的myelement模板实现可能如下
v-bind:href="url"
class="nav-link"
>
这样写,当组件渲染的时候,这个slot元素就会代替上面的something intresting
插槽中可以包含任何模板代码,包括html
Your Profile
具名插槽
结合父组件上使用slot从而指定插槽的位置,例如
模板中的内容可能如下,每个位置拥有不一样的元素
那么我们在使用父组件的时候
Here might be a page title
A paragraph for the main content.
And another one.
Here's some contact info
可以看到父组件里面的模板就会出现在slot里面name属性所对应的位置下面
如果不像上面,用template元素以及slot属性包裹,直接用在一个普通元素上但是结合slot属性使用,也可以
Here might be a page title
A paragraph for the main content.
And another one.
Here's some contact info
我们还是可以保留一个未命名插槽,这个插槽是默认插槽,也就是说它会作为所有未匹配到插槽的内容的统一出口
插槽的默认内容
有的时候为插槽提供的内容是很有用的,例如,个
你可以在组件模板里的
如果父组件为这个插槽提供了内容,那么这个默认的内容就会被替换掉
9)动态组件&异步组件
在一个多标签的界面中使用is特性来切换不同的组件
Vue.component('tab-home', {
template: '
})
Vue.component('tab-posts', {
template: '
})
Vue.component('tab-archive', {
template: '
})
new Vue({
el: '#dynamic-component-demo',
data: {
currentTab: 'Home',
tabs: ['Home', 'Posts', 'Archive']
},
computed: {
currentTabComponent: function () {
return 'tab-' + this.currentTab.toLowerCase()
}
}
})
v-for="tab in tabs" v-bind:key="tab" v-bind:class="['tab-button', { active: currentTab === tab }]" v-on:click="currentTab = tab" >{{ tab }} v-bind:is="currentTabComponent" class="tab" > 这样实现的话,每次切换,都会重新渲染的。 当在这些组件进行切换的时候,有时会想保持这些组件的状态,以免反复重新渲染所导致的性能问题,为了解决这个问题,用一个keep-alive元素将动态组件包裹起来
v-bind:is="currentTabComponent" class="tab" >
异步组件 所谓异步组件就是等到需要的时候,才会去服务器加载这个模块。vue用工厂函数的的方式定义组件,这个函数会异步的解析运行,也就是只有在需要被渲染的时候才会触发这个函数。 Vue.component('async-example', function (resolve, reject) { setTimeout(function () { // 向 `resolve` 回调传递组件定义 resolve({ template: ' }) }, 1000) }) 我们来写一个类似于公众号历史记录这样的东西(就是div结构,样式都一样,仅仅是内容不一样的多个div) 其中基本的css代码和html的代码如下:下来定义一个组件,代码如下:,这个组件名叫myarticle,拥有props属性和template属性,其中pros属性用来接收参数,这些参数会在模板里面用到。 然后创建一个vue实例,需要注意的是,一定要确保实例vm在创建之前,组件已经成功注册。 注册完组件之后,就开始使用这个自定的组件 八.组件之间的通信 组件实例的作用域都是孤立的,也就是子组件在模板中不能引用父组件的数据。 1.父->子,父组件将数据传递给子组件 先创建一个实例
//创建一个vue实例 const app = new Vue({ el:"#app", data:{ msg:"我是父组件的数据哦嘻嘻嘻" } });
实例中含有数据msg。 注册子组件 Vue.component('son',{ template:"" }) 并将子组件插入到父组件下面: 接下来我们开始传递数据,父组件向子组件传递参数,用组件提供的props属性,在下面的代码中,父组件app向son组件的:message属性传输了它自己的值msg。子组件message接收了这个参数,然后在组件的定义里面,用props来接收这一个值,并将这个值在div中输出。这样就完成了父组件传递给子组件了。props选项声明了它要接受的参数是message,而接收到的对应的值是父组件的数据msg。我们在子组件顺利地把message展示出来。 Vue.component('son',{ props:['message'], template:" }) 而props是单向绑定的,也就是说子组件接收到这个值之后无论怎么修改,父组件中也不会受到影响。因为vue为了防止子组件无意修改了父组件的数据和状态,如果多个子组件任意地对父组件进行修改,会让数据流难以阅读。 2.子->父,子组件传数据给父组件。 父组件监听子组件的事件,并接收子组件传过来的数据 vue实例都有一个事件的接口,这个接口就是$emit(eventname)来触发一个事件 1.注册一个新组件 Vue.component('son',{ template:` 点击 `, methods:{ send(){ this.$emit('connect'); } } }); 一个按钮button,点击它的时候,会触发组件内部的send( )方法,而方法体里面会触发一个事件,事件名取名为:“connect”。 然后我们就去父组件监听这个事件‘connect’,监听方式跟普通原生的事件一模一样,也是用 v-on 指令,缩写用@符号。 我们采用缩写来监听: const app = new Vue({ el:"#app", methods:{ say(){ console.log(`大家好,我监听到了 事件connect的触发`); } } }); 这样,当点击按钮的时候,就成功调用了父组件的say函数。这时,我们只需要把子组件的数据,通过这个事件connect传递给父组件,就可以实现子->父的通信。在data函数里面返回需要返回的数据,并在$emit里面讲msg一并发射出去,在父组件中的say中接收 Vue.component('son',{ props:['message'], template:"", data(){ return{ msg:'大家好,我是儿子的数据' } }, methods:{ send(){ this.$emit('connect',this.msg); } } }) const app = new Vue({ el:"#app", methods:{ say(msg){ console.log("hello,我监听到了子组件的connect事件的触发,儿子的数据是"+msg); } } }) 这样就完成了子->父组件的数据传输 九.动态绑定class和style 1.class的绑定 1)对象法:动态绑定的class的值是一个对象{},键是一个样式名,而值是是否显示这个样式。在对象中,若该属性对应的value为true,则该属性会被渲染出来,为false,则不会渲染出来。并且这写法可以与单独的class属性可以共存 这是文字 这是文字
let vm = new Vue({ el:"#app", data:{ isActive:true, error:"active", active:"erro", color:"red", ifshow:"block", } }) 2)数组法,数组语法中也可以使用对象语法: hello 数组中的值在实例中对应的值是一个属性样式,也就是说数组法中html中数组的值可以不是一个样式名,只要它在vue实例中data中对应的键值的值是一个样式名。 2.style的绑定(绑定内联样式style) CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) 来命名: 1)对象语法,需要注意的是这样绑定的属性之间要用逗号隔开,不要用分号 world 2)数组语法 v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:
当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀。 你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。 3.用在组件上 当在一个自定义组件上使用class属性的时候,这些类将被添加到该组件的根元素上面。这个元素上已经存在的类不会被覆盖。 例如,如果你声明了这个组件: Vue.component('my-component', { template: ' ' }) 然后在使用它的时候添加一些 class: HTML 将被渲染为:
对于带数据绑定 class 也同样适用: 当 isActive 为 truthy 时,HTML 将被渲染成为:
十.demo,做一个TODOlist 确定T 十一.用transition组件轻松实现过渡效果 1.transition过度原理分析 vue提供的transition组件会在以下四种情况下起作用 条件渲染(v-if) 条件展示(v-show) 动态组件 组件根节点 在上述的任意一种情况发生的时候(比如v-show的值本来为true的时候,当发生某种变化导致值变为false的时候),就可以给transition组件包含的节点元素添加enter或leave过度动画元素。 当插入或删除包含在transition组件中的元素时,vue将会做以下的处理 自动嗅探目标元素是否应用了css过渡或动画,如果是,在适当的时候添加或者删除css类名 如果过渡组件提供了JavaScript钩子函数,这些钩子函数将会在恰当的时机被调用 如果没有找到JavaScript钩子也没有检测到css过渡/动画,dom操作在下一帧立即执行 下面的v在实际应用改成transition中name属性的值 v-enter:定义过渡的开始状态,在元素被插入之前生效,在元素被插入之后的下一帧移除。 v-enter-active:定义过渡生效时的状态,在整个进入过渡的阶段都会应用,在元素被完全插入之前生效,在过渡或者动画完成之后被移除。(就是这个类来定义过渡的时间等) v-enter-to:定义过渡的结束状态,在元素被插入之后下一帧生效,在过渡或者动画完成之后被移除。 v-leave:离开过渡的开始的那一刻(用在从显示到隐藏的时候) v-leave-active:离开过渡的的持续状态,在整个离开过渡的阶段中应用(在这里定义过渡的时间等) v-leave-to离开过渡的结束状态,在离开过渡被触发之后下一帧生效。 需要注意的是 节点一定要被transition元素包含 transition的name属性一定要与css类名开始的地方相同(如果没有使用name属性,那么类名前缀为v) 2.例子 hello
let vm = new Vue({ el:"#test", data:{ show:false } });
.fade-enter-active, .fade-leave-active { /*定义过渡的状态*/ transition: all 1.5s ease; } .fade-enter, .fade-leave-to { /*定义进入动画的状态,以及离开动画最后结束的状态*/ transform: translateX(20px); } (2)css动画 在动画中,v-enter类名在节点插入dom后不会立即删除,而是在animationend时间触发时删除。 (也就是说,直到触发动画事件的时候才会删除这个类名) .animate-enter-active{ animation: bounze 1s; } .animate-leave-active{ animation: bounze 1.5s reverse; } @keyframes bounze { 0%{ transform: translateX(0px); } 50%{ transform: translateX(20px); } 100%{ transform: translateX(40px); } } (3)自定义过渡类名 可以通过自定义类名来使用其他第三方css动画库里面的类名(在transition元素上面指定自定义的类名) * enter-class * enter-active-class * enter-to-class (2.1.8+) * leave-class * leave-active-class * leave-to-class (2.1.8+)
Toggle render name="custom-classes-transition" enter-active-class="animated tada" leave-active-class="animated bounceOutRight" > hello (4)JavaScript钩子 就是在transition 过渡或者动画中使用JavaScript的的处理函数进行处理,有一下八个钩子 before-enter enter after-enter enter-cancelled before-leave leave after-leave leave-cancelled v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:after-enter="afterEnter" v-on:enter-cancelled="enterCancelled" v-on:before-leave="beforeLeave" v-on:leave="leave" v-on:after-leave="afterLeave" v-on:leave-cancelled="leaveCancelled" >
// ... methods: { // -------- // 进入中 // -------- beforeEnter: function (el) { // ... }, // 当与 CSS 结合使用时 // 回调函数 done 是可选的 enter: function (el, done) { // ... done() }, afterEnter: function (el) { // ... }, enterCancelled: function (el) { // ... }, // -------- // 离开时 // -------- beforeLeave: function (el) { // ... }, // 当与 CSS 结合使用时 // 回调函数 done 是可选的 leave: function (el, done) { // ... done() }, afterLeave: function (el) { // ... }, // leaveCancelled 只用于 v-show 中 leaveCancelled: function (el) { // ... } } 当仅仅使用JavaScript过渡的时候,需要有两个注意事项 在enter和leave中必须使用done来进行回调 在transition中使用v-bind:css="false",这样vue会跳过css的检测。 (5)初始渲染的过渡 可以通过appear特性来设置节点在初始渲染的过渡 (6)多个元素的过渡 就是多个元素之间只显示其中一个,当状态发生改变的时候,显示的元素不一样,就会出现元素之间的过渡。值得注意的是,官网推荐在相同标签名的元素之间进行切换的时候,需要给这些元素设置key特性,并拥有不一样的值,否则vue为了效率只会替换相同标签内部的内容。
Save
Edit
此外,在vue中,当两个元素进行过度的时候,transition的默认行为是进入和离开同时发生,那么此时两个元素就都会同时发生过度效果,这会影响效果,所以vue中有一个过渡模式 这个模式有两个值,分别是 out-in 离开元素先过渡然后再到进入的元素过渡 in-out 与上面相反 Save Edit
(6)多个组件的过渡 多个组件的过渡使用动态组件 Vue.component("tab-home",{ template:` this is home }); Vue.component("tab-aboutus",{ template:` this is aboutus }); Vue.component("tab-vechicle",{ template:` this is vechicle }); let vm = new Vue({ el:"#test", data:{ buts:["home","aboutus","vechicle"], currentTab:"home" }, computed:{ currentActiveTab:function () { return "tab-"+this.currentTab; } } }); {{but}} (7)列表过渡 使用transition-group组件包含列表元素 使用transition-group的特点 不同于transition,它会以一个真实的元素呈现,默认为一个span,可以通过tag特性来更改为其他元素 过渡模式不可以用,因为不再切换其他元素 内部元素总是需要有一个唯一的key属性 new Vue({ el: '#list-demo', data: { items: [1,2,3,4,5,6,7,8,9], nextNum: 10 }, methods: { randomIndex: function () { return Math.floor(Math.random() * this.items.length) }, add: function () { this.items.splice(this.randomIndex(), 0, this.nextNum++) }, remove: function () { this.items.splice(this.randomIndex(), 1) }, } }) {{ item }} .mutiple-item { display: inline-block; margin-right: 10px; } .mutiple-enter-active,.mutiple-leave-active{ transition: all 1s ; } .mutiple-enter,.mutiple-leave-to{ transform: translateY(10px); } 列表的排序过渡,transition-group组件还有一个特殊之处,不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需要了解新增的v-move特性,他会在元素的改变定位的过程中应用。 css的类名可以使用name属性自定义前缀,也可以通过move-class属性手动设置。 十二.vue-router 1.vue-router是vue官方的路由插件,适合用于构建单页面应用。 2.先引入vue-router 3.准备组件,vue-route给我们提供了两个新组件, 接下来定义视图组件以及与导航地址关联起来。创建一个router实例,创建实例的时候我们需要传参数routes来进行配置。 const hins = { template:` }; const ste = { template:` }; const adele = { template:` }; const router = new VueRouter({ routes:[ { path:"/hins", component:hins }, { path:"/ste", component:ste }, { path:"/adele", component:adele } ] }); 最后,创建一个vue实例,创建的时候通过配置router参数来注入我们刚定义好的router路由 //创建vue实例,注入路由router const app = new Vue({ el:"#app", router //此处是ES6语法, //相当于router:router }); 十三.vuex 1.vuex是什么? vuex是一个专为vue开发的应用程序的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex采用类似全局对象的形式来管理所有组件的公用数据,如果你想想修改这个全局对象的数据?是可以的,但没那么简单,你得按照Vuex提供的方式来修改,不能自己随意用自己的方式来修改 (1)vue的状态存储是响应式的,方你的组件使用到了这个vuex的状态,一旦它改变了,所有关联的组件都会自动更新相对应的数据。 (2)不能直接修改vuex的状态,想修改就得使用vuex提供的唯一途径:显示地提交mutations来实现修改, 2.vuex的3个核心概念 (1)要使用vuex,就要创建一个实例store,我们称之为仓库,利用这个仓库对状态进行管理 const store = new Vuex.Store({}); (2)State vuex使用单一状态树,用一个对象state包含了整个应用层级的所有状态。在下面代码中,假如我们有一个全局状态count,值为5,就可以将其定义为state对象里面的一对键值对,作为全局状态供我们使用。 const store = new Vuex.Store({ state:{ count:5 } }); (2)Getters 当我们需要从state中派生出一些状态的时候,就会使用到getters,相当于getters是state的计算属性。 const store = new Vuex.Store({ state:{ count:5 }, getters:{ newCount:state =>state.count*3 } }); (3)mutations vuex给我们提供修改仓库store中的状态的唯一方法就是通过提交mutation。 定义一个mutations: const store = new Vuex.Store({ state:{ count:5 }, getters:{ newCount:state =>state.count*3 }, mutations:{ increment(state){ state.count ++; } } }); 定义好了后就必须要提交mutation,语法如下: store.commit('increment'); 4.例子 需求:做一个计算器,那么运算的结果result和输入的值enter就是会跟每一个组件(按键) 相关联的,所以这里我们将result和enter作为应用层的状态(全局数据)来处理,这里就会简单的使用到vuex提供的仓库store。 首先完成上面界面的实现。 一共有3个主要区域,一个运算的结果,一个输入的结果,还有按键区域。 按键区域的使用可以使用组件来实现,定义一个keyboard组件,根据传入的参数来定义不一样的按键。
Vue.component("keyboard",{ props:['value'], template:` :data-value="value"> {{value}} }); const app = new Vue({ el:"#app", data:{ keys:[ 'clear','+','-','*', '7','8','9','/', '4','5','6','0', '1','2','3','=', ] } });
#app{ width: 100%; height: 100%; } .result{ width: 324px; height: 56px; font-size: 34px; margin-left: 178px; } .enter{ width: 324px; height: 50px; font-size: 24px; margin-left: 218px; } .keys{ width: 324px; height: 324px; } .list div{ width: 80px; height: 80px; line-height: 80px; background-color: #efefef; border: 0.5px solid #cccccc; border-bottom: none; border-right: none; float: left; text-align: center; font-size: 18px; vertical-align: center; } .list div:first-child{ color: #ff8282; } .list div:last-child{ color: white; background-color: #ff8282; } 界面实现了,接下来就是数据的显示以及如何进行数据绑定,当按键被按下的时候,显示输入的值以及运算的结果。这时候,组件们就共用到了result和enter这两个值,所以我们首先需要建立仓库来管理它们,并初始化为空字符串 const store = new VUex.Store({ state:{ result:"", enter:"" } }) vuex提供了store选项,允许我们将仓库store引入到根组件中,并且此跟组件的所有子组件都可以使用仓库store,而且子组件无需显示的引入。 首先我们在vue实例下面引入我们的仓库store const app = new Vue({ el:"#app", data:{ keys:[ 'clear','+','-','*', '7','8','9','/', '4','5','6','0', '1','2','3','=', ] }, store, } 引入之后我们就来使用,那么怎么使用仓库的result和enter呢?我们这里用到vue的computed计算属性。通过this.$store来回去仓库,然后this.$store.state来获取属性,最后用this.$store.state.result来获取仓库里的result。同理获得enter computed:{ result(){ return this.$store.state.result; }, enter(){ return this.$store.state.enter; } } 将result和enter显示在html代码里面 最后,绑定按键的点击事件,当我们点击键盘的时候,对仓库的result和enter进行修改。但是vuex中不能任意修改应用层的状态,所以就通过它提供的唯一的途径:通过commit提交mutation。当用户点击键盘的时候我们就提交一个mutation,并把当前按的键对应的值传过去。所以我们要重新定义keyboard组件的事件。 Vue.component("keyboard",{ props:['value'], template:` @click="getKeyboardValue" :data-value="value"> {{value}} methods:{ getKeyboardValue(event){ let value=event.target.dataset.value; this.$store.commit('calculate',value) } } }); 增加了一个监听点击事件的处理函数getKeyboardValue,我们会提交体格名为calculate的mutation给仓库,并将当前按键的值一起提交过去。这个calculate的mutation要在仓库中定义 const store = new Vuex.Store({ state:{ result:"", enter:"" }, mutations:{ calculate(state,value){ if (value === '='){ state.result = eval(state.enter); state.enter+=value; }else if (value === 'clear'){ state.result = state.enter = ""; }else { state.enter +=value; } } } }); 十四.使用axios (1)get请求 axios.get('detail.html?id=10').then(function (res) { console.log(res); }).catch(function (err) { console.log(err); }); axios.get('detail.html',{ params:{ id :10 } }).then(function (res) { console.log(res); }).catch(function (err) { console.log(err) }) (2)post请求 axios.post('detail.html',{ name:"krys", age:22 }).then(function (res) { console.log(res) }).catch(function (err) { console.log(err); }) (3)多个请求并发 function getProfile(){ //请求1 return axios.get('vuex.html'); } function getUser(){ //请求2 return axios.get('todo.html'); } //并发请求 axios.all([ getProfile(), getUser() ]).then(axios.spread((res1, res2)=>{ //两个请求现已完成 console.log(res1); console.log(res2); })); 我们事先定义了两个方法getProfile()和getUser()帮我们实现get请求。在某个业务场景中,我们需要同时产生以上两个get请求,并需要等待它们都请求完毕后再做逻辑处理,你也许会想到回调嵌套,但现在你可以使用axios的并发请求API: axios.all() 当这两个请求都完成的时候会触发 axios.spread() 函数,res1和res2两个参数分别代表返回的结果,相当实用的API。 上面的写法当中是axios提供给我们的别名写法,也可以写成下面这样 axios({ method: 'post', url: 'detail.html', data: { name: 'krys', } }); 十五、官网上看到的一些总结 1.使用object.freeze()会阻止修改现有的属性,也就是说,当代码中使用了Object.freeze(obj),那么在vue实例中将不会再追踪这个对象的变化。(仅仅针对data属性中的值是外部的一个对象) {{ foo }} var obj = { foo: 'bar' } Object.freeze(obj); new Vue({ el: '#app', data: obj }) 2.混入 (1)什么是混入? 混入(mixins)是一种分发vue组件中可以重复使用的功能的方式。 混入对象可以包含任意组件选项。当组件使用混入对象是,所有混入独享的选项将被混入该组件本身的选项。(其实就是多个组件中相同功能的部分) 组件在引用之后相当于在父组件内开辟了一块单独的空间,来根据父组件props过来的值进行相应的操作,单本质上两者还是泾渭分明,相对独立。 而mixins则是在引入组件之后,则是将组件内部的内容如data等方法、method等属性与父组件相应内容进行合并。相当于在引入后,父组件的各种属性方法都被扩充了。 (2)当组件和混入对象含有同名选项的时候,以下面这些方式进行混合 数据对象在内部会进行递归合并,在和组件的数据发生冲突的时候以组件数据为优 同名钩子函数将混为同一个数组,因此两个都会被调用,但是混入对象的钩子将会在组件自身钩子之前被调用 值为对象的选项,例如methods,components等将会被混合为同一个对象,当两个对象里面属性名冲突的时候,取组件对象的键值对 var mixin = { data: function () { return { message: 'hello', } }, created: function () { console.log('混入对象的钩子被调用') }, methods: { foo: function () { console.log('foo') }, conflicting: function () { console.log('from mixin') } } }; let vm = new Vue({ el:"#list-demo", mixins: [mixin], data: function () { return { message: 'goodbye', } }, created: function () { console.log('组件的钩子被调用') }, methods:{ bar: function () { console.log('bar') }, conflicting: function () { console.log('from self') } } }); vm.foo() // => "foo" vm.bar() // => "bar" vm.conflicting() // => "from self" (3)全局混入 全局声明混入对象,在全局混入对象中声明的属性,会影响所有的vue实例。 全局声明混入对象的代码 // 为自定义的选项 'myOption' 注入一个处理器。 Vue.mixin({ created: function () { var myOption = this.$options.myOption if (myOption) { console.log(myOption) } } }) let vm = new Vue({ el:"#list-demo", data: function () { return { message: 'goodbye', } }, myOption:"hello" }); 这样控制台会立即打印出hello 大多数情况下,只应当应用于自定义选项,就像上面示例一样。 3.自定义指令 自定义全局指令 Vue.directive("myfirst",{ inserted:function (el) { el.focus(); } }); 自定义局部指令 directives: { focus: { // 指令的定义 inserted: function (el) { el.focus() } } } 然后可以在模板中的任何元素上面使用
此外,指令定义对象有如下几个钩子函数(均为可选) bind:只调用一次,指令第一次绑定到元素时调用 inserted:被绑定你元素插入父节点时调用(也就是dom生成的时候调用) update:所在组件爱你的vnode更新时调用 componentUpdated所在组件的vnode及其子vnode全部更新后调用 unbind:只调用一次,指令与元素解绑时调用 这些钩子函数会被传入下面这些参数 * el:指令所绑定的元素,可以用来直接操作 DOM 。 * binding:一个对象,包含以下属性: * name:指令名,不包括 v- 前缀。 * value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。 * oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。 * expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1"中,表达式为 "1 + 1"。 * arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。 * modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。 * vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。 * oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。 4、渲染函数以及JSX 在vue中使用render生成html代码 Vue.component('anchored-heading', { render: function (createElement) { return createElement( 'h' + this.level, // 标签名称 this.$slots.default // 子元素数组 ) }, props: { level: { required: true } } }); (2)虚拟DOM 虚拟节点:包含的信息会告诉vue页面上需要渲染什么样的节点,以及其子节点。如下面会返回一个虚拟及诶单 createElement('h1', this.blogTitle) createElement( // {String | Object | Function} // 一个 HTML 标签字符串,组件选项对象,或者 // 解析上述任何一种的一个 async 异步函数。必需参数。 'div', // {Object} // 一个包含模板相关属性的数据对象 // 你可以在 template 中使用这些特性。可选参数。 { // (详情见下一节) }, // {String | Array} // 子虚拟节点 (VNodes),由 `createElement()` 构建而成, // 也可以使用字符串来生成“文本虚拟节点”。可选参数。 [ '先写一些文字', createElement('h1', '一则头条'), createElement(MyComponent, { props: { someProp: 'foobar' } }) ] )
这是文章的标题
Vue.component('myarticle',{
props:['detail'],
template :'
{{detail.title}}
\n' +
' let vm = new Vue({
el:"#app",
data:{
articles:[
{
title:"这是第一个标题",
date:"2018年11月14日",
is_oringinal:true,
imgurl:"img/logo.jpg"
},
{
title:"这是第二个标题",
date:"2018年11月15日",
is_oringinal:true,
imgurl:"img/logo.jpg"
},
{
title:"这是第三个标题",
date:"2018年11月16日",
is_oringinal:true,
imgurl:"img/logo.jpg"
}
]
}
})