1. 网站优化
1)代码压缩/第三方工具进行代码压缩
2)控制图片大小,超出的图片用canvas进行处理
3)CDN加速,把公用的的js包或CSS包放在公共服务器或npm仓库进行异步加载减少页面阻塞
4)图片懒加载,尽量用CSS替换图片,图片存放在服务器中转换成https链接的形式,减小包容量
5)大数据分页处理,减少DOM节点读取的卡顿
6)利用缓存存储必要信息,减少接口的频繁调用,减轻服务器压力
7)防爆操作,如支付/表单提交等,用户不得频繁调用接口,只有接口处理返回之后才能重新调用,惰 性函数/一次性函数/异步函数增强业务逻辑的同时,也可以合理延长请求时间,减少接口请求频率
8)组件仓库化(npm仓库进行管理),减少项目体积
【补充】
【1】按需加载第三方库(UI 组件库或工具库等);
【2】移除调试信息,例如 log;
【3】合理使用 SourceMap;
【4】通过 externals 忽略第三方包再配合 CDN 的方式进行加载;
【5】路由/组件/图片/数据懒加载、图片压缩/雪碧图、压缩合并 CSS、提取公共 JS 等;
【6】合理的使用缓存(包含前端缓存或 HTTP 缓存);
【7】webpack实现代码压缩/代码分区合并/图片压缩/console.log打印清除/注释清除/debugger清除。
2、网站安全
1)使用第三方的图片验证码实现防字典爆破
2)md5/sha1实现数据加密,譬如支付前的token加密,确保信息安全;
3)前端生成验证码,短信验证码,登陆密码校验等实现真人校验
4)输入信息校验/过滤,防SQL语句注入
3、浏览器兼容
1)兼容IE9及以上,360浏览器,火狐浏览器和谷歌浏览器
2)JS通过渐进增强和平稳退化的方式,
比如,IE的JS的api不兼容,冒泡模式不一样,怪异盒模型,IE8以下浏览器的浮动样式问题
3)CSS兼容问题
不同浏览器的默认样式存在差异,可以使用 Normalize.css 抹平这些差异。当然,你也可以定制属于自己业务的 reset.css
4、前端跨域问题
1)JQuery的jsonP跨越【需要配置jsonp接口】,get请求可通过 /
标签实现跨域访问
2)webpack 或 vue/cli 或 vite 的dev-serve设置跨域
3)服务器设置代理跨域,后台跨域返给前端,或者后端做转发,把数据返给前端
4)uniapp内置浏览器自带跨域
5)使用谷歌浏览器的跨域插件,或者解除谷歌跨域限制【推荐,关于如何设置谷歌浏览器禁止检查ajax跨域问题的解决方案】
6)使用nodeJS实现代理转发
5、面向对象的程序设计(设计模式)
常用模式:
1)工厂模式
2)构造函数模式
3)原型模式
4)原型模式和构造函数模式混合使用
5)动态原型模式
6)发布者订阅者模式
【补充】推荐文章:15分钟带你了解前端工程师必知的javascript设计模式(附详细思维导图和源码)
单例模式
构造器模式
建造者模式
代理模式
外观模式
观察者模式
策略模式
迭代器模式
6、ES6新特性
网址:官网
7、开发项目中遇到的难点
1)小程序唤起微信浏览器打开H5
解决办法:
1、微信城市服务的资格申请【直接能打开H5,也能关闭H5回到小程序中】
2、利用客服会话,推送H5跳转链接【要经过一遍客服会话,增加流程】
3、使用该小程序关联的公众号的文章的二维码实现H5跳转【先扫码关注公众号,公众号模板链接跳H5】
2)解决小程序中canvas层级过高的问题
通过小程序处理canvas的wx.canvasToTempFilePath使之变为图片,打开蒙层的那一刻之前先变为图片,关闭蒙层,则重新渲染
3)10w+数据处理
前端处理:分页处理,预加载处理(譬如说下一页的预加载),或者分批请求,1w为节点请求,有十万就请求十次。
后端: redis缓存
4)小程序组件生命周期响应大于页面周期,数据不渲染的问题
1、this.setData数据超出258k的限制,数据切片处理/分批渲染等
例如:通过迭代器将数组中的元素遍历出来 entries()
2、组件更加细分,组件与组件之间异步加载,数据加载更快
3、尽可能少使用大数据的二维数组,如果要使用,也得一个组件只能有一个v-for
5)PC端微信小程序的wx.getLocation无效【因为PC端是没GPS定位的】
1、通过wx.getSystemSync()获取platform信息,除了andiron,ios之外的系统都是PC端。给定经纬度的默认值
2、通过授权登录获取用户信息,获取手机号判断用户上次用手机定位的信息
6)前端版本控制清除用户本地缓存
通过前端版本控制清除用户本地缓存使之重新登陆,刷新用户本地缓存信息,并推送相应的版本更新提示,或者通过对localStorage过期时间设置实现,推荐文章:面试官: 如何让localStorage支持过期时间设置?
大概思想为:再存入对应的键值时,同时通过加连接符绑定一个过期时间。当下次进入页面使用时,与当前时间比较,比较后做对应的操作
7)iphoneX的安全区域问题
1、CSS的constant(),env()方法实现安全区域兼容
2、通过wx.getSystemSync()获取statusHeight和windowHeight来判别安全高度
8)react中声明的纯函数组件的ref为null
纯函数组件的状态更改只能够通过hooks,手动更改或渲染
9)react的纯函数组件视图与数据分离,视图更改数据未更改,数据更改视图未更改
10)数组对象里的属性更改,视图不渲染
React 并不会检查数组里的元素是否有改变,它只会检查数组本身的引用是否有改变。通过扩展运算符更改数组的引用
8、js事件循环(event loop)
推荐视频
js事件循环包括宏任务和微任务、js单线程执行过程、浏览器异步执行机制
下述文字讲解的是浏览器异步执行原理和事件驱动的基础上开拓的js的事件循环机制
1)浏览器js异步执行原理
浏览器是多线程的,js是单线程的(指的是执行js的代码是单线程的,是浏览器提供的js引擎线程);浏览器还有定时器线程和http请求线程等等。
比如说主线程中会发送一个ajax请求,就会把这个任务交给浏览器的http请求线程去请求,js主线程不受影响继续向下执行,当请求成功之后,会将请求中的回调函数在返给js主线程。
换句话说,浏览器才是真正执行请求的角色,而js是处理请求的回调函数的角色
浏览器包括浏览器进程、渲染进程、GPU进程、网络进程等
我们前端主要关注的是渲染进程【包括js引擎线程、http请求线程,定时器线程,条件出发、GUI线程等】
浏览器异步执行的原理是通过事件驱动(事件触发、任务选择、任务执行)来触发的
事件循环就是在事件驱动模式中来管理和执行事件的一套流程
事件驱动和状态/数据驱动来改变渲染,浏览器中典型的事件处理也是典型的事件驱动,在事件驱动中,当有事件触发后,被触发的事件会按顺序暂时存在一个队列中,等待js的同步任务执行完成之后就从这个队列中取出要处理的事件并处理,什么时候取出任务,优先处理那些任务,这都是由我们的事件循环来控制
浏览器中的事件循环、必须了解执行栈和任务队列
js在执行一段代码时,会将同步代码按顺序排列在某个地方,这就是执行栈。依次执行里面的函数,当遇到异步任务时就交给其他线程来处理。等待当前同步代码执行完成之后,它会从一个队列中去取出完成中的异步任务的回调加入执行栈继续执行。遇到异步任务然后又交给其他线程。通过这样一个循环来执行完整个代码
js按顺序执行执行栈里面的方法,每次执行一个方法时,都会为这个方法生成一个独有的执行环境,也就是我们平时说的上下文(context) 等待这个方法执行完成之后,会销销毁掉这个环境,并从执行栈中弹出此方法,然后又继续调用下一个方法
由此可见,在事件驱动的模式下,至少包含了一个执行循环来检测任务队列是否有新的任务,通过不断循环取出异步回调来执行,这个过程就是我们所说的事件循环(事件周期)
宏任务和微任务
任务队列根据任务不同可以分为宏任务队列和微任务队列。执行栈在同步代码执行完成之后优先会去检查微任务队列是否会有任务需要执行,如果没有,再去宏任务队列检查。如此往复
微任务一般会在当前循环优先执行,而宏任务会等到下一次循环开始的时候执行。
微任务要比宏任务先执行,并且微任务只有一个队列,而宏任务有多个【因为宏任务就包括定时器任务,请求任务,鼠标点击,键盘按下等等事件】
宏任务包括 setTimeOut() \ setInterval() \ setImmediate()
微任务包括 Promise.then() \ Promise.catch \
宏任务特征:有明确的异步任务需要执行和回调,需要其他异步线程支持
微任务特征,没有明确的异步任务需要执行,只有回调,不需要其他异步线程支持
如果我们将定时器设置为0ms,谷歌浏览器会默认为4ms,Node.js会默认为1ms
定时器误差
因为js单线程先执行同步代码,再取出执行异步回调。当执行setTimeOut时,浏览器会启用新的线程去计时,计时结束后重新出发定时器事件,将回调存入宏任务队列中,等待JS主线程空了的时候再取出执行。如果同步代码还在执行,这个时候的宏任务只能挂起,这就会导致计时不准确的问题。同步代码越长/微任务耗时长,计时也就越不准确
微任务队列执行完成之后,也就是一次事件循环结束之后,浏览器会执行视图渲染。当然这里也有浏览器自己的一个优化,它可能合并多次事件循环的结果做一次视图重绘。因此视图更新是在事件循环之后,所以并不是每一次操作dom都会立马刷新视图,视图重绘之前会先执行requestAnimationFrame回调
9、js判断空对象的5种方法
知识点:
1)Object.getOwnPropertyNames ,获取对象的全部属性名存放在一个数组中
2)Object.keys(obj) 获取给定对象的所有可枚举属性的字符串数组
3)hasOwnProperty检测属性是否存在对象实例中(可枚举属性),如果存在返回true,不存在返回false
例如:
let arrObj = { name: "字符串", age: 18 };
// 第一种方法
console.log(JSON.stringify(arrObj) === "{}"); //false
// 第二种方法 for...in
let fun = (arr) => {
for (let i in arr) {
return false;
}
return true;
};
console.log(fun(arrObj)); // false,表示不是空对象
// 第三种方法
let _arr = Object.getOwnPropertyNames(arrObj);
console.log(_arr); // ["name" , "age"]
console.log(_arr.length != 0); // true,表示不为空对象
// 第四种方法
let _arr1 = Object.keys(arrObj);
console.log(_arr.length != 0); // true,表示不为空对象
// 第五种方法
let fun1 = (arr) => {
for (let i in arr) {
if (arr.hasOwnProperty(i)) {
return false;
}
}
return true;
};
console.log(fun1(arrObj)); // false,表示不为空对象
10、forEach方法跳出循环
知识点:
1)forEach是函数,不是if、for之类的语句。它的参数是一个匿名函数,是一个callback,实际每次forEach操作的是一个函数,所以不能使用for语句相关的continue,break
2)forEach方法机制,是对数组的每个有效元素执行一次callback函数
3)forEach跳出循环通过try…catch…方式
let arr = [1, 2, 3, 4];
// 第一种示例,未跳出循环
arr.forEach((n) => {
// forEach方法内是传递的一个匿名函数
if (n === 2) {
// break;
// continue; 这两个适用于for语句,不适用于forEach函数的
return;
}
console.log(n); // 控制台分别会打印1,3,4
});
// 第二种示例,跳出循环
let k = null;
try {
arr.forEach((n) => {
// forEach方法内是传递的一个匿名函数
if (n === 2) {
k = n;
throw new Error();
}
});
} catch (e) {}
console.log(k); // 2
11、什么是中间件
中间件是一个起连接作用的东西
只要能够获取数据的,都可以算是中间件。比如axios
12、JSONP跨域
1)什么是跨域?
当请求url时,它的协议、域名、端口,任意一个与你当前页面的url不同即为跨域
2)什么是JSONP?
JSONP其实是一个跨域解决方案
3)JSONP跨域原理
1、img\script等标签的src属性,它不受同源策略的限制
2、JSONP请求数据时,服务器返回的是一段可执行的javascript代码
4)JSONP用途:解决请求其他服务器时的跨域问题
5)JSONP局限:只能用get方式,需要服务器做一些处理、配合
6)分为三个部分:
1、安装nodeJS Express框架:npm i express -D
2、创建静态目录:app.use(‘/public’ , express.static(‘public’))
3、中间件js文件,响应get请求
示例:
13、Promise链式调用,和axios处理高并发
应用场景:下一个操作依赖于上一个操作的状态和结果
前提条件:.then方法中必须返回一个promise对象
运行过程:1).then在链式调用时,会等其前一个then中回调函数执行完毕;2)并且成功返回状态的promise,才会执行下一个then的回调函数
情景如下,我们需要请求三个接口,这三个接口都是要等上一次的请求完成之后,才能请求下一个的请求
let url1 = 'https://123.com/api/api1'
let url2 = 'https://123.com/api/api2'
let url3 = 'https://123.com/api/api3'
// 链式调用,回调地狱模式【不推荐】,可维护性差
axios.get('url1').then(res1=>{
console.log(res1)
axios.get('url1').then(res2 => {
console.log(res2)
axios.get('url1').then(res3 => {
console.log(res3)
})
})
})
// 因为 axios.get('url1') 就会返回一个Promise对象
// 所以要这样处理,可维护性强
axios.get('url1').then(res1 => {
console.log(res1)
return axios.get('url2')
}).then(res2=>{
console.log(res2)
return axios.get('url3')
}).then(res3 => {
console.log(res3)
})
Promise.all() 异步任务同步请求
任务一:【推荐用这种】
getProps () {
const that = this
const cellStyle = this.$refs.formTable0.submit() // 这是一个Promise对象
const rowStyle = this.$refs.formTable1.submit()
// 异步任务同步执行
Promise.all([cellStyle, rowStyle]).then(res => {
console.log('res', res)
}).catch(res => {
console.log('任务阻塞', res)
that.$emit('update:submit', res)
})
}
任务二: await会阻塞下面的await,Promise.all中的catch捕获reject传来的错误
async getProps () {
const that = this
const cellStyle = await this.$refs.formTable0.submit()
const rowStyle = await this.$refs.formTable1.submit()
// 异步任务同步执行
Promise.all([cellStyle, rowStyle]).then(res => {
console.log('res', res)
}).catch(res => {
console.log('任务阻塞', res)
that.$emit('update:submit', res)
})
}
}
【补充】Promise.all和Promise.race的区别和使用
详细Promise介绍:Promise 的各种方法的详细使用
1)Promise.all 类似于Promise链式请求
比如当一个页面需要在很多个模块的数据都返回回来时才正常显示,否则loading
比如当数组里的P1,P2都执行完成时,页面才显示。
值得注意的是,返回的数组结果顺序不会改变,即使P2的返回要比P1的返回快,顺序依然是P1,P2
// 获取表格下拉框数据
async getSelectList () {
const that = this
const P1 = await orderumber({ type: 'order' })
const P2 = await getCustomerList()
const P3 = await listBaseOtherOffer()
// 异步任务同步执行,为了让所有的数据都请求完毕,再打开新增弹框,否则不打开
// 优点保证数据加载完毕,缺点是加载时长过长
Promise.all([P1,P2,P3]).then(res => {
that.outerVisible = true
}).catch(res => {
console.log('下拉框数据获取失败')
})
},
2)Promise.race
race是赛跑的意思,也就是说Promise.race([p1, p2, p3])里面的结果哪个获取的快,就返回哪个结果,不管结果本身是成功还是失败
// 获取表格下拉框数据
async getSelectList () {
const that = this
const P1 = await orderumber({ type: 'order' })
const P2 = await getCustomerList()
const P3 = await listBaseOtherOffer()
// 异步任务同步执行,为了让所有的数据都请求完毕,再打开新增弹框,否则不打开
// 优点保证数据加载完毕,缺点是加载时长过长
Promise.race([P1,P2,P3]).then(res => {
that.outerVisible = true
}).catch(res => {
console.log('下拉框数据获取失败')
})
},
一般用于和定时器绑定,比如将一个请求和一个三秒的定时器包装成Promise实例,加入到race队列中,请求三秒中还没有回应时,给用户一些提示或一些相应的操作。
3)Promise.resolve()
将现有对象转换未Promise对象
4)Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
5)实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误
Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。
【若想详细了解Promsie的底层原理】推荐视频:bilibili视频
14、Vue的render函数
render函数的用途: 创建html模板
知识点:
1)render函数的参数是createElement函数,createElement返回值是一个虚拟dom,即VNode,也就是我们需要渲染的节点
2)createElement有三个参数
第一个参数,必需,{ String | Object | Function } 是要渲染的html标签
第二个参数,可选:{ Object } html标签的各种属性
第三个参数,可选,{ String | Array } 当前html标签的子元素
3)渲染过程
1、独立构建,包括模板编译器,HTML字符串=> render函数=> VNode=>真实的DOM节点
2、运行时构建,不包含模板编译器,render函数=>VNode=>真实的DOM节点
也可以将创建的模板存放在一个变量里面
也可以将创建的子元素存放在一个data属性里面
15、Vue的动态组件
作用:通过component的is属性,来切换不同的组件
知识点:
使用将允许您按名称访问全局和本地组件,可以动态绑定我们的组件,根据数据的不同更换不同的组件。在这种情况下,它更像一个容器
:is 属性 是is-bind的缩写
component标签中的is属性决定了当前采用的是哪个组件
Vue 动态组件
【典型案例】动态组件的方式实现不同标签的转换
应用场景为: 侧边菜单栏 实现跳转页面内或跳转外链
示例代码:
v-bind 可以返回一系列属性v-bind="linkProps(to)"
16、Vue的keep-alive
会缓存当前不活动组件的状态
作用:避免多次重复渲染降低性能
常用知识点:1)include – 字符串或正则表达式,只有名称匹配的组件会被缓存
2)exclude-- 字符串或正则表达式,任何名称匹配的组件都不会背后缓存
3)max – 数字。最多可以缓存多少组件实例
4)结合router。缓存部分页面 。 $router.meta.keepAlive选项
1)第一种方式
通过设置include/exclude/max在标签上设置属性,实现组件活性保持
2)第二种方式
结合router的方式
17、vue.config.js配置解决跨域问题
浏览器的同源策略:就是两个页面具有相同的协议/主机/端口号
请求同一个接口时,出现Access-Control-Allow-Origin等,就说明跨域了
vue中解决跨域,配置vue.config.js文件,如果没有就自行新建一个
原理:
1)将域名发送给本地的服务器(localhost:8080)
2)再由本地的服务器去请求真正的服务器
3)因为请求是从服务器端发出的,所以不存在跨域问题
注意:修改vue.config.js 文件,需要重启服务
module.exports = {
devServer: {
// 跨域
proxy: {
"/api": {
// 目标路径
target: "https://www.123.com/index",
// 是否跨域
changeOrigin: true,
// 重写路径
pathRewrite: {
"^/api": "",
},
},
},
},
chainWebpack: (config) => {
// 发行或运行时启用了压缩时会生效
config.optimization.minimizer("terser").tap((args) => {
const compress = args[0].terserOptions.compress;
// 非 App 平台移除 console 代码(包含所有 console 方法,如 log,debug,info...)
compress.drop_console = true;
compress.pure_funcs = [
"__f__", // App 平台 vue 移除日志代码
//'console.dug' // 可移除指定的 console 方法
];
return args;
});
},
};
17、Vue.extend()创建动态组件
动态创建组件:只在事件触发的时候,才产生某组件。平时它并不存在
Vue.extend() extend创建的是一个组件的构造器,而不是一个具体的组件实例
v-if 只在为true的时候被渲染,根据表达式的值在DOM中生成或移除HTML元素
v-show 更具表达式的值来,显示或者隐藏HTML元素
使用场景:
1)动态渲染组件
2)类似于window.alert() 提示组件
1)先创建一个组件模板 toast.vue ,也是以组件的方式创建,动态组件模板是没有data对象的
2)在extend…js文件中
【补充】
效果为:
toast.vue文件
{{ text || '动态通知组件' }}
toast-extend.js文件
/*
* @Author: Null
* @Date: 2022-04-21 10:58:21
* @Description: 动态通知组件----toast-extend.js
*/
import Vue from 'vue'
import Toast from './index.vue'
// 生成构造函数构造器
const ToastConstructor = Vue.extend(Toast)
export function showToast (text, time = 2000) {
const dom = new ToastConstructor({
el: document.createElement('div'),
data () {
return {
text,
isShow: true
}
}
})
document.body.appendChild(dom.$el)
setTimeout(() => {
dom.isShow = false
}, time)
}
页面中使用
测试点击
import { showToast } from '@/components/Toast/toast-extend'
testClick () {
console.log('显示文本')
showToast('显示文本')
},
【补充】Vue.extend() 内使用computed属性失效 , 这个只接受静态的vue模板,如下图
18、javascript的闭包机制
闭包:父函数中,返回子函数
this , this的指向是,由它所在函数调用上下文【执行环境】决定的,而不是,由它所在函数定义的上下文决定的
var 和 let的区别
var 创建的变量会挂载在window对象上,而let 、 const创建的变量不会挂载在window对象上
【补充】const声明引用类型数据,不改变引用地址就不会报错
当我们在单独创建好的js文件中,声明的 const 变量 = 变量值
,必须是先声明,再引用。也就是 const 变量 = 变量值
,声明先在前,引用在后
打印结果为 : 我是老尚
如果我将var 更改为 let ,那么打印值为undefined,因为let 不会将变量挂载到window对象上
19、backdrop-filter(实现毛玻璃效果)
CSS效果为:
知识点:
样式为:
20、如何使用匿名自执行函数创建独立命名空间
匿名自执行函数,IIFE 。它被称之为,立即执行函数
格式:
(function (i) {
console.log("匿名自执行函数IIFE");
})(i);
优点:1)避免变量名的重复;2)独立的作用域;3)提高一些性能,减少对作用域链的查找;4)单独的js模块
()圆括号在js中的作用:1)调用函数表达式;2)对表达式求值
函数有两种创建的方式:1)通过function声明函数:function fun(){} ; 2)函数表达式:var fun = function(){}
示例:
(function (win) {
win.myFun = function () {
console.log("添加属性");
};
win.abc = "123";
console.log("封装好的实例", win); // 打印出 {abc:123,myFun:f() }
})((window.fun = window.fun || {})); // 传入的值相当于独立的命名空间,是在window对象下挂载fun命名空间,
// IIFE表达式的属性和方法不直接挂载到window对象下;
21、v-if和v-for为什么不能同时使用?
原因:1)v-for优先级更高;2)会造成性能浪费
解决方案:1)把v-if写在外层dom中;2)把v-if写在外层template中;3)把v-if写在v-for内
【注意只要保证不同级就可以,因为v-for和v-if都是虚拟dom,都要经过vue的diff算法的,生成真实节点】
22、Promise对象的resolve与pending状态的区别?
知识点:
1)async 函数,返回一个Promise对象,如果在函数中return 一个直接量,async会把这个直接量通过Promise.resolve() 封装成Promise对象
2)Resolved,完成状态
3)Pending,进行状态,表示延迟(异步)操作正在进行
23、await 和 .then方法的区别
示例代码1:
24、hash模式和history模式
这个hash值就是我们访问链接#号之后的东西,
示例: https://www.123.com/hash.html#xx
我们可以通过window.location.hash能够访问到‘#xx’ 这个hash值,这个hash值不会带到服务器去
两个比较重要的参数,一个是newURL【当前路由】和oldURL【上一个路由】
我们可以根据hash值去显示不同的div,从而实现路由跳转
示例为:
Document
首页首页首页首页首页首页首页
详情详情详情详情详情详情详情详情详情详情详情详情详情