补充了redis和vue的相关介绍,还没看,等开发的时候,重新学一下

This commit is contained in:
法然
2022-12-04 11:39:53 +08:00
parent 19d3b3e3e2
commit 012fc7a16d
40 changed files with 14458 additions and 18 deletions

View File

@@ -1,6 +1,6 @@
## 1 实例
```
```html
<!DOCTYPE html>
<html>
<head>

View File

@@ -163,11 +163,11 @@ BodyInserter<Object, ReactiveHttpOutputMessage> inserter3
```java
WebClient.ResponseSpec response1 = uri1
.body(inserter3)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
.acceptCharset(Charset.forName("UTF-8"))
.ifNoneMatch("*")
.ifModifiedSince(ZonedDateTime.now())
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
.acceptCharset(Charset.forName("UTF-8"))
.ifNoneMatch("*")
.ifModifiedSince(ZonedDateTime.now())
.retrieve();
```

379
Vue/01-ES6补充.md Normal file
View File

@@ -0,0 +1,379 @@
# 1. 块级作用域
ES6之前没有块级作用域ES5的var没有块级作用域的概念只有function有作用域的概念ES6的let、const引入了块级作用域。
ES5之前if和for都没有作用域所以很多时候需要使用function的作用域比如闭包。
## 1.1. 什么是变量作用域
变量在什么范围内可用类似Java的全局变量和局部变量的概念全局变量全局都可用局部变量只在范围内可用。ES5之前的var是没有块级作用域的概念使用var声明的变量就是全局的。
```js
{
var name = 'zzz';
console.log(name);
}
console.log(name);
```
上述代码中{}外的`console.log(name)`可以获取到name值并打印出来用var声明赋值的变量是全局变量没有块级作用域。
## 1.2. 没有块级作用域造成的问题
### if块级
```javascript
if(true){
var name = 'zzz';
func = function (){
console.log(name);
}
func();
}
name = 'ttt';
func();
console.log(name);
```
代码输出结果为`'zzz','ttt','ttt'`第一次调用func()此时name=zzz在if块外将name置成ttt此时生效了if没有块级作用域。
### for块级
定义五个按钮,增加事件,点击哪个按钮打印“第哪个按钮被点击了”。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>块级作用域</title>
</head>
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"> </script>
<script>
// 3.没有块级作用域引起的问题:for块级
var btns = document.getElementsByTagName("button");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click',function (param) {
console.log("第"+i+"个按钮被点击了");
});
}
</script>
</body>
</html>
```
for块级中使用`var`声明变量i时是全局变量点击任意按钮结果都是“第五个按钮被点击了”。说明在执行`btns[i].addEventListener('click',function())`for块级循环已经走完此时`i=5`所有添加的事件的i都是5。
改造上述代码将for循环改造由于函数有作用域使用闭包能解决上述问题。
```javascript
// 使用闭包,函数有作用域
for (var i = 0; i < btns.length; i++) {
(function (i) {
btns[i].addEventListener('click',function (param) {
console.log("第"+i+"个按钮被点击了");
})
})(i);
}
```
结果如图所示借用函数的作用域解决块级作用域的问题因为有块级作用域每次添加的i都是当前i。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200506210027.png)
在ES6中使用let/const解决块级作用域问题let和const有块级作用域const定义常量在for块级中使用let解决块级作用域问题。
```javascript
// ES6使用let/const
const btns = document.getElementsByTagName("button");
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click',function (param) {
console.log("第"+i+"个按钮被点击了");
})
}
```
结果和使用闭包解决一致。
# 2. const的使用
1.const用来定义常量赋值之后不能再赋值再次赋值会报错。
```javascript
<script>
//1.定义常量,赋值后不能再赋值,在赋值报错
const count = 1
// count = 2
</script>
```
2.const不能只声明不赋值会报错。
```javascript
<script>
//2.只声明不赋值,必须赋值
// const count;
</script>
```
3.const常量含义是你不能改变其指向的对象例如user都是你可以改变user属性。
```javascript
<script>
//3.常量的含义是你不能改变其指向的对象user但是你可以改变user属性
const user = {
name:"zzz",
age:24,
height:175
}
console.log(user)
user.name = "ttt"
user.age = 22
user.height = 188
console.log(user)
</script>
```
**const内存地址详解**
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200506210427.png)
对象count一开始只是0x10的地址直接将count给count重新赋值指向一个新的对象指向地址改为0x20会报错const是常量无法更改对象地址。
对象user一开始指向0x10地址user有`Name``Age``Height`三个属性,此时修改属性`Name='ttt'`user对象的地址未改变不会报错。
# 3. ES6的增强写法
## 3.1. ES6的对象属性增强型写法
ES6以前定义一个对象
```javascript
const name = "zzz";
const age = 18;
const user = {
name:name,
age:age
}
console.log(user);
```
ES6写法
```javascript
const name = "zzz";
const age = 18;
const user = {
name,age
}
console.log(user);
```
## 3.2 ES6对象的函数增强型写法
ES6之前对象内定义函数
```javascript
const obj = {
run:function(){
console.log("奔跑");
}
}
```
ES6写法
```javascript
const obj = {
run(){
console.log("奔跑");
}
}
```
# 4. 箭头函数
> 传统定义函数的方式
```javascript
const aaa = function (param) {
}
```
> 对象字面量中定义函数
```javascript
const obj = {
bbb (param) { },
}
```
> ES6中的箭头函数
```javascript
//const ccc = (参数列表) => {}
const ccc = () => {}
```
## 4.1 箭头函数的参数和返回值
### 4.1.1 参数问题
> 1.放入两个参数
```javascript
const sum = (num1,num2) => {
return num1 + num2
}
```
> 2.放入一个参数,()可以省略
```javascript
const power = num => {
return num * num
}
```
### 4.1.2 函数内部
> 1.函数中代码块中有多行代码
```javascript
const test = () =>{
console.log("hello zzz")
console.log("hello vue")
}
```
> 2.函数代码块中只有一行代码可以省略return
```javascript
// const mul = (num1,num2) => {
// return num1 * num2
// }
const mul = (num1,num2) => num1* num2
// const log = () => {
// console.log("log")
// }
const log = () => console.log("log")
```
## 4.3 箭头函数的this使用
> 什么时候使用箭头函数
```javascript
setTimeout(function () {
console.log(this)
} , 1000);
setTimeout(() => {
console.log(this)//这里this找的是window的this
}, 1000);
```
> 结论箭头函数没有this这里this引用的是最近作用域aaa函数里的this的this。
```javascript
const obj = {
aaa(){
setTimeout(function () {
console.log(this)//window
});
setTimeout(() => {
console.log(this)//obj
});
}
}
obj.aaa()
```
> 上述中第一个是window对象的this第二个箭头函数的this是obj的。
```javascript
const obj = {
aaa() {
setTimeout(function () {
setTimeout(function () {
console.log(this) //window
})
setTimeout(() => {
console.log(this) //window
})
})
setTimeout(() => {
setTimeout(function () {
console.log(this) //window
})
setTimeout(() => {
console.log(this) //obj
})
})
}
}
obj.aaa()
```
# 5. 高阶函数
## 5.1 filter过滤函数
```javascript
const nums = [2,3,5,1,77,55,100,200]
//要求获取nums中大于50的数
//回调函数会遍历nums中每一个数传入回调函数在回调函数中写判断逻辑返回true则会被数组接收false会被拒绝
let newNums = nums.filter(function (num) {
if(num > 50){
return true;
}
return false;
})
//可以使用箭头函数简写
// let newNums = nums.filter(num => num >50)
```
## 5.2 map高阶函数
```javascript
// 要求将已经过滤的新数组每项乘以2
//map函数同样会遍历数组每一项传入回调函数为参数num是map遍历的每一项回调函数function返回值会被添加到新数组中
let newNums2 = newNums.map(function (num) {
return num * 2
})
//简写
// let newNums2 = newNums.map(num => num * 2)
console.log(newNums2);
```
## 5.3 reduce高阶函数
```javascript
// 3.reduce高阶函数
//要求将newNums2的数组所有数累加
//reduce函数同样会遍历数组每一项传入回调函数和0为参数0表示回调函数中preValue初始值为0回调函数中参数preValue是每一次回调函数function返回的值currentValue是当前值
//例如数组为[154, 110, 200, 400],则回调函数第一次返回值为0+154=154第二次preValue为154返回值为154+110=264以此类推直到遍历完成
let newNum = newNums2.reduce(function (preValue,currentValue) {
return preValue + currentValue
},0)
//简写
// let newNum = newNums2.reduce((preValue,currentValue) => preValue + currentValue)
console.log(newNum);
```
## 5.4综合使用
```javascript
//三个需求综合
let n = nums.filter(num => num > 50).map(num => num * 2).reduce((preValue,currentValue) => preValue + currentValue)
console.log(n);
```

155
Vue/02-HelloVue.md Normal file
View File

@@ -0,0 +1,155 @@
# 1. HelloVuejs
如何开始学习Vue当然是写一个最简单的demo直接上代码。此处通过cdn`<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>`获取vuejs。
vue是声明式编程区别于jquery的命令式编程。
## 1.1. 命令式编程
原生js做法命令式编程
1. 创建div元素设置id属性
2. 定义一个变量叫message
3. 将message变量放在div元素中显示
4. 修改message数据
5. 将修改的元素替换到div
## 1.2 . 声明式编程
vue写法声明式
1. 创建一个div元素设置id属性
2. 定义一个vue对象将div挂载在vue对象上
3. 在vue对象内定义变量message并绑定数据
4. 将message变量放在div元素上显示
5. 修改vue对象中的变量messagediv元素数据自动改变
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<title>HelloVuejs</title>
</head>
<body>
<div id="app">
<h2>{{message}}</h2>
<p>{{name}}</p>
</div>
<script>
//let变量/const常量
//编程范式:声明式编程
const app = new Vue({
el:"#app",//用于挂载要管理的元素
data:{//定义数据
message:"HelloVuejs",
name:"zzz"
}
})
</script>
</body>
</html>
```
在谷歌浏览器中按F12在开发者模式中console控制台改变vue对象的message值页面显示也随之改变。
`{{message}}`表示将变量message输出到标签h2中所有的vue语法都必须在vue对象挂载的div元素中如果在div元素外使用是不生效的。`el:"#app"`表示将id为app的div挂载在vue对象上data表示变量对象。
# 2. vue列表的展示v-for
开发中常用的数组有许多数据需要全部展示或者部分展示在原生JS中需要使用for循环遍历依次替换div元素在vue中使用`v-for`可以简单遍历生成元素节点。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<title>vue列表展示</title>
</head>
<body>
<div id="app">
<h2>{{message}}</h2>
<ul>
<li v-for="(item, index) in movies" :key="index">{{item}}</li>
</ul>
</div>
<script>
const app = new Vue({
el:"#app",//用于挂载要管理的元素
data:{//定义数据
message:"你好啊",
movies:["星际穿越","海王","大话西游","复仇者联盟"]//定义一个数组
}
})
</script>
</body>
</html>
```
显示结果如图所示:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508144357.png)
`<li v-for="(item, index) in movies" :key="index">{{item}}</li>`item表示当前遍历的元素index表示元素索引 为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 `key` 属性。建议尽可能在使用 `v-for` 时提供 `key` attribute除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为它是 Vue 识别节点的一个通用机制,`key` 并不仅与 `v-for` 特别关联。
> 不要使用对象或数组之类的非基本类型值作为 `v-for` 的 `key`。请用字符串或数值类型的值。
# 3. vue案例-计数器
使用vue实现一个小计数器点击`+`按钮,计数器+1使用`-`按钮计数器-1。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<title>vue计数器</title>
</head>
<body>
<div id="app">
<h2>当前计数:{{count}}</h2>
<!-- <button v-on:click="count--">-</button>
<button v-on:click="count++">+</button> -->
<button v-on:click="sub()">-</button>
<button @click="add()">+</button>
</div>
<script>
const app = new Vue({
el:"#app",//用于挂载要管理的元素
data:{//定义数据
count:0
},
methods: {
add:function(){
console.log("add")
this.count++
},
sub:function(){
console.log("sub")
this.count--
}
},
})
</script>
</body>
</html>
```
1. 定义vue对象并初始化一个变量count=0
2. 定义两个方法`add``sub`用于对count++或者count--
3. 定义两个button对象给button添加上点击事件
在vue对象中使用methods表示方法集合使用`v-on:click`的关键字给元素绑定监听点击事件,给按钮分别绑定上点击事件,并绑定触发事件后回调函数`add``sub`。也可以在回调方法中直接使用表达式。例如:`count++``count--`

219
Vue/03-插值操作.md Normal file
View File

@@ -0,0 +1,219 @@
# 1. Mustache语法
mustache是胡须的意思因为`{{}}`像胡须,又叫大括号语法。
在vue对象挂载的dom元素中`{{}}`不仅可以直接写变量,还可以写简单表达式。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Mustache的语法</title>
</head>
<body>
<div id="app">
<h2>{{message}}</h2>
<h2>{{message}},啧啧啧</h2>
<!-- Mustache的语法不仅可以直接写变量还可以写简单表达式 -->
<h2>{{firstName + lastName}}</h2>
<h2>{{firstName + " " + lastName}}</h2>
<h2>{{firstName}}{{lastName}}</h2>
<h2>{{count * 2}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"你好啊",
firstName:"skt t1",
lastName:"faker",
count:100
}
})
</script>
</body>
</html>
```
# 2. v-once
v-once表示该dom元素只渲染一次之后数据改变不会再次渲染。
```html
<div id="app">
<h2>{{message}}</h2>
<!-- 只会渲染一次,数据改变不会再次渲染 -->
<h2 v-once>{{message}}</h2>
</div>
```
上述`{{message}}`的message修改后第一个h2标签数据会自动改变第二个h2不会。
# 3. v-html
在某些时候我们不希望直接输出`<a href='http://www.baidu.com'>百度一下</a>`这样的字符串而输出被html自己转化的超链接。此时可以使用v-html。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-html指令的使用</title>
</head>
<body>
<div id="app">
<h2>不使用v-html</h2>
<h2>{{url}}</h2>
<h2>使用v-html直接插入html</h2>
<h2 v-html="url"></h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"你好啊",
url:"<a href='http://www.baidu.com'>百度一下</a>"
}
})
</script>
</body>
</html>
```
输出结果如下:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508144442.png)
# 4. v-text
v-text会覆盖dom元素中的数据相当于js的innerHTML方法。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-text指令的使用</title>
</head>
<body>
<div id="app">
<h2>不使用v-text</h2>
<h2>{{message}},啧啧啧</h2>
<h2>使用v-text以文本形式显示,会覆盖</h2>
<h2 v-text="message">,啧啧啧</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"你好啊"
}
})
</script>
</body>
</html>
```
如图所示,使用`{{message}}`是拼接变量和字符串而是用v-text是直接覆盖字符串内容。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508144513.png)
# 5. v-pre
有时候我们期望直接输出`{{message}}`这样的字符串,而不是被`{{}}`语法转化的message的变量值此时我们可以使用`v-pre`标签。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-pre指令的使用</title>
</head>
<body>
<div id="app">
<h2>不使用v-pre</h2>
<h2>{{message}}</h2>
<h2>使用v-pre,不会解析</h2>
<h2 v-pre>{{message}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"你好啊"
}
})
</script>
</body>
</html>
```
结果如图使用v-pre修饰的dom会直接输出字符串。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508144536.png)
# 6. v-cloak
有时候因为加载延时问题,例如卡掉了,数据没有及时刷新,就造成了页面显示从`{{message}}`到message变量“你好啊”的变化这样闪动的变化会造成用户体验不好。此时需要使用到`v-cloak`的这个标签。在vue解析之前div属性中有`v-cloak`这个标签在vue解析完成之后v-cloak标签被移除。简单类似div开始有一个css属性`display:none;`加载完成之后css属性变成`display:block`,元素显示出来。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-cloak指令的使用</title>
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<h2>{{message}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
//在vue解析前div中有一个属性cloak
//在vue解析之后div中没有一个属性v-cloak
setTimeout(() => {
const app = new Vue({
el: "#app",
data: {
message: "你好啊"
}
})
}, 1000);
</script>
</body>
</html>
```
这里通过延时1秒模拟加载卡住的状态结果一开始不显示message的值div元素中有v-cloak的属性1秒后显示message变量的值div中的v-cloak元素被移除。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508144618.gif)

View File

@@ -0,0 +1,245 @@
# 1. v-bind的基本使用
某些时候我们并不想将变量放在标签内容中,像这样`<h2>{{message}}</h2>`是将变量h2标签括起来类似js的innerHTML。但是我们期望将变量`imgURL`写在如下位置,想这样`<img src="imgURL" alt="">`导入图片是希望动态获取图片的链接此时的imgURL并非变量而是字符串imgURL如果要将其生效为变量需要使用到一个标签`v-bind:`,像这样`<img v-bind:src="imgURL" alt="">`而且这里也不能使用Mustache语法类似`<img v-bind:src="{{imgURL}}" alt="">`,这也是错误的。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-bind的基本使用</title>
</head>
<body>
<div id="app">
<!-- 错误的做法这里不能使用Mustache语法 -->
<!-- <img v-bind:src="{{imgURL}}" alt=""> -->
<!-- 正确的做法使用v-bind指令 -->
<img v-bind:src="imgURL" alt="">
<a v-bind:href="aHerf"></a>
<!-- 语法糖写法 -->
<img :src="imgURL" alt="">
<a :href="aHerf"></a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"你好啊",
imgURL:"https://cn.bing.com/th?id=OIP.NaSKiHPRcquisK2EehUI3gHaE8&pid=Api&rs=1",
aHerf:"http://www.baidu.com"
}
})
</script>
</body>
</html>
```
此时vue对象中定义的`imgURL`变量和`aHerf`变量可以动态的绑定到img标签的src属性和a标签的href属性。`v-bind:`由于用的很多vue对他有一个语法糖的优化写法也就是`:`此时修改imgURL变量图片会重新加载。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508144717.gif)
# 2. v-bind动态绑定class
## 2.1. v-bind动态绑定class(对象语法)
有时候我们期望对Dom元素的节点的class进行动态绑定选择此Dom是否有指定class属性。例如给h2标签加上`class="active"`当Dom元素有此class时候变红`<style>.active{color:red;}</style>`,在写一个按钮绑定事件,点击变黑色,再次点击变红色。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-bind动态绑定class(对象语法)</title>
<style>
.active{
color:red;
}
</style>
</head>
<body>
<div id="app">
<!-- <h2 class="active">{{message}}</h2>
<h2 :class="active">{{message}}</h2> -->
<!-- 动态绑定class对象用法 -->
<!-- <h2 :class="{key1:value1,key2:value2}">{{message}}</h2>
<h2 :class="{类名1:true,类名2:boolean}">{{message}}</h2> -->
<h2 class="title" :class="{active:isActive}">{{message}}</h2>
<h2 class="title" :class="getClasses()">{{message}}</h2>
<button @click="change">点击变色</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"你好啊",
active:"active",
isActive:true
},
methods: {
change(){
this.isActive = !this.isActive
},
getClasses(){
return {active:this.isActive}
}
},
})
</script>
</body>
</html>
```
定义两个变量`active``isActive`在Dom元素中使用`:class={active:isActive}`,此时绑定的`class='active'`isActive为trueactive显示定义方法change()绑定在按钮上,点击按钮`this.isActive = !this.isActive`控制Dom元素是否有`class='active'`的属性。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508145221.gif)
## 2.2. v-bind动态绑定class(数组用法)
class属性中可以放数组会依次解析成对应的class。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-bind动态绑定class(数组用法)</title>
<style>
</style>
</head>
<body>
<div id="app">
<!-- 加上单引号当成字符串 -->
<h2 class="title" :class="['active','line']">{{message}}</h2>
<!-- 不加会被当成变量 -->
<h2 class="title" :class="[active,line]">{{message}}</h2>
<h2 class="title" :class="getClasses()">{{message}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"你好啊",
active:"aaaa",
line:'bbbb'
},
methods: {
getClasses(){
return [this.active,this.line]
}
},
})
</script>
</body>
</html>
```
1. 加上单引号的表示字符串
2. 不加的会当成变量
3. 可以直接使用方法返回数组对象
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508145051.png)
# 3. v-for和v-bind结合
使用v-for和v-bind实现一个小demo将电影列表展示并点击某一个电影列表时候将此电影列表变成红色。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>作业(v-for和v-bind的结合)</title>
<style>
.active{
color:red;
}
</style>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item, index) in movies" :key="index" :class="{active:index===currentIndex}" @click="changeColor(index)" >{{index+"---"+item}}</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
currentIndex:0,
movies:["海王","海贼王","火影忍者","复仇者联盟"]
},
methods: {
changeColor(index){
this.currentIndex = index
}
},
})
</script>
</body>
</html>
```
v-for时候的index索引给每行绑定事件点击事件点击当行是获取此行索引index并赋值给`currentIndex`,使用`v-bind:`绑定class`index===currentIndex`Dom元素有active的class颜色变红。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508145120.gif)
# 4. v-bind动态绑定style
## 4.1 v-bind动态绑定style(对象语法)
```html
<!-- <h2 :style="{key(属性名):value(属性值)}">{{message}}</h2> -->
<!-- 加单引号,当成字符串解析 -->
<h2 :style="{fontSize:'50px'}">{{message}}</h2>
<!-- 不加单引号,变量解析 -->
<h2 :style="{fontSize:fontSize}">{{message}}</h2>
<h2 :style="getStyle()">{{message}}</h2>
```
## 4.2 v-bind动态绑定style(数组语法)
```html
<div id="app">
<h2 :style="[baseStyle]">{{message}}</h2>
<h2 :style="getStyle()">{{message}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"你好啊",
baseStyle:{backgroundColor:'red'}
},
methods: {
getStyle(){
return [this.baseStyle]
}
},
})
</script>
```
类似绑定class绑定style也是一样的。

View File

@@ -0,0 +1,375 @@
# 1. 计算属性的基本使用
现在有变量姓氏和名字,要得到完整的名字。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>计算属性的基本使用</title>
</head>
<body>
<div id="app">
<!-- Mastache语法 -->
<h2>{{firstName+ " " + lastName}}</h2>
<!-- 方法 -->
<h2>{{getFullName()}}</h2>
<!-- 计算属性 -->
<h2>{{fullName}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
firstName:"skt t1",
lastName:"faker"
},
computed: {
fullName:function(){
return this.firstName + " " + this.lastName
}
},
methods: {
getFullName(){
return this.firstName + " " + this.lastName
}
},
})
</script>
</body>
</html>
```
1. 使用Mastache语法拼接`<h2>{{firstName+ " " + lastName}}</h2>`
2. 使用方法methods`<h2>{{getFullName()}}</h2>`
3. 使用计算属性computed`<h2>{{fullName}}</h2>`
> 例子中计算属性computed看起来和方法似乎一样只是方法调用需要使用(),而计算属性不用,方法取名字一般是动词见名知义,而计算属性是属性是名词,但这只是基本使用。
# 2. 计算属性的复杂使用
现在有一个数组数据books里面包含许多book对象数据结构如下
```javascript
books:[
{id:110,name:"JavaScript从入门到入土",price:119},
{id:111,name:"Java从入门到放弃",price:80},
{id:112,name:"编码艺术",price:99},
{id:113,name:"代码大全",price:150},
]
```
要求计算出所有book的总价格`totalPrice`
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>计算属性的复杂使用</title>
</head>
<body>
<div id="app">
<h2>总价格:{{totalPrice}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
books:[
{id:110,name:"JavaScript从入门到入土",price:119},
{id:111,name:"Java从入门到放弃",price:80},
{id:112,name:"编码艺术",price:99},
{id:113,name:"代码大全",price:150},
]
},
computed: {
totalPrice(){
let result= 0;
for (let i = 0; i < this.books.length; i++) {
result += this.books[i].price;
}
return result
}
}
})
</script>
</body>
</html>
```
获取每一个book对象的price累加当其中一个book的价格发生改变时候总价会随之变化。
# 3. 计算属性的setter和getter
在计算属性中其实是由这样两个方法setter和getter。
```javascript
computed: {
fullName:{
//计算属性一般没有set方法只读属性
set:function(newValue){
console.log("-----")
const names = newValue.split(" ")
this.firstName = names[0]
this.lastName = names[1]
},
get:function(){
return this.firstName + " " + this.lastName
}
}
}
```
但是计算属性一般没有set方法只读属性只有get方法但是上述中newValue就是新的值也可以使用set方法设置值但是一般不用。
***computed的getter/setter***
> 请看如下代码:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue计算属性的getter和setter</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>计算属性computed的getter/setter</h1>
<h2>fullName</h2>
{{fullName}}
<h2>firstName</h2>
{{firstName}}
<h2>lastName</h2>
{{lastName}}
</div>
<script>
var app = new Vue({
el:"#app",
data:{
firstName:"zhang",
lastName:"san",
},
computed: {
fullName:{
get:function(){
return this.firstName+" "+this.lastName
},
set:function(value){
var list = value.split(' ');
this.firstName=list[0]
this.lastName=list[1]
}
}
},
});
</script>
</body>
</html>
```
> *初始化*
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508145719.png)
> 修改fullName
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508145752.png)
> *结论*
\- 通过这种方式,我们可以在改变计算属性值的同时也改变和计算属性相关联的属性值。
# 4. 计算属性和methods的对比
直接看代码分别使用计算属性和方法获得fullName的值。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>计算属性和methods的对比</title>
</head>
<body>
<div id="app">
<!-- methods即使firstName和lastName没有改变也需要再次执行 -->
<h2>{{getFullName()}}</h2>
<h2>{{getFullName()}}</h2>
<h2>{{getFullName()}}</h2>
<h2>{{getFullName()}}</h2>
<!-- 计算属性有缓存,只有关联属性改变才会再次计算 -->
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
firstName:"skt t1",
lastName:"faker"
},
computed: {
fullName(){
console.log("调用了计算属性fullName");
return this.firstName + " " + this.lastName
}
},
methods: {
getFullName(){
console.log("调用了getFullName");
return this.firstName + " " + this.lastName
}
},
})
</script>
</body>
</html>
```
分别使用方法和计算属性获取四次fullName结果如图。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508145941.png)
由此可见计算属性有缓存,在`this.firstName + " " + this.lastName`的属性不变的情况下methods调用了四次而计算属性才调用了一次性能上计算属性明显比methods好。而且在改动firstName的情况下计算属性只调用一次methods依然要调用4次。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508150011.png)
# 5. Vue计算属性与侦听器总结
> **照例看一段代码:**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue计算属性/侦听器/方法比较</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>计算属性computed</h1>
{{fullName}}
<h1>方法methods</h1>
{{fullName2()}}
<h1>侦听器watch</h1>
{{watchFullName}}
<h1>年龄</h1>
{{age}}
</div>
<script>
var other = 'This is other';
var app = new Vue({
el:"#app",
data:{
firstName:"zhang",
lastName:"san",
watchFullName:"zhangsan",
age:18,
},
watch: {
firstName:function(newFirstName, oldFirstName){
console.log("firstName触发了watch,newFirstName="+newFirstName+",oldFirstName="+oldFirstName)
this.watchFullName = this.firstName+this.lastName+","+other
},
lastName:function(newLastName, oldLastName){
console.log("lastName触发了watch,newLastName="+newLastName+",oldLastName="+oldLastName)
this.watchFullName = this.firstName+this.lastName+","+other
}
},
computed: {
fullName:function(){
console.log("调用了fullName,计算了一次属性")
return this.firstName+this.lastName+","+other;
}
},
methods: {
fullName2:function(){
console.log("调用了fullName,执行了一次方法")
fullName2 = this.firstName+this.lastName+","+other;
return fullName2;
}
}
});
</script>
</body>
</html>
```
> 初始化:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508150240.png)
> 修改firstName/lastName/两者都修改
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508150503.png)
> 修改computed中没计算的age
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508150641.png)
> 修改Vue实例外的对象
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508150723.png)
> 修改Vue实例外对象后在修改Vue实例内的对象
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200508150803.png)
> 测试结论:
1. 使用computed计算了fullName属性值为firstName+lastName。计算属性具有`缓存功能`当firstName和lastName都不改变的时候fullName不会重新计算比如我们改变age的值fullName的值是不需要重新计算的。
2. methods并没有缓存特性比如我们改变age的值fullName2()方法会被执行一遍。
3. 当一个功能可以用上面三个方法来实现的时候明显使用computed更合适代码简单也有缓存特性。
4. 计算属性范围在vue实例内修改vue实例外部对象不会重新计算渲染但是如果先修改了vue实例外对象在修改vue计算属性的对象那么外部对象的值也会重新渲染。
> *计算属性computed*
计算属性范围在Vue实例的fullName内所管理的firstName和lastName,通常监听多个变量
> *侦听器watch*
监听数据变化,一般只监听一个变量或数组
> 使用场景
watch(`异步场景`)computed(`数据联动`)

