为了实现实时更新或类似于聊天室的应用

  1. 一开始想到的肯定是配合setTimeout每隔一小段时间就给服务器发送请求,但是这样给服务器的压力很大,不够随机应变太耗费资源

  2. 第二个尝试地是长轮询,顾名思义,是服务器发出一个时间比较长的请求。比如时间设定为30秒,在30秒之内,如果收到服务器更新的数据,则进行对应的操作之后再发出请求,如果在30秒之内没有收到返回数据则执行error回调函数继续发出请求。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function Ask(){
    $.ajax({
    url: "http://47.92.139.26:8080/spring-boot-panda-0.0.1-SNAPSHOT/question/getNew/" + lastId + "/" + questionId
    dataType: "json",
    async: "true",
    timeout: "30000",
    //服务器发出一个30秒的请求
    success: function(msg) {
    done
    Ask();
    },
    error: function() {
    done
    Ask();
    }
    })
    }

    这种操作比起第一种肯定会比较节约资源,但是本质还是没有改变,还是客户端和服务器之间的单向对话,不够随机应变:D

  3. 最后使用了WebSocket,操作简单体验感很好:accept:

    (记得引入对应的WebSocket包

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
//打开双通道
function connect(){
var socket = new SockJS('http://47.92.139.26:8080/spring-boot-panda-0.0.1-SNAPSHOT/endpointAnswer');
//新建一个WebSocket对象

stompClient = Stomp.over(socket);
//使用STMOP子协议的WebSocket客户端

stompClient.connect({},function(frame){
//连接WebSocket服务端

//广播接收信息
stompTopic();
QuesTest = 1;
localStorage.setItem('QuesTest', QuesTest);

});
}

//关闭双通道
function disconnect(){
if(stompClient != null) {
stompClient.disconnect();
}
console.log("Disconnected");
}

//广播(一对多) = 接收服务器传过来的消息
function stompTopic(){
//通过stompClient.subscribe订阅目标(destination)发送的消息(广播接收信息)
stompClient.subscribe('/answerMass/getNew',function(response){
done
});
}

//群发 = 发送消息
function sendMassMessage(){
done
}

WebSocket和之前两种方式的原理都不一样,WebSocket可以打开双通道,做到请求不止可以有客户端发起也可以有服务器发出来,实现了双向对话,节约资源。一般有四个函数:打开双通道,接收服务器发送的消息,给服务器发消息,关闭双通道。

总结: WebSocket真香!

[TOC]

PHP基础语法

  • 定义变量:$num = 10;

  • 定义数组:$arr = array(1,3,5);

  • 打印内容:echo $num;

    echo不能输出集合,比如打印数组什么的

  • print_r($arr); // Array([0] => 1 [1] => 3 [2] => 5)

    echo arr[0]; // 1

  • 定义字典(对象):$dict = array("name"=>"lnj", "age"=>"33")

    echo $dict["name"];

  • 分支循环:if/switch/三目/for/while 和JS的一样

    1
    2
    3
    4
    5
    6
    7
    <?php
    $arr = array(1,3,5);
    for($i = 0;$i < count($arr),$i++){ //count($arr)获取数组长度
    echo $arr[$i];
    echo "<br>";
    }
    ?>

    后端编写的代码不能直接运行,只能放到服务器对应的文件夹下,通过服务器运行

get请求

1
2
3
4
5
<form action="http://www.baidu.com" method="get">
<input type="text" name="userName"><br>
<input type="password" name="userPwd"><br>
<input type="submit" value="提交"><br>
</form>
1
2
3
4
5
<?php
print_r($_GET); // Array([userName] => xx [userPwd] => 123456)
echo $_GET["userName"];
echo $_GET["userPwd"];
?>

Post请求

1
2
3
4
5
6
7
8
9
10
11
<form action="http://www.baidu.com" method="post">
<input type="text" name="userName"><br>
<input type="password" name="userPwd"><br>
<input type="submit" value="提交"><br>
</form>

<?php
print_r($_POST);
echo $_POST["userName"];
echo $_POST["userPwd"];
?>

GET请求和POST请求的异同:

  • 相同点:

    ​ 都是将数据提交到远程服务器

  • 不同点:

  1. GET请求会将数据提交到url后面

    POST请求会将数据提交到Nework里面的请求头里面

  2. GET请求对数据大小有限制

    POST请求对数据大小没有限制

  3. GET请求用于提交非敏感数据和小数据

    POST请求用于提交敏感数据和大数据

上传文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<form action="03-post-file.php" method="post" enctype="multipart/form-data">
<input type="file" name="upFile"><br>
<input type="submit" value="上传"><br>
</form>

<?php
print_r($_FILES);
//1.获取文件上传对应的字典(对象)
$fileInfo = $_FILES["upFile"];
//2.获取上传文件的名称
$fileName = $fileInfo["name"];
//3.获取上传文件保存的临时路径
$filePath = $fileInfo["tmp_name"];
//4.移动文件move_uploaded_file(旧路径,新路径加上传的文件名称); php中的字符串拼接用.
move_uploaded_file($filePath,destination:"./source/".$fileName);

?>

注意:上传文件一般用POST提交

必须指定该属性enctype="multipart/form-data"

上传的文件在php中可以通过$_FILES获取,文件默认会上传到一个临时目录,接收完毕之后会自动删除

GET基本使用

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
oBtn.onclick = function(ev){
//1.创建一个异步对象
var xmlhttp=new XMLHttpRequest();
//2.设置请求方式和请求地址
/*
method:请求的类型:GET 或者 POST
url: 文件在服务器上的位置
async: true(异步) 或 false(同步)
*/
xmlhttp.open("GET","04-ajax-get.php?t="+(new Date().getTime()),true);
//3.发送请求
xmlhttp.send();
//4.监听状态的变化
/*
0:请求未初始化
1:服务器连接已建立
2:请求已接收
3:请求处理中
4:请求已完成,且响应已就绪
*/
xmlhttp.onreadystatechange = function(ev1){
//判断是否请求成功
if(xmlhttp.readyState === 4){
//5.处理返回的结果
//判断http状态码,可以判断输入的地址是否正确
if(xmlhttp.status >= 200 && xmlhttp.status <300 ||xmlhttp.atatus === 304){
console.log(xhr.responseText);
//responseText获得字符串形式的服务器返回的响应式数据
//responseXML获得XML形式的响应数据
}
}
}


}

POST基本使用

xmlhttp.open("POST","04-ajax-get.php?t="+(new Date().getTime()),true);

传递参数的POST:

注意点:必须在open和send方法之间加入xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");

1
2
3
xmlhttp.open("POST","04-ajax-get.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xml.send("userName=zs&userPwd=111");

jQurey+Ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
oBtn.onclick = function(ev1){
$.ajax({
type:"GET",
url:"some.php",
data:"name=John&location=Boston",
success: function(msg){
slert(msg);
}
error:function(xhr){
//错误则显示状态码
alert(xhr.status);
}
})
}

XML

  • 书写XML代码

    必须有根标签

    标签名可以自定义

    1
    2
    3
    4
    5
    <?xml version="1.0" encoding="UTF-8" ?>
    <person>
    <name>xxxx</name>
    <age>11</age>
    </person>
  • 在PHP中获取XML文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php
    //执行结果中有中文,必须在php文件顶部设置
    //header("content-type:text/html;charset=utf-8");

    //如果PHP中需要返回XML数据,也必须在PHP文件顶部设置
    //header(string:"content-type:text/xml;charset=utf-8");


    header(string:"content-type:text/xml;charset=utf-8");
    file-get-contents(filename:"info.xml")

  • 在前端中获得后台返回的XML

    1
    2
    3
    var res = xhr.responseXML;
    //获得XML标签中的值
    var name = res.querySelector("name").innerHTML;

JSON

1
2
3
//php
<?php
echo file_get_contents(filename:"name.txt")

封装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
import axios from 'axios'

export function request(config) {
// 1.创建axios的实例
const instance = axios.create({
baseURL: 'http://123.207.32.32:8000',
timeout: 5000
//超时时间
})

// 2.axios的拦截器
// 2.1.请求拦截的作用:比如发送请求转圈圈的图标
//记得把拦截的东西返回!!!
instance.interceptors.request.use(config => {
return config
}, err => {
// console.log(err);
})

// 2.2.响应拦截
instance.interceptors.response.use(res => {
return res.data
}, err => {
console.log(err);
})

// 3.发送真正的网络请求
return instance(config)
}

调用axios

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import {request} from "./request";

export function getHomeMultidata() {
return request({
url: '/home/multidata'
})
}

export function getHomeGoods(type, page) {
return request({
url: '/home/data',
params: {
type,
page
}
})
}

[TOC]

安装

直接CDN引入

开发环境版本:<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

生产环境版本:<script src="https://cdn.jsdelivr.net/npm/vue"></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
<div id="app">
<div>
{{message}}
</div>
<ul>
<!--拿到遍历对象的内容和下标-->
<li v-for="(item,index) in movies">{{item}}</li>
</ul>
<button v-on:click="add">
+
</button>
<button @click="sub">
-
</button>
</div>
<script src="../js/vue.js">
const app = new Vue({
el:'#app',
data:{
message:'xxx',
name:'wjy',
counter:0,
movies:['第一部','第二部','第三部','第四部']
},
methods:{
add:function(){
console.log("add被执行");
this.counter++;//内部调用需要加this
}
sub:function(){
console.log("sub被执行");
}
}
})
</script>

el:string | HTMLElement

data:Object | Function

methods:{[key:string]:Function}

mustache

Mustache语法,也就是双大括号,将data中的文本数据插入到HTML中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="app">
<div>
<h3>
{{message}}
</h3>
<h3>
{{firstName+lastName}}
</h3>
<h3>
{{firstName+' '+lastName}}
</h3>
</div>
</div>
<script src="../js/vue.js">
const app = new Vue({
el:'#app',
data:{
message:'xxx',
firstName:'ddd',
lastName:'sss'
})
</script>

v-once,v-html,v-text,v-pre,v-cloak

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
<style>
[v-cloak]{
display:none;
}
</style>

<div id="app" v-cloak>
<!-- 当 vue开始解析就会去掉v-cloak属性 -->

<div>
<h3>
{{message}}
</h3>
<h3 v-once>
{{message}}
</h3>
<h3>
{{url}}
</h3>
<h3 v-html="url"></h3>
<!--识别html文档-->

<h3 v-html="v-text">你好啊</h3>
<!-- xxx 使用v-text会有覆盖现象-->

<h3 v-pre>{{message}}</h3>
<!-- 打印{{message}} -->

</div>
</div>
<script src="../js/vue.js">
const app = new Vue({
el:'#app',
data:{
message:'xxx',
url:'<a href="http://www.baidu.com">百度一下</a>'
})
</script>

加上v-once之后,即使在控制台改app.message='111',但是页面上显示的依旧是xxx ,表明改元素和组件只渲染一次,不会随着数据的改变而改变

v-bind

动态绑定某些属性,比如类名或者style

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">

</div>
<script src="../js/vue.js">
const app = new Vue({
el:'#app',
data:{
message:'xxx'
},
computed:{

}
})
</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
<div id="app">
<h2>
{{fullName}}
<!--计算属性不需要加小括号-->
</h2>
</div>
<script src="../js/vue.js">
const app = new Vue({
el:'#app',
data:{
books:[
{id:110,name:"第一本",price:119},
{id:111,name:"第二本",price:120},
{id:112,name:"第三本",price:121},
{id:113,name:"第四本",price:122}
],
firstName:'111',
lastName:'222'
}
computed:{
//filter/map/reduce
totalPrice:function(){
let result - 0;
for(let i in this.books){
result += this.books[i].price;
}
return result;
}
fullName:{
set:function(newValue){
//一般没有set方法,将计算属性设置为可读属性,使用set方法:控制台输入app.fullName = 'gggg'就会将这个值赋给newValue
console.log(newValue);//gggg
}
get:function(){
return this.firstName+lastName;
}
}
//上下两种fullName效果相同
fullName:function(){
return this.firstName+lastName;
}
}
})
</script>

计算属性相较于methods性能会更好,计算属性会进行缓存,如果多次使用时,计算属性只会调用一次

v-on

作用:绑定事件监听器

缩写:@

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
<div id="app">
<!--如果该方法不需要额外参数,那么方法后面的()可以不添加
如果该方法需要一个参数,那么没有加小括号的情况下,会默认将原生事件event参数传递进去-->
<button v-on:click="add">
+
</button>
<!--需要event参数同时需要其他参数的情况下,使用$event可以拿到event参数-->
<!--字符串记得加单引号-->
<button @click="sub('fff',$event)">
-
</button>

<div @click="divClick">
<button @click.stop="btnClick">
按钮/阻止事件冒泡
</button>
</div>
</div>
<script src="../js/vue.js">
const app = new Vue({
el:'#app',
data:{
message:'xxx',
name:'wjy',
counter:0,
movies:['第一部','第二部','第三部','第四部']
},
methods:{
add(){
console.log("add被执行");
this.counter++;//内部调用需要加this
}
sub(abc,event){
console.log("sub被执行");
console.log(abc,event);
}
}
})
</script>

修饰符的使用:

  • .stop阻止事件冒泡
  • .prevent阻止默认事件
  • .{keyCode|keyAlias} 只当事件是从特定键触发时才触发回调,例: @keyup.enter="keyUp"监听回车键
  • .native监听组件根元素的原生事件
  • .once只触发一次回调

v-if,v-else,v-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
<div id="app">
<h2 v-if=isShow>
{{message}}
</h2>
<h1 v-else>
isShow为false时,显示我
</h1>

<!--不推荐使用v-else-if,直接用计算属性更方便-->
<h2 v-if="score>=90">优秀</h2>
<h2 v-else-if="score>=80">良好</h2>
<h2 v-else-if="score>=60">及格</h2>
<h2 v-else>不及格</h2>
</div>

<script>
const app = new Vue({
el:'#app',
data:{
message:'nihaoa',
isShow: false,
score:99
}
})
</script>

input有复用问题,会对某一些元素进行复用,如果确实需要创建新的元素的话可以用key值加以区别。

v-show

1
2
3
4
5
6
 <h2 v-if="false" id="aaa">
{{message}}<!--不会出现在DOM元素中-->
</h2>
<h2 v-show="isShow" id="bbb">
{{message}}<!--出现在DOM元素中,但display为none-->
</h2>

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
<!--需要中间插入值的时候,使用key属性可以更高效-->

<!--遍历数组-->
<!--不需要下标值-->
<ul>
<li v-for="item in movies" :key:"item">
{{item}}
</li>
</ul>

<!--需要下标值-->
<ul>
<li v-for="(item,index) in movies">
{{index+1}}{{item}}
</li>
</ul>

<!--遍历对象-->
<!--一个参数:value-->
<ul>
<li v-for="item in movies">
{{item}}
</li>
</ul>

<!--(value,key)-->
<ul>
<li v-for="(value,key) in movies">
{{key}}-{{value}}
</li>
</ul>

<!--(value,key,index)-->
<ul>
<li v-for="(value,key) in movies">
{{index+1}}-{{key}}-{{value}}
</li>
</ul>
<script src="../js/vue.js"></script></script>
<script>
const app = new Vue({
el:'#app',
data:{
message:'xxx',
info:{
name:'ddd',
age:18,
height:1.88
},
movies:['第一部','第二部','第三部','第四部']
}
})
</script>

直接通过数组下标进行修改的话,并不会响应式,不会重新渲染页面

解决:

  1. Vue方法:set(要修改的对象,索引值,修改后的值) 响应式方法Vue.set(this.movies,0,'第五部');
  2. splice()

数组中的响应式方法: (使用它们改变数组也会发生对应的更新)

  • push()添加到最后,可以接受多个参数

  • pop()删除数组中的最后一个元素

  • shift()删除数组中的第一个元素

  • unshift()添加到第一位,可以接受多个参数

  • splice()删除元素/插入元素/替换元素

    删除元素:(开始位置,删除个数)

    替换参数:(开始位置,替换的个数,替换的元素。。。)

    插入元素:(开始位置,0,插入的元素。。。)

  • sort()排序

  • reverse()反转

过滤器

| 过滤器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div>
{{item.price}} | showPrice
</div>

<script>
const app = new Vue({
el:'#app',
data:{
item:{
price:11,
id:11,
num:11
}
filters:{//过滤器
showPrice(price){
return '¥' + price.toFixed(2)
}
}
}
})
</script>

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
//for in循环的是下标
for(let i in this.books){
const book = this.books[i];
totalPrice += books.price*book.count;
}

//for of循环的是每一个数组元素
for(let item of this.books){
totalPrice += item.price*item.count;
}

//JS的高阶函数

//1.filter中的回调函数有一个要求:必须返回一个Boolean值。当返回true时,函数内部会自动将这次回调的n加入到新的数组中,当返回false,函数内部会过滤到这次的n

//例:取出数组中所有小于100的数字
let newArr = arr.filter(function(n){
return n < 100
})

//2.map 例:返回原来数组的两倍
let newArr2 = newArr.map(function(n){
return n*2
})

//3.reduce 对数组中所有的内容进行汇总 例:对数组中所有的数字相加
let total = newArr2.reduce(function(preValue,n){
return preValue + n
},0)

//上述三步操作的结合:类似链式
let total = arr.filter(function(n){
return n<100
}).map(function(n){
return n*2
}).reduce(function(preValue,n){
return preValue + n
},0)

//结合箭头函数
let total = arr.filter(n => n<100).map(n => n*2).reduce((pre,n)=>pre +n);

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
27
28
29
30
 <!-- v-model可以绑定input或者textarea -->


<div id="app">
<!-- v-model双向绑定 -->
<input type="text" v-model="message">
<!-- "message"会直接成为input的value,修改input里面的内容,message的值也会被改变 -->
</div>
<script>
const app = new Vue({
el:'#app',
data:{
message:'xxx'
}
})
</script>

<!--input有一个input事件-->
<div id="app">
<input type="text" :value="message" @input="message = $event.target.value">
</div>
<script>
const app = new Vue({
el:'#app',
data:{
message:'xxx'
}
}
})
</script>

v-model结合radio

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--通过v-model绑定,可以将数据传递到data里面的sex,同时v-model绑定了同一个变量这时可以省略name属性-->

<div id="app">
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
</label>
</div>

<script>
const app = new Vue({
el:'#app',
data:{
message:'xxx',
sex:'男'//radio会默认选中男的单选框
}
}
})
</script>

v-model结合checkbox

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
<div id="app">
<!--单选框,对应布尔值-->
<!--加label后点击文字也可以选中复选框-->
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>

<!--同意协议之后才能点击下一步-->
<button :disabled="!isAgree">
下一步
</button>


<!--复选框,对应数组-->
<input type="checkbox" value="爱好1" v-model="hobbies">爱好1
<input type="checkbox" value="爱好2" v-model="hobbies">爱好2
<input type="checkbox" value="爱好3" v-model="hobbies">爱好3
<input type="checkbox" value="爱好4" v-model="hobbies">爱好4

</div>

<script>
const app = new Vue({
el:'#app',
data:{
message:'xxx',
isAgree:false,
hobbies:[]
}
})
</script>

v-model结合select

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
<div id="app">
<!--单选,字符串类型-->
<select name="abc" v-model="fruit">
<option value="水果1">水果1</option>
<option value="水果2">水果2</option>
<option value="水果3">水果3</option>
<option value="水果4">水果4</option>
<option value="水果5">水果5</option>
</select>


<!--多选mutiple,数组类型-->
<select name="abc" v-model="fruits" multiple>
<option value="水果1">水果1</option>
<option value="水果2">水果2</option>
<option value="水果3">水果3</option>
<option value="水果4">水果4</option>
<option value="水果5">水果5</option>
</select>
</div>

<script>
const app = new Vue({
el:'#app',
data:{
message:'xxx',
fruit:'水果1',//默认选择水果1
fruits:[]

}
})
</script>

修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<!--lazy失去焦点或者敲回车的时候再进行绑定-->
<input type="text" v-model.lazy="message">

<!--v-model默认绑定过去的值都是string类型-->
<!--number-将绑定的值改为数字类型-->
<input type="text" v-model.number="message">

<!--trim将字符串左右两边的空格消除-->
<input type="text" v-model.trim="message">
</div>

<script>
const app = new Vue({
el:'#app',
data:{
message:'xxx',
fruit:'水果1',//默认选择水果1
fruits:[]

}
})
</script>

判断图片是否加载完成

  • JS方法:img.onload
  • Vue: @load
1
<img :src="goods.img" @load="imageLoad">

生命周期

20170303180741807

它可以总共分为8个阶段:

  1. beforeCreate(创建前)
  2. created(创建后)
  3. beforeMount(载入前)
  4. mounted(载入后)一般将vue写在这里面
  5. beforeUpdate(更新前)
  6. updated(更新后)
  7. beforeDestroy(销毁前)
  8. 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
<div id=app>{{a}}</div>

<script>

var myVue = new Vue({
el: "#app",
data: {
a: "Vue.js"
},
beforeCreate: function() {
console.log("创建前")
console.log(this.a)
console.log(this.$el)

},
created: function() {
console.log("创建之后");
console.log(this.a)
console.log(this.$el)
},
beforeMount: function() {
console.log("mount之前")
console.log(this.a)
console.log(this.$el)
},

mounted: function() {
console.log("mount之后")
console.log(this.a)
console.log(this.$el)
},
beforeUpdate: function() {
console.log("更新前");
console.log(this.a)
console.log(this.$el)
},
updated: function() {
console.log("更新完成");
console.log(this.a);
console.log(this.$el)
},
beforeDestroy: function() {
console.log("销毁前");
console.log(this.a)
console.log(this.$el)
console.log(this.$el)
},
destroyed: function() {
console.log("已销毁");
console.log(this.a)
console.log(this.$el)
}
});
</script>

组件化

  1. 创建组件构造器Vue.extend()
  2. 注册组件Vue.component()
  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
50
51
52
53
54
55
56
57
58
59
60
61
62
<div id="app">
<!--3.使用组件 -->
<my-cpn></my-cpn>
</div>

<!--组件模板抽离写法1 -->
<script type="text/x-template" id="cpn">
<div>
<h2>111111</h2>
<h2>111111</h2>
</div>
</script>

<!--组件模板抽离写法2 -->
<template id="cpn">
<div>
<h2>111111</h2>
<h2>111111</h2>
</div>
</template>



<script>
//1.创建组件构造器对象
const cpnC = Vue.extend({
template:`#cpn` //模板抽离
})
//2.注册组件(全局组件)
Vue.component('my-cpn',cpnC);


//语法糖写法:(将一二两步合并)
Vue.component('cpn1',{
template:`
<div>
<h2>111111</h2>
<h2>111111</h2>
</div>`
})
const app = new Vue({
el:'#app',

/*注册局部组件
components:{
//cpn;使用组件的标签名
cpn:cpnC
}

局部组件语法糖:
components:{
'cpn1':{
template:`
<div>
<h2>111111</h2>
<h2>111111</h2>
</div>`
}
}
*/
})
</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
<script>
const cpnC1 = Vue.extend({
template:`
<div>
<h2>111111</h2>
<h2>111111</h2>
</div>`
})

const cpnC2 = Vue.extend({
template:`
<div>
<h2>222222</h2>
<h2>2222222</h2>
</div>`,
cpmponents:{
cpn1:cpnC1
//在父组件里面注册子组件
//在这里注册作用域只在于cpnC2
}
})

//root组件
const app = new Vue({
el:'#app',
components:{
cpn2:cpnC2
cpn1:cpnC1
//在这里注册就可以在实例里面使用了
}
})
</script>

父子组件的通信

1.通过props向子组件传递数据

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
<!--父传子-->

<div id="app">
<cpn :cmovies="movies" :cmessage="message"></cpn>
<!-- v-bind不支持驼峰标识,如果是驼峰要绑定要用横杠-写法-->
</div>

<template id="cpn">
<div>
<!--必须有一个外层div作为根元素-->
{{cmovies}}
</div>
</template>

<script>
const cpn = {
template:'#cpn',
//props:['cmovies','cmessage'],
props:{
//类型限制
cmovies: Array,
//
cmessage:{
type: String,
default:'aaaaaa'
}
//如果没有传入则cmessage的默认值为'aaaaaa'
required: true
//表示必须传入该值
}
},
data(){
return{}
}
}

const app = new Vue({
el:'#app',
data:{
message:"nnnn",
movies:['第一步','第一步','第一步'];
}
components:{
cpn
//属性的增强写法
}
})
</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
Vue.component('my-component',{
props{
//多个可能的值
propA:[Srting,Number],

//带有默认值的数字
propB:{
type: Number,
default:100
}

//带有默认值的对象
propC:{
type:Object,
//对象或数组的默认值必须从一个工厂函数获取
default: function(){
return{message:'hello'}
}
}

//自定义验证函数
propD:{
validator: function(value){
return ['success','warning','danger'].indexof(value) !== -1
}
}
}
})
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
<!--子传父-->

<!--父组件模板-->
<div id="app">
<!--接受子组件的事件-->
<cpn @itemclick="cpnclick"> </cpn>
</div>

<!--子组件模板-->
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnclick(item)">
{{item.name}}
</button>
</div>
</template>

<script>
const cpn = {
template:'#cpn',
data(){
return{
categories:[
{id:'aaa',name:'第一'},
{id:'bbb',name:'第二'},
{id:'ccc',name:'第三'}
]
}
},
methods:{
btnclick(item){
//子组件发射自定义事件
this.$emit('itemclick',item)
}
}
}

//父组件
const app = new Vue({
el:'#app',
data:{
message:'aaa'
},
components{
cpn
},
methods{
cpnClick(item){
concole.log('接收到子组件的事件',item)
}
}
})
</script>

父子组件间的访问

  • 父组件访问子组件使用$children或者$refs

  • 子组件访问父组件使用$parent

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <!--父访问子--> 

    //父组件的method
    methods:{
    btnClick(){
    //打印第一个子组件
    console.log(this.children[0]);
    }
    }


    <cpn ref="aaa"></cpn>
    methods:{
    btnClick(){
    //打印ref为aaa的子组件
    console.log(this.$refs.aaa);
    }
    }

    <!--子访问父-->
    $parent

    <!--访问根组件-->
    $root

组件data

组件内部使用的数据需要放在自己的data,methods属性

data必须为一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
Vue.component('cpn',{
template:'#cpn',
data(){
return{
title:'abc'
}
},
watch:{
//监听值是否有改变
name(newValue,oldValue){
//改变后执行的函数
}
}
})
</script>

slot

插槽的基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<cpn>
<button>
会替换到slot处,只会替换没有名字的
</button>
</cpn>


<template>
<div>
<h2>
我是组件
</h2>
<slot>
<div>
我是默认值
</div>
</slot>
</div>
</template>

具名插槽的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<cpn>
<button slot="left">
会替换到name为left的slot处
</button>
</cpn>



<template>
<div>
<slot name="left"></slot>
<slot name="center"></slot>
<slot name="right"></slot>
</div>
</template>

作用域

父级模板的数据来自父组件,子级模板的数据来自子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<!--isShow数据来自挂载id=app的Vue实例的data-->
<cpn v-show="isShow"></cpn>

<!--取到的name.data就是pLanguages-->
<cpn>
<template slot-scope="name">
<span v-for="item in slot.data"></span>
</template>
</cpn>
</div>

//data name这些名字都是可以自定义的


//作用域插槽:
//父组件替换插槽的标签,内容由子组件提供
<template>
<div>
<!--如果想要拿到cpn中的data中的pLanguages值-->
<slot :data="pLanguages"></slot>
</div>
</template>

CommonJS

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

//导出
module.exports = {
flag: true,
test(a,b){
return a+b
},
demo(a,b){
return a*b
}
}

//导入
//CommonJS模块
let {test,demo,flag} = require('moduleA');

//等同于
let _mA = require('moduleA');
let test = _mA.test;
let demo = _mA.demo;

ES模块化的导入导出

1
2
3
<!--引入js文件需要加上type属性-->
<script src="aaa.js" type="module"></script>
<script src="bbb.js" type="module"></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
//aaa.js
var name="111";
var age="222";
var flag = true;

function sum(num1,num2){
return num1+num2;
}

//导出方式1:
export{
flag,sum
}

//导出方式2:
export var num1 = 1000;

//导出函数/类
export function des(num1, num2){
return num1-num2;

export class Person{
run(){
console.log('ssss');
}
}

//export default 自定义名称
//一个模块中最多只能有一个default
const address = '北京';
export default address



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//bbb.js
//导入
import{flag,sum}from"./aaa.js";

if(flag){
console.log(sum(20,30));
}

//导入类
import{Person}from"./aaa.js";

const p = new Person();
p.run();

//导入default,接收address并命名为add
import add from"./aaa.js";

//将全部变量导入
import * as item from './aaa.js'

console.log(item.name);

webpack

1
2
3
4
5
//依赖css文件
require('./css/normal.css')

//依赖less文件
requiree('./css/special.less')

ES6和CommonJS的导出文件不可以写在同一个文件里面

Vue CLI

安装cnpm:

cnpm: npm install -g cnpm –registry=https://registry.npm.taobao.org

使用cnpm命令来安装模块:

cnpm install [name]

安装vue脚手架:

npm install -g @vue/cli

runtime-compiler和runtime-only的区别

runtime-compiler: template -> ast -> render -> vdom ->UI

runtime-only: render -> v-dom -> UI

runtime-only:1.性能更高 2.下面的代码量更少

createElement用法

createElement('标签',{标签的属性}),['数组的内容']

createElement('h2',{class:'box'},['Hello World',createElement('button',['按钮'])])

路由器

hash

通过localtion.hash进行改变网站的url而且不会进行再次刷新和重新向服务器请求资源

pushState

history.pushState({},'','home')

压缩进一个栈,永远显示的时是在栈顶的那一个,会记住每一个历史记录

replace

history.replaceState({},'','home')

替换,不会记录每一次历史记录

go

history.go(-1) = history.back()

history.go(1) = history.forward()

往前跳一页,用数字来进行在栈里面的跳转

打包文件的解析

npm run build

dist ->static ->js

app:当前应用程序开发的所有代码(业务代码)

vwndor:提供商,比如第三方Vue,axios,bs

manifest:为了打包的代码数据做底层支撑

路由的懒加载

当打包构建应用时,js包会变得非常大,在第一次加载时影响页面的加载速度,如果我们能把不同路由的组件分割成不同的代码块,然后当路由被访问时才会加载对应的组件。这样加载会更加的高效,提高用户的体验舒适度。

1
2
3
4
5
6
const routes = [
{
path: '/home',
components: () => import('../cpmponents/Home')
}
]

路由传递参数

两种类型

  1. params动态路由

    配置路由格式:/router/:id

    传递方式:在path后面跟上对应的值

    传递后形成的路径:/router/123

  2. query

    配置路由的格式:/router,也就是普通配置

    传递的方式:对象中使用query的key作为传递方式

    传递后形成的路径:/router?id=123

    拿参数:this.id = this.$route.params.id

两种使用方式

  1. 代码修改
  2. router-link

导航守卫

监听页面跳转

meta 元数据(描述数据的数据)

metaclass 元类

全局守卫:

router.beforeEach 前置守卫 页面跳转前发生,调用next函数可以跳到指定页面

router.afterEach 后置钩子 页面跳转后发生,不需要主动调用next()函数

路由独享守卫:…

组件内的守卫:…

vue-router-keep-alive

两个属性

include 字符串或者正则,只有匹配的组件会被缓存

exclude 字符串或者正则,任何匹配的组件都不会被缓存

Promise

一般情况下有异步操作的话,使用Promise对异步操作进行封装

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
new Promise((resolve,reject) => {
setTimeout(() => {
resolve()
//调用resolve函数就会来到then
},1000)
}).then(() => {
console.log("1111111");
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve()
})
}).then(() => {
console.log("222222");
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve()
})
}).then(() => {
console.log("3333333333");
})
})
})


