0%

内容概要

  • JavaScript编程语言开头
  • 数值类型
  • 字符类型
  • 布尔值
  • null与undefined
  • 对象
  • 数组
  • 自定义对象
  • 流程控制
  • 函数
  • 内置对象
  • 时间对象
  • 正则对象
  • JSON对象
  • BOM操作(js操作浏览器 显示动态效果等…)
  • DOM操作
  • 事件

内容详细

Js简介

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
1.js也是一门编程语言 它也是可以写后端代码的
用js一统天下 前后端都可以写
nodejs 支持js代码跑在后端服务器上
然而并不能 想的太天真了!!!
2.js跟java一毛钱关系都没有,纯粹是为了蹭当时java的热度


ECMAScript和JavaScript的关系
因此ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现。


JS版本
主要还是用的5.16.0

JavaScript 是脚本语言
JavaScript 是一种轻量级的编程语言。
JavaScript 是可插入 HTML 页面的编程代码。
JavaScript 插入 HTML 页面后,可由所有的现代浏览器执行。
JavaScript 很容易学习

# js的注释
"""
// 单行注释

/*
多行注释1
多行注释2
多行注释3
*/
"""

# 两种引入方式
1.script标签内部直接书写js代码
2.script标签src属性引入外部js代码

# js语法结构
js是以分号作为语句的结束
但是如果你不写分号,问题也不大 也能够正常执行 但是它就相当于没有结束符

js学习流程

  • 变量
  • 数据类型
  • 流程控制
  • 函数
  • 对象
  • 内置方法/模块

变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"""
在js中 首次定义一个变量名的时候需要用关键字声明
1.关键字var
var name='jason'
2.es6推出的新语法
let name='jason'
如果你的编辑器支持的版本是5.1那么无法使用let
如果是6.0则向下兼容 var let
"""
# var与let的区别
n = 10
for n in range(5):
print(n)
print(n)
# var 5 let 10

"""
var在for循环里面定义也会影响到全局
let在局部定义只会在局部生效
"""

常量

1
2
3
# python中没有真正意义上的常量 默认全大写就是表示常量
# js中是有真正意义上的常量的
const pi = 3.14

变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
"""
js变量的命名规范
1.变量名只能是
数字 字母 下划线 $
2.变量名命名规范(不遵循也可以)
1.js中推荐使用驼峰式命名
userName
dataOfDb
2.python推荐使用下划线的方式
user_name
data_of_db
3.不能用关键字作为变量名
不需要记忆

js代码的书写位置
1.可以单独开设js文件书写
2.还可以直接在浏览器提供的console界面书写
在用浏览器书写js的时候 左上方的清空按钮只是清空当前页面 代码其实还在
如果你想要重新来 最好重新开设一个 页面
(在使用浏览器书写 你自己的js代码的时候推荐你在 自己的html页面打开)
"""

数据类型

js也是一门面向对象 的编程语言 即一切皆对象!!!

js/python是一门拥有动态类型

1
2
3
4
5
name = 'jason'
name = 123
name = [1,2,3,4]
# name可以指向任意的数据类型
# 但是有一些语言中,变量名之内指向一种后续不能更改

数值类型(number)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var a = 11;
var b = 11.11;
// 如何查看当前数据类型
typeof a;

var a = 11;
var b = 11.11;
typeof a;
typeof b;
"number"

// 特殊的 NaN:数值类型 表示的意思是“不是一个数字” NOT A NUMBER

// 类型转换
parseInt()
parseFloat()


parseInt('12312312')
12312312
parseFloat('11.11')
11.11
parseInt('11.11')
11
parseInt('123sdasdajs2312dasd')
123
parseInt('asdasdad123sdasdajs2312dasd')
NaN

字符类型(string)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
var s = 'jason'
undefined
typeof s
"string"
var s1 = "jason"
undefined
typeof s1;
"string"
var s2 = '''egon''' // 不支持三引号
VM665:1 Uncaught SyntaxError: Unexpected string

// 模版字符串
var s3 = `
asdkajsd
sdjkladj
asdjlajdkl
`
typeof s3
"string"
// 模版字符串除了可以定义多行文本之外还可以实现格式化字符串操作
// 书写${} 会自动去前面找大括号里面的变量名对应的值 如果没有定义直接报错
var name = 'jason'

var age = 18

var sss = `
my name is ${name} and my age is ${age}
`
sss
"
my name is jason and my age is 18
"

var s4 = `my name is ${namemmmmm}`
VM1140:1 Uncaught ReferenceError: namemmmmm is not defined
at <anonymous>:1:24
// 在写js代码的时候 不要去管左侧箭头的内容


// 字符串的拼接
// 在python中不推荐你使用+做拼接 join
// 在js中推荐你直接使用+做拼接
name + age

字符类型常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""
.length 返回长度
.trim() 移除空白
.trimLeft() 移除左边的空白
.trimRight() 移除右边的空白
.charAt(n) 返回第n个字符
.concat(value, ...) 拼接
联想记忆
MySQL
concat
concat_ws
group_concat
python
join
.indexOf(substring, start) 子序列位置
.substring(from, to) 根据索引获取子序列,不支持复数
.slice(start, end) 切片
.toLowerCase() 小写
.toUpperCase() 大写
.split(delimiter, limit) 分割
"""
# 记忆的时候一定要对比python去记

var name = 'egondsb'
undefined

name.length
7

var name1 = ' egonDSB '
undefined
name1
" egonDSB "
name1.trim()
"egonDSB"
name1.trimLeft()
"egonDSB "
name1.trimRight()
" egonDSB"

var name2 = '$$jason$$'
undefined
name2.trim('$') # 不能加括号指定去除的内容
"$$jason$$"

name2.charAt(0)
"$"
name2.indexOf('as')
3

name2.substring(0,5)
"$$jas"
name2.slice(0,5)
"$$jas"
name2.substring(0,-1) # 不识别负数
""
name2.slice(0,-1) # 后面推荐就使用slice就可以
"$$jason$"


var name3 = 'eGoNDsb123666HahA'
undefined
name3.toLowerCase()
"egondsb123666haha"
name3.toUpperCase()
"EGONDSB123666HAHA"
var name = 'tank|hecha|liaomei|mengsao|...'
undefined

name.split('|')
(5) ["tank", "hecha", "liaomei", "mengsao", "..."]
name.split('|',2)
(2) ["tank", "hecha"]0: "tank"1: "hecha"length: 2__proto__: Array(0)
name.split('|',10) # 第二个参数不是限制切割字符的个数而是获取切割之后元素的个数
(5) ["tank", "hecha", "liaomei", "mengsao", "..."]


name.concat(name1,name2)
"tank|hecha|liaomei|mengsao|... egonDSB $$jason$$"
var p = 1111
undefined
name.concat(p) # js是弱类型(内部会自动转换成相同的数据类型做操作)
"tank|hecha|liaomei|mengsao|...1111"


l = [1,2,3,4,5,6,7]
res = '|'.join(l) # 直接报错
print(res)

布尔值(boolean)

1
2
3
4
5
6
7
8
9
10
"""
1.在python中布尔值是首字母大写的
True
False
2.但是在js中布尔值是全小写的
true
false
# 布尔值是false的有哪些
空字符串、0、null、undefined、NaN
"""

null与undefined

1
2
3
4
5
6
7
8
9
10
11
12
"""
null
表示值为空 一般都是指定或者清空一个变量时使用
name = 'jason'
name = null
undefined
表示声明了一个变量 但是没有做初始化操作(没有给值)
函数没有指定返回值的时候 返回的也是undefined

参考博客图解:厕所卷纸
https://www.cnblogs.com/Dominic-Ji/p/9111021.html
"""

对象

一切皆对象

数组(类似于python里面的列表) []

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
var l = [11,22,33,44,55]

typeof l
"object"
var l1 = [11,'sdasd',11.11,true]

l1[1]
"sdasd"
l1[-1] # 不支持负数索引


var l = [111,222,333,444,555,666]
undefined
l.length
6
l.push(777)
7
l
(7) [111, 222, 333, 444, 555, 666, 777]
l.pop()
777
l
(6) [111, 222, 333, 444, 555, 666]
l.unshift(123)
7
l
(7) [123, 111, 222, 333, 444, 555, 666]
l.shift()
123
l.slice(0,3)
(3) [111, 222, 333]
l.reverse()
(6) [666, 555, 444, 333, 222, 111]
l.join('$') # 跟python刚好相反
"666$555$444$333$222$111"

l.concat([111,222,333]) # extend
(9) [666, 555, 444, 333, 222, 111, 111, 222, 333]
l.sort()
(6) [111, 222, 333, 444, 555, 666]

# 三个比较重要的方法
var ll = [111,222,333,444,555,666]
ll.forEach(function(value){console.log(value)},ll)
VM2277:1 111 # 一个参数就是数组里面每一个元素对象
VM2277:1 222
VM2277:1 333
VM2277:1 444
VM2277:1 555
VM2277:1 666

ll.forEach(function(value,index){console.log(value,index)},ll)
VM2346:1 111 0 # 两个参数就是元素 + 元素索引
VM2346:1 222 1
VM2346:1 333 2
VM2346:1 444 3
VM2346:1 555 4
VM2346:1 666 5
undefined
ll.forEach(function(value,index,arr){console.log(value,index,arr)},ll) # 元素 + 元素索引 + 元素的数据来源
VM2430:1 111 0 (6) [111, 222, 333, 444, 555, 666]
VM2430:1 222 1 (6) [111, 222, 333, 444, 555, 666]
VM2430:1 333 2 (6) [111, 222, 333, 444, 555, 666]
VM2430:1 444 3 (6) [111, 222, 333, 444, 555, 666]
VM2430:1 555 4 (6) [111, 222, 333, 444, 555, 666]
VM2430:1 666 5 (6) [111, 222, 333, 444, 555, 666]
undefined

ll.forEach(function(value,index,arr,xxx){console.log(value,index,arr,xxx)},ll) # 最多三个
VM2532:1 111 0 (6) [111, 222, 333, 444, 555, 666] undefined
VM2532:1 222 1 (6) [111, 222, 333, 444, 555, 666] undefined
VM2532:1 333 2 (6) [111, 222, 333, 444, 555, 666] undefined
VM2532:1 444 3 (6) [111, 222, 333, 444, 555, 666] undefined
VM2532:1 555 4 (6) [111, 222, 333, 444, 555, 666] undefined
VM2532:1 666 5 (6) [111, 222, 333, 444, 555, 666] undefined



ll
(6) [111, 222, 333, 444, 555, 666]
ll.splice(0,3) # 两个参数 第一个是起始位置 第二个是删除的个数
(3) [111, 222, 333]
ll
(3) [444, 555, 666]
ll.splice(0,1,777) # 先删除后添加
[444]
ll
(3) [777, 555, 666]
ll.splice(0,1,[111,222,333,444])
[777]
ll
(3) [Array(4), 555, 666]



var l1 = [11,22,33,44,55,66]
undefined
l1.map(function(value){console.log(value)},l1)
VM3115:1 11
VM3115:1 22
VM3115:1 33
VM3115:1 44
VM3115:1 55
VM3115:1 66
l1.map(function(value,index){return value*2},l1)
(6) [22, 44, 66, 88, 110, 132]
l1.map(function(value,index,arr){return value*2},l1)
(6) [22, 44, 66, 88, 110, 132]

运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 算术运算符
var x = 10;
var res1 = x++;
var res2 = ++x;
res1 10
res2 12
++表示自增1 类似于 +=1
加号在前先加后赋值
加号在后先赋值后加

# 比较运算符
1 == '1' # 弱等于 内部自动转换成相同的数据类型比较了
true

1 === '1' # 强等于 内部不做类型转换

1 != '1'
false
1 !== '2'
true

# 逻辑运算符
# python中 and or not
# js中 && || !
5 && '5'
'5'

0 || 1
1

!5 && '5'
false

"""
一定要注意到底什么时候返回的是布尔值 什么是返回的是数据
按照后端逻辑理解吧 js这块看看就行了
"""

# 赋值运算符
= += -= *= ....

流程控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# if判断
var age = 28;
# if(条件){条件成立之后指向的代码块}
if (age>18){
console.log('来啊 来啊')
}
# if-else
if (age>18){
console.log('来啊 来啊')
}else{
console.log('没钱 滚蛋')
}
# if-else if else
if (age<18){
console.log("培养一下")
}else if(age<24){
console.log('小姐姐你好 我是你的粉丝')
}else{
console.log('你是个好人')
}
"""
在js中代码是没有缩进的 只不过我们出于python书写习惯人为的加上了而已
()条件
{}代码块
"""

# switch语法
"""
提前列举好可能出现的条件和解决方式
"""
var num = 2;
switch(num){
case 0:
console.log('喝酒');
break; # 不加break 匹配到一个之后 就一直往下执行
case 1:
console.log('唱歌');
break;
case 2:
console.log('洗脚');
break;
case 3:
console.log('按摩');
break;
case 4:
console.log('营养快线');
break;
case 5:
console.log('老板慢走 欢迎下次光临');
break;
default:
console.log('条件都没有匹配上 默认走的流程')
}

# for循环
# 打印0-9数字
for(let i=0;i<10;i++){
console.log(i)
}
# 题目1 循环打印出数组里面的每一个元素
var l1 = [111,222,333,444,555,666]
for(let i=0;i<l1.length;i++){
console.log(l1[i])
}

# while循环
var i = 0
while(i<100){
console.log(i)
i++;
}

# 三元运算符
# python中三元运算符 res = 1 if 1>2 else 3
# JS中三元运算 res = 1>2?1:3
条件成立取问好后面的1 不成立取冒号后面的3
var res = 2>5?8:10 # 10
var res = 2>5?8:(8>5?666:444) # 666
"""
三元运算符不要写的过于复杂
"""

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 在python定义函数需要用到关键字def
# 在js中定义函数需要用到关键字function

# 格式
function 函数名(形参1,形参2,形参3...){函数体代码}

# 无参函数
function func1(){
console.log('hello world')
}
func1() # 调用 加括调用 跟python是一样的

# 有参函数
function func2(a,b){
console.log(a,b)
}
func2(1,2)

func2(1,2,3,4,5,6,7,8,9) # 多了没关系 只要对应的数据
VM3610:2 1 2
undefined

func2(1) # 少了也没关系
VM3610:2 1 undefined

# 关键字arguments
function func2(a,b){
console.log(arguments) # 能够获取到函数接受到的所有的参数
console.log(a,b)
}

function func2(a,b){
if(arguments.length<2){
console.log('传少了')
}else if (arguments.length>2){
console.log('传多了')
}else{
console.log('正常执行')
}
}


# 函数的返回值 使用的也是关键字return
function index(){
return 666
}
function index(){
return 666,777,888,999
}
res = index();
999
res
999 # 只能拿到最后一个

function index(){
return [666,777,888,999]
}
# js不支持解压赋值 a,b,c,d = index()不支持


# 匿名函数 就是没有名字
function(){
console.log('哈哈哈')
}
var res = function(){
console.log('哈哈哈')
}

# 箭头函数(要了解一下) 主要用来处理简单的业务逻辑 类似于python中的匿名函数
var func1 = v => v; """箭头左边的是形参 右边的是返回值"""
等价于
var func1 = function(v){
return v
}

var func2 = (arg1,arg2) => arg1+arg2
等价于
var func1 = function(arg1,arg2){
return arg1+arg2
}

函数的全局变量与局部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 跟python查找变量的顺序一致
var city = "BeiJing";
function f() {
var city = "ShangHai";
function inner(){
var city = "ShenZhen";
console.log(city);
}
inner();
}

f(); //输出结果是?


var city = "BeiJing";
function Bar() {
console.log(city);
}
function f() {
var city = "ShangHai";
return Bar;
}
var ret = f();
ret(); // 打印结果是?


var city = "BeiJing";
function f(){
var city = "ShangHai";
function inner(){
console.log(city);
}
return inner;
}
var ret = f();
ret();

自定义对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 你可以看成是我们python中的字典 但是js中的自定义对象要比python里面的字典操作起来更加的方便

# 创建自定义对象 {}
"""第一种创建自定义对象的方式"""
var d1 = {'name':'jason','age':18}


var d = {'name':'jason','age':18}
typeof d
"object"

d['name']
"jason"
d.name # 比python从字典获取值更加的方便
"jason"
d.age
18

for(let i in d){
console.log(i,d[i])
} # 支持for循环 暴露给外界可以直接获取的也是键


"""第二种创建自定义对象的方式 需要使用关键字 new"""
var d2 = new Object() # {}

d2.name = 'jason'
{name: "jason"}

d2['age'] = 18
{name: "jason", age: 18}

Date对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let d3 = new Date()
Fri May 15 2020 14:41:06 GMT+0800 (中国标准时间)

d3.toLocaleString()
"2020/5/15 下午2:41:06"

# 也支持自己手动输入时间
let d4 = new Date('2200/11/11 11:11:11')
d4.toLocaleString()

let d5 = new Date(1111,11,11,11,11,11)
d5.toLocaleString() # 月份从0开始0-11
"1111/12/11 上午11:11:11"

# 时间对象具体方法
let d6 = new Date();
d6.getDate() 获取日
d6.getDay() 获取星期
d6.getMonth() 获取月份(0-11)
d6.getFullYear() 获取完整的年份
d6.getHours() 获取小时
d6.getMinutes() 获取分钟
d6.getSeconds() 获取秒
d6.getMilliseconds() 获取毫秒
d6.getTime() 时间戳

JSON对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"""
在python中序列化反序列化
dumps 序列化
loads 反序列化

