ES6语法
**var:**没有作用域的概念(尽量不要用)
**let:**有作用域的概念,声明变量
**const:**声明常量
1、一旦给const修饰的标识符被赋值之后,不能修改
2、在使用const定义标识符,必须进行赋值
3、常量的含义是指向的对象不能修改,但是可以修改对象里的内容
ES6对象增强写法
<script>
const name = 'aaa';
const age = 18;
const height = 183;
const obj = {
name,
age,
height,
}
</script>
ES6方法增强写法
<script>
//ES5
getList: function(){
...
}
//ES6
getList(){
...
}
</script>
for循环的三种方式
<script>
//1、原生方式
for(let i = 0; i < this.books.length; i++){
totalPrice += this.books[i].price * this.books[i].count;
}
//2、取下标
for(let i in this.books){
totalPrice += this.books[i].price * this.books[i].count;
}
//3、取对象
for(let item of this.books){
totalPrice += item.price *item.count;
}
</script>
高级函数
filter
<script>
const nums = [10, 20, 111, 123, 145];
let newNums = nums.filter(function(n){
return n < 100;
})
console.log(newNums); //[10, 20]
</script>
相当于遍历数组,之后又一个回调函数,这个回调函数返回的是布尔值,当为true的时候,就会把当前元素加入到新的数组内,false时,函数会过滤掉这次的元素。
map
<script>
const nums = [10, 20, 30];
let newNums = nums.map(function(n){
return n*2;
})
console.log(newNums);//[20, 40, 60]
</script>
道理与filter相同,也是回调函数,之后利用每次的返回值,组成新的数组
reduce
<script>
const nums = [10, 20, 30];
let total = nums.reduce(function(preValue, n){
return preValue + n;
},0)
console.log(total); //60
</script>
主要作用是对数组中所有内容进行汇总
reduce一共需要两个参数,一个是回调方法,另一个参数是对preValue进行初始化的值
以上的例子一共需要执行三次:
1、preValue = 0;n = 10;
2、preValue = 10;n = 20;
3、preValue = 30;n = 30;
preValue = 60;
例子:
需求:找出数组内小于100的元素,进行乘以二的操作,之后得到数组的相加的结果
<script>
const nums = [10, 20, 111, 222, 444, 40, 50];
let total = nums.filter(function(n){
return n < 100;
}).map(function(){
return n * 2;
}).reduce(function(preValue, n){
return preValue + n;
},0)
//箭头函数
let total = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n);
</script>
el属性:该属性决定了这个Vue对象挂载到哪一个元素上
data属性:该属性存储一些数据
<body>
<div id="app">
<h1>{{message}}</h1>
<h2>{{name}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',//用于挂载要管理的元素
data: { //定义数据
message: 'Hello, World!',
name: '韩帅'
}
})
</script>
</body>
列表展示
v-for:循环movies,进行遍历展示
<body>
<div id="app">
<li v-for="item in movies">{{item}}</li>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
movies: ['王一','王二','王三']
}
})
</script>
</body>
计数器
@click:语法糖
v-on:click=“counter–”:直接对属性进行操作
v-on:click=“add”:绑定点击事件,在Vue对象内定义methods属性
<body>
<div id="app">
<h2>当前计数:{{counter}}</h2>
<!-- <button v-on:click="counter--">-</button> -->
<!-- <button v-on:click="counter++">+</button> -->
<button v-on:click="add">+</button>
<button @click="sub">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
add: function(){
console.log("add被执行!");
this.counter++;
},
sub: function(){
console.log("sub被执行!");
this.counter--;
},
}
})
</script>
</body>
创建Vue实例传入的options
el:
- 类型: String | HTMLElement
- 作用: 决定之后Vue实力会管理哪一个DOM
data:
- 类型: Object | Function(组件当中data必须是一个函数)
- 作用: Vue实力对应的数据对象
methods:
- 类型: {[key:string]:Function}
- 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用
函数:直接定义的叫做函数
方法:定义在类里面的叫做方法
基础语法
1、插值操作
Mustache语法(也就是双大括号)
可以做一些基本的表达式操作
<body>
<div id="app">
<h1>{{message}}</h1><!-- Hello, World! -->
<h1>{{message}},可以加文本</h1><!-- Hello, World!,可以加文本 -->
<h1>{{lastName + firstName}}</h1><!-- 韩帅 -->
<h1>{{lastName +' '+ firstName}}</h1><!-- 韩 帅 -->
<h1>{{lastName}} {{firstName}}</h1><!-- 韩 帅 -->
<h1>{{counter*2}}</h1><!-- 200 -->
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
firstName: '帅',
lastName: '韩',
counter: 100
}
})
</script>
</body>
**v-once:**vue为响应式编程,当值发生改变时页面会自动进行改变,使用v-once,就是不论值怎么改变,页面显示不变
<body>
<div id="app">
<h1>{{message}}</h1><!-- 会随着message的改变而改变 -->
<h1 v-once>{{message}}</h1><!-- 不会随着message的改变而改变 -->
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!'
}
})
</script>
</body>
**v-html:**渲染url地址
<body>
<div id="app">
<h1>{{url}}</h1><!-- <a href="http://www.baidu.com">百度一下</a> -->
<h1 v-html="url">{{url}}</h1><!-- 百度一下 -->
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
url: '<a href="http://www.baidu.com">百度一下</a>'
}
})
</script>
</body>
**v-text:**与双大括号作用相同,但是不灵活,不能做拼接,使用较少
<body>
<div id="app">
<h1>{{message}},cc</h1><!-- Hello, World!,cc -->
<h1 v-text="message">,cc</h1><!-- Hello, World! --><!-- 会直接覆盖掉原来的,cc -->
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
}
})
</script>
</body>
**v-pre:**相当于转义字符,就是让双大括号不进行解析
<body>
<div id="app">
<h1>{{message}}</h1><!-- Hello, World! -->
<h1 v-pre>{{message}}</h1><!-- {{message}} -->
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
}
})
</script>
</body>
**v-cloak:**页面解析的时候先解析div内的内容,这个时候代码卡住了,页面显示的就是message,vue的渲染并没有生效,加上v-cloak这个属性,并给他个none的样式,这样卡住的话就不会显示了,当vue去解析的时候,会自动去掉这个v-cloak属性。
<head>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<h1>{{message}}</h1><!-- Hello, World! -->
<h1 v-cloak>{{message}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
setTimeout(function(){
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
}
})
}, 1000)
</script>
</body>
2、动态绑定
1、基本使用
绑定主要是绑定属性
比如动态绑定a元素的href属性
比如动态绑定img元素的src属性
属性内不能直接用mustache语法
v-bind:src 语法糖写法 :src
<body>
<div id="app">
<!-- 错误的写法,这里不能用mustache语法 -->
<!-- <img src="{{url}}"></img> -->
<img v-bind:src="imgurl">
<a v-bind:href="ahref">百度一下</a>
<!-- 语法糖的写法 -->
<img :src="imgurl">
<a :href="ahref">百度一下</a>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
imgurl: 'https://image.baidu.com/search/detail',
ahref: 'http://www.baidu.com'
}
})
</script>
</body>
2、动态绑定class
1、对象语法
<body>
<div id="app">
<!-- 直接通过style进行绑定class -->
<h1 class="active">{{message}}</h1>
<!-- 通过Vue对象去找到style进行绑定class -->
<h1 :class="active">{{message}}</h1>
<!-- 传入对象key:value形式,active为style,isActive由Vue对象控制,传入true或false -->
<!-- true则样式生效,false不生效 -->
<h1 :class="{active: isActive,line: isLine}">{{message}}</h1>
<!-- 如果对象属性太多,可以在Vue对象内定义成方法,之后直接调用方法就可以 -->
<h1 :class="getClasses()">{{message}}</h1>
<button v-on:click="btnClick">按钮</button>
<!-- 还可以再加class属性,后边都为true的话,三个class都会生效 -->
<h1 class="title" :class="{active: isActive,line: isLine}">{{message}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
active: 'active',
isActive: false,
isLine: true,
},
methods: {
btnClick:function(){
this.isActive = !this.isActive
this.isLine = !this.isLine
},
getClasses: function(){
//注意要加this this.isActive this.isLine
return {active: this.isActive,line: this.isLine}
}
}
})
</script>
</body>
对象语法:
1、直接通过{}绑定一个类(isActive由Vue对象控制true或false)
<h1 :class="{active: isActive}">{{message}}</h1>
2、也可以通过判断,传入多个值
<h1 :class="{active: isActive,line: isLine}">{{message}}</h1>
3、和普通类同时存在,并不冲突。注:如果isActive、isLine都为true,那么会有三个class title、active、line
<h1 class="title" :class="{active: isActive,line: isLine}">{{message}}</h1>
4、如果过于复杂,可以放在一个method或者computed中
<h1 class="title" :class="getClasses()">{{message}}</h1>
2、数组语法
与对象语法写法类似,但是不能动态控制
<body>
<div id="app">
<h1 :class="[active, line]">{{message}}</h1>
<h1 :class="getClasses()">{{message}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
active: 'aaa',
line: 'bbb'
},
methods: {
getClasses: function(){
return [this.active, this.line]
}
}
})
</script>
</body>
3、动态绑定style
1、对象语法
与绑定class类似
<body>
<div id="app">
<h1 :style="{fontSize: fontSize, color: color}">{{message}}</h1>
<h1 :style="getStyle()">{{message}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
fontSize: '100px',
color: 'green',
},
methods: {
getStyle: function(){
return {fontSize: this.fontSize, color: this.color}
}
}
})
</script>
</body>
2、数组语法
<body>
<div id="app">
<h1 :style="[style1, style2]">{{message}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
style1: {fontSize: '100px'},
style2: {color: 'green'},
}
})
</script>
</body>
3、计算属性
1、基本使用
**computed:**为定义计算属性的关键字,在Vue对象内定义,对属性值进行一些操作
<body>
<div id="app">
<h1>{{lastName + ' ' + firstName}}</h1>
<h1>{{lastName}} {{firstName}}</h1>
<h1>{{getFullName()}}</h1>
<h1>{{fullName}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'shuai',
lastName: 'han',
},
computed:{
fullName: function(){
return this.lastName + ' ' + this.firstName
}
},
methods: {
getFullName: function(){
return this.lastName + ' ' + this.firstName
}
}
})
</script>
</body>
2、复杂操作
在computed内计算好之后,界面直接取值
<body>
<div id="app">
<h1>总价格:{{totalPrice}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
books: [
{id: 100, name: '王一', price: 185},
{id: 101, name: '王二', price: 123},
{id: 102, name: '王三', price: 165},
{id: 103, name: '王四', price: 112},
{id: 104, name: '王五', price: 154},
]
},
computed:{
totalPrice: function(){
let result = 0;
for(let i = 0; i < this.books.length; i++){
result += this.books[0].price;
}
return result;
}
}
})
</script>
</body>
3、计算属性的getter setter方法
与java类似,就是get set方法,赋值取值,set方法一般不常用,computed相当于只读属性
<body>
<div id="app">
<h1>{{fullName}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'shuai',
lastName: 'han'
},
//计算属性一般是没有set方法,是一个只读属性
computed:{
fullName: {
get: function(){
return this.lastName + ' ' + this.firstName
}
},
//简写(通用写法)
fullName: function(){
return this.lastName + ' ' + this.firstName
}
}
})
</script>
</body>
4、computed与methods对比
<body>
<div id="app">
<!-- 1、直接拼接:语法过于繁琐 不建议使用 -->
<h1>{{lastName}} {{firstName}}</h1>
<!-- 2、通过定义methods -->
<h1>{{getFullName()}}</h1>
<!-- 3、通过computed -->
<h1>{{fullName}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'shuai',
lastName: 'han'
},
methods: {
getFullName(){
return this.lastName + ' ' + this.firstName
}
},
computed:{
fullName: function(){
return this.lastName + ' ' + this.firstName
}
}
})
</script>
</body>
**computed:**当一个取值取多次的时候,这个方法只会调用一次,他会监听你返回的值是否发生改变,没有改变会直接把缓存内的值返回出去,有改变重新调用
**methods:**每取值一次调用一次方法,效率相对较低
4、事件监听
1、基本使用
v-on:click 语法糖写法 @click
<body>
<div id="app">
<h1>{{counter}}</h1>
<button v-on:click="counter++">+</button>
<button v-on:click="counter--">-</button>
<button v-on:click="add()">+</button>
<button v-on:click="subtract()">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
add(){
this.counter++
},
subtract(){
this.counter--
}
}
})
</script>
</body>
2、参数问题
<body>
<div id="app">
<button @click="btn1Click()">按钮1</button><!-- btnClick -->
<button @click="btn1Click">按钮2</button><!-- btnClick -->
<!-- 在事件定义时,写方法省略了小括号,但是方法本身需要一个参数,
这个时候Vue会默认将浏览器生产的event事件对象作为参数传入到方法 -->
<button @click="btn2Click">按钮3</button><!-- event对象 -->
<button @click="btn2Click(10)">按钮4</button><!-- 10 -->
<!-- 在方法定义是,我们需要event对象,同时又需要其他参数 -->
<!-- 在调用方法时,手动获取浏览器参数的event对象:$event -->
<button @click="btn3Click">按钮5</button><!-- event对象undefind -->
<button @click="btn3Click(123, $event)">按钮5</button><!-- 123event对象 -->
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!'
},
methods: {
btn1Click(){
console.log("btnClick");
},
btn2Click(num){
console.log("-------" + num);
},
btn3Click(num, event){
console.log("+++++++" + num + event);
},
}
})
</script>
</body>
3、修饰符
<body>
<div id="app">
<!-- .stop修饰符的使用,阻止冒泡 -->
<div @click="divClick()">
aaa
<button @click.stop="btnClick()">按钮1</button>
</div>
<!-- .prevent修饰符的使用,阻止默认事件,注:以下type="submit" 点击提交之后会自动提交 -->
<form action="baidu">
<input type="submit" value="提交" @click.prevent="submitClick()">
</form>
<!-- 监听键盘键的点击 keyup:监听键盘抬起的事件与click性质类似-->
<!-- .enter敲回车会触发的事件 -->
<input type="text" @keyup.enter="keyup()">
<!-- 只能触发一次 once -->
<button @click.once="once()">按钮1</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!'
},
methods: {
btnClick(){
console.log("btnClick");
},
divClick(){
console.log("divClick");
},
submitClick(){
console.log("submitClick");
},
keyup(){
console.log("keyup");
},
once(){
console.log("once");
}
}
})
</script>
</body>
1、.stop修饰符的使用,阻止冒泡
2、.prevent修饰符的使用,阻止默认事件,注:以下type=“submit” 点击提交之后会自动提交
3、监听键盘键的点击 keyup:监听键盘抬起的事件与click性质类似
4、只能触发一次 once
5、条件判断
<body>
<div id="app">
<h1 v-if="score>=90">优秀</h1>
<h1 v-else-if="score>=80">良好</h1>
<h1 v-else-if="score>=60">及格</h1>
<h1 v-else>不及格</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
score: 99,
},
})
</script>
</body>
逻辑复杂的不建议在标签内判断,用计算属性来做
1、用户登录切换案例
<body>
<div id="app">
<span v-if="isUser">
<label for="username">用户账号</label>
<!-- key的作用就是给input一个唯一标识 -->
<input type="text" id="username" placeholder="用户账号" key="username">
</span>
<span v-else>
<label for="email">用户邮箱</label>
<input type="text" id="email" placeholder="用户邮箱" key="email">
</span>
<button @click="isUser = !isUser">切换类型</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isUser: true
}
})
</script>
</body>
2、v-show
切换频率高的时候使用v-show 反之使用v-if
<body>
<div id="app">
<!-- 当条件为false时,包含v-if的元素,根本就不会存在dom中 -->
<!-- 每次在true和false之间切换的时候,都是删除或者新建 -->
<h1 v-if="isShow" id="aaa">{{message}}</h1>
<!-- 当条件为false时,v-show只是给元素加了一个行内样式,display:none -->
<h1 v-show="isShow" id="bbb">{{message}}</h1>
<!-- 切换频率高的时候使用v-show 反之使用v-if -->
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello, World!',
isShow: true,
}
})
</script>
</body>
6、循环遍历
1、v-for遍历数组
<body>
<div id="app">
<!-- 在遍历过程中没有使用索引值(下标值) -->
<ul>
<li v-for="item in names">{{item}}</li>
</ul>
<!-- 在遍历过程中使用索引值(下标值) -->
<ul>
<li v-for="(item, index) in names">{{index+1}}.{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
names: ['wangyi', 'wanger','wangsan']
}
})
</script>
</body>
2、v-for遍历对象
<body>
<div id="app">
<!-- 在遍历对象的过程中,如果只取一个值,那么获取到的是value -->
<ul>
<li v-for="item in info">{{item}}</li>
</ul>
<!-- 获取key和value 格式:(value, key) -->
<ul>
<li v-for="(value, key) in info">{{key}}:{{value}}</li>
</ul>
<!-- 获取key和value和index 格式:(value, key, index) -->
<ul>
<li v-for="(value, key, index) in info">{{key}}:{{value}} {{index}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
info: {
name: '王一',
age: 24,
height: 1.88
}
}
})
</script>
</body>
v-for循环,给对应元素加一个key属性
<li v-for="item in info" :key="item">{{item}}</li>
往中间插入元素的时候效率会更高
3、数组中哪些方法不是响应式的
<body>
<div id="app">
<ul>
<li v-for="item in name">{{item}}</li>
</ul>
<button @click="btnClick()">按钮</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: ['a', 'b', 'c']
},
methods: {
btnClick(){
//1、push方法:在数组最后添加元素
this.name.push('aaa');
this.name.push('aaa','bbb','ccc');
//2、unshift方法:在数组最前面添加元素
this.name.unshift('aaa');
this.name.unshift('aaa','bbb','ccc');
//3、pop方法:删除数组中最后一个元素
this.name.pop();
//4、shift方法:删除数组中第一个元素
this.name.shift();
//5、splice方法:删除元素、插入元素、替换元素
// 这个方法一共有三个参数
//param1:从哪个下标开始
//param2:删除几个或替换几个
//param3:插入的新元素的值
//删除元素
//不传第二个默认删除1后边的全部元素
this.name.splice(1);
//删除1后边的三个元素
this.name.splice(1,3);
//替换元素
//替换1后边的两个元素,替换为v b
this.name.splice(1,2,'v','b');
//插入元素
//在1后边加三个元素x y z
this.name.splice(1,0,'x','y','z');
//6、sort方法:排序
this.name.sort();
//7、reverse方法:反转
this.name.reverse();
//以上都是响应式的
//8、注意通过索引值修改数组中的元素不是响应式的
this.name[0] = 'dd';
}
}
})
</script>
</body>
7、v-model
1、v-model基本使用
<body>
<div id="app">
<input type="text" v-model="message">
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
}
})
</script>
</body>
v-model:使用的是双向绑定,就是不论上面或者下面的值改变,都会自动改变对方
2、v-model的原理
<body>
<div id="app">
<input type="text" v-model="message">
<input type="text" :value="message" @input="valueChange">
<input type="text" :value="message" @input="message = $event.target.value">
{{message}}
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
},
methods: {
valueChange(){
this.message = event.target.value;
}
}
})
</script>
</body>
v-model的原理就是:value与@input的组合使用
:value:通过监听vue对象内的额message的值,动态回显在input框上
@input:监控input的值,为Vue对象的message赋值
3、v-model结合radio使用
<body>
<div id="app">
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
</label>
<h1>{{sex}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
sex:'男',
},
})
</script>
</body>
4、v-model结合checkedbox使用
<body>
<div id="app">
<!-- 单选 -->
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
<h1>您选择的是:{{isAgree}}</h1>
<button :disabled="!isAgree">下一步</button>
<br>
<!-- 多选 -->
<input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="足球" v-model="hobbies">足球
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
<input type="checkbox" value="排球" v-model="hobbies">排球
<input type="checkbox" value="橄榄球" v-model="hobbies">橄榄球
<h1>您的爱好是:{{hobbies}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
isAgree: false,
hobbies: []
},
})
</script>
</body>
5、v-model结合select使用
<body>
<div id="app">
<select name="abc" id="" v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="橙子">橙子</option>
<option value="鸭梨">鸭梨</option>
</select>
<h1>您选择的是:{{fruit}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
fruit: ''
},
})
</script>
</body>
6、v-model值绑定(动态赋值)
<body>
<div id="app">
<label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</label>
<h1>您选择的是:{{hobbies}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
originHobbies: ['篮球', '足球', '羽毛球', '乒乓球', '橄榄球',],
hobbies: []
},
})
</script>
</body>
7、v-model修饰符使用
<body>
<div id="app">
<!-- lazy:失去焦点或者敲回车之后在赋值 -->
<input type="text" v-model.lazy="message">
<h1>您选择的是:{{message}}</h1>
<!-- number:v-model默认全是字符串类型,加number可改成数字类型-->
<input type="text" v-model.number="age">
<h1>您选择的是:{{age}}</h1>
<!-- trim:去左右两边的空格 -->
<input type="text" v-model.trim="name">
<h1>您选择的是:{{name}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
age: 0,
name: '王一'
},
})
</script>
</body>
**lazy:**失去焦点或者敲回车之后在赋值
**number:**v-model默认全是字符串类型,加number可改成数字类型
**trim:**去左右两边的空格
8、组件化开发
1、注册组件的基本步骤
-
创建组件构造器:调用Vue.extend()方法,创建组件构造器
-
注册组件:调用Vue.component()方法,注册组件
-
使用组件:在Vue实例的作用范围使用组件
<body>
<div id="app">
<!-- 使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
//1、创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h1>我是标题</h1>
<p>我是内容</p>
</div>`
})
//2、注册组件
Vue.component('my-cpn', cpnC);
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
},
})
</script>
</body>
2、全局组件和局部组件
<body>
<div id="app">
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
//1、创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h1>我是标题</h1>
<p>我是内容</p>
</div>`
})
//2、注册组件(全局组件:可以在多个Vue实例中使用)
Vue.component('my-cpn', cpnC);
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
age: 0,
name: '王一'
},
components: {
//局部组件:只可以在当前Vue实例中使用 cpn 使用组件时的标签名
cpn: cpnC
}
})
</script>
</body>
3、父组件与子组件
<body>
<div id="app">
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
//子组件
const cpnC1 = Vue.extend({
template: `
<div>
<h1>我是标题1</h1>
<p>我是内容1</p>
</div>`
})
//父组件
const cpnC2 = Vue.extend({
template: `
<div>
<h1>我是标题2</h1>
<cpn1></cpn1>
<p>我是内容2</p>
</div>`,
components: {
//注册子组件
cpn1: cpnC1
}
})
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
},
components: {
cpn2: cpnC2
}
})
</script>
</body>
4、组件注册语法糖写法
<body>
<div id="app">
<cpn1></cpn1>
</div>
<script src="../js/vue.js"></script>
<script>
//注册全局组件语法糖
Vue.component('cpn1',{
template: `
<div>
<h1>我是标题1</h1>
<p>我是内容1</p>
</div>`
})
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
},
components: {
//注册局部组件语法糖
'cpn2' :{
template: `
<div>
<h1>我是标题1</h1>
<p>我是内容1</p>
</div>`
}
}
})
</script>
</body>
5、组件模板分离写法
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script type="text/x-template" id="cpn2">
<div>
<h1>我是标题666</h1>
<p>我是内容666</p>
</div>
</script>
<template id="cpn">
<div>
<h1>我是标题123</h1>
<p>我是内容123</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn1',{
template: '#cpn'
})
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
},
components: {
//注册局部组件语法糖
'cpn2' :{
template: '#cpn2'
}
}
})
</script>
</body>
通过template标签或者script标签将代码抽取出来,之后直接映射Id即可
6、组件中数据存放问题
组件内不可以访问Vue实例内的数据,但是组件对象也有一个data属性(也可以有methods等属性),只是data属性必须是一个函数,而这个函数返回一个对象,对象内部保存着数据
<body>
<div id="app">
<cpn1></cpn1>
</div>
<template id="cpn">
<div>
<h1>{{title}}</h1>
<p>我是内容123</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn1',{
template: '#cpn',
data(){
return {
title: 'ccc'
}
}
})
const app = new Vue({
el: "#app",
})
</script>
</body>
7、组件中为什么data是函数
以上的例子,是一个计数器功能,当组件调用多次的时候,data每次返回的都是不同的对象,多个组件之间不相互影响,如果不是函数,返回都是一个对象,那么多个组件就都会操作一个数据了。
如果想让多个组件操作同一个数据的方式:如下,创建一个对象,之后data函数返回这个对象,这时如果使用这个组件多次,操作的也都是一个对象
<script>
const obj = {
counter : 0
}
Vue.component('cpn1',{
template: '#cpn',
data(){
return : obj
}
})
</script>
<body>
<div id="app">
<cpn1></cpn1>
<cpn1></cpn1>
</div>
<template id="cpn">
<div>
<h1>当前计数:{{counter}}</h1>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn1',{
template: '#cpn',
data(){
return {
counter: 0
}
},
methods:{
increment(){
this.counter++
},
decrement(){
this.counter--
}
}
})
const app = new Vue({
el: "#app",
})
</script>
</body>
8、组件通信-父组件向子组件传递数据
<body>
<div id="app">
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<h1>{{cmessage}}</h1>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
//props: ['cmovies', 'cmessage'],
props: { //props 接收父组件传过来的数据
cmessage:{
type: String, //类型限制
default: 'abc', //默认值
required: true //是否必传
},
cmovies: { //类型是对象或者数组的时候,默认值必须是一个函数
type: Array,
default(){
return []
}
}
},
}
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
movies: ['海尔兄弟1', '海尔兄弟2', '海尔兄弟3']
},
components: {
cpn :cpn
}
})
</script>
</body>
以上例子,app为父组件,cpn为子组件,子组件想获取父组件的数据,
1、先定义props属性进行接收
<script>
props: { //props 接收父组件传过来的数据
cmessage:{
type: String, //类型限制
default: 'abc', //默认值
required: true //是否必传
},
cmovies: { //类型是对象或者数组的时候,默认值必须是一个函数
type: Array,
default(){
return []
}
}
},
</script>
2、进行传值,:cmovies为子组件中定义的属性名 movies为父组件中定义的属性名
<cpn :cmovies="movies" :cmessage="message"></cpn>
3、之后就直接可以在组件中使用了
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<h1>{{cmessage}}</h1>
</div>
</template>
注意:子组件定义属性名尽量不要驼峰,驼峰:cMovies这样可能接收不到,需要c-movies接收
9、组件通信-子组件向父组件传递数据
<body>
<div id="app">
<cpn @itemclick="cpnclick"></cpn>
</div>
<template id="cpn">
<div>
<button v-for="item in cate" @click="btnclick(item)">{{item.name}}</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
data(){
return {
cate :[
{id:1, name:'王一'},
{id:2, name:'王二'},
{id:3, name:'王三'},
{id:4, name:'王四'},
]
}
},
methods: {
btnclick(item){
this.$emit("itemclick",item)
}
}
}
const app = new Vue({
el: "#app",
data: {
message: 'Hello World!',
},
components: {
cpn :cpn
},
methods: {
cpnclick(item){
console.log(item);
}
}
})
</script>
</body>
逻辑就是子组件通过定义方法,输出数据,之后父组件进行监听,监听到了进行回调
以上的例子总结:
1、子组件定义点击事件
2、在点击时间内创建向父组件发送数据请求 this.$emit(“itemclick”,item)
3、父组件定义v-on进行监听子组件的自定义请求 <cpn @itemclick=“cpnclick”>
4、父组件接收到数据的回调 cpnclick(item){console.log(item);}
10、组件访问-父组件访问子组件
1、通过$children去访问子组件的方法或者属性,但是需要通过下表去取具体的哪一个组件(不常用)
2、通过$refs去访问子组件的方法或者属性(常用)
- 给需要访问的子组件标签上加上ref属性(ref=“aaa”)
- this.$refs.aaa.name去访问相应的子组件属性(.aaa相当于通过键去取值)
- $refs为对象类型,不绑定ref属性的时候默认为空
<body>
<div id="app">
<cpn ref="aaa"></cpn>
<button @Click="btnClick">按钮</button>
</div>
<script src="../js/vue.js"></script>
<template id="cpn">
<div>我是子组件</div>
</template>
<script>
const app = new Vue({
el:'#app',
data:{
message:'Hellod World'
},
methods:{
btnClick(){
// 1、通过$children去与访问子组件方法或属性
console.log(this.$children)
this.$children[0].showMessage()
console.log(this.$children[0].name)
//2、通过$refs访问子组件方法或属性 $refs为对象类型,默认为空的对象
console.log(this.$refs.aaa.name);
}
},
components:{
cpn: {
template:'#cpn',
data(){
return{
name: '我是子组件的属性'
}
},
methods: {
showMessage(){
console.log("showMessage");
}
}
}
}
})
</script>
</body>
11、组件访问-子组件访问父组件
1、访问父组件this.$parent.message
2、访问根组件this.$root.message
<body>
<div id="app">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<template id="cpn">
<ccpn></ccpn>
</template>
<template id="ccpn">
<div>
<div>我是子组件</div>
<button @click="btnClick">按钮</button>
</div>
</template>
<script>
const app = new Vue({
el:'#app',
data:{
message:'Hellod World'
},
components:{
cpn: {
template:'#cpn',
data(){
return{
message:'hello'
}
},
components:{
ccpn:{
template: '#ccpn',
methods: {
btnClick(){
//1、访问父组件(父组件为cpn)
console.log(this.$parent.message)//cpn 内的message hello
//2、访问根组件(根组件为Vue)
console.log(this.$root.message)//Vue 内的message Hellod World
}
},
}
}
}
}
})
</script>
</body>
12、组件插槽的基本使用
组件的插槽也是为了让我们封装的组件更加具有扩展性
让使用者可以决定组件内部的一些内容到底展示什么
抽取共性,保留不同(slot)
1、插槽的基本使用
<slot></slot>
2、插槽的默认值(就是界面不往插槽内传值的时候显示的东西)
<slot><button>插槽的默认值</button></slot>
3、如果有多个值,一起传入到插槽内,都会替换,都会显示
<body>
<div id="app">
<cpn><span>插槽span</span></cpn>
<cpn><li>插槽li</li></cpn>
<cpn><a href="www.baidu.com">插槽a</a></cpn>
<cpn>
<span>插槽span</span>
<li>插槽li</li>
<a href="www.baidu.com">插槽a</a>
</cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<template id="cpn">
<div>
<h1>我是子组件</h1>
<p>子组件</p>
<slot><button>插槽默认值按钮</button></slot>
</div>
</template>
<script>
const app = new Vue({
el:'#app',
data:{
message:'Hellod World'
},
components:{
cpn: {
template:'#cpn',
}
}
})
</script>
</body>
13、具名插槽的使用
当一个组件具有多个插槽时,给插槽定义一个name属性,在使用的时候加上一个slot属性,指定当前的内容替换哪一个插槽
<body>
<div id="app">
<cpn><span slot="center">标题替换中间</span></cpn>
<cpn><button slot="left">按钮替换左边</button></cpn>
</div>
<script src="../js/vue.js"></script>
<template id="cpn">
<div>
<slot name="left"><span>左边</span></slot>
<slot name="center"><span>中间</span></slot>
<slot name="right"><span>右边</span></slot>
</div>
</template>
<script>
const app = new Vue({
el:'#app',
data:{
message:'Hellod World'
},
components:{
cpn: {
template:'#cpn',
}
}
})
</script>
</body>
14、变量的作用域
在Vue的实例内,使用的变量时Vue里面定义的,与是否在组件上使用无关
官方:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译
<body>
<div id="app">
<!-- 使用的是Vue实例内的siShow -->
<cpn v-show="isShow"></cpn>
</div>
<script src="../js/vue.js"></script>
<template id="cpn">
<div>
<h1>我是子组件</h1>
<span>子组件内容</span>
<!-- 使用的是组件内的属性isShow -->
<button v-show="isShow"></button>
</div>
</template>
<script>
const app = new Vue({
el:'#app',
data:{
message:'Hellod World',
isShow: true
},
components:{
cpn: {
template:'#cpn',
isShow: false
}
}
})
</script>
</body>
15、作用域插槽
父组件替换插槽的标签,但是内容由子组件来提供
数据是子组件的,显示方式由父组件来确认
- 在子组件内的slot内定义属性 **:data=“子组件的属性”(**data也是随便起名字的)
- 之后在使用的时候加上template标签,加上**slot-scope=“scope”**属性,scope.data为子组件内的值
<body>
<div id="app">
<cpn></cpn>
<cpn>
<template slot-scope="scope">
<span v-for="item in scope.data">{{item}}</span>
<span>{{scope.data.join(' - ')}}</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<slot :data="pLanguage">
<ul>
<li v-for="item in pLanguage">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
message:'Hellod World',
},
components:{
cpn: {
template:'#cpn',
data(){
return{
pLanguage: ['JavaScript','Java','C','C++','c#']
}
}
}
}
})
</script>
</body>
9、模块化开发
随着前端代码越来越复杂,会出现一系列的问题(导入多个js文件,导致变量重名等等),所以前端引入了模块化的概念
1、ES6的模块化实现
就是把自己写的变量函数封装起来,之后导出,别的模块需要用的话,就需要导入(java的导入类)
10、Webpack
从本质上来讲,webpack是一个现代的JavaScript应用的静态模块打包工具
把浏览器不能识别的语言,进行打包转化,让浏览器可以识别
Webpack依赖node环境
node环境为了可以正常的执行很多代码,必须其中包含各种依赖的包
npm工具(node packages manager)管理node之中的包
具体没记笔记,也没跟这敲(有点复杂,就是打包前端代码,图片、css等一些东西让浏览器识别)
11、箭头函数和this的指向
1、基本使用
<script>
//箭头函数:也是一种函数的定义方式
//1、定义函数的方式:function
const aaa = function(){
}
//2、对象字面量中定义函数
const obj = {
bbb: function(){
},
bbb(){
}
}
//3、ES6中的箭头函数
const ccc = (参数列表) =>{
}
</script>
2、参数和返回值
<script>
//参数问题
//1、放入两个参数
const sum = (num1, num2) => {
return num1 + num2
}
//2、放入一个参数(小括号可以省略)
const power = num => {
}
//函数中
//1、函数中有多行代码时
const test = () =>{
console.log("aaa")
console.log("ccc")
}
//2、函数中只有一行代码时(可以省略大括号)
const mul = (num1, num2) => num1 * num2
</script>
3、箭头函数的this
箭头函数的this引用的就是最近作用域的this(一层一层向外查找this,直到找到有this的定义)
<script>
//什么时候使用箭头函数(当把一个函数作为另一个函数的参数的时候)
setTimeout(function(){
console.log(this) //打印Window
}, 1000)
setTimeout(()=>{
console.log(this) //打印Window
}, 1000)
//结论:箭头函数的this引用的就是最近作用域的this(一层一层向外查找this,直到找到有this的定义)
const obj = {
aaa(){
setTimeout(function(){
console.log(this) //打印Window
})
setTimeout(() => {
console.log(this) //打印obj对象
})
}
}
</script>
12、router(路由)
1、后端路由
后端处理URL和页面之间的映射关系
- 一个界面有自己对应的网址,也就是URL
- URL会发送到服务器,服务器通过正则对该URL进行匹配,并且最后交给一个Controller进行处理
- Controller进行各种处理,最终生成HTML或者数据,返回给前端
- 完成一次IO操作
这种情况下渲染好的界面,不需要单独加载任何的js和css,可以直接交给浏览器展示,有利于SEO优化
后端渲染
后端通过解析url,之后查询数据渲染好界面(java+css+html )之后传给浏览器
2、前后端分离
后端只负责提供数据,不负责任何前端的内容
- 浏览器根据用户输入的rul地址,去静态资源服务器请求响应Html+css+js
- 静态资源服务器内会有多套html+css+js的组合,每一个url对应一套
- js代码执行,可能会调用api接口,去后端请求数据(ajax)
- 之后再浏览器渲染界面
- 小程序或者app都适用这种开发模式,后端不需改变,api接口都适用
前端渲染:
浏览器中现实的网页中大部分内容都是前端写的js代码,在浏览器中执行,最终渲染出来的网页
3、SPA页面
SPA:单页富应用(整个网页只有一个html页面)
- 当用户输入url地址的时候,浏览器会拿到仅有的index.html+css+js
- 静态资源服务器只有一份html+css+js的组合
- 之后用户点击按钮或者其他什么切换界面,前端路由会生成相应url,与请求到的所有资源进行对应
- 全部资源内分组件,每一个生成的url对应某一个组件,之后进行相应渲染
前端路由:
维护一套路由关系(url ——> 组件)
生成对应url,之后去请求到的所有资源内拿出与之对应的组件,进行渲染
其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由,也就是由前端来维护一套路由规则
前端路由的核心:
改变URL,但是页面不进行整体的刷新(因为不需要在向服务器请求资源了,资源已经在第一次请求的时候都拿到了)
4、Vue-router的使用
<script>
//以下代码为router文件夹内的index.js里的内容
//配置路由相关的信息
import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from '../components/Home'
import About from '../components/About'
//1、通过Vue.use(插件),安装插件
Vue.use(VueRouter)
//2、创建VueRouter对象
const routers = [
{
path: '',
//重定向 第一次进入界面显示的内容
redirect:'/home'
},
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
}
]
const router = new VueRouter({
//配置路由和组件之间的应用关系
routers,
//就是url里面不带#的,默认时hash的
mode: 'history'
})
//3、将router对象传入到Vue实例
export default router
</script>
<script>
//以下代码为main.js的内容(路由挂载到Vue实例上)
import Vue from 'vue'
import App from './App'
import router from './router'
new Vue({
el: '#app',
router,
render: h => h(App)
})
</script>
<html>
<!-- 以下为App.vue -->
<template>
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
</html>
<html>
<!-- 以下为Home.vue -->
<template>
<div>
<h1>我是首页</h1>
<p>我是首页内容</p>
</div>
</template>
<script>
export default {
name: "Home"
}
</script>
</html>
router-link:
to:用于跳转指定路径
tag:可以指定之后渲染成什么组件 例:
replace:replace不会留下history记录,所以指定replace的情况下,后退键返回不能返回到上一个界面
1、动态路由:
动态拼接路由
2、路由懒加载:
因为SPA页面按道理来说只请求一次静态资源服务器,但是随着项目越来越大,js文件也就变得越来越大,如果一次全都加载过来,就会造成界面的短暂空白等情况
vue build的时候,正常会生成三个文件
- vendor:第三方的东西,插件之类的
- manifest:做底层支撑的
- app:js文件,就是项目的业务逻辑
项目越大,app文件也就越大,加载变慢,所以需要懒加载
就是把app文件分开,第一次只加载当前需要的组件,之后点击其他界面的时候,需要哪个再去加载哪个
写法:
const routers = [
{
path: '/home'
component: () => import('../components/Home')
},
]
3、路由的嵌套:
再home的界面上加上以下内容
<router-link to="/message">信息</router-link>
<router-view></router-view>
const routers = [
{
path: '/home'
component: () => import('../components/Home')
children: [
{
path: '/message'
component: () => import('../components/message')
},
]
},
]
4、路由传递参数:
传递参数主要有两种类型:params和query
params的类型:
配置路由格式:/router/:id
传递的方式:在path后面跟上对应的值
传递后形成的路径:/router/123,/router/abc
query的类型:
配置路由格式:/router,也就是普通配置
传递的方式:对象中使用query的key作为传递方式
传递后形成的路径:/router?id=123,/router?id=abc
5、路由导航守卫:
就是界面跳转的时候能做事情(以下是实现,跳转的时候更改界面的标题)
//这个router就是路由对象
//from是从哪跳转 to是跳转到哪里 next()方法必须执行
//to.matched[]是一个数组,因为可能包含路由的嵌套,就写死永远取第一个就行,有业务需求在修改
//前置回调
router.beforeEach((to, from, next) => {
document.title = to.matched[0].meta.title
next()
})
//后置回调(不需要主动调用next())
router.afterEach((to, from) => {
})
以上为全局守卫,还有路由独享守卫,还有组件内守卫
//路由独享守卫
const router = new VueRouter({
routers: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
//...
next()
}
},
]
})
6、keep-alive
就是从这个界面条状到另一个界面,如果不保持keep-alive的话,就会重新创建界面,每次进入与离开,都会相应的执行创建和销毁,但是如果保持了keep-alive的话,那么这个界面就会放入缓存内,就不会每次离开都销毁了,也可以使用相应的回调函数activated(活跃的时候执行)、deactivated(不活跃的时候执行),总结就是不让组件频繁的创建与销毁
<keep-alive>
<router-view></router-view>
</keep-alive>
keep-alive的属性
exclude:保持keep-alive,但是除了哪几个
<keep-alive exclude="user,home">
13、Vuex
Vuex是一个专门为Vue.js应用程序开发的状态管理模式
状态管理:就是多个组件需要共享的变量,全部存储在一个对象内,将这个对象放在顶层vue实例中,其他组件就可以使用了
VueJS为响应式的,动态改变数据
响应式规则:
要想实现响应式,所有数据必须在store中初始化好的
Const store = new Vuex.Store({
state: {
info: {
name: 'wangyi',
age: 21,
heighe: 170
},
},
})
以上info内的属性都会被加入到响应式系统中,而响应式系统用会监听属性的变化,当属性发生变化时,会通知所有界面中用到该属性的地方,让界面发生刷新
如果要通过mutations来修改state内的属性的时候:
mutations: {
updateInfo(){
//这种修改现有属性的是为响应式的,因为他在初始化的时候已经被监听了
state.info.name = 'wanger'
//这种添加在原有基础上添加属性的方式不是响应式的,因为该address属性没有被监听,所以界面不会显示
state.info['address'] = '吉林市'
//这种方式是响应式的,也是添加属性,但是他会把新加入的属性,加到监听里面去,所以界面会显示
Vue.set(state.info, 'address', '吉林市')
//删除与添加同理
delete state.info.age
Vue.delete(state.info, 'age')
}
},
修改数据:
如果想要修改store内的数据
从Vue Components直接修改数据,传回State,是可以修改的,但是官方不建议这样做
<h1>{{$store.state.num++}}</h1>
官方建议,经过Actions到Mutations,之后再传回State
如果有异步操作,可以经过Actions,没有异步操作,直接从Vue Compinents到Mutations也是可以的
Mutations这一步,是有一个Devtools(浏览器内)的工具,帮我们跟踪,哪一个组件修改了属性,方便后期调试等
Vuex基本使用:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
Const store = new Vuex.Store({
modules: {},
mutations: {},
actions: {},
state: {
num: 100
},
getters: {},
})
export default store
//使用
<h1>{{$store.state.num}}</h1>
1、store:
单一状态树,所有的信息都放在一个store内,方便后期维护管理。(永远只有一个store)
2、state:
用来存放信息(变量),其他界面可以使用
3、getters:
类似于Vue的计算属性,就是经过操作之后把数据再拿出去
Const store = new Vuex.Store({
state: {
num: 100
},
getters: {
//state为默认参数
powerCounter(state){
return state.num * state.num
},
//这个getters里面的方法可以有第二个属性,这个第二个属性可以调用其他的方法
test(state,getters){
return getters.powerCounter
},
//调用的时候传来的参数
testparam(state){
return function(param){
return num * param
}
}
}
})
//使用
<h1>{{$store.getters.powerCounter}}</h1>
//传参使用
<h1>{{$store.getters.testparam(param)}}</h1>
**4、mutations: **
只能定义同步方法(修改数据)不能进行异步操作
export default new Vuex.Store({
mutations: {
//state为默认参数
increment(state){
state.num++
},
incrementCount(state, count){
state.num += count
},
addStudent(state, stu){
state.students.push(stu)
}
},
state: {
num: 100,
students: []
},
})
//界面
<button @click="add"></button>
//传参
<button @click="addCount(10)"></button>
//传递多个参数
<button @click="addStudent"></button>
...
methods:{
add(){
//调用mutations方法的时候使用commit之后跟方法名
this.$store.commit('increment')
},
addCount(count){
//普通的提交方式
this.$store.commit('incrementCount', count)
//特殊的提交方式通过这中方方式传过去的就不是一个值了,而是一个对象,相应接收也是需要改变的
this.$store.commit({
type: 'incrementCount',
count: count
})
},
addStudent(){
const stu = {id:114, name:'wangyi', age:21}
this.$store.commit('addStudent', stu)
}
}
5、Action:
定义异步方法(修改数据)
action执行完之后还要回到mutations(详情见上面图片)
Const store = new Vuex.Store({
state: {
num: 100
},
actions: {
//context 上下文 params 参数
updateInfo(context, params){
setTimeout(() => {
//提交到mutations
context.commit('updateInfo')
console.log(params)
}, 1000)
},
updateInfo2(context, params){
return new Promise(resolve, reject) =>{
setTimeout(() => {
//提交到mutations
context.commit('updateInfo')
console.log(params)
//action执行完成之后的回调
resolve("action执行完成!")
}, 1000)
}
}
},
})
//组件内调用
<button @click="updateInfo"></button>
...
methods:{
//正常携带参数调用
updateInfo(){
this.$store.dispatch('updateInfo',params)
},
//有回调,就是action执行完成之后,这边会有个回调函数,这边能知道action已经执行完成
this.$store.dispatch('updateInfo2', params).then(res => {
//res action执行完成之后返回的信息
console.log(res);//action执行完成!
})
}
6、modules:
Vuex使用单一状态树,那么意味着很多状态都会交给Vuex来管理
当应用百年的非常复杂时,store对象就有可能变得非常臃肿
为了解决这个问题,Vuex允许我们将store分割成模块(Module)每个模块拥有自己的state、mutations、actions、getters等
const moduleA = {
state: {
name: 'wangyi'
},
mutations: {
updateName(state, newName){
state.name = newName
}
},
getters: {
fullName(state){
return state.name + '111'
},
fullName2(state, getters){
//modules里面也可以调用getters
return getters.fullName + '222'
},
fullName3(state, getters, rootState){
//rootState为原始store内的属性
return getters.fullName2 + rootState.num
}
},
actions: {
updateName(context){
//异步操作,当前这个context是上下文,他commit只能commit自己的mutation,不能提交到原始的 //store内
context.commit('updateName', 'wangsan')
}
},
}
const moduleB = {
state: {...},
mutations: {...},
actions: {...},
getters: {...}
}
const store = new Vuex.Store({
state: {
num: 100
},
modules: {
a: moduleA,
b: moduleB
},
})
//调用属性
<h1>{{$store.state.a.name}}</h1>
//调用mutations
<button @click="updateName"></button>
//调用getters
<h1>{{$store.state.fullName}}</h1>
...
methods:{
updateName(){
//调用模块内的mutation 程序会先去原始的store内查找mutation,找不到的话就会去模块内找
this.$store.commit('updateName', 'wanger')
}
}
如果代码过多的话,可以把每个属性都抽取出去,方便维护和修改
Vue CLI(脚手架)
淘宝镜像:npm install --registry=https://registry.npm.taobao.org
之后使用:cnpm install [name]
下载Vue CLI(最新):npm install -g @vue/cli
下载Vue CLI(下载2):
Vue CLI2初始化项目:vue init webpack my-project
Project name //项目名称
Project description //项目描述
Author //作者
Vue build //用什么编译器,后续还会有笔记(当前理解就是可不可以编译template)
Install vue-router//是否安装路由
User ESLint to lint your code//是否需要语法规范
Pick an ESLint preset//选择一种规范
Set up unit tests//是否需要测试模块
Setup e2e tests with Nightwatch//是否需要测试模块
should we run .....//用什么来管理当前项目(npm/yarm)
runtime-compiler
template —> ast(抽象语法树) —> render —>virtual dom(虚拟dom)—> UI(真实dom)
runtime-only(1、效率更高 2、代码量更少)
render —>virtual dom(虚拟dom)—> UI(真实dom)
Vue CLI3初始化项目:vue create my-project
Vue CLI3:我创建的是4.x版本,就是配置文件更少,看起来更简洁
回调函数:
created:创建之前执行
destroyed:销毁之后执行
beforeRouteleave:组件内导航守卫,离开当前页面之前调用,具体还有另外几个函数,官网有
以下两个函数,只有该组件被保持了状态使用了keep-alive时,才是有效的
activated:处于活跃状态的时候执行
deactivated:不活跃的时候执行