new Promise((resolve,reject) => {
setTimeout(() => {
//成功的时候调用resolve
//失败的时候调用reject就不会进入then函数料而是调用catch函数
reject('error message')
},1000)
}).then(() => {}).catach((err) => {
console.log(err)
//error message
})

//当我们主动回调resolve时,就处于满足状态,并且会回调.then()
//当我们主动回调reject时,就处于拒绝状态,会回调.catch()



//简写
new Promise((resolve,reject) => {
setTimeout(() => {
resolve('aaa')
},1000)
}).then(res => {
console.log("......");
return Promise.resolve(res + '111')
}).then(res => {
console.log(".......")
})

//等于 如果要调用reject的话直接把resolve换成reject就可以了
new Promise((resolve,reject) => {
setTimeout(() => {
resolve('aaa')
},1000)
}).then(red => {
console.log("......");
return res + '111'
}).then(res => {
console.log(".......")
})

Promise 的 all方法使用

使用该方法可以再所有的网络请求都结束之后再进行then方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Promise.all([
new Promise((resolve,reject) => {
$ajax({
url:'url1',
success: function(data){
resolve(data)
}
})
}),
new Promise((resolve,reject) => {
$ajax({
url: 'url2',
success: function(data){
resolve(data)
}
})
})
]).then(results => {
//拿到上诉网络请求的结果
results[0]
//上面第一个ajax的结果
results[1]
//上面第二个ajax的结果
} )