在js中也有序列化反序列化
JSON.stringify() dumps
JSON.parse() loads
"""
let d7 = {'name':'jason','age':18}
let res666 = JSON.stringify(d7)
"{"name":"jason","age":18}"

JSON.parse(res666)
{name: "jason", age: 18}

RegExp对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"""
在python中如果需要使用正则 需要借助于re模块
在js中需要你创建正则对象
"""
# 第一种 有点麻烦
let reg1 = new RegExp('^[a-zA-Z][a-zA-Z0-9]{5,11}')
# 第二种 个人推荐
let reg2 = /^[a-zA-Z][a-zA-Z0-9]{5,11}/

# 匹配内容
reg1.test('egondsb')
reg2.test('egondsb')

# 题目 获取字符串里面所有的字母s
let sss = 'egondsb dsb dsb'
sss.match(/s/) # 拿到一个就停止了
sss.match(/s/g) # 全局匹配 g就表示全局模式

sss.match(/s/)
["s", index: 5, input: "egondsb dsb dsb", groups: undefined]
sss.match(/s/g)
(3) ["s", "s", "s"]

# 全局匹配模式吐槽点
let reg3 = /^[a-zA-Z][a-zA-Z0-9]{5,11}/g
reg2.test('egondsb')

reg3.test('egondsb') # 全局模式有一个lastIndex属性
true
reg3.test('egondsb')
false
reg3.test('egondsb')
true
reg3.test('egondsb')
false

reg3.lastIndex
0
reg3.test('egondsb')
true
reg3.lastIndex
7

# 吐槽点二
let reg4 = /^[a-zA-Z][a-zA-Z0-9]{5,11}/
reg4.test()

reg4.test() # 什么都不传 默认传的是undefined
true
reg4.test()
true

reg4.test(undefined)
true
let reg5 = /undefined/
undefined
reg5.test('jason')
false
reg5.test()
true

"""
总结 你在用js书写正则的时候一定要注意上述问题
一般情况下你后续也不会解除到了
"""

Math对象(有兴趣就看一下)

abs(x) 返回数的绝对值。
exp(x) 返回 e 的指数。
floor(x) 对数进行下舍入。
log(x) 返回数的自然对数(底为e)。
max(x,y) 返回 x 和 y 中的最高值。
min(x,y) 返回 x 和 y 中的最低值。
pow(x,y) 返回 x 的 y 次幂。
random() 返回 0 ~ 1 之间的随机数。
round(x) 把数四舍五入为最接近的整数。
sin(x) 返回数的正弦。
sqrt(x) 返回数的平方根。
tan(x) 返回角的正切。

BOM与DOM操作

1
2
3
4
5
6
7
8
9
# 截至目前为止 我们虽然已经学会了js语法 但是你会发现跟浏览器和html文件还是一点关系没有
"""
BOM
浏览器对象模型 Browser Object Model
js代码操作浏览器
DOM
文档对象模型 Document Object Model
js代码操作标签
"""

BOM操作

1
2
3
4
5
6
7
8
9
10
11
12
13
# window对象
window对象指代的就是浏览器窗口

window.innerHeight 浏览器窗口的高度
900
window.innerWidth 浏览器窗口的宽度
1680

window.open('https://www.mzitu.com/','','height=400px,width=400px,top=400px,left=400px')
# 新建窗口打开页面 第二个参数写空即可 第三个参数写新建的窗口的大小和位置
# 扩展父子页面通信window.opener() 了解

window.close() 关闭当前页面

window子对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
window.navigator.appName
"Netscape"
window.navigator.appVersion
"5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"

window.navigator.userAgent 掌握 # 用来表示当前是否是一个浏览器
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"

"""
扩展:仿爬措施
1.最简单最常用的一个就是校验当前请求的发起者是否是一个浏览器
userAgent
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
如何破解该措施
在你的代码中加上上面的user-agent配置即可
"""

window.navigator.platform
"MacIntel"

# 如果是window的子对象 那么window可以省略不写

history对象

1
2
3
window.history.back()  		回退到上一页
window.history.forward() 前进到下一页
# 对应的就是你浏览器左上方的两个的箭头

location对象(掌握)

1
2
3
window.location.href        # 获取当前页面的url
window.location.href = url # 跳转到指定的url
window.location.reload() # 属性页面 浏览器左上方的小圆圈

弹出框

  • 警告框
  • 确认框
  • 提示框
1
2
3
4
5
6
7
8
9
10
alert('你不要过来啊!!!')   --- 警告框
undefined

confirm('你确定真的要这么做吗?能不能有其他方式能够满足你...') --- 确认框
false
confirm('你确定真的要这么做吗?能不能有其他方式能够满足你...') --- 确认框
true

prompt('手牌号给我看一下','22号消费888') --- 提示框
"来宾三位"

计时器相关(掌握)

  • 过一段时间之后触发(一次)
  • 每隔一段时间触发一次(循环)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
function func1() {
alert(123)
}
let t = setTimeout(func1,3000); // 毫秒为单位 3秒之后自动执行func1函数

clearTimeout(t) // 取消定时任务 如果你想要清除定时任务 需要日前用变量指代定时任务


function func2() {
alert(123)
}
function show(){
let t = setInterval(func2,3000); // 每隔3秒执行一次
function inner(){
clearInterval(t) // 清除定时器
}
setTimeout(inner,9000) // 9秒中之后触发
}
show()
</script>

DOM操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
"""
https://www.cnblogs.com/Dominic-Ji/p/9121560.html DOM树结构图

DOM树的概念

所有的标签都可以称之为是节点

JavaScript 可以通过DOM创建动态的 HTML:

JavaScript 能够改变页面中的所有 HTML 元素
JavaScript 能够改变页面中的所有 HTML 属性
JavaScript 能够改变页面中的所有 CSS 样式
JavaScript 能够对页面中的所有事件做出反应


DOM操作操作的是标签 而一个html页面上的标签有很多
1.先学如何查找标签
2.再学DOM操作标签

DOM操作需要用关键字document起手
"""

查找标签

  • 直接查找(必须要掌握)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    """
    id查找
    类查找
    标签查找
    """
    # 注意三个方法的返回值是不一样的
    document.getElementById('d1') --- id查找
    <div id="d1">…</div>

    document.getElementsByClassName('c1') --- 类查找
    HTMLCollection [p.c1]0: p.c1length: 1__proto__: HTMLCollection

    document.getElementsByTagName('div') --- 标签查找
    HTMLCollection(3) [div#d1, div, div, d1: div#d1]


    let divEle = document.getElementsByTagName('div')[1]
    divEle
    <div>div>div</div>
    """
    当你用变量名指代标签对象的时候 一般情况下都推荐你书写成
    xxxEle
    divEle
    aEle
    pEle
    """
  • 间接查找(熟悉)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    let pEle = document.getElementsByClassName('c1')[0]  # 注意是否需要索引取值

    pEle.parentElement # 拿父节点
    <div id="d1">"div
    "<div>div>div</div><p class="c1">…</p><p>div>p</p></div>
    pEle.parentElement.parentElement
    <body>…</body>
    pEle.parentElement.parentElement.parentElement
    <html lang="en"><head></head><body></body></html>
    pEle.parentElement.parentElement.parentElement.parentElement
    null

    let divEle = document.getElementById('d1')
    divEle.children # 获取所有的子标签
    divEle.children[0]
    <div>div>div</div>

    divEle.firstElementChild
    <div>div>div</div>

    divEle.lastElementChild
    <p>div>p</p>

    divEle.nextElementSibling # 同级别下面第一个
    <div>div下面div</div>

    divEle.previousElementSibling # 同级别上面第一个
    <div>div上面的div</div>

    节点操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    """
    通过DOM操作动态的创建img标签
    并且给标签加属性
    最后将标签添加到文本中
    """
    let imgEle = document.createElement('img') # 创建标签

    imgEle.src = '111.png' # 给标签设置默认的属性
    "111.png"
    imgEle

    imgEle.username = 'jason' # 自定义的属性没办法点的方式直接设置
    "jason"
    imgEle
    <img src="111.png">


    imgEle.setAttribute('username','jason') # 既可以设置自定义的属性也可以设置默认的书写
    undefined
    imgEle
    <img src="111.png" username="jason">
    imgEle.setAttribute('title','一张图片')
    imgEle
    <img src="111.png" username="jason" title="一张图片">

    let divEle = document.getElementById('d1')
    undefined
    divEle.appendChild(imgEle) # 标签内部添加元素(尾部追加)
    <img src="111.png" username="jason" title="一张图片">


    """
    创建a标签
    设置属性
    设置文本
    添加到标签内部
    添加到指定的标签的上面
    """
    let aEle = document.createElement('a')

    aEle
    <a></a>
    aEle.href = 'https://www.mzitu.com/'
    "https://www.mzitu.com/"
    aEle
    <a href="https://www.mzitu.com/"></a>

    aEle.innerText = '点我有你好看!' # 给标签设置文本内容
    "点我有你好看!"
    aEle
    <a href="https://www.mzitu.com/">点我有你好看!</a>
    let divEle = document.getElementById('d1')
    undefined
    let pEle = document.getElementById('d2')
    undefined
    divEle.insertBefore(aEle,pEle) # 添加标签内容指定位置添加
    <a href="https://www.mzitu.com/">点我有你好看!</a>


    """
    额外补充
    appendChild()
    removeChild()
    replaceChild()


    setAttribute() 设置属性
    getAttribute() 获取属性
    removeAttribute() 移除属性
    """

    # innerText与innerHTML
    divEle.innerText # 获取标签内部所有的文本
    "div 点我有你好看!
    div>p
    div>span"

    divEle.innerHTML # 内部文本和标签都拿到
    "div
    <a href="https://www.mzitu.com/">点我有你好看!</a><p id="d2">div&gt;p</p>
    <span>div&gt;span</span>
    "

    divEle.innerText = 'heiheihei'
    "heiheihei"
    divEle.innerHTML = 'hahahaha'
    "hahahaha"

    divEle.innerText = '<h1>heiheihei</h1>' # 不识别html标签
    "<h1>heiheihei</h1>"
    divEle.innerHTML = '<h1>hahahaha</h1>' # 识别html标签
    "<h1>hahahaha</h1>"

    获取值操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # 获取用户数据标签内部的数据
    let seEle = document.getElementById('d2')
    seEle.value
    "111"
    seEle.value
    "333"

    let inputEle = document.getElementById('d1')
    inputEle.value

    # 如何获取用户上传的文件数据
    let fileEle = document.getElementById('d3')
    fileEle.value # 无法获取到文件数据
    "C:\fakepath\02_测试路由.png"

    fileEle.files
    FileList {0: File, length: 1}0: File {name: "02_测试路由.png", lastModified: 1557043082000, lastModifiedDate: Sun May 05 2019 15:58:02 GMT+0800 (中国标准时间), webkitRelativePath: "", size: 29580, …}length: 1__proto__: FileList

    fileEle.files[0] # 获取文件数据
    File {name: "02_测试路由.png", lastModified: 1557043082000, lastModifiedDate: Sun May 05 2019 15:58:02 GMT+0800 (中国标准时间), webkitRelativePath: "", size: 29580, …}

    class、css操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    let divEle = document.getElementById('d1')
    undefined
    divEle.classList # 获取标签所有的类属性
    DOMTokenList(3) ["c1", "bg_red", "bg_green", value: "c1 bg_red bg_green"]


    divEle.classList.remove('bg_red') # 移除某个类属性
    undefined

    divEle.classList.add('bg_red') # 添加类属性
    undefined
    divEle.classList.contains('c1') # 验证是否包含某个类属性
    true
    divEle.classList.contains('c2')
    false

    divEle.classList.toggle('bg_red') # 有则删除无则添加
    false
    divEle.classList.toggle('bg_red')
    true
    divEle.classList.toggle('bg_red')
    false
    divEle.classList.toggle('bg_red')
    true
    divEle.classList.toggle('bg_red')
    false
    divEle.classList.toggle('bg_red')
    true



    # DOM操作操作标签样式 统一先用style起手
    let pEle = document.getElementsByTagName('p')[0]
    undefined
    pEle.style.color = 'red'
    "red"
    pEle.style.fontSize = '28px'
    "28px"
    pEle.style.backgroundColor = 'yellow'
    "yellow"
    pEle.style.border = '3px solid red'
    "3px solid red"

事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
"""
达到某个事先设定的条件 自动触发的动作
"""
# 绑定事件的两种方式
<button onclick="func1()">点我</button>
<button id="d1">点我</button>

<script>
// 第一种绑定事件的方式
function func1() {
alert(111)
}
// 第二种绑定事件的方式(推荐)
let btnEle = document.getElementById('d1');
btnEle.onclick = function () {
alert(222)
}
</script>


"""
script标签既可以放在head内 也可以放在body内
但是通常情况下都是放在body内的最底部


