CSS
盒模型
-
模型的组成大家肯定都懂,由里向外content,padding,border,margin.
-
盒模型是有两种标准的,一个是标准模型,一个是IE模型。
-
盒模型的宽高只是内容(content)的宽高,而在IE模型中盒模型的宽高是内容(content)+填充
(padding)+边框(border)的总宽高。
css如何设置两种模型
这里用到了CSS3 的属性 box-sizing
/* 标准模型 */
box-sizing:content-box;
/*IE模型*/
box-sizing:border-box;
CSS的单位和应用场景及Rem的设置
rem浏览器兼容性:
IE9以上等支持CSS3的浏览器是肯定的可以支持的,如果想要兼容IE低版本,那可以考虑针对IE9以下低版本浏览器,用px来实现
1rem = 16px
1px = 2rpx
定位,回流与重绘
定位:position 属性
- absolute 生成绝对定位的元素,相对于值不为 static 的第一个父元素进行定位。
- fixed (老 IE 不支持) 生成绝对定位的元素,相对于浏览器窗口进行定位。
- relative 生成相对定位的元素,相对于其正常位置进行定位。
- static 默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right z-index 声明)。
- inherit 规定从父元素继承 position 属性的值。
Reflow,即回流。
一般意味着元素的内容、结构、位置或尺寸发生了变化,需要重新计算样式和渲染树
Repaint,即重绘。
意味着元素发生的改变只是影响了元素的一些外观之类的时候(例如,背景色,边框颜色,文字颜色等),此时只需要应用新样式绘制这个元素就可以了
回流产生原因
页面渲染初始化
DOM结构改变,比如删除了某个节点
render树变化,比如减少了padding
窗口resize
最复杂的一种:获取某些属性,引发回流
CSS 选择符有哪些?哪些属性可以继承?
-
id 选择器( # myid)
-
类选择器(.myclassname)
-
标签选择器(div, h1, p)
-
相邻选择器(h1 + p)
-
子选择器(ul > li)
-
后代选择器(li a)
-
通配符选择器( * )
-
属性选择器(a[rel = “external”])
-
伪类选择器(a:hover, li:nth-child)
-
可继承的样式: font-size font-family color, UL LI DL DD DT;
-
不可继承的样式:border padding margin width height ;
CSS 优先级
优先级为: !important > id > class > tag important 比 内联优先级高
如何居中 div?
-
负 margin 方法
在父元素上position:relative;
子元素position:absolute;left:50%;top:50%;maring-left子元素宽度的一半 margin-top子元素高度的一半 -
位移方法
和负 margin 方法方法一样
子元素把margin改为transform:translate(-50%,-50%)
-
table-cell方法
通过将盒子转换为 table 元素,table 元素本身是可以通过属性来控制位置,div 上面 的 vertical-align: middle 是控制垂直方向上的居中的,而 text-align: center 是控制 水平方向的
<div>
<img src='1.png'/>
</div>
<style>
div{
width:300px;
height:300px;
background:#eee;
display:table-cell;
vertical-align:middle;
text-align:center;
}
img{
vertical-align:middle;
}
</style>
-
div绝对定位水平垂直居中 margin:auto实现绝对定位元素的居中
div{ width:200px; height:200px; background:pink; position:absolute; left:0; top:0; bottom:0; right:0; margin:auto; }
-
弹性盒
父元素设置display:flex; align-items:center; 控制垂直方向居中 just-content:center; 控制水平方向居中
display 有哪些值?说明他们的作用。
-
block 象块类型元素一样显示。
-
none 缺省值。象行内元素类型一样显示。
-
inline-block 象行内元素一样显示,但其内容象块类型元素一样显示。
-
list-item 象块类型元素一样显示,并添加样式列表标记。
-
table 此元素会作为块级表格来显示
-
inherit 规定应该从父元素继承 display 属性的值
IFC、BFC与清除浮动
浮动元素引起的问题
(1)父元素的高度无法被撑开,影响与父元素同级的元素
(2)与浮动元素同级的非浮动元素(内联元素)会跟随其后
(3)若非第一个元素浮动,则该元素之前的元素也需要浮动,否则会影响页面显示 的结构
解决方法:
使用 CSS 中的 clear:both;属性来清除元素的浮动可解决 2、3 问题,对于问题 1,添 加如下样式,给父元素添加 clearfix 样式:
.clearfix:after{
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.clearfix{
display: inline-block;
}
清除浮动的几种方法:
1,额外标签法,
2,使用 after 伪类 #parent:after{ content:"."; height:0; visibility:hidden; display:block; clear:both; }
3,浮动外部元素
4,设置 overflow 为 hidden 或者 auto
兼容处理
常见的浏览器内核可以分四种:
Trident、Gecko、Blink、Webkit
常见的兼容性问题:
1、不同浏览器的标签默认的外补丁( margin )和内补丁(padding)不同
解决方案: css 里增加通配符 * { margin: 0; padding: 0; }
2、IE6双边距问题;在 IE6中设置了float , 同时又设置margin , 就会出现边距问题
解决方案:设置display:inline;
3、当标签的高度设置小于10px,在IE6、IE7中会超出自己设置的高度
解决方案:超出高度的标签设置overflow:hidden,或者设置line-height的值小于你的设置高度
4、图片默认有间距
解决方案:使用float 为img 布局
5、IE9一下浏览器不能使用opacity
解决方案:
opacity: 0.5;filter: alpha(opacity = 50);filter: progid:DXImageTransform.Microsoft.Alpha(style = 0, opacity = 50);
6、边距重叠问题;当相邻两个元素都设置了margin 边距时,margin 将取最大值,舍弃最小值;
解决方案:为了不让边重叠,可以给子元素增加一个父级元素,并设置父级元素为overflow:hidden;
7、cursor:hand 显示手型在safari 上不支持
解决方案:统一使用 cursor:pointer
8、两个块级元素,父元素设置了overflow:auto;子元素设置了position:relative ;且高度大于父元素,在IE6、IE7会被隐藏而不是溢出;
解决方案:父级元素设置position:relative
原生JS
闭包
闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭突破作用链域,将函数内部的变量和方法传递到外部。
闭包的特性:
1,函数内再嵌套函数
2,内部函数可以引用外层的参数和变量
3,参数和变量不会被垃圾回收机制回收 闭包的使用场景 常见的闭包的使用场景就是模块化,用来做模块内部的实现通过接口的扩展贡块使用
在就是闭包可以用来缓存值,减少不必要的技术,例如 vue 里面的计算属性
闭包的适用场景
闭包的适用场景非常广泛,首先从闭包的优点出发就是:
- 减少全局环境的污染生成独立的运行环境
模块化就是利用这个特点对不同的模块都有自己独立的运行环境,不会和全局冲突, 模块和模块之间通过抛出的接口进行依赖使用 以及像我们常用的jquery类库(避免和全局冲突使用闭包实现自己独立的环境)
- 可以通过返回其他函数的方式突破作用域链 可以利用这个功能做一些值的缓存工作
例如常见的设计模式(单例模式),以及现 在比较火的框架vue中的计算属性
其实当遇到以下场景的时候都可以使用闭包
1.维护函数内的变量安全,避免全局变量的污染。
2.维持一个变量不被回收。
3.封装模块
闭包的缺点
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大。所以在闭包不用之 后,将不使用的局部变量删除,使其被回收。在IE中可能导致内存泄露,即无法回收 驻留在内存中的元素,这时候需要手动释放
构造函数
-
new实例之后发生了什么
function New(func){ //声明一个中间对象,该对象为最终返回的实例 var res = {}; if(func.prototype != null){ //将实例的原型指向构造函数的原型 res.prototype = func.prototype; } //ret为构造函数执行的结果,这里通过```apply```,将构造函数内部的```this```指向修改为指向```res```,即实例对象 var ret = func.apply(res,Array.prototype.slice.call(arguments,1)); //当我们在构造函数上明确指定了返回对象时,那么```new```的执行结果就是该返回对象 if((typeof ret === 'object' || typeof ret === 'function')&&ret != null){ return ret; } return res; }
1、声明一个中间对象
2、将该中间对象的原型指向构造函数的原型
3、将构造函数中的this指向该中间对象
4、返回该中间对象,即返回实例对象
- 构造函数存在的问题:
每个方法都要在每个实例上重新创建一遍。(每定义一个函数就是实例化了一个对象),不同实例上的同名函数是不相等的。其解决方法便是使用原型模型
原型模式:
-
每个函数都有一个属性prototype,这个属性对应的值是一个对象,我们叫做原型对象
原型对象的作用:该对象的属性和方法是被所有的实例共享的,换句话就是所有的实例都能使用原型对象的属性和方法 -
原型对象有一个constructor属性指向构造函数
即Person.prototype.constructor指向Person -
proto
function A(){
}
var a1 = new A();
console.log(A.prototype.constructor===A);//true
console.log(a1.proto===A.prototype);//true
var A = new Function()
//A是一个函数 A是Function的一个实例
console.log(A.proto===Function.prototype);//true
//Array 构造函数 是Function的一个实例每个对象都有这个属性,实例的__proto__指向的是原型对象(该属性对应的值就是原型对象)
变量提升配合作用域链
原型链
- 原型链继承
每个构造函数都有一个指向原型对象的属性(prototype),每个原型对象也有一个指向构造函数的一个属性(constructor),
每个实例都有一个指向原型对象的属性(proto)
如果我把某个类型(假如A)原型对象指向另外一个类型(假如是B)的实例,
该A的原型对象是不是也有一个属性指向另外一个类型的(B)原型对象,如果我再把B的原型对象指向另外一个类型(假如是C),上面的结论仍然成立,
层层递进,形成了实例与原型对象的一个链条,就被称作原型链
function A(){
}
function B(){
}
A.prototype = new B();
console.log(A.prototype.constructor==B)//TRUE
console.log(A.prototype.__proto__==B.prototype)//TRUE
console.log(B.prototype.__proto__==Object.prototype)//TRUE
//A继承B B继承Object
console.log(Object.prototype.__proto__);//null
注意:
使用原型链的方式进行继承,子类型定义自己的方法或者子类型重写父类型的方法,注意书写的位置,
要放在原型链继承的那条语句下面
当对象访问属性或方法时,先从自身查找,找不到就去构造函数的原型对象上找,如果找不到,就去原型对象的原型对
象上找,直到 Object.prototype.proto == null. 如果没找到,返回 undefined。
原型链继承的问题:
父类型的引用类型的属性会被子类型所有的实例共享
原型链继承一般不会单独使用
- 构造函数继承
在子类型的构造函数里面调用父类型的构造函数,使用call或者apply
但是构造函数继承解决原型链引用数据类型的问题,但是它还有作为构造函数自己的问题
构造函数继承一般也不会单独使用
function B(name,age,famliy){ this.name = name; this.age = age; this.famliy = famliy; this.friends = ["zhangsan","lisi","wangwu"]; } B.prototype.say = function(){ console.log("能说话"); } function A(name,age,famliy){ (第一种方法) B.call(this,name,age,famliy); (第二种)B.apply(this,[name,age,famliy]); (第三种)B.apply(this,arguments); this指s1 } s1 = new A("aa",21,"张"); B.prototype.say = function(){ console.log("能说话"); } s1.say(); //报错
- 通常使用原型链和构造函数结合起来进行继承
function Father(name,age){ this.name = name; this.age = age; this.friends = ["zhangsan","lisi","wangwu"]; } Father.prototype.say = function(){ console.log("能说话"); //能说话 } function Son(name,age){ Father.call(this,name.age);//this指s1 } Son.prototype = new Father(); var s1 = new Son("heihei",20); console.log(s1.friends);//["zhangsan", "lisi", "wangwu"] s1.say();
- 系统自带继承关系
在javascript 中,继承就是靠原型链来实现的。
Array 继承了Object Function 继承了Object Date 继承了Object RegExp 继承了Object
Array.prototype=new Object() Array.prototype.__proto__===Object.prototype Function.prototype.__proto__===Object.prototype Date.prototype.__proto__===Object.prototype Object.prototype.__proto__===null
- 确定原型和实例的关系:
实例 instanceof 构造函数
原型对象.isPrototypeOf(实例)
event loop
event delegation/proxy
节流与防抖
防抖,直到事件触发间隔频率大于一定时间再触发一次
export function Debounce(func, ms=500){
let timer = 0;
return function(){
window.clearTimeout(timer);
timer = setTimeout(()=>{
func()
}, ms)
}
}
节流,事件每隔一定时间一定触发一次
export function Throttle(func, ms=500){
let start = +new Date();
return function(){
let current = +new Date();
if (current - start > ms){
func()
start = current;
}
}
}
Promise原理
-
Promise的实例有三种状态
1 成功 resolve 2 失败 reject 3 进行中 pending
-
promise的实例有一个then方法
该方法有两个参数,第一个参数是成功对应的函数,第二个参数是失败对应的函数;
第二个参数可以省略不写,可以写到catch里面
p1.then(function(){}).catch(funtion(){})
- Promise的两个方法
Promise.all([p1,p1]) all方法接收一个由多个Promise组成的数组作为参数,返回一个新的Promise实例
当数组里面的所有的实例都是成功的状态,那么这个新的实例它的状态才是成功,
并且返回的数据是这多个实例返回的数据组合成的数组
失败的状态:有一个实例失败,那么这个新实例的状态就是失败,并且新实例返回的数据是最先失败的那个实例的数据
race
和all方法类似,接收一个由多个Promise组成的数组作为参数,返回一个新的Promise实例
哪一个实例最先执行完,该实例对应的状态就是新的实例的状态,并且返回的数据也是新的实例的数据
SPA路由原理
前端路由: 随着(SPA)单页应用的不断普及,前后端开发分离,目前项目基本都使用前端路由,在项目使用期间页面不会重新加载
优点:
1、用户体验好,和后台网速没有关系,不需要每次都从服务器全部获取,界面展现快。
2、可以再浏览器中输入指定想要访问的url路径地址。
3.实现了前后端的分离,方便开发。有很多框架都带有路由功能模块。
缺点:
1、对SEO不是很友好
2、在浏览器前进和后退时候重新发送请求,没有合理缓存数据。
3,初始加载时候由于加载所有模块渲染,会慢一点。
- SPA 中前端路由有2中实现方式
修改 url 中 Hash
利用 H5 中的 history
hash
www.test.com/#/ 就是 Hash URL,当 # 后面的哈希值发生变化时,可以通 过 hashchange 事件来监听到
URL 的变化,从而进行跳转页面,并且无论哈希值如何 变化,服务端接收到的 URL 请求永远是 www.test.com。
window.addEventListener(‘hashchange’, () => { // … 具体逻辑 })Hash
模式相对来说更简单,并且兼容性也更好。
History 模式
History 模式是 HTML5 新推出的功能,主要使 用 history.pushState 和 history.replaceState 改变 URL。
通过 History 模式改变 URL 同样不会引起页面的刷新,只会更新浏览器的历史记录。
// 新增历史记录 history.pushState(stateObject, title, URL)
// 替换当前历史记录 history.replaceState(stateObject, title, URL)
当用户做出浏览器动作时,比如点击后退按钮时会触发 popState 事件
window.addEventListener(‘popstate’, e => {
// e.state 就是 pushState(stateObject) 中的 stateObject console.log(e.state)
})
- 两种模式对比
1.Hash 模式只可以更改 # 后面的内容,History 模式可以通过 API 设置任意的 同源 URL
2.History 模式可以通过 API 添加任意类型的数据到历史记录中,Hash 模式只能更 改哈希值,也就是字符串
3.Hash 模式无需后端配置,并且兼容性好。History 模式在用户手动输入地址或者 刷新页面的时候会发起 URL 请求,后端需要配置 index.html 页面用于匹配不到静态资 源的时候
本地存储localStorage、sessionStorage与cookie之间的区别
cookie是网站为了标示用户身份而储存在用户本地终端上的数据(通 常经过加密),cookie还可以设置有效时间 cookie数据始终在同源的http请求中携带(即使不需要),会在浏览器和服务器间 来回传递, 每次ajax请求都会吧cookie传送到后台,cookie一半用做用户登陆,后台可以根cookie信息判断用户是否登陆状态
sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存
区别在于
存储大小:
cookie数据大小不能超过4k。
sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得 多,可以达到5M或更大。
有期时间:
localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;
sessionStorage 数据在当前浏览器窗口关闭后自动删除。
cookie 设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
Vue
深入Vue的响应原理?
“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。
当一个Vue实例创建时,vue会遍历data选项的属性,用Object.defineProperty将它们转为getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
Vue多版本之间的区别(运行时依赖,运行时编译)?
Vue中computed、watch与methods的区别
https://blog.csdn.net/lfcss/article/details/89136943
多种指令与自定义指令
- 局部自定义指令(只针对组件内的元素)
directives:{ // 自定义指令的名字 autoFocus:{ // 钩子函数,被绑定元素插入父节点时调用 (父节点存在即可调用,不必存在于 document 中)。 inserted(el){ el.focus() console.log( 'inserted' ); } } }
- 定义全局自定义指令
在任意页面的任意input里加上v-autoFcs
Vue.directive('autoFcs',{ // 钩子函数,被绑定元素插入父节点时调用 (父节点存在即可调用,不必存在于 document 中)。 inserted(el){ el.focus() console.log( 'inserted' ); } }
路由传参与导航守卫
- 父子传参
props
父组件上,通过动态属性传递
子组件,props接收
1.数组形式接收
2.对象形式接收
props:{
属性名:类型
}
- 子父传参
子组件里面使用$emit
$emit(’事件名称‘,‘实参1’,‘实参2’) 触发
<button @click="$emit('addCount',count)"></button>
父组件里面的子组件使用
@事件名称=’事件处理程序‘
<list @addCount='add'></list>
- 非父子通信
在main.js里面Vue.prototype.$bus=new Vue()
子组件
在methods里面
this.$bus.$emit('addCount',num,id)
非父组件 $bus
在created 生命周期写
this.$bus.$on('addCount',(num,id)=>{ })
Element UI && so on
什么是MVVM?
MVVM是Model-View-ViewModel的简写。即模型-视图-视图模型。
【模型】指的是后端传递的数据。
【视图】指的是所看到的页面。
【视图模型】mvvm模式的核心,它是连接view和model的桥梁。
它有两个方向:
一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。
二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定。
总结:在MVVM的框架下视图和模型是不能直接通信的。它们通过ViewModel来通信,ViewModel通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中的View 和 ViewModel可以互相通信
React
虚拟dom和真实dom
-
虚拟DOM的损耗计算:
总损耗 = 虚拟DOM增删改 + (与Diff算法效率有关)真实DOM差异增删改 + (较少的节点)排版与重绘
-
直接使用真实DOM的损耗计算:
总损耗 = 真实DOM完全增删改 + (可能较多的节点)排版与重绘
VDOM的渲染流程
获取数据
根据数据创建VDOM (相当于给对象赋值)
根据VDOM渲染生成真实DOM ( 根据createElmeent(‘DIV’) )
当数据发生改变后,又会生成新的VDOM
通过 diff 算法 比对 多次生成的 VDOM, 将不同的内容比对出来,然后在进行真实DOM渲染,
一样的内容是不会进行渲染的,这就是VDOM 的 ‘就地复用’ | ‘惰性原则’
React与Vue的区别与联系? -
监听数据变化的实现原理不同
Vue 通过 getter/setter 以及一些函数的劫持,能精确知道数据变化,不需要特别的优化就能达到很好的性能React 默认是通过比较引用的方式进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的VDOM的重新渲染 -
数据流的不同
Vue中默认是支持双向绑定的
React一直提倡的是单向数据流,他称之为 onChange/setState()模式。 -
模板渲染方式的不同
React 是通过JSX渲染模板
Vue是通过一种拓展的HTML语法进行渲染
React是在组件JS代码中,通过原生JS实现模板中的常见语法,比如插值,条件,循环等,都是通过JS语法实现的
Vue是在和组件JS代码分离的单独的模板中,通过指令来实现的,比如条件语句就需要 v-if 来实现 -
Vuex 和 Redux 的区别
在 Vuex 中,$store 被直接注入到了组件实例中
在 Redux 中,我们每一个组件都需要显示的用 connect 把需要的 props 和 dispatch 连接起来。
Redux 使用的是不可变数据,而Vuex的数据是可变的。Redux每次都是用新的state替换旧的state,而Vuex是直接修改
详细介绍生命周期(三个阶段,生命周期的触发顺序)?
三个阶段:挂载、更新、卸载
新生命周期
出生阶段
1.constructor :初始化设置
2.static getDerivedStateFromProps 更新state状态 监听props变化
return 对象 state变化会重复这个生命周期
return null 不会重复调用
3.render 渲染dom节点
4.componentDidMount 组件初始渲染完成
成长阶段 state或者props发生变化都会执行
1.static getDerivedStateFromProps 更新state状态 监听props变化
2.shouldComponentUpdate 是否要执行更新
return true/false
3.render
4.getSnapshotBeforeUpdate 组件更新完成之前执行 snapshot 快照
必须配合componentDidUpdate 这个生命周期使用
该函数的返回值 就是componentDidUpdate 的第三个参数
5.componentDidUpdate 组件更行新完成
6.死亡阶段componentWillUnmount
合成事件与改变this指向的三种方式及之间的区别?
React合成事件一套机制:React并不是将click事件直接绑定在dom上面,而是采用事件冒泡的形式冒泡到document上面,然后React将事件封装给正式的函数处理运行和处理。
改变this指向
在构造函数constructor this.fn=this.fn.bind(this)
在调用函数时绑定 this.fn.bind(this)
箭头函数 fn=()=>{}
样式绑定与CSS Modules
多种组件创建方式及其区别?
- 函数定义的无状态组件
为了创建纯展示组件 只负责根据传入的props来展示,不涉及到要state状态的操作,无生命周期,不能访问this对象 - React.Component
- 其成员函数不会自动绑定this,需要开发者手动绑定,否则this不能获取当前组件实例对象。
什么是受控组件?
假设我们现在有一个表单,表单中有一个input标签,input的value值必须是我们设置在constructor构造函数的state中的值,然后,通过onChange触发事件来改变state中保存的value值,这样形成一个循环的回路影响。也可以说是React负责渲染表单的组件仍然控制用户后续输入时所发生的变化。
高阶组件与装饰器
组件通信的多种方式及其之间的区别
- 父组件向子组件通信
props - 子组件向父组件通信
利用回调函数
父组件将一个函数作为 props 传递给子组件,子组件调用该回调函数,便可以向父组件通信 - 跨级组件之间通信
中间组件层层传递 props
使用 context 对象
let Context=React.createContext(); console.log(Context) //Provider提供者 Consumer 消费者 let {Provider,Consumer}=React.createContext();
父组件
孙子组件
- 非嵌套组件间通信(同级)
利用发布订阅者模式 先监听后触发
可以找有共同有关系的父组件
yarn add events let Bus=require('events'); export default new Bus() 引入 import Bus from './bus' Bus.on('事件名',('参数1','参数2')=>{}) Bus.emit('事件名','参数1','参数2')
React Router及其简单实现
react-router依赖基础 - history yarn add react-router-dom BrowserRouter 不会返回dom节点 只是监听history事件
Redux数据流向及三个中间件(redux-logger、redux-thunk、redux-saga)
仓库对象里面的方法 store.getState() 获取仓库状态 得到reducer函数返回值 store.dispatch() //会触发reducer函数 派发reducer 必须传入一个对象action action就是描述仓库状态的一个对象 对象里面必须要有一个type属性 store,subscribe() 监听
connect实现原理
Ant Design && so on