HTML、CSS 相关
1、 BFC
1、BFC 是什么?
BFC(Block Formatting Context) 格式化上下文; 指一个独立的渲染区域,或者说是一个隔离的独立容器;可以理解为一个独立的封闭空间。无论如何不会影响到它的外面
2、形成BFC的条件
1)浮动元素,float 除 none 以外的值
2)绝对定位元素,position(absolute, fixed)
3)display 为以下其中之一的值 : inline-block, table-cell,table-caption, flex
4)overflow 除了 visible 以外的值(hidden, auto, scroll)
5)body 根元素
3、BFC 的特性
1)内部的 Box 会在垂直方向上一个接一个的放置
2)垂直方向上的距离由 margin 决定
3)bfc 的区域不会与 float 的元素区域重叠。
4)计算 bfc 的高度时,浮动元素也参与计算
5)bfc 就是页面上的一个独立容器,容器里面的子元素不会影响外面的元素
2、padding 和 margin 有什么不同?
作用对象不同:
padding 是针对于自身的
margin 是作用于外部对象的
3、 vw 与百分比 有什么区别?
在父元素的宽度为 100% 的时候,vw 和 百分比没有区别。
在父元素的宽度为 50% 的时候,vw 不会有变化, 百分比会变成原来的一半。
本质就是: 百分比有继承关系,直接继承父元素的。vw 不会继承父元素的宽度,指针对浏览器原本的宽度。
4、行内元素和块级元素
行内元素 没有自己的宽高, 宽高由内容决定,没有继承关系。
块级元素,独立站一行,继承父级元素的宽度。
5、如何让谷歌浏览器支持小字体。
谷歌浏览器最小字体为 12px。
想要设置 12px 以下,可以使用 transform: scale(0.8) ; -webkit-transform: scale(0.8); 将字体缩放到原来的多少倍即可将字体变小。
js 相关
一、深浅拷贝
1、目标
(1) 什么是深浅拷贝
(2)实现的方式
(3)在vue 中的使用
2、前置知识
(1)js 的一般数据类型的存储
(2)js 的引用类型的数据存储
3、浅拷贝 深拷贝
(1)浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
(2)深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
4、针对引用类型来说 赋值 深拷贝 浅拷贝的区别
(1) 浅拷贝 赋值的区别
-
当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
-
浅拷贝 : 重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会互相影响。
-
深拷贝 : 从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。
和原数据是否指向同一对象 和原数据是否指向同一对象 第一层数据为一般数据类型 第一层数据不是原始数据 赋值 是 改变会使原始数据一同改变 改变会使原始数据一同改变 浅拷贝 否 改变不会使原始数据一同改变 改变会使原始数据一同改变 深拷贝 否 改变不会使原始数据一同改变 改变不会使原始数据一同改变
5、浅拷贝的实现方式
- Object.assign()
- loadsh 里面的 _.clone
- … 展开运算符
- Array.prototype.concat
- Array.prototype.slice
6、深拷贝的实现方式
- JSON.parse(JSON.stringify())
- 递归的操作
- loadsh 里面的 _.cloneDeep
- Jquery.extend()
- date: new RegExp(‘\w+’)
二、防抖节流函数
1、为什么要学习 js 的防抖节流函数
- 作为前端开发不得不知道的
- 面试得得时候是经常会问到的
- 是闭包的实际应用
2、防抖函数-- 固定时间内,将多次操作变成一次
当持续触发事件 一定时间内 没有再触发事件 事件处理函数才会执行一次
如果设定的时间到来之前 又一次触发了事件 就重新开始延时
触发事件 一段时间内 没有触发 事件执行 肯定是定时器
(在设定的时间内 又一次触发了事件 重新开始延时 代表的就是重新开始定时器)
(那么意味着上一次还没有结束的定时器要清除掉 重新开始)
let timer;
clearTimeout(timer);
timer = setTimeout(function(){
},delay)
// 防抖封装--闭包的应用
function antiShake(fn, wait) {
let timeOut = null;
return args => {
if(timeOut) clearTimeout(timeOut);
timeOut = setTimeout(fn,wait);
}
}
3、 实际的应用
使用 echarts 时, 改变浏览器宽度的时候,希望重新渲染
echarts 的图像,可以使用此函数,提升性能。(虽然 echarts 里有自带的 resize 函数)
典型的案例就是输入搜索:输入结束后 n 秒才进行搜索请求, n 秒内又输入的内容,就重新计时。
解决搜索的 bug
//2000
//js 的异步操作
function thro(func, wait) {
// 会执行你点击了 多少次 就会执行多少次
// 这不是我们想要的 我们想要的是 比如说 时间是一秒 然后 哪怕你手速再快 一秒内你点了 100次 也只会执行一次
let timerOut;
return function () {
if(!timerOut) {
timerOut = setTimeOut(function(){
func()
timerOut = null;
},wait)
}
}
}
function handle() {
console.log(Math.random())
}
document.getElementById("test").onclick = thor(handle,2000)
4、节流函数-- 一定时间内的多个变成一个
应用场景: 提交表单、高频的监听事件
function throttle(event, time) {
let timer = null;
return function() {
if(!timer) {
timer = setTimeout(() => {
event();
timer = null;
},time)
}
}
}
三、js 作用域
1、作用域说明:一般理解指一个变量的作用范围
1、全局作用域
(1) 全局作用域在页面打开的时候被创建,页面关闭时被销毁
(2)编写在 script 标签中的变量和函数,作用域为全局,在页面的任意位置都可以访问到
(3)在全局作用域中有全局对象 window,代表一个浏览器窗口,由浏览器创建,可以直接调用
(4)全局作用域中声明的变量和函数会作为 window 对象的属性和方法保存
2、函数作用域
(1)调用 函数时,函数作用域被创建,函数执行完毕,函数作用域被销毁
(2)每调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的
(3)在函数作用域中可以访问到全局作用域的变量,在函数外无法访问到函数作用域内的变量
(4)在函数作用域中访问到变量、函数时,会先在自身作用域中寻找,若没有找到,则会到函数的上一级作用域中寻找,一直到全局作用域
2、作用域的深层次理解
执行期的上下文
-
当函数代码执行的前期,会创建一个执行期上下文的内部对象 AO(作用域)
-
这个内部的对象是预编译的时候创建出来的 因为当函数被调用的时候 会先进行预编译
-
在全局代码执行的前期会创建一个执行期的上下文的对象 GO
延展内容:
1、函数作用域预编译
(1)创建 ao 对象 AO{}
(2) 找形参和变量声明 将 变量和 形参名 当作 AO 对象的属性名 值为 undefined
(3)实参形参相统一
(4)在函数体里面找函数声明 值赋予函数体
2、全局作用域的预编译
(1)创建 GO 对象
(2)找变量声明 将变量名作为 GO 对象的属性名 值是 undefined
3、预编译习题
function fn(a,c) {
console.log(a);
var a = 123;
console.log(a);
console.log(c);
function a() {};
if(!false) {
var d = 678;
}
console.log(d);
console.log(b);
var b = function (){};
console.log(b);
function c() {};
console.log(c);
}
fn(1,2);
// 预编译
// 作用域的创建阶段 == 预编译的阶段
// 预编译的时候做了哪些事情
// 1、创建了 ao 对象 2、找形参和变量的声明,作为 ao 对象的属性名,值是 undefined 3、实参和形参相统一 4、找函数声明 会覆盖变量的声明
AO:{
a: undefined 1
c: undefined 2
d: undefined
b: undefined
}
四、图片懒加载
let doms = document.getElementsByTagName("img");
let num = doms.length;
let img = doms;
let n = 0;//存储图片加载到的位置,避免每次都从第一张图片开始遍历
let isLoding = false;// 是否当前容器/页面里的图片加载完成
let _clientHeight = document.documentElement.clientHeight;//可见区域高度
let _scrollTop = document.documentElement.scrollTop || document.body.scrollTop;// 滚动条距离顶部的高度
//监听 窗口变化重新计算可视区域
function computedClientHeight() {
_clientHeight = document.documentElement.clientHeight;//可见区域高度
}
//页面载入完毕加载可视区域内的图片
lazyLoad();
function lazyLoad() {
//获取滚动条距离顶部高度
isLoadImg = n >= num;
_scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
for(let i = n; i < num; i++) {
if(img[i].offsetTop < _clientHeight + _scrollTop) {
if(img[i].getAttribute('src') == '') {
img[i].src = img[i].getAttribute('data-src');
}
n = i+1;
}
}
}
五、Promise
1、Promise.all 是支持链式调用的,本质上就是返回了一个 Promise 实例,通过 resolve 和 reject 来改变实例状态
Promise.myAll = function(promiseArr) {
return new Promise((resolve,reject) => {
const ans = [];
let index = 0;
for(let i = 0; i < promiseArr.length; i++) {
promiseArr[i].then(res => {
ans[i] = res;
index++;
if(index === promiseArr.length) {
resovle(ans)
}
})
.catch(err => reject(err))
}
})
}
2、Promise.race
Promise.race = function(promiseArr) {
return new Promise((resolve,reject) => {
// 如果不是 Promise实例需要转化为 Promise 实例
Promise.resolve(p).then(
val => resolve(val),
err => reject(err)
)
})
}
六、跨域、发送请求相关
1、jsonp 解决跨域问题
script 标签不遵循同源协议,可以用来进行跨域请求,优点就是兼容性好但仅限于 GET 请求。
实现:
const jsonp = ({url,params,callbackName}) => {
const generateUrl = ()=>{
let dataSrc = '';
for(let key in params) {
if(Object.prototype.hasOwnProperty.call(params,key)) {
dataSrc += `${key}=${params[key]}&`;
}
}
}
return new Promise((resolve, reject) => {
const scriptEle = document.createElement("script");
scriptEle.src = generateUrl;
document.body.appendChild(scriptEle);
window[callbackName] = data => {
resolve(data);
document.removeChild(scriptEle);
}
})
}
2、封装一个 Promise 请求
1)new 一个 XMLHttpRequest 对象
2)open 方法
3)onreadystatechange 方法
4)send 方法
const getJson = function(url) {
return new Promise((resolve,reject) => {
const xhr = XMLHttpRequest ? new XMLHttRequest() : new ActiveXObject('Mscrosoft.XMLHttp');
xhr.open("GET",url,false);
xhr.setRequestHeader("Accept",'application/json');
xhr.onreadystatechange = function() {
if(xhr.readyState !== 4) return;
if(xhr.status === 200 || xhr.status === 304) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.responseText));
}
}
xhr.send();
})
}
3、vue 中解决跨域
1、浏览器的同源策略:
就是两个页面具有相同的 : 协议(protocol)、主机(host)、端口号(port)
2、请求一个接口时,出现 Access-Control-Allow-Origin 等就说明请求跨域了
3、vue 中解决跨域: 配置 vue.config.js 文件,如果没有就自行新建一个
原理:
1)将域名发送给本地的服务器(localhost:8080)
2)再由本地的服务器去请求真正的服务器
3)因为请求是从服务端发出的,所以就不存在跨域问题了
注意: 修改 vue.config.js 文件 需要重启服务
七、ES6 相关面试题
1、事件循环机制
1、 js 中的异步操作 比如 fetch setTimeout setInterval 压入到调用栈中的时候里面的消息会进去到消息队列中去,消息队列中 会等到调用栈清空之后再执行
2、Promise async await 的异步操作的时候会加入到微任务中去,会在调用栈清空的时候立即执行调用栈中加入的微任务会立马执行
// 构造函数同步执行
const promist = new Promise((resolve,reject) => {
console.log(1);
resolve();
console.log(2);
})
// .then 方法是 异步执行的
promise.then(() => {
console.log(3);
})
console.log(4);
//输出结果: 1,2,4,3
let p = new Promise(resolve => {
console.log(4)
resolve(5)
})
function func1() {
console.log(1)
}
function func2() {
setTimeout(()=>{
console.log(2)
},0)
func1();
console.log(3);
p.then(resolve => {
console.log(resolve);
})
}
func2()
//4 1 3 5 2
2、 null 和 undefined
console.log(typeOf null);//表示为 “无” 对象 ,0
console.log(typeOf undefined) // 表示为 “无” 的原始值, NaN
undefined 出现的情况:
1、已声明,为赋值
2、对象某个属性不存在
3、函数调用,少了参数
4、函数的默认返回值
null 出现的情况:
1、手动释放内存
2、作为函数的参数(此参数不是对象)
3、原型链的顶端
3、 ES6 – filter、forEach 与map、递归
1、filter 方法: 不会影响原数组,会返回一个新数组
let arr = [1,3,4,5,89];
// 1、current =》当前值 2、index =》 当前值的下标 3、这个数组对象
let arr2 = arr.filter((current, index, array) => {
return current < 10;
})
console.log(arr);
console.log(arr2)
2、forEach 与map
1) forEach 的特点:
没有返回值;
不能用 break 打断;
遍历的是 value
2)map 的特点:
有返回值(数组),默认 return 是undefined;
接受的参数是一个函数(key,value);
不能用 break 打断.
let arr = ['a','b'];
let res = arr.map((value,key) => {
return value + "1";
})
console.log(res)
3、js 递归求和 1–100
function add(num1, num2) {
let num = num1 + num2;
if(num2 + 1 >100) {
return num;
} else {
return add(num, num2 + 1)
}
}
add(1, 2)
4、如何实现函数柯里化
定义: 一个函数,可以接收多个参数,反复被调用
fn(1,2,3)(6,7)
特点:
1、 不固定参数个数的函数
2、第一次执行 返回函数
3、后续执行 返回函数
4、缓存参数
5、 内部函数 闭包 二次缓存函数
// 闭包的应用场景: 避免全局变量命名冲突
// 第一种实现
function curring() {
// arguments 伪数组,具备数组的属性,但是不具备数组的方法
const args = Array.prototype.slice.call(arguments)
// const args1 = [].__proto__.slice.call(arguments);
// Array.prototype.slice 是一个原型上的方法
// call 改变 this 的指向
// 数组方法依赖于内部是 this 数据容器来执行
console.log(args);
// 一个函数访问外部变量 形成闭包
const inner = function inner() {
args.push(...arguments)
return inner
}
inner.getValue = function (){
console.log(args);
return args.reduce((res,ele)=>{
return res + ele;
},0)
}
return inner
}
const res = curring(1,2,3,4,5)(6,7,8)
res.getValue();
// 可以允许将返回值,进行二次计算。
// 第二种实现
function curring() {
// arguments 伪数组,具备数组的属性,但是不具备数组的方法
const args = Array.prototype.slice.call(arguments)
// const args1 = [].__proto__.slice.call(arguments);
// Array.prototype.slice 是一个原型上的方法
// call 改变 this 的指向
// 数组方法依赖于内部是 this 数据容器来执行
console.log(args);
// 一个函数访问外部变量 形成闭包
const inner = function inner() {
args.push(...arguments)
return inner
}
inner.__proto__[Symbol.toPrimitive] = inner.toString = inner.getValue = function (){
console.log(args);
return args.reduce((res,ele)=>{
return res + ele;
},0)
}
return inner
}
const res = curring(1,2,3,4,5)(6,7,8)
console.log(res - 1);
5、如何实现 ajax 重连机制
需求: 能实现ajax 请求,自动在失败的时候重新连接,重试次数可以传递,延迟时间
const rate = 0.5;
function request() {
return new Promise((resolve,reject) => {
setTimeout(()=>{
// 随机数 计算 来实现失败与 成功
let res = Math.random(); //0~~~0.9999之间
if(res > rate) {
resolve(res);//成功
} else {
reject(res);
}
},rate)
})
}
function retry(func, times=0, delay=0) {
return new Promise((resolve, reject) => {
//func 是一 事件,将它封装起来,,才能后续 times -- 时调用
let inner = async function inner() {
try{
const result = await func();
resolve(result)
} catch(e){
if(times-- <= 0) {
//彻底失败
reject(e)
} else {
//延迟执行
setTimeout(()=>{
inner();//再次尝试
},delay)
}
}
}
inner();
});
}
//函数设计
retry(request, 3, 2000).then(res => {
console.log(res,'成功');
}).catch(e => {
console.log(e,'失败')
})
八、大量数据的处理
1、 渲染大数据时,合理使用 createDocumentFragment 和 requestAnimationFrame ,将操作切分为 一小段一小段执行
setTimeout(() => {
// 插入十万条数据
const total = 100000;
// 一次插入的数据
const once = 20;
// 插入数据需要的次数
const loopCount = Math.ceil(total / once);
let countOfRender = 0;
const ul = document.querySelector("ul");
fucntion add() {
const fragment = document,createDocumentFragment();
for(let i = 0; i < once ;i++) {
const li = document.createElement("li");
li.innerText = Math.floor(Math.random() * total);
fragment.appendChild(li);
}
ul.appendChild(fragment);
countOfRender += 1;
loop();
}
fucntion loop(){
if(countOfRender < loopCount) {
window.requestAnimationFrame(add);
}
}
loop();
},0)
2、 下拉加载功能的实现
原理就是监听页面滚动事件,分析 clientHeight、scrollTop、scrollHeight 三者的属性关系
window.addEventListener("scroll",function(){
const clientHeight = document.documentElement.clientHeight;
const scrollTop = document.documentElement.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
if(clientHeight + scrollTop >= scrollHeight) {
//检测到滚动至页面底部,进行后续操作
}
}, false)
3、过滤出页面中所有的 dom 节点
注意点: DOM 操作返回的是类数组,需要转为数组之后才可以调用数组的方法。
const fn = () =>{
return [...new Set([...document.querySelectorAll("*")].map(el=> el.tagName))].length;
}
九、原型与原型链
原型 prototype ==> 函数特有
原型链 proto ==> 大家都有的
从当前实例属性去查找,如果找到了就返回,否则就顺着原型链一层一层往上面找,直到找到 null 为止,如果找到 null 都没有找到,则报错。
如果是他自身就有的 属性(私有属性),hasOwnProperty () 方法可以判断。
1、构造函数-- 创建实例对象
每一个 js 对象(除了 null)都只有一个 --proto-- 的原型对象。
原型链继承 是使用 构造函数来实现的。
1)创建一个 空对象
2)将 this 指向原型对象
function Person() {
this.name = 'paipai';
this.age = 18;
}
let p = new Person();
console.log(p);// 打印 Person 的对象
console.log(p.__proto__ === Person.prototype) // true
十、如何理解 this 指向? new 关键字执行过程
参考: 一般情况下,大部分人会认为 this 指向就是 谁调用 this就指向谁。并且在js 中也确实是 谁调用 this 就指向谁,但是这个东西也分不同场景,如果是普通函数,this 就是指向window。但是箭头函数是没有 this 的,所以 this 是 在调用的时候 确认。还需要却分 this 是根据调用方式来确认的。
十一、改变 this 的指向,手写 call,bind,apply
1、call 方法
1)首先是所有函数都可以调用
2)他有 N 个参数 至少有一个
3)第一个参数就是我么想要去改变的 this , 从第二个开始就是函数本身的参数
4) call 还有返回值,返回值就是函数的函数值
let newCall = function(obj) {
// 当前函数 newCall 的 this 是谁?
// console.log(this) ?? 需要去改变 this 指向的函数
// console.log(obj) ?? 需要去指向的 this
// console.log(arguments) // 函数中的伪数组(具备数组的属性,不具备 数组的方法)
let arr = [];
for(let index = 1; index < arguments.length; index++) {
const element = arguments[index];
arr.push(element);
}
obj.fun = this;
let res = obj.fun(...arr);
delete obj.fun
return res;
}
Function.prototype.newCall = newCall
let obj = {
name:"小明"
}
function getInfo(age,vip) {
return {
name: this.name,
age: age,
vip: vip
}
}
console.log(getInfo.newCall(obj, 20, 'vip'))
2、apply
跟call 方法差不多,唯一的区别就是只有两个参数,并且 第二个参数必须是数组
let newApply = function(obj) {
// 当前函数 newCall 的 this 是谁?
// console.log(this) ?? 需要去改变 this 指向的函数
// console.log(obj) ?? 需要去指向的 this
// console.log(arguments) // 函数中的伪数组(具备数组的属性,不具备 数组的方法)
let arr = [];
for(let index = 1; index < arguments[1].length; index++) {
const element = arguments[index];
arr.push(element);
}
obj.fun = this;
let res = obj.fun(...arr);
delete obj.fun
return res;
}
Function.prototype.newApply = newApply
let obj = {
name:"小明"
}
function getInfo(age,vip) {
return {
name: this.name,
age: age,
vip: vip
}
}
console.log(getInfo.newApply(obj, [20, 'vip']))
3、bind 方法
1) 他有 N 个参数 至少有1个
2)返回一个函数 并且只有调用该返回函数的时候才可以拿到 getInfo 的返回值
3)返回一个函数可以作为构造函数使用 作为构造函数使用的时候 this 就失效了
拓展: 构造函数的 new 是指向实例的。
let obj = {
name:"小明"
}
function getInfo(age,vip) {
return {
name: this.name,
age: age,
vip: vip
}
}
let newBind = function(obj) {
// 只能会只能拿到一部分参数
let _this = this,
arr = Array.slice.call(arguments, 1);
let newFun = function() {
// 也只能会只能拿到一部分参数
// 调用时 拿到 当前函数 getInfo 的返回值
let arr2 = Array.slice.call(arguments);
let list = arr.concat(arr2);
if(this instancef newFun) {
return _this.newCall(this, ...list);
} else {
return _this.newCall(obj, ...list);
}
}
return newFun;
}
Function.prototype.newBind = newBind
let func = getInfo.newBind(obj,18);
console.log(new func("vip"));
console.log(func("vip"));
十二、 es6 Proxy 的理解
1、proxy 代理的方式
1、get(target, prop, receiver) : 拦截对象属性的访问。
2、set(target, prop, value, receiver) : 拦截对象属性的设置,最后返回一个布尔值。
3、apply(target, object, args) : 用于拦截函数的调用,比如 proxy()。
4、construct(target, args) : 方法用于拦截 new 操作符, 比如 new proxy()。 为了使 new 操作符在生成的 Proxy 对象上生效,用于初始化代理的目标对象自身必须具有【[Construct]】内部方法(即 new target 必须使有效的)。
5、has(target, prop) : 拦截例如 prop in proxy 的操作,返回一个布尔值
6、deleteProperty(target, prop) :拦截 delete proxy[prop] 的操作,返回一个布尔值。
7、ownKeys(target) : 拦截 Object.getOwnPropertyNames(proxy) 、Object.keys(proxy)、 for in 循环等操作,最终会返回一个数组。
2、使用 Proxy 和 Reflect 实现双向数据绑定
<input type='text' id='input' />
<h2>您输入的内容使:<i id='txt'></i> </h2>
<script>
// 获取dom 元素
let oInput = document.getElementById("input");
let oTxt = document.getElementById("txt");
// 初始化代理对象
let obj = {};
// Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更容易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
// 给obj 增加代理对象
let newProxy = new Proxy(obj, {
get:(target, key, receiver) => {
//console.log("get:",key);
return Reflect.get(target, key, receiver)
},
set:(target, key, value, receiver) => {
// 监听 newProxy 是否有新的变化
if(key === 'text') {
oTxt.innerHTML = value;
}
return Reflect.set(target, key, value, receiver)
},
})
// 监听 input 输入事件
oInput.addEventListener("keyup",e =>{
// 修改代理 对象的值
newProxy.text = e.target.value
})
</script>
defineProperty 无法一次性监听对象所有属性,必须遍历或者递归
十三、闭包
1、递归中的闭包
1、什么是递归? 函数直接或间接调用自己的函数
2、什么是闭包? 父函数里包含子函数;子函数被 return 到父函数之外,这个子函数就是闭包
3、下面这样的写法,每次 return 的,都是一个全新的函数
return {
fn: fucntion(){
// 函数体
}
}
2、js单例模式 – 实现登录(闭包的应用)
// js 的单例模式,实现登录
let createLogin = function(a,b,c) {
console.log(a,b,c);
let div = document.createElement("div");
div.innerHTML ='我是登录的弹窗';
div.style.display = 'none';
document.body.appendChild(div);
return div;
}
let getSingle = function(fn) {
let result;
return function() {
return result || (result = fn.apply(this, arguments));
}
}
let create = getSingle(createLogin);
document.getElementById("loginBtn").onclick = function() {
let loginLay = create(1,2,3);
loginLay.style.display = 'block';
}
//es6 的实现 单例模式
class Person {
constructor(name, sex) {
this.name = name;
this.sex = sex;
}
say() {
console.log("学前端很开心")
}
}
let person1 = new Person("星空", "女");
console.log(person1.name)
person1.say();
console.log(person1.say === Person.prototype.say)
十四、手写 代码
1、手写 map
let arr = [1,2,3];
let array = arr.map((item,index) => {
return item * 2;
})
console.log(array) // 2,4,6
function map(arr, mapCallback) {
//检查参数是否正确
if(!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') {
return [];
} else {
// 进行下面的操作
let result = [];
for(let i = 0, len = arr.length; i < len; i++) {
result.push(mapCallback(arr[i],i,arr));
}
return result;
}
}
let res = map(arr, item => {
return item * 2;
})
console.log(res)
十五、零散知识点
1、let 与 var
Vue 相关1(Vue2)
1、 MVC 和MVVM
2、 v-model 原理
1、双向数据绑定
Object.defineProperty() 用来操作对象,也可以用来劫持对象。
3、 data 为什么是一个函数
1、闭包 =》 每一个组件都有自己的私有作用域,确保各组件的数据不会互相干扰
2、纯对象 =》 干扰 let obj = {};
4、 v-if 和 v-show
1、v-if : 不满足条件,不会渲染 dom 单次判断
2、v-show : 隐藏 dom =》 多次切换,(不能用于权限操作,因为可能会被更改)
5、虚拟 Dom 详解
1、虚拟 Dom 是什么?
vue 2.x 版本才有虚拟 dom
本质是 js 对象 --》跨平台
2、虚拟 Dom 在 Vue 中做了什么
vue 的渲染过程 (html、css、JavaScript)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GrRuM08L-1663812263247)(C:\Users\Lq\AppData\Roaming\Typora\typora-user-images\image-20220825093753243.png)]
1) 将真是 dom 转化为 虚拟 dom(js 对象)
2)更新的时候做对比
3、 虚拟 Dom 是如何提升 Vue 的渲染效率的?
1)局部更新(节点数据)
2)将直接操作 dom 的地方拿到两个 js 对象之中去做比较
6、 Diff 中的 patch()
初始化
当前的目标元素
7、 Vue 响应式原理
最简单的响应式实现:发布订阅模型+ 数据劫持
响应式核心: 1、数据联动(双向绑定) 2、需要捕获到修改
let Dep = {
clientList: {},// 容器
// 添加订阅
listen: function(key, fn) {
//短路表达式
(this.clientList[key] || (this.clientList[key] = [])).push(fn);
},
// 发布
trigger: function() {
let key = Array.prototype.shift.call(arguments),
fns = this.clientList[key];
if(!fn || fn.length === 0) {
return false;
}
for(let i = 0, fn; fn = fn[i++];) {
fn.appy(this, arguments)
}
}
}
// 数据劫持
let dataHi = function(data, tag, datakey, selector) {
let value = '',
el = document.querySelector(selector);
Object.defineProperty(data, datakey,{
get: function (){
return value;
},
set: function(val) {
value = val;
Dep.trigger(tag, val)
}
})
//订阅
Dep.listen(tag,function(text) {
el.innerHTML = text
})
}
// 使用
let obj = {};
dataHi({
data: obj,
tag: 'view-1',
datakey: 'one',
selector: '.box-1'
})
// 1、 初始化赋值
obj.one = '这是视图1'
//2、劫持数据:更新者负责重新渲染 N 次
8、 Vue3 响应式原理
9、其他
1、 $nexTick()
当 dom 更新之后延迟回调
2、Vue-router 与 location.href 有什么区别?
location.href : 简单方便,刷新页面
Vue-router: 实现了按需加载,减少了 dom 消耗
Vue-router:js 原生 history
10、实现观测数据侦听器(Observer)
// 简单实现对 属性的一个监听
let obj = {}
let val = 20;
Object.defineProperty(obj,'age',{
get(){
console.log(`age属性被读取${val}`);
return val;
},
set(newVal) {
console.log(`age属性被修改了,${newVal}`);
val = newVal;
}
})
// 使用 Observer 变化侦测
export class Observer {
constructor(value) {
this.value = value;
if(Array.isArray(value)) {
// 数组的逻辑
} else {
// 对象的逻辑
this.walk(value)
}
}
walk(obj) {
const keys = Object.keys(obj);
for(let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]);
}
}
}
// 循环 让对象的每一个属性都变成可观测的
function defineReactive(obj, key, val) {
if(arguments.length === 2) {
val = obj[key]; // 对象的某个值
}
if(typeOf val === 'object') {
// 递归
new Observer(val);
}
Object.defineProperty(obj, key,{
enumerable: true, // 可枚举(可被循环)
configurable: true,// 可改变
get() {
console.log(`${key}属性被读取了`);
return val
},
set(newVal) {
console.log(`${key} 属性被修改了,新值是${newVal}`);
val = newVal;
}
})
}
//使用
import {Observer} from './observer.js';
let obj = new Observer({
name: 'xiaoxiao',
age: 18,
demo:{
a:'123'
}
})
console.log(obj.value.name);
obj.value.age = 20
console.log(obj.value.demo.a)
11、 nextTick 在哪里使用?原理是?
1、nextTick 功能是批处理,多次调用默认会将逻辑暂存到队列中,稍后同步代码执行完毕后会采用,同步的方式依次刷新队列(nextTick 本身内部采用了异步方法,但是执行逻辑的时候采用的是同步)
2、内部实现原理(异步的实现原理 先采用 promise.then 不行再采用 mutationObserver 不行再采用 setImmediate 不行再采用 setTimeout 优雅降级)
补充:
语法:this.$nextTick(回调函数)
作用: 在下一次 DOM 更新结束后执行其指定的回调
什么时候用? 当改变数据后,要基于更新后的新 DOM进行某些操作时,要在 nextTick 所指定的回调函数中执行。
12、computed 和 watch 的区别
1、这两个内部都是基于 watcher 的,区别是 computed 数据可以用于页面渲染,wathc 不行,computed 只有在取值时才会执行对应的回调(lazy 为 true 所以不会立即执行)
watch 默认会执行一次(拿到老的值)。computed 用了一个 dirty 属性实现了缓存机制,多次取值如果依赖的值不发生变化不会更改 dirty 的结果,拿到的就是 老的值
13、Vue 中的事件修饰符
1、prevent :阻止默认事件(常用)
2、stop: 阻止事件冒泡(常用)
3、once :事件只触发一次(常用)
4、capture: 使用事件的捕获模式
5、self : 只有 event.target 是当前操作的元素时才触发事件
6、passive : 事件的默认行为立即执行,无需等待事件回调执行完毕
14、 Vue 封装的过度与动画
1、作用: 在插入、更新或移除 DOM 元素时,在合适的时候给元素添加样式类名
2、写法:
1)准备好样式:
元素进入的样式: 【1】v-enter: 进入的起点 【2】v-enter-active: 进入的过程 【3】v-enter-to :进入的终点
元素离开的样式:【1】v-leave :离开的起点 【2】 v-leave-active :离开的过程中 【3】v-leave-to:离开的终点
2)使用 包裹要过度的元素,并配置 name 属性:
<transition name='hello'>
<h1 v-show='isShow'>你好呀</h1>
</transition>
3、备注:若有多个元素需要过度,则需要使用: ,且每个元素都要指定 key 值。
15、插槽-默认插槽、具名插槽、作用域插槽
1、作用域插槽
1)理解: 数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games 数据在 Category 组件中,但使用数据所遍历出来的结构由 App 组件决定)
// 子组件
<template>
<div>
<div>
{{title}}分类
</div>
<slot :games='games'> 我是默认的一些内容 </slot>
</div>
</template>
<script>
export default {
name: "Category",
props: ['title'],
data() {
return {
games:['小红帽','超级玛丽','穿越火线']
}
}
}
</script>
// 父组件
<Catergory>
<template slot-scope='{games}'>
<h4 v-for='(ite,index) in games' :key='index'>
{{ite}}
</h4>
</template>
</Catergory>
16、一些基础知识点
1、组件的定义: 实现应用中 局部 功能 代码 和资源的 集合
2、组件:用来实现局部(特定)功能效果的代码集合(html/css/js/image。。。)【理解】。一个界面的功能很复杂【为什么】。 复用编码,简化项目编码,提高运行效率【作用】
3、模块:向外提供特定功能的 js 程序,一般就是一个 js 文件【理解】。js 文件很多且很复杂【为什么】。复用 js ,简化 js 的编写,提高 js 运行效率【作用】。
4、模块化: 当应用中的 js 都以模块来编写的时候,那这个应用就是一个模块化的应用
5、组件化: 当应用中的功能都是多组件的方式来编写的时候,那这个应用就是一个组件化的应用
6、不同版本的 Vue:
1) vue.js 与 vue.runtime.xxx.js 的区别: 【1】 vue.js是完整版的 vue,包含: 核心功能+模板解析器 【2】vue.runtime.xxx.js 是运行版的 vue,只包含核心功能,没有模板解析器
2)因为 vue.runtime.xxx.js 没有模板解析器,所以不能使用 template 配置项,需要使用 render 函数接收到的 createElement 函数去指定具体内容
7、关于 VueComponet
1)school 组件本质是一个名为 VueComponent 的构造函数,且不是程序员定义的,是 Vue.extend 生成的。
2)我们只需要写 或者非闭合标签,Vue 解析时会帮我们创建 School 组件的实例对象,即 Vue 版我们执行的 : new VueComponent(options)
3)特别注意: 每次调用 Vue.extend 。返回的都是一个全新的 VueComponet !!!
4)关于 this 指向: 【1】组件配置中: data 函数、methods 中的函数、watch 中的函数、computed 中的函数,它们的 this 是 VueComponent实例对象。 【2】 new Vue() 配置中: data 函数、methods 中的函数、watch 中的函数、computed 中的函数,它们的this 指向是 Vue 实例对象。
5)VueComponent 的实例对象,以后简称 vc (也可称之为:组件实例对象)。Vue 的实例对象,以后简称为 vm
8、mixin(混入)
// 功能:可以把各个组件共用的配置提取成一个混入对象
// 使用方式:
// 第一步定义混合,例如:
{
data(){.....},
methods:{....}
....
}
// 第二步使用混入,例如:
(1)全局混入: Vue.mixin(xxx)
(2)局部混入: mixins:['xxx']
9、插件
功能:用于增强 Vue
// 本质: 包含 install 方法的一个对象,install 的第一个参数是 Vue,第二个以后的参数是插件使用者传递的数据
// 定义插件
对象.install = function(Vue, optiosn) {
//1、添加全局过滤器
Vue.filter(...)
// 2、添加全局指令
Vue.directive(...)
// 3、添加实例方法
Vue.prototype.$myMethods = function(){...}
}
使用 插件: Vue.use()
10、scoped 样式
作用: 让样式在局部生效、防止冲突
11、 ref 属性
1)被用来给元素或子组件注册引用信息(id 的替代者)
2)应用在 html 标签上获取的是 真是 DOM 元素,应用在标签上是组件实例对象(vc)
12、 props 配置项
1)功能: 让组件接收外部传过来的数据
2)传递数据:
- 接收数据:
【1】第一种方式(只接收):props:[‘name’] 。
【2】第二种方式(限制类型): props:[name:String]
13、消息订阅与发布(pubsub-js)
1)一种组件间通信的方式,适用于 任意组件间通信
2)使用步骤
//1、安装 pubsub: npm i pubsub-js
//2、引入 : import pubsub from "pubsub-js"
//3、接收数据:A组件想接收数据,则在 A组件中订阅消息,订阅的回调留在 A组件自身
methods(){
demo(data){...}
}
...
mounted() {
this.pid = pubsub.subscribe("xxx",this.demo)// 订阅消息
}
// 4、提供数据: pubsub.publish("xxx",数据)
// 5、最好在 beforeDestroy 钩子中,用 pubsub.unsubscribe(pid) 去取消订阅
14、全局事件总线
// 1)一种组件间通信的方式,适用于 任意组件间通信
// 2) 安装全局事件总线
new Vue({
....
beforeCreate(){
Vue.prototype.$bus = this;// 安装全局事件总现,$bus 就是当前应用的 vm
}
})
// 3)使用事件总线
// 1、接收数据: A组件想要接收数据,则在A组件中给 $bus 绑定自定义事件,事件的回调留在 A组件自身
methods(){
demo(data){...}
}
...
mounted() {
this.$bus.$on('xxx',this.demo)
}
// 2、提供数据: this.$bus.$emit('xxx',数据)
// 4)最好在 beforeDestory 钩子中,用 $off 去解绑当前组件所用到的事件。
Vue 项目优化相关
1、 一行 代码 彻底优化项目性能
1、对于大数据量的列表,如果不需要操作,只需要显示的,就使用 Object.freeze 冻结数据即可。
Object.freeze 冻结无需该百年的数据,避免框架继续遍历其属性,完成响应式(页面和内存的同步)
Object.isFrozen() 用于判断是否冻结数据。
2、代码片段: 虚拟的元素 – 框架作为代码片段来蒙骗框架, 在页面中成为注释 的库(vue-fragment)
对于路由组件的根组件,切换时 出于本身时代码片段,所以无法使用 parent.removeChild , insertBefore 等方法
可以使用在子组件内部(路由根组件有问题)
3、200个组件:
首屏卡顿 ==》懒加载;
常用组件切换时卡顿 ==》缓存常用组件;
keep-alive -随着用户使用的时间,不关闭浏览器会卡 ;
动态缓存
2、DOM、CSS 层面深层优化及 理论思想
3、详解大型项目路由优化方案(解决性能瓶颈)
4、业务需求面试
1、重置表单的优化方案
Object.assign(this.$data, this.$options.data());
Vue相关2(Vue3)
1、Vue3 带来了什么?
1、性能的提升
- 打包大小减少 41%
- 初次渲染快 55%,更新渲染快 133%
- 内存减少 54%
2、新的特性
1、Composition API(组合API)
1)setup 配置
2)ref 和 reactive
3)watch 与 watchEffect
4)provde 与 inject
2、新的内置组件
Fragment、Teleport、Suspense
3、其他改变
1)新的生命周期钩子
2)data 选项应始终被声明为一个函数
3)移除 keyCode 支持作为 v-on 的修饰符
3、拉开序幕的 setup
1、理解: Vue3.0 中一个新的配置项,值为一个函数
2、setup 是所有 Composition API(组合API)“表演的舞台”
3、组件中所用到的: 数据、方法等等,均要配置在 setup 中。
4、setup 函数的两种返回值:
【1】若返回一个对象,则对象中的属性、方法,在
Vuex
1、Vuex 概述
1、使用 Vuex 统一管理状态的好处
1)能够在 vuex 中集中管理共享的数据,易于开发和后期维护
2)能够高效地实现组件之间的数据共享,提高开发效率
3)存储在 vuex 中的数据都是响应式的,能够实时保持数据与页面的同步
补充: Vuex 是 专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 Vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间统新的方式,且适用于任意组件间通信
2、什么样的数据适合存储到 Vuex 中?
一般情况下,只有组件之间共享的数据,才有必要 储存到 vuex 中,对于组件中的私有数据,依旧存储在组件自身的 data 中即可。
补充: 多个组件依赖同一状态;来自不同组件的行为需要变更同一状态
2、 Vuex 的基本使用
1、安装 Vuex 的依赖包 :
npm install vuex --save
2、导入 vuex 包:
inport Vuex from 'vuex';
Vue.use(Vuex);
3、创建 store 对象
const store = new Vuex,Store({
// state 中存放的就是全局共享的数据。
state: {
count: 0
}
})
3、Vuex 的核心概念
1、主要核心概念如下: State、Mutation、Action、Getter
2、State
State 提供唯一的公共数据源,所有共享的数据都要统一放到 Store 的State 中进行存储。
// 创建 store 数据源,提供唯一公共数据
const store = new Vuex.Store({
state:{
count:0
}
})
1)组件访问 State 中数据的第一种方式:
this.$store.state.全局数据名称
2)组件访问 State 中数据的第二种方式:
// 1、从 vuex 钟按需导入 mapState 函数
import {mapState} from "vuex";
//通过刚才导入的mapState 函数,将当前组件需要的全局数据,映射为当前组件的 computed 计算属性:
//2、将全局数据,映射为当前组件的计算属性
computed: {
...mapState(['count'])
}
3、Mutation
Mutation 用于变更 Store 中的数据
1)只能通过 mutation 变更 Store 数据,不可以直接操作 Store 中的数据。
2)通过这种方式虽然操作起来稍微一些,但是可以集中监控所有数据的变化
// 定义 Mutation
const store = new Vuex.Store({
state:{
count:0
},
mutations: {
add(state){
//变更状态
state.count ++;
}
}
})
// 触发mutation
methos:{
handle() {
// 触发 mutations 的第一种方式
this.$store.commit('add')
}
}
// this.$store.commit('add') 是触发 mutations 的第一种方式
// 触发 mutations 的第二种方式:
// 1、从 vuex 中按需导入 mapMutations 函数
import {mapMutations} from 'vuex';
// 2、将指定的 mutation 函数,映射为当前组件的 methods 函数
methos:{
...mapMutatiosn(['add','addN'])
}
4、Action
// 定义 action
const store = new Vuex.Store({
state:{
count:0
},
mutations: {
add(state){
//变更状态
state.count ++;
}
},
actions: {
addAsync(context){
//在 actions 中,不能直接修改 state 中的数据;
// 必须通过 context,commit 触发某个 mutation 才行
setTimeout(()=>{
context.commit('add')
},1000)
}
}
})
// 触发
methos:{
handler() {
// 这里的 diapatch 函数,专门用来触发 action,
this.$store.dispatch('addAsync')
}
}
5、 Getter — 类似于组件中的计算属性
1)组件访问 getters中数据的第一种方式:
this.$store.getters.名称
2)组件访问 getters中数据的第二种方式:
// 1、从 vuex 钟按需导入 mapGetters 函数
import {mapGetters} from "vuex";
//通过刚才导入的mapState 函数,将当前组件需要的全局数据,映射为当前组件的 computed 计算属性:
//2、将全局数据,映射为当前组件的计算属性
computed: {
...mapGetters(['showNum'])
}
6、模块化+命名空间
1)目的:让代码更好维护,让多种数据分类更加明确
2)修改 store.js
const countAbout = {
namespaced: true,// 开启命名空间
state:{ x:1 },
mutations: {....},
actions: {...},
getters: {
bigSum(state) {
return state.sum * 10;
}
}
}
const personAbout = {
namespaced: true,// 开启命名空间
state:{ x:1 },
mutations: {....},
actions: {...},
getters: {
bigSum(state) {
return state.sum * 10;
}
}
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
})
3)开启命名空间后,读取数据
1、组件中读取 state 数据
// 方式一:自己直接读取
this.$store.state.personAbout.list
// 方式二: 借助 mapState 读取
...mapState('personAbout',['sum','school','subject'])
2、组件中读取 getters 数据
// 方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
// 方式二: 借助 mapGetters 读取
...mapGetters('personAbout',['sum','school','subject'])
3、组件中调用 dispatch
// 方式一:自己直接读取
this.$store.dispatch['personAbout/jiaWait',person]
// 方式二: 借助 mapActions 读取
...mapActions('personAbout',{incrementWait: "jiaWait"})
4、组件中调用 commit
// 方式一:自己直接读取
this.$store.commit['personAbout/ADD_PERSON',person]
// 方式二: 借助 mapMutations 读取
...mapMutations('personAbout',{incrementWait: "jiaWait"})
vue-router 路由
1、vue-router 的理解
vue 的一个插件库,专门用来实现 SPA 应用
2、对 SPA 应用的理解
1、单页 Web 应用(single page web application, SPA)
2、整个应用只有一个完整的页面
3、点击页面中的导航链接不会刷新页面,只会做页面的局部更新
4、数据需要通过 ajax 请求获取
3、路由的理解
1、什么是路由?
一个路由就是一组映射关系(key-value)
key 为路劲, value 可能是 function 或 component
4、缓存路由组件
1、作用: 让不展示的路由组件保持挂载,不被销毁
2、具体编码:
<keep-alive include='news'>
<router-view> </router-view>
</keep-alive>
5、两个新的生命周期钩子
1、作用: 路由组件所独有的两个钩子,用于捕获路由组件的激活状态
2、具体名字
1) activated 路由组件被激活时触发
2)deactivated 路由组件失活时触发
6、路由守卫–保护路由的安全(权限)
1、作用:对路由进行权限控制
2、分类: 全局守卫、独享守卫、组件内守卫
3、全局守卫:
//1、全局前置路由守卫--初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,form,next) => {
// 判断当前路由是否需要进行权限控制
if(to.meta.isAuth) {
if(有权限) {// 权限控制的具体规则
next()
} else {
alert("暂无访问权限")
}
} else {
next();
}
})
// 全局后置路由守卫,初始化时执行、每次路由切换后执行
router.after((to,from) =>{
if(to.meta.title) {
document.title = to.meta.title// 修改网页的 title
} else {
document.title = 'vue'// 默认名称
}
})
4、独享守卫:
beforeEnter(to, form, next) {
// 判断当前路由是否需要进行权限控制
if(to.meta.isAuth) {
if(有权限) {// 权限控制的具体规则
next()
} else {
alert("暂无访问权限")
}
} else {
next();
}s
}
5、组件内守卫:
// 通过路由规则,进入该组件时被调用-- 跟 mounted 同级
beforeRouteEnter(to, from, next) {
console.log("离开组件--",to, from);
next();
}
// 通过路由规则,离开该组件时 被调用
beforeRouteLeave(to, from, next) {
console.log("离开组件--",to, from);
next();
}
6、路由器的两种工作模式
1)对于一个 url 来说,什么时 hash 值? — #及其后面的内容就是 hash 值
2)hash 值不会包含 在 HTTP 请求中,即: hash 值不会带给服务器
3)hash 模式:
【1】地址中永远带着 # 号,不美观
【2】若以后将地址通过第三方手机 app 分享,若 app 校验严格,则地址会被标记为不合法
【3】兼容性好
4)history 模式:
【1】地址干净、美观
【2】兼容性 和 hash 模式相比略差
【3】应用部署上线时需要后端人员支持,解决刷新页面服务端 404 的问题
Mutations(‘personAbout’,{incrementWait: “jiaWait”})
# vue-router 路由
### 1、vue-router 的理解
vue 的一个插件库,专门用来实现 SPA 应用
### 2、对 SPA 应用的理解
1、单页 Web 应用(single page web application, SPA)
2、整个应用只有一个完整的页面
3、点击页面中的导航链接不会刷新页面,只会做页面的局部更新
4、数据需要通过 ajax 请求获取
### 3、路由的理解
1、什么是路由?
一个路由就是一组映射关系(key-value)
key 为路劲, value 可能是 function 或 component
### 4、缓存路由组件
1、作用: 让不展示的路由组件保持挂载,不被销毁
2、具体编码:
```javascript
<keep-alive include='news'>
<router-view> </router-view>
</keep-alive>
5、两个新的生命周期钩子
1、作用: 路由组件所独有的两个钩子,用于捕获路由组件的激活状态
2、具体名字
1) activated 路由组件被激活时触发
2)deactivated 路由组件失活时触发
6、路由守卫–保护路由的安全(权限)
1、作用:对路由进行权限控制
2、分类: 全局守卫、独享守卫、组件内守卫
3、全局守卫:
//1、全局前置路由守卫--初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,form,next) => {
// 判断当前路由是否需要进行权限控制
if(to.meta.isAuth) {
if(有权限) {// 权限控制的具体规则
next()
} else {
alert("暂无访问权限")
}
} else {
next();
}
})
// 全局后置路由守卫,初始化时执行、每次路由切换后执行
router.after((to,from) =>{
if(to.meta.title) {
document.title = to.meta.title// 修改网页的 title
} else {
document.title = 'vue'// 默认名称
}
})
4、独享守卫:
beforeEnter(to, form, next) {
// 判断当前路由是否需要进行权限控制
if(to.meta.isAuth) {
if(有权限) {// 权限控制的具体规则
next()
} else {
alert("暂无访问权限")
}
} else {
next();
}s
}
5、组件内守卫:
// 通过路由规则,进入该组件时被调用-- 跟 mounted 同级
beforeRouteEnter(to, from, next) {
console.log("离开组件--",to, from);
next();
}
// 通过路由规则,离开该组件时 被调用
beforeRouteLeave(to, from, next) {
console.log("离开组件--",to, from);
next();
}
6、路由器的两种工作模式
1)对于一个 url 来说,什么时 hash 值? — #及其后面的内容就是 hash 值
2)hash 值不会包含 在 HTTP 请求中,即: hash 值不会带给服务器
3)hash 模式:
【1】地址中永远带着 # 号,不美观
【2】若以后将地址通过第三方手机 app 分享,若 app 校验严格,则地址会被标记为不合法
【3】兼容性好
4)history 模式:
【1】地址干净、美观
【2】兼容性 和 hash 模式相比略差
【3】应用部署上线时需要后端人员支持,解决刷新页面服务端 404 的问题