# 等待浏览器窗口加载完毕之后再执行代码
window.onload = function () {
// 第一种绑定事件的方式
function func1() {
alert(111)
}
// 第二种绑定事件的方式
let btnEle = document.getElementById('d1');
btnEle.onclick = function () {
alert(222)
}
}
"""

原生js事件绑定

我们直接写几个案例,看懂即可

  • 开关灯案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <div id="d1" class="c1 bg_red bg_green"></div>
    <button id="d2">变色</button>

    <script>
    let btnEle = document.getElementById('d2')
    let divEle = document.getElementById('d1')
    btnEle.onclick = function () { // 绑定点击事件
    // 动态的修改div标签的类属性
    divEle.classList.toggle('bg_red')
    }
    </script>
  • input框获取焦点失去焦点案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <input type="text" value="老板 去吗?" id="d1">

    <script>
    let iEle = document.getElementById('d1')
    // 获取焦点事件
    iEle.onfocus = function () {
    // 将input框内部值去除
    iEle.value = ''
    // 点value就是获取 等号赋值就是设置
    }
    // 失去焦点事件
    iEle.onblur = function () {
    // 给input标签重写赋值
    iEle.value = '没钱 不去!'
    }
    </script>
  • 实时展示当前时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <input type="text" id="d1" style="display: block;height: 50px;width: 200px">
    <button id="d2">开始</button>
    <button id="d3">结束</button>

    <script>
    // 先定义一个全局存储定时器的变量
    let t = null
    let inputEle = document.getElementById('d1')
    let startBtnEle = document.getElementById('d2')
    let endBtnEle = document.getElementById('d3')
    // 1 访问页面之后 将访问的时间展示到input框中
    // 2 动态展示当前时间
    // 3 页面上加两个按钮 一个开始 一个结束
    function showTime() {
    let currentTime = new Date();
    inputEle.value = currentTime.toLocaleString()
    }

    startBtnEle.onclick = function () {
    // 限制定时器只能开一个
    if(!t){
    t = setInterval(showTime,1000) // 每点击一次就会开设一个定时器 而t只指代最后一个
    }
    }
    endBtnEle.onclick = function () {
    clearInterval(t)
    // 还应该将t重置为空
    t = null
    }
    </script>
  • 省市联动

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    <select name="" id="d1">
    <option value="" selected disabled>--请选择--</option>
    </select>
    <select name="" id="d2"></select>

    <script>
    let proEle = document.getElementById('d1')
    let cityEle = document.getElementById('d2')
    // 先模拟省市数据
    data = {
    "河北": ["廊坊", "邯郸",'唐山'],
    "北京": ["朝阳区", "海淀区",'昌平区'],
    "山东": ["威海市", "烟台市",'临沂市'],
    '上海':['浦东新区','静安区','黄浦区'],
    '深圳':['南山区','宝安区','福田区']
    };
    // 选for循环获取省
    for(let key in data){
    // 将省信息做成一个个option标签 添加到第一个select框中
    // 1 创建option标签
    let opEle = document.createElement('option')
    // 2 设置文本
    opEle.innerText = key
    // 3 设置value
    opEle.value = key // <option value="省">省</option>
    // 4 将创建好的option标签添加到第一个select中
    proEle.appendChild(opEle)
    }
    // 文本域变化事件 change事件
    proEle.onchange = function () {
    // 先获取到用户选择的省
    let currentPro = proEle.value
    // 获取对应的市信息
    let currentCityList = data[currentPro]
    // 清空市select中所有的option
    cityEle.innerHTML = ''
    // 自己加一个请选择
    let ss = "<option disabled selected>请选择</option>"
    // let oppEle = document.createElement('option')
    // oppEle.innerText = '请选择'
    // oppEle.setAttribute('selected','selected')
    cityEle.innerHTML = ss

    // for循环所有的市 渲染到第二个select中
    for (let i=0;i<currentCityList.length;i++){
    let currentCity = currentCityList[i]
    // 1 创建option标签
    let opEle = document.createElement('option')
    // 2 设置文本
    opEle.innerText = currentCity
    // 3 设置value
    opEle.value = currentCity // <option value="省">省</option>
    // 4 将创建好的option标签添加到第一个select中
    cityEle.appendChild(opEle)
    }
    }
    </script>
    </body>
    </html>

前端目前形势

前端发展史

1.HTML(5)、CSS(3)、JavaScript(ES5、ES6):编写一个个的页面 -> 给后端(PHP、Python、Go、Java) -> 后端嵌入模板语法 -> 后端渲染完数据 -> 返回数据给前端 -> 在浏览器中查看

2.Ajax的出现 -> 后台发送异步请求,Render+Ajax混合

3.单用Ajax(加载数据,DOM渲染页面):前后端分离的雏形

4.Angular框架的出现(1个JS框架):出现了“前端工程化”的概念(前端也是1个工程、1个项目)

5.ReactVue框架:当下最火的2个前端框架(Vue:国人喜欢用,React:外国人喜欢用)

6.移动开发(Android+IOS) + Web(Web+微信小程序+支付宝小程序) + 桌面开发(Windows桌面):前端 -> 大前端

7.一套代码在各个平台运行(大前端):谷歌Flutter(Dart语言:和Java很像)可以运行在IOS、Android、PC端

8.在Vue框架的基础性上 uni-app一套编码 编到10个平台

9.在不久的将来 ,前端框架可能会一统天下

介绍

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架

与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用

Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合

渐进式框架

可以一点一点地使用它,只用一部分,也可以整个工程都使用它

网站

特点

  • 易用
    • 通过HTML、CSS、JavaScript构建应用
  • 灵活
    • 不断繁荣的生态系统,可以在一个库和一套完整框架之间自如伸缩
  • 高效
    • 20kB min+gzip 运行大小
    • 超快虚拟DOM
    • 最省心的优化

M-V-VM思想

介绍

MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,是一种事件驱动编程方式

  • Model :vue对象的data属性里面的数据,这里的数据要显示到页面中
  • View :vue中数据要显示的HTML页面,在vue中,也称之为“视图模板” (HTML+CSS)
  • ViewModel:vue中编写代码时的vm对象,它是vue.js的核心,负责连接 View 和 Model数据的中转,保证视图和数据的一致性,所以前面代码中,data里面的数据被显示中p标签中就是vm对象自动完成的(双向数据绑定:JS中变量变了,HTML中数据也跟着改变)

img

特性

  • 低耦合视图(View)可以独立于Model变化和修改,1个ViewModel可以绑定到不同的View上,当View变化的时候 Model可以不变,当Model变化的时候 View也可以不变
  • 可复用:可以把一些视图逻辑放在1个ViewModel中,让很多View重用这端视图的逻辑(以此减少代码冗余)
  • 独立开发开发人员可以专注于业务逻辑数据的开发(ViewModel),设计人员可以专注于页面设计
  • 可测试:界面元素是比较难以测试的,而现在的测试可以针对ViewModel来编写

MVVM的逻辑

img

组件化开发、单页面开发

类似于DTL中的include,每一个组件的内容都可以被替换和复用

img

单页面开发

  • 只需要一个页面,结合组件化开发来替换页面中的内容
  • 页面的切换只是组件的替换,页面还是只有一个index.html

版本

  • 1.X:使用得较少
  • 2.X:普遍使用
  • 3.X:刚出没多久,只有Beta版

引入方式

补充

  • 解释型的语言是需要解释器的
  • nodejs:一门后端语言

使用

模板语法

1
{{变量、js语法}}

指令之文本指令

1
2
3
4
v-html		# 让HTML字符串渲染成标题
v-text # 标签内容显示js变量对应的值
v-show # 放一个布尔值:为真 标签就显示;为假,标签就隐藏
v-if # 放一个布尔值:为真 标签就显示;为假,标签就隐藏(删除)

指令之事件指令

1
2
3
v-on:事件		# 触发事件(不推荐)
@事件 # 触发事件(推荐)
@[event] # 触发event事件(可以是其他任意事件)

Style与Class

数据的绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
:属性名 = js变量/js语法

class: 字符串、三目运算符、数组、对象{red:true}
:class='js变量、字符串、js数组'

style: 字符串、三目运算符、数组[{backgreound:'red'}]、对象{red:true}

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.cls_obj_red{
background-color: red;
}
</style>
</head>
<body>
<div id="app">
<!-- <p :class="cls_obj">阿巴阿巴</p>-->
<!-- <p :style="style_obj">滴滴答答</p>-->
</div>

<script>
var vm = new Vue({
el:'#app',
data:{
// cls_obj:{cls_obj_red:true}
// cls_obj:['cls_obj_red']
// cls_obj:'cls_obj_red'
// style_obj:'background-color:red'
// style_obj:[{backgroundColor:'red'},]
// style_obj:{backgroundColor:'red'}
}
})
</script>
</body>
</html>

条件渲染

指令 释义
v-if 相当于: if
v-else 相当于:else
v-else-if 相当于:else if
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.cls_obj_red{
background-color: red;
}
</style>
</head>
<body>
<div id="app">
<p v-if="show">嘀哩嘀哩</p>
<p v-if="type==1">1</p>
<p v-else-if="type==2">2</p>
<p v-else-if="type==3">3</p>
<p v-else="">Boom!</p>
</div>

<script>
var vm = new Vue({
el:'#app',
data:{
show:true,
type:1
}
})
</script>
</body>
</html>

列表渲染

v-if + v-for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link rel="stylesheet" href="bootstrap.min.css">

</head>
<body>
<div id="app">
<button @click="shopping_click" class="btn btn-primary">哔哩哔哩</button>
<table class="table table-hover table-striped">
<thead>
<tr>
<th>编号</th>
<th>商品名</th>
<th>单价</th>
</tr>
</thead>
<tbody>
<tr v-if="shopping.length==0">
<td>售罄</td>
<td>售罄</td>
<td>售罄</td>
</tr>
<tr v-else v-for="value,key in shopping">
<td>{{key+1}}</td>
<td>{{value.name}}</td>
<td>{{value.price}}</td>
</tr>
</tbody>
</table>
</div>

<script>
var vm = new Vue({
el:'#app',
data:{
shopping:[]
},
methods:{
shopping_click(){
this.shopping=[
{id:1,name:'小包子', price:188},
{id:2,name:'包子', price:288},
{id:3,name:'大包子', price:388}
]
}
}
})
</script>
</body>
</html>

v-for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link rel="stylesheet" href="bootstrap.min.css">

<style>
.cls_obj_red{
background-color: red;
}
</style>
</head>
<body>
<div id="app">
<p v-for="name in names">id:{{name.cid}} name: {{name.name}} age: {{name.age}}</p>
</div>

<script>
var vm = new Vue({
el:'#app',
data:{
names:[
{cid:1, name:'jerry', age:18},
{cid:2, name:'tom', age:19},
{cid:3, name:'哔哩哔哩', age:20}
]
}
})
</script>
</body>
</html>

数组更新与检测

1
2
3
4
<!--vue中使用的是虚拟DOM, 会和原生的DOM进行比较, 然后进行数据的更新,提高数据的刷新速度(虚拟DOM用了diff算法)-->

一定会触发DOM的比较,如果有数据变了,页面没变,使用该方法赋值 ↓
Vue.set(vm.class_obj, 'background', true)

过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link rel="stylesheet" href="bootstrap.min.css">

</head>
<body>
<div id="app">

<input type="text" @input="changeData" v-model="search" >
<p v-for="value in new_list">{{value}}</p>
</div>

<script>
var vm = new Vue({
el:'#app',
data:{
search:'',
list:['a', 'ab', 'b', 'bs'],
new_list:['a', 'ab', 'b', 'bs']
},
methods: {
changeData(){
this.new_list = this.list.filter(item=>{
return item.indexOf(this.search)>-1
})
}
}
})
</script>
</body>
</html>

事件修饰符

事件修饰符 释义
.stop 只处理自己的事件,父控件冒泡的事件不处理(阻止事件冒泡)
.self 只处理自己的事件,子控件冒泡的事件不处理
.prevent 阻止a链接的跳转
.once 事件只会触发一次(适用于抽奖页面)

使用修饰符时,顺序很重要,相应的代码会以同样的顺序产生

1
2
@click.prevent.self		:会阻止所有的点击
@click.self.prevent :只会阻止对元素自身的点击
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件修饰符</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
<!-- <ul @click="handleUl">-->
<ul @click.self="handleUl">
<!-- <li v-for="data in dataList" @click="handleLi">{{data}}</li>-->
<li v-for="data in dataList" @click.stop="handleLi">{{data}}</li>
<li><a href="http://www.baidu.com">不拦截</a></li>
<li><a href="http://www.baidu.com" @click="handleLink($event)">点击拦截</a></li>
<li><a href="https://www.baidu.com" @click.prevent="handleLink">点击拦截</a></li>
<li><button @click.once="test">只执行一次</button></li>
</ul>
</div>
</body>
<script>
var vm = new Vue({
el: '#box',
data: {
dataList: ['1','22','333','4444']
},
methods: {
handleUl(ev){
console.log('ul被点击了')
},
handleLi(){
console.log('li被点击了')
ev.stopPropagation() // 点击事件停止 冒泡(向父组件传递时间)
},
handleLink(ev){
ev.preventDefault()
},
test(){
alert('只触发1次')
}
}
})
</script>
</html>

数据的双向绑定(v-model的使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link rel="stylesheet" href="bootstrap.min.css">


</head>
<body>
<div id="app">

<input type="text" v-model="test">{{test}}
</div>

<script>
var vm = new Vue({
el:'#app',
data:{
test:'',
}
})
</script>
</body>
</html>

按键修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>按键修饰符</title>
<script src="./js/vue.js"></script>
</head>

<body>


<div id="app">

<input type="text" v-model="name" @keyup="handelKey1">
<input type="text" v-model="name" @keyup.enter="handelKey2">
<!-- <button @click="handelClick">点我</button>-->

</div>


</body>
<script>
var vm = new Vue({
el: '#app',
data: {
name: ''
},
methods: {
handelKey1(ev) {
console.log(ev)
if (ev.keyCode == 13) {
console.log('按下了回车')
}
},
handelKey2() {
console.log('回车键按下了')
},
handelClick(ev) {
console.log(ev)
}
}

})
</script>

</html>

表单控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表单控制</title>
<script src="./js/vue.js"></script>
</head>

<body>


<div id="app">

<h1>checkbox的选中与不选中</h1>
<p>用户名: <input type="text" v-model="name"></p>
<p>密码: <input type="password" v-model="password"></p>
<p><input type="checkbox" v-model="isRem">记住密码</p>
<button @click="submit">登录</button>
<hr>
<h1>性别单选:radio</h1>

<input type="radio" v-model="gender" value="1">
<input type="radio" v-model="gender" value="2">
<input type="radio" v-model="gender" value="3">其他
<br>
您选择的性别是:{{gender}}
<hr>
<h1>爱好多选:checkbox</h1>

<input type="checkbox" v-model="hobby" value="1">篮球
<br>
<input type="checkbox" v-model="hobby" value="2">足球
<br>
<input type="checkbox" v-model="hobby" value="3">美女
<br>
您的爱好是:{{hobby}}

</div>


</body>
<script>
var vm = new Vue({
el: '#app',
data: {
name: '',
password: '',
isRem: false,
gender: '',
hobby:[],
},
methods: {
submit() {
console.log(this.name)
console.log(this.password)
console.log(this.isRem)
}
}

})
</script>

</html>

购物车案例(全选、数量加减)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link rel="stylesheet" href="bootstrap.min.css">
</head>
<body>
<div id="app">

<table class="table-hover table table-striped">
<thead>
<tr>
<th>商品名</th>
<th>单价</th>
<th>数量</th>
<th>选择 <input type="checkbox" v-model="allCheck" @change="allChange"></th>
</tr>
</thead>
<tbody>
<tr v-for="shop in shop_list">
<td>{{shop.name}}</td>
<td>{{shop.price}}</td>
<td><button class="btn btn-primary" @click="click_down(shop)">-</button>{{shop.num}}<button class="btn btn-primary" @click="shop.num++">+</button></td>
<td><input type="checkbox" :value="shop" v-model="check_list" @change="oneChange"></td>
</tr>
</tbody>
</table>
总价为: {{get_price()}}
</div>


<script>
var vm = new Vue({
el: '#app',
data: {
shop_list:[
{name:'哔哩', price:123, num:2},
{name:'书', price:323, num:3},
{name:'滴滴', price:223, num:4},
],
check_list:[],
allCheck:false
},
methods:{
get_price(){
let total = 0
if (this.check_list.length > 0){
for (i in this.check_list){
total += this.check_list[i].num * this.check_list[i].price
}
}
return total
},
allChange(){
if (this.allCheck){
this.check_list = this.shop_list
}else {
this.check_list=[]
}
},
oneChange(){
if (this.check_list.length === this.shop_list.length){
this.allCheck = true
}else {
this.allCheck = false
}
},
click_down(shop){
if (shop.num >1){
shop.num --
}else {
shop.num = 1
}
}
}
})
</script>
</body>
</html>

v-model进阶

  • .lazy :并不是实时改变,而是在失去焦点或者按回车时才会更新
  • .number :将输入转换成Number类型
  • .trim :可以自动过滤输入首尾的空格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-model进阶</title>
<script src="./js/vue.js"></script>
</head>

<body>


<div id="app">

<!-- <input type="text" v-model.lazy="name"> 输入内容是:{{name}}-->
<!-- <input type="text" v-model.number="name"> 输入内容是:{{name}}-->
<input type="text" v-model.trim="name"> 输入内容是:{{name}}

</div>


</body>
<script>
var vm = new Vue({
el: '#app',
data: {
name: ''

},

})
</script>

</html>

Vue生命周期钩子

img

钩子函数 描述
beforeCreate 创建Vue实例之前调用
created 创建Vue实例成功后调用(可以在此处发送异步请求后端数据)
beforeMount 渲染DOM之前调用
mounted 渲染DOM之后调用
beforeUpdate 重新渲染之前调用(数据更新等操作时,控制DOM重新渲染)
updated 重新渲染完成之后调用
beforeDestroy 销毁之前调用
destroyed 销毁之后调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-model进阶</title>
<script src="./js/vue.js"></script>
</head>

<body>


<div id="app">

<!-- <child v-if="isShow"></child>-->
</div>


</body>
<script>
//定义一个组件
Vue.component('child', {
template: `
<div>
{{name}}
<br>
{{age}}
<button @click="name='Darker1'">更新数据1</button>
<button @click="name='Darker2'">更新数据2</button>
</div>`,
data() {
return {
name: 'Darker1',
age: 19
}
},

beforeCreate() {
console.group('当前状态:beforeCreate')
console.log('当前el状态:', this.$el)
console.log('当前data状态:', this.$data)
console.log('当前name状态:', this.name)
},
created() {
console.group('当前状态:created')
console.log('当前el状态:', this.$el)
console.log('当前data状态:', this.$data)
console.log('当前name状态:', this.name)
},
beforeMount() {
console.group('当前状态:beforeMount')
console.log('当前el状态:', this.$el)
console.log('当前data状态:', this.$data)
console.log('当前name状态:', this.name)
},
mounted() {
console.group('当前状态:mounted')
console.log('当前el状态:', this.$el)
console.log('当前data状态:', this.$data)
console.log('当前name状态:', this.name)
//用的最多,向后端加载数据,创建定时器等
console.log("页面已被vue实例渲染, data, methods已更新");
console.log('mounted')
this.t = setInterval(function () {
console.log('daada')
}, 3000)

clearInterval(this.t)

},
beforeUpdate() {
console.group('当前状态:beforeUpdate')
console.log('当前el状态:', this.$el)
console.log('当前data状态:', this.$data)
console.log('当前name状态:', this.name)
},
updated() {
console.group('当前状态:updated')
console.log('当前el状态:', this.$el)
console.log('当前data状态:', this.$data)
console.log('当前name状态:', this.name)
},
beforeDestroy() {
console.group('当前状态:beforeDestroy')
console.log('当前el状态:', this.$el)
console.log('当前data状态:', this.$data)
console.log('当前name状态:', this.name)
},
destroyed() {
console.group('当前状态:destroyed')
console.log('当前el状态:', this.$el)
console.log('当前data状态:', this.$data)
console.log('当前name状态:', this.name)
//组件销毁,清理定时器
clearInterval(this.t)
this.t = null
console.log('destoryed')
},


})


var vm = new Vue({
el: '#app',
data: {
isShow: true

},
methods:{
init(){
console.log('init')
},

},
mounted() {
console.log('mounted执行;了')
//ajax向后端获取数据
this.init()

},



})
</script>

</html>

与后端交互的几种方式

向后端发送ajax请求

三种方式

  • jQuery的ajax
  • fetch(原生)
  • axios(用的最多)

jQuery的ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="./js/vue.js"></script>
</head>
<body>

<div id="app">
<div v-for="data in data_list">
<h3>{{data.name}}</h3>
<img :src="data.poster" alt="">
<h5>导演:{{data.director}}</h5>
</div>

</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
data_list: []
},
methods: {
get_data() {
//发送请求
// let _this=this
$.ajax({
url: 'http://127.0.0.1:5000/',
type: 'get',
success: (data) => {
let data_obj=JSON.parse(data)
// console.log(typeof data_obj)
this.data_list = data_obj.data.films
}
// success: function (data) {
// // console.log(data)
// _this.data_list = data
// }
})
}
},
mounted() {
this.get_data()
},
})
</script>
</html>

fetch(原生)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>

<div id="app">
<div v-for="data in data_list">
<h3>{{data.name}}</h3>
<img :src="data.poster" alt="">
<h5>导演:{{data.director}}</h5>
</div>

</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
data_list: []
},
methods: {
get_data() {
//发送请求
fetch("http://127.0.0.1:5000/").then(res => res.json()).then(res => {
console.log(res.data.films)
this.data_list = res.data.films
})

}
},
mounted() {
this.get_data()
},
})
</script>
</html>

axios(用的最多)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>

<div id="app">

<div v-for="data in data_list">
{[ data.name ]}
<img :src="data.poster" alt="">
{[ data.director ]}
{[ data.synopsis ]}
</div>

</div>


<script>
var vm = new Vue({
el:'#app',
data:{
data_list:[]
},
delimiters:['{[', ']}'],
methods:{
get_data(){
axios({
url:'http://127.0.0.1:8090/index/',
methods:'get'
}).then(res=>{
this.data_list = res.data.data.films
})
}
},
mounted(){
this.get_data()
}
})
</script>
</body>
</html>

计算属性

优点

  • 在同一个页面中使用多次计算属性,不会多次执行
  • 不需要加括号,直接使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>

<div id="app">
<input type="text" v-model='name'>
<br>
您输入的是:{{get_name()}}
<br>
您输入的是2:{{get_name()}}

<hr>
<!-- 您输入的是:{{name.substring(0,1).toUpperCase()+name.substring(1)}}-->
<!-- 计算属性优点-->
<!-- 1 在同一个页面中使用多次计算属性,不会多次执行-->
<!-- 2 不需要加括号,直接使用-->


<br>
计算属性:您输入的是:{{upper_name}}
<br>
计算属性2:您输入的是:{{upper_name}}
计算属性2:您输入的是:{{upper_name}}
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
name:''
},
computed:{

upper_name(){
console.log('计算属性我执行了')
return this.name.substring(0,1).toUpperCase()+this.name.substring(1)
},
},
methods:{
get_name(){
console.log('get_name我执行了')
return this.name.substring(0,1).toUpperCase()+this.name.substring(1)
},
}

})
</script>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>

<div id="app">
<input type="text" v-model='search'>
<div v-for="data in new_list">
{{data}}
</div>


</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
search: '',
data_list: ['aaa', 'abc', 'abcde', 'abcdef', 'bbb', 'bac']
},
computed: {
new_list() {
return this.data_list.filter(item => {
return item.indexOf(this.search) > -1
})


}


},
methods: {}

})
</script>
</html>

虚拟dom和diff算法

其实呢不只是vue,react中在执行列表渲染时也会要求给每个组件添加key这个属性。
key简单点来说就是唯一标识,就像ID一样唯一性
要知道,vue和react都实现了一套虚拟DOM,使我们可以不直接操作DOM元素,只操作数据便可以重新渲染页面。而隐藏在背后的原理便是其高效的Diff算法。

只做同层级的对比
按照key值比较,出现新的key就插入
通组件对比

img

image-20201214225437290

具体实现

把树按照层级分解

image-20201214225736585

同key值比较

image-20201214225827633

通组件对比

image-20201214225913886

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="box">
<div v-if="isShow">111</div>
<p v-else>222</p>
<!--
{tag:div,value:111}
{tag:p,value:222}
直接不比较,直接删除div,新增p
-->

<div v-if="isShow">111</div>
<div v-else>222</div>
<!--
{tag:div,value:111}
{tag:div,value:222}
比较都是div,只替换文本内容
-->
</div>

详细:https://segmentfault.com/a/1190000020170310

组件

  • 介绍
  • 全局组件
  • 局部组件
  • 组建通信之父传子

介绍

1
2
3
4
5
6
7
1 组件的作用
扩展 HTML 元素,封装可重用的代码,目的是复用
例如:
有一个轮播图,可以在很多页面中使用,一个轮播有js,css,html
组件把js,css,html放到一起,有逻辑,有样式,有html

2 组件分类:局部组件和全局组件

全局组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>

<div id="app">

<myheader></myheader>

<div>我是div</div>

<myheader></myheader>

</div>
</body>
<script>


// 定义一个全局组件
// 组件可以有data,methods,computed....,但是data 必须是一个函数

Vue.component('myheader', {
template: `
<div>
<h1 style="background-color: greenyellow">我是全局组件:{{name}}</h1>
<button @click="handleClick">点我弹出美女</button>

</div>
`,
data(){
return {
name:'lqz'
}
},
methods:{
handleClick(){
alert('美女')
}
},
mounted(){},
computed:{

}


})

var vm = new Vue({
el: '#app',
data: {},

})
</script>
</html>

局部组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>

<div id="app">

<myheader></myheader>

<div>我是div</div>

<myheader></myheader>

<div></div>


</div>
</body>
<script>


// 定义一个全局组件
//组件可以有data,methods,computed....,但是data 必须是一个函数

Vue.component('myheader', {
template: `
<div>
<h1 style="background-color: greenyellow">我是全局组件:{{name}}</h1>
<button @click="handleClick">点我弹出美女</button>
<hr>
<child></child>

</div>
`,
data(){
return {
name:'lqz'
}
},
methods:{
handleClick(){
alert('美女')
}
},
mounted(){},
computed:{

},
components:{
child:{
template: `<div>
<span>{{age}}</span>
</div>`,
data(){
return {
age:19
}
},
methods:{

}
}
}


})

var vm = new Vue({
el: '#app',
data: {},

})
</script>
</html>

组建通信之父传子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>

<div id="app">


<myheader :myname="name" :myshow="false"></myheader>

{{obj.length}}



</div>
</body>
<script>


// 定义一个全局组件
//组件可以有data,methods,computed....,但是data 必须是一个函数

Vue.component('myheader', {
template: `
<div>
<h1 style="background-color: greenyellow">我是全局组件:{{myname}}</h1>
<button @click="handleClick">点我弹出美女</button>
<br>
{{myshow}}
<hr>
<child v-if=""></child>

</div>
`,
data(){
return {
name:'lqz'
}
},
methods:{
handleClick(){
alert('美女')
}
},
mounted(){},
computed:{

},
components:{
child:{
template: `<div>
<span>{{age}}</span>
</div>`,
data(){
return {
age:19
}
},
methods:{

}
}
},
// props:['myname'] , //注册一下

// 属性验证
props:{
myname:String,
myshow:Boolean
},


})

var vm = new Vue({
el: '#app',
data: {
name:'egon'
},

})
</script>
</html>

组建通信之子传父(通过自定义事件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="box">

<navbar @myevent="handleEvent"></navbar>


</div>
</body>
<script>
// 定义全局组件
Vue.component('navbar', {
template: `
<div>
<h1>我是navbar</h1>
<hr>
<input type="text" v-model="name">
<br>

<button @click="handleClick">我是子组件的button</button>
</div>
`,
data() {
return {
name: 'lqz'
}
},
methods: {
handleClick() {
// 触发父组件中myevent这个自定义事件对应的函数执行
this.$emit('myevent',this.name)
}
}
})
var vm = new Vue({
el: '#box',
data: {},
methods: {
handleEvent(name) {
console.log('我执行了')
console.log('从子组件传递的name:'+name)
}
}

})
</script>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="box">

<navbar @myevent="handleEvent" v-if="myshow"></navbar>


</div>
</body>
<script>
// 定义全局组件
Vue.component('navbar', {
template: `
<div>
<h1>我是navbar</h1>
<hr>

<button @click="handleClick">点我隐藏子组件</button>
</div>
`,
data() {
return {
// myshow: true
}
},
methods: {
handleClick() {
// 触发父组件中myevent这个自定义事件对应的函数执行
// this.myshow = !this.myshow
this.$emit('myevent', false)
}
}
})
var vm = new Vue({
el: '#box',
data: {
myshow: true
},
methods: {
handleEvent(show) {
this.myshow = show
}
}

})
</script>
</html>

ref属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="box">
ref放在标签上,拿到的是原生节点
<br>
ref放在组件上,拿到的是组件对象(数据,方法,直接使用即可)
<hr>
<h1>ref用在标签上</h1>
<input type="text" ref="myinput">
<button @click="handleClick">点我触发事件</button>

<hr>
<h1>ref用在组件上</h1>
<navbar ref="mynavbar"></navbar>


</div>
</body>
<script>
// 定义全局组件
Vue.component('navbar', {
template: `
<div>
<h3>我是navbar</h3>
<hr>
</div>
`,
data() {
return {
myshow: true
}
},
methods: {
handleClick(a) {
console.log('父组件调用我,传入了:'+a)
}
}
})
var vm = new Vue({
el: '#box',
data: {
myshow: true
},
methods: {
handleClick() {
//this.$refs 取到一个对象,放着你在标签上写得ref对应的value值

//在父组件中直接取到了子组件的值(从子传到父)
// console.log(this.$refs.mynavbar.myshow)
//从父传子
// this.$refs.mynavbar.myshow='sss'
//调用子组件方法
this.$refs.mynavbar.handleClick('lqz')

}
}

})
</script>
</html>

事件总线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="box">

<child1></child1>

<hr>
<child2></child2>




</div>
</body>
<script>
// 借助事件总线,实现跨组件通信
//定义一个事件总线

var bus=new Vue()

Vue.component('child1', {
template: `
<div>
<input type="text" v-model="msg">
<button @click="send_msg">发送</button>
<hr>
</div>
`,
data() {
return {
msg: ''
}
},
methods:{
send_msg(){
bus.$emit('suibian',this.msg)
}
}
})

Vue.component('child2', {
template: `
<div>
<h5>我收到的内容是:{{recv_msg}}</h5>
</div>
`,
data() {
return {
recv_msg:''
}
},
mounted(){
bus.$on('suibian',msg=> {
this.recv_msg=msg
})
}
})

var vm = new Vue({
el: '#box',
data: {},
methods: {}

})
</script>
</html>

动态组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="box">


<button @click="who='Home'">首页</button>
<button @click="who='User'">用户</button>
<button @click="who='Order'">订单</button>

<keep-alive>
<component :is="who"></component>
</keep-alive>

</div>
</body>
<script>

Vue.component('Home', {
template: `
<div>
首页
</div>
`,
data() {
return {}
},
})

Vue.component('User', {
template: `
<div>
用户组件
<input type="text" v-model="name">
</div>
`,
data() {
return {
name: ''
}
},
})
Vue.component('Order', {
template: `
<div>
订单页面
</div>
`,
data() {
return {}
},
})

var vm = new Vue({
el: '#box',
data: {
who: 'Home'
},

})
</script>
</html>

插槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="box">

<Home>阿斯顿发送到</Home>

<Order>
<button slot="b" @click="handleClick">我是个按钮</button>
</Order>


</div>
</body>
<script>

Vue.component('Home', {
template: `
<div>
首页
<div>
<slot></slot>
</div>

</div>
`,
data() {
return {}
},
})

Vue.component('Order', {
template: `
<div>
订单页面

<slot name="a"></slot>
<br>
我是一行数据
<br>
<slot name="b"></slot>


</div>
`,
data() {
return {}
},
})

var vm = new Vue({
el: '#box',
data: {
who: 'Home'
},
methods:{
handleClick(){
console.log('我被点了')
}
}

})
</script>
</html>

Vue-CLI 搭建vue项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1 ECMAScript,javascript,nodejs的关系
2 安装nodejs(安装解释器)
3 java:sun---》oracle(甲骨文)----》有些包,收费
-安卓使用java开发,涉及到版权
-jdk:java开发工具包,做java开发,需要安装它 1.5 1.8大更新 java 15
-jre:java运行环境,
-jvm:java虚拟机,最少要占200m内存空间


4 安装node,一路下一步
node:就是python命令
npm:就是python的pip命令,npm下载模块慢,我们使用cmpm下载
cnpm:阿里提供的
npm install -g cnpm --registry=https://registry.npm.taobao.org
5 安装vue脚手架
cnpm install -g @vue/cli

6 多出vue命令
vue create my-project: 创建出一个vue项目,等同于djagnoadmin createproject 名字
# OR
vue ui
7 开发
-使用pycharm开发vue项目
-装vue插件
8 运行起项目
-terminal中:npm run serve

1626408322582

1626408646319

1626409485838

vue项目目录介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
myfirstvue  # 项目名
-node_modules # 该项目所有的依赖,如果要把项目给别人,这个文件夹要删掉(很大)
-public # 文件夹
-favicon.ico #小图标
-index.html # 整个vue项目的index.html,单页面开发

-src # 核心
-store # 如果装了vuex,就会有这个文件夹
-router #如果装了vue router ,就会有这个文件夹
-assets # 存放资源文件(js,css,img)
-components # 小组件(在这写小组件)
-views #页面组件(在这写页面组件)
main.js # 整个项目的入口,核心配置
App.vue # 根组件

-package.json # 项目的依赖,不能删
-README.md # 介绍

vue组件介绍

1
2
3
4
每个组件有三部分组成
template:写html
script:写js
style:写css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>   
<div class="about">
<h1>写html</h1>
</div>
</template>

<script>

js代码

</script>

<style>

css代码

</style>

vue-router的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1 以后,新建一个页面组件,写三部分
2 在router下的index.js内配置一下
const routes = [
{ // / 路径,显示Home这个页面组件
path: '/',
name: 'Home',
component: Home
},
{
path: '/order',
name: 'Order',
component: Order
},
{
path: '/about',
name: 'About',
// component: () => import('../views/About.vue')
component: About,
}
]


3 在根vue中写
<div id="app">
<router-view/>
</div>

4 以后要跳转到某个页面
<router-link to="/路径">
<button>点我跳转到order</button>
</router-link>

在项目中新建组件和使用

1
2
3
4
5
6
7
8
9
10
11
12
13
1 在components文件夹创建一个 xx.vue
2 在其他组件中使用
-在scripts中
import HelloWorld from '../components/HelloWorld.vue'
export default {
name: 'Home',
components: {
HelloWorld
}
}

3 在html中直接使用
<HelloWorld/>

js导入导出语法(了解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1 有一些变量,函数,想在其他地方都用,可以写成一个包
新建一个文件夹,在文件夹中写index.js
在内部写函数,写变量,最后一定要导出
var name='lqz'
function add(a,b) {
return a+b
}
export default {
name,
add
}
2 在想用的位置,导入使用即可
import xx from '../lqz'
console.log(xx.add(1,2))

使用axios

1
2
3
4
1 安装 cnpm install axios
2 导入使用
import axios from 'axios'
axios.get('api/index/').then(res => {})

前端代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1 vue.config.js中module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8000',
changeOrigin: true
}
}
},
}

2 使用axios发送请求
mounted() {
axios.get('api/index/').then(res => {
console.log(res.data)
this.data_list = res.data.data.films
})
}

bootstrap和jq的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1 安装
cnpm install jquery
cnpm install bootstrap@3

2 配置vue.config.js中
const webpack = require("webpack");
module.exports = {
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
"window.$": "jquery",
Popper: ["popper.js", "default"]
})
]
}
};

3 main.js中
import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css'

elementui的使用

1
2
3
4
5
6
1 安装
cnpm install element-ui -S
2 main.js中配置
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

什么是Hexo ?

img

Hexo是一款基于Node.js的静态博客框架,依赖少易于安装使用,可以方便的生成静态网页托管在GitHub和Heroku上,是搭建博客的首选框架。这里我们选用的是GitHub,你没看错,全球最大的同性恋交友网站(逃……)。Hexo同时也是GitHub上的开源项目,参见:hexojs/hexo 如果想要更加全面的了解Hexo,可以到其官网 Hexo 了解更多的细节,因为Hexo的创建者是台湾人,对中文的支持很友好,可以选择中文进行查看。这里,默认各位猿/媛儿都知道GitHub就不再赘述。

下面正式从零开始搭建年轻人的第一个网站。

搭建步骤:

  • 获得个人网站域名
  • GitHub创建个人仓库
  • 安装Git
  • 安装Node.js
  • 安装Hexo
  • 推送网站
  • 绑定域名
  • 更换主题
  • 初识MarkDown语法
  • 发布文章
  • 寻找图床
  • 个性化设置
  • 其他
  • 附录

获得个人网站域名

域名是网站的入口,也是网站的第一印象,比如饿了么的官网的域名是:https://www.ele.me/ ,很是巧妙。常见的有com,cn,net,org等后缀,也有小众的xyz,me,io等后缀,根据你自己的喜好,选择不同的后缀,比如我选择就是常见的com后缀。很多小众奇特的后缀在大陆是没办法备案的,网站也就无法上线。然而使用GitHub托管我们的网站,完全不需要备案,因为托管我们的网站内容的服务器在美国,而且在国内备案流程也比较繁杂,时间需要一周左右。

申请域名的地方有很多,这里推荐阿里云:阿里云-为了无法计算的价值 申请入口:域名注册 购买域名这也是我们整个搭建过程中惟一一个需要花钱的地方。如果你已经有了空闲域名就无需购买,直接使用即可。

GitHub创建个人仓库

登录到GitHub,如果没有GitHub帐号,使用你的邮箱注册GitHub帐号:Build software better, together 点击GitHub中的New repository创建新仓库,仓库名应该为:用户名.http://github.io 这个用户名使用你的GitHub帐号名称代替,这是固定写法,比如我的仓库名为:

img

安装Git

什么是Git ?简单来说Git是开源的分布式版本控制系统,用于敏捷高效地处理项目。我们网站在本地搭建好了,需要使用Git同步到GitHub上。如果想要了解Git的细节,参看廖雪峰老师的Git教程:Git教程 从Git官网下载:Git - Downloading Package 现在的机子基本都是64位的,选择64位的安装包,下载后安装,在命令行里输入git测试是否安装成功,若安装失败,参看其他详细的Git安装教程。安装成功后,将你的Git与GitHub帐号绑定,鼠标右击打开Git Bash

img

或者在菜单里搜索Git Bash,设置user.name和user.email配置信息:

1
2
git config --global user.name "你的GitHub用户名"
git config --global user.email "你的GitHub注册邮箱"

生成ssh密钥文件:

1
ssh-keygen -t rsa -C "你的GitHub注册邮箱"

然后直接三个回车即可,默认不需要设置密码
然后找到生成的.ssh的文件夹中的id_rsa.pub密钥,将内容全部复制

img

打开GitHub_Settings_keys 页面,新建new SSH Key

img

Title为标题,任意填即可,将刚刚复制的id_rsa.pub内容粘贴进去,最后点击Add SSH key。
在Git Bash中检测GitHub公钥设置是否成功,输入 ssh git@github.com

img

如上则说明成功。这里之所以设置GitHub密钥原因是,通过非对称加密的公钥与私钥来完成加密,公钥放置在GitHub上,私钥放置在自己的电脑里。GitHub要求每次推送代码都是合法用户,所以每次推送都需要输入账号密码验证推送用户是否是合法用户,为了省去每次输入密码的步骤,采用了ssh,当你推送的时候,git就会匹配你的私钥跟GitHub上面的公钥是否是配对的,若是匹配就认为你是合法用户,则允许推送。这样可以保证每次的推送都是正确合法的。

安装Node.js

Hexo基于Node.js,Node.js下载地址:Download | Node.js 下载安装包,注意安装Node.js会包含环境变量及npm的安装,安装后,检测Node.js是否安装成功,在命令行中输入 node -v :

img

检测npm是否安装成功,在命令行中输入npm -v :

img

到这了,安装Hexo的环境已经全部搭建完成。

安装Hexo

Hexo就是我们的个人博客网站的框架, 这里需要自己在电脑常里创建一个文件夹,可以命名为Blog,Hexo框架与以后你自己发布的网页都在这个文件夹中。创建好后,进入文件夹中,按住shift键,右击鼠标点击命令行

img

使用npm命令安装Hexo,输入:

1
npm install -g hexo-cli 

这个安装时间较长耐心等待,安装完成后,初始化我们的博客,输入:

1
hexo init blog

注意,这里的命令都是作用在刚刚创建的Blog文件夹中。

为了检测我们的网站雏形,分别按顺序输入以下三条命令:

1
2
3
4
5
hexo new test_my_site

hexo g

hexo s

这些命令在后面作介绍,完成后,打开浏览器输入地址:

localhost:4000

可以看出我们写出第一篇博客,只不过我下图是我修改过的配置,和你的显示不一样。

img

现在来介绍常用的Hexo 命令

npm install hexo -g #安装Hexo
npm update hexo -g #升级
hexo init #初始化博客

命令简写
hexo n “我的博客” == hexo new “我的博客” #新建文章
hexo g == hexo generate #生成
hexo s == hexo server #启动服务预览
hexo d == hexo deploy #部署

hexo server #Hexo会监视文件变动并自动更新,无须重启服务器
hexo server -s #静态模式
hexo server -p 5000 #更改端口
hexo server -i 192.168.1.1 #自定义 IP
hexo clean #清除缓存,若是网页正常情况下可以忽略这条命令

刚刚的三个命令依次是新建一篇博客文章、生成网页、在本地预览的操作。

推送网站

上面只是在本地预览,接下来要做的就是就是推送网站,也就是发布网站,让我们的网站可以被更多的人访问。在设置之前,需要解释一个概念,在blog根目录里的_config.yml文件称为站点配置文件,如下图

img

进入根目录里的themes文件夹,里面也有个_config.yml文件,这个称为主题配置文件,如下图

img

下一步将我们的Hexo与GitHub关联起来,打开站点的配置文件_config.yml,翻到最后修改为:

deploy:
type: git
repo: 这里填入你之前在GitHub上创建仓库的完整路径,记得加上 .git
branch: master参考如下:

img

保存站点配置文件。

其实就是给hexo d 这个命令做相应的配置,让hexo知道你要把blog部署在哪个位置,很显然,我们部署在我们GitHub的仓库里。最后安装Git部署插件,输入命令:

1
npm install hexo-deployer-git --save

这时,我们分别输入三条命令:

1
2
3
hexo clean 
hexo g
hexo d

其实第三条的 hexo d 就是部署网站命令,d是deploy的缩写。完成后,打开浏览器,在地址栏输入你的放置个人网站的仓库路径,即 http://xxxx.github.io (知乎排版可能会出现”http://“字样,参考下图) 比如我的xxxx就是我的GitHub用户名:

img

你就会发现你的博客已经上线了,可以在网络上被访问了。

绑定域名

虽然在Internet上可以访问我们的网站,但是网址是GitHub提供的:http://xxxx.github.io (知乎排版可能会出现”http://“字样) 而我们想使用我们自己的个性化域名,这就需要绑定我们自己的域名。这里演示的是在阿里云万网的域名绑定,在国内主流的域名代理厂商也就阿里云和腾讯云。登录到阿里云,进入管理控制台的域名列表,找到你的个性化域名,进入解析

img

然后添加解析

img

包括添加三条解析记录,192.30.252.153是GitHub的地址,你也可以ping你的 http://xxxx.github.io 的ip地址,填入进去。第三个记录类型是CNAME,CNAME的记录值是:你的用户名.http://github.io 这里千万别弄错了。第二步,登录GitHub,进入之前创建的仓库,点击settings,设置Custom domain,输入你的域名

img

点击save保存。第三步,进入本地博客文件夹 ,进入blog/source目录下,创建一个记事本文件,输入你的域名,对,只要写进你自己的域名即可。如果带有www,那么以后访问的时候必须带有www完整的域名才可以访问,但如果不带有www,以后访问的时候带不带www都可以访问。所以建议,不要带有www。这里我还是写了www(不建议带有www):

img

保存,命名为CNAME ,注意保存成所有文件而不是txt文件

完成这三步,进入blog目录中,按住shift键右击打开命令行,依次输入:

1
2
3
hexo clean
hexo g
hexo d

这时候打开浏览器在地址栏输入你的个性化域名将会直接进入你自己搭建的网站。

更换主题

如果你不喜欢Hexo默认的主题,可以更换不同的主题,主题传送门:Themes 我自己使用的是Next主题,可以在blog目录中的themes文件夹中查看你自己主题是什么。现在把默认主题更改成Next主题,在blog目录中(就是命令行的位置处于blog目录)打开命令行输入:

1
git clone https://github.com/iissnan/hexo-theme-next themes/next

这是将Next主题下载到blog目录的themes主题下的next文件夹中。打开站点的_config.yml配置文件,修改主题为next

img

打开主题的_config.yml配置文件,不是站点主题文件,找到Scheme Settings

img

next主题有三个样式,我用的是Pisces,你们可以自己试试看,选择你自己喜欢的样式(只需要把行首的#去除,#是注释),选择好后,再次部署网站,hexo g、hexo d,查看效果。选择其他主题,按照上述过程即可实现。

初识Markdown语法

Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式。Markdown语法简洁明了、容易掌握,而且功能比纯文本更强,因此写博客使用它,可以让用户更加专注的写文章,而不需要费尽心力的考虑样式,相对于html已经算是轻量级语言,像有道云笔记也支持Markdown写作。并且Markdown完全兼容html,也就是可以在文章里直接插入html代码。比如给博文添加音乐,就可以直接把音乐的外链html代码插入文章中。具体语法参看:Markdown 语法说明(简体中文版) 可以说十分钟就可以入门。当然,工欲善其事必先利其器,选择一个好的Markdown编辑器也是非常重要的,这里推荐MarkPadThe Markdown Editor for Windows ,这是带有预览效果的编辑器,也可以使用本地的文本编辑器,更多的Markdown的语法与编辑器自己可以搜索了解。

发布文章

我们开始正式发布上线博客文章,在命令行中输入:

1
hexo n "博客名字"

我们会发现在blog根目录下的source文件夹中的_post文件夹中多了一个 博客名字.md 文件,使用Markdown编辑器打开,就可以开始你的个人博客之旅了,Markdown常用的样式也就十来种,完全能够满足一般博文的样式要求,这是我的一篇博文内容示例:

img

通过带有预览样式的Markdown编辑器实时预览书写的博文样式,也可以通过命令 hexo s –debug 在本地浏览器的localhost:4000 预览博文效果。写好博文并且样式无误后,通过hexo g、hexo d 生成、部署网页。随后可以在浏览器中输入域名浏览。

寻找图床

图床,当博文中有图片时,若是少量图片,可以直接把图片存放在source文件夹中,但这显然不合理的,因为图片会占据大量的存储的空间,加载的时候相对缓慢 ,这时考虑把博文里的图片上传到某一网站,然后获得外部链接,使用Markdown语法,**图片信息** 完成图片的插入,这种网站就被成为图床。常见的简易的图床网站有:贴图库图片外链 国内算比较好的图床我认为有两个:新浪微博和 七牛云 ,七牛云的使用方法可以参看其他文章。图床最重要的就是稳定速度快,所以在挑选图床的时候一定要仔细,下图是博文插入图片,使用图床外链的示例:

img

个性化设置

所谓的个性化设置就是根据个人需要添加不同的插件及功能。

基本的有:

在站点配置文件_config.yml修改基本的站点信息

img

依次是网站标题、副标题、网站描述、作者、网站头像外部链接、网站语言、时区等。

在主题配置文件_config.yml修改基本的主题信息,如:

img

博文打赏的微信、支付宝二维码图片,这里我是直接把这两张放在根目录的source文件夹中,并没有使用图床外链。

img

社交外链的设置,即在侧栏展示你的个人社交网站信息。

img

博文分享的插件jiathis,值设置为true。在配置文件中有很多的个性化设置,可以自尝试更多的修改。

进阶个性化:

  • 添加网易云音乐

打开网页版的网易云音乐,选择喜欢的音乐,点击生成外链播放器

img

复制外链的代码

img

比如在侧栏插入这首歌的音乐播放器,修改 blog\themes\next\layout_macro的sidebar.swig文件,添加刚刚复制的外链代码

img

重新生成、部署网页,效果如下

img

  • 设置背景

把你挑选的背景图片命名为:background.jpg,放在blog\themes\next\source\images里,在blog\themes\next\source\css_custom文件的custom.styl首部添加:

1
2
3
4
body {
background:url(/images/background.jpg);
background-attachment: fixed;
}

background-attachment: fixed;是固定背景图片。

这是设置一张静态图片作为背景,其实Next主题自带有动态的背景效果,修改主题配置文件中的canvas_nest: false为canvas_nest: true即可。

  • 增加侧栏菜单条目

默认的侧栏菜单条目有:首页、归档、标签、关于、搜索等。如果你想要增加其他的菜单条目,修改主题配置文件_config.yml里的Menu Settings中的menu和menu_icons两个地方

img

其中menu里是配置菜单项对应的页面位置(如:/love),menu_icons对应菜单项的图标,这里的图标是来自于Font Awesome ,所以你需要在Font Awesome网站上找到你需要的icon,然后把该icon的名字写在menu_icons对应菜单名后面,注意冒号有一个英文输入状态的空格。设置好后,在命令行里输入:

1
hexo new page "你所要增加的菜单项名称(要和你在menu中的填写要匹配)"

新建的页面在博客根目录下的source文件里,这时你就可以对新建的页面自定义设计。

还有更多的进阶个性化设置,如SEO、评论系统、个人头像、博客分享、订阅功能、High功能、404网页设置等,可以参看:

主题配置 - NexT 使用文档

第三方服务集成 - NexT 使用文档

内置标签 - NexT 使用文

进阶设定 - NexT 使用文档

内容概要

  • 查询关键字
  • 多表查询的思想
  • 子查询
  • 连表操作
  • Navicat可视化软件
  • 鼠标点点点完成数据库的操作(内部还是SQL语句)
  • 多表查询练习题(重点)

内容详细

查询关键字

表准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
create table emp(
id int primary key auto_increment,
name varchar(20) not null,
sex enum('male','female') not null default 'male', #大部分是男的
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),
office int, #一个部门一个屋子
depart_id int
);

#插入记录
#三个部门:教学,销售,运营
insert into emp(name,sex,age,hire_date,post,salary,office,depart_id) values
('jason','male',18,'20170301','张江第一帅形象代言',7300.33,401,1), #以下是教学部
('tom','male',78,'20150302','teacher',1000000.31,401,1),
('kevin','male',81,'20130305','teacher',8300,401,1),
('tony','male',73,'20140701','teacher',3500,401,1),
('owen','male',28,'20121101','teacher',2100,401,1),
('jack','female',18,'20110211','teacher',9000,401,1),
('jenny','male',18,'19000301','teacher',30000,401,1),
('sank','male',48,'20101111','teacher',10000,401,1),
('哈哈','female',48,'20150311','sale',3000.13,402,2),#以下是销售部门
('呵呵','female',38,'20101101','sale',2000.35,402,2),
('西西','female',18,'20110312','sale',1000.37,402,2),
('乐乐','female',18,'20160513','sale',3000.29,402,2),
('拉拉','female',28,'20170127','sale',4000.33,402,2),
('僧龙','male',28,'20160311','operation',10000.13,403,3), #以下是运营部门
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬银','female',18,'20130311','operation',19000,403,3),
('程咬铜','male',18,'20150411','operation',18000,403,3),
('程咬铁','female',18,'20140512','operation',17000,403,3);
1 select
- 用来指定表的字段数据
select * from emp;
select id,name from emp;
'''
在工作中很少使用*号 ---> 内容大
'''

2 from
- 后面跟需要查询的表名

3 where
筛选数据

查询关键字之where

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 1 查询id大于等于3小于等于6的数据

# 2 查询薪资是20000或者18000或者17000的数据

'''
模糊查询:
关键字 ---> like
关键符号
%:匹配任意个数的任意字符
_:匹配单个个数的任意字符
'''

# 3 查询姓名中带有字母o的员工姓名和薪资
select name,salary from emp where name like '%o%';

# 4 查询姓名由四个字符组成的员工姓名和薪资
select name,salary from emp where name like'____';
select name,salary from emp where char_length(name) = 4;

# 5 查询id小于3或者大于6的数据
select * from emp where id < 3 or id > 6;
select * from emp where id not between 3 and 6;

# 6 查询薪资不在20000,18000,17000范围的数据
select * from emp where salary not in (20000,18000,17000);

# 7 查询岗位描述为空的员工名与岗位名 针对null不能用'=',只能用is
select name,post from emp where post_comment = NULL; # 查询为空
select name,post from emp where post_comment is NULL;
select name,post from emp where post_comment is not NULL;

查询关键字之group by分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
分组 ---> 将单个单个的个体按照指定的条件分成一个个整体
'''
分组之后默认只能直接获取分组的依据
其他字段无法再直接获取(可以间接获取)
'''

# 严格模式
set global sql_mode='STRICT_TRANS_TABLES,PAD_CHAR_TO_FULL_LENGTH,only_full_group_by'


# 1 每个部门的最高薪资
select post,max(salary) from emp group by post;

# 2 每个部门的最低薪资
select post,min(salary) from emp group by post;

# 3 每个部门的平均薪资
select post avg(salary) from emp group by post;

# 4 每个部门的人数
select post,count(id) from emp group by post;

# 5 每个部门的月工资总和
select post,sum(salary) fromm emp group by post;

'''
可以给字段起别名(as还可以给表起别名)
select post as'部门',sum(salary) as '总和' from emp group by post;
'''

# 6 查询分组之后的部门名称和每个部门下所有的员工姓名

'''
group_concat() ---> 获取分组以外的字段数据,并且支持拼接操作(掌握)

select post,group_concat(name) from emp group by post;
select post,group_concat(name,':',salary) from emp group by post;

concat() ---> 未分组之前使用的拼接功能(了解)
select concat(name,':',sex) from emp;

concat_ws() ---> 省步骤(了解)
select concat_ws(':',name,sex,salary,age) from emp
'''

聚合函数

1
2
3
分组之后频繁需要使用的
    max 最大值
    min 最小值    sum 求和    count 计数    avg 平均值

查询关键字之having(过滤)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
功能上having与where是一模一样的
但是使用位置上有有所不同
-where在分组之前使用
-having在分组之后使用

1 统计各部门年龄在30岁以上的员工平均工资,并且保留工资大于10000的部门

# 1.1 先筛选出所有30岁以上的员工
select * from emp where age >30;
# 1.2 然后再按照部门分组
'''SQL语句的查询结果我们也可以直接看成是一张表'''
select post,avg(salary) from emp where age >30 group by post;
# 1.3 分组之后再做过滤操作
select post,avg(salary) from emp
where age>30
group by post
having avg(salary)>10000
;

查询关键字之distinct(去重)

1
2
3
去重有一个非常严格的前提条件 数据必须是完全一样
如果数据带有主键name肯定无法去重
select distinct age from emp;

查询关键字之order by(排序)

1
2
3
4
5
6
7
8
9
10
11
12
13
select * from emp order by salary; # 默认是升序
select * from emp order by salary asc; # 升序关键字(可不写)
select * from emp order by salary desc; # 降序

# 排序也可以指定多个字段
select from emp order by age desc,salary asc;

# 统计各部门年龄在10岁以上的员工平均工资,并且保留平均工资大于1000的部门,然后对平均工资进行排序
select post,avg(salary) from emp
where age > 10
group by post
having avg(salary)>1000
order by avg(salary);

查询关键字之limit(分页)

1
2
3
4
5
6
7
用来限制数据的展示条数
select * from emp limit 5;# 前五条
select * from emp limit 5,5; # 起始位置、条数

# 查询工资最高的人的详细信息
# 1 先按照工资排序,然后限制展示条数
select * from emp order by salary desc limit 1;

查询关键字之regexp正则

1
2
3
4
5
正则表达式
用一些特殊符号的组合去字符串中筛选出符合条件的数据

select * from emp where name regexp '^j.*(n|y)$';
# '^j.*(n|y)$' j开头 中间无所谓 n或者y结尾

多表查询思想

1
2
3
4
5
6
7
1 子查询
分步解决问题
将一条SQL语句的查询结果用括号括起来,当做另一条SQL语句的查询条件

2 连表操作
先将所有需要用到的表拼接到一起(一张表)
然后就是转换成单表查询

前期表准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#建表
create table dep(
id int primary key auto_increment,
name varchar(20)
);

create table emp(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);

#插入数据
insert into dep values
(200,'技术'),
(201,'人力资源'),
(202,'销售'),
(203,'运营');

insert into emp(name,sex,age,dep_id) values
('jason','male',18,200),
('egon','female',48,201),
('kevin','male',18,201),
('nick','male',28,202),
('owen','male',18,203),
('jerry','female',18,204);

子查询

1
2
3
4
5
6
7
# 查询Jason所在的部门名称
# 第一步 先获取Jason所在的部门id
select dep_id from emp where name='jason';
# 第二步 根据id号去部门表中筛选
select * from dep where id = 200;
# 完整句式
select * from dep where id = (select dep_id from emp where name='jason');

连表操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 前戏(了解)
select * from emp,dep;
# 基于上表筛选数据(了解)
'''为了避免字段冲突 可以在字段名前面加表明确'''
select * from emp,dep where emp.dep_id = dep.id;

--------------------------------------- 掌握 -------------------------------------------
inner join --- 内连接 拼接公共的部分
select * from emp inner join dep on emp.dep_id = dep.id;
left join --- 左连接 以左表为基准展示所有数据 没有的null填充 (用得少)
select * from emp left join dep on emp.dep_id=dep.id;
right join --- 右连接 以右表为基准展示所有数据 没有的null填充 (用得少)
select * from emp right join dep on emp.dep_id=dep.id;
union 全连接
select * from emp left join dep on emp.dep_id=dep.id
union
select * from emp right join dep on emp.dep_id=dep.id;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
可以充当很多数据库软件的客户端 封装了很多快捷方法

该软件默认也是收费的 需要破解
正版不破解免费试用14
破解版(老版本):https://pan.baidu.com/s/1bpo5mqj

1.下载与安装
2.使用方法
创建库 表 记录
注意主键
外键字段
逆向数据库到模型
转储SQL文件
查询

多表查询练习题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
1、 查询所有的课程的名称以及对应的任课老师姓名
4、 查询平均成绩大于八十分的同学的姓名和平均成绩
7、 查询没有报李平老师课的学生姓名
8、 查询没有同时选修物理课程和体育课程的学生姓名
9、 查询挂科超过两门(包括两门)的学生姓名和班级



#####################关键字习惯都用大写#######################
# 建议:在书写SQL语句的时候一定不要想着一次性成功 写一点看一点再写一点 慢慢拼凑起来
-- 1、 查询所有的课程的名称以及对应的任课老师姓名
# 1.先明确需要的表 course表 teacher表
-- select * from course;
-- select * from teacher;
# 2.连表操作 明确字段
-- SELECT
-- course.cname,
-- teacher.tname
-- FROM
-- course
-- INNER JOIN teacher ON course.teacher_id = teacher.tid;
-- 4、 查询平均成绩大于八十分的同学的姓名和平均成绩
# 1.先查看成绩表
-- select * from score;
# 2.求所有学生的平均成绩
-- select score.student_id,avg(num) from score group by score.student_id;
# 3.筛选出大于80分
-- select score.student_id,avg(num) as 'avg_num' from score group by score.student_id having avg(num)>80
-- ;
# 4.学生表与上述查询出来的表连接
-- SELECT
-- student.sname,
-- t1.avg_num
-- FROM
-- student
-- INNER JOIN ( SELECT score.student_id, avg( num ) AS 'avg_num' FROM score GROUP BY score.student_id HAVING avg( num )> 80 ) AS t1 ON student.sid = t1.student_id;
-- 7、 查询没有报李平老师课的学生姓名
# 1.正向思路:课下可以尝试一下
# 2.反向思路:先找所有报了李平老师课程的学生 再取反
# 1.先查询李平老师教授的课程id号
-- select tid from teacher WHERE tname='李平老师';
-- select cid from course where teacher_id in (select tid from teacher WHERE tname='李平老师');
# 2.去成绩表中筛选出所有报了李平老师课程的学生id号
-- select distinct student_id from score where course_id in (select cid from course where teacher_id in (select tid from teacher WHERE tname='李平老师'));
# 3.去学生表中 取反获取没有报李平老师课程的学生姓名
-- SELECT
-- sname
-- FROM
-- student
-- WHERE
-- sid NOT IN (
-- SELECT DISTINCT
-- student_id
-- FROM
-- score
-- WHERE
-- course_id IN (
-- SELECT
-- cid
-- FROM
-- course
-- WHERE
-- teacher_id IN ( SELECT tid FROM teacher WHERE tname = '李平老师' )));
-- 8、 查询没有同时选修物理课程和体育课程的学生姓名(只要报了一门的 两门和都不报都不要)
# 1.先查询物理 和 体育课程的id号
-- select cid from course where cname in ('物理','体育');
# 2.去成绩表中先筛选出所有报了课程的数据(报了一门 报了两门)
-- select * from score where course_id in (select cid from course where cname in ('物理','体育'));
# 3.按照学生id分组 统计每个学生报了的课程数目
-- select student_id from score where course_id in (select cid from course where cname in ('物理','体育'))
-- group by student_id
-- having count(course_id) = 1
-- ;
# 4.去学生表中根据id获取学生姓名
-- SELECT
-- sname
-- FROM
-- student
-- WHERE
-- sid IN (
-- SELECT
-- student_id
-- FROM
-- score
-- WHERE
-- course_id IN (
-- SELECT
-- cid
-- FROM
-- course
-- WHERE
-- cname IN ( '物理', '体育' ))
-- group by student_id
-- having count(course_id) = 1);
-- 9、 查询挂科超过两门(包括两门)的学生姓名和班级
# 1.先去成绩表中 筛选出分数小于60分的数据
-- select * from score where num<60;
# 2.按照学生id分组 然后统计个数
-- select student_id from score where num<60 group by student_id
-- having count(num) >= 2
-- ;
# 3.将班级表与学生表拼接起来
SELECT
class.caption,
student.sname
FROM
class
INNER JOIN student ON class.cid = student.class_id
WHERE
student.sid IN ( SELECT student_id FROM score WHERE num < 60 GROUP BY student_id HAVING count( num ) >= 2 );

数据库前戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
储存数据

1 文件:
文件地址与数据格式杂乱(每个人都可以按自己的格式来存) 例如:
user:xxx:age:xxx
user.xxx.age.xxx
user-xxx-age-xxx
从而出现↓↓↓

2 软件开发目录规范:
规定了数据存储的位置,例如:
bin:启动文件
conf:配置文件
core:核心逻辑代码文件
db:数据文件
log:日志文件
lib:公共模块文件
...
(现在还是自己和自己玩,每个人的数据都自己储存--->单机游戏)
接着出现↓↓↓

3 数据库:
实现了数据共享,每个人的数据都储存在一个数据库里--->网络游戏
但是存在安全问题--->所有人的数据都放在一个数据库里,这个数据库没了,所有人的数据就都没了
所以出现了--->数据库集群 图解↓↓↓
(从原先一个数据库存储数据到现在每收到一份数据,就往其他所以数据库发送一份数据,这样一个数据库没了,还有其他数据库,从而避免了一个数据库挂了,所有数据陪葬的问题)

img

数据库分类

1
2
3
4
5
6
7
8
9
10
11
12
数据库本质就是一款cs架构软件(client--->客户端,server--->服务端)


数据库相关软件:
1 关系型数据库:(记)
固定表结构并且表与表之间可以建关系
常见软件:mysql --- postgresql---sqlite---db2---oracle---access---sql server

2 非关系型数据库:(记)
没有固定表结构并且储存数据以key value键值对的形式(类似字典)
常见软件:MongoDB---redis---memcache(逐渐淘汰)

MySQL简介

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
'''
MySQL本质上也是一款基于网络通信的应用程序
'''

任何基于网络通信的软件底层肯定都是socket


服务端
基于socket通信
本地文件操作
解析指令(SQL语句)

客户端
基于socket通信
发送指令
解析指令(SQL语句)

SQL语句的由来:
'''
mysql不单单支持mysql客户端操作,也支持其他编程语言来操作
python、Java、php、c++、go...
这些编程语言语法都不一样,mysql如何做到兼容↓↓↓
两种思路
1 自己精通多种语言,自由切换(不现实,且切换也需要耗费资源)
2 统一的思想(采用统一的一种语言--->sql语句)
'''

重要概念介绍

1
2
3
4
5
6
7
8
'''
库 ------> 文件夹
表 ------> 文件夹里的文件
记录 ------> 文件内一行行的数据

表头 ---> 表格的第一行数据
字段 ---> 字段名 + 字段类型
'''

下载与安装

1
2
3
4
5
6
7
1 版本问题
5.6(主流,逐渐向 5.7 过度)
5.7(主流)
8.0(最新版,一般不采用最新版本)

2 下载
官网 ---> 选择版本 ---> 下载压缩包 ---> 解压安装

系统服务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1 环境变量添加
我的电脑 ---> 属性 ---> 高级配置 ---> 环境变量 ---> path ---> 将bin目录路径添加进去

2 系统服务制作(全部在管理员身份下操作)
# 初始化(5.7之前需要先初始化)
mysqld --initialize-insecure
# 查看系统服务
services.msc
# 制作成系统服务
mysqld --install
# 启动服务
net start mysql
# 停止服务
net stop mysql
ps(windows操作系统上没有重启mysql服务的命令,如果需要重启服务,只能先stop再start)

用户登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 管理员初次登录无需密码
mysql -u用户名 -p密码 ---> 密码可以换行再输入,会将输入的密码隐藏成*****,如果在-p后直接输入密码,则是明文

# 游客模式
直接输入mysql 则进入游客模式,有很多限制

# 忘记密码
1 跳过授权表启动
mysqld --skip-grant-tables
2 无密码登录
mysql -uroot -p
3 修改用户密码(修改对应用户的密码)
update mysql.user set password=password(新密码) where user="root" and host="localhost";
4 退出正常启动

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
mysql默认配置文件my-default.ini不能修改
自己创建并命名my.ini
# \s 查看基本信息

my.ini文件内容 ---> 将字符编码改成utf8,Windows默认gbk
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8

基本SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-------------------------------- 库的增删改查 -----------------------------------------
# 增:
create database 库名(文件夹名)

# 改
alter database 库名(文件夹名) charset='gbk';
alter database ...

# 查
show database;
show create database db1(按创建的库名查)

# 删
drop database 库名(文件夹名);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--------------------------------- 表的增删改查 --------------------------------------
注意:要先切换到需要操作的库里(文件夹里)--->use 库名(文件夹名)
查看当前所在的库(文件夹):select database();

# 增:
create table 表名(文件名) (id int,name char(4)); # 创建出来的可能是多个文件,解耦管理

# 改
alter table 表名 modify name char(16); # 更改名字的最大字符长度

# 查
show tables; # 查所有表
show create table 表名; # 根据表名查看该表的详细信息
describe 表名; == desc 表名; # 根据表名查看该表的表结构

# 删
drop table 表名;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
----------------------------------- 记录的增删改查 -------------------------------------
# 增
insert into 库名.表名 values(1,'xxx'); # 增加单条数据
insert into 库名.表名 values(2,'xxx'),(3,'xxx'),(4,'xxx') # 增加多条数据

# 改
update 库名.表名 set name='XXX' where id > 1; # 修改名字,条件放在where后
update 库名.表名 set name='XXX' where id = 2 or id = 3; # where后可以用逻辑运算符

# 查
select id,name from 库名.表名; # 库名可不指定,默认当前库
select * from 表名; # 根据表名查看该表下所有记录

# 删
delete from 库名.表名 where id > 3; # 根据where后的条件判断需要删除哪些记录(数据)
delete from 库名.表名 where name='XXX';
delete from 库名.表名; # 删除该表中的所有记录(数据)


# 总结:
库 == 表:show(查)、alter(改)、create(增)、drop(删) ps(表:查看当前所在库:select 库名())
(关键字后要带database/table 再+具体库名/表名)

记录:select(查)、update(改)、insert into(增)、delete(删)

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
1 数据库发展史:
bs/cs架构

文件存储(单机) ---> 存储在一个数据库上(不安全) ---> 数据库集群(联机)

2 数据库分类

2.1 关系型数据库
-固定的表结构,表与表之间可以简历关联
-常见软件:mysql、postgresql、oracl、sqlite、db2、sql server access

2.2 非关系型数据库
-没有固定的表结构,存储数据按照kv键值对的格式(类似字典)
-常见软件:MongoDB、redis、memcache

'''
常见软件最好能记住默认端口号
-mysql -3306
-redis -6379
-MongoDB -27017
-Django -8000
-flask -5000
'''

3 mysql简介
# ps:防呆思想
3.1 版本问题
-SQL语句的由来
-统一思想

3.2 下载与安装

4 环境变量及系统服务

4.1 先添加环境变量

4.2 命令
mysqld --install
net start mysql
net stop mysql
mysqld --remove

5 用户登录及密码相关
mysql 游客模式
mysql -u用户名 -p密码

5.1 直接修改
mysqladmin -u用户名 -p原密码 password 新密码

5.2 忘记密码
...

6 重要概念
-库 ---> 文件夹
-表 ---> 文件
-数据 ---> 文件内一行行数据

7 配置文件
my.ini
...

8 基本SQL语句
...

存储引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
存储数据的方式不一样

# 查看所有的存储引擎
show engines;

# 需要知道的四个引擎
1 myisam
-mysql 5.5 及之前的版本默认的存储引擎
-存储数据的速度都非常快,但是功能较少,安全性较低

2 innodb
-mysql 5.6 及之后的版本默认的存储引擎
-存取速度没有myisam快,但是功能更多,安全性更高

3 blackhole
-往里面写入任何数据都会直接消失(黑洞)

4 menory
-用内存存储数据,断电数据消失

# 文件个数不一致
innodb
.frm --- 表结构
.ibd --- 表数据和索引
myisam
.frm --- 表结构
.MYD --- 表数据
.MYI --- 索引
blackhole
.frm --- 表结构
menory
.frm --- 表结构

MySQL基本数据类型

1
2
3
4
5
6
7
8
9
10
11
1 整型
tinyint smallint int bigint
1.1 不同的类型能够存储的数字范围不一样

# 结论:所有的整型都是自带符号

'''
(了解)如果不想让整型有符号:
create table t6(id tinyint unsigned);
insert into t6 values(-129),(256);
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

2 浮点型
float(255, 30)
-总共255位,小数占30
double(255, 30)
-总共255位,小数占30
decimal(65, 30)
-总共65位,小数占30

'''
精确度不一样
float < double < decimal

在实际开发中,很多时候看似需要用数字存储的数据
其实我们可能都是使用的字符串存储
'''
1
2
3
4
5
3 字符类型
char(4)
最大存储四个字符 超出了直接报错 不超过空格填充至四位
varchar(4)
最大存储四个字符,超出了直接报错 不超过有几位存几位
1
2
3
4
5
6
4 日期类型

datetime --- 年月日 时分秒
time --- 时分秒
date --- 年月日
year --- 年
1
2
3
4
5
6
7
8
9
10
11
12
13
14
5 枚举与集合
枚举 --- 多选一 ---> (例如性别)
create table t1(
id int,
name varchar(32)
gender enum('male','female')
);

集合 --- 多选多(多选一) ---> (例如爱好)
create table t2(
id int,
name varchar(32)
hobby set('read','study','music')
)

严格模式

1
2
3
4
5
6
7
8
9
10
# 查看sql_mode
show variables like '%mode%';

# 修改
get global sql_mode='strict_trans_tables';

# 修改完成后退出客户端重新进入即可

# 验证char是否自动填充
set global sql_mode='strict_trans_tables,pad_char_to_full_length'

字符类型对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
char(4)
优点:整存整取 速度快
缺点:浪费硬盘空间

varchar(4)
优点:节省硬盘空间
缺点:速度没有char快

# 各有优缺点,具体结合实际情况


'''
针对整型字段,括号里面的数字不是用来限制存储长度的而是用来控制显示长度的
---> 定义整型字段无需自己写括号和数字
'''

创建表的完整语法

1
2
3
4
5
6
7
8
9
10
11
create table 表名(
字段名1 字段类型(宽度) 约束条件1 约束条件2...,
字段名2 字段类型(宽度) 约束条件,
字段名3 字段类型(宽度) 约束条件
)
'''
易错点:
1 字段名和字段类型是必须的,宽度和约束条件是可选的
2 约束条件可以有多个
3 最后一个字段结尾不能加逗号,否则报错
'''

内容概要

  • 约束条件
  • 约束条件之外键(重点)
  • 表与表之间建立关系
  • 查询关键字(重点)
1
2
3
4
5
6
7
8
9
select
from
where
group by
having
distinct
order by
limit
regexp

内容详细

约束条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 在字段类型的基础之上添加额外的约束

1 unsigned ---> 无符号(正负号)

2 zerofill ---> 填充

3 not null ---> 非空
create table t1(
id int,
name varchar(32) not null
);

4 default ---> 默认值
create table t2(
id int,
name char(32),
gender enum('male','female') default 'male'
);

5 unique ---> 唯一
# 单列唯一:
create table t3(
id int,
name varchar(32) unique
);
# 多列唯一
create table t4(
id int,
host varchar(32),
port int,
unique(host,port)
);

6 primary key ---> 主键
"""
1 单从限制角度上来看 主键相当于是非空且唯一
id int primary key == id int not null unique
create table t5(
id int primary key ------>书写规范
);

