前端知识点

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+’)

7、手写深拷贝

// 标准的深拷贝 =》 引用数据类型(数组,对象)
function deepClone(source) {// [] => Array(基类) {}=>{}const targetObj = sorce.contructor === Array ? [] : {};for(let keys in source) {if(source.hasOwnProperty(keys)) {if(source[keys] && typeof source[keys] === 'object') {targetObj[keys] = source[keys].constructor === Array ? [] : {};//递归targetObj[keys] = deepClone(source[keys]);} else {//基本数据类型,直接赋值targetObj[keys] = source[keys];}}}return targetObj;
}

二、防抖节流函数

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(active object) 对象 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 1c: undefined 2d: 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;}}
}//---------- 分隔线,下面是第二种写法 ------------------------
let doms = document.getElementsByTagName("img");
let num = doms.length;
let img = doms;
let n = 0;// 避免重复加载lazyLoad();
window.onscroll = lazyLoad;function lazyLoad() {let seeHeight = document.documentElement.clientHeight; // 可见区域let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;for(let i = n; i < num; i++) {if(img[i].offsetTop < seeHeight + 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 是根据调用方式来确认的。

let j = {a:10,b:{a:12,fn:function (){consosle.log(this.a)}}
}
j.b.fn();// 12   this指向它自身的上一级var id = 66;
function fn5() {// 箭头函数没有作用域, 没有自己的this。 this 指向外层setTimeout(()=>{console.log(this.id)}, 500)
}
fn5({id:22})//  指向widow,打印 66
fn5.call({id:22})// 改变this

new 关键字执行的时候发生了什么?

默认情况下,函数的返回值是 undefined。

在构造函数中,默认返回新创建的对象。

function Person(){}
let person1 = new Person();//1、创建一个 空 对象 let obj = new Object();// Object => 基类
//2、设置它的原型链
obj.__proto__ = Person.prototype;
//3、改变 this 的指向
let result = Person.call(obj)
//4、判断返回值类型
if(typeof (result) == 'object') {person1 = result;
} else {person1 = obj;
}
// 面试题:obj 和 obj2 有什么区别?
let obj = Object.create(null);
let obj2 = {};
console.log(obj,obj2)
// 参考: obj 没有原型链,Object.create创建的对象是一个很纯净的对象,不存在原型链 ——proto--。obj2有原型链。
// 应用场景:如果只需要调用值,不需要调用方法,使用 Object.create 执行效率会更高。

十一、改变 this 的指向,手写 call,bind,apply

bind() 不调用 ,只改变 this 指向

call,apply 改变之后,并且执行一次

apply 传数组, call 传对象

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.funreturn res;
}
Function.prototype.newCall = newCalllet 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.funreturn res;
}
Function.prototype.newApply = newApplylet 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 = newBindlet 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 的,都是一个全新的函数

4、为什么要有闭包? 1)避免变量被污染。2) 私有化。3)保存变量,常驻内存

return {fn: fucntion(){// 函数体}
}
// 闭包应用--- 处理私有数据
let makeCounter = function() {let privateCounter = 0;function changeBy(val) {privateCounter += val;}return {increment: function() {changeBy(1);},decrement: function(){changeBy(-1);},value: function(){return privateCounter;}}
} 
// 使用暴露出来函数,如果使用私有变量则会报错

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 = [123]let array = arr.map((item,index) => {return item * 2;
})
console.log(array) // 2,4,6function 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

var :

1、具有声明提升。

2、不存在局部作用域(无块级作用域)。

3、声明覆盖。(后面声明的变量 会覆盖前面的同名变量)

let 没有声明提升。具有局部作用域。不允许声明同名变量。

2、原生 JS 实现事件委托

什么是事件委托?

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类的所有事件

let ul = document.getElementById("ul");
ul.onclick = function(event) {console.log(event);event = event || window.event;let target = event.target;if(target.nodeName == 'LI') {alert(target.innerHTML);//委托的事情}
}
let btn = document.getElementById("btn");
btn.onclick = functon (){let li = document.createElement("li");li.textContent = ul.children.length;ul.appendChild(li)
}

3、匿名自执行函数 — 底层封装框架

1、自执行 =》 单例模式

2、防止变量污染

(function(window){// 获取节点window.$ = jquery = function(nodeSelector) {// 存放东西let nodes = {};if(typeof nodeSelector === 'string') {// let temp = document.querySelectorAll(nodeSelector);for(let i = 0; i < temp.length; i++) {nodes[i] = temp[i];}//类数组(具备数组的属性,不具备数组的方法)nodes.length = temp.length;} else {throw new Error("必须输入字符串!")}// 添加方法nodes.addClass = function(classes) {let className = classes.split(" ");//循环 class 类名className.forEach(value => {for(let i =0; i < nodes.length; i++) {// 循环节点,给 节点添加样式类名nodes[i].classList.add(value);// .classList 是原生 js 的属性}})}//修改 textnodes.setText = function(text) {for(let i 0; i < nodes.length; i++) {nodes[i].textContent = text;//  原生js修改 }}return nodes;}
})(window)// 使用

浏览器相关

1、在浏览器中输入 URL 按下回车会发生什么?

https://www.baidu.com

URL: 统一资源定位符,俗称 网址

URL 只是IP 的一个映射

https : 传输协(http 和 TCP 之间加了一层 TSL 或者 SSL)

www: 万维网,可以看作 服务器

baidu.com: 域名

  1. 1 第一次访问

1.2 第二次访问: 将域名解析的IP存在本地=》读取浏览器缓存

  1. 解析 url

DNS 域名系统匹配真实的 IP

ping www.baidu.com ( 测试连接)

  1. 根据真实的 IP 建立一个连接 (TCP 的三次握手)

  2. 拿数据,渲染页面

  3. 四次挥手(断开连接)

    渲染页面的过程:
    客户端拿到数据以后,并行构建 dom 树和css 结构体,生成渲染树,计算布局信息,调用UI引擎渲染 ,最后生成用户所见页面

    IP 和域名之间的关系:

    1、ip地址和域名是一对多的关系,一个ip地址可以有多个域名,但是相反,一个域名只能有一个ip地址;
    2、ip地址是数字型的,为了方便记忆,才有了域名,通过域名地址就能找到ip地址;
    3、ip,全称为互联网协议地址,是指ip地址,意思是分配给用户上网使用的网络协议的设备的数字标签;
    4、常用的ip地址分为IPv4和IPv6两大类;

2、从哪些点做性能优化?

a、加载(优化方向): 1)减少 http 请求(精灵图,将多张图合并成 一个图。文件的合并);2)减少文件大小(资源压缩,图片压缩,代码压缩);3)CDN (第三方库,大文件,大图);4)SSR服务端渲染,预渲染;5)懒加载;6)分包(小程序分包就是为了加快首页的加载)

