关于JSX底层处理机制
大苹果

关于JSX底层处理机制

关于JSX底层处理机制第一步:把我们编写的JSX语法,编译为虚拟DOM对象“VirtualDOM”虚拟DOM对象:框架自己内部构建的一套对象体系(对象的相关成员都是React内部规定的),基于这些属性描述出我们所构建视图中的DOM节点的相关特征基于babel-preset-react-app把JSX编译为React.createElement(...)这种格式!!只要是元素节点,必然会基于createElement进行处理React.createElement(ele,props,...children)ele:元素标签名“或组件名”props:元素的属性集合(对象),“如果没有设置过任何的属性,则此值是null”children:第三个及以后的参数,都是当前元素的子节点在把createElement方法执行,创建出virtualDOM虚拟DOM对象“也有称之为:JSX元素、JSX对象、ReactChild对象...”!virtualDOM={$$typeof:Symbol(react.element),ref:null,key:null,type:"标签名或组件",props:{元素的相关属性children:子节点信息“没有子节点则没有这个属性,属性值可能是一个值,也可能是一个数组”}存储了元素的相关属性&&子节点信息}第二步:把构建的virtualDOM渲染为真实DOM真实DOM:浏览器页面中,最后渲染出来让用户看见的DOM元素!!基于ReactDom中的render方法处理的!!v16ReactDOM.render()v18constroot=ReactDOM.createRoot();root.render();补充说明:第一次渲染页面是从virtualDOM->真实DOM;但是后期视图更新的时候,需要经过一个DOM-DIFF的对比,计算出补丁包PATCH(两次视图差异的部分),把PATCH补丁包进行渲染!!

React 65 2月前
React是web前端框架
大苹果

React是web前端框架

React是web前端框架目前市面上比较主流的前端框架ReactVueAngular"NG"主流思想:不在直接取操作DOM,而是改为“数据驱动思想”操作DOM思想操作DOM比较消耗性能“主要原因是:可能导致DOM重排(回流)/重绘”操作起来相对来讲麻烦一些数据驱动思想我们不会直接操作DOM我们去操作数据“当我们修改了数据,框架会按照相关的数据,让页面重新渲染”框架底层实现的视图的渲染,也是基于操作DOM完成的构建了一套虚拟DOM->真实DOM的渲染体系有效避免了DOM的重排/重绘开发效率更高、最后的性能也相对较好React框架采用的是MVC体系;Vue框架采用的是MVVM体系;MVC:M是model,是数据层,V是View视图层+C是controller是控制层我们需要安照专业的语法去构建视图(页面):React中基于jsx语法来构建视图的构建数据层:但凡在视图中,需要“动态”处理的(获取需要变化的,不论是样式还是内容),我们都需要有对应的数据模型控制层:当我们在视图中(或者根据业务需求)进行某些操作的时候,都是去修改相关的数据,然后React框架会按照最新的数据,重新渲染视图,以此让用户看到最新的效果,数据驱动视图的渲染,视图中的表单内容改变,想要修改数据,需要开发者自己去写代码实现!!“单向驱动”MVVM:M是model,是数据层,V是View视图层+VM是viewModel数据/视图监听层数据驱动视图的渲染:修改数据,视图会跟着更新视图驱动数据的更改:监听页面中表单元素内容改变,自动去修改相关的数据“双向驱动”JSX构建视图的基础知识JSX:javascriptandxml(html)把js和html标签混合在了一起(并不是我们之前玩的字符串拼接)vscode如何支持JSX语法(格式化、快捷提示)创建的js文件,我们把后缀设置为jsx即可,这样js文件中就可以支持JSX语法了webpack打包规则中,也会对.jsx这种文件,按照js的方式进行处理的在html中嵌入“JS表达式”,需要基于“{}胡子语法“JS表达式:执行有结果的JS表达式{text},数学运算{i+1}->{2}{x+y}判断:三元运算符{1===1?ok:no}循环:借助于数组的迭代方法处理“map”这不算js表达式if(i===1){}switch(num){}所有命令式编程的循环“for”for/infor/ofwhile等{for(leti=0;i<=10;i++){}}在ReactDom.createRoot()的时候,不能直接把HTML/BODY作为根容器,需要指定一个额外的盒子“例如:#root”每一个构建的视图,只能有一个“根节点”出现多个根节点时会报错提示AdjacentJSXelementsmustbewrappedinanencloseingtag.React给我们提供了一个特殊的节点(标签):React.Fragment空文档标记标签<></>既保证了可以只有一个根节点,又不会新增一个HTML层级结构!!{}胡子语法中嵌入不同的值,所呈现出来的特点number/string:值是啥,就渲染出来啥boolean:渲染的内容是空除数组对象外,其余对象一般不支持在{}中进行渲染,但是也有特殊情况;JSX虚拟DOM对象给元素设置style行内样式,要求必须写成一个对象格式不支持渲染:普通对象数组对象:把数组的每一项都分别拿出来渲染“并不是变为字符串渲染,中间没有逗号”函数对象:不支持在{}中渲染,但是可以作为函数组件,用<Componet/>方式渲染给元素设置样式行内样式:需要基于对象的格式处理,直接写样式字符串会报错<h2style={{color:'red',fontSize:'18px'//样式属性要基于驼峰命名法处理}}>设置样式类名:需要把class替换为className<h2className='box'>importReactfrom'react';//React核心语法importReactDOMfrom'react-dom/client';//构建HTML(WebApp)的核心//获取页面中id为root的容器,作为“根“容器constroot=ReactDOM.createRoot(document.getElementById('root'));//基于render方法渲染我们编写的视图root.render(<div>完美起航</div>);