2 innodb存储引擎规定了再创建表的时候必须有且只有一个主键
2.1 为什么之前创建表的时候没有主键也可以呢?
当表中没有任何的约束条件字段,为了能够顺利的创建出表,innodb存储引擎会采用内部隐藏的字段作为 主键
隐藏主键意味着我们无法使用到,正常主键其实可以帮助我们加快数据的查询速度,但因为被隐藏了,所以 其实在速度上并没有变化 ---> 要自己设定主键

2.2 没有主键但是有非空且唯一的字段,那么会自动将该字段升级为主键
create table t6(
id int,
age int not null unique, <----- 会自动将age升级为主键
pwd int not null unique
);

结论: 我们在创建表的时候一般都会创建一个id字段(序号\编号)
并且我们会将该字段设置为主键
id int primery key
ps:(id的名称可以不固定,uid、cid、pid...)
"""

7 auto_increment ---> 自增
# 单一主键
专门用来给key键使用(就理解成是专门给主键用的)
create table t7(
id int primary key auto_increment,
name varchar(32)
);
# 联合主键(了解)
create table t8(
id int,
nid int,
name varchar(32),
primary key(id,nid)...
);

'''
以后的主键字段固定写法:id int primary key atuo_increment
'''

主键的特征

1
2
3
# 1 delete from   ---> 不会影响主键的自增

# 2 truncate(了解) ---> 清空数据并且重置主键 (还不如删表重建)

添加数据的两种方式

1
2
3
1 insert into t1 values(...) # 按照字段顺序一一传值(用得少)

2 insert into t2(name) values(...) # 指名道姓的传值

外键前戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
'''员工表'''
id emp_name emp_salary dep_name dep_desc

# 问题
1 表结构不够明确
- 到底是员工表还是部门表

2 浪费存储空间

3 表拓展性极差

# 拆分
上诉三个问题都能够解决,但员工与部门之间就没有了关联

---> 外键字段
---> 用来建立表与表之间的关系的字段

表关系判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
表关系分类
1 一对多
2 多对多
3 一对一

表关系判断 ---> '换位思考'

1 以员工表与部门表为例
1.1 先站在员工表角度
问:一名员工能否对应多个部门
答:否
2.1 再站在部门表角度
问:一个部门能否对应多名员工
答:能
结论:两边一个能一个否 ---> 表关系就是:'一对多'
'''
针对一对多的表关系
外键字段建立在多的一方 ---> 建在员工表中
'''

2 以书籍表与作者表为例
2.1 先站在书籍表角度
问:一本书能否对应多名作者
答:能

2.2 再站在作者表角度
问:一本书能否对应多名作者
答:能
结论:两边都能 ---> 表关系就是:'多对多'
'''
针对多对多表关系
外键字段并不会建在任何一张关系表
而是单独开设一张新表专门存储
'''

3 以用户表与用户详情表为例
3.1 先站在用户表角度
问:一名用户能否对应多个用户详情
答:否
3.2 再站在用户详情表角度
问:一个用户详情能否对应多名用户
答:否
结论:两边都否 ---> 表关系就是:'一对一'

'''
针对一对一表关系
外键字段建在任何一方都可以
但是推荐建在查询评率较高的表中 ---> 用户表
'''

SQL语句实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
--------------------------------- 一对多 --------------------------------

# 1 先创建基本字段
# 2 再考虑外键字段
create table emp(
id int primary key auto_increment,
emp_name varchar(32),
emp_salary int,
dep_id int,
foregin key(dep_id) references dep(id)
);
create table dep(
id int primary key auto_increment,
dep_name varchar(32),
dep_desc varchar(64)
);
'''
1 在创建表的是先创建被关联的表(没有外键字段的表)
2 在录入数据的时候先录入被关联表的数据
3 修改或者删除被关联表数据
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

------------------------------------ 多对多 -------------------------------------
create table book(
id int primary key auto_increment,
title varchar(32),
price int
);
create table author(
id int primary key auto_increment,
name varchar(32),
age int
);
create table book2author(
id int primary key auto_increment,
author_id int,
foreign key(author_id) references author(id)
on update cascade
on delete cascade,
book_id int,
foreign key(book_id) references book(id)
on update cascade
on delete cascade
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

------------------------------------ 一对一 -------------------------------------
create table userinfo(
id int primary key auto_increment,
name varchar(32),
age int,
detail_id int unique,
foreign key(detail_id) reference userinfo_detail(id)
on update cascade
on delete cascade
);
create table userinfo_detail(
id int primary key auto_increment,
addr varchar(64),
phone varchar(32)
);

级联更新级联删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
create table dep(
id int primary key auto_increment,
dep_name varchar(32),
dep_desc varchar(64)
);
create table dep(
id int primary key auto_increment,
emp_name varchar(32),
emp_salary int
dep_id int,
foreign key(dep_id) references dep(id)
on update cascade # 级联更新
on delete cascade # 级联删除
);

总结:

1
2
3
七个约束条件:unsigned,zerofill,not null,default,unique,primary key,auto_increment

表关系判断(外键):一对多 --- 多对多 --- 一对一

内容概要

  • 表字段操作补充
  • python操作MySQL
  • 视图
  • 触发器
  • 存储过程
  • 事务
  • 内置函数
  • 流程控制
  • 索引

内容详细

表字段操作补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1 添加表字段
alter table 表名 add 字段名 字段类型 约束条件; # 默认尾部追加
alter table 表名 add 字段名 字段类型 约束条件 after 已经存在的字段名;
alter table 表名 add 字段名 字段类型 约束条件 first; # 了解

# 2 修改字段
'''modify只能修改字段数据类型完整约束,不能修改字段名,但是change二者都可以修改'''
ALTER TABLE 表名
MODIFY 字段名 数据类型 [完整性约束条件…];
ALTER TABLE 表名
CHANGE 旧字段名 新字段名 数据类型 [完整性约束条件…];

# 3.删除字段
ALTER TABLE 表名
DROP 字段名;

python操作mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
1.下载模块
pip3 install pymysql

"""
python默认下载模块的地址是国外的网站
速度有时候会非常的慢 如果想要提升速度我们可以切换下载源
"""
1)阿里云 http://mirrors.aliyun.com/pypi/simple/
2)豆瓣http://pypi.douban.com/simple/
3)清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
4)中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/
5)华中科技大学http://pypi.hustunique.com/
# 1.命令行切换源
pip3 install pymysql -i 源地址
# 2.pycharm永久切换
file
setting
interpreter
双击
manage 仓库

2 基本使用
import pymysql


# 创建连接对象
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
password = '123',
database = 'db4_29',
charset = 'utf8'
)

# 生成游标对象
cursor = conn.cursor(cursor = pymysql.cursor.DictCursor) # 括号内不写参数使元组组织不够明确

# 定义SQL语句
# sql = 'show tables'
sql = 'select * from teacher'

# 执行SQL语句
affect_rows = cursor.execute(sql)
print(affect_rows) # 执行SQL语句所影响的行数

# 获取结果
res = cursor.fetchall()
print(res)

sql注入

1
2
3
4
5
6
7
8
利用特殊符号的组合绕过相应的机制

如何解决
不要自己手动处理敏感数据

sql = "select * from userinfo where name=%s and password=%s"
# 执行sql语句
cursor.execute(sql,(username,password)) # 交由execute自动拼接 自动筛选

其他操作

1
2
3
4
5
6
7
8
9
10
11
12
13
针对增删改查
查的重要程度很低,无需二次确认
增改删重要程度很高,都需要二次确认

conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='1',
database='db5',
charset='utf8',
autocommit=True # 自动二次确认
)

视图

什么是视图

视图就是通过查询得到一张虚拟表,然后保存下来,下次直接使用即可

为什么要用视图

如果要频繁使用一张虚拟表,可以不用重复查询

如何用视图

1
2
3
4
5
6
7
create view teacher2course as
select * from teacher inner join course on teacher.tid = course.teacher_id;
"""
创建好了之后 验证它的存在navicat验证 cmd终端验证
最后文件验证 得出下面的结论 视图只有表结构数据还是来源于之前的表
delete from teacher2course where id=1;
"""

强调 1、在硬盘中,视图只有表结构文件,没有表数据文件 2、视图通常是用于查询,尽量不要修改视图中的数据

1
drop view teacher2course;

思考:开发过程中会不会去使用视图?

不会!视图是mysql的功能,如果你的项目里面大量的使用到了视图,那意味着你后期想要扩张某个功能的时候这个功能恰巧又需要对视图进行修改,意味着你需要先在mysql这边将视图先修改一下,然后再去应用程序中修改对应的sql语句,这就涉及到跨部门沟通的问题,所以通常不会使用视图,而是通过重新修改sql语句来扩展功能

触发器

在满足对某张表数据的增、删、改的情况下,自动触发的功能称之为触发器

为何要用触发器

触发器专门针对我们对某一张表数据增insert、删delete、改update的行为,这类行为一旦执行
就会触发触发器的执行,即自动运行另外一段sql代码

创建触发器语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""语法结构
create trigger 触发器的名字 before/after insert/update/delete on 表名 for each row
begin
sql语句
end
"""