Vuex

1.安装vuex插件并使用

2.创建对象const store = new Vuex.store({}) 并在state里面定义状态

3.在其他页面使用e.g. 拿到定义的couter状态

4.定义方法:在mutations里面进行定义,必须是同步操作

5.在需要使用这个方法的页面的method进行commit this.$store.commit('mutation里面定义的方法名')就可以啦

6.当有异步操作的时候,方法要先放在actions里面然后放在mutations里面

1
2
3
4
5
6
7
8
9
10
11
12
13
mutation: {
updateInfo(state){

}
}
actions: {
aUpdateInfo(context,payload) {
setTimeout(() => {
context.commit('updateInfo');
console.log('payload')
},1000)
}
}

在需要使用这个方法的页面的method进行commit this.$store.dispatch('aUpdateInfo','我是payload')就可以啦

如果一开始address属性不在state中定义,

添加:state.info['address'] = '洛杉矶'不会被显示,因为这个时候不是响应式,而需要要用Vue.set(state.info,'address','洛杉矶')就会变成响应式。

删除:

delete state.info.age没有响应式

Vue.delete(state.info,'age')有响应式

推荐将mutations里面的方法名字使用常量代替,之后在store和method里面要用到这个方法名的话直接使用这个常量

Vuex getter

Vuex里面的计算属性

