s?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u0)for(u=-1;++u=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.xg.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++it?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++oe&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0;
if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++ue.dx)&&(f=e.dx);++ue&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++au;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}();
================================================
FILE: src/page-mobx/index.js
================================================
import ReactDOM from 'react-dom'
import DevTools from 'mobx-react-devtools'
import Route from './routes'
import './style/app.styl'
ReactDOM.render(
{__DEV__ && }
{/**/}
,
document.getElementById('root')
);
================================================
FILE: src/page-mobx/p0.js
================================================
import React from 'react'
import './style/p0.styl'
class P0 extends React.Component {
render() {
return (
MobX分享
);
}
}
export default P0;
================================================
FILE: src/page-mobx/p1.js
================================================
import React from 'react'
import d3 from './d3'
import './style/p1.styl'
class P1 extends React.Component {
drawChart(){
let width = 900;
let height = 700;
this.data = {
'name':'Mobx知识结构',
'_children':
[
{
'name':"API" ,
'_children':
[
{
'name': 'mobx',
_children: [
{'name':"@observable", isV: true},
{'name':"@action", isV: true},
{'name':"autorun / when / reaction" },
{'name':"@computed" },
{'name':"intercept & observe" },
{'name':"工具API: extendObservable, toJS, isObservable" },
]
},
{
'name': 'mobx-react',
_children: [
{'name':"@observer", isV: true},
{'name':"Observer" },
{'name':"Provider / inject" },
{'name':"componentWillReact" },
{'name':"PropTypes" }
]
},
]
},
{
'name':'Observable类型',
"_children":
[
{
"name":"object",
"_children":
[
{"name":"extendObservable"}
]
},
{
'name':'arrays',
"_children":
[
{"name":"clear/replace/remove方法"},
{"name":"slice/ toJS方法"},
{"name":"arrays更新触发"}
]
},
{
'name':'maps',
"_children":
[
{"name":"两种定义的差别"},
{"name":"和ES6map的区别"}
]
}
]
},
{
"name":"问题",
"_children":
[
{"name":"1. 关于清理autorun"},
{"name":"2. 关于触发视图更新的一种方案"},
{"name":"3. 关于store的传递 / 单例模式"},
{"name":"4. 关于细粒度拆分"},
{"name":"5. 关于跨组件触发更新的问题"},
{"name":"6. 在async/await中使用runInAction"},
]
},
]
};
//边界空白
let padding = {left: 150, right:50, top: 20, bottom: 50 };
this.svg = d3.select('#p1')
.append('svg')
.attr('width', width + padding.left + padding.right)
.attr('height', height + padding.top + padding.bottom)
.append('g')
.attr("transform",`translate(${padding.left}, ${padding.top})`);
//树状图布局
this.tree = d3.layout.tree().size([height, width]);
//对角线生成器
this.diagonal = d3.svg.diagonal().projection(d => [d.y, d.x]);
//给第一个节点添加初始坐标x0和x1
let root = this.data;
root.x0 = height / 2;
root.y0 = 0;
//以第一个节点为起始节点,重绘
this.redraw(root);
}
//重绘函数
redraw = (source) => {
let that = this;
/** 1. 计算节点和连线的位置 */
//应用布局,计算节点和连线
let nodes = this.tree.nodes(this.data);
let links = this.tree.links(nodes);
//重新计算节点的y坐标
nodes.forEach(d => d.y = d.depth * 180);
/** 2. 节点的处理*/
//获取节点的update部分
let nodeUpdate = this.svg.selectAll(".node")
.data(nodes, d => d.name);
//获取节点的enter部分
let nodeEnter = nodeUpdate.enter();
//获取节点的exit部分
let nodeExit = nodeUpdate.exit();
//1. 节点的 Enter 部分的处理办法
let enterNodes = nodeEnter.append("g")
.attr("class", "node")
.attr("transform", d => `translate(${source.y0}, ${source.x0})`)
.on("click", function(d){
that.toggle(d);
that.redraw(d);
});
enterNodes.append("circle")
.attr("r", 0)
.style("fill", d => d._children ? "lightsteelblue" : "#fff");
enterNodes.append("text")
.attr("x", d => d.children || d._children ? -14 : 14)
.attr("dy", ".35em")
.attr("text-anchor", d => d.children || d._children ? "end" : "start")
.text(d => d.name)
.style("fill-opacity", 0);
//2. 节点的 Update 部分的处理办法
let updateNodes = nodeUpdate.transition()
.duration(500)
.attr("transform", d => `translate(${d.y}, ${d.x})`);
updateNodes.select("circle")
.attr("r", 8)
.style("fill", d => d._children ? "lightsteelblue" : "#fff");
updateNodes.select("text")
.style("fill-opacity", 1)
.style('font-weight', d => d.isV ? 'bold' : 'normal')
.style('fill', d => d.isV ? 'red' : 'black');
//3. 节点的 Exit 部分的处理办法
let exitNodes = nodeExit.transition()
.duration(500)
.attr("transform", d => `translate(${source.y}, ${source.x})`)
.remove();
exitNodes.select("circle").attr("r", 0);
exitNodes.select("text").style("fill-opacity", 0);
/** 3. 连线的处理 */
//获取连线的update部分
let linkUpdate = this.svg.selectAll(".link")
.data(links, function (d) {
return d.target.name;
});
//获取连线的enter部分
let linkEnter = linkUpdate.enter();
//获取连线的exit部分
let linkExit = linkUpdate.exit();
//1. 连线的 Enter 部分的处理办法
linkEnter.insert("path", ".node")
.attr("class", "link")
.attr("d", function (d) {
var o = {x: source.x0, y: source.y0};
return that.diagonal({source: o, target: o});
})
.transition()
.duration(500)
.attr("d", that.diagonal);
//2. 连线的 Update 部分的处理办法
linkUpdate.transition()
.duration(500)
.attr("d", that.diagonal);
//3. 连线的 Exit 部分的处理办法
linkExit.transition()
.duration(500)
.attr("d", function (d) {
var o = {x: source.x, y: source.y};
return that.diagonal({source: o, target: o});
})
.remove();
/** 4. 将当前的节点坐标保存在变量x0、y0里,以备更新时使用*/
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
};
//切换开关,d 为被点击的节点
toggle = (d) => {
if (d.children) { //如果有子节点
d._children = d.children; //将该子节点保存到 _children
d.children = null; //将子节点设置为null
} else { //如果没有子节点
d.children = d._children; //从 _children 取回原来的子节点
d._children = null; //将 _children 设置为 null
}
};
componentDidMount(){
this.drawChart();
}
render() {
return (
);
}
}
export default P1;
================================================
FILE: src/page-mobx/p10.js
================================================
import React from 'react'
import {observable, action,computed} from 'mobx'
import {observer} from 'mobx-react'
import Markdown from '../markdown'
let info = `
注意: 如果是异步请求,在请求的回调中需要改变observable的变量,必须在回调的外层包一层action函数,才能触发更新
\`\`\`js
import {action } from 'mobx'
fetch('xxxx')
.then(action(res => {
// 成功操作,可能会改变observable的值,从而触发更新,必须外边包一层action函数
})).catch(action(err => {
// 错误回调, 如果有改变observable值的操作,也需要外边包一层action函数
}));
\`\`\`
如果使用 async / await, 需要使用runInAction
\`\`\`js
import {runInAction } from 'mobx'
@observable data = ''
async initData() {
let content = await fetch('xxxx')
runInAction(() => {
this.data = content.data
})
}
\`\`\`
`;
@observer
class P10 extends React.Component {
render() {
return (
action / runInAction
);
}
}
export default P10;
================================================
FILE: src/page-mobx/p2.js
================================================
import React from 'react'
import {observable, action, autorun, when} from 'mobx'
import {observer, whyRun} from 'mobx-react'
import Markdown from '../markdown'
import './style/p1.styl'
import { Button, notification, Switch } from 'antd'
let info = `
\`\`\`js
\import {observable, action, autorun, when} from 'mobx'
@observable isError = false;
@action handleClick = () => {
this.isError = !this.isError;
};
componentDidMount(){
autorun(()=> {
if (this.isError) {
notification.error({
message: '有错误',
description: '这是错误提示'
});
}
});
when(
() => !this.isError,
() => {
notification.success({message: '没有错误', description: '没有错误'});
}
);
}
render() {
return (
);
}
\`\`\`
`;
let result = `
\`\`\`js
1. when 满足条件后只执行一次,autorun 满足条件可以可以一直执行
2. reaction 函数是 autorun 的变种,粗略地讲,reaction 是 computed(expression).observe(action(sideEffect)) 或
autorun(() => action(sideEffect)(expression) 的语法糖
3. 注意 autorun 的清理查看Q1
\`\`\`
`;
@observer
class P2 extends React.Component {
@observable showResult = false;
@observable isError = false;
@action handleClick = () => {
this.isError = !this.isError;
};
componentDidMount(){
autorun(() => {
if (this.isError) {
notification.error({message: '有错误', description: '这是错误提示'});
}
});
when(
() => !this.isError,
() => {
notification.success({message: '没有错误', description: '没有错误'});
}
);
}
componentWillUnmount(){
console.log('this is componentWillUnmount');
}
render() {
return (
this.showResult = !this.showResult}/>
autorun / when / reaction
);
}
}
export default P2;
================================================
FILE: src/page-mobx/p3.js
================================================
import React from 'react'
import {observable, action,computed} from 'mobx'
import {observer} from 'mobx-react'
import Markdown from '../markdown'
import './style/p1.styl'
import { Button, notification } from 'antd';
let info = `
\`\`\`js
import {observable, action,computed} from 'mobx'
class store {
@observable num = 8;
@computed get price(){
return this.num * 100;
}
}
@observer
class App extends React.Component {
@action handleClick = () => {
store.num = Math.random();
};
render() {
return (
衬衫的价格是: {store.price}
);
}
}
\`\`\`
`;
@observer
class P3 extends React.Component {
@observable num = 8;
@computed get price(){
return this.num * 100;
}
@action handleClick = () => {
this.num = Math.random();
};
render() {
return (
@computed
衬衫的价格是: {this.price}
);
}
}
export default P3;
================================================
FILE: src/page-mobx/p4.js
================================================
import React from 'react'
import {observable, action, computed, extendObservable} from 'mobx'
import {observer} from 'mobx-react'
import Markdown from '../markdown'
import './style/p4.styl'
import { Button, Spin, Switch} from 'antd';
let info = `
参考资料:[extendObservable](https://github.com/mobxjs/mobx/issues/194)
\`\`\`js
import {observable, action, computed, extendObservable} from 'mobx'
import {observer} from 'mobx-react'
@observable isSpinning = {
S1: false
};
@action handleClick1 = () => {
this.isSpinning.S1 = !this.isSpinning.S1;
};
@action handleClick2 = () => {
if(Object.keys(this.isSpinning).includes('S2')){
this.isSpinning.S2 = !this.isSpinning.S2;
}else{
this.isSpinning.S2 = true;
}
};
@action handleClick3 = () => {
if(Object.keys(this.isSpinning).includes('S3')){
this.isSpinning.S3 = !this.isSpinning.S3;
}else{
extendObservable(this.isSpinning, {S3:true});
}
};
render() {
return (
);
}
\`\`\`
`;
let result = `
\`\`\`js
let arr = [];
_.forOwn(this.isSpinning, (value, key, obj) => {
arr.push(
{key}
);
});
\`\`\`
`;
@observer
class P4 extends React.Component {
@observable showResult = false;
@observable isSpinning = {
S1: false
};
@action handleClick1 = () => {
this.isSpinning.S1 = !this.isSpinning.S1;
};
@action handleClick2 = () => {
if(Object.keys(this.isSpinning).includes('S2')){
this.isSpinning.S2 = !this.isSpinning.S2;
}else{
this.isSpinning.S2 = true;
}
};
@action handleClick3 = () => {
if(Object.keys(this.isSpinning).includes('S3')){
this.isSpinning.S3 = !this.isSpinning.S3;
}else{
extendObservable(this.isSpinning, {S3:true});
}
};
render() {
return (
this.showResult = !this.showResult}/>
object
S1
S2
S3
);
}
}
export default P4;
================================================
FILE: src/page-mobx/p5.js
================================================
import React from 'react'
import {observable, action, computed, extendObservable, toJS, isObservable} from 'mobx'
import {observer} from 'mobx-react'
import Markdown from '../markdown'
import { Table, Button, notification} from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: '40%',
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '30%',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}];
let info = `
\`\`\`js
@observable isLoading = false;
@observable isLoading = false;
@observable data = [
{
key: 1,
name: 'John Brown sr.',
age: 60,
address: 'New York No. 1 Lake Park',
children: [
{
key: 11,
name: 'John Brown',
age: 42,
address: 'New York No. 2 Lake Park',
}, {
key: 12,
name: 'John Brown jr.',
age: 30,
address: 'New York No. 3 Lake Park'
}
]
},
{ key: 2, name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park'},
{ key: 3, name: 'Jack Rose', age: 55, address: 'test', children: null},
{ key: 4, name: ' CKTest', age: 55, address: 'test'}
];
handleClick1 = () => {
setTimeout(()=>{
let key = Math.random();
this.data.push({
key: key,
name: 'CK-' + key,
age: 26,
address: 'Hang Zhou'
});
if(isObservable(this.data[this.data.length-1])){
notification.success({message: '消息提示', description: '新增的数据是observable的'});
}
},200);
};
handleClick2 = () => {
setTimeout(() => {
let key = Math.random();
// push 操作
}, 200);
};
handleClick3 = () => {
setTimeout(()=>{
if(!Object.keys(this.data[1]).includes('children')){
this.data[1].children = [];
}else{
let key = Math.random();
// push操作
}
},200);
};
handleClick4 = () => {
setTimeout(()=>{
if (!Array.isArray(toJS(this.data[2].children))) {
this.data[2].children = [];
}else{
let key = Math.random();
// push操作
}
},200);
};
handleClick5 = () => {
this.isLoading = true;
setTimeout(()=>{
if(!Object.keys(this.data[3]).includes('children')){
this.data[3].children = [];
this.isLoading = false;
}else{
let key = Math.random();
// push操作
this.isLoading = false;
}
},200);
};
render() {
return (
);
}
\`\`\`
`;
@observer
class P5 extends React.Component {
@observable isLoading = false;
@observable data = [{
key: 1,
name: 'John Brown sr.',
age: 60,
address: 'New York No. 1 Lake Park',
children: [
{
key: 11,
name: 'John Brown',
age: 42,
address: 'New York No. 2 Lake Park',
}, {
key: 12,
name: 'John Brown jr.',
age: 30,
address: 'New York No. 3 Lake Park'
}
]
}, {
key: 2,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
},{
key: 3,
name: 'Jack Rose',
age: 55,
address: 'test',
children: null
}, {
key: 4,
name: ' CKTest',
age: 55,
address: 'test'
}];
handleClick1 = () => {
setTimeout(()=>{
let key = Math.random();
this.data.push({
key: key,
name: 'CK-' + key,
age: 26,
address: 'Hang Zhou'
});
if(isObservable(this.data[this.data.length-1])){
notification.success({message: '消息提示', description: '新增的数据是observable的'});
}
},200);
};
handleClick2 = () => {
setTimeout(() => {
let key = Math.random();
this.data[0].children.push({
key: key,
name: 'John Brown jr.' + key,
age: 60,
address: 'New York No. 3 Lake Park'
});
if (isObservable(this.data[0].children[this.data[0].children.length-1])) {
notification.success({message: '消息提示', description: '新增的数据是observable的'});
}
}, 200);
};
handleClick3 = () => {
setTimeout(()=>{
if(!Object.keys(this.data[1]).includes('children')){
this.data[1].children = [];
if (isObservable(this.data[1].children)) {
notification.success({message: '消息提示', description: '新增的children属性是observable的'});
}else{
notification.error({message: '消息提示', description: '新增的children属性不是observable的'});
}
}else{
let key = Math.random();
this.data[1].children.push({
key: key,
name: 'Joe Black-' + key,
age: 30,
address: 'New York No. 3 Lake Park'
});
if (isObservable(this.data[1].children[this.data[1].children.length-1])) {
notification.success({message: '消息提示', description: '新增的数据是observable的'});
}else{
notification.error({message: '消息提示', description: '新增的数据不是observable的'});
}
}
},200);
};
handleClick4 = () => {
setTimeout(()=>{
if (!Array.isArray(toJS(this.data[2].children))) {
this.data[2].children = [];
if (isObservable(this.data[2].children)) {
notification.success({message: '消息提示', description: '新增的children属性是observable的'});
}else{
notification.error({message: '消息提示', description: '新增的children属性不是observable的'});
}
}else{
let key = Math.random();
this.data[2].children.push({
key: key,
name: 'Jack Rose-' + key,
age: 55,
address: 'test'
});
if (isObservable(this.data[2].children[this.data[2].children.length-1])) {
notification.success({message: '消息提示', description: '新增的数据是observable的'});
}else{
notification.error({message: '消息提示', description: '新增的数据不是observable的'});
}
}
},200);
};
handleClick5 = () => {
this.isLoading = true;
setTimeout(()=>{
if(!Object.keys(this.data[3]).includes('children')){
this.data[3].children = [];
if (isObservable(this.data[3].children)) {
notification.success({message: '消息提示', description: '新增的children属性是observable的'});
}else{
notification.error({message: '消息提示', description: '新增的children属性不是observable的'});
}
this.isLoading = false;
}else{
let key = Math.random();
this.data[3].children.push({
key: key,
name: 'Joe Black-' + key,
age: 30,
address: 'New York No. 3 Lake Park'
});
if (isObservable(this.data[3].children[this.data[3].children.length-1])) {
notification.success({message: '消息提示', description: '新增的数据是observable的'});
}else{
notification.error({message: '消息提示', description: '新增的数据不是observable的'});
}
this.isLoading = false;
}
},200);
};
render() {
return (
arrays
);
}
}
export default P5;
================================================
FILE: src/page-mobx/p6.js
================================================
import React from 'react'
import {observable, action, computed, extendObservable, isObservable,isObservableMap} from 'mobx'
import {observer} from 'mobx-react'
import Markdown from '../markdown'
import './style/p4.styl'
import { Button, Spin, Switch} from 'antd';
let info = `
\`\`\`js
import {observable, action, computed, extendObservable, isObservable,isObservableMap} from 'mobx'
@observable map1 = observable.map();
@observable map2 = new Map();
@observable map3 = observable.map([
['map3',[{value: true, key:'map3-0'}]]
]);
@observable map4 = new Map([
['map4',[{value: true, key:'map4-0'}]]
]);
@action handleClick1 = () => {
let key = 'map1-' + this.map1.size;
this.map1.set(key, true);
console.log('------ this is map1------');
console.log(this.map1.keys());
console.log(isObservable(this.map1.get('map1-1')));
console.log(isObservable(this.map1));
console.log(isObservableMap(this.map1));
};
@action handleClick2 = () => {
let status = !this.map1.get('map1-1');
this.map1.set('map1-1',status);
};
@action handleClick3 = () => {
let key = 'map2-' + this.map2.size ;
this.map2.set(key, true);
console.log('------ this is map2------');
console.log(this.map2.keys());
console.log(isObservable(this.map2.get('map2-1')));
console.log(isObservable(this.map2));
console.log(isObservableMap(this.map2));
};
@action handleClick4 = () => {
let status = !this.map2.get('map2-1');
this.map2.set('map2-1', status);
};
@action handleClick5 = () => {
let arr = this.map3.get('map3');
let key = 'map3-' + arr.length;
arr.push({value: true, key:key});
console.log('------ this is map3------');
console.log(this.map3.keys());
console.log(isObservable(this.map3.get('map3')));
console.log(isObservable(this.map3.get('map3')[1]));
console.log(isObservable(this.map3));
console.log(isObservableMap(this.map3));
};
@action handleClick6 = () => {
let arr = this.map3.get('map3');
arr[1].value = ! arr[1].value;
};
@action handleClick7 = () => {
let arr = this.map4.get('map4');
let key = 'map4-' + arr.length;
arr.push({value: true, key:key});
console.log('------ this is map4------');
console.log(this.map4.keys());
console.log(isObservable(this.map4.get('map4')));
console.log(isObservable(this.map4.get('map4')[1]));
console.log(isObservable(this.map4));
console.log(isObservableMap(this.map4));
};
@action handleClick8 = () => {
let arr = this.map4.get('map4');
arr[1].value = ! arr[1].value;
};
render() {
let arr = [];
this.map1.forEach((value,key,map) => {
if(value){
arr.push(
map1: {key}
);
}
});
let arr2 = [];
this.map2.forEach((value,key,map) => {
if(value) {
arr2.push(
map3: {key}
);
}
});
let arr3 = [];
this.map3.get('map3').forEach(item => {
if(item.value) {
arr3.push(
map3: {item.key}
);
}
});
let arr4 = [];
this.map4.get('map4').forEach(item => {
if(item.value) {
arr4.push(
map4: {item.key}
);
}
});
return (
{arr}
{arr2}
{arr3}
{arr4}
\`\`\`
`;
let result = `
\`\`\`js
class Store {
@observable map1 = observable.map([
['key1', 'value1'],
['key2', 'value2'],
]);
@observable map2 = new Map([
['key1', 'value1'],
['key2', 'value2'],
]);
map3 = new Map([
['key1', 'value1'],
['key2', 'value2'],
]);
}
const store = new Store();
console.log(Array.isArray(store.map1.values())) // true
console.log(Array.isArray(store.map2.values())) // true
console.log(Array.isArray(store.map3.values())) // false
console.log(store.map1.keys(); // ['key1', 'key2']
console.log(store.map2.keys()); // ['key1', 'key2']
console.log(store.map3.keys()); // MapIterator {"key1", "key2"}
values()方法类似keys()方法
测试结果表明,使用mobx的方式定义的map类型,通过values/keys方法返回的是一个数组,
而原生定义的map类型返回的是遍历器
\`\`\`
`;
@observer
class P6 extends React.Component {
@observable showResult = false;
@observable map1 = observable.map();
@observable map2 = new Map();
@observable map3 = observable.map([
['map3',[{value: true, key:'map3-0'}]]
]);
@observable map4 = new Map([
['map4',[{value: true, key:'map4-0'}]]
]);
map5 = new Map([
['key1', 'value1'],
['key2', 'value2'],
]);
@action handleClick1 = () => {
let key = 'map1-' + this.map1.size;
this.map1.set(key, true);
console.log('------ this is map1------');
console.log(this.map1.keys());
console.log(isObservable(this.map1.get('map1-1')));
console.log(isObservable(this.map1));
console.log(isObservableMap(this.map1));
console.log(this.map5);
};
@action handleClick2 = () => {
let status = !this.map1.get('map1-1');
this.map1.set('map1-1',status);
};
@action handleClick3 = () => {
let key = 'map2-' + this.map2.size ;
this.map2.set(key, true);
console.log('------ this is map2------');
console.log(this.map2.keys());
console.log(isObservable(this.map2.get('map2-1')));
console.log(isObservable(this.map2));
console.log(isObservableMap(this.map2));
};
@action handleClick4 = () => {
let status = !this.map2.get('map2-1');
this.map2.set('map2-1', status);
};
@action handleClick5 = () => {
let arr = this.map3.get('map3');
let key = 'map3-' + arr.length;
arr.push({value: true, key:key});
console.log('------ this is map3------');
console.log(this.map3.keys());
console.log(isObservable(this.map3.get('map3')));
console.log(isObservable(this.map3.get('map3')[1]));
console.log(isObservable(this.map3));
console.log(isObservableMap(this.map3));
};
@action handleClick6 = () => {
let arr = this.map3.get('map3');
arr[1].value = ! arr[1].value;
};
@action handleClick7 = () => {
let arr = this.map4.get('map4');
let key = 'map4-' + arr.length;
arr.push({value: true, key:key});
console.log('------ this is map4------');
console.log(this.map4.keys());
console.log(isObservable(this.map4.get('map4')));
console.log(isObservable(this.map4.get('map4')[1]));
console.log(isObservable(this.map4));
console.log(isObservableMap(this.map4));
};
@action handleClick8 = () => {
let arr = this.map4.get('map4');
arr[1].value = ! arr[1].value;
};
render() {
let arr = [];
this.map1.forEach((value,key,map) => {
if(value){
arr.push(
map1: {key}
);
}
});
let arr2 = [];
this.map2.forEach((value,key,map) => {
if(value) {
arr2.push(
map3: {key}
);
}
});
let arr3 = [];
this.map3.get('map3').forEach(item => {
if(item.value) {
arr3.push(
map3: {item.key}
);
}
});
let arr4 = [];
this.map4.get('map4').forEach(item => {
if(item.value) {
arr4.push(
map4: {item.key}
);
}
});
return (
this.showResult = !this.showResult}/>
maps
{arr}
{arr2}
{arr3}
{arr4}
);
}
}
export default P6;
================================================
FILE: src/page-mobx/p7.js
================================================
import React from 'react'
import {observable, action, computed, extendObservable, toJS, isObservable} from 'mobx'
import {observer, Observer, componentWillReact} from 'mobx-react'
import Markdown from '../markdown'
import { Table, Button, notification, Switch} from 'antd';
// import './css/p7.scss'
let info = `
\`\`\`js
import {observable, action} from 'mobx'
import {observer, Observer, componentWillReact} from 'mobx-react'
//@observer
class Test extends React.Component \{
componentWillReact() {
notification.info({message: '消息提示', description: 'This is componentWillReact'});
}
componentWillReceiveProps(){
notification.error({message: '消息提示', description: 'This is componentWillReceiveProps'});
}
componentDidUpdate(){
notification.warn({message: '消息提示', description: 'This is Test componentDidUpdate'});
}
render() {
return (
num1: {this.props.num1}
{() => num2: {this.props.num2}
}
num3: {this.props.num3.value}
{() => num4: {this.props.num4.value}
}
);
}
}
//@observer
class App extends React.Component {
@observable num1 = 1;
@observable num2 = 1;
@observable num3 = {
value: 1
};
@observable num4 = {
value: 1
};
handleClick = () => {
this.num1 = Math.random();
this.num2 = Math.random();
this.num3.value = Math.random();
this.num4.value = Math.random();
};
componentDidUpdate(){
notification.info({message: '消息提示', description: 'This is App componentDidUpdate'});
}
render() {
return (
);
}
}
\`\`\`
`;
let result = `
\`\`\`js
1. App / Test 都不加 @observer
- 只有num4的值变化了
2. 只有Test组件加了 @observer
- num3 和 num4 的值发生了变化
- Test组件触发了componentWillReact / componentDidUpdate
- 测试表明 num3导致了Test触发componentWillReact / componentDidUpdate
3. 只有App组件加了 @observer
- num1 / num2 / num3 /num4 的值都改变了
- App组件触发了 componentDidUpdate
- Test组件触发了 componentWillReceiveProps / componentDidUpdate
4. App / Test都加了@observer
- num1 / num2 / num3 /num4 的值都改变了
- App组件触发了 componentDidUpdate
- Test组件触发了 componentWillReact componentWillReceiveProps / componentDidUpdate
-------------------------------------------------------
App re-render => Test componentWillReceiveProps
componentWillReact的生效依赖于 @observer
-------------------------------------------------------
贴士与技巧:
在所有渲染 @observable 的组件上使用 @observer
@observer 只会增强你正在装饰的组件,而不是内部使用了的组件。
所以通常你的所有组件都应该是装饰了的。但别担心,这样不会降低效率,相反 observer 组件越多,渲染效率越高
-------------------------------------------------------
MobX 可以做许多事,但是它无法将原始类型值转变成 observable
所以说值不是 observable,而对象的属性才是
永远只传递拥有 observable 属性的对象
\`\`\`
`;
//@observer
class Test extends React.Component {
componentWillReact() {
notification.info({message: '消息提示', description: 'This is Test componentWillReact'});
}
componentWillReceiveProps(){
notification.error({message: '消息提示', description: 'This is Test componentWillReceiveProps'});
}
componentDidUpdate(){
notification.warn({message: '消息提示', description: 'This is Test componentDidUpdate'});
}
render() {
return (
num1: {this.props.num1}
{() => num2: {this.props.num2}
}
num3: {this.props.num3.value}
{() => num4: {this.props.num4.value}
}
);
}
}
@observer
class P7 extends React.Component {
@observable showResult = false;
@observable num1 = 1;
@observable num2 = 1;
@observable num3 = {
value: 1
};
@observable num4 = {
value: 1
};
handleClick = () => {
this.num1 = Math.random();
this.num2 = Math.random();
this.num3.value = Math.random();
this.num4 = {value:12323}
};
componentDidUpdate(){
notification.info({message: '消息提示', description: 'This is App componentDidUpdate'});
}
render() {
return (
this.showResult = !this.showResult}/>
Observer (mobx-react)
);
}
}
export default P7;
================================================
FILE: src/page-mobx/p8.js
================================================
import React from 'react'
import {observable, action, computed, extendObservable, toJS, isObservable} from 'mobx'
import {observer, Observer, componentWillReact, inject, Provider} from 'mobx-react'
import Markdown from '../markdown'
import { Table, Badge, Button, notification, Switch} from 'antd';
//import './css/p8.scss'
let info = `
\`\`\`js
import {observable, action } from 'mobx'
import {observer, inject, Provider} from 'mobx-react'
@inject('color','count')
class BadgeContainer extends React.Component {
render() {
return (
);
}
}
class Message extends React.Component {
render() {
return (
{this.props.text}
);
}
}
@observer
class App extends React.Component {
messages = [
{text: '这是第一条消息'},
{text: '这是第二条消息'},
{text: '这是第三条消息'},
{text: '这是第四条消息'}
];
@observable color = '#87d068';
@observable count = 19;
handleClick = () => {
this.color = '#'+Math.floor(Math.random()*16777215).toString(16);
this.count = Math.floor(Math.random()*100);
};
render() {
const children = this.messages.map((message,index) =>
);
return (
)
}
}
\`\`\`
`;
let result = `
\`\`\`js
1. Provider is a component that can pass stores (or other stuff) using React's context mechanism to child components
2. inject can be used to pick up those stores
3. if your stores will change over time, like an observable value of another store,
MobX will warn you. To suppress that warning explicitly,
you can use suppressChangedStoreWarning={true} as a prop at your own risk.
\`\`\`
`;
@inject('color','count') //@observer
class BadgeContainer extends React.Component {
componentWillReact() {
notification.info({message: '消息提示', description: 'This is componentWillReact'});
}
render() {
return (
);
}
}
//@observer
class Message extends React.Component {
render() {
return (
{this.props.text}
);
}
}
@observer
class P8 extends React.Component {
@observable showResult = false;
messages = [
{text: '这是第一条消息'},
{text: '这是第二条消息'},
{text: '这是第三条消息'},
{text: '这是第四条消息'}
];
@observable color = '#87d068';
@observable count = 19;
handleClick = () => {
this.color = '#'+Math.floor(Math.random()*16777215).toString(16);
this.count = Math.floor(Math.random()*100);
};
render() {
const children = this.messages.map((message,index) =>
);
return (
this.showResult = !this.showResult}/>
Provider and inject
{children}
)
}
}
export default P8;
================================================
FILE: src/page-mobx/p9.js
================================================
import React from 'react'
import {observable, action, intercept, observe} from 'mobx'
import {observer, componentWillReact} from 'mobx-react'
import Markdown from '../markdown'
import { Table, Input, notification, Switch} from 'antd';
let info = `
参考资料:[Intercept & Observe](http://cn.mobx.js.org/refguide/observe.html)
\`\`\`js
import {observable, action, intercept, observe} from 'mobx'
@observable style = {
color:''
};
@action handleEnter = (e) => {
this.style.color = e.target.value
};
componentDidMount(){
this.disposer = intercept(this.style, 'color', change => {
if (!change.newValue) {
// 忽略取消设置背景颜色
notification.error({message: '错误提示', description: '颜色值不能为空'});
return null;
}
if (change.newValue.length === 6) {
// 补全缺少的 '#' 前缀
change.newValue = '#' + change.newValue;
notification.warn({message: '警告提示', description: '缺少#前缀'});
return change;
}
if (change.newValue.length === 7) {
return change;
}
if (change.newValue.length > 10) this.disposer(); // 不再拦截今后的任何变化
notification.error({message: '错误提示', description: change.newValue + ' 不是一个合法的颜色值'});
});
this.disposer2 = observe(this.style, 'color', (change) => {
notification.success({message: '颜色设置成功', description:
\`更新类型:\${change.type},
输入的值:\${change.newValue},
旧的值:\${change.oldValue}
\`});
});
}
\`\`\`
`;
let result = `
\`\`\`js
1. intercept 可以在变化作用于 observable 之前监测和修改变化。
2. observe 允许你在 observable 变化之后拦截改变
3. intercept/observe 返回一个 disposer 函数,当调用时可以取消拦截器
4. autorun 通常是一个更强大的和更具声明性的 observe 替代品
5. 当 observe 被创建出来后就会对变化作出反应,而像 autorun 或 reaction 这样的反应,
当它们变得可用时,它们会对新值做出反应。在大多数情况下,后者就足够了
6. intercept 和 observe 的回调函数接收一个事件对象
\`\`\`
`;
@observer
class P9 extends React.Component {
@observable showResult = false;
@observable style = {
color:''
};
@action handleEnter = (e) => {
this.style.color = e.target.value
};
componentDidMount(){
this.disposer = intercept(this.style, 'color', change => {
if (!change.newValue) {
// 忽略取消设置背景颜色
notification.error({message: '错误提示', description: '颜色值不能为空'});
return null;
}
if (change.newValue.length === 6) {
// 补全缺少的 '#' 前缀
change.newValue = '#' + change.newValue;
notification.warn({message: '警告提示', description: '缺少#前缀'});
return change;
}
if (change.newValue.length === 7) {
return change;
}
if (change.newValue.length > 10) this.disposer(); // 不再拦截今后的任何变化
notification.error({message: '错误提示', description: change.newValue + ' 不是一个合法的颜色值'});
});
this.disposer2 = observe(this.style, 'color', (change) => {
notification.success({message: '颜色设置成功', description:
`更新类型:${change.type},
输入的值:${change.newValue},
旧的值:${change.oldValue}
`});
});
}
render() {
return (
this.showResult = !this.showResult}/>
intercept & observe(注意和 observer/ Observer的区别)
);
}
}
export default P9;
================================================
FILE: src/page-mobx/routes.js
================================================
import React from 'react';
import {HashRouter as Router, Route, Switch} from 'react-router-dom'
import Aside from './aside';
import P0 from './p0';
import P1 from './p1';
import P2 from './p2';
import P3 from './p3';
import P4 from './p4';
import P5 from './p5';
import P6 from './p6';
import P7 from './p7';
import P8 from './p8';
import P9 from './p9';
import P10 from './p10';
import Q1 from './Q/q1';
import Q2 from './Q/q2';
import Q3 from './Q/q3';
import Q4 from './Q/q4';
export default () => (
);
================================================
FILE: src/page-mobx/store6.js
================================================
import {observable, action,computed} from 'mobx'
class store6 {
@observable map3 = observable.map();
@observable map4 = new Map();
}
export default new store6();
================================================
FILE: src/page-mobx/style/app.styl
================================================
body {
height: 100%;
}
h1 {
text-align: center;
}
.app-wrap {
height: 100vh;
display: flex;
flex-direction: column;
}
.app-content, .app-sidebar {
overflow-y: scroll;
padding: 2em;
}
.app-content {
flex: 5;
position: relative;
}
.app-sidebar {
background: #ff6347;
flex: 1;
border-bottom: 1px solid #eee;
text-align: center;
padding-bottom: 0.5em;
nav {
font-family: 'Unica One', sans-serif;
//text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 2rem;
> a {
display: block;
color: #fff;
text-decoration: none;
padding: 1em;
border: 1px solid #ff4726;
border-top: 0;
&:first-child {
border-top: 1px solid #ff4726;
}
&:hover {
background: #ff5537;
}
}
.active {
background: #ff5537;
}
}
}
@media (min-width: 800px) {
.app-wrap {
flex-direction: row;
}
.app-content {
order: 2;
}
.app-sidebar {
order: 1;
}
}
================================================
FILE: src/page-mobx/style/p0.styl
================================================
.p0
height: 100%
display: flex
justify-content: center
flex-direction: column
================================================
FILE: src/page-mobx/style/p1.styl
================================================
#p1 {
.node circle {
cursor: pointer;
fill: #fff;
stroke: steelblue;
stroke-width: 2px;
}
.node text {
font-size: 16px;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
}
================================================
FILE: src/page-mobx/style/p4.styl
================================================
.example {
text-align: center;
background: rgba(0,0,0,0.05);
border-radius: 4px;
margin-bottom: 20px;
padding: 30px 50px;
margin: 20px 0;
span {
}
}
================================================
FILE: tool.js
================================================
#!/usr/bin/env node
const fs = require('fs-extra')
const yargs = require('yargs')
const _ = require('lodash')
const Metalsmith = require('metalsmith')
const collections = require('metalsmith-collections')
const minimatch = require('minimatch')
const path = require('path')
yargs
.usage('$0 [args]')
.option('page', {
alias: 'p',
desc: 'Page name',
demand: false,
type: 'string'
})
// .option('force', {
// alias: 'f',
// desc: 'Force to overwrite existed page dir',
// demand: false,
// type: 'boolean'
// })
.option('remove', {
alias: 'r',
desc: 'Remove page files',
demand: false,
type: 'boolean'
})
.option('index', {
alias: 'i',
desc: 'Update index.html',
demand: false,
type: 'boolean'
})
.help()
const argv = yargs.argv
// console.log(JSON.stringify(argv, null, 4))
if (argv.index) {
updateIndexHtml()
process.exit(0)
}
// 验证page参数
if (!argv.page) {
console.log('Invalid value: ')
process.exit(0)
}
const componentName = argv.page
const metadata = {
ComponentName: _.upperFirst(_.camelCase(componentName)),
componentName: _.camelCase(componentName),
'component-name': _.kebabCase(componentName)
}
// 执行:删除页面文件
if (argv.remove) {
removePage()
// 执行:更新首页链接列表
updateIndexHtml()
}
// 执行:创建页面文件
else {
createPage()
// 执行:更新首页链接列表
updateIndexHtml()
}
// ========================== 以下的函数集合 ==========================
// 删除页面文件
function removePage () {
// 删除配置文件
fs.removeSync(`./page-config/${metadata['component-name']}.js`)
// 删除页面文件
fs.removeSync(`./src/page-${metadata['component-name']}`)
}
// 创建页面级初始化文件
function createPage () {
// 替换变量
function insertVariables(content) {
content = content.replace(/<%ComponentName%>/g, metadata.ComponentName)
content = content.replace(/<%componentName%>/g, metadata.componentName)
content = content.replace(/<%component-name%>/g, metadata['component-name'])
return content
}
// 创建对应的`page-config`文件
const content = fs.readFileSync('./_template/page-config.js', 'utf8')
fs.writeFileSync(`./page-config/${metadata['component-name']}.js`, insertVariables(content), 'utf8')
Metalsmith(__dirname)
.metadata(metadata)
.source(`./_template/page-dir`)
.destination(`./src/page-${metadata['component-name']}`)
.clean(true) // 是否先清除目录,不写这一行时默认为true
.use(collections({
files: '*/*'
}))
.use(renameFileAndInsertVariables({
filesToRename: {
pattern: '*.*',
rename: function (name) {
return createFileName(name, metadata)
}
}
}, metadata))
.build(function (error) {
if (error) {
console.log('error: ' + error)
} else {
console.log(`success: page-${metadata['component-name']} was created.`)
}
})
}
// 创建文件名
function createFileName(name, metadata) {
return name.replace(/<%component-name%>/g, metadata['component-name'])
}
// 文件重命名 + 文件内容替换
function renameFileAndInsertVariables(options, metadata) {
return function(files, metalsmith, done) {
Object.keys(options).forEach(function(opt) {
var matcher = minimatch.Minimatch(options[opt].pattern);
Object.keys(files).forEach(function(file) {
if (!matcher.match(file)) {
return;
}
var rename = options[opt].rename;
var renamedEntry = path.dirname(file) + '/';
if (typeof rename === 'function') {
renamedEntry += rename(path.basename(file));
} else {
renamedEntry += rename;
}
// 加入新的文件 files[renamedEntry] 是 node 的 file 对象
files[renamedEntry] = files[file];
var fileContent = fs.readFileSync(`./_template/page-dir/${file}`, 'utf8');
fileContent = fileContent.replace(/<%ComponentName%>/g, metadata.ComponentName);
fileContent = fileContent.replace(/<%componentName%>/g, metadata.componentName);
fileContent = fileContent.replace(/<%component-name%>/g, metadata['component-name']);
files[renamedEntry].contents = fileContent;
delete files[file];
});
});
done();
};
}
function updateIndexHtml() {
let content = fs.readFileSync('./index.html', 'utf8')
const listStart = ''
const listEnd = ''
const pageConfigPath = path.join(__dirname, 'page-config')
// 已经创建的页面名称
let pageNames = fs.readdirSync(pageConfigPath).map(function (item) {
if (!fs.statSync(pageConfigPath + '/' + item).isFile()) {
console.error(`Page config must be a file: "${pageConfigPath + '/' + item}" is not a file!`)
process.exit(0)
}
return item.split('.')[0]
})
// 过滤掉空的文件名
// 出现原因:系统自动生成.开头的隐藏文件
pageNames = _.filter(pageNames, function(pageName) {
return !!pageName
})
let newContent = pageNames.map(function(pageName) {
return `
`
})
newContent.unshift(listStart)
newContent.push(listEnd)
content = content.replace(/<\!\-\-list\sstart\-\->[\s\S]*<\!\-\-list\send\-\->/, newContent.join('\n'))
fs.writeFileSync('./index.html', content, 'utf8')
}
================================================
FILE: webpack.config.js
================================================
const fs = require('fs');
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const pkg = require('./package.json')
const _ = require('lodash')
const HOST = "0.0.0.0";
const PORT = 3000;
let commonPlugins = [];
const isDev = process.env.NODE_ENV !== "production";
const pageBasePath = path.join(__dirname, 'page-base')
const pageConfigPath = path.join(__dirname, 'page-config')
// 要创建的页面名称
let pageNames = fs.readdirSync(pageConfigPath).map(function (item) {
if (!fs.statSync(pageConfigPath + '/' + item).isFile()) {
console.error(`Page config must be a file: "${pageConfigPath + '/' + item}" is not a file!`)
process.exit(0)
}
return item.split('.')[0]
})
// 过滤掉空的文件名
// 出现原因:系统自动生成.开头的隐藏文件
pageNames = _.filter(pageNames, function(pageName) {
return !!pageName
})
let entry = {};
pageNames.map(function (name) {
entry[name] = [
path.join(__dirname, 'src', `page-${name}`, 'index.js')
]
});
// 环境数据
const envData = {
__DEV__: isDev,
__PRODUCTION__: !isDev,
};
const projectData = {
__PUBLIC_PREFIX__: '//cdn.dtwave.com/public/',
__PROJECT_PREFIX__: isDev ? '/' : '//cdn.dtwave.com/' + pkg.name + '/' + pkg.version + '/',
__PROJECT_NAME__: pkg.name,
__VERSION__: pkg.version
}
// 页面基础数据
let pageBaseData = require(path.join(pageBasePath, 'base'));
pageBaseData = typeof pageBaseData === 'function' ? pageBaseData(envData, projectData) : pageBaseData;
// process.exit(0)
module.exports = {
// https://webpack.js.org/configuration/dev-server/
devServer: {
compress: true,
inline: true,
hot: true,
port: PORT,
host: HOST,
open: true,
headers: {'Access-Control-Allow-Origin': '*'},
noInfo: true
},
entry: entry,
output: {
// path配置只在build时才有用到
path: path.join(__dirname, 'dist'),
filename: "[name].js",
chunkFilename: "[name].[chunkhash:6].chunk.js",
// publicPath只是dev时用到
publicPath: '/',
},
resolveLoader: {
moduleExtensions: ["-loader"]
},
module: {
rules: [
{
test: /\.css$/,
use:['style','css']
},
{
test: /\.js$/,
use: ["babel?cacheDirectory"],
// use: ["babel", "eslint"],
include: path.join(__dirname, 'src')
},
{
test: /\.styl$/,
use: ExtractTextPlugin.extract({
fallback: "style",
use: ["css", "postcss", "stylus"],
}),
exclude: /node_modules/,
},
{
test: /\.json$/,
use: ['json']
}
// {
// test: /\.(jpg|jpeg|png|gif|svg)$/,
// use: ['file'],
// query: {
// name: '[name].[hash:8].[ext]'
// }
// }
]
},
plugins: [
// 从多个模块中提取共用的模块到common.js
// https://webpack.js.org/plugins/commons-chunk-plugin/#commons-chunk-for-entries
new webpack.optimize.CommonsChunkPlugin({
name: "common",
filename: "common.js"
}),
new webpack.DefinePlugin(envData),
new ExtractTextPlugin('[name].css')
].concat((function () {
return pageNames.map(function (pageName) {
// console.log('privatePageData', path.join(pageConfigPath, pageName))
// 页面私有数据
let privatePageData = require(path.join(pageConfigPath, pageName));
privatePageData = typeof privatePageData === 'function' ? privatePageData(envData, projectData, pageBaseData) : privatePageData;
// https://github.com/jantimon/html-webpack-plugin#configuration
return new HtmlWebpackPlugin(Object.assign(
{
// NOTE 不允许配置成绝对路径 `filename`是相对于`webpack`的`output.path`(build时)和`output.publicPath`(dev时)值的
filename: isDev ? pageName+'.html' : 'html/' + pageName + '.html',
// NOTE 不允许配置成绝对路径 `template`是相对于`webpack`的`resolve.root`值的
template: 'page-base/base.html',
// 页面自己决定如何插入
inject: false,
pageName: pageName
},
envData,
projectData,
pageBaseData,
privatePageData
))
});
})()),
externals: {
'react': 'var React',
'react-dom': 'var ReactDOM',
'mobx': 'var mobx',
'mobx-react': 'var mobxReact',
'_': 'var _',
'antd': 'var antd',
}
};
if (!isDev) {
commonPlugins = [
// new HtmlWebpackPlugin({
// filename: "index.html",
// template: "wwwindex.html",
// inject: true,
// hash: true,
// minify: {
// removeComments: true,
// // collapseWhitespace: true,
// removeAttributeQuotes: true,
// // minifyJS: true,
// minifyCSS: true,
// minifyURLs: true,
// }
// }),
// 是否压缩JS 如果不压缩 需要全部注释掉
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
output: false,
compress: {
unused: true,
dead_code: true,
pure_getters: true,
warnings: false,
screw_ie8: true,
conditionals: true,
comparisons: true,
sequences: true,
evaluate: true,
join_vars: true,
if_return: true,
},
}),
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false,
quiet: true,
}),
];
} else {
commonPlugins = [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
// new webpack.LoaderOptionsPlugin({
// debug: true,
// options: {
// eslint: {
// configFile: './.eslintrc',
// },
// }
// }),
];
module.exports.devtool = "source-map";
}
module.exports.plugins = module.exports.plugins.concat(commonPlugins);