# 针对插入
create trigger tri_after_insert_t1 after insert on 表名 for each row
begin
sql代码。。。
end
create trigger tri_after_insert_t2 before insert on 表名 for each row
begin
sql代码。。。
end

# 针对删除
create trigger tri_after_delete_t1 after delete on 表名 for each row
begin
sql代码。。。
end
create trigger tri_after_delete_t2 before delete on 表名 for each row
begin
sql代码。。。
end

# 针对修改
create trigger tri_after_update_t1 after update on 表名 for each row
begin
sql代码。。。
end
create trigger tri_after_update_t2 before update on 表名 for each row
begin
sql代码。。。
end

"""
需要注意 在书写sql代码的时候结束符是; 而整个触发器的结束也需要分号;
这就会出现语法冲突 需要我们临时修改结束符号
delimiter $$
delimiter ;
该语法只在当前窗口有效
"""

# 案例
CREATE TABLE cmd (
id INT PRIMARY KEY auto_increment,
USER CHAR (32),
priv CHAR (10),
cmd CHAR (64),
sub_time datetime, #提交时间
success enum ('yes', 'no') #0代表执行失败
);

CREATE TABLE errlog (
id INT PRIMARY KEY auto_increment,
err_cmd CHAR (64),
err_time datetime
);

delimiter $$ # 将mysql默认的结束符由;换成$$
create trigger tri_after_insert_cmd after insert on cmd for each row
begin
if NEW.success = 'no' then # 新记录都会被MySQL封装成NEW对象
insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
end if;
end $$
delimiter ; # 结束之后记得再改回来,不然后面结束符就都是$$了

