Vue基本知识
MVVM
MVVM拆开来即为Model-View-ViewModel,有View,ViewModel,Model三部分组成。View层代表的是视图、模版,负责将数据模型转化为UI展现出来。Model层代表的是模型、数据,可以在Model层中定义数据修改和操作的业务逻辑。ViewModel层连接Model和View。
view对应dom元素,Vue对应VM,js数据对象对应Model
■View层:
视图层
在我们前端开发中,通常就是DOM层。
➢主要的作用是给用户展示各种信息。
■Model层 :
➢数据层
➢数据可能是我们固定的死数据,更多的是来自我们服务器,从网络上请求下来的数据。
➢在我们计数器的案例中,就是后面抽取出来的obj, 当然,里面的数据可能没有这么简单。
■VueModel层:
➢视图模型层
视图模型层是View和Model沟通的桥梁。
➢–方面它实现了DataBinding,也就是数据绑定,将
Model的改变实时的反应到View中
➢另一方面它实现了DOM Listener, 也就是DOM监听,当DOM发生一.些事件(点击、滚动、touch等)时,可以监听到,并在需要的情况下改变对应的Data。
MVVM的原理
MVVM的原理: Vue框架是如何实现MVVM设计模式的
(1). new Vue()加载data对象
a. 将data对象打散,data内部的属性直接隶属于new Vue()对象
b. 将data中每个原始属性隐姓埋名,隐藏
c. 为data中每个属性请保镖:
1). Data中每个属性都有一对儿get/set方法
2). 今后只要想修改data中的变量都会自动触发set()
3). 在每个属性的set方法中,都自动植入一个notify()函数调用,只要试图修改data中的属性值时,都会自动调用set(),只要自动调用set()势必会自动notify()发出通知
(2). 加载虚拟DOM树:
a. 通过el属性值的选择器找到要监控区域的父元素
b. 创建虚拟DOM树
c. 扫描这个要监控的区域:
1). 每发现一个{{变量}}的元素,就将该元素的信息,记录进虚拟DOM树,同时首次用data中同名变量的值,代替页面中{{n}}的位置。
2). 每发现一个@事件名="函数名"的元素,就自动变为:
On事件名=“new Vue().函数名”
(3). 加载methods对象: methods对象中的所有方法,都会被打散,直接隶属于new Vue()和data中被打散的属性平级
所以,在methods中的方法中,想操作data中的属性,都可以写为"this.属性名"即可!
(4). 当触发事件时,自动调用new Vue()中methods中指定的函数,执行其中this.属性名的修改。修改会自动触发属性的set()方法,自动触发set()内部的notify函数:
a. 遍历虚拟DOM树,只找出受影响的个别元素
b. 利用虚拟DOM树提前封装好的DOM操作,只修改页面中受影响的个别元素!——效率高!
总结: MVVM的原理/Vue的绑定原理:
访问器属性+观察者模式+虚拟DOM树
什么是虚拟DOM树:
(1). 什么是虚拟DOM树: 仅保存可能发生变化的少量元素的精简版DOM树
(2). 优点:
a. 小, 遍历快!
b. 只更新受影响的元素,效率高!
c. 封装了DOM操作,无需我们程序员重复编码!
插值表达式{{}}
注意:差值表达式不会解析html元素
指令
v-on
注意:v-on指令事件绑定机制,缩写是@,在vue中,使用事件绑定机制,为元素指定事件处理机制,如果加了小括号,就可以给函数传参了
<div id="app">
<input type="button" value="v-on指令" v-on:click="doit">
<input type="button" value="v-on指令简写" @click="doit">
</div>
var app=new Vue({
el:"#app",
methods:{
doit:function(){
console.log('Do it');
}
}
})
vue中如何获得事件对象 (vue中如何获得鼠标位置)
1). 如果事件处理函数不需要传入实参值时,则:
事件对象也是作为处理函数第一个参数值自动传入,也是在函数定义时,用一个形参e,就可接住——同DOM
示例: 使用e获得鼠标位置:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
div{
width:300px; height:100px;
margin:20px;
}
#d1{
background-color:#aaf
}
#d2{
background-color:#ffa
}
</style>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<div id="d1" @click="doit">d1</div>
<div id="d2">d2</div>
</div>
<script>
var vm=new Vue({
el:"#app",
data:{
},
methods:{
doit(e){//同DOM的e
console.log(`点在d1的: x:${e.offsetX},y:${e.offsetY}`);
}
}
})
</script>
</body>
v-text
注意:v-text指令会直接覆盖元素内部的内容,而且没有闪烁问题
<p v-text='messsge+"!!!!"'>哈哈哈</p>
v-html
可以解析html元素
<p v-html="content"></p>
var app=new Vue({
el:"#app",
data:{
content:'<a href="http://baidu.com">黑马程序员</a>'
}
})
v-cloak
防止用户短暂看到{{}}
问题: 因为vue代码是放在js文件中,所以,如果网速慢,vue代码暂时没有下载下来时,用户很可能短暂看到页面上的绑定语法,用户体验不好!
解决: 2个办法:
v-cloak
(1). 用v-cloak暂时隐藏带有{{}}内容的元素:
a. 2步:
1). 在包含绑定语法{{}}的元素上添加v-cloak属性
2). 在css中手动添加样式: [v-cloak]{ display:none }
b. 原理:
1). 用属性选择器查找所有带有v-cloak属性的元素,暂时隐藏
2). 当new Vue()渲染完成时,自动找到所有v-cloak属性,自动移除。
(2). 用v-text代替内容中{{}}语法,来绑定非HTML片段内容:
a. <元素 v-text=“原{{}}内容”></元素>
b. 原理:
1). 因为绑定语法写在了元素的属性里,所以,如果不是vue帮忙,用户无论如何是看不到元素属性中的内容的!
2). New Vue()读取到v-text时,会解析v-text的内容,替换元素开始标签和结束标签之间的内容
c. 强调:
1). 和v-html不同,v-text等效于{{}}等效于DOM中的textContent,所以如果v-text中包含HTML片段,是不会被编译,而是原样显示给人看!
2). v-text也是指令,所以v-text后的""中也可以写js表达式,比如字符串拼接!
3). 用了v-text,也不要在元素开始标签和结束标签直接写内容!因为会被v-text内容替换!
<span v-cloak>{{msg}}</span>
/* v-cloak能够解决插值表达式闪烁的问题*/
[v-cloak]{
display: none;
}
v-if与v-show
v-show 和 v-if 对比
v-show 采用display:none方式隐藏元素,不改变DOM树,效率高!
当条件为false时,v-show只是给元素添加一个行内样式:display: none
v-if 采用添加删除元素方式控制元素显示隐藏,可能频繁修改DOM树,效率低!
当条件为false时,包含v-if指令的元素,根本就不会存在dom中
开发中如何选择:
当需要在显示与影藏之间切片很频繁时,使用v-show
当只有一次切换时,通过使用v-if
<p v-if='isshow'>HHHHHHHHHHHHHHv-if</p>
<p v-if='tel>=35'></p>>热死了</p>
v-if与v-else
控制两个元素二选一显示隐藏:
(1). <元素1 v-if=“条件”>
<元素2 v-else>
(2). 强调:
a. v-else后不要写条件!(同js程序中的else)
b. v-if 和 v-else之间必须连着写,不能插入任何其他元素
v-for指令
<div id="app">
<!--1.在遍历对象的过程中,如果只获取一个值,那么获取到的是value-->
<ul>
<li v-for="item in obj">{{item}}</li>
</ul>
<!-- 循环普通数组 -->
<ul>
<li v-for="(item,index) in arr">
索引{{index}}
值{{item}}
</li>
</ul>
<!-- 循环对象数组 -->
<h2 v-for="items in objarr">
{{items.name}}{{items.id}}
</h2>
<!-- 循环对象 -->
<p v-for="(val,key,i) in obj">
键是{{key}} 值是{{val}} 索引是{{i}}
</p>
</div>
var app = new Vue({
el: "#app",
data: {
arr: ["北京", "上海", "深圳", "广州"],
objarr: [{ name: "wyh", id: '1' },
{ name: "ldh", id: '2' }],
obj:{
age:'19',
height:'183',
weight:'65kg'
}
}
})
注意: v-for绑定key时key的值必须是字符串或者数字,保证数据的唯一性,key在使用中必须使用v-bind进行绑定
强调: v-for一定要放在那个要反复生成的元素上,而不是放在父元素上!
(5) 坑: 如果v-for遍历的是数组时,在程序中通过下标修改数组元素值,页面上的HTML元素不会自动更改!
比如: this.teachers[0]=“燕儿” 页面上是不会变的!
因为数组中的数字类型的下标012…无法添加访问器属性,也就不受监控!
解决: 今后,vue中修改数组中的元素值!必须用数组家函数!才能自动更新页面。因为函数都是受监控的。
比如: this.teachers.splice(0,1,“燕儿”)
删除0位置的1个元素,再在0位置放入"燕儿"
结果: 页面会自动变化!
key
笔试: 为什么v-for必须加:key
答: 因为v-for反复生成的多个元素,除了内容不同之外,从元素属性上来看多个元素毫无差别!每个反复生成的元素都是一样的。所以,如果将来修改了数组中一个元素时,v-for因为无法识别每个HTML元素,所以只能把所有的HTML元素重新生成一遍——效率低!
如果给每个元素都绑定:key="i"属性,则每个HTML元素上都有一个唯一的标识key=“0” key=“1” … 。当将来修改了数组中每个位置的元素时,只需要修改对应key的HTML元素即可,其他HTML元素保持不变!——效率高!
总结: 避免修改数组元素时,重新生成所有HTML元素,而是只更新其中一个HTML元素即可!提高修改效率!
获取v-for中的下标
注意:click绑定多哥事件中间用分号隔开
<div id="app">
<ul>
<li v-for="(item,index) in list" @click="change();getindex(index)">{{item}}</li>
</ul>
</div>
遍历三层数据
<table v-for="(table, index) in paramInfo.sizes"
class="info-size" :key="index">
<tr v-for="(tr, indey) in table" :key="indey">
<td v-for="(td, indez) in tr" :key="indez">{{td}}</td>
</tr>
</table>
v-bind
注意:v-bind不支持驼峰命名法
v-bind是vue提供绑定属性的指令,相当于后面是解析表达式和变量了,v-bind指令可以被简写成一个:
<div id="app">
<img src="" alt="" v-bind:src=imgurl>
<!-- 简写 -->
<img src="" alt="" :src=imgurl>
<!-- 点击加边框 -->
<input type="button" @click="change" value="点击修改边框">
<img src="" alt="" :src=imgurl :class="{border:flag}">
</div>
v-model
v-model数据双向绑定,只能用于获取表单元素的值,数据实时更新
<input type="text" v-model="message">
data: {message: "Clivan"},
属性绑定设置样式
style
<div id='app'>
<h1 :style="{color:'pink'}">这是一个H1标签</h1>
<h1 :style="styleobj">这是一个H1标签</h1>
</div>
var app=new Vue({
el:'#app',
data:{
styleobj:{
color:'pink'
}
},
})
class
<style>
.thin {
font-weight: 200;
}
.italic {
font-style: italic;
}
.active {
color: red;
}
</style>
<div id="app">
<!-- 第一种方式,直接传入一个数组,用:绑定 -->
<!-- flag==true?'active':''等同于{'active':flag},提高可读性 -->
<h1 :class="['thin','italic',{'active':flag}]">这是一个很瘦并且倾斜的H1标签</h1>
<!-- 第二种方式,传入一个对象,对象的属性是类名 -->
<h1 :class="{thin:true,italic:true}">这是一个很瘦并且倾斜的H1标签</h1>
<h1 :class="classobj">这是一个很瘦并且倾斜的H1标签</h1>
</div>
var app = new Vue({
el: '#app',
data: {
flag: true,
classobj: { thin: true, italic: true }
},
methods: {}
})
组件
强调: 因为HTML标签名不区分大小写,所组件名如果包含多个单词,绝对不能用驼峰命名!必须用-分割多个单词。
比如: myCounter,错的。因为myCounter可能被转换为全大写或全消息的标签名,使用时会有歧义。
创建组件的方式
全局注册组件
Vue.component(‘Mycomp’, coml)
<div id="app">
<!-- 如果要使用组件,直接把组件的名称以标签的形式写入即可 -->
<Mycomp></Mycomp>
</div>
var coml = Vue.extend({
/* template属性展示了组件的html结构 */
template: ' <h3>这是使用Vue.extend创建的组件</h3>'
})
/*Vue.component(组件的名称,创建出来的组件模板对象) */
//如果使用Vue.component定义全局组件的时候,组件名称使用了驼峰命名,则在引用组件的时候,
//需要把大写的驼峰改为小写的字母,同时,两个单词之前,使用-链接
Vue.component('Mycomp', coml)
var app = new Vue({
el: '#app',
data: {},
})
注意:组件有且只有一个根元素,在被控制的#app元素外部,使用template元素定义组件的html结构
注意:组件不能直接访问vue实例上data的数据
局部注册组件
<div id="app">
<login></login>
</div>
<template id="tml">
<div>
<h1>这是通过template元素定义的组件结构</h1>
</div>
</template>
var app = new Vue({
el: '#app',
components: {
//定义实例内部私有组件
login: {
template: "#tml"
}
}
})
组件中的data为什么是一个函数?
因为如果多次使用同个组件,并操作其数据时,为了保证每个组件都有自己独立的数据,需要用函数return数据,这样每次得到的数据不变,但是内存地址不同。
父子组件传值
我们知到了子组件是不能弓|用父组件或者Vue实例的数据的。但是,在开发中,往往一些数据确实需要从上层传递到下层:比如在一个页面中,我们从服务器请求到了很多的数据。
其中一部分数据 ,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
这个时候,并不会让子组件再次发送一个网络请求 ,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
父子组件访问件
1.父组件访问子组件用$chilidren和 $refs, $children是子组件构成的数组
2.子组件访问父组件用 $parent /* 是访问最近的父组件 */
父组件向子组件传值
父组件可以在引用子组件的的时候通过属性绑定v-bind的形式,把需要传递给子组件的数据定义
props也可以写成对象,里面写数据对应的类型,默认值
<div id="app">
<coml v-bind:parentmsg='msg'></coml>
</div>
var app=new Vue({
el:'#app',
data:{
msg:'这是父组件的数据'
},
components:{
/* 默认子组件无法访问到父组件中data的数据和methods中的方法 */
coml:{
template: ' <h1>这是子组件,{{parentmsg}}</h1>',
/* 只有在props数组中定义才能使用,props里面的数据都是只读的,不能被修改 */
props:['parentmsg']
}
}
})
父组件向子组件传递方法
父组件向子组件传递方法使用的是事件绑定机制
<div id="app">
<coml @fun='show'></coml>
</div>
<template id="tml">
<div>
<h1>这是子组件</h1>
<input type="button" value="点击我触发父组件的fun方法" @click='cli'>
</div>
</template>
var coml = {
template: '#tml',
data() {
return {
sonmsg: '数据'
}
},
methods: {
cli() {
// 通过this.$emit触发,后面还可以传参
this.$emit('fun', 123, this.sonmsg)
}
}
}
var app = new Vue({
el: '#app',
data: {
fathermsg: null
},
methods: {
show(data1, data2) {
console.log('调用了父组件的show方法' + data1 + data2);
this.fathermsg = data2;
console.log(this.fathermsg);
}
},
components: {
coml
}
})
计算属性 computed
<div id="app">
firstname<input type="text" v-model="msg">+lastname<input type="text" v-model="msg1">=fullname<input type="text"
v-model="msg2">
</div>
var login = {
template: ' <h1>登录组件</h1>',
}
var register = {
template: ' <h1>注册组件</h1>',
}
var app = new Vue({
el: '#app',
data: {
msg: '',
msg1: '',
},
/* 在computed中可以定义一些属性,这些属性叫计算属性,计算属性本质就是方法 */
computed: {
/* 计算属性在引用的时候一定不要加()调用 */
/* 只要计算属性这个函数内部中的data发生了改变,就会触发这个函数 */
/*computed里面的数据如果没有改变,则会缓存 */
msg2:function(){
return this.msg+'-'+this.msg1;
}
},
})
solt的使用
如何去封装这类的组件呢?
它们也很多区别,但是也有很多共性。
如果,我们每一个单独去封装一个组件 ,显然不合适:比如每个页面都返回,这部分内容我们就要重复去封装。口但是,如果我们封装成一个,好像也不合理:有些左侧是菜单,有些是返回,有些中间是搜索,有些是文字,等等。
如何封装合适呢?抽取共性,保留不同。
最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽。
一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容。
是搜索框,还是文字,还是菜单。由调用者自己来决定。
这就是为什么我们要学习组件中的插槽slot的原因。
样式一样的不改动,不同的地方用插槽
<div id="app">
<!-- slot里面要写的东西 -->
<cpn> <button>btn</button></cpn>
<cpn></cpn>
</div>
<template id="tml">
<div>
<h1>哈哈哈</h1>
<!-- slot里面可以给默认值,如果组件里面没有写就使用默认值 -->
<slot><p>hhh</p></slot>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const cpn={
template: '#tml',
}
const app=new Vue({
el:'#app',
data:{},
components:{
cpn
}
})
具名slot
<div id="app">
<cpn> <h2 slot="left">66666</h2></cpn>
</div>
<template id="tml">
<div>
<h1>哈哈哈</h1>
<slot name="left"><span>默认值</span></slot>
<slot name='right'><span>默认值</span></slot>
</div>
</template>
Vue生命周期
<div id="app">
<input type="button" value="修改msg" @click='change'>
<p>{{msg}}</p>
</div>
var app = new Vue({
el: "#app",
data: {
msg: '红火火恍恍惚惚'
},
methods: {
change() {
this.msg = "no"
}
},
beforeCreate() {
console.log(this.msg);
//这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
//注意:在beforeCreate执行的时候,data和methods中的数据还没有初始化
},
//*重要
created() {
//这是第二个生命周期函数
//在created中,data和methods已经被创建好了
console.log(this.msg);
},
beforeMount() {
//这是第三个生命周期函数,表示模板已经在内存中编辑完成,但是尚未渲染到页面中去
console.log(document.querySelector('p').innerText);
},
//*重要
mounted() {
//这是第四个生命周期函数,表示内存中的模板已经被真实的挂载到了页面中
//这个函数执行完成后就表示整个vue实例被创建完成,开始进行运行阶段
console.log(document.querySelector('p').innerText);
},
//运行函数
beforeUpdate() {
//这个函数发生的时机是data被更新后执行的
//beforeUpdate执行时输出的数据是未更新前的数据,此时data数据是最新的
console.log(document.querySelector('p').innerText);
console.log(this.msg);
},
updated() {
//update执行时输出的数据是更新后的数据,此时页面与data中的数据已经保持同步了
console.log(document.querySelector('p').innerText);
console.log(this.msg);
},
//销毁阶段
/* 当执行 beforeDestroy钩子函数的时候, Vue实例就已经从运行阶段,进入到了销毁阶段;
当执行beforeDestroy 的时候,实例身上所有的data和所有的methods,
以及过滤器、指....都处于可用状态,此时,还没有真正执行销毁的过程*/
beforeDestroy() { },
/* 当执行到destroyed函数的时候,组件已经被完全销毁了,此时,
组件中所有的数据、方法、指令、过滤器.... 都已经不可用*/
Destroyed() { }
})
Vue过滤器
过滤器只能用于插值表达式或v-bind中,不影响原来的数据
什么是: 专门对变量的原始值进行加工后,再显示的特殊函数
为什么: 个别变量的原始值不能直接给人看!
比如: 性别 0和1 日期的毫秒数
何时: 如果一个变量的值不能直接给人看时,必须经过加工,才能给人看时
如何:
(1). 向Vue大家庭中添加过滤器函数
Vue.filter(“过滤器名字”, function(oldVal){ //接受一个变量的原始值
return 根据oldVal的不同,动态返回的新值
})
(2). 在绑定语法中使用过滤器函数:
{{变量 | 过滤器名 }}
结果:
(1). 变量的原始值不会立刻显示出来,而是先交给|后的过滤器函数
(2). 再将过滤器处理后的返回值,返回出来,显示在元素内容中
原理:
(1). Vue.filter(“过滤器名”, function(oldVal){ return 新值 })
定义一个过滤器函数,加入到Vue大家庭中备用
(2). 当new Vue()扫描到{{}}中的|时,会回Vue大家庭中查找相应名称的过滤器函数。
(3). 只要找到,就先将|前的变量原始值,交给过滤器函数的oldVal参数,经过过滤器函数的加工,返回新值。显示到当前绑定语法的位置。
<div id="app">
<!-- 过滤器格式 -->
<p>{{msg | change}}</p>
</div>
//定义一个全局的过滤器fliter,第一个参数已经被规定死了,永远都是过滤器前面的数据
Vue.filter('change',function(data){
return data.replace(/单纯/g,'邪恶')
})
var app=new Vue({
el:'#app',
data:{
msg:'我是一个单纯的人,很单纯'
},
methods:{}
})
Vue-resource
<div id="app">
<input type="button" value="get请求" @click='get'>
<input type="button" value="post请求" @click='post'>
<input type="button" value="jsonp请求" @click='jsonp'>
</div>
//请求过来的数据都在res.body里面
var app=new Vue({
el:'#app',
methods:{
get(){
this.$http.get('http://jsonplaceholder.typicode.com/users').then(function(res){
console.log(res);
})
},
//手动发起post请求默认没有表单格式,所以有的服务器处理不了
post(){
//post请求,中间花括号空的部分为提交给服务器的数据这里默认,emulateJSON:true,将手动提交表单格式设置为application/x-www-form-urlencoded格式
//第一个是请求地址,第二个是我们提交给服务器的数据,第三个规定格式
this.$http.post('http://jsonplaceholder.typicode.com/users',{},{emulateJSON:true}).then(res=>{
console.log(res);
})
},
jsonp(){
this.$http.jsonp('http://jsonplaceholder.typicode.com/users').then(result=>{
console.log(result)
})
}
}
})
使用ref获取dom元素
<div id="app">
<input type="button" value="点击获取value和h3中的文本" ref='mybtn' @click='getcontent'>
<h3 ref="myh3">这是h3</h3>
<login ref="mylogin"></login>
</div>
<template id="tml">
<div>
登录组件
</div>
var login={
template: '#tml',
methods:{
show(){
return console.log('调用了show方法');
}
}
}
var app=new Vue({
el:'#app',
methods:{
getcontent(){
console.log(this.$refs);
console.log(this.$refs.myh3.innerText);
console.log(this.$refs.mybtn.value);
console.log(this.$refs.mylogin.show());
}
},
components:{
login
}
})
Vue-router的基本使用
router-link默认会渲染为一个a标签
router-view这是vue router提供的,充当占位符,将来匹配到的组件就会在其中展示
<div id="app">
<!-- tag可以设置对应元素 -->
<router-link to="/login" tag="span">登录组件</router-link>
<router-link to="/register">注册组件</router-link>
<router-view></router-view>
</div>
var login = {
template: ' <h1>登录组件</h1>',
}
var register = {
template: ' <h1>注册组件</h1>',
}
//创建一个路由对象
const routers = new VueRouter({
//routes表示路由匹配规则,这个规则对象上有两个必须的属性:
//属性1:是path,表示监听哪个路由链接地址
//属性2是component,表示如果路由匹配到path则展示component对应的那个组件
//注意component属性值必须是一个组件的模板对象,不能是组件的引用名称
routes: [
{ path: '/login', component: login },
//重定向redirect默认打开页面在login页面
{ path: '/', redirect: '/login' },
{ path: '/register', component: register },
]
});
var app=new Vue({
el:'#app',
//将路由规则对象注册到vue实例上,用来监听url地址的变化
router:routers
})
params和query传递参数
params
<router-link to="/login/18">登录组件</router-link>
query
<router-link to="/login?name=wyh&age=18">登录组件</router-link>
使用chilidren属性实现路由嵌套
<div id="app">
<router-link to="/account">account</router-link>
<router-view></router-view>
</div>
<template id="tml">
<div>
<h1>这是account组件</h1>
<router-link to="/account/login">登录组件</router-link>
<router-link to="/account/register">注册组件</router-link>
<router-view></router-view>
</div>
</template>
var account={
template:'#tml'
}
var login = {
template: ' <h1>登录组件</h1>',
}
var register = {
template: ' <h1>注册组件</h1>',
}
//创建一个路由对象
const routers = new VueRouter({
routes: [
{ path: '/account',
component:account,
/* 使用childred实现子路由,并且前面不带/,否则永远以根路径开始请求 */
children:[{path:'login',component: login},
{path:'register',component:register }] },
]
});
var app=new Vue({
el:'#app',
router:routers
})
watch监听data和路由
watch属性用于监听指定数据的变化,并且触发里面的函数
<div id="app">
firstname<input type="text" v-model="msg">+lastname<input type="text" v-model="msg1">=fullname<input type="text"
v-model="msg2">
<router-link to="/login">登录组件</router-link>
<router-link to="/register">注册组件</router-link>
<router-view></router-view>
</div>
var login = {
template: ' <h1>登录组件</h1>',
}
var register = {
template: ' <h1>注册组件</h1>',
}
const router = new VueRouter({
routes: [
{ path: '/login', component: login },
{ path: '/', redirect: '/login' },
{ path: '/register', component: register },
]
});
var app = new Vue({
el: '#app',
data: {
msg: '',
msg1: '',
msg2: ''
},
router,
watch: {
msg: function () {
this.msg2 = this.msg + '-' + this.msg1
},
msg1: function () {
this.msg2 = this.msg + '-' + this.msg1
},
/* 这个函数有两个指定的形参,第一个是改变后新的数据,第二个是旧数据 */
'$route.path': function (newval, oldval) {
if(newval=='/login'){
console.log('欢迎进入登录页面');
}else{
console.log("欢迎进入注册页面");
}
}
}
})
路由导航守卫
vue-router传参
mixin混入
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
// 定义一个混入对象
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// 定义一个使用混入对象的组件
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component() // => "hello from mixin!"
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。
比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
vue中的el属性
每个vue2.0项目中我们都会看到入口文件(即main.js)中,在生成根实例时会配置el属性,而我们自己创建的组件中则不能配置该属性。
- - - - -
比方说我这里想获取自定义组件tabControl,并获取它的OffsetTop。就需要先获取该组件。
在组件内设置 属性 ref=‘一个名称(tabControl2)’,
然后 this.$refs.tabControl2 就拿到了该组件
切记:ref属性,而获取组件的时候要用$refs
获取 OffsetTop,组件不是DOM元素,是没有OffsetTop的,无法通过 点 .OffsetTop来获取的。就需要通过$el来获取组件中的DOM元素
响应式数组方法
因为vue是响应式的,所以当数据发生变化时,vue会自动检测数据的变化,视图会发生对应的更新。
vue中包含了一组观察数组编译的方法,使用他们改变数组也会触发视图的更新。
push() //往数组最后添加元素
pop() //删除数组中最后一个元素
shift() //删除数组中第一个元素
unshift() //在数组最前面添加元素
splice() //删除元素/插入元素/替换元素
splice(start,length,元素)
删除元素:第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)
替换元素:第二个参数,表示我们要替换几个元素,后面是用于替换前面的元素
插入元素:第二个参数,传入0,并且后面跟上要插入的元素
sort() //数组元素排序
reverse() //数组元素位置倒序
这里讲一下哪些数组操作不会引起视图更新?
1.通过索引值修改数组的元素
比如:this.books[0] = "深入浅出vue.js"
解决方法:
1. this.books.splice(0,1,"深入浅出vue.js")//替换
2. Vue.set(要修改的对象,索引值,修改后的值)
Vue.set(this.books,0,"深入浅出vue.js")
或者this.$set(this.books,0,"深入浅出vue.js") (这也是全局 Vue.set 方法的别名)
2.修改数组的长度
this.books.length = 10
Vue computed和watch的区别
计算属性computed:
支持缓存,只有依赖数据发生改变,才会重新进行计算
不支持异步,当computed内有异步操作时无效,无法监听数据的变化
computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
如果computed属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
侦听属性watch:
不支持缓存,数据变,直接会触发相应的操作;
watch支持异步;
监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
当一个属性发生变化时,需要执行对应的操作;一对多;
监听数据必须是data中声明过或者父组件传递过来的props中的数据.