监听器Observer、解析器Compile、订阅者Watcher、订阅器Dep
vue是通过push来侦测变化的,当vue程序初始化的时候就会对数据data进行依赖收集,一旦数据发生变化,响应式系统就会立刻得知。因此vue是一开始就知道是在哪里发生变化,,然后我们这个vue的响应式系统在绑定一个数据的时候就需要一个watcher(双向数据绑定的原理图)
一旦我们的绑定细粒度过高就会产生大量的watcher,这会带来内存以及依赖追踪的过度开销。而细粒度过低会无法精确侦测变化,因此vue的设计师选择中等的细粒度方案,在组件级别进行push侦测的方式,也就是那套响应式系统,通常我们会第一时间侦测到发生变化的组件,然后在组件内部进行VDOM diff获取更加具体的差异,而VDOM diff则是pull操作,vue是push+pull结合的方式进行变化侦测的。
1.区别:vuex是一种状态管理模式,它的数据存储在内存,localstorage(本地存储)则以文件的方式存储在本地,永久保存;sessionstorage( 会话存储 ) ,临时保存,浏览器关闭就会清除。localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理
2.应用场景:vuex用于组件之间的传值,localstorage,sessionstorage则主要用于不同页面之间的传值。
3.永久性:当刷新页面(这里的刷新页面指的是 --> F5刷新,属于清除内存了)时vuex存储的值会丢失,sessionstorage页面关闭后就清除掉了,localstorage不会。
注:很多同学觉得用localstorage可以代替vuex, 对于不变的数据确实可以,但是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件改变了该数据源,希望另一个组件响应该变化时,localstorage,sessionstorage无法做到。
- 在实例创建以前调用
- 没有实例化,数据访问不到
- 实例被创建完成后调用
- 能拿到数据、修改数据,且修改数据不会触发updated beforeUpdate钩子函数
- 可以在这个钩子函数里发送请求,访问后端接口拿数据
- 判断是否存在el,是否存在template,如果二者都有,以template为主优先,如果没有template,会选择el模板。如果二者都没有,有$mount也可以调用模板
- 真实的dom节点挂载到页面之前
- 编译模板已经结束,虚拟dom已经存在
- 可以访问数据,也可以更改数据,且修改数据不会触发updated beforeUpdate钩子函数
- 在beforeMount和mounted之间隐藏了一个render函数,千万不能写,会覆盖系统函数
- 真实的dom节点挂载到页面以后
- this.$refs找到ref表示的节点
mounted(){ this.$refs.txt.focus();}
- 可以访问和更新数据
且修改数据会触发updated beforeUpdate钩子函数
- 修改之前调用
- 修改之后调用
- beforeUpdate、update可以监控data里的所有数据变化
- 不要在update beforeUpdate修改不定数据,否则会引起死循环
*监听data里的所有的数据,非updated莫属
1).methods方法和computed计算属性,两种方式的最终结果确实是完全相同
2).不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。
3).methods方法,每当触发重新渲染时,调用方法将总会再次执行函数。不需要缓存的话,就用methods
4).对于任何复杂逻辑,你都应当使用计算属性。
watch和computed各自处理的数据关系场景不同
(1).watch擅长处理的场景:一个数据影响多个数据
(2).computed擅长处理的场景:一个数据受多个数据影响
自定义指令全解
1、通过 Vue.directive( id, [definition] )
方式注册全局指令,第一个参数为自定义指令名称(指令名称不需要加 v- 前缀,默认是自动加上前缀的,使用指令的时候一定要加上前缀),第二个参数可以是对象数据,也可以是一个指令函数。
<script>
Vue.directive("focus", {
inserted: function(el){
el.focus();
}
})
new Vue({
el: "#app"
})
</script>
2、通过在Vue实例中添加 directives 对象数据注册局部自定义指令。
script>
new Vue({
el: "#app",
directives: {
focus2: {
inserted: function(el){
el.focus();
}
}
}
})
</script>
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
vue API
(1)router-link
不带参数:
带参数:
(2)this.$router.push()
(函数里面调用)
不带参数:this.$router.push('/home')
带参数:this.$router.push({name:'home',params: {id:'1'}})
(3)this.$router.replace()
(用法同上,push)
(4)this.$router.go(n)
this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
ps : 区别
this.$router.push
跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面
this.$router.replace
跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面
(就是直接替换了当前页面)
this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
query和params区别
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传,刷新之后id还在
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
相同点:v-show和v-if都能控制元素的显示和隐藏。
不同点:
vue不能检测以下变动的数组:
1、当你利用索引直接设置一个项时,vm.items[indexOfItem] = newValue
2、当你修改数组的长度时,例如: vm.items.length = newLength
解决办法:
1、使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象上
2、强制刷新this.$forceUpdate();
3、vm.item.splice(newlength)
vdom:可以看作是一个使用javascript模拟DOM结构的树形结构。
1、用javascript对象模拟DOM树并且渲染DOM树;
2、通过 diff算法 比较新旧DOM树,得到差异的对象;
3、将差异的对象应用到渲染的DOM树中。
在使用 v-model 后既绑定了数据(默认为value) 有添加了一个@input事件监听:
等价于
语法糖:一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件。组件的v-model等同于
v-bind:value="…"
加上v-on:input=“…”
1.hash模式(后面加#号):
hashchange
事件可以知道 hash 发生了哪些变化,然后根据 hash 变化来实现更新页面部分内容的操作。2.history模式:
pushState
和 replaceState
,这两个 API 可以改变 url,但是不会发送请求。这样就可以监听 url 变化来实现更新页面部分内容的操作区别:
- url 展示上,hash 模式有“#”,history 模式没有
- 刷新页面时,hash 模式可以正常加载到 hash 值对应的页面,而 history 没有处理的话,会返回 404,一般需要后端将所有页面都配置重定向到首页路由
- 兼容性,hash 可以支持低版本浏览器和 IE。
设置:在router目录下的index.js文件中,对path属性加上/:id
获取:使用router对象的params.id
父组件中的数据可以通过props流动到子组件中,并且当父组件中的数据发生改变的时候,子组件会自动接收到这个修改后的数据,并且更新页面中的内容。这就是 Vue 中的单项数据流。
所以,数据一般是由父组件提供的,当父组件中的数据发生了改变,子组件就会自动接收到这个数据的变化,从而更新子组件。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
computed | watch |
---|---|
支持缓存,只有依赖数据发生改变,才会重新进行计算 | 不支持缓存,数据变,直接会触发相应的操作 |
不支持异步,当computed内有异步操作时无效,无法监听数据的变化 | 支持异步 |
computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值 | 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值; |
如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一的关系,一般用computed | 当一个属性发生变化时,需要执行对应的操作;一对多; |
如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。 | 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,immediate :组件加载立即触发回调函数执行,deep : 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。 |
总结:
computed:是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch:没有缓存性,更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;当我们需要深度监听对象中的属性时,可以打开deep:true选项,这样便会对对象中的每一项进行监听
keep-alive是Vue提供的一个抽象组件,用来对组件进行缓存,可以使被包含的组件保留状态,避免重新渲染 ,由于是一个抽象组件,所以在v页面渲染完毕后不会被渲染成一个DOM元素
<keep-alive>
<loading></loading>
</keep-laive>
当组件在keep-alive内被切换时组件的activated、deactivated
这两个生命周期钩子函数会被执行
被包裹在keep-alive中的组件的状态将会被保留。
可以在钩子函数 created
、beforeMount
、mounted
中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端返回的数据进行赋值。
但是推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
(1)能更快获取到服务端数据,减少页面loading 时间;
(2)ssr不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
(1)编码阶段
1、子传父
this.$emit(方法名,传递数据)
传输数据@方法名
接受数据2、兄弟组件之间传值
创建一个Vue的实例,让各个兄弟共用同一个事件机制。
传递数据方,通过一个事件触发bus.$emit(方法名,传递的数据)
。
接收数据方,通过mounted(){}触发bus.$on(方法名,function(接收数据的参数){用该组件的数据接收传递过来的数据})
,此时函数中的this已经发生了改变,可以使用箭头函数。
实现原理:
vue-router的原理就是更新视图而不重新请求页面
vue-router可以通过mode参数设置为三种模式:hash模式、history模式、abstract模式
1、hash模式
默认是hash模式,基于浏览器history api,使用window.addEventListener("hashchange",callback,false)
对浏览器地址进行监听。当调用push时,把新路由添加到浏览器访问历史中的栈顶。使用replace时,把浏览器访问历史的栈顶路由替换成新路由。
hash值等于url中#及其以后的内容。浏览器是根据hash值的变化,将页面加载到相应的DOM位置。锚点变化只是浏览器的行为,每次锚点变化依然会在浏览器中留下一条历史记录,可以通过浏览器的后退按钮回到上一个位置。
2、history
history模式基于浏览器的history api ,使用window.onpopstate
对浏览器地址进行监听。对浏览器history api中pushState()、==replaceState()==进行封装,当方法调用,会对浏览器历史栈进行修改。从而实现URL的跳转而无需加载页面。
但是它的问题在于当刷星页面的时候会走后端路由,所以需要服务端的辅助来兜底,避免URL无法匹配到资源时能返回页面。
3、abstract
不涉及和浏览器地址的相关记录。流程和hash模式一样,通过数组维护模拟浏览器的历史记录栈。
服务端下使用。使用一个不依赖与浏览器的浏览历史虚拟管理后台。
4、总结
hash模式和history模式都是通过window.addEventListener()
方法监听hashchange
和popState
进行相应路由的操作,可以通过back、foward、go等方法访问浏览器的历史记录栈,进行各种跳转。而abstract模式是自己维护一个模拟的浏览器历史记录栈的数组。
在beforeMount
之前执行编译过程,第一步通过html-parser
(解析html的一个库)将template
解析成ast抽象语法树,第二步通过optimize
优化ast并标记静态节点和静态根节点,第三步通过generate
(生成器)将ast抽象语法树编译成render字符串并将静态部分放到staticRenderFns
中,最后通过new Function(render)
生成render函数。在beforeMount和mounted之间执行render函数生成VNode,然后通过patch(VNode)
生成dom树并挂载,调用mounted。
这是js的一个特性,js中只有函数能构成作用域,当data是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响。
一,减少DOM数量的方法
1、可以使用伪元素,阴影实现的内容尽量不使用DOM实现,如清除浮动、样式实现等
2、按需加载,减少不必要的渲染
3、结构合理,语义化标签
二、大量DOM时的优化
当对Dom元素进行一系列操作时,对Dom进行访问和修改Dom引起的重绘和重排都比较消耗性能,所以关于操作Dom,应该从以下几点出发:
(1)缓存Dom对象
首先不管在什么场景下,操作Dom一般首先会去访问Dom,尤其是像循环遍历这种时间复杂度可能会比较高的操作。那么可以在循环之前就将主节点,不必循环的Dom节点先获取到,那么在循环里就可以直接引用,而不必去重新查询
let rootElem = document.querySelector('#app');
let childList = rootElem.childElementCount; //假设全是Dom节点
for (let i = 0; i < childList.len; i++) {
}
(2)文档片段
利用==document.createDocumentFragment()==方法创建文档碎片节点,创建的是一个虚拟的节点对象。向这个节点添加dom节点,修改dom节点并不会影响到真实的dom结构。
我们可以利用这一点先将我们需要修改的dom一并修改完,保存至文档碎片中,然后用文档碎片一次性的替换真实的Dom节点。与虚拟dom类似,同样达到了不频繁修改Dom
生命周期 | 发生了什么 |
---|---|
beforecreat | 在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问 |
created | 在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick 来访问Dom |
beforeMount | 发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated |
mounted | 在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs 属性对Dom进行操作 |
beforeUpdate | 发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重新渲染 |
updated | 发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新 |
beforeDestroy | 发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,**比如清除计时器 ** |
destroyed | 发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁 |
activited keep-alive 专属 | 组件被激活时调用 |
deactivated keep-alive 专属 | 组件被销毁时调用 |
实例 | 详细 |
---|---|
vm.$data | Vue 实例观察的数据对象。Vue 实例代理了对其 data 对象 property 的访问。 |
vm.$props | 当前组件接收到的 props 对象 |
vm.$el | Vue 实例使用的根 DOM 元素。 |
vm.$slots | 用来访问被插槽分发的内容 |
vm.$refs | 一个对象,持有注册过 ref attribute 的所有 DOM 元素和组件实例。 |
vm.$emit | 触发当前实例上的事件。附加参数都会传给监听器回调。 |
vm.$on | 监听当前实例上的自定义事件 |
组件 | 详细 |
---|---|
component | 渲染一个“元组件”为动态组件。依 is 的值,来决定哪个组件被渲染。 |
slot | 分发插槽 |
keep-alive | 缓存组件 |
在组件中访问State的方式:
1).this.$store.state.全局数据名称
如this.$store.state.count
2).先按需导入mapState函数: import { mapState } from ‘vuex’
然后数据映射为计算属性: computed:{ …mapState([‘全局数据名称’]) }
触发mutation的两种方式:
- this.$store.commit(‘add’,10)
- …mapMutations([‘add’])
触发actions的两种方式:
- this.$store.dispatch(‘addAsync’,5)
-…mapActions([‘subAsync’])
使用getter的两种方式:
1、this.$store.getters.方法名
2、…mapGetters([‘方法名’])
modules: { moduleA, moduleB }
(1)值类型(基本类型):字符串(string)、数值(number)、布尔值(boolean)、undefined、null ;ex6新增了易种基本数据类型symbol(唯一标识)。
let user = 'zhangsan'
user.age = 18
user.method = function () {
console.log('12345')
}
console.log(user.age) // 输出:undefined
console.log(user.method) // 输出:undefined
(2)引用类型:对象(Object)、数组(Array)、函数(Function)
// 利用typeof来区分
typeof 'abc' // string
typeof 123 // number
typeof true // boolean
typeof undefined // undefined
typeof null // object
// typeof 区分不出来引用类型(除了函数)
typeof {
} // object
typeof [] // object
typeof console.log //function
基本数据类型是存储在栈中,引用类型是存储在堆中
通过Symbol函数生成let s =Symbol()
。Symbol类型的对象永远不相等,即便创建它们的时候传入了相同的值,因此,可借助此特性解决属性名的冲突问题(比如适用于多人编码的时候),这也是该数据类型存在的主要用途,意为标记
(1)事件流的区别 :
(2)件侦听函数的区bai别:
IE使用:
[Object].attachEvent("name_of_event_handler", fnHandler); //绑定函数
[Object].detachEvent("name_of_event_handler", fnHandler); //移除绑定
DOM使用:
[Object].addEventListener("name_of_event", fnHandler, bCapture); //绑定函数
[Object].removeEventListener("name_of_event", fnHandler, bCapture); //移除绑定
就是利用事件冒泡原理,让自己的所触发的事件,让父元素代替执行。
async和defer一样,都不会阻塞其他资源下载,所以不会影响页面的加载。
缺点:不能控制加载的顺序
$.getScript("outer.js",function(){
//回调函数,成功获取文件后执行的函数
console.log("脚本加载完成")
});
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
浅拷贝 的实现方式:
注意:当被复制的对象只有一层的时候,是深拷贝
Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。
深拷贝 的实现方式
原理: 用JSON.stringify将对象转成JSON字符串,再用==JSON.parse()==把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。但不能处理函数。
$.extend( [deep ], target, object1 [, objectN ] )
deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
target 为Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。
css 与 dom 解析完毕后,合并为渲染树(render tree)
1,创建一个空的对象 var obj=new Object();
2,设置原型链,让空对象的原型属性指向构造函数的原型链,obj.proto=Func.prototype;
3,让构造函数的this指向obj,并执行函数体 var result=Func.call(obj);
函数节流:一个函数执行一次后,只有大于设定的执行周期后才会执行第二次。
(应用于手机验证码)
function throttle(func,delay){
let timer;
return function(){
const context=this;
const args=arguments;
//如果定时器存在,就不执行
if(!timer){
timer=setTimeout(function(){
timer=null;
func.apply(context,args);
},delay)
}
}
}
函数防抖:一个需要频繁触发的函数,在规定的时间内,只让最后一次执行,前面的不生效。
function debounce(func,delay){
let timer;
return function(){
const context = this;
const args=arguments;
clearTimeout(timer);
timer=setTimeout(function(){
func.apply(context,args);
},delay);
}
}
1、DNS解析:将域名地址解析成ip地址
2、TCP连接:TCP三次握手
3、发送请求报文:HTTP协议的通信内容
4、接受响应:响应报文
5、渲染页面
6、断开连接:TCP四次挥手
方法 | 解析 |
---|---|
charAt() | 返回给定位置的那个字符 |
charCodeAt() | 返回给定位置的字符编码 |
concat() | 将一个或多个字符串拼接起来,返回一个新的字符串(浅拷贝) |
slice() | 返回被操作字符串的一个子字符串 |
substring() | 返回被操作字符串的一个子字符串,不接受负数,负数转为0 |
substr() | 返回被操作字符串的一个子字符串,两个参数分别是起始位置和长度 |
indexOf() | 从一个字符串搜索指定的子字符串,返回子字符串的第一个位置(没有找到返回-1) |
lastindexOf() | 从一个字符串搜索指定的子字符串,返回子字符串的最后位置(没有找到返回-1) |
trim() | 会创建一个字符串副本,删除前置以及后缀的所有空格 |
search() | 返回字符串中第一个匹配项的索引 |
replace() | 替换 |
改变原数组
方法 | 解析 |
---|---|
reverse() | 数组翻转 |
sort() | 排序(a-b升序)(b-a降序) |
splice() | 返回剪接的元素数组,原数组变化 ,索引从0开始(第一参数是索引(从0开始),第二是长度,第三是添加新的数据) |
push() | 可以在数组的末属添加一个或多个元素 |
pop() | 把数组中的最后一个元素删除 |
shift() | 把数组中的第一个元素删除 |
unshift() | 可以在数组的前端添加一个或多个元素 |
不改变原数组
方法 | 解析 |
---|---|
join() | 数组转成字符串(原数组不变)默认分隔符为逗号 |
split() | 字符串变数组 |
slice() | 返回从原数组中指定开始索引(包含)到结束索引(不包含)之间的项组成的新数组,原数组木变 ,索引从0开始 |
foreach() | 用于调用数组的每个元素,并将元素传递给回调函数。 |
concat() | 返回合并后的新数组,原数组没变 |
filter() | 创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。(筛选) |
map() | 返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值 |
reduce() | 数组的归并方法,但是reduce() 可同时将前面数组项遍历产生的结果与当前遍历项进行运算 |
reduceRight() | 遍历的顺序相反,它是从数组的最后一项开始,向前遍历到第一项。 |
方法 | 解析 |
---|---|
typeof | 一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型 |
instanceof | 用来判断 A 是否为 B 的实例 |
Array.isArray() | 用以确认某个对象本身是否为 Array 类型 |
constructor | prototype上添加一个 constructor 属性,并让其指向 F 的引用 |
toString.call() | toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] |
方法 | 解析 |
---|---|
caller | 函数的一个属性,引用当前函数的函数 |
callee | 不是函数对象的属性,是函数上下文中arguments对象的属性 |
方法 | 解析 |
---|---|
obj.appendChild() | 添加 |
obj.removeChild() | 删除 |
obj.replaceChild() | 替换 |
obj.insetBefore() | 插入 |
阻止冒泡事件:e.stopPropagation()
阻止默认事件:e.preventDefault()
在一个函数里声明的函数叫闭包函数,闭包函数能够访问到所在的外部函数的变量和参数,形成闭包。闭包函数return出去之后才能使用。
$.ajax({
method:'POST',//设置请求方式
headers:{
'Content-type':'application/json'},//json格式提交数据
data:JSON.stringify({
a:10})
url:'...',
success:function(data){
console.log(data)},
})
fetch("url",{
method:"POST",
body:"a=2&b=3",//数据,不能用data这个参数
}).then(res=>res.json()).then(data=>console.log(data))
axios({
url:'',
method:'',
data:{
a:12}
}).then(res=>{
console.log(res.data)})
用法详解
2、创建原生的ajax请求
<script type="text/javascript">
window.onload = function(){
var xhr=null;
if(window.XMLHttpRequest){
xhr=new XMLHttpRequest();
}
xhr.open('get','01.php',true);
xhr.send(null);
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if(xhr.status==200){
var data=xhr.responseText;
}
}
}
}
</script>
promise版本:
function queryData(url) {
var p = new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('get', url);
xhr.send(null);
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) return;
if (xhr.readyState == 4 && xhr.status == 200) {
//处理正常请况
resolve(xhr.responseText);
} else {
//处理异常情况
reject('服务器错误');
}
};
});
}
区别:
1、箭头函数语法上比普通函数更加简洁(ES6中每一种函数都可以使用形参默认值和剩余预算符)
2、箭头函数中的this始终指向箭头函数定义时的离this最近的一个函数,如果没有最近的函数就指向window。使用call或apply等任何方式都无法改变this指向。
3、箭头函数中没有arguments(类数组),只能给予…ARG(剩余参数)获取传递的参数集合(数组)。
4.箭头函数不能new执行(因为:箭头函数没有prototype)
async function(){
try{
var data1=await query(sql1);
var data2=await query(sql2);
var data3=await query(sql3);
}catch(e){
console.log(e);
}
}
用一对反引号(`)标识,它可以当作普通字符串使用,也可以用来定义多行字符串,也可以在字符串中嵌入变量,js表达式或函数,变量、js表达式或函数需要写在${ }中。
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
数组中的值会自动被解析到对应接收该值的变量中,数组的解构赋值要一一对应 如果有对应不上的就是undefined
Promise.allSettled(iterable)概念
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved,rejected]);
allSettledPromise.then(function (results){
console.log(results);
})
// 输出:
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]
@media only screen and (min-width:500px) and (max-width:700px){......}
实际的样式值可以理解为浏览器的计算样式
style对象中包含支持style属性的元素为这个属性设置的样式信息,但不包含从其他样式表层叠继承的同样影响该元素的样式信息。
DOM2 style 在document.defaultView上增加了getComputedStyle()方法。这个方法接收两个参数:要取得计算样式的元素和伪元素字符串(如":after")。如果不需要查询伪元素,则第二个参数可以传null。getComputedStyle()方法返回一个CSSStyleDeclaration对象(与Style属性的类型一样),包含元素的计算样式。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
#myDiv {
background-color: blue;
width: 100px;
height: 200px;
}
</style>
</head>
<body>
<div id="myDiv" style="background-color: red; border: 1px solid black;">
</div>
</body>
<script>
let myDiv = document.getElementById("myDiv");
let computedStyle = document.defaultView.getComputedStyle(myDiv,null);
console.log(computedStyle.backgroundColor);
</script>
</html>
(1)绝对定位:
top:50%;left:50%;margin-top:1/2height; margin-left:1/2width;
margin:auto; top:0;bottom:0;left:0;right:0;
(3)flex布局:display:flex;justify-content:center;align-item:center;
(4)对父容器使用display: table-cell
+vertical-align: middle
;使其内的子元素实现垂直居中;
src和href都是属于外部资源的引用,像一些图片,css文件,js文件,或者其他的web页面。
src通常用作“拿取”(引入),href 用作 “连结前往”(引用)
可以用这样的一句话来概括:src用于替换这个元素,而href用于建立这个标签与外部资源的关系。
一、什么是跨域
跨域是针对浏览器的"同源策略"提出的说法。之所以有"同源策略"这种模式是基于网络安全方面电视考虑。所谓的同源策略关注三点:1、协议 2、域名 3、端口
二、哪些网络资源涉及到跨域
"同源策略"对于跨域网络资源的设定非常清晰。
这些场景涉及到跨域禁止操作:
1、无法获取非同源网页的cookie、localstorage和indexedDB
2、无法访问非同源网页的DOM(iframe)。
3、无法非同源地址发送AJAX请求或fetch请求(可以发送,但浏览器拒绝接受响应)
为什么要阻止跨域呢?上边我们说过是基于安全策略:比如一个恶意网站的页面通过iframe嵌入了银行的登录页面(二者不同源),如果没有同源限制,恶意网页上的javaScript
cors跨域资源共享
通过href或者src去请求的js脚本、css、图片、视频不存在跨域问题,只有通过ajax请求才会存在跨域问题
#app.js
app.get("/",function(req,res){
var funcname=res.query.callback;//回调函数f()
res.send(funcname+"('你好')";//f('你好')
})
#index.html
<script>
function f(data){
alert(data);
}
</script>
<script src="http://localhost:91?callback=f"> </script>
(1) 在 HTTP 中,数据称为资源,可以是 html 文档、图片、也可以是普通文本。资源是通过 URL 进行定位的。
(2)URL 的组成部分有:
http://jsonplaceholder.typicode.com/comments?postId=1
服务端收到 url 会解析它们并返回相应的数据。
(4)HTTP 协议是无状态的,每次客户端发出的请求都被认为是从全新的客户端发出来的,如果需要记录状态则需要使用 cookie 和 session 来保持会话,实现登录和购物车之类的功能。
(5)现在 HTTP/2已经可以正式开始使用了,它跟 HTTP 1.1不同的是:
DNS是什么 — 域名系统,作为域名和IP地址相互映射的一个分布式数据库
浏览器根据自定义的规则,提前去解析后面可能用到的域名,来加速网站的访问速度。简单来讲就是提前解析域名
,以免延迟。
使用方式
<link rel="dns-prefetch" href="//wq.test.com"/>
这个功能有个默认加载条件,所有的a标签的href都会自动去启用DNS Prefetching,也就是说,你网页的a标签href带的域名,是不需要在head里面加上link手动设置的。但a标签的默认启动在HTTPS不起作用。
这个时候要使用meta里面http-equiv来强制启动功能。
<meta http-equiv="x-dns-prefetch-control" content="on"/>
总结
可以用在web下的性能优化1(网络方向)
我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。这是一个错误的说法,实际上HTTP协议从未规定get/post的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。为了明确这个概念,我们必须再次强调下面几点:
1、 首先回答加载的流程,回答要点
2、 稍加分析
https://www.baidu.com
的时候,首先经过DNS解析,baidu.com
对应的IP是101.211.145.250
,然后浏览器向该IP发送HTTP请求。3、接下来会是渲染过程,回答要点
时,会执行并阻塞渲染4、加以分析
和
这种外链加载CSS和JS的标签,浏览器会异步下载,下载过程和上文中下载HTML的流程一样。只不过,这里下载下来的字符串是CSS或者JS格式的。
就停止渲染,执行JS代码。因为浏览器渲染和JS执行共用一个线程,而且这里必须是单线程操作,多线程会产生渲染DOM冲突。待
内容执行完之后,浏览器继续渲染。最后再思考一个问题–为何要将JS放在HTML底部?–JS放在底部可以保证让浏览器优先渲染完现有的HTML内容,让用户先看到内容,体验好。另外,JS执行如果涉及DOM操作,得等待DOM解析完成才行,JS放在底部执行时,HTML肯定都解析成了DOM结构。JS如果放在HTML顶部,JS执行的时候HTML还没来的及转换为DOM结构,可能会报错。针对每个过程进行优化
网页从加载到呈现会经历一系列过程,针对每个过程进行优化
1.1网络连接方面优化
主要是针对重点向、DNS、TCP连接进行优化
dns-prefetch
,同时将同类型的资源放到,减少domain
数量也是可以减少DNS查找Keep-Alive
选项和服务器建立长连接,让多个资源通过一个TCP连接传输。1.2请求方面优化
减少浏览器向浏览器发送的请求数目以及请求资源的大小是请求优化的核心思想
1、合理运用浏览器对于资源并行加载的特性,在资源的加载的数量和资源的大小之间做一个合理的平衡
2、在移动端页面中,将首屏的请求资源控制在5个以内,每个资源在Gzip之后的大小控制在28.5KB之内,可以显著的提升首屏时间。
静态资源使用CDN等方式放在和当前域不同的域上,以避免请求静态资源时携带Cookie
资源分散到多个不同的CDN中,最大化的利用浏览器的并行加载能力
1、利用Manifest + 本地存储做持久化缓存
2、将对访问实时性要求不高的其他资源,如图片、广告脚本等内容存放在IndexDB或WebSQL中,IndexDB后WebSQL的存储容量比LocalStorage大的多,可以用来存放这些资源。
3、使用localForage操作持久化缓存
4、库文件放入CDN或者开启强缓
1.3、响应优化
1.3.1 页面加载的核心指标
1.4 浏览器首屏渲染优化
1.4.1 首屏时间
指用户打开网站开始,到浏览器首屏内容渲染完成的时间。对于用户体验来说,首屏时间是用户对一个网站的重要体验因素。通常一个网站,如果首屏时间在5秒以内是比较优秀的,10秒以内是可以接受的,10秒以上就不可容忍了。超过10秒的首屏时间用户会选择刷新页面或立刻离开。