#往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志
INSERT INTO cmd (
USER,
priv,
cmd,
sub_time,
success
)
VALUES
('egon','0755','ls -l /etc',NOW(),'yes'),
('egon','0755','cat /etc/passwd',NOW(),'no'),
('egon','0755','useradd xxx',NOW(),'no'),
('egon','0755','ps aux',NOW(),'yes');

# 查询errlog表记录
select * from errlog;
# 删除触发器
drop trigger tri_after_insert_cmd;

事务

什么是事务

开启一个事务可以包含一些sql语句,这些sql语句要么同时成功
要么一个都别想成功,称之为事务的原子性

事务的作用

保证了对数据操作的数据安全性

案例:用交行的卡操作建行ATM机给工商的账户转钱

事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性

原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。

一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。

隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

如何用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 先介绍事务的三个关键字 再去用表实际展示效果

create table user(
id int primary key auto_increment,
name char(32),
balance int
);

insert into user(name,balance)
values
('jason',1000),
('egon',1000),
('tank',1000);

# 修改数据之前先开启事务操作
start transaction;

# 修改操作
update user set balance=900 where name='jason'; #买支付100元
update user set balance=1010 where name='egon'; #中介拿走10元
update user set balance=1090 where name='tank'; #卖家拿到90元

# 回滚到上一个状态
rollback;

