赶紧点赞收藏起来吧!
rgba() 和 opacity 都能实现透明效果,但最⼤的不同是 opacity 作⽤于元素,以及元
素内的所有内容的透明度,
⽽ rgba() 只作⽤于元素的颜⾊或其背景⾊。(设置 rgba 透明的元素的⼦元素不会继承
透明效果!)
赶紧点赞收藏起来吧!
prototype(显示原型),改prototype上面会有一个constructor属性指向的是该构造函数本身。通过该构造函数new出来的实例对象上面,会有一个隐式原型__ ‘proto’ __指向的是构造函数的本身
原型链:当JS引擎在查找某个对象的属性时,所形成的查找路径为原型链。先查找对象本身有没有该属性,如果没有则沿着隐式原型找到原型对象,看原型对象上面有没有。如果没有,则继续沿着隐式原型查找上一层的原型对象,知道Object.prototype—>null。如果整条函数都没有找到,则返回undefined
call,apply,bind。前两个直接在后面加括号,括号内加要指定的位置。bind则是需要接受了以后再调一下。call中接受数组,直接传元素。apply则需要用数组的方式
冒泡传递:从里到外,依次传递。最里面的叫目标事件源。
addEventListener:监听
语义化标签,拓展运算符,解构赋值,箭头函数,promise事件,let、const
三种:1.回调风格(存在回调地狱,代码阅读性差) 2.promise,但是也没有彻底解决回调地狱的问题。3. async / await
pending fulfilled rejected 当我们创建一个Promise对象的时候,它的状态默认是pending ,要修改它的状态必须调用resolve方法或者reject方法。是一种异步的解决方案。
promise.all([p1,p2,p3]).then().catch() 。有一个失败就进catch
promise.settleter([p1,p2,p3]).then() 。全部执行完毕
promise.race([p1,p2,p3]) 。先进的先执行,只执行一个
const myPromiseObj = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve()
})
})
myPromiseObj.then(()=>{}).catch(()=>{})
拷贝几层的区别。浅拷贝只拷贝一层数据
浅拷贝:Object.assign 或者拓展运算符...
如果有对象在里面,那么只会拷贝对象地址。所以修改拷贝对象数据时,原数据也会发生变化。const newObj = Object.assign({},obj)
(obj为原数组)
深拷贝:const deepCopiedObj = JONS.parse(JSON.stringify(obj))
深拷贝在面对特殊字符的时候会出问题
px是固定单位,rem和em是响应式单位,1rem的大小取决于根节点html的字体大小(即font-size属性),1em的大小取决于其父节点的字体大小,在移动端我们一般用rem,但是要注意:这时html的fontSize属性应该是动态计算的,而不是写死,一般是把当前可视窗口的宽度除以一个经验值得到的比值作为html的fontSize属性值的。
break:跳出整个循环
continue:跳出档次循环,继续执行后面的循环
addEventListener
xxx.addEventListener(‘click’,function(){
});
xxx.addEventListener(‘click’,function(){
})
click:事件类型。function:事件处理函数
js引擎是通过事件循环(Event Loop)实现异步的。
浏览器是多线程的,常用的主要线程有:js引擎,GUI渲染引擎,http请求线程,事件触发线程,定时触发线程
js引擎的两个核心:
1.执行栈:所有的js代码都会加载到执行栈去执行。(先进后出)
2.任务队列:存放是所有待执行异步任务的回调函数。
当js引擎在执行栈中执行代码的时候,如果发现有异步任务(如:发送请求),则把异步任务交给浏览器的其他线程(http请求线程)来处理。然后继续执行后续的代码。
两条线:
1.js引擎继续执行后面的同步任务。当把执行栈清空以后,会到任务队列中查找有没有待执行的回调函数。有则拿到执行栈中去执行,执行完了,又会去任务队列中查找,如此循环往复的过程,就是事件循环(Event Loop)
2.其他线程(http请求线程)在处理异步任务。当把异步任务处理完成以后(请求成功相应之后),会把之前设置的回调函数,推送到js引擎的任务队列中。
什么是事件循环??
js引擎里面有两个非常重要的部分:执行栈和任务队列
所有的代码都要加载到执行栈里面进行执行
在执行过程中如果发现有异步任务,则js引擎会把这个异步任务交给浏览器的其他线程去处理,比如在执行的时候遇到
接口发送,则js引擎会把它交给http请求线程去处理,然后js引擎继续执行后边的同步任务,但是同http请求线程也在同时
运作,当请求发送成功之后,即数据拿到之后,http请求线程会将我们之前在js里面设置的回调函数推送到js引擎的任务队列里面
当js引擎在执行栈里面把任务清空之后,则回到任务队列里面去寻找有没有待执行的回调函数,有则拿到执行栈里面去执行
执行完了之后,又会到任务队列里面去寻找有没有待执行的回调,如此循环往复的过程就是事件循环,理解了事件循环就理解了
* 浏览器是多线程的,js引擎只是其中一个线程,除此之外还有http请求线程,定时触发线程,事件处理线程,GUI渲染线程
通过函数返回函数的方式(闭包),让一个一次性接受多个参数的函数,分解为一次只接受一个参数的若干函数的组合
其作用是为了参数的复用,
function towSum(a,b){
return a + b;
}
//现在对上面的函数进行柯理化
function towSum(a){
return function(b){
return a + b;
}
}
或者
const res = (a)=>(b)=>a+b;
区别:执行时机。前者早于load事件。
在高频触发的事件中,可能会导致一些性能问题,比如在PC端浏览器缩放时(resize事件),我们在事件处理函数里面如果操作DOM,则用户缩放的时候,可能会导致浏览器卡顿,这是我们就可以通过防抖节流来控制操作DOM的频率,避免过快的操作DOM,但是这两者的区别在于:
防抖的思路是:设置一个延时器,延时n秒在执行相应的逻辑(即频繁执行可能会影响性能的逻辑),如果在n秒内,再次执行了,则清空延时器,并从当下开始从新设置延时器,n秒后再执行。用户动作结束之后执行
节流的思路是:在特定的时间段内,相应的逻辑必须有且只能执行一次。
//防抖
function debunce(func,time){
let timer = null;
return function(){
if(timer)clearInterval(timer);
timer = setTimeout(()=>{
func.apply(this,arguments)
},time);
}
}
//节流
function throttle(func,time){
let preTime = +new Date()
return function(){
const curTime = +new Date()
if(curTime - preTime >= time){
func.apply(this,arguments);
preTime = curTime;
}
}
}
for(var i=0;i<10;i++){
setTimeout(()=>{
console.log(i)
},1000);
}
for(let i=0;i<10;i++){
setTimeout(()=>{
console.log(i)
},1000);
}
var age = 100;
let years = 6;
if(age > 12){
let age = 10;
var years = age*3;
}
//问:以上代码有问题吗?
//var没有块级作用域一说。let 声明是不能重复定义的。外面定义的var不会影响块级作用域的let
componentDidMount() {
this.setState({ count: this.state.count + 1 });
console.log('1', this.state.count);
this.setState({ count: this.state.count + 1 });
console.log('2', this.state.count);
setTimeout(() => {
this.setState({ count: this.state.count + 1 });
console.log('3', this.state.count);
this.setState({ count: this.state.count + 1 });
console.log('4', this.state.count);
}, 0);
}
//打印结果
showName();//1
function Cat() {
let showName = function () {
console.log(1);
}
return this;
}
Cat.showName = function () { console.log(2) };
Cat.prototype.showName = function () { console.log(3) };
var showName = function () { console.log(4) };
function showName() { console.log(5) };
Cat.showName();//2
showName();//3
Cat().showName();//4
showName();//4
new Cat.showName();//5
new Cat().showName();//6
//打印结果??
function Cat() {
showName = function () {
console.log(1);
}
console.log('this',this)
return this;
}
Cat.showName = function () { console.log(2) };
Cat.prototype.showName = function () { console.log(3) };
var showName = function () { console.log(4) };
function showName() { console.log(5) };
Cat.showName();
showName();
Cat().showName();
showName();
new Cat.showName();
new Cat().showName();
//打印结果,注意跟上一题的区别
这段代码有什么问题吗?
this.setState((state,props)=>{
return {total:state.total + props.count}
});
查看以下代码:如果你在页面中创建了一个React元素,请完成他的组件定义?
{user=>user===null ? : }{" "}
import React ,{Component} from 'react';
import PropTypes from 'prop-types';
import fetchUser from 'utils';
//fetchUser接收用户名,并返回promise
//当得到用户数据的时候返回resolve状态
class Profile extends Component{
//在这里写下你的代码
}
1. watch中一个值的变化可能会引起多个值的变化,而compouted中多个值的变化会只会影响一个值的变化(即该计算属性返回的值)。可以监听属性,data中的数据。
2. 在watch中我们可能会有副作用,比如发送请求等,而computed则没有副作用,仅仅是根据母体数据衍生出新的数据而已。
1.watch里面可能会有副作用。即,一个函数在执行过程当中对函数的外部造成了一定的影响,该影响就称之为副作用
钩子函数 | created | mounted | updated | beforeDestory |
---|---|---|---|---|
执行时机 | 数据初始化完毕之后 | DOM渲染完毕之后 | 数据更新完毕并且DOM更新完毕之后 | 组件卸载之前 |
作用 | 发送请求 | 发送请求、获取DOM等 | 发送请求(注意加上条件判断)、获取DOM等 | 性能优化相关,比如清除定时器、延时器、解绑事件等 |
组件关系 | 父子 | 兄弟 | 复杂组件关系 |
---|---|---|---|
通信方式 | props/绑定自定义事件、$emit | 状态提升、事件总线 | 状态机vuex |
全局路由守卫(router.beforeEach)、局部(页面内部)路由守卫
vue组件中data里面的数据,在初始渲染之前都会被Object.defineProperty转化成响应式数据,然后这些数据的访问和修改就能够被监听了,每当数据被访问的时候就会触发getter,接着会通知watcher,watcher这是会做一次依赖的收集或者更新,然后watcher会负责通知渲染界面(先转化为虚拟DOM,在映射为真正的DOM)。每当数据被设置的时候会触发setter,接着setter也会通知watcher,watcher在去通知渲染界面
前者操作节点,后者操控样式。前者消耗更大,频繁切换用v-show
打灭。后者优先级更高
缓存组件
类似updated,你可以称作移动版的updated,如果你想获取数据更新过后,DOM的最新状态,则可以使用该方法(如果你直接获取DOM状态是拿不到的,因为在vue里面数据更新虽然是同步的,但是DOM的更新是异步的),当然你也可以利用updated生命周期钩子函数,但是不方便
在组件data中预定义的数据,一定是响应式的,但是我们后续,通过常规的方式(比如someObject.propName = xxx)为data中某些对象添加新的属性时,这些新的属性并不是响应式的,这是可以使用该方法。 this.$set(target,prop,value) 这里的prop属性就是响应式的
路由配置中component属性的值修改为:()=>import(组件路径)
开发思想不同,vue是数据驱动,并且可以组件化开发,jquery则是DOM驱动的,开发效率低
vuex/mixins/自定义指令/插件
它是react组件的一个 生命周期钩子函数,每次数据更新时,首先会执行该钩子函数,如果返回true,表示可以更新界面,然后后续的钩子函数才会陆续执行,返回false,则表示不可以更新,后续的钩子函数也不会执行,当然它默认返回true,
我们一般不会直接在组件中使用该钩子函数,而是会选择PureCompopnent来代替Component,在PureComponent里面就利用来该钩子函数来做优化
它的作用是更新组件内部状态,可以接受两个参数,参数一个一般是个对象,在底层react会通过Object.assign把传入的对象和原来的state进行合并,当然也可以传入一个函数,这个函数会接受两个形参(一个表示state,一个表示props),并返回个对象,要注意的是,这个函数在执行是会保证之前的state已经更新完毕,最后在把该函数返回的对象和原来的对象进行合并。当新的状态值会基于老的状态值时,我们会倾向于给setState的第一个参数传递一个函数进去。它的第二个参数则是一个回调函数,会在数据更新完毕,并且dom更新完毕时执行。
另外,当我们在react事件体系或者组件生命周期钩子函数里面直接调用setState的时候,它是异步的,其他情况是同步的,比如在setTimeout/setInterval里面调用就是同步的
this.setState({name:'张三'});
this.setState((state,props)=>({})
提升渲染性能
父子(props)、兄弟(状态提升、事件总线)、复杂组件关系(状态机)
store action reducer
钩子函数名 | constructor | render | componentDidMount | componentDidUpdate | componentWillUnmount |
---|---|---|---|---|---|
执行时机和作用 | 组件初始化时负责数据的初始化 | 渲染界面 | DOM渲染完毕时执行,可以在这里发送接口,获取DOM等 | 数据更新完毕且界面更新完毕后执行 | 组件即将卸载的时候执行,可以在这里清除定时器,延时器,解绑事件等等 |
1. 获取DOM
2. 获取组件实例
(1)直接用微信官方提供的接口 wx.request接口,跟jquery的ajax差不多
(1)调用微信提供的现成的接口(wx.开头的),即可,具体参数怎么传参照文档即可
(1)简单,直接用微信官方提供的现成组件button即可,具体用法是在button上加一个属性opent-type,方它的值为getPhoneNumber,不知道怎么用的去找微信官文档看看、
提示:跟vue路由懒加载有类似的作用,提升用户体验度,减少首屏渲染速度
https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/basic.html
https://blog.csdn.net/acmdown/article/details/80037660
DNS域名解析------》发起http或者https请求------》服务端处理请求-------》浏览器接受请求响应,并渲染界面
在html头部使用link标签进行DNS预解析
减少请求次数
采用css sprinte(也叫雪碧图或者精灵图),即:如果有多张比较小的背景图,可以合成一个,然后通过background-position去设置背景,而不用每个都去发送请求
某些图片编译成可以是用base64格式(webpack中的url-loader会自动把大小小于某些值的图片编译成base64格式的,然后内嵌到js文件中,所以后续在访问这些图片时,不在需要单独发送请求)
何时用base64格式的图片??,可以参考以下几个因素
这类图片不能与其他图片以CSS Sprite的形式存在,只能独行
这类图片从诞生之日起,基本上很少被更新
这类图片的实际尺寸很小
这类图片在网站中大规模使用
合并脚本和样式表,但是要视情况而定,如果合并脚本或者样式表后,包的体积变得非常大,反而会过度延长网络请求时间,从而导致首先渲染更慢
利用http缓存(新鲜度限值和服务器在验证)
减少请求或者服务端响应式时(当然主要是响应),网络数据包的大小
缩短网络的传输距离,优化网络传输路径
使用单独的图片服务器、使用redis缓存、使用负载均衡等等
css放在body之前,script放在body之后
ssr(server side render—服务端渲染)
requestAnimationFrame
对于动画来讲:定时器为什么不是一个好的方案
1. 开发人员并不知道,间隔多少时间出发一个动画何时,间隔时间如果太短,则会损耗浏览器的性能,如果间隔时间太长,则用户就会感觉卡顿。并且定时器自身机制的问题,它的回调函数触发的时机可能并不是你设置的时间,因为他要等到执行栈里面的任务清空之后再去执行
优化要达到的目的:间隔时间合适,同时用户不会觉得卡
分析:
第一:间隔时间要合适,你自己手动设置肯定不行,因为延时器或定时器触发的时机本就不精确。
第二:用户不会觉得卡顿,首先我们要搞清楚是什么原因导致的卡顿,所以接下来,我们要解释一下浏览器帧的概念
帧:浏览器中的页面是有一帧一帧绘制出来的,当每秒钟绘制的帧数(FPS–Frame Per Second)大于或等于60的时候,用户是感觉不到卡顿的,换言每一帧的时间控制在1s/60,也就是大约16.6毫秒之内时,用户是感觉不到卡顿的。
在一帧中浏览器大致做了如下事情(有时不一定每一步都会执行):
1. 处理用户的交互
2. js解析执行(例如用户事件处理函数中的逻辑)
3. 调用window.requestAnimationFrame中注册的函数(注意:这个函数其实是上一帧中注册的函数,且如果在这一帧中发现有注册的函数,则该函数一定会被执行)
4. Layout(布局,如果第二次触发,我们就称之为回流)
5. Paint(绘制,如果第二次触发我们就称之为重绘)
6. 如果前五步骤执行完了之后,这一帧还有剩余时间,则调用window.requestIdleCallback中注册的函数
也就是说这前五个步骤(其他我们先暂时不用考虑)执行时间总和要在16.6毫秒内,才能保证用户体验。
结论:我们可以利用requestAnimationFrame实现动画逻辑,并且不需要传递时间,在这里触发动画,一般情况1秒钟也能达到60帧(当然动画里的逻辑也不能太复杂,在这一步,如果执行时间过长也会导致掉帧(1秒钟没有达到60次),从而让用户决定卡顿),具体用法如下(实现一个元素,在水平方向上左右移动的效果)
let originStep = 10;
let step = 10;//每次移动的距离
let direction = 1;//移动的方向1表示向右,-1表示向左
let left = 0;//元素的偏移量
const offsetWidth = document.body.offsetWidth;//body的宽度
const el = document.getElementById('myDiv');
const eleWidth = el.offsetWidth;//元素的宽度
const runAnimation = () => {
if(direction === 1){
const remainOffset = offsetWidth - (left + eleWidth);//向右移动时,距离右边距剩余距离
if(remainOffset < step && remainOffset !== 0){
step = remainOffset;//保证向右的移动不会超出右边接线
}
}else{
step = originStep;//当向左移动的时候恢复移动速度
}
if(left <=0){
direction = 1;
}else if(offsetWidth <= left + eleWidth){
direction = -1;
}
const xOffset = left += step*direction;
el.style.transform = `translate(${xOffset}px,0)`;
requestAnimationFrame(runAnimation);//在注册回调
}
requestAnimationFrame(runAnimation)
requestAnimationFrame执行过后会返回一个id,跟定时器一样,我们可以根据这个id取消对应的requestAnimationFrame
const cancelId = requestAnimationFrame(runAnimation);
cancelAnimationFrame(cancelId);
requestIdleCallback
浏览器中显示的页面,是由服务器拼接好模板,然后通过网络传送到浏览器,在有浏览器直接渲染的,由于在网络传输路径中携带了网页的内容,所以ssr模式非常适合做SEO,同时服务器拼装模板的速度会比浏览器快很多,所以SSR模式也会提升网站的首屏渲染速度。但是也会增加服务端的压力。
ssr两个优势
利于seo
缩短首屏渲染速度
什么是webpack ? 模块化的打包工具,原理是,我们必须向它提供一个入口js文件,然后webpack会根据这个入口文件生成一张依赖图,然后在堆这张依赖图里面的文件进行转义和打包,最后生成浏览器能够识别的资源文件(css js 。。。)
作用:
1. 让项目模块化
2. 编译scss/less .vue jsx
3. 性能优化:压缩、代码拆分、图片转base64格式。。。
4. 工程化:自动编译、打包
entry:入口文件
output:出口文件
module
module:{
rules:[
{
test:/\.css$/,
use:['style-loader','css-loader'],//从后往前执行
}
]
}
1. 200
2. 404
3. 401
4. 500
5. 304
6. 301 资源永久重定向
安全:https协议在传输数据的时候,会对数据进行加密,http则是明文传输
性能:http协议的传输性能更高,因为对于传输同样的数据量来讲,由于https协议需要加密处理,所以最终在线路上它的数据量要大一点
费用:https需要购买证书,http则免费
默认端口不同:https–443 http—80
websokect能够实现客户端和服务端的双向通信,http则只能是单向通信,由客户端去请求服务端,服务端不能主动的向客户端推送数据
应用场景不同
websokect 实时通信 数据的实时更新
http 简单的客户端请求服务端
比如后台管理系统,不同的角色看到的侧边栏项是不同的,如何实现?
首先把侧边栏抽象成一个数组,数组里面每一元素(对象)则表示每一个侧边栏,并且在每一个元素里面,我们需要添加一个表示角色的字典,以此来区分,何种角色能够看到当前侧边栏项,
接线来,就要根据当前登录人的角色 去 过滤当前数组,并返回当前角色能看到的侧边栏数据
最后在根据得到的数据,进行遍历循环,渲染侧边栏
Set-Cookie
字段把sessionId投放并存储到客户端(浏览器)的HTTP-cookies
,当客户端下一次发送请求到服务端的时候会自动携带上存有sessionId的cookie。这样后端服务器拿到sessionId和之前存储的sessionId一对比,如果一样就确定该用户是之前的用户,然后就把数据响应到客户端了。JWT
结合用户信息通过一个秘钥(该秘钥会存储到服务器)加密生成一个token,然后把该token返回给客户端,客户端一般把token存储到本地存储,比如localStorage
或者sessionStorage
中。后续在在发送接口的时候会把token从本地存储中取出来,放到请求头Header
中发送给后端服务器。后端拿到token,然后通过之前的秘钥尝试去解该token,如果能解密出来就说明该token是合法的,否则就是不合法的。option setOption(option)
如何实现数据的实时更新 websocket
使用uni.preloadPage页面预加载,但是该方法有兼容性问题,只在app端和H5端有效
参照:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
promise.all同时注意与promise.allSettled做对比
1. 把get方式换成POST
2. 如果我用抓包工具,把数据抓下来,在伪装发送接口,怎么办?-----http-->https
微信小程序上线发布,通过有上角的上传按钮即可哈,上传后,微信官方会做一个审核,审核通过之后,我们就能够通过微信搜索到该小程序了
跨域是浏览器的同源策略导致的问题,当我们的协议、域名和端口号,任意一个不同的时候,就会导致跨域的出现
常见的一个场景,前端请求后端api的时候出现跨域,如何解决?
前端处理:代理服务器 webpack-dev-server (vue.config.js)
devServer:{
proxy:{
"/api":{
target:"http://localhost:8000"
}
}
}
后端处理
GUI渲染线程负责界面的渲染过程
同时解析html/css
解析html ---->DOMTree
解析css—>CSSTree
然后合并DOMTree和CSSTree形成RenderTree(渲染树)
接下来开始layout (布局,回流)
绘制(重绘)
根据这些过程我们可以知晓在写html的时候需要注意的一些事项(性能优化)
1. css放在头部
2. js放在尾部,因为js是有js引擎去编译和执行的,而js引擎和GUI渲染引擎是互斥的
栈、队列、链表、二叉树
这个两个概念是用来衡量一个算法优劣的两个维度,空间复杂度表示,算法所消耗的内存大小,而时间复杂度,表示一个算法所消耗的时间。一般情况下,我们更看重一个算法在时间上的消耗。如果时间太长,则认为这个算法是不太好的需要优化,所以才会有用空间换时间这个说法。
时间复杂度可以用大O表示法。