React,JavaScript 47 2月前
前端开发
大苹果

前端开发

前端开发当前以及未来的前端开发,一定是:组件化、模块化有利于团队协作开发便于组件的复用:提高开发效率、方便后期维护、减少页面中冗余代码如何规划组件业务组件:针对项目需求封装的普通业务组件“没有啥复用性,只是单独拆出来的一个模块“通用业务组件“具备复用性”功能组件:适用于多个项目“例如:UI组件库的组件”通用功能组件因为组件化开发,必然会带来“工程化”的处理也就是基于webpack等工具“vite/rollup/turbopack...”实现组件的合并、压缩、打包等代码编译、兼容、校验等React的工程化/组件化开发我们可以基于webpack自己去搭建一套工程化的架子,但是这样非常的麻烦、复杂;React官方,为我们提供了一个脚手架;create-react-app脚手架:基于它创建项目,默认就是把webpack的打包规则已经处理好,把一些项目需要的基本文件也都创建好了!!create-react-app基本运用安装脚手架npmicreate-react-app-g#mac前面需要设置sudo#检查安装情况create-react-app--version基于脚手架创建React工程化的项目create-react-app项目名称项目名称要遵循npm包命名规范:使用“数字、小写字母、_”命名项目目录|-src:所有后续编写的代码,几乎都放在src下“打包的时候,一般只对这个目录下面的代码进行处理”-->|-index.js|-public:放页面模板-->|index.html|-package.json|-...react常用的版本很早之前是15版本(太早了)16版本:一些项目用的最多的17版本:最大的升级就是看不出升级“语法没变啥,只是底层处理机制上升级了”18版本:新版本“机制和语法上都有区别”一个React项目中,默认会安装:react:React框架的核心react-dom:React视图渲染的核心“基于React构建WebApp(html页面)”--->react-native:构建和渲染App的react-scripts:脚手架为了让项目目录看起来干净一些,把webpac打包的规则及相关的插件/LOADER等都隐藏到了node_modules目录下,react-scripts就是脚手架中自己对打包命令的一种封装、基于它的打包,会调用node_modules中的webpack等进行处理!!如图web-vitals:性能检测eslintbrowserslist

React,前端 89 2月前
JavaScript之封装
大苹果

JavaScript之封装