# 开启事务之后,只要没有执行commit操作,数据其实都没有真正刷新到硬盘
commit;
"""开启事务检测操作是否完整,不完整主动回滚到上一个状态,如果完整就应该执行commit操作"""

# 站在python代码的角度,应该实现的伪代码逻辑,
try:
update user set balance=900 where name='jason'; #买支付100元
update user set balance=1010 where name='egon'; #中介拿走10元
update user set balance=1090 where name='tank'; #卖家拿到90元
except 异常:
rollback;
else:
commit;

存储过程

存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql,类似于python中的自定义函数

基本使用

1
2
3
4
5
6
7
8
9
delimiter $$
create procedure p1()
begin
select * from user;
end $$
delimiter ;

# 调用
call p1()

三种开发模型

第一种

1
2
3
4
5
6
"""
应用程序:只需要开发应用程序的逻辑
mysql:编写好存储过程,以供应用程序调用
优点:开发效率,执行效率都高
缺点:考虑到人为因素、跨部门沟通等问题,会导致扩展性差
"""

第二种

1
2
3
4
5
6
7
"""
应用程序:除了开发应用程序的逻辑,还需要编写原生sql
优点:比方式1,扩展性高(非技术性的)
缺点:
1、开发效率,执行效率都不如方式1
2、编写原生sql太过于复杂,而且需要考虑到sql语句的优化问题
"""

第三种

1
2
3
4
5
"""
应用程序:开发应用程序的逻辑,不需要编写原生sql,基于别人编写好的框架来处理数据,ORM
优点:不用再编写纯生sql,这意味着开发效率比方式2高,同时兼容方式2扩展性高的好处
缺点:执行效率连方式2都比不过
"""

创建存储过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 介绍形参特点  再写具体功能

delimiter $$
create procedure p2(
in m int, # in表示这个参数必须只能是传入不能被返回出去
in n int,
out res int # out表示这个参数可以被返回出去
)
begin
select tname from teacher where tid > m and tid < n;
set res=0; # 用来标志存储过程是否执行
end $$
delimiter ;


# 针对res需要先提前定义
set @res=10; 定义
select @res; 查看
call p1(1,5,@res) 调用
select @res 查看

如何用存储过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 大前提:存储过程在哪个库下面创建的只能在对应的库下面才能使用!!!

# 1、直接在mysql中调用
set @res=10 # res的值是用来判断存储过程是否被执行成功的依据,所以需要先定义一个变量@res存储10
call p1(2,4,10); # 报错
call p1(2,4,@res);

# 查看结果
select @res; # 执行成功,@res变量值发生了变化

# 2、在python程序中调用
pymysql链接mysql
产生的游表cursor.callproc('p1',(2,4,10)) # 内部原理:@_p1_0=2,@_p1_1=4,@_p1_2=10;
cursor.excute('select @_p1_2;')


# 3、存储过程与事务使用举例(了解)
delimiter //
create PROCEDURE p5(
OUT p_return_code tinyint
)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
set p_return_code = 1;
rollback;
END;


DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
set p_return_code = 2;
rollback;
END;

START TRANSACTION;
update user set balance=900 where id =1;
update user123 set balance=1010 where id = 2;
update user set balance=1090 where id =3;
COMMIT;

-- SUCCESS
set p_return_code = 0; #0代表执行成功


END //
delimiter ;

函数

注意与存储过程的区别,mysql内置的函数只能在sql语句中使用!

参考博客:http://www.cnblogs.com/linhaifeng/articles/7495918.html#_label2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CREATE TABLE blog (
id INT PRIMARY KEY auto_increment,
NAME CHAR (32),
sub_time datetime
);

