js是单线程非阻塞的语言,只有一个调用栈。js的任务有同步任务和异步任务,异步任务需要耗费时间没有合适的机制会引起阻塞,所以优先执行同步任务,同步任务直接放入调用栈中开始执行。
遇到同步代码就放到调用栈中,遇到异步代码先交由webApi,等合适时机放到任务队列中,等调用栈中同步代码执行完毕被清空,开始事件循环机制执行异步代码,每次调用栈被清空时,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环EventLoop。
异步代码执行时优先执行微任务,后执行宏任务,微任务有setTimeout、ajax,宏任务有promise\async+await
事件循环开始时间:调用栈中空了之后开启事件循环机制来管理任务队列中异步代码的执行。
事件循环:先清空调用栈中的同步代码,执行微任务队列中的微任务,尝试DOM渲染,触发事件循环反复询问回调队列中是否还有要执行的语句(轮询),有就push到调用栈执行。
定时器(setTomeout)是设置的那个毫秒数到了,才开始放入任务对列,而不是毫秒数到了就会执行。
原型和原型链的概念是模拟传统面向对象语言中的类的概念,挂载在原型属性上的方法可以被所有实例化对象所共享,这样可以节省空间。原型链可以实现对象的继承。
原型:每一个构造函数也就是类,都有prototype属性,这个属性指向它的原型对象。
而每个实例化对象都有一个__proto__隐式原型属性,这个属性也指向它的构造函数的原型对象。Person.prototype = zs.__prpto__
原型链:原型链是通过__proto__连接起来的链条,每个对象都有自己的原型Object.prototype = null,最顶层的原型对象是null。当我们调用一个属性或方法时,会先在自身找,如果自身没有会去原型对象身上找,如果找不到会一直向上追溯到顶层,如果还找不到,则返回undefined。
hasOwnProperty():可以用来检查对象自身是否含有某个属性,返回值是布尔值,当属性不存在时不会向上查找对象原型链,hasOwnProperty是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。
浅拷贝和深拷贝都是针对引用数据类型的,基本数据类型在内存中直接存在栈中,引用数据类型在内存中变量名和指针存在栈中,其他主体内容存在栈的指针指向的堆内存中。
浅拷贝:浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
深拷贝:深拷贝会另创造一个一模一样的对象,新对象会有新的内存空间,跟原对象不共享内存,修改新对象不会改到原对象。
深拷贝要通过递归来实现,如果没有引用数据类型(如数组和Object)、undefined的情况下,还可以通过 JSON转换、Object.assign()、扩展运算符这些方法来实现深拷贝,这些共同的缺点是只能拷贝基本数据类型,不能拷贝引用数据类型。
// 浅拷贝出现的前提是:引用类型的数据
// 浅拷贝:就是与原对象共用一套内存空间 --- 就是obj2指向与obj1一个内存空间
const obj1 = {
a:1
}
const obj2 = obj1;
obj2.a = 2;
console.log(obj1,obj2);
// 深拷贝:再开辟出一块内存空间来
// 1.JSON转换方法----缺点:数据类型为function或数值为undefined情况下无法复制
const obj1 = {
a: 1,
b: undefined,
arr: [1,2,4],
fun: () => {}
}
// 将引用类型,转为基本类型
const obj2 = JSON.parse(JSON.stringify(obj1))
obj2.a = 2;
obj2.arr[0] = 111;
console.log(obj1,obj2);
// 2.Object.assign()----缺点:只能深拷贝一级属性,二级以上属性(即数组中包含的元素)就是浅拷贝
const obj1 = {
a: 1,
b: undefined,
arr: [1,2,4],
fun: () => {}
}
const obj2 = Object.assign({},obj1) // 将引用类型,转为基本类型
obj2.a = 2;
obj2.arr[0] = 111;
console.log(obj1,obj2);
// 3.扩展运算符---缺点:只能深拷贝一级属性,二级以上属性(即数组中包含的元素)就是浅拷贝
const obj1 = {
a: 1,
b: undefined,
arr: [1,2,4],
fun: () => {}
}
const obj2 = {...obj1}
obj2.a = 2;
obj2.arr[0] = 111;
console.log(obj1,obj2);
// 4.递归 --- 可以完美解决深拷贝问题
function CloneDeep(data){
const newData = Array.isArray(data) ? [] : {}; // 初始化
for(let key in data){
if(data[key] && typeof data[key] === "object"){
// 如果是数组和对象类型
newData[key] = CloneDeep(data[key])
}else{ // 如果是基本数据类型
newData[key] = data[key]
}
}
return newData
}
const obj1 = {
a: 1,
b: undefined,
arr: [1,2,4],
fun: () => {}
}
const obj2 = CloneDeep(obj1)
obj2.a = 2;
obj2.arr[0] = 111;
console.log(obj1,obj2);
闭包是函数内部的函数,是为了在外部读取外部函数内部的函数级作用域中的变量而存在的,闭包因为存在函数嵌套函数的关系,所以可以帮助保存for循环中的变量。但滥用闭包容易造成内存泄漏。因为js的垃圾回收机制检测不到,所以造成了内存泄漏。解决办法是给变量赋值为null。
垃圾是没有被引用的对象或变量等,因为它占用内存空间,如果没有垃圾回收的话,会造成内存消耗,影响代码运行性能。
js的垃圾回收方式:标记清除 和 引用计数
标记清除:当变量进入执行环境时(函数中声明变量),就标记这个变量“进入环境” ,当变量离开环境时(函数执行结束),则删除"进入环境"标记,并将其标记为“离开环境”。当js检测到一个变量被标记取消标记又重新标记之后,就会收集起来,等待垃圾收集器处理后,这个变量将不再占有内存空间,使内存空间得到释放。
引用计数:当一个变量被赋值为引用类型时,就会使计数+1,当变量又取了其他数据时,计数-1。计数为0的就是需要被回收的垃圾。引用计数方法可能会导致循环引用,这时该机制检测到的计数一直是2,从而导致这个内存永远不会被垃圾回收检测到,从而造成了内存泄漏。
内存泄漏就是变量已使用,但未释放内存空间,一直占用着内存的情况。严重的话,无用的内存持续增加,会导致系统卡顿,甚至崩溃。
防止泄漏:不滥用闭包,定时器及时清除,减少全局变量等
通过数据劫持
Object.defineProperty(对象,属性,{
set: function() { } // 设置属性
get: function() { } // 读取属性
})
// 原型链继承优化【es5】--- 寄生组合继承
// 【Person类】----------------------------
function Person(){
// 构造函数的属性和方法
this.name = "people",
this.say = function(){
console.log("say");
}
}
// 实例方法,也就是原型链上的
Person.prototype.hello = function(){
console.log("hello!!!");
}
// 【Student类】------------------------------
function Student(){
// 【 1. 继承实例属性和方法】****
Person.call(this)// 拿到父类的属性和方法
}
// 【 2. 继承原型上的属性和方法】*****
Student.prototype = Object.create(Person.prototype)
// Student的构造函数
Student.prototype.constructor = Student
let s1 = new Student()
let p1 = new Person()
// 方法使用
console.log(s1.name); // people
s1.say(); // say
s1.hello(); // hello!!!
console.log(p1,"p1")
console.log(s1,"s1");
// ES6继承
class Person{
constructor(name){
this.name = name;
}
say(){
console.log("不同区域都有语言");
}
}
class Country extends Person{
// 父类中有构造函数,子类必须实现父类的构造函数
constructor(name,lang){
// 构造函数中想继承,先super塑形,super必须放在第一行
super(name);
this.lang = lang; // 扩展了专属于自己的lang属性
}
say(){
console.log("中国话");
}
}
let chinese = new Country("中国","各地方言");
console.log(chinese.name); // 中国
console.log(chinese.lang); // 各地方言
chinese.say(); // 中国话
clientHeight:可视区域大小,只包含padding
offsetHeight:可视区域包含padding+border+滚动条
scrollHeight:包含了因为滚动被隐藏的部分的高度,即整个页面中某个元素距离顶部元素的高度
scrollTop:滚动条滚动的距离,即被卷上去的页面高度
offsetTop:到定位父级的上边界的间距
clientTop;获取元素上边框(border)宽度..px
这三个都是改变this指向的函数,call和apply的第一个参数都是要改变指向的对象,其中call的第二个参数是"参数1,参数2,…"这样的排列,apply的第二个参数必须是一个数组,bind返回的是一个函数,必须被调用才可以使用。
使用new关键字可以new出实例化对象。
明明只是一个函数,可是为什么new Person()执行后会突然返回一个对象呢?
因为new帮我们创建了一个空对象,例如:obj;
将空对象原型的内存地址__proto__指向函数的原型对象;
利用函数的call方法,将原本指向window的绑定对象this指向了obj。
(这样一来,当我们向函数中再传递实参时,对象的属性就会被挂载到obj上。)
利用函数返回对象obj。
js中的数据交互是通过事件驱动实现的,鼠标点击事件onclick等。
ie的事件流是事件冒泡,网景的事件流是事件捕获,事件冒泡是由内向外的,由子级向父级传递的。
事件捕获是从父级传递到子级,事件捕获在点击完元素后不会立即触发事件,而是一层一层向下捕获,最终到达这个元素才触发事件。
DOM2级的addEventListener的第三个参数是true,也就是说当你点击目标元素后,不会立刻执行触发事件,而是先执行事件捕获阶段 ——> 然后处于目标阶段(触发事件) ——> 事件冒泡阶段。
事件委托是利用事件冒泡的机制,通过给父级统一添加事件,点击子级会冒泡到父级,执行父级身上的事件,这样可以统一管理一系列标签的事件。比如ul和li标签,为了给每个li一个点击事件,可以统一给父级ul一个点击事件来管理。当动态添加li标签,也会有事件监听函数。
输入url ----> DNS服务器 —> tcp三次握手建立连接 —> 发送http/tsl+https请求 —> 服务器返回数据包 —> tcp四次挥手关闭连接
根据数据包 —> 解析HTML\CSS\JS资源 ----> 生成DOM树+CSS规则树 —> 合成渲染树 —> 对页面进行绘制和渲染
输入url后,浏览器发送请求到DNS域名解析服务器,获得到ip地址,建立tcp三次握手,形成tcp/ip连接,发送http请求(如果是https请求在发送前还需要进行tsl协商才能发送请求),服务器响应数据包,tcp四次挥手关闭与服务器的连接。
浏览器会根据拿到的数据包解析HTML文档,构建DOM树,构建CSS样式树,解析js脚本,下载资源。
页面渲染的过程:
浏览器通过HTML解析器,将HTML解析成DOM树。通过CSS解析器将CSS解析成CSS规则树,DOM树和CSS规则树合成一个渲染树。根据渲染树对页面进行渲染。
tcp包括三次握手-传输确认-四次挥手
tcp的三次握手,客户端要询问服务器是否能够建立连接[发送一个SYN包],服务器端要应答客户端同意连接[发送一包SYN+ACK包],客户端收到之后应答确定是要建立连接[回复一个ACK包],连接建立。因为互相发送了三包数据,所以是三次握手。(SYN包就是要发送的数据包,ACK包是做应答的数据包)
为什么一定要是三次握手?两次握手不行吗?
不行,因为三次握手才能建立一个可靠的连接,若是两次握手,客户端发送第一包数据过程中阻塞,由于客户端没有收到数据会再次发一次第一次握手的数据过去,此时服务器端收到这包数据给了一个应答,一个连接建立。而阻塞的那个包突然也传到了服务器端,那么服务器相当于建立了两个连接,而客户端却认为是一次连接,所以服务器端会一直在等待数据状态。如果是三次握手,服务器端在接收到这个阻塞的数据包后,应答客户端,客户端没有发送第三次握手的数据过来,服务器就会认为这次连接没有建立成功。三次握手就是为了解决网络信道不可靠的问题。为了在不可靠的信道上建立可靠连接。
tcp连接是在不可靠的信道上建立可靠连接,那如何解决一包数据被拆成多包发送时的丢包和乱序问题?
tcp是全双工通信,即客户端和服务器端都采用的这种机制发送和接收数据。机制如下:tcp协议为每一个连接建立了一个发送缓冲区,建立连接后的第一个字节号是0,后面每个字节的序列号增加1,发送数据时,从发送缓冲区取一部分数据组成发送报文,在tcp协议头中附带序列号和长度。
服务器端收到数据后,需要回复确认包ACK,确认报文中的ACK=接收序列号+长度,也就是下一包数据需要的起始序列号,这样能够使发送端确认发送的数据已经被对方收到,发送端也可以一次性发送连续的多包数据,接收端只需要回复一个确认包ACK即可。
tcp的四次挥手
客户端和服务器端,都可以发送“关闭连接”请求。客户端要关闭连接【发送一个FIN包】【自己进入FIN-WAIT-1终止等待1状态】;
服务器端自查数据【发送一个ACK包】【自己进入CLOSE-WAIT关闭等待状态】【客户端进入FIN-WAIT-2终止等待2状态】此时服务端还可以发送未发送的数据,客户端还可以接受数据;
服务端发送完数据后【发送一个FIN包】【自己进入LAST-ACK最后确认状态】;
客户端收到数据后【发送ACK包】【自己进入TIME-WAIT超时等待状态】,到达超时时间后关闭连接,而服务端收到ACK包后立即关闭连接;
为什么客户端需要设置超时时间呢?
如果没有超时时间,服务端没有收到客户端发送的ACK包的话,就会一直停留在最后确认状态。 超时时间是为了确定服务端已经收到ACK包,如果服务端没有收到ACK包会重发FIN包,再次等待客户端再次发送ACK包。也是为了保证在不可靠的传输信道进行可靠传输。
UDP协议
UDP协议是基于非连接的,发送数据是直接发数据包,正因为它发送没有状态确认等,所以占用cpu资源较少,性能损耗小,但是传输数据可能造成丢包,稳定性较弱,
TCP协议与UDP协议的不同适用场景:
tcp协议和udp协议都属于传输层协议,都是在程序之间传输数据。数据可以是文本视频文件等。tcp基于连接的可靠传输协议,udp是非连接的协议。
TCP协议传输数据稳定可靠,适用于对网络通讯质量要求较高的场景,需要准确无误的传输给对方。传输文件,发送邮件,浏览网页等。
UDP传输数据速度快,但是可能产生丢包,适用于对实时性要求较高,但是对数据丢包并没有太大要求的场景,比如:语音通话,视频直播等。
promise是异步操作的同步解决方案,可以避免层层嵌套的回调地狱。它有几个状态:pedding(等待)/fufiled(成功)/rejected(失败),状态一旦改变,就不会再变了。promise的创建是构造函数new出来的对象,promise中有两个函数异步操作成功会调用resolve(),失败会调用reject()。通过.then()来执行下一步操作,一直返回Promise对象的话,可以一直.then下去。
promise中代码的执行是先主任务,再微任务,再宏任务。所以先打印同步代码的结果,其次执行promise.then(res=>{})中的结果,因为拿到数据res的步骤是微任务,然后才是执行定时器代码这种宏任务。
>>>输出结果:1 2 3 4
async是加在函数前,证明这个函数是异步函数,内部的await代表每一次的状态,每到一个await都会等待执行完,才会继续向下进行下一步的操作。await是基于generator中的yield状态东西,每到一个状态都会等待。也是可以将异步操作以同步的形式展示出来。更有利于理解代码。网络请求用这种配合也比较多。
构造函数在es5中是用于充当类的一个概念,需要new关键字创建实例化对象,而且书写方式上函数名是开头字母大写。普通函数直接调用即可,而且普通函数调用时不会创建出实例化对象来。
构造函数中的this指向实例,普通函数中的this指向调用函数的对象,没有对象调用指向window。
es6中构造函数的函数名与类名相同
面向对象是一种编程范式,一切皆对象,类是对一类事物的共同属性的一种抽象,可以通过new关键字创建出一系列的实例化对象。对象可以继承类的一些属性和方法,也可以扩展自己的属性和方法,具有封装、继承、多态的特点。
push/pop: 从尾部插入和删除
unshift/shift: 从头部插入和删除
concat 可拼接数组,返回值为原数组
splice 可裁切数组
split 将字符串转换为数组
map 遍历数组
let const是es6新增的关键字,都是块级作用域,
var是全局作用域或函数级作用域,没有块级作用域的概念
var能重复声明和赋值,let const不能
var存在变量提升,let const没有
const声明的是一个常量,初始化时必须赋值,且一旦赋值不能改变
es6有模板字符串,用反引号包起来方便字符串拼接。
有数组和对象的解构赋值,符号是…
有set数据结构
有promise
有Symbol这种独一无二是数据类型
箭头函数
for…of for…in这种遍历方式
类的概念和继承有了新的实现方式
Module语法
存储大小不同:cookie是4kb,其他两个是5M
读取方式不同:cookie是随着网络请求携带到服务器,其他两个是存储在本地的,需要通过getItem读取
存储时长不同:
cookie可以设置过期时间,cookie有一个注意点是cookie不能跨域,必须是同源的
sessionStorage是只在当前会话页面有效,刷新页面数据还存在,关闭页面数据将会清空
localStorage是存储在本地,且长期有效,除非在代码中做删除操作,或用户手动删除
常规方法无法识别NaN,如果要判断,需要用es6新增的Object.is(NaN === NaN),返回值为布尔值
ajax是网络请求,可以让页面不需要刷新就能得到更新。它主要是基于xhr对象
第一步:创建xhr对象
var xhr = new XMLHttpReauest()
第二步:发送http请求
xhr.open('get','接口') // 准备数据
xhr.send() // 发送请求
第三步:接收数据
通过onreadyStateChange事件监听readyState的状态变化,=4就是请求完成得到响应数据了
第四步:通过读取status状态码,来判断拿到的数据,并更新页面
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
// status状态码:目前讲的分为2种:成功200 失败404等非200
if(xhr.status === 200){
// 成功了,打印响应文本
console.log(xhr.responseText);
}else{
// 打印失败时的状态信息
console.log(xhr.statusText);
}
}
}
防抖:用户触发事件特别频繁,只要最后一次事件的操作,
思路是:只要用户触发事件了,那我们就把之前的定时器清掉,再新建一次定时器。
节流:控制事件执行次数
思路是:用一个valid作为标志为,为真的时候就进入定时器执行业务,为假的时候就出来定时器
// 防抖,什么时候停,什么时候执行
function debounce(fn,delay){
var timer = null;
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(fn,delay)
}
}
function Print(){
var scrollTop = document.documentElement.scrollTop;
console.log(scrollTop);
}
window.onscroll = debounce(Print,300)
// 节流:间隔时间内只执行一次
function throttle(fn,delay){
var valid = true;
return function(){
if(!valid){
return false;
}
valid = false
setTimeout(()=>{
fn();
valid = true;
},delay)
}
}
function Print(){
var scrollTop = document.documentElement.scrollTop;
console.log(scrollTop);
}
window.onscroll = throttle(Print,300)
for...in:
适用于遍历对象而产生的,不适用于遍历数组,但可以遍历数组。
获取对象的 键名 key;
会遍历整个对象的原型链;
返回数组中所有可枚举的属性
for...of:
只能遍历数组
会遍历获取对象的 键值 value;
只遍历当前对象;
返回数组下标对应的属性值
map:
返回新数组,不改变原数组,按return后面的方式返回新数组
filter:
用来过滤出符合条件的内容,返回新数组,若没有符合条件的,返回[ ]空数组
// map forEach for...in for...of filter
// map
const arr = [1, 2, 3, 4]
const newArr = arr.map((it) => {
return {num: it}
});
console.log(newArr); // [{num: 1},{num: 2},{num: 3},{num: 4}]
console.log(arr); // [1, 2, 3, 4]
// forEach 不能用break和continue跳出当前的迭代,只能用return进入下一个迭代
const arr1 = [1, 2, 3, 4]
arr1.forEach((it, index, self) => {
if (it == 2) {
return;
}
console.log(it, index, self);
// 第一行:1 0 [1, 2, 3, 4] 第二行:3 2 [1, 2, 3, 4] 第三行: 4 3 [1, 2, 3, 4]
})
// for..in可用于遍历对象和数组
const obj = {
num1: 11,
num2: 1
}
for (let key in obj) {
console.log(key); // num1 num2
}
// for...in
const arr2 = [1, 3, 4]
for (let value in arr2) {
console.log(value); // 0 1 2
}
// for..of只能用于数组
for (let value of arr2) {
console.log(value); // 1 3 4
}
// for..of用于数组,遍历拿到的是键值,也就是每个索引中存放的value值
// for..in用于对象和数组,遍历拿到的是数组的索引号,或对象的键值对中的键名
// filter()过滤数组,返回过滤后的新数组
var arr3 = [1, 2, 3, 4].filter((item) => {
return item > 3
})
console.log(arr3); // [4]
const arr = [1, 2, 2, 'abc', 'abc', true, true, false, false, undefined, undefined, NaN, NaN]
// set --- set数据结构中的内容都不重复[ES6]
const s = new Set(arr)
console.log(s); // {1, 2, 'abc', true, false, undefined, NaN}
console.log([...s]); // [1, 2, 'abc', true, false, undefined, NaN]
// indexOf --- 没有找到值返回-1---不可检测NaN
const newArr = []
for(let i of arr){
if(newArr.indexOf(i) == -1){
newArr.push(i)
}
}
console.log(newArr); // [1, 2, 'abc', true, false, undefined, NaN, NaN]
// includes --- 返回true/false[ES7]---可以检测到NaN
const newArr1 = []
for(let i of arr){
if(!newArr1.includes(i)){
newArr1.push(i)
}
}
console.log(newArr1); // [1, 2, 'abc', true, false, undefined, NaN]
// 双层for循环 --- 不能检测NaN
let len= arr.length;
for(let i=0; i<len; i++){
for(let j=i+1; j<len+1;j++){
if(arr[i] === arr[j]){
arr.splice(j,1) // 把j索引的这个元素切掉
}
}
}
console.log(arr); // [1, 2, 'abc', true, false, undefined, NaN, NaN]
// filter遍历数组
function unique(arr) {
return arr.filter(function(item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item) === index;
});
}
console.log(unique(arr)) // [1, 2, 'abc', true, false, undefined]
因为浏览器有同源策略,即协议域名端口号必须相同,才能进行访问。目的是为了规避掉一些安全问题。
但公司产品可能需要关联,此时就需要跨域访问,解决跨域问题的方法有三种:
jsonp解决跨域(只能是get请求)cors(后端配置)proxy代理
jsonp解决跨域主要是后端要给的接口中必须将数据给到回调函数里,前端实现这个回调函数拿到数据。首先需要动态创建script标签,script标签的src属性为接口地址,将script标签添加到页面中,前端实现接口,拿到后台给我们的数据。
冒泡排序思想:相邻两个数比较,每一趟冒出一个最小的数,下一趟内循环次数减1
eg:3个数,因为每一趟都会冒出最小的数,这个数就不用参与比较了,
所以第一趟比较2次,第二趟比较1次
【3个数比较2趟,且内层循环每一趟比较次数-1】
------------------------------------------------------------------------------------
const arr = [5,1,65,43,454,90,545,34,435,356,342,12]
// 冒泡排序
function bubbleSort(arr){
for(let i=0; i< arr.length; i++){
for(let j=0; j arr[j+1]){
const temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
return arr;
}
console.log(bubbleSort(arr)); // (12) [1, 5, 12, 34, 43, 65, 90, 342, 356, 435, 454, 545]
快速排序(分治思想):
快速排序:
选择数组中任何一个数作为pivot基准数,比pivot小的数放在它左边,比它大或相等的放在它右边
再对左右区间重复上述操作,直到各区间少于2个元素
-------------------------------------------------------------------------------------------------------------
const arr = [5,1,65,43,454,90,545,34,435,356,342,12]
function quickSort(arr){
if(arr.length <= 1){
return arr;
}
let pivotIndex = Math.floor(arr.length / 2);
let pivot = arr.splice(pivotIndex, 1)[0];
let left = [];
let right = [];
for(let i=0; i < arr.length; i++){
if(arr[i] < pivot){
left.push(arr[i])
}else{
right.push(arr[i])
}
}
return [...quickSort(left),pivot,...quickSort(right)]
}
console.log( quickSort(arr)); // (12) [1, 5, 12, 34, 43, 65, 90, 342, 356, 435, 454, 545]
选择排序:
选择排序:
第一趟:第一个值,分别和后面每个值进行比较,若满足规则则交换位置,开始下一趟比较
第二趟:第二个值,分别和后面每个值比较,直到遇到满足交换条件的,交换位置,开始下一趟
第三趟:第三个值,分别和后面每个值比较,直到遇到满足交换条件的
【5个数比较4趟】
------------------------------------------------------------------------------------------------------
const arr = [5,1,65,43,454,90,545,34,435,356,342,12]
function selectSort(arr){
for(let i = 0;i
const arr = [5,1,65,43,454,90,545,34,435,356,342,12]
function insertSort(arr) {
let len = arr.length;
for(let i=1; i=0 && current
客户端与服务器端进行交互时,需要ip地址作为标识来区分,所以输入url地址后,会先通过DNS域名解析服务器拿到ip地址。此时http协议会调用传输层的tcp协议,若是https协议会在tcp协议前先进行TSL握手(相当于一个安全层,用于给数据加密)。
http协议和https协议按五层网络结结构来分都是应用层的协议,https是需要加密传输的。
http传输过程是完全透明的,任何人都能在链路中截取、修改、伪造请求响应报文,数据不具有可信性。使用https时,所有的http请求和响应在发送到网络前,都会进行加密处理。
TSL的对称加密,使用异或算法(异或即相同为0,不同为1)。
非对称加密有两个密钥,一个公钥一个私钥,公钥私钥都是服务端保管,一般运维来部署,公钥分发给客户端,私钥服务端保管好进行解密
这个答案不确定,目前搜到的是可以在网络请求的响应拦截器中过滤返回的数据,如果是第二次的就显示,如果是第一次的就过滤掉。
defer属性:延迟脚本执行,等DOM加载生成后再执行script脚本
async属性:异步的形式,脚本下载的同时,浏览器继续渲染,但是若几个js资源有依赖关系不推荐这个,因为可能被依赖的那个文件后下载完的话会导致问题
动态创建script标签,给script标签一个src属性放要加载的资源,将script标签插入到页面代码中
//这些代码应被放置在
标签前(接近HTML文件底部)
设置计时器:设置一个定时器来延迟加载js脚本文件
放在所有js代码底部:
xxxxx正常代码
1.event.stopPropagation()方法
阻止事件的冒泡方法,不让事件向document上蔓延,但是默认事件会执行,当你掉用这个方法的时候,如果点击一个链接,这个链接仍会被打开,
2.event.preventDefault()方法
阻止默认事件的方法,调用此方法是,链接会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;
typeof操作符可返回的数据类型有:“undefined”、“object”、“boolean”、“number”、“string”、“symbol”、“function”等。
// 在javascript中,typeof操作符可返回的数据类型有:“undefined”、“object”、“boolean”、“number”、“string”、“symbol”、“function”等。
var a;
console.log(typeof a); // undefined
console.log(typeof {}); // Object
var c = NaN;
console.log(typeof c); // number
console.log(typeof false); // boolean
console.log(typeof 1); // number
console.log(typeof 'hello world');// string
var s = Symbol('独一无二');
console.log(typeof s);// symbol
var print = function(){
console.log("我是一个打印函数");
}
console.log(typeof print); // function
强制类型转换:
parseInt() Number() String() Boolean()
parseInt("1234blue"); // 1234
Number("56") // 56
String(123) // "123"
Boolean(0); // false
隐式类型转换:
+号和字符串连接,任何类型都会被隐式转换为字符串1+'abc' = '1abc'
用==做判断时,会自动进行隐式类型转换,比如:1=='1'
// split将字符串分割成数组
// join将数组拼接成字符串
var s = "hello*world*!!*today";
var s1 = ['say', 'hi','hi'];
console.log(s.split('*')); // ['hello', 'world', '!!', 'today']
console.log(s1.join('*')); // say*hi*hi
通过JSON.parse()方法,将json格式的数据转化为js对象,拿到的json数据就可以正常使用了
console.log(JSON.parse('{ "name":"张三", "age":18, "email":"xxx.zs.com" }'));
>>> 输出结果:{name: '张三', age: 18, email: 'xxx.zs.com'}
jsonp是一种跨域解决方案,只能解决get请求的跨域问题,不适用于post请求。
不是真正的ajax是因为jsonp中没有通过xhr对象来实现,而是通过script标签动态创建的方式来拿到后台给的数据的。而且jsonp不能进行post请求,所以本质上不是ajax。
$(document).load是当页面所有资源全部加载完成后(包括DOM文档树,css文件,js文件,图片资源等),执行一个函数。
$(document).ready是当DOM文档树加载完成后执行一个函数 (不包含图片,css等),会比load较早执行。
原生的js中不包括ready()这个方法,只有load方法也就是onload事件。
typeof
typeof 的返回类型为字符串,值有:number、boolean、string、object、function、undefined、symbol、bigint
typeof 一般用来判断基本数据类型,除了判断null会输出"object",其它都是正确的
typeof 判断引用数据类型时,除了判断函数会输出"function",其它都是输出"object"
注意:这里涉及两个经常考的面试题!
null 的数据类型是object (null是一个空的引用对象,是一个占位符)
console.log 的数据类型是function
对于引用数据类型的判断,使用typeof并不准确,所以可以使用instanceof来判断引用数据类型
console.log(typeof 1); //number
console.log(typeof true); //boolean
console.log(typeof null); //object!!!
instanceof
instanceof 可以准确的判断引用数据类型,它的原理是检测构造函数的prototype属性是否在某个实例对象的原型链上
instanceof用于判断实例是否是某个对象的实例,返回值是true/false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
constructor
能准确区分Array|Object 因为它没有instanceof那样会遍历整条原型链,只是在实例身上进行判断。
但也有个致命的缺陷,实例上的这一属性太容易被修改了,一旦修改,这个方法就没有意义了。
不能检测出null和undefined
console.log([].constructor === Array); // true
console.log(function() {}.constructor === Function); // true
console.log({}.constructor === Object); // true
console.log((null).constructor === Null); // Cannot read property 'constructor'
console.log((undefined).constructor === Undefined); // Cannot read property 'constructor'
Object.prototype.toString.call()
可以检测所有的数据类型,包括null和undefined
bject.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); // [object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Array.isArray()
判断数组:检测数组 --- 返回 true/false
console.log(Array.isArray(arr));// true
Number.isNaN(NaN)
如果是NaN则返回true,否则返回false
// rest参数 ...items
function add1(...items){
console.log(items);
}
add1(10,20,30); // [10, 20, 30]
箭头函数中用…arg存储所有参数。
(1) Set执行时间最短,那么查找速度最快,当然了Set 和 Map的查找速度都很快想差不大,
所以说这两种方法具有极快的查找速度。
(2) 初始化需要的值不一样,Map需要的是一个二维数组,而Set 需要的是一维 Array 数组
(3) Map 和 Set 都不允许键重复
(4) Map的键是不能修改,但是键对应的值是可以修改的;Set不能通过迭代器来改变Set的值,
因为Set的值就是键。
(5) Map 是键值对的存在,值也不作为健;而 Set 没有 value 只有 key,value 就是 key;
Object.create(null) 创建的对象是一个空对象,在该对象上没有继承 Object.prototype 原型链上的属性或者方法。bject.create(o),如果o是一个字面量对象或实例对象,那么相当于是实现了对象的浅拷贝。
new Object()是使用构造方法创造对象,新建一个对象实例,继承原对象的prototype属性。
100-199 提示信息 – 表示请求正在处理,除非在实验条件下,否则服务器不能发送100-199错误码给前端
200-299 成功 – 表示请求正常处理完毕,一般表示请求成功
300-399 重定向 – 要完成请求必须进行更进一步的处理
400-499 客户端错误 – 请求有语法错误或请求无法实现
500-599 服务器端错误 – 服务器处理请求出错。
100 客户端应当继续发送请求。
200 请求已成功,请求所希望的响应头或数据体将随此响应返回。
202 服务器已接受请求,但尚未处理。
300 被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的商议信息。
301 被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。
400 语义有误,当前请求无法被服务器理解。请求参数有误。
403 服务器已经理解请求,但是拒绝执行它。
404 请求失败,请求所希望得到的资源未被在服务器上发现。
浏览器会将请求得到的资源储存为离线资源,下次需要该资源时,浏览器会根据缓存机制决定直接使用缓存还是再次向服务器发送请求
作用:减少了不必要的数据传输、降低了服务器的压力
加快了客户端访问速度,增强了用户体验
强制缓存
:不向服务器端发送请求,强制使用缓存数据。强制缓存实现跟前端没关系,都是后端在处理,实现方式: 比如网页的图片,后端在返回数据时,在响应头中返回expires(指定资源到期具体时间)和cache-control。查看方式:查看源码点击网页图片,Network–img–Headers–ResponseHeaders中有expires和cache-control这些数据。
协商缓存
到了过期时间后,是否是后台将过期时间提早了一个月之类的,(比如零食6月才真的过期,厂家为了容错性写的5月过期)。当强缓存失效后,会使用协商缓存(即问一下后台,我这个资源是真的过期了吗,后端说没有过期可以接着用,接着用强缓存读即可)。协商缓存由服务器决定是否还能继续使用缓存,向服务器发送请求资源,并携带过期资源标识Etag字段。 服务器会进行判断浏览器缓存的资源是否真的失效,真的失效,则服务端返回新的资源和缓存标识,返回状态码200。浏览器再次存入缓存,后续再次从强缓存读数据。
缓存时间到了,但是资源没有更新,那就继续使用本地数据,直接返回304
参考资料: reduce视频链接
[将前面数组项遍历产生的结果与当前遍历项进行运算 --- 需要reduce来处理]
reduce用法 --- 可以将前面每一次运行结果跟当前项进行运算比较
prev:上一个值,第二个参数是初始值,有初始值就是初始值,没有初始值就是数组中的第一个值
cur:当前值,有初始值的话,cur就是数组中的第一个值,没有的话,cur就是数组中的第二个值
index:cur的索引号
[1. 数组求和----------------------------------------------------------]
const arr = [1,2,3];
const sum = arr.reduce((prev,cur,index)=>{
console.log(prev,index); // 00 11 32
return prev + cur;
},0)
console.log(sum); // 6
[2. 数组最大值----------------------------------------------------------]
const max = arr.reduce((prev,cur,index)=>{
return Math.max(prev,cur);
})
console.log(max); // 3
[3. 数组去重----------------------------------------------------------]
const arr1 = [1,2,3,5,4,4,3]
const newArr = arr1.reduce((prev,cur,index) =>{
console.log( prev);
if(prev.indexOf(cur) === -1) {prev.push(cur);}
return prev;
},[])
console.log(newArr); // (5) [1, 2, 3, 5, 4]
[4. 数组扁平化----------------------------------------------------------]
const arr2 = [[1,2],[3,5],[4,4],[1,2,3,[4,5,6,[7,8]]]]
function getflatArr(a){
return a.reduce((prev,cur)=>{
console.log(prev);
return prev.concat(Array.isArray(cur) ? getflatArr(cur) : cur)
},[])
}
console.log(getflatArr(arr2)); // (14) [1, 2, 3, 5, 4, 4, 1, 2, 3, 4, 5, 6, 7, 8]
xss是一种发生在web前端的漏洞,其主要危害对象是前端用户,其攻击手段主要有诱惑用户点击钓鱼链接/获取用户cookie信息,甚至可以结合浏览器漏洞对用户主机远程操控。
xss攻击流程(跨站脚本漏洞):往web页面中插入恶意的script代码,用户请求页面时,该代码将会被执行(在普通用户不知情的情况下执行),从而获取用户信息,达到攻击用户的目的。
形成XSS漏洞的原因:程序对输入输出的控制不够严格,导致黑客脚本攻击服务器,服务器输出时会被浏览器当做正常代码执行,从而造成危害。
XSS攻击类型(危害由高到低):存储型 > 反射型 > DOM型
存储型:恶意代码存储在数据库中,永久存储,一般出现在留言板注册等页面
反射型:【恶意代码出现在了url链接地址中】恶意代码是一次性的,所见即所得,出现在查询页中。黑客需要诱导客户点击钓鱼链接。比如邮箱中的垃圾邮件,只要点了就触发了XSS攻击。
DOM型:不与后台服务器产生的数据交互,是一种通过修改页面DOM节点产生的问题。
测试流程:在目标站点找到输入点,查询接口,留言板等,输入特殊字符和唯一识别字符,点击提交查看返回源码,是否有做对应处理。通过搜索定位唯一字符,结合唯一字符前后语法,确认是都可以构造js代码(即代码是否可以构造闭合)
提交构造的脚本代码,绕过各种姿势,看是否可以成功执行,如果成功执行,则证明存在XSS漏洞。
XSS可以盗取cookie,钓鱼,获取键盘输入记录等。黑客通过img标签可以拿到正常用户的cookie,
防范: 防止 HTML 中出现注入。防止 JavaScript 执行时,执行恶意代码。
输入做过滤,输出做转义。 用token替代cookie
Vue等框架会默认屏蔽XSS攻击,Vue中使用v-html可能会造成XSS攻击
给cookie设置http-only
// 无法获取到设置 http-only 的cookie
document.cookie
避免使用内联事件onLoad="onload('{{data}}')",在 js中通过addEventlistener() 事件绑定会更安全。
前端过滤不靠谱, 主要还是由后端去处理. 并且过滤恶意输入适用范围有限.
参考博客: 点我跳转
sort排序默认按照字典顺序排。即111会排在20的前面。
底层 :查到博客说:sort使用的是插入排序和快速排序结合的排序算法。
为什么:数组长度不超过10时,使用插入排序。长度超过10使用快速排序。在数组较短时插入排序更有效率。
console.log("1"+1); // 11
console.log("1"*1); // 1
null是一个表示 " 无 " 的对象,转为数值时为0; Number(null) // 0
undefined是一个表示 " 无 " 的原始值,转为数值时为NaN。 Number(undefined) // NaN
typeof(null) // object
typeof(undefined) // undefined
null表示 " 没有对象 " ,即该处不应该有值。典型用法是:
(1)作为函数的参数,表示该函数的参数不是对象。
(2)null作为对象原型链的终点。Object.getPrototypeOf(Object.prototype)
(3) 当需要释放一个对象的时候可以将该对象赋值为null,进而来释放对象
undefined表示 " 缺少值 " ,就是此处应该有一个值,但是还没有定义。典型用法是:
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有传,该参数等于undefined。
(3)对象没有某个属性,读取属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
null、undefined是怎么产生的
1、产生null方式一:当访问一个不存的DOM节点时
console.log(document.getElementById(“#aaaaaaa”)); // null
2、产生null方式二:Object的原型链终点:
console.log(Object.prototype.__proto__);// null
1、产生undefined方式一:声明了变量但未赋值:
var a;
console.log(a);//undefined
2、产生undefined方式二:对象的属性没有赋值的情况下:
var obj = {a:1};
console.log(obj.age)//undefined
3、产生undefined方式三:函数调用的时候,函数的参数没有提供的情况下:
function add(num){
console.log(num)
};
add(); // undefined
4、产生undefined方式四:当函数没有返回值的情况下:
var a = function(){};
console.log(a()) // undefined
常见的伪元素有::after和::brfore,他们用于在CSS渲染中向元素的头或者尾插入内容,其不影响文档本身,只会影响最终的样式。
也即是说,这些伪元素不会出现在DOM中,仅仅在CSS渲染层中。
我们使用Window…querySelector(“::before”)其实操作的是DOM元素,因为伪元素不存在DOM中,所以无法操作伪元素。
只查到可以使用webworker这种模拟多线程处理,其他不知道。。。这个题不大会
在 IE6-8 下,数组的 indexOf 方法还不存在。所以要写出一个方法代替indexOf方法。
思路:通过对比数组前后元素是否相等,如果相等利用splice方法删除后面再次出现的元素,将删除重复元素后的原数组元素push到结果数组
splice删除元素方法的参数为两个splice(index,num),分别为开始下标和删除个数
indexOf有两个参数,第一个参数是要查询的元素,第二个参数是开始查询位置的下标start。
Array.prototype.myIndexOf = function (param,start=0) {
//如果start大于等于数组长度,此时this[start]越界,返回-1
if (start >= this.length) return -1
//如果start小于0,判断start + this.length
if (start < 0) {
start = start + this.length < 0 ? 0 : start + this.length;
}
//start处理完毕,开始从start处遍历数组,查找元素下标
for (let i = start; i < this.length; i++) {
if (this[i] === param) return i;
}
//遍历完毕没有找到相应元素,返回-1
return -1
}
什么是函数柯里化?
就是将一个函数的多个参数,分成多个单一参数的函数返回,柯里化函数是闭包的典型应用
好处:
入口单一,便于测试和复用; 易于定位bug
坏处:
嵌套的函数多,占用内存较多,效率低,每个函数都会在栈中占据内存,都有单独的函数级作用域
// 函数柯里化
function add(){
// let args = arguments; // 第一个括号里的参数 // 但args不是一个数组,要进行数组转换
let args = [...arguments];
let inner = function(){ // 接收第二个括号里的参数
args.push(...arguments)
return inner
}
inner.toString = function(){
return args.reduce((prev,current)=>{
return prev + current;
},0)
}
return inner
}
console.log(+add(1)(3)); // 4
console.log(+add(1,2)(3)); // 6
console.log(+add(1)(2)(3)) // 6
console.log(+add(1)(2)(3)(1,4)) // 11
函数式编程是一种编程范式,主要是利用函数把运算过程封装起来,通过组合各种函数来计算结果。
localstorage
localstorage是浏览器多个标签共用的存储空间,可以实现多标签之间的通信。
使用 Websocket,通信的标签页连接同一个服务器,发送消息到服务器后,服务器推送消息给所有连接的客户端。
参考博客: 点击跳转参考博客
webSocket在开始的时候依旧使用的是http协议,只不过后面保持tcp持久链接,webSockets和http很像,它的请求uri用的是ws、wss,对应http、https
webSocket参考博客:点击跳转参考博客
这里补充一下http的轮询(面试也有可能问轮询):点击跳转http轮询参考博客
压缩代码
压缩图片
导入时使用CDN资源
减少http请求
路由懒加载,图片懒加载
使用CDN
提取公共功能
CSS样式放在头部
js资源可以放在底部引入
(function(){函数体}()); // 自执行函数写法1
(function(){ 函数体 })(); // 自执行函数写法2
前面的函数都是声明和调用分开,而且可以不限次被调用,这是模块化思想的体现。但是对那些只需要执行一次的函数,怎么办呢?这就要用到立即执行函数。
立即执行函数,其实也可以叫初始化函数,英文名:IIFE。
立即执行函数就是在定义的时候就立即执行,执行完以后就释放,包括函数内部的所有变量。
比如在页面完成初始化完成后执行的函数一般都是立即执行函数。
// cookie的应用代码
<body>
<form>
<label>用户名</label>
<input type="username">
<label>密码</label>
<input type="password">
<input type="checkbox" id="rememberMe">
<label for="rememberMe">记住我</label>
<input type="submit" value="登录">
</form>
<script>
const username = document.querySelector('input[type="username"]')
const checkbox = document.querySelector('input[type="checkbox"]')
const submit = document.querySelector('input[type="submit"]')
// 读取cookie
let cookie = {}
let array = document.cookie.split('; ').map(cookie => cookie.split('='));
for(let i =0;i<array.length;i++){
let name = array[i][0];
let value = array[i][1];
// 设置cookie的时候编码了,所以这里要解码
cookie[name] = decodeURIComponent(value);
}
console.log(cookie);
if(document.cookie){
username.value = cookie.username;
checkbox.checked = true;
}
// 设置cookie
submit.addEventListener('click',e=>{
if(checkbox.checked && username.value != ''){
let key = 'username';
// 因为cookie的值必须不能有空格等多余字符,所以需要对这个值进行编码后存储
let value = encodeURIComponent(username.value);
// cookie只能一条一条设置
// 给cookie设置过期时间:max-age 和towDays值
let twoDays = 2 * 24 * 60 * 60;
document.cookie = `${key}=${value}; max-age=${twoDays}`;
}
e.preventDefault();
})
</script>
</body>
localStorage设置过期时间,可以给某个值设置一个过期时间字段存储一下,通过设置定时器,过了多长时间后让它失效,但页面关闭时定时器也会失效,此时可以通过当前时间减去本地存储的过期时间计算一下,然后删除localStorage
cookie的存储位置是:浏览器和服务器都有存储,因为cookie在进行网络请求时会被携带。
参考了技术蛋老师的视频讲解:视频链接
后续学习:store.js 是一个实现了浏览器的本地存储的 JavaScript 封装 API 。可以自定义储存方式,命名空间,设置过期时间等功能。在github搜索包“store.js”,可以下下来学习,能够设置过期时间之类的,以及选择存储方式之类的
重载,从简单说,就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者重载方法。
多态是指不同类的对象在调用同一个方法时所呈现出的多种不同行为。
参考博客:点我跳转参考博客
html标签有很多,语义化标签有header footer section aside article nav等 ,这些标签本身就具有语义化,容易读懂标签的含义,便于浏览器搜索引擎爬虫对网页的爬取,从而提升了SEO。
移除了big、center、font、s、strike、tt、u、frame等元素
新特性:canvas svg webStorage(localstorage/sessionstorage) video audio 语义化标签 webSocket
区分html和html5:区分文档声明
html5的文档声明很短:
SSR是指服务器端渲染页面,指由服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程
好处:1. 网页内容在服务器端渲染完成,一次性传输到浏览器,所以,首屏加载速度非常快(能够提高首页加载速度,因为模板渲染动作由后端完成,前端直接拿到后端替换好的html页面)
2. 有利于SEO,因为服务器返回的是一个完整的html,在浏览器可以看到完整的DOM,对于爬虫、百度搜索等引擎友好。
单页面应用:页面主要由服务器端渲染
只有访问频率非常高的才会用SSR
css的解析不会影响js的执行,css的加载会影响js的执行。由于js可能会操作之前的Dom节点和css样式,因此浏览器会维持html中css和js的顺序。因此,样式表会在后面的js执行前先加载执行完毕。所以css会阻塞后面js的执行。
优化:可以使用CDN资源
压缩CSS,可以使用打包工具webpack,开启gzip压缩
减少http请求次数,合并多个css文件
参考链接: https://blog.csdn.net/qq_44606064/article/details/120619095
行内元素:a span em strong i b... 【独占一行,可以设置宽高】
块级元素:div ul ol form h1-h6 p...【占自身大小,不可设置宽高】
行内块级元素:input img 【占自身大小,可设置高宽】
css盒模型:标准盒模型 和 IE盒模型
由内到外都是 content padding border margin
标准盒模型width、height = content 其他都是往外扩
IE盒模型width、height = content + padding + border 属于向内收
CSS的选择器有:id选择器 > 类选择器 > 元素选择器 通配符选择器 合并类选择器 关系选择器 属性选择器
属性继承:text- font- line-height都可以继承,cursor光标也可以继承,visibility元素可见性
优先级计算:根据选择器的权重不同来叠加
优先级从高到低: !important>内联样式 > id选择器 > 类选择器 > 元素选择器 >通配符选择器>从父标签身上继承的样式
!important的优先级高于内联样式和其他选择器
css精灵图是将网页要用到的多张图片合成一张图片使用,
可以减少网络请求的次数,减少服务器压力。
使用:
background-image:url('./1.jpg')引入精灵图图片,
background-position:-10px 10px;调整图片位置。来规定显示整个图片中的哪一张小图。
缺点:图片也有可能会失真
文件合并(目的是减少http请求):Web性能优化最佳实践中最重要的一条是减少HTTP 请求,减少HTTP请求的方案主要有合并JavaScript和CSS文件、CSS Sprites(雪碧图)、图像映射 (Image Map)和使用Data URI来编码图片。
文件压缩:目的是直接减少文件体积,减少文件加载时间
使用 CDN (内容分发网络)来托管资源。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。从而使用户就近取得数据,提升浏览网站的速度。
使用缓存(并且多个域名来提供缓存),可以减少服务器请求的次数,加快加载速度。
服务端开启GZIP 压缩,对用户请求的页面进行压缩处理,以达到节省网络带宽,提高浏览网站速度的作用
position主要有 relative、absolute、fixed这几个属性,默认是static属性
区别是:relative相对定位不会脱离文档流,元素本身相对自身原位置进行偏移。
absolute和fixed会脱离文档流,且这两个定位时是根据定位父级进行定位,如果没有定位父级,则相对于文档定位。
都有left/right/bottom/top四个属性,relative的这四个属性代表向哪里移动的距离,absolute和fixed的这个属性,代表距离左边的距离,即向相反方向移动的距离。
static 代表正常的文档流,static和fixed的区别是,fixed定位的元素不随页面的滚动而滚动。
Zoom 非标属性,有兼容问题,缩放会改变了元素占据的空间大小,触发重排
.span1 {
font-size: 12px;
display: inline-block;
zoom: 0.8;
}
.span2 {
font-size: 12px;
}
测试10px
测试12px
transform:scale() 大部分现代浏览器支持,并且对英文、数字、中文也能够生效,缩放不会改变了元素占据的空间大小,页面布局不会发生变化
.span3 {
font-size: 12px;
display: inline-block;
-webkit-transform:scale(0.8);
}
.span4 {
font-size: 12px;
display: inline-block;
}
测试10px
测试12px
BFC是块级格式化上下文,内部的布局不会影响外部的整体布局,属于一个独立的区域。视为具有块级元素的属性。
BFC的应用/开启BFC:
清除浮动:父元素高度塌陷时,给父元素加上visibility:hidden,即可将父元素包裹的盒子变成BFC形式,从而父元素高度就不再塌陷了。
开启BFC的一些属性:overflow:hidden/auto; float:left;[会导致元素只占自身大小,宽度丢失,不推荐] display:inline-block;[元素只占自身大小,宽度丢失,不推荐]
BFC的应用2:当两个盒子一个设置了margin-top一个设置了margin-bottom,垂直方向只有大的那个生效,要想要两个都生效,可以分别给两个盒子外层嵌套一个div.container,将div.container加上visbility:hidden,高度重叠问题将得到解决。
<style>
/* 弹性盒 */
.out{
display: flex;
align-items: center;
}
/* margin回移(必须知道宽高) */
.out{
position: relative;
}
.box{
position: absolute;
top: 50%;
margin-top: -100px;
}
/* 定位取值0法(必须有宽高,否则铺满全屏) */
.out{
position: relative;
}
.box{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
/* translate -50%法 */
.out{
position: relative;
}
.box{
position: absolute;
top: 50%; 外盒子的一半
transform: translateY(-50%);回移自身的50%
}
/* display:table-cell方法 */
.out{
display: table-cell;
vertical-align: middle;
}
</style>
<body>
<div class="out" style="width: 500px;height:500px;border:1px solid #000;">
<div class="box" style="width: 200px;height: 200px;background-color: #0f0;"></div>
</div>
</body>
子项目之间的间隙用margin处理即可。
flex布局适合于移动端布局。有主轴和交叉轴两个轴。
采用flex布局的元素,称为flex容器,简称容器。它的所有的子元素自动称为容器成员,称为flex项目,简称项目。div就是flex父容器,span就是子容器flex项目,子容器可以横向排列也可以纵向排列
总结:flex布局原理:就是通过给父盒子添加flex属性,来控制子盒子的位置和排列方式
有一个有名的圣杯布局:
<style>
.out{
width: 80%;
height: 500px;
margin: 0 auto;
border: 1px solid #000;
display: flex;
}
.in{
display: inline-block;
height: 500px;
}
.in:nth-child(1),.in:nth-child(3){
/* 1\3 盒子宽度固定*/
width: 50px;
background-color: blueviolet;
}
.in:nth-child(2){
flex: 1; /*自适应宽度: 占据剩余空间 */
background-color: blue;
}
style>
<body>
<div class="out">
<span class="in">1span>
<span class="in">2span>
<span class="in">3span>
div>
body>
圣杯布局:两边宽度写死,中间盒子宽度自适应,自适应宽度通过弹性盒的flex:1这种实现的,因为flex属性可以自动的分配剩余空间,此处相当于2号盒子直接占据整个剩余空间。
1、方法一
var domain = document.domain;
2、方法二
var domain = window.location.host;
二、获取当前Url的4种方法
var url = window.location.href;
var url = self.location.href;
var url = document.URL;
var url = document.location;
ie 地址栏显示的是什么,获取到的 url 就是什么。
移动端浏览器对新特性的支持更好,兼容性只考虑webkit内核即可
移动端屏幕尺寸不同
视口:是浏览器显示页面内容的屏幕区域,视口可以分为布局视口、视觉视口、理想视口
我们写的网页应该根据实际手机的视口宽度而自动进行缩放,设备有多宽,我们布局的视口就有多宽。布局视口的宽度应该与理想视口一致。【meta视口标签】
单位:在pc端1px=1个物理像素 在移动端1px不一定=1个物理像素
流式布局:百分比布局,主要是宽度要设置为百分比形式,高度单位可以是px,高度需要随着屏幕变化的话,可以用rem单位
<title>流式布局,就是百分比布局title>
<style>
.out{
width: 100%;
height: 500px;
/* 这两个属性是为了让它不无限伸缩 */
/* 浏览器视口宽度>980px就不会再放大了 */
max-width: 980px;
/* 浏览器视口宽度<320px就不会再缩小了 */
min-width: 320px;
}
.in{
float: left;
width: 50%;
height: 100%;
}
.in:nth-child(1){
background-color: pink;
}
.in:nth-child(2){
background-color: blue;
}
style>
<body>
<div class="out">
<div class="in">1div>
<div class="in">2div>
div>
body>
运行结果:流式布局随着视口变化等比例缩小
文字大小随着屏幕大小变化:rem单位(相对于根元素的字体大小设置大小的单位)
媒体查询:可以根据不同尺寸设备设置不同样式
/* <700px */
@media screen and (max-width: 700px){
body{
background-color: blue;
}
}
/* >200 <500 */
@media screen and (min-width: 200px) and (max-width: 500px){
body{
background-color: aquamarine;
}
}
媒体查询配合rem单位实现文字大小适配:
<style>
@media screen and (min-width: 320px){
html{
font-size: 50px;
}
}
@media screen and (min-width: 640px){
html{
font-size: 100px;
}
}
.text{
height: 1rem; // 1rem=(1*font-size)px
font-size: .5rem;
background-color: green;
color: #fff;
text-align: center;
line-height: 1rem;
}
style>
<body>
<div class="text">我是文字div>
body>
运行结果:可以看到文字也随页面缩放了
响应式布局:BootStrap框架,基于媒体查询实现的布局,一套代码适配多个屏幕
less scss都是css预处理器,他们都有相关的文档有语法规范。
他们可以使得css样式书写简化,选择器有层次嵌套,还有变量(方便样式统一修改),可以通过vsCode中的easy less 和 easy sass插件,通过ctrl+s保存后,即可转化成正常的css样式。它有计算功能,可以方便单位转化,比如html{font-size:50px;} 问一个元素在设计稿中是82px,它是多少rem? 1rem = 50px => 82px = ?rem = 82 / 50rem这种单位可以使用计算来得到。
圆角 (border-radius:8px)
多列布局 (multi-column layout)
阴影和反射 (Shadow\Reflect)
文字特效 (text-shadow、)
文字渲染 (Text-decoration)
线性渐变 (gradient)
旋转 (transform)
渐进增强(Progressive Enhancement):一开始就针对低版本浏览器进行构建页面,
完成基本的功能,然后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。
优雅降级(Graceful Degradation):一开始就构建站点的完整功能,然后针对浏览器
测试和修复。比如一开始使用CSS3 的特性构建了一个应用,然后逐步针对各大浏览器进行
hack 使其可以在低版本浏览器上正常浏览。
其实渐进增强和优雅降级并非什么新概念,只是旧的概念换了一个新的说法。在传统软件开发中,
经常会提到向上兼容和向下兼容的概念。渐进增强相当于向上兼容,而优雅降级相当于向下兼容
px:像素单位
rpx:由微信小程序官方推出的新单位,适用于移动端的 uni-app或者微信小程序的开发。
1rpx实际上等于相对于屏幕宽度的1物理像素。在设计时可以将1px约等于2rpx。
em:相对于父元素字体大小而言的大小
rem:相对于根元素字体大小而言的大小【css3的单位】
vw:1%的视口宽度,视窗宽度1vw等于是窗宽度的1%。也就是说,如果当前开发浏览器或者设备的宽度为1280px,那么1vw就等于12.8px。在开发设计的过程中,100vw才能真正占据当前屏幕宽度的100%。【css3的单位】
答案:【1,NaN,NaN】
答案详解: https://zhuanlan.zhihu.com/p/457662291
const promise = new Promise((resolve,reject) => {
console.log(1);
setTimeout(() => {
console.log("timerStart");
resolve("success");
console.log("timerEnd");
}, 0);
console.log(2);
})
promise.then((res) => {
console.log(res);
});
console.log(4);
------------------------------------------------------------------------------
>>> 运行结果:1 2 4 "timerStart" "timerEnd" "success"
>>> 原因:
>Promise内部的代码仍然是同步执行代码,它的then和catch才会被推入微任务队列。
>因此输出1,将setTimeout推入宏任务队列,再输出2。
>接下来看到Promise.then,然而我们此时并不能将其推入微任务队列,它此时还是pedding状态,
>定义它的promise还并未执行resolve或者是reject,先不执行它。因此接下来输出的是4。
>接下来查看微任务队列,无,再查看宏任务,有,输出timerStart,将该promise状态改为resolved
>并将之前的promise.then推入微任务队列,输出timerEnd。
>该宏任务执行完毕。执行完一段宏任务后查看微任务队列,有,输出success。
浮点小数计算精度问题:
0.8 - 0.6 != 0.2 以及经典的 0.2 + 0.1 != 0.3
Promise.reject('err!!!')
.then((res) => {
console.log('success', res)
}, (err) => {
console.log('error', err)
})
.catch(err => {
console.log('catch', err)
})
>>> 运行结果:error err!!!
>>> 因为reject()函数表示失败的回调函数,会进入.then(),.then中的res接收的是resolve()成功后的回调函数返回的结果,err接收的是reject()失败后返回的结果。
>>>只有.then()中抛出问题才会到.catch()去执行
// 时间戳转日期 --- 为了方便,这里直接拿总毫秒数来作为时间戳处理
// 若使用总秒数时间戳,需要 timestamp = timestamp * 1000
function timestampToTime(timestamp){
var date = new Date(timestamp); // 时间戳对应的utc格式的日期,date是实例化对象
var Y = date.getFullYear(); // 年
var M = date.getMonth()+1; // 月
var D = date.getDate(); // 日
var h = date.getHours(); // 时
var m = date.getMinutes()>=10 ? date.getMinutes():"0"+date.getMinutes(); // 分
var s = date.getSeconds()>=10 ? date.getSeconds():"0"+date.getSeconds(); // 秒
return Y +"-"+M+"-"+D+" "+h+":"+m+":"+s;
}
var currentTime = timestampToTime(1605305889755);
console.log(currentTime);
1 // 获取元素
2 var div=document.getElementById("div1");
3 // 创建元素,给元素添加些文字
4 var p=document.createElement("p");
5 p.innerHTML="添加1";
6 var p1=document.createElement("p");
7 p1.innerHTML="添加2";
8 var p2=document.createElement("p");
9 p2.innerHTML="添加3";
10 // 添加到div中
11 div.appendChild(p); // 添加
12 div.insertBefore(p1,p); // 插入
13 div.insertBefore(p2,p1);
14 // 删除p1
15 div.removeChild(p1); // 删除
16 // 将p2替换为p
17 div.replaceChild(p,p2); // 替换
display:none
console.log(parseInt("X8X8")); // NaN
console.log(parseFloat("8")); // 8
console.log(parseInt("X8X8")+parseFloat("8")); // NaN
function b ( x , y , a ){
arguments [ 2 ] = 10 ;
alert ( 2 );
}
b ( 1 , 2 , 3 )
>>> 弹出:2
function b ( x , y , a ){
arguments [ 2 ] = 10 ;
alert ( a );
}
b ( 1 , 2 , 3 )
>>> 弹出:10
function b ( x , y , a ){
a = 10 ;
alert ( arguments [ 2 ]); // 10
}
b ( 1 , 2 , 3 )
>>> 弹出:10
答案解析:
function b ( x , y , a ){
console.log(arguments); // Arguments(3) [1, 2, 3]
arguments [ 2 ] = 10 ;
console.log(arguments); // Arguments(3) [1, 2, 10]
alert ( a );
}
b ( 1 , 2 , 3 )
// 1.创建xhr对象
var xhr = new XMLHttpRequest();
// 2.准备数据,准备发送请求
xhr.open('get','http://xxx.com')
// 发送请求
xhr.send(),
// 3.事件监听
xhr.onreadystatechange = function(){
// 4.监听前后端交互状态
if(xhr.readyState === "4"){ // 4代表请求完成了
if(res.state === "200"){
// 成功了,打印响应文本
console.log(xhr.responseText);
}else{
// 打印失败时的状态文本
console.log(xhr.statusText);
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
// xxx省略了一些meta标签代码
<title>用view实现一个下拉框</title>
// 引入vue资源
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 Vue模板-->
<div id="app">{{ message }}
<form>
<input type="text" :value="item">
<span @click="clickHandle">></span>
<div v-if="flag">
<ul v-for="(item,index) in data" @click="clickLiHandle(item)">
<li>{{ item }}</li>
</ul>
</div>
</form>
</div>
<script>
// 创建vue实例 1个Vue实例只能对应1个实例
var app = new Vue({
// el:元素 css的id选择器
el: '#app', // el用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
// data中用于存储数据,供el所指定的容器去使用
data: {
message: 'Hello Vue!',
data:['Jack','Lucky','Tom'],
flag: false,
item:''
},
methods:{
clickHandle(){
this.flag = this.flag == true ? false : true;
},
clickLiHandle(item){
console.log(item)
this.item = item;
console.log(this.item)
}
}
})
</script>
</body>
</html>
(function () {
var a = b = 10; // 相当于var a = 5; b = 5;
// 函数外部读不到a这个局部变量
})()
console.log(typeof a, b) // undefined 10
>>> 输出结果:undefined 10
var data = [ 8 , [ 3 ,[ [ 7 , 4 ], 5 ], 6 ]];
转换成一维数组答案:链接: https://jingyan.baidu.com/article/9158e0005b8dcfe35512287a.html
var nodes =document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', function(){
console.log(i); // 4 4 4 4
})
}
>>> 输出结果: 4 4 4 4
var fullname = ' 李雷 ';
var obj = {
fullname: ' 韩梅梅 ' ,
prop: {
fullname: ' 汤姆 ' ,
getFullname: function () {
return this.fullname;
}
}
}
console.log(obj.prop.getFullname()); // 汤姆
var test = obj.prop.getFullname; // 李雷
console.log(test());
>>> 汤姆 李雷
因为var有变量提升,let没有变量提升
var name = 'Tom' ;
( function () {
if ( typeof name == 'undefined' ) {
name = 'Jack' ;
console . log ( 'Goodbye ' + name );
} else {
console . log ( 'Hello ' + name );
}
})();
>>> 输出: Hello Tom
var name = 'Tom';
(function () {
if (typeof name == 'undefined') {
var name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
>>> 输出: Goodbye Jack
var name = 'Tom';
(function () {
if (typeof name == 'undefined') {
let name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name); // Hello Tom
}
})();
>>> 输出: Hello Tom
var a = [1, 2]
var b = [1, 2]
console.log(a + b); // 1,21,2
MVC框架中,View可以直接访问Model!View里会包含Model信息,还可能包括一些业务逻辑。
在MVC模型里,Model不依赖于View,但是 View是依赖于Model的。因为有一些
业务逻辑在View里实现了,导致要更改View也是比较困难的。
MVVM框架中,View的改变会导致Model的改变,Model的改变也可以导致View的改变,
它的逻辑是通过中间的ViewModel来实现双向数据绑定,ViewModel中可以监听数据的变化。
由于数据双向绑定,开发人员就不用一次次的操作DOM来更新视图了,只需要关注简单的业务逻辑。
Vue的MVVM模型的理解
MVVM模型(Model-view-viewModel)是一种软件架构模式。
Vue的模型是参考MVVM模型进行设计的。
M(Model)模型--- 对应data中的数据
V(View)视图 ---- 页面结构
VM(ViewModel)视图模型 --- Vue的实例化对象 --- 所以vue的实例对象用vm表示
【双向数据绑定】:数据的修改可以映射在视图上,视图的表单中修改数据能映射回data上
数据劫持监听数据的变化,配合订阅者和解析器来实现。主要是数据劫持比较重要,可能会问。
<script>
// Object.defineproperty()是ES6的内容 --- 作用拦截对象,为对象增加新的属性
var Person = {
name: 'Tom',
sex: 'boy'
}
// 通过它添加的属性,不能被遍历到
Object.defineProperty(Person,'age',{
value: 18,
enumerable: true, // 加上这个属性就可以被遍历到了,默认是false
writable: true, // age的值可以被修改,默认是false
configurable: true // 控制属性是否可以被删除,默认是false
})
console.log(Person); // {name: 'Tom', sex: 'boy', age: 18}
</script>
getter 和 setter
<script>
// Object.defineproperty()是ES6的内容 --- 作用拦截对象,为对象增加新的属性
// Person对象
var Person = {
name: 'Tom',
sex: 'boy'
}
let number = 22; // 初始化年龄
Object.defineProperty(Person,'age',{
//【getter】 当读取Person的age属性时,get函数(getter)会被调用,且返回值是age的值
get(){
console.log("有人读取了age属性")
return number;
},
// 【setter】当修改Person的age属性时,set函数(setter)会被调用,且会收到具体值
set(value){
console.log("有人修改了age属性,值是",value);
number = value;
}
})
console.log(Person);
</script>
Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
Vue中数据代理的好处:更方便操作data中的数据
基本原理:
通过Object.defineProperty()把data对象中所有属性和方法添加到vm上,
为每一个添加到vm上的属性,都指定一个getter和setter
在getter/setter内部操作(读/写)data中对应的属性
<script>
// 【数据代理的定义,在obj2中也要能读写obj1的内容】---通过obj2代理obj1
let obj1 = {x:100}
let obj2 = {y:200}
// obj2读写obj1中的x属性
Object.defineProperty(obj2,'x',{
get(){
return obj1.x
},
set(value){
obj1.x = value
}
})
</script>
vue中的vm实例化对象中有个属性 _data,这里面存放的是我们在data中定义是数据。数据代理就是把data中的数据放到了vm身上,通过vm操作数据的getter和setter,来实现双向绑定
父传子:props【单向数据流】【对象和数组的默认类型必须是工厂函数方式】
父组件
<!-- 父级中的数据传递 -->
<Child :message="message" :age="age" :list="list"/>
子组件
export default {
// 子级名字
name:"Child",
// 子级中用props读取父级传递过来的数据
// props:["message","age"]
props:{
message:{
type:String,
// required是必选项,不传会报警告
required:true
},
age:{
// type:Number,
// age可以既接收number又接收string
type:[Number,String],
// 当age没有传值的时候,页面默认显示age为0
default:0
},
// 数组和对象的默认值写法: default:[]这种写法错误 -> 工厂模式
// 数字和字符串可以直接写:default:0 default:""
list:{
type:Array,
default:function(){
return []
}
}
}
}
子传父:自定义事件 $emit (自定义事件)
子组件
<button @click="chickHandle">发送数据给parent</button>
methods:{
chickHandle(){
this.$emit("onEvent",this.message)
}
}
父组件
<Child2 @onEvent="messageHandle"/> // 实现子组件传递过来的事件
methods:{
// data为onEvent事件所传递过来的this.message就是参数
messageHandle(data){
this.child2Data = data;
}
},
参考博客:参考博客
1eventbus(适合小型项目) 2 provide inject(父传子) 3props(父传子) 4emit自定义事件(子传父) 5 vuex(大型项目)
vuex是一个综合管理程序中数据的一个插件,它的核心是一个store,其中数据存放在state中,被称为状态。用户触发页面事件时,通过dispatch分发给actions,actions执行完这个异步操作将结果拿到并通过commit提交给mutations,mutations中对数据进行修改,通过mutate让state中数据状态发生变更,state通过render重新渲染页面数据呈现给用户
主要属性:state mutations actions getters modules
state:存储项目中要用的数据
mutations:里面放同步操作,对数据进行修改
actions:里面放异步操作,通过context对象将获取到的异步数据commit提交给mutations进行修改
getters:相当于一个数据过滤器,对数据进行过滤后显示
module:为了防止主模块中state mutations ations getters等函数中内容过多,而划分不同的模块来写各个模块的state mutations actions getters,以及加入namespace命名空间,这样即使方法同名也互不影响。
v-if与v-else配合来控制切换
v-show来控制元素的显示隐藏
v-for用于循环创建标签等,语法的item in items
// src / directives / index.js
import Vue from 'vue'
// 使用v-focus来使用这个指令
// v-focus
Vue.directive('focus', {
// inserted是指令提供的,相当于指令的生命周期
// inserted自定义指令的钩子函数
// inserted函数表示当绑定了该指令的元素被插入到dom时候会自动触发
inserted(el){
console.log(el); // HTML元素对象--当前元素对象
el.focus();
}
})
// v-red
Vue.directive('red',{
inserted(el){
el.style.color = "#ff0000";
}
})
// main.js
import './directives' // 全局自定义指令,在所有组件中生效
// 其他组件中,直接以 “v-..” 形式使用
<input type="text" v-focus>
<p v-red>Hello</p>
const router = new VueRouter({
routes,
})
// 1 【全局前置导航守卫】 --- 所有的路由跳转都会触发这个函数
router.beforeEach((to, from, next) => {
// console.log(from); // 从哪个页面
console.log(to); // 跳转到哪个页面
// some来进行遍历
if(to.matched.some(record => record.meta.requiresAuth)){
// 需要判断用户是否登录
// 用户是否已经登录
//token为true代表用户已经登录了,token为false代表用户未登录,则要进入about页面会进入登录页面,登录之后才能进入
const token = true; // token之后再讲,这里先把数据写死
// if(token){
// next(); // 进入当前页面
// }else{
// // 如果用户未登录,那我们就进入login页面
// next("/login"); // 进入登录页面进行登录
// }
// 注释掉的if...else..这段,可以改成三目运算符实现,代码更优雅
token ? next() : next('/login');
}else{
next(); // 必须要调用,否则不跳转 // next中可以指定跳转到哪个页面中
}
})
// 2 【全局后置导航守卫】 --- 完成时触发 --没有next参数,是因为都已经跳转完成了
router.afterEach((to, from) => {
console.log(from);
console.log(to);
})
export default router
{
path: '/about/:id',
name: 'about',
component: () => import( '../views/AboutView.vue'),
// 3. 【路由独享的导航守卫】--- 只有进入about页面才会触发该导航守卫
beforeEnter: (to, from, next) => {
// console.log(from);
// console.log(to);
next(); // 不调用next会无法跳转
},
【组件中的钩子函数】
<script>
export default {
data(){
return{
message:"测试数据message"
}
},
beforeRouteEnter(to, from, next) {
// 因为当守卫执行前,组件实例还没被创建,所以没有this
// 不过,你可以通过传一个回调给 next来访问组件实例vm。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
// console.log("beforeRouteEnter内-this存在与否",this);
// console.log("beforeRouteEnter-from",from);
// console.log("beforeRouteEnter-to",to);
next(vm => {
// console.log(vm.message);
});
},
beforeRouteUpdate(to, from, next) {
// console.log("beforeRouteUpdate内-this存在与否",this);
// console.log("beforeRouteUpdate-from",from);
// console.log("beforeRouteUpdate-to",to);
next();
},
beforeRouteLeave(to, from, next) {
// console.log("beforeRouteLeave内-this存在与否",this);
// console.log("beforeRouteLeave-from",from);
// console.log("beforeRouteLeave-to",to);
next();
},
methods:{
// 参数发生变化时,触发的是beforeRouteUpdate
gotoSelfHandle(){
this.$router.push("/about/1002")
}
}
}
axios七大特点
1、在浏览器中发送 XMLHttpRequests 请求;
2、在 node.js 中发送 http请求;
3、基于 promise 的 HTTP 库,支持promise所有的API
4、拦截请求和响应;(修改请求数据,只能用’PUT’,'POST’和’PATCH’这几个请求方法)
5、转换请求和响应数据,响应回来的内容自动转换;
6、自动转换 JSON 数据;
7、客户端支持保护安全免受 XSRF 攻击;
当我们做一些查询或者翻页操作后,进行了路由的跳转,当我们返回到起始页面时发现,数据已经回到了初始状态。
解决方法:经过对比,决定将数据保存在sessionStorage中。
解决方法的原因:
1- 仅在当前会话下有效,浏览器被关闭或当前页面被关闭的情况下清除。存储的记录将不会对下次开启页面的查询或操作产生影响。
2- 使用简单,存取方便。
3- 只存在于客户端(浏览器)中,不参与和服务器的通信。
参考博客:参考博客点击跳转
还有就是可以通过keep-alive组件保持组件数据状态
代码简洁
引入CDN
懒加载,视频音频取消自动播放
使用按需加载
js放在底部引入,防止async阻塞
服务器启用gzip压缩
provide和inject
参考博客:参考博客
hash模式网址中有#,history模式需要后台做重定向,增加了服务器开发人员的工作量。
v-if是DOM元素的添加和删除,v-show是通过控制display:block、none;来进行显示 和隐藏。所以v-if切换开销较高。v-show渲染开销较高,若切换不频繁,可以使用v-if,若切换频繁就使用v-show。
pop/push/shift/unshift这些数组方法会更新原数组,而filter/concat/slice不会更新原数组,所以数组的更新需要重新对该数组进行赋值操作。
更新对象的话,需要用Vue.set() $set()两种方案
Vue.set(this.userInfo,'job','it') // 工作是it这个项加到userInfo对象中
this.$set(this.userInfo,'job','it')
v-bind:value=“msg” v-on:input=“事件名”
实现事件名的函数中,拿e.target.value来拿到表单中输入的值
计算属性computed: 本质上是一个属性,有setter和getter,第一次会调用执行,后面若依赖的数据不发生变化会直接读取第一次执行的结果返回,而不去执行内部代码。调用时不需要加()
侦听器 watch:监听data中某一个属性的变化,一般进行异步操作
methods:每次被调用都会被执行,调用需要加()
组件中包含一个代表html结构,代表js业务逻辑, 中代表CSS样式。style中的scoped代表当前样式的作用域只在当前组件生效,它的原理是会被编译成一些data-xxx的唯一标识,通过这些标识来指定样式的唯一性。
data()是函数的话,可以保证实例化时是相互独立的组件,不共享内存空间
而且代码具有可复用性
如果data是对象,那么共享的是一块内存空间,那么数据会在多个组件中共用,会污染全局环境
// A组件中(在A组件中引入并注册B组件)
<B组件名>
<div>我是A组件中的一段HTML结构</div>
</B组件名>
// B组件中接收这段结构
<slot>默认值</slot>
具名插槽
// A组件中(在A组件中引入并注册B组件)
<B组件名>
<template v-slot:header>
<div>我是A组件中的头部结构</div>
</template>
<!-- v-slot: 可以简写为 # -->
<template v-slot:footer>
<div>我是A组件中的底部结构</div>
</template>
</B组件名>
// B组件中接收这段结构
<slot name="header"></slot>
好处是两个插槽中间可以插入其他本组件自己的结构
<slot name="footer"></slot>
作用域插槽:通过插槽传值到父级进行显示
// A组件中(在A组件中引入并注册B组件)
<B组件名>
<template v-slot:header>
<div>我是A组件中的头部结构div>
template>
<template #footer="slotProps">
<div>底部测试:{{ slotProps.message }}div>
template>
B组件名>
// B组件中接收这段结构
<slot name="header">slot>
// message就是传过去的值
<slot name="footer" :message='message'>slot>
<p ref="container">Hello ref</p>
// 获得p节点
console.log(this.$refs.container);
登录流程:前端用表单收集用户输入的用户名和密码,将用户输入的用户名和密码提交给后台服务器端进行验证,后台验证成功的话,生成一个token令牌返回给前端,前端拿到token数据将其存放在localStorage中,并且配置路由进行页面跳转。
为什么要生成token返回?因为用户名和密码不能明文存储,容易被劫持造成数据泄漏,所以需要后台对数据进行处理,将处理后的数据返回前端。
nodejs中后台如何生成token字段?通过jwt鉴权(需要安装jsonwebtoken依赖)来生成。jwt中对用户名和相关信息(这些信息中不能放密码,密码不能对外暴露)进行加密处理,生成token字段。
vuex和本地localStorage中都要存储token,因为为了防止刷新浏览器造成数据丢失,如果只存在vuex中,那么刷新浏览器就会重新获取token,无法实现页面跳转。
参考博客点我跳转
退出登录:要清除本地存储的token; 要清除vuex中的存储的token
methods: {
...mapMutations("loginModule", ["clearToken"]),
logout() {
/**
* 1. 存储方式:存储在Vuex
* 2. 存储在本地localStorage
*/
// 1 清空vuex中的token
this.clearToken();
// 2 清空本地存储的token,localStorage中清空token令牌,代表用户退出登录了
localStorage.removeItem("token");
// 3 点退出就跳转到登录页
this.$router.push("/login");
},
}
相对于Vue2.0,Vue3.0重构了响应式系统,使用Proxy替换Object.,那现在,为什么要用Proxy替换掉Object.defineProperty?
我们先来看一下Object.defineProperty存在着哪些弊端:
1、Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。 push()pop()shift()unshift()splice()sort()reverse()
2、但尽管如此,由于只针对以上八中方法进行hack处理,所以其他数组的二属性也是检测不到,存在很大的局限性。Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue 2.x里,是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择。
3、相比之下,Proxy的优点就非常明显了:它可以劫持整个对象,并返回一个新的对象,有13种劫持操作,可直接监听数组类型的数据变化,监听的目标为对象本身,不需要像Object.defineProperty一样遍历每个属性,有一定的性能提升,直接实现对象属性的新增/删除,可以说非常nice了
主要有三个分支:开发环境分支 测试环境分支 生产环境分支
Link参考博客
Link参考博客
view 视图 --- 相当于div盒子
text 文本 --- 相当于span标签
button 按钮
block
image 图片 --- 图片默认有固定大小,需要通过设置mode来进行图片设置
video 视频 audio 音频
swiper 轮播图
scroll-view 滚动视图
icon 字体图标
progress 进度条
slider 滑块选择器
form表单
input输入框
textarea文本框
picker 底部弹起的选择器
slider 滑块选择器
switch 开关
navigator 路由跳转
carema 相机
map 可以使用腾讯地图插件
wx.navigateTo 有回退箭头
wx.redirectTo 会关闭当前页面
wx.reLaunch 关闭所有页面
wx.switchTab 可跳转底部导航tabbar页面
wx.showToast 消息提示框
wx.showToast(icon:"loading") 消息加载框
wx.showLoading() / wx.hideLoading() 加载框不会主动消失,而消息提示框会主动消失
wx.showModel() 模态对话框
wx.showActionSheet() 底部弹出框
wx.iploadFile() 上传文件 success回调函数中就可以显示上传成功提示
wx.request() --- request是小程序专门用来实现网络请求的解决方案,其中method可以指定请求方式get/post,有success和fail和complete这些回调函数。在complete这个回调函数代表网络请求完成无论成功或失败都会调用,所以在这个里面进行隐藏loading加载框。
// pages/allrequest/allrequest.js
Page({
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
wx.request({
url:'http://xxx.com/xxx/xxx/xxx.php',
// get请求的参数
data:{
// id=868987
id:"868987"
},
// 请求头
header:{
'content-type':'application/json'
},
timeout:10000,
// 若是post接口,method:"POST" --- 不写的话默认是get请求
method:"GET",
success(res){
console.log("网络请求成功了");
console.log(res.data);
},
fail(err){
console.log("网络请求失败了");
console.log(err);
},
complete(){
console.log("网络请求完成,无论网络请求成功、失败都会调用这个");
}
})
},
})
生命周期函数
onPullDownRefresh(){} // 实现下拉刷新
onReachBottom(){} // 实现上拉加载
上拉加载不能是替换数据,而是老数据concat合并新数据
数据缓存有同步和异步两种方案:异步无论数据是否保存成功,程序都会继续往下执行。而同步只有数据保存成功,才会执行后面的代码。
使用异步性能更好,使用同步数据更安全
【异步操作】
存储: wx.setStorage(){key:"键",data:"值",encrypt:true // 加密存储} 除非用户主动删除,否则数据一直存在
数据通常要用AES加密处理
读取: wx.getStorage(excrypt:true // 读取的时候也需要加密方式读取) 通过success回调函数来拿到数据
删除: wx.removeStorage()
清空: wx.clearStorage()
【同步操作】
存储: wx.setStorageSync()
数据通常要用AES加密处理
读取: wx.getStorageSync()
删除: wx.removeStorageSync()
清空: wx.clearStorageSync()
wx.onShareAppMessage() 转发小程序
wx.onShareTimeline() 分享到朋友圈
通过wx.login()来实现的,调用这个方法,会自动将code发送给服务器,服务器会结合appID和appSecret来一起发送给微信的服务器,微信的服务器将标识返回给服务器,服务器经过处理后返回给前端,前端将内存存储在本地,这样来实现数据持久化存储
view 视图
text 文本
image 图片
button 按钮
form 表单
input 输入框
video 视频
audio 音频
tabbar 底部导航
uni.request() 里面可以写请求的url地址和一些参数
tabbar 底部导航 — 最少2个,最多5个
uniapp的网络请求主要是uniapp内部提供的 uni.request()
网络请求的封装:通过promise对象来封装网络请求,这样就可以实现通过.then()来调用这种
走马灯 — 轮播图
面包屑
导航条