封装es5封装functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex||'female';this.age=age||0;this.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}this.study=function(skill){console.log('学习'+skill);}this.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}//调用方式//new关键字创建实例varp=newPerson('小名','male',10);//直接调用创建varp1=Person('小红','female',9);p.walk();//走路p1.study('游泳');//学习游泳p.introduce();//我是小名,我是一个男孩,今年10岁了。p1.introduce();//我是小红,我是一个女孩,今年9岁了。原型链的方式functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex;this.age=age;}Person.prototype.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}Person.prototype.study=function(skill){console.log('学习'+skill);}Person.prototype.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}//调用方式//new关键字创建实例varp=newPerson('小名','male',10);//直接调用创建varp1=Person('小红','female',9);p.walk();//走路p1.study('游泳');//学习游泳p.introduce();p1.introduce();es6封装classPerson{constructor(name,sex,age){this.name=name;this.sex=sex;this.age=age;}walk(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}study(skill){console.log('学习'+skill);}introduce(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}//调用方式//new关键字创建实例varp=newPerson('小名','male',10);p.walk();//走路p.introduce();//直接调用创建//varp1=Person('小红','female',9);//TypeError:ClassconstructorPersoncannotbeinvokedwithout'new'//class定义的不能直接调用,只能通过new关键字实例化后调用console.log(typeofPerson);//function继承es5原型链实现继承functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex||'female';this.age=age||0;this.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}this.study=function(skill){console.log('学习'+skill);}this.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}functionBoy(name,age){this.name=name;this.age=age;this.sex='male';this.doHouseWork=function(){console.log('我在做家务');}}Boy.prototype=newPerson();Boy.prototype.constructor=Boy;varboy=newBoy('汤姆',12);boy.introduce();//我是汤姆,我是一个男孩,今年12岁了。boy.doHouseWork();//我在做家务console.log(boyinstanceofBoy);//trueObject.createfunctioncreate(obj){returnObject.create(obj);}varperson={name:'Tom',age:20,sex:'male',walk:function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');},study:function(skill){console.log('学习'+skill);},introduce:function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}};varboy=create(person);boy.age=15,boy.name='晓东';boy.sex='male';boy.doHouseWork=function(){console.log('我在做家务');}boy.introduce();//我是晓东,我是一个男孩,今年15岁了boy.doHouseWork();//我在做家务call方法functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex||'female';this.age=age||0;this.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}this.study=function(skill){console.log('学习'+skill);}this.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}functionBoy(name,age){varobj=Person.call(this,name,'male',age);obj.doHouseWork=function(){console.log('我在做家务');}returnobj}letboy=Boy('小米',16);boy.introduce(boy);//我是小米,我是一个男孩,今年16岁了。boy.doHouseWork();//我在做家务apply方法functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex||'female';this.age=age||0;this.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}this.study=function(skill){console.log('学习'+skill);}this.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}functionBoy(name,age){varobj=Person.apply(this,[name,'male',age]);obj.doHouseWork=function(){console.log('我在做家务');}returnobj}letboy=Boy('小米',17);boy.introduce(boy);//我是小米,我是一个男孩,今年16岁了。boy.doHouseWork();//我在做家务es6extends关键字classPerson{constructor(name,sex,age){this.name=name;this.sex=sex;this.age=age;}walk(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}study(skill){console.log('学习'+skill);}introduce(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}classBoyextendsPerson{constructor(name,age){super(name,'male',age);}doHouseWork(){console.log('我在做家务');}}varboy=newBoy('汤姆',14);boy.introduce();//我是汤姆,我是一个男孩,今年12岁了。boy.doHouseWork();//我在做家务console.log(boyinstanceofBoy);//true多态函数参数不定个数functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex||'female';this.age=age||0;this.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}this.study=function(skill){console.log('学习'+skill);}this.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}functionMathematician(name,age){this.sex='male';this.calc=function(){varargsLength=arguments.length;switch(argsLength){case1:console.log("这是"+arguments[0]);break;case2:console.log(arguments[0]+'+'+arguments[1]+'='+(arguments[0]+arguments[1]));break;default:console.log('太复杂的暂时不会');}}}Mathematician.prototype=newPerson();Mathematician.prototype.constructor=Mathematician;varboy=newMathematician();boy.calc();//太复杂的暂时不会boy.calc(1);//这是1boy.calc(1,3);//1+3=4

JavaScript 78 2月前
JavaScript之Map
大苹果

JavaScript之Map