b、减少 dom 操作,避免回流,文档碎片

3、哪些是性能优化?

1、页面加载性能(加载时间,用户体验)

2、动画与操作性能(是否流畅无卡顿)

大片的dom操作,首选 translate 和定位。

3、内存占用(内存占用过大,浏览器崩掉等)

4、电量消耗(游戏方面,可以暂不考虑)

4、

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)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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. 接收数据:

【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 的问题

调用 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、具体编码:```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 的问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/39449.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

贪心+后缀和,CF 1903C - Theofanis‘ Nightmare

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1903C - Theofanis Nightmare 二、解题报告 1、思路分析 我们任意一种分组其实都是若干个后缀和相加 比如我们分成了三组&#xff0c;第一组的数被加了一次&#xff0c;第二组的数被加了两次&#xff0c;第…

JDK动态代理-AOP编程

AOPTest.java&#xff0c;相当于main函数&#xff0c;经过代理工厂出来的Hello类对象就不一样了&#xff0c;这是Proxy.newProxyInstance返回的对象&#xff0c;会hello.addUser会替换为invoke函数&#xff0c;比如这里的hello.addUser("sun", "13434");会…

Web3 ETF 的软件开发框架

Web3 ETF 的软件开发框架主要包含以下几个方面&#xff0c;需要说明的是&#xff0c;Web3 ETF 仍处于早期发展阶段&#xff0c;相关技术和标准尚未成熟。在开发 Web3 ETF 时&#xff0c;需要谨慎评估风险&#xff0c;并做好安全防范措施。北京木奇移动技术有限公司&#xff0c;…

基于python的随机森林回归预测+贝叶斯优化超参数前后训练效果对比

目录 1.导入必要的库 2.导入数据与数据预处理 3.查看数据分布 4.特征选择 5.模型建立与训练 6.训练集预测结果 7.模型评估 8.预测新数据 9.贝叶斯优化超参数 1.导入必要的库 # 导入所需的库 from sklearn.model_selection import cross_val_score import pandas as …

【SkiaSharp绘图14】SKCanvas方法详解(三)URL注释、按顶点绘制、 是否裁切区域之外、旋转、缩放、倾斜、平移、保存/恢复画布

文章目录 SKCanvas方法DrawUrlAnnotation 绘制URL注释DrawVertices 按顶点绘制Flush 立即绘制QuickReject 判断区域是否在裁切区域之外ResetMatrix重置矩阵Restore、RestoreToCountRotateDegrees按角度旋转画布RotateRadians按弧度旋转画布SaveLayer保存并新建图层Scale 缩放画…

Python协作运动机器人刚体力学解耦模型

&#x1f3af;要点 &#x1f3af;腿式或固定式机器人模型 | &#x1f3af;网格、点云和体素网格碰撞检测 | &#x1f3af;正反向运动学和动力学 | &#x1f3af;机器人刚体力学计算 | &#x1f3af;编辑参考系姿势和路径 | &#x1f3af;软件接口实体机器人模拟 | &#x1f3a…

分子AI预测赛Task2笔记

下面所述比较官方的内容都来自官方文档 ‍‌⁠‌‍​​​‌​​⁠​​​​​&#xfeff;​​​&#xfeff;‍‬​​‍⁠‍‍​​‬​&#xfeff;‌​​​‌‍‬​​​​​​‍‌Task2&#xff1a;赛题深入解析 - 飞书云文档 (feishu.cn) 赛题背景 强调了人工智能在科研领域&…

苹果电脑废纸篓数据被清空了,有什么方法可以恢复吗?

使用电脑的用户都知道&#xff0c;被删除的文件一般都会经过回收站&#xff0c;想要恢复它直接点击“还原”就可以恢复到原始位置。mac电脑同理也是这样&#xff0c;但是“回收站”在mac电脑显示为“废纸篓”。 苹果电脑废纸篓数据被清空了&#xff0c;有什么方法可以恢复吗&am…

java反射和注解

反射 获取class对象的三种方法 ①&#xff1a;Class.forName("全类名"); ②&#xff1a;类名.class ③&#xff1a;对象.getclass(); 代码样例 package com.ithema;public class Main {public static void main(String[] args) throws ClassNotFoundException {//第…

基于Canvas的Html5多时区动态时钟实战

目录 前言 一、关于Canvas技术 1、Canvas是什么 2、Canvas的属性及渲染特性 二、Canvas动态多时区展示 1、新建html页面 2、创建Canvas对象 3、绘制所有的时钟 总结 前言 出差旅行相信大家一定会住酒店&#xff0c;大家在酒店的前台进行预订的时候&#xff0c;是不是都…

centos执行yum相关命令报错的可能原因

文章目录 1. 执行yum命令是报下面一大帕拉2. 安装某个包报错&#xff0c;找不到这个包 1. 执行yum命令是报下面一大帕拉 最后一行报错&#xff0c;在repo文件中找不到空baseurl&#xff1a;xxx / x86_64 执行这行命令把这个找不到的 xxx 禁掉即可sudo yum-config-manager --di…

【项目日记(三)】搜索引擎-搜索模块

❣博主主页: 33的博客❣ ▶️文章专栏分类:项目日记◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多项目内容 目录 1.前言2.项目回顾3.搜索流程3.1分词3.2触发3.3去重3.4排序3.5包装 4.总结 1.前言 在前…

虚拟机USB——解决每次插U盘都得选择连接到主机还是虚拟机问题

虚拟机USB——解决每次插U盘都得选择连接到主机还是虚拟机问题 1.编辑–>首选项–> 2.如果想每次插U盘都连接到主机就选“将设备连接到主机” 如果想每次插U盘都进行选择&#xff0c;就选择“询问要执行的操作”

在 Mac 上使用 本地 LLM 文本终结

我们可使用本地大型语言模型&#xff0c;如Mistral、Llama等&#xff0c;来给文本做总结&#xff0c;相比在线的 Kimi &#xff0c;ChatGPT&#xff0c; 我们不用担心数据泄露&#xff0c;因为整个操作都是在本地电脑完成的。 我们用 ollama 举例 首先安装 ollama https://ol…

数组-二分查找

二分查找 leetcode704 /*** param {number[]} nums* param {number} target* return {number}*/ var search function(nums, target) {let left 0, right nums.length - 1;while (left < right) {const mid Math.floor((right - left) / 2) left;const num nums[mid]…

SpringSecurity中文文档(体系结构).md

体系结构&#xff08;Architecture&#xff09; 本节讨论基于 Servlet 的应用程序中 Spring Security 的高级体系结构。我们将在参考文献的身份验证、授权和防止利用部分中构建这种高层次的理解。 过滤器的综述 &#xff08;A Review of Filters&#xff09; Spring Securit…

实现WebSocket聊天室功能

实现WebSocket聊天室功能 什么是WebSocket&#xff1f;WebSocket的工作原理服务器端实现客户端实现 在现代Web开发中&#xff0c;实时通信已经变得越来越重要。传统的HTTP协议由于其无状态和单向通信的特点&#xff0c;无法很好地满足实时通信的需求。而WebSocket协议则应运而生…

【讨论C++继承】

讨论C继承 继承定义继承方式和访问限定符 基类和派生类的赋值转换继承中的作用域派生类的默认成员函数继承和友元继承和静态成员菱形继承虚拟继承 继承是面向对象程序设计中&#xff0c;使代码可以复用的重要手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展。 继…

【ONLYOFFICE】| 桌面编辑器从0-1使用初体验

目录 一. &#x1f981; 写在前面二. &#x1f981; 在线使用感受2.1 创建 ONLYOFFICE 账号2.2 编辑pdf文档2.3 pdf直接创建表格 三. &#x1f981; 写在最后 一. &#x1f981; 写在前面 所谓桌面编辑器就是一种用于编辑文本、图像、视频等多种自媒体的软件工具&#xff0c;具…

算法训练营day24--93.复原IP地址 +78.子集 +90.子集II

一、93.复原IP地址 题目链接&#xff1a;https://leetcode.cn/problems/restore-ip-addresses/ 文章讲解&#xff1a;https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1fA4y1o715 1.1 初…