【学习日记】浏览器内核、reflow和repaint、 get/post、JS模块化、跨域、组件通信2020-8-28

浏览器内核


浏览器内核多线程,包括:GUI渲染引擎JS引擎浏览器事件触发线程http请求线程定时触发器线程

1、事件触发线程:将需要触发的任务放在事件线程中,当符合触发条件时,会把事件添加到待处理队列的队尾,等待JS引擎处理。【待处理队列中的任务都在等待JS引擎处理】
2、定时器触发线程:setTimeout和setInterval所在线程,用于计时,计时完成添加到事件队列等待JS引擎处理
3、http异步请求线程:在XMLHttpRequest连接之后,开一个线程进行请求,回调函数放在事件队列中,等JS引擎处理。
4、5、浏览器的渲染引擎从上到下加载并解析将CSS代码解析为CSSOM,HTML解析为DOM树。遇到script标签时,HTML挂起渲染,浏览器将网页控制权交给JS引擎。执行完JS代码后,将控制权交给渲染引擎。渲染引擎将CSSOM和DOM合成一颗渲染树,布局渲染树,绘制渲染树,最终将内容展示在页面上。

script标签放在之前:浏览器多线程,但js可以操作dom,影响渲染,所以JS引擎和渲染引擎会互斥。js代码载入后会立即执行,会阻塞HTML的渲染,所以需要确保在DOM构建完成之后运行JS代码。
也可以在js标签内部使用window.οnlοad=function(){}或者onready事件,确保DOM树构建完成之后再运行JS脚本。

style标签放在之前:style放在之前,先构建DOM树,将无样式的HTML结构渲染在页面,再reflow加载样式。如果放在之前,先加载css样式,构建CSSOM树的同时构建DOM树。相比少了一次reflow,并且不会出现样式失效的情况,有利于网络性能优化。


避免reflow和repaint


页面生成后,脚本操作、样式表操作、用户的互动都会触发重流(reflow)和重绘(repaint)
为了避免reflow:
1、将一次性更改的样式写为一个class
2、避免重复操作DOM
3、复杂的元素脱离文档流


get/post


get携带的请求参数在URL字符串中,【Query String Parameters】,有长度限制,不同的浏览器URL长度限制不同,一般在2k-8K之间。get请求具有幂等性和安全性,get的结果会被浏览器主动缓存。
post的请求信息放在请求体中【params form】,没有请求信息长度限制。有可能会改变服务器上的资源,不符合安全性和幂等性。


JS模块化


为什么模块化:
1、每个文件都是一个模块,里面的变量都是私有的,只暴露了共有的属性和方法
2、模块化处理了每个模块之间的依赖关系,处理循环加载

ES6标准:import/export
commonJS规范:require/module exports

import:放在文件的开头,静态引入,在编译时提升到模块头部进行调用。由于import静态引入,使得webpack容易实现tree-shaking,在代码不运行的情况下就能分析出不需要的代码。在production模式下,webpack4自动启动tree-shaking。

export (default):导出值的引用,当原值修改,到处的值也会改变
格式:
export default {
}
or
export let a={…}
[引入时使用import {a} from xxx]

require:动态引入,运行时调用,可以写在代码里。
module.exports:导出的是值的拷贝
格式:
modules.exports={…}
or
exports.foo=…
exports.bar=…


跨域


由于浏览器为了防止同一页面不同站点向服务器发起攻击,提出同源策略,限制了同一个源加载的文档或脚本与来自另外一个源的资源进行交互,只有协议、域名、端口相同,才能进行通信。
同源策略保证安全性的同时,还要保证可用性,所以:
1、script、img、iframe、link、video、audio的src属性可以跨域访问
2、表单提交【但要考虑CSRF】或重定向允许跨域

JSONP:
利用script标签的src属性,请求服务端地址。服务端返回js代码,为客户端所定义的函数的调用,将参数传入,可以实现跨域请求。
JSONP只能发送get请求,由于是通过src属性请求。

CORS【跨域资源共享】:
1、对于简单请求:
(1) 请求方法是以下三种方法之一:

HEAD
GET
POST

(2)HTTP的头信息不超出以下几种字段:

Accept
Accept-Language
Content-Language
Content-Type://只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
 

浏览器会在请求头:

origin:协议+域名+端口

服务端会返回响应头:

Access-Control-Allow-Origin:...
Access-Control-Allow-Credentials:
//是否允许携带cookie,如果允许,Access-Control-Allow-Origin不能为*
content-type:text/html;charset=utf-8

2、对于特殊请求:
请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
在正式通信之前,会先使用OPTIONS方法预检
浏览器发送:

origin:协议+域名+端口
Access-Control-Request-Method:
Access-Control-Request-Headers:

如果服务端许可跨域会发出响应:

Access-Control-Allow-Methods: 
Access-Control-Allow-Headers:
Access-Control-Allow-Credentials: 
Access-Control-Max-Age: 

通过预检之后,之后的请求和正常的请求无区别。
如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。


组件通信


一、EventBus:

1、Vue.prototype.$EventBus = new Vue(),用一个空的vue实例作事件总线
2、在vue实例中,使用this.$EventBus.emit("方法名",function(msg){....}):注册事件,this.$EventBus.on("方法名",msg):对特定的方法,传入msg,运行对应的函数。

EventBus的原理为基于一个消息中心、订阅、发布消息的模式。有on、once、emit、off方法

let EventBusClass=function(){
	this.msgQueue=[]
}
EventBusClass.prototype={
	on:function(msgname,func){
		if(this.msgQueue.hasOwnProperty(msgname)){
			if(typeof this.msgQueue[msgname]==="function"){
				this.msgQueue[msgname]=[this.msgQueue[msgname],func]
			}else{
				this.msgQueue[msgname]=[...this.msgQueue[msgname],func]
			}
		}else{
			this.msgQueue[msgname]=func
		}
	},
	once:function(msgname,func){
		this.msgQueue[msgname]=func
	},
	emit:function(msgname,msg){
		if(!this.msgQueue.hasOwnProperty(msgname)){
			return 
		}
		if(typeof this.msgQueue.msgname==="function"){
			this.msgQueue[msgname](msg)
		}else{
			this.msgQueue.msgname.map(i=>{i(msg)})
		}
	},
	off:function(msgname){
		if(!this.msgQueue.hasOwnProperty(msgname)){
			return 
		}
		delete this.msgQueue[msgname]
	}
}

二、props、emit:

props:父组件给子组件传值,父组件中使用v-bind:childmsg=parentmsg,在父组件中使用props:["childmsg"]进行接收。达到父给子传值的目的,
父组件parentmsg值进行更改可以传递到子组件中。但子组件中不能对值进行更改,如果需要更改,可以在子组件data中定义变量进行接收,也可以子组件中使用computed计算属性。
emit:子组件给父组件传值,子组件的方法中,使用this.$emit("methodname",msg),通过methodname,将msg信息从子组件传递到父组件。

使用v-model="parentmsg"进行父子之间相互传值并实现双向绑定,在子组件中使用props:["value"]进行接收,在子组件的data中,使用curMsg接收props里的value 。
1、子组件改变传递到父组件