149
Vue/06-事件监听.md Normal file
View File

@@ -0,0 +1,149 @@
# 1. v-on的基本使用
在前面的计数器案例中使用了`v-on:click`监听单击事件。这里在回顾一下:
```vue
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{count}}</h2>
<!-- <button v-on:click="count++"></button>
<button v-on:click="count--"></button> -->
<button @click="increment"></button>
<button @click="decrement()"></button>
</div>
<script>
const app = new Vue({
el:"#app",
data:{
count:0
},
methods: {
increment(){
this.count++
},
decrement(){
this.count--
}
}
})
</script>
</body>
</html>
```
使用`v-on:click`给button绑定监听事件以及回调函数@是`v-on:`的语法糖,也就是简写也可以使用`@click`。方法一般是需要写方法名加上(),在`@click`中可以省掉,如上述的`<button @click="increment">加</button>`
# 2. v-on的参数传递
了解了v-on的基本使用现在需要了解参数传递。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 事件没传参 -->
<button @click="btnClick">按钮1</button>
<button @click="btnClick()">按钮2</button>
<!-- 事件调用方法传参,写函数时候省略小括号,但是函数本身需要传递一个参数 -->
<button @click="btnClick2(123)">按钮3</button>
<button @click="btnClick2()">按钮4</button>
<button @click="btnClick2">按钮5</button>
<!-- 事件调用时候需要传入event还需要传入其他参数 -->
<button @click="btnClick3($event,123)">按钮6</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
methods:{
btnClick(){
console.log("点击XXX");
},
btnClick2(value){
console.log(value+"----------");
},
btnClick3(event,value){
console.log(event+"----------"+value);
}
}
})
</script>
</body>
</html>
```
1. 事件没传参,可以省略()
2. 事件调用方法传参了写函数时候省略了小括号但是函数本身是需要传递一个参数的这个参数就是原生事件event参数传递进去
3. 如果同时需要传入某个参数同时需要event是可以通过`$event`传入事件。
按钮4调用`btnClick2(value){}`,此时`undefined`。按钮5调用时省略了()会自动传入原生event事件如果我们需要event对象还需要传入其他参数可以使用`$event`对象。
# 3. v-on的修饰词
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-on的修饰符</title>
</head>
<body>
<div id="app">
<!--1. .stop的使用btn的click事件不会传播不会冒泡到上层调用event.stopPropagation() -->
<div @click="divClick">
<button @click.stop="btnClick">按钮1</button>
</div>
<!-- 2. .prevent 调用event.preeventDefault阻止默认行为 -->
<form action="www.baidu.com">
<button type="submit" @click.prevent="submitClick">提交</button>
</form>
<!--3. 监听键盘的事件 -->
<input type="text" @click.enter="keyup">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
methods:{
btnClick(){
console.log("点击button");
},
divClick(){
console.log("点击div");
},
submitClcik(){
console.log("提交被阻止了")
},
keyup(){
console.log("keyup点击")
}
}
})
</script>
</body>
</html>
```
1. `.stop`的使用btn的click事件不会传播不会冒泡到上层调用`event.stopPropagation()`
2. `.prevent` 调用`event.preeventDefault`阻止默认行为。
3. `.enter`监听键盘事件

133
Vue/07-条件判断.md Normal file
View File

@@ -0,0 +1,133 @@
# 1. v-if、v-else、v-elseif
v-if用于条件判断判断Dom元素是否显示。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h2 v-if="isFlag">isFlag为true显示这个</h2>
<h2 v-show="isShow">isShow为true是显示这个</h2>
<div v-if="age<18">小于18岁未成年</div>
<div v-else-if="age<60">大于18岁小于60岁正值壮年</div>
<div v-else="">大于60岁,暮年</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
isFlag:true,
isShow:false,
age:66
}
})
</script>
</body>
</html>
```
1. 单独使用v-if变量为布尔值为true才渲染Dom
2. v-show的变量也是布尔值为true才显示内容类似css的display
3. v-if、v-else、v-else-if联合使用相当于if、elseif、else但是在条件比较多的时候建议使用计算属性。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200513141713.png)
# 2. v-if的demo
在登录网站是经常可以选择使用账户名或者邮箱登录的切换按钮。要求点击按钮切换登录方式。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<span v-if="isUser">
<label for="username">用户账号</label>
<input type="text" id="username" placeholder="请输入用户名" >
</span>
<span v-else="isUser">
<label for="email">用户邮箱</label>
<input type="text" id="email" placeholder="请输入用户邮箱" >
</span>
<button @click="isUser=!isUser">切换类型</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
isUser:true
}
})
</script>
</body>
</html>
```
使用`v-if``v-else`选择渲染指定的Dom点击按钮对`isUser`变量取反。
> 这里有个小问题,如果已经输入了账号了,此时想切换到邮箱输入,输入框未自己清空。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200513142053.gif)
这里需要了解一下vue底层操作此时input输入框值被复用了。
1. vue在进行DOM渲染是处于性能考虑会复用已经存在的元素而不是每次都创建新的DOM元素。
2. 在上面demo中Vue内部发现原来的input元素不再使用所以直接将其映射对应虚拟DOM用来复用。
3. 如果不希望出现类似复用问题可以给对应的dom元素加上`key`值,并保证`key`不同。
```html
<input type="text" id="username" placeholder="请输入用户名" key="username">
<input type="text" id="email" placeholder="请输入用户邮箱" key="email">
```
# 3. v-show
v-if 在首次渲染的时候如果条件为假什么也不操作页面当作没有这些元素。当条件为真的时候开始局部编译动态的向DOM元素里面添加元素。当条件从真变为假的时候开始局部编译卸载这些元素也就是删除。
v-show 不管条件是真还是假第一次渲染的时候都会编译出来也就是标签都会添加到DOM中。之后切换的时候通过display: none;样式来显示隐藏元素。可以说只是改变css的样式几乎不会影响什么性能。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h2 v-show="isFlag">v-show只是操作元素的style属性display</h2>
<h2 v-if="isFlag">v-if是新增和删除dom元素</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
isFlag:true
}
})
</script>
</body>
</html>
```

270
Vue/08-循环遍历.md Normal file
View File

@@ -0,0 +1,270 @@
# 1. v-for遍历数组
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 1.遍历过程没有使用索引(下标值) -->
<ul>
<li v-for="item in names" >{{item}}</li>
</ul>
<!-- 2.遍历过程有使用索引(下标值) -->
<ul>
<li v-for="(item,index) in names" >{{index+":"+item}}</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
names:["zzz","ttt","yyy"]
}
})
</script>
</body>
</html>
```
一般需要使用索引值。`<li v-for="(item,index) in names" >{{index+":"+item}}</li>`index表示索引item表示当前遍历的元素。
# 2. v-for遍历对象
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 1.遍历过程没有使用index索引-->
<!-- 格式为:(value, name) -->
<ul>
<li v-for="(item,key) in user" >{{key+"-"+item}}</li>
</ul>
<!-- 格式为:(value, name, index) -->
<ul>
<li v-for="(item,key,index) in user" >{{key+"-"+item+"-"+index}}</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
user:{
name:"zzz",
height:188,
age:24
}
}
})
</script>
</body>
</html>
```
1. 遍历过程没有使用index索引`<li v-for="(item,key) in user" >{{key+"-"+item}}</li>`item表示当前元素是属性值key表示user对象属性名。
2. 遍历过程使用index索引index表示索引从0开始。
3. ![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200513144634.png)
# 3. v-for使用key
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-for使用key</title>
</head>
<body>
<div id="app">
<!-- 不加key如果要插入f依次改变 -->
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
<button @click="add1">没有key</button>
<!-- 加key如果要插入f使用diff算法高效,如果使用index做key一直变所以item如果唯一可以使用item-->
<ul>
<li v-for="item in letters" :key="item">{{item}}</li>
</ul>
<button @click="add2">有key</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
letters:['a','b','c','d','e']
},
methods: {
add1(){
this.letters.splice(2,0,'f')
},
add2(){
this.letters.splice(2,0,'f')
}
}
})
</script>
</body>
</html>
```
1. 使用key可以提高效率加key如果要插入f使用diff算法高效,如果使用index做key一直变所以item如果唯一可以使用item。
2. 不加key如果要插入f依次替换。
**v-for加key与不加**
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200513145801.png)
> 不加key渲染时候会依次替换渲染加了key会直接将其放在指定位置加key提升效率。
# 4. 数组的响应方式
我们改变DOM绑定的数据时DOM会动态的改变值。数组也是一样的。但是对于动态变化数据有要求不是任何情况改变数据都会变化。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>数组的响应式方法 </title>
</head>
<body>
<div id="app">
<!-- 数组的响应式方法 -->
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
<button @click="btn1">push</button><br>
<button @click="btn2">通过索引值修改数组</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
letters:['a','b','c','d','e']
},
methods: {
btn1(){
//1.push
this.letters.push('f')
//2.pop()删除最后一个元素
//this.letters.pop()
//3.shift()删除第一个
//this.letters.shift()
//4.unshift()添加在最前面,可以添加多个
//this.letters.unshift('aaa','bbb','ccc')
//5.splice():删除元素/插入元素/替换元素
//splice(1,1)在索引为1的地方删除一个元素,第二个元素不传,直接删除后面所有元素
//splice(index,0,'aaa')再索引index后面删除0个元素加上'aaa',
//splice(1,1,'aaa')替换索引为1的后一个元素为'aaa'
// this.letters.splice(2,0,'aaa')
//6.sort()排序可以传入一个函数
//this.letters.sort()
//7.reverse()反转
// this.letters.reverse()
},
btn2(){
this.letters[0]='f'
}
}
})
</script>
</body>
</html>
```
1. btn2按钮是通过索引值修改数组的值这种情况数组letters变化DOM不会变化。
2. 而数组的方法,例如`push()``pop()``shift()``unshift()``splice()``sort()``reverse()`等方法修改数组的数据DOM元素会随之修改。
3. > push():在最后添加元素
>
> pop():删除最后一个元素
>
> shift():删除第一个元素
>
> unshift():添加在最前面,可以添加多个
>
> splic():删除元素、插入元素、替换元素
>
> splice(1,1)再索引为1的地方删除一个元素,第二个元素不传,直接删除后面所有元素
>
> splice(index,0,'aaa')再索引index后面删除0个元素加上'aaa'
>
> splice(1,1,'aaa')替换索引为1的后一个元素为'aaa'
# 5. 综合练习
现在要求将数组内的电影展示到页面上,并选中某个电影,电影背景变红,为选中状态。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>综合练习</title>
<style>
.active {
background-color: red;
}
</style>
</head>
<body>
<div id="app">
<!-- 数组的响应式方法 -->
<ul>
<li v-for="(item,index) in movies" @click="liClick(index)" :class="{active:index===curIndex}">{{index+"---"+item}}</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
movies: ['复仇者联盟', '蝙蝠侠', '海贼王', '星际穿越'],
curIndex:0
},
methods: {
liClick(index){
this.curIndex = index
}
}
})
</script>
</body>
</html>
```
1. 先使用`v-for`将电影列表展示到页面上并获取index索引定位当前的`<li>`标签。
2. 给每个`<li>`标签加上,单击事件并将index传入单击事件的回调函数methods的`liClick()`
3. 定义一个变量`curIndex`表示当前索引初始值为0用于表示选中状态的电影列。
4. 定义个class样式active在active为激活状态是` background-color: red;`为红色。使用表达式`index=curIndex`判断当前选中状态的列。
5. ![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200513190250.gif)

182
Vue/09-综合练习.md Normal file
View File

@@ -0,0 +1,182 @@
综合前面的知识需要通过一个小demo来串联起知识。
如图所示:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200514101913.png)
点击“+”按钮,总价增加,点击“-”按钮总价减少,点击移除,移除当列。
# 1. 目录结构
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200514123111.png)
# 2. index.html
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>综合练习</title>
<link rel="stylesheet" href="./css/style.css">
</head>
<body>
<div id="app">
<table>
<thead>
<th>&nbsp;</th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</thead>
<tbody>
<tr v-for="(book,index) in books">
<td>{{index}}</td>
<td>{{book.name}}</td>
<td>{{book.beginDate}}</td>
<td>{{book.price | showPrice}}</td>
<td>
<button @click="decrement(index)" :disabled="book.count<=1">-</button>
{{book.count}}
<button @click="increment(index)">+</button>
</td>
<td>
<button @click="remove(index)">移除</button>
</td>
</tr>
</tbody>
</table>
<h3>总价:{{totalPrice | showPrice}}</h3>
</div>
<script src="../js/vue.js"></script>
<script src="./js/main.js"></script>
</body>
</html>
```
# 3.main.js
```javascript
const app = new Vue({
el: '#app',
data: {
books: [
{
name: "《算法导论》",
beginDate: "2006-9",
price: 85.00,
count: 1
},
{
name: "《UNIX编程艺术》",
beginDate: "2006-2",
price: 59.00,
count: 1
},
{
name: "《编程大全》",
beginDate: "2008-10",
price: 39.00,
count: 1
},
{
name: "《代码大全》",
beginDate: "2006-3",
price: 128.00,
count: 1
},
]
},
methods: {
increment(index){
this.books[index].count++
},
decrement(index){
this.books[index].count--
},
remove(index){
this.books.splice(index,1)
}
},
computed: {
totalPrice(){
return this.books.map(book => book.price*book.count)
.reduce((preValue,currentValue) => preValue+currentValue)
}
},
filters: {
showPrice: function(price){
console.log(typeof price);
let priceStr = price.toFixed(2)
console.log(priceStr);
return "¥" + priceStr
}
}
})
```
# 4. style.css
```css
table{
border: 1px;
border-collapse: collapse;
border-spacing: 0;
}
th,td{
padding: 8px 16px;
border: ipx solid #e9e9e9;
text-align: left;
}
th{
background-color: #f7f7f7;
color: #5c6b77;
font-weight: 600;
}
```
# filter、map、reduce
```javascript
// 1.filter过滤函数
const nums = [2,3,5,1,77,55,100,200]
//要求获取nums中大于50的数
//回调函数会遍历nums中每一个数传入回调函数在回调函数中写判断逻辑返回true则会被数组接收false会被拒绝
let newNums = nums.filter(function (num) {
if(num > 50){
return true;
}
return false;
})
//可以使用箭头函数简写
// let newNums = nums.filter(num => num >50)
console.log(newNums);
// 2.map高阶函数
// 要求将已经过滤的新数组每项乘以2
//map函数同样会遍历数组每一项传入回调函数为参数num是map遍历的每一项回调函数function返回值会被添加到新数组中
let newNums2 = newNums.map(function (num) {
return num * 2
})
//简写
// let newNums2 = newNums.map(num => num * 2)
console.log(newNums2);
// 3.reduce高阶函数
//要求将newNums2的数组所有数累加
//reduce函数同样会遍历数组每一项传入回调函数和0为参数0表示回调函数中preValue初始值为0回调函数中参数preValue是每一次回调函数function返回的值currentValue是当前值
//例如数组为[154, 110, 200, 400],则回调函数第一次返回值为0+154=154第二次preValue为154返回值为154+110=264以此类推直到遍历完成
let newNum = newNums2.reduce(function (preValue,currentValue) {
return preValue + currentValue
},0)
//简写
// let newNum = newNums2.reduce((preValue,currentValue) => preValue + currentValue)
console.log(newNum);
//三个需求综合
let n = nums.filter(num => num > 50).map(num => num * 2).reduce((preValue,currentValue) => preValue + currentValue)
console.log(n);
```

230
Vue/10-v-model.md Normal file
View File

@@ -0,0 +1,230 @@
# 1. v-model的基本使用
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="text" v-model="message">{{message}}
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: "hello"
}
})
</script>
</body>
</html>
```
v-model双向绑定既输入框的value改变对应的message对象值也会改变修改message的值input的value也会随之改变。无论改变那个值另外一个值都会变化。
# 2. v-model的原理
先来一个demo实现不使用v-model实现双向绑定。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- $event获取事件对象$event.target.value获取input值 -->
<!-- <input type="text" :value="message" @input="changeValue($event.target.value)">{{message}}-->
<!--事件调用方法传参了写函数时候省略了小括号但是函数本身是需要传递一个参数的这个参数就是原生事件event参数传递进去-->
<input type="text" :value="message" @input="changeValue">{{message}}
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: "hello world"
},
methods: {
changeValue(event){
console.log("值改变了");
this.message = event.target.value
}
}
})
</script>
</body>
</html>
```
`v-model = v-bind + v-on`实现双向绑定需要是用v-bind和v-on使用v-bind给input的value绑定message对象此时message对象改变input的值也会改变。但是改变input的value并不会改变message的值此时需要一个v-on绑定一个方法监听事件当input的值改变的时候将最新的值赋值给message对象。`$event`获取事件对象target获取监听的对象domvalue获取最新的值。
# 3. v-model结合radio类型使用
radio单选框的`name`属性是互斥的如果使用v-model可以不使用`name`就可以互斥。
```html
<div id="app">
<!-- name属性radio互斥 使用v-model可以不用name就可以互斥 -->
<label for="male">
<input type="radio" id="male" name="sex" value="男" v-model="sex">
</label>
<label for="female">
<input type="radio" id="female" name="sex" value="女" v-model="sex">
</label>
<div>你选择的性别是:{{sex}}</div>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"zzz",
sex:"男"
},
})
</script>
```
v-model绑定`sex`属性,初始值为“男”,选择女后`sex`属性变成“女”,因为此时是双向绑定。
# 4. v-model结合checkbox类型使用
checkbox可以结合v-model做单选框也可以多选框。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--单选框-->
<h2>单选框</h2>
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
<!--多选框-->
<h2>多选框</h2>
<label :for="item" v-for="(item,index) in hobbies" :key="index">
<input type="checkbox" name="hobby" :value="item" :id="item" v-model="hobbies">{{item}}
</label>
<h2>你的爱好是:{{hobbies}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isAgree: false,
hobbies: ["篮球","足球","乒乓球","羽毛球"]
}
})
</script>
</body>
</html>
```
1. checkbox结合v-model实现单选框定义变量`isAgree`初始化为`false`点击checkbox的值为`true``isAgree`也是`true`
2. checkbox结合v-model实现多选框定义数组对象`hobbies`,用于存放爱好,将`hobbies`与checkbox对象双向绑定此时选中一个多选框就多一个true`hobbies`就添加一个对象。
# 5. v-model结合select
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-model结合select类型</title>
</head>
<body>
<div id="app">
<!-- select单选 -->
<select name="fruit" v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="西瓜">西瓜</option>
</select>
<h2>你选择的水果是:{{fruit}}</h2>
<!-- select多选 -->
<select name="fruits" v-model="fruits" multiple>
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="西瓜">西瓜</option>
</select>
<h2>你选择的水果是:{{fruits}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
fruit:"苹果",
fruits:[]
},
})
</script>
</body>
```
v-model结合select可以单选也可以多选。
# 6. v-model的修饰符的使用
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-model修饰符</title>
</head>
<body>
<div id="app">
<h2>v-model修饰符</h2>
<h3>lazy,默认情况是实时更新数据加上lazy从输入框失去焦点按下enter都会更新数据</h3>
<input type="text" v-model.lazy="message">
<div>{{message}}</div>
<h3>修饰符number,默认是string类型使用number赋值为number类型</h3>
<input type="number" v-model.number="age">
<div>{{age}}--{{typeof age}}</div>
<h3>修饰符trim:去空格</h3>
<input type="text" v-model.trim="name">
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"zzz",
age:18,
name:"ttt"
},
})
</script>
</body>
</html>
```
1. `lazy`:默认情况下是实时更新数据,加上`lazy`从输入框失去焦点按下enter都会更新数据。
2. `number`默认是string类型使用`number`复制为number类型。
3. `trim`:用于自动过滤用户输入的首尾空白字符
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200515091935.png)

977
Vue/11-组件化开发.md Normal file
View File

@@ -0,0 +1,977 @@
# 1. 组件的基本使用
简单的组件示例
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 3.使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<cpnc></cpnc>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器对象
const cpnc = Vue.extend({
template:`
<div>
<h2>标题</h2>
<p>内容1...<p>
<p>内容2...<p>
</div>`
})
// 2.全局注册组件
Vue.component('my-cpn', cpnc)
const app = new Vue({
el:"#app",
data:{
},
components:{
//局部组件创建
cpnc:cpnc
}
})
</script>
</body>
</html>
```
组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 `my-cpn`。我们可以在一个通过 `new Vue` 创建的 Vue 根实例中,把这个组件作为自定义元素来使用: `<my-cpn></my-cpn>`
## 1.1 创建组件构造器对象
`template`中是组件的DOM元素内容。
## 1.2 注册组件
1. 全局注册,通过 `Vue.component `
2. 局部注册,通过 `components:{cpnc:cpnc}`
## 1.3 使用组件
像使用html标签一样使用。
```html
<div id="app">
<!-- 3.使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<cpnc></cpnc>
</div>
```
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200515093842.png)
# 2. 全局组件和局部组件
组件的注册方式有两种,一种是全局组件一种是局部组件。
```html
<div id="app">
<h2>全局组件</h2>
<my-cpn></my-cpn>
<h2>局部组件</h2>
<cpnc></cpnc>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器对象
const cpnc = Vue.extend({
template:`
<div>
<h2>标题</h2>
<p>内容1</p>
<p>内容2</p>
</div>`
})
// 2.注册组件全局组件可以在多个vue实例中使用
Vue.component('my-cpn', cpnc)
const app = new Vue({
el:"#app",
components:{//局部组件创建
cpnc:cpnc
}
})
</script>
```
## 2.1 全局组件
全局组件可以在多个vue实例中使用类似于全局变量。
使用`Vue.component('my-cpn', cpnc)`方式注册,直接使用`<my-cpn></my-cpn>`调用。`my-cpn`是全局组件的名字,`cpnc`是定义的组件对象。
## 2.2 局部组件
局部组件只能在当前vue实例挂载的对象中使用类似于局部变量有块级作用域。
> 注册方式
```javascript
const app = new Vue({
el:"#app",
components:{//局部组件创建
cpnc:cpnc
}
})
```
使用方式与全局变量一样,直接使用`<cpnc></cpnc>`调用。`cpnc:cpnc`第一个cpnc是给组件命名的名字第二个是定义的组件对象。如果俩个同名也可以直接使用es6语法
```javascript
components:{//局部组件创建
cpnc
}
```
# 3. 父组件和子组件的区别
```html
<div id="app">
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器对象
const cpn1 = Vue.extend({
template:`
<div>
<h2>标题1</h2>
<p>组件1</p>
</div>`
})
// 组件2中使用组件1
const cpn2 = Vue.extend({
template:`
<div>
<h2>标题2</h2>
<p>组件2</p>
<cpn1></cpn1>
</div>`,
components:{
cpn1:cpn1
}
})
const app = new Vue({
el:"#app",
components:{//局部组件创建
cpn2:cpn2
}
})
</script>
```
上述代码中定义了两个组件对象`cpn1``cpn2`,在组件`cpn2`中使用局部组件注册了`cpn1`,并在`template`中使用了注册的`cpn1`然后在vue实例中使用注册了局部组件`cpn2`在vue实例挂载的div中调用了`cpn2``cpn2``cpn1`形成父子组件关系。
> 注意组件就是一个vue实例vue实例的属性组件也可以有例如data、methods、computed等。
# 4. 注册组件的语法糖
```html
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.注册全局组件语法糖
Vue.component('cpn1', {
template:`
<div>
<h2>全局组件语法糖</h2>
<p>全局组件语法糖</p>
</div>`
})
const app = new Vue({
el:"#app",
components:{//局部组件创建
cpn2:{
template:`
<div>
<h2>局部组件语法糖</h2>
<p>局部组件语法糖</p>
</div>`
}
}
})
</script>
```
注册组件时候可以不实例化组件对象,直接在注册的时候实例化。`{}`就是一个组件对象。
# 5. 组件模板的分离写法
## 5.1 script标签
使用`script`标签定义组件的模板,`script`标签注意类型是`text/x-template`
```html
<!-- 1.script标签注意类型是text/x-template -->
<script type="text/x-template" id="cpn1">
<div>
<h2>组件模板的分离写法</h2>
<p>script标签注意类型是text/x-template</p>
</div>
</script>
```
## 5.2 template标签
使用`template`标签,将内容写在标签内。
```html
<!-- 2.template标签 -->
<template id="cpn2">
<div>
<h2>组件模板的分离写法</h2>
<p>template标签</p>
</div>
</template>
```
> 调用分离的模板,使用`template:'#cpn1'`
```html
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
components: { //局部组件创建
cpn1:{
template:'#cpn1'
},
cpn2: {
template: '#cpn2'
}
}
})
</script>
```
# 6. 组件的数据
## 6.1 存放问题
前面说过vue组件就是一个vue实例相应的vue组件也有`data`属性来存放数据。
```html
<div id="app">
<cpn1></cpn1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
components: { //局部组件创建
cpn1:{
template:'<div>{{msg}}</div>',
data(){
return {
msg:"组件的数据存放必须要是一个函数"
}
}
}
}
})
</script>
```
`template`中使用组件内部的数据`msg`
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200515095037.png)
## 6.2 组件的data为什么必须要是函数
组件的思想是复用,定义组件当然是把通用的公共的东西抽出来复用。
```html
<div id="app">
<h2>data不使用函数</h2>
<cpn1></cpn1>
<cpn1></cpn1>
<hr>
<h2>data使用函数</h2>
<cpn2></cpn2>
<cpn2></cpn2>
<hr>
</div>
<script src="../js/vue.js"></script>
<template id="cpn1">
<div>
<button @click="count--">-</button>
当前计数:{{count}}
<button @click="count++">+</button>
</div>
</template>
<template id="cpn2">
<div>
<button @click="count--">-</button>
当前计数:{{count}}
<button @click="count++">+</button>
</div>
</template>
<script>
const obj = {
count:0
};
const app = new Vue({
el: "#app",
components: { //局部组件创建
cpn1: {
template: '#cpn1',
data() {
return obj;
}
},
cpn2: {
template: '#cpn2',
data() {
return {
count: 0
}
}
}
}
})
</script>
```
上述代码中定义了两个组件`cpn1``cpn2`,都是定义了两个计数器,`con1`的data虽然使用了函数但是为了模拟`data:{count:0}`,使用了常量`obj`来返回count。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200515095642.gif)
图中可以看到,不使用函数`data`的好像共用一个`count`属性,而使用函数的`data`的count是各自用各自的像局部变量一样有块级作用域这个块级就是vue组件的作用域。
> 我们在复用组件的时候肯定希望各自组件用各自的变量如果确实需要都用一样的可以全局组件注册也可以是用vuex来进行状态管理。
# 7. 父组件给子组件传递数据
## 7.1 使用`props`属性,父组件向子组件传递数据
> 使用组件的`props`属性
```javascript
const cpn = {
template: "#cpn",
props: {
cmessage: {
type: String,
default: 'zzzzz',
required: true //在使用组件必传值
}
}
}
```
> 向cmessage对象传值
```html
<div id="app">
<cpn :cMessage="message"></cpn>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
message: "你好",
movies: ["复仇者联盟", "钢铁侠", "星际穿越", "哪吒传奇"]
},
components: {
cpn
}
})
</script>
```
## 7.2 props属性使用
> 数组写法
```javascript
props: ['cmovies', 'cmessage']
```
> 对象写法
```javascript
props: {
cmessage: {
type: String,
default: 'zzzzz',
required: true //在使用组件必传值
}
}
```
> props属性的类型限制
```javascript
//1.类型限制(多个类使用数组)
cmovies:Array,//限制为数组类型
cmessage:String,//限制为Strin类型
cmessage:['String','Number']//限制为String或Number类型
```
> props属性的默认值
```javascript
// 2.提供一些默认值,以及必传值
cmessage: {
type: String,
default: 'zzzzz',//默认值
}
```
> props属性的必传值
```javascript
cmessage: {
type: String,
default: 'zzzzz',
required: true //在使用组件必传值
}
```
> 类型是Object/Array默认值必须是一个函数
```javascript
//类型是Object/Array默认值必须是一个函数
cmovies: {
type: Array,
default () {
return [1, 2, 3, 4]
}
},
```
> 自定义验证函数
```javascript
vaildator: function (value) {
//这个传递的值必须匹配下列字符串中的一个
return ['zzzzz', 'ttttt', 'yyy'].indexOf(value) !== -1
}
```
> 自定义类型
```javascript
function Person(firstName,lastName) {
this.firstName = firstName
this.lastName = lastName
}
cmessage:Person//限定了cmeessage必须是Person类型
```
> 综合使用
```html
<div id="app">
<cpn :cMovies="movies" :cMessage="message"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="(item, index) in cmovies" :key="index">{{item}}</li>
</ul>
<h2>{{cmessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
function Person(firstName,lastName) {
this.firstName = firstName
this.lastName = lastName
}
// 父传子props
const cpn = {
template: "#cpn",
// props: ['cmovies', 'cmessage'],//数组写法
props: { //对象写法
// 1.类型限制(多个类使用数组)
// cmovies:Array,
// cmessage:String,
// cmessage:['String','Number'],
// 2.提供一些默认值,以及必传值
cmessage: {
type: String,
default: 'zzzzz',
required: true //在使用组件必传值
},
//类型是Object/Array默认值必须是一个函数
cmovies: {
type: Array,
default () {
return [1, 2, 3, 4]
}
},
// 3.自定义验证函数
// vaildator: function (value) {
// //这个传递的值必须匹配下列字符串中的一个
// return ['zzzzz', 'ttttt', 'yyy'].indexOf(value) !== -1
// }
// 4.自定义类型
// cmessage:Person,
},
data() {
return {
}
},
methods: {
},
};
const app = new Vue({
el: "#app",
data: {
message: "你好",
movies: ["复仇者联盟", "钢铁侠", "星际穿越", "哪吒传奇"]
},
components: {
cpn
}
})
</script>
```
# 8. 组件通信
## 8.1 父传子props的驼峰标识
v-bind是 不支持使用驼峰标识的,例如`cUser`要改成`c-User`
```html
<div id="app">
<!-- v-bind不支持驼峰 :cUser改成 :c-User-->
<!-- <cpn :cUser="user"></cpn> -->
<cpn :c-User="user"></cpn>
<cpn :cuser="user" ></cpn>
</div>
<template id="cpn">
<div>
<!-- 使用驼峰 -->
<h2>{{cUser}}</h2>
<!-- 不使用 -->
<h2>{{cuser}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 父传子props
const cpn = {
template: "#cpn",
props: { //对象写法
//驼峰
cUser:Object,
//未使用驼峰
cuser:Object
},
data() {return {}},
methods: {},
};
const app = new Vue({
el: "#app",
data: {
user:{
name:'zzz',
age:18,
height:175
}
},
components: {
cpn
}
})
</script>
```
## 8.2 子传父`$emit`
子组件向父组件传值,使用自定义事件`$emit`
```html
<!-- 父组件 -->
<div id="app">
<!-- 不写参数默认传递btnClick的item -->
<cpn @itemclick="cpnClcik"></cpn>
</div>
<!-- 子组件 -->
<template id="cpn">
<div>
<button v-for="(item, index) in categoties" :key="index" @click="btnClick(item)">{{item.name}}</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: "#cpn",
data() {
return {
categoties: [{
id: 'aaa',
name: '热门推荐'
},
{
id: 'bbb',
name: '手机数码'
},
{
id: 'ccc',
name: '家用家电'
},
{
id: 'ddd',
name: '电脑办公'
},
]
}
},
methods: {
btnClick(item) {
this.$emit('itemclick', item)
}
},
};
const app = new Vue({
el: "#app",
data() {
return {
}
},
methods: {
cpnClcik(item) {
console.log('cpnClick'+item.name);
}
},
components: {
cpn
},
})
</script>
```
1.在子组件中定义一个方法`btnClick(item)`,使用`$emit`'itemclick'是事件名,`item`是传过去的值。
```javascript
methods: {
btnClick(item) {
this.$emit('itemclick', item)
}
},
```
2.在子组件中监听点击事件并回调此方法
```html
<div>
<button v-for="(item, index) in categoties" :key="index" @click="btnClick(item)">{{item.name}}</button>
</div>
```
3.在父组件中定义一个方法cpnClcik(item)
```javascript
methods: {
cpnClcik(item) {
console.log('cpnClick'+item.name);
}
},
```
4.并在父组件vue实例中调用`<cpn @itemclick="cpnClcik"></cpn>`*不写参数默认传递btnClick的item* ),父组件监听事件名为`itemclick`的子组件传过来的事件。
```html
<cpn @itemclick="cpnClcik"></cpn>
```
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200515103008.gif)
## 8.3 父子组件通信案例
实现父子组件的值双向绑定。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件通信-父子通信案例</title>
</head>
<body>
<!-- 父组件 -->
<div id="app">
<h2>子组件</h2>
<cpn :number1='num1' :number2='num2'
@num1change="num1Change"
@num2change="num2Change"></cpn>
<h2>--------------</h2>
<h2>父组件{{num1}}</h2>
<input type="text" v-model="num1" >
<h2>父组件{{num2}}</h2>
<input type="text" v-model="num2">
</div>
<!-- 子组件 -->
<template id="cpn">
<div>
<h2>number1:{{number1}}</h2>
<h2>dnumber1:{{dnumber1}}</h2>
<input type="text" :value="dnumber1" @input="num1input">
<h2>number2{{number2}}</h2>
<h2>dnumber2:{{dnumber2}}</h2>
<input type="text" :value="dnumber2" @input="num2input">
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 父传子props
const cpn = {
template: "#cpn",
data() {
return {
dnumber1:this.number1,
dnumber2:this.number2
}
},
props:{
number1:[Number,String],
number2:[Number,String],
},
methods: {
num1input(event){
this.dnumber1 = event.target.value
this.$emit('num1change',this.dnumber1)
},
num2input(event){
this.dnumber2 = event.target.value
this.$emit('num2change',this.dnumber2)
}
},
};
const app = new Vue({
el: "#app",
data: {
num1:1,
num2:2
},
methods: {
num1Change(value){
this.num1=value
},
num2Change(value){
this.num1=value
}
},
components: {
cpn
},
})
</script>
</body>
</html>
```
使用watch实现。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件通信-父子通信案例(watch实现)</title>
</head>
<body>
<!-- 父组件 -->
<div id="app">
<cpn :number1='num1' :number2='num2' @num1change="num1Change" @num2change="num2Change"></cpn>
<h2>父组件{{num1}}</h2>
<input type="text" v-model="num1" >
<h2>父组件{{num2}}</h2>
<input type="text" v-model="num2">
</div>
<!-- 子组件 -->
<template id="cpn">
<div>
<h2>{{number1}}</h2>
<input type="text" v-model="dnumber1">
<h2>{{number2}}</h2>
<input type="text" v-model="dnumber2">
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 父传子props
const cpn = {
template: "#cpn",
data() {
return {
dnumber1:this.number1,
dnumber2:this.number2
}
},
props:{
number1:[Number,String],
number2:[Number,String],
},
watch: {
dnumber1(newValue){
this.dnumber1 = newValue * 100
this.$emit('num1change',newValue)
},
dnumber2(newValue){
this.dnumber1 = newValue * 100
this.$emit('num2change',newValue)
}
},
};
const app = new Vue({
el: "#app",
data() {
return {
num1:1,
num2:2,
}
},
methods: {
num1Change(value){
this.num1=value
},
num2Change(value){
this.num1=value
}
},
components: {
cpn
},
})
</script>
</body>
</html>
```
# 9. 父访问子children-ref
父组件访问子组件,有时候需要直接操作子组件的方法,或是属性,此时需要用到`$children``$ref`
```html
<!-- 父组件 -->
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn ref="aaa"></cpn>
<button @click="btnClick" >按钮</button>
</div>
<!-- 子组件 -->
<template id="cpn">
<div>
我是子组件
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 父传子props
const cpn = {
template: "#cpn",
data() {
return {
name:"我是子组件的name"
}
},
methods: {
showMessage(){
console.log("showMessage");
}
},
};
const app = new Vue({
el: "#app",
data() {
return {
message:"hello"
}
},
methods: {
btnClick(){
// 1.children
// console.log(this.$children[0].showMessage)
// for (let cpn of this.$children) {
// console.log(cpn.showMessage)
// }
// 2.$ref
console.log(this.$refs.aaa.name)
}
},
components: {
cpn
},
})
</script>
```
> `$children`方式
```javascript
// 1.children
console.log(this.$children[0].showMessage)
for (let cpn of this.$children) {
console.log(cpn.showMessage)
}
```
使用`this.$children`直接获取**当前实例的直接子组件,需要注意 `$children` 并不保证顺序,也不是响应式的。**如果你发现自己正在尝试使用 `$children` 来进行数据绑定,考虑使用一个数组配合 `v-for` 来生成子组件,并且使用 Array 作为真正的来源。
> $refs方式
**先定义子组件**
```html
<cpn ref="aaa"></cpn>
```
**直接调用**

348
Vue/12-组件化高级.md Normal file
View File

@@ -0,0 +1,348 @@
# 1. slot-插槽的基本使用
我们在使用组件的时候有时候希望,在组件内部定制化内容,例如京东这样。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516083523.png)
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516083549.png)
这两个都是导航栏,组件的思想是可以复用的,把这个导航栏看做一个组件。
这个组件都可以分成三个部分,左边中间右边,如果可以分割组件,就可以定制化组件内容了。
```html
<!-- 父组件 -->
<div id="app">
<cpn></cpn>
<cpn>
<span style="color:red;">这是插槽内容222</span>
</cpn>
<cpn>
<i style="color:red;">这是插槽内容333</i>
</cpn>
<cpn></cpn>
</div>
<!-- 插槽的基本使用使用<slot></slot> -->
<!-- 子组件 -->
<template id="cpn">
<div>
<div>
{{message}}
</div>
<!-- 插槽默认值 -->
<slot><button>button</button></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: "#cpn",
data() {
return {
message: "我是子组件"
}
},
}
const app = new Vue({
el: "#app",
data() {
return {
message: "我是父组件消息"
}
},
components: {
cpn
},
})
</script>
```
> 简单使用插槽定义template时候使用`slot`
```html
<!-- 子组件 -->
<template id="cpn">
<div>
<div>
{{message}}
</div>
<!-- 插槽默认值 -->
<slot><button>button</button></slot>
</div>
</template>
```
> 插槽可以使用默认值,`<button>button</button>`就是插槽的默认值。
```html
<cpn></cpn>
<cpn><span style="color:red;">这是插槽内容222</span></cpn>
```
> 使用插槽,`<span style="color:red;">这是插槽内容222</span>`将替换插槽的默认值
上述代码结果如图所示
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516083936.png)
> 替换了两次插槽两次未替换显示默认的button。
>
> 如果想实现组件分成三部分就可以使用三个`<slot></slot>`来填充插槽了。
# 2. slot-具名插槽的使用
具名插槽,就是可以让插槽按指定的顺序填充,而没有具名的插槽是按照你填充的顺序排列的,而具名插槽可以自定义排列。
```html
<!-- 父组件 -->
<div id="app">
<cpn>
<span>具名插槽</span>
<span slot="left">这是左边具名插槽</span>
<!-- 新语法 -->
<template v-slot:center>这是中间具名插槽</template>
<!-- 新语法缩写 -->
<template #right>这是右边具名插槽</template>
</cpn>
</div>
<!-- 插槽的基本使用使用<slot></slot> -->
<!-- 子组件 -->
<template id="cpn">
<div>
<slot name="left">左边</slot>
<slot name="center">中间</slot>
<slot name="right">右边</slot>
<slot>没有具名的插槽</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: "#cpn",
data() {
return {
message: "我是子组件"
}
},
}
const app = new Vue({
el: "#app",
data() {
return {
message: "我是父组件消息"
}
},
components: {
cpn
},
})
</script>
```
> 如图所示
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516084157.png)
> 没有具名的插槽排在最后,因为在定义组件的时候,排在了最后,如果有多个按顺序排列。具名插槽按照自定义的顺序排列。
> 定义具名插槽,使用`name`属性,给插槽定义一个名字。
```html
<!-- 插槽的基本使用使用<slot></slot> -->
<!-- 子组件模板 -->
<template id="cpn">
<div>
<slot name="left">左边</slot>
<slot name="center">中间</slot>
<slot name="right">右边</slot>
<slot>没有具名的插槽</slot>
</div>
</template>
```
> 使用具名插槽,在自定义组件标签内使用`slot="left"`,插入指定插槽
```html
<!-- 父组件 -->
<div id="app">
<cpn>
<span>具名插槽</span>
<span slot="left">这是左边具名插槽</span>
<!-- 新语法 -->
<template v-slot:center>这是中间具名插槽</template>
<!-- 新语法缩写 -->
<template #right>这是右边具名插槽</template>
</cpn>
</div>
```
> 注意:此处有是三种写法,获取指定插槽。
# 3. 编译的作用域
前面说过组件都有自己的作用域,自己组件的作用在自己组件内。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>编译的作用域</title>
</head>
<body>
<!-- 父组件 -->
<div id="app">
<!-- 使用的vue实例作用域的isShow -->
<cpn v-show="isShow"></cpn>
</div>
<!-- 插槽的基本使用使用<slot></slot> -->
<!-- 子组件 -->
<template id="cpn">
<div>
<h2>我是子组件</h2>
<p>哈哈哈</p>
<!-- 组件作用域,使用的子组件的作用域 -->
<button v-show="isShow"></button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: "#cpn",
data() {
return {
isShwo:false
}
},
}
const app = new Vue({
el: "#app",
data() {
return {
message: "我是父组件消息",
isShow:true
}
},
components: {
cpn
},
})
</script>
</body>
</html>
```
结果如下
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516084606.png)
> 子组件使用的是子组件的isShow子组件为false所以button没显示被隐藏。
# 4. 作用域插槽案例
父组件替换插槽的标签,但是内容是由子组件来提供。
当组件需要在多个父组件多个界面展示的时候,将内容放在子组件插槽中,父组件只需要告诉子组件使用什么方式展示界面。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>作用域插槽案例</title>
</head>
<body>
<!-- 父组件 -->
<div id="app">
<cpn></cpn>
<!-- 目的是获取子组件数据 -->
<cpn>
<!-- 2.5以下必须使用template -->
<template slot-scope="slot">
<!-- <span v-for="(item, index) in slot.data" :key="index">{{item}}-</span> -->
<span>{{slot.data.join(' - ')}}</span>
</template>
</cpn>
<cpn>
<!-- 2.5以下必须使用template -->
<template slot-scope="slot">
<!-- <span v-for="(item, index) in slot.data" :key="index">{{item}}*</span> -->
<span>{{slot.data.join(' * ')}}</span>
</template>
</cpn>
</div>
<!-- 插槽的基本使用使用<slot></slot> -->
<!-- 子组件 -->
<template id="cpn">
<div>
<slot :data="pLanguage">
<ul>
<li v-for="(item, index) in pLanguage" :key="index">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: "#cpn",
data() {
return {
isShwo:false,
pLanguage:['JavaScript','Java','C++','C']
}
},
}
const app = new Vue({
el: "#app",
data() {
return {
isShow:true
}
},
components: {
cpn
},
})
</script>
</body>
</html>
```
> 组件中使用`slot-scope="slot"`**2.6.0已经废弃)**给插槽属性命名,在通过`slot`调用绑定在插槽上的属性。也可以使用`v-slot="slot"`。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516084957.png)

View File

@@ -0,0 +1,312 @@
# 1. 生命周期图
Vue实例的生命周期中有多个状态。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516085242.png)
> 测试代码
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue实例的生命周期</title>
</head>
<body>
<div id="app">
<h1>测试生命周期</h1>
<div>{{msg}}</div>
<hr>
<h3>测试beforeUpdate和update两个钩子函数</h3>
<button @click="handlerUpdate">更新数据</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
msg: "12345"
},
methods: {
handlerUpdate() {
this.msg=this.msg.split("").reverse().join("")
}
},
//按照示意图依次调用
beforeCreate(){
console.log("调用了beforeCreate钩子函数");
},
created(){
console.log("调用了created钩子函数");
},
beforeMount(){
console.log('调用了beforeMount钩子函数');
},
mounted(){
console.log('调用了mounted钩子函数');
},
beforeUpdate(){
console.log("调用了beforeUpdate钩子函数")
},
updated(){
console.log("调用了updated钩子函数");
},
beforeDestroy(){
console.log("调用了beforeDestroy钩子函数");
},
destroyed(){
console.log("调用了destroyed钩子函数");
}
})
</script>
</body>
</html>
```
如图所示:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516091149.png)
初始化页面依次调用了:
> 1. 调用了beforeCreate钩子函数
> 2. 调用了created钩子函数
> 3. 调用了beforeMount钩子函数
> 4. 调用了mounted钩子函数
点击更新数据后:
`12345`变成了`54321`,此时调用了:
> 1. 调用了beforeUpdate钩子函数
> 2. 调用了updated钩子函数
打开F12控制台
直接输入`app.$destroy()`主动销毁Vue实例调用
>1. 调用了beforeDestroy钩子函数
>2. 调用了destroyed钩子函数
# 2. 再探究
## 2.1 beforeCreate之前
初始化钩子函数和生命周期
## 2.2 beforeCreate和created钩子函数间的生命周期
在beforeCreate和created之间进行数据观测(data observer) 也就是在这个时候开始监控data中的数据变化了同时初始化事件。
## 2.3 created钩子函数和beforeMount间的生命周期
对于created钩子函数和beforeMount有判断
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516091448.png)
### 2.3.1 el选项对生命周期影响
>1. 有el选项
```javascript
new Vue({
el: '#app',
beforeCreate: function () {
console.log('调用了beforeCreat钩子函数')
},
created: function () {
console.log('调用了created钩子函数')
},
beforeMount: function () {
console.log('调用了beforeMount钩子函数')
},
mounted: function () {
console.log('调用了mounted钩子函数')
}
})
```
结果:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516091646.png)
>2. 无el选项
```javascript
new Vue({
beforeCreate: function () {
console.log('调用了beforeCreat钩子函数')
},
created: function () {
console.log('调用了created钩子函数')
},
beforeMount: function () {
console.log('调用了beforeMount钩子函数')
},
mounted: function () {
console.log('调用了mounted钩子函数')
}
})
```
结果:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516092238.png)
>证明没有el选项则停止编译也意味着暂时停止了生命周期。生命周期到created钩子函数就结束了。而当我们不加el选项但是手动执行vm.$mount(el)方法的话,也能够使暂停的生命周期进行下去,例如:
```javascript
var app = new Vue({
beforeCreate: function () {
console.log('调用了beforeCreat钩子函数')
},
created: function () {
console.log('调用了created钩子函数')
},
beforeMount: function () {
console.log('调用了beforeMount钩子函数')
},
mounted: function () {
console.log('调用了mounted钩子函数')
}
})
app.$mount('#app')
```
结果:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516091646.png)
### 2.3.2 template
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516092602.png)
>同时使用`template``HTML`,查看优先级:
```html
<h1>测试template和HTML的优先级</h1>
<div id="app">
<p>HTML优先</p>
</div>
<script>
var app = new Vue({
el:"#app",
data:{
msg:"template优先"
},
template:"<p>{{msg}}</p>",
});
</script>
```
结果:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516092750.png)
>结论
1. 如果Vue实例对象中有template参数选项则将其作为模板编译成render函数
2. 如果没有template参数选项则将外部的HTML作为模板编译template也就是说template参数选项的优先级要比外部的HTML高
3. 如果1,2条件都不具备则报错
>注意
1. Vue需要通过el去找对应的templateVue实例通过el的参数首先找自己有没有template如果没有再去找外部的html找到后将其编译成render函数。
2. 也可以直接调用[render](https://cn.vuejs.org/v2/api/#render)选项,优先级:`render函数选项 > template参数 > 外部HTML`
```javascript
new Vue({
el: '#app',
render (createElement) {
return (....)
}
})
```
## 2.4 beforeMount和mounted钩子函数间的生命周期
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516092939.png)
>beforeMount
载入前完成了data和el数据初始化但是页面中的内容还是vue中的占位符data中的message信息没有被挂在到Dom节点中在这里可以在渲染前最后一次更改数据的机会不会触发其他的钩子函数一般可以在这里做初始数据的获取。
>Mount
载入后html已经渲染(ajax请求可以放在这个函数中)把vue实例中的data里的message挂载到DOM节点中去
>这里两个钩子函数间是载入数据。
## 2.5 beforeUpdate钩子函数和updated钩子函数间的生命周期
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516093232.png)
在Vue中修改数据会导致重新渲染依次调用beforeUpdate钩子函数和updated钩子函数
如果待修改的数据没有载入模板中,不会调用这里两个钩子函数
```javascript
var app = new Vue({
el: '#app',
data: {
msg: 1
},
template: '<div id="app"><p></p></div>',
beforeUpdate: function () {
console.log('调用了beforeUpdate钩子函数')
},
updated: function () {
console.log('调用了updated钩子函数')
}
})
app.msg = 2
```
结果:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516093509.png)
如果绑定了数据,会调用两个钩子函数:
```javascript
<h1>测试有数据绑定修改数据钩子函数调用情况</h1>
<div id="app">
</div>
<script>
var app = new Vue({
el:"#app",
template:"<p>{{msg}}</p>",
data:{
msg:"原数据"
},
beforeUpdate: function () {
console.log("调用了beforeUpdate钩子函数")
},
updated: function () {
console.log("调用了updated钩子函数");
},
});
app.msg = "数据被修改了";
</script>
```
结果:
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516093551.png)
>注意只有写入模板的数据才会被追踪
## 2.6 beforeDestroy和destroyed钩子函数间的生命周期
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516093638.png)
### 2.6.1 beforeDestroy
销毁前执行($destroy方法被调用的时候就会执行,一般在这里善后:清除计时器、清除非指令绑定的事件等等…’)
### 2.6.2 destroyed
销毁后 Dom元素存在只是不再受vue控制,卸载watcher事件监听子组件
# 总结
- beforecreate : 可以在这加个loading事件
- created 在这结束loading还做一些初始数据的获取实现函数自-执行
- mounted 在这发起后端请求,拿回数据,配合路由钩子做一些事情
- beforeDestroy 你确认删除XX吗
- destroyed :当前组件已被删除,清空相关内容

271
Vue/14-前端模块化.md Normal file
View File

@@ -0,0 +1,271 @@
# 1. 为什么要模块化
随着前端项目越来越大团队人数越来越多多人协调开发一个项目成为常态。例如现在小明和小张共同开发一个项目小明定义一个aaa.js小张定义了一个bbb.js。
> aaa.js
```javascript
//小明开发
var name = '小明'
var age = 22
function sum(num1, num2) {
return num1 + num2
}
var flag = true
if (flag) {
console.log(sum(10, 20));
}
```
此时小明的`sum`是没有问题的。
> bbb.js
```javascript
//小红
var name = "小红"
var flag = false
```
此时小明和小红各自用各自的`flag`你变量没问题。
> 但是此时小明又创建了一个mmm.js
```javascript
//小明
if(flag){
console.log("flag是true")
}
```
在index.html页面导入这些js文件
```php+HTML
<script src="aaa.js" ></script>
<script src="bbb.js" ></script>
<script src="ccc.js" ></script>
```
此时小明知道自己在aaa.js中定义的`flag`是`true`认为打印没有问题但是不知道小红的bbb.js中也定义了`flag`为`true`所以mmm.js文件并没有打印出“flag是true”。
> 这就是全局变量同名问题。
# 2. 使用导出全局变量模块解决全局变量同名问题
> aaa.js
```javascript
//模块对象
var moduleA = (function (param) {
//导出对象
var obj = {}
var name = '小明'
var age = 22
function sum(num1, num2) {
return num1 + num2
}
var flag = true
if (flag) {
console.log(sum(10, 20))
}
obj.flag=false
return obj
})()
```
> mmm.js
```javascript
//小明
//使用全局变量moduleA
if(moduleA.flag){
console.log("flag是true")
}
```
这样直接使用aaa.js导出的moduleA变量获取小明自己定义的`flag`。
# 3. CommonJS的模块化实现
CommonJS需要nodeJS的依支持。
> aaa.js
```javascript
var name = '小明'
var age = 22
function sum(num1, num2) {
return num1 + num2
}
var flag = true
if (flag) {
console.log(sum(10, 20))
}
// module.exports = {
// flag : flag,
// sum : sum
// }
//导出对象
module.exports = {
flag,
sum
}
```
使用`module.exports = {}`导出需要的对象。
> mmm.js
```javascript
//导入对象,nodejs语法,需要node支持,从aaa.js取出对象
var {flag,sum} = require("./aaa")
console.log(sum(10,20));
if(flag){
console.log("flag is true");
}
```
使用 `var {flag,sum} = require("./aaa")`获取已经导出的对象中自己所需要的对象。
# 4. ES6的模块化实现
如何实现模块化在html中需要使用`type='module'`属性。
```html
<script src="aaa.js" type="module"></script>
<script src="bbb.js" type="module"></script>
<script src="mmm.js" type="module"></script>
```
此时表示aaa.js是一个单独的模块此模块是有作用域的。如果要使用aaa.js内的变量需要在aaa.js中先导出变量再在需要使用的地方导出变量。
## 4.1 直接导出
###
```javascript
export let name = '小明'
```
> 使用
```javascript
import {name} from './aaa.js'
console.log(name)
```
`./aaa.js`表示aaa.js和mmm.js在同级目录。
如图打印结果。
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516094415.png)
## 4.2 统一导出
```javascript
var age = 22
function sum(num1, num2) {
return num1 + num2
}
var flag = true
if (flag) {
console.log(sum(10, 20))
}
//2.最后统一导出
export {
flag,sum,age
}
```
> 使用`import {name,flag,sum} from './aaa.js'`导入多个变量
```javascript
import {name,flag,sum} from './aaa.js'
console.log(name)
if(flag){
console.log("小明是天才");
}
console.log(sum(20,30));
```
> 使用{}将需要的变量放置进去
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516094621.png)
## 4.3 导出函数/类
> 在aaa.js中添加
```javascript
//3.导出函数/类
export function say(value) {
console.log(value);
}
export class Person{
run(){
console.log("奔跑");
}
}
```
> 在mmm.js中添加
```javascript
import {name,flag,sum,say,Person} from './aaa.js'
console.log(name)
if(flag){
console.log("小明是天才");
}
console.log(sum(20,30));
say('hello')
const p = new Person();
p.run();
```
> 如图
![](https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516094739.png)
## 4.4 默认导入 export default
> 导出
```javascript
export default {
flag,sum,age
}
```
> 导入
```javascript
//4.默认导入 export default
import aaa from './aaa.js'
console.log(aaa.sum(10,110));
```
> 注意:使用默认导出会将所有需要导出的变量打包成一个对象,此时导出一个对象,此时我在`mmm.js`中导入变量时候命名为aaa如果要调用变量需要使用aaa.变量。
## 4.5 统一全部导入
> 使用`import * as aaa from './aaa.js'`统一全部导入
```javascript
// 5.统一全部导入
import * as aaa from './aaa.js'
console.log(aaa.flag);
console.log(aaa.name);
```

1300
Vue/15-webpack.md Normal file

File diff suppressed because it is too large Load Diff

432
Vue/16-VueCLI.md Normal file
View File