INSERT INTO blog (NAME, sub_time)
VALUES
('第1篇','2015-03-01 11:31:21'),
('第2篇','2015-03-11 16:31:21'),
('第3篇','2016-07-01 10:21:31'),
('第4篇','2016-07-22 09:23:21'),
('第5篇','2016-07-23 10:11:11'),
('第6篇','2016-07-25 11:21:31'),
('第7篇','2017-03-01 15:33:21'),
('第8篇','2017-03-01 17:32:21'),
('第9篇','2017-03-01 18:31:21');

select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');

流程控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# if条件语句
delimiter //
CREATE PROCEDURE proc_if ()
BEGIN

declare i int default 0;
if i = 1 THEN
SELECT 1;
ELSEIF i = 2 THEN
SELECT 2;
ELSE
SELECT 7;
END IF;

END //
delimiter ;
# while循环
delimiter //
CREATE PROCEDURE proc_while ()
BEGIN

DECLARE num INT ;
SET num = 0 ;
WHILE num < 10 DO
SELECT
num ;
SET num = num + 1 ;
END WHILE ;

END //
delimiter ;

索引

知识回顾:数据都是存在硬盘上的,那查询数据不可避免的需要进行IO操作

索引就是一种数据结构,类似于书的目录。意味着以后再查数据应该先找目录再找数据,而不是用翻页的方式查询数据

索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。

  • primary key
  • unique key
  • index key

注意foreign key不是用来加速查询用的,不在我们研究范围之内,上面三种key前两种除了有加速查询的效果之外还有额外的约束条件(primary key:非空且唯一,unique key:唯一),而index key没有任何约束功能只会帮你加速查询

本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。

索引的影响:

  • 在表中有大量数据的前提下,创建索引速度会很慢
  • 在索引创建完毕后,对表的查询性能会大幅度提升,但是写的性能会降低

b+树

https://images2017.cnblogs.com/blog/1036857/201709/1036857-20170912011123500-158121126.png

只有叶子结点存放真实数据,根和树枝节点存的仅仅是虚拟数据

查询次数由树的层级决定,层级越低次数越少

一个磁盘块儿的大小是一定的,那也就意味着能存的数据量是一定的。如何保证树的层级最低呢?一个磁盘块儿存放占用空间比较小的数据项

思考我们应该给我们一张表里面的什么字段字段建立索引能够降低树的层级高度>>> 主键id字段

聚集索引(primary key)

聚集索引其实指的就是表的主键,innodb引擎规定一张表中必须要有主键。先来回顾一下存储引擎。

myisam在建表的时候对应到硬盘有几个文件(三个)?

innodb在建表的时候对应到硬盘有几个文件(两个)?frm文件只存放表结构,不可能放索引,也就意味着innodb的索引跟数据都放在idb表数据文件中。

**特点:**叶子结点放的一条条完整的记录

辅助索引(unique,index)

辅助索引:查询数据的时候不可能都是用id作为筛选条件,也可能会用name,password等字段信息,那么这个时候就无法利用到聚集索引的加速查询效果。就需要给其他字段建立索引,这些索引就叫辅助索引

特点:叶子结点存放的是辅助索引字段对应的那条记录的主键的值(比如:按照name字段创建索引,那么叶子节点存放的是:{name对应的值:name所在的那条记录的主键值})

select name from user where name=’jason’;

上述语句叫覆盖索引:只在辅助索引的叶子节点中就已经找到了所有我们想要的数据

select age from user where name=’jason’;

上述语句叫非覆盖索引,虽然查询的时候命中了索引字段name,但是要查的是age字段,所以还需要利用主键才去查找

测试索引

准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#1. 准备表
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);

#2. 创建存储过程,实现批量插入记录
delimiter $$ #声明存储过程的结束符号为$$
create procedure auto_insert1()
BEGIN
declare i int default 1;
while(i<3000000)do
insert into s1 values(i,'jason','male',concat('jason',i,'@oldboy'));
set i=i+1;
end while;
END$$ #$$结束
delimiter ; #重新声明分号为结束符号

#3. 查看存储过程
show create procedure auto_insert1\G

#4. 调用存储过程
call auto_insert1();
# 表没有任何索引的情况下
select * from s1 where id=30000;
# 避免打印带来的时间损耗
select count(id) from s1 where id = 30000;
select count(id) from s1 where id = 1;

# 给id做一个主键
alter table s1 add primary key(id); # 速度很慢

select count(id) from s1 where id = 1; # 速度相较于未建索引之前两者差着数量级
select count(id) from s1 where name = 'jason' # 速度仍然很慢


"""
范围问题
"""
# 并不是加了索引,以后查询的时候按照这个字段速度就一定快
select count(id) from s1 where id > 1; # 速度相较于id = 1慢了很多
select count(id) from s1 where id >1 and id < 3;
select count(id) from s1 where id > 1 and id < 10000;
select count(id) from s1 where id != 3;

alter table s1 drop primary key; # 删除主键 单独再来研究name字段
select count(id) from s1 where name = 'jason'; # 又慢了

create index idx_name on s1(name); # 给s1表的name字段创建索引
select count(id) from s1 where name = 'jason' # 仍然很慢!!!
"""
再来看b+树的原理,数据需要区分度比较高,而我们这张表全是jason,根本无法区分
那这个树其实就建成了“一根棍子”
"""
select count(id) from s1 where name = 'xxx';
# 这个会很快,我就是一根棍,第一个不匹配直接不需要再往下走了
select count(id) from s1 where name like 'xxx';
select count(id) from s1 where name like 'xxx%';
select count(id) from s1 where name like '%xxx'; # 慢 最左匹配特性

# 区分度低的字段不能建索引
drop index idx_name on s1;

# 给id字段建普通的索引
create index idx_id on s1(id);
select count(id) from s1 where id = 3; # 快了
select count(id) from s1 where id*12 = 3; # 慢了 索引的字段一定不要参与计算

drop index idx_id on s1;
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';
# 针对上面这种连续多个and的操作,mysql会从左到右先找区分度比较高的索引字段,先将整体范围降下来再去比较其他条件
create index idx_name on s1(name);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 并没有加速

drop index idx_name on s1;
# 给name,gender这种区分度不高的字段加上索引并不难加快查询速度

create index idx_id on s1(id);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 快了 先通过id已经讲数据快速锁定成了一条了
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 慢了 基于id查出来的数据仍然很多,然后还要去比较其他字段

drop index idx_id on s1

create index idx_email on s1(email);
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 快 通过email字段一剑封喉

联合索引

1
2
3
4
5
6
7
8
9
10
11
12
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  
# 如果上述四个字段区分度都很高,那给谁建都能加速查询
# 给email加然而不用email字段
select count(id) from s1 where name='jason' and gender = 'male' and id > 3;
# 给name加然而不用name字段
select count(id) from s1 where gender = 'male' and id > 3;
# 给gender加然而不用gender字段
select count(id) from s1 where id > 3;

# 带来的问题是所有的字段都建了索引然而都没有用到,还需要花费四次建立的时间
create index idx_all on s1(email,name,gender,id); # 最左匹配原则,区分度高的往左放
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 速度变快

总结:

上面这些操作,你感兴趣可以敲一敲,不感兴趣你就可以不用敲了,权当看个乐呵。理论掌握了就行了

慢查询日志

设定一个时间检测所有超出该时间的sql语句,然后针对性的进行优化!

链接管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql

-u 指定用户
-p 指定密码
-h 指定主机域
-S 指定socket文件
-P 指定端口
-e 指定SQL语句

# 配置优先级
/etc/my.cnf 1
/etc/mysql/my.cnf 10
/application/mysql/my.cnf 20
~/.my.cnf 30

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
什么是实例?

进程+多个线程+预分配内存结构


什么是多实例?

(进程+多个线程+预分配内存结构)*多个

多个端口
多套配置文件
多个socket文件
datadir(多套数据目录)


多套配置文件:端口,多个data目录,多个socket文件

SQL结构化查询语言包含6个部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1.数据查询语言(DQL: Data Query Language)
数据检索语句,用于从表中获取数据。通常最常用的为保留字SELECT,并且常与FROM子句、WHERE子句组成查询SQL查询语句。
语法:
SELECT <字段名> FROM <表或视图名> WHERE <查询条件>;

# 2.数据操纵语言(DML:Data Manipulation Language)
主要用来对数据库的数据进行一些操作,常用的就是INSERT、UPDATE、DELETE。
语法:
INSERT INTO <表名>(列1,列2,...) VALUES (值1,值2,...);
UPDATE <表名> SET <列名>=新值 WHERE <列名>=某值;
DELETE FROM <表名> WHERE <列名>=某值;

# 3.事务处理语言(DPL)

事务处理语句能确保被DML语句影响的表的所有行及时得以更新。TPL语句包括BEGIN TRANSACTION、COMMIT和ROLLBACK。

# 4.数据控制语言(DCL)
通过GRANT和REVOKE,确定单个用户或用户组对数据库对象的访问权限。

# 5.数据定义语言(DDL)
常用的有CREATE和DROP,用于在数据库中创建新表或删除表,以及为表加入索引等。

# 6.指针控制语言(CCL)
它的语句,想DECLARE CURSOR、FETCH INTO和UPDATE WHERE CURRENT用于对一个或多个表单独行的操作。

字段注释

1
2
3
4
5
6
# comment字段注释
id int not null primary key auto_increment comment '学号',
name varchar(10) not null comment '姓名',
age tinyint unsigned comment '年龄',
sex enum('f','m','bgx') not null default 'bgx' comment '性别',
date datetime not null default NOW() comment '入学时间');

查询优化

1
2
3
4
5
6
7
8
9
10
#范围查询OR语句
select * from city where countrycode='CHN' or countrycode='USA';
#范围查询IN语句
select * from city where countrycode in ('CHN','USA');
-- 上述两条sql语句等价且效率一致

# 效率更高(推荐)
select * from city where countrycode='CHN'
union all
select * from city where countrycode='USA' limit 10

MySQL索引管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
btree       叶子结点不能直接跨(指针)
b+tree 叶子结点可以直接跨(指针)(5.6/5.7用的都是该算法)
b*tree 除了叶子结点、枝结点也能跨(指针)

这些指针的添加都是为了提升范围查询的效率
优化范围查询

#创建索引
alter table test add index index_name(name);
#创建索引
create index index_name on test(name);
#查看索引
desc table;
#查看索引
show index from table;
#删除索引
alter table test drop key index_name;
#添加唯一性索引
alter table student add unique key uni_xxx(xxx);
"""
添加唯一索引需要事先判断是否有重复数据 没有重复才能创建
#查看表中数据行数
select count(name) from city;
#查看去重数据行数
select count(distinct name) from city;
"""


# 前缀索引
根据字段的前N个字符建立索引
避免对大列建索引
如果有,就使用前缀索引
alter table test add index idx_name(name(10));

# 联合索引
多个字段建立一个索引
例:
where a.女生 and b.身高 and c.体重 and d.身材好
index(a,b,c)
特点:前缀生效特性
a,ab,ac,abc,abcd 可以走索引或部分走索引
b bc bcd cd c d ba ... 不走索引
创建people表
create table people (id int,name varchar(20),age tinyint,money int ,gender enum('m','f'));
#创建联合索引
alter table people add index idx_all(gender,age,money);

explain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
1.全表扫描
在explain语句结果中type为ALL
2.什么时候出现全表扫描?
2.1 业务确实要获取所有数据
2.2 不走索引导致的全表扫描
2.2.1 没索引
2.2.2 索引创建有问题
2.2.3 语句有问题
'''mysql在使用全表扫描时的性能是极其差的,所以MySQL尽量避免出现全表扫描'''


# explain命令使用方法
explain select name,countrycode from city where id=1;

2.索引扫描
常见的索引扫描类型:
1)index
2range
3)ref
4)eq_ref
5)const
6)system
7)null
"""从上到下,性能从最差到最好,我们认为至少要达到range级别"""
1.index:Full Index Scan,index与ALL区别为index类型只遍历索引树
2.range:索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行。显而易见的索引范围扫描是带有between或者where子句里带有<,>查询
3.ref:使用非唯一索引扫描或者唯一索引的前缀扫描,返回匹配某个单独值的记录行
4.eq_ref:类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件A
5.const、system:当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问:如将主键置于where列表中,MySQL就能将该查询转换为一个常量
6.NULL:MySQL在优化过程中分解语句,执行时甚至不用访问表或索引(条件不存在),例如从一个索引列里选取最小值可以通过单独索引查找完成。

查询语句最好
key_len: 越小越好
rows: 越小越好

建立索引原则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
1.选择唯一性索引(尽量考虑这个)
唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录
# 如果重复值较多,可以考虑采用联合索引
2.为经常需要排序、分组和联合操作的字段建立索引
3.为常作为查询条件的字段建立索引
3.1 经常查询
3.2 列值的重复值少
注:如果经常作为条件的列,重复值特别多,可以建立联合索引
4.尽量使用前缀来索引
如果索引字段的值很长,最好使用值的前缀来索引
"""
5.限制索引的数目
索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。
修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。

6.删除不再使用或者很少使用的索引
表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理
员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。
"""


*******************不走索引的情况*******************
1.没有查询条件,或者查询条件没有建立索引
"""
在业务数据库中,特别是数据量比较大的表,是没有全表扫描这种需求。
1)对用户查看是非常痛苦的。
2)对服务器来讲毁灭性的。
3)SQL改写成以下语句:
#情况1
#全表扫描
select * from table;
#需要在price列上建立索引
selec * from tab order by price limit 10;

#情况2
#name列没有索引
select * from table where name='zhangsan';
1、换成有索引的列作为查询条件
2、将name列建立索引
"""
2.查询结果集是原表中的大部分数据大概25%以上
哪怕建了索引也不会走,所以这个时候一般会用limit限制
3.索引本身失效,统计数据不真实
索引有自我维护的能力
对于表内容变化比较频繁的情况下,有可能会出现索引失效。
# 重建索引就可以解决
4.查询条件使用函数在索引列上或者对索引列进行运算,运算包括(+,-,*等)
错误的例子:select * from test where id-1=9;
正确的例子:select * from test where id=10;
5.隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误
# telnum定义的时候用的是varchar
create table test (id int ,name varchar(20),telnum varchar(10));
insert into test values(1,'zs','110'),(2,'l4',120),(3,'w5',119),(4,'z4',112);
explain select * from test where telnum=120;
alter table test add index idx_tel(telnum);
# 查询的时候给的确实数字类型
explain select * from test where telnum=120;
explain select * from test where telnum=120;
explain select * from test where telnum='120';
6. <> ,not in 不走索引
单独的>,<,in 有可能走,也有可能不走,和结果集有关,尽量结合业务添加limit
orin尽量改成union
EXPLAIN SELECT * FROM teltab WHERE telnum IN ('110','119');
#改写成
EXPLAIN SELECT * FROM teltab WHERE telnum='110'
UNION ALL
SELECT * FROM teltab WHERE telnum='119'
7.like "%_" 百分号在最前面不走
#不走索引
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110';
#走range索引扫描
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%';
# %linux%类的搜索需求,可以使用Elasticsearch -------> ELK
8.单独引用联合索引里非第一位置的索引列

docker-compose是容器编排工具,需要注意的是,docker-compose只是单机的容器编排工具

docker-compose 默认的配置文件为 : ==docker-compose.yaml==

安装docker-compose

1
2
3
4
5
6
7
8
9
10
11
# 下载

docker文档 : `https://docs.docker.com/compose/install/`

1 sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

2 sudo chmod +x /usr/local/bin/docker-compose

3 sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

4 docker-compose --version

docker-compose模板文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
version: "3.0"
services:
django:
image: django:v2
ports:
- "8080:8080"
container_name: django
networks:
- django
depends_on:
- mysql

mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: admin123
MYSQL_DATABASE: bbs09
container_name: mysql
volumes:
- "/root/docker-compose/mysql/data:/var/lib/mysql"
networks:
- django
depends_on:
- redis

redis:
image: redis:latest
container_name: redis
networks:
- django
networks:
django:

docker-compose指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# build

version: "3.0"
services:
django:
build:
./django
---

version: "3.0"
services:
django:
build:
context: ./django
dockerfile: Dockerfile
args:
NAME: python

# command : 设置容器启动命令

# depends_on : 指定在某容器之后启动(依赖于某容器)

# env_file : 指定环境变量文件

docker-compose参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
`启动`		 : docker-compose up

`停止并删除` : docker-compose down

`停止` : docker-compose stop

`删除容器` : docker-compose rm

`重启` : docker-compose restart 容器名称

`进入容器` : docker-compose exec 参数 service CMD

`容器列表` : docker-compose ps

`查看容器进程` : docker-compose top

`暂停运行` : docker-compose pause

`恢复运行` : docker-compose unpause

可视化工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: '3.0'
services:
portainer:
image: portainer/portainer-ce
ports:
- "8001:8000"
- "9001:9000"
container_name: portainer
networks:
- "portainer"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/root/docker-compose/portainer_data:/data"
healthcheck:
test: ["CMD", "ping", "-c1", "-t1", "portainer"]
interval: 30s
timeout: 10s
retries: 3

networks:
portainer: