重新系统学习一遍
视频教程地址:vue教程-黑马-205集完整版
视频教程源码:视频教程配套资料
Day1
P3:前端框架
vue Angular React三大主流框架。VUE只关注视图层,易上手。
P4:前端发展历程
原生JS》Jquery类库》前端模板引擎》Angular/Vue
在VUE里,用户不再操作DOM元素,提高渲染率,双向绑定
P6:MVC和MVVM
MVVM是Model-View-ViewModel的缩写。
- Model代表数据模型负责业务逻辑和数据封装;
- View代表UI组件负责界面和显示;
- ViewModel监听模型数据的改变和控制视图行为,处理用户交互,简单来说就是通过双向数据绑定把View层和Model层连接起来。
在MVVM架构下,View和Model没有直接联系,而是通过ViewModel进行交互,我们只关注业务逻辑,不需要手动操作DOM,不需要关注View和Model的同步工作。
P8:v-cloak
v-cloak能够解决插值表达式未加载完成的问题。默认v-text是没有闪烁问题的。v-text会覆盖元素中原本的内容。v-cloak不会。
<p v-cloak>++++++{{ msg }}+++++</p>
<p v-text=”msg”>+++++++++++++</p>
P9:v-bind
v-bind是vue中用于提供绑定属性的指令。其中也可以写合法的js表达式。缩写是“:”
P10:v-on
v-on是事件绑定机制。methods属性中定义该实例所有的方法。缩写是“@”。
P11:
在VM实例中,如果想要获取data上的数据或调用methods中的方法,必须通过this.数据属性名或this.方法名。
VM实例会监听自己data中所有数据的改变,一改变就会同步到页面上。
箭头函数()=>
P12:定时器
setInterval()定时器。clearInterval()停止定时器。
P13:事件修饰符
.stop阻止冒泡;
.prevent阻止默认事件;
.capture添加事件监听器时使用事件捕获模式。
.self只有点击该事件的元素的时候才会触发事件处理函数(阻止自己冒泡行为)
.once事件只触发一次
P14:v-model
v-model唯一双向绑定。
v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V,无法实现数据的双向绑定
<input type="text" v-bind:value="msg" style="width:100%;">
使用 v-model 指令,可以实现 表单元素和 Model 中数据的双向数据绑定
注意: v-model 只能运用在 表单元素中,如input(radio, text, address, email…) select checkbox textarea
<input type="text" style="width:100%;" v-model="msg">
P16:使用class样式
数组
<h1 :class="['red', 'thin']">这是一个邪恶的H1</h1>
数组中使用三元表达式
<h1 :class="['red', 'thin', isactive?'active':'']">这是一个邪恶的H1</h1>
数组中嵌套对象
<h1 :class="['red', 'thin', {'active': isactive}]">这是一个邪恶的H1</h1>
直接使用对象
<h1 :class="{red:true, italic:true, active:true, thin:true}">这是一个邪恶的H1</h1>
P17:使用内联样式
在data上定义样式:
data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },
h1StyleObj2: { fontStyle: 'italic' }
}
在元素中,通过属性绑定的形式,将样式对象应用到元素中:
<h1 :style="[h1StyleObj, h1StyleObj2]">这是一个善良的H1</h1>
P18~P19:v-for用法
P20:v-if和v-show的区别
v-if 的特点:每次都会重新删除或创建元素
v-show 的特点: 每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式
Day2
P26:vue-devtools调试工具的安装和使用
P28:全局过滤器的定义和使用
// 定义一个 Vue 全局的过滤器,名字叫做 msgFormat,第一个参数一定是data数据
Vue.filter('msgFormat', function (msg, arg, arg2) {
return msg.replace(/单纯/g, arg + arg2)
})
//定义第二个过滤器
Vue.filter('test', function (msg) {
return msg + '========'
})
调用方法如下:使用|符号调用,msg是第一个参数
<p>{{ msg | msgFormat('疯狂+1', '123') | test }}</p>
P30:私有过滤器的定义
var vm2 = new Vue({
el: '#app2',
data: {
dt: new Date()
},
methods: {},
filters: {
// 定义私有过滤器,有两个条件【过滤器名称 和 处理函数】
// 过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器名称一致了,这时候 优先调用私有过滤器
dateFormat: function (dateStr, pattern = '') {
//处理逻辑
}
}
});
P31:padStart()
//padStart方法:如果字符串没有满足长度2则自动补字符串‘0’上去。
var d = dt.getDate().toString().padStart(2, '0')
P32:自定义全局按键修饰符
定义:
//113是对应按键F2的ASCII码
Vue.config.keyCodes.f2 = 113
调用:
<input type="text" class="form-control" v-model="name" @keyup.f2="add">
P33:使用全局自定义指令
//使用Vue.directive()定义全局的指令v-focus
//其中:参数1:指令的名称,注意,在定义的时候,指令的名称前面,不需要加 v- 前缀,
//但是:在调用的时候,必须在指令名称前加上 v- 前缀来进行调用
//参数2:是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
Vue.directive('focus', {
//注意:在每个钩子函数中,第一个参数,永远是el,表示被绑定了指令的那个元素,这个 el 参数,是一个原生的JS对象
bind: function (el) { //每当指令绑定到元素上的时候,会立即执行这个 bind 函数,只执行一次
//在元素 刚绑定了指令的时候,还没有 插入到 DOM中去,这时候,调用 focus 方法没有作用
//因为,一个元素,只有插入DOM之后,才能获取焦点
//el.focus()
},
inserted: function (el) { //inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
el.focus()
//和JS行为有关的操作,最好在 inserted 中去执行,放置 JS行为不生效
},
updated: function (el) { //当VNode更新的时候,会执行 updated, 可能会触发多次
}
})
P34:binding
自定义指令中钩子函数的第二个参数binding,表示该指令元素的一个对象。该对象包含参数“name”,“value”,“expression”等。
P35~P36:定义私有指令。
原理和私有过滤器一样,Vue对象里的自定义指令属性是“directives”;
directives: { // 自定义私有指令
'fontweight': { // 设置字体粗细的
bind: function (el, binding) {
el.style.fontWeight = binding.value
}
},
//简写形式
'fontsize': function (el, binding) { // 注意:这个 function 等同于 把 代码写到了 bind 和 update 中去
el.style.fontSize = parseInt(binding.value) + 'px'
}
}
P37~P38:生命周期
什么是生命周期
从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!
- 生命周期钩子:就是生命周期事件的别名而已;
- 生命周期钩子 = 生命周期函数 = 生命周期事件
主要的生命周期函数分类
- 创建期间的生命周期函数:
beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板
beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示 - 运行期间的生命周期函数:
beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了! - 销毁期间的生命周期函数:
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: 'ok'
},
methods: {
show() {
console.log('执行了show方法')
}
},
beforeCreate() { // 这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
// console.log(this.msg)
// this.show()
// 注意: 在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的 数据都还没有没初始化
},
created() { // 这是遇到的第二个生命周期函数
// console.log(this.msg)
// this.show()
// 在 created 中,data 和 methods 都已经被初始化好了!
// 如果要调用 methods 中的方法,或者操作 data 中的数据,最早,只能在 created 中操作
},
beforeMount() { // 这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成了,但是尚未把 模板渲染到 页面中
// console.log(document.getElementById('h3').innerText)
// 在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串
},
mounted() { // 这是遇到的第4个生命周期函数,表示,内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
// console.log(document.getElementById('h3').innerText)
// 注意: mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,此时,如果没有其它操作的话,这个实例,就静静的 躺在我们的内存中,一动不动
},
// 接下来的是运行中的两个事件
beforeUpdate() { // 这时候,表示 我们的界面还没有被更新【数据被更新了吗? 数据肯定被更新了】
/* console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
console.log('data 中的 msg 数据是:' + this.msg) */
// 得出结论: 当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的,此时 data 数据是最新的,页面尚未和 最新的数据保持同步
},
updated() {
console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
console.log('data 中的 msg 数据是:' + this.msg)
// updated 事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
}
});
</script>
生命周期图解
P39: vue-resource
Vue实现ajax:
vue-resource过时了,现在一般用的是Axios,需要引用第三方包。
axios
var test=new Vue({
el:"#app",
data:{
},
methods:{
send:function(){
axios({
method: 'get',
url: 'http://127.0.0.1/xinxi/public/index.php/index/index/szxx'
})
.then(function (response) {
console.log(response)
})
.catch(function (error) {
console.log(error)
})
}
}
});
Day3
P44:全局配置请求地址
如果我们通过全局配置了,请求的数据接口根域名,则在每次单独发起 http 请求的时候,请求的 url 路径,应该以相对路径开头,前面不能带 / ,否则 不会启用根路径做拼接;
Vue.http.options.root = 'http://vue.studyit.io/';
P45:全局启用emulateJSON
全局启用 emulateJSON 选项
Vue.http.options.emulateJSON = true;
P46:动画过渡
在进入/离开的过渡中,会有 6 个 class 切换。
<style>
/* v-enter 【这是一个时间点】 是进入之前,元素的起始状态,此时还没有开始进入 */
/* v-leave-to 【这是一个时间点】 是动画离开之后,离开的终止状态,此时,元素 动画已经结束了 */
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(150px);
}
/* v-enter-active 【入场动画的时间段】 */
/* v-leave-active 【离场动画的时间段】 */
.v-enter-active,
.v-leave-active{
transition: all 0.8s ease;
}
</style>
<!-- 使用 transition 元素,把需要被动画控制的元素,包裹起来 -->
<!-- transition 元素,是 Vue 官方提供的 -->
<transition>
<h3 v-if="flag">这是一个H3</h3>
</transition>
P47:自定义动画类前缀
标签transition没有指定名称时,Vue动画类默认以“v-”做前缀。指定名称后则以名称做前缀。
<style>
.my-enter,
.my-leave-to {
opacity: 0;
transform: translateY(70px);
}
.my-enter-active,
.my-leave-active{
transition: all 0.8s ease;
}
</style>
<transition name="my">
<h6 v-if="flag2">这是一个H6</h6>
</transition>
P48:第三方动画库animate.css
第三方类库了解就行了,用的也不多,一般动画也是自己写,不会因为一个动画而且引用一整个库,
<link rel="stylesheet" href="./lib/animate.css">
<transition
enter-active-class="bounceIn"
leave-active-class="bounceOut"
:duration="{ enter: 200, leave: 400 }">
<h3 v-if="flag" class="animated">bounceIn,bounceOut,animated都是第三方类库定义好的。</h3>
</transition>
P49~P51:动画钩子函数
<body>
<div id="app">
<input type="button" value="快到碗里来" @click="flag=!flag">
<!-- 1. 使用 transition 元素把 小球包裹起来 -->
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<div class="ball" v-show="flag"></div>
</transition>
</div>
</body>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: false
},
methods: {
// 注意: 动画钩子函数的第一个参数:el,表示 要执行动画的那个DOM元素,是个原生的 JS DOM对象
// 大家可以认为 , el 是通过 document.getElementById('') 方式获取到的原生JS DOM对象
beforeEnter(el){
// beforeEnter 表示动画入场之前,此时,动画尚未开始,可以 在 beforeEnter 中,设置元素开始动画之前的起始样式
// 设置小球开始动画之前的,起始位置
el.style.transform = "translate(0, 0)"
},
enter(el, done){
// 这句话,没有实际的作用,但是,如果不写,出不来动画效果;
// 可以认为 el.offsetWidth 会强制动画刷新
el.offsetWidth
// enter 表示动画 开始之后的样式,这里,可以设置小球完成动画之后的,结束状态
el.style.transform = "translate(150px, 450px)"
el.style.transition = 'all 1s ease'
// 这里的 done, 起始就是 afterEnter 这个函数,也就是说:done 是 afterEnter 函数的引用
done()
},
afterEnter(el){
// 动画完成之后,会调用 afterEnter
// console.log('ok')
this.flag = !this.flag
}
}
});
</script>
P52~P54:列表动画transition-group
<!-- <ul> -->
<!-- 在实现列表过渡的时候,如果需要过渡的元素,是通过 v-for 循环渲染出来的,不能使用 transition 包裹,需要使用 transitionGroup -->
<!-- 如果要为 v-for 循环创建的元素设置动画,必须为每一个 元素 设置 :key 属性 -->
<!-- 给 ransition-group 添加 appear 属性,实现页面刚展示出来时候,入场时候的效果 -->
<!-- 通过 为 transition-group 元素,设置 tag 属性,指定 transition-group 渲染为指定的元素,如果不指定 tag 属性,默认,渲染为 span 标签 -->
<transition-group appear tag="ul">
<li v-for="(item, i) in list" :key="item.id" @click="del(i)">
{{item.id}} --- {{item.name}}
</li>
</transition-group>
<!-- </ul> -->
P55:模块化和组件化的区别
- 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;
- 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;
P56~P58:创建组件的三种方式
- 使用 Vue.extend 配合 Vue.component 方法:
// 1.1 使用 Vue.extend 来创建全局的Vue组件
// var com1 = Vue.extend({
// template: '<h3>这是使用 Vue.extend 创建的组件</h3>' // 通过 template 属性,指定了组件要展示的HTML结构
// })
// 1.2 使用 Vue.component('组件的名称', 创建出来的组件模板对象)
// Vue.component('myCom1', com1)
// 如果使用 Vue.component 定义全局组件的时候,组件名称使用了 驼峰命名,则在引用组件的时候,需要把 大写的驼峰改为小写的字母,同时,两个单词之前,使用 - 链接;
// 如果不使用驼峰,则直接拿名称来使用即可;
// Vue.component('mycom1', com1)
// Vue.component 第一个参数:组件的名称,将来在引用组件的时候,就是一个 标签形式 来引入 它的
// 第二个参数: Vue.extend 创建的组件 ,其中 template 就是组件将来要展示的HTML内容
var login = Vue.extend({
template: '<h1>登录</h1>'
});
Vue.component('login', login);
//调用方式在html里面直接<login></login>
- 直接使用 Vue.component 方法:
Vue.component('register', {
template: '<h1>注册</h1>'
});
- 将模板字符串,定义到script标签种:
<script id="tmpl" type="x-template">
<div><a href="#">登录</a> | <a href="#">注册</a></div>
</script>
同时,需要使用 Vue.component 来定义组件:
Vue.component('account', {
template: '#tmpl'
});
注意: 组件中的DOM结构,即template的值有且只能有唯一的根元素(Root Element)来进行包裹!
P59:定义实例内部私有组件
var vm2 = new Vue({
el: '#app2',
components: { // 定义实例内部私有组件的
login: {
template: '#tmpl2'
}
},
})
<template id="tmpl2">
<h1>这是私有的 login 组件</h1>
</template>
<div id="app2">
<login></login>
</div>
P60~P61:组件中的data数据
// 1. 组件可以有自己的data数据
// 2. 组件的data和实例的data有点不一样,实例中的data可以为一个对象,但是 组件中的data必须是一个方法
// 3. 组件中的data除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行;
// 4. 组件中的data数据,使用方式,和实例中的data使用方式完全一样!!!
Vue.component('mycom1', {
template: '<h1>这是全局组件 --- {{msg}}</h1>',
data: function () {
return {
msg: '这是组件的中data定义的数据'
}
}
})
P63:组件的切换
<body>
<div id="app">
<a href="" @click.prevent="comName='login'">登录</a>
<a href="" @click.prevent="comName='register'">注册</a>
<!-- Vue提供了 component ,来展示对应名称的组件 -->
<!-- component 是一个占位符, :is 属性,可以用来指定要展示的组件的名称 -->
<component :is="comName"></component>
<!-- 总结:当前学习了几个 Vue 提供的标签了??? -->
<!-- component, template, transition, transitionGroup -->
</div>
<script>
// 组件名称是 字符串
Vue.component('login', {
template: '<h3>登录组件</h3>'
})
Vue.component('register', {
template: '<h3>注册组件</h3>'
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
},
methods: {}
});
</script>
</body>
P64:组件切换动画
在组件component外面再包一层transition实现动画效果
<!-- 通过 mode 属性,设置组件切换时候的 模式 -->
<transition mode="out-in">
<component :is="comName"></component>
</transition>
Day4
P67:父组件向子组件传值
- 组件实例定义方式,注意:一定要使用
props
属性来定义父组件传递过来的数据
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: '123 啊-父组件中的数据'
},
components: {
// 子组件默认无法访问到父组件中的 data 上的数据 和 methods 中的方法
com1: {
data() { // 注意: 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上;
// data 上的数据,都是可读可写的;
return {
title: '123',
content: 'qqq'
}
},
template: '<h1 @click="change">这是子组件 --- {{ parentmsg }}</h1>',
// 注意: 组件中的 所有 props 中的数据,都是通过 父组件传递给子组件的
// props 中的数据,都是只读的,无法重新赋值
props: ['parentmsg'], // 把父组件传递过来的 parentmsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据
methods: {
change() {
this.parentmsg = '被修改了'
}
}
}
}
});
</script>
- 使用
v-bind
或简化指令,将数据传递到子组件中:
<div id="app">
<!-- 父组件,可以在引用子组件的时候, 通过 属性绑定(v-bind:) 的形式, 把 需要传递给 子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 -->
<com1 :parentmsg="msg"></com1>
</div>
P68:子组件向父组件传数据
- 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
- 父组件将方法的引用传递给子组件,其中,
show
是父组件中methods
中定义的方法名称,func
是子组件调用传递过来方法时候的方法名称
<com2 @func="show"></com2>
- 子组件内部通过
this.$emit('方法名', 要传递的数据)
方式,来调用父组件中的方法,同时把数据传递给父组件使用
<body>
<div id="app">
<!-- 父组件向子组件 传递 方法,使用的是 事件绑定机制; v-on, 当我们自定义了 一个 事件属性之后,那么,子组件就能够,通过某些方式,来调用 传递进去的 这个 方法了 -->
<com2 @func="show"></com2>
</div>
<template id="tmpl">
<div>
<h1>这是 子组件</h1>
<input type="button" value="这是子组件中的按钮 - 点击它,触发 父组件传递过来的 func 方法" @click="myclick">
</div>
</template>
</body>
<script>
// 定义了一个字面量类型的 组件模板对象
var com2 = {
template: '#tmpl', // 通过指定了一个 Id, 表示 说,要去加载 这个指定Id的 template 元素中的内容,当作 组件的HTML结构
data() {
return {
sonmsg: { name: '小头儿子', age: 6 }
}
},
methods: {
myclick() {
// 当点击子组件的按钮的时候,如何 拿到 父组件传递过来的 func 方法,并调用这个方法???
// emit 英文原意: 是触发,调用、发射的意思
// this.$emit('func123', 123, 456)
this.$emit('func', this.sonmsg)
}
}
}
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
datamsgFormSon: null
},
methods: {
show(data) {
// console.log('调用了父组件身上的 show 方法: --- ' + data)
// console.log(data);
this.datamsgFormSon = data
}
},
components: {
com2
// com2: com2
}
});
</script>
P71:ref获取DOM元素和组件
使用 this.$refs
来获取元素和组件
<div id="app">
<div>
<input type="button" value="获取元素内容" @click="getElement" />
<!-- 使用 ref 获取元素 -->
<h1 ref="myh1">这是一个大大的H1</h1>
<hr>
<!-- 使用 ref 获取子组件 -->
<my-com ref="mycom"></my-com>
</div>
</div>
<script>
Vue.component('my-com', {
template: '<h5>这是一个子组件</h5>',
data() {
return {
name: '子组件'
}
}
});
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
getElement() {
// 通过 this.$refs 来获取元素
console.log(this.$refs.myh1.innerText);
// 通过 this.$refs 来获取组件
console.log(this.$refs.mycom.name);
}
}
});
</script>
P72:前端路由
- 前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;
- 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);
P73:导入vue-router组件
<!-- 1.导入 vue-router 组件类库 -->
<script src="./lib/vue-router-2.7.0.js"></script>
P74~P78:vue-router基本使用
// 组件的模板对象
var login = {
template: '<h1>登录组件</h1>'
}
var register = {
template: '<h1>注册组件</h1>'
}
// 创建一个路由对象, 当 导入 vue-router 包之后,在 window 全局对象中,就有了一个 路由的构造函数,叫做 VueRouter
// 在 new 路由对象的时候,可以为 构造函数,传递一个配置对象
var routerObj = new VueRouter({
// route // 这个配置对象中的 route 表示 【路由匹配规则】 的意思
routes: [ // 路由匹配规则
// 每个路由规则,都是一个对象,这个规则对象,身上,有两个必须的属性:
// 属性1 是 path, 表示监听 哪个路由链接地址;
// 属性2 是 component, 表示,如果 路由是前面匹配到的 path ,则展示 component 属性对应的那个组件
// 注意: component 的属性值,必须是一个 组件的模板对象, 不能是 组件的引用名称;
// { path: '/', component: login },
{ path: '/', redirect: '/login' }, // 这里的 redirect 和 Node 中的 redirect 完全是两码事
{ path: '/login', component: login },
{ path: '/register', component: register }
],
linkActiveClass: 'myactive'//用来修改路由router-link标签默认的class类
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj // 将路由规则对象,注册到 vm 实例上,用来监听 URL 地址的变化,然后展示对应的组件
});
<div id="app">
<!-- router-link 默认渲染为一个a 标签,可以用tag修改-->
<router-link to="/login" tag="span">登录</router-link>
<router-link to="/register">注册</router-link>
<!-- 在外面包一层transition实现动画效果 -->
<transition mode="out-in">
<!-- 这是 vue-router 提供的元素,专门用来 当作占位符的,将来,路由规则,匹配到的组件,就会展示到这个 router-view 中去,所以我们可以把 router-view 认为是一个占位符 -->
<router-view></router-view>
</transition>
</div>
P79~P80:路由传参的两种方式
<!-- 这种方式给路由传递参数,不需要修改路由规则的 path 属性,组件内部使用$route.query获取参数 -->
<router-link to="/login?id=10&name=zs">登录</router-link>
var login = {
template: '<h1>登录 --- {{ $route.query.id }} --- {{ $route.query.name }}</h1>',
}
var router = new VueRouter({
routes: [
{ path: '/login', component: login }
]
})
<!-- 这种方式给路由传递参数,不需要加参数名但参数名需要在path里面指定,组件内部使用$route.params获取参数 -->
<router-link to="/login/10/zs">登录</router-link>
var login = {
template: '<h1>登录 --- {{ $route.params.id }} --- {{ $route.params.name }}</h1>',
}
var router = new VueRouter({
routes: [
{ path: '/login/:id/:name', component: login }
]
})
P81:路由嵌套
使用children
实现路由嵌套
var router = new VueRouter({
routes: [
{
path: '/account',
component: account,
// 使用 children 属性,实现子路由,同时,子路由的 path 前面,不要带 / ,否则永远以根路径开始请求,这样不方便我们用户去理解URL地址
children: [
{ path: 'login', component: login },
{ path: 'register', component: register }
]
}
]
})
P82:使用路由的命名视图实现布局
<div id="app">
<router-view></router-view>
<div class="container">
<router-view name="left"></router-view>
<router-view name="main"></router-view>
</div>
</div>
<script>
var header = {
template: '<h1 class="header">Header头部区域</h1>'
}
var leftBox = {
template: '<h1 class="left">Left侧边栏区域</h1>'
}
var mainBox = {
template: '<h1 class="main">mainBox主体区域</h1>'
}
// 创建路由对象
var router = new VueRouter({
routes: [
{
path: '/', components: {
'default': header,
'left': leftBox,
'main': mainBox
}
}]
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router
});
</script>
Day5
P87:watch
用于监听数据或对象的改变
<div id="app">
<input type="text" v-model="firstName"> +
<input type="text" v-model="lastName"> =
<span>{{fullName}}</span>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
firstName: 'jack',
lastName: 'chen',
fullName: 'jack - chen'
},
methods: {},
watch: {
'firstName': function (newVal, oldVal) { // 第一个参数是新数据,第二个参数是旧数据
this.fullName = newVal + ' - ' + this.lastName;
},
}
});
</script>
P89:computed
<div id="app">
<input type="text" v-model="firstname"> +
<input type="text" v-model="middlename"> +
<input type="text" v-model="lastname"> =
<input type="text" v-model="fullname">
<p>{{ fullname }}</p>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
firstname: '',
lastname: '',
middlename: ''
},
methods: {},
computed: { // 在 computed 中,可以定义一些 属性,这些属性,叫做 【计算属性】, 计算属性的,本质,就是 一个方法,只不过,我们在使用 这些计算属性的时候,是把 它们的 名称,直接当作 属性来使用的;并不会把 计算属性,当作方法去调用;
// 注意1: 计算属性,在引用的时候,一定不要加 () 去调用,直接把它 当作 普通 属性去使用就好了;
// 注意2: 只要 计算属性,这个 function 内部,所用到的 任何 data 中的数据发送了变化,就会 立即重新计算 这个 计算属性的值
// 注意3: 计算属性的求值结果,会被缓存起来,方便下次直接使用; 如果 计算属性方法中,所以来的任何数据,都没有发生过变化,则,不会重新对 计算属性求值;
'fullname': function () {
console.log('ok')
return this.firstname + '-' + this.middlename + '-' + this.lastname
}
}
});
</script>
P90:watch、computed和methods之间的对比
computed
属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;methods
方法表示一个具体的操作,主要书写业务逻辑;watch
一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computed
和methods
的结合体;
组件间的传递方式:
props
- 在组件内声明所有的props
- 方式一: 只指定名称
props: ['name', 'age', 'setName']
- 方式二: 指定名称和类型
props: {
name: String,
age: Number,
setNmae: Function
}
- 方式三: 指定名称/类型/必要性/默认值
props: {
name: {type: String, required: true, default:xxx},
}
- 使用组件标签时声明
<my-component :name='tom' :age='3' :set-name='setName'></my-component>
- 注意
- 此方式用于父组件向子组件传递数据
- 所有标签属性都会成为组件对象的属性, 模板页面可以直接引用
- 问题:
a. 如果需要向非子后代传递数据必须多层逐层传递
b. 兄弟组件间也不能直接props 通信, 必须借助父组件才可以
自定义事件
- 绑定事件监听
- 方式一: 通过v-on 绑定
@delete_todo="deleteTodo"
- 方式二: 通过$on()
this.$refs.xxx.$on('delete_todo', function (todo) {
this.deleteTodo(todo)
})
2.触发事件
// 触发事件(只能在父组件中接收)
this.$emit(eventName, data)
3.注意:
- 此方式只用于子组件向父组件发送消息(数据)
- 问题: 隔代组件或兄弟组件间通信此种方式不合适
订阅发布
- 引入pubsub
//终端安装
npm install --save pubsub-js
//js引入
import PubSub from ‘pubsub-js’
- 订阅消息
PubSub.subscribe('msg', function(msg, data){})
- 发布消息
PubSub.publish('msg', data)
- 注意
优点: 此方式可实现任意关系组件间通信(数据)
slot
此方式用于父组件向子组件传递标签数据
- 子组件里定义
<template>
<div>
<slot name="xxx">不确定的标签结构1</slot>
<div>组件确定的标签结构</div>
<slot name="yyy">不确定的标签结构2</slot>
</div>
</template>
- 父组件调用
<child><!--child自定义组件的标签-->
<div slot="xxx">xxx 对应的标签结构</div>
<div slot="yyy">yyyy 对应的标签结构</div>
</child>
- 注意
使用slot后,所有属性方法可以写在父组件里。
vuex
vuex是对vue 应用中多个组件的共享状态进行集中式的管理(读/写)
store 对象
映射store
import store from './store'
new Vue({
store
})
- 所有用vuex 管理的组件中都多了一个属性$store, 它就是一个store 对象
- 属性:
state: 注册的state 对象
getters: 注册的getters 对象 - 方法:
dispatch(actionName, data): 分发调用action
store的属性
- state
vuex 管理的状态对象,唯一的。
const state = {
xxx: initValue
}
- mutations
- 包含多个直接更新state 的方法(回调函数)的对象
- 谁来触发: action 中的commit(‘mutation 名称’)
- 只能包含同步的代码, 不能写异步代码
const mutations = {
yyy (state, {data1}) {
// 更新state 的某个属性,data1是个对象
}
}
- actions
- 包含多个事件回调函数的对象
- 通过执行: commit()来触发mutation 的调用, 间接更新state
- 谁来触发: 组件中: $store.dispatch(‘action 名称’, data1) // ‘zzz’
- 可以包含异步代码(定时器, ajax)
const actions = {
zzz ({commit, state}, data1) {
commit('yyy', {data1})
}
}
- getters
- 包含多个计算属性(get)的对象
- 谁来读取: 组件中: $store.getters.xxx
const getters = {
mmm (state) {
return ...
}
}
- modules
- 包含多个module
- 一个module 是一个store 的配置对象
- 与一个组件(包含有共享数据)对应
- 向外暴露store 对象
export default new Vuex.Store({
state,
mutations,
actions,
getters
})