@@ -0,0 +1,432 @@
# 1. vue-cli起步
## 1.1 什么是vue-cli
Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供:
- 通过 `@vue/cli` 搭建交互式的项目脚手架。
- 通过 `@vue/cli` + `@vue/cli-service-global` 快速开始零配置原型开发。
- 一个运行时依赖 (`@vue/cli-service`),该依赖:
- 可升级;
- 基于 webpack 构建,并带有合理的默认配置;
- 可以通过项目内的配置文件进行配置;
- 可以通过插件进行扩展。
- 一个丰富的官方插件集合,集成了前端生态中最好的工具。
- 一套完全图形化的创建和管理 Vue.js 项目的用户界面。
Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问题。与此同时,它也为每个工具提供了调整配置的灵活性,无需 eject。
## 1.2 **CLI是什么意思**
- CLI是Command-Line Interface即命令行界面也叫脚手架。
- vue cli 是vue.js官方发布的一个vue.js项目的脚手架
- 使用vue-cli可以快速搭建vue开发环境和对应的webpack配置
## 1.3 vue cli使用
**vue cli使用前提node**
vue cli依赖nodejs环境vue cli就是使用了webpack的模板。
安装vue脚手架现在脚手架版本是vue cli3
```shell
npm install -g @vue/cli
```
如果使用yarn
```bash
yarn global add @vue/cli
```
安装完成后使用命令查看版本是否正确:
```bash
vue --version
```
> 注意安装cli失败
1. 以管理员使用cmd
2. 清空npm-cache缓存
```bash
npm clean cache -force
```
**拉取2.x模板旧版本**
Vue CLI >= 3 和旧版使用了相同的 `vue` 命令,所以 Vue CLI 2 (`vue-cli`) 被覆盖了。如果你仍然需要使用旧版本的 `vue init` 功能,你可以全局安装一个桥接工具:
```bash
npm install -g @vue/cli-init
# `vue init` 的运行效果将会跟 `vue-cli@2.x` 相同
vue init webpack my-project
```
**1.在根目录新建一个文件夹`16-vue-cli`cd到此目录新建一个vue-cli2的工程。**
```bash
cd 16-vue-cli
//全局安装桥接工具
npm install -g @vue/cli-init
//新建一个vue-cli2项目
vue init webpack 01-vuecli2test
```
> 注意如果是创建vue-cli3的项目使用
```bash
vue create 02-vuecli3test
```
2.创建工程选项含义
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517153417.png)
- project name项目名字默认
- project description项目描述
- author作者会默认拉去git的配置
- vue buildvue构建时候使用的模式
- runtime+compiler大多数人使用的可以编译template模板
- runtime-only比compiler模式要少6kb并且效率更高直接使用render函数
- install vue-router是否安装vue路由
- user eslint to lint your code是否使用ES规范
- set up unit tests是否使用unit测试
- setup e2e tests with nightwatch是否使用end 2 end点到点自动化测试
- Should we run `npm install` for you after the project has been created? (recommended)使用npm还是yarn管理工具
等待创建工程成功。
> 注意如果创建工程时候选择了使用ESLint规范又不想使用了需要在config文件夹下的index.js文件中找到useEslint并改成false。
```javascript
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
```
# 2. vue-cli2的目录结构
创建完成后,目录如图所示:
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517153359.png)
其中build和config都是配置相关的文件。
## 2.1 build和config
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517153602.png)
如图所示build中将webpack的配置文件做了分离
- `webpack.base.conf.js`(公共配置)
- `webpack.dev.conf.js`(开发环境)
- `webpack.prod.conf.js`(生产环境)
我们使用的脚本命令配置在`package.json`中。
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517153710.png)
打包构建:
```bash
npm run build
```
如果搭建了本地服务器`webpack-dev-server`,本地开发环境:
```bash
npm run dev
```
此时`npm run build`打包命令相当于使用node 执行build文件夹下面的build.js文件。
> build.js
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517153748.png)
1. 检查dist文件夹是否已经存在存在先删除
2. 如果没有err就使用webpack的配置打包dist文件夹
在生产环境即使用build打包时候使用的是`webpack.prod.conf.js`配置文件。
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517153859.png)
源码中,显然使用了`webpack-merge`插件来合并prod配置文件和公共的配置文件合并成一个配置文件并打包`webpack.dev.conf.js`也是如此操作在开发环境使用的是dev的配置文件。
config文件夹中是build的配置文件中所需的一些变量、对象`webpack.base.conf.js`中引入了`index.js`
```javascript
const config = require('../config')
```
## 2.2 src和static
src源码目录就是我们需要写业务代码的地方。
static是放静态资源的地方static文件夹下的资源会原封不动的打包复制到dist文件夹下。
## 2.3 其他相关文件
### 2.3.1 .babelrc文件
.babelrc是ES代码相关转化配置。
```json
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"]
}
```
1. browsers表示需要适配的浏览器份额大于1%最后两个版本不需要适配ie8及以下版本
2. babel需要的插件
### 2.3.2 .editorconfig文件
.editorconfig是编码配置文件。
```properties
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
```
一般是配置编码代码缩进2空格是否清除空格等。
### 2.3.3 .eslintignore文件
.eslintignore文件忽略一些不规范的代码。
```
/build/
/config/
/dist/
/*.js
```
忽略build、config、dist文件夹和js文件。
### 2.3.4 .gitignore文件
.gitignore是git忽略文件git提交忽略的文件。
### 2.3.5 .postcssrc.js文件
css转化是配置的一些。
### 2.3.6 index.html文件
index.html文件是使用`html-webpack-plugin`插件打包的index.html模板。
### 2.3.7 package.json和package-lock.json
1. package.json(包管理,记录大概安装的版本)
2. package-lock.json(记录真实安装版本)
# 3. runtime-compiler和runtime-only区别
新建两个vuecli2项目
```bash
//新建一个以runtime-compiler模式
vue init webpack 02-runtime-compiler
//新建一个以runtime-only模式
vue init webpack 03-runtime-only
```
两个项目的main.js区别
> runtime-compiler
```javascript
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
```
> runtime-only
```javascript
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
render: h => h(App)
})
```
`render: h => h(App)`
```javascript
render:function(h){
return h(App)
}
```
**compiler编译解析template过程**
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517154504.png)
`vm.options.template`解析成`ast(abstract syntax tree)`抽象语法树,抽象语法树编译成`vm.options.render(functions)`render函数。render函数最终将template解析的ast渲染成虚拟DOM`virtual dom`最终虚拟dom映射到ui上。
**runtime-compiler**
template会被解析 => ast(抽象语法树) => 然后编译成render函数 => 渲染成虚拟DOMvdom=> 真实dom(UI)
**runtime-only**
render => vdom => UI
1.性能更高2.需要代码量更少
> render函数
```javascript
render:function(createElement){
//1.createElement('标签',{标签属性},[''])
return createElement('h2',
{class:'box'},
['Hello World',createElement('button',['按钮'])])
//2.传入组件对象
//return createElement(cpn)
}
```
h就是一个传入的createElement函数.vue文件的template是由vue-template-compiler解析。
将02-runtime-compiler的main.js修改
```javascript
new Vue({
el: '#app',
// components: { App },
// template: '<App/>'
//1.createElement('标签',{标签属性},[''])
render(createElement){
return createElement('h2',
{class:'box'},
['hello vue', createElement('button',['按钮'])])
}
})
```
并把config里面的inedx.js的`useEslint: true`改成false即关掉eslint规范打包项目`npm run dev`,打开浏览器。
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517154721.png)
在修改main.js
```javascript
new Vue({
el: '#app',
// components: { App },
// template: '<App/>'
//1.createElement('标签',{标签属性},[''])
render(createElement){
// return createElement('h2',
// {class:'box'},
// ['hello vue', createElement('button',['按钮'])])
//2.传入组件
return createElement(App)
}
```
再次打包发现App组件被渲染了。
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517154741.png)
# 4. vue-cli3
## 4.1 vue-cli3起步
**vue-cli3与2版本区别**
- vue-cli3基于webpack4打造vue-cli2是基于webpack3
- vue-cli3的设计原则是"0配置"移除了配置文件build和config等
- vue-cli3提供`vue ui`的命令,提供了可视化配置
- 移除了static文件夹新增了public文件夹并将index.html移入了public文件夹
**创建vue-cli3项目**
```bash
vue create 04-vuecli3test
```
**目录结构:**
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517155055.png)
- public 类似 static文件夹里面的资源会原封不动的打包
- src源码文件夹
使用`npm run serve`运行服务器打开浏览器输入http://localhost:8080/
打开src下的main.js
```javascript
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
```
`Vue.config.productionTip = false`构建信息是否显示
如果vue实例有el选项vue内部会自动给你执行`$mount('#app')`,如果没有需要自己执行。
## 4.2 vue-cli3的配置
在创建vue-cli3项目的时候可以使用`vue ui`命令进入图形化界面创建项目,可以以可视化的方式创建项目,并配置项。
vue-cli3配置被隐藏起来了可以在`node_modules`文件夹中找到`@vue`模块,打开其中的`cli-service`文件夹下的`webpack.config.js`文件。
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517155115.png)
再次打开当前目录下的`lib`文件夹,发现配置文件`service.js`并导入了许多模块来自与lib下面的config、util等模块
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200517155137.png)
**如何要自定义配置文件**
在项目根目录下新建一个`vue.config.js`配置文件,必须为`vue.config.js`vue-cli3会自动扫描此文件在此文件中修改配置文件。
```javascript
//在module.exports中修改配置
module.exports = {
}
```

1155
Vue/17-Vue-Router.md Normal file

File diff suppressed because it is too large Load Diff

333
Vue/18-Promise.md Normal file
View File

@@ -0,0 +1,333 @@
# 1. 什么是Promies
**简单说Promise是异步编程的一种解决方案。**
Promise是ES6中的特性。
> 什么是异步操作?
网络请求中对端服务器处理需要时间信息传递过程需要时间不像我们本地调用一个js加法函数一样直接获得`1+1=2`的结果。这里网络请求不是同步的有时延,不能立即得到结果。
> 如何处理异步事件?
对于网络请求这种一般会使用回调函数在服务端传给我数据成功后调用回调函数。例如ajax调用。
```js
$.ajax({
success:function(){
...
}
})
```
> 如果碰到嵌套网络请求,例如第一次网络请求成功后回调函数再次发送网络请求,这种代码就会让人很难受。
```json
$.ajax({
success:function(){
$.ajax({
...
})
}
})
```
如果还需要再次网络请求,那么又要嵌套一层,这样的代码层次不分明很难读,也容易出问题。
# 2. Promise的基本使用
## 2.1 什么时候使用Promise
解决异步请求冗余这样的问题promise就是用于封装异步请求的。
## 2.2 Promise对象
```js
new Promise((resolve, reject) => {})
```
Promise对象的参数是一个函数`(resolve, reject) => {}`这个函数又有2个参数分别是`resolve``reject`。这2个参数本身也是函数是不是有点绕后面还有回调函数`then(func)`的参数也是一个函数。
> 模拟定时器的异步事件
用定时器模拟网络请求定时一秒为网络请求事件用console.log()表示需要执行的代码。
```js
//1.使用setTimeout模拟嵌套的三次网络请求
setTimeout(() => {//第一次请求
console.log("hello world")//第一次处理代码
setTimeout(() => {//第二次请求
console.log("hello vuejs")//第二次处理代码
setTimeout(() => {//第三次请求
console.log("hello java")//第三次处理代码
}, 1000)
}, 1000)
}, 1000)
```
一层套一层,看起是不是很绕。
使用promise来处理异步操作
```js
//参数 -> 函数
// resolve和reject本身也是函数
//then()的参数也是一个函数
new Promise((resolve, reject) => {
setTimeout(() => {//第一次网络请求
resolve()
}, 1000)
}).then(() => {
console.log("hello world")//第一次处理代码
return new Promise((resolve, reject) => {
setTimeout(() => {//第二次网络请求
resolve()
}, 1000).then(() => {
console.log("hello vuejs")//第二次处理代码
return new Promise((resolve, reject) => {
setTimeout(() => {//第三次网络请求
resolve()
}, 1000)
}).then(() => {
console.log("hello java")//第三次处理代码
})
})
})
})
```
是不是觉得代码还要更复杂了?仔细看看第一个如果使用了多个就找不到对应关系了。相反第二个流程就很清楚,调用`resolve()`就能跳转到`then()`方法就能执行处理代码,`then()`回调的返回值又是一个`Promise`对象。层次很明显,只要是`then()`必然就是执行处理代码如果还有嵌套必然就是返回一个Promise对象这样调用就像java中的StringBuffer的append()方法一样,链式调用。
```js
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000).then(success => {
console.log(success)
})
})
```
setTimeout()模拟的是网络请求而then()执行的是网络请求后的代码这就将网络请求和请求得到响应后的操作分离了每个地方干自己的事情。在resolve中传参了那么在then()方法中的参数就有这个参数例如data。
**网络请求中也会有失败情况?例如网络堵塞。**
如何处理失败情况此时就要用到reject()
```js
new Promise((resolve, reject) => {
setTimeout(() => {
reject('error message')
}, 1000).catch(error => {
console.log(error)
})
})
```
此时`reject(error)``catch()`方法捕获到`reject()`中的error。
> 合起来
```js
new Promise((resolve, reject) => {
setTimeout(() => {
// 成功的时候调用resolve()
// resolve('hello world')
// 失败的时候调用reject()
reject('error message')
}, 1000).then(success => {
console.log(success)
}).catch(error => {
console.log(error)
})
})
```
拿ajax来举例子
```js
new Promise((resolve, reject) => {
$.ajax({
success:function(){
// 成功的时候调用resolve()
// resolve('hello world')
// 失败的时候调用reject()
reject('error message')
}
}).then(success => {
console.log(success)
}).catch(error => {
console.log(error)
})
})
```
# 3. Promise的三种状态
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200518093523.png)
- pending:等待状态,比如正在进行的网络请求还未响应,或者定时器还没有到时间
- fulfill:满足状态当我们主动回调了resolve函数就处于满足状态并会回调then()
- reject:拒绝状态当我们主动回调reject函数就处于该状态并且会回调catch()
# 4. Promies的链式调用
1. 网络请求响应结果为 hello ,打印hello
2. 处理: hello world ,打印hello world
3. 处理: hello worldvuejs ,打印hello worldvuejs
```js
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 1000)
}).then(res => {
console.log(res)//打印hello
return new Promise(resolve => {
resolve(res + ' world')
}).then(res => {
console.log(res)//打印hello world
return new Promise(resolve => {
resolve(res + ',vuejs')
}).then(res => {
console.log(res)//打印hello world,vuejs
})
})
})
```
链式调用就是`then()`方法的返回值返回一个Promise对象继续调用`then()`,此外还有简写`Promise.resolve()`
```js
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 1000)
}).then(res => {
console.log(res)//打印hello
return Promise.resolve(res + ' world')
}).then(res => {
console.log(res)//打印hello world
return Promise.resolve(res + ',vuejs')
}).then(res => {
console.log(res)//打印hello world,vuejs
})
```
还可以直接省略掉`Promise.resolve()`
```js
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 1000)
}).then(res => {
console.log(res)//打印hello
return res + ' world'
}).then(res => {
console.log(res)//打印hello world
return res + ',vuejs'
}).then(res => {
console.log(res)//打印hello world,vuejs
})
```
如果中途发生异常,可以通过`catch()`捕获异常
```js
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 1000)
}).then(res => {
console.log(res)//打印hello
return res + ' world'
}).then(res => {
console.log(res)
// return Promise.reject('error message')//发生异常
throw 'error message' //抛出异常
}).then(res => {
console.log(res)//打印hello world,vuejs
}).catch(error => {
console.log(error)
})
```
也可以通过`throw`抛出异常类似java
```js
throw 'error message' //抛出异常
```
# 5. Promies的all使用
有这样一个情况一个业务需要请求2个地方A和B的数据只有A和B的数据都拿到才能走下一步。
> ajax实现
```js
$.ajax({
...//结果A
resultA = true
callback()
})
$.ajax({
...//结果B
resultB = true
callback()
})
//回调函数
function callback(){
if(resultA&&resultB){
...
}
}
```
由于不知道网络请求A和网络请求B哪个先返回结果所以需要定义一个函数只有2个请求都返回数据才回调成功。
> Promise实现
```js
Promise.all([
new Promise((resolve, resjct) => {
$.ajax({
url: 'url1',
success: function (data) {
resolve(data)
}
})
}),
new Promise((resolve, resjct) => {
$.ajax({
url: 'url2',
success: function (data) {
resolve(data)
}
})
}).then(results => {
console.log(results)
})
])
```
上面是伪代码只是包装了ajaxajaxA和ajaxB的结果都放在`resolve()`Promise将其放在`results`中了,使用`setTimeout`模拟。
```js
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => {// 请求A
resolve('结果A')
}, 1000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {// 请求B
resolve('结果B')
}, 1000)
})
]).then(results => {
console.log(results)
})
```

1534
Vue/19-Vuex.md Normal file

File diff suppressed because it is too large Load Diff

625
Vue/20-Axios的封装.md Normal file
View File