Map语法newMap([iterable])参数iterableIterable可以是一个数组或者其他iterable对象,其元素或为键值对,或为两个元素的数组。每个键值对都会添加到新的Map。null会被当做undefined。描述一个Map对象以插入顺序迭代其元素—一个for…of循环为每次迭代返回一个[key,value]数组。键的相等(Keyequality)键的比较是基于“SameValueZero”算法:NaN是与NaN相同的(虽然NaN!==NaN),剩下所有其它的值是根据===运算符的结果判断是否相等。在目前的ECMAScript规范中,-0和+0被认为是相等的,尽管这在早期的草案中并不是这样。有关详细信息,请参阅浏览器兼容性表中的“valueequalityfor-0and0”。Objects和maps的比较Object和Map类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成Map使用。不过Map和Object有一些重要的区别,在下列情况里Map会是更好的选择:一个对象的键只能是字符串或者Symbols,但一个Map的键可以是任意值,包括函数、对象、基本类型。你可以通过size属性直接获取一个Map的键值对个数,而Object的键值对个数只能手动计算。Map是可迭代的,而Object的迭代需要先获取它的键数组然后再进行迭代。Object都有自己的原型,所以原型链上的键名有可能和对象上的键名产生冲突。虽然ES5开始可以用map=Object.create(null)来创建一个没有原型的对象,但是这种用法不太常见。Map在涉及频繁增删键值对的场景下会有些性能优势。属性Map.length属性length的值为0。getMap[@@species]本构造函数用于创建派生对象。Map.prototype表示Map构造器的原型。允许添加属性从而应用于所有的Map对象。Map实例所有的Map对象实例都会继承Map.prototype。属性Map.prototype.constructor返回一个函数,它创建了实例的原型。默认是Map函数。Map.prototype.size返回Map对象的键/值对的数量。方法Map.prototype.clear()移除Map对象的所有键/值对。Map.prototype.delete(key)移除任何与键相关联的值,并且返回该值,该值在之前会被Map.prototype.has(key)返回为true。之后再调用Map.prototype.has(key)会返回false。Map.prototype.entries()返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的[key,value]数组。Map.prototype.forEach(callbackFn[,thisArg])按插入顺序,为Map对象里的每一键值对调用一次callbackFn函数。如果为forEach提供了thisArg,它将在每次回调中作为this值。Map.prototype.get(key)返回键对应的值,如果不存在,则返回undefined。Map.prototype.has(key)返回一个布尔值,表示Map实例是否包含键对应的值。Map.prototype.keys()返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的键。Map.prototype.set(key,value)设置Map对象中键的值。返回该Map对象。Map.prototype.values()返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的值。Map.prototype[@@iterator]()返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的[key,value]数组。示例使用映射对象varmyMap=newMap();varkeyObj={},keyFunc=function(){},keyString="astring";//添加键myMap.set(keyString,"和键'astring'关联的值");myMap.set(keyObj,"和键keyObj关联的值");myMap.set(keyFunc,"和键keyFunc关联的值");myMap.size;//3//读取值myMap.get(keyString);//"和键'astring'关联的值"myMap.get(keyObj);//"和键keyObj关联的值"myMap.get(keyFunc);//"和键keyFunc关联的值"myMap.get("astring");//"和键'astring'关联的值"//因为keyString==='astring'myMap.get({});//undefined,因为keyObj!=={}myMap.get(function(){})//undefined,因为keyFunc!==function(){}将NaN作为映射的键NaN也可以作为Map对象的键.虽然NaN和任何值甚至和自己都不相等(NaN!==NaN返回true),但下面的例子表明,两个NaN作为Map的键来说是没有区别的:varmyMap=newMap();myMap.set(NaN,"notanumber");myMap.get(NaN);//"notanumber"varotherNaN=Number("foo");myMap.get(otherNaN);//"notanumber"使用for..of方法迭代映射映射也可以使用for..of循环来实现迭代:varmyMap=newMap();myMap.set(0,"zero");myMap.set(1,"one");for(var[key,value]ofmyMap){console.log(key+"="+value);}//将会显示两个log。一个是"0=zero"另一个是"1=one"for(varkeyofmyMap.keys()){console.log(key);}//将会显示两个log。一个是"0"另一个是"1"for(varvalueofmyMap.values()){console.log(value);}//将会显示两个log。一个是"zero"另一个是"one"for(var[key,value]ofmyMap.entries()){console.log(key+"="+value);}//将会显示两个log。一个是"0=zero"另一个是"1=one"使用forEach()方法迭代映射映射也可以通过forEach()方法迭代:myMap.forEach(function(value,key){console.log(key+"="+value);},myMap)//将会显示两个logs。一个是"0=zero"另一个是"1=one"映射与数组对象的关系varkvArray=[["key1","value1"],["key2","value2"]];//使用映射对象常规的构造函数将一个二维键值对数组对象转换成一个映射关系varmyMap=newMap(kvArray);myMap.get("key1");//返回值为"value1"//使用展开运算符将一个映射关系转换成一个二维键值对数组对象console.log(uneval([...myMap]));//将会向您显示和kvArray相同的数组//或者使用展开运算符作用在键或者值的迭代器上,进而得到只含有键或者值得数组console.log(uneval([...myMap.keys()]));//输出["key1","key2"]

JavaScript 49 2月前
redux的基础操作和思想
大苹果

redux的基础操作和思想

Redux相关源码:可以理解为主,但不要求一定都会写redux不会,虽然也有一些其他的替代方案:mobx,但是基本可以宣告react阵亡了VUE->VuexRedux生态圈中的知识reduxreact-reduxredux中间件mobx类的装饰器react-router-domv5/v6redux-saga/dva/umifetch及其二次封装antdpro复合组件通信:父子通信“具备相同父亲的兄弟组件”:props属性“基于ref”祖先和后代“具备相同祖先的平行组件”:context上下文redux/react-redux也是实现组件之间的通信技术“插件”不管任何类型的组件,都可以基于这种方法,实现组件通信公共状态管理方案后期实战开发中,父子组件一般是基于:props/ref/redux其余组件的通信一般都是基于redux管理的三个组件中,都需要用到创建的store容器我在根组件中,导入store,把其放在上下文中,后期其他组件需要,只要是它的后代组件,则直接获取使用即可!!!redux的使用import{createStore}from'redux';/*管理员:修改STORE容器中的公共状态*/letinitial={supNum:10,oppNum:5}constreducer=functionreducer(state=initial,action){//state:存储STORE容器中的公共状态“最开始没有的时候,赋值初始状态值initial”//action:每一次基于dispatch派发的时候,传递进来的行为对象“要求必须具备type属性,存储派发的行为标识”//接下来我们需要基于派发的行为标识,修改STORE容器中的公共状态信息switch(action.type){case"VOTE_SUP":break;case"VOTE_OPP":break;default:}//return的内容,会整体替换STORE容器中的内容returnstate;}//创建STORE公共容器conststore=createStore(reducer);store.dispatch({type:"VOTE_SUP",step:10});exportdefaultstore;通过上下文获取storestore.jsimport{createStore}from'redux';/*管理员:修改STORE容器中的公共状态*/letinitial={supNum:10,oppNum:5}constreducer=functionreducer(state=initial,action){//state:存储STORE容器中的公共状态“最开始没有的时候,赋值初始状态值initial”//action:每一次基于dispatch派发的时候,传递进来的行为对象“要求必须具备type属性,存储派发的行为标识”//接下来我们需要基于派发的行为标识,修改STORE容器中的公共状态信息switch(action.type){case"VOTE_SUP":state.supNum++;break;case"VOTE_OPP":state.oppNum++;break;default:}//return的内容,会整体替换STORE容器中的内容returnstate;}//创建STORE公共容器conststore=createStore(reducer);exportdefaultstore;index.jsximportReactfrom'react';importReactDOMfrom'react-dom/client';import'./index.sass';importVotefrom'./views/Vote';import{ConfigProvider}from'antd';importstorefrom'./store';importThemeContextfrom'./ThemeContext';constroot=ReactDOM.createRoot(document.getElementById('root'));root.render(<ConfigProvider><ThemeContext.Providervalue={store}><Vote></Vote></ThemeContext.Provider></ConfigProvider>);vote.jsximportReact,{useContext,useEffect,useState}from"react";import"./Vote.sass";importVoteMainfrom"./VoteMain";importVoteFooterfrom"./VoteFooter";importThemeContextfrom"../ThemeContext";functionVote(){const{store}=useContext(ThemeContext);const{supNum,oppNum}=store.getState();//组件第一次渲染完毕之后,把组件更新的方法,放在STORE的事件池中//const[num,setNum]=useState(0);//constupdate=()=>{//setNum(num+1);//}//useEffect(()=>{////letunsubscribe=store.subscribe(让组件更新的方法);////把让组件更新的方法放在STORE的事件池中////返回的unsubscribe方法执行,可以把刚才放入事件池中的方法移除掉//letunsubscribe=store.subscribe(update);//return()=>{//unsubscribe();//}//},[num])const[_,setNum]=useState(0);useEffect(()=>{//letunsubscribe=store.subscribe(让组件更新的方法);//把让组件更新的方法放在STORE的事件池中//返回的unsubscribe方法执行,可以把刚才放入事件池中的方法移除掉store.subscribe(()=>{setNum(newDate().valueOf());});//return()=>{//unsubscribe();//}},[])return<divclassName="vote-box"><divclassName="header"><h2className="title">React是很棒的前端框架</h2><spanclassName="num">{supNum+oppNum}</span></div><VoteMainsupNum={supNum}oppNum={oppNum}></VoteMain><VoteFooter></VoteFooter></div>}exportdefaultVote;VoteMain.jsximportReactfrom"react";importThemeContextfrom"../ThemeContext";classVoteMainextendsReact.Component{staticcontextType=ThemeContext;render(){const{store}=this.context;const{supNum,oppNum}=store.getState();letratio="--";lettotal=supNum+oppNum;if(total>0){ratio=(supNum/total).toFixed(2);}return<divclassName="main"><p>支持人数:{supNum}</p><p>反对人数:{oppNum}</p><p>支持比率:{ratio}</p></div>}componentDidMount(){const{store}=this.context;store.subscribe(()=>{this.forceUpdate();});}}exportdefaultVoteMain;VoteFooter.jsximportReact,{useContext}from"react";import{Button}from"antd"importThemeContextfrom"../ThemeContext";functionVoteFooter(){const{store}=useContext(ThemeContext);return<divclassName="footer"><ButtononClick={()=>{store.dispatch({type:"VOTE_SUP"})}}type="primary">支持</Button><ButtononClick={()=>{store.dispatch({type:"VOTE_OPP"})}}type="primary"danger>反对</Button></div>}exportdefaultVoteFooter;类组件的更新总结:redux具体的代码编写顺序1.创建store,规划出reducerr当中的业务处理逻辑可以后续不断完善,但是最开始reducer的这个架子需要先搭建取来J2.在入口中,基于上下文对象,把store放入到上下文中;需要用到store的组件,从上下文中获取!!3.组件中基于store,完成公共状态的获取、和任务的派发+使用到公共状态的组件,必须向store的事件池中加入让组件更新的办法,只有这样,才可以确保,公共状态改变,可以让组件更新,才可以获取最新的状态进行绑定!!

React,前端 83 2月前
flex弹性布局(一)
大苹果

flex弹性布局(一)

flex弹性布局Flex是FlexibleBox的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为Flex布局。.flex{display:flex;display:-webkit-flex;/*Safari*/border:none;}<divclass="flex"><div>12</div><div>13</div><div>14</div><div>15</div><div>16</div><div>17</div><div>18</div><div>19</div></div>效果如下:note:设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。flex-directionrow(默认值):主轴为水平方向,起点在左端。/*水平从左至右排列*/.flex-direction.row{flex-direction:row;}<divclass="flexflex-directionrow"><div>12</div><div>13</div><div>14</div><div>15</div><div>16</div><div>17</div><div>18</div><div>19</div></div>效果如下:row-reverse主轴为水平方向,起点在右端。/*效果类似于float:right,但是row-reverse不是浮动,而元素的排列也不一样,原来左边的排列在右边*/.flex-direction.row-reverse{flex-direction:row-reverse;}<divclass="flexflex-directionrow-reverse"><div>12</div><div>13</div><div>14</div><div>15</div><div>16</div><div>17</div><div>18</div><div>19</div></div>效果如下:column主轴为垂直方向,起点在上沿。/*垂直向下排列*/.flex-direction.column{flex-direction:column;width:100px;}<divclass="flexflex-directioncolumn"><div>12</div><div>13</div><div>14</div><div>15</div><div>16</div><div>17</div><div>18</div><div>19</div></div>效果如下:column-reverse主轴为垂直方向,起点在下沿。/*垂直向下排列,原来在上边的排列在下边,下边排列在上边*/.flex-direction.column-reverse{flex-direction:column-reverse;width:100px;}<divclass="flexflex-directioncolumn-reverse"><div>12</div><div>13</div><div>14</div><div>15</div><div>16</div><div>17</div><div>18</div><div>19</div></div>效果如下:

flex,css,弹性布局 1797 5年前
es6之Map对象保存键值对
大苹果

es6之Map对象保存键值对

Map语法newMap([iterable])参数iterableIterable可以是一个数组或者其他iterable对象,其元素或为键值对,或为两个元素的数组。每个键值对都会添加到新的Map。null会被当做undefined。描述一个Map对象以插入顺序迭代其元素—一个for...of循环为每次迭代返回一个[key,value]数组。键的相等(Keyequality)键的比较是基于"SameValueZero"算法:NaN是与NaN相同的(虽然NaN!==NaN),剩下所有其它的值是根据===运算符的结果判断是否相等。在目前的ECMAScript规范中,-0和+0被认为是相等的,尽管这在早期的草案中并不是这样。有关详细信息,请参阅浏览器兼容性表中的“valueequalityfor-0and0”。Objects和maps的比较Object和Map类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成Map使用。不过Map和Object有一些重要的区别,在下列情况里Map会是更好的选择:一个对象的键只能是字符串或者Symbols,但一个Map的键可以是任意值,包括函数、对象、基本类型。你可以通过size属性直接获取一个Map的键值对个数,而Object的键值对个数只能手动计算。Map是可迭代的,而Object的迭代需要先获取它的键数组然后再进行迭代。Object都有自己的原型,所以原型链上的键名有可能和对象上的键名产生冲突。虽然ES5开始可以用map=Object.create(null)来创建一个没有原型的对象,但是这种用法不太常见。Map在涉及频繁增删键值对的场景下会有些性能优势。属性Map.length属性length的值为0。getMap[@@species]本构造函数用于创建派生对象。Map.prototype表示Map构造器的原型。允许添加属性从而应用于所有的Map对象。Map实例所有的Map对象实例都会继承Map.prototype。属性Map.prototype.constructor返回一个函数,它创建了实例的原型。默认是Map函数。Map.prototype.size返回Map对象的键/值对的数量。方法Map.prototype.clear()移除Map对象的所有键/值对。Map.prototype.delete(key)移除任何与键相关联的值,并且返回该值,该值在之前会被Map.prototype.has(key)返回为true。之后再调用Map.prototype.has(key)会返回false。Map.prototype.entries()返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的[key,value]数组。Map.prototype.forEach(callbackFn[,thisArg])按插入顺序,为Map对象里的每一键值对调用一次callbackFn函数。如果为forEach提供了thisArg,它将在每次回调中作为this值。Map.prototype.get(key)返回键对应的值,如果不存在,则返回undefined。Map.prototype.has(key)返回一个布尔值,表示Map实例是否包含键对应的值。Map.prototype.keys()返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的键。Map.prototype.set(key,value)设置Map对象中键的值。返回该Map对象。Map.prototype.values()返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的值。Map.prototype[@@iterator]()返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的[key,value]数组。示例使用映射对象varmyMap=newMap();varkeyObj={},keyFunc=function(){},keyString="astring";//添加键myMap.set(keyString,"和键'astring'关联的值");myMap.set(keyObj,"和键keyObj关联的值");myMap.set(keyFunc,"和键keyFunc关联的值");myMap.size;//3//读取值myMap.get(keyString);//"和键'astring'关联的值"myMap.get(keyObj);//"和键keyObj关联的值"myMap.get(keyFunc);//"和键keyFunc关联的值"myMap.get("astring");//"和键'astring'关联的值"//因为keyString==='astring'myMap.get({});//undefined,因为keyObj!=={}myMap.get(function(){})//undefined,因为keyFunc!==function(){}将NaN作为映射的键NaN也可以作为Map对象的键.虽然NaN和任何值甚至和自己都不相等(NaN!==NaN返回true),但下面的例子表明,两个NaN作为Map的键来说是没有区别的:varmyMap=newMap();myMap.set(NaN,"notanumber");myMap.get(NaN);//"notanumber"varotherNaN=Number("foo");myMap.get(otherNaN);//"notanumber"使用for..of方法迭代映射映射也可以使用for..of循环来实现迭代:varmyMap=newMap();myMap.set(0,"zero");myMap.set(1,"one");for(var[key,value]ofmyMap){console.log(key+"="+value);}//将会显示两个log。一个是"0=zero"另一个是"1=one"for(varkeyofmyMap.keys()){console.log(key);}//将会显示两个log。一个是"0"另一个是"1"for(varvalueofmyMap.values()){console.log(value);}//将会显示两个log。一个是"zero"另一个是"one"for(var[key,value]ofmyMap.entries()){console.log(key+"="+value);}//将会显示两个log。一个是"0=zero"另一个是"1=one"使用forEach()方法迭代映射映射也可以通过forEach()方法迭代:myMap.forEach(function(value,key){console.log(key+"="+value);},myMap)//将会显示两个logs。一个是"0=zero"另一个是"1=one"映射与数组对象的关系varkvArray=[["key1","value1"],["key2","value2"]];//使用映射对象常规的构造函数将一个二维键值对数组对象转换成一个映射关系varmyMap=newMap(kvArray);myMap.get("key1");//返回值为"value1"//使用展开运算符将一个映射关系转换成一个二维键值对数组对象console.log(uneval([...myMap]));//将会向您显示和kvArray相同的数组//或者使用展开运算符作用在键或者值的迭代器上,进而得到只含有键或者值得数组console.log(uneval([...myMap.keys()]));//输出["key1","key2"]

Map,es6 1892 5年前
React、Redux和redux-thunk的简单应用
大苹果

React、Redux和redux-thunk的简单应用

react-redux、reduxconnect、Provider,createStore用法//redux/index.jsconstADD='add';constSUB='sub';exportfunctioncounter(state=0,action){switch(action.type){caseADD:returnstate+1;caseSUB:returnstate-1;default:returnstate;}}//actionexportfunctionadd(){return{type:ADD};}//actionexportfunctionsub(){return{type:SUB};}//index.js入口importReactfrom'react';importReactDOMfrom'react-dom';importAppfrom'./App'import{counter}from'./redux/index';import{createStore}from'redux';import{Provider}from'react-redux';letstore=createStore(counter);//通过props传递storeReactDOM.render(,document.getElementById('root'));//App.jsimportreactfrom'react'import{connect}from'react-redux'import{add,sub}from'./reduxs/index'classAppextendsreact.Component{render(){return({this.props.num}addsub);}}constMapStateToProps=(state)=>{return{num};}constActionCreator={add,sub};App=connect(MapStateToProps,ActionCreator)(App);装饰器配置配置babelpackage.json里面找到babel,增加插件transform-decorators-legacy"babel":{"presets":["react-app"],"plugins":["transform-decorators-legacy"]}npminstall--savebabel-plugin-transform-decorators-legacy//直接替换上面的connect方法@connect((state)=>{return{num:state}},{add,sub})reducerimport{combineReducers}from'redux'import{counter}from'./redux/index'consttodoApp=combineReducers({counter})exportdefaulttodoApp//index.jsimportreducersfrom'./reducer'//解决异步等待importthunkfrom'redux-thunk'import{createStore,applyMiddleware,compose}from'redux';//开启redux调试功能constreduxDevTools=window.devToolsExtension?window.devToolsExtension():()=>{};conststore=createStore(reducers,compose(applyMiddleware(thunk),//使用中间件thunkreduxDevTools//配置调试));

React、Redux,redux-thunk 1525 5年前
JavaScript封装、继承和多态实现
大苹果

JavaScript封装、继承和多态实现

封装(概念)封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。封装是面向对象的特征之一,是对象和类概念的主要特性。简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。继承(概念)继承是指可以让某个类型的对象获得另一个类型的对象的属性的方法。它支持按级分类的概念。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;多态多态就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。封装es5封装functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex||'female';this.age=age||0;this.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}this.study=function(skill){console.log('学习'+skill);}this.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}//调用方式//new关键字创建实例varp=newPerson('小名','male',10);//直接调用创建varp1=Person('小红','female',9);p.walk();//走路p1.study('游泳');//学习游泳p.introduce();//我是小名,我是一个男孩,今年10岁了。p1.introduce();//我是小红,我是一个女孩,今年9岁了。原型链的方式functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex;this.age=age;}Person.prototype.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}Person.prototype.study=function(skill){console.log('学习'+skill);}Person.prototype.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}//调用方式//new关键字创建实例varp=newPerson('小名','male',10);//直接调用创建varp1=Person('小红','female',9);p.walk();//走路p1.study('游泳');//学习游泳p.introduce();p1.introduce();es6封装classPerson{constructor(name,sex,age){this.name=name;this.sex=sex;this.age=age;}walk(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}study(skill){console.log('学习'+skill);}introduce(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}//调用方式//new关键字创建实例varp=newPerson('小名','male',10);p.walk();//走路p.introduce();//直接调用创建//varp1=Person('小红','female',9);//TypeError:ClassconstructorPersoncannotbeinvokedwithout'new'//class定义的不能直接调用,只能通过new关键字实例化后调用console.log(typeofPerson);//function继承es5原型链实现继承functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex||'female';this.age=age||0;this.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}this.study=function(skill){console.log('学习'+skill);}this.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}functionBoy(name,age){this.name=name;this.age=age;this.sex='male';this.doHouseWork=function(){console.log('我在做家务');}}Boy.prototype=newPerson();Boy.prototype.constructor=Boy;varboy=newBoy('汤姆',12);boy.introduce();//我是汤姆,我是一个男孩,今年12岁了。boy.doHouseWork();//我在做家务console.log(boyinstanceofBoy);//trueObject.createfunctioncreate(obj){returnObject.create(obj);}varperson={name:'Tom',age:20,sex:'male',walk:function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');},study:function(skill){console.log('学习'+skill);},introduce:function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}};varboy=create(person);boy.age=15,boy.name='晓东';boy.sex='male';boy.doHouseWork=function(){console.log('我在做家务');}boy.introduce();//我是晓东,我是一个男孩,今年15岁了boy.doHouseWork();//我在做家务call方法functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex||'female';this.age=age||0;this.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}this.study=function(skill){console.log('学习'+skill);}this.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}functionBoy(name,age){varobj=Person.call(this,name,'male',age);obj.doHouseWork=function(){console.log('我在做家务');}returnobj}letboy=Boy('小米',16);boy.introduce(boy);//我是小米,我是一个男孩,今年16岁了。boy.doHouseWork();//我在做家务apply方法functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex||'female';this.age=age||0;this.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}this.study=function(skill){console.log('学习'+skill);}this.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}functionBoy(name,age){varobj=Person.apply(this,[name,'male',age]);obj.doHouseWork=function(){console.log('我在做家务');}returnobj}letboy=Boy('小米',17);boy.introduce(boy);//我是小米,我是一个男孩,今年16岁了。boy.doHouseWork();//我在做家务es6extends关键字classPerson{constructor(name,sex,age){this.name=name;this.sex=sex;this.age=age;}walk(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}study(skill){console.log('学习'+skill);}introduce(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}classBoyextendsPerson{constructor(name,age){super(name,'male',age);}doHouseWork(){console.log('我在做家务');}}varboy=newBoy('汤姆',14);boy.introduce();//我是汤姆,我是一个男孩,今年12岁了。boy.doHouseWork();//我在做家务console.log(boyinstanceofBoy);//true多态函数参数不定个数functionPerson(name,sex,age){if(!(thisinstanceofPerson)){returnnewPerson(name,sex,age);}this.name=name;this.sex=sex||'female';this.age=age||0;this.walk=function(){if(this.age<=2){returnconsole.log('我不会走路');}if(this.age>2&&this.age<4){returnconsole.log('我会走路了');}returnconsole.log('走路');}this.study=function(skill){console.log('学习'+skill);}this.introduce=function(){console.log(`我是${this.name},我是一个${this.sex==='male'?"男":"女"}孩,今年${this.age}岁了。`);}}functionMathematician(name,age){this.sex='male';this.calc=function(){varargsLength=arguments.length;switch(argsLength){case1:console.log("这是"+arguments[0]);break;case2:console.log(arguments[0]+'+'+arguments[1]+'='+(arguments[0]+arguments[1]));break;default:console.log('太复杂的暂时不会');}}}Mathematician.prototype=newPerson();Mathematician.prototype.constructor=Mathematician;varboy=newMathematician();boy.calc();//太复杂的暂时不会boy.calc(1);//这是1boy.calc(1,3);//1+3=4

封装、继承、多态,JavaScript 1626 5年前
1 2 3 4 5 6 >> 共 6 页