一. css相关
1. flex布局
-
1行排列,多个换行
- 1
- 2
- 3
- 4
-
垂直水平居中
文本内容
-
左边固定大小,右边自适应
文本内容
右边盒子
2. 第三方字体的引用
@font-face {
font-family: 'JDbold';
src: url('jdfont/JDLangZhengTi_Bold.TTF');
font-weight: normal;
font-style: normal;
}
3. BFC
BFC块级格式化上下文,是一个完全独立的空间,让空间里的子元素不会影响到外面的布局。
触发BFC:
- overflow:hidden
- display:inline-block
- position:absolute
- display:flex
BFC解决了什么问题 - 使用flort脱离文档流,高度塌陷,可以给父元素触发BFC ;
- margin边距重叠,为其中一个元素包裹一个盒子形成一个独立空间
- 两栏布局,其中一个盒子浮动后会压住另一个盒子,为另一个盒子设置BFC,display:flex
二. js相关
3. 原型与原型链
原型: 创建Class类,有prototype属性,称为显示属性; 当你实例一个对象的时候,有proto属性称为隐士原型
原型链:
4. call、apply 、bind的区别
- call、apply、bind 这三个函数的第一个参数都是 this 的指向对象;
- call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔;
- apply 的所有参数都必须放在一个数组里面传进去 ;
- bind 返回值是函数,它 的参数和 call 一样。
obj.myFun.call(db,'成都','上海'); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.apply(db,['成都','上海']); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.bind(db,'成都','上海')();
5. async/await
- 用同步的方式执行异步操作
- await 操作符只能在异步函数 async function 内部使用。如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果,也就是说它会阻塞后面的代码,等待 Promise 对象结果。如果等待的不是 Promise 对象,则返回该值本身。
- async声明function是一个异步函数,返回一个promise对象,可以使用 then 方法添加回调函数。async函数内部return语句返回的值,会成为then方法回调函数的参数。
async getCountByLevel(type, index) {
const res = await mgopHttp("mgop.jd.bzgf.countByLevel", APP_KEY, {
stLevel: type,
});
this.list[index].number = res.data;
},
8. 宏任务,微任务
先执行微任务,后执行宏任务
宏任务:script(整体代码),setTimeout,setInterval,dom事件,ajax请求
微任务: Promise.then,Object.observe,async/await
微任务>dom渲染>宏任务
9. Event Loop 轮循事件
- 同步代码 一行一行放到调用栈先执行
- 遇到异步, 会先记录下,放到webApis里,等待时机
- 时机到了,webApi代码就会移动到callback Queue 回调队列
- 如果同步代码执行完毕,Event Loop开始工作
- 轮循查找,如有则移动到调用栈执行 ,一直轮循查找
10. 函数节流、防抖
- 节流: 控制事件发生的频率,控制为1s发生或1分钟发生一次,达到控制流动次数的效果,例如红绿灯
场景:
1.scroll 事件,滚动监听事件,每隔一秒计算一次位置信息等;
2.浏览器播放事件,每隔一秒计算一次进度信息等;
3.input 框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (也可做防抖)
function throttle (f, wait) {
let timer
return (...args) => {
if (timer) { return }
timer = setTimeout(() => {
f(...args)
timer = null
}, wait)
}
}
// test
function testFn(e, content) {
console.log(e, content);
}
let _testFn= throttle(testFn, 1000); // 节流函数
document.onmousemove = function (e) {
_testFn(e, 'throttle'); // 给节流函数传参
}
- 防抖:防止抖动,把一次事件误执行多次,影响性能,比如敲键盘就是一个每天都会接触到的防抖操作。
场景:
1.登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
2.调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
3.mousemove、mouseover鼠标移动事件防抖
4.搜索框搜索输入。只需用户最后一次输入完,再发送请求防抖
function debounce (fn, wait=1000) {
let timer,
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn(...args);
}, wait)
}
}
// test
function testDebounce(e, content) {
console.log(e, content);
}
var testDebounceFn = debounce(testDebounce, 1000); // 防抖函数
document.onmousemove = function (e) {
testDebounceFn(e, 'debounce'); // 给防抖函数传参
}
\
- 相同点
1.都是通过setTimeout实现的
2.目的都是降低回调频率,节省计算资源 - 不同点
1.函数防抖连续操作的事件只执行一次,而函数节流则侧重于一段时间内只执行一次。
2.节流:控制流量,单位时间内事件只能触发一次。代码实现timer=timeout; timer=null。节流可以比作过红绿灯,每等一个红灯时间就可以过一批。
3.防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零clearTimeout。防抖可以比作等电梯,只要有一个人进来,就需要再等一会儿。业务场景有避免登录按钮多次点击的重复提交。
9. 数组去重方法
- new Set方法 [...new Set()] 、Array.from() 讲set格式转为数组
var arr = [1,3,4,5,1,23,4,5]
function unique(arr){
return new Set(arr) //Set(5) {1, 3, 4, 5, 23} //new Set返回set数据格式
// return Array.from(new Set(arr)) //[1, 3, 4, 5, 23] //Array.from 将set格式转为数组
// return [...new Set(arr)] //[1, 3, 4, 5, 23] //[...]将set格式转为数组
}
- for循环
function unique1(arr){
for(var i = 0 ,len = arr.length; i < len;i++ ){
for(var j = i +1 ,len = arr.length;j < len;j++){
if(arr[i] == arr[j]){
arr.splice(j ,1)
j--;
len--
}
}
}
return arr
}
- indexOf
function unique2(arr){
let arr1 = []
for(var i = 0 ;i < arr.length;i++ ){
if(arr1.indexOf(arr[i]) == -1){
arr1.push(arr[i])
}
}
return arr1
}
- includes
function unique3(arr){
let arr1 = []
for(var i = 0 ;i < arr.length;i++ ){
if(!arr1.includes(arr[i])){
arr1.push(arr[i])
}
}
return arr1
}
- filter
function unique4(arr){
return arr.filter( function(item,index){
return arr.indexOf(item,0) == index
})
}
三. ES6相关
模块化、面向对象语法、promise、箭头函数、let和const、数组解构赋值、模板字符串....
1. let与const 的区别
- let 用来声明变量,const 用来声明常量。
- let、const 变量都不能进行重复声明,重复声明会报错。
- const 一定要初始化值,一般定义const使用大写(潜规则)。
- let可以修改值,const的值不可以进行修改,但如果声明的是数组或对象时,可更改它的属性,不可重新赋值。
- let、const都不存在变量提升,有自己的块级作用域(if else while for ),只在代码块读取有效。
例题:
var a = 1;
console.log(window.a ) //1
let b = 1;
console.log(window.b ) //undefined
例题
const a = [];
a.push('Hello');
a.length = 0;
a = ['Dave']; //报错
//例题:
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); //5
}
2. 变量的解构赋值
- 数组结构
let person = ['赵本山','刘能','宋小宝']
let [a,b,c] = person;
console.log(b)
- 对象结构
let xiaopin = {
name:'小娜',
age:18,
see:function(){
console.log('我会演小品')
}
}
let {name,age,see} = xiaopin
console.log(age)
3. 箭头函数与普通函数的区别
var name = "xiaona"
let obj = {
name: 'xiaona2',
getName:function(){
console.log(this.name)
},
getName2:()=>{
console.log(this.name)
}
}
obj.getName()
obj.getName2()
4. 函数的默认值与解构赋值搭配使用
function contain ({host='127:100.1.20',port}){
console.log(host)
console.log(port)
}
contain({
port:'8080'
})
5. rest参数, 类似ES5的arguments获取实参参数
rest 返回的是一个数组 ,必须要放到所有参数最后面
function fn (){
console.log(arguments)
}
fn(1,2,3)
function fn2 (a,b...args){
console.log(a) //1
console.log(b) //2
console.log(args) //[3,4,5]
}
fn2(1,2,3,4,5)
6. 扩展运算符
let stars = ['易烊千玺','王源','王俊凯']
function fn(){
console.log(arguments)
}
fn(stars) //arguments1个值
fn(...stars) //arguments3个值
应用场景
- 数组合并
- 将伪数组转为真正的数组
const a = ['肖央','王太利']
const b = ['曾毅','玲花']
const c = a.concat(b)
const d = [...a,...b]
7. Symbol
Symbol 不可以使用关键字new;它是唯一的 ; Symbol的诞生是处理对象的属性名重复后属性值会被覆盖 ;
参数只作为修饰使用;
let game = {
name:'狼人杀',
up:function(){
console.log('快速向上')
},
down:function(){
console.log('快速向上')
}
}
let methods = {
up:Symbol(),
down:Symbol()
}
game[methods.up] = function(){
console.log('修改方法')
}
game.up() //快速向上
//game里的 up 与down方法都是唯一的,不会直接改变此方法
7. 生成器Generator
- 生成器是一个特殊的函数,异步编程的一个解决方案;
- 生成器对象中包含三个函数,next() return() throw()
- next()我们已经熟悉了,Generator.prototype.next(),返回一个由 yield表达式生成的值。
- return() 方法返回给定的值并结束生成器。gen.return(value)
- throw() 向生成器抛出一个错误
- yield关键字,函数代码分隔符,这个yield关键字让 Generator内部的逻辑能够切割成多个部分。
// 案例:1秒后控制台输出111,2s后输出222,3s后输出333
//回调地域
setTimeout(()=>{
console.log(111)
setTimeout(()=>{
console.log(222)
setTimeout(()=>{
console.log(333)
},3000)
},2000)
},1000)
function fun1(){
setTimeout(()=>{
console.log(111)
comput.next()
},1000)
}
function fun2(){
setTimeout(()=>{
console.log(222)
comput.next()
},2000)
}
function fun3(){
setTimeout(()=>{
console.log(333)
},3000)
}
function * gen(){
yield fun1()
yield fun2()
yield fun3()
}
let comput = gen();
comput.next();
8. promise
- Promise对象是一个构造函数,用来生成Promise实例。
- Promise是实现异步编程的一种解决方案。
- Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。 resolve的作用是从未完成变为成功(即从 pending 变为 resolved),reject的作用是从未完成变为失败(即从 pending 变为 rejected)
- Promise.all([Promise1, Promise2, Promise3]) ,all的用法:谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都算返回Promise对象
let p = new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10); //生成1-10的随机数
if(num<=5){
resolve(num);
}
else{
reject('数字太大了');
}
}, 2000);
});
p.then((data) => {
console.log('resolved',data);
}).catch((err) => {
console.log('rejected',err);
});
8.集合 Set ,它的值都是唯一的
let s = new Set(['好事','坏事','喜事','坏事'])
console.log(typeof s ) //object
s.size() //长度
s.add('糟心事') //添加
s.delete('坏事') //删除
s.clear() //清空
s.has('好事') //判断是否有值
//集合实践
let arr1 = [2,1,3,2,1,4]
let arr2= [3,4,6,5,1]
//数组去重
let s1 = [...new Set(arr1)]
console.log(s1)
//并集
let s2 = [...new Set([...arr1,...arr2])]
console.log(s2)
//交集
let s3 = [...new Set(arr1)].filter(item => new Set(arr2).has(item))
console.log(s3)
9.class类
- ES5构造函数实例化一个对象,实现继承
function Phone(name,price){
this.name = name
this.price= price
}
Phone.prototype.call = function(){
console.log('我可以打电话')
}
function SmartPhone(name,price,color){
Phone.call(this,name,price) //继承属性
this.color = color
}
SmartPhone.prototype = Phone.prototype; //继承方法
SmartPhone.prototype.photo = function(){
console.log('我可以拍照')
}
let apple = new SmartPhone('苹果',5999,'金色')
console.log(apple)
- calss类实例化一个对象,实现继承 extend super
class Phone{
constructor(name,price){
this.name = name
this.price= price
}
call(){
console.log('我可以打电话')
}
}
class SmartPhone extends Phone{
constructor(name,price,color){
super(name,price) //Phone.call('SmartPhone',name,price)
this.color = color
}
photo(){
console.log('我可以拍照')
}
}
let apple = new SmartPhone('苹果',5999,'金色')
console.log(apple)
- 静态属性
class Phone{
static name = '手机'; //静态属性 , 属性只在Phone对象上,实例对象不能调用
}
let apple = new Phone()
console.log(Phone.name) //手机
console.log(apple.name) //undefined
10. 数值扩展
- Number.EPSILON 表示数值最小精度
function equal(a,b){
if(Math.abs(a-b) < Number.EPSILON ){
return true
}else{
return false
}
}
console.log(0.1 + 0.2 == 0.3) //false
console.log(equal(0.1 + 0.2 , 0.3)) //false
- Number.isNaN 检测 一个数组是否是NAN
- Number.parseInt Number.parseFloat 字符串转整数
- Number.isInteger 判断一个数是否为整数
- Math.trunc 将数字的小数部分抹掉
- Math.sign 判断一个数为正数1,负数-1,零0
11.对象扩展
- Object.is 判断两个值是否完全相等
console.log(Object.is(210,200))
console.log(Object.is(NaN,NaN))
- Object.assign 对象合并
let config1 = {
host:'localhost',
port:'8080',
test:'tinajia'
}
let config2 = {
host:'123.127.0.12',
port:'8080',
}
console.log(Object.assign(config1,config2))
- Object.setPrototypeOf Object.getPrototypeOf 设置原型对象
let school = {
name:'离石'
}
let citys = {
xiaoqu:['北京','上海']
}
console.log(Object.setPrototypeOf(school,citys))
12. 模块化
模块功能主要由两个命令构成:export 和 import
export 命令用于规定模块的对外接口
import命令用于输入其他模块提供的功能
- 暴露语法
// 分别暴露
export let name = 'xiaona'
export function change (){}
import {name,change} from '' //解构赋值
// 统一暴露
let name = 'xiaona'
function change(){}
export {name,change}
import {name as othername ,change} from '' //解构赋值
//默认暴露
export default {
name:'xiaona',
change:function(){}
}
import {default as obj} from '' //解构赋值
import obj from '' //简便形式
语法总结:
//export 可以导出多个变量、方法等
export { getParameter, getCookie,};
import { formatDate } from "@/common/util";
//export default 只能导出一个 , 适合全局引入
export default { getParameter, getCookie, setCookie,};
import _ from "@/common/util";
四. vue相关
1. Vue双向数据绑定
2. MVC和MVVM的区别
- MVC
Model(模型):负责从数据库中取数据
View(视图):负责展示数据的地方
Controller(控制器):用户交互的地方,例如点击事件等等
思想:Controller将Model的数据展示在View上 - MVVM
VM:也就是View-Model,数据的双向绑定 一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。
思想:实现了 View 和 Model 的自动同步,也就是当 Model 的属性改变时,我们不用再自己手动操作 Dom 元素,来改变 View 的显示,而是改变属性后该属性对应 View 层显示会自动改变(对应Vue数据驱动的思想) - 区别
MVVM 比 MVC 精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作 DOM 元素。
3. 为什么data是个函数并且返回一个对象呢?
data之所以只一个函数,是因为一个组件可能会多处调用,而每一次调用就会执行data函数并返回新的数据对象,这样,可以避免多处调用之间的数据污染。
4. 使用过哪些Vue的修饰符呢?
5. 使用过哪些Vue的内部指令呢?
6. 组件之间的传值方式有哪些?
- 父组件传值给子组件,子组件使用props进行接收
- 子组件传值给父组件,子组件使用$emit+事件对父组件进行传值
- 组件中可以使用children获取到父组件实例和子组件实例,进而获取数据
- 使用listeners,在对一些组件进行二次封装时可以方便传值,例如A->B->C
- 使用$refs获取组件实例,进而获取数据
- 使用Vuex进行状态管理
- 使用eventBus进行跨组件触发事件,进而传递数据
- 使用provide和inject,官方建议我们不要用这个,我在看ElementUI源码时发现大量使用
- 使用浏览器本地缓存,例如localStorage
7. 路由有哪些模式呢?又有什么不同呢?
- hash模式:通过#号后面的内容的更改,触发hashchange事件,实现路由切换
- history模式:通过pushState和replaceState切换url,触发popstate事件,实现路由切换,需要后端配合
8. 如何设置动态class,动态style?
- 动态class对象:
- 动态class数组:
- 动态style对象:
- 动态style数组:
9. computed和watch有何区别,哪个可以进行异步操作?
- computed是依赖已有的变量来计算一个目标变量,大多数情况都是多个变量凑在一起计算出一个变量,并且computed具有缓存机制,依赖值不变的情况下其会直接读取缓存进行复用,computed不能进行异步操作
- watch是监听某一个变量的变化,并执行相应的回调函数,通常是一个变量的变化决定多个变量的变化,watch可以进行异步操作
- 简单记就是:一般情况下computed是多对一,watch是一对多
10. Vue的生命周期,讲一讲?
- 父子之间的生命周期执行顺序
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
11.为什么v-if和v-for不建议用在同一标签?
v-for和v-if同时存在,会先把元素都遍历出来,然后再一个个判断,这样的坏处就是,渲染了无用的节点,增加无用的dom操作,建议使用computed来解决这个问题;
{{item}}
computed() {
list() {
return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3)
}
}
12. vuex的有哪些属性?用处是什么?
- State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
- Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
- Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
- Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
- Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
13. 对象新属性无法更新视图,删除属性无法更新视图,为什么?怎么办?
- 原因:Object.defineProperty没有对对象的新属性进行属性劫持
- 对象新属性无法更新视图:使用Vue.set(obj, key, value)
- 删除属性无法更新视图:使用Vue.delete(obj, key)
14. 直接arr[index] = xxx无法更新视图怎么办?为什么?怎么办?
- 原因:Vue没有对数组进行Object.defineProperty的属性劫持,所以直接arr[index] = xxx是无法更新视图的
- 使用数组的splice方法,arr.splice(index, 1, item)
- 使用Vue.$set(arr, index, value)
15. vue相关知识点比较全面的网址
https://mp.weixin.qq.com/s/-zLlWJxA67GoctMaK0Sk8A
五. vue3相关
六. 前端性能相关
六. 前端性能相关
9. 跨域
同源策略:协议、主机、端口号一致 ,否则跨域
处理跨域:
- jsonp跨域
利用script请求src进行访问,callback返回值 - cors:核心简单就是响应头部Access-Control-Allow-Origin。
当用户端想用put delete修改服务端数据时,浏览器发送预检请求options,服务端也可设置methods方法;
response Headers : Access-Control-Allow-Origin:*
Access-Control-Allow-Methods:GET,POST - 反向代理
修改nginx.conf配置文件
客户端访问nginx,由nginx进行反向代理到服务器 -
vue解决跨域
设置vue.config.js
本地服务器设置反向代理访问服务器
10. nginx
-
正向代理:代理客户端:例vpn
反向代理:代理服务器:例nginx
11.qiankun
import { registerMicroApps, setDefaultMountApp, start } from "qiankun";
//注册子应用
registerMicroApps([
{
name: 'retail',
entry: process.env.VUE_APP_RETAIL, //入口地址
container: '#retail',
activeRule: getActiveRule('#/cydn/retail'),
},
]);
11.预编译
预编译的阶段就是创建作用域的阶段
预编译过程
- 创建AO对象
- 找形参和变量的声明,作为AO对象的属性,初始值为undefined
- 实参和形参相统一
- 找函数声明 覆盖变量的声明
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)
// 解析过程
// AO:{
// a:undefined 1 function a(){}
// c:undefined 2 function c(){}
// d:undefined
// b:undefined function(){}
// }
12. this
// 分析过程
function get(content){
console.log(content)
}
get('你好') //get.call(window,'你好')
var person = {
name:'张三',
run :function(time){
console.log(`${thia.name} 在跑不 最多${time} min就不行了`)
}
}
person.run(30) //person.run.call(person,20)
var name = 222
var a = {
name:111,
say:function(){
console.log(this.name)
}
}
var fun = a.say
fun() //fun.call(window)
a.say() //a.say.call(a)
var b = {
name:333,
say:function(fun){
fun() //fun.call(window)
}
}
b.say(a.say)
b.say = a.say
b.say() //b.say.call(b)
13.Es6箭头函数
箭头函数中的this指向外层代码块的this ,它本身没有this指向父级。
var x = 11;
var obj = {
x:22,
say:()=>{
console.log(this.x)
}
}
obj.say();
var obj = {
birth:1990,
getAge:function(){
var b = this.birth;
var fn = ()=> {
new Date().getFullYear() - this.birth
}
return fn();
}
}
16. 前端安全
- XSS攻击:跨站脚本攻击,页面被注入恶意代码,没有被转义被执行的代码。
分类:
储存型XSS攻击:存入到数据库,数据库在返回渲染到页面;
反射性XSSS攻击:通过a链接或者url地址参数;
DOM型XSS攻击:代码未经过后台 ,纯前端执行恶意操作的代码;
17. Object.defineProperty 与 proxy
Object.defineProperty
Object.defineProperty(obj,'name',{
set:function(val){
},
get: function(){
}
})
proxy : ES6为了操作对象引入的API,可以对目标对象读取、函数调用等操作进行拦截,不直接操作对象,通过代理,进行操作。
一个 Proxy 对象由两个部分组成: target(目标对象)、 handler (一个对象,声明了代理 target 的指定行为)
let proxy = new Proxy(target, {
get: function(target, key) {
console.log('getting '+key);
return target[key];
},
set: function(target, key, value) {
console.log('setting '+key);
target[key] = value;
}
})
两者对比
- 对象监听
- Object.defineProperty 无法一次性监听对象属性,必须遍历(Object.keys().forEach)或者递归实现;
- proxy 不需要遍历, get(目标对象,属性名、实例本身) ;set (目标对象、属性名、属性值、实例本身)
- 监听新增加属性
- Object.defineProperty 无法监听新增加的属性 ,需要手动去做监听,vue2使用Vue.set()
- proxy 可以
- 无法响应数组操作
- Object.defineProperty 无法实现监听数组, pop\shift\unshift\sort\reverse\splice\push 重写Array.prototype原型方法
- proxy 可以
18.组件之间传值
- 父组件传值子组件
//父组件
//子组件
{{msg}}
export default {
props:["msg"]
}
- 子组件传值父组件:父组件通过属性值的方法传递给子组件,传递一个函数,回调函数
//子组件
sendFather(){
this.$emit('sendSon',this.value)
},
//父组件
getSon(value){
this.msg = value
}
//兄弟组件传值eventBus.
eventBus.js
import Vue from 'vue'
export default new Vue
// one组件 (发送)
import bus from 'eventBus.js'
sendSos(){
bus.$emit("SOS",this.message)
},
//two 组件 (接收)
import bus from 'eventBus.js'
bus.$on("SOS",(data)=>{
this.message = data;
})
19.数组
- map的返回一个新数组
- forEach没有返回值
- filter返回新数组,匹配条件符合的
- every数组有一项不满足条件 返回false
- some数组有一项满足条件 返回true
- push方法返回数组长度
20. 前端性能优化
- 首页加载慢的优化
- 对于图片可以懒加载(监听滚动条事件,如果滚动条距离浏览器顶部的高度 == 图片距离顶部的高度,那么将data-src的值赋值到src上),减少首屏图片加载量。以及对于小图标和小图片分别使用iconfont字体库和雪碧图来解决,最大程度减少首屏图片数量,提升首页渲染性能。
- 对于其他资源可以通过打包(nginx combo或者webpack打包)合并资源,并可以通过懒加载路由的方式减少首页JS的加载量 ( import('../views/XXX') 动态导入 )。
- 引入大型的第三方库,比如组件库按需加载,一般都是用babel插件实现,配置babel-plugin-component。
- 减少资源的方式通过压缩和混淆加密减少文件体积。
- 通过开启gzip进行全部资源压缩,gzip是一种压缩文件格式,可以通过nginx服务器的配置进行开启压缩。
- 图片的优化(太多和太大)
- 通过懒加载减少图片请求,或者用过雪碧图合并图片,以及将小图转为base64的格式解决多的问题。
- 图片大的问题,可以通过自动化压缩工具压缩图片,或者使用webP格式。
- 实现webPack打包优化(太多、太大)
- 可以通过设置mode=production来默认实现webPack对代码的混淆和压缩,从而最大程度减少代码体积。
- 使用webpack + dynamic import 结合路由的入口文件做拆包处理,懒加载路由。
- 设定一些打包策略optimization.splitChunks,将node_modules单独打包以及公共包,配合网络缓存做加载性能优化。
- 实现CDN加速
- cdn服务器主要是用来放静态资源的服务器,可以用来加速静态资源的下载
- cdn之所以能够加速,是因为会在很多地方都部署cdn服务器,如果用户需要下载静态资源,会自动选择最近的节点下载
- 由于cdn服务的地址与主服务器的地址不同,可以破除浏览器对同一个域名发送请求的限制,http1 同一域名地址最多6个TCP链接,http2 无限制。
- 渲染十万条数据如何不造成卡顿
- 导致浏览器卡顿的原因一般都是操作DOM次数太频繁, 如果想要渲染很多条数据不造成卡顿,那么一定要减少操作DOM的次数。用虚拟DOM就是为了大大减少操作的DOM次数。
- 在渲染的时候,可以使用document.createDocumentFragment创建虚拟DOM,避免引起没必要的渲染。
- 可以采取分段渲染的方式,最后使用window.requestAnimationFrame来逐帧渲染。
21. 前端webpack打包优化
- 优化耗时分析speed-measure-webpack-plugin插件
npm install -D speed-measure-webpack-plugin
22. 闭包
23.vue3新特性
vue2将业务代码分散到data、methods、mounted里,修改代码 需要反复跳转,所以vue3将其作为优化, 可读性维护性都更高。vue2也曾给出方案Mixin,但是也会遇到命名冲突,不清楚暴露出来变量的作用。
新特性:
- setup : 是组件内使用Composition Api的入口;
- reactive、 ref、toRefs
第 {{ year }} 年
姓名: {{ nickname }}
年龄: {{ age }}
- watch 、 watchEffect
watch 监听 reactive定义的数据
import { defineComponent, ref, reactive, toRefs, watch } from "vue";
export default defineComponent({
setup() {
const state = reactive({ nickname: "xiaofan", age: 20 });
setTimeout(() => {
state.age++;
}, 1000);
// 修改age值时会触发 watch的回调
watch(
() => state.age,
(curAge, preAge) => {
console.log("新值:", curAge, "老值:", preAge);
}
);
return {
...toRefs(state),
};
},
});
watch 监听ref定义的数据
const year = ref(0);
setTimeout(() => {
year.value++;
}, 1000);
watch(year, (newVal, oldVal) => {
console.log("新值:", newVal, "老值:", oldVal);
});
监听多个数据
watch([() => state.age, year], ([curAge, newVal], [preAge, oldVal]) => {
console.log("新值:", curAge, "老值:", preAge); console.log("新值:", newVal,
"老值:", oldVal); });
监听复杂嵌套对象,wacth第三个参数 {deep:true} ;
stop 停止监听,直接调用watch的返回值
watchEffect
- watchEffect 不需要手动传入依赖
- watchEffect 会先执行一次用来自动收集依赖
- watchEffect 无法获取到变化前的值, 只能获取变化后的值
watchEffect(() => {
console.log(state);
console.log(year);
});
- 自定义hookes
import { ref, Ref, computed } from "vue";
type CountResultProps = {
count: Ref;
multiple: Ref;
increase: (delta?: number) => void;
decrease: (delta?: number) => void;
};
export default function useCount(initValue = 1): CountResultProps {
const count = ref(initValue);
const increase = (delta?: number): void => {
if (typeof delta !== "undefined") {
count.value += delta;
} else {
count.value += 1;
}
};
const multiple = computed(() => count.value * 2);
const decrease = (delta?: number): void => {
if (typeof delta !== "undefined") {
count.value -= delta;
} else {
count.value -= 1;
}
};
return {
count,
multiple,
increase,
decrease,
};
}
count: {{ count }}
倍数: {{ multiple }}
24. npm发包管理
- 组件库项目改造
|-- examples // 原 src 目录,改成 examples 用作示例展示
|-- packages // 新增 packages 用于编写存放组件
2.vue.config.js的配置文件
// vue-config.js
module.exports = {
// 修改 src 目录 为 examples 目录
pages: {
index: {
// page 的入口
entry: 'examples/main.js', // 把src 修改为examples
// 模板来源
template: 'public/index.html',
// 在 dist/index.html 的输出
filename: 'index.html'
}
},
// 扩展 webpack 配置,使 packages 加入编译
/* chainWebpack 是一个函数,会接收一个基于 webpack-chain 的 ChainableConfig 实例。允许对内部的 webpack 配置进行更细粒度的修改。 */
chainWebpack: config => {
config.module
.rule('js')
.include
.add(__dirname + 'packages') // 注意这里需要绝对路径,所有要拼接__dirname
.end()
.use('babel')
.loader('babel-loader')
.tap(options => {
// 修改它的选项...
return options
})
}
}
- 编写组件
|——
|——packages
| |——index.js
| |——ChanFuEmpty
| |——index.js
| |——src
| |——chanfu-empty.vue
|——
{{ desc }}
ChanFuEmpty文件夹内的index.js
import ChanFuEmpty from './src/chanfu-empty.vue'
// 为组件提供 install 安装方法,供按需引入
ChanFuEmpty.install = function (Vue) {
Vue.component(ChanFuEmpty.name, ChanFuEmpty)
}
// 默认导出组件
export default ChanFuEmpty
- packages 文件夹内的index.js
import ChanFuEmpty from './ChanFuEmpty'
import ChanFuFooter from './ChanFuFooter'
// 存储组件列表
const components = [
ChanFuEmpty,
ChanFuFooter
]
/*
定义install 方法,接收Vue作为参数,如果使用use注册插件,则所有的组件都将被注册
*/
const install = function (Vue) {
// 判断是否安装
if (install.installed) { return }
// 遍历所有组件
components.map(item => {
Vue.component(item.name, item)
})
}
// 判断是否引入文件
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
export default {
install,
ChanFuEmpty,
ChanFuFooter
}
export {
ChanFuEmpty,
ChanFuFooter
}
- 测试引用
//main.js
import ChanFuComps from "./../packages/index";
Vue.use(ChanFuComps)
//app.vue
- package.json 中新增一条编译为库的命令
"scripts": {
// ...
"lib": "vue-cli-service build --target lib --name vcolorpicker --dest lib packages/index.js"
}
- 执行编译库命令
npm run lib
- 配置 package.json 文件中发布到 npm 的字段
name: 包名,该名字是唯一的。可在 npm 官网搜索名字,如果存在则需换个名字。
version: 版本号,每次发布至 npm 需要修改版本号,不能和历史版本号相同。
description: 描述。
main: 入口文件,该字段需指向我们最终编译后的包文件。
keyword:关键字,以空格分离希望用户最终搜索的词。
author:作者
private:是否私有,需要修改为 false 才能发布到 npm
license: 开源协议
- 登录npm
npm config set registry https://registry.npmjs.org
npm login
- 发布到npm
npm publish
- 项目 安装引用
https://www.npmjs.com/package/chanfu-components
25. 深浅拷贝
- 数据类型的存储方式:
- 基本类型:基本类型值在内存中占据固定大小,保存在栈内存中
- 引用类型:引用类型的值是对象,保存在堆内存中,而栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址
- 不同类型的复制方式
- 基本类型:从一个变量向另外一个新变量复制基本类型的值,会创建这个值的一个副本,并将该副本复制给新变量
- 引用类型:从一个变量向另一个新变量复制引用类型的值,其实复制的是指针,最终两个变量最终都指向同一个对象
- 深拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响
如果将一个引用数据类型 实现为深拷贝
JSON.parse()和JSON.stringify()是完全的深拷贝。
JSON.stringify():把一个js对象序列化为一个JSON字符串
JSON.parse():把JSON字符串反序列化为一个js对象
let copyObj = JSON.parse(JSON.stringify(obj));动手实现深拷贝 利用递归来实现对对象或数组的深拷贝。递归思路:对属性中所有引用类型的值进行遍历,直到是基本类型值为止。
function deepCopy(obj) {
if (!obj && typeof obj !== 'object') {
throw new Error('error arguments');
}
// const targetObj = obj.constructor === Array ? [] : {};
const targetObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
//只对对象自有属性进行拷贝
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === 'object') {
targetObj[key] = deepCopy(obj[key]);
} else {
targetObj[key] = obj[key];
}
}
}
return targetObj;
}
27. webpack打包优化
- webpack打包速度分析
npm install --save-dev webpack-bundle-analyzer
//vue.config.js
const {BundleAnalyzerPlugin} = require("webpack-bundle-analyzer")
configureWebpack: {
plugins:[
new BundleAnalyzerPlugin({
generateStatsFile: true
})
]
}
// 生成stats.json文件上传此网站进行分析 http://webpack.github.io/analyse/#warnings
- gzip压缩
// gzip压缩
npm install [email protected] --save-dev cli4用这个版本
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i
new CompressionWebpackPlugin({
filename: "[path].gz[query]", //目标资源名称
algorithm: "gzip",
test: productionGzipExtensions, //处理所有匹配此 {RegExp} 的资源
threshold: 10240,//只处理比这个值大的资源。按字节计算(楼主设置10K以上进行压缩)
minRatio: 0.8 //只有压缩率比这个值小的资源才会被处理
})
// nginx也需要开启gzip设置浏览器解析.gz文件
异步编程方案
六种异步方案, 重点是 Promise、Async、发布 / 订阅原理实现-白鹤资源网 (wcrane.cn)
vue源码原理
网络相关
前端工程化知识
Lerna的Monorepo,微前端
微前端qiankun+vue - (jianshu.com)
目标是最完善的微前端解决方案 - qiankun 2.0 - 知乎 (zhihu.com)
25. typescript
// 1.泛型
type Generics = {
name: string
age: number
sex: T
}
const my: Generics<'nan'> = {
name: '1',
age: 1,
sex: 'nan'
}
// 2. 接口
interface CatInfo {
age: number;
breed: string;
}
function fun ( obj: CatInfo) {
}
fun({
age: 1,
breed: '1'
})
// 3.Record 将一个类型的属性值映射到某一个值上
type CatName = "miffy" | "boris" | "mordred";
const cats: Record = {
miffy: { age: 10, breed: "Persian" },
boris: { age: 5, breed: "Maine Coon" },
mordred: { age: 16, breed: "British Shorthair" },
};
// 4. Pick | Omit
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview1 = Pick; // 选择包含的key来构造出新的类型
type TodoPreview2 = Omit; // Omit忽略删除选中的key
const todo: TodoPreview1 = {
title: "Clean room",
completed: false,
};
// 5.不知道key是什么,但是知道key与value的类型
interface StringArray {
[index: number]: string;
}
// 6.不知道属性的长度,用[propName:string]: string | null 联合类型来处理剩余属性,剩余属性必须包含前面所有的属性类型
interface PropArray {
id: number;
[propName: string]: string | number;
}
const t1: PropArray = {
name: 'a',
id: 123,
a: 123
}
// 7.interface继承
interface BasicAddress {
name?: string;
street: string;
city: string;
country: string;
postalCode: string;
}
interface AddressWithUnit extends BasicAddress {
unit: string;
}
// 8.合并 interface 与type
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
interface ColorfulCircle extends Colorful, Circle {}
type ColorfulCircle = Colorful & Circle;
const cc: ColorfulCircle = {
color: "red",
radius: 42,
};
// 9.不确定类型unknown
interface Box {
contents: unknown;
}
// 10.联合类型
interface T1 {
name: string | number
}
const a: T1 = {
name: 1
}
a.name = a.name + ''
26. vue.store
const user = {
state: {
token: getToken(),
name: '',
avatar: '',
roles: [],
permissions: []
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
},
SET_PERMISSIONS: (state, permissions) => {
state.permissions = permissions
}
},
actions: {
// 登录
Login({ commit }, userInfo) {
const username = userInfo.username.trim()
const password = userInfo.password
const code = userInfo.code
const uuid = userInfo.uuid
return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => {
setToken(res.token)
commit('SET_TOKEN', res.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
GetInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo().then(res => {
const user = res.user
const avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
commit('SET_ROLES', res.roles)
commit('SET_PERMISSIONS', res.permissions)
} else {
commit('SET_ROLES', ['ROLE_DEFAULT'])
}
commit('SET_NAME', user.userName)
commit('SET_AVATAR', avatar)
resolve(res)
}).catch(error => {
reject(error)
})
})
},
// 退出系统
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
commit('SET_PERMISSIONS', [])
removeToken()
resolve()
}).catch(error => {
reject(error)
})
})
},
// 前端 登出
FedLogOut({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
removeToken()
resolve()
})
}
}
}
export default user
this.$store.dispatch("Login", this.loginForm)
.then(() => {
this.$router.push({ path: this.redirect || "/" }).catch(() => {});
})
.catch(() => {
this.loading = false;
if (this.captchaOnOff) {
this.getCode();
}
});