//子组件中
watched :
{
	curMsg(newvalue){
		this.$emit("input",newvalue)//监听curMsg,更新后通过input方法传递给父组件
}

2、父组件改变传递到子组件:

//子组件中
watched:{
	value(newvalue){
		this.curMsg=newvalue//监听value,当父组件中的parentmsg改变,将新的值赋给curMsg
	}
}

三、Vuex:

解决跨组件共享数据的需求,来统一管理组件状态。

Vuex的引入:

1import Vuex from 'Vuex'
2、将Vuex挂载在Vue上,Vue.use(Vuex)
3、定义Vuex的实例
const store=new Vuex({
//Vuex的配置
})
4、挂载在vue的实例上
new Vue({
	store:store//当store里的数据变化,对应组件进行更新
})
 

Vuex的配置:

const store=new Vuex({
	state:{},//保存store里的数据,在组件内使用this.$store.state. 进行访问state里的状态
	mutations:{},//保存store里的方法,store里状态的变化只能由store的mutations里的方法来实现
	getters:{},
	actions:{}
})

mutations的使用:

//1、main.js中:
mutations:{
	increment(state){
		state.属性//进行store中的状态操作
	}
}
//组件中:
this.$store.commit('increment')

//2、main.js中:
mutations:{
	increment(state,n=1){//未传入第二个参数时默认为1
		state.属性
	}
}
//组件中:
this.$store.commit('increment'10)//传一个参数
 
//3、main.js中:
mutations:{
	increment(state,params){//子组件里传来的参数
		state.属性
		params.count
	}
}
//组件中:
this.$store.commit({
	type:'increment',
	count:10//可以实现子组件向store内传入多个参数。
})

getters的使用:
相当于computed计算属性,将state里的状态经过简单计算

//1、 main.js中:
getters:{
	filterList(state){
		return state.list.filter(item=>item<10)
	}
}
//组件中:
this.$store.getter.filterList
//2、 main.js中:
getters:{
	filterList(state){
		return state.list.filter(item=>item<10)
	}
	listCount(state,getters){//getters函数中可以传入getters参数,用来访问其他的getter
		return getters.filterList.length
	}
}
//组件中:
this.$store.getter.listCount

actions的使用:进行异步操作

//main.js中:
actions:{
	increment(context){
		context.commit('increment')
	}
}
//组件中:
this.$store.dispatch('increment')

Vuex全局状态管理模式实现
对于每个组件,为了使状态私有化,data是一个函数,返回一个函数。如果直接使用data:{},所有组件的实例都可以对data里的状态进行改变。
借助这一思想,为了实现全局状态管理,使用一个vue实例,data:{},可以实现状态统一管理,并且具有响应式。

//Vuex模拟:
//data---->对应Vuex的state,使用store.state.xxx进行调用
//methods------>对应Vuex的mutations,使用store.commit("函数名")进行调用
 
 
function createStore ({ state, mutations }) {
  return new Vue({
    data: {
      state//使用store.state进行调用
    },
    methods: {
      commit (mutation) {//使用store.commit("函数名")进行调用
        if (!mutations.hasOwnProperty(mutation)) {
          throw new Error('Unknown mutation')
        }
        mutations[mutation](state)//调用
      }
    }
  })
}
 
 
const store = createStore({1⃣️//state用data实现,mutations用methods实现
  state: { count: 0 },
  mutations: {
    inc (state) {
      state.count++
    }
  }
})
//createStore为一个函数[相当于vuex],将state、mutations当作形参传入createStore函数,返回一个vue实例,data里为state,methods里为mutations。返回的store,可以直接使用store.state访问data里的state,store.commit(mutation)传入方法名,访问mutations里的方法。

使用Vue.use(Vuex),调用Vuex中的install方法,install中使用Vue.mixin全局混入,在beforeCreated时调用VuexInit方法。
VuexInit方法里,通过this.$options.store判断store是否存在在Vue实例中。接下来对Vue实例对Vuex的方法进行包装,完成响应式和全局状态管理。
vuex插入
vuex的状态是响应式的,但不会存起来,刷新之后回到初始状态,可以将状态存在localstorage中。

四、ref$parent$children
无法在跨级或兄弟组件间通信

父链:子组件内访问父组件的数据
子组件内:

this.$parent.message='父组件的message数据'

子组件索引:父组件内访问子组件
1、父组件中,在子组件标签上使用ref指定一个名称

<component-a  ref="comA"></component-a>

2、父组件内使用this.$ref.comA对该子组件进行访问,获取其数据。

五、$attrs$listeners
支持跨级通信

$attrs:子组件中,没有被props接收的父组件中v-bind绑定的值,在子组件中可以通过this.$attrs访问到这些值。可以在子组件中使用v-bind="$attrs"this.$attrs里的值传给下一级的组件。
$listeners:包含了父组件中v-on监听事件,可以通过v-on="$listeners"在传入内部组件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值