第 1 章 课程介绍
1-1 课程简介
学习前提:
有一些 css、js、es6、webpack、npm 等基础知识
第 2 章 Vue 起步
2-1 学习方法
多看一下官网 api Vue 官网
务必把官网的小的 demo 自己敲一遍加深一下对语法糖的理解
2-2 hello world
那么初步认识一下 vue,那么从一个简单的 hello world 开始吧
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">{{content}}</div>
<script>
var app = new Vue({
el: '#root',
data: {
content: 'hello world'
}
});
setTimeout(function () {
app.$data.content = 'bye world'
}, 2000)
</script>
</body>
</html>
el:'#root' vue 实例化的数据只针对 id 为 root 内使用
{{content}} : 获取 vue 里面 data 里面的数据值
2-3 开发 TodoList(v-model、v-for、v-on)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">
<div>
<input type="text" v-model="inputValue">
<button v-on:click="handleAdd">提交</button>
</div>
<ul>
<li v-for="(item,index) in list" v-on:click="handleRemove(index)">{{item}}</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
inputValue: '',
list: []
},
methods: {
handleAdd() {
this.list.push(this.inputValue);
this.inputValue = '';
},
handleRemove(index) {
this.list.splice(index, 1);
}
}
});
</script>
</body>
</html>
v-on:click="handleClick" 绑定点击事件
v-model="inputValue" 数据双向绑定
v-for="(item,index) in list" 数据循环
2-4 MVVM 模式
MVP (Model View Presenter)
Model:接口请求操作
View:页面展示
P:处理数据和页面(大量的 dom 操作)
MVVM
而 Vue 则采用的是 mvvm 这种模式,它在乎的是数据的变化驱动 Ui 的变化,不用用户管 dom 的操作,vue 则扮演的是 VM 的操作,我们的重心是放在了 M 层也就是数据这一层
2-5 前端组件化
页面有好多部分组成,把页面切成一部分一部分,而拆分出来的部分,就是一个组件
2-6 使用组件改造 TodoList
1.全局组件的声明和使用
//Vue创建全局组件的方法
Vue.component('TodoItem', {
props: ['content'],
template: '<li>{{content}}</li>'
});
<todo-item v-bind:content="item" v-for="item in list"></todo-item>
ps:
数据的传递通过v-bind: 来定义传递的属性,后面跟上要传递的值
通过“props”来接受属性,再通过插值表达式来展示{{content}}
2.Vue 局部组件的创建和使用
//Vue局部组件的创建和使用
var TodoItem = {
props: ['content'],
template: '<li>{{content}}</li>'
};
var app = new Vue({
el: '#root',
components: {
TodoItem: TodoItem
}
})
ps:
定义一个变量,值为对象,把属性值和模板作为属性的键值
在Vue实例化中通过components这个参数来调用这个定义好的变量(局部组件)
2-7 简单的组件间传值
父组件向子组件传值:
子组件通过定义一个属性:v-bind:content="item",将 item 值传给子组件的 content
子组件通过 props:['content'] 来接受父组件传过来的值,再通过插值表达式来展示{{content}}
子组件向父组件传值:
子组件通过定义一个方法或者一个事件 handleItemClick,在方法或者事件中,通过 this.$emit(''delete",this.index)方法给给父组件发送一个信号,
父组件监听这个信号:@delete="handleDeleleItem"
下面代码演示:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello world</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">
<div>
<input type="text" v-model="inputValue">
<button v-on:click="handleAdd">提交</button>
</div>
<ul>
<!--<li v-for="(item,index) in list" v-on:click="handleRemove(index)">{{item}}</li>-->
<todo-item
v-bind:content="item"
v-bind:index="index"
v-for="(item,index) in list"
@delete="handleItemDelete"
>
</todo-item>
</ul>
</div>
<script>
//Vue创建全局组件的方法
// Vue.component('TodoItem', {
// props: ['content'],
// template: '<li>{{content}}</li>'
// });
//Vue局部组件的创建和使用
var TodoItem = {
props: ['content', 'index'],
template: '<li @click="handleItemClick">{{content}}</li>',
methods: {
handleItemClick() {
this.$emit('delete', this.index);
}
}
};
var app = new Vue({
el: '#root',
components: {
TodoItem: TodoItem
},
data: {
inputValue: '',
list: []
},
methods: {
handleAdd() {
this.list.push(this.inputValue);
this.inputValue = '';
},
// handleRemove(index) {
// this.list.splice(index, 1);
// },
handleItemDelete(index) {
console.log(index);
this.list.splice(index, 1)
}
}
});
</script>
</body>
</html>
2-8 Vue 的一些指令简写方法
v-on:click 等价于 @click //this.emit('delete') 接受也是 @delete
v-bind:content 等价于 :content
第 3 章 Vue 基础精讲
3-1 Vue 实例
vue 实例是根实例,组件也是 vue 实例,所以说页面是由很多 vue 实例组成的
.data(): 以 $ 开头的指的是 vue 实例的属性或方法
vm.$destroy():用于销毁 vue 实例,但是之前的数据和方法并没有被销毁
var app = new Vue({
el:'#root',
data:{
message:'hello world'
},
methods: {
handleClick(){},
},
watch:{
},
computed:{
}
})
3-2 Vue 实例生命周期
//生命周期函数就是vue实例在某个时间点自动执行的函数
var app = new Vue({
el:'#root',
data:{
inputValue:''
},
beforeCreate: function () {
console.log('beforeCreate');
},
created: function () {
console.log('created');
},
beforeMount: function () {
console.log(this.$el);
console.log('beforeMount');
},
mounted: function () {
console.log(this.$el);
console.log('mounted');
},
beforeDestroy: function () {
//app.$destroy()
console.log('beforeDestroy');
},
destroyed: function () {
console.log('destroyed');
},
beforeUpdate: function () {
//app.$data.inputValue = 1
console.log('beforeUpdate')
},
updated: function () {
console.log('updated')
}
})
3-3 Vue 的模版语法
插值表达式{{}} : 用{{输入的值}}
v-指令 写的是 js 表达式
v-text 就是 innertext 其实就是 {{}}
v-html 就是 innerhtml
v-指令 后面除了可以写 js 名称还可以加字符串,插值表达式也可以写字符串
var app = new Vue({
el: '#root',
data: {
name: 'hello',
bigName: '<h1>hello</h1>'
}
})
{{name + ' world'}}
<div v-text="name + ' world' "></div>
<div v-html="name + ' world' "></div>
ps:v-html 会对bigName进行转义,字体变成h1字体大小,而不会出现标签
3-4 计算属性、方法与侦听器
1.计算属性
computed 属性,因为他是属性,所以在用插值表达式取值的时候不用加括号
computed:内置变量缓存的功能,当 data 里面 age 变量更改时,如果不是计算属性内边的变量更改,那么他就不会渲染 computed 内部的变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">
{{fullName}}
</div>
<script>
var app = new Vue({
el: '#root',
data: {
firstName: 'sunny',
lastName: 'fan',
age: 28
},
//计算属性:内置缓存(firstName、lastName)
computed: {
fullName: function () {
console.log('计算了一次');
return this.firstName + " " + this.lastName
}
}
})
</script>
</body>
</html>
2.methods 方法
因为是方法,所以插值表达式要用括号取值,
他不具有缓存变量的功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">
{{fullName()}}
</div>
<script>
var app = new Vue({
el: '#root',
data: {
firstName: 'sunny',
lastName: 'fan'
},
methods: {
fullName: function () {
console.log("计算了一次")
return this.firstName + " " + this.lastName
}
}
})
</script>
</body>
</html>
3.侦听器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>侦听器</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">
{{fullName}} {{age}}
</div>
<script>
var app = new Vue({
el: '#root',
data: {
firstName: 'sunny',
lastName: 'fan',
fullName: 'sunny fan',
age: 28
},
watch: {
firstName: function () {
console.log('计算了一次');
this.fullName = this.firstName + " " + this.lastName
},
lastName: function () {
console.log('计算了一次');
this.fullName = this.firstName + " " + this.lastName
}
}
})
</script>
</body>
</html>
总结:我们可以通过 methods、computed、watch 来实现 fullName 显示的问题
computed 和 watch 都具备缓存的功能
但是从代码量的编写程度来看,computed 属性会更加方便和便捷一些。
3-5 计算属性的 getter 和 setter
computed 属性当中有两个方法,分别是:get 和 set
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>getter和setter</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<body>
<div id="root">
{{fullName}} {{age}}
</div>
<script>
var app = new Vue({
el: '#root',
data: {
firstName: 'sunny',
lastName: 'fan',
age: 28
},
computed: {
fullName: {
get: function () {
return this.firstName + " " + this.lastName
},
set: function (value) {
console.log(value);
var arr = value.split(" ");
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
</script>
</body>
</body>
</html>
3-6 Vue 中的样式绑定
1.class 的对象绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>class的对象绑定</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<style>
.activated {
color: red;
}
</style>
</head>
<body>
<body>
<div id="root">
<div @click="handleChangeColor" :class="{activated:isActivated}">
hello world
</div>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
isActivated: false
},
methods: {
handleChangeColor: function () {
this.isActivated = !this.isActivated
}
}
})
</script>
</body>
</body>
</html>
2.class 的数组绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>class的数组绑定</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<style>
.activated-one {
font-size: 20px;
}
.activated {
color: red;
}
</style>
</head>
<body>
<div id="root">
<div @click="handleChangeColor" :class="[activated,activatedOne]">hello world</div>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
activated: '',
activatedOne: 'activated-one'
},
methods: {
handleChangeColor: function () {
this.activated = this.activated === 'activated' ? "" : "activated"
}
}
})
</script>
</body>
</html>
3.style 对象绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>style对象绑定</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">
<div @click="handleChangeColor" :style="styleObj">hello world</div>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
styleObj: {
color: 'black'
}
},
methods: {
handleChangeColor: function () {
this.styleObj.color = this.styleObj.color === 'black' ? 'red' : 'black'
}
}
})
</script>
</body>
</html>
4.style 的数组绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>style数组绑定</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">
<div @click="handleChangeColor" :style="[styleObj,{fontSize:'30px'},styleOne]">hello world</div>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
styleObj: {
color: 'black'
},
styleOne: {
fontWeight: 'bold'
}
},
methods: {
handleChangeColor: function () {
this.styleObj.color = this.styleObj.color === 'black' ? 'red' : 'black'
}
}
})
</script>
</body>
</html>
3-7 条件渲染
v-if 、v-else-if、v-else
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>V-if</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">
<h5>实例一:v-if</h5>
<template v-if="isShow">
hello world
</template>
<button @click="handleChange">{{toggleText}}</button>
<hr>
<h5>实例二:v-else</h5>
<div v-if="isShowTwo">
要是我显示
</div>
<div v-else>
要么你显示
</div>
<button @click="handleChangeRole">切换显示</button>
<hr>
<h5>实例三:v-else-if</h5>
<div v-if="status==='A'">
A
</div>
<div v-else-if="status==='B'">
B
</div>
<div v-else-if="status==='C'">
C
</div>
<div v-else>
其他
</div>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
isShow: false,
toggleText: '显示',
isShowTwo: true,
status: 'A'
},
methods: {
handleChange: function () {
this.isShow = !this.isShow;
this.toggleText = this.toggleText === '显示' ? '隐藏' : '显示'
},
handleChangeRole: function () {
this.isShowTwo = !this.isShowTwo;
}
}
})
</script>
</body>
</html>
用 key
管理可复用的元素
当切换两个 input 输入框的时候,为了不让 input 框的输入内容被占用,所以我们通过设置 input 的 key 值来解决这个问题
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="userName-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
demo 例子:https://codepen.io/sunnyfan/pen/JQjRry
v-show
v-show 很相似,只要设置值为 true 则显示
v-if 和 v-show 的区别
- v-show 不能和 v-else 和 v-else-if 结合使用
- v-show 不管是 ture 还是 fasle div 元素都会渲染出来(false style 的 display:none),如果如果有频繁的切换,我们会首选 v-show,减少对 dom 的频繁操作
3-8 Vue 列表渲染
1.v-for
# view 部分
<li v-for="(item,index) in list" :key="index">{{index}}--{{item}}</li>
<li v-for="(item,key,index) of userInfo" :key="index">{{key}}-{{item}}-{{index}}</li>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表渲染</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">
<ul>
<li v-for="(item,index) in list" :key="index">{{index}}--{{item}}</li>
</ul>
<ul>
<li v-for="(item,key,index) of userInfo" :key="index">{{key}}-{{item}}-{{index}}-</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
list: [
'hello',
'world'
],
userInfo: {
name: 'sunny',
age: 29
}
}
})
</script>
</body>
</html>
template 可以当一个空标签做为 for 循环渲染,而这个 template 不会渲染到 dom 里面
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
为了防止子组件循环渲染出现 dom 结构不对的情况,我们一般会通过 is
来给子组件命名
//html
<table>
<tbody>
<tr is="row" v-for="item in list" :title="item"></tr> //这个地方调用is属性
</tbody>
</table>
//js
Vue.component('row', {
props: ['title'],
template: '<tr><td>{{title}}</td></tr>'
});
var app = new Vue({
el: '#root',
data: {
list: [
'hello',
'world'
]
}
})
更改数组值方法有哪些?
1.变异方法
push()、 pop()、 shift()、unshift()、splice()、sort()、reverse()
2.替换数组
当也可以创建一个新的数组,在通过
filter()、concat()、slice()
更改原始数组的值,再把更改后的数组替换旧的数组
3.set 或者 $set 方法
Vue.set(app.userInfo,'age',22)
//或者
vm.$set(app.userInfo,'age',22)
4.Object.assign()或者_.extend()
vm.userInfo = Object.assign({},vm.userInfo,{
sex:'男',
email:'fx35792@163.com'
})
ps:不可以通过数组下标去更改数组的值
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
3-9 Vue 事件处理
监听事件、方法处理、内联处理器中的方法
1.监听事件
通过 v-on
指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>监听事件</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="root">
<button v-on:click="counter+=1">add 1</button>
<p>The button above has been clicked {{counter}} times.</p>
</div>
<script type="text/javascript">
var app = new Vue({
el: '#root',
data: {
counter: 0
}
})
</script>
</body>
</html>
2.事件处理方法
但是在开发的过程中,有时候直接把 JavaScript 代码写在 v-on
指令中是不可行的。因此 v-on
还可以接收一个需要调用的方法名称
<div id="root">
<button v-on:click="greet">greet</button>
</div>
<script>
const app = new Vue({
el: '#root',
data: {
name: 'sunny'
},
methods: {
greet: function (event) {
console.log(`hello ${this.name}`)
if (event) {
console.log(event.target.tagName)
}
}
}
})
</script>
3.内联处理器中的方法
除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法:
<button v-on:click="say('hi')">say hi</button>
<button v-on:click="say('hello')">say hello</button>
const app = new Vue({
el: '#root',
data: {
name: 'sunny'
},
methods: {
say: function (val) {
console.log(`${this.name} ${val}`)
}
}
})
方法总通过传递参数 $event
,可以原始的 DOM
<button v-on:click="warning('please input number',$event)">warning</button>
const app = new Vue({
el: '#root',
data: {},
methods: {
warning: function (val, event) {
if (event) event.preventDefault();
console.log(val)
}
}
})
4.事件修饰符
我们在开发个过程中经常调用一些
event.preventDefault() //阻止事件的默认行为
event.stopPropagation() //阻止冒泡的行为
而 vue 为了更好的让我们关注业务逻辑代码的编写,它封装了很多 v-on
修饰符来帮助我们完成上面那些工作。
stop
//event.stopPropagation()prevent
//event.preventDefault()capture
self
once
passive
第 4 章 深入理解 Vue 组件
4-1 使用组件细节点
1.is 的使用
当我们写循环组件的时候,经常给
table 中的 tr
select 中的 option
ul 中的 li 或者 ol 中的 li
等等定义组件的时候,我们经常用 is 来定义组件的名称,为了让浏览器成功的渲染正确的 dom 结构
<div id="root">
<table>
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table>
<select name="" id="">
<option is="selectOption"></option>
<option is="selectOption"></option>
<option is="selectOption"></option>
</select>
<ul>
<li is="ulLi"></li>
<li is="ulLi"></li>
<li is="ulLi"></li>
</ul>
</div>
<script>
Vue.component('row', {
template: '<tr><td>this is a row</td></tr>'
});
Vue.component('selectOption',{
template: '<option>this is option</option>'
});
Vue.component('ulLi',{
template: '<li>this is li</li>'
});
var app = new Vue({
el: '#root',
data: {},
})
</script>
2.在子组件定义 data 的时候,必须是一个函数,而不能是一个对象,返回一个对象是为了每个子组件都能拥有一个独立的数据存储。这样子组件之间的数据不会互相影响
而在根组件中,data 可以是一个对象。
<div id="root">
<table>
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table>
</div>
<script>
Vue.component('row', {
data: function () {//返回的是一个函数
return {
content: 'this is row'
}
},
template: '<tr><td>{{content}}</td></tr>'
});
var app = new Vue({
el: '#root',
data: {}
})
</script>
3.有时候我们在开发过程中,因为一些业务的需求,少不了对 dom 的操作,那么我们就可以借助 ref 来实现
//实例一
<div id="root">
<div ref="hello" @click="handleClick">hello world</div>
</div>
<script>
var app = new Vue({
el: '#root',
data: {},
methods: {
handleClick: function () {
console.log(this.$refs.hello.innerHTML);//通过refs属性 获取当前节点的文本
}
}
});
</script>
//案例二 counter求和
<div id="root">
<counter ref="one" @change="handleChange"></counter>
<counter ref="two" @change="handleChange"></counter>
<div>{{total}}</div>
</div>
<script>
Vue.component('counter', {
data: function () {
return {
number: 0
}
},
template: '<div @click="handleClick">{{number}}</div>',
methods: {
handleClick: function () {
this.number++;
this.$emit('change');//触发一个监听器
}
}
});
var app = new Vue({
el: '#root',
data: {
total: 0
},
methods: {
handleChange: function () {
this.total = this.$refs.one.number + this.$refs.two.number //通过refs 来回去组件的值
}
}
});
</script>
4-2 父子组件之间的数据传递
父组件向子组件传值:是通过属性的方式
子组件向父组件传值:可以通过 $emit
来触发一个事件
vue 数据传递遵循的是单向数据流,
所以在下面的案例中我们并没有对 content 数据直接进行数据的累加,而是把 content 数据赋值给了 number,对 number 进行数据的累加操作。
<div id="root">
<counter :content="1" @inc="handleInc"></counter><!--父组件通过属性向子组件传值-->
<counter :content="3" @inc="handleInc"></counter>
<div>{{total}}</div>
</div>
<script>
Vue.component('counter', {
props: ['content'],
data: function () {
return {
number: this.content //遵循单向数据流
}
},
template: '<div @click="handleClick">{{number}}</div>',
methods: {
handleClick: function () {
this.number += 2;
//子组件通过方法向父组件传值
this.$emit('inc', 2);
}
}
});
var app = new Vue({
el: '#root',
data: {
total: 4
},
methods: {
handleInc: function (step) {
this.total += step
}
}
})
</script>
4-3 组件参数校验和非 props 特性
1.组件的的参数校验
<div id="root">
<child content="hello"></child>
</div>
<script>
Vue.component('child', {
props: {
content: {
type: String,
required: true,
default: 'default Value',
validator: function (value) {
return (value.length > 5)
}
}
},
template: '<div>{{content}}</div>'
});
var app = new Vue({
el: '#root',
})
</script>
2.props 特性和非 props 特性的对比
props 特性:
父组件传递属性,子组件要接受该属性
props 属性不会显示在 dom 的标签之中
非 props 特性:
父组件传递属性,子组件没有去接受,而是直接调用
props 属性会显示在 dom 的标签之中
4-4 给组件绑定原生事件
通过 .native
属性来绑定原生事件
<div id="root">
<child @click.native="handleClick"></child>
</div>
<script>
Vue.component('child', {
template: '<div>child</div>'
})
var app = new Vue({
el: '#root',
methods: {
handleClick: function () {
console.log('click');
}
}
})
</script>
4-5 非父子组件间的传值
1.通过 vuex
2.通过发布订阅模式(Bus/总线/发布订阅模式/观察者模式/)
<div id="root">
<child content="sunny"></child>
<child content="fan"></child>
</div>
<script>
Vue.prototype.bus = new Vue();//定义bus
Vue.component('child', {
data: function () {
return {
newContent: this.content //保证单向数据流
}
},
props: {
content: String
},
template: '<div @click="handleClick">{{newContent}}</div>',
methods: {
handleClick: function () {
this.bus.$emit('change', this.newContent); //在bus上发布一个事件,并且传值
}
},
mounted: function () {//通过这个钩子,来监听change的变化,通过回调拿到相对应的的值
var that = this;
this.bus.$on('change', function (msg) {
console.log(msg)
that.newContent = msg//this 指向发生变更,所以上面要从新获取一下this的指向
})
}
});
var app = new Vue({
el: '#root'
})
4-6 在 vue 中使用插槽
插槽只能有一个
而剧名插槽可以有多个
<div id="root">
<body-content>
<p slot="header">this is header</p>
<p slot="footer">this is footer</p>
</body-content>
</div>
<script>
Vue.component('body-content',{
template:
`<div>
<slot name="header">default header</slot> //设置默认值
<p>this is content</p>
<slot name="footer"></slot>
</div>`
})
var app = new Vue({
el:'#root'
})
</script>
4-7 作用域插槽
父组件调用子组件的时候,给子组件传了一个插槽,这个插槽是一个作用域的插槽,这个插槽必须是一个 <template slot-scope="props">{{props.item}}</template>
那什么时候使用作用插槽呢?
1.当子组件做循环
2.或者当子组件的 dom 结构由外部传递进来,或者有外部决定的时候
<div id="root">
<child>
<template slot-scope="props">
<li>{{props.item}}</li>
</template>
</child>
</div>
<script>
Vue.component('child', {
data: function () {
return {
list: [1, 2, 3, 4]
}
},
template: `<div>
<ul>
<slot v-for="item of list" :item=item></slot>
</ul>
</div>`
})
var app = new Vue({
el: '#root'
})
</script>
4-8 动态组件和 v-once 指令
<div id="root">
<component :is="type"></component> <!--这就是动态组件-->
<child-one v-if="type==='child-one'"></child-one>
<child-two v-if="type==='child-two'"></child-two>
<button @click="hanleBtnClick">change</button>
</div>
<script>
Vue.component('child-one', {
template: '<div v-once>child-one</div>'
})
Vue.component('child-two', {
template: '<div v-once>child-two</div>'
})
var app = new Vue({
el: '#root',
data: {
type: 'child-one'
},
methods: {
hanleBtnClick: function () {
this.type = this.type === 'child-one' ? 'child-two' : 'child-one'
}
}
})
</script>
第 5 章 表单
5-1 双向数据绑定 v-model
<div id="root">
<p>
<label for="">请输入姓名</label>
<input type="text" v-model="name" placeholder="请输入名字">
</p>
<p>
你的名字是:{{name}}
</p>
</div>
<script>
var app = new Vue({
el: '#root',
data() {
return {
name: ''
}
},
})
</script>
5-2 复选框(checkbox)相关的操作
1)、单个复选框的取反操作
2)、多个复选框的数组操作
<div id="root">
<p>单个复选框:</p>
<p>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{checked}}</label>
</p>
<p>多个复选框:</p>
<p>
<input type="checkbox" id="chinese" value="chinese" v-model="checkedNames">
<label for="chinese">chinese</label>
<input type="checkbox" id="Math" value="Math" v-model="checkedNames">
<label for="Math">Math</label>
<input type="checkbox" id="English" value="English" v-model="checkedNames">
<label for="English">English</label>
</p>
<p>选择的值为:{{checkedNames}}</p>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
checked: false,
checkedNames: []
}
})
</script>
5-3 单选框(radio)相关的操作
<div id="root">
<p>单个复选框:</p>
<p>
<input type="radio" id="man" value="man" v-model="picked">
<label for="man">man</label>
<input type="radio" id="female" value="female" v-model="picked">
<label for="female">female</label>
</p>
<p>
选中的值:{{picked}}
</p>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
picked: 'man',
}
})
</script>
5-4 选择框(select)相关的操作
<div id="root">
<p>选择框:</p>
<select name="age" id="age" v-model="ages">
<option value="0-12">儿童</option>
<option value="12-18">少年</option>
<option value="18-30">青年</option>
<option value="30-40">中年</option>
<option value="40-50">壮年</option>
<option value="50-">老年</option>
</select>
<p>你先则的值是:{{ages}}</p>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
ages: '0-12',
}
})
</script>
5-5 表单中一些修饰符的操作(.lazy、.number、.trim)
<div id="root">
<p>.lazy(input事件同步输入看的值,通过lazy转为change事件中同步):</p>
<input type="text" v-model.lazy="text">
<p>你输入的文本内容是:{{text}}</p>
<p>.number(输入文本内容为数字):</p>
<input type="number" v-model.number="number">
<p>输入的数字是:{{number}}</p>
<p>.trim(去除输入框两端的空格):</p>
<input type="text" v-model.trim="trimText">
<p>显示输入的内容:{{trimText}}</p>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
text: '',
number: '',
trimText: ''
}
})
</script>
第 6 章 动画
6-1 Vue 中的 css 动画原理
我们给 transition name 属性定义的是 fade 所以是下面名称:
fade-enter
fade-enter-to
fade-enter-active
fade-leave
fade-leave-to
fade-leave-active
如果我们没有给 transition 定义 name 属性,用默认的那么就是:
v-enter
v-enter-to
v-enter-active
v-leave
v-leave-to
v-leave-active
刚开始存在 fade-enter 和 fade-enter-active
紧接第二帧的时候,fade-enter 消失、fade-enter-to 出现
到最后的时候 fade-enter-to 消失、fade-enter-active 消失
刚开始存在 fade-leave 和 fade-leave-active
紧接第二帧的时候,fade-leave 消失、fade-leave-to 出现
到最后的时候 fade-leave-to 消失、fade-leave-active 消失
//css动画效果(css过度动画效果)
<style>
.fade-enter {
opacity: 0;
}
.fade-enter-active {
transition: opacity 1s;
}
.fade-leave-to {
opacity: 0;
}
.fade-leave-active{
transition: opacity 1s;
}
</style>
<div id="root">
<transition name="fade">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">toggle</button>
</div>
<script>
var app = new Vue({
el: '#root',
data: {
show: true
},
methods: {
handleClick: function () {
this.show = !this.show;
}
}
})
</script>
ps:
显示操作
刚开始 fade-enter opacity 为 0 第二帧 fade-enter 消失 opacity 变为 1 这个过程一直在 fade-enter-active 监听 1 秒时间
隐藏操作
刚开始 fade-leave opacity 默认是 1 第二帧 fade-leave 消失 fade-leave-to 出现 opacity 变为 0 这个过程一直在 fade-leave-active 监听 1 秒时间后消失
第 7 章 路由
7-1.什么是路由
路由:就是我们通过不同的 URL 访问不同的内容。
7-2.Vue 路由的安装
npm install vue-router
7-3.Vue 路由的简单案例
7-4.router-link 的相关配置
1)、
to
表示路由链接
当被点击后,内部会立即把 to 的值传到 router.push,所以这个值可以是一个字符串
或者是描述目标位置的对象
<!-- 字符串 --> <router-link to="home">Home</router-link> <!-- 渲染结果 --> <a href="home">Home</a> <!-- 使用 v-bind 的 JS 表达式 --> <router-link v-bind:to="'home'">Home</router-link> <!-- 不写 v-bind 也可以,就像绑定别的属性一样 --> <router-link :to="'home'">Home</router-link> <!-- 同上 --> <router-link :to="{ path: 'home' }">Home</router-link> <!-- 命名的路由 --> <router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link> <!-- 带查询参数,下面的结果为 /register?plan=private --> <router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>
2)、
replace
设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),导航后不会留下 history 记录。<router-link :to="{ path: '/home'}" replace></router-link>
3)、
tag
有时候想要 渲染成某种标签,例如
4)、active-class
设置 链接激活时使用的 CSS 类名。可以通过以下代码来替代。
<style>
._active{
background-color : red;
}
</style>
<p>
<router-link v-bind:to = "{ path: '/route1'}" active-class = "_active">Router Link 1</router-link>
<router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>
注意这里 class 使用 active_class="_active"。
5)、exact-active-class
配置当链接被精确匹配的时候应该激活的 class。可以通过以下代码来替代。
<p>
<router-link v-bind:to = "{ path: '/route1'}" exact-active-class = "_active">Router Link 1</router-link>
<router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>
6)、event
声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。
<router-link v-bind:to = "{ path: '/route1'}" event = "mouseover">Router Link 1</router-link>
以上代码设置了 event 为 mouseover ,及在鼠标移动到 Router Link 1 上时导航的 HTML 内容会发生改变。
7)、exact-active-class 和 active-class 的区别
exact-active-class:路由严格匹配
active-class:路由模糊匹配
如果你访问的是:
/article
或者 /article/1
<router-link to="/article" active-class="router-active"></router-link>
都会被渲染
<a href="#/article" class="router-active" rel="nofollow"></a>
而
<router-link to="/article" exact-active-class="router-active"></router-link>
只有访问 /article/1
才会被渲染
<a href="#/article" class="router-active" rel="nofollow"></a>
如果是 /article
,class 不会被渲染出来
<a href="#/article" rel="nofollow"></a>
第 8 章 Vue 项目预热
8-1.NodeJS 安装
//验证node和npm 是否安装 以及安装的版本
node -v
npm -v
根据自己电脑是什么系统去安装:NodeJS 安装
8-2.vue 脚手架安装
//电脑全局安装
npm install --global vue-lci
//a. 实例出一个项目
vue init webpack vue-travel //vue-travel 名称自己定义
//b. 如果你本地有一个git项目了,你想把这个vue脚手架放入到这个项目中
vue init webpack git-project //git-project 本地git项目名称
8-3.运行脚手架项目
cd vue-travel //or cd git-project
npm run dev //or npm start
浏览器访问 localhost:8080 即可
8-4.如何更改端口号
项目 config 文件件,我们打开其目录下的 index.js,就是端口号的最终设置的地方:
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8081, //在这个地方进行端口号的更改
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
.....
}
8-5.如何通过 ip 来访问我们的网站呢
http://localhost:8080
http://127.0.0.1:8080
http://自己电脑ip:8080 //如果手机和电脑在同一个网段,手机可以联调项目,查看手机效果
第一种方法修改:package.json 文件
在 dev 命令里面添加 --host 0.0.0.0
"scripts": {
"dev": "webpack-dev-server --host 0.0.0.0 --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build.js"
},
第二种方法修改:config/index.js 文件
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
host: '0.0.0.0', // 修改这个地方 将localhost 改为 0.0.0.0
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
......
}
重启 npm run dev 或者 npm start
区别:
修改 package.json 之后的结果(和之前没有什么区别):
Your application is running here: http://localhost:8080
修改 config/index.js 文件 之后的运行结果:
Your application is running here: http://0.0.0.0:8080
所以我推荐 方法一(纯粹个人意见)
8-6.Vue 项目初始化的准备工作
因为是手机端的 vue 项目,所以项目 index.html 我们要设置一下
1.禁止缩放
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
2.reset.css
@charset "utf-8";html{background-color:#fff;color:#000;font-size:12px}
body,ul,ol,dl,dd,h1,h2,h3,h4,h5,h6,figure,form,fieldset,legend,input,textarea,button,p,blockquote,th,td,pre,xmp{margin:0;padding:0}
body,input,textarea,button,select,pre,xmp,tt,code,kbd,samp{line-height:1.5;font-family:tahoma,arial,"Hiragino Sans GB",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,small,big,input,textarea,button,select{font-size:100%}
h1,h2,h3,h4,h5,h6{font-family:tahoma,arial,"Hiragino Sans GB","微软雅黑",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,b,strong{font-weight:normal}
address,cite,dfn,em,i,optgroup,var{font-style:normal}
table{border-collapse:collapse;border-spacing:0;text-align:left}
caption,th{text-align:inherit}
ul,ol,menu{list-style:none}
fieldset,img{border:0}
img,object,input,textarea,button,select{vertical-align:middle}
article,aside,footer,header,section,nav,figure,figcaption,hgroup,details,menu{display:block}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1}
blockquote:before,blockquote:after,q:before,q:after{content:"\0020"}
textarea{overflow:auto;resize:vertical}
input,textarea,button,select,a{outline:0 none;border: none;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}
mark{background-color:transparent}
a,ins,s,u,del{text-decoration:none}
sup,sub{vertical-align:baseline}
html {overflow-x: hidden;height: 100%;font-size: 50px;-webkit-tap-highlight-color: transparent;}
body {font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;color: #333;font-size: .28em;line-height: 1;-webkit-text-size-adjust: none;}
hr {height: .02rem;margin: .1rem 0;border: medium none;border-top: .02rem solid #cacaca;}
a {color: #25a4bb;text-decoration: none;}
在我们项目 assets 文件下面
创建 styles 文件
在 styles 文件下面添加 reset.css
最后在项目的 main.js 引入 reset.css
import './assets/styles/reset.css'
3.border.css
解决 1px 边框问题
@charset "utf-8";
.border,
.border-top,
.border-right,
.border-bottom,
.border-left,
.border-topbottom,
.border-rightleft,
.border-topleft,
.border-rightbottom,
.border-topright,
.border-bottomleft {
position: relative;
}
.border::before,
.border-top::before,
.border-right::before,
.border-bottom::before,
.border-left::before,
.border-topbottom::before,
.border-topbottom::after,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::before,
.border-topleft::after,
.border-rightbottom::before,
.border-rightbottom::after,
.border-topright::before,
.border-topright::after,
.border-bottomleft::before,
.border-bottomleft::after {
content: "\0020";
overflow: hidden;
position: absolute;
}
/* border
* 因,边框是由伪元素区域遮盖在父级
* 故,子级若有交互,需要对子级设置
* 定位 及 z轴
*/
.border::before {
box-sizing: border-box;
top: 0;
left: 0;
height: 100%;
width: 100%;
border: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
left: 0;
width: 100%;
height: 1px;
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
top: 0;
width: 1px;
height: 100%;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
border-top: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-right::before,
.border-rightbottom::before,
.border-rightleft::before,
.border-topright::after {
border-right: 1px solid #eaeaea;
transform-origin: 100% 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::before {
border-bottom: 1px solid #eaeaea;
transform-origin: 0 100%;
}
.border-left::before,
.border-topleft::after,
.border-rightleft::after,
.border-bottomleft::after {
border-left: 1px solid #eaeaea;
transform-origin: 0 0;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
top: 0;
}
.border-right::before,
.border-rightleft::after,
.border-rightbottom::before,
.border-topright::after {
right: 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::after {
bottom: 0;
}
.border-left::before,
.border-rightleft::before,
.border-topleft::after,
.border-bottomleft::before {
left: 0;
}
@media (max--moz-device-pixel-ratio: 1.49), (-webkit-max-device-pixel-ratio: 1.49), (max-device-pixel-ratio: 1.49), (max-resolution: 143dpi), (max-resolution: 1.49dppx) {
/* 默认值,无需重置 */
}
@media (min--moz-device-pixel-ratio: 1.5) and (max--moz-device-pixel-ratio: 2.49), (-webkit-min-device-pixel-ratio: 1.5) and (-webkit-max-device-pixel-ratio: 2.49), (min-device-pixel-ratio: 1.5) and (max-device-pixel-ratio: 2.49), (min-resolution: 144dpi) and (max-resolution: 239dpi), (min-resolution: 1.5dppx) and (max-resolution: 2.49dppx) {
.border::before {
width: 200%;
height: 200%;
transform: scale(.5);
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
transform: scaleY(.5);
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
transform: scaleX(.5);
}
}
@media (min--moz-device-pixel-ratio: 2.5), (-webkit-min-device-pixel-ratio: 2.5), (min-device-pixel-ratio: 2.5), (min-resolution: 240dpi), (min-resolution: 2.5dppx) {
.border::before {
width: 300%;
height: 300%;
transform: scale(.33333);
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
transform: scaleY(.33333);
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
transform: scaleX(.33333);
}
}
在 main.js 文件下面
import './assets/styles/border.css'
4.fastclick 解决 300 毫秒点击延迟问题
在 main.js 文件下面
import fastClick from 'fastclick'
fastClick.attach(document.body)
如果想预览项目整体的效果和配置项 git 地址:
https://github.com/fx35792/vue-travel
5.安装 stylus、stylus-loader 第三方依赖
npm install stylus -S
npm install stylus-loader -S
那么在 vue 组件中如何使用呢?
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于