1
2
3
4
5
getters: {
fullname(state){
return state.name + '11111'
}
}

Vuex modules

将需要的模块可以放在modules,而每一个模块都可以拥有自己的state,mutation,actions,getter

使用方法:

$store.state.a.name

拿到模块a里面的state里面的name状态

this.$store.commit('updateName','lisi)

commit:模块和mutations的上传方式都一样

axios

1.进行安装:npm install axios –save

2.在项目中进行导入:import axios from 'axios'

3.直接使用

1
2
3
4
5
6
7
8
9
10
11
12
axios({
url: 'http://123.207.32.32:8000/home/multidata',
method: 'get',
params: {
//传入参数,会自动进行拼接,专门针对get请求的参数拼接
type: 'pop',
page: 1
}
}).then(res => {
console.log(res);
})

axios发送并发请求与全局配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//全局配置
axios.defaults.baseURL = 'http://123.207.32.32:8000'
//设置根路径
axios.defaults.timeout = 5000


axios.all([
axios({
url: '/home/multidata'
}),
axios({
url: '/home/data'
})
])then(axios.spread((res1,res2) => {
//使用axios的spread方法就可以将放回的两个结果展开
//这两个结果的顺序是按照网络请求发出去的顺序还是收到的顺序
console.log(res1);
console.log(res2);
}))

get请求对应params

post请求对应data

axios的实例和模块封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const instance2 = axios.create({
baseURL: 'http://,,,,'
timeout:5000
})

instance1({
url:'/home/multidata'
}).then(res => {
console.log(res);
})

instance1({
url:'/home/data'
}).then({
res => {
console.log("这是使用这个实例的第二个方法")
}
})
//使用实例可以避免使用全局配置,适用于比如两个timeout或者根目录的情况,而且一个实例可以被多次使用

一般使用第三方框架的时候不要直接进行引用,进行模块封装之后再进行使用

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
//封装方式一
import axios from 'axios'

export function request(config,success,failure) {
const instance = axios.create({
baseURL: 'http://....'
timeout: 5000
})

//发送真正的网络请求
instance(config)
.then(
res =>{
console.log(res);
success(res)
}
)
.catch(
err =>{
console.log(err)
failure(err)
}
)
}
//在别的页面需要用到的时候
import {request} from "./network/request";

request(
{
url: '/home/multidata'
},
res => {
console.log(res)
},
err => {
console.log(err)
}
)

//封装方式二,用Promise进行优化:
export function request(config) {
return new Promise((resolve,reject) => {
const instance = axios.create({
baseURL: 'http......'
timeout: 5000
})

//拦截器
//发送拦截
instance.interceptors.request.use(
config => {
//发送成功并拦截消息
//应用:在每次发送网络请求的时候,都在界面显示一个请求的图标
//判断传入来的信息是否正确判断返回页面
console.log(config)
//继续将拦截消息发送,否则拦截了就发不出去了
return config
},
err => {
//发送失败
console.log(err)
})

//响应拦截
instance.interceptors.response.use(
res => {
console.log(res)
//切记拦截之后要返回
return res
},
err => {

}
)

return instance(config)
})
}
//在别的页面需要用到的时候
request({
url: '/home/multidata'
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})

axios拦截器

用于在发送每次请求或者得到回应之后,进行相应的处理

事件总线

当两个部分距离太远互相之间没有联系但是需要传输数据的时候,可以使用事件总线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//在main.js里面创建事件总线
Vue.prototype.$bus = new Vue()

//在需要发射数据的组件里:
methods: {
imageLoad() {
this.$bus.$emit('itemTmageLoad',参数)
}
}

//在需要接收的组件里面:
created() {
this.$bus.$on('itemTmageLoad', () => {
Done;
})
}

$emit('事件名称',参数)

$on('事件名称',回调函数(参数))

刷新频繁的防抖操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
debounce(func,delay) {
let timer = null

return function (...args) {
if(timer){
clearTimerout(timer)
}
timer = setTimeout(() => {
func.apply(this,args)
},delay)
}
}

const refresh = this.debounce(this.$refs.scroll.refresh,500)
//传入函数作为参数的时候:function代表传入这个参数,function()代表传入这个参数的返回值

:el

所有的组件都有一个$el:属性,用于获取组件中的元素


[TOC]

$符号的释放

e.g. var nj=jQuery.noConflict();
用nj代替$

$(function(){

})

nj(function(){

})

$(); jQ核心函数

1.接受函数 $(function(){});
作为入口函数

2.接受一个字符串代码片段 $(“.box1”);
返回一个jQ对象,对象中保存了找到的DOM元素

3.接受一个字符串代码 $(“

我是段落

“);
返回一个jQ对象,对象中保存了创建的DOM元素

4.接受DOM元素 $(“div”);
会被包装成一个jQuery对象返回

each方法 可以遍历伪数组

$.each(arr, function(index,value){
console.log(index,value);
})

注意:和Js的参数位置刚好相反 索引在前,值再后

map方法

$.map(obj, function(value,index){
conlose.log(index,value);
return value+index;
})

两者区别:

each静态方法默认的返回值:当前遍历的数组
map静态方法默认的返回值是一个空的数组
each数组不支持在回调函数中对遍历的数组进行处理
map可以在回调函数中通过对return对遍历的数组进行处理,输出我们想要的值,然后生成一个数组返回出来

trim方法

var res = $.trim(str); 可以删除字符串两端的空格
不会改变原字符串 需要接受新产生的字符串

isWindow方法

作用:判断传入的对象是否是window
参数:需要判断的对象
返回值:true/false

var res = $.isWindow();

isArray方法

作用:判断传入对象是否是真数组 伪数组是false
参数:需要判断的对象
返回值:true/false

var res = $.isArray();

isFunction

同上
!! var res = $.isFunction(jQuery); => true
所以jq框架本质是一个函数

holdReady

{所有dom元素执行完之后会执行ready函数}
$.holdReady(true);
作用:暂停ready事件(入口函数)执行

$.holdReady(false);
作用:恢复ready事件

内容选择器

:empty

var $div = $(“div:empty”);//找出没有子元素也没有文本内容的div;
console.log($div);

:parent

var $div = $(“div:parent);//找到有元素或者有子元素的div

:contains(text)

var $div = $(“div:contains(‘我是div’)”);
首先找到所有的div 查看哪个div中含有‘我是div’的文本。是包含不是等于噢!

:has(selector)

var $div = $(“div:has(‘span’)”);
首先找到所有的div 找到包含指定子元素的div。是包含不是等于噢!

操作属性节点

1.attr()
作用:获取或者设置属性节点的值,新增属性节点
传递一个参数:获取该指定节点的值 => !!注意,无论找到多少个元素,都只会返回第一个元素指定的属性节点的值
传递两个参数:设置该属性节点的值 => !!找到多少个元素就会设置多少个元素
$(“span”).attr(“class”,”box”)
//如果传入的属性节点不存在则自动创建新的属性节点并赋值

获取返回值:该属性节点的内容,没有内容是undefin ed

2.removeAttr();
作用:删除属性节点
找到多少个元素就会删除多少个元素的指定属性节点
删除多个属性节点:(用空格隔开即可)
$(“span”).removeAttr
(“class name abc”);
同时删除class、name、abc三个属性节点

操作属性

1.prop
$(“span”).eq(0).prop(“demo”,”666”)
给找到的第一个span添加上demo属性

获取的时候和attr()一样一个参数而且只能返回第一个
!! prop不仅能操作属性也能操作属性节点
$(“span”).prop(“class”) =>也可以实现效果

获取返回值:true/false

2.removeProp

Css操作

1.addClass()
$(“div”).addClass(“class1 class2”);//可以同时添加两个类名

2.removeClass()
$(“div”).removeClass(“class1 class2”)//同时删除多个类名

3.toggleClass()
切换类名 有就删除 没有就添加
$(“div”).toggleClass(“class1 class2”)
我感觉这个可以做点一下有点两下没有循环这样子的东西

HTML操作

1.html()
$(“div”).html(“

我是段落我是span

“);//可以创建html节点
设置html 和innerHTML作用一样

2.console.log($(“div”).html());
获取html的值

3.text()
$(“div”).text(“

我是段落我是span

“)//只传入文本无法设置HTML节点

4.console.log($(“div”).text());
获取HTML的值

5.val()
$(“input”).val(“请输入内容”)
设置输入框内的值

6.console.log($(“input”).val());
获取输入框里面的内容

操作样式

1.逐个设置
$(“div”).css(“width”,”100px”);
$(“div”).css(“height”,”100px”);
$(“div”).css(“background”,”red”);

2.链式设置(不建议太长 阅读性差
$(“div”).css(“width”,”100px”).css(“height”,”100px”).css(“background”,”red”);

3.批量设置
$(“div”).css({
width:”100px”;
height:”100px”;
});

获取CSS样式值

$(“div”).css(“width”);

尺寸操作

1.获取元素宽度
$(“.father”).width();

2.设置元素宽度
$(“.father”).width(“500px”);

3.innerheight和outerheight的区别
innerheight = height +padding;
outerheight = height +padding + border +一定情况下的margin;

位置操作

1.offset()
作用:获取和设置元素距离窗口的偏移位
$(“.son”).offset().left() =>获取
$(“.son”).offset(
{
left:10
}
) =>设置

2.position()
作用:获取元素距离定位元素的偏移位 !!不能设置噢~ 想设置距离的话直接在JQ里面写CSS就可以了
$(“.son”).position().left() =>获取

滚动条

1.scrollTop()
$(“.box”).scrollTop() =>获取元素滚动条现在滚动的距离
$(“html”).scrollTop()+$(“body”).sbodycrollTop() =>获取网页滚动的距离 实现浏览器之间的兼容
$(“.box”).scrollTop(300) =>设置滚动条滚动的距离
$(“html,body”).scrollTop(300) =>设置网页的滚动距离 实现浏览器之间的兼容

1
2
3
4
5
6
7
8
9
//监听滚动条

$(window).scroll(function(){
var offset = $("html,body").scrollTop();
//获取网页滚动的偏移位
if(offset >= 500){
//判断网页是否滚动到了相应的位置并执行对应的操作
}
})

事件绑定

  1. ```
    eventName(fn)
    $(“button”).click(function(){

    })

    1
    2
    3
    4
    5
    6
    7
    8

    !!部分JS事件不能实现(JQ有 JS没有的那种),可以绑定多个事件不会覆盖

    2. ```
    on(enentName,fn)
    $("button").on("click",function(){

    })

    !!所有JS事件(不管JQ里面有没有,只要JS里面有就可以)都可以实现,可以绑定多个事件不会覆盖

    one:执行一次

    事件解绑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$(function(){
function test1(){
alert('aaaa');
}
function test2(){
alert('bbbb');
}
$("button").on("click",test1);
$("button").on("click",test2);

$("button").off(); //不传参: 将button上面所有的事件全部解绑

$("button").off("click"); //传一个参数:把所有click事件移除

$("button").off("click",text1); //传两个参数: 把指定事件移除
})

事件冒泡和默认行为

解除事件冒泡和默认行为分别有两种方法

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
$(".son".click(function(){
slert('son');
return false; // 阻止事件冒泡
}))


$(".son".click(function(event){
slert('son');
event.stopPropagation(); // 阻止事件冒泡
}))

//记得都是写在儿子里面的

默认事件:比如点击提交数据就会提交到远程服务器,点击链接就会跳转

$("a".click(function(){
slert('不跳转');
return false; // 阻止默认行为
}))

$("a".click(function(event){
slert('不跳转');
event.preventDefault(); // 阻止默认行为
}))

事件自动触发

1
2
3
4
5
6
7
$(".son".trigger("click");	//	自动触发son上面的click事件,冒泡事件仍然存在,也会触发默认行为

$(".son".triggerHandler("click"); // 自动触发son上面的click事件,冒泡事件会被阻止,不会触发默认行为

$("input[type = 'submit']").click(function(){
alert("submit"); // 监听类型为submit的input的点击事件
})

jQuery自定义事件

1
2
3
4
5
6
7
8

$(".son").on("myClick",function(){
alert("son");
})
//自定义事件第一步:用on绑定事件,只能用on这个绑定的方法!

$(".son").trigger("myClick");
//自定义事件第二步:用trigger进行自动触发

jQuery事件命名空间

1
2
3
4
5
6
7
8
9
10
11
$(".son").on("click.zs",function(){
alert("click1");
});

$(".son").on("click.ls",function(){
alert("click2");
});

// .zs和.l就是命名空间,可以区别同个相同名字的事件

$(".son").trigger("click.zs");

事件委托

1
2
3
4
5
6
7
8
9
10
11
12
$("button").on("click",function(){
$("ul").append("<li>我是一个新增的li<li>")
});


$("ul").delegate("li","click",function(){
alert(this); // li
});

//将li的点击事件委托给ul,这样即使在新增li也有点击事件了,不委托的话,原来的绑定事件绑定不到新增的li噢

//找一些在入口函数之前就有的元素来监听我们动态增加的元素的事件

jQuery移入 移出

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
$(".father").mouseover(function(){
console.log("移入");
})

$(".father").mouseout(function(){
console.log("移出");
})

//注意:子元素的移入移出也会触发父元素的mouseover,mouseout事件

$(".father").mouseenter(function(){
console.log("移入");
})

$(".father").mouseleave(function(){
console.log("移出");
})

//使用mouseleave,mouseenter,子元素移入移出不会触发父元素的事件,推荐使用!

$(".father").hover(function(){
console.log("移入");
},function(){
console.log("移出");
})
//第一个参数:移入触发的事件,第二个参数:移除触发的事件,将mouseleave,mouseenter合成!

//如果只接受一个参数,则同时监听移入和移出事件

index, eq,siblings

$(this).index()获取到该元素在兄弟中的排位

$(".content").eq(0)获取第0个元素

$(this).siblings获取到除了当前元素(this)外的其他所有兄弟元素

显示和隐藏动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//显示动画
$("button").mouseover(function(){
$("div").show(1000,function(){
//第二个参数:动画执行完毕之后执行的函数
})
})

//隐藏动画
$("button").mouseleave(function(){
$("div").hide(1000,function(){
//第二个参数:动画执行完毕之后执行的函数
})
})

//切换动画 显示变隐藏 隐藏变显示
$("button").mouseleave(function(){
$("div").toggle(1000,function(){
//第二个参数:动画执行完毕之后执行的函数
});
})

展开和收起动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//展开动画(由上往下滑下来展开)
$("button").mouseover(function(){
$("div").slideDown(1000,function(){
//第二个参数:动画执行完毕之后执行的函数
})
})

//隐藏动画(由下往上滑下来隐藏)
$("button").mouseleave(function(){
$("div").slideUp(1000,function(){
//第二个参数:动画执行完毕之后执行的函数
})
})

//切换动画 显示变隐藏 隐藏变显示
$("button").mouseleave(function(){
$("div").slideToggle(1000,function(){
//第二个参数:动画执行完毕之后执行的函数
});
})

$(this).children(".sub");//找到this里面的sub子元素

CSS: text-indent:2em前面空出两个字的宽度

:nth-child(n)第n个数字

:nth-child(2n) 偶数标签

:nth-child(2n-1)奇数标签

:nth-child(-n+6) 负向范围,从第一个到第六个

:nth-child(n+6)正向选择,从第六个到结束

:nth-child(-n+9) :nth-child(n+6)第6到9个,取两者交集

$(this).children(".sub")找到this里面的sub子元素

淡入淡出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$("div").fadeIn(1000,function(){
//淡入完毕
})

$("div").fadeIn(1000,function(){
//淡出完毕
})

$("div").fadeToggle(1000,function(){
//淡入淡出切换
})

$("div").fadeTo(1000,0.5,function(){
//指定淡入程度 第二个参数:透明度
})

$(".ad").stop().slideDown(1000).fadeOut(1000).fadeIn(1000); // 前一个动画执行完才会执行下一个动画,比嵌套式写法可读性高很多
//加上stop()可以规避掉一些隐藏bug

自定义动画

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
$("button").click(function(){
$(".one").animate({
marginLeft:500
},1000"linear",function(){
//执行完毕
})
})

$("button").click(function(){
$(".two").animate({
marginLeft:500
},1000,function(){
//执行完毕
})
})

//第一个参数:想要修改的属性 可以同时修改多个属性,用逗号隔开即可 (多个属性的动画是同时进行的)
//第二个参数:时长
//第三个参数:指定动画节奏 linear swing(默认)缓冲运动(大多数参数都有这个属性 )
//第三or四个参数:动画结束之后执行的回调函数

//可以接受三个或者四个参数

//累加动画
$("button").click(function(){
$(".one").animate({
width:"+=100" //每次点击都会进行累加
},1000,function(){
//执行完毕
})
})

//关键字动画
$("button").click(function(){
$(".three").animate({
width:"hide" //会进行隐藏动画
height:"toggle" //切换
},1000,function(){
//执行完毕
})
})

stop() delay()

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
$("button").click(function(){
$(".one").animate({
width:500
},1000).delay(2000).animate({height:500
},1000)
//delay 隔某个时间段之后再进行下一个动画

$("button").eq(1).click(function(){
$("div").stop();
})
//立即停止div当前执行的动画,然后继续进行后续动画

$("button").eq(1).click(function(){
$("div").stop(true);
})
//立即停止div当前执行的动画和后续动画


$("button").eq(1).click(function(){
$("div").stop(false,true);
})
//立即完成当前动画继续后续动画

//第一个参数:是否继续后续所有动画(false或者没有则继续执行后续,true则停止执行后续动画)
//第二个参数:是否马上完成完现在的动画(没有参数或者false代表立即停止当前动画,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
$("button").click(function(){
//创建一个新的节点(直接写HTML代码即可)
var $li = $("<li>新增的li<li>");

//四个内部方法:

$("ul").append($li);
//将节点新增到原有节点的最后
$("ul").prepend($li);
//将节点新增到原有节点的最前
$li.appendTo("ul");
$li.prependTo("ul");
//与前两个方法一样

//外部方法:

$("ul").after($li);
//会把新增元素插入到ul的外部的后面,即紧跟ul后面
$("ul").before($li);
//会把新增元素插入到ul的外部的前面,即在ul前头
$li.insertAfter("ul");
$li.insertBefore("ul");
//与前两个方法一样
})

删除节点

1
2
3
4
$("button").click(function(){
$("div").remove();//删除指定元素
$("div").empty();//删除指定元素的内容和子元素,但是指定元素自身并不会被删除,就是剩下一堆空标签
})

替换节点

1
2
3
4
5
6
7
var $h6 = $("<h6>我是标题666</h6>")

$("h1").replaceWith($h6);
//将h1替换成h6

$h6.replaceAll("h1");
//将h1替换成h6

复制节点

1
2
3
4
5
var $li = $("li:first").clone(false);
//浅复制第一个li,仅复制元素,不会复制元素事件

var $li = $("li:first").clone(true);
//深复制第一个li,复制元素和复制元素事件

子节点

children找到第一代儿子

find可以找到所有后代孙子重孙都阔以

滚动条样式

使用插件mCustomScrollbar

注意点:先插入别人的CSS和JS文件,之后再 导入我们自身的文件,便于我们操作别人的文件

jquery原理

  1. 本质是一个闭包:立即执行的函数
  2. jQuery如何让外界访问内部定义的局部变量:window.num = num;将局部变量加到全局身上
  3. 为什么jQuery要给增加传一个window实参 :方便后期压缩代码,提高查找的效率

原型上的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$().jquery //获取jQ版本号
$().selector //实例默认的选择器取值,默认为空
$().length//默认长度,默认为0
$().push//给实例添加新数组
$().toArray() //把实例,伪数组转换为数组返回
$().get() //把实例,伪数组转换为数组返回

//get方法不传参数效果和toArray一样

arr.get(1);//正数第一个元素
arr.get(-1);//倒数第一个元素



//将伪数组转换成真数组
var arr = [].slice.call(obj);
//如果slice方法中什么参数都没有传递,则会将数组中的元素放到一个新的数组中原样返回

二维布局方式,可以同时控制行和列的分布和对齐方式。只需在外层容器添加display: grid;即可开启

grid-template-columns

固定宽度:grid-template-columns: 100px 100px 100px; 将容器分为3列,每一列的宽度为100px

浮动宽度:grid-template-columns: 1fr 1fr 1fr; 将容器分为3列,每一列的宽度为1/3

grid-template-columns: 1fr 2fr 1fr; 将容器分为3列,宽度为1/4,2/4,1/4

column-gap和row-gap

column-gap: 12px; 列间距为12px

row-gap: 24px; 行间距为24px

gap: 24px; 统一设置行间距和列间距都为24px

grid-template-areas

1
2
3
4
5
6
<div class="layout">
<header>头部</header>
<aside>侧边栏</aside>
<main>内容</main>
<footer>底部</footer>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.layout{
grid-template-areas:
"header header header"
"sidebar content content"
"footer footer footer";
}
header{
grid-area: header;
}
main{
grid-area: content;
}
aside{
grid-area:sidebar;
}
footer{
grid-area: footer;
}

对齐方式

行轴,块轴

垂直居中: align-items: center; //每一个单元格内容物的对齐方式

垂直靠下:align-items: end;

水平居中:justify-items: center;

水平靠右对齐:justify-items: end;

水平两端对齐:justify-items:space-between; //最左一列水平靠左对齐 最右一列水平靠右对齐 其余的水平居中对齐

如果行轨道和列轨道小于grid容器:

1
2
3
.grid{
align-content: center;
}

所有行垂直居中于grid容器(我觉得很像是把所有单元格合并看作是一块,相对于有grid属性的外容器使用margin来进行水平居中)

align-content: end; 靠下对齐

justify-contentjustifyy-items用法差不多,一个是用于所有单元格合并的内容,一个是用于单元格

[TOC]

声明变量

let

let所声明的变量只在let命令所在的代码块内有效,特别是在for循环内部,设置循环变量的那部分是一个父作用域,而循环内部是一个单独的子作用域。

1
2
3
4
for (let i = 0; i < 3 ; i++){
let i = 'abc';
console.log(i);
} //结果输出三次abc

不存在变量死区

var命令会发生变量提升,即变量可以在声明之前使用,值为undefinded。而let所声明的变量一定要在声明之后使用。

暂时性死区

如果在区块中let和const命令,则这个区块对这个命令声明的变量,从一开始就形成封闭作用域。只要在声明之前就使用这些变量,就会报错。在代码块,使用let命令声明变量之前,该变量都是不可用的。这在语法上称为“暂时性死区”。暂时性死区的本质就是,只要进入了当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

不允许重复声明

let不允许在相同作用域内声明同一个变量,因此,不能在函数内部重新声明参数。

块级作用域

内层作用域可以定义外层作用域的同名变量。外层作用域无法读取内层作用域的变量。

避免在块级作用域内声明函数,如果确实需要,可以写成函数表达式的形式 let f = function(){ return a; };而不是函数声明语句 function f(){ return a; };

do表达式

块级作用域不返回值,除非其中的变量是全局变量,为了使其可以返回一个值,可以将其变成do表达式,以下代码中变量x会得到整个块级作用域的返回值。

1
2
3
4
let x = do{
let t = f();
t*t+1;
}

const

const一旦声明常量,就必须立即初始化,不能留到以后赋值,同样也只在声明所在的块级作用域内有效。不会提升,不可重复声明。

const实际上保证的并不是变量的值不得改变,而是变量指向的那个内存地址不得改动,所以依然可以为其添加新属性。

如果真的想将对象冻结,应该使用Object.freeze方法,此时添加新属性时不起作用,严格模式时还会报错。

一个将对象彻底冻结的函数:

1
2
3
4
5
6
7
8
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach((key,i) => {
if(typeof obj[key] === 'object'){
constantize( obj[key] );
}
});
};

ES6声明变量的6种方法:var function let const import class

变量的解构赋值

ES6:允许按照一定模式从数组和对象中提取值,然后对变量进行赋值。

数组的解构赋值

let [a,b,c] = [1,2,3];

只要等号两边的模式相同,左边的变量就会被赋予对应的值。如果解构不成功,变量的值就等于undefined。

解构赋值允许指定默认值:

let[foo = true] = [];

foo // true

let [x,y = 'b'] = ['a',undefined]; // x='a',y='b';

注意:null不严格等于undefined,所以当一个数组成员是null时,默认值就不会生效

对象的解构赋值

数组的元素时按照次序排列的,变量的取值是由它的位置决定的;而对象的属性没有次序,变量必须与属性同名才能取到正确的值。

let { foo: baz} = { foo: "aaa", bar: "bbb" };

baz // "aaa"

foo // error: foo is not defined

foo是匹配的模式,baz才是变量,真正被赋值的是变量baz

对象的解构赋值的内部机制是先找到同名的属性,然后赋值给对应的变量。真正被赋值的是后者而不是前者

对象的解构也可以指定默认值,同样需要严格等于undefined

对数组进行对象属性的解构:

数组的本质是特殊的对象

1
2
3
4
let arr = [1,2,3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3

字符串的解构赋值

const [a,b,c,d,e] = 'hello';

a // "h"

b // "e"

c // "l"

d // "l"

e // "o"

类似数组的对象都有一个length的属性:

let {length: len} = 'hello';

len // 5

数值和布尔值的解构赋值

先将数值和布尔值转化为对象

let {toString: s} = 123;

s === Number.prototype.toString //true

解构赋值的规则是,如果等号右边不是对象或者数组,就先将其转化为对象。由于undefined和null无法转化为对象,所以对他们进行解构赋值的都会报错

函数参数的解构赋值

function add({x, y}){

return x+y;

}

传进去的参数是一个数组,但是无形之中已经被解构了!可以很方便的拿到数组中的某一个值。

函数参数的解构也可以使用默认值

圆括号

尽可能不要再模式中放置圆括号

不能使用圆括号的情况:

  1. 变量声明语句
  2. 函数参数(也属于变量声明)
  3. 赋值语句var {p:a} = {p:42};

只有在赋值而非声明语句中,可以用圆括号

字符串的扩展

codePoinAt()

codePointAt()方法是判断一个字符是由2个字节还是4个字节组成的最简单的方法

1
2
3
function is32Bit(c){
return c.codePointAt(0) > 0xFFFF;
}

4个字节则返回true

2个字节则返回false

String.fromCodePoint()

String.fromCodePonit(0x20BB7) //"𠮷"

就是和codePointAt()发过来的

for…of

1
2
3
4
5
6
for (let codePonit of 'foo'){
console.log(codePoint);
}
//"f"
//"o"
//"o"

for循环和for…of的区别:for不能识别大于0xFFFF的码点,for…of可以识别大于0xFFFF的码点

includes(), startsWith(), endsWith()

includes():返回布尔值,表示是否找到了参数字符串

startsWith():返回布尔值,表示参数字符串是否在源字符串的头部

endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部

以上三种方法都支持第二个参数,表示开始搜索的位置

使用第二个参数n的时候,includes()startsWith()针对从第n个位置到字符串结束位置之间的字符,而 endsWith()针对前n个字符

repeat()

repeat方法返回一个新的字符串,表示将原字符串重复n次

'x'.repeat(3) // "xxx"

参数如果是小数,则会被取整2.9=>2

如果参数是0到-1之间的小数,则等同于0,参数NAN等同于0.如果参数是字符串,则会先转化为数字

padStart(), padEnd()

padStart()用于头部补全

padEnd()用于尾部补全

如果源字符串的长度等于或大于指定的最大长度,则返回原字符串。如果用来补全的字符串与原字符串的长度之和超过了指定的最小长度,则会截去超出位数的补全字符串。

'abc'.padStart(10,'0123456789); //'0123456abc'

如果省略第二个参数,则会用空格补全

作用:

  1. 为数值补全指定位数
  2. 提示字符串格式

正则的扩展

RegExp构造函数

如果 RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,值使用新指定的修饰符。

字符串对象共有4个方法可以使用正则表达式:

match() replace() search() split()

U修饰符

用来处理大于\uFFFF的UniCode字符

i修饰符

识别非规范的字符

y修饰符

y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就行,而y修饰符会确保匹配必须从剩余的第一个位置开始,所谓“粘连”

1
2
3
4
5
6
7
8
9
var s = "aaa_aa_a";
var r1 = /a+/g;
var r2 = /a+/y;

r1.exec(s) // ["aaa"];
r2.exec(s) // ["aaa"];

r1.exec(s) // ["aa"];
r2.exec(s) // null;

lastIndex属性指定每一次搜索开始的位置,g修饰符从这个位置开始向后搜索,直到发现匹配为止。y修饰符同样可以使用这个属性,但是要求必须在lastIndex指定的位置发现匹配。

sticky属性

(/hello\d/y).sticky // true

表示是否设置了y修饰符

flags属性

返回正则表达式的修饰符

/abc/ig.flags // 'gi'

s修饰符:dotAll模式

正则表达式中,点(.)是一个特殊的字符,代表任意的单个字符,但是行终止符除外。

行终止符:

  • U+000A 换行符(\n)
  • U+000D 回车符(\r)
  • U+2028 行分隔符
  • U+2029 段分隔符

引入/s修饰符,使得.可以匹配任意单个字符,这称为dotAll模式。

1
2
3
4
5
6
const re = /foo.bar/s;
//另一种写法
//const re = new RegExp('foo.bar','s');

//dotAll属性,返回一个布尔值,判断该正则表达式是否处在dotAll模式下
re.dotAll // true

具名组匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//数字方式
const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
const a = RE_DATE.exce('1999-12-31');
const year = a[1]; // 1999
const month = a[2]; // 12
const day = a[3]; // 31

//具名组匹配:在模式的头部添加“问号 + 尖括号 + 组名”(?<year>),然后就可以在exce方法返回结果的groups属性上引用该组名。

const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const a = RE_DATE.exce('1999-12-31');
const year = a.groups.year; // 1999
const month = a.groups.month; // 12
const day = a.groups.day; // 31

字符串替换时,使用$<组名>引用具名组:

1
2
3
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;

'2015-01-02'.replace(re, '$<day>/$<month>/$<year>'); // '02/01/2015'

引用:

如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>,数字引用依然有效

1
2
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>!\1$/;
RE_TWICE.test('abc!abc!abc'); // true

数值的扩展

Number()

前缀 0b(0B)表示二进制,0o(0O)表示八进制

Number()方法可以将二进制和八进制的数转化为十进制数

Number('0b111'); // 7

Number.isFinite()

检查一个数值是否为有限的

只对数值有效,对于非数值一律返回false

1
2
3
4
5
Number.isFinite(0.8);	//	true
Number.isFinite(NAN); // false
Number.isFinite('foo'); // false
Number.isFinite('15'); // false
Number.isFinite(true); // false

Number.isNAN()

检查一个数值是否为NAN

只有对NAN才返回true

1
2
3
4
5
Number.isNAN('15');		//	false
Number.isNAN(true); // false
Number.isNAN(9/NAN) // true
Number.isNAN('true'/0); // true
Number.isNAN('true'/'true'); // true

Number.parseInt(), Number.parseFloat()

将原本parseInt(), parseFloat()方法移植到了Number对象上面。

1
2
3
4
5
//ES5
parseInt('12.34');

//ES6
Number.parseInt('12.34');

Number.isInteger()

判断一个数是否为整数

3和3.0被视为同一个数

1
2
Number.isInteger("15");	//	false
Number.isInteger(true); // false

Number.EPSILON

ES6在Number对象上面新增一个极小的常量——Number.EPSILON

引入一个这么小的量,目的在于为浮点数设置一个误差范围,如果浮点数计算能够小于Number.EPSILON,则我们认为得到了正确的结果。

Number.isSafeInteger()

JS能够精确表示的整数范围在 -Math.pow(2,53)Math.pow(2,53)之间,超过这个范围就无法精确表示

Math.pow(2,53) === Math.pow(2,53) + 1

ES6引入Number.MAX_SAFT_INTEGER和Number.MIN_SAFT_INTEGER两个常量用来表示这个范围的上下限。

Math.trunc()

用于去除一个数的小数部分,返回整数部分

对于非数值,内部使用Number方法将其转化为数值

对于空值和和无法截取整数的值,返回NAN

x < 0 ? Math.ceil(x) : Math.floor(x);

Math.sign()

用来判断一个数到底是正数,负数还是零。对于非数值,会先将其转化为数值。

  • 参数为正数,返回+1
  • 参数为负数,返回-1
  • 参数为0,返回0
  • 参数为-0,返回-0
  • 其他值,返回NAN

Math.cbrt()

用于计算一个数的立方根,对于非数值,内部先转化为数值

Math.clz32()

JS的整数使用32位二进制形式表示,该方法返回一个数的32位无符号整数形式有多少个前导0

0的二进制形式全为0,所以有32个前导0

1的二进制形式是0b1,只有1位,所以32位之中有31个前导0

左移运算符(<<)

1
2
3
Math.clz32(1);		//31
Math.clz32(1 << 1); //30
Math.clz32(1 << 2); //29

对于小数,该方法只考虑整数部分

对于空值和其他类型的值,该方法会在内部先将它们转化为数值

1
2
3
4
5
6
7
8
Math.clz32();	// 32
Math.clz32(NAN); // 32
Math.clz32(Infinity); // 32
Math.clz32(null); // 32
Math.clz32('foo'); // 32
Math.clz32([]); // 32
Math.clz32({}); // 32
Math.clz32(true); // 31

Math.imul()

该方法返回两个数以32位带符号整数形式相乘的结果,返回的也是一个32位的带符号整数。

Math.imul(2,4); // 8

Math.fround

该方法返回一个数的单精度浮点数的形式

Math.hypot()

返回所有参数的平方和的平方根

Math.hypot(3,4); //5

如果参数不是数值,Math.hypot方法将会转化为数值,只要有一个参数无法转化为数值,就会返回NAN

对数方法

  1. Math.expm1(x)返回e的x次方再减1,即Math.exp(x)-1

  2. Math.log1p(x)返回ln(1+x),即 Math.log(1+x),如果x小于-1,则返回NAN

    Math.log1p(-1); // -Infinity

  3. Math.log10(),返回以10为底的x的对数,如果x小于0,则返回NAN

  4. Math.log2(),返回以2为底的对数,如果x小于0,则返回NAN

双曲函数方法

1
2
3
4
5
6
Math.sinh(x);	//双曲正弦
Math.cosh(x); //双曲余弦
Math.tanh(x); //双曲正切
Math.asinh(x); //反双曲正弦
Math.acosh(x); //反双曲余弦
Math.atanh(x); //反双曲正切

Math.signbit()

1
2
3
4
5
Math.signbit(NAN);	//	false
Math.signbit(-0); // true
Math.signbit(负值); // true

//其他情况返回false

指数运算符(**)

1
2
3
4
2 ** 2;	//	4

a **= 2;
//等同于 a = a * a;

函数的扩展

函数参数的默认值

1
2
3
4
5
6
7
//ES6允许为函数的参数设置默认值,即直接写在参数定义的后面
function log(x, y = 'world'){
console.log(x,y);
}

log('Hello'); // Hello World
log('Hello', 'China'); // Hello China

注意:

  • 函数参数是默认声明的,所以不能用let或者const在函数内部再次声明
  • 使用参数默认值时,函数不能有同名参数。(即使不是有默认值的那个参数,其他的参数也是不能出现两次的
  • 参数默认值不是传值,而是每次都重新计算默认值表达式的值,即参数默认值是惰性求值的

参数默认值可以与解构赋值的默认值结合起来使用

1
2
3
4
5
6
7
function fetch(url, { method = 'GET'} = {}){
console.log(method);
}

fetch('httP://example.com'); // "GET"

//没有传入第二个参数则默认传入一个空对象,空对象没有method属性于是使用默认值。

通常情况下,默认值的参数应该是函数的尾参数,如果非尾部的参数设置默认值,实际上这个参数是无法省略的。

1
2
3
4
5
6
7
8
function f(x=1, y){
return [x,y];
}

f(,1); //报错
f(undefined,1); //[1,1]

//可以看出,当有默认值的参数不是尾参数,这时无法省略该参数而不省略其后的参数,除非显式输入unfined。如果传入undefined,将触发该参数等于默认值,null则没有该效果

应用:

  • 利用函数默认值可以指定某一个参数不得省略,如果省略就抛出一个错误
  • 可以将参数默认值设为undefined,表示这个参数是可以被省略的

函数的length属性

length属性 = 参数个数 - 指定默认值参数的个数 - rest参数

注意:如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数

函数作用域

1
2
3
4
5
6
7
8
9
var x = 1;

function foo(x = x){
//....
}

foo(); // 报错

//参数x = x形成一个单独的作用域,实际执行的是let x = x.由于暂时性死区,执行上述代码会产生“定义”错误

rest参数

1
2
3
4
5
6
7
8
9
10
11
function add(...values){
let sum = 0;

for (var val of values){
sum += val;
}

return sum;
}

//rest参数(形式为"...变量名"),用于获取函数的多余参数,rest参数搭配的变量是一个数组,该变量将多余的参数放入其中。

注意:rest参数之后不能再有其他参数,否则会报错

name属性

函数的name参数返回该函数的函数名

const bar = function baz(){};

bar.name; //baz

bind返回的函数,name属性值会加上bound前缀

foo.bind({}).name; // "bound foo"

箭头函数

1
2
3
var f = () => 5;
//等同于
var f = function(){return 5};

注意:

函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象

不可以当作构造函数,即不可以使用new命令

不可以使用arguments对象,可以用rest进行代替

不可以使用yield命令,因此箭头函数不能用作Generator函数

数组的扩展

扩展运算符

扩展运算符(…),rest参数的逆运算。可以将一个数组变成参数序列

如果将扩展运算符用于数组赋值,则只能将其放在参数的最后一位

应用:

数组的合并: [...arr1, ...arr2, ...arr3];

将字符串转为真正的数组:[...'hello'] // ["h", "e", "l", "l", "o"](可以用于返回正确字符串长度)

Array.from()

将类似数组的对象,即有length属性的对象,都可以通过这个方法转化为数组。

1
2
3
Array.from([1,2,3],(x) => x * x)	//	[1,4,9]
//还可以接受第二个参数,对每个元素进行处理,将处理之后的值放入返回数组
//也可以传入第三个参数,用来绑定this

Array.of()

将一组值转化为数组

Array.of(3,11,8) // [3,11,8]

copyWithin()

1
2
3
4
5
6
7
8
Array.prototype.copyWithin(target, start, end);
//三个参数
//target:开始替换的位置
//start:从该位置开始读取数据
//end:停止读取数据的位置

[1,2,3,4,5].copyWithin(0,3,4) //[4,2,3,4,5]

find() findIndex()

找出第一符合条件的数组成员

[1,4,-5,10].find((n) => n < 0) // -5

find的回调函数可以接受三个参数,依次是当前的值,当前的位置和原数组

fill()

使用定值填充一个数组

['a','b','c'].fill(7,1,2) // ['a', 7, 'c']

参数: 填充的定值,起始位置,结束位置

entries() keys() values()

用于遍历数组

keys()键名

values()键名

entries()键值对

对象的扩展

ES6中允许在对象中只写属性名,不写属性值。这时,属性值等于属性名所代表的变量

属性名表达式与简洁表示法不能同时使用,否则会报错

属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object]

方法也有name属性,返回方法名

Object.is()

判断值是否相等

Object.is(NAN,NAN) // true

Object.assign()

1
2
3
4
5
6
7
8
9
var target = { a : 1 };
var source1 = { b : 2 };
var aource2 = { c : 3 };

Object.assign(target,source1,source2);
target // {a:1, b:2, c:3}

//如果目标对象和源对象有同名属性,则后面的会覆盖前面
//浅复制,得到的是这个对象的引用

属性的可枚举性

Object.getOwnPropertyDescriptor(对象,属性)获取这个属性的可描述对象

属性的遍历

  • for...in
  • Object.keys(obj)
  • Object.getOwnPropertyDescriptor(obj)
  • Object.getOwnPropertySymbols(obj)
  • Reflect.ownKeys(obj)

原型

Object.getPrototype读取一个对象的prototype

Object.setPrototype设置一个对象的prototype

对象的遍历

Object.keys()

Object.values()

Object.entries()

Iterator

原生具备Iterator接口的数据解构:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的arguments对象
  • NodeList对象

而没有Iterator接口的数据结构,比如对象,需要我们在Symbol.iterator属性上部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class RangeIterator{
constructor(start,stop){
this.value = start;
this.stop = stop;
}

[Symbol.iterator]() { return this;}
next(){
var value = this.value;
if(value < this.stop){
this.value++;
return{done: false, value: value};
}
return {done: true, value: undefined};
}
}

function range(start,stop){
return new RangeIterator(atart,stop);
}

for(var value of range (0,3)){
console.log(value); //0,1,2
}

Generator函数

像一个状态机,封装了多个函数,,使用yield语句定义不同的内部状态。一直执行同步代码,直到遇到yield语句为止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function* helloWorld(){
yield 'hello';
yield 'world';
return 'ending'; // generator函数的return语句必须返回一个对象
}

var hw = helloWorld();

//调用next()才会开始执行

hw.next(); // {value: 'hello', done: false}
hw.next(); // {value: 'world', done: false}
hw.next(); // {value: 'ending', done: true}
hw.next(); // {value: undefined, done: true}

yield语句本身没有返回值,而next方法可以带有一个参数,该参数会被当成上一天 yield语句的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function* f(){
for(var i = 0; true; i++){ //第二个参数是true可以无限运行噢
var reset = yield i;
if(reset){
i=-1;
}
}
}

var g = f();

g.next() // {value: 0, done: false}
g.next() // {value: 1, done: false}
g.next(true) // {value: 0, done: false} // reset等于true然后i就从-1开始递增啦~

async函数

  • 与generator函数相比,async内置执行器
  • async函数的返回值是Promise对象,可以使用then方法指定下一步操作

正常情况下,await命令后面是一个Promise对象,如果不是,会被转成一个立即resolve的Promise对象,如果async函数内部抛出错误会导致返回的Promise对象变成reject对象。如果有一个await语句后面的Promise变成reject,那么整个async函数都会中断执行,把await放进try…catch结构里面,可以避免这种情况,即使try..catch;里面的await失败,外面的await依旧会执行。

1
2
3
4
5
6
7
8
9
async function f(){
try {
await Promise.reject('出错了');
}catch(e){
}
return await Promise.resolve('hello,world');
}

f().then(v => console.log(v)) // hello world

Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let methodName = 'getArea';

class Point{//方法之间不需要逗号分隔
constructor{
//...
}

toString{
//...
}

toValue{
//...
}

[methodName](){//类属性名可以采用表达式,通过表达式获得方法名getArea
//...
}
}

类不存在变量提升

私有属性,私有方法:在属性名之前,使用#来表示

类的方法中如果含有this,它将默认指向类的实例

类相当于实例的原型,所有在类中定义的方法都会被实例继承。如果在一个方法前加上static关键字,就表示该方法不会被实例继承,而是直接通过调用,称为“静态方法”。

[TOC]

继承发展史

原型链:

原型:是function对象的一个属性,定义了构造函数制造出的对象的公共祖先,通过该构造函数产生的对象,可以继承该原型的属性和方法,原型也是对象。
构造函数和普通函数的区别
调用区别:
普通函数的调用方式:直接调用

person();

构造函数的调用方式:需要使用new关键字来调用 new

Person();

构造函数的函数名与类名相同:

Person( );

这个构造函数,Person 既是函数名,也是这个对象的类名
定义:

Person.prototype = {
height: 10000;
width: 1000;
carname: "nn";
}

注意:改了整个空间

Person.prototype.height = 10000;

注意:只改了个值

Person.prototype.name = "";

则所有new出来的Person的后代全部先天带有name属性或者方法,但是可以后天进行改变。

删除属性:

delete person.name; =>true

注意:删除不存在的属性结果也是true

constructor方法:

作用:后代可以通过这个属性来找到他的祖先,通过constructor虽然是系统隐性自带属性但是可以手动更改

prototype通过

__proto__

连接后代和祖先,所以后代所带的祖先原型可以被手动更改,可以换祖先

后代. __proto__=祖先.prototype

原型链的连接点:prototype
弊端:继承太多不想要的东西,没办法进行选择
所有最开始的祖先都有一个自带的

__proto__

原型链里面有很多东西比如toString;
注意:子代可以给祖先加属性,加在后代没有的属性上面,所以会直接给到祖先
son.fortune.name son没有fortune属性,继承自他的祖先,这样写可以添加祖先的fortune属性的属性
son.fortune =》相当于son.fortune = son.fortune +1; 给自己加了一个属性并加1

创建对象方法:
var obj1 = Object.creat(原型)
var obj1 = Objecct.creat(obj);
var person = Object.creat(Person.prototype)
Object.creat(null) =>现在构造出的对象没有原型,

__proto__也没有,再人为去加__proto__,属性后代也是不会继承的

借用构造函数:

弊端:不能继承借用构造函数的原型
每次构造函数都要多走一个函数
call
所有的方法都可以使用call

作用:将这个方法里面所有的this替换成括号内内容

function Person(name,age){}
Person.call(obj,'Wu',300);

在这个Person里面先执行

this==obj

然后Wu和300作为Person的两个参数被传进去
应用:在某一个函数里面用别的函数的东西,很方便:D
apply:
apply(this,实参)
唯一不同:只能传一个实参,所以有多个实参的话要用数组传进去哦,比如[num1,num2]

公有原型:

让两个函数的原型指向同一个原型
弊端:当往一个函数的原型添加属性的时候,另外一个函数也会增加这个属性,因为两个函数的原型现在指向了同一个空间,简单来说现在原型只有一个。

圣杯模型:

公有原型+原型链(一个以公有原型为原型的新构造函数,并使得我们的目标函数成为一个被这个新构造函数new出来的对象)

var inherit = (function(){

var F = function(){};

return function(Target, Origin){

F.prototype = Origin.prototype;

Targrt.prototype = new F();

Target.prototype.constuctor = Target;构造函数

Target.prototype.uber = Origin.prototype;超级父级

}

}());

应用到了闭包,举例:

一个在构造函数里面定义的变量,当该构造函数中有一个方法{将该变量赋给这个构造函数的一个属性};此时调用该构造函数的这个方法可以将定义在函数内部的值通过属性传了出来 =》私有化变量,这个变量是他自己的,除非设置了自己的方法,否则别人根本找不到这个变量。圣杯模型中的构造函数F()就成为了一个私有化变量。

others

  1. 可正常计算的范围小数点前后16位

  2. str.charCodeAt(i) => 求出str的第i位的字符编码

    如果字符编码小于等于255则该位字节长度为1,若大于255则该位字节长度为2

    注意:中文的逗号的占两位噢~英文的逗号占一位

  3. 实现一个构造函数的方法连续调用,只要在每一个方法最后return this就可以实现链式调用模式。

    Person.sex.name.age

    ————7.14

命名空间

闭包

变量私有化,不会污染到全局变量

var init = (function(){

var name = 'abc';

function callName(){

console.log(name);

}

return function(){

callName(); 留下一个以后可以调用的接口

}

}())

init() => abc

对象枚举

属性的表示方法:

(1)Person.name

(2)Person[‘name’] =>用法更灵活,字符串进行拼接作为属性名

for in循环

for(var prop in obj){ prop也可以用其他单词代替,在for in循环中每一圈prop代表一个属性名,用其他单词也是同理;

console.log(prop);

console.log obj[prop]; 注意:prop作为一个变量,不可以使用obj.prop访问

}

hasOwnProperty过滤方法

obj.hasOwnProperty(prop)

作用:辨别该方法是不是这个对象的方法,可以排除方法的方法(也就是一级)返回true,false`

in

判断这个对象上面是否有该属性,一级二级多级的都算

'name' in obj 属性是字符串,要加单引号

instanceof

A instanceof B 判断A对象是不是构造函数B构造出来的(即等同于判断A的原型链上是否有B的原型)

区别 []数组 和 {}对象 的三种方法:

var obj={};

  1. constructor

    [].constructor =>Array

    obj.constructor =>Object

  2. instanceof

    [] instanceof Array =>true

    obj instanceof Array =>false

  3. toString

    Object.prototype.toString.call([]) => object Array

    Object.prototype.toString.call(obj) => object Number

    Object.prototype.toString.call(123) => object Object

Deep Clone

  1. 遍历对象 for(var prop in obj)【一般和hasOwnpropety绑定使用】

  2. 判断是不是原始值 typeof()

  3. 判断是数组还是对象 (三种方法,一般采用toString)

  4. 建立相应的数组或对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function deepClone(origin, target){
var target = target || {},
toStr = Object.prototype.toString, //简短代替
arrStr = "[object Array]"; //为了进行后期比对
for(var prop in origin){
if(origin,hasOwnproerty(prop)){
if(prigin[prop] !== "null" && typeof(origin[prop]) == 'object'){ //判断不为空的引用值
if(toStr.call(origin[prop]) == arrStr){ //判断是否为数组
target[prop] = [];
}
else{
target[prop] = {};
}

deepClone(origin[prop],target[prop]); //递归,进行数组或对象内的克隆

}
else{ //原始值
target[prop] = origin[prop]
}
}
}
return target;
}

三目运算符

条件判断?是:否

JSON

一种用于传输数据的格式

JSON.parse(); string –> json

JSON.stringify(); json –>string

JS异步加载

  1. defer异步加载

    <script type="text/javascript" src="tools.js" defer="defer"></script>

    要等到dom文档全部解析完才会被执行,只有IE能用,也可以将代码写到内部

  2. aysnc异步加载

    <script type="text/javascript" src="tools.js" aysnc="aysnc"></script>

    加载完就执行,aysnc只能加载外部脚本,不能把js写在script标签里面,IE9及以上

  3. 创建script,插入到DOM中,加载完毕之后callBack

    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
    function loadScript(url,callback){		//按需加载

    var script = document.creatElement('script');
    script.type = "text/javascript"; //下载需要时间,并不能马上用引入的JS中的方法,还没下载完呢

    if(script.readyState){ //存在状态码的时候
    script.onreadystatechange = function(){ //IE 监听状态码是否改变
    if(script.readyState == "complete" || script.readyState == "loaded"){
    callback(); //当状态码发生改变,从loading变成complete或loaded时,script下载完成,可以执行别的内容了
    }
    }

    }

    else{
    script.onload = function(){ //Safari chrome firefox opera
    callback(); //当script 确保下载完再进行执行
    }

    }
    script.src = url; //传进来的url带双引号,放在尾部可以防止状态码改变事件不被触发
    document.head.appendChild(script);
    }


    //引用的话需要用匿名函数
    loadScript('demo.js',text());//报错,因为未解析JS文件呢,哪能识别到JS里面的text是啥
    loadScript('demo.js',function(){//使用匿名函数,函数体内部只有当被执行的时候才会被读取,就是要用到他的时候才去识别他
    text();
    })

JS加载时间线

​ 1.创建Document对象,开始解析web页面,解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中。这个阶段Documen.readyState = "loading"。

​ 2.遇到link外部css,创建线程加载,并继续解析文档。

​ 3.遇到script外部js,并且没有设置asyncdefer ,浏览器加载,并阻塞,等待js加载完成并执行该脚本,然后继续解析文档

​ 4.遇到script外部js,并且设置有asyncdefer 浏览器创建线程加载,并继续解析文档,对于async属性的脚本,脚本加载完成后立即执行(异步禁止使用docuemnt.write())。

​ 5.遇到img标签等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档

​ 6.当文档解析完成,document.readyState = "interactive";

​ 7.文档解析完成后,所有设置有defer的脚本会按照顺序执行。注意和async的不同,但同样禁止document.write()

​ 8..当文档解析完成之后,document对象触发DOMContentLoaded事件,这也标志着程序执行从同步脚本执行阶段,转化为事件驱动阶段

​ 9.当所有saync的脚本加载完成并执行后,img等加载完成后,document.readyState = "complete" window对象触发load事件

​ 10.从此,页面以异步响应方式处理用户输入,网络事件等。

window.onload //当页面全部加载完才执行

document.addEventListener('DOMContent',function(){

},false) //dom解析完就执行的部分

垃圾加载回收机制和内存泄漏

垃圾加载回收机制Garbage Collection:执行环境负责管理代码执行过程中使用的内粗,垃圾收集器会定期找出那些不再继续使用的变量,然后释放内存。

内存泄漏:不再用到的内存,没有及时释放

垃圾回收策略

标记清除:对脱离作用域的变量进行回收,当进入作用域时,进行标记,离开作用域的时候,标记并回收这些变量。各大浏览器实现的都是标记清除的垃圾回收策略或者类似的策略,但是垃圾收集的时间间隔互不相同。

引用计数:跟踪记录每个值被引用的次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收的对象,每过一段时间开始垃圾回收的时候,就把被引用数为0的变量回收。这种方法很容易引起内存泄漏,如即使两个对象都离开了作用域,但是只要它们的引用次数不是0,它们还会继续存在。

常见内存泄漏的原因

  1. 全局变量引起的内存泄漏
  2. 闭包引起的内存泄漏
  3. dom清空或者删除时,时间未清除导致的内存泄漏
  4. 循环引起的内存泄漏

创建版本库

版本库就是我们所说的代码仓库(repository),在这个仓库里的文件,无论是删除还是修改,git都能追踪记录

git的使用命令


​ cd

在某个文件中使用git bash,与点击该文件右键打开git bash效果相同


​ git add; git commit -m”备注”

add表示要添加的文件,commit是添加进去的动作,所以可以通过多次的add和最后一次的commit达到一次添加多个文件的效果

git statues

查看文件当前状态

git diff

查看文件的变化,是否有修改,修改了什么都一目了然

 git branch

创建一个新的分支,但是这个时候head仍然在master上面而不是在新创建的分支上面

git checkout

可以切换到目的分支并对其进行操作,此时head也发生了移动,移动到了目的分支上面

git checkout -b

合并以上两步操作,即创建了一个新的分支,又切换到了新的分支可以直接对新的分支进行操作

git merge

将现在所在的分支与目的分支进行合并,但是仍然会有两个节点在同一个地方

git checkout; git merge master;

先切换到另一分支上,在对其进行合并操作,这样最终合并之后只有一个节点master

git rebase;

将现在所处的这个节点线性移动到目的节点的下面进行合并

git checkout master^

将head移动到master的上一个父节点

git HEAD^

多次使用可以将head所指的位置一直往上移动

GitHub托管

  1. 在GitHub上新建一个仓库
  2. 复制仓库克隆地址
  3. 进入到项目终端 git remote add origin https://……
  4. git push -u origin master
  5. 之后修改代码再次提交
  6. git add
  7. git commit -m ‘注释’
  8. git push

Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

0%