@@ -0,0 +1,625 @@
# 1. Axios简介
## 1.1 什么是Axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
## 1.2 特性
- 浏览器端发起XMLHttpRequests请求
- node端发起http请求
- 支持Promise API
- 监听请求和返回
- 转化请求和返回
- 取消请求
- 自动转化json数据
- 客户端支持抵御
# 2. Axios的使用和配置
## 2.1 安装
```shell
npm install axios --save
```
或者使用cdn
```javascript
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
```
## 2.2 基本使用
### 2.2.1 Get请求
```javascript
axios.get('/user', {
params: {
name: 'krislin'
}
}).then(function (response) {
console.log(response);
}).catch(function (error) {
console.log(error)
}
```
### 2.2.2 Post请求
```javascript
axios.post('/user',{
name:'krislin',
address:'china'
})
.then(function(response){
console.log(response);
})
.catch(function(error){
console.log(error);
});
```
### 2.2.3 并发操作
```javascript
function getUserAccount(){
return axios.get('/user/12345');
}
function getUserPermissions(){
return axios.get('/user/12345/permissions');
}
axios.all([getUerAccount(),getUserPermissions()])
.then(axios.spread(function(acc,pers){
//两个请求现在都完成
}));
```
## 2.3 请求API配置
axios 能够在进行请求时进行一些设置,具体如下:
```javascript
axios({
method:'post',
url:'/user/12345',
data:{
name:'krislin',
address:'china'
}
});
```
## 2.4 请求设置
请求配置中只有url是必须的如果没有指明的话默认是Get请求
```javascript
{
//`url`是服务器链接,用来请求用
url:'/user',
//`method`是发起请求时的请求方法
method:`get`,
//`baseURL`如果`url`不是绝对地址,那么将会加在其前面。
//当axios使用相对地址时这个设置非常方便
//在其实例中的方法
baseURL:'http://some-domain.com/api/',
//`transformRequest`允许请求的数据在传到服务器之前进行转化。
//这个也支持`PUT`,`GET`,`PATCH`方法。
//数组中的最后一个函数必须返回一个字符串,一个`ArrayBuffer`,或者`Stream`
transformRequest:[function(data){
//依自己的需求对请求数据进行处理
return data;
}],
//`transformResponse`允许返回的数据传入then/catch之前进行处理
transformResponse:[function(data){
//依需要对数据进行处理
return data;
}],
//`headers`是自定义的要被发送的头信息
headers:{'X-Requested-with':'XMLHttpRequest'},
//`params`是请求连接中的请求参数必须是一个纯对象或者URLSearchParams对象
params:{
ID:12345
},
//`paramsSerializer`是一个可选的函数,是用来序列化参数
//例如https://ww.npmjs.com/package/qs,http://api.jquery.com/jquery.param/)
paramsSerializer: function(params){
return Qs.stringify(params,{arrayFormat:'brackets'})
},
//`data`是请求提需要设置的数据
//只适用于应用的'PUT','POST','PATCH',请求方法
//当没有设置`transformRequest`时,必须是以下其中之一的类型(不可重复?):
//-string,plain object,ArrayBuffer,ArrayBufferView,URLSearchParams
//-仅浏览器FormData,File,Blob
//-仅NodeStream
data:{
firstName:'fred'
},
//`timeout`定义请求的时间,单位是毫秒。
//如果请求的时间超过这个设定时间,请求将会停止。
timeout:1000,
//`withCredentials`表明是否跨域请求,
//应该是用证书
withCredentials:false //默认值
//`adapter`适配器,允许自定义处理请求,这会使测试更简单。
//返回一个promise并且提供验证返回查看[response docs](#response-api)
adapter:function(config){
/*...*/
},
//`auth`表明HTTP基础的认证应该被使用并且提供证书。
//这个会设置一个`authorization` 头header并且覆盖你在header设置的Authorization头信息。
auth:{
username:'janedoe',
password:'s00pers3cret'
},
//`responsetype`表明服务器返回的数据类型,这些类型的设置应该是
//'arraybuffer','blob','document','json','text',stream'
responsetype:'json',
//`xsrfHeaderName` 是http头header的名字并且该头携带xsrf的值
xrsfHeadername:'X-XSRF-TOKEN'//默认值
//`onUploadProgress`允许处理上传过程的事件
onUploadProgress: function(progressEvent){
//本地过程事件发生时想做的事
},
//`onDownloadProgress`允许处理下载过程的事件
onDownloadProgress: function(progressEvent){
//下载过程中想做的事
},
//`maxContentLength` 定义http返回内容的最大容量
maxContentLength: 2000,
//`validateStatus` 定义promise的resolve和reject。
//http返回状态码如果`validateStatus`返回true或者设置成null/undefinedpromise将会接受其他的promise将会拒绝。
validateStatus: function(status){
return status >= 200 && stauts < 300;//默认
},
//`httpAgent` 和 `httpsAgent`当产生一个http或者https请求时分别定义一个自定义的代理在nodejs中。
//这个允许设置一些选选个,像是`keepAlive`--这个在默认中是没有开启的。
httpAgent: new http.Agent({keepAlive:treu}),
httpsAgent: new https.Agent({keepAlive:true}),
//`proxy`定义服务器的主机名字和端口号。
//`auth`表明HTTP基本认证应该跟`proxy`相连接,并且提供证书。
//这个将设置一个'Proxy-Authorization'头(header),覆盖原先自定义的。
proxy:{
host:127.0.0.1,
port:9000,
auth:{
username:'cdd',
password:'123456'
}
},
//`cancelTaken` 定义一个取消,能够用来取消请求
//(查看 下面的Cancellation 的详细部分)
cancelToke: new CancelToken(function(cancel){
})
}
```
## 2.5 响应数据Response
一个请求的返回包含以下信息
```javascript
{
//`data`是服务器的提供的回复(相对于请求)
data{},
//`status`是服务器返回的http状态码
status:200,
//`statusText`是服务器返回的http状态信息
statusText: 'ok',
//`headers`是服务器返回中携带的headers
headers:{},
//`config`是对axios进行的设置目的是为了请求request
config:{}
}
```
## 2.6 拦截器Interceptors
你可以在 请求 或者 返回 被 then 或者 catch 处理之前对他们进行拦截。
添加拦截器:
```javascript
//添加一个请求拦截器
axios.interceptors.request.use(function(config){
//在请求发送之前做一些事
return config;
},function(error){
//当出现请求错误是做一些事
return Promise.reject(error);
});
//添加一个返回拦截器
axios.interceptors.response.use(function(response){
//对返回的数据进行一些处理
return response;
},function(error){
//对返回的错误进行一些处理
return Promise.reject(error);
});
```
移除拦截器:
```javascript
var myInterceptor = axios.interceptors.request.use(function(){/*...*/});
axios.interceptors.rquest.eject(myInterceptor);
```
# 3. 跨域
因为在Vue的开发阶段基本都是用webpack打包编译需要node环境本地运行因而运行的域名为本地的localhost,这个时候调用后端接口就涉及到跨域的问题了。
## 3.1 ProxyTable
vue 的 proxyTable 是用于开发阶段配置跨域的工具可以同时配置多个后台服务器跨越请求接口其真正依赖的npm包是 [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware), 在GitHub上拥有更丰富的配置可以按需配置
**在不考虑后端CROS跨域方案的情况下前端配置ProxyTable实现跨域请求的用法如下:**
### 1. 找到 **config/index.js** 文件中的 `proxyTable:{}` 将其修改
```javascript
proxyTable: {
'/api': {
target: 'https://tasst.sinoxk.cn', // 这个是你要代理的地址(开发阶段接口地址)
changeOrigin: true, //跨域需要加上这个
pathRewrite: {
'^/api': '' //可以理解为用 / api代表target里的地址
}
}
}
```
proxyTable支持配置多个接口:
```javascript
proxyTable: {
'/api': {
target: 'https://tasst.sinoxk.cn', // 这个是你要代理的地址(开发阶段接口地址)
changeOrigin: true, //跨域需要加上这个
pathRewrite: {
'^/api': '' //可以理解为用 / api代表target里的地址
}
},
'/service': {
target: 'https://tasst.sinoxk.cn', // 这个是你要代理的地址(开发阶段接口地址)
changeOrigin: true, //跨域需要加上这个
pathRewrite: {
'^/service': '' //可以理解为用 / api代表target里的地址
}
}
}
```
### 2. 找到 **config/dev.env.js** 文件,配置`BASE_URL`
```javascript
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
BASE_URL:'"/api"' //开发环境域名
})
```
### 3. 找到 **config/prod.env.js** 文件,配置`BASE_URL`
```javascript
module.exports = {
NODE_ENV: '"production"',
BASE_URL:'"https://asst.sinoxk.com"' //生产环境保持正式域名
}
```
### 4. 配置 **axios** 的基础域名
```properties
axios.defaults.baseURL = process.env.BASE_URL
```
**修改完所有的配置文件后,要注意,需要重启下环境**
```shell
npm run dev / npm run start
```
# 4. 封装
在日常项目开发过程中在和后台交互获取数据的时候我们都需要使用到网络库通常在vue的项目中 ,使用的是 **axios** 库 ,在此基于自身项目业务,做一个二次封装。
## 4.1 条件准备
在UI轻提示组件上选定的是 **vant** 库中的 **Toast** 组件([Vant文档](https://youzan.github.io/vant/#/zh-CN/intro)),可按实际需要选定具体要使用的UI框架
安装:
```shell
npm install vant --save
```
数据序列化,如果有实际需要的项目,可以使用`qs`,在这里做一个简单的介绍
安装:
```shell
npm install qs --save
```
**qs.stringify和JSON.stringify的使用和区别**
qs.stringify()将对象 序列化成URL的形式以&进行拼接
JSON.stringify 是将对象转化成一个json字符串的形式
**用法:**
```javascript
var a = {name:'xiaoming',age:10}
qs.stringify(a); //log: 'name=xiaoming&age=10'
JSON.stringify(a) //log: '{"name":"hehe","age":10}'
```
基于底层配置和业务接口分离在src目录中会新建文件夹 **httpServer**,同时新建立 **ajax.js** 和 **api.js** 文件
```
ajax.js: axios的二次封装作为基础网络库添加基础的配置
```
```
api.js: 管理项目实际业务基础接口的输出,以及返回响应数据的处理
```
在日常项目模块中基于多人开发当然可以在api.js的基础上可以根据功能模块实现业务拓展延伸比如
```javascript
小明负责list模块业务
新建api-list.js并导入api.js ....
//api-list.js文件中:
import api from './api'
export default {
getList(url,params){
api.get(url,params)
}
}
```
对于个别项目,可能存在多个域名配置的情况下, 可以重新建立 **base.js** , 来管理多个接口域名
**base.js:**
```javascript
/**
* 接口域名的管理
*/
const base = {
sq: 'https://xxxx111111.com/api/v1',
bd: 'http://xxxxx22222.com/api'
}
export default base;
```
## 4.2 axios封装(单域名)
**src/main.js文件:**
```javascript
import Vue from 'vue'
import App from './App'
import router from './router'
import Api from './httpServer/api'
//挂载到vue的全局属性上
Vue.prototype.$https = Api
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
```
**src/httpServer/ajax.js文件:**
```javascript
import axios from 'axios'
import {Toast} from 'vant'
const ajax = axios.create({
timeout:60000,
baseURL:process.env.BASE_URL //基础域名
})
/**
* 请求拦截器
* 每次请求前如果存在token则在请求头中携带token
*/
ajax.interceptors.request.use(
config => {
//判断token(根据实际情况拦截)
return config;
},
error => Promise.error(error)
)
/**
* 响应拦截器
*/
ajax.interceptors.response.use(
// 请求成功
res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res),
error => {
const {response} = error;
if (response) { // 请求已发出但是不在2xx的范围
Toast({message: response.message});
return Promise.reject(response);
} else {
// 处理断网的情况
// eg:请求超时或断网时更新state的network状态
// network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
// 关于断网组件中的刷新重新获取数据,会在断网组件中说明
Toast({message: '网络开小差,请稍后重试'});
}
}
)
export default ajax;
```
对于**process.env.BASE_URL**的配置,在开发环境中,需要以代理的方式进行访问:
```javascript
//config/dev.env.js
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
BASE_URL:'"/api"' //对api进行处理
})
```
```javascript
//config/prod.env.js
'use strict'
module.exports = {
NODE_ENV: '"production"',
BASE_URL:'"https://www.xxx.com"' //生产环境不需要处理
}
```
```javascript
//config/index.js
...
proxyTable: {
'/api': {
target: 'https://tasst.sinoxk.cn',//后端接口地址
changeOrigin: true,//是否允许跨越
pathRewrite: {
'^/api': '',//重写(接口地址带api会被替换)
},
}
},
...
```
**src/httpServer/api.js文件:**
```javascript
import ajax from './ajax'
import {Toast} from 'vant'
/**
* 业务接口成功或者失败的情况处理
*
*/
const handleResponse = (res, success, failure) => {
switch (res.code) {
case 200: //成功
success && success(res.data);
break;
case 401: //登录token失效
break;
default:
if (failure) {
failure(res);
} else {
Toast({message:res.msg || '请求失败,请稍后重试!'});
}
break;
}
}
export default {
get: function (url, params, success, failure) {
ajax.get(url, {
params: params
}).then(res => {
if (res.status == 200) {
handleResponse(res.data.data, success, failure);
}
});
},
post: function (url, params, success, failure) {
ajax.post(url, params).then(res => {
if (res.status == 200) {
handleResponse(res.data.data, success, failure);
}
})
}
}
```
在**src/components/HelloWorld.vue**文件中使用:
```javascript
<script>
export default {
name: 'HelloWorld',
data() {
return {
msg: 'Welcome to Your Vue.js App'
}
},
created() {
//请求接口数据
this.$https.get('/xkzx/member/service', {
pageNum: 1,
pageSize: 10
}, function (data) { //成功
console.log(data);
}, function (res) { //失败
})
}
}
</script>
```

311
Vue/README.md Normal file
View File

@@ -0,0 +1,311 @@
# [01-ES6补充](01-ES6补充.md)
- [1. 块级作用域](01-ES6补充.md/#1-块级作用域)
- [1.1. 什么是变量作用域](01-ES6补充.md/#11-什么是变量作用域)
- [1.2. 没有块级作用域造成的问题](01-ES6补充.md/#12-没有块级作用域造成的问题)
- [if块级](01-ES6补充.md/#if块级)
- [for块级](01-ES6补充.md/#for块级)
- [2. const的使用](01-ES6补充.md/#2-const的使用)
- [3. ES6的增强写法](01-ES6补充.md/#3-es6的增强写法)
- [3.1. ES6的对象属性增强型写法](01-ES6补充.md/#31-es6的对象属性增强型写法)
- [3.2 ES6对象的函数增强型写法](01-ES6补充.md/#32es6对象的函数增强型写法)
- [4. 箭头函数](#4箭头函数)
- [4.1 箭头函数的参数和返回值](01-ES6补充.md/#41箭头函数的参数和返回值)
- [4.1.1 参数问题](01-ES6补充.md/#411参数问题)
- [4.1.2 函数内部](01-ES6补充.md/#412函数内部)
- [4.3 箭头函数的this使用](01-ES6补充.md/#43箭头函数的this使用)
- [5. 高阶函数](01-ES6补充.md/#5-高阶函数)
- [5.1 filter过滤函数](01-ES6补充.md/#51filter过滤函数)
- [5.2 map高阶函数](01-ES6补充.md/#52map高阶函数)
- [5.3 reduce高阶函数](01-ES6补充.md/#53reduce高阶函数)
- [5.4综合使用](01-ES6补充.md/#54综合使用)
# [02-HelloVue](02-HelloVue.md)
- [1. HelloVuejs](02-HelloVue.md/#1-hellovuejs)
- [1.1. 命令式编程](02-HelloVue.md/#11-命令式编程)
- [1.2 . 声明式编程](02-HelloVue.md/#12--声明式编程)
- [2. vue列表的展示v-for](02-HelloVue.md/#2-vue列表的展示v-for)
- [3. vue案例-计数器](02-HelloVue.md/#3-vue案例-计数器)
# [03-插值操作](03-插值操作.md)
- [1. Mustache语法](03-插值操作.md/#1-mustache语法)
- [2. v-once](03-插值操作.md/#2-v-once)
- [3. v-html](03-插值操作.md/#3-v-html)
- [4. v-text](03-插值操作.md/#4-v-text)
- [5. v-pre](03-插值操作.md/#5-v-pre)
- [6. v-cloak](03-插值操作.md/#6-v-cloak)
# [04-动态绑定属性](04-动态绑定属性.md)
- [1. v-bind的基本使用](04-动态绑定属性.md/#1-v-bind的基本使用)
- [2. v-bind动态绑定class](04-动态绑定属性.md/#2-v-bind动态绑定class)
- [2.1. v-bind动态绑定class(对象语法)](04-动态绑定属性.md/#21-v-bind动态绑定class对象语法)
- [2.2. v-bind动态绑定class(数组用法)](04-动态绑定属性.md/#22-v-bind动态绑定class数组用法)
- [3. v-for和v-bind结合](04-动态绑定属性.md/#3-v-for和v-bind结合)
- [4. v-bind动态绑定style](04-动态绑定属性.md/#4-v-bind动态绑定style)
- [4.1 v-bind动态绑定style(对象语法)](04-动态绑定属性.md/#41-v-bind动态绑定style对象语法)
- [4.2 v-bind动态绑定style(数组语法)](04-动态绑定属性.md/#42-v-bind动态绑定style数组语法)
# [05-计算属性与侦听器](05-计算属性与侦听器.md)
- [1. 计算属性的基本使用](05-计算属性与侦听器.md/#1-计算属性的基本使用)
- [2. 计算属性的复杂使用](05-计算属性与侦听器.md/#2-计算属性的复杂使用)
- [3. 计算属性的setter和getter](05-计算属性与侦听器.md/#3-计算属性的setter和getter)
- [4. 计算属性和methods的对比](05-计算属性与侦听器.md/#4-计算属性和methods的对比)
- [5. Vue计算属性与侦听器总结](05-计算属性与侦听器.md/#5-vue计算属性与侦听器总结)
# [06-事件监听](06-事件监听.md)
- [1. v-on的基本使用](06-事件监听.md/#1-v-on的基本使用)
- [2. v-on的参数传递](06-事件监听.md/#2-v-on的参数传递)
- [3. v-on的修饰词](06-事件监听.md/#3-v-on的修饰词)
# [07-条件判断](07-条件判断.md)
- [1. v-if、v-else、v-elseif](07-条件判断.md/#1-v-ifv-elsev-elseif)
- [2. v-if的demo](07-条件判断.md/#2-v-if的demo)
- [3. v-show](07-条件判断.md/#3-v-show)
# [08-循环遍历](08-循环遍历.md)
- [1. v-for遍历数组](08-循环遍历.md/#1-v-for遍历数组)
- [2. v-for遍历对象](08-循环遍历.md/#2-v-for遍历对象)
- [3. v-for使用key](08-循环遍历.md/#3-v-for使用key)
- [4. 数组的响应方式](08-循环遍历.md/#4-数组的响应方式)
- [5. 综合练习](08-循环遍历.md/#5-综合练习)
# [09-综合练习](09-综合练习.md)
- [1. 目录结构](09-综合练习.md/#1-目录结构)
- [2. index.html](09-综合练习.md/#2-indexhtml)
- [3.main.js](09-综合练习.md/#3mainjs)
- [4. style.css](09-综合练习.md/#4-stylecss)
- [filter、map、reduce](09-综合练习.md/#filtermapreduce)
# [10-v-model](10-v-model.md)
- [1. v-model的基本使用](10-v-model.md/#1-v-model的基本使用)
- [2. v-model的原理](10-v-model.md/#2-v-model的原理)
- [3. v-model结合radio类型使用](10-v-model.md/#3-v-model结合radio类型使用)
- [4. v-model结合checkbox类型使用](10-v-model.md/#4-v-model结合checkbox类型使用)
- [5. v-model结合select](10-v-model.md/#5-v-model结合select)
- [6. v-model的修饰符的使用](10-v-model.md/#6-v-model的修饰符的使用)
# [11-组件化开发](11-组件化开发.md)
- [1. 组件的基本使用](11-组件化开发.md/#1-组件的基本使用)
- [1.1 创建组件构造器对象](11-组件化开发.md/#11-创建组件构造器对象)
- [1.2 注册组件](11-组件化开发.md/#12-注册组件)
- [1.3 使用组件](11-组件化开发.md/#13-使用组件)
- [2. 全局组件和局部组件](11-组件化开发.md/#2-全局组件和局部组件)
- [2.1 全局组件](11-组件化开发.md/#21-全局组件)
- [2.2 局部组件](11-组件化开发.md/#22-局部组件)
- [3. 父组件和子组件的区别](11-组件化开发.md/#3-父组件和子组件的区别)
- [4. 注册组件的语法糖](11-组件化开发.md/#4-注册组件的语法糖)
- [5. 组件模板的分离写法](11-组件化开发.md/#5-组件模板的分离写法)
- [5.1 script标签](11-组件化开发.md/#51-script标签)
- [5.2 template标签](11-组件化开发.md/#52-template标签)
- [6. 组件的数据](11-组件化开发.md/#6-组件的数据)
- [6.1 存放问题](11-组件化开发.md/#61-存放问题)
- [6.2 组件的data为什么必须要是函数](11-组件化开发.md/#62-组件的data为什么必须要是函数)
- [7. 父组件给子组件传递数据](11-组件化开发.md/#7-父组件给子组件传递数据)
- [7.1 使用`props`属性,父组件向子组件传递数据](11-组件化开发.md/#71-使用props属性父组件向子组件传递数据)
- [7.2 props属性使用](11-组件化开发.md/#72-props属性使用)
- [8. 组件通信](11-组件化开发.md/#8-组件通信)
- [8.1 父传子props的驼峰标识](11-组件化开发.md/#81-父传子props的驼峰标识)
- [8.2 子传父`$emit`](11-组件化开发.md/#82-子传父emit)
- [8.3 父子组件通信案例](11-组件化开发.md/#83-父子组件通信案例)
- [9. 父访问子children-ref](11-组件化开发.md/#9-父访问子children-ref)
# [12-组件化高级](12-组件化高级.md)
- [1. slot-插槽的基本使用](12-组件化高级.md/#1-slot-插槽的基本使用)
- [2. slot-具名插槽的使用](12-组件化高级.md/#2-slot-具名插槽的使用)
- [3. 编译的作用域](12-组件化高级.md/#3-编译的作用域)
- [4. 作用域插槽案例](12-组件化高级.md/#4-作用域插槽案例)
# [13-Vue实例的生命周期](13-Vue实例的生命周期.md)
- [1. 生命周期图](13-Vue实例的生命周期.md/#1-生命周期图)
- [2. 再探究](13-Vue实例的生命周期.md/#2-再探究)
- [2.1 beforeCreate之前](13-Vue实例的生命周期.md/#21-beforecreate之前)
- [2.2 beforeCreate和created钩子函数间的生命周期](13-Vue实例的生命周期.md/#22-beforecreate和created钩子函数间的生命周期)
- [2.3 created钩子函数和beforeMount间的生命周期](13-Vue实例的生命周期.md/#23-created钩子函数和beforemount间的生命周期)
- [2.3.1 el选项对生命周期影响](13-Vue实例的生命周期.md/#231-el选项对生命周期影响)
- [2.3.2 template](13-Vue实例的生命周期.md/#232-template)
- [2.4 beforeMount和mounted钩子函数间的生命周期](13-Vue实例的生命周期.md/#24-beforemount和mounted钩子函数间的生命周期)
- [2.5 beforeUpdate钩子函数和updated钩子函数间的生命周期](13-Vue实例的生命周期.md/#25-beforeupdate钩子函数和updated钩子函数间的生命周期)
- [2.6 beforeDestroy和destroyed钩子函数间的生命周期](13-Vue实例的生命周期.md/#26-beforedestroy和destroyed钩子函数间的生命周期)
- [2.6.1 beforeDestroy](13-Vue实例的生命周期.md/#261-beforedestroy)
- [2.6.2 destroyed](13-Vue实例的生命周期.md/#262-destroyed)
- [总结](13-Vue实例的生命周期.md/#总结)
# [14-前端模块化](14-前端模块化.md)
- [1. 为什么要模块化](14-前端模块化.md/#1-为什么要模块化)
- [2. 使用导出全局变量模块解决全局变量同名问题](14-前端模块化.md/#2-使用导出全局变量模块解决全局变量同名问题)
- [3. CommonJS的模块化实现](14-前端模块化.md/#3-commonjs的模块化实现)
- [4. ES6的模块化实现](14-前端模块化.md/#4-es6的模块化实现)
- [4.1 直接导出](14-前端模块化.md/#41-直接导出)
- [](#)
- [4.2 统一导出](14-前端模块化.md/#42-统一导出)
- [4.3 导出函数/类](14-前端模块化.md/#43-导出函数类)
- [4.4 默认导入 export default](14-前端模块化.md/#44-默认导入-export-default)
- [4.5 统一全部导入](14-前端模块化.md/#45-统一全部导入)
# [15-webpack](15-webpack.md)
- [1. webpack起步](15-webpack.md/#1-webpack起步)
- [1.1 什么是webpack](15-webpack.md/#11-什么是webpack)
- [1.2 webpack的安装](15-webpack.md/#12-webpack的安装)
- [1.3 起步](15-webpack.md/#13-起步)
- [**目录结构**](15-webpack.md/#目录结构)
- [**1.新建入口js文件`main.js`和`mathUtils.js``main.js`依赖`mathUtils.js`。**](15-webpack.md/#1新建入口js文件mainjs和mathutilsjsmainjs依赖mathutilsjs)
- [**2.使用webpack命令打包js文件**](15-webpack.md/#2使用webpack命令打包js文件)
- [**3.新建一个index.html文件导入bundle.js**](15-webpack.md/#3新建一个indexhtml文件导入bundlejs)
- [**4.新建一个`info.js`使用ES6的语法导出**](15-webpack.md/#4新建一个infojs使用es6的语法导出)
- [**5.打开index.html测试**](15-webpack.md/#5打开indexhtml测试)
- [2. webpack的配置](15-webpack.md/#2-webpack的配置)
- [2.1 基本配置](15-webpack.md/#21-基本配置)
- [**1.在根目录下新建一个`webpack.config.js`**](15-webpack.md/#1在根目录下新建一个webpackconfigjs)
- [**2.在根目录执行`npm init`初始化node包因为配置文件中用到了node的path包**](15-webpack.md/#2在根目录执行npm-init初始化node包因为配置文件中用到了node的path包)
- [**3.使用webpack打包**](15-webpack.md/#3使用webpack打包)
- [**4.使用自定义脚本script启动**](15-webpack.md/#4使用自定义脚本script启动)
- [2.2 全局安装和局部安装](15-webpack.md/#22-全局安装和局部安装)
- [3. webpack的loader](15-webpack.md/#3-webpack的loader)
- [3.1 什么是loader](15-webpack.md/#31-什么是loader)
- [3.2 CSS文件处理](15-webpack.md/#32-css文件处理)
- [3.3 less文件处理](15-webpack.md/#33-less文件处理)
- [3.4 图片文件的处理](15-webpack.md/#34-图片文件的处理)
- [3.5 ES6语法处理](15-webpack.md/#35-es6语法处理)
- [4. webpack的vue](15-webpack.md/#4-webpack的vue)
- [4.1 简单安装使用vue](15-webpack.md/#41-简单安装使用vue)
- [4.2 如何分步抽取实现vue模块](15-webpack.md/#42-如何分步抽取实现vue模块)
- [5. webpack的plugin](15-webpack.md/#5-webpack的plugin)
- [5.1 添加版权的Plugin](15-webpack.md/#51-添加版权的plugin)
- [5.2 打包html的plugin](15-webpack.md/#52-打包html的plugin)
- [5.3压缩打包代码插件](15-webpack.md/#53压缩打包代码插件)
- [6. webpack搭建本地服务器](15-webpack.md/#6-webpack搭建本地服务器)
- [7. webpack的配置文件分离](15-webpack.md/#7-webpack的配置文件分离)
# [16-VueCLI](16-VueCLI.md)
- [1. vue-cli起步](16-VueCLI.md/#1-vue-cli起步)
- [1.1 什么是vue-cli](16-VueCLI.md/#11-什么是vue-cli)
- [1.2 **CLI是什么意思**](16-VueCLI.md/#12-cli是什么意思)
- [1.3 vue cli使用](16-VueCLI.md/#13-vue-cli使用)
- [2. vue-cli2的目录结构](16-VueCLI.md/#2-vue-cli2的目录结构)
- [2.1 build和config](16-VueCLI.md/#21-build和config)
- [2.2 src和static](16-VueCLI.md/#22-src和static)
- [2.3 其他相关文件](16-VueCLI.md/#23-其他相关文件)
- [2.3.1 .babelrc文件](16-VueCLI.md/#231-babelrc文件)
- [2.3.2 .editorconfig文件](16-VueCLI.md/#232-editorconfig文件)
- [2.3.3 .eslintignore文件](16-VueCLI.md/#233-eslintignore文件)
- [2.3.4 .gitignore文件](16-VueCLI.md/#234-gitignore文件)
- [2.3.5 .postcssrc.js文件](16-VueCLI.md/#235-postcssrcjs文件)
- [2.3.6 index.html文件](16-VueCLI.md/#236-indexhtml文件)
- [2.3.7 package.json和package-lock.json](16-VueCLI.md/#237-packagejson和package-lockjson)
- [3. runtime-compiler和runtime-only区别](16-VueCLI.md/#3-runtime-compiler和runtime-only区别)
- [4. vue-cli3](16-VueCLI.md/#4-vue-cli3)
- [4.1 vue-cli3起步](16-VueCLI.md/#41-vue-cli3起步)
- [4.2 vue-cli3的配置](16-VueCLI.md/#42-vue-cli3的配置)
# [17-Vue-Router](17-Vue-Router.md/)
- [1. 路由简介](17-Vue-Router.md/#1-路由简介)
- [2. 前端/后端路由](17-Vue-Router.md/#2-前端后端路由)
- [3. URL的hash和HTML5的history](17-Vue-Router.md/#3-url的hash和html5的history)
- [3.1 URL的hash](17-Vue-Router.md/#31-url的hash)
- [3.2 HTML5的history模式](17-Vue-Router.md/#32-html5的history模式)
- [4. vue-router的安装配置](17-Vue-Router.md/#4-vue-router的安装配置)
- [5. vue-router的使用](17-Vue-Router.md/#5-vue-router的使用)
- [5.1 创建路由组件](17-Vue-Router.md/#51-创建路由组件)
- [5.2 配置路由映射:组件和路径映射关系](17-Vue-Router.md/#52-配置路由映射组件和路径映射关系)
- [5.3 使用路由:通过`<router-link>`和`<router-view>`](17-Vue-Router.md/#53-使用路由通过router-link和router-view)
- [5.4 路由的默认值和history模式](17-Vue-Router.md/#54-路由的默认值和history模式)
- [5.5 `<router-link>`的其他属性](17-Vue-Router.md/#55-router-link的其他属性)
- [5.6 通过代码修改路由跳转](17-Vue-Router.md/#56-通过代码修改路由跳转)
- [6. vue-router深入](17-Vue-Router.md/#6-vue-router深入)
- [6.1 vue-router的动态路由](17-Vue-Router.md/#61-vue-router的动态路由)
- [6.2 vue-router的打包文件解析](17-Vue-Router.md/#62-vue-router的打包文件解析)
- [6.3 嵌套路由](17-Vue-Router.md/#63-嵌套路由)
- [6.4 vue-router的参数传递](17-Vue-Router.md/#64-vue-router的参数传递)
- [6.5 router和route的由来](17-Vue-Router.md/#65-router和route的由来)
- [7. vue-router其他](17-Vue-Router.md/#7-vue-router其他)
- [7.1 vue-router的导航守卫](17-Vue-Router.md/#71-vue-router的导航守卫)
- [7.2 导航守卫补充](17-Vue-Router.md/#72-导航守卫补充)
- [7.3 完整的导航解析流程](17-Vue-Router.md/#73-完整的导航解析流程)
- [8. keep-alive](17-Vue-Router.md/#8-keep-alive)
# [18-Promise](18-Promise.md)
- [1. 什么是Promies](18-Promise.md/#1-什么是promies)
- [2. Promise的基本使用](18-Promise.md/#2-promise的基本使用)
- [2.1 什么时候使用Promise](18-Promise.md/#21-什么时候使用promise)
- [2.2 Promise对象](18-Promise.md/#22-promise对象)
- [3. Promise的三种状态](18-Promise.md/#3-promise的三种状态)
- [4. Promies的链式调用](18-Promise.md/#4-promies的链式调用)
- [5. Promies的all使用](18-Promise.md/#5-promies的all使用)
# [19-Vuex](19-Vuex.md/)
- [1. 什么是Vuex](19-Vuex.md/#1-什么是vuex)
- [1.1 使用场景](19-Vuex.md/#11-使用场景)
- [1.2 数据流层](19-Vuex.md/#12-数据流层)
- [注意事项](19-Vuex.md/#注意事项)
- [2. 核心概念](19-Vuex.md/#2-核心概念)
- [2.1 state](19-Vuex.md/#21-state)
- [2.1.1 在 Vue 组件中获得 Vuex 状态](19-Vuex.md/#211-在-vue-组件中获得-vuex-状态)
- [2.1.2 mapState 辅助函数](19-Vuex.md/#212-mapstate-辅助函数)
- [2.2 Getter](19-Vuex.md/#22-getter)
- [2.2.1 通过属性访问](19-Vuex.md/#221-通过属性访问)
- [2.2.2 通过方法访问](19-Vuex.md/#222-通过方法访问)
- [2.2.3 mapGetters 辅助函数](19-Vuex.md/#223-mapgetters-辅助函数)
- [2.3 Mutation](#23-mutation)
- [2.3.1 提交载荷Payload](19-Vuex.md/#231-提交载荷payload)
- [2.3.2 对象风格的提交方式](19-Vuex.md/#232-对象风格的提交方式)
- [2.3.3 Mutation 需遵守 Vue 的响应规则](19-Vuex.md/#233-mutation-需遵守-vue-的响应规则)
- [2.3.4 使用常量替代 Mutation 事件类型](19-Vuex.md/#234-使用常量替代-mutation-事件类型)
- [2.3.5 Mutation 必须是同步函数](19-Vuex.md/#235-mutation-必须是同步函数)
- [2.3.6 在组件中提交 Mutation](19-Vuex.md/#236-在组件中提交-mutation)
- [2.4 Action](19-Vuex.md/#24-action)
- [2.4.1 分发 Action](19-Vuex.md/#241-分发-action)
- [2.4.2 在组件中分发 Action](19-Vuex.md/#242-在组件中分发-action)
- [2.4.3 组合 Action](19-Vuex.md/#243-组合-action)
- [2.5 Module](19-Vuex.md/#25-module)
- [2.5.1 模块的局部状态](19-Vuex.md/#251-模块的局部状态)
- [2.5.2 命名空间](19-Vuex.md/#252-命名空间)
- [3. `Vuex`项目开发中常见的文件布局](19-Vuex.md/#3-vuex项目开发中常见的文件布局)
- [3.1 项目结构](19-Vuex.md/#31-项目结构)
- [3.2 文件的说明](19-Vuex.md/#32-文件的说明)
- [4. Vuex的简单案例](19-Vuex.md/#4-vuex的简单案例)
- [4.1 目录结构](19-Vuex.md/#41-目录结构)
- [4.2 新建store存储于vuex相关](19-Vuex.md/#42-新建store存储于vuex相关)
- [4.2.1 state.js](19-Vuex.md/#421-statejs)
- [4.2.2 getters.js](19-Vuex.md/#422-gettersjs)
- [4.2.3 mutations-types.js](19-Vuex.md/#423-mutations-typesjs)
- [4.2.4 mutations.js](19-Vuex.md/#424-mutationsjs)
- [4.2.5 index.js](19-Vuex.md/#425-indexjs)
- [4.3 在main.js中注册store](19-Vuex.md/#43-在mainjs中注册store)
- [4.4 在App.vue中使用](19-Vuex.md/#44-在appvue中使用)
- [4.5 结果](#45-结果)
- [5. Vuex工作原理详解](19-Vuex.md/#5-vuex工作原理详解)
- [5.1 理解computed](19-Vuex.md/#51-理解computed)
- [vue中data属性和computed相关的源代码](19-Vuex.md/#vue中data属性和computed相关的源代码)
- [`initData`](19-Vuex.md/#initdata)
- [`initComputed`](19-Vuex.md/#initcomputed)
- [`defineComputed`所代理属性的get方法](19-Vuex.md/#definecomputed所代理属性的get方法)
- [获取依赖并更新的过程](19-Vuex.md/#获取依赖并更新的过程)
- [5.2 vuex插件](19-Vuex.md/#52-vuex插件)
# [20-Axios的封装](20-Axios的封装.md)
- [1. Axios简介](20-Axios的封装.md/#1-axios简介)
- [1.1 什么是Axios](20-Axios的封装.md/#11-什么是axios)
- [1.2 特性](20-Axios的封装.md/#12-特性)
- [2. Axios的使用和配置](20-Axios的封装.md/#2-axios的使用和配置)
- [2.1 安装](20-Axios的封装.md/#21-安装)
- [2.2 基本使用](20-Axios的封装.md/#22-基本使用)
- [2.2.1 Get请求](20-Axios的封装.md/#221-get请求)
- [2.2.2 Post请求](20-Axios的封装.md/#222-post请求)
- [2.2.3 并发操作](20-Axios的封装.md/#223-并发操作)
- [2.3 请求API配置](20-Axios的封装.md/#23-请求api配置)
- [2.4 请求设置](20-Axios的封装.md/#24-请求设置)
- [2.5 响应数据Response](20-Axios的封装.md/#25-响应数据response)
- [2.6 拦截器Interceptors](20-Axios的封装.md/#26-拦截器interceptors)
- [3. 跨域](20-Axios的封装.md/#3-跨域)
- [3.1 ProxyTable](20-Axios的封装.md/#31-proxytable)
- [1. 找到 **config/index.js** 文件中的 `proxyTable:{}` 将其修改](20-Axios的封装.md/#1-找到-configindexjs-文件中的-proxytable-将其修改)
- [2. 找到 **config/dev.env.js** 文件,配置`BASE_URL`](20-Axios的封装.md/#2-找到-configdevenvjs-文件配置base_url)
- [3. 找到 **config/prod.env.js** 文件,配置`BASE_URL`](20-Axios的封装.md/#3-找到-configprodenvjs-文件配置base_url)
- [4. 配置 **axios** 的基础域名](20-Axios的封装.md/#4-配置-axios-的基础域名)
- [4. 封装](20-Axios的封装.md/#4-封装)
- [4.1 条件准备](20-Axios的封装.md/#41-条件准备)
- [4.2 axios封装(单域名)](20-Axios的封装.md/#42-axios封装单域名)
# [综合练习-实现Tab-Bar ](综合练习-实现Tab-Bar.md)
- [1. 实现Tab-Bar思路](综合练习-实现Tab-Bar.md/#1-实现tab-bar思路)
- [2. 代码实现](综合练习-实现Tab-Bar.md/#2-代码实现)
- [3. 别名配置](综合练习-实现Tab-Bar.md/#3-别名配置)

View File

@@ -0,0 +1,461 @@
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200518092031.gif)
# 1. 实现Tab-Bar思路
1. 下方单独的`Tab-Bar`组件如何封装?
- 自定义`Tab-Bar`组件在APP中使用
-`Tab-Bar`位置在底部,并设置你需要的样式
2. `Tab-Bar`中显示的内容由外部决定
- 定义插槽
- flex布局平分`Tab-Bar`
3. 自定义`Tab-Bar-Item`,可以传入图片和文字
- 定义`Tab-Bar-Item`,并定义两个插槽:图片和文字
- 给插槽外层包装`div`,设置样式
- 填充插槽,实现底部`Tab-Bar`的效果
4. 传入高亮图片
- 定义另一个插槽,插入`active-icon`的数据
- 定义一个变量`isActicve`,通过`v-show`来决定是否显示对应的icon
5. `Tab-Bar-Item`绑定路由数据
- 安装路由:`npm install vue-router --save`
-`router/index.js`配置路由信息,并创建对应的组件
- `main.js`中注册`router`
- `App.vue`中使用`router-link``router-view`
6. 点击item跳转到对应的路由并且动态决定`isActive`
- 监听`item`的点击,通过`this.$router.replace()`替换路由路径
- 通过`this.$route.path.indexOf(this.link)!==-1`来判断是否使`active`
7. 动态计算active样式
- 封装新的计算属性:`this.isActive?{'color': 'red'}:{}`
# 2. 代码实现
使用`vue init webpack 02-vue-router-tabbar-v1`新建一个项目工程(使用`vuecli2`)。
1. 在文件夹assest下新建css/base.css,用于初始化css
> base.css
```css
body {
padding: 0;
margin: 0;
}
```
> 修改App.vue添加初步样式
```vue
<template>
<div id="app">
<div id="tar-bar">
<div class="tar-bar-item">首页</div>
<div class="tar-bar-item">分类</div>
<div class="tar-bar-item">购物车</div>
<div class="tar-bar-item">我的</div>
</div>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
/* style中引用使用@import */
@import url('./assets/css/base.css');
#tar-bar {
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 1px rgba(100, 100, 100, .2);
}
.tar-bar-item {
flex: auto;
text-align: center;
height: 49px;
font-size: 20px;
}
</style>
```
> 使用npm run dev查看网页效果
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200518092055.png)
> 思考如果每次都要复用tabbar那每次都需要复制粘贴应该要把tabbar抽离出来vue就是组件化思想。
2. 将tabbar抽离成组件
在components下新建tabbar文件夹新建`TarBar.vue`和`TabBarItem.vue`,`TabBarItem`组件是在组件`TarBar`中抽取出来的,可以传入图片和文字(比如首页),所有需要使用插槽`<slot>`代替。
> TarBar.vue
```vue
<template>
<div id="tab-bar">
<!-- 插槽代替tabbaritem -->
<slot></slot>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: 'TabBar'
}
</script>
<style scoped>
#tab-bar {
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 1px rgba(100, 100, 100, .2);
}
</style>
```
TabBar弄一个slot插槽用于插入TabBarItem组件可能插入多个.
> TabBarItem.vue
```vue
<template>
<div class="tab-bar-item">
<!-- item-icon表示图片插槽 item-text表示文字插槽例如首页 -->
<slot name="item-icon"></slot>
<slot name="item-text"></slot>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: 'TabBarItem'
}
</script>
<style scoped>
.tab-bar-item {
flex: auto;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
height: 24px;
width: 24px;
margin-top: 3px;
vertical-align: middle;
margin-bottom: 2px;
}
</style>
```
TabBarItem组件中插入2个插槽一个用于插入图片一个用于插入文字。
> MainTabBar.vue
```vue
<template>
<div class="main-tab-bar">
<TabBar>
<TabBarItem path="/home" activeColor="blue">
<img slot="item-icon" src="~assets/img/tabbar/home.png" alt="" srcset="">
<template v-slot:item-text>
<div>首页</div>
</template>
</TabBarItem>
<TabBarItem path="/categories">
<template #item-icon>
<img src="~assets/img/tabbar/categories.png" alt="" srcset="">
</template>
<template #item-text>
<div>分类</div>
</template>
</TabBarItem>
<TabBarItem path="/shop">
<template #item-icon>
<img src="~assets/img/tabbar/shopcart.png" alt="" srcset="">
</template>
<template #item-text>
<div>购物车</div>
</template>
</TabBarItem>
<TabBarItem path="/me">
<template #item-icon>
<img src="~assets/img/tabbar/profile.png" alt="" srcset="">
</template>
<template #item-text>
<div>我的</div>
</template>
</TabBarItem>
</TabBar>
</div>
</template>
<script type="text/ecmascript-6">
import TabBar from "@/components/tabbar/TabBar"
import TabBarItem from "@/components/tabbar/TabBarItem"
export default {
name: "MainTabBar",
components: {
TabBar,
TabBarItem
}
}
</script>
<style scoped>
</style>
```
在MainTabBar组件中加入另外2个组件。
> 注意此处使用`~assets`和`@/components`是使用了别名配置,[详情请看3.别名配置](#3.别名配置)
最后在app.vue中导入MainTabBar组件。
```vue
<template>
<div id="app">
<MainTabBar></MainTabBar>
</div>
</template>
<script>
import MainTabBar from '@/components/MainTabBar'
export default {
name: 'App',
components: {
MainTabBar
}
}
</script>
<style>
/* style中引用使用@import */
@import url('./assets/css/base.css');
</style>
```
效果如图所示将组件进行了分离重组只要修改MainTabBar组件就可以修改图片和文字描述可以复用。
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200518092125.png)
3. 如何实现点击首页首页字体变红图片变红色
这里需要用到路由的`active-class`。
> 思路引用2张图片一张是正常颜色一张是红色使用`v-if`和`v-else`来处理是否变色,在路由处于活跃状态的时候,变红色。
引入路由使用路由就不细说了这里仅贴上tabbar的修改代码。
> TabBarItem.vue组件
```vue
<template>
<div class="tab-bar-item" :style="activeStyle" @click="itemClick">
<!-- item-icon表示图片插槽 item-text表示文字插槽例如首页 -->
<div v-if="!isActive">
<slot name="item-icon"></slot>
</div>
<div v-else>
<slot name="item-icon-active"></slot>
</div>
<div :class="{active:isActive}">
<slot name="item-text"></slot>
</div>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: 'TabBarItem',
props:{
path:String,
activeColor:{
type:String,
default:'red'
}
},
computed: {
isActive(){
return this.$route.path.indexOf(this.path) !== -1
},
activeStyle(){
return this.isActive ? {color: this.activeColor} : {}
}
},
methods: {
itemClick(){
this.$router.push(this.path)
}
}
}
</script>
<style scoped>
.tab-bar-item {
flex: auto;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
height: 24px;
width: 24px;
margin-top: 3px;
vertical-align: middle;
margin-bottom: 2px;
}
</style>
```
1. 使用`props`获取传递的值,这里传递是激活颜色,默认是红色
2. 设置计算属性`isActive`和`activeStyle`,分别表示激活状态和激活的样式
3. 定义`itemClick()`方法用于获取点击事件点击后使用代码实现路由跳转这里使用默认的hash模式
4. 使用`v-if`和`v-else`来进行条件判断
> MainTabBar.vue组件
```vue
<TabBarItem path="/home">
<img slot="item-icon" src="~assets/img/tabbar/home.png" alt="" srcset="">
<img slot="item-icon-active" src="~assets/img/tabbar/home_active.png" alt="" srcset="">
<template v-slot:item-text>
<div>首页</div>
</template>
</TabBarItem>
```
添加激活状态的图片与未激活的图片并列。
4. 配置路由信息,参考之前的代码
```js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const routes = [
{
path: '/',
redirect: '/home'//缺省时候重定向到/home
},
{
path: '/home',
component: () => import ('../views/home/Home.vue')
},
{
path: '/categories',
component: () => import ('../views/categories/Categories.vue')
},
{
path: '/shop',
component: () => import ('../views/shop/Shop.vue')
},
{
path: '/profile',
component: () => import ('../views/profile/Profile.vue')
},
]
export default new Router({
routes,
// linkActiveClass:"active"
})
```
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200518092146.png)
5. 修改main.js和App.vue
```js
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App)
})
```
```vue
<template>
<div id="app">
<router-view></router-view>
<MainTabBar></MainTabBar>
</div>
</template>
<script>
import MainTabBar from '@/components/MainTabBar'
export default {
name: 'App',
components: {
MainTabBar
}
}
</script>
<style>
/* style中引用使用@import */
@import url('./assets/css/base.css');
</style>
```
# 3. 别名配置
经常的我们向引入图片文件等资源的时候使用相对路径,诸如`../assets/xxx`这样的使用`../`获取上一层,如果有多个上层就需要`../../xxx`等等这样不利于维护代码。此时就需要一个能获取到指定目录的资源的就好了。
> 配置
在`webpack.base.config`中配置使用别名找到resolve:{}模块,增加配置信息
```js
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'assets': resolve('src/assets'),
'components': resolve('src/components'),
'views': resolve('scr/views')
}
},
```
这里`@`指定目录是`src`,例如`@/components`表示`src/components`目录,`assets`表示`src/assets`前缀,如果是`assets/img`就表示`src/assets/img`目录。

90
test.json Normal file
View File

@@ -0,0 +1,90 @@
{
"status": "UP",
"details": {
"SOFABootReadinessHealthCheckInfo": {
"status": "UP",
"details": {
"HealthChecker": {
"alipaySofaRuntimeHealthChecker": {
"status": "UP"
},
"sofaComponentHealthChecker": {
"status": "UP",
"details": {
"reference:com.alipay.sofa.cloud.osp.instance.facade.MeterFacade:#-259276000": "[bolt,passed] [jvm,passed]",
"extension-point:opscloudExtension$opsSyncChangeService": "passed",
"service:com.alipay.opscloudcore.common.service.client.http.HttpHeartbeatCheck": "[jvm,passed]",
"reference:com.alipay.sofa.cloud.osp.instance.facade.InstanceFacade:#-528282148": "[bolt,passed] [jvm,passed]",
"service:com.alipay.opscloudcore.common.service.client.ExecutionNotifyClient": "[jvm,passed]",
"extension-point:opscloudExtension$opsChangeService": "passed",
"service:com.alipay.sofa.servicegovern.common.service.facade.RouterRuleFacade": "[tr,passed]",
"reference:com.alipay.sofa.cloud.osp.user.facade.UserFacade:#1276066022": "[bolt,passed] [jvm,passed]",
"service:com.alipay.antcloud.dsrconsole.common.service.facade.DrmServiceFacade": "[bolt,passed]",
"reference:com.alipay.antcloud.drmdata.facade.DrmDataQueryFacade:#1340292234": "[bolt,passed] [jvm,passed]",
"service:com.alipay.opscloudcore.framework.sdk.clientframework.SyncChangeServiceShortcut": "[jvm,passed]",
"reference:com.alipay.antscheduler.facade.IAntJobFacade:#-54943409": "[jvm,passed] [bolt,passed]",
"service:com.alipay.opscloudcore.common.service.client.OpsChngTraceClient": "[jvm,passed]",
"service:com.alipay.opscloudcore.common.service.client.OpscloudApiSwitch": "[jvm,passed]",
"service:com.alipay.antcloud.dsrconsole.core.service.opscloud.template.OpscloudFacadeTemplate": "[jvm,passed]",
"service:com.alipay.huanyu.facade.change.ChangePlanCallback:dsrconsoleMAIN_SITE": "[tr,passed]",
"service:com.alipay.opscloudcore.framework.core.facade.OpscloudClientSyncServiceFacade:dsrconsole": "[tr,passed] [jvm,passed]",
"reference:com.alipay.antcloud.drmdata.facade.DrmDataClusterFacade:#-234876751": "[bolt,passed] [jvm,passed]",
"extension-point:opscloudExtension$opsChangeTransaction": "passed",
"service:com.alipay.opscloudcore.common.service.client.ChangeServiceCheckClient": "[jvm,passed]",
"service:com.alipay.opscloudcore.common.service.client.http.HttpOpsChngTraceFacade": "[jvm,passed]",
"reference:com.alipay.huanyu.facade.change.ChangePlanFacade:#2078914018": "[tr,passed] [jvm,passed]",
"reference:com.alipay.sofa.discovery.sync.enterprise.facade.api.ServiceRestFacade:#223367432": "[bolt,passed] [jvm,passed]",
"service:com.alipay.opscloudcore.common.service.client.http.HttpChangeServiceSyncExecuteFacade": "[jvm,passed]",
"reference:com.alipay.sofa.cloud.osp.instance.facade.OSPMetadataFacade:#1502309493": "[bolt,passed] [jvm,passed]",
"service:com.alipay.opscloudcore.common.service.client.http.HttpChangeServiceCheckFacade": "[jvm,passed]",
"service:com.alipay.opscloudcore.common.service.client.ChangeServiceAsyncCheckClient": "[jvm,passed]",
"service:com.alipay.opscloudcore.common.service.client.ChangeServiceSyncExecuteClient": "[jvm,passed]",
"service:com.alipay.antcloud.dsrconsole.core.service.opscloud.OpscloudStrategy": "[jvm,passed]",
"service:com.alipay.antcloud.dsrconsole.common.service.facade.ReportServiceFacade": "[tr,passed]",
"extension-point:opscloudClientExtension$facades": "passed",
"reference:com.alipay.antscheduler.facade.ISwitchZoneFacade:#-1973549567": "[bolt,passed] [jvm,passed]",
"reference:com.alipay.antscheduler.facade.IJobTriggerInstanceFacade:#973874193": "[bolt,passed] [jvm,passed]",
"service:com.alipay.opscloudcore.common.service.client.http.HttpChangeServiceAsyncCheckFacade": "[jvm,passed]",
"reference:com.alipay.sofa.cloud.auth.AuthenticationService:#-1387992122": "[bolt,passed] [jvm,passed]",
"reference:com.alipay.antscheduler.facade.IJobExecuteInstanceFacade:#1941186356": "[bolt,passed] [jvm,passed]",
"service:com.alipay.antcloud.dsrconsole.common.service.facade.DrmResourceMetaFacade": "[bolt,passed]",
"service:com.alipay.opscloudcore.common.service.client.extension.OpscloudClientExtensionImpl": "[jvm,passed]",
"service:com.alipay.antcloud.dsrconsole.common.service.facade.AuthFacade": "[bolt,passed]",
"reference:com.alipay.sofa.discovery.sync.enterprise.facade.api.ClusterRestFacade:#685631288": "[bolt,passed] [jvm,passed]",
"reference:com.alipay.sofa.cloud.osp.console.openapi.OspOpenAPIConfigFacade:#208429739": "[bolt,passed] [jvm,passed]"
}
}
},
"ReadinessCheckCallback": {
"applicationAfterReadinessCheckCallback": {
"status": "UP"
},
"lazyActivateBeanProcessor": {
"status": "UP"
},
"rpcAfterHealthCheckCallback": {
"status": "UP"
}
}
}
},
"diskSpace": {
"status": "UP",
"details": {
"total": 528311816192,
"free": 457311166464,
"threshold": 10485760,
"exists": true
}
},
"pingHealthContributor": {
"status": "UP"
},
"sessionConnectionHealthChecker": {
"status": "UP",
"details": {
"sessionConnection": "session connection is health"
}
}
}
}

234
test.log

File diff suppressed because one or more lines are too long

1353
test.sql Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,214 @@
- [1. 在主机上安装](#1-在主机上安装)
- [**(1) 修改后修改配置文件**](#1-修改后修改配置文件)
- [**(2) 设置开机启动**](#2-设置开机启动)
- [**(3) 将redis的bin命令添加到PATH**](#3-将redis的bin命令添加到path)
- [2. 在docker安装redis](#2-在docker安装redis)
- [**(1) 无持久化方式**](#1-无持久化方式)
- [**(2) 有持久化方式**](#2-有持久化方式)
- [**(3) 自定义redis配置**](#3-自定义redis配置)
官网:[http://redis.io](http://redis.io/)
redis定义是开源、BSD许可、高级的key-value存储系统。可以用来存储字符串、哈希结构、链表、集合因此常用来提供数据结构服务。
# 1. 在主机上安装
| 名称 | 操作 |
| :-------------- | :------------------------------------------- |
| 下载redis源代码 | wget http://t.cn/Eqw5XJ3 |
| 解压 | tar -zxvf 5.0.2.tar |
| 进入目录 | cd redis-5.0.2 |
| 编译 | make |
| 安装指定目录 | make install PREFIX=/usr/local/redis install |
| 复制配置文件 | cp redis.conf /usr/local/redis/ |
## **(1) 修改后修改配置文件**
```bash
# 打开配置文件
vim /usr/local/redis/redis.conf
# 使redis启动后在后台运行
daemonize yes
# 设置密码,默认密码为空
requirepass 123456
# 允许远程连接,取消绑定本机
#bind 127.0.0.1
```
## **(2) 设置开机启动**
在自启动/etc/init.d/文件夹下新建名字为redis的启动脚本内容如下
```bash
#!/bin/sh
# chkconfig: 2345 90 10
# description: Redis is a persistent key-value database
PATH=/usr/local/bin:/sbin:/usr/bin:/bin
REDISPORT=6379
EXEC=/usr/local/redis/bin/redis-server
REDIS_CLI=/usr/local/redis/bin/redis-cli
PIDFILE=/var/run/redis_6379.pid
CONF="/usr/local/redis/redis.conf"
case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
$EXEC $CONF
fi
if [ "$?"="0" ]
then
echo "Redis is running..."
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
$REDIS_CLI -p $REDISPORT SHUTDOWN
while [ -x ${PIDFILE} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1
done
echo "Redis stopped"
fi
;;
restart|force-reload)
${0} stop
${0} start
;;
*)
echo "Usage: /etc/init.d/redis {start|stop|restart|force-reload}" >&2
exit 1
esac
exit 0
```
```bash
# 设置脚本文件的可执行权限
chmod +x /etc/init.d/redis
# 添加服务到service开机启动
chkconfig --add /etc/init.d/redis
#启动服务
service redis start
# 停止服务
service redis stop
```
## **(3) 将redis的bin命令添加到PATH**
```bash
# 打开profile文件
vim /etc/profile
# 在文件末尾添加内容如下:
export PATH=$PATH:/usr/local/redis/bin
# 使profile生效
source /etc/profile
# 停止无密码redis服务
redis-cli shutdown
# 停止有密码redis服务
redis-cli -a 123456 shutdown
# 启动客户端
redis-cli
# 授权
auth 123456
```
# 2. 在docker安装redis
## **(1) 无持久化方式**
操作的数据只在内存中,容器关闭了,数据会消失。
docker-compose.yml配置内容如下
```dockerfile
version: "3"
services:
redis:
image: redis:latest
restart: always
container_name: "redis-app"
command: redis-server --requirepass 123456
ports:
- 6379:6379
```
其中参数requirepass表示客户端连接redis服务端时需要密码。
## **(2) 有持久化方式**
AOF模式持久化每秒钟强制写入磁盘一次。
docker-compose.yml配置内容如下
```dockerfile
version: "3"
services:
redis:
image: redis:latest
restart: always
container_name: "redis-app"
command: redis-server --requirepass 123456 --appendonly yes --appendfsync everysec
ports:
- 6379:6379
volumes:
- /data/redis:/data
```
其中requirepass 表示客户端连接redis服务端时需要密码 appendonly表示使用AOF模式持久化 appendfsync表示多长时间把数据写入硬盘
## **(3) 自定义redis配置**
docker-compose.yml配置内容如下
```dockerfile
version: "3"
services:
redis:
image: redis:latest
restart: always
container_name: "redis-app"
command: redis-server /usr/local/etc/redis/redis.conf
ports:
- 6379:6379
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
- /data/redis:/data
```
自定义配置文件,为了支持远程连接,在默认的配置修改了以下信息:
- bind默认绑定本机已取消
- protected-mode默认开启已取消
- requirepass默认密码为空已开启和设置密码

View File

@@ -0,0 +1,236 @@
- [key操作命令](#key操作命令)
- [set 设置key的值](#set-设置key的值)
- [get 获取key的值](#get-获取key的值)
- [del 删除key](#del-删除key)
- [exists 判断key是否存在](#exists-判断key是否存在)
- [type 获取key类型](#type-获取key类型)
- [expire 设置key有效期](#expire-设置key有效期)
- [tll 查看key有效期](#tll-查看key有效期)
- [rename 重命名key](#rename-重命名key)
- [renamenx 重命名不存在的key](#renamenx-重命名不存在的key)
- [persist 设置key永久有效](#persist-设置key永久有效)
- [move 把key移动到其他库](#move-把key移动到其他库)
- [库操作命令](#库操作命令)
- [dbsize 查看当前有多少个key](#dbsize-查看当前有多少个key)
- [select 选择库](#select-选择库)
- [flushdb 删除选中数据库中的key](#flushdb-删除选中数据库中的key)
- [flushall 删除所有库的key](#flushall-删除所有库的key)
# key操作命令
## set 设置key的值
```bash
set key value
# 示例
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> set age 25
OK
```
## get 获取key的值
```bash
get key
# 示例
127.0.0.1:6379> get name
"zhangsan"
```
## del 删除key
```bash
del key
# 示例
127.0.0.1:6379> del name
(integer) 1
```
## exists 判断key是否存在
```bash
exists key
# 判断key是否存在存在返回1不存在返回0
# 示例
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists title
(integer) 0
```
## type 获取key类型
```bash
type key
# 获取key存储的值的类型
# 示例
127.0.0.1:6379> type name
string
127.0.0.1:6379> type age
string
```
## expire 设置key有效期
```bash
expire key
# 设置key的生命周期
# pexpire key 表示以毫秒为单位设置声明周期
# 示例
127.0.0.1:6379[1]> expire login 60
(integer) 1
127.0.0.1:6379[1]> ttl login
(integer) 47
```
## tll 查看key有效期
```bash
ttl key
# 查询key的生命周期
# 大于0 :生命周期单位为秒,
# 等于-1永久有效
# 等于-2该key不存在
# pttl key表示毫秒为单位
# 示例
127.0.0.1:6379> ttl name
(integer) -1
127.0.0.1:6379> ttl title
(integer) -2
```
## rename 重命名key
```bash
rename key newkey
# 重命名key如果newkey已经存在修改后则替换新key的值
# 示例
127.0.0.1:6379> set title "redis test"
OK
127.0.0.1:6379> exists title
(integer) 1
127.0.0.1:6379> rename title biaoti
OK
127.0.0.1:6379> get biaoti
"redis test"
```
## renamenx 重命名不存在的key
```bash
renamenx key newkey
# 重命名key如果newkey已经存在则不修改。
# nx表示not exists
# 示例
127.0.0.1:6379> keys *
1) "biaoti"
2) "age"
3) "name"
127.0.0.1:6379> renamenx biaoti name
(integer) 0
```
## persist 设置key永久有效
```bash
persist key
# 设置key永久有效
# 示例
127.0.0.1:6379> set login on
OK
127.0.0.1:6379> expire login 60
(integer) 1
127.0.0.1:6379> ttl login
(integer) 55
127.0.0.1:6379> persist login
(integer) 1
127.0.0.1:6379> ttl login
(integer) -1
```
## move 把key移动到其他库
```bash
move key db
# 把key移动到另一个数据库db为整数
# 示例
127.0.0.1:6379> keys *
1) "biaoti"
2) "age"
3) "name"
127.0.0.1:6379> move biaoti 1
(integer) 1
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "biaoti"
```
# 库操作命令
## dbsize 查看当前有多少个key
```bash
dbsize
# 查看当前有多少个key
# 示例
127.0.0.1:6379> dbsize
12
```
## select 选择库
```bash
select db
# 选择使用哪个数据库db为整数
# 默认有16个数据库0~15如果想修改数据库数量修改redis.conf配置文件的databases值
# 示例
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[2]> select 15
OK
```
## flushdb 删除选中数据库中的key
```bash
flushdb
# 删除当前选择数据库中的所有key
# 示例
127.0.0.1:6379[1]> keys *
1) "biaoti"
127.0.0.1:6379[1]> flushdb
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
```
## flushall 删除所有库的key
```bash
flushall
# 删除所有数据库中的key
# 示例
127.0.0.1:6379[1]> flushall
OK
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
(empty list or set)
```

View File

@@ -0,0 +1,220 @@
- [set 设置kv、效期、判断key是否存在](#set-设置kv效期判断key是否存在)
- [mset 一次性输入多个kv](#mset-一次性输入多个kv)
- [setrange 修改偏移字节值为valuev](#setrange-修改偏移字节值为valuev)
- [append 在key的值后面追加字符串](#append-在key的值后面追加字符串)
- [getrange 获取key值的部分内容](#getrange-获取key值的部分内容)
- [getset 设置新值返回旧值](#getset-设置新值返回旧值)
- [incr/decr 指定key的值加/减1](#incrdecr-指定key的值加减1)
- [incrby/decrby 指定key的值加/减number](#incrbydecrby-指定key的值加减number)
- [incrbyfloat 指定key的值加浮点数](#incrbyfloat-指定key的值加浮点数)
- [setbit 设置二进制位上的值](#setbit-设置二进制位上的值)
- [getbit 获取二进制位上的值](#getbit-获取二进制位上的值)
- [bitop 对多个key逻辑操作](#bitop-对多个key逻辑操作)
# set 设置kv、效期、判断key是否存在
```bash
set key value [ex 秒数]|[px 毫秒数] [nx]|[xx]
# 设置kv时也可以设置有效期和判断key是否存在
# ex和px不要同时写否则以后面有效期为准
# nx表示key不存在时执行操作
# xx表示key存在时执行操作
# 示例
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> set name zhangsan ex 100
OK
127.0.0.1:6379> ttl name
(integer) 78
127.0.0.1:6379> set name lisi nx
(nil)
127.0.0.1:6379> get name
"zhangsan"
```
# mset 一次性输入多个kv
```bash
mset key1 value1 key2 value2......
# 一次性输入多个key-value
# 示例
127.0.0.1:6379> mset x 1 y 2 z 3
OK
127.0.0.1:6379> keys *
1) "y"
2) "z"
3) "x"
```
# setrange 修改偏移字节值为valuev
```bash
setrange key offset value
# 把字符串的偏移字节改为value
# 如果偏移量大于字符长度中间字符自动补0x00
# 示例
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> setrange name 5 ***
(integer) 8
127.0.0.1:6379> get name
"zhang***"
```
# append 在key的值后面追加字符串
```bash
append key value
# 在key的值后面追加value字符串
# 示例
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> append name "@126.com"
(integer) 16
127.0.0.1:6379> get name
"zhangsan@126.com"
```
# getrange 获取key值的部分内容
```bash
getrange key start stop
# 获取key值的一部分内容
# start表示起始位置
# stop表示结束位置可以为为负数表示从最后数起
# start>length 空字符串
# stop>length 截取到结尾
# 示例
127.0.0.1:6379> set title "hello world"
OK
127.0.0.1:6379> getrange title 6 11
"world"
127.0.0.1:6379> getrange title 0 -7
"hello"
```
# getset 设置新值返回旧值
```bash
getset key newvalue
# 设置新值,并返回旧值
# 示例
127.0.0.1:6379> set login on
OK
127.0.0.1:6379> get login
"on"
127.0.0.1:6379> getset login off
"on"
127.0.0.1:6379> get login
"off"
```
# incr/decr 指定key的值加/减1
```bash
incr/decr key
# 指定key的值加/减1返回结果
# key不存在时自动创建并加减1
# key的值为字符串时无效
# 示例
127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> incr num
(integer) 101
127.0.0.1:6379> decr num
(integer) 100
```
# incrby/decrby 指定key的值加/减number
```bash
incrby/decrby key number
# 指定key的值加减number大小
# 示例
127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> incrby num 50
(integer) 150
127.0.0.1:6379> decrby num 100
(integer) 50
```
# incrbyfloat 指定key的值加浮点数
```bash
incrbyfloat key floatnumber
# 指定key的值加浮点数
# 示例
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> incrbyfloat num 0.5
"10.5"
127.0.0.1:6379> incrbyfloat num -1.5
"9"
```
# setbit 设置二进制位上的值
```bash
setbit key offset value
# 设置offset对应二进制位上的值
# 返回改位的旧值
# 如果offset过大则会在中间填充0
# offset最大为2^32-1即512M
# 示例
127.0.0.1:6379> set letter A
OK
127.0.0.1:6379> setbit letter 2 1
(integer) 0
127.0.0.1:6379> get letter
"a"
# 把0100 0001(65)改为0110 0001(97)即把大写A改为了小写a
```
# getbit 获取二进制位上的值
```bash
getbit key offset
# 获取二进制offset对应位的值
# 示例
127.0.0.1:6379> set letter A
OK
127.0.0.1:6379> getbit letter 0
(integer) 0
127.0.0.1:6379> getbit letter 1
(integer) 1
127.0.0.1:6379> getbit letter 7
(integer) 1
```
# bitop 对多个key逻辑操作
```bash
bitop operation destkey key1 [key2 ......]
# 对key1 key2 keyN做operation并把结果保存到destkey 上
# operation有AND、OR、NOT、XOR
# 示例
127.0.0.1:6379> setbit lower 2 1
(integer) 0
127.0.0.1:6379> setbit upper 2 0
(integer) 0
127.0.0.1:6379> set letter A
OK
127.0.0.1:6379> bitop or letter letter lower
(integer) 1
127.0.0.1:6379> get letter
"a"
```

View File

@@ -0,0 +1,180 @@
- [lpush/rpush 在链表头/尾增加一个成员](#lpushrpush-在链表头尾增加一个成员)
- [lrange 获取链表成员](#lrange-获取链表成员)
- [lpop/rpop 弹出链表中头/尾的成员](#lpoprpop-弹出链表中头尾的成员)
- [lrem 删除链表成员](#lrem-删除链表成员)
- [lindex 获取链表索引对应的值](#lindex-获取链表索引对应的值)
- [llen key 获取链表成员个数](#llen-key-获取链表成员个数)
- [linsert 在链表中指定位置插入成员](#linsert-在链表中指定位置插入成员)
- [blpop/brpop 一直等待弹出头/尾成员](#blpopbrpop-一直等待弹出头尾成员)
# lpush/rpush 在链表头/尾增加一个成员
```bash
lpush/rpush key value
# 在链表头/尾增加一个成员,返回链表成员的个数
# 示例
127.0.0.1:6379> lpush letters A
(integer) 1
127.0.0.1:6379> rpush letters B
(integer) 2
127.0.0.1:6379> rpush letters C
(integer) 3
127.0.0.1:6379> rpush letters D
(integer) 4
```
# lrange 获取链表成员
```bash
lrange key start stop
# 返回链表中[start,stop]范围的成员
# 规律: 左数从0开始,右数从-1开始
# 示例
127.0.0.1:6379> lrange letters 0 -1
1) "A"
2) "B"
3) "C"
4) "D"
127.0.0.1:6379> lrange letters 1 2
1) "B"
2) "C"
```
# lpop/rpop 弹出链表中头/尾的成员
```bash
lpop/rpop key
# 弹出链表中头/尾的成员
# 示例
127.0.0.1:6379> lrange letters 0 -1
1) "A"
2) "B"
3) "C"
4) "D"
127.0.0.1:6379> lpop letters
"A"
127.0.0.1:6379> rpop letters
"D"
127.0.0.1:6379> lrange letters 0 -1
1) "B"
2) "C"
```
# lrem 删除链表成员
```bash
lrem key count value
# 从key链表中删除 value值
# 删除count的绝对值个value后结束
# count>0 从表头删除
# count<0 从表尾删除
# 示例
127.0.0.1:6379> rpush letters A B C D A B C D A B C D
(integer) 12
127.0.0.1:6379> lrem letters 2 A
(integer) 2
127.0.0.1:6379> lrange letters 0 -1
1) "B"
2) "C"
3) "D"
4) "B"
5) "C"
6) "D"
7) "A"
8) "B"
9) "C"
10) "D"
127.0.0.1:6379> lrem letters -3 D
(integer) 3
127.0.0.1:6379> lrange letters 0 -1
1) "B"
2) "C"
3) "B"
4) "C"
5) "A"
6) "B"
7) "C"
```
# lindex 获取链表索引对应的值
```bash
lindex key index
# 获取链表索引index对应的值
# 示例
127.0.0.1:6379> rpush letters A B C D
(integer) 4
127.0.0.1:6379> lindex letters 1
"B"
127.0.0.1:6379> lindex letters 2
"C"
```
# llen key 获取链表成员个数
```bash
BASHllen key
# 获取链表成员个数
# 示例
127.0.0.1:6379> rpush letters A B C D
(integer) 4
127.0.0.1:6379> llen letters
(integer) 4
```
# linsert 在链表中指定位置插入成员
```bash
linsert key after|before search value
# 在key链表中寻找"search"并在search值之前|之后插入value
# 如果没有找到,不插入值
# 如果找到一个search后命令就结束了因此不会插入多个value
# 示例
127.0.0.1:6379> rpush id 1 3 5 7
(integer) 4
127.0.0.1:6379> linsert id before 3 2
(integer) 5
127.0.0.1:6379> lrange id 0 -1
1) "1"
2) "2"
3) "3"
4) "5"
5) "7"
127.0.0.1:6379> linsert id after 5 6
(integer) 6
127.0.0.1:6379> lrange id 0 -1
1) "1"
2) "2"
3) "3"
4) "5"
5) "6"
6) "7"
```
# blpop/brpop 一直等待弹出头/尾成员
```bash
blpop/brpop key timeout
# 等待弹出key的头/尾成员
# Timeout为等待超时时间
# 如果timeout为0,则一直等待
# 应用s场景: 长轮询Ajax,在线聊天时,能够用到
# 示例
# 第一个终端操作:
127.0.0.1:6379> brpop chat 0
1) "chat"
2) "hello"
(40.97s)
# 第二个终端操作:
127.0.0.1:6379> rpush chat "hello"
(integer) 1
```

View File

@@ -0,0 +1,210 @@
- [sadd 往集合添加成员](#sadd-往集合添加成员)
- [srem 删除集合成员](#srem-删除集合成员)
- [spop 随机删除集合一个成员](#spop-随机删除集合一个成员)
- [srandmember 随机获取集合成员](#srandmember-随机获取集合成员)
- [smembers 获取集合所有的成员](#smembers-获取集合所有的成员)
- [sismember 判断成员是否存在集合中](#sismember-判断成员是否存在集合中)
- [scard 获取集合成员的个数](#scard-获取集合成员的个数)
- [smove 把一个集合中成员移动到另一个集合](#smove-把一个集合中成员移动到另一个集合)
- [sunion 获取多个集合的并集](#sunion-获取多个集合的并集)
- [sdiff 获取多个集合的差集](#sdiff-获取多个集合的差集)
- [sinterstore 获取多个集合的交集并储存](#sinterstore-获取多个集合的交集并储存)
集合特性:无序性、唯一性、确定性
# sadd 往集合添加成员
```bash
sadd key value1 value2 ...
# 往集合key中增加成员
# 增加相同成员时只会添加一个(唯一性)
# 示例
127.0.0.1:6379> sadd names zhangsan lisi
(integer) 2
127.0.0.1:6379> sadd names wangwu wangwu
(integer) 1
```
# srem 删除集合成员
```bash
srem key value1 value2 ...
# 删除集合中为value1 value2...成员
# 返回真正删除掉的成员个数(不包括不存在的成员)
# 示例
127.0.0.1:6379> sadd names zhangsan lisi wangwu
(integer) 3
127.0.0.1:6379> srem names zhangsan lisi
(integer) 2
127.0.0.1:6379> smembers names
1) "wangwu"
```
# spop 随机删除集合一个成员
```bash
spop key
# 随机删除集合key中的一个成员
# 应用场景:抽奖,抽中的人已经排除,不可能会被再次抽中了
# 示例
127.0.0.1:6379> sadd letters A B C D E F
(integer) 6
127.0.0.1:6379> spop letters
"A"
127.0.0.1:6379> spop letters
"F"
127.0.0.1:6379> spop letters
"B"
127.0.0.1:6379> spop letters
"D"
```
# srandmember 随机获取集合成员
```bash
srandmember key [count]
# 随机获取集合key的count个成员默认count是1
# 示例
127.0.0.1:6379> srandmember letters
"C"
127.0.0.1:6379> srandmember letters 2
1) "E"
2) "B"
127.0.0.1:6379> srandmember letters 3
1) "D"
2) "C"
3) "E"
```
# smembers 获取集合所有的成员
```bash
smembers key
# 返回集合所有的成员
# 返回值的顺序不一定是添加成员的顺序(无序性)
# 示例
127.0.0.1:6379> sadd names zhangsan lisi wangwu
(integer) 3
127.0.0.1:6379> smembers names
1) "lisi"
2) "wangwu"
3) "zhangsan"
```
# sismember 判断成员是否存在集合中
```bash
sismember key value
# 判断value是否存在集合key中存在返回1不存在返回0
# 示例
127.0.0.1:6379> sadd names zhangsan lisi wangwu
(integer) 3
127.0.0.1:6379> sismember names lisi
(integer) 1
127.0.0.1:6379> sismember names zhaoliu
(integer) 0
```
# scard 获取集合成员的个数
```bash
scard key
# 获取集合成员的个数
# 示例
127.0.0.1:6379> sadd letters A B C D
(integer) 4
127.0.0.1:6379> sadd letters E F
(integer) 2
127.0.0.1:6379> scard letters
(integer) 6
```
# smove 把一个集合中成员移动到另一个集合
```bash
smove <source> <dest> value
# 把集合source中的value删除并添加到集合dest中
# 示例
127.0.0.1:6379> sadd letters A B C
(integer) 3
127.0.0.1:6379> sadd num 1 2 3
(integer) 3
127.0.0.1:6379> smove letters num A
(integer) 1
127.0.0.1:6379> smembers letters
1) "C"
2) "B"
127.0.0.1:6379> smembers num
1) "3"
2) "1"
3) "A"
4) "2"
```
# sunion 获取多个集合的并集
```bash
sunion key1 key2 ...
# 获取多个集合的并集
# 示例
127.0.0.1:6379> sadd zhangsan A E G
(integer) 3
127.0.0.1:6379> sadd lisi B E F
(integer) 3
127.0.0.1:6379> sadd wangwu C D E
(integer) 3
127.0.0.1:6379> sunion zhangsan lisi wangwu
1) "B"
2) "G"
3) "D"
4) "C"
5) "E"
6) "F"
7) "A"
```
# sdiff 获取多个集合的差集
```bash
sdiff key1 key2 ...
# 获取key1与key2...的差集
# 即key1-key2...(key1有其他集合没有的成员)
# 示例
127.0.0.1:6379> sadd zhangsan A B C
(integer) 3
127.0.0.1:6379> sadd lisi B D E
(integer) 3
127.0.0.1:6379> sadd wangwu C E F
(integer) 3
127.0.0.1:6379> sdiff zhangsan lisi wangwu
1) "A"
```
# sinterstore 获取多个集合的交集并储存
```bash
sinterstore dest key1 key2 ...
# 求出key1 key2 ...集合中的交集并赋给dest
# 示例
127.0.0.1:6379> sadd zhangsan A C D
(integer) 3
127.0.0.1:6379> sadd lisi B D E
(integer) 3
127.0.0.1:6379> sadd wangwu D E G
(integer) 3
127.0.0.1:6379> sinterstore class zhangsan lisi wangwu
(integer) 1
127.0.0.1:6379> smembers class
1) "D"
```

View File

@@ -0,0 +1,224 @@
- [1. zadd 往有序集合添加成员](#1-zadd-往有序集合添加成员)
- [2. zrange 按名次取成员](#2-zrange-按名次取成员)
- [3. zrangebyscore 按分数取成员](#3-zrangebyscore-按分数取成员)
- [4. zscore 获取指定成员的分数](#4-zscore-获取指定成员的分数)
- [5. zcount 计算分数区间成员个数](#5-zcount-计算分数区间成员个数)
- [6. zrank/zrevrank 获取成员升序/降序的排名](#6-zrankzrevrank-获取成员升序降序的排名)
- [7. zrem 删除有序集合成员](#7-zrem-删除有序集合成员)
- [8. zremrangebyrank 按排名删除成员](#8-zremrangebyrank-按排名删除成员)
- [9. zremrangebyscore 按分数删除成员](#9-zremrangebyscore-按分数删除成员)
- [10. zinterstore 求交集再计算](#10-zinterstore-求交集再计算)
- [11. zunionstore 求并集再计算](#11-zunionstore-求并集再计算)
# 1. zadd 往有序集合添加成员
```bash
zadd key score1 key2 score2 key2 ...
# 往有序集合key添加成员
# 示例
127.0.0.1:6379> zadd ages 28 zhangsan 24 lisi 26 wangwu
(integer) 0
127.0.0.1:6379> zrange ages 0 -1
1) "lisi"
2) "wangwu"
3) "zhangsan"
```
# 2. zrange 按名次取成员
```bash
zrange key start stop [WITHSCORES]
# 把集合排序后,返回名次[start,stop]的成员按名次取成员
# 默认是升续排列withscores 是把score也打印出来
# 示例
127.0.0.1:6379> zrange ages 0 -1 withscores
1) "lisi"
2) "24"
3) "wangwu"
4) "26"
5) "zhangsan"
6) "28"
```
# 3. zrangebyscore 按分数取成员
```bash
zrangebyscore key min max [withscores] limit offset N
# 集合(升序)排序后取score在[min,max]内的成员并跳过offset个取出N个按分数取成员
# 示例
127.0.0.1:6379> zadd ages 28 zhangsan 24 lisi 26 wangwu
(integer) 3
127.0.0.1:6379> zrangebyscore ages 25 30
1) "wangwu"
2) "zhangsan"
127.0.0.1:6379> zrangebyscore ages 25 30 limit 1 1
1) "zhangsan"
```
# 4. zscore 获取指定成员的分数
```bash
ZSCORE key member
# 获取指定成员的分数
# 示例
127.0.0.1:6379> zadd height 175 zhangsan 167 lisi 185 wangwu
(integer) 3
127.0.0.1:6379> zscore height lisi
"167"
```
# 5. zcount 计算分数区间成员个数
```bash
zcount key min max
# 计算[min,max]区间内成员的数量
# 示例
127.0.0.1:6379> zadd height 175 zhangsan 167 lisi 185 wangwu
(integer) 3
127.0.0.1:6379> zcount height 170 180
(integer) 1
```
# 6. zrank/zrevrank 获取成员升序/降序的排名
```bash
zrank/zrevrank key member
# 查询member的升序/降序排名名次从0开始
# 示例
127.0.0.1:6379> zadd ages 28 zhangsan 24 lisi 26 wangwu
(integer) 0
127.0.0.1:6379> zrange ages 0 -1
1) "lisi"
2) "wangwu"
3) "zhangsan"
127.0.0.1:6379> zrank ages zhangsan
(integer) 2
127.0.0.1:6379> zrevrank ages zhangsan
(integer) 0
```
# 7. zrem 删除有序集合成员
```bash
zrem key value1 value2 ..
# 删除集合中的成员
# 示例
127.0.0.1:6379> zrem ages wangwu
(integer) 1
127.0.0.1:6379> zrange ages 0 -1
1) "lisi"
2) "zhangsan"
```
# 8. zremrangebyrank 按排名删除成员
```bash
zremrangebyrank key start end
# 按排名删除成员,删除名次在[start,end]之间的
# 示例
127.0.0.1:6379> zadd height 175 zhangsan 167 lisi 185 wangwu 178 zhaoliu
(integer) 1
127.0.0.1:6379> zremrangebyrank height 0 1
(integer) 2
127.0.0.1:6379> zrange height 0 -1
1) "zhaoliu"
2) "wangwu"
```
# 9. zremrangebyscore 按分数删除成员
```bash
zremrangebyscore key min max
# 按照socre来删除成员删除score在[min,max]之间的
# 示例
127.0.0.1:6379> zadd height 175 zhangsan 167 lisi 185 wangwu 178 zhaoliu
(integer) 2
127.0.0.1:6379> zremrangebyscore height 170 180
(integer) 2
127.0.0.1:6379> zrange height 0 -1
1) "lisi"
2) "wangwu"
```
# 10. zinterstore 求交集再计算
```bash
zinterstore destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
# 求key1、key2...的交集key1、key2...的权重分别是 weight1、weight2...
# 聚合方法用: sum|min|max
# 聚合的结果保存在destination集合内
# 示例
127.0.0.1:6379> zadd zhangsan 5 iphone6s 7 galaxyS7 6 huaweiP9
(integer) 3
127.0.0.1:6379> zadd lisi 3 iphone6s 9 galaxyS7 4 huaweiP9 2 HTC10
(integer) 4
127.0.0.1:6379> zinterstore result 2 zhangsan lisi
(integer) 3
127.0.0.1:6379> zrange result 0 -1 withscores
1) "iphone6s"
2) "8"
3) "huaweiP9"
4) "10"
5) "galaxyS7"
6) "16"
127.0.0.1:6379> zinterstore result 2 zhangsan lisi aggregate max
(integer) 3
127.0.0.1:6379> zrange result 0 -1 withscores
1) "iphone6s"
2) "5"
3) "huaweiP9"
4) "6"
5) "galaxyS7"
6) "9"
```
# 11. zunionstore 求并集再计算
```bash
zunionstore destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
# 求key1、key2...的并集key1、key2...的权重分别是 weight1、weight2...
# 聚合方法用: sum|min|max
# 聚合的结果保存在destination集合内
# 示例
127.0.0.1:6379> zadd zhangsan 4 iphone6s 6 huaweiP9 8 xiaomi5
(integer) 3
127.0.0.1:6379> zadd lisi 2 iphone6s 8 galaxS7 5 meizu6
(integer) 3
127.0.0.1:6379> zunionstore result 2 zhangsan lisi
(integer) 5
127.0.0.1:6379> zrange result 0 -1 withscores
1) "meizu6"
2) "5"
3) "huaweiP9"
4) "6"
5) "iphone6s"
6) "6"
7) "galaxS7"
8) "8"
9) "xiaomi5"
10) "8"
127.0.0.1:6379> zunionstore result 2 zhangsan lisi aggregate max
(integer) 5
127.0.0.1:6379> zrange result 0 -1 withscores
1) "iphone6s"
2) "4"
3) "meizu6"
4) "5"
5) "huaweiP9"
6) "6"
7) "galaxS7"
8) "8"
9) "xiaomi5"
10) "8"
```

View File

@@ -0,0 +1,183 @@
- [1. hset 设置哈希field域的值](#1-hset-设置哈希field域的值)
- [2. hmset 设置哈希多个field域的值](#2-hmset-设置哈希多个field域的值)
- [3. hget 获取field域的值](#3-hget-获取field域的值)
- [4. hmget 获取多个field域的值](#4-hmget-获取多个field域的值)
- [5. hgetall 获取所有field域和值](#5-hgetall-获取所有field域和值)
- [6. hlen 获取field的数量](#6-hlen-获取field的数量)
- [7. hdel 删除field域](#7-hdel-删除field域)
- [8. hexists 判断field域是否存在](#8-hexists-判断field域是否存在)
- [9. hincrby 使field域的值加上整数](#9-hincrby-使field域的值加上整数)
- [10. hincrbyfloat 使field域的值加上浮点数](#10-hincrbyfloat-使field域的值加上浮点数)
- [11. hkeys 获取所有所有field域的名字](#11-hkeys-获取所有所有field域的名字)
- [12. kvals 获取所有所有field域的值](#12-kvals-获取所有所有field域的值)
# 1. hset 设置哈希field域的值
```bash
hset key field value
# 把key中 filed域的值设为value
# 注:如果没有field域直接添加如果有则覆盖原field域的值
# 示例
127.0.0.1:6379> hset user name zhangsan
(integer) 1
127.0.0.1:6379> hset user age 25
(integer) 1
127.0.0.1:6379> hset user gender male
(integer) 1
```
# 2. hmset 设置哈希多个field域的值
```bash
hmset key field1 value1 [field2 value2 field3 value3 ......fieldn valuen]
# 一次设置多个field和对应的value
# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
OK
```
# 3. hget 获取field域的值
```bash
hget key field
# 获取field域的值
# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
OK
127.0.0.1:6379> hget user age
"26
```
# 4. hmget 获取多个field域的值
```bash
hget key field
# 获取多个field域的值
# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
OK
127.0.0.1:6379> hmget user name age
1) "lisi"
2) "26"
```
# 5. hgetall 获取所有field域和值
```bash
hgetall key
# 获取哈希key的所有field域和值
# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
127.0.0.1:6379> hgetall user
1) "name"
2) "lisi"
3) "age"
4) "26"
5) "gender"
6) "male"
```
# 6. hlen 获取field的数量
```bash
hlen key
# 获取field的数量
# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
127.0.0.1:6379> hlen user
(integer) 3
```
# 7. hdel 删除field域
```bash
hdel key field
# 删除key中 field域
# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hgetall user
1) "name"
2) "lisi"
3) "gender"
4) "male"
```
# 8. hexists 判断field域是否存在
```bash
hexists key field
# 判断key中有没有field域
# 示例
127.0.0.1:6379> hmset user name lisi age 26 gender male
OK
127.0.0.1:6379> hexists user age
(integer) 1
127.0.0.1:6379> hexists user height
(integer) 0
```
# 9. hincrby 使field域的值加上整数
```bash
hincrby key field value
# 使key中的field域的值加上整型值value
# 示例
127.0.0.1:6379> hmset user name zhangsan height 158
OK
127.0.0.1:6379> hincrby user height 2
(integer) 160
```
# 10. hincrbyfloat 使field域的值加上浮点数
```bash
hincrbyfloat key field value
# 使key中的field域的值加上浮点值value
# 示例
127.0.0.1:6379> hmset user name zhangsan height 158
OK
127.0.0.1:6379> hincrbyfloat user height 5.5
"165.5"
```
# 11. hkeys 获取所有所有field域的名字
```bash
hkeys key
# 获取key中所有的field
# 示例
127.0.0.1:6379> hmset user name zhangsan age 25 gender male
OK
127.0.0.1:6379> hkeys user
1) "name"
2) "age"
3) "gender"
```
# 12. kvals 获取所有所有field域的值
```bash
kvals key
# 返回key中所有的value
# 示例
127.0.0.1:6379> hmset user name zhangsan age 25 gender male
OK
127.0.0.1:6379> hvals user
1) "zhangsan"
2) "25"
3) "male"
```

View File

@@ -0,0 +1,228 @@
- [1. geoadd 添加地理位置信息](#1-geoadd-添加地理位置信息)
- [2. geopos 查询位置的坐标](#2-geopos-查询位置的坐标)
- [3. geodist 查询位置距离](#3-geodist-查询位置距离)
- [4. georadius 查询某点的附近点](#4-georadius-查询某点的附近点)
- [5. georadiusbymember 查询某位置距离的附近点](#5-georadiusbymember-查询某位置距离的附近点)
- [6. geohash 查询位置GEOHASH编码](#6-geohash-查询位置geohash编码)
# 1. geoadd 添加地理位置信息
命令:`geoadd key longitude latitude member [longitude latitude member ...]`
longitude表示经度latitude表示纬度member表示成员
命令描述将指定的地理空间位置纬度、经度、名称添加到指定的key中。
返回值添加到sorted set元素的数目但不包括已更新score的元素。
```bash
# 示例
127.0.0.1:6379> geoadd Guangdong-cities 113.2278442 23.1255978 Guangzhou 113.106308 23.0088312 Foshan 113.7943267 22.9761989 Dongguan 114.0538788 22.5551603 Shenzhen
(integer) 4
```
# 2. geopos 查询位置的坐标
命令:`geopos location-set name [name ...]`
命令描述从key里返回所有给定位置元素的位置经度和纬度
返回值GEOPOS 命令返回一个数组, 数组中的每个项都由两个元素组成: 第一个元素为给定位置元素的经度, 而第二个元素则为给定位置元素的纬度。当给定的位置元素不存在时, 对应的数组项为空值。
```bash
# 示例
127.0.0.1:6379> geopos Guangdong-cities Guangzhou Shenzhen
1) 1) "113.22784155607223511"
2) "23.1255982020608073"
2) 1) "114.05388146638870239"
2) "22.55515920515157546"
```
# 3. geodist 查询位置距离
命令:`geodist key member1 member2 [m|km|mi|ft]`
命令描述:
返回两个给定位置之间的距离。如果两个位置之间的其中一个不存在, 那么命令返回空值。指定单位的参数 unit 必须是以下单位的其中一个:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
```bash
# 示例
127.0.0.1:6379> geodist Guangdong-cities Guangzhou Shenzhen
"105806.7782"
```
# 4. georadius 查询某点的附近点
命令:`GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]`
命令描述:以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
范围可以使用以下其中一个单位:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
在给定以下可选项时, 命令会返回额外的信息:
**WITHDIST**: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
**WITHCOORD**: 将位置元素的经度和维度也一并返回。
**WITHHASH**: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
命令默认返回未排序的位置元素。 通过以下两个参数, 用户可以指定被返回位置元素的排序方式:
**ASC**: 根据中心的位置, 按照从近到远的方式返回位置元素。
**DESC**: 根据中心的位置, 按照从远到近的方式返回位置元素。
在默认情况下, GEORADIUS 命令会返回所有匹配的位置元素。 虽然用户可以使用 COUNT <count> 选项去获取前 N 个匹配元素, 但是因为命令在内部可能会需要对所有被匹配的元素进行处理, 所以在对一个非常大的区域进行搜索时, 即使只使用 COUNT 选项去获取少量元素, 命令的执行速度也可能会非常慢。 但是从另一方面来说, 使用 COUNT 选项去减少需要返回的元素数量, 对于减少带宽来说仍然是非常有用的。
返回值:
在没有给定任何 WITH 选项的情况下, 命令只会返回一个像 [“New York”,”Milan”,”Paris”] 这样的线性linear列表。
在指定了 WITHCOORD 、 WITHDIST 、 WITHHASH 等选项的情况下, 命令返回一个二层嵌套数组, 内层的每个子数组就表示一个元素。
在返回嵌套数组时, 子数组的第一个元素总是位置元素的名字。 至于额外的信息, 则会作为子数组的后续元素, 按照以下顺序被返回:
1. 以浮点数格式返回的中心与位置元素之间的距离, 单位与用户指定范围时的单位一致。
2. geohash 整数。
3. 由两个元素组成的坐标,分别为经度和纬度。
```bash
# 示例
127.0.0.1:6379>georadius Guangdong-cities 113.2278442 23.1255978 100 km
1) "Foshan"
2) "Guangzhou"
3) "Dongguan"
127.0.0.1:6379>georadius Guangdong-cities 113.2278442 23.1255978 100 km withcoord
1) 1) "Foshan"
2) 1) "113.10631066560745"
2) "23.008831202413539"
2) 1) "Guangzhou"
2) 1) "113.22784155607224"
2) "23.125598202060807"
3) 1) "Dongguan"
2) 1) "113.79432410001755"
2) "22.9761992022082"
127.0.0.1:6379>georadius Guangdong-cities 113.2278442 23.1255978 100 km withcoord withdist
1) 1) "Foshan"
2) "17.9820"
3) 1) "113.10631066560745"
2) "23.008831202413539"
2) 1) "Guangzhou"
2) "0.0003"
3) 1) "113.22784155607224"
2) "23.125598202060807"
3) 1) "Dongguan"
2) "60.3111"
3) 1) "113.79432410001755"
2) "22.9761992022082"
127.0.0.1:6379>georadius Guangdong-cities 113.2278442 23.1255978 100 km withcoord withdist withhash
1) 1) "Foshan"
2) "17.9820"
3) (integer) 4046506835759376
4) 1) "113.10631066560745"
2) "23.008831202413539"
2) 1) "Guangzhou"
2) "0.0003"
3) (integer) 4046533621643967
4) 1) "113.22784155607224"
2) "23.125598202060807"
3) 1) "Dongguan"
2) "60.3111"
3) (integer) 4046540375616238
4) 1) "113.79432410001755"
2) "22.9761992022082"
127.0.0.1:6379>georadius Guangdong-cities 113.2278442 23.1255978 100 km withcoord withdist withhash count 2
1) 1) "Guangzhou"
2) "0.0003"
3) (integer) 4046533621643967
4) 1) "113.22784155607224"
2) "23.125598202060807"
2) 1) "Foshan"
2) "17.9820"
3) (integer) 4046506835759376
4) 1) "113.10631066560745"
2) "23.008831202413539"
127.0.0.1:6379>georadius Guangdong-cities 113.2278442 23.1255978 100 km withcoord withdist withhash asc
1) 1) "Guangzhou"
2) "0.0003"
3) (integer) 4046533621643967
4) 1) "113.22784155607224"
2) "23.125598202060807"
2) 1) "Foshan"
2) "17.9820"
3) (integer) 4046506835759376
4) 1) "113.10631066560745"
2) "23.008831202413539"
3) 1) "Dongguan"
2) "60.3111"
3) (integer) 4046540375616238
4) 1) "113.79432410001755"
2) "22.9761992022082"
127.0.0.1:6379>georadius Guangdong-cities 113.2278442 23.1255978 100 km withcoord withdist withhash desc
1) 1) "Dongguan"
2) "60.3111"
3) (integer) 4046540375616238
4) 1) "113.79432410001755"
2) "22.9761992022082"
2) 1) "Foshan"
2) "17.9820"
3) (integer) 4046506835759376
4) 1) "113.10631066560745"
2) "23.008831202413539"
3) 1) "Guangzhou"
2) "0.0003"
3) (integer) 4046533621643967
4) 1) "113.22784155607224"
2) "23.125598202060807"
```
# 5. georadiusbymember 查询某位置距离的附近点
```bash
georadiusbymember key member radius m|km|ft|mi
# 指定成员作为中心来查询附近的点
# 示例
127.0.0.1:6379> georadiusbymember Guangdong-cities Guangzhou 100 km
1) "Foshan"
2) "Guangzhou"
3) "Dongguan"
```
# 6. geohash 查询位置GEOHASH编码
命令:`geohash key member [member ...]`
命令描述:返回一个或多个位置元素的 Geohash 表示。通常使用表示位置的元素使用不同的技术使用Geohash位置52点整数编码。由于编码和解码过程中所使用的初始最小和最大坐标不同编码的编码也不同于标准。此命令返回一个标准的Geohash
返回值:一个数组, 数组的每个项都是一个 geohash 。 命令返回的 geohash 的位置与用户给定的位置元素的位置一一对应。
```bash
# 示例
127.0.0.1:6379> geohash Guangdong-cities Guangzhou
1) "ws0e89curg0"
```

View File

@@ -0,0 +1,65 @@
- [1. 事务命令](#1-事务命令)
- [2. 乐观锁](#2-乐观锁)
# 1. 事务命令
| 命令 | 说明 |
| :------ | :----------- |
| muitl | 开启事务命令 |
| command | 普通命令 |
| discard | 在提交前取消 |
| exec | 提交 |
discard只是结束本次事务前2条语句已经执行造成的影响仍然还在。
语句出错有两种情况: - 语法有问题exec时报错所有语句取消执行没有对数据造成影响。 - 语法本身没错,但适用对象有问题(比如 zadd 操作list对象)exec之后会执行正确的语句并跳过有不适当的语句对数据会造成影响这点由程序员负责。
# 2. 乐观锁
redis的事务中启用的是乐观锁只负责监测key没有被改动如果在事务中发现key被改动则取消事务。使用watch命令来监控一个或多个key使用unwatch命令来取消监控所有key。
```bash
# 示例
watch key
muitl
操作数据...
exec
unwatch
```
模拟抢票场景用户买一张票扣掉100元
```bash
# 在zhangsan买票过程中在提交事务前一瞬间有人成功买到票ticket已经改变(即使ticket还有票)导致zhangsan抢票失败。
127.0.0.1:6379> watch ticket
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decr ticket
QUEUED
127.0.0.1:6379> decrby zhangsan 100
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get zhangsan
"1000"
127.0.0.1:6379> get ticket
"2"
127.0.0.1:6379> unwatch
OK
# lisi在买票整个过程都没有人抢票所以lisi一次抢票成功。
127.0.0.1:6379> watch ticket
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby lisi 100
QUEUED
127.0.0.1:6379> decr ticket
QUEUED
127.0.0.1:6379> exec
1) (integer) 700
2) (integer) 1
127.0.0.1:6379> unwatch
```

View File

@@ -0,0 +1,75 @@
- [1. publish 发布频道](#1-publish-发布频道)
- [2. subscribe 订阅指定频道](#2-subscribe-订阅指定频道)
- [3. psubscribe 订阅已匹配频道](#3-psubscribe-订阅已匹配频道)
- 发布端publish
- 订阅端subscribepsubscribe
# 1. publish 发布频道
```bash
publish 频道名称 发布内容
# 示例
127.0.0.1:6379> publish music_2 "It's Not Goodbye"
(integer) 1
127.0.0.1:6379> publish music "just one last dance"
(integer) 2
127.0.0.1:6379> publish music "stay"
(integer) 2
127.0.0.1:6379> publish music_2 "It's Not Goodbye"
(integer) 1
```
# 2. subscribe 订阅指定频道
```bash
subscribe 频道名称
# 示例
127.0.0.1:6379> subscribe music
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "music"
3) (integer) 1
1) "message"
2) "music"
3) "just one last dance"
1) "message"
2) "music"
3) "stay" "music"
3) (integer) 1
1) "message"
2) "music"
3) "just one last dance"
1) "message"
2) "music"
3) "stay"
```
# 3. psubscribe 订阅已匹配频道
```bash
psubscribe 匹配频道名称
# 示例
127.0.0.1:6379> psubscribe music*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "music*"
3) (integer) 1
1) "pmessage"
2) "music*"
3) "music"
4) "just one last dance"
1) "pmessage"
2) "music*"
3) "music"
4) "stay"
1) "pmessage"
2) "music*"
3) "music_2"
4) "It's Not Goodbye"
```

View File

@@ -0,0 +1,79 @@
- [1. redis持久化](#1-redis持久化)
- [1.1. rdb快照持久化](#11-rdb快照持久化)
- [1.2. aof日志持久化](#12-aof日志持久化)
- [2. 导入和导出数据库](#2-导入和导出数据库)
# 1. redis持久化
## 1.1. rdb快照持久化
rdb的工作原理每隔N分钟或N次写操作后从内存dump数据形成rdb文件压缩放在备份目录设置配置文件参数
```bash
# 打开配置文件
vim /usr/local/redis/ redis.conf
save 900 1 # 900秒内有1条写入则产生快照
save 300 1000 # 如果300秒内有1000次写入则产生快照
save 60 10000 # 如果60秒内有10000次写入则产生快照
(这3个选项都屏蔽,则rdb禁用)
stop-writes-on-bgsave-error yes # 后台备份进程出错时,主进程停不停止写入
rdbcompression yes # 导出的rdb文件是否压缩
Rdbchecksum yes # 导入rbd恢复时数据时要不要检验rdb的完整性
dbfilename dump.rdb # 导出来的rdb文件名
dir /usr/local/redis/data # rdb的放置路径
# 压力测试来检测是否启用了rdb快照
/usr/local/redis/bin/redis-benchmark
```
rdb的缺陷在2个保存点之间断电将会丢失1-N分钟的数据
## 1.2. aof日志持久化
工作原理redis主进程 > 后台日志进程 > aof文件
设置配置文件参数:
```bash
# 打开配置文件
vim /usr/local/redis/ redis.conf
appendonly no # 是否打开 aof日志功能
appendfilename "appendonly.aof" # aof文件名和rdb的dir公用一个路径
#appendfsync always # 每1个写命令都立即同步到aof文件安全但速度慢
appendfsync everysec # 折衷方案每秒写1次
上面方案选择一种一般选择everysec
appendfsync no # 写入工作交给操作系统由操作系统判断缓冲区大小统一写入到aof文件同步频率低但速度快
no-appendfsync-on-rewrite yes # 正在导出rdb快照的过程中要不要停止同步aof
auto-aof-rewrite-percentage 100 # aof文件大小比起上次重写时的大小增长率100%时重写
auto-aof-rewrite-min-size 64mb # aof文件至少超过64M时才重写
```
注意如果需要持久化一般推荐rdb和aof同时开启同时开启后redis进程启动优先选择aof恢复数据。rdb恢复速度快。
在dump rdb过程中aof如果停止同步会不会丢失? 不会所有的操作缓存在内存的队列里dump完成后统一操作.
aof重写是指什么? aof重写是指把内存中的数据逆化成命令写入到.aof日志里以解决 aof日志过大的问题手动重写aof命令bgrewriteaof
# 2. 导入和导出数据库
```bash
(1) 安装redis-dump工具
yum install ruby rubygems ruby-devel
gem sources --remove http://ruby.taobao.org/
gem sources -a https://ruby.taobao.org/
gem install redis-dump -V
(2) 导出redis数据
redis-dump -u 127.0.0.1:6379 > test.json
(3) 导入redis数据
< test.json redis-load
```

View File

@@ -0,0 +1,350 @@
- [1. 统计活跃用户](#1-统计活跃用户)
- [2. 搭建高可用redis集群](#2-搭建高可用redis集群)
- [1. 常见redis集群](#1-常见redis集群)
- [2. 单机版redis集群](#2-单机版redis集群)
- [2.1 修改redis配置文件](#21-修改redis配置文件)
- [2.2 重新打包redis镜像](#22-重新打包redis镜像)
- [2.3 编辑docker-compose.yml文件](#23-编辑docker-composeyml文件)
- [2.4 创建redis集群](#24-创建redis集群)
- [2.5 创建带有密码的redis集群](#25-创建带有密码的redis集群)
- [2.6 创建集群中遇到的问题](#26-创建集群中遇到的问题)
# 1. 统计活跃用户
场景: 1亿个用户用户登陆标记为今天活跃否则记为不活跃记录最活跃用户。
```bash
# 思路
每个用户在数据库都有一个id用第id个位的0和1来表示是否登录例如
login0721: '011001...............0'
......
login0726: '011001...............0'
login0727: '0110000.............1'
# 实现过程
(1) 记录用户登陆每天按日期生成一个位图用户登陆后把user_id位上的bit值置为1
首先把所有用户的位置位0
redis 127.0.0.1:6379> setbit login0721 100000000 0
(integer) 0
redis 127.0.0.1:6379> setbit login0721 3 1
(integer) 0
redis 127.0.0.1:6379> setbit login0721 5 1
(integer) 0
redis 127.0.0.1:6379> setbit login0721 7 1
(integer) 0
......
redis 127.0.0.1:6379> setbit login0722 100000000 0
(integer) 0
redis 127.0.0.1:6379> setbit login0722 3 1
(integer) 0
redis 127.0.0.1:6379> setbit login0722 5 1
(integer) 0
redis 127.0.0.1:6379> setbit login0722 8 1
(integer) 0
......
redis 127.0.0.1:6379> setbit login0723 100000000 0
(integer) 0
redis 127.0.0.1:6379> setbit login0723 3 1
(integer) 0
redis 127.0.0.1:6379> setbit login0723 4 1
(integer) 0
redis 127.0.0.1:6379> setbit login0723 6 1
(integer) 0
......
(2)把1周/月的位图用and计算, 位为1的是连续登陆的用户。
redis 127.0.0.1:6379> bitop and login0721 login0722 login0723......
(integer) 12500001
# 优点
(1) 节约空间用1亿bit表示1亿人每天的登陆情况1亿bit约为10M。
(2) 计算方便,计算速度非常快
```
# 2. 搭建高可用redis集群
## 1. 常见redis集群
常见redis集群有RedisCluster、Codis、Twemproxy其中Codis、Twemproxy是有中心节点的而RedisCluster是没中心节点而且是redis内置的集群方案推荐使用redisCluster集群。
redisCluster特点
- 无中心节点,客户端与 redis节点直连不需要中间代理层。
- 数据可以被分片存储,集群数据加起来就是全量数据。
- 可以通过任意一个节点,读写不属于本节点的数据。
- 管理方便,后续可自行增加或删除节点。
由于RedisCluster是分片存储当集群中一个节点故障时会损失该节点数据为了实现高可用需要对每个节点各添加一个从节点形成主从同步当主redis(集群节点)出现故障时,从节点替换故障的主节点。
- Redist集群中的数据库复制是通过主从同步来实现的。
- 主节点( Master)把数据分发给从节点(Slave)。
- 主从同步的好处在于高可用, Redis节点有冗余设计。
- 集群节点个数最好是奇数并且3个以上 奇数个的好处是,有一个节点出现故障了,集群数量过半数,集群可以继续正常使用,数量少于半数则认为集群瘫痪了。
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200601094320.png)
如果客户端本身带有负载均衡功能可以省去使用haproxy搭建负载均衡也可以在程序简单的实现负载均衡功能。
## 2. 单机版redis集群
在单机上搭建3个节点的集群3个主节点分别有各自的从节点如图所示
![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200601094436.png)
### 2.1 修改redis配置文件
因为官方镜像中没有redis配置也没有启动redis需要去官方下载对应版本的redis源码获取redis.conf和redis-trib.rb文件下载官方源码地址http://download.redis.io/releases/
```bash
mkdir redis-cluster
cd redis-cluster
wget http://download.redis.io/releases/redis-4.0.11.tar.gz
tar zxvf redis-4.0.11.tar.gz
# 从源码中复制redis.conf和redis-trib.rb文件到当前目录
cp redis-4.0.11/redis.conf .
cp redis-4.0.11/src/redis-trib.rb .
# 删除redis源码文件
rm -rf redis-4.0.11 redis-4.0.11.tar.gz
```
因为redis默认没有开启集群功能需要修改redis配置文件redis.conf主要修改下面几个参数
```bash
bind 0.0.0.0 # 允许外部登录
cluster-enabled yes # 开启集群
cluster-config-file nodes-6379.conf # 集群配置文件
cluster-node-timeout 15000 # 超时时间
appendonly yes # 并开启AOF模式
```
### 2.2 重新打包redis镜像
下载指定版本redis官方镜像
> docker pull redis:4.0.11
因为官方redis镜像不能满足搭建redis集群要求需要在官方镜像基础上添加ruby和启动redis命令功能在当前目录创建Dockerfile文件文件内容如下
```dockerfile
FROM redis
# 安装ruby
RUN apt-get -y update
RUN apt-get -y install ruby
RUN apt-get -y install rubygems
# ruby 安装redis接口
RUN gem install redis
# 安装vim
RUN apt-get -y install vim
# 启动redis
CMD redis-server /usr/local/etc/redis/redis.conf
```
### 2.3 编辑docker-compose.yml文件
在当前目录创建docker-compose.yml文件来管理容器共6个容器文件内容如下
```yaml
version: "3"
services:
redis-node1:
build:
context: .
dockerfile: Dockerfile
ports:
- 20001:6379
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
# 创建集群的配置文件,只需创建一次,其他节点不需要映射此文件
- ./redis-trib.rb:/usr/local/etc/redis/redis-trib.rb
networks:
- redis-net
redis-node2:
build:
context: .
dockerfile: Dockerfile
ports:
- 20002:6379
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
networks:
- redis-net
redis-node3:
build:
context: .
dockerfile: Dockerfile
ports:
- 20003:6379
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
networks:
- redis-net
redis-node4:
build:
context: .
dockerfile: Dockerfile
ports:
- 20004:6379
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
networks:
- redis-net
redis-node5:
build:
context: .
dockerfile: Dockerfile
ports:
- 20005:6379
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
networks:
- redis-net
redis-node6:
build:
context: .
dockerfile: Dockerfile
ports:
- 20006:6379
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
networks:
- redis-net
networks:
redis-net:
driver: bridge
```
### 2.4 创建redis集群
当前目录下完整文件列表如下所示:
```bash
.
├── docker-compose.yml
├── Dockerfile
├── redis.conf
└── redis-trib.rb
```
准备好文件后开始创建redis集群:
```bash
# 先打包好新的redis镜像
docker-compose build
# 启动容器
docker-compose up -d
# 获取所有启动redis容器ip地址获取ip地址是用来创建集群准备
docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq) | grep redis
# 执行命令的结果如下:
# /redis-cluster_redis-node2_1 - 172.20.0.6
# /redis-cluster_redis-node4_1 - 172.20.0.7
# /redis-cluster_redis-node6_1 - 172.20.0.4
# /redis-cluster_redis-node1_1 - 172.20.0.5
# /redis-cluster_redis-node5_1 - 172.20.0.3
# /redis-cluster_redis-node3_1 - 172.20.0.2
# 进入redis-node1容器因为只有redis-node1容器映射了redis-trib.rb文件
docker-compose exec redis-node1 bash
# 创建集群
/usr/local/etc/redis/redis-trib.rb create --replicas 1 172.20.0.2:6379 172.20.0.3:6379 172.20.0.4:6379 172.20.0.5:6379 172.20.0.6:6379 172.20.0.7:6379
# 其中参数--replicas 1表示每个主节点有一个从节点。
# 在创建集群过程会提示Can I set the above configuration? 在终端输入yes即可如果不出现错误很快就完成集群的创建。
# 创建完集群后在任意一个redis节点都可以进入redis集群-c表示连接的是集群
redis-cli -c
# 查看集群节点
127.0.0.1:6379> cluster nodes
# afb979236e96b6e9aac972af7508e4cd9505676d 172.20.0.6:6379@16379 slave 9715f836310b872961a7defc64bdb4a319f9848d 0 1537636386000 5 connected
# 1ffa00413a5f75465e1bb5aaef551c38fb3db1f3 172.20.0.5:6379@16379 myself,master - 0 1537636387000 7 connected 10923-16383
# 9715f836310b872961a7defc64bdb4a319f9848d 172.20.0.2:6379@16379 master - 0 1537636387811 1 connected 0-5460
# d59a0ad710f35fe349f62eda4279e02972983fd3 172.20.0.3:6379@16379 master - 0 1537636386000 2 connected 5461-10922
# c9371c4936d12b9f764e5bf5a7257928aa36cbc2 172.20.0.7:6379@16379 slave d59a0ad710f35fe349f62eda4279e02972983fd3 0 1537636385000 6 connected
# b39a684ad281f03c1befcd90048c503d95526645 172.20.0.4:6379@16379 slave 1ffa00413a5f75465e1bb5aaef551c38fb3db1f3 0 1537636386807 7 connected
# 测试集群的主从复制
127.0.0.1:6379> set testkey 100
# -> Redirected to slot [4757] located at 172.20.0.2:6379
# OK
# 根据172.20.0.2查到对应节点为redis-node3暂停该容器
docker-compose pause redis-node3
# 然后再次读看是否能够读取到数据
172.20.0.2:6379> get testkey
# "100"
# 此时可以查看到集群节点状态显示172.20.0.2和集群连接失败状态
127.0.0.1:6379> cluster nodes
```
### 2.5 创建带有密码的redis集群
实际使用中一般连接redis是需要密码授权的添加密码很简单创建集群前修改redis.conf配置和gems的redis客户端client.rb配置。
删除没有密码的集群容器,重新建一个集群,然后在原来基础上修改配置文件。
> docker-compose down
修改redis.conf配置
```bash
masterauth 123456
requirepass 123456
```
进入redis-node1容器修改redis客户端client.rb配置
```bash
# 找出client.rb配置文件位置
find / -name client.rb
# 在配置中添加密码
# 把 :password => nil 改为 :password => 123456
# 创建集群
/usr/local/etc/redis/redis-trib.rb create --replicas 1 172.20.0.2:6379 172.20.0.3:6379 172.20.0.4:6379 172.20.0.5:6379 172.20.0.6:6379 172.20.0.7:6379
# 带密码登录集群
redis-cli -a 123456 -c
```
### 2.6 创建集群中遇到的问题
> redis-trib.rb create replicas 1 redis-node1:6379 redis-node2:6379 redis-node3:6379 redis-node4:6379 redis-node5:6379 redis-node6:6379
问题1使用容器名创建去创建集群报错”ERR Invalid node address specified”
> redis-trib.rb 不支持域名或主机名必须使用ip:port的方式
问题2创建集群时报错err slot 0 is already busy (redis::commanderror)
> 答由于第一次创建集群没有成功但还在待创建状态需要将nodes.conf和dir里面的文件全部删除如果是在docker创建的话删除容器重新创建。

View File

@@ -0,0 +1,127 @@
# [01.redis安装](01.redis安装.md)
- [1. 在主机上安装](01.redis安装.md/#1-在主机上安装)
- [**(1) 修改后修改配置文件**](01.redis安装.md/#1-修改后修改配置文件)
- [**(2) 设置开机启动**](01.redis安装.md/#2-设置开机启动)
- [**(3) 将redis的bin命令添加到PATH**](01.redis安装.md/#3-将redis的bin命令添加到path)
- [2. 在docker安装redis](01.redis安装.md/#2-在docker安装redis)
- [**(1) 无持久化方式**](01.redis安装.md/#1-无持久化方式)
- [**(2) 有持久化方式**](01.redis安装.md/#2-有持久化方式)
- [**(3) 自定义redis配置**](01.redis安装.md/#3-自定义redis配置)
# [02.库和key操作命令](02.库和key操作命令.md)
- [key操作命令](02.库和key操作命令.md/#key操作命令)
- [set 设置key的值](02.库和key操作命令.md/#set-设置key的值)
- [get 获取key的值](02.库和key操作命令.md/#get-获取key的值)
- [del 删除key](02.库和key操作命令.md/#del-删除key)
- [exists 判断key是否存在](02.库和key操作命令.md/#exists-判断key是否存在)
- [type 获取key类型](02.库和key操作命令.md/#type-获取key类型)
- [expire 设置key有效期](02.库和key操作命令.md/#expire-设置key有效期)
- [tll 查看key有效期](02.库和key操作命令.md/#tll-查看key有效期)
- [rename 重命名key](02.库和key操作命令.md/#rename-重命名key)
- [renamenx 重命名不存在的key](02.库和key操作命令.md/#renamenx-重命名不存在的key)
- [persist 设置key永久有效](02.库和key操作命令.md/#persist-设置key永久有效)
- [move 把key移动到其他库](02.库和key操作命令.md/#move-把key移动到其他库)
- [库操作命令](02.库和key操作命令.md/#库操作命令)
- [dbsize 查看当前有多少个key](02.库和key操作命令.md/#dbsize-查看当前有多少个key)
- [select 选择库](02.库和key操作命令.md/#select-选择库)
- [flushdb 删除选中数据库中的key](02.库和key操作命令.md/#flushdb-删除选中数据库中的key)
- [flushall 删除所有库的key](02.库和key操作命令.md/#flushall-删除所有库的key)
# [03.字符串类型操作](03.字符串类型操作.md)
- [set 设置kv、效期、判断key是否存在](03.字符串类型操作.md#set-设置kv效期判断key是否存在)
- [mset 一次性输入多个kv](03.字符串类型操作.md#mset-一次性输入多个kv)
- [setrange 修改偏移字节值为valuev](03.字符串类型操作.md#setrange-修改偏移字节值为valuev)
- [append 在key的值后面追加字符串](03.字符串类型操作.md#append-在key的值后面追加字符串)
- [getrange 获取key值的部分内容](03.字符串类型操作.md#getrange-获取key值的部分内容)
- [getset 设置新值返回旧值](03.字符串类型操作.md#getset-设置新值返回旧值)
- [incr/decr 指定key的值加/减1](03.字符串类型操作.md#incrdecr-指定key的值加减1)
- [incrby/decrby 指定key的值加/减number](03.字符串类型操作.md#incrbydecrby-指定key的值加减number)
- [incrbyfloat 指定key的值加浮点数](03.字符串类型操作.md#incrbyfloat-指定key的值加浮点数)
- [setbit 设置二进制位上的值](03.字符串类型操作.md#setbit-设置二进制位上的值)
- [getbit 获取二进制位上的值](03.字符串类型操作.md#getbit-获取二进制位上的值)
- [bitop 对多个key逻辑操作](03.字符串类型操作.md#bitop-对多个key逻辑操作)
# [04.链表类型操作](04.链表类型操作.md)
- [lpush/rpush 在链表头/尾增加一个成员](04.链表类型操作.md/#lpushrpush-在链表头尾增加一个成员)
- [lrange 获取链表成员](04.链表类型操作.md/#lrange-获取链表成员)
- [lpop/rpop 弹出链表中头/尾的成员](04.链表类型操作.md/#lpoprpop-弹出链表中头尾的成员)
- [lrem 删除链表成员](04.链表类型操作.md/#lrem-删除链表成员)
- [lindex 获取链表索引对应的值](04.链表类型操作.md/#lindex-获取链表索引对应的值)
- [llen key 获取链表成员个数](04.链表类型操作.md/#llen-key-获取链表成员个数)
- [linsert 在链表中指定位置插入成员](04.链表类型操作.md/#linsert-在链表中指定位置插入成员)
- [blpop/brpop 一直等待弹出头/尾成员](04.链表类型操作.md/#blpopbrpop-一直等待弹出头尾成员)
# [05.无序集合操作](05.无序集合操作.md)
- [sadd 往集合添加成员](05.无序集合操作.md/#sadd-往集合添加成员)
- [srem 删除集合成员](05.无序集合操作.md/#srem-删除集合成员)
- [spop 随机删除集合一个成员](05.无序集合操作.md/#spop-随机删除集合一个成员)
- [srandmember 随机获取集合成员](05.无序集合操作.md/#srandmember-随机获取集合成员)
- [smembers 获取集合所有的成员](05.无序集合操作.md/#smembers-获取集合所有的成员)
- [sismember 判断成员是否存在集合中](05.无序集合操作.md/#sismember-判断成员是否存在集合中)
- [scard 获取集合成员的个数](05.无序集合操作.md/#scard-获取集合成员的个数)
- [smove 把一个集合中成员移动到另一个集合](05.无序集合操作.md/#smove-把一个集合中成员移动到另一个集合)
- [sunion 获取多个集合的并集](05.无序集合操作.md/#sunion-获取多个集合的并集)
- [sdiff 获取多个集合的差集](05.无序集合操作.md/#sdiff-获取多个集合的差集)
- [sinterstore 获取多个集合的交集并储存](05.无序集合操作.md/#sinterstore-获取多个集合的交集并储存)
# [06.有序集合操作](06.有序集合操作.md)
- [1. zadd 往有序集合添加成员](06.有序集合操作.md/#1-zadd-往有序集合添加成员)
- [2. zrange 按名次取成员](06.有序集合操作.md/#2-zrange-按名次取成员)
- [3. zrangebyscore 按分数取成员](06.有序集合操作.md/#3-zrangebyscore-按分数取成员)
- [4. zscore 获取指定成员的分数](06.有序集合操作.md/#4-zscore-获取指定成员的分数)
- [5. zcount 计算分数区间成员个数](06.有序集合操作.md/#5-zcount-计算分数区间成员个数)
- [6. zrank/zrevrank 获取成员升序/降序的排名](06.有序集合操作.md/#6-zrankzrevrank-获取成员升序降序的排名)
- [7. zrem 删除有序集合成员](06.有序集合操作.md/#7-zrem-删除有序集合成员)
- [8. zremrangebyrank 按排名删除成员](06.有序集合操作.md/#8-zremrangebyrank-按排名删除成员)
- [9. zremrangebyscore 按分数删除成员](06.有序集合操作.md/#9-zremrangebyscore-按分数删除成员)
- [10. zinterstore 求交集再计算](06.有序集合操作.md/#10-zinterstore-求交集再计算)
- [11. zunionstore 求并集再计算](06.有序集合操作.md/#11-zunionstore-求并集再计算)
# [07.哈希数据类操作](07.哈希数据类操作.md)
- [1. hset 设置哈希field域的值](07.哈希数据类操作.md/#1-hset-设置哈希field域的值)
- [2. hmset 设置哈希多个field域的值](07.哈希数据类操作.md/#2-hmset-设置哈希多个field域的值)
- [3. hget 获取field域的值](07.哈希数据类操作.md/#3-hget-获取field域的值)
- [4. hmget 获取多个field域的值](07.哈希数据类操作.md/#4-hmget-获取多个field域的值)
- [5. hgetall 获取所有field域和值](07.哈希数据类操作.md/#5-hgetall-获取所有field域和值)
- [6. hlen 获取field的数量](07.哈希数据类操作.md/#6-hlen-获取field的数量)
- [7. hdel 删除field域](07.哈希数据类操作.md/#7-hdel-删除field域)
- [8. hexists 判断field域是否存在](07.哈希数据类操作.md/#8-hexists-判断field域是否存在)
- [9. hincrby 使field域的值加上整数](07.哈希数据类操作.md/#9-hincrby-使field域的值加上整数)
- [10. hincrbyfloat 使field域的值加上浮点数](07.哈希数据类操作.md/#10-hincrbyfloat-使field域的值加上浮点数)
- [11. hkeys 获取所有所有field域的名字](07.哈希数据类操作.md/#11-hkeys-获取所有所有field域的名字)
- [12. kvals 获取所有所有field域的值](07.哈希数据类操作.md/#12-kvals-获取所有所有field域的值)
# [08.经纬度数据操作](08.经纬度数据操作.md)
- [1. geoadd 添加地理位置信息](08.经纬度数据操作.md/#1-geoadd-添加地理位置信息)
- [2. geopos 查询位置的坐标](08.经纬度数据操作.md/#2-geopos-查询位置的坐标)
- [3. geodist 查询位置距离](08.经纬度数据操作.md/#3-geodist-查询位置距离)
- [4. georadius 查询某点的附近点](08.经纬度数据操作.md/#4-georadius-查询某点的附近点)
- [5. georadiusbymember 查询某位置距离的附近点](08.经纬度数据操作.md/#5-georadiusbymember-查询某位置距离的附近点)
- [6. geohash 查询位置GEOHASH编码](08.经纬度数据操作.md/#6-geohash-查询位置geohash编码)
# [09.事务](09.事务.md)
- [1. 事务命令](09.事务.md/#1-事务命令)
- [2. 乐观锁](09.事务.md/#2-乐观锁)
# [10.频道发布与订阅](10.频道发布与订阅.md)
- [1. publish 发布频道](10.频道发布与订阅.md/#1-publish-发布频道)
- [2. subscribe 订阅指定频道](10.频道发布与订阅.md/#2-subscribe-订阅指定频道)
- [3. psubscribe 订阅已匹配频道](10.频道发布与订阅.md/#3-psubscribe-订阅已匹配频道)
# [11.redis持久化和导入导出数据库](11.redis持久化和导入导出数据库.md)
- [1. redis持久化](11.redis持久化和导入导出数据库.md/#1-redis持久化)
- [1.1. rdb快照持久化](11.redis持久化和导入导出数据库.md/#11-rdb快照持久化)
- [1.2. aof日志持久化](11.redis持久化和导入导出数据库.md/#12-aof日志持久化)
- [2. 导入和导出数据库](11.redis持久化和导入导出数据库.md/#2-导入和导出数据库)
# [12.redis应用示例](12.redis应用示例.md)
- [1. 统计活跃用户](12.redis应用示例.md/#1-统计活跃用户)
- [2. 搭建高可用redis集群](12.redis应用示例.md/#2-搭建高可用redis集群)
- [1. 常见redis集群](12.redis应用示例.md/#1-常见redis集群)
- [2. 单机版redis集群](12.redis应用示例.md/#2-单机版redis集群)
- [2.1 修改redis配置文件](12.redis应用示例.md/#21-修改redis配置文件)
- [2.2 重新打包redis镜像](12.redis应用示例.md/#22-重新打包redis镜像)
- [2.3 编辑docker-compose.yml文件](12.redis应用示例.md/#23-编辑docker-composeyml文件)
- [2.4 创建redis集群](12.redis应用示例.md/#24-创建redis集群)
- [2.5 创建带有密码的redis集群](12.redis应用示例.md/#25-创建带有密码的redis集群)
- [2.6 创建集群中遇到的问题](12.redis应用示例.md/#26-创建集群中遇到的问题)