SegmentFault 前端学习记录最新的文章
2020-03-07T20:32:18+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
上传文件进度
https://segmentfault.com/a/1190000021943740
2020-03-07T20:32:18+08:00
2020-03-07T20:32:18+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>上传文件进度</h2>
<h3>如何获取到文件的上传进度</h3>
<p><code>Javascript</code> 的 <code>XMLHttpRequest</code> 提供了一个 <code>progress</code> 事件,这个事件会返回文件已上传的大小和总大小;</p>
<p>这个事件会在浏览器接收新数据期间周期性地触发。而<code>onprogess</code>事件处理程序会接收到一个 <code>event</code>对象,<code>target</code>属性是 <code>XHR</code>对象</p>
<pre><code class="js">var formData = new FormData();
formData.append("file", document.getElementById("file").files[0]);
formData.append("token", token_value); // 其他参数按这样子加入
var xhr = new XMLHttpRequest();
xhr.open("POST", "/uploadurl");
// 上传完成后的回调函数
xhr.onload = function() {
if (xhr.status === 200) {
console.log("上传成功");
} else {
console.log("上传出错");
}
};
// 获取上传进度
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
var percent = Math.floor((event.loaded / event.total) * 100);
// 设置进度显示
$("#J_upload_progress").progress("set progress", percent);
}
};
xhr.send(formData);</code></pre>
<h3>XHR</h3>
<pre><code class="js">function reqListener() {
console.log(this.responseText);
}
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();</code></pre>
DOM 元素大小
https://segmentfault.com/a/1190000021898225
2020-03-03T11:22:51+08:00
2020-03-03T11:22:51+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>DOM 元素大小</h2>
<h3>window.getComputedStyle(dom 元素,'伪类').属性名</h3>
<pre><code class="js">let style = window.getComputedStyle(element, [pseudoElt]);</code></pre>
<blockquote>
<a href="https://link.segmentfault.com/?enc=IA71Is99TzueMfIKnVFHUw%3D%3D.XtgYUGs%2FSLJmcUJG4ydaW5lM3ddr%2By4Ls6YdJz6z4%2BoYAWodI1hkO2%2BbvUKCjYifQLKkR400Bk0xJCbzgD7UgudWJY3KFVVZfUWZYc%2BQpt%2BWrM8e8%2FS9j9yEnk7kFbtw%2BB5lzDX1WP0KDtCTLRj7%2BQ%3D%3D" rel="nofollow">https://www.zhangxinxu.com/wo...</a><p><a href="https://link.segmentfault.com/?enc=p%2Bodvbdd6Ett913zhZtAnA%3D%3D.RIaaxk3FbComxnFSlzTn%2BNBgSU%2FJImYG6B3kv8ox1OFrjF4gRPVkTyojCP0tsj90x5X1uxU493N8nW3zQm9OEW5Msfjlx%2BcinisqRRIJNgI%3D" rel="nofollow">https://developer.mozilla.org...</a></p>
</blockquote>
<p><code>getComputedStyle</code> 是一个可以获取当前元素所有最终使用的 <code>CSS</code> 属性值。返回的是一个 <code>CSS</code> 样式声明对象([<code>object CSSStyleDeclaration]</code>),只读.</p>
<p>这个方法取得值是内容 <code>CSS</code> 区域的值,与 <code>padding、margin</code>和边框无关。</p>
<h3>偏移量</h3>
<p>包括元素在屏幕上占用的所有可见空间,元素的可见大小是由其高度,宽度决定的。包括所有内边距、滚动条和边框大小(不包括外边距)</p>
<h4>offsetHeight</h4>
<p>元素在垂直方向上的占用空间大小,以像素计。</p>
<p><code>content-height + (可见的水平滚动条的高度) + padding-top + padding-bottom + border-left-width + border-right-width</code></p>
<h4>offsetWidth</h4>
<p>元素在水平方向上的占用空间大小,以像素计。</p>
<p><code>content-width + (可见的垂直滚动条的宽度) + padding-right + padding-left + border-top-width + border-bottom-width</code></p>
<h4>offsetLeft</h4>
<p>元素外边框至包含元素左外边框的之间的像素距离</p>
<h4>offsetTop</h4>
<p>元素外边框至包含元素上外边框的之间的像素距离</p>
<p>要想知道元素在页面上方的偏移量,将这个元素的<code>offsetLeft</code>和<code>offsetTop</code>与其<code>offsetParent</code>的属性相加。循环至根元素,就可以基本得到一个准确的值。</p>
<pre><code class="js">function getElementLeft(element) {
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current !== null) {
actualLeft = actualLeft + current.offetLeft;
current = current.offsetParent;
}
return actualLeft;
}</code></pre>
<pre><code class="js">function getElementLeft(element) {
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current !== null) {
actualTop = actualTop + current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}</code></pre>
<h3>客户区大小</h3>
<p>元素的客户区指的是元素内容及其边框所占据的空间大小。元素内部空间大小,滚动条的空间不算在内。</p>
<h4>clientWidth</h4>
<p><code>content-width + padding-left + padding-right + boreder-left-width + boreder-right-width</code></p>
<h4>clientHeight</h4>
<p><code>content-height + padding-top + padding-bottom + boreder-top-width + boreder-bottom-width</code></p>
<pre><code class="js">function getViewPort() {
if (document.conpatMode == "BackCompat") {
return {
width: document.body.clientWidth,
height: document.body.clientHeight
};
} else {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
};
}
}</code></pre>
<h3>滚动大小</h3>
<p>包含滚动内容的元素大小。</p>
<h4>scrollHeight</h4>
<p>在没有滚动条的情况下,元素内容的总高度</p>
<h4>scrollWidth</h4>
<p>在没有滚动条的情况下,元素内容的总宽度</p>
<h4>scrollLeft</h4>
<p>被隐藏在内容区域<code>左侧</code>的像素数。通过设置这个属性,可以改变滚动条的位置</p>
<h4>scrollTop</h4>
<p>被隐藏在内容区域<code>上方</code>的像素数。通过设置这个属性,可以改变滚动条的位置</p>
<p>确定文档的总高度,必须取得 <code>scrollHeight/clientHeight</code>和 <code>scrollWidth/clientWidth</code>的最大值</p>
<h3>确定元素大小</h3>
<h4>getBoundingClientRect</h4>
<p>返回元素的大小及其相对于视口的位置。</p>
<p>返回值是一个 <code>DOMRect</code> 对象,这个对象是由该元素的 <code>getClientRects()</code> 方法返回的一组矩形的集合,就是该元素的 <code>CSS</code> 边框大小</p>
<p><img src="/img/remote/1460000010540151" alt="img" title="img"></p>
<p>兼容性:</p>
<pre><code class="js">// For scrollX
(((t = document.documentElement) || (t = document.body.parentNode)) &&
typeof t.scrollLeft == "number"
? t
: document.body
).scrollLeft(
// For scrollY
((t = document.documentElement) || (t = document.body.parentNode)) &&
typeof t.scrollTop == "number"
? t
: document.body
).scrollTop;</code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=MYho8OBB%2FuBAFasofIflfQ%3D%3D.0iL84yYEYpAMss8XBhLmvzTLg0E45plNbzXX%2Bazwoqp9d64HI1WEmr5YeAk%2FR8fgVcVeaf6ELO8pxegsU9Suv5v7QTr%2B%2BoMuJEfFBMC7ByQ%3D" rel="nofollow">https://developer.mozilla.org...</a></blockquote>
JS-创建对象的模式
https://segmentfault.com/a/1190000021896621
2020-03-03T09:36:59+08:00
2020-03-03T09:36:59+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>JS-创建对象的模式</h2>
<h3>1.工厂模式</h3>
<p>抽象了创建具体对象的过程,创建了一种函数,封装特定的接口创建对象的细节。</p>
<ul>
<li>新建一个对象</li>
<li>定义属性和方法</li>
<li>
<code>return</code>刚新建的对象</li>
</ul>
<pre><code class="js">function createPerson(name, age, job) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.job = job;
obj.sayHi = function() {
congsole.log(this.name);
};
return obj;
}
var person1 = createPerson("name1", "age1", "job1");
var person2 = createPerson("name2", "age2", "job2");</code></pre>
<p>存在问题:</p>
<ul><li>没有解决对象识别的问题(怎么识别对象的类型)</li></ul>
<h3>2.构造函数模式</h3>
<ul>
<li>不显式创建对象</li>
<li>直接将属性和方法定义在<code>this</code>中</li>
<li>没有<code>return</code>
</li>
<li>通过 <code>new</code>操作符调用</li>
</ul>
<pre><code class="js">function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayHi = function() {
console.log(this.name);
};
}
var person1 = new Person("name1", "age1", "job1");
var person2 = new Person("name2", "age2", "job2");</code></pre>
<p>存在问题:</p>
<ul><li>每个方法都要在实例上创建一次</li></ul>
<h3>3.原型模式</h3>
<p>创建的每一个函数都有<code>prototype</code>原型属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。</p>
<p>所有原生引用类型都在其构造函数的原型上定义了方法</p>
<pre><code class="js">function Person{
}
Person.prototype.name="name"
Person.prototype.age="age"
Person.prototype.job="job"
Person.prototype.sayHi=function(){
console.log(this.name)
}
var person1=new Person()
var person2=new Person()
</code></pre>
<p>存在问题:</p>
<ul><li>所有属性是被很多实例共享的</li></ul>
<h3>4.组合构造和原型模式</h3>
<p>构造函数用来定义实例属性,原型模式用来定义方法和共享的属性</p>
<pre><code class="js">function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
Person.prototype={
constructor:Person;
sayHi:function(){
console.log(this.name)
}
}
var person1 = new Person("name1", "age1", "job1");</code></pre>
<h3>5.动态原型模式</h3>
<p>这里对原型的修改,能够对所有实例生效,只在初次调用构造函数会执行</p>
<pre><code class="js">function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
if(typeof this.sayHi !="function" ){
Person.prototype.sayHi=functong(){
console.log(this.name)
}
}
}
var person1 = new Person("name1", "age1", "job1");
</code></pre>
<h3>6.寄生构造函数模式</h3>
<p>工厂模式+构造函数</p>
<ul>
<li>返回的对象与构造函数、构造函数的原型属性之间没有关系</li>
<li>不能依赖<code>instanceof</code>来去定对象原型</li>
<li>不建议使用</li>
</ul>
<pre><code class="js">function Person(name, age, job) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.job = job;
obj.sayHi = function() {
congsole.log(this.name);
};
return obj;
}
var person1 = new Person("name1", "age1", "job1");</code></pre>
<h3>7.稳妥构造模式</h3>
<ul>
<li>没有公共属性</li>
<li>不使用<code>this</code>
</li>
<li>不使用<code>new</code>
</li>
</ul>
<pre><code class="js">function Person(name, age, job) {
var obj = new Object();
// 定义室友变量和函数
obj.sayHi = function() {
congsole.log(name);
};
return obj;
}
var person1 = Person("name1", "age1", "job1");
// name1</code></pre>
<blockquote>摘抄自《JavaScript 高级程序设计》第六章</blockquote>
手写 flat
https://segmentfault.com/a/1190000021858836
2020-02-27T21:06:34+08:00
2020-02-27T21:06:34+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>手写 flat</h2>
<p><code>flat()</code>方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回</p>
<pre><code class="js">var newArray = arr.flat([depth]);
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]</code></pre>
<h3>展开一层</h3>
<pre><code class="js">var arr = [1, 2, [3, 4]];
// 展开一层数组
arr.flat();
// 等效于
arr.reduce((acc, val) => acc.concat(val), []);
// [1, 2, 3, 4]
// 使用扩展运算符 ...
const flattened = arr => [].concat(...arr);</code></pre>
<pre><code class="js">// 使用 reduce、concat 和递归展开无限多层嵌套的数组
var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]];
function flatDeep(arr, d = 1) {
return d > 0
? arr.reduce(
(acc, val) =>
acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val),
[]
)
: arr.slice();
}
flatDeep(arr1, Infinity);
// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]</code></pre>
<h3>Use Generator function</h3>
<pre><code class="js">function* flatten(array) {
for (const item of array) {
if (Array.isArray(item)) {
yield* flatten(item);
} else {
yield item;
}
}
}
var arr = [1, 2, [3, 4, [5, 6]]];
const flattened = [...flatten(arr)];
// [1, 2, 3, 4, 5, 6]</code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=0lyE3rhyRCV7yFAfxMp%2BEw%3D%3D.MCmSospIRvvrKbKT5RglNgA%2F%2FE8kAw01t1qO4zXEKiD6cPeQ%2BKWE2okUhEjrJKlc0Iwv%2B%2BdQjmPqqM0pp25GXGEZGkJwZOhH5%2FKokB4LXTYKbdDkQFri%2B3H9qjsuXWNn" rel="nofollow">https://developer.mozilla.org...</a></blockquote>
手写 reduce
https://segmentfault.com/a/1190000021858714
2020-02-27T20:53:37+08:00
2020-02-27T20:53:37+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
3
<h2>手写 reduce</h2>
<p><code>reduce()</code> 方法对数组中的每个元素执行一个由您提供的 <code>reducer</code> 函数(升序执行),将其结果汇总为单个返回值</p>
<p><code>reducer</code> 函数接收 4 个参数:</p>
<ul>
<li>Accumulator (acc) (累计器)</li>
<li>Current Value (cur) (当前值)</li>
<li>Current Index (idx) (当前索引)</li>
<li>Source Array (src) (源数组)</li>
</ul>
<p>您的 <code>reducer</code> 函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。</p>
<p>回调函数第一次执行时,<code>accumulator</code> 和 <code>currentValue</code> 的取值有两种情况:</p>
<ul>
<li>如果调用 reduce()时提供了 initialValue,accumulator 取值为 initialValue,currentValue 取数组中的第一个值;</li>
<li>如果没有提供 initialValue,那么 accumulator 取数组中的第一个值,currentValue 取数组中的第二个值。</li>
</ul>
<p><code>arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])</code></p>
<h3>代码</h3>
<pre><code class="js">Array.prototyp.reduce=fuction(fn,accumulator){
if(typeof fn !== "function"){
throw "参数必须为函数"
}
//get array going to be iterated
let arr = this;
if(!Array.isArray(arr)){
throw "只能对数组使用reduce方法"
}
let index = 0;
if(!accumulator){
index = 1;
accumulator = arr[0];
}
for(;index<arr.length;index++){
let invokedReturn = fn(accumulator ,arr[index],index,arr);
accumulator = invokedReturn ;
}
return accumulator ;
}</code></pre>
手写 some
https://segmentfault.com/a/1190000021858684
2020-02-27T20:51:56+08:00
2020-02-27T20:51:56+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>手写 some</h2>
<p><code>some()</code>方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 <code>Boolean</code> 类型的值。</p>
<ul><li>
<p>callback<br>用来测试每个元素的函数,接受三个参数:</p>
<ul>
<li>element<br>数组中当前正在处理的元素。</li>
<li>index 可选<br>正在处理的元素在数组中的索引。</li>
<li>array 可选<br>调用了 filter 的数组本身。</li>
<li>thisArg 可选<br>执行 callback 时,用于 this 的值。</li>
</ul>
</li></ul>
<pre><code class="js">Array.prototype.some = function(fn) {
if (typeof fn !== "function") {
throw "参数必须为函数";
}
//get array going to be iterated
let arr = this;
if (!Array.isArray(arr)) {
throw "只能对数组使用some方法";
}
for (let index = 0; index < arr.length; index++) {
let invokedReturn = fn(arr[index], index, arr);
if (invokedReturn) {
return true;
}
}
return false;
};</code></pre>
手写 filter
https://segmentfault.com/a/1190000021858662
2020-02-27T20:46:43+08:00
2020-02-27T20:46:43+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>手写 filter</h2>
<p><code>filter()</code>方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。</p>
<p><code>filter</code> 不会改变原数组,它返回过滤后的新数组</p>
<pre><code class="js">var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])</code></pre>
<ul><li>
<p>callback<br>用来测试数组的每个元素的函数。返回 true 表示该元素通过测试,保留该元素,false 则不保留。它接受以下三个参数:</p>
<ul>
<li>element<br>数组中当前正在处理的元素。</li>
<li>index 可选<br>正在处理的元素在数组中的索引。</li>
<li>array 可选<br>调用了 filter 的数组本身。</li>
<li>thisArg 可选<br>执行 callback 时,用于 this 的值。</li>
</ul>
</li></ul>
<pre><code class="js">Array.prototype.filter = function(fn, thisArg) {
var _this;
if (typeof fn !== "function") {
throw "参数必须为函数";
}
//get array going to be iterated
let arr = this;
if (!Array.isArray(arr)) {
throw "只能对数组使用forEach方法";
}
if (arguments.length > 1) {
_this = thisArg;
}
let result = [];
for (let index = 0; index < arr.length; index++) {
let invokedReturn = fn.call(_this, arr[index], index, arr);
if (invokedReturn) {
result.push(arr[index]);
}
}
return result;
};</code></pre>
手写 forEach
https://segmentfault.com/a/1190000021858625
2020-02-27T20:41:31+08:00
2020-02-27T20:41:31+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>手写 forEach</h2>
<p><code>forEach()</code>方法对数组的每个元素执行一次提供的函数</p>
<pre><code class="js">arr.forEach(callback(currentValue [, index [, array]])[, thisArg]);</code></pre>
<ul>
<li>
<p>callback</p>
<ul>
<li>currentValue<br>数组中正在处理的当前元素。</li>
<li>index 可选<br>数组中正在处理的当前元素的索引。</li>
<li>array 可选<br>forEach() 方法正在操作的数组。</li>
<li>thisArg 可选<br>可选参数。当执行回调函数 callback 时,用作 this 的值。</li>
</ul>
</li>
<li>没有返回值</li>
</ul>
<p>如果提供了一个 <code>thisArg</code> 参数给 <code>forEach</code> 函数,则参数将会作为回调函数中的 <code>this</code> 值。否则 <code>this</code> 值为 <code>undefined</code>。回调函数中 <code>this</code> 的绑定是根据函数被调用时通用的 <code>this</code> 绑定规则来决定的。</p>
<pre><code class="js">let arr = [1, 2, 3, 4];
arr.forEach((...item) => console.log(item));
// [1, 0, Array(4)] 当前值</code></pre>
<pre><code class="js">function Counter() {
this.sum = 0;
this.count = 0;
}
// 因为 thisArg 参数(this)传给了 forEach(),每次调用时,它都被传给 callback 函数,作为它的 this 值。
Counter.prototype.add = function(array) {
array.forEach(function(entry) {
this.sum += entry;
++this.count;
}, this);
// ^---- Note
};
const obj = new Counter();
obj.add([2, 5, 9]);
obj.count;
// 3 === (1 + 1 + 1)
obj.sum;
// 16 === (2 + 5 + 9)</code></pre>
<ul>
<li>每个数组都有这个方法</li>
<li>回调参数为:每一项、索引、原数组</li>
</ul>
<pre><code class="js">Array.prototype.forEach = function(fn, thisArg) {
var _this;
if (typeof fn !== "function") {
throw "参数必须为函数";
}
if (arguments.length > 1) {
_this = thisArg;
}
if (!Array.isArray(arr)) {
throw "只能对数组使用forEach方法";
}
for (let index = 0; index < arr.length; index++) {
fn.call(_this, arr[index], index, arr);
}
};</code></pre>
DOM 事件机制
https://segmentfault.com/a/1190000021854085
2020-02-27T12:55:48+08:00
2020-02-27T12:55:48+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
3
<h2>DOM 事件机制</h2>
<ul>
<li>事件是元素天生具备的行为方式(和写不写 <code>JS</code> 代码没关系),当我们去操作元素的时候会触发元素的很多事件</li>
<li>给当前元素的某一个事件绑定方法,目的是为了让当前元素某个事件被触发的时候,可以做一些事情</li>
</ul>
<h3>DOM0 事件</h3>
<p>DOM0 事件绑定</p>
<pre><code class="js">element.onclick=function(e){
//=>this:element
e=e||window.event;
element.onmouseenter=function(){}</code></pre>
<ul>
<li>给当前元素对象的某一个私有属性(onxxx 这样的私有属性)赋值的过程(之前属性默认值是 <code>null</code>,如果我们给赋值一个函数,相当于绑定了一个方法)</li>
<li>当赋值成功(赋值一个函数),此时浏览器会把 <code>DOM</code> 元素和赋值的函数建立关联,以及建立 <code>DOM</code> 元素行为操作的监听,当某一个行为被用户触发,浏览器会把相关行为赋值的函数执行</li>
<li>1、只有 DOM 元素天生拥有这个私有属性(onxxx 事件私有属性),我们赋值的方法才叫做事件绑定,否则属于给当前元素设置一个自定义属性而已</li>
<li>2、移除事件绑定的时候,我们只需要赋值为 <code>null</code> 即可</li>
<li>3、在 DOM0 事件绑定中,只能给当前元素的某一个事件行为(某一个事件私有属性)绑定一个方法,绑定多个方法,最后一次绑定的会把之前绑定的都替换掉</li>
</ul>
<pre><code class="js">btn.onclick = function() {
console.log("this is a click event");
};</code></pre>
<p><code>click</code> 事件并没有像其他函数一样,必须要调用才可以执行,click 事件并不确定什么时候发生,而当浏览器发现用户点击该按钮时,浏览器就检测 <code>btn.onclick</code> 是否有值,如果有,就会执行 <code>btn.onclick.call(btn,event)</code>,此时函数执行,<code>call()</code> 方法接收两个参数,第一个指向调用当前方法的对象,也就是 <code>this</code>。</p>
<p>需要注意的是,指定的 <code>this</code> 值并不一定是该函数执行时真正的 <code>this</code> 值,如果这个函数处于非严格模式下,则指定为 <code>null</code> 和 <code>undefined</code> 的 <code>this</code> 值会自动指向全局对象(浏览器中就是 <code>window</code> 对象),同时值为原始值(数字,字符串,布尔值)的 <code>this</code> 会指向该原始值的自动包装对象。</p>
<p>另一个参数则是事件对象 <code>event</code>,该对象也可以通过 <code>arguments[0]</code> 来访问,它包含了事件相关的所有信息,如本例子中,则包含了点击事件的全部信息。可以通过给函数传参来获取事件信息。</p>
<pre><code class="js">btn.onclick = function(e) {
console.log("this is a click event");
console.log(e); // 事件对象
};</code></pre>
<p>在 DOM0 级中,如果想要实现一个对象绑定多个函数,可以这样实现。</p>
<pre><code class="js">function fn1() {
// do something
}
function fn2() {
// do something
}
btn.onclick = function(e) {
fn1.call(this, xxx);
fn2.call(this, yyy);
};</code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=vPWJ1K7AvmCUQ2SIAlUXtQ%3D%3D.Fr88%2BofNG8dnrIdSXljIvNphOAKpSJ2Gt4uT429zXSg7omb3acNEtFZuF9rnydxN" rel="nofollow">https://github.com/HarleyWang...</a></blockquote>
<h3>DOM2 事件</h3>
<p>当前这个实例 <code>document.body</code> 之所以能用 <code>addEventListener</code> 这个方法,说明这个方法肯定在它的原型上出现。</p>
<p>可以用 <code>instanceof</code> 检测</p>
<p><code>DOM2</code> 级事件定义了两个方法,用于处理指定和删除事件处理程序的操作,<code>addEventListener()</code>和 <code>removeEventListener()</code>,所有的 <code>DOM</code> 节点中都包含这两个方法,它们都接收 <code>3</code> 个参数</p>
<ul>
<li>要处理的事件名</li>
<li>作为事件处理程序的函数</li>
<li>布尔值,<code>true</code> 代表在捕获阶段调用事件处理程序,<code>false</code> 表示在冒泡阶段调用事件处理程序,默认为 <code>false</code>。</li>
</ul>
<pre><code class="js">btn.addEventListener("click", function() {
// do something
});
btn.addEventListener("click", function() {
// do something else
});</code></pre>
<pre><code class="js">element.addEventListener(
"click",
function(event) {
// 只执行一次的代码
},
{ once: true }
);</code></pre>
<ul>
<li>1、 <code>DOM2</code> 事件绑定使用的 <code>addEventListener/attachEvent</code> 都是在 <code>EventTarget</code> 这个内置类的原型上定义的,调取使用的时候,首先通过原型链找到这个方法,然后执行完成事件绑定的效果</li>
<li>浏览器首先会给当前元素的某一个事件行为开辟一个事件池(事件队列)[其实是浏览器有一个统一的事件池,我们每个元素的某个行为绑定的方法都放在这个事件池中,只是通过相关的标识来区分的],当我们通过 addEventListener 做事件监听的时候,会把绑定的方法存放在事件池中</li>
<li>当元素的某一个行为触发,浏览器会到对应的事件池中,把当前存放在事件池中的所有方法,依次按照存放的先后顺序执行</li>
</ul>
<p><code>DOM0</code> 是私有属性赋值,<code>DOM2</code> 是事件池的事件机制</p>
<ul>
<li>1、所有 <code>DOM0</code> 支持的事件行为,DOM2 都可以使用,不仅如此,DOM2 还支持一些 <code>DOM0</code> 没有的事件行为:<code>DOMContentLoaded...</code>
</li>
<li>2、DOM2 中可以给当前元素的某一个事件行为绑定‘多个不同的方法’(不同的方法=>不同的空间地址,比如一个私有 fn 和全局的 fn 地址不一样,所以不是同一个 fn 方法,)(因为绑定的所有方法都存放在事件池中了;而 DOM0 是通过给私有属性赋值来绑定方法的,一个私有属性只能附一个值)</li>
<li>3、<code>DOM2</code> 事件绑定的移除比较麻烦一些,需要和绑定的时候:事件类型、绑定的方法、传播阶段,三个完全一致才可以移除掉</li>
</ul>
<h3>DOM3 事件</h3>
<p>DOM3 级事件在 DOM2 级事件的基础上添加了更多的事件类型,全部类型如下</p>
<table>
<thead><tr>
<th>事件类型</th>
<th>说明</th>
<th>举例</th>
</tr></thead>
<tbody>
<tr>
<td>UI 事件</td>
<td>当用户与页面上的元素交互时触发</td>
<td>{load、scroll</td>
</tr>
<tr>
<td>焦点事件</td>
<td>当元素获得或失去焦点时触发</td>
<td>blur、focus</td>
</tr>
<tr>
<td>鼠标事件</td>
<td>当用户通过鼠标在页面执行操作时触发</td>
<td>dbclick、mouseup</td>
</tr>
<tr>
<td>滚轮事件</td>
<td>当使用鼠标滚轮或类似设备时触发</td>
<td>mousewheel</td>
</tr>
<tr>
<td>文本事件</td>
<td>当在文档中输入文本时触发</td>
<td>textInput</td>
</tr>
<tr>
<td>键盘事件</td>
<td>当用户通过键盘在页面上执行操作时触发</td>
<td>keydown、keypress</td>
</tr>
<tr>
<td>合成事件</td>
<td>当为 IME(输入法编辑器)输入字符时触发</td>
<td>compositionstart</td>
</tr>
<tr>
<td>变动事件</td>
<td>当底层 DOM 结构发生变化时触发</td>
<td>DOMsubtreeModified</td>
</tr>
</tbody>
</table>
<p>同时 <code>DOM3</code> 级事件也允许开发人员自定义一些事件。</p>
<pre><code class="js">var event = new Event('build');
// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);
// Dispatch the event.
elem.dispatchEvent(event);</code></pre>
<p>添加自定义数据 – <code>CustomEvent()</code></p>
<pre><code class="js">var event = new CustomEvent("build", { detail: elem.dataset.time });</code></pre>
<h3>冒泡事件&捕获事件</h3>
<p><img src="/img/remote/1460000021854088" alt="img" title="img"></p>
<h3>事件委托</h3>
<p>由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理(<code>delegation</code>)<br>。</p>
<p>那么利用事件冒泡或捕获的机制,可以对事件绑定做一些优化。 在 JS 中,如果我们注册的事件越来越多,页面的性能就越来越差,因为:</p>
<ul>
<li>函数是对象,会占用内存,内存中的对象越多,浏览器性能越差</li>
<li>注册的事件一般都会指定 DOM 元素,事件越多,导致 DOM 元素访问次数越多,会延迟页面交互就绪时间。</li>
<li>删除子元素的时候不用考虑删除绑定事件</li>
</ul>
<pre><code class="js">
<ul id="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
// 给父层元素绑定事件
document.getElementById('list').addEventListener('click', function (e) {
// 兼容性处理
var event = e || window.event;
var target = event.target || event.srcElement;
// 判断是否匹配目标元素
if (target.nodeName.toLocaleLowerCase() === 'li') {
console.log('the content is: ', target.innerHTML);
}
});
</script>
</code></pre>
<h3>EventTarget</h3>
<p><code>DOM</code> 的事件操作(监听和触发),都定义在 <code>EventTarget</code> 接口。所有节点对象都部署了这个接口,其他一些需要事件通信的浏览器内置对象(比如,<code>XMLHttpRequest</code>、<code>AudioNode</code>、<code>AudioContext</code>)也部署了这个接口</p>
<p>该接口主要提供三个实例方法。</p>
<ul>
<li>addEventListener:绑定事件的监听函数;用于在当前节点或对象上,定义一个特定事件的监听函数。一旦这个事件发生,就会执行监听函数。该方法没有返回值。</li>
<li>removeEventListener:移除事件的监听函数;方法用来移除 addEventListener 方法添加的事件监听函数;removeEventListener 方法的参数,与 addEventListener 方法完全一致</li>
<li>dispatchEvent:触发事件;当前节点上触发指定事件,从而触发监听函数的执行。该方法返回一个布尔值,只要有一个监听函数调用了 Event.preventDefault(),则返回值为 false,否则为 true;参数是 Event 实例</li>
</ul>
<h3>Event 对象常见的方法和属性</h3>
<ul>
<li>event. preventDefault();如果调用这个方法,默认事件行为将不再触发。什么是默认事件呢?例如表单一点击提交按钮(submit)刷新页面、a 标签默认页面跳转或是锚点定位等</li>
<li>event.stopPropagation() & event.stopImmediatePropagation() event.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行; stopImmediatePropagation 既能阻止事件向父元素冒泡,也能阻止元素同事件类型的其它监听器被触发。而 stopPropagation 只能实现前者的效果</li>
</ul>
<blockquote>
<a href="https://link.segmentfault.com/?enc=hrmda1XmlcC9LXI9toTo%2FA%3D%3D.SLo7YrLzgUDCrI2jAqyFroNulA7Xpo3FRsSpNAj0qebAdN3MWlCHGGYnG0L0YTSG" rel="nofollow">https://juejin.im/post/5be643...</a><p><a href="https://link.segmentfault.com/?enc=9F%2F4Hh%2F08GHkfpN2%2B7QeEQ%3D%3D.pH8FecAiwowEcVkdShoTdh1deR5s6z361pSfeoEuA3P5xxYYEZUfBgo5rRJeJjNtmeitIxOXL2eZP36gksm3Jbq3F%2BJASk%2FmTr0c5R%2FN41c%3D" rel="nofollow">https://www.w3.org/TR/DOM-Lev...</a></p>
<p><a href="https://link.segmentfault.com/?enc=tSws6PWCZYR6ZzKlwpxNOA%3D%3D.yI0RbER%2FSD5HbgVAFuA0CXMaVracTfzbrM5xLwzr2EOx%2BBuakIOEODxxd%2BHKa5lrzGN3AyZ2IjY3Mi9qyTAS1w%3D%3D" rel="nofollow">https://javascript.ruanyifeng...</a></p>
<p><a href="https://link.segmentfault.com/?enc=aWXY41XykN3TuoXZuLep3A%3D%3D.%2BwecWyrmHcPBaMSHkp3BCjvff74iqTKoB7WLbMmN4wOTY44tn5GSHpcih385Nh1JmvIUuUynvOFXVhd4waA5Iw%3D%3D" rel="nofollow">https://javascript.ruanyifeng...</a></p>
</blockquote>
React 错误边界
https://segmentfault.com/a/1190000021853897
2020-02-27T12:30:40+08:00
2020-02-27T12:30:40+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 错误边界</h2>
<blockquote><a href="https://link.segmentfault.com/?enc=6%2B66epqtIMERGTKipDfl3A%3D%3D.V81gKV4hNBfIotcBco8a%2BpoqwW0ZwcrQKEA0EeLfaYvFCPmkNhwoBh4ZVcNUfzzj" rel="nofollow">http://react.html.cn/docs/err...</a></blockquote>
<h3>错误边界介绍</h3>
<p>部分 <code>UI</code> 中的 <code>JavaScript</code> 错误不应该破坏整个应用程序。 为了解决 <code>React</code> 用户的这个问题,<code>React 16</code> 引入了一个 <code>错误边界(Error Boundaries)</code> 的新概念</p>
<p>错误边界是 <code>React</code> 组件,它可以 在子组件树的任何位置捕获 <code>JavaScript</code> 错误,记录这些错误,并显示一个备用 <code>UI</code> ,而不是使整个组件树崩溃。 错误边界(<code>Error Boundaries</code>) 在渲染,生命周期方法以及整个组件树下的构造函数中捕获错误。g</p>
<p>错误边界无法捕获如下错误:</p>
<ul>
<li>事件处理</li>
<li>异步代码 (例如 setTimeout 或 requestAnimationFrame 回调函数)</li>
<li>服务端渲染</li>
<li>错误边界自身抛出来的错误 (而不是其子组件)</li>
</ul>
<p>如果一个类组件定义了生命周期方法中的任何一个(或两个)<code>static getDerivedStateFromError()</code> 或 <code>componentDidCatch()</code>,那么它就成了一个错误边界。 使用 <code>static getDerivedStateFromError()</code>在抛出错误后渲染回退 <code>UI</code>。 使用 <code>componentDidCatch()</code> 来记录错误信息。</p>
<pre><code class="js">class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}</code></pre>
<p>而后你可以像一个普通的组件一样使用:</p>
<pre><code class="jsx"><ErrorBoundary>
<MyWidget />
</ErrorBoundary></code></pre>
<p>错误边界(<code>Error Boundaries</code>) 仅可以捕获其子组件的错误。错误边界无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会向上冒泡至最接近的错误边界</p>
<h3>static getDerivedStateFromError()</h3>
<p><code>在后代组件抛出错误后调用此生命周期</code>。 它接收作为参数抛出的错误,并应返回值以更新 <code>state</code>(状态)。</p>
<h3>componentDidCatch()</h3>
<p><code>componentDidCatch(error, info)</code></p>
<ul>
<li>error - 抛出的错误。</li>
<li>info - 包含 componentStack 键的对象,其中包含 有关哪个组件引发错误的信息 。</li>
</ul>
JavaScript 双向链表_048
https://segmentfault.com/a/1190000021166696
2019-12-02T09:56:45+08:00
2019-12-02T09:56:45+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>JavaScript 双向链表</h2>
<p>一个 双向链表(<code>doubly linked list</code>) 是由一组称为节点的顺序链接记录组成的链接数据结构。每个节点包含两个字段,称为链接,它们是对节点序列中上一个节点和下一个节点的引用</p>
<p>开始节点和结束节点的上一个链接和下一个链接分别指向某种终止节点,通常是前哨节点或null,以方便遍历列表。如果只有一个前哨节点,则列表通过前哨节点循环链接。它可以被概念化为两个由相同数据项组成的单链表,但顺序相反。</p>
<pre><code class="js">class DNode {
constructor(val) {
this.val = val;
this.prev = null;
this.next = null;
}
}</code></pre>
<h3>增加节点</h3>
<pre><code class="js">function add(el) {
var currNode = this.head;
while (currNode.next != null) {
currNode = currNode.next;
}
var newNode = new DNode(el);
newNode.next = currNode.next;
currNode.next = newNode;
}</code></pre>
<h3>查找</h3>
<pre><code class="js">function find(el) {
var currNode = this.head;
while (currNode && currNode.el != el) {
currNode = currNode.next;
}
return currNode;
}</code></pre>
<h3>插入</h3>
<pre><code class="js">function (newEl, oldEl) {
var newNode = new DNode(newEl);
var currNode = this.find(oldEl);
if (currNode) {
newNode.next = currNode.next;
newNode.prev = currNode;
currNode.next = newNode;
} else {
throw new Error('未找到指定要插入节点位置对应的值!')
}
}</code></pre>
<h3>展示</h3>
<pre><code class="js">// 顺序
function () {
var currNode = this.head.next;
while (currNode) {
console.log(currNode.el);
currNode = currNode.next;
}
}
// 逆序
function () {
var currNode = this.head;
currNode = this.findLast();
while (currNode.prev != null) {
console(currNode.el);
currNode = currNode.prev;
}
}</code></pre>
<h3>删除</h3>
<pre><code class="js">function (el) {
var currNode = this.find(el);
if (currNode && currNode.next != null) {
currNode.prev.next = currNode.next;
currNode.next.prev = currNode.prev;
currNode.next = null;
currNode.previous = null;
} else {
throw new Error('找不到要删除对应的节点');
}
}</code></pre>
JavaScript 链表_047
https://segmentfault.com/a/1190000021166038
2019-12-02T08:28:46+08:00
2019-12-02T08:28:46+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>JavaScript 链表</h2>
<h3>链表</h3>
<p>一个 <strong>链表</strong> 是数据元素的线性集合, 元素的线性顺序不是由它们在内存中的物理位置给出的。 相反, 每个元素指向下一个元素。它是由一组节点组成的数据结构,这些节点一起,表示序列。</p>
<p><strong>链表的一个缺点是访问时间是线性的(而且难以管道化)。</strong></p>
<pre><code class="js">class Node {
constructor(val) {
this.val = val;
this.next = null;
}
}</code></pre>
<h3>显示链表</h3>
<pre><code class="js">function display () {
var currNode = this.head;
while ( !(currNode.next == null) ){
console.log( currNode.next.element );
currNode = currNode.next;
}
}</code></pre>
<h3>查找</h3>
<pre><code class="js">function find ( item ) {
var currNode = this.head;
while ( currNode.element != item ){
currNode = currNode.next;
}
return currNode;
}</code></pre>
<h3>插入</h3>
<pre><code class="js">function insert ( newElement , item ) {
var newNode = new Node( newElement );
var currNode = this.find( item );
newNode.next = currNode.next;
currNode.next = newNode;
}</code></pre>
<h3>删除</h3>
<pre><code class="js">function findPrev( item ) {
var currNode = this.head;
while ( !( currNode.next == null) && ( currNode.next.element != item )){
currNode = currNode.next;
}
return currNode;
}
function remove ( item ) {
var prevNode = this.findPrev( item );
if( !( prevNode.next == null ) ){
prevNode.next = prevNode.next.next;
}
}
</code></pre>
<blockquote>
<a href="https://link.segmentfault.com/?enc=i1dG%2Bnxi1E8lFifBQD767Q%3D%3D.Xfk08%2FQ%2B3ATvTZ35M5eOu1a6fN2zbgaX%2FbK1Q8HraxNLvo9iInnl9HKUUob9ky7yVgUWvHC2BXjr1yYfpIGqElmt2QD%2Bn8t7jgHgaIhWhWETLA8sET%2BHbHGvzw%2BaSQ6pUs5dakfB0xxRqti2395CZg%3D%3D" rel="nofollow">https://github.com/trekhleb/j...</a><p><a href="https://link.segmentfault.com/?enc=o3ByjIBpyPMojHA%2FuwJ5HQ%3D%3D.5IZRvtQ7x%2B%2BWqF77rzr106Kl5HnBrp63Swyg%2FJlOTW%2FR8DHhAVkBZCyu5E3MEHp4FVOG8B9ytXwfFhLhLkG63A%3D%3D" rel="nofollow">https://juejin.im/entry/59cb7...</a></p>
</blockquote>
CSS - position 定位_046
https://segmentfault.com/a/1190000021081597
2019-11-21T21:59:02+08:00
2019-11-21T21:59:02+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<p><img src="/img/bVbAEdp" alt="CSS-position 定位.png" title="CSS-position 定位.png"></p>
<blockquote><a href="https://link.segmentfault.com/?enc=D9rYh%2BPt4lnz1M0t9yyzAw%3D%3D.IbVwsltlXFFOYygNCG2m4nTp9DteM5fnogFIdgGhiRToadwfwSd7vQImwUPVqg2mE0JidH96Yvgl2RdIGG7jTw%3D%3D" rel="nofollow">https://developer.mozilla.org/zh-CN/docs/Web/CSS/position</a></blockquote>
JavaScript 继承_045
https://segmentfault.com/a/1190000021047561
2019-11-19T00:18:15+08:00
2019-11-19T00:18:15+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>
<code>JavaScript</code> 继承</h2>
<p><img src="/img/bVbAtA7" alt="JavaScript 继承.png" title="JavaScript 继承.png"></p>
<h3>原型链继承</h3>
<pre><code class="js">function Parent() {
this.name = '父亲'; // 实例基本属性 (该属性,强调私有,不共享)
this.arr = [1]; // (该属性,强调私有)
}
Parent.prototype.say = function() { // -- 将需要复用、共享的方法定义在父类原型上
console.log('hello')
}
function Child(like) {
this.like = like;
}
Child.prototype = new Parent() // 核心
let boy1 = new Child()
let boy2 = new Child()
// 优点:共享了父类构造函数的say方法
console.log(boy1.say(), boy2.say(), boy1.say === boy2.say); // hello , hello , true
// 缺点1:不能传参数
// 缺点2:
console.log(boy1.name, boy2.name, boy1.name===boy2.name); // 父亲,父亲,true
boy1.arr.push(2); // 修改了boy1的arr属性,boy2的arr属性,也会变化,因为两个实例的原型上(Child.prototype)有了父类构造函数的实例属性arr;所以只要修改了boy1.arr,boy2.arr的属性也会变化。 ---- 原型上的arr属性是共享的。
console.log(boy2.arr); // [1,2]
注意:修改boy1的name属性,是不会影响到boy2.name。因为name是基本属性,不是引用属性。</code></pre>
<h3>构造函数继承</h3>
<pre><code class="js">function Parent(name) {
this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
this.arr = [1]; // (该属性,强调私有)
this.say = function() { // 实例引用属性 (该属性,强调复用,需要共享)
console.log('hello')
}
}
function Child(name,like) {
Parent.call(this,name); // 核心
this.like = like;
}
let boy1 = new Child('小红','apple');
let boy2 = new Child('小明', 'orange ');
// 优点1:可传参
console.log(boy1.name, boy2.name); // 小红, 小明
// 优点2:不共享父类构造函数的引用属性
boy1.arr.push(2);
console.log(boy1.arr,boy2.arr);// [1,2] [1]
// 缺点1:方法不能复用
console.log(boy1.say === boy2.say) // false (说明,boy1和boy2
的say方法是独立,不是共享的)
// 缺点2:不能继承父类原型上的方法
Parent.prototype.walk = function () { // 在父类的原型对象上定义一个walk方法。
console.log('我会走路')
}
boy1.walk; // undefined (说明实例,不能获得父类原型上的方法)</code></pre>
<h3>组合继承</h3>
<pre><code class="js">function Parent(name) {
this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
this.arr = [1]; // (该属性,强调私有)
}
Parent.prototype.say = function() { // --- 将需要复用、共享的方法定义在父类原型上
console.log('hello')
}
function Child(name,like) {
Parent.call(this,name,like) // 核心 第二次
this.like = like;
}
Child.prototype = new Parent() // 核心 第一次
<!--这里是修复构造函数指向的代码-->
let boy1 = new Child('小红','apple')
let boy2 = new Child('小明','orange')
// 优点1:可以传参数
console.log(boy1.name,boy1.like); // 小红,apple
// 优点2:可复用父类原型上的方法
console.log(boy1.say === boy2.say) // true</code></pre>
<h3>原型式继承</h3>
<pre><code class="js">// 两种方法
// Object.create
// function object
function object(o){
function F(){
}
F.pototype=o;
return new F()
}
let parent = {
name: 'parent',
share: [1, 2, 3], // 父类的引用属性全部被子类所共享
log: function() { // 父类方法可以复用
return this.name
}
}
let child = Object.create(parent) // 子类不能向父类传递参数</code></pre>
<h3>寄生继承</h3>
<pre><code class="js">function createAnother(original){
var clone = object(original)
clone.sayHi = function(){
alert('hi');
}
return clone;
}
var person = {
name:"hello",
friends:['z',"x","p"]
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi() //'hi'
</code></pre>
<h3>寄生组合继承</h3>
<pre><code class="js">function Parent(name, friends) {
this.name = name
this.friends = friends
}
Parent.prototype = {
constructor: Parent, // 需要手动绑定 constructor
share: [1, 2, 3],
log: function() {
return this.name
}
}
function Child(name, friends, gender) {
Parent.call(this, name, friends) // 这里只需要调用一次 Parent
this.gender = gender
}
// 上半部分和组合继承一样
let F = function() {} // 创建一个中介函数
F.prototype = Parent.prototype // 这个中介的原型指向 Parent 的原型
Child.prototype = new F() // 注意这里没有使用 new 操作符调用 Parent
Child.prototype.constructor = Child
// 封装
function Parent(name, friends) {
this.name = name // 可以定义私有属性
this.friends = friends // 可以定义公有引用属性不会被共享
}
Parent.prototype = {
constructor: Parent, // 需要手动绑定 constructor
share: [1, 2, 3], // 这里定义的公有属性会被共享
log: function() { // 方法被共享了
return this.name
}
}
function Child(name, friends, gender) {
Parent.call(this, name, friends) // 可以向父类传递参数 这里又调用了一次 Parent
this.gender = gender
}
function proto(child, parent) {
let clonePrototype = Object.create(parent.prototype)
//核心 通过创建中间对象,子类原型和父类原型,就会隔离开
child.prototype = clonePrototype
child.prototype.constructor = child
}
proto(Child, Parent)
</code></pre>
<h3>
<code>ES2015 class extends</code> 继承</h3>
<pre><code class="js">lass Parent {
constructor(name, friends) { // 该属性在构造函数上,不共享
this.name = name
this.friends = friends
}
log() { // 该方法在原型上,共享
return this
}
}
Parent.prototype.share = [1, 2, 3] // 原型上的属性,共享
class Child extends Parent {
constructor(name, friends, gender) {
super(name, friends)
this.gender = gender
}
}</code></pre>
React 源码阅读-13_044
https://segmentfault.com/a/1190000020971927
2019-11-11T23:18:42+08:00
2019-11-11T23:18:42+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 源码阅读-13</h2>
<h3><code>React Element</code></h3>
<p><img src="/img/bVbz9WE?w=2724&h=4922" alt="ReactElement.png" title="ReactElement.png"></p>
<h3><code>Function</code></h3>
<p><code>Object.getOwnPropertyDescriptor()</code> 方法返回指定对象上一个自有属性对应的属性描述符</p>
<p><code>Object.defineProperty()</code> 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。</p>
<h4><code>hasValidRef</code></h4>
<p>判断是否有有效的<code>ref</code></p>
<pre><code class="js">function hasValidRef(config) {
if (__DEV__) {
if (hasOwnProperty.call(config, 'ref')) {
const getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.ref !== undefined;
}</code></pre>
<h4><code>hasValidKey</code></h4>
<p>判断是否有有效的 <code>Key</code></p>
<pre><code class="js">function hasValidKey(config) {
if (__DEV__) {
if (hasOwnProperty.call(config, 'key')) {
const getter = Object.getOwnPropertyDescriptor(config, 'key').get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.key !== undefined;
}</code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=KOTNtCb6Gr%2FSU43L07ePDg%3D%3D.Zhd4A3ghEBPQFE%2F%2FpmMtQw1V3Xk7Em1GRmJfoa8SG%2FLCRH1SzOW8Ix84N2UqfslBWBKR3vmduw38tX%2BcNbV6k0GdrJYey%2FZNgMxk7Jy1laVb8Km3yIVTU0RykA3%2FsF7c5up0koGazHiFUcf0GExeijq07S0vdHbmIJI3mdV8qdM%3D" rel="nofollow">https://developer.mozilla.org...</a></blockquote>
<h4><code>defineKeyPropWarningGetter</code></h4>
<pre><code class="js">function defineKeyPropWarningGetter(props, displayName) {
const warnAboutAccessingKey = function() {
if (!specialPropKeyWarningShown) {
specialPropKeyWarningShown = true;
warningWithoutStack(
false,
'%s: `key` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
displayName,
);
}
};
warnAboutAccessingKey.isReactWarning = true;
Object.defineProperty(props, 'key', {
get: warnAboutAccessingKey,
configurable: true,
});
}</code></pre>
<h4><code>defineRefPropWarningGetter</code></h4>
<pre><code class="js">function defineRefPropWarningGetter(props, displayName) {
const warnAboutAccessingRef = function() {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;
warningWithoutStack(
false,
'%s: `ref` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
displayName,
);
}
};
warnAboutAccessingRef.isReactWarning = true;
Object.defineProperty(props, 'ref', {
get: warnAboutAccessingRef,
configurable: true,
});
}</code></pre>
<h4><code>ReactElement</code></h4>
<pre><code class="js"> const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// 这个标签使我们可以唯一地将其标识为React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// 记录负责创建此元素的组件。
_owner: owner,
};
if (__DEV__) {
//验证标志当前是可变的。我们穿上
//外部后备存储,以便我们可以冻结整个对象。
//可以在WeakMap中实现后,将其替换为WeakMap
//常用的开发环境。
element._store = {};
//为了便于比较ReactElement以进行测试,我们使
//验证标志不可枚举(在可能的情况下,应
//包括我们在其中运行测试的所有环境),因此测试框架
//忽略它。
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false,
});
// self和source是仅DEV的属性。
Object.defineProperty(element, '_self', {
configurable: false,
enumerable: false,
writable: false,
value: self,
});
//应该考虑在两个不同位置创建的两个元素
//出于测试目的是相等的,因此我们将其从枚举中隐藏起来。
Object.defineProperty(element, '_source', {
configurable: false,
enumerable: false,
writable: false,
value: source,
});
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}
return element;
};</code></pre>
<h3><code>export function</code></h3>
<h4><code>jsx</code></h4>
<pre><code class="jsx">export function jsx(type, config, maybeKey) {
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
//当前,key可以作为props传播。这导致潜在的
//如果还明确声明了key,则会出现问题(即<div {... props} key =“ Hi” />
//或<div key =“ Hi” {... props} />)。我们要弃用key传值,
//但作为中介步骤,我们将对所有内容使用jsxDEV
//<div {... props} key =“ Hi” />,因为我们目前无法确定是否
//key已明确声明为未定义。
if (maybeKey !== undefined) {
key = '' + maybeKey;
}
if (hasValidKey(config)) {
key = '' + config.key;
}
if (hasValidRef(config)) {
ref = config.ref;
}
// Remaining properties are added to a new props object
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
// Resolve default props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
return ReactElement(
type,
key,
ref,
undefined,
undefined,
ReactCurrentOwner.current,
props,
);
}</code></pre>
<h4><code>jsxDev</code></h4>
<p>行为几乎和 <code>jsx</code> 一致,主要就是<code>defineKeyPropWarningGetter</code>和<code>defineRefPropWarningGetter</code>的处理</p>
<h4><code>createElement</code></h4>
<p>创建并返回指定类型的新 React 元素。</p>
<pre><code class="js">export function createElement(type, config, children) {
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
}
if (hasValidKey(config)) {
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
// Resolve default props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
if (key || ref) {
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}</code></pre>
<h4><code>createFactory</code></h4>
<p>此辅助函数已废弃</p>
<pre><code class="js">export function createFactory(type) {
const factory = createElement.bind(null, type);
//公开工厂和原型上的类型,以便可以
//易于访问的元素。例如。 `<Foo />.type === Foo`。
//不应将其命名为“ constructor”,因为它可能不是函数
//创建了元素,甚至可能不是构造函数。
//旧版挂钩:将其删除
factory.type = type;
return factory;
}</code></pre>
<h4><code>cloneElement</code></h4>
<p>以 <code>element</code> 元素为样板克隆并返回新的 <code>React</code> 元素。返回元素的 props 是将新的 <code>props</code> 与原始元素的 <code>props</code> 浅层合并后的结果。新的子元素将取代现有的子元素,而来自原始元素的 <code>key</code> 和 <code>ref</code> 将被保留。</p>
<h4><code>isValidElement</code></h4>
<p>验证对象是否为 <code>React</code> 元素,返回值为 <code>true</code> 或 <code>false</code>。</p>
<pre><code class="js">export function isValidElement(object) {
// typeof null = object
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}</code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=emg3%2FcDpfDn%2FpQy%2BBRqzGQ%3D%3D.oTr1FgLIlOzH55DogILHAJqaKFz4liEsHBs0dv7ixaimMqQjfW3ip2S%2FZGmi%2B7zPHBdCg2TYq6jy20Cjl7TrKA%3D%3D" rel="nofollow">https://zh-hans.reactjs.org/d...</a></blockquote>
React 源码阅读-12_043
https://segmentfault.com/a/1190000020959358
2019-11-10T16:08:37+08:00
2019-11-10T16:08:37+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 源码阅读-12</h2>
<h3><code>ReactChildren</code></h3>
<p><img src="/img/bVbz6E5?w=6302&h=4600" alt="ReactChildren.png" title="ReactChildren.png"></p>
<pre><code class="js">/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// invariant ,warning 用户报错警告
import invariant from 'shared/invariant';
import warning from 'shared/warning';
// 引入 react Symbol 对象类型标识
import {
getIteratorFn,
REACT_ELEMENT_TYPE,
REACT_PORTAL_TYPE,
} from 'shared/ReactSymbols';
// isValidElement 检测是否是一个 reactelement对象
//cloneAndReplaceKey复制一个 React 元素并替换的留属性: key 值。
import {isValidElement, cloneAndReplaceKey} from './ReactElement';
//react 当前调试信息
import ReactDebugCurrentFrame from './ReactDebugCurrentFrame';
const SEPARATOR = '.';
const SUBSEPARATOR = ':';
/**
* Escape and wrap key so it is safe to use as a reactid
*
* @param {string} key to be escaped.
* @return {string} the escaped key.
*/
// 定义和转换key
// 将传入的key中所有的'='替换成'=0',':'替换成 '=2',并在key之前加上'$'
function escape(key) {
const escapeRegex = /[=:]/g;
const escaperLookup = {
'=': '=0',
':': '=2',
};
const escapedString = ('' + key).replace(escapeRegex, function(match) {
return escaperLookup[match];
});
return '$' + escapedString;
}
/**
* TODO: Test that a single child and an array with one item have the same key
* pattern.
*/
let didWarnAboutMaps = false;
const userProvidedKeyEscapeRegex = /\/+/g;
function escapeUserProvidedKey(text) {
// /如果字符串中有连续多个 / 的话,在匹配的字串后再加 /
// react对key定义的一个规则:
// 如果字符串中有连续多个/的话,在匹配的字串后再加/
// 这个函数一般是第二层递归时,会用到
//匹配一个或者多个 "/",并用'$&/'替换
return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
}
const POOL_SIZE = 10;
const traverseContextPool = [];
// 实际上维护了一个 size 为 10 的缓冲池.
// 如果 pool 中有存货, 则 pop 出一个进行使用.
// 如果 pool 中空空如也, 则 return 一个新的对象.
function getPooledTraverseContext(
mapResult,
keyPrefix,
mapFunction,
mapContext,
) {
// func: 这就是用户传入的 forEach 处理函数
// context: 这是个可选参数, 用户可以传入作为调用上述 func 时的上下文.
// 看到这里你就知道, 默认情况下,
// 你的 处理函数执行的时候, 是没有 context, 也就是处理函数中,this === undefined.
// 如果想在 处理函数中绑定 this, 只能通过这个参数指定.
// 这一点在后面分析 forEachSingleChild 会看到原理
if (traverseContextPool.length) {
// pop() 方法用于删除并返回数组的最后一个元素。
const traverseContext = traverseContextPool.pop();
traverseContext.result = mapResult;
traverseContext.keyPrefix = keyPrefix;
traverseContext.func = mapFunction;
traverseContext.context = mapContext;
traverseContext.count = 0;
return traverseContext;
} else {
return {
result: mapResult,
keyPrefix: keyPrefix,
func: mapFunction,
context: mapContext,
count: 0,
};
}
}
function releaseTraverseContext(traverseContext) {
// 这个方法简单的不能再简单,
// 核心目的就是 if 里面的那块代码,
// 如果池数量小于 POOL_SIZE(上文中得知这个数字是 10),
// 则把对象放回到池中, 以备后续使用.
traverseContext.result = null;
traverseContext.keyPrefix = null;
traverseContext.func = null;
traverseContext.context = null;
traverseContext.count = 0;
if (traverseContextPool.length < POOL_SIZE) {
traverseContextPool.push(traverseContext);
}
}
/**
* @param {?*} children Children tree container.
* @param {!string} nameSoFar Name of the key path so far.
* @param {!function} callback Callback to invoke with each child found.
* @param {?*} traverseContext Used to pass information throughout the traversal
* process.
* @return {!number} The number of children in this subtree.
*/
function traverseAllChildrenImpl(
children,
nameSoFar,
callback,
traverseContext,
) {
const type = typeof children;
// 这个分支处理了大部分类型. 这里的 children 实际上是单个对象, 并不是像它的名字一样是个复数.
// 接下来执行 callback(traverseContext, children, nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar)
// 并返回 1.
// 看到这里, 我们就知道, children 不仅仅可以是 Component,
// 还可以是 String/Boolean/Undefined 等等.
if (type === 'undefined' || type === 'boolean') {
// All of the above are perceived as null.
children = null;
}
let invokeCallback = false;
if (children === null) {
invokeCallback = true;
} else {
switch (type) {
case 'string':
case 'number':
invokeCallback = true;
break;
case 'object':
switch (children.$$typeof) {
case REACT_ELEMENT_TYPE:
case REACT_PORTAL_TYPE:
invokeCallback = true;
}
}
}
if (invokeCallback) {
callback(
traverseContext,
children,
// 如果是唯一的元素,则将名称视为包裹在数组中
// If it's the only child, treat the name as if it was wrapped in an array
// so that it's consistent if the number of children grows.
// 这样一来,如果元素数量增加,这是一致的s
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
// 这个 callback 实际上是 forEachSingleChild
// 而 forEachSingleChild 则调用 func.call(context, child, ...)
// 实现了绑定上下文调用处理函数. 所以 callback(traverseContext, children, ...)
// 可以简单理解为以 children(实际上是单个对象, 并不是集合, 名字容易误导读者) 为参数, 调用了用户的处理函数.
);
return 1;
}
let child;
let nextName;
let subtreeCount = 0; // Count of children found in the current subtree.
const nextNamePrefix =
nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
if (Array.isArray(children)) {
// !! 如果是 Array, 则深度递归 !!
for (let i = 0; i < children.length; i++) {
child = children[i];
nextName = nextNamePrefix + getComponentKey(child, i);
subtreeCount += traverseAllChildrenImpl(
child,
nextName,
callback,
traverseContext,
);
}
} else {
// !! 如果不是 Array, 则看该对象是否可迭代 !!
const iteratorFn = getIteratorFn(children);
if (typeof iteratorFn === 'function') {
if (__DEV__) {
// Warn about using Maps as children
if (iteratorFn === children.entries) {
warning(
didWarnAboutMaps,
'Using Maps as children is unsupported and will likely yield ' +
'unexpected results. Convert it to a sequence/iterable of keyed ' +
'ReactElements instead.',
);
didWarnAboutMaps = true;
}
}
const iterator = iteratorFn.call(children);
let step;
let ii = 0;
while (!(step = iterator.next()).done) {
child = step.value;
nextName = nextNamePrefix + getComponentKey(child, ii++);
subtreeCount += traverseAllChildrenImpl(
child,
nextName,
callback,
traverseContext,
);
}
} else if (type === 'object') {
// !! 如果该对象不可迭代, 则提示错误 !!
let addendum = '';
if (__DEV__) {
addendum =
' If you meant to render a collection of children, use an array ' +
'instead.' +
ReactDebugCurrentFrame.getStackAddendum();
}
const childrenString = '' + children;
invariant(
false,
'Objects are not valid as a React child (found: %s).%s',
childrenString === '[object Object]'
? 'object with keys {' + Object.keys(children).join(', ') + '}'
: childrenString,
addendum,
);
}
}
// React.Children.forEach 是不支持 Map 但却支持 Set 的.看代码中的注释,
return subtreeCount;
}
/**
* Traverses children that are typically specified as `props.children`, but
* might also be specified through attributes:
*
* - `traverseAllChildren(this.props.children, ...)`
* - `traverseAllChildren(this.props.leftPanelChildren, ...)`
*
* The `traverseContext` is an optional argument that is passed through the
* entire traversal. It can be used to store accumulations or anything else that
* the callback might find relevant.
*
* @param {?*} children Children tree object.
* @param {!function} callback To invoke upon traversing each child.
* @param {?*} traverseContext Context for traversal.
* @return {!number} The number of children in this subtree.
*/
function traverseAllChildren(children, callback, traverseContext) {
if (children == null) {
return 0;
}
// traverseAllChildren 只是个入口, 真实的递归遍历是定义在 traverseAllChildrenImpl 中:
// 递归遍历
return traverseAllChildrenImpl(children, '', callback, traverseContext);
}
/**
* Generate a key string that identifies a component within a set.
*
* @param {*} component A component that could contain a manual key.
* @param {number} index Index that is used if a manual key is not provided.
* @return {string}
*/
function getComponentKey(component, index) {
// Do some typechecking here since we call this blindly. We want to ensure
// that we don't block potential future ES APIs.
// 所以在这里做一些类型检查。我们要确保
// /不会阻止潜在的未来ES API。
if (
typeof component === 'object' &&
component !== null &&
component.key != null
) {
// Explicit key
//组件 key
return escape(component.key);
}
// Implicit key determined by the index in the set
return index.toString(36);
// 转换成36进制
}
function forEachSingleChild(bookKeeping, child, name) {
const {func, context} = bookKeeping;
func.call(context, child, bookKeeping.count++);
}
/**
* Iterates through children that are typically specified as `props.children`.
*
* See https://reactjs.org/docs/react-api.html#reactchildrenforeach
*
* The provided forEachFunc(child, index) will be called for each
* leaf child.
*
* @param {?*} children Children tree container.
* @param {function(*, int)} forEachFunc
* @param {*} forEachContext Context for forEachContext.
*/
function forEachChildren(children, forEachFunc, forEachContext) {
if (children == null) {
return children;
}
const traverseContext = getPooledTraverseContext(
null,
null,
forEachFunc,
forEachContext,
);
// 其中, children 是要遍历的子节点对象,
// traverseContext 是上一步封装了 处理函数 和 处理函数执行上下文 的一个对象.
// 而 forEachSingleChild 则是真正调用处理函数的方法:
traverseAllChildren(children, forEachSingleChild, traverseContext);
releaseTraverseContext(traverseContext);
}
function mapSingleChildIntoContext(bookKeeping, child, childKey) {
const {result, keyPrefix, func, context} = bookKeeping;
let mappedChild = func.call(context, child, bookKeeping.count++);
if (Array.isArray(mappedChild)) {
mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
} else if (mappedChild != null) {
if (isValidElement(mappedChild)) {
mappedChild = cloneAndReplaceKey(
mappedChild,
// Keep both the (mapped) and old keys if they differ, just as
// traverseAllChildren used to do for objects as children
keyPrefix +
(mappedChild.key && (!child || child.key !== mappedChild.key)
? escapeUserProvidedKey(mappedChild.key) + '/'
: '') +
childKey,
);
}
result.push(mappedChild);
}
}
// getPooledTraverseContext()/traverseAllChildren()/releaseTraverseContext()
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
// 对prefix进行处理
//如果字符串中有连续多个 / 的话,在匹配的字串后再加 /
let escapedPrefix = '';
if (prefix != null) {
escapedPrefix = escapeUserProvidedKey(prefix) + '/';
}
// 遍历上下文
//traverseContext=
// {
// result:[],
// keyPrefix:'',
// func:(item)=>{return [item,[item,] ]},
// context:undefined,
// count:0,
// }
const traverseContext = getPooledTraverseContext(
array,
escapedPrefix,
func,
context,
);
// 遍历所有的 Children 将嵌套的数组展平 绑定上下文调用处理函数
// 其中, children 是要遍历的子节点对象,
// traverseContext 是上一步封装了 处理函数
// 和 处理函数执行上下文 的一个对象.
// 而 mapSingleChildIntoContext 则是真正调用处理函数的方法:
traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
// 这个方法简单的不能再简单,
// 核心目的就是 if 里面的那块代码,
// 如果池数量小于 POOL_SIZE(上文中得知这个数字是 10),
// 则把对象放回到池中, 以备后续使用.
releaseTraverseContext(traverseContext);
}
/**
* Maps children that are typically specified as `props.children`.
* 映射通常指定为`props.children`的子级
* See https://reactjs.org/docs/react-api.html#reactchildrenmap
*
* The provided mapFunction(child, key, index) will be called for each
* leaf child.
*
* @param {?*} children Children tree container.
* @param {function(*, int)} func The map function.
* @param {*} context Context for mapFunction.
* @return {object} Object containing the ordered map of results.
*/
function mapChildren(children, func, context) {
if (children == null) {
return children;
}
const result = [];
// 将 children 完全遍历,遍历的节点最终全部存到 array 中,是 ReactElement 的节点会更改 key 之后再放到 array 中。
mapIntoWithKeyPrefixInternal(children, result, null, func, context);
return result;
}
/**
* Count the number of children that are typically specified as
* `props.children`.
*
* See https://reactjs.org/docs/react-api.html#reactchildrencount
*
* @param {?*} children Children tree container.
* @return {number} The number of children.
*/
function countChildren(children) {
return traverseAllChildren(children, () => null, null);
}
/**
* Flatten a children object (typically specified as `props.children`) and
* return an array with appropriately re-keyed children.
* 展开一个成一个数组
* See https://reactjs.org/docs/react-api.html#reactchildrentoarray
*/
function toArray(children) {
const result = [];
mapIntoWithKeyPrefixInternal(children, result, null, child => child);
return result;
}
/**
* Returns the first child in a collection of children and verifies that there
* is only one child in the collection.
* 返回子集合中的first child 并验证 只有一个
*
* See https://reactjs.org/docs/react-api.html#reactchildrenonly
*
* The current implementation of this function assumes that a single child gets
* passed without a wrapper, but the purpose of this helper function is to
* abstract away the particular structure of children.
*
* @param {?object} children Child collection structure.
* @return {ReactElement} The first and only `ReactElement` contained in the
* structure.
*/
function onlyChild(children) {
invariant(
isValidElement(children),
'React.Children.only expected to receive a single React element child.',
);
return children;
}
export {
forEachChildren as forEach,
mapChildren as map,
countChildren as count,
onlyChild as only,
toArray,
};
</code></pre>
React 源码阅读-11_042
https://segmentfault.com/a/1190000020809654
2019-10-27T10:58:50+08:00
2019-10-27T10:58:50+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 源码阅读-11</h2>
<h3><code>createRef</code></h3>
<pre><code class="js">// ReactTypes定义的 react 类型
import type {RefObject} from 'shared/ReactTypes';
// an immutable object with a single mutable value
// 具有单个可变值的不可变对象
export function createRef(): RefObject {
const refObject = {
current: null,
};
if (__DEV__) {
Object.seal(refObject);
}
return refObject;
}
</code></pre>
<p><code>createRef</code>源码比较简短,就是返回一个带有<code>current</code>属性的的<code>refObject</code>对象.</p>
<p>这也是使用<code>createRef</code>的时候需要使用<code>const node = this.myRef.current;</code></p>
<h3><code>Object.seal()</code></h3>
<p><code>Object.seal()</code>方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要可写就可以改变。</p>
<h4>与<code>Object.freeze()</code>区别</h4>
<p>使用Object.freeze()冻结的对象中的现有属性是不可变的。用Object.seal()密封的对象可以改变其现有属性。</p>
<blockquote><a href="https://link.segmentfault.com/?enc=AyXltomlkLukx5b9JykN8w%3D%3D.ElEQTCK3%2BhmF2RrjACJiRRixcayi8S5aGmZ2HmCHNjME%2BrRK3K8BEtLQfzAdNF%2BXscggsffugB4iWUHEXmpF8AB4Lzt2hV6SnzkLVukTK5YNPMDpU1f56LpRWp6QOjvs" rel="nofollow">https://developer.mozilla.org...</a></blockquote>
React 源码阅读-10_041
https://segmentfault.com/a/1190000020790230
2019-10-24T16:05:46+08:00
2019-10-24T16:05:46+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 源码阅读-10</h2>
<p><img src="/img/bVbz6Fa?w=2050&h=854" alt="ReactBaseClasses.png" title="ReactBaseClasses.png"></p>
<h3><code>ReactBaseClasses</code></h3>
<p>这个文件是<code>export</code>出了<code>Component</code>, <code>PureComponent</code></p>
<pre><code class="js">export {Component, PureComponent};</code></pre>
<h3>源码+注释</h3>
<pre><code class="js">/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
//生产环境使用,为了抛出错误信息
import invariant from 'shared/invariant';
//只在开发环境有效,使用 console.warn(message);
import lowPriorityWarning from 'shared/lowPriorityWarning';
//没有传入参数updater参数时,this.updater的值就是ReactNoopUpdateQueue
//用于报警告的 可以忽略
import ReactNoopUpdateQueue from './ReactNoopUpdateQueue';
const emptyObject = {};
if (__DEV__) {
Object.freeze(emptyObject);
// Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象
}
/**
* Base class helpers for the updating state of a component.
*/
// Base class用于更新组件的state。\
// Component()本质是一个类:
function Component(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
// 如果组件ref具有字符串引用,稍后将分配一个不同的对象
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
// 我们初始化默认的更新程序,但是真正的更新程序会被渲染
this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.isReactComponent = {};
/**
*不能保证`this.state`会立即更新,因此
*调用此方法后访问`this.state`可能会返回旧值。
*
*不能保证对setState的调用将同步运行,
*因为它们最终可能会一起批处理。您可以提供可选
*实际调用setState时将执行的回调
*完成。
*
*将函数提供给setState时,它将在以下时间点被调用
*未来(不同步)。它将被称为最新
*组件参数(状态,道具,上下文)。这些值可以不同
*from this。*因为您的函数可能在receiveProps之后但之前被调用
*shouldComponentUpdate,这个新的状态,道具和上下文还没有
*分配给这个
*
* @param {object|function} partialState Next partial state or function to
* produce next partial state to be merged with current state.
* @param {?function} callback Called after state is updated.
* @final
* @protected
*/
Component.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
/**
强制更新。仅当已知时才应调用此方法
*确定我们不是DOM事务中的**。
*
*如果您知道
*组件的状态已更改,但未调用`setState`。
*
*这不会调用`shouldComponentUpdate`,但是会调用
*componentWillUpdate和componentDidUpdate。
*
* @param {?function} callback Called after update is complete.
* @final
* @protected
*/
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
/**
不推荐使用的API。这些API曾经存在于经典的React类上,但是由于
*我们要弃用它们,我们不会将它们移至此
*现代基层。取而代之的是,我们定义了一个getter,如果它被访问,它会发出警告
已经废弃
*/
if (__DEV__) {
const deprecatedAPIs = {
isMounted: [
'isMounted',
'Instead, make sure to clean up subscriptions and pending requests in ' +
'componentWillUnmount to prevent memory leaks.',
],
replaceState: [
'replaceState',
'Refactor your code to use setState instead (see ' +
'https://github.com/facebook/react/issues/3236).',
],
};
const defineDeprecationWarning = function(methodName, info) {
Object.defineProperty(Component.prototype, methodName, {
get: function() {
lowPriorityWarning(
false,
'%s(...) is deprecated in plain JavaScript React classes. %s',
info[0],
info[1],
);
return undefined;
},
});
};
for (const fnName in deprecatedAPIs) {
if (deprecatedAPIs.hasOwnProperty(fnName)) {
defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
}
}
}
//虚拟组件
function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;
// 带有默认浅层相等性检查的便利组件。
/**
* Convenience component with default shallow equality check for sCU.
*/
// PureComponent最佳情况是展示组件
function PureComponent(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
// PureComponent.prototype等于ComponentDummy的实例 只继承Component的原型,不包括constructor,以此来节省内存。
pureComponentPrototype.constructor = PureComponent;
// 原型的constructor等于自身,覆盖掉Component.prototype的constructor(Component)
// Avoid an extra prototype jump for these methods.
// 对于这些方法,请避免额外的原型跳转 为了减少一次原型链查找
Object.assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;
// PureComponent是自带了一个简单的shouldComponentUpdate来优化更新机制的
export {Component, PureComponent};
</code></pre>
<blockquote>
<a href="https://link.segmentfault.com/?enc=HhBThMrlxhyEs5RPKE3WJw%3D%3D.SD6YW%2Fn8fohwQxUt3CmHxq7%2BfTnfk4T969k0T4ex4vd9v6ENN%2BqmAJbs3XZ4CHP9" rel="nofollow">https://juejin.im/post/5b614d...</a><p><a href="https://segmentfault.com/a/1190000019783986">https://segmentfault.com/a/11...</a></p>
</blockquote>
React 源码阅读-9_040
https://segmentfault.com/a/1190000020744688
2019-10-20T16:23:54+08:00
2019-10-20T16:23:54+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>React 源码阅读-9</h2>
<h3>继续阅读<code>react 16.8.6</code>源码</h3>
<p>之前写的就是相当于的一个学习的笔记,虽然工作中的技术都是 <code>react</code> 相关,但是<code>80%</code>都是一些平常很少涉及到的,导致自己对 <code>react</code>的认识相对比较片面.一些 <code>react</code>的<code>api</code>很少用到,通过 <code>react</code>阅读,系统地熟悉了一下 <code>react</code>所有的 <code>api</code>以及一些相关特性使用场景.继续根据官方的文档和 <code>react.js</code>阅读源码.</p>
<p><img src="/img/bVbz6Ff?w=1536&h=1970" alt=" React@16.8.6 源码学习.png" title=" React@16.8.6 源码学习.png"></p>
<h3>一些准备工作</h3>
<ul><li>官方贡献指南</li></ul>
<p><a href="https://link.segmentfault.com/?enc=RSyoZ3f8x8v8qv%2Fo9F3Xtg%3D%3D.FCnOukuKBUTQg1OEYXIMxOIuONWnZOrEoOqgdVgCLmyPQrKxdkRd3aGnfoAdy5iOAHOF9HyEUXRFFiKeINUMgQ%3D%3D" rel="nofollow">https://zh-hans.reactjs.org/d...</a></p>
<h3><code>ReactSymbols</code></h3>
<p><img src="/img/remote/1460000020958917" alt="img" title="img"></p>
<p><code>The Symbol used to tag the ReactElement-like types</code> 即 <code>用于标记类似ReactElement的类型的Symbol</code>;</p>
<p>如果没有原生的 <code>Symbol</code> 符号或 <code>polyfill</code>,则使用普通数字进行表示。</p>
<p>采用的数字是十六进制;</p>
<pre><code class="js">const hasSymbol = typeof Symbol === 'function' && Symbol.for;</code></pre>
<p><code>Symbol.for(key)</code>方法会根据给定的键 <code>key</code>,来从运行时的 <code>symbol</code> 注册表中找到对应的 <code>symbol</code>,如果找到了,则返回它,否则,新建一个与该键关联的 <code>symbol</code>,并放入全局 <code>symbol</code> 注册表中。</p>
<p>返回由给定的 <code>key</code> 找到的 <code>symbol</code>,否则就是返回新创建的 <code>symbol</code></p>
<p>和 <code>Symbol()</code> 不同的是,用 <code>Symbol.for()</code>方法创建的的 <code>symbol</code> 会被放入一个全局 <code>symbol</code> 注册表中。<code>Symbol.for()</code> 并不是每次都会创建一个新的 <code>symbol</code>,它会首先检查给定的 <code>key</code> 是否已经在注册表中了。假如是,则会直接返回上次存储的那个。否则,它会再新建一个</p>
<pre><code class="js">export const REACT_ELEMENT_TYPE = hasSymbol
? Symbol.for('react.element')
: 0xeac7;
export const REACT_PORTAL_TYPE = hasSymbol
? Symbol.for('react.portal')
: 0xeaca;
export const REACT_FRAGMENT_TYPE = hasSymbol
? Symbol.for('react.fragment')
: 0xeacb;
export const REACT_STRICT_MODE_TYPE = hasSymbol
? Symbol.for('react.strict_mode')
: 0xeacc;</code></pre>
<ul><li>
<p>为什么要赋值为十六进制的数字</p>
<ul>
<li>0xeac7看起来有点像React</li>
<li>
<code>React</code> 为了保持一致性依然会在元素中定义<code>$$typeof</code>属性,只不过它会被设置成一个 <code>number</code> 类型的值——<code>0xeac7等</code>
</li>
<li>
<a href="https://link.segmentfault.com/?enc=O9d9qosNLLI7aFylmHt1gg%3D%3D.dacQgcK4%2F0NXdE4nFPRgL0R%2B4XbhUHtMNzcyLTn3INBdRiDDW4ambgTOvFSVnJIBbDfH5JWIeBuXHK%2B%2FtHk66Q%3D%3D" rel="nofollow">https://juejin.im/entry/5c170...</a> 解决了疑惑,防止<code>xss</code>注入攻击</li>
</ul>
</li></ul>
<blockquote><a href="https://link.segmentfault.com/?enc=7fIqE5bY6aDBCB5hlWCMDQ%3D%3D.bgDYk%2Bf35v8ZQswdFY8FWNrzBzvurhSE2tYTKJ0khGG%2F2gTc8E4NeC0HqM0TgYdE%2F8JkiIyRTwBC3SzAnlvfnAjmrr8ES83rx41CXl0NhBw5F2USjFXhcQxzCg0E8M7C" rel="nofollow">https://developer.mozilla.org...</a></blockquote>
React 源码阅读-8_039
https://segmentfault.com/a/1190000020709093
2019-10-16T16:44:37+08:00
2019-10-16T16:44:37+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 源码阅读-8</h2>
<h3><code>ReactHooks</code></h3>
<p><code>Hook</code> 是 <code>React 16.8</code> 的新增特性。它可以让你在不编写 <code>class</code> 的情况下使用 <code>state</code> 以及其他的 <code>React</code> 特性。</p>
<pre><code class="jsx">import {
useCallback,
useContext,
useEffect,
useImperativeHandle,
useDebugValue,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState,
useResponder,
} from './ReactHooks';</code></pre>
<h3><code>useState</code></h3>
<p><code>useState</code> 会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数</p>
<p>它类似 <code>class</code>组件的 <code>this.setState</code>,但是它不会把新的 <code>state</code> 和旧的 <code>state</code> 进行合并;</p>
<pre><code class="jsx">import React, { useState } from "react";
import "./App.css";
function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
</div>
);
}
export default App;</code></pre>
<p><img src="/img/remote/1460000020709096" alt="img" title="img"></p>
<h3><code>useEffect</code></h3>
<pre><code class="jsx">import React, { useState, useEffect } from "react";
import "./App.css";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div className="App">
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
</div>
);
}
export default App;</code></pre>
<p>相当于 <code>React class</code> 的生命周期函数,你可以把 <code>useEffect Hook</code> 看做 <code>componentDidMount</code>,<code>componentDidUpdate</code> 和 <code>componentWillUnmount</code> 这三个函数的组合.</p>
<p>默认情况下,<code>effect</code> 会在每轮组件渲染完成后执行。这样的话,一旦<br><code>effect</code> 的依赖发生变化,它就会被重新创建.</p>
<p>可以给 <code>useEffect</code> 传递第二个参数,它是 effect 所依赖的值数组。更新后的示例如下:</p>
<pre><code class="jsx">useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);</code></pre>
<p>如果想执行只运行一次的 <code>effect</code>(仅在组件挂载和卸载时执行),可以传递一个空数组(<code>[]</code>)作为第二个参数。这就告诉 <code>React</code> 你的 <code>effect</code> 不依赖于 <code>props</code> 或 <code>state</code> 中的任何值,所以它永远都不需要重复执行。这并不属于特殊情况 —— 它依然遵循输入数组的工作方式。</p>
<h3><code>useContext</code></h3>
<p>类似之前提到过的<code>上下文 Context</code></p>
<pre><code class="jsx">const value = useContext(MyContext);</code></pre>
<p>接收一个 <code>context</code> 对象(<code>React.createContext</code> 的返回值)并返回该 <code>context</code> 的当前值。当前的 <code>context</code> 值由上层组件中距离当前组件最近的 <code><MyContext.Provider></code> 的 <code>value prop</code> 决定</p>
<pre><code class="jsx">const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}</code></pre>
<p><code>useContext</code> 的参数必须是 <code>context</code> 对象本身</p>
<h3><code>useReducer</code></h3>
<pre><code class="jsx">const [state, dispatch] = useReducer(reducer, initialArg, init);</code></pre>
<p>接收一个形如 <code>(state, action) => newState</code> 的 <code>reducer</code>,并返回当前的 <code>state</code> 以及与其配套的 <code>dispatch</code> 方法</p>
<p>和 <code>redux</code>比较类似.</p>
<p>实现的计数器</p>
<pre><code class="jsx">const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
throw new Error();
}
}
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
</>
);
}</code></pre>
<h4>指定初始的<code>state</code>
</h4>
<pre><code class="js">const [state, dispatch] = useReducer(
reducer,
{count: initialCount}
);</code></pre>
<h4>惰性初始化</h4>
<p>你可以选择惰性地创建初始 <code>state</code>。为此,需要将 <code>init</code> 函数作为 <code>useReducer</code> 的第三个参数传入,这样初始 <code>state</code> 将被设置为 <code>init(initialArg)</code>。</p>
<pre><code class="jsx">function init(initialCount) {
return {count: initialCount};
}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return init(action.payload);
default:
throw new Error();
}
}
function App({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
</code></pre>
<h4>跳过 <code>dispatch</code>
</h4>
<p>如果 <code>Reducer Hook</code> 的返回值与当前 <code>state</code> 相同,<code>React</code> 将跳过子组件的渲染及副作用的执行</p>
<p><code>React</code> 使用 <code>Object.is</code> 比较算法 来比较 <code>state</code></p>
<h3><code>useCallback</code></h3>
<p>把内联回调函数及依赖项数组作为参数传入 <code>useCallback</code>,它将返回该回调函数的 <code>memoized</code> 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 <code>shouldComponentUpdate</code>)的子组件时,它将非常有用。</p>
<pre><code class="jsx">const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);</code></pre>
<h3><code>useRef</code></h3>
<p><code>useRef</code> 返回一个可变的 <code>ref</code> 对象,其 <code>.current</code> 属性被初始化为传入的参数(<code>initialValue</code>)。返回的 <code>ref</code> 对象在组件的整个生命周期内保持不变。</p>
<p>和之前的 <code>ref</code>,<code>forwardref</code>,<code>createref</code>类似,只不过这个是在函数组件里的</p>
<pre><code class="jsx">function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}</code></pre>
<h3><code>useMemo</code></h3>
<pre><code class="jsx">const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);</code></pre>
<p>把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 <code>memoized</code> 值。这种优化有助于避免在每次渲染时都进行高开销的计算。</p>
<p>类似<code>memo</code></p>
<h3>其他 API</h3>
<p>不展开叙述了 感觉都是差不多的</p>
<blockquote><a href="https://link.segmentfault.com/?enc=sXs4TXTxK1BZvPsBI2qPnA%3D%3D.PCCWEKmouS4AjVcLN49X6sU2TcQLzYE7UsA8cg9kTDcazlfch6gWUGRXvbZdwFcE%2FlrDsNe7f%2BkNZ%2FaZRKIEFw%3D%3D" rel="nofollow">https://zh-hans.reactjs.org/d...</a></blockquote>
React 源码阅读-7_038
https://segmentfault.com/a/1190000020671761
2019-10-13T17:35:41+08:00
2019-10-13T17:35:41+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>React 源码阅读-7</h2>
<h3><code>memo</code></h3>
<p><code>React.memo</code> 为高阶组件。它与 <code>React.PureComponent</code> 非常相似,但它适用于函数组件,但不适用于 <code>class</code>组件。</p>
<pre><code class="jsx">const MyComponent = React.memo(function MyComponent(props) {
/* 使用 props 渲染 */
});</code></pre>
<p>如果你的函数组件在给定相同 <code>props</code> 的情况下渲染相同的结果,那么你可以通过将其包装在 <code>React.memo</code> 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,<code>React</code> 将跳过渲染组件的操作并直接复用最近一次渲染的结果。</p>
<p>此方法仅作为性能优化的方式而存在。但请不要依赖它来“阻止”渲染,因为这会产生 <code>bug</code>。</p>
<p>默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现</p>
<pre><code class="jsx">function MyComponent(props) {
/* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
}
export default React.memo(MyComponent, areEqual);</code></pre>
<h3>何时使用<code>React.memo()</code>
</h3>
<p>组件经常重新渲染,又是差不多的内容.纯展示组件,渲染相同的<code>props</code>,比如只是数字的更新,其他无变化.</p>
<pre><code class="jsx">// Initial render
<MovieViewsRealtime
views={0}
title="Forrest Gump"
releaseDate="June 23, 1994"
/>
// After 1 second, views is 10
<MovieViewsRealtime
views={10}
title="Forrest Gump"
releaseDate="June 23, 1994"
/>
// After 2 seconds, views is 25
<MovieViewsRealtime
views={25}
title="Forrest Gump"
releaseDate="June 23, 1994"
/>
function MovieViewsRealtime({ title, releaseDate, views }) {
return (
<div>
<MemoizedMovie title={title} releaseDate={releaseDate} />
Movie views: {views}
</div>
)
}
// etc</code></pre>
<h3><code>React.memo() and callback functions</code></h3>
<p>这里的 <code>callback</code> 指的是<code>usecallback</code><br><code>react</code>新增的<code>api</code>.还没看到那一部分,暂时先 <code>mark</code> 一下</p>
<pre><code class="jsx">
const MemoizedLogout = React.memo(Logout);
function MyApp({ store, cookies }) {
const onLogout = useCallback(() => cookies.clear(), [cookies]);
return (
<div className="main">
<header>
<MemoizedLogout
username={store.username}
onLogout={onLogout}
/>
</header>
{store.content}
</div>
);
}</code></pre>
<blockquote>
<a href="https://segmentfault.com/a/1190000018444604">https://segmentfault.com/a/11...</a><p><a href="https://link.segmentfault.com/?enc=lM4ydd7doBuDU5nes4yUOg%3D%3D.yr4p2QuYCKltTLJn46tLg%2BH6YDBnUMciMRgDdipAPMjQm37wT9t516d8k4gob34%2FFbv%2Bcdk5MXPBz90WDV1wHw%3D%3D" rel="nofollow">https://dmitripavlutin.com/us...</a></p>
<p><a href="https://link.segmentfault.com/?enc=cQtlc3Fa8NvYlgE%2F6Mbg0w%3D%3D.shg6nfYiVoEzhwfsIgSDI3A8qnw%2FFpRlB61OwXxOqWJyoH2i4Az6e3XNPVZZUIC99BovPDpfBlYXWH%2BDjjCmCw%3D%3D" rel="nofollow">https://zh-hans.reactjs.org/d...</a></p>
</blockquote>
React 源码阅读-6_037
https://segmentfault.com/a/1190000020671291
2019-10-13T16:06:35+08:00
2019-10-13T16:06:35+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 源码阅读-6</h2>
<h3><code>lazy</code></h3>
<p><code>React.lazy</code> 函数能让你像渲染常规组件一样处理动态引入(的组件)。</p>
<p><code>React.lazy</code> 接受一个函数,这个函数需要动态调用 <code>import()</code>。它必须返回一个 <code>Promise</code>,该 <code>Promise</code> 需要 <code>resolve</code> 一个 <code>defalut export</code> 的 <code>React</code> 组件。</p>
<p><code>React.lazy并不适合SSR</code></p>
<pre><code class="js">import type {LazyComponent, Thenable} from 'shared/ReactLazyComponent';
import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
import warning from 'shared/warning';
export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
let lazyType = {
$$typeof: REACT_LAZY_TYPE,
_ctor: ctor,
// React uses these fields to store the result.
_status: -1,
_result: null,
};
if (__DEV__) {
// In production, this would just set it on the object.
let defaultProps;
let propTypes;
Object.defineProperties(lazyType, {
defaultProps: {
configurable: true,
get() {
return defaultProps;
},
set(newDefaultProps) {
warning(
false,
'React.lazy(...): It is not supported to assign `defaultProps` to ' +
'a lazy component import. Either specify them where the component ' +
'is defined, or create a wrapping component around it.',
);
defaultProps = newDefaultProps;
// Match production behavior more closely:
Object.defineProperty(lazyType, 'defaultProps', {
enumerable: true,
});
},
},
propTypes: {
configurable: true,
get() {
return propTypes;
},
set(newPropTypes) {
warning(
false,
'React.lazy(...): It is not supported to assign `propTypes` to ' +
'a lazy component import. Either specify them where the component ' +
'is defined, or create a wrapping component around it.',
);
propTypes = newPropTypes;
// Match production behavior more closely:
Object.defineProperty(lazyType, 'propTypes', {
enumerable: true,
});
},
},
});
}
return lazyType;
}
</code></pre>
<p>使用前</p>
<pre><code class="jsx">import OtherComponent from './OtherComponent';</code></pre>
<p>使用后</p>
<pre><code class="jsx">const OtherComponent = React.lazy(() => import('./OtherComponent'));</code></pre>
<p>在组件首次渲染时,自动导入包含 <code>OtherComponent</code> 组件的包。</p>
<h4>路由分割代码</h4>
<pre><code class="jsx">import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspense, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
);</code></pre>
<blockquote>
<a href="https://link.segmentfault.com/?enc=91EGEdBldN9UCtLUbUCrWA%3D%3D.nU6RN%2FaryTqe1aIp4180nzXT4wx1so9T9cg0LHdKta3jGFAmu4EWsDEoUNId%2FD5w" rel="nofollow">https://juejin.im/post/5c7d4a...</a><br><a href="https://link.segmentfault.com/?enc=cvsf6mKbqZWMYKgWT9hDAg%3D%3D.kuATsZGOSSrULURArjAqHuxSNL013g9zOQ98H%2BoYNKKPyt9yJQ5mZ8jJgYLU0f0eixseQgzE%2BErAzIoxkGtr3A%3D%3D" rel="nofollow">https://zh-hans.reactjs.org/d...</a>
</blockquote>
React 源码阅读-5_036
https://segmentfault.com/a/1190000020670702
2019-10-13T14:41:34+08:00
2019-10-13T14:41:34+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 源码阅读-5</h2>
<h3>源码</h3>
<pre><code class="jsx">
import {REACT_FORWARD_REF_TYPE, REACT_MEMO_TYPE} from 'shared/ReactSymbols';
import warningWithoutStack from 'shared/warningWithoutStack';
export default function forwardRef<Props, ElementType: React$ElementType>(
render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
// ... 暂时忽略 也看不太懂
//应该这部分代码是为了在DevTools中显示一个自定义名称 不太确定
return {
$$typeof: REACT_FORWARD_REF_TYPE,
render,
};
}</code></pre>
<p><code>React.forwardRef</code> 接受一个渲染函数,该函数接收 <code>props</code> 和 <code>ref</code> 参数并返回一个 <code>React</code> 节点</p>
<p>为了在高阶组件中转发 <code>refs</code></p>
<h3>
<code>forwardRef</code>例子</h3>
<pre><code class="jsx">const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
</code></pre>
<p>1.我们通过调用 <code>React.createRef</code> 创建了一个 <code>React ref</code> 并将其赋值给 <code>ref</code> 变量。</p>
<p>2.我们通过指定 <code>ref</code> 为 <code>JSX</code> 属性,将其向下传递给<code><FancyButton ref={ref}></code>。</p>
<p>3.<code>React</code> 传递 <code>ref</code>给 <code>fowardRef</code> 内函数 <code>(props, ref) => ...</code>,作为其第二个参数。</p>
<p>4.我们向下转发该 <code>ref</code> 参数到<code><button ref={ref}></code>,将其指定为 <code>JSX</code> 属性。</p>
<p>5.当 <code>ref</code>挂载完成,<code>ref.current</code> 将指向 <code><button> DOM</code>节点。</p>
<h3>用法</h3>
<ul>
<li>写高阶组件时,返回的无状态组件用 <code>forwardRef</code> 包裹,并且可以传递第二个参数 <code>ref</code>
</li>
<li>无状态组件中的返回值可将 <code>ref</code> 作为 <code>props</code> 传入。</li>
</ul>
<pre><code class="jsx">import React from 'react'
// 高阶组件,注意返回值用 `React.forwardRef` 包裹
// 里面的无状态组件接收第二个参数:ref
const paintRed = Component => React.forwardRef(
// 此例中,ref 为 ForwardRef 中的 textRef
(props, ref) => (
<Component color='red' ref={ref} {...props}></Component>
)
)
class Text extends React.Component {
// 仅用于检测是否取到 ref
value = 1
render() {
const style = {
color: this.props.color
}
return (
<p style={style}>
我是红色的!
</p>
)
}
}
const RedText = paintRed(Text)
export default class ForwardRef extends React.Component {
textRef = React.createRef()
componentDidMount() {
// value = 1
console.log(this.textRef.current.value)
}
render() {
// 如果没有 forwardRef,那么这个ref只能得到 `RedText`,而不是里面的 `Text`
return (
<RedText ref={this.textRef}></RedText>
)
}
}</code></pre>
<p>大部分需要使用 <code>forwardRef</code> 的时候都可以用其他方式解决.</p>
<blockquote><a href="https://link.segmentfault.com/?enc=mRFRIvJFhpzgoCGieHMlBA%3D%3D.UIVXOtb8hLfFTH5J%2FH7p3cSMVxpp%2BPs4lS4lIZ4V1ZUMgPqxZcYBdUsYI34nvfPg" rel="nofollow">https://juejin.im/post/5ad949...</a></blockquote>
JavaScript 异步编程_035
https://segmentfault.com/a/1190000020670493
2019-10-13T13:41:18+08:00
2019-10-13T13:41:18+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>
<code>JavaScript</code> 异步编程</h2>
<h3>一道面试题</h3>
<pre><code class="js">for(var i = 0; i < 3; i++) {
setTimeout(function() {
console.log('timeout' + i);
})
}
new Promise(function(resolve) {
console.log('promise1');
for(var i = 0; i < 1000; i++) {
i == 99 && resolve();
}
console.log('promise2');
}).then(function() {
console.log('then1');
})
console.log('global1');
promise1
promise2
global1
then1
3 timeout3
</code></pre>
<h3>
<code>JavaScript</code>单线程</h3>
<p>在浏览器的一个页面中,该页面的JS程序只有一个线程,故曰单线程。因为是单线程,所以程序的执行顺序就是从上到下依次执行,同一时间内只能有一段代码被执行。</p>
<h3>浏览器多进程</h3>
<ul>
<li>浏览器是多进程的</li>
<li>浏览器之所以能够运行,是因为系统给它的进程分配了资源(cpu、内存)</li>
<li>简单点理解,每打开一个Tab页,就相当于创建了一个独立的浏览器进程。</li>
<li>
<p>浏览器的渲染进程是多线程的</p>
<ul>
<li>GUI渲染线程</li>
<li>JS引擎线程</li>
<li>事件触发线程</li>
<li>定时触发器线程</li>
<li>异步http请求线程</li>
</ul>
</li>
</ul>
<p>浏览器渲染进程图:<br><img src="/img/remote/1460000020670496" alt="img" title="img"></p>
<h3>异步机制</h3>
<pre><code class="js"> for (var i = 0; i < 5; i ++) {
setTimeout(function(){
console.log(i);
}, 0);
}
console.log(i);
//5 ; 5 ; 5 ; 5; 5</code></pre>
<h4>回调函数<code>Callback</code>
</h4>
<p>这是异步编程最基本的方法。</p>
<p>假定有两个函数f1和f2,后者等待前者的执行结果。</p>
<pre><code class="js"> f1();
f2();</code></pre>
<p>如果<code>f1</code>是一个很耗时的任务,可以考虑改写<code>f1</code>,把<code>f2</code>写成<code>f1</code>的回调函数。\</p>
<pre><code class="js"> function f1(callback){
setTimeout(function () {
// f1的任务代码
callback();
}, 1000);
}</code></pre>
<p>执行代码就变成下面这样:</p>
<pre><code class="js"> f1(f2);</code></pre>
<p>采用这种方式,我们把同步操作变成了异步操作,<code>f1</code>不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。</p>
<p>回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合<code>Coupling</code>,流程会很混乱,而且每个任务只能指定一个回调函数。</p>
<h4>发布订阅</h4>
<p>发布-订阅模式又叫做<strong>观察者模式</strong>,他定义了一种一对多的依赖关系,即当一个对象的状态发生改变的时候,所有依赖他的对象都会得到通知。</p>
<pre><code class="js">let yourMsg = {};
yourMsg.peopleList = [];
yourMsg.listen = function (fn) {
this.peopleList.push(fn);
}
yourMsg.triger = function () {
for(var i = 0,fn;fn=this.peopleList[i++];){
fn.apply(this,arguments);
}
}
yourMsg.listen(function (name) {
console.log(`${name}收到了你的消息`);
})
yourMsg.listen(function (name) {
console.log('哈哈');
})
yourMsg.triger('张三');
yourMsg.triger('李四');</code></pre>
<h4><code>Promise</code></h4>
<p><code>Promise</code> 是一个对象,它代表了一个异步操作的最终完成或者失败。</p>
<p><code>Promise 最</code>直接的好处就是链式调用(<code>chaining</code>)</p>
<pre><code class="js">const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});</code></pre>
<h4>生成器<code>Generators/ yield</code>
</h4>
<h5><code>Generator</code></h5>
<pre><code class="js">function* Hello() {
yield 100
yield (function () {return 200})()
return 300
}
var h = Hello()
console.log(typeof h) // object
console.log(h.next()) // { value: 100, done: false }
console.log(h.next()) // { value: 200, done: false }
console.log(h.next()) // { value: 300, done: true }
console.log(h.next()) // { value: undefined, done: true }</code></pre>
<h5><code>yield</code></h5>
<p><code>yield</code>关键字使生成器函数执行暂停,<code>yield</code>关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的<code>return</code>关键字。</p>
<pre><code class="js">function* countAppleSales () {
var saleList = [3, 7, 5];
for (var i = 0; i < saleList.length; i++) {
yield saleList[i];
}
}
var appleStore = countAppleSales(); // Generator { }
console.log(appleStore.next()); // { value: 3, done: false }
console.log(appleStore.next()); // { value: 7, done: false }
console.log(appleStore.next()); // { value: 5, done: false }
console.log(appleStore.next()); // { value: undefined, done: true }
</code></pre>
<h4><code>async/await</code></h4>
<pre><code class="js">async function func() {
try {
let res = await asyncFunc()
} catch (e) {
//......
}
}</code></pre>
<p><code>async</code> 函数返回的 <code>Promise</code> 对象,必须等到内部所有的 <code>await</code> 命令的 <code>Promise</code> 对象执行完,才会发生状态改变</p>
<p><code>async</code> 函数的语法不难,难在错误处理上。</p>
<blockquote>
<a href="https://link.segmentfault.com/?enc=Im6G4IYNmH14yrq25E8vMA%3D%3D.c644JxUtkVaDm%2BB%2FU4JmpUn1ggEvn7sAiLN0IoZ4bVsIMvo5H2Qc%2BtEWZc2cvg2a" rel="nofollow">https://juejin.im/post/5a6547...</a><br><a href="https://link.segmentfault.com/?enc=UrdFO6DwFVMMRcQoC3fgJw%3D%3D.kLlpMq7do5THD%2Btpo9NZ4lgbuZD%2Fyr4deDMFl%2FzfnuqoByX6GVW9gN5F6QoSTTFZOVF9FM9YCK6rcimBpkGB58821qy9sM3OyY3LN%2By1uRA%3D" rel="nofollow">http://www.ruanyifeng.com/blo...</a><br><a href="https://link.segmentfault.com/?enc=PEzQfEPYT9i7YK0n0YcbBA%3D%3D.3Row69FtaQ%2FqJ9oDFzJGyQNJxxbB905wqvfiVg%2BCiLgZmkR%2BqDrA05t9e%2FHNbNVJ" rel="nofollow">https://juejin.im/post/5a1681...</a><br><a href="https://link.segmentfault.com/?enc=ETxfEg6R%2B9ZlrApXATU0gw%3D%3D.O8TO%2FhioAXsp0tFBsbLsebI1%2B8re8rsWhNzch7%2BWNNmY1gU3L8IOAha0Sefh18b6" rel="nofollow">https://juejin.im/post/5ce75f...</a><br><a href="https://link.segmentfault.com/?enc=e76SmGnxLvxToYg9fdRwYw%3D%3D.W8rw5aplJ3psc7FyTzLBt%2BC1JjlO9%2BBdGOhwdpymKXsK%2B0p1o2%2BVwh8YeNJO%2F9wqn5aOrgmjaBWq%2B3Y9pyIn6GbVJLx0nuSoirUw8k2xcSs%3D" rel="nofollow">https://developer.mozilla.org...</a><br><a href="https://link.segmentfault.com/?enc=sqHWlIvhWSYv8jO8qdfPsw%3D%3D.f6RmMUdvvI9f57WZi3m4n%2FQ%2FIHMPFMzlmMMqUcZwG01zbr7abs30K1HK5ynUVv8Z" rel="nofollow">https://juejin.im/post/596e14...</a>
</blockquote>
React-SVG圆环_034
https://segmentfault.com/a/1190000020646599
2019-10-11T10:13:53+08:00
2019-10-11T10:13:53+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>
<code>React</code> 圆环 <code>SemiCircle</code>
</h2>
<p><img src="/img/remote/1460000020646602" alt="img" title="img"></p>
<h3>代码</h3>
<pre><code class="jsx">import React from "react";
class SemiCircle extends React.Component {
constructor(props) {
super(props);
}
polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
var angleInRadians = ((angleInDegrees - 180) * Math.PI) / 180;
return {
x: centerX + radius * Math.cos(angleInRadians),
y: centerY + radius * Math.sin(angleInRadians)
};
};
describeArc = (x, y, radius, startAngle, endAngle) => {
var start = this.polarToCartesian(x, y, radius, endAngle);
var end = this.polarToCartesian(x, y, radius, startAngle);
var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
var d = [
"M",
start.x,
start.y,
"A",
radius,
radius,
0,
largeArcFlag,
0,
end.x,
end.y
].join(" ");
return d;
};
render() {
const { size, scoreSafe, strokeColor, safeText } = this.props;
const gradient = strokeColor => {
if (!strokeColor) return;
let arr = strokeColor.map((item, index) => {
let { offset, color, opacity } = item;
return (
<stop
offset={offset + "%"}
key={index}
style={{ stopColor: color, stopOpacity: opacity }}
/>
);
});
return (
<defs>
<linearGradient
id="sanyuelanvGradient"
x1="0%"
y1="0%"
x2={this.gradientDirection == "horizontal" ? "0%" : "100%"}
y2={this.gradientDirection == "vertical" ? "0%" : "100%"}
>
{arr}
</linearGradient>
</defs>
);
};
return (
<div className="u-tc">
<svg
viewBox={"0 0 " + 128 + " " + 128}
width={size}
height={size}
version="1.1"
>
{gradient(strokeColor)}
<path
id="arc1"
fill="none"
stroke="#F6F6FB"
d={this.describeArc(64, 64, 54, -45, 225)}
strokeWidth="10"
strokeLinecap="round"
/>
<path
id="arc2"
fill="none"
d={this.describeArc(64, 64, 54, -45, scoreSafe)}
style={{
strokeLinecap: "round",
stroke: "url('#sanyuelanvGradient')",
fill: "transparent",
strokeWidth: 10
}}
/>
<text
x={"50%"}
y={"35%"}
dy={20}
style={{
fontSize: "20px",
fill: "url('#sanyuelanvGradient')",
textAnchor: "middle"
}}
>
{localTranslate(safeText)}
</text>
<text
x={"50%"}
y={"55%"}
dy={20}
style={{
fontSize: "12px",
fill: "#6b798e",
textAnchor: "middle"
}}
>
{localTranslate("USERINFO142")}
</text>
</svg>
</div>
);
}
}
export default SemiCircle;
// 示例
<SemiCircle
size={128}
scoreSafe={scoreSafe * 37.5}
strokeColor={strokeColor[scoreSafe]}
safeText={safeText[scoreSafe - 1]}
gradientDirection={"horizontal"}
></SemiCircle></code></pre>
React 源码阅读-4_033
https://segmentfault.com/a/1190000020642522
2019-10-10T18:27:49+08:00
2019-10-10T18:27:49+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 源码阅读-4</h2>
<h3><code>上下文 Context</code></h3>
<p><code>上下文Context</code> 提供了一种通过组件树传递数据的方法,无需在每个级别手动传递 props 属性。</p>
<h4>何时使用 <code>Context</code>
</h4>
<p><code>Context</code> 旨在共享一个组件树内可被视为 “全局” 的数据,例如当前经过身份验证的用户,主题或首选语言等;</p>
<pre><code class="js"> const context: ReactContext<T> = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,
_currentValue: defaultValue,
_currentValue2: defaultValue,
// Used to track how many concurrent renderers this context currently
// supports within in a single renderer. Such as parallel server rendering.
_threadCount: 0,
// These are circular
Provider: (null: any),
Consumer: (null: any),
};</code></pre>
<p>例子:</p>
<pre><code class="jsx">class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
// Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
// 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
// 因为必须将这个值层层传递所有组件。
function Toolbar(props) {
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}</code></pre>
<p>使用 <code>context</code>, 我们可以避免通过中间元素传递 <code>props</code>:</p>
<pre><code class="js">const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {.
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}</code></pre>
<h4>使用<code>Context</code>之前的考虑</h4>
<p>1.Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。</p>
<p><strong>如果你只是想避免层层传递一些属性,组件组合(<code>component composition</code>)有时候是一个比 context 更好的解决方案。</strong></p>
<pre><code class="jsx"><Page user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<PageLayout user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<NavigationBar user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<Link href={user.permalink}>
<Avatar user={user} size={avatarSize} />
</Link></code></pre>
<p>一种无需 <code>context</code> 的解决方案是将 <code>Avatar</code> 组件自身传递下去,因而中间组件无需知道 <code>user</code> 或者 <code>avatarSize</code> 等 <code>props</code>:</p>
<pre><code class="js">function Page(props) {
const user = props.user;
const userLink = (
<Link href={user.permalink}>
<Avatar user={user} size={props.avatarSize} />
</Link>
);
return <PageLayout userLink={userLink} />;
}
// 现在,我们有这样的组件:
<Page user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<PageLayout userLink={...} />
// ... 渲染出 ...
<NavigationBar userLink={...} />
// ... 渲染出 ...
{props.userLink}</code></pre>
<p>但是,有的时候在组件树中很多不同层级的组件需要访问同样的一批数据。<code>Context</code> 能让你将这些数据向组件树下所有的组件进行“广播”,所有的组件都能访问到这些数据,也能访问到后续的数据更新。使用 context 的通用的场景包括管理当前的 <code>locale,theme</code>,或者一些缓存数据,这比替代方案要简单的多。</p>
<h3><code>React.createContext</code></h3>
<pre><code class="js">const MyContext = React.createContext(defaultValue);</code></pre>
<p>创建一个 <code>Context</code> 对象。当 <code>React</code>渲染一个订阅了这个 <code>Context</code> 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 <code>Provider</code> 中读取到当前的 <code>context</code> 值。</p>
<p>只有当组件所处的树中没有匹配到 <code>Provider</code> 时,其 <code>defaultValue</code> 参数才会生效。这有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意:将 <code>undefined</code> 传递给 <code>Provider</code> 的 <code>value</code> 时,<code>consumer</code>组件的 <code>defaultValue</code> 不会生效</p>
<h3><code>Context.Provider</code></h3>
<p>每个 <code>Context</code> 对象都会返回一个 <code>Provider React</code> 组件,它允许<code>consumer</code>组件订阅 <code>context</code> 的变化。</p>
<pre><code class="js"> context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};</code></pre>
<p><code>Provider</code> 接收一个 <code>value</code> 属性,传递给<code>consumer</code>组件。一个 <code>Provider</code> 可以和多个<code>consumer</code>组件有对应关系。多个 <code>Provider</code> 也可以嵌套使用,里层的会覆盖外层的数据。</p>
<p>当 <code>Provider</code> 的 <code>value</code> <code>值发生变化时,它内部的所有</code>consumer<code>组件都会重新渲染。Provider</code> 及其内部 <code>consumer</code> 组件都不受制于 <code>shouldComponentUpdate</code> 函数,因此当 <code>consumer</code> 组件在其祖先组件退出更新的情况下也能更新。</p>
<p>通过新旧值检测来确定变化,使用了与 <code>Object.is</code> 相同的算法。</p>
<p><img src="/img/remote/1460000020642525" alt="img" title="img"></p>
<h3><code>Class.contextType</code></h3>
<pre><code class="jsx">class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* 基于 MyContext 组件的值进行渲染 */
}
}
MyClass.contextType = MyContext;</code></pre>
<p>挂载在 <code>class</code> 上的 <code>contextType</code> 属性会被重赋值为一个由 <code>React.createContext()</code> 创建的 <code>Context</code> 对象。这能让你使用 <code>this.context</code> 来消费最近 <code>Context</code> 上的那个值。你可以在任何生命周期中访问到它,包括 <code>render</code> 函数中。</p>
<p>多个的情况</p>
<blockquote><a href="https://link.segmentfault.com/?enc=PIca1tAv13OeN%2F%2FyeMk4QQ%3D%3D.ptsxHXX%2FL77XBH8V2rhls7vPHBPeGcICRRWEnxx9bJvaEqhEyUnFm8zrnY5UK0j1gIpPxroOybujaVILAo%2BRQx7lddsWlFEf0QCgtb%2BLgCE%3D" rel="nofollow">https://zh-hans.reactjs.org/d...</a></blockquote>
<pre><code class="jsx">class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
/* 基于这个值进行渲染工作 */
}
}</code></pre>
<h3><code>Context.Consumer</code></h3>
<pre><code class="js">const Consumer = {
$$typeof: REACT_CONTEXT_TYPE,
_context: context,
calculateChangedBits: context._calculateChangedBits,
};</code></pre>
<pre><code class="jsx"><MyContext.Consumer>
{value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer></code></pre>
<p><code>这里,React</code> 组件也可以订阅到 <code>context</code> 变更。这能让你在函数式组件中完成订阅 <code>context。</code></p>
<p>这需要函数作为子元素<code>(function as a child)</code>这种做法。这个函数接收当前的 <code>context</code> 值,返回一个 <code>React</code> 节点。传递给函数的 <code>value</code> 值等同于往上组件树离这个 <code>context</code> 最近的 <code>Provider</code> 提供的 <code>value</code> 值。如果没有对应的 <code>Provider,value</code> 参数等同于传递给 <code>createContext()</code> 的 <code>defaultValue</code>。</p>
<blockquote><a href="https://link.segmentfault.com/?enc=FbPeHoI96pPnrDcv217Itg%3D%3D.QOLdwGZDOv%2BxHfPqI%2BafUjqVTktbUZC8%2F3H6bwZ6bcdMDa6yDD2OOCVvzKN5eKp3" rel="nofollow">http://react.html.cn/docs/con...</a></blockquote>
React 源码阅读-3_032
https://segmentfault.com/a/1190000020642433
2019-10-10T18:20:47+08:00
2019-10-10T18:20:47+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>React 源码阅读-3</h2>
<h3><code>React.Component</code></h3>
<pre><code class="js">class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}</code></pre>
<p>在 <code>React</code> 组件中,<strong>代码重用的主要方式是组合而不是继承</strong>。</p>
<blockquote>在 React.Component 的子类中有个必须定义的 render() 函数。本章节介绍其他方法均为可选。</blockquote>
<h3><code>React.PureComponent</code></h3>
<p><code>React.PureComponent</code> 与 <code>React.Component</code> 很相似。两者的区别在于 <code>React.Component</code> 并未实现 <code>shouldComponentUpdate()</code>,而 <code>React.PureComponent</code> 中以浅层对比<code>prop</code> 和 <code>state</code> 的方式来实现了该函数。</p>
<p>如果赋予 <code>React</code> 组件相同的 <code>props</code> 和 <code>state,render()</code> 函数会渲染相同的内容,那么在某些情况下使用 <code>React.PureComponent</code> 可提高性能。</p>
<p><code>React.PureComponent</code> 中的 <code>shouldComponentUpdate()</code>仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。仅在你的 <code>props</code> 和 <code>state</code> 较为简单时,才使用 <code>React.PureComponent</code>,或者在深层数据结构发生变化时调用 forceUpdate() 来确保组件被正确地更新。你也可以考虑使用 <code>immutable</code>对象加速嵌套数据的比较。</p>
<p>此外,<code>React.PureComponent</code> 中的 <code>shouldComponentUpdate()</code> 将跳过所有子组件树的 <code>prop</code> 更新。因此,请确保所有子组件也都是“纯”的组件。</p>
<h4>原理</h4>
<p>当组件更新时,如果组件的 <code>props</code> 和 <code>state</code> 都没发生改变, <code>render</code> 方法就不会触发,省去 <code>Virtual DOM</code> 的生成和比对过程,达到提升性能的目的。具体就是 <code>React</code> 自动帮我们做了一层浅比较:</p>
<pre><code class="js">if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps)
|| !shallowEqual(inst.state, nextState);
}</code></pre>
<p>而 <code>shallowEqual</code> 又做了什么呢?会比较 <code>Object.keys(state | props)</code> 的长度是否一致,每一个 <code>key</code> 是否两者都有,并且是否是一个引用,也就是只比较了第一层的值,确实很浅,所以深层的嵌套数据是对比不出来的。</p>
<pre><code class="js">function shallowEqual(objA: mixed, objB: mixed): boolean {
if (is(objA, objB)) {
return true;
}
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) {
if (
!hasOwnProperty.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])
) {
return false;
}
}
return true;
}
export default shallowEqual;
function is(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
);
}
// Object.is()是在ES6中定义的一个新方法,它与‘===’相比,特别针对-0、+0、NaN做了处理。Object.is(-0, +0)会返回false,而Object.is(NaN, NaN)会返回true。这与===的判断恰好相反,也更加符合我们的预期。
</code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=XPnxA4uPy99SzdX2T664Mg%3D%3D.BMgPY8edMaxGhxZ5kXgcK7djPzL6s%2Bf28go2GtFJapAxaD23G9ZMkkGktosNiuGdvLfreAP8mc%2FammHPa%2BltBg%3D%3D" rel="nofollow">https://www.imweb.io/topic/59...</a></blockquote>
<h4>使用指南</h4>
<h5>易变数据不能使用一个引用</h5>
<pre><code class="jsx">class App extends PureComponent {
state = {
items: [1, 2, 3]
}
handleClick = () => {
const { items } = this.state;
items.pop();
this.setState({ items });
}
render() {
return (<div>
<ul>
{this.state.items.map(i => <li key={i}>{i}</li>)}
</ul>
<button onClick={this.handleClick}>delete</button>
</div>)
}
}</code></pre>
<p>会发现,无论怎么点 <code>delete</code> 按钮, <code>li</code>都不会变少,因为 items 用的是一个引用, <code>shallowEqual</code> 的结果为 <code>true</code> 。改正:</p>
<pre><code class="js">handleClick = () => {
const { items } = this.state;
items.pop();
this.setState({ items: [].concat(items) });
}</code></pre>
<h4>不变数据使用一个引用</h4>
<h5>子组件数据</h5>
<p>如果是基本类型, 是能够更新的.<br>引用类型,则不会更新.</p>
<pre><code class="js">handleClick = () => {
const { items } = this.state;
items.splice(items.length - 1, 1);
this.setState({ items });
}</code></pre>
<p>子组件里还是re-render了。这样就需要我们保证不变的子组件数据的引用不能改变。这个时候可以使用<code>immutable-js</code>函数库。</p>
<h4>函数属性</h4>
<pre><code class="js">// 1
<MyInput onChange={e => this.props.update(e.target.value)} />
// 2
update(e) {
this.props.update(e.target.value)
}
render() {
return <MyInput onChange={this.update.bind(this)} />
}</code></pre>
<p>由于每次 <code>render</code> 操作 <code>MyInput</code> 组件的 <code>onChange</code> 属性都会返回一个新的函数,由于引用不一样,所以父组件的 render 也会导致 MyInput 组件的 <code>render</code> ,即使没有任何改动,所以需要尽量避免这样的写法,最好这样写:</p>
<pre><code class="js">// 1,2
update = (e) => {
this.props.update(e.target.value)
}
render() {
return <MyInput onChange={this.update} />
}</code></pre>
<h4>空对象、空数组或固定对象</h4>
<p>有时候后台返回的数据中,数组长度为0或者对象没有属性会直接给一个 <code>null</code> ,这时候我们需要做一些容错:</p>
<pre><code class="js">class App extends PureComponent {
state = {
items: [{ name: 'test1' }, null, { name: 'test3' }]
}
store = (id, value) => {
const { items } = this.state;
items[id] = assign({}, items[id], { name: value });
this.setState({ items: [].concat(items) });
}
render() {
return (<div>
<ul>
{this.state.items.map((i, k) =>
<Item style={{ color: 'red' }} store={this.store} key={k} id={k} data={i || {}} />)
}
</ul>
</div>)
}
}</code></pre>
<p><code>PureComponent</code> 真正起作用的,只是在一些纯展示组件上,复杂组件用了也没关系,反正 <code>shallowEqual</code> 那一关就过不了,不过记得 <code>props</code> 和 <code>state</code> 不能使用同一个引用哦。</p>
<h3>组件的生命的周期</h3>
<blockquote><a href="https://link.segmentfault.com/?enc=%2F40cy2wNhffbS%2Ffag1nG0Q%3D%3D.i1PEa3AvbpGsb%2BJk0kDdb%2F2qcgEt%2B%2BvZdbP9MwMIHUPq9tq9cCd0uNAdR1tLrBZ4m5lLnWCt4aVuuclkcPTQGg%3D%3D" rel="nofollow">生命周期地址</a></blockquote>
<p><img src="/img/remote/1460000020642436" alt="img" title="img"></p>
React 源码阅读-2_031
https://segmentfault.com/a/1190000020642389
2019-10-10T18:18:13+08:00
2019-10-10T18:18:13+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 源码阅读-2</h2>
<p>在典型的React数据流中,props是父组件与其子组件交互的唯一方式。要修改子项,请使用new props 重新呈现它。但是,在某些情况下,需要在典型数据流之外强制修改子项。要修改的子项可以是React组件的实例,也可以是DOM元素。对于这两种情况,React都提供了api。</p>
<h3>何时使用refs</h3>
<p><code>refs</code>有一些很好的用例:</p>
<ul>
<li>1.文本选择或媒体播放。</li>
<li>2.触发势在必行的动画。</li>
<li>3.与第三方DOM库集成。</li>
</ul>
<p>避免将<code>refs</code>用于可以声明性地完成的任何操作。</p>
<p><strong>不要过度使用<code>Refs</code></strong></p>
<h3>旧版<code>API</code>:字符串引用</h3>
<p>如果您之前使用过<code>React</code>,那么您可能熟悉一个旧的<code>API</code>,其中<code>ref</code>属性是一个字符串<code>textInput</code>,并且DOM节点被访问为<code>this.refs.textInput</code>。建议不要使用它,因为字符串引用有一些问题,被认为是遗留问题,很可能会在未来的某个版本中删除。</p>
<h3>回调引用</h3>
<p>当组件安装时,<code>React</code>将使用<code>DOM</code>元素调用<code>ref</code>回调,并在卸载时调用<code>null</code>。<br>在<code>componentDidMoun</code>t或<code>componentDidUpdate</code>触发之前,<code>Refs</code>保证是最新的.</p>
<pre><code class="jsx">class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.focusTextInput = () => {
// Focus the text input using the raw DOM API
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// autofocus the input on mount
this.focusTextInput();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in an instance field (for example, this.textInput).
return (
<div>
<input
type="text"
ref={element => this.textInput = element}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}</code></pre>
<h4>关于回调 <code>refs</code> 的说明</h4>
<p>如果 <code>ref</code> 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以<code>React</code> 清空旧的 <code>ref</code> 并且设置新的。通过将 <code>ref</code> 的回调函数定义成 <code>class</code> 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。</p>
<h4>
<code>refs</code>例子--点击获取<code>input</code>焦点</h4>
<pre><code class="JavaScript">class Example extends React.Component {
handleClick() {
// 使用原生的 DOM API 获取焦点
this.refs.myInput.focus
();
}
render() {
// 当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
return (
<div>
<input type="text" ref="myInput" />
<input
type="button"
value="点我输入框获取焦点"
onClick={this.handleClick.bind(this)}
/>
</div>
);
}
}
</code></pre>
<h3>使用<code>React.createRef()</code>
</h3>
<p>React.createRef()React 16.3中引入的API。如果您使用的是早期版本的React,我们建议您使用回调引用。</p>
<h4>创建<code>React.createRef()</code>
</h4>
<p><code>Refs</code>是使用属性创建的,<code>React.createRef()</code>并通过<code>ref</code>属性附加到<code>React</code>元素。在构造组件时,通常将<code>Refs</code>分配给实例属性,以便可以在整个组件中引用它们。</p>
<pre><code class="jsx">class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}</code></pre>
<h4>访问参考</h4>
<p>当<code>ref</code>被传递给元素时<code>render</code>,对该节点的引用变得可以在<code>currentref</code> 的属性处访问</p>
<pre><code class="js">
const node = this.myRef.current;
</code></pre>
<p><strong><em>ref的值根据节点的类型而有所不同</em></strong></p>
<ul>
<li>当在<code>refHTML</code>元素上使用该属性时,ref在构造函数中创建的属性将<code>React.createRef()</code>接收底层DOM元素作为其current属性。</li>
<li>在<code>ref</code>自定义类组件上使用该属性时,该<code>ref</code>对象将接收组件的已安装实例作为其<code>current</code>。</li>
</ul>
<p>您可能无法<code>ref</code>在函数组件上使用该属性,因为它们没有实例。</p>
<pre><code class="jsx">class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
}
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}</code></pre>
<p><strong><em>current当组件安装时,React将为该属性分配DOM元素,并null在卸载时将其分配回。ref更新发生之前componentDidMount或componentDidUpdate生命周期方法。</em></strong></p>
<h4>源码</h4>
<pre><code class="js">// an immutable object with a single mutable value
export function createRef(): RefObject {
const refObject = {
current: null,
};
if (__DEV__) {
Object.seal(refObject);
}
return refObject;
}</code></pre>
<h4>无法ref在函数组件上使用该属性</h4>
<pre><code class="js">function MyFunctionComponent() {
return <input />;
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
// This will *not* work!
return (
<MyFunctionComponent ref={this.textInput} />
);
}
}</code></pre>
<p>**如果需要引用它,则应该将组件转换为类,就像您需要生命周期方法或状态时一样。<br>但是,只要引用DOM元素或类组件,就可以在函数组件中使用该<code>ref</code>属性:</p>
<pre><code class="js">function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
let textInput = React.createRef();
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
</code></pre>
<h4>将DOM引用公开给父组件</h4>
<p>在极少数情况下,可能希望从父组件访问子节点的DOM节点。通常不建议这样做,因为它会破坏组件封装,但它偶尔可用于触发焦点或测量子DOM节点的大小或位置。</p>
<p>虽然可以向子组件添加引用,但这不是一个理想的解决方案,因为只能获得组件实例而不是DOM节点。此外,这不适用于功能组件。</p>
<p>如果您使用React 16.3或更高版本,我们建议在这些情况下使用ref forwarding。引用转发允许组件选择将任何子组件的引用公开为自己的组件。可以在ref转发文档中找到有关如何将子DOM节点公开给父组件的详细示例。</p>
<p>如果您使用React 16.2或更低版本,或者您需要比ref转发提供的更多灵活性,您可以使用此替代方法并明确地将ref作为不同名称的prop传递。</p>
<p>如果可能,建议不要暴露DOM节点,但它可以是一个有用的逃生舱。请注意,此方法要求您向子组件添加一些代码。如果您完全无法控制子组件实现,则最后一个选项是使用findDOMNode(),但不鼓励使用它<code>StrictMode</code>。</p>
代码规范_030
https://segmentfault.com/a/1190000020534268
2019-09-29T17:41:21+08:00
2019-09-29T17:41:21+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>代码规范</h2>
<h3>
<code>git commit</code>规范</h3>
<p>每次提交 <code>git commit</code>的时候总是有点难受,不知道怎么写?</p>
<p>还有不同人写的风格完全不一样,</p>
<p>1.安装<code>Commitizen</code></p>
<p><code>npm install -g commitizen</code></p>
<blockquote><a href="https://link.segmentfault.com/?enc=vIUdnKNdLFAoQ%2B2RkXmiZw%3D%3D.opzt32PKYLP%2BQ4kM15fA6Xzo2ESCqAowqQg%2FiInlGPa1qfENTChN9rZx1tlUk%2Bt9" rel="nofollow">https://github.com/commitizen...</a></blockquote>
<p>2.项目运行命令行</p>
<p><code>commitizen init cz-conventional-changelog --save --save-exact</code></p>
<p>凡是用到<code>git commit</code>命令,一律改为使用<code>git cz</code></p>
<p>这时,就会出现选项,用来生成符合格式的 <code>Commit message</code></p>
<p>还有一些其他安装方式,具体参考上面链接的文档即可.</p>
<h3>代码规范</h3>
<p>Airbnb 代码规范 <a href="https://link.segmentfault.com/?enc=IzUvVI6tHx2uU41HIJI0Lg%3D%3D.7DujkqSlIq8WeRMs3nZPYv65KYXIYnoqdmEHqLSZLHpnpdDFDZTOdI%2BUdKYAwDSO" rel="nofollow">https://github.com/airbnb/jav...</a></p>
<p>eslint 代码检查 <a href="https://link.segmentfault.com/?enc=KMPrMGHDgU7bsMxhcY6DjA%3D%3D.WaGPY%2Fxgyra1A6%2F8W918vdwtyHQdK9qtd0GEFY0E0us%3D" rel="nofollow">https://cn.eslint.org</a></p>
<p>korofileheader 注释生成 <a href="https://link.segmentfault.com/?enc=sy6T7x3b1l1rQMM8kP2SzA%3D%3D.tNzKDRUdzByf3%2Bg%2F2%2BJ1m6j5fu5%2FnhHzvnb%2By%2FtJFTHib3pt73jlpSTYvRIn%2F92WDuItNeVwztYjtKNswm9OWvKKiQanYSvzB%2F6RxNx6E9c%3D" rel="nofollow">https://marketplace.visualstu...</a></p>
<p>凹凸实验室代码规范 <a href="https://link.segmentfault.com/?enc=trQWW2W7zA%2BIMKp5jqJTLQ%3D%3D.1mlWqnTO5W4EGvM3FgQ9ciuNkVZ5gtlB4Xq%2BKjSxef%2BLBlcaOsMHCO%2FTp3n6dM0z" rel="nofollow">https://guide.aotu.io/index.html</a></p>
<p>react 代码规范 <a href="https://link.segmentfault.com/?enc=FzOicbryMZ%2Bh2AWJkF96Gg%3D%3D.%2Fwq2nzlFwPivMmb6cbGQslkGDyww5XrhxAaBfz0sXsYq%2BUrBXl3PxsQlephlWHHobkURZe9eWfAtVRuYd%2FYyaQ%3D%3D" rel="nofollow">https://github.com/airbnb/jav...</a></p>
<p>vue 代码规范 <a href="https://link.segmentfault.com/?enc=qcZtVwu5l1dKnTMKLR5nzQ%3D%3D.7cyM3Z%2B%2FT19%2B%2B7JqIYfjXx12di%2FDZ6ic2NziYty5v3gGHrJYRUI5P48yWpzLJPPg" rel="nofollow">https://cn.vuejs.org/v2/style...</a></p>
<h3>命名规则</h3>
<pre><code class="js">Pascal Case 大驼峰式命名法:首字母大写。eg:StudentInfo、UserInfo、ProductInfo
Camel Case 小驼峰式命名法:首字母小写。eg:studentInfo、userInfo、productInfo</code></pre>
<h3>函数命名规则</h3>
<table>
<thead><tr>
<th>动词</th>
<th>含义</th>
<th>返回值</th>
<th> </th>
</tr></thead>
<tbody>
<tr>
<td>can</td>
<td>判断是否可执行某个动作(权限)</td>
<td>函数返回一个布尔值。true:可执行;false:不可执行</td>
<td> </td>
</tr>
<tr>
<td>has</td>
<td>判断是否含有某个值</td>
<td>函数返回一个布尔值。true:含有此值;false:不含有此值</td>
<td> </td>
</tr>
<tr>
<td>is</td>
<td>判断是否为某个值</td>
<td>函数返回一个布尔值。true:为某个值;false:不为某个值</td>
<td> </td>
</tr>
<tr>
<td>get</td>
<td>获取某个值</td>
<td>函数返回一个非布尔值</td>
<td> </td>
</tr>
<tr>
<td>set</td>
<td>设置某个值</td>
<td>无返回值、返回是否设置成功或者返回链式对象</td>
<td> </td>
</tr>
<tr>
<td>load</td>
<td>加载某些数据</td>
<td>无返回值或者返回是否加载完成的结果</td>
<td> </td>
</tr>
</tbody>
</table>
<h3>辅助命名<code>codelf</code>
</h3>
<p><a href="https://link.segmentfault.com/?enc=tadObfO4DB%2B4vAHAaKNJsA%3D%3D.CBkv%2F9a%2BAacerYp9%2BPJUDe4mWhsbeSDjN2E%2BfafpdXI%3D" rel="nofollow">https://unbug.github.io/codelf/</a></p>
<p>一个辅助命名的网站, 也有<code>VS Code</code>插件扩展,取名的时候时间比较实用</p>
LeetCode-01-两数之和_029
https://segmentfault.com/a/1190000020518211
2019-09-27T19:50:48+08:00
2019-09-27T19:50:48+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>LeetCode-01-两数之和</h2>
<h3>题目描述</h3>
<p>给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。</p>
<p>你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。</p>
<p>::: tip<br>给定 nums = [2, 7, 11, 15], target = 9</p>
<p>因为 nums[0] + nums[1] = 2 + 7 = 9<br>所以返回 [0, 1]<br>:::</p>
<h3>解法</h3>
<p>利用 <code>Map</code> 记录数组元素值和对应的下标,对于一个数 <code>nums[i]</code>,判断 <code>target - nums[i]</code> 是否存在 <code>Map</code></p>
<pre><code class="js">var twoSum = function(nums, target) {
const _length = nums.length;
const _mayMap = new Map();
for (let i = 0; i < _length; i++) {
if (_mayMap.has(target - nums[i])) {
return [_mayMap.get(target - nums[i]), i];
}
_mayMap.set(nums[i], i);
}
};</code></pre>
<h3>
<code>Map</code>回顾</h3>
<p><code>Map</code> 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。</p>
<pre><code class="js">new Map([iterable])</code></pre>
<p><code>Iterable</code> 可以是一个数组或者其他 <code>iterable</code> 对象,其元素为键值对(两个元素的数组,例如:<code>[[ 1, 'one' ],[ 2, 'two' ]]</code>)。 每个键值对都会添加到新的 <code>Map</code>。<code>null</code> 会被当做 <code>undefined</code>。</p>
<h4>
<code>Objects</code> 和 <code>maps</code> 的比较</h4>
<p><code>Objects</code> 和 <code>Maps</code> 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。</p>
<p>因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 <code>Maps</code> 使用。</p>
<p>不过 <code>Maps</code> 和 <code>Objects</code> 有一些重要的区别,在下列情况里使用 <code>Map</code> 会是更好的选择:</p>
<p>一个<code>Object</code>的键只能是字符串或者 <code>Symbols</code>,但一个 <code>Map</code> 的键可以是任意值,包括函数、对象、基本类型。</p>
<p><code>Map</code> 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,<code>Map</code> 对象是按插入的顺序返回键值。</p>
<p>可以通过 <code>size</code> 属性直接获取一个<code>Map</code>的键值对个数,而 <code>Object</code>的键值对个数只能手动计算。</p>
<p><code>Map</code> 可直接进行迭代,而 <code>Object</code> 的迭代需要先获取它的键数组,然后再进行迭代。</p>
<p><code>Object</code> 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。<br>虽然 <code>ES5</code> 开始可以用 <code>map = Object.create(null)</code> 来创建一个没有原型的对象,但是这种用法不太常见。</p>
<p><code>Map</code> 在涉及频繁增删键值对的场景下会有些性能优势。</p>
<h4>属性</h4>
<h4><code>Map.length</code></h4>
<p>属性 length 的值为 0 。</p>
<h4><code>get Map[@@species]</code></h4>
<p>本构造函数用于创建派生对象。</p>
<h4><code>Map.prototype</code></h4>
<p>表示 <code>Map</code> 构造器的原型。 允许添加属性从而应用于所有的 <code>Map</code> 对象。</p>
<p><strong>所有的 Map 对象实例都会继承 Map.prototype。</strong></p>
<h4><code>Map.prototype.constructor</code></h4>
<p>返回一个函数,它创建了实例的原型。默认是<code>Map</code>函数。</p>
<h4><code>Map.prototype.size</code></h4>
<p>返回<code>Map</code>对象的键/值对的数量。</p>
<h4><code>Map.prototype.clear()</code></h4>
<p>移除Map对象的所有键/值对 。</p>
<h4><code>Map.prototype.delete(key)</code></h4>
<p>如果 Map 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false</p>
<h4><code>Map.prototype.entries()</code></h4>
<p>返回一个新的 Iterator 对象,它按插入顺序包含了Map对象中每个元素的 [key, value] 数组。</p>
<h4><code>Map.prototype.forEach(callbackFn[, thisArg])</code></h4>
<p>按插入顺序,为 Map对象里的每一键值对调用一次callbackFn函数。如果为forEach提供了thisArg,它将在每次回调中作为this值。</p>
<h4><code>Map.prototype.get(key)</code></h4>
<p>返回键对应的值,如果不存在,则返回undefined。</p>
<h4><code>Map.prototype.has(key)</code></h4>
<p>返回一个布尔值,表示Map实例是否包含键对应的值。</p>
<h4><code>Map.prototype.keys()</code></h4>
<p>返回一个新的 Iterator对象, 它按插入顺序包含了Map对象中每个元素的键 。</p>
<h4><code>Map.prototype.set(key, value)</code></h4>
<p>设置Map对象中键的值。返回该Map对象。</p>
<h4><code>Map.prototype.values()</code></h4>
<p>返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的值 。</p>
<h4><code>Map.prototype[@@iterator]()</code></h4>
<p>返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的 [key, value] 数组。</p>
<h4>例子</h4>
<pre><code class="js">var myMap = new Map();
var keyObj = {},
keyFunc = function () {},
keyString = "a string";
// 添加键
myMap.set(keyString, "和键'a string'关联的值");
myMap.set(keyObj, "和键keyObj关联的值");
myMap.set(keyFunc, "和键keyFunc关联的值");
myMap.size; // 3
// 读取值
myMap.get(keyString); // "和键'a string'关联的值"
myMap.get(keyObj); // "和键keyObj关联的值"
myMap.get(keyFunc); // "和键keyFunc关联的值"
myMap.get("a string"); // "和键'a string'关联的值"
// 因为keyString === 'a string'
myMap.get({}); // undefined, 因为keyObj !== {}
myMap.get(function() {}) // undefined, 因为keyFunc !== function () {}</code></pre>
<p>传递数据</p>
<pre><code class="js">let valuesMap = new Map();
class payserviceclass {
constructor() {}
getItem(key) {
const stringKey = String(key);
if (valuesMap.has(key)) {
return valuesMap.get(stringKey);
}
return null;
}
setItem(key, val) {
return valuesMap.set(String(key), val);
}
clear() {
return valuesMap.clear();
}
}
const payservice = new payserviceclass();
export default payservice;</code></pre>
<blockquote> <a href="https://link.segmentfault.com/?enc=HpwPICV1yBf3Bjvdh%2B6SaA%3D%3D.Bw03pmKN0fOBQjtOkEloHrRf2bSTNt6PTB4QQUsiu0zxtYEDZC90QkLp%2FzU0AEdenk1VXp9PTagVvA9WR4wG%2BGCDGQm0xYqclKtYtumcoEx8wn5PScvrYTkeScbJbI8K" rel="nofollow">https://developer.mozilla.org...</a>
</blockquote>
MacOS配置 iTerm2,oh-my-zsh,zsh_028
https://segmentfault.com/a/1190000020507152
2019-09-26T21:26:14+08:00
2019-09-26T21:26:14+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>
<code>MacOS</code>配置 <code>iTerm2</code>,<code>oh-my-zsh</code>,'zsh'</h2>
<p><img src="/img/remote/1460000020507155" alt="img" title="img"></p>
<h3><code>zsh</code></h3>
<p><code>Mac</code> 自带的 <code>zsh</code>终端</p>
<p><code>cat /etc/shells</code></p>
<p>修改系统默认的终端为<code>zsh</code></p>
<pre><code class="sh"># 更改终端
chsh -s /bin/zsh
# 确认是否更改成功
echo $SHELL
# /bin/zsh</code></pre>
<h3>安装<code>oh-my-zsh</code>
</h3>
<p><a href="https://link.segmentfault.com/?enc=2mkIdk5mlbF%2Bl%2FMfFLbOKA%3D%3D.gdC7fb%2BL0a8ySyu0EakenCu%2BpqM%2F8vXmpYEiIxthlpdLTMGF5Way9zui93VvN2wD" rel="nofollow">https://github.com/robbyrusse...</a></p>
<p><img src="/img/remote/1460000020507156" alt="img" title="img"></p>
<h4>安装</h4>
<pre><code class="sh">sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"</code></pre>
<h3>安装主题和字体</h3>
<p>链接 <a href="https://link.segmentfault.com/?enc=8dCwDtD3aSkuQTm%2BNxvU8w%3D%3D.esvnKWYTflE8CClYql7iacYd8g446%2FzWmOoIngZa8DfhHSwDikyF1tvgCQpS3r48" rel="nofollow">https://github.com/Powerlevel...</a></p>
<p><img src="/img/remote/1460000020507157" alt="img" title="img"></p>
<p>因为安装了 <code>oh-my-zsh</code>,就用下面的方式安装即可</p>
<pre><code class="sh">git clone https://github.com/bhilburn/powerlevel9k.git ~/.oh-my-zsh/custom/themes/powerlevel9k</code></pre>
<h4>编辑<code>.zshc</code>
</h4>
<pre><code class="sh">vi .zshrc
# 更改
ZSH_THEME="powerlevel9k/powerlevel9k"</code></pre>
<h4>下载字体</h4>
<p>直接找到字体文件下载下来 安装</p>
<p><a href="https://link.segmentfault.com/?enc=D02sEis9kOPxlRE%2FoQM90g%3D%3D.JkHSfdBfbUOphv1SUf9aP0hJgceAyQlB7p%2FEmsDLaz1iA21%2BSe9Nn6dIcJgMPwZpp6yuIv2Wkb9KNGRyd4EUmA%3D%3D" rel="nofollow">https://github.com/gabrielela...</a></p>
<p><img src="/img/remote/1460000020507159" alt="img" title="img"></p>
<p><img src="/img/remote/1460000020507160" alt="img" title="img"></p>
<p><img src="/img/remote/1460000020507161" alt="img" title="img"></p>
<p>安装好即可</p>
<h4>更改字体</h4>
<p>这时候打开终端字体是乱码的 需要配置一下字体</p>
<p><img src="/img/remote/1460000020507162" alt="img" title="img"></p>
<p><img src="/img/remote/1460000020507163" alt="img" title="img"></p>
<h4>下载主题</h4>
<p><a href="https://link.segmentfault.com/?enc=%2FMWlDjECgjls2jzfCNTOrA%3D%3D.C9KyB2H6WRWd%2BcIAHr5Fpe%2BZUNZdR7%2F2ItK7RiKfBnM%3D" rel="nofollow">https://iterm2colorschemes.com/</a><br><a href="https://link.segmentfault.com/?enc=aBquXhMku99ZiOt9KqOOEA%3D%3D.azN3rEKGhu0vSCmNhK%2F5ge8tsjkCtlAGDLdZ49ujypJdWanwPjVxjikq8APz9ydK3%2BcOn2%2F9rUgsxqeJcbpm5g%3D%3D" rel="nofollow">https://github.com/mbadolato/...</a></p>
<p>主题设置,字体设置</p>
<p><img src="/img/remote/1460000020507164" alt="img" title="img"></p>
<p><img src="/img/remote/1460000020507165" alt="img" title="img"></p>
<h3>安装 <code>iTerm2</code>
</h3>
<p><a href="https://link.segmentfault.com/?enc=padbDFyOOwuw8G2YpH1BwA%3D%3D.20e1OfTOLi8%2BRqzbDQH9xbxmLmFZk63u%2B3OO%2FB%2BisUr2ycxG8ApvwbpL9qVekk2M" rel="nofollow">https://www.iterm2.com/downlo...</a></p>
<p>主题设置,字体配置</p>
<p><img src="/img/remote/1460000020507166" alt="img" title="img"></p>
<h3>更改<code>.zshc</code>设置</h3>
<p><a href="https://link.segmentfault.com/?enc=jx6vCWfUd41eY1%2FkdlEFHg%3D%3D.4GEEDd5ZX7nxUvpjWhIcjW5XsSb3hqw6ikylIW2WBHtsXfH3osjuWmXpppwSnMPQ2IqNIDsp4DmHenymwoVXgRg9f6QxksCcVH74EnomAYA%3D" rel="nofollow">https://github.com/Powerlevel...</a></p>
<p>进一步配置参考</p>
<h4>注意</h4>
<p><code>iTerm</code>图标无法显示问题</p>
<p>这边字体配置要放在主题前面</p>
<pre><code class="sh"># 字体设置房放在主题设置前面
POWERLEVEL9K_MODE='awesome-patched'
ZSH_THEME="powerlevel9k/powerlevel9k"</code></pre>
<h4>
<code>VSCode</code> 配置</h4>
<pre><code class="sh"> "terminal.external.osxExec": "iTerm.app",
"terminal.integrated.shell.osx": "zsh",
"terminal.integrated.fontFamily": "Droid Sans Mono Awesome",</code></pre>
<p>我的配置</p>
<pre><code class="sh">
POWERLEVEL9K_MODE='awesome-patched'
ZSH_THEME="powerlevel9k/powerlevel9k"
plugins=(
git
extract
z
)
source $ZSH/oh-my-zsh.sh
source ~/.bash_profile
POWERLEVEL9K_HOME_ICON=''
POWERLEVEL9K_HOME_SUB_ICON=''
POWERLEVEL9K_FOLDER_ICON=''
DISABLE_AUTO_TITLE="true"
POWERLEVEL9K_VCS_GIT_ICON=''
POWERLEVEL9K_VCS_STAGED_ICON='\u00b1'
POWERLEVEL9K_VCS_UNTRACKED_ICON='\u25CF'
POWERLEVEL9K_VCS_UNSTAGED_ICON='\u00b1'
POWERLEVEL9K_VCS_INCOMING_CHANGES_ICON='\u2193'
POWERLEVEL9K_VCS_OUTGOING_CHANGES_ICON='\u2191'
POWERLEVEL9K_VCS_MODIFIED_BACKGROUND='green'
POWERLEVEL9K_VCS_UNTRACKED_BACKGROUND='yellow'
#POWERLEVEL9K_VCS_UNTRACKED_ICON='?'
POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(status os_icon context dir vcs)
POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(background_jobs virtualenv rbenv rvm time)
POWERLEVEL9K_SHORTEN_STRATEGY="truncate_middle"
POWERLEVEL9K_SHORTEN_DIR_LENGTH=4
POWERLEVEL9K_TIME_FORMAT="%D{%H:%M \uE868 %d.%m.%y}"
POWERLEVEL9K_STATUS_VERBOSE=false
export DEFAULT_USER="$USER"
</code></pre>
React 源码阅读1_027
https://segmentfault.com/a/1190000020438666
2019-09-19T23:17:26+08:00
2019-09-19T23:17:26+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React 源码阅读1</h2>
<p>Fork最新版的 React 源码<a href="https://link.segmentfault.com/?enc=cZaj9GG6hn3Oxd3%2BVUhs0g%3D%3D.r%2F%2FJ0zkrIihLh1ie936rnOtat2%2BKpSP7cdm9wD2EtSlevz0F9uCBiP%2FBFHO76ZjU" rel="nofollow">地址</a></p>
<p>刚开始看源码,先过一遍.先看最顶层暴露出来的 <code>API</code>,再具体看实现的源码.<br>保持学习.</p>
<h3>React 入口</h3>
<p><img src="/img/remote/1460000020438670?w=1000&h=411" alt="img" title="img"></p>
<pre><code class="js">
const React = {
Children: {
map,
forEach,
count,
toArray,
only,
},
createRef,
Component,
PureComponent,
createContext,
forwardRef,
lazy,
memo,
useCallback,
useContext,
useEffect,
useImperativeHandle,
useDebugValue,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState,
Fragment: REACT_FRAGMENT_TYPE,
Profiler: REACT_PROFILER_TYPE,
StrictMode: REACT_STRICT_MODE_TYPE,
Suspense: REACT_SUSPENSE_TYPE,
unstable_SuspenseList: REACT_SUSPENSE_LIST_TYPE,
createElement: __DEV__ ? createElementWithValidation : createElement,
cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
isValidElement: isValidElement,
version: ReactVersion,
unstable_withSuspenseConfig: withSuspenseConfig,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,
};
export default React;
</code></pre>
<h3><code>Children</code></h3>
<p><code>React.Children</code> 提供了用于处理 <code>this.props.children</code> 不透明数据结构的实用方法。</p>
<pre><code class="js">// React.js
import {forEach, map, count, toArray, only} from './ReactChildren';
...
...
Children: {
map,
forEach,
count,
toArray,
only,
},
//ReactChildren,js
export {
forEachChildren as forEach,
mapChildren as map,
countChildren as count,
onlyChild as only,
toArray,
};</code></pre>
<h3><code>React.Children.map</code></h3>
<p>在 <code>children</code> 里的每个直接子节点上调用一个函数,并将 <code>this</code> 设置为 <code>thisArg</code>。如果 <code>children</code> 是一个数组,它将被遍历并为数组中的每个子节点调用该函数。<br>如果子节点为<code>null</code> 或是 <code>undefined</code>,则此方法将返回 <code>null</code> 或是 <code>undefined</code>,而不会返回数组。</p>
<blockquote>如果 children 是一个 Fragment 对象,它将被视为单一子节点的情况处理,而不会被遍历。</blockquote>
<pre><code class="js">render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}</code></pre>
<h4>
<code>map</code>源码</h4>
<pre><code class="js">function mapChildren(children, func, context) {
if (children == null) {
return children;
}
// 返回 result 数组
const result = [];
//result 进去走了一圈 这边就不再细说
mapIntoWithKeyPrefixInternal(children, result, null, func, context);
return result;
}</code></pre>
<blockquote>
<a href="https://segmentfault.com/a/1190000019968074">https://segmentfault.com/a/11...</a> 写的比较详细</blockquote>
<h3><code>React.Children.forEach</code></h3>
<pre><code class="js">React.Children.forEach(children, function[(thisArg)])</code></pre>
<p>与 <code>React.Children.map()</code> 类似,但它不会返回一个数组。</p>
<h4>
<code>forEach</code>源码</h4>
<pre><code class="js">/**
* Iterates through children that are typically specified as `props.children`.
*
* See https://reactjs.org/docs/react-api.html#reactchildrenforeach
*
* The provided forEachFunc(child, index) will be called for each
* leaf child.
*
* @param {?*} children Children tree container.
* @param {function(*, int)} forEachFunc
* @param {*} forEachContext Context for forEachContext.
*/
function forEachChildren(children, forEachFunc, forEachContext) {
if (children == null) {
return children;
}
const traverseContext = getPooledTraverseContext(
null,
null,
forEachFunc,
forEachContext,
);
traverseAllChildren(children, forEachSingleChild, traverseContext);
releaseTraverseContext(traverseContext);
}</code></pre>
<h3><code>React.Children.count</code></h3>
<pre><code class="js">React.Children.count(children)</code></pre>
<p>返回 <code>children</code> 中的组件总数量,等同于通过<code>map</code>或 <code>forEach</code>调用回调函数的次数。</p>
<h4>
<code>count</code>源码</h4>
<pre><code class="js">function countChildren(children) {
return traverseAllChildren(children, () => null, null);
}</code></pre>
<h3><code>React.Children.only</code></h3>
<pre><code class="js">React.Children.only(children)</code></pre>
<p>验证 <code>children</code> 是否只有一个子节点(一个 <code>React</code> 元素),如果有则返回它,否则此方法会抛出错误。</p>
<h4>
<code>only</code>源码</h4>
<pre><code class="js">function onlyChild(children) {
invariant(
isValidElement(children),
'React.Children.only expected to receive a single React element child.',
);
return children;
}</code></pre>
<blockquote>
<code>React.Children.only()</code> 不接受 <code>React.Children.map()</code>的返回值,因为它是一个数组而并不是 <code>React</code> 元素。</blockquote>
<h3><code>React.Children.toArray</code></h3>
<pre><code class="js">React.Children.toArray(children)</code></pre>
<p>将 <code>children</code> 这个复杂的数据结构以数组的方式扁平展开并返回,并为每个子节点分配一个 <code>key</code>。<br>当你想要在渲染函数中操作子节点的集合时,它会非常实用,特别是当你想要在向下传递 <code>this.props.children</code> 之前对内容重新排序或获取子集时。</p>
<p><code>React.Children.toArray()</code> 在拉平展开子节点列表时,更改 <code>key</code> 值以保留嵌套数组的语义。<br>也就是说,<code>toArray</code> 会为返回数组中的每个 <code>key</code>添加前缀,以使得每个元素 <code>key</code> 的范围都限定在此函数入参数组的对象内。</p>
<h4>
<code>toArray</code>源码</h4>
<pre><code class="js">function toArray(children) {
const result = [];
mapIntoWithKeyPrefixInternal(children, result, null, child => child);
return result;
}</code></pre>
Github travis-ci CI CD _026
https://segmentfault.com/a/1190000020436193
2019-09-19T18:17:35+08:00
2019-09-19T18:17:35+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>Github travis-ci CI CD</h2>
<p><code>CICD</code> 是 持续集成<code>Continuous Integration</code>和持续部署<code>Continuous Deployment</code>简称。指在开发过程中自动执行一系列脚本来减低开发引入<code> bug</code> 的概率,在新代码从开发到部署的过程中,尽量减少人工的介入。</p>
<p>本文主要介绍一下 <code>travis-ci</code> 持续集成和给 github Actions</p>
<h3>Travis-ci</h3>
<blockquote><a href="https://link.segmentfault.com/?enc=5MzzjWJpesC0hAUp1e%2FNpw%3D%3D.kvnE5FAb719Hy5JNMV6JGlV36z35LnYwNjuI1BS4v78%3D" rel="nofollow">https://www.travis-ci.org/</a></blockquote>
<h4>1.登录travis-ci</h4>
<p>通过 <code>github</code>账号登录,会自动同步你的仓库</p>
<p><img src="/img/remote/1460000020436196?w=1313&h=871" alt="" title=""></p>
<h4>选择需设置的仓库</h4>
<p><img src="https://raw.githubusercontent.com/xiaoping027/imgroom/master/20190919142034.png" alt="" title=""></p>
<p>先勾选一个测试仓库</p>
<h4>3 设置</h4>
<p>一些解释说明可以看具体的文档,主要包括这几方面</p>
<p><img src="https://raw.githubusercontent.com/xiaoping027/imgroom/master/20190919143017.png" alt="" title=""></p>
<p><img src="https://raw.githubusercontent.com/xiaoping027/imgroom/master/20190919142427.png" alt="" title=""></p>
<h4>添加<code>.travis.yml</code>
</h4>
<p><a href="https://link.segmentfault.com/?enc=ICphYlJHxRBLLtgl%2Fu5uOQ%3D%3D.i4oAFsfbAEiAKHY9FgFEUtumHchlfQUwSGvhgkz9jrKn7IVkZBD8px4eokEgaMGw" rel="nofollow">Travis-ci 构建的生命周期</a> 具体一些步骤可以查看文档.</p>
<p><img src="/img/remote/1460000020436201?w=896&h=685" alt="" title=""></p>
<p>这个文件主要是告诉 <code>Travis CI </code>应该做什么,以前端<code>node.js</code>为例:</p>
<pre><code class="yaml">language: node_js # 语言设置
node_js: # node 版本
- "8"
# npm现在默认缓存,如果您要禁用它,请将以下内容添加到您的.travis.yml:
cache:
npm: false
before_install: # 安装前
- npm install
script:
- npm run build
</code></pre>
<p>如果当前目录存在<code>yarn.lock</code>可以使用 <code>Yarn</code>;</p>
<p>如果当前目录中都存在<code>package.json</code>和<code>yarn.lock</code>,则运行以下命令<em>而不是</em> <code>npm install</code></p>
<p>具体的一些配置,通过查看文档即可;</p>
<p>现在已经构建成功;</p>
<p><img src="https://raw.githubusercontent.com/xiaoping027/imgroom/master/20190919150107.png" alt="" title=""></p>
<h4>发布部署</h4>
<p>如果每次构建完都自动部署,或者手动部署可以再下一步;</p>
<pre><code class="yaml">language: node_js
node_js:
- "8"
before_install:
- yarn install
script:
- yarn build
after_script:
- cd ./dist
- git init
- git config user.name "${U_NAME}"
- git config user.email "${U_EMAIL}"
- git add .
- git commit -m "Update tools"
- git push --force --quiet "https://${GH_TOKEN}@${GH_REF}" master:${P_BRANCH}
#指定分支,只有指定的分支提交时才会运行脚本
branches:
only:
- master
</code></pre>
<p>发布的是 <code>github page </code>博客. </p>
<p><img src="/img/remote/1460000020436203?w=1515&h=401" alt="" title=""></p>
<p><img src="https://raw.githubusercontent.com/xiaoping027/imgroom/master/20190919151451.png" alt="" title=""></p>
<p>私密变量 <code>GitHub token </code>设置</p>
<p><strong>配置私密的环境变量时一定要加密,因为会显示在日志中且能够被他人看到</strong></p>
<blockquote>
<a href="https://link.segmentfault.com/?enc=AhMgePHEGKQMOP8yi4iscg%3D%3D.niFPpLngW%2FooSbUcmOQwPR8g%2BwMP2dDl%2Bb2nyd5IyJonxfaQjLUHMltmJqbPHZDT" rel="nofollow">https://www.cnblogs.com/moran...</a><p><a href="https://link.segmentfault.com/?enc=ILRf2ywI7eGLqQrCr2FX9Q%3D%3D.PqJW51cWI4LDf%2FfqBC5q45LtWwJMny8JZm0utWmIcYY%3D" rel="nofollow">https://docs.travis-ci.com/</a></p>
</blockquote>
<h3>GitHub Actions!</h3>
<p>github 推出的新功能 项目自动化地构建工作流,例如代码检查,自动化打包,测试,发布版本等等</p>
<h4>申请内测资格</h4>
<p><img src="https://raw.githubusercontent.com/xiaoping027/imgroom/master/20190919174829.png" alt="" title=""></p>
<p><img src="/img/remote/1460000020436206?w=1133&h=538" alt="" title=""></p>
<h4>具体操作</h4>
<p><img src="/img/remote/1460000020436207?w=1807&h=748" alt="" title=""></p>
<p>点进页面能够发现有跟过提示了</p>
<p>因为这个项目是基于 <code>node.js</code> </p>
<p><img src="/img/remote/1460000020436207?w=1807&h=748" alt="" title=""></p>
<p>文档还是挺详细的 mark 一下</p>
<blockquote>
<a href="https://link.segmentfault.com/?enc=RaeyXJ%2FILSGLq3AQ%2FSFIjg%3D%3D.K8OKJ9cnSp3ekk%2BpDzrMBPeRKwAgRXYdoCfC%2BrXsLWEFfJsiOfdHDK5ZGykpegIt2hQtlea6TfkfDwzdfzFvHQ%3D%3D" rel="nofollow">https://help.github.com/en/ar...</a><p><a href="https://link.segmentfault.com/?enc=jkDQlDnoLDXxitnH7v5G1g%3D%3D.5xdFoNzLZ3AXXt3DVPY5EnVf3WYsuxAa8MenrXpAV2N2EWwNCiXcNLtq1eUW83Kn" rel="nofollow">https://github.com/features/a...</a></p>
<p><a href="https://link.segmentfault.com/?enc=8XiFpKePniAlUnlBDeCFeQ%3D%3D.6CP3JpdZwqMJ0n3u6ywHV6ARSgGZFqhQcKrWupaoLYepwUjyfgZgW%2FC9S0ShKUAUigyyLrOcoEIeXm5zswCdNxESuHYXSLJmy5fJ9ezNwdVFo1VgMm0rEU5Lbvcu1fiA" rel="nofollow">https://help.github.com/en/ca...</a></p>
<p><a href="https://link.segmentfault.com/?enc=sIaIvsK%2BiExOCO0vy%2BpGVQ%3D%3D.BCdJrWufkk4GYbqZkoQLlAk%2B61y%2FTJ%2B6%2FqUycGey0ZdF5KEMtTGGiw4XERie%2BbAXYDu531tgJIr9Q2ekooGixS82iw%2FctImB1TanIDsBjcw%3D" rel="nofollow">http://www.ruanyifeng.com/blo...</a></p>
</blockquote>
vuepress 搭建博客_025
https://segmentfault.com/a/1190000020396632
2019-09-16T16:28:48+08:00
2019-09-16T16:28:48+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>搭建个人博客</h2>
<blockquote>github博客地址 <a href="https://link.segmentfault.com/?enc=zWKglC5UXnLdzrqL%2FZpenA%3D%3D.f7Qa40XkCpnyMtosTZcP%2FSObHbhvlFp7VNqkkKxwnJs%3D" rel="nofollow">https://xiaoping027.github.io</a>
</blockquote>
<h3>准备工作</h3>
<h4>初始化</h4>
<pre><code class="sh">yarn init 或者 npm init</code></pre>
<p>按照提示一步一步即可</p>
<h4>安装VuePress</h4>
<pre><code class="sh">yarn add -D vuepress
或者
npm install -D vuepress</code></pre>
<h4>package.json</h4>
<pre><code class="json">{
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
}
}
</code></pre>
<p>分别是<code>dev</code>和<code>build</code>模式</p>
<h4>新建文件夹</h4>
<pre><code class="sh">mkdir docs
├── docs/
├── package.json
└── yarn.lock
cd docs
mkdir .vuepress
├── docs
│ └── .vuepress
├── package.json
└── yarn.lock
</code></pre>
<p>在 <code>.vueress</code>文件夹下 新建 <code>config.js</code></p>
<pre><code class="js">
// .vuepress/config.js
module.exports = {
title: "前端学习记录",
description: "前端进阶,前端学习",
head: [["link", { rel: "icon", href: "/logo.png" }]],
lang: "zh-CN",
themeConfig: {
nav: [
{ text: "主页", link: "/" },
{ text: "React | Vue", link: "/react/" }
],
sidebar: "auto",
sidebarDepth: 4,
activeHeaderLinks: true,
lastUpdated: "Last Updated"
}
};
</code></pre>
<p>新建<code>react</code> 文件夹<br>新建 <code>md</code>文件</p>
<p>默认指向 <code>README.md</code></p>
<pre><code class="sh">├── docs
│ ├── .vuepress
│ │ └── config.js
│ ├── README.md
│ └── react
│ └── README.md
├── package.json
└── yarn.lock</code></pre>
<pre><code class="sh">yarn docs:dev
# 或者
npm run docs:dev</code></pre>
<p><img src="/img/remote/1460000020396635?w=1304&h=567" alt="image" title="image"></p>
<h3>配置</h3>
<blockquote>
<a href="https://link.segmentfault.com/?enc=w2T6fMjpvKvqAqgvK69wUg%3D%3D.eVd3o%2F3MhRv%2BJXJ8AzZAPT9Jt46EiU85zBHHOa28e0GwA%2BWdbBAyyfUYvNnmLn0S" rel="nofollow">https://vuepress.vuejs.org/zh...</a><br><a href="https://link.segmentfault.com/?enc=Y8BtcqdtLqLvMKfOb%2BMNBQ%3D%3D.zLGZy3O%2Fl3GgfDpK425T7nANtZ1%2BqQoi3LCeEUbcDR%2FSeWOX6p4hGMETy0oTY%2FhkUu5Fp70%2Fr5%2BcasVgW%2BotaYRJ8UIPgpeZBrLFR2m1CLd6qetKQOOH%2FLq9iLywCVlY" rel="nofollow">https://vuepress.vuejs.org/zh...</a>
</blockquote>
<p>具体配置可以查看官方文档</p>
<h4>默认主题</h4>
<pre><code class="md">---
home: true
heroImage: /hero.png
heroText: Hero 标题
tagline: Hero 副标题
actionText: 快速上手 →
actionLink: /zh/guide/
features:
- title: 简洁至上
details: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
- title: Vue驱动
details: 享受 Vue + webpack 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
- title: 高性能
details: VuePress 为每个页面预渲染生成静态的 HTML,同时在页面被加载的时候,将作为 SPA 运行。
footer: MIT Licensed | Copyright © 2018-present Evan You
---</code></pre>
<h4>导航栏设置</h4>
<pre><code class="js">// .vuepress/config.js
module.exports = {
themeConfig: {
nav: [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/' },
{ text: 'External', link: 'https://google.com' },
]
}
}
或者
module.exports = {
themeConfig: {
nav: [
{
text: 'Languages',
ariaLabel: 'Language Menu',
items: [
{ text: 'Chinese', link: '/language/chinese/' },
{ text: 'Japanese', link: '/language/japanese/' }
]
}
]
}
}
嵌套的情况
module.exports = {
themeConfig: {
nav: [
{
text: 'Languages',
items: [
{ text: 'Group1', items: [/* */] },
{ text: 'Group2', items: [/* */] }
]
}
]
}
}
</code></pre>
<h4>侧边栏设置</h4>
<pre><code class="js">// .vuepress/config.js
module.exports = {
themeConfig: {
sidebar: [
{
title: 'Group 1', // 必要的
path: '/foo/', // 可选的, 应该是一个绝对路径
collapsable: false, // 可选的, 默认值是 true,
sidebarDepth: 1, // 可选的, 默认值是 1
children: [
'/'
]
},
{
title: 'Group 2',
children: [ /* ... */ ]
}
]
}
}
</code></pre>
<h3>项目部署</h3>
<pre><code class="sh">#!/usr/bin/env sh
# 确保脚本抛出遇到的错误
set -e
# 生成静态文件
yarn build
# 进入生成的文件夹
cd docs/.vuepress/dist
# 如果是发布到自定义域名
# echo 'www.maybee.wang' > CNAME
git init
git add -A
git commit -m 'deploy'
ls -l
# 如果发布到 https://<USERNAME>.github.io
git push -f https://github.com/xiaoping027/xiaoping027.github.io.git master
# 如果发布到 https://<USERNAME>.github.io/<REPO>
# git push -f git@github.com:<USERNAME>/<REPO>.git master:gh-pages
</code></pre>
Chart.js使用小结_024
https://segmentfault.com/a/1190000020014222
2019-08-08T22:38:31+08:00
2019-08-08T22:38:31+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>Chart.js使用小结</h2>
<h3>官方文档</h3>
<blockquote>英文文档 <a href="https://link.segmentfault.com/?enc=9dUSRKHeJt9%2BSLMtuikuLg%3D%3D.57%2FGG6AlY%2FwxFQjMx1owBq%2FKjNJ736F9CSoKkMLOXQKl5OP7oNRfjUPZgBubY3yo" rel="nofollow">https://www.chartjs.org/docs/...</a><p>中文文档 <a href="https://link.segmentfault.com/?enc=YaKcvpns%2F2vqA5bNYcZ2zg%3D%3D.5i7yzju1ETgM6PV3wQMCkBxtu3PUuE94HafTactQ7oqzYxWgcLx7RWzAQgl3f8Ax" rel="nofollow">https://chartjs-doc.abingoal.com</a></p>
</blockquote>
<h3>在<code>react</code>中的使用</h3>
<h4>开始使用</h4>
<pre><code>npm install chart.js --save</code></pre>
<p>在相应的页面中引入 <code>chartjs</code></p>
<pre><code>import Chart from "chart.js"</code></pre>
<p>先写一个小的<code>demo</code></p>
<pre><code class="js">import React from "react";
import ReactDOM from "react-dom";
import Chart from "chart.js";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.renderCanvas()
}
// 作图
renderCanvas = () => {
const myChartRef = this.chartRef.getContext("2d");
new Chart(myChartRef, {
type: "line",
data: {
labels: [1,2,3,4,5],
datasets: [
{
data: [10, 20, 50, 80, 100],
backgroundColor: "rgba(71, 157, 255, 0.08)",
borderColor: "rgba(0, 119, 255, 1)",
pointBackgroundColor: "rgba(56, 96, 244, 1)",
pointBorderColor: "rgba(255, 255, 255, 1)",
pointRadius: 4
}
]
},
options: {
responsive: true,
legend: {
display: false
},
maintainAspectRatio: false
}
});
};
render() {
return (
<div style={{ height: 288 }}>
<canvas id="myChart" ref={ref => (this.chartRef = ref)} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
</code></pre>
<p><img src="/img/bVbv8IV?w=1312&h=704" alt="clipboard.png" title="clipboard.png"></p>
<blockquote><a href="https://link.segmentfault.com/?enc=C%2Bi5QocGtyE6rfnvny3mtw%3D%3D.p9YngyUiOdcMseCtnl4nYxSgYfjt0AbckwUteBpO4RKixRZsE7F1WZ61bHWVGNPZDHsqVxy2zM%2FMLNzbjUU8Tg%3D%3D" rel="nofollow">https://codesandbox.io/embed/...</a></blockquote>
<h3>动态更新的数据</h3>
<pre><code class="js">import React from "react";
import ReactDOM from "react-dom";
import Chart from "chart.js";
let currentChart;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [30, 60, 90, 120, 100]
};
}
componentDidMount() {
this.renderCanvas();
this.renderCurrent();
}
// 作图
renderCanvas = () => {
const myChartRef = this.chartRef.getContext("2d");
new Chart(myChartRef, {
type: "line",
data: {
labels: [1, 2, 3, 4, 5],
datasets: [
{
data: [10, 20, 50, 80, 100],
backgroundColor: "rgba(71, 157, 255, 0.08)",
borderColor: "rgba(0, 119, 255, 1)",
pointBackgroundColor: "rgba(56, 96, 244, 1)",
pointBorderColor: "rgba(255, 255, 255, 1)",
pointRadius: 4
}
]
},
options: {
responsive: true,
legend: {
display: false
},
maintainAspectRatio: false
}
});
};
renderCurrent = () => {
const { data } = this.state;
const currentCharttemp = this.currentChart.getContext("2d");
if (typeof currentChart !== "undefined") {
currentChart.destroy();
}
currentChart = new Chart(currentCharttemp, {
type: "line",
data: {
labels: [1, 2, 3, 4, 5],
datasets: [
{
data: data,
backgroundColor: "rgba(71, 157, 255, 0.08)",
borderColor: "rgba(0, 119, 255, 1)",
pointBackgroundColor: "rgba(56, 96, 244, 1)",
pointBorderColor: "rgba(255, 255, 255, 1)",
pointRadius: 4
}
]
},
options: {
responsive: true,
legend: {
display: false
},
maintainAspectRatio: false
}
});
};
render() {
return (
<div>
<canvas id="myChart" ref={ref => (this.chartRef = ref)} />
<br />
<button
onClick={()=>
this.setState({ data: [200, 500, 20, 50, 100] }, this.renderCurrent)
}
>
更新数据
</button>
<canvas id="currentChart7" ref={ref => (this.currentChart = ref)} />
</div>
);
}
}</code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=4BwKG%2FVj3hwKjhFW%2FJhlHg%3D%3D.iDZh2Obw2euhvI86iu9JeO09HX%2F8QyJsNqGG%2Fd28XTIJfisEqpEdv0E%2FTs%2BHKgi%2F5OTo64ppHvj01lGA22Xf9HQpOB%2BKQ1rGF%2BYHDYuFD8c%3D" rel="nofollow">https://blog.bitsrc.io/custom...</a></blockquote>
codereview 思考和前端规范_023
https://segmentfault.com/a/1190000019803458
2019-07-18T17:03:32+08:00
2019-07-18T17:03:32+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>codereview 思考</h2>
<ul>
<li>提升代码质量</li>
<li>代码复盘</li>
<li>有利于规范的落地</li>
<li>对业务的理解加深</li>
<li>表达沟通能力的增强</li>
<li>相互学习</li>
<li>仪式感</li>
</ul>
<h2>前端代码规范</h2>
<blockquote>Airbnb 代码规范 <a href="https://link.segmentfault.com/?enc=Q4MD%2BlMD1xOCEvF6shGyQQ%3D%3D.5MdFS%2BMXMFC1pqn9boD5vkQOlDtnuOuLaGkuQg9YCXI9qWwDLBmdCv29uz5CBkfG" rel="nofollow">https://github.com/airbnb/jav...</a><p>eslint 代码检查 <a href="https://link.segmentfault.com/?enc=f6k5W7F4GKBVEyhU%2FPp7%2FQ%3D%3D.HXAXAv%2FiTUqmUJLWSHhJY4NFYXCxt%2BHLSF91TdRPnk0%3D" rel="nofollow">https://cn.eslint.org</a></p>
<p>korofileheader 注释生成 <a href="https://link.segmentfault.com/?enc=uTh0jIpILd%2Bi%2F0In1QLwcA%3D%3D.6YpHvt1W7cNayq1qcKxXxo%2FGRxr%2FzzSMgRt2gFVVELiD2JsW27wRJ%2BRN15e7zqSEsRcig%2BN4kCp0hsBsFKTEYMPHIQvuIb984Ho3majdtAU%3D" rel="nofollow">https://marketplace.visualstu...</a></p>
<p>凹凸实验室代码规范 <a href="https://link.segmentfault.com/?enc=gNjV4FXWZvh%2BBVhh72%2FaTg%3D%3D.%2BYdGpfIAenIudRoTwGia0%2Bbl4XcT9atPLPE8NFHALjI8wLKZp4qMq0jwU%2Bcf6fce" rel="nofollow">https://guide.aotu.io/index.html</a></p>
<p>react 代码规范 <a href="https://link.segmentfault.com/?enc=HFVkXE%2BFMQzWlsaMG12TVA%3D%3D.LLtBEsy4lnCw0w7LXJDFetztk0G9sNLFnBFnRJgWCePUeZNC%2FPvQc9Y0b%2B%2B%2BvvZuVgCxpGfUXYS8iFgOzKEkbg%3D%3D" rel="nofollow">https://github.com/airbnb/jav...</a></p>
<p>vue 代码规范 <a href="https://link.segmentfault.com/?enc=1RoGLfP4WKKxTyx1U%2BU8GA%3D%3D.FPB1Lweq7Wy39H1ZwwstYEj6%2B6gBx%2FkuldlD6CyRtMPy%2Fpl0fK%2BJ7hGwEp0eP0FM" rel="nofollow">https://cn.vuejs.org/v2/style...</a></p>
</blockquote>
<h3>命名规则</h3>
<pre><code>Pascal Case 大驼峰式命名法:首字母大写。eg:StudentInfo、UserInfo、ProductInfo
Camel Case 小驼峰式命名法:首字母小写。eg:studentInfo、userInfo、productInfo
</code></pre>
<h3>函数命名</h3>
<table>
<thead><tr>
<th>动词</th>
<th>含义</th>
<th>返回值</th>
</tr></thead>
<tbody>
<tr>
<td>can</td>
<td>判断是否可执行某个动作(权限)</td>
<td>函数返回一个布尔值。true:可执行;false:不可执行</td>
</tr>
<tr>
<td>has</td>
<td>判断是否含有某个值</td>
<td>函数返回一个布尔值。true:含有此值;false:不含有此值</td>
</tr>
<tr>
<td>is</td>
<td>判断是否为某个值</td>
<td>函数返回一个布尔值。true:为某个值;false:不为某个值</td>
</tr>
<tr>
<td>get</td>
<td>获取某个值</td>
<td>函数返回一个非布尔值</td>
</tr>
<tr>
<td>set</td>
<td>设置某个值</td>
<td>无返回值、返回是否设置成功或者返回链式对象</td>
</tr>
<tr>
<td>load</td>
<td>加载某些数据</td>
<td>无返回值或者返回是否加载完成的结果</td>
</tr>
</tbody>
</table>
<h3>注释规范</h3>
<h4>文件顶部注释</h4>
<pre><code>/*
* @description:
* @Author: xiaoping.zhang
* @Date: 2019-02-17 10:22:28
* @LastEditors: xiaoping.zhang
* @LastEditTime: 2019-02-17 10:22:28
*/
</code></pre>
<h4>代码片段注释</h4>
<pre><code>/**
* @Author: xiaoping.zhang
* @description:
* @return:
* @Date: 2019-07-17 22:35:35
*/</code></pre>
<h3>类型</h3>
<h4>基本类型</h4>
<ul>
<li>字符串</li>
<li>数值</li>
<li>布尔类型</li>
<li>null</li>
<li>undefined</li>
</ul>
<pre><code class="js">const foo = 1
let bar = foo
bar = 9
console.log(foo, bar) // 1, 9</code></pre>
<h4>复杂类型</h4>
<ul>
<li>object</li>
<li>array</li>
<li>function</li>
</ul>
<pre><code class="js">const foo = [1, 2, 3]
const bar = foo
bar[0] = 9
console.log(foo[0], bar[0]) // 9, 9</code></pre>
<h3>引用</h3>
<ul><li>对所有引用都使用 <code>const</code>,不要使用 <code>var</code>
</li></ul>
<pre><code class="js">// bad
var a = 1
var b = 2
// good
const a = 1
const b = 2</code></pre>
<ul><li>如果引用是可变动的,则使用<code>let</code>
</li></ul>
<pre><code class="js">// bad
var count = 1
if (count < 10) {
count += 1
}
// good
let count = 1
if (count < 10) {
count += 1
}</code></pre>
<h3>对象</h3>
<ul><li>请使用字面量值创建对象</li></ul>
<pre><code class="js">/ bad
const a = new Object{}
// good
const a = {}</code></pre>
<ul><li>别使用保留字作为对象的键值,这样在 IE8 下不会运行</li></ul>
<pre><code class="js">// bad
const a = {
default: {}, // default 是保留字
common: {}
}
// good
const a = {
defaults: {},
common: {}
}</code></pre>
<ul><li>请使用对象方法的简写方式</li></ul>
<pre><code class="js">const job = 'FrontEnd'
// bad
const item = {
job: job
}
// good
const item = {
job
}
</code></pre>
<p>-对象属性值的简写方式要和声明式的方式分组</p>
<pre><code class="js">onst job = 'FrontEnd'
const department = 'JDC'
// bad
const item = {
sex: 'male',
job,
age: 25,
department
}
// good
const item = {
job,
department,
sex: 'male',
age: 25
}</code></pre>
<h3>数组</h3>
<ul><li>请使用字面量值创建数组</li></ul>
<pre><code class="js">// bad
const items = new Array()
// good
const items = []</code></pre>
<ul><li>向数组中添加元素时,请使用 <code>push</code> 方法</li></ul>
<pre><code class="js">const items = []
// bad
items[items.length] = 'test'
// good
items.push('test')</code></pre>
<ul><li>使用拓展运算符 <code>...</code> 复制数组</li></ul>
<pre><code class="js">// bad
const items = []
const itemsCopy = []
const len = items.length
let i
// bad
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i]
}
// good
itemsCopy = [...items]</code></pre>
<p>-使用数组的 <code>map</code> 等方法时,请使用 <code>return</code> 声明,如果是单一声明语句的情况,可省略 return</p>
<pre><code class="js">// good
[1, 2, 3].map(x => {
const y = x + 1
return x * y
})
// good
[1, 2, 3].map(x => x + 1)
// bad
const flat = {}
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item)
flat[index] = flatten
})
// good
const flat = {}
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item)
flat[index] = flatten
return flatten
})
// bad
inbox.filter((msg) => {
const { subject, author } = msg
if (subject === 'Mockingbird') {
return author === 'Harper Lee'
} else {
return false
}
})
// good
inbox.filter((msg) => {
const { subject, author } = msg
if (subject === 'Mockingbird') {
return author === 'Harper Lee'
}
return false
})</code></pre>
<h3>解构赋值</h3>
<ul><li>当需要使用对象的多个属性时,请使用解构赋值</li></ul>
<pre><code class="js">// bad
function getFullName (user) {
const firstName = user.firstName
const lastName = user.lastName
return `${firstName} ${lastName}`
}
// good
function getFullName (user) {
const { firstName, lastName } = user
return `${firstName} ${lastName}`
}
// better
function getFullName ({ firstName, lastName }) {
return `${firstName} ${lastName}`
}</code></pre>
<ul><li>当需要使用数组的多个值时,请同样使用解构赋值</li></ul>
<pre><code class="js">const arr = [1, 2, 3, 4]
// bad
const first = arr[0]
const second = arr[1]
// good
const [first, second] = arr</code></pre>
<ul><li>函数需要回传多个值时,请使用对象的解构,而不是数组的解构</li></ul>
<pre><code class="js">// bad
function doSomething () {
return [top, right, bottom, left]
}
// 如果是数组解构,那么在调用时就需要考虑数据的顺序
const [top, xx, xxx, left] = doSomething()
// good
function doSomething () {
return { top, right, bottom, left }
}
// 此时不需要考虑数据的顺序
const { top, left } = doSomething()</code></pre>
<h3>字符串</h3>
<ul><li>字符串统一使用单引号的形式<code>' '</code>
</li></ul>
<pre><code class="js">// bad
const department = "JDC"
// good
const department = 'JDC'
</code></pre>
<ul><li>字符串太长的时候,请不要使用字符串连接符换行<code>\</code>,而是使用<code> +</code>
</li></ul>
<pre><code class="js">const str = '前端js规范' +
'前端js规范' +
'前端js规范前端js规范前端js规范'
</code></pre>
<ul><li>程序化生成字符串时,请使用模板字符串</li></ul>
<pre><code class="js">const test = 'test'
// bad
const str = ['a', 'b', test].join()
// bad
const str = 'a' + 'b' + test
// good
const str = `ab${test}`
</code></pre>
<h3>函数</h3>
<ul><li>请使用函数声明,而不是函数表达式</li></ul>
<pre><code class="js">/ bad
const foo = function () {
// do something
}
// good
function foo () {
// do something
}</code></pre>
<ul><li>不要在非函数代码块中声明函数</li></ul>
<pre><code class="js">// bad
if (isUse) {
function test () {
// do something
}
}
// good
let test
if (isUse) {
test = () => {
// do something
}
}</code></pre>
<ul><li>不要使用 <code>arguments</code>,可以选择使用 <code>...</code>
</li></ul>
<pre><code>// bad
function test (opts) {
opts = opts || {}
}
// good
function test (opts = {}) {
// ...
}</code></pre>
<h3>模块</h3>
<ul><li>使用标准的 <code>ES6</code> 模块语法 <code>import</code> 和 <code>export</code>
</li></ul>
<pre><code class="js">// bad
const util = require('./util')
module.exports = util
// good
import Util from './util'
export default Util
// better
import { Util } from './util'
export default Util</code></pre>
<ul><li>不要使用 <code>import</code> 的通配符 <code>*</code>,这样可以确保你只有一个默认的 <code>export</code>
</li></ul>
<pre><code class="js">// bad
import * as Util from './util'
// good
import Util from './util'</code></pre>
<h3>迭代器</h3>
<ul><li>不要使用 <code>iterators</code>
</li></ul>
<pre><code>const numbers = [1, 2, 3, 4, 5]
// bad
let sum = 0
for (let num of numbers) {
sum += num
}
// good
let sum = 0
numbers.forEach(num => sum += num)
// better
const sum = numbers.reduce((total, num) => total + num, 0)</code></pre>
VS code-前端配置_022
https://segmentfault.com/a/1190000019684911
2019-07-06T19:10:41+08:00
2019-07-06T19:10:41+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>VS code-前端配置</h2>
<p><img src="/img/bVbuK6l?w=1224&h=999" alt="clipboard.png" title="clipboard.png"></p>
<h3>Chinese (Simplified) Language Pack for Visual Studio Code</h3>
<p>简体中文插件,一般会自动识别你的环境,自动提示是否需要简体中文的语言包。要是没有提示,就在扩展搜索一下即可。</p>
<h3>Settings Sync</h3>
<p><a href="https://link.segmentfault.com/?enc=7Fq7QMCH8Zm7sHVb3Sdyhg%3D%3D.snA%2B37wvEF86Bi%2FCMyIlADCNASSSvJqZg0UkZVHp%2BVawurFqldEK9VAk3ahS8Ghfsg5jPO3t2rpL4kI60lr%2Bp3lK49Bd65ts5yTqk4AW394%3D" rel="nofollow">Settings Sync</a></p>
<p>使用插件将目前配置保存到GitHub上,以后只需要从GitHub上获取,就可以一次性安装插件配置信息。</p>
<p>具体怎么配置还需要搜一下网上的配置教程,这样就不会换电脑,或者重装一下vscode 还需要重新配置。强力推荐。。。</p>
<blockquote>
<a href="https://link.segmentfault.com/?enc=YRf1MgzHD89xkcWAuzFU%2BQ%3D%3D.ejjhi53HX4jGwAx4Zo%2FE%2BB8xBiOsBicpPs%2FznLxUjJLAD8AtXKBTJBLq1GAZtIzz" rel="nofollow">https://juejin.im/post/5b9b5a...</a><br><a href="https://segmentfault.com/a/1190000010648319">https://segmentfault.com/a/11...</a>
</blockquote>
<h3>Auto Rename Tag</h3>
<p><a href="https://link.segmentfault.com/?enc=uhuQR1bZJ%2F0dLy4tCkUUjw%3D%3D.BmgQDsjggOrJXbbOAdXMzwKLsm4BHgzmslCkrNG507XgTSf%2BPq4XlVCuF5p4P2ZwVYnb%2Fsm8dqjDPuClxlzRGD4mQk%2Fav0UAZ3XDZ5o7C5F0YDBsEQ%2Bzq5oLf%2FApnyHR" rel="nofollow">Auto Rename Tag</a></p>
<p>如题 自动重命名标签 对于前端来说还是很必要的</p>
<h3>Auto Close Tag</h3>
<p><a href="https://link.segmentfault.com/?enc=LIavzM3u9sd1Smpv2INhtg%3D%3D.cfDMW6%2BNEKyhI8pNdxWRVkFd%2Bk68N7AxHxTViGHyFpk68NL4xleJhm8UkJutHcVqz7c%2BoBLmIGD%2FXU4BOyEGCERzoztyVD%2FQ4DmhA5WxmlFKkG2kt%2BLb4vQhV5gfVKrU" rel="nofollow">Auto Close Tag</a></p>
<p>自动闭合标签</p>
<h3>ES7 React/Redux/GraphQL/React-Native snippets</h3>
<p><a href="https://link.segmentfault.com/?enc=JuHjFteyN%2BVttvI8YW63kw%3D%3D.a9W0CbE40BzOe7GG%2F8tjEt%2Fc%2FMb%2FusxZUK4PsSa7IrdaxDehCtWftgDnkB1iEjAw1k5KW4eOR1HkvgeBAadjvFCmDkN%2B52Vx9g9UCE3Xgy%2FSM9zXsfXOgQrRK3X9Hmmq" rel="nofollow">ES7 React/Redux/GraphQL/React-Native snippets</a></p>
<p>对于用vscode 写react方便跟多 不过一些快捷键的使用需要记住熟能生巧 多用用就记住的了;</p>
<p>具体的快捷键 可以看插件的说明 很详细的表格:</p>
<p><img src="/img/bVbuK6p?w=610&h=890" alt="clipboard.png" title="clipboard.png"></p>
<h3>Vue 2 Snippets</h3>
<p><a href="https://link.segmentfault.com/?enc=JH7a19OrO3ihnV3OoIHj%2FA%3D%3D.yp78bI9wl7cGpL1cAw3V208lzdTriKFtoMzWuP48Kl8p3XM0FccpOAPcuGw%2BikbCV9wrMvQsl%2F1h0EU6CC3GvP4lDFppFo8FUKvOTJZJZNI%3D" rel="nofollow">Vue 2 Snippets</a></p>
<p>vue 提高coding 效率插件</p>
<h3>Vetur</h3>
<p><a href="https://link.segmentfault.com/?enc=LR3qbiWu5TdjZPstiffN%2BQ%3D%3D.yTflbbEqC0RF0P7t1q4HSZkDdVBcWX3CDTdnJ7ArLpYfn8Gr3crku0Xr72ttA3pkDRZUARh1V8%2B9CrVmwmc3JDfTFRL2Msq5hx1R9UlnmS4%3D" rel="nofollow">Vetur</a></p>
<p>vue 必备插件</p>
<h3>ESLint</h3>
<p><a href="https://link.segmentfault.com/?enc=GEu%2B7GERWiCHbKKliy6Kgg%3D%3D.sL%2FGIm%2FStY1ga1nLXndU6CBzELu0aBUQ7blp0M9UjTAGorjsqj0ZyAsOzQejRHQfST2gbQWCHClvpgahlc2GFL8mjFR8gmKFeHqyIA1aFW4%3D" rel="nofollow">ESLint</a></p>
<p>语法检查。前端必备了,不过还需要根据项目配置<code>eslintrc</code>配置一下,否则有时候全是警告,具体为文档和配置可以看官方文档</p>
<blockquote><a href="https://link.segmentfault.com/?enc=iQ24HJCgkPlu6%2Fkm6ztQWg%3D%3D.ohimqUMfDY6HB6PLmfXEENRP7Nwoa%2B4phgLIP%2BTKlpi3RTuHOLaJAAno7kdjfxoj2XhsDC06pd5Nxb45982pQQ%3D%3D" rel="nofollow">https://eslint.org/docs/user-...</a></blockquote>
<h3>JavaScript (ES6) code snippets</h3>
<p><a href="https://link.segmentfault.com/?enc=19xCYN8TQfCIHZOOolg3YA%3D%3D.BWtSazwbtQBFdB5H4SZQao53c%2FquWf3Db1HoCqVaIUAKqirjjWRZgwlUc1XMUCBYJHyeAe79qRy7HD98HENjQVxtenb5izhKYeDihX9iYd8%3D" rel="nofollow">JavaScript (ES6) code snippets</a></p>
<p>和刚才的 <code>ES7 React/Redux/GraphQL/React-Native snippets</code> 比较类似,这个主要针对的是<code>ES6</code>;</p>
<p><img src="/img/bVbuK6u?w=744&h=547" alt="clipboard.png" title="clipboard.png"></p>
<h3>HTML Boilerplate</h3>
<p><a href="https://link.segmentfault.com/?enc=JLrVussTRzYBdxImouKHDg%3D%3D.ON5XcUwp15bydTguCjDPKXm87OwHdNyypeQF1J6bdYD0lIJuo7Tbzu96fsRWcq2w%2BHl4rudiksAURaJX8MrllGI1OQvgG3UgBPzXQocCilwLAFR5pif9UwWCTw0nsqAe" rel="nofollow">HTML Boilerplate</a></p>
<p><code>HTML</code> 模板,新建文件的时候 用的上,一些头部标签防止出错</p>
<h3>Git History</h3>
<p><a href="https://link.segmentfault.com/?enc=ov%2B5OSW4KZ4kJVLzLy5tAw%3D%3D.QNXWGCYUTI3pbfJrl6H4hdpuOcyXKO21GWTmgxp03p6JdWqanxHMrYdfBiUyfxNpHMB8Q4B4fB2HGGCgZ%2FU9UWZHkJkXE1JwTtSGAifUSo4%3D" rel="nofollow">Git History</a></p>
<p>查看<code>git</code>历史的插件,很实用必备。</p>
<h3>GitLens — Git supercharged</h3>
<p><a href="https://link.segmentfault.com/?enc=Payd7nkWD0Q5MnXWZSQdoA%3D%3D.waD7DWcXVAFfgAy22wiPVQ8zoa%2F6tt3GP4FQnHYRvuMMJblEUbSgqh9pDJ1C8gXArnT9jdNqlsquPEmaygbjsgiApSbHhxS%2FOfENyj%2BFsUE%3D" rel="nofollow">GitLens</a></p>
<p>查看文件内代码提交历史的插件</p>
<h3>gitignore</h3>
<p><a href="https://link.segmentfault.com/?enc=w8ilm9y0%2F7tILXkC3bkQZg%3D%3D.itnRvYqkAMv26kIjLl9yWrzuu0L9wcjZaAU%2BQHCQ3r23ZIFEXGNPPH8tHaDD2sVgADRQ%2F5piOs9DJg%2Fopk34zdBXmH476dSSzOy1CPybzS4%3D" rel="nofollow">gitignore</a></p>
<p>快速把文件加入<code>gitignore</code>,右键选择一下即可;</p>
<h3>koroFileHeader</h3>
<p><a href="https://link.segmentfault.com/?enc=%2FVMdzCMdNGzLlEgAB57R7w%3D%3D.wS1hBo1QrNHkyPGOeiAzwz71ri2eIWujdasyKSBhctbbCLCNkzJa1lPHqKfZWesd8M%2FsRFBzvyIWcphpfKW7V72Oe8OhbAwuUK8LJJyi9rc%3D" rel="nofollow">koroFileHeader</a></p>
<blockquote>在vscode中用于生成文件头部注释和函数注释的插件,经过多版迭代后,插件:支持所有主流语言,功能强大,灵活方便,文档齐全,食用简单!觉得插件不错的话</blockquote>
<p>需要配置一下。<a href="https://link.segmentfault.com/?enc=i%2Bs7QBDRA4F7Bxl4h6P2CA%3D%3D.qPZHjZvzYIo8i%2B721waMWh2ayuZtjW4Rg4txXYs6i%2Ba1Yos0ao8VEUnJgLBrjbSTuJpieS1Q3w6iqroS%2BDdFdM3SzrxsCX6LhAi7HKSfXxI%3D" rel="nofollow">传送门</a></p>
<h3>VS Color Picker</h3>
<p><a href="https://link.segmentfault.com/?enc=KtU5wN%2FmYayegG5PR1zOwg%3D%3D.S7Qnuq3JAqw%2FUhShJLvADf7VgnL%2Fwcyw0FUYjAzFmLZPHe8RKGBMuSx2aZU20ZT1zh%2BTwqmd8zA2UWDlfPFC1ccI3wR%2BOlUk6uYZd52VOGs%3D" rel="nofollow">VS Color Picker</a><br>写样式文件的时候必备</p>
<h3>Prettier - Code formatter</h3>
<p><a href="https://link.segmentfault.com/?enc=Y56BHsx%2F6TiHh3CvLVs0OA%3D%3D.Nh3ZP%2FPielCPAYMO4X4zTREnqzACxR%2FCUO3WaaoF0P5im69QewDKMRFQhXdRvJAiAUmGbKa8n1Jfv2FlInT8GxMD3RLRi1eaVULkTPaJW9E%3D" rel="nofollow">Prettier - Code formatter</a></p>
<p>格式化代码插件 <br>配置文件说明:</p>
<blockquote><a href="https://link.segmentfault.com/?enc=fs%2B35s16bACLWAfyeWCWUg%3D%3D.S9AM0FTxmv7nj%2BNH0E8YI%2F%2Fa547Qx2kK%2BXNSUGWbmaUYmSWzQnifLvmzffh%2BD4cB" rel="nofollow">https://prettier.io/docs/en/c...</a></blockquote>
<h3>Sass</h3>
<p><a href="https://link.segmentfault.com/?enc=zv37Xu7RSFk%2F%2BsJt4PAPoQ%3D%3D.%2BhigApy6cYefNuYlBuvX0F3%2BSL7Cp638WBGU7WXZNt%2FqobrXzq0kmW%2Fzgcus4wDJq2EGGvh2ZXviMfVesR1NqhPE7n8jw88a52j8OsuLrHY%3D" rel="nofollow">Sass</a></p>
<h3>WakaTime</h3>
<p><a href="https://link.segmentfault.com/?enc=DN53mz9%2FuX8PQVWu%2FYAtyA%3D%3D.7aQonA9aNxbRS1hPWRHIW02MpkBMPYtsr59Ws45kFQE5YV5%2FZLGTY33WInWS4uQY1htrh50C19VM3qfI1rbZ76zYqt2pnRxJ3SZWackqM7s%3D" rel="nofollow">WakaTime</a></p>
<p>可以收集<code>coding</code> 工作强度,还会区分<code>git</code> 项目</p>
<h3>超越鼓励师</h3>
<p><a href="https://link.segmentfault.com/?enc=dTNyVOOOuwnDcSTBORiZ8w%3D%3D.zAWgiAJt9esLRRvoHQtelLyfjrZuNsyVHzKcji0w%2BqUhClskfDuYpZUvhq31IfO7L4mZMRlGMqakxr2EX9KZ%2BzuFwmJeZJEpriUIhfVSObY%3D" rel="nofollow">超越鼓励师</a></p>
<blockquote>超越鼓励师<br>在 VS Code 中连续写代码一小时(时间可配置),会有杨超越提醒你该休息啦~</blockquote>
<p>提醒自己定时起来活动一下,不要久坐不动;</p>
<h3>Power Mode</h3>
<p><a href="https://link.segmentfault.com/?enc=GJ6ek040Bo5McizpxF%2F5EQ%3D%3D.gH%2FwsNZK7V2%2BJ0dVaqv2Z9lFnqmRmm5wRiahLK%2FZl8sFcSKj3kh4HWzIbiyovBD8HrWvoM4AsuTLv9fu%2BJGStUh18Lyxvu%2BcqrRlEGycB6E%3D" rel="nofollow">Power Mode</a></p>
<p>配置写代码的输入效果,</p>
<h3>Material Icon Theme</h3>
<p><a href="https://link.segmentfault.com/?enc=Pfx1BfmqdexEjUUVlRvMhQ%3D%3D.B2KFy1dCiIc6dKf2rVi64gS5KAlkd7IJGV7g6FycyoHpeVIMqPNU9fFYCq%2FtMuShZRfnf18ikUWo9WBrcMAqDlZGXdmgJI2dKRwjs8LxCmc%3D" rel="nofollow"> Material Icon Theme</a></p>
<p>美化文件图标的插件</p>
<p><img src="/img/remote/1460000019684915" alt="image" title="image"></p>
<h3>Palenight Theme</h3>
<p>主题插件</p>
<p><a href="https://link.segmentfault.com/?enc=2RLo0y0ZkAYYbd99cLMngQ%3D%3D.JunxZkyTf1A6SU6Uxqv2tSBVwwgx5%2FvQ%2B%2FyPkxl6rMxJl2%2BTu5VqVpXZLEnkNx4a3FRCZ4Y7N5l09uh82Pv752GKTorTVvMMOfT88zVFw5511fnxbklYAWvzWlgnqaZB" rel="nofollow">Palenight Theme</a></p>
<h3>Eva Theme</h3>
<p><a href="https://link.segmentfault.com/?enc=odalV3aWFMtT4v50GgpN6A%3D%3D.yahZxl7%2Fd5V%2FtDsEsHX11A2TRfBDNs%2F9l7A8YCBv%2Fwm5lFU1gvWc%2FLdkrBntbbUtoQDWc%2F%2F21pO59qbpSBmM8kpnbe7eYiSVTgmc7%2Fiuu8M%3D" rel="nofollow">Eva Theme</a></p>
DOM和CSS渲染过程摘抄-021
https://segmentfault.com/a/1190000019580655
2019-06-25T18:18:50+08:00
2019-06-25T18:18:50+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>DOM和CSS渲染过程</h2>
<h2>DOM</h2>
<pre><code class="html">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
</html></code></pre>
<p>一个简单的html页面如上所示。</p>
<p>DOM有两个概念:</p>
<ul>
<li>解析</li>
<li>渲染</li>
</ul>
<h3>DOM解析</h3>
<p>DOM解析:就是把你所写的各种html标签,生成一个DOM TREE,可以认为就是生成了一个最原始的页面,一点样式都没有,毫无CSS修饰。</p>
<p>DOM渲染:浏览器会把本身默认的样式+用户自己写得样式整合到一起,形成一个CSS TREE,而DOM渲染就是指DOM TREE 和 CSS TREE 结合到一起,生成一个Render TREE,呈现出一个带有样式的页面。</p>
<p><img src="/img/bVbujZr?w=1550&h=740" alt="clipboard.png" title="clipboard.png"></p>
<p>1)浏览器会解析三个东西:</p>
<p>一个是HTML/SVG/XHTML,事实上,Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。<br>CSS,解析CSS会产生CSS规则树。<br>Javascript,脚本,主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.<br>2)解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意:</p>
<p>Rendering Tree 渲染树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在渲染树中了。<br>CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。<br>然后,计算每个Frame(也就是每个Element)的位置,这又叫layout和reflow过程。<br>3)最后通过调用操作系统Native GUI的API绘制。</p>
<h2>线程</h2>
<p>js 是单线程,但是浏览器是多线程的。</p>
<p>浏览器会有不同的线程,比如说</p>
<ul>
<li>GUI 渲染线程</li>
<li>JS 线程</li>
<li>定时器触发线程 (setTimeout)</li>
<li>浏览器事件线程 (onclick)</li>
<li>http 异步线程</li>
<li>.....</li>
</ul>
<p>上面的几个线程,在同一个瞬间,只有一个线程起作用,也就是互斥的。比如说浏览器在执行GUI 渲染线程呢,那么其他的4个进程,都处于挂起的状态,在队列里面呆着。<br>DOM的渲染对应的就是GUI渲染进程;JS的执行对应的就是JS线程;所以,DOM的渲染与JS代码的运行,在同一瞬间只能有一个执行!</p>
<h2>阻塞</h2>
<p>阻塞XXX是指让XXX暂停了。比如JS的执行阻塞DOM解析,就是</p>
<p>DOM解析 --> JS执行(此时DOM解析暂停) --> JS执行完毕 --> DOM继续解析</p>
<h2>DOM与CSS</h2>
<p>先看它俩之间的关系,也就是分析CSS的加载对DOM的解析和渲染的影响。<br>很明显,DOM自己在那解析DOM TREE 和 css样式有啥关系啊,所以css不影响DOM解析。<br>也很明显,DOM渲染就是要生成样式呢,肯定和css有关系啊,所以css影响DOM渲染。<br>结论:</p>
<ul>
<li>css的加载不会阻塞DOM的解析</li>
<li>css的加载会阻塞DOM的渲染</li>
</ul>
<h2>DOM与JS</h2>
<p>JS(加载和执行) 都会阻塞 DOM 的解析,因为JS中可能会对DOM进行操作,可能改变DOM的结构,所以JS的加载和执行是会阻塞DOM解析的。<br>JS(加载和执行) 都会阻塞 DOM 的渲染,同上面一样,因为JS中可能对样式进行操作。<br>注: html中每遇到< script >标签,页面就会重新渲染一次,因为要保证标签中的JS代码拿到的都是最新的样式。<br>结论:</p>
<ul>
<li>JS的加载和执行会阻塞DOM的解析</li>
<li>JS的加载和执行会阻塞DOM的渲染</li>
<li>
</li>
</ul>
<h2>CSS与JS</h2>
<p>在线程那里说了,CSS的加载会阻塞JS的执行,因为CSS的渲染GUI线程和JS运行线程互斥。<br>但是CSS不会阻塞JS的加载(浏览器可以预先扫描并下载)<br>注1:</p>
<ul><li>CSS、JS之间的加载是否阻塞,这里不考虑,因为现代的浏览器都会预先偷看文档,并且下载。</li></ul>
<p>注2:</p>
<ul><li>这里的JS引入方式不包括async和defer</li></ul>
<p>结论:</p>
<ul><li>CSS的加载阻塞JS的运行,不阻塞JS的加载</li></ul>
<hr>
<p><img src="/img/bVbujZn?w=2046&h=1064" alt="clipboard.png" title="clipboard.png"></p>
<p>Note: 简单提一句,请注意 visibility: hidden 与 display: none 是不一样的。前者隐藏元素,但元素仍占据着布局空间(即将其渲染成一个空框),而后者 (display: none) 将元素从渲染树中完全移除,元素既不可见,也不是布局的组成部分。</p>
<p>下面简要概述了浏览器完成的步骤:</p>
<ul>
<li>处理 HTML 标记并构建 DOM 树。</li>
<li>处理 CSS 标记并构建 CSSOM 树。</li>
<li>将 DOM 与 CSSOM 合并成一个渲染树。</li>
<li>根据渲染树来布局,以计算每个节点的几何信息。</li>
<li>将各个节点绘制到屏幕上。</li>
</ul>
<p>优化关键渲染路径_就是指最大限度缩短执行上述第 1 步至第 5 步耗费的总时间</p>
<blockquote>
<a href="https://link.segmentfault.com/?enc=c5DKcEAoanvcGxvgiaGOQA%3D%3D.PmMOmHtTVsX4vLlJnguI%2BZ7xV%2FgCCzLpduM6GADf4%2BrOr6J4u90Zb%2FMTm1GQUPK%2BlBKqS3KIDExH1rurjAhQ7Q%3D%3D" rel="nofollow">https://developers.google.com...</a><p><a href="https://link.segmentfault.com/?enc=1lOO54MAW01KOTFFMlKrpQ%3D%3D.GLV1y3m142kS%2FHH%2FO%2BAo7nzvCMxnkj4AFPryXHcLg5tRJAhshPWVIa520q7Hy6ZcngC2I9o02LRcGWuHGjXIwRP0WKK4yyrWJrFUERTXerUhed7YrFgSc1sBJVWueOi9lphwowKGadMCQdDrNGHv5OEIBkkvmBOJTsOy6%2B0NYew%3D" rel="nofollow">https://developers.google.com...</a></p>
<p><a href="https://link.segmentfault.com/?enc=aFkNHSEOnJhwx%2BObByZCIA%3D%3D.71WOQznQzQYMbmeBJjnOaYehGvksDIMOlbfCiMPngNrpcqCgXQdkoPlXcgu1hGI7RQhnz2g9PRDxJosrBpJJ%2BlU6XM7%2FpakrhuInTs9k%2Bqels7wP2RKVMxjkah4cnLFJhzuhDFmlVANexBtwUmtJhw%3D%3D" rel="nofollow">https://developers.google.com...</a></p>
</blockquote>
git rebase使用_020
https://segmentfault.com/a/1190000019580641
2019-06-25T18:16:47+08:00
2019-06-25T18:16:47+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>git rebase 使用</h2>
<p><code>rebase</code>:顾名思义 <strong>==变基==</strong> </p>
<p>假设现在从<code>master</code> 分支上,切出一个本地开发分支<code>mydev</code></p>
<pre><code>git checkout -b mydev</code></pre>
<p>这时候<code>master</code>上的<code>git</code>记录是这样子的</p>
<pre><code>A-->B-->C-->D</code></pre>
<p>这个时候另外一个开发人员把ta的dev分支合并到master分支了<br><code>tadev</code></p>
<pre><code>A-->B-->C-->D
|-->E-->F-->G</code></pre>
<ul><li>查看git记录</li></ul>
<pre><code>git lg or git log</code></pre>
<p>实际上现在<code>master</code>上的分支的git 提交记录已经是这样子的了:</p>
<pre><code>A-->B-->C-->D-->E-->F-->G</code></pre>
<p>但是你本地上的分支还是这样的,你分别了提交了 3个 <code>commit</code>记录。</p>
<pre><code> |—->M-->N-->O
A-->B-->C-->D
</code></pre>
<p>这时,你的代码开发完成了,需要合并代码。这时候有两种选择:</p>
<p>直接</p>
<pre><code>git merge </code></pre>
<p>或者</p>
<pre><code> 经过 git rebase 再提交 git merge </code></pre>
<h3>git merge</h3>
<p>如果直接<code>git merge</code>,假设你提交<code>commit</code>的时间和刚才合并进去的其他人的dev分支是有重合的:</p>
<pre><code>A-->B-->C-->D
|-->E |-->F-->G
|-->M-->N |-->O</code></pre>
<p>这样的提交的记录就比较乱,假设现在需要回退代码什么的,就有点小麻烦了,就需要一个文件去回退。对于多人协作的话 就比较需要<code>git rebase</code></p>
<h3>git rebase</h3>
<p>先对<code>mydev</code>分支进行 <code>git rebase</code> 操作</p>
<p>这时候分支的提交记录就会变为如下所示:</p>
<pre><code>A-->B-->C-->D-->E-->F-->G
|-->M-->N-->O</code></pre>
<p>会把<code>mydev</code>分支新增的<code>commit</code>记录新增在master分支最新的后面,这个时候<code>push</code>分支,就需要强制更新你的分支。再提交<code>merge</code>。</p>
<pre><code>git push -f </code></pre>
<p>变基的作用就是修整历史,将分支历史并入主线。</p>
<h4>git rebase 注意事项</h4>
<ul>
<li>确保没有其它分支同时更改同一个文件</li>
<li>
<code>git rebase</code>前需要确保<code>master</code>分支为最新。</li>
<li>变基会修整历史,然后将分支历史并入主线,可以理解成美化过的历史,而合并则可以不修改历史,让分支历史依然独立存在,可以看作原始的历史。</li>
<li>永远不要对已经推到主干分支服务器或者团队其他成员的提交进行变基,我们选择变基还是合并的范围应该在自己当前工作范围内。</li>
<li>
<p>如果<code>git rebase</code>之后提示冲突的话,需要解决冲突:</p>
<ul><li>解决冲突后 add 更改的文件</li></ul>
<pre><code>git add </code></pre>
<ul><li>无需 <code>commit</code>, 继续 <code>rebase</code>
</li></ul>
<pre><code>git rebase --continue</code></pre>
</li>
</ul>
<p><code>git rebase</code>还有一些其他的操作,</p>
<ul><li>放弃<code>git rebase</code>
</li></ul>
<pre><code>git rebase --abort</code></pre>
<ul><li>压缩最近几次提交</li></ul>
<pre><code>git rebase -i HEAD~4 合并最近4次提交</code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=vSrU5CJoYZCwnImsIvGg1Q%3D%3D.ZutZF%2FWh24dL5W1HonYpE%2FidsMeQEPGTkt89lJj1y9OuAAjfhf06sWIWpsthL%2B3FAVmPL7xtt8mf4nPFY0Abcg%3D%3D" rel="nofollow">http://blog.codingplayboy.com...</a></blockquote>
FlexBox学习 弹性布局_019
https://segmentfault.com/a/1190000018663601
2019-03-26T18:51:23+08:00
2019-03-26T18:51:23+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>FlexBox学习 弹性布局</h2>
<p><code>Flexible Box</code> 模型,通常被称为 <code>flexbox</code>,<strong>是一种一维的布局模型</strong>。它给 <code>flexbox</code> 的子元素之间提供了强大的空间分布和对齐能力。</p>
<p>一维布局:一个 <code>flexbox</code></p>
<p><strong>一次只能处理一个维度上的元素布局,一行或者一列</strong>。</p>
<p><img src="/img/bVTiJQ?w=563&h=333" alt="clipboard.png" title="clipboard.png"></p>
<pre><code>flex: auto | initial | none | inherit | [ flex-grow ] || [ flex-shrink ] || [ flex-basis ]
</code></pre>
<h3>FlexBox的两根轴线</h3>
<p>当使用 flex 布局时,首先想到的是两根轴线 — <strong>主轴</strong>和<strong>交叉轴</strong>。主轴由 <code>-direction</code> 定义,另一根轴垂直于它。使用 <code>flexbox</code> 的所有属性都跟这两根轴线有关, 所以有必要在一开始首先理解它。</p>
<h4>主轴 <code>flex-direction</code>
</h4>
<ul><li><code>row</code></li></ul>
<p><img src="/img/bVbqto7?w=1194&h=186" alt="clipboard.png" title="clipboard.png"></p>
<ul><li><code>row-reverse</code></li></ul>
<p><img src="/img/bVbqto9?w=1298&h=242" alt="clipboard.png" title="clipboard.png"></p>
<ul><li><code>column</code></li></ul>
<p><img src="/img/bVbqtpj?w=308&h=750" alt="clipboard.png" title="clipboard.png"></p>
<ul><li><code>column-reverse</code></li></ul>
<p><img src="/img/bVbqtpu?w=358&h=730" alt="clipboard.png" title="clipboard.png"></p>
<pre><code class="html"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>FlexBox学习</title>
<style>
.container {
display: flex;
flex-direction: row;
}
.container-item {
height: 100px;
width: 100px;
margin: 10px;
border: 1px solid #dddddd;
background-image: linear-gradient(-90deg, #3860f4 0%, #5f87f8 100%);
}
</style>
</head>
<body>
<div class="container">
<div class="container-item">1</div>
<div class="container-item">2</div>
<div class="container-item">3</div>
<div class="container-item">4</div>
<div class="container-item">5</div>
<div class="container-item">6</div>
</div>
</body>
</html>
</code></pre>
<h4>交叉轴</h4>
<p>交叉轴垂直于主轴,所以如果你的<code>flex-direction </code>(主轴) 设成了 <code>row </code>或者 <code>row-reverse</code> 的话,交叉轴的方向就是沿着列向下的。</p>
<p>如果主轴方向设成了 <code>column</code> 或者 <code>column-reverse</code>,交叉轴就是水平方向。</p>
<h3>起始线和终止线</h3>
<p><code>flexbox</code> 不会对文档的书写模式提供假设。过去,<code>CSS</code>的书写模式主要被认为是水平的,从左到右的。现代的布局方式涵盖了书写模式的范围,所以不再假设一行文字是从文档的左上角开始向右书写,新的行也不是必须出现在另一行的下面。<br> 如果 <code>flex-direction</code> 是 <code>row</code> ,那么主轴的起始线是左边,终止线是右边。</p>
<p><img src="/img/bVbqtpz?w=522&h=152" alt="clipboard.png" title="clipboard.png"></p>
<blockquote><a href="https://link.segmentfault.com/?enc=THehReM57muNQct1PRUOhg%3D%3D.unCvwgHDdVEoMAsCuHbmkCGL7bWRl7iRyjkIHGdsgVbAJ%2BcdJHVTXOWaINwfJMpDUiTivlsnzsa5yFUgHOdRjfYa6B5jN1I7qPUV7ohtC3LaXtuqDpgd8MBnVtmKMEBnH1VsSeqQmUasVFYt2WiGSA%3D%3D" rel="nofollow">https://developer.mozilla.org...</a></blockquote>
<h3>Flex 容器</h3>
<p><code>CSS</code>采用了 <code>flexbox</code> 的区域就叫做 <code>flex</code> 容器。为了创建 <code>flex</code> 容器,把一个容器的 <code>display</code> 属性值改为 <code>flex</code> 或者 <code>inline-flex</code>。</p>
<p>容器中的直系子元素就会变为 <code>flex</code> 元素。所有<code>CSS</code>属性都会有一个初始值,所以 <code>flex</code> 容器中的所有 <code>flex</code> 元素都会有下列行为:</p>
<ul>
<li>元素排列为一行 (<code>flex-direction</code> 属性的初始值是 <code>row</code>)。</li>
<li>元素从主轴的起始线开始。</li>
<li>元素不会在主维度方向拉伸,但是可以缩小。</li>
<li>元素被拉伸来填充交叉轴大小。</li>
<li>
<code>flex-basis</code> 属性为 <code>auto</code>。</li>
<li>
<code>flex-wrap</code> 属性为 <code>nowrap</code>。</li>
</ul>
<p>这会让你的元素呈线形排列,并且把自己的大小作为主轴上的大小。如果有太多元素超出容器,它们会溢出而不会换行。如果一些元素比其他元素高,那么元素会沿交叉轴被拉伸来填满它的大小。</p>
<h4>用<code>flex-wrap</code>实现多行<code>Flex</code>容器</h4>
<p><code>flexbox</code>是一维模型,但可以使我们的<code>flex</code>项目应用到多行中。 在这样做的时候,应该把每一行看作一个新的<code>flex</code>容器。 任何空间分布都将在该行上发生,而不影响该空间分布的其他行。</p>
<pre><code class="css">.container {
display: flex;
flex-direction: column-reverse;
flex-flow: wrap;
}
.container-item {
height: 100px;
margin: 10px;
width: 40%;
border: 1px solid #dddddd;
background-image: linear-gradient(-90deg, #3860f4 0%, #5f87f8 100%);
}</code></pre>
<p><img src="/img/bVbqtpF?w=2014&h=466" alt="clipboard.png" title="clipboard.png"></p>
<h4>简写属性 <code>flex-flow</code>
</h4>
<p>可以将两个属性 <code>flex-direction</code> 和 <code>flex-wrap</code> 组合为简写属性 <code>flex-flow</code>。第一个指定的值为 <code>flex-direction</code> ,第二个指定的值为 <code>flex-wrap</code>。</p>
<h3>
<code>flex</code> 元素上的属性</h3>
<p>在考虑这几个属性的作用之前,需要先了解一下 布局空白 <code>available space</code> 这个概念。这几个 <code>flex</code> 属性的作用其实就是改变了 <code>flex</code> 容器中的布局空白的行为。同时,布局空白对于 <code>flex</code> 元素的对齐行为也是很重要的。</p>
<p>假设在 1 个 500px 的容器中,我们有 3 个 100px 宽的元素,那么这 3 个元素需要占 300px 的宽,剩下 200px 的布局空白。在默认情况下, flexbox 的行为会把这 200px 的空白留在最后一个元素的后面。</p>
<blockquote><img src="/img/bVbqtpL?w=528&h=198" alt="clipboard.png" title="clipboard.png"></blockquote>
<p><strong>如果期望这些元素能自动地扩展去填充满剩下的空白,那么我们需要去控制布局空白在这几个元素间如何分配,这就是元素上的那些 <code>flex</code> 属性要做的事。</strong></p>
<h4>
<code>flex-grow</code> 定义弹性盒子元素的扩展比率。</h4>
<p><code>flex-grow</code> 若被赋值为一个正整数, <code>flex</code> 元素会以 <code>flex-basis</code> 为基础,沿主轴方向增长尺寸。这会使该元素延展,并占据此方向轴上的布局空白<code>(available space)</code>。如果有其他元素也被允许延展,那么会各自占据布局空白的一部分。</p>
<p>如果我们给上例中的所有元素设定 <code>flex-grow</code> 值为1, 容器中的布局空白会被这些元素平分。它们会延展以填满容器主轴方向上的空间。</p>
<p><code>flex-grow</code> 属性可以按比例分配空间。如果第一个元素 <code>flex-grow </code>值为2, 其他元素值为1,则第一个元素将占有<code>2/4</code>(上例中,即为<code>200px 中的 100px</code>), 另外两个元素各占有<code>1/4(各50px)</code>。</p>
<h4>
<code>flex-shrink</code> 定义弹性盒子元素的收缩比率</h4>
<p><code>flex-grow</code>属性是处理<code>flex</code>元素在主轴上增加空间的问题,相反<code>flex-shrink</code>属性是处理<code>flex</code>元素收缩的问题。如果容器中没有足够排列<code>flex</code>元素的空间,那么可以把<code>flex</code>元素<code>flex-shrink</code>属性设置为正整数来缩小它所占空间到<code>flex-basis</code>以下。与<code>flex-grow</code>属性一样,可以赋予不同的值来控制<code>flex</code>元素收缩的程度 —— 给<code>flex-shrink</code>属性<strong>赋予更大的数值可以比赋予小数值的同级元素收缩程度更大。</strong></p>
<p>在计算flex元素收缩的大小时,它的最小尺寸也会被考虑进去,就是说实际上<code>flex-shrink</code>属性可能会和flex-grow属性表现的不一致。</p>
<h4>
<code>flex-basis</code> 定义弹性盒子元素的默认基准值</h4>
<p><code>flex-basis</code> 定义了该元素的布局空白<code>(available space)</code>的基准值。 该属性的默认值是 <code>auto</code> 。此时,浏览器会检测这个元素是否具有确定的尺寸。 在上面的例子中, 所有元素都设定了宽度<code>(width)</code>为<code>100px</code>,所以 <code>flex-basis</code> 的值为<code>100px</code>。</p>
<p>如果没有给元素设定尺寸,<code>flex-basis</code> 的值采用元素内容的尺寸。这就解释了:我们给只要给Flex元素的父元素声明 <code>display: flex</code> ,所有子元素就会排成一行,且自动分配小大以充分展示元素的内容。</p>
<h3>元素间的对齐和空间分配</h3>
<p><code>Flexbox</code>的一个关键特性是能够设置<code>flex</code>元素沿主轴方向和交叉轴方向的对齐方式,以及它们之间的空间分配。</p>
<h4><code>align-items</code></h4>
<p><code>align-items</code> 属性可以使元素在交叉轴方向对齐。</p>
<ul>
<li><code>stretch</code></li>
<li><code>flex-start</code></li>
<li><code>flex-end</code></li>
<li><code>center</code></li>
</ul>
<h4><code>justify-content</code></h4>
<p><code>justify-content</code>属性用来使元素在主轴方向上对齐,主轴方向是通过 <code>flex-direction</code> 设置的方向。初始值是flex-start,元素从容器的起始线排列。但是你也可以把值设置为<code>flex-end</code>,从终止线开始排列,或者<code>center</code>,在中间排列.</p>
<ul>
<li><code>stretch</code></li>
<li><code>flex-start</code></li>
<li><code>flex-end</code></li>
<li><code>center</code></li>
<li><code>space-around</code></li>
<li><code>space-between</code></li>
</ul>
<h5>水平居中</h5>
<pre><code class="css">.container {
display: flex;
justify-content: center;
} </code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=r1tpTpKFn2ySEuajDeiQqA%3D%3D.m%2FeBJ5yW%2FVGdHIFVF660qkB23SNIQMXUYOAOjmgz%2Fwb3YyQnFOOhDNef2n6jO8Q%2B" rel="nofollow">https://www.html.cn/archives/...</a></blockquote>
React-图片输入框-移动端网页_018
https://segmentfault.com/a/1190000018634629
2019-03-24T20:24:14+08:00
2019-03-24T20:24:14+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React-图片输入框-移动端网页</h2>
<ul><li>gitHub地址 觉得有参考价值,点个赞</li></ul>
<blockquote><a href="https://link.segmentfault.com/?enc=yMOPMJGjeBviX2tQ4y05kQ%3D%3D.UJcy47fgJLBJOU7n%2FqOEUBtkp%2FMLmiZFMAZVHN4OXXh5evUDZvVxWVaU1UZqYee%2BpQFANwBUO2Vl86w6uKCLwg%3D%3D" rel="nofollow">https://github.com/xiaopingzh...</a></blockquote>
<h3>目录结构</h3>
<pre><code>.
├── README.md
├── dist
│ ├── bundle.js
│ └── index.html
├── package.json
├── src
│ ├── components
│ │ ├── ErrorPage
│ │ │ ├── ErrorPage.css
│ │ │ └── ErrorPage.js
│ │ ├── Notice
│ │ │ ├── Notice.css
│ │ │ └── Notice.js
│ │ ├── PersonalUploadFileForm
│ │ │ ├── PersonalUploadFileForm.css
│ │ │ └── PersonalUploadFileForm.js
│ │ ├── SuccessPage
│ │ │ ├── SuccessPage.css
│ │ │ └── SuccessPage.js
│ │ └── common
│ │ ├── actions.js
│ │ ├── getUrlpargm.js
│ │ └── utils.js
│ ├── index.html
│ ├── index.js
│ └── page
│ ├── UploadForm.js
│ └── upLoadForm.css
├── webpack.config.js
└── webpack.production.config.js</code></pre>
<h3>上传组件代码</h3>
<p>基于原生的API接口,点击输入输入框 手机上会出现 文件 相机选项 <br>可自行选择拍照上传,<br>为解决微信内置上传的bug 其中<code>accept </code>设置为 <code> accept="image/*"</code></p>
<p><code>loading</code>组件找了一个简单的动效</p>
<pre><code>npm config set '@bit:registry' https://node.bit.dev
npm i @bit/davidhu2000.react-spinners.pulse-loader</code></pre>
<p>来源</p>
<blockquote><a href="https://link.segmentfault.com/?enc=POYDZUomHJl28QBPeJx9OQ%3D%3D.Sx%2FL%2BXrva2NGyBwCctXjyd3iMRwsolosmUJv6eRD5mLqIGcOCGooOHO9KBV9kk%2Bv" rel="nofollow">https://bit.dev/davidhu2000/r...</a></blockquote>
<pre><code class="js">import React from 'react';
import './PersonalUploadFileForm.css';
import toast from '../Notice/Notice';
import PulseLoader from '@bit/davidhu2000.react-spinners.pulse-loader';
class PersonalUploadFileForm extends React.Component {
constructor(props) {
super(props);
this.state = {
filePreviewUrl: this.props.value ? this.props.value.base64 : '',
overSizeModel: false,
};
}
selectFile = e => {
e.preventDefault();
this.refs.fileinput.click();
};
_handleFileChange = e => {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
if (file.type != 'image/jpeg' && file.type != 'image/png') {
toast('请选择图片',"error");
return false;
}
if (file.size > 8000000){
toast('图片太大,请重新选择',"error");
return false;
}
this.setState({
filePreviewUrl: reader.result,
});
this.props.onChange({ name: file.name, base64: reader.result });
};
reader.readAsDataURL(file);
e.target.value = null;
};
render() {
let { filePreviewUrl } = this.state;
let { backimg, textTip, displaybackimg, uploading } = this.props;
return (
<div className="uploadpicture">
<div>
<div className="uploadpictureimg" onClick={this.selectFile}>
<div className="img-preshow">
{uploading ? (
<div
className="img-preshow-loading"
style={{
backgroundImage: `url(${filePreviewUrl})`,
opacity:"0.7"
}}
>
<div style={{ position: 'relative', display: 'inline', lineHeight: '2.62rem' }}>
<PulseLoader size={0.16} margin={'0.2rem'} color="#5f87f8" sizeUnit="rem" />
</div>
</div>
) : (
<img
src={displaybackimg ? filePreviewUrl : backimg}
style={{ width: '100%', height: '100%' }}
/>
)}
</div>
</div>
<div>
<input
type="file"
ref="fileinput"
onChange={this._handleFileChange}
style={{ display: 'none' }}
accept="image/*"
/>
</div>
</div>
{textTip && <span className="textTippict">{textTip}</span>}
</div>
);
}
}
export default PersonalUploadFileForm;
</code></pre>
<ul><li>初试界面</li></ul>
<p><img src="/img/bVbqlAZ?w=528&h=948" alt="clipboard.png" title="clipboard.png"></p>
<p>— 选择图片</p>
<p><img src="/img/bVbqlA4?w=598&h=883" alt="clipboard.png" title="clipboard.png"></p>
<p>-上传失败提示</p>
<p><img src="/img/bVbqlBc?w=485&h=882" alt="clipboard.png" title="clipboard.png"></p>
<ul><li>上传中</li></ul>
<p><img src="/img/bVbqlBF?w=610&h=844" alt="clipboard.png" title="clipboard.png"></p>
<ul><li>上传成功</li></ul>
<p><img src="/img/bVbqlBJ?w=525&h=837" alt="clipboard.png" title="clipboard.png"></p>
<ul><li>微信内置浏览器打开</li></ul>
<p><img src="/img/bVbqlSE?w=1080&h=2160" alt="clipboard.png" title="clipboard.png"></p>
HTML-Location摘抄_017
https://segmentfault.com/a/1190000018530065
2019-03-16T18:05:03+08:00
2019-03-16T18:05:03+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>HTML-Location摘抄</h2>
<p><code>Location</code> 接口表示其链接到的对象的位置<code>URL</code>。所做的修改反映在与之相关的对象上。 <code>Document</code> 和 <code>Window</code> 接口都有这样一个链接的<code>Location</code>,分别通过 <code>Document.location</code>和<code>Window.location</code> 访问。</p>
<h3>属性</h3>
<p><code>Location</code> 接口不继承任何属性,但是实现了那些来自 <code>URLUtils</code> 的属性。</p>
<h4>Location.href</h4>
<p>包含整个URL的一个<code>DOMString</code></p>
<pre><code class="js">// https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/href
console.log(location.href)
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/href</code></pre>
<h4>Location.protocol</h4>
<p>包含URL对应协议的一个<code>DOMString</code>,最后有一个"<code>:</code>"。</p>
<pre><code class="js">// https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/protocol
console.log(location.protocol)
// https:</code></pre>
<h4>Location.host</h4>
<p>包含了域名的一个<code>DOMString</code>,可能在该串最后带有一个"<code>:</code>"并跟上<code>URL</code>的端口号。</p>
<pre><code class="js">//https://developer.mozilla.org:4097/en-US/HTMLHyperlinkElementUtils.host
console.log(location.host)
//developer.mozilla.org:4097</code></pre>
<h4>Location.hostname</h4>
<p>包含<code>URL</code>域名的一个<code>DOMString</code>。</p>
<pre><code class="js">// https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/hostname
console.log(location.hostname)
//developer.mozilla.org</code></pre>
<h4>Location.port</h4>
<p>包含端口号的一个<code>DOMString</code>。</p>
<pre><code class="js">// https://developer.mozilla.org:443/en-US/docs/HTMLHyperlinkElementUtils.port
console.log(location.port)
//'443'</code></pre>
<h4>Location.pathname</h4>
<p>包含URL中路径部分的一个<code>DOMString</code>,开头有一个“<code>/</code>"。</p>
<pre><code class="js">// https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname
console.log(location.pathname)
// /en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname</code></pre>
<h4>Location.search</h4>
<p>包含URL参数的一个<code>DOMString</code>,开头有一个“<code>?</code>”。</p>
<pre><code class="js">// https://developer.mozilla.org/en-US/docs/HTMLHyperlinkElementUtils.search?q=123
console.log(location.search)
//?q=123</code></pre>
<ul><li>获取路由参数</li></ul>
<pre><code class="js">var anchor = document.getElementById("myAnchor");
var queryString = anchor.search; // Returns:'?q=123'
// Further parsing:
let params = new URLSearchParams(queryString);
let q = parseInt(params.get("q")); // is the number 123</code></pre>
<ul><li>获取路由参数 返回一个object</li></ul>
<pre><code class="js">const getUrlPargm = () => {
const url = location.search; // 获取url中"?"符后的字串
const theRequest = new Object();
if (url.indexOf('?') != -1) {
const str = url.substr(1);
let strs = str.split('&');
for (let i = 0; i < strs.length; i++) {
theRequest[strs[i].split('=')[0]] = unescape(strs[i].split('=')[1]);
}
}
return theRequest;
};</code></pre>
<ul><li></ul>
<pre><code class="js">
// 获取指定的URL参数值
//URL:http://www.baidu.com/index?name=liziceshi
//参数:paramName URL参数
//调用方法:getParam("name")
//返回值:liziceshi
function getParam(paramName) {
paramValue = "", isFound = !1;
if (this.location.search.indexOf("?") == 0 && this.location.search.indexOf("=") > 1) {
arrSource = unescape(this.location.search).substring(1, this.location.search.length).split("&"), i = 0;
while (i < arrSource.length && !isFound) arrSource[i].indexOf("=") > 0 && arrSource[i].split("=")[0].toLowerCase() == paramName.toLowerCase() && (paramValue = arrSource[i].split("=")[1], isFound = !0), i++
}
return paramValue == "" && (paramValue = null), paramValue
</code></pre>
<p><img src="/img/bVbpUGl?w=1624&h=168" alt="clipboard.png" title="clipboard.png"></p>
<h4>Location.hash</h4>
<p>包含块标识符的<code>DOMString</code>,开头有一个“<code>#</code>”。</p>
<pre><code class="js">//https://developer.mozilla.org/en-US/docs/HTMLHyperlinkElementUtils.href#youhou
console.log(location.hash);
// #youhou</code></pre>
<h4>Location.username</h4>
<p>包含URL中域名前的用户名的一个<code>DOMString</code>。</p>
<pre><code class="js">//https://anonymous:flabada@developer.mozilla.org/en-US/docs/HTMLHyperlinkElementUtils.username
console.log(location.username);
//anonymous</code></pre>
<h4>Location.password</h4>
<p>包含URL域名前的密码的一个 <code>DOMString</code>。</p>
<pre><code class="js">// Let's <a id="myAnchor" href="https://anonymous:flabada@developer.mozilla.org/en-US/docs/HTMLHyperlinkElementUtils.username"> be in the document
var anchor = document.getElementByID("myAnchor");
var result = anchor.password; // Returns:'flabada';
</code></pre>
<h4>Location.origin 只读</h4>
<p>包含页面来源的域名的标准形式<code>DOMString</code>。</p>
<p>如果在没有首先设置用户名属性的情况下设置,则会静默失败</p>
<pre><code class="js">//https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/origin
console.log(location.origin)
//https://developer.mozilla.org</code></pre>
<ul><li>来自MDN</li></ul>
<pre><code class="js">var url = document.createElement('a');
url.href = 'https://developer.mozilla.org/en-US/search?q=URL#search-results-close-container';
console.log(url.href); // https://developer.mozilla.org/en-US/search?q=URL#search-results-close-container
console.log(url.protocol); // https:
console.log(url.host); // developer.mozilla.org
console.log(url.hostname); // developer.mozilla.org
console.log(url.port); // (blank - https assumes port 443)
console.log(url.pathname); // /en-US/search
console.log(url.search); // ?q=URL
console.log(url.hash); // #search-results-close-container
console.log(url.origin); // https://developer.mozilla.org</code></pre>
<h3>方法</h3>
<p>Location没有继承任何方法,但实现了来自URLUtils的方法。</p>
<h4>Location.assign()</h4>
<p>加载给定URL的内容资源到这个<code>Location</code>对象所关联的对象上。<br><code>Location.assign()</code>方法会触发窗口加载并显示指定的URL的内容。</p>
<p>如果由于安全原因无法执行跳转,那么会抛出一个<code>SECURITY_ERROR</code>类型的<code>DOMException</code>。当调用此方法的脚本来源和页面的<code>Location</code>对象中定义的来源隶属于不同域的时候,就会抛出上述错误。</p>
<p>如果传入了一个无效的URL,则会抛出一个<code>SYNTAX_ERROR</code>类型的<code>DOMException</code>。</p>
<pre><code class="js">// 跳转到Location.reload这篇文章
document.location.assign('https://developer.mozilla.org/zh-CN/docs/Web/API/Location.reload');
</code></pre>
<h4>Location.reload()</h4>
<p>重新加载来自当前 <code>URL</code>的资源。他有一个特殊的可选参数,类型为 <code>Boolean</code>,该参数为true时会导致该方法引发的刷新一定会从服务器上加载数据。如果是 <code>false</code>或没有制定这个参数,浏览器可能从缓存当中加载页面。</p>
<p><code>Location.reload()</code> 方法用来刷新当前页面。该方法只有一个参数,当值为 <code>true</code> 时,将强制浏览器从服务器加载页面资源,当值为<code> false</code> 或者未传参时,浏览器则可能从缓存中读取页面。</p>
<p>该方法在跨域调用(执行该方法的脚本文件的域和 <code>Location</code> 对象所在页面的跨不同)时,将会抛出 <code>DOMException</code> 异常.</p>
<pre><code class="js">object.reload(forcedReload);</code></pre>
<h4>Location.replace()</h4>
<p>用给定的<code>URL</code>替换掉当前的资源。与 <code>assign()</code>方法不同的是用 <code>replace()</code>替换的新页面不会被保存在会话的历史 <code>History</code>中,这意味着用户将不能用后退按钮转到该页面。</p>
<p><code>Location.replace()</code>方法以给定的URL来替换当前的资源。 与<code>assign() </code>方法 不同的是调用<code>replace()</code>方法后,当前页面不会保存到会话历史中<code>(session History)</code>,这样用户点击回退按钮将不会再跳转到该页面。</p>
<p>因违反安全规则导致的赋值失败,浏览器将会抛出类型为SECURITY_ERROR的<code>DOMException</code> 异常。当调用该方法的脚本所属的源与拥有<code>Location</code>对象所属源不同时,通常情况会发生这种异常,此时通常该脚本是存在不同的域下。</p>
<p>如果URL不合法,浏览器也会抛出SYNTAX_ERROR类型DOMException 的异常。</p>
<h4>Location.toString()</h4>
<p>返回一个<code>DOMString</code>,包含整个<code>URL</code>。 它和读取<code>URLUtils.href</code>的效果相同。但是用它是不能够修改<code>Location</code>的值的。</p>
<pre><code class="js">// Let's imagine an <a id="myAnchor" href="https://developer.mozilla.org/en-US/docs/HTMLHyperlinkElementUtils/toString"> element is in the document
var anchor = document.getElementById("myAnchor");
var result = anchor.toString(); // Returns: 'https://developer.mozilla.org/en-US/docs/HTMLHyperlinkElementUtils/toString'</code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=r42GpCKaNQ7cRHZ9X2PVyQ%3D%3D.uvMyNh4h1y183q%2Fm7oA7sWh9y1vnTz%2BxD7xdqwFctha8btTvKU5ogLY8qTwK%2Bb80FTw5YC5vr00i%2BXOiX1r3Hw%3D%3D" rel="nofollow">https://developer.mozilla.org...</a></blockquote>
JavaScript-console的使用_016
https://segmentfault.com/a/1190000018161842
2019-02-15T11:38:14+08:00
2019-02-15T11:38:14+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>JavaScript-console的使用</h2>
<p>onsole 对象提供对浏览器控制台的接入(如:Firefox 的 Web Console)。不同浏览器上它的工作方式是不一样的,但这里会介绍一些大都会提供的接口特性。</p>
<p>Console对象可以在任何全局对象中访问,如 Window,WorkerGlobalScope 以及通过属性工作台提供的特殊定义。</p>
<p>它被浏览器定义为 Window.console,也可被简单的 console 调用。</p>
<h3>方法</h3>
<h4>console.log()</h4>
<pre><code class="js">console.log(obj1 [, obj2, ..., objN);
console.log(msg [, subst1, ..., substN);
console.log('String: %s, Int: %d,Float: %f, Object: %o', str, ints, floats, obj)
console.log(`temp的值为: ${temp}`)</code></pre>
<p>对于打印对象数据的时候要注意:<br><strong>原来浏览器在打印对象的时候只是打印的一个对象快照信息,当你在控制台点击展开对象的时候,浏览器才会去读这个对象具体属性,但是那个时候,这段代码早就已经运行完了</strong></p>
<p>类似出现这种,都为<code>null</code>的情况:</p>
<pre><code class="js">SyntheticClipboardEvent {dispatchConfig: {…}, _targetInst: ReactDOMComponent, nativeEvent: ClipboardEvent, type: "paste", target: input, …}
bubbles: null
cancelable: null
clipboardData: null
currentTarget: null
defaultPrevented: null
dispatchConfig: null
eventPhase: null
isDefaultPrevented: null
isPropagationStopped: null
isTrusted: null
nativeEvent: null
target: null
timeStamp: null
type: null
_dispatchInstances: null
_dispatchListeners: null
_targetInst: null
__proto__: SyntheticEvent
</code></pre>
<h4> console.table()</h4>
<p>这个方法需要一个必须参数 data,data 必须是一个数组或者是一个对象;还可以使用一个可选参数 columns。</p>
<p><img src="/img/bVbomST?w=709&h=123" alt="clipboard.png" title="clipboard.png"></p>
<p>表格的第一列是 index。如果数据 data 是一个数组,那么这一列的单元格的值就是数组的索引。 如果数据是一个对象,那么它们的值就是各对象的属性名称。 注意(在 FireFox 中)console.table 被限制为只显示1000行(第一行是被标记的索引(原文:labeled index))。</p>
<p><img src="/img/bVbomSv?w=688&h=177" alt="clipboard.png" title="clipboard.png"></p>
<h4>console.assert()</h4>
<p><code>console.assert</code>为断言输出。第一个参数的表达式值为false时,则打印输出后面参数的值,否则为 true,则无输出并继续执行程序。例如:</p>
<pre><code class="js">function notEqual(a, b) {
console.assert(a === b, {
msg: 'a is not equal b',
a: a,
b: b
});
}
// console.assert
notEqual({a: 1}, {a: 2});</code></pre>
<h4>console.time</h4>
<p>你可以启动一个计时器(timer)来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行10,000个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间.</p>
<blockquote>
<a href="https://link.segmentfault.com/?enc=mEIM5HovzsL8Y23iGfCMoA%3D%3D.ughhnlHOISWOA9FInqXGVHIHHV%2FrUie1n1kBsqG%2FYIhh%2FRQ4cA8OXrUeexMqsvBOL4ytmHH3osOyaqAoxau4Ew%3D%3D" rel="nofollow">https://developer.mozilla.org...</a><p><a href="https://link.segmentfault.com/?enc=n5QS0JFrzn1myQ1NKfERjw%3D%3D.XMUYrftEO5Qg1xgMsQon9KrUd%2B%2Bcshtp48pLb%2BDS286oL75fPIx%2FK8WGUBg1owF3" rel="nofollow">https://www.jianshu.com/p/cf2...</a></p>
</blockquote>
HTML-form标签学习_015
https://segmentfault.com/a/1190000018102843
2019-02-06T09:43:37+08:00
2019-02-06T09:43:37+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>HTML-form标签学习</h2>
<p>HTML 中 <code><form></code> 元素表示了文档中的一个区域,这个区域包含有交互控制元件,用来向web服务器提交信息。</p>
<p>可以用 <code>:valid</code> 和<code>:invalid</code> <code>CSS</code> 伪类 来给一个元素指定样式</p>
<ul><li>注释:form 元素是块级元素,其前后会产生折行。</li></ul>
<h3>form 属性</h3>
<p><img src="/img/remote/1460000018102846" alt="image" title="image"></p>
<h3>form 事件</h3>
<p><img src="/img/remote/1460000018102847" alt="image" title="image"></p>
<h3>form 标签踩坑</h3>
<ul><li>form内只有一个输入框时,按回车会自动提交</li></ul>
<p>有多个input时(不管是否是隐藏的),任意一个input获得焦点都不会提交。</p>
<p>解决方法:<br>1.解决单个输入框的回车即提交问题,可以增加一个隐藏的input="text" display='none'; 然后type类型为button。</p>
<p>2.在form表单或input中加入:onkeydown="if(event.keyCode==13){return false;}"</p>
<blockquote><a href="https://link.segmentfault.com/?enc=TdxeNY19yxH6htBtH2uBtg%3D%3D.vsG7ARsYbu9ZZPI8EIhWSYjomDCuaDGQQegDsnO0nr27eDvxkkOEdmwcxWw90TGqTM8bMq%2BfZuv6kzl2VdFtug%3D%3D" rel="nofollow">http://www.w3school.com.cn/js...</a></blockquote>
webpack4+react+antd+axios+router4+redux 学习以及脚手架搭建_014
https://segmentfault.com/a/1190000018081262
2019-01-31T18:41:40+08:00
2019-01-31T18:41:40+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
6
<h2>webpack4 学习脚手架搭建</h2>
<h3>npm 命令</h3>
<p>初始化打包公共npm包,加快打包速度</p>
<pre><code class="js">npm run dll
或者
yarn dll</code></pre>
<p>开发环境</p>
<pre><code>npm run dev
或者
yarn dev</code></pre>
<p>生产环境打包</p>
<pre><code>npm run build
或者
yarn build</code></pre>
<h3>安装和初始化</h3>
<p>首先附上官方的<a href="https://link.segmentfault.com/?enc=SyLyF6nFzDWT8WGLVMW8xQ%3D%3D.w0zT4%2FUBE%2FMC6tJY0P%2FPwlYvAFypsSToelXAOfTBQCts78Mfonhf7OaKQ8%2BpY11g" rel="nofollow">文档</a></p>
<p>github地址</p>
<blockquote><a href="https://link.segmentfault.com/?enc=GHoLNG34Z77LO5U21pZQBw%3D%3D.EOsdP%2FJKf%2FMGRFUsFqG0Cut9vcCHrpVu4iYCVXyvijAIemOIqfnof8F9V%2BSnJOl34TkZYByyQnNMuMc30MYngLXGiIUjhta1%2Fft0PxDsMeo%3D" rel="nofollow">https://github.com/xiaopingzh...</a></blockquote>
<p>会不定时更新,如果觉得有帮助到你,给个Star当做鼓励可好。</p>
<pre><code>.
├── README.md
├── build
│ ├── webpack.dev.conf.js
│ ├── webpack.dll.conf.js
│ └── webpack.prod.conf.js
├── dist
├── dll
├── manifest.json
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── components
│ │ ├── Bread
│ │ │ └── Bread.js
│ │ └── SiderBar
│ │ └── SiderBar.js
│ ├── index.js
│ ├── layouts
│ │ └── BasicLayout.js
│ ├── pages
│ │ ├── Counter
│ │ │ └── Counter.js
│ │ └── Home
│ │ └── Home.js
│ ├── redux
│ │ ├── actions
│ │ │ └── counter.js
│ │ ├── reducer.js
│ │ ├── reducers
│ │ │ └── counter.js
│ │ └── store.js
│ ├── request
│ │ └── request.js
│ ├── router
│ │ └── Router.js
│ └── util
│ └── loadable.js
└── yarn.lock</code></pre>
<ol><li>新创建一个目录并初始化npm,在本地安装<code>webpack</code>,再安装<code>webpack-cli</code>
</li></ol>
<pre><code>>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (webpack4)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/xiaopingzhang/UCloud/webpack4/package.json:
{
"name": "webpack4",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Is this OK? (yes) yes</code></pre>
<p>初始化之后按照提示一步步往下就可以了,可以输入该项目的描述等等信息。一开始也没有关系,后面也还可以更改。</p>
<p>下一步 本地安装<code>webpack</code>,再安装<code>webpack-cli</code></p>
<pre><code>npm install webpack webpack-cli --save-dev</code></pre>
<p><strong>==<code>--save-dev</code> 是你开发时候依赖的东西,<code>--save</code> 是你发布之后还依赖的东西。==</strong></p>
<pre><code>>npm install webpack webpack-cli --save-dev
> fsevents@1.2.7 install /Users/xiaopingzhang/UCloud/webpack4/node_modules/fsevents
> node install
node-pre-gyp WARN Using needle for node-pre-gyp https download
[fsevents] Success: "/Users/xiaopingzhang/UCloud/webpack4/node_modules/fsevents/lib/binding/Release/node-v57-darwin-x64/fse.node" is installed via remote
> webpack-cli@3.2.1 postinstall /Users/xiaopingzhang/UCloud/webpack4/node_modules/webpack-cli
> lightercollective
*** Thank you for using webpack-cli! ***
Please consider donating to our open collective
to help us maintain this package.
https://opencollective.com/webpack/donate
***
npm WARN webpack4@1.0.0 No description
npm WARN webpack4@1.0.0 No repository field.
+ webpack-cli@3.2.1
+ webpack@4.29.0
added 458 packages from 239 contributors and audited 5208 packages in 18.624s
found 0 vulnerabilities</code></pre>
<p>安装好之后,也会显示安装的哪个版本,一般安装没有啥问题。实在安装不成功,试一下全局安装。</p>
<p>2.新建<code>src</code>文件夹,入口的<code>js</code>文件和<code>html</code>文件。</p>
<pre><code>.
├── index.html
├── package.json
└── src
└── index.js</code></pre>
<p>index.js文件</p>
<pre><code class="js">const component = () => {
let element = document.createElement("div");
element.innerHTML = "webpackworks";
return element;
};
document.body.appendChild(component());
</code></pre>
<p>index.html</p>
<pre><code class="html"><!DOCTYPE html>
<html>
<head>
<title>Start</title>
</head>
<body>
<script src="./dist/main.js"></script>
</body>
</html>
</code></pre>
<p>3.学会使用webpack编译文件</p>
<p>输入 <code>npx webpack</code></p>
<pre><code>>npx webpack
Hash: 9ad2a368debc9967c1f4
Version: webpack 4.29.0
Time: 269ms
Built at: 2019-01-27 21:15:22
Asset Size Chunks Chunk Names
main.js 1.01 KiB 0 [emitted] main
Entrypoint main = main.js
[0] ./src/index.js 218 bytes {0} [built]
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/</code></pre>
<p>再用浏览器打开<code>index.html</code>,查看网页是否正常的显示了。<br><img alt="image" title="image" src=""></p>
<p><strong>webpack 把入口文件 <code>index.js</code> 经过处理之后,生成 <code>main.js</code></strong></p>
<h3>配置文件</h3>
<p>经过第一部分的尝试,已经初步了解<code>webpack</code>的作用,这一部分通过配置文件进行相应的一些设置。</p>
<h4>babel</h4>
<blockquote>Babel 把用最新标准编写的 JavaScript 代码向下编译成可以在今天随处可用的版本。 这一过程叫做“源码到源码”编译, 也被称为转换编译。</blockquote>
<pre><code>npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-0</code></pre>
<p>新建babel配置文件.babelrc</p>
<pre><code class="js">{
"presets": [
"es2015",
"react",
"stage-0"
],
"plugins": []
}
//babel-core 调用Babel的API进行转码
//babel-loader
//babel-preset-es2015 用于解析 ES6
//babel-preset-react 用于解析 JSX
//babel-preset-stage-0 用于解析 ES7 提案</code></pre>
<h4>新建配置文件</h4>
<pre><code>webpack.base.conf.js
webpack.dev.conf.js
webpack.prod.conf.js</code></pre>
<p>分别是公共配置,开发配置,生产配置。<br>目前目录结构为</p>
<pre><code>.
├── build
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── dist
│ └── main.js
├── index.html
├── package.json
└── src
└── index.js</code></pre>
<h5>加载js/jsx文件</h5>
<pre><code>npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-0</code></pre>
<p>在 src 目录下新建<code>.babelrc</code></p>
<pre><code class="js">{
"presets": [
[
"env",
{
"targets": {
"browsers": [">1%", "last 3 versions"]
}
}
],
"stage-2",
"latest",
"react"
],
"plugins": [
"syntax-dynamic-import",
"transform-class-properties",
<!--[-->
<!-- "import",-->
<!-- {-->
<!-- "libraryName": "antd",-->
<!-- "libraryDirectory": "es",-->
<!-- "style": true-->
// "style": "css" //主题设置
<!-- }-->
<!--]-->
不用antd 可以去掉
]
}
</code></pre>
<p>文件新增</p>
<pre><code> {
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/, //排除
include: [
path.resolve(__dirname, '../src')
], //包括
use: {
loader: 'babel-loader'
}
},</code></pre>
<h5>加载CSS文件</h5>
<pre><code>npm install --save-dev style-loader css-loader</code></pre>
<p>在配置文件里添加</p>
<pre><code> {
test: /\.css$/,
use: ["style-loader", "css-loader"]
}</code></pre>
<h5>加载图片</h5>
<pre><code>npm install --save-dev url-loader file-loader</code></pre>
<p>在配置文件里添加</p>
<pre><code> {
test: /\.(png|jpg|gif)$/,
use: [
{
loader: "url-loader",
options: {
limit: 8192
}
}
]
}</code></pre>
<p><code>options limit:8192</code>意思是,小于等于8K的图片会被转成base64编码,直接插入HTML中,减少HTTP请求。</p>
<h5>加载less</h5>
<p>在这个踩了一个坑,记得安装 <code>less</code></p>
<pre><code>npm install --save-dev less-loader less</code></pre>
<p>更改antd 默认主题设置需要,不用的话应该把相应的设置忽略即可。</p>
<pre><code> {
test: /\.less$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader' // translates CSS into CommonJS
},
{
loader: 'less-loader', // compiles Less to CSS
options: {
modifyVars: {
'font-size-base': '12px',
'primary-color': '#0EA679'
},
javascriptEnabled: true
}
}
]
}</code></pre>
<h5>加载字体</h5>
<p>那么,像字体这样的其他资源如何处理呢?file-loader 和 url-loader 可以接收并加载任何文件,然后将其输出到构建目录。这就是说,我们可以将它们用于任何类型的文件,包括字体。更新 webpack.config.js 来处理字体文件:</p>
<pre><code> {
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ["file-loader"]
}</code></pre>
<h5>增加HtmlWebpackPlugin</h5>
<p>HtmlWebpackPlugin作用是生成一个HTML模板。<br>HtmlWebpackPlugin简化了HTML文件的创建,以便为你的webpack包提供服务。这对于在文件名中包含每次会随着编译而发生变化哈希的 webpack bundle 尤其有用。</p>
<p>你可以让插件为你生成一个HTML文件,使用lodash模板提供你自己的模板,或使用你自己的loader<br>首先需要安装插件:</p>
<pre><code>npm install --save-dev html-webpack-plugin</code></pre>
<p>在生产配置文件里添加</p>
<pre><code class="js"> plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
title: 'title', // 更改HTML的title的内容
favicon: 'public/favicon.ico',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
},
}),</code></pre>
<h4>清理 /dist 文件夹</h4>
<p>在每次构建前清理 /dist 文件夹.</p>
<pre><code>npm install clean-webpack-plugin --save-dev</code></pre>
<pre><code>new CleanWebpackPlugin(['../dist'])</code></pre>
<h4>模块热替换</h4>
<blockquote><a href="https://link.segmentfault.com/?enc=ayOZBX%2F6QEE0IqNabG0soA%3D%3D.vhS0S6lbs8aGQFuOSmjmzG93AFqiEtce83H6UTq4vnmgoULCHkzLz9hEQzs5nCQAHTBr2LIDtajHWvfwc5e%2B4Q%3D%3D" rel="nofollow">https://webpack.docschina.org...</a></blockquote>
<p>模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需进行完全刷新。<br>有两种方式</p>
<h5>一</h5>
<ul><li>更改package.json</li></ul>
<pre><code>"dev": "webpack --config build/webpack.dev.config.js --color --progress --hot"</code></pre>
<ul><li>更改index.js</li></ul>
<pre><code>import React from 'react';
import ReactDom from 'react-dom';
**if (module.hot) {
module.hot.accept();
}**
//增加
</code></pre>
<h5>二</h5>
<ul><li>更改配置文件</li></ul>
<pre><code>const webpack = require('webpack');
devServer: {
hot: true
}
plugins:[
new webpack.HotModuleReplacementPlugin()
]
</code></pre>
<h4>redux</h4>
<blockquote>
<a href="https://link.segmentfault.com/?enc=mSK%2FsCHaNE4zhLn6NQ6w3A%3D%3D.lXXyudbD%2BiFPwrDGNAz2c0FZRSLOfrtFZgyBsOYqvqI%3D" rel="nofollow">https://www.redux.org.cn/</a><br>官方文档先给上,一开始学的时候也以为这个比较难,开始写就不会了。<br>网上看看例子,自己在coding一下就差不多了。</blockquote>
<p>这边用到了一个中间件 <code>redux-thunk</code></p>
<pre><code>npm install --save redux-thunk
</code></pre>
<p>附上写的代码</p>
<h5>store</h5>
<p>注释的部分为生产环境使用。</p>
<p>为了方便debug代码,在控制台打印readux日志。</p>
<pre><code class="js">// import { createStore, applyMiddleware } from 'redux';
// import thunk from 'redux-thunk';
// import rootReducer from './reducer';
// const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
// const store = createStoreWithMiddleware(rootReducer);
// export default store;
// 打印操作日志,方便调试,生产环境可以去掉,用上面注释的配置。
import thunk from "redux-thunk"; // redux 作者开发的异步处理方案 可以在action 里传入 dispatch getState
import { createLogger } from "redux-logger"; // 利用redux-logger打印日志
import { createStore, applyMiddleware } from "redux"; // 引入redux createStore、中间件及compose
import { composeWithDevTools } from "redux-devtools-extension"; // devToolsEnhancer,
import reducer from "./reducer"; // 引入reducers集合
// 调用日志打印方法 collapsed是让action折叠,看着舒服点
const loggerMiddleware = createLogger({ collapsed: true });
// 创建一个中间件集合
const middleware = [thunk, loggerMiddleware];
// 创建store
const store = createStore(
reducer,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
</code></pre>
<h5>action</h5>
<pre><code class="js">export const INCREMENT = 'counter/INCREMENT';
export const DECREMENT = 'counter/DECREMENT';
export const RESET = 'counter/RESET';
export function increment() {
return { type: INCREMENT };
}
export function decrement() {
return { type: DECREMENT };
}
export function reset() {
return { type: RESET };
}</code></pre>
<h5>reducer</h5>
<h6>每个页面的reduce文件</h6>
<pre><code class="js">import { INCREMENT, DECREMENT, RESET } from '../actions/counter';
const initState = {
count: 0,
};
export default function reducer(state = initState, action) {
switch (action.type) {
case INCREMENT:
return {
count: state.count + 1,
};
case DECREMENT:
return {
count: state.count - 1,
};
case RESET:
return { count: 0 };
default:
return state;
}
}
</code></pre>
<h6>redecers 整合所有文件的reducer</h6>
<pre><code class="js">import { combineReducers } from "redux";
import counter from "./reducers/counter";
export default combineReducers({
counter
});
</code></pre>
<h4>react-loadable</h4>
<blockquote><a href="https://link.segmentfault.com/?enc=CjOhaBsSb47%2Bpqywr0ITsA%3D%3D.UsA9EsEyMv8xIAa4wIAfzo2xDrLCCNpKILas0P6UmHLLt7S5uZSmMuzkaP1rZxK5" rel="nofollow">https://github.com/jamiebuild...</a></blockquote>
<p>官方文档先附上</p>
<pre><code class="js">// 加载页面
import Loadable from 'react-loadable';
import React, { Component } from 'react';
import { Spin, Icon } from 'antd';
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />;
const antLong = (
<Icon type="loading" style={{ fontSize: 24, color: 'red' }} spin />
);
const antError = (
<Icon type="loading" style={{ fontSize: 24, color: 'red' }} spin />
);
export const Loading = props => {
if (props.error) {
return (
<Spin
size="large"
tip="加载错误 。。。"
indicator={antError}
style={{ position: 'absolute', color: 'red', top: '40%', left: '50%' }}
/>
);
} else if (props.timedOut) {
return (
<Spin
size="large"
tip="加载超时 。。。"
indicator={antLong}
style={{ position: 'absolute', color: 'red', top: '40%', left: '50%' }}
/>
);
} else if (props.pastDelay) {
return (
<Spin
size="large"
tip="Loading 。。。"
indicator={antError}
style={{ position: 'absolute', color: 'red', top: '40%', left: '50%' }}
/>
);
} else {
return null;
}
};
export const importPath = ({ loader }) => {
return Loadable({
loader,
loading: Loading,
delay: 200,
timeout: 10000
});
};
</code></pre>
<h4>axios 统一拦截所有的请求和返回数据</h4>
<p>在需要用到的地方引入这个文件就ok了。只是简单的写了一个例子,后续再完善吧。axios使用起来很简洁。</p>
<pre><code class="js">import axios from "axios";
import { message } from "antd";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
// 拦截所有有请求与回复
// Add a request interceptor
axios.interceptors.request.use(
config => {
NProgress.start();
return config;
},
error => {
message.error("请求错误,请重试");
return Promise.reject(error);
}
);
// Add a response interceptor
axios.interceptors.response.use(
response => {
// NProgress.done();
// if (response.data.RetCode === 101) {
// message.error(response.data.Message);
// return response;
// }
// if (response.data.RetCode === 100) {
// message.error(response.data.Message);
// return response;
// }
return response;
},
error => {
message.error("请求错误,请重试");
NProgress.done();
return Promise.reject(error);
}
);
export default request;
</code></pre>
<h4>公共路径(public path)</h4>
<h3>插件配置</h3>
<pre><code class="js">
plugins: [
// 处理html
new HtmlWebpackPlugin({
template: 'public/index.html',
path: '../public/index.html',
inject: 'body',
title: '管理平台',
favicon: 'public/favicon.ico',
filename: 'index.html',
hash: true,
minify: {
html5: true,
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
}
}),
new CleanWebpackPlugin(['../dist'], { allowExternal: true }),
new BundleAnalyzerPlugin(),
new MiniCssExtractPlugin({
chunkFilename: '[chunkhash].css'
}),
new webpack.HashedModuleIdsPlugin(),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('../dll/manifest.json')
}),
new CopyWebpackPlugin([
{ from: 'dll/Dll.js', to: DIST_PATH }
])
]
</code></pre>
<h4>html-webpack-plugin</h4>
<pre><code class="js">const HtmlWebpackPlugin = require('html-webpack-plugin');
new HtmlWebpackPlugin({
template: 'public/index.html',
path: '../public/index.html',
inject: 'body',
title: '管理平台',
favicon: 'public/favicon.ico',
filename: 'index.html',
hash: true,
minify: {
html5: true,
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
}
}),</code></pre>
<h4>copy-webpack-plugin</h4>
<pre><code class="js">
const CopyWebpackPlugin = require('copy-webpack-plugin');
new CopyWebpackPlugin([
{ from: 'dll/Dll.js', to: DIST_PATH }
])
</code></pre>
<h4>clean-webpack-plugin</h4>
<pre><code class="js">
const CleanWebpackPlugin = require('clean-webpack-plugin');
new CleanWebpackPlugin(['../dist'], { allowExternal: true })
</code></pre>
<h4>webpack-bundle-analyzer</h4>
<pre><code class="js">const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
.BundleAnalyzerPlugin;
new BundleAnalyzerPlugin(),
</code></pre>
<h4>mini-css-extract-plugin</h4>
<pre><code class="js">const MiniCssExtractPlugin = require('mini-css-extract-plugin');
new MiniCssExtractPlugin({
chunkFilename: '[chunkhash].css'
})
</code></pre>
<h3>附上三个配置文件</h3>
<h4>webpack.dev.conf.js</h4>
<pre><code class="js">const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const DIST_PATH = path.resolve(__dirname, '../dist'); //生产目录
const APP_PATH = path.resolve(__dirname, '../src'); //源文件目录
module.exports = {
mode: 'development',
entry: {
index: './src/index.js'
},
output: {
path: DIST_PATH, //出口路径
filename: 'index.js',
chunkFilename: 'js/[name].[chunkhash].js', //按需加载名称
// publicPath: "./"
},
// 源错误检查
devtool: 'inline-source-map',
//模块配置
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/, //排除
include: [
path.resolve(__dirname, '../src'),
path.resolve(__dirname, '../node_modules/antd/')
], //包括
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
},
//更改antd主题设置
{
test: /\.less$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader' // translates CSS into CommonJS
},
{
loader: 'less-loader', // compiles Less to CSS
options: {
modifyVars: {
'font-size-base': '12px',
'primary-color': '#0EA679'
},
javascriptEnabled: true
}
}
]
}
]
},
//插件
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
path: '../public/index.html',
inject: 'body',
favicon: 'public/favicon.ico',
title: '管理平台',
overlay: true,
minify: {
html5: false
},
hash: true
}),
// 热更新
new webpack.HotModuleReplacementPlugin(),
new webpack.HashedModuleIdsPlugin(),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('../dll/manifest.json')
}),
new CopyWebpackPlugin([
{ from: 'dll/Dll.js', to: DIST_PATH }
])
],
// 热更新
devServer: {
port: '3300',
contentBase: DIST_PATH,
historyApiFallback: true,
hot: true, // 开启
https: false,
compress: false,
noInfo: true,
open: true,
proxy: {
// '/': {
// target: '',
// changeOrigin: true,
// secure: false,
// },
}
}
};
</code></pre>
<h4>webpack.dll.conf.js</h4>
<pre><code class="js">const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const vendors = [
'antd',
'axios',
'nprogress',
'react',
'react-dom',
'react-loadable',
'react-redux',
'react-router',
'react-router-dom',
'redux'
];
module.exports = {
entry: {
vendor: vendors
},
output: {
path: path.resolve(__dirname, '../dll'),
filename: 'Dll.js',
library: '[name]_[hash]'
},
plugins: [
new webpack.DllPlugin({
path: path.resolve(__dirname, '../dll', 'manifest.json'),
name: '[name]_[hash]',
context: __dirname
}),
new CleanWebpackPlugin(['../dll'], { allowExternal: true })
]
};
</code></pre>
<h4>webpack.prod.conf.js</h4>
<pre><code class="js">
const path = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
.BundleAnalyzerPlugin;
const DIST_PATH = path.resolve(__dirname, '../dist'); //生产目录
module.exports = {
mode: 'production',
entry: {
index: './src/index.js'
},
output: {
path: DIST_PATH, //出口路径
filename: 'index.js',
chunkFilename: '[name]_[hash].js', //按需加载名称
// publicPath: './'
},
// 源错误检查
devtool: 'source-map',
//模块配置
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/, //排除
include: [
path.resolve(__dirname, '../src'),
path.resolve(__dirname, '../node_modules/antd/')
], //包括
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
},
//更改antd主题设置
{
test: /\.less$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader' // translates CSS into CommonJS
},
{
loader: 'less-loader', // compiles Less to CSS
options: {
minimize: true,
modifyVars: {
'font-size-base': '12px',
'primary-color': '#0EA679'
},
javascriptEnabled: true
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
}
]
},
//插件
plugins: [
// 处理html
new HtmlWebpackPlugin({
template: 'public/index.html',
path: '../public/index.html',
inject: 'body',
title: '管理平台',
favicon: 'public/favicon.ico',
filename: 'index.html',
hash: true,
minify: {
html5: true,
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
}
}),
new CleanWebpackPlugin(['../dist'], { allowExternal: true }),
new BundleAnalyzerPlugin(),
new MiniCssExtractPlugin({
chunkFilename: '[chunkhash].css'
}),
new webpack.HashedModuleIdsPlugin(),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('../dll/manifest.json')
}),
new CopyWebpackPlugin([
{ from: 'dll/Dll.js', to: DIST_PATH }
])
]
// 热更新
};
</code></pre>
<h3>学习过程中的踩坑</h3>
<h4>生产环境打包报错</h4>
<pre><code class="js">ERROR in Path must be a string. Received undefined
Child html-webpack-plugin for "index.html":
1 asset
Entrypoint undefined = index.html
</code></pre>
<p>这个错误不影响打包结果,应该是版本问题导致。</p>
<blockquote><a href="https://link.segmentfault.com/?enc=Db4GBoA7rghKhzj%2FTLdhFA%3D%3D.ftK5oDvY35tKA6ameYyuQ21H%2FyeDYq7nTdedLhk8f3optEfSbwL5TxV7esZVnK6G1aI94aEIAnz3yEfi91SaRA%3D%3D" rel="nofollow">https://github.com/jantimon/h...</a></blockquote>
<p>写完才发现有些忘记记录了,会保持更新。</p>
<p>学习的过程中也学习参考了其他优秀的博客和github,以及文档。</p>
<blockquote>
<a href="https://link.segmentfault.com/?enc=raLCZ1hT9kHYmjPclwahDw%3D%3D.tHxejI1cmguUDItPMnUonBAhe9hqFbdnozmipLsXfQniqIqOu%2Bs6QIg8mkF7jDJN" rel="nofollow">https://github.com/brickspert...</a><p><a href="https://link.segmentfault.com/?enc=uiD5LiBlpL%2B5BwimC63hVQ%3D%3D.654yKbcdtYtKcZoAPwIejKfGdu5hfKs6rJef1atrSCQAnzk7TrXtcmyG1FqoksH7moP1y7RU7zPCGkezQ53CRw%3D%3D" rel="nofollow">https://github.com/NewPrototy...</a></p>
<p><a href="https://link.segmentfault.com/?enc=4l5DQnapQMrojwI83asZ1g%3D%3D.GeFvmdtD2ml3o0pUAqqDh%2Fn7xwqX9PWkjeagILoJ83c%3D" rel="nofollow">https://github.com/axios/axios</a></p>
<p><a href="https://link.segmentfault.com/?enc=cMrbM%2BR%2FkTVrWsvHrflsMQ%3D%3D.KuKbpo6r6kNhNef5Xl41moDPIUQ8RhHEBGA29ozo76ksmiTAiXmBP5LVn3Qluzwp" rel="nofollow">https://github.com/jamiebuild...</a></p>
<p><a href="https://link.segmentfault.com/?enc=PKV7M%2BRDONprS9221p7Uhg%3D%3D.ucSbw1hn3DNDcBxRd9p5Gy2scycCOtKyWQsoHLlhFLhknh%2BKumMYQlcqftMRN8KT" rel="nofollow">https://www.webpackjs.com/con...</a></p>
</blockquote>
JavaScript中Date学习记录_013
https://segmentfault.com/a/1190000017785032
2019-01-06T22:52:46+08:00
2019-01-06T22:52:46+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>JavaScript中Date对象学习记录</h2>
<p>Date 实例用来处理日期和时间。Date对象基于1970年1月1日(世界标准时间)起的毫秒数。</p>
<p>JavaScript 的Date对象提供了数个UTC时间的方法,也相应提供了当地时间的方法。UTC,也就是我们所说的格林威治时间,指的是time中的世界时间标准。而当地时间则是指执行JavaScript的客户端电脑所设置的时间。</p>
<h3>Date 构造函数</h3>
<pre><code class="js">
new Date();
//Sun Jan 06 2019 20:18:04 GMT+0800 (中国标准时间)
new Date(value);
//value 代表自1970年1月1日00:00:00 (世界标准时间) 起经过的毫秒数。
new Date(000000000000);
//Thu Jan 01 1970 08:00:00 GMT+0800 (中国标准时间)
new Date(dateString);
//dateString表示日期的字符串值。该字符串应该能被 Date.parse() 方法识别
new Date("2019.01.01");
//Tue Jan 01 2019 00:00:00 GMT+0800 (中国标准时间)
new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);
//year代表年份的整数值。为了避免2000年问题最好指定4位数的年份; 使用 1998, 而不要用 98.
//month代表月份的整数值从0(1月)到11(12月)。
//day代表一个月中的第几天的整数值,从1开始。
//hour代表一天中的小时数的整数值 (24小时制)。
// minute分钟数。
// second秒数。
//millisecond表示时间的毫秒部分的整数值。
new Date(2019,01,01,01,01,01);
//Fri Feb 01 2019 01:01:01 GMT+0800 (中国标准时间)</code></pre>
<h3>Date方法</h3>
<h4>Date.now()</h4>
<p>返回自 1970-1-1 00:00:00 UTC (世界标准时间)至今所经过的毫秒数,类型为Number。</p>
<pre><code class="js">Date.now()
//1546777708417</code></pre>
<h4>Date.parse()</h4>
<p>解析一个表示日期的字符串,并返回从 1970-1-1 00:00:00 所经过的毫秒数。</p>
<pre><code class="js">Date.parse("2019.01.01")
//1546272000000
Date.parse('01 Jan 1970 00:00:00 GMT');
//0</code></pre>
<h4>Date.UTC()</h4>
<p>接受和构造函数最长形式的参数相同的参数(从2到7),并返回从 1970-01-01 00:00:00 UTC 开始所经过的毫秒数。</p>
<ul>
<li>year:1900 年后的某一年份。</li>
<li>month:0 到 11 之间的一个整数,表示月份。</li>
<li>date:1 到 31 之间的一个整数,表示某月当中的第几天。</li>
<li>hrs:0 到 23 之间的一个整数,表示小时。</li>
<li>min:0 到 59 之间的一个整数,表示分钟。</li>
</ul>
<p>sec 0 到 59 之间的一个整数,表示秒。<br>ms<br>0 到 999 之间的一个整数,表示毫秒。</p>
<pre><code class="js">new Date(Date.UTC(2019, 0, 0, 0, 0, 1));
//Mon Dec 31 2018 08:00:01 GMT+0800 (中国标准时间)</code></pre>
<h4>时间戳格式转换</h4>
<pre><code class="js"> dateFormmat(time) {
let date = new Date(time * 1000); //如果date为13位不需要乘1000
let Ye = date.getFullYear() + '/';
let Mo =
(date.getMonth() + 1 < 10
? '0' + (date.getMonth() + 1)
: date.getMonth() + 1) + '/';
let Da =
(date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
let hh =
(date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
let mm =
(date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) +
':';
let ss =
date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
return Ye + Mo + Da +hh + mm + ss
}
//let value=dateFormmat(1234567890)
//console.log(value)
//2009/02/14 07:31:30
</code></pre>
<h3>Date 实例-(get)</h3>
<p>所有的 Date 实例都继承自 Date.prototype。修改 Date 构造函数的原型对象会影响到所有的 Date 实例。</p>
<h4>Date.getDate()</h4>
<p>根据本地时间,返回一个指定的日期对象为一个月中的第几天。getDate() 返回一个1 到 31的整数值</p>
<pre><code class="js">let date = new Date("December 25, 2019 11:11:00");
let day = date.getDate();
console.log(day)
//25</code></pre>
<h4>Date.getDay()</h4>
<p>getDay() 返回一个整数值: 0 代表星期日, 1 代表星期一,2 代表星期二, 依次类推</p>
<h4>Date.getFullYear()</h4>
<p>getFullYear() 方法根据本地时间返回指定日期的年份。</p>
<h4>Date.getMonth()</h4>
<p>根据本地时间,返回一个指定的日期对象的月份,为基于0的值(0表示一年中的第一月)。</p>
<h4>Date.getHours()</h4>
<p>getHours() 方法根据本地时间,返回一个指定的日期对象的小时。getHours返回一个0 到 23之间的整数值。</p>
<h4>Date.getMinutes()</h4>
<p>getMinutes() 方法根据本地时间,返回一个指定的日期对象的分钟数。getMinutes 返回一个0 到 59的整数值</p>
<h4>Date.getSeconds()</h4>
<p>getSeconds() 方法根据本地时间,返回一个指定的日期对象的秒数,返回一个 0 到 59 的整数值。</p>
<h4>Date.getMilliseconds()</h4>
<p>getMilliseconds() 方法,根据本地时间,返回一个指定的日期对象的毫秒数。getMilliseconds() 方法返回一个0 到 999的整数。</p>
<h4>Date.getTime()</h4>
<p>getTime 方法的返回值一个数值,表示从1970年1月1日0时0分0秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。</p>
<h3>Date 实例-(set)</h3>
<h4>Date.setDate()</h4>
<p>setDate() 方法根据本地时间来指定一个日期对象的天数。<br><strong><em>如果 dayValue 超出了月份的合理范围,setDate 将会相应地更新 Date 对象</em></strong>。例如,如果为 dayValue 指定0,那么日期就会被设置为上个月的最后一天。</p>
<h4>Date.setFullYear()</h4>
<p>setFullYear() 方法根据本地时间为一个日期对象设置年份<br>如果有一个参数超出了合理的范围,setFullYear 方法会更新其他参数值,日期对象的日期值也会被相应更新。 例如,为 monthValue 指定 15, 则年份会加1,月份值会为3。</p>
<h4>Date.setHours()</h4>
<p>setHours() 方法根据本地时间为一个日期对象设置小时数,返回从1970-01-01 00:00:00 UTC 到更新后的 日期 对象实例所表示时间的毫秒数。</p>
<p>如果有一个参数超出了合理范围,setHours 会相应地更新日期对象中的日期信息。例如,如果为 secondsValue 指定了 100,则分钟会加 1,然后秒数使用 40。</p>
<h4>Date.setMilliseconds()</h4>
<p>setMilliseconds() 方法会根据本地时间设置一个日期对象的豪秒数。</p>
<p>如果指定的数字超出了合理范围,则日期对象的时间信息会被相应地更新。例如,如果指定了 1005,则秒数加 1,豪秒数为 5。</p>
<h4>Date.setMinutes()</h4>
<p>setMinutes() 方法根据本地时间为一个日期对象设置分钟数。</p>
<p>如果有一个指定的参数超出了合理范围,setMinutes 会相应地更新日期对象中的时间信息。例如,为 secondsValue 指定 100,分钟数将会加 1,而秒数会为 40。</p>
<h4>Date.setMonth()</h4>
<p>setMonth() 方法根据本地时间为一个设置年份的日期对象设置月份</p>
<p>如果有一个指定的参数超出了合理范围,setMonth 会相应地更新日期对象中的日期信息。例如,为 monthValue 指定 15,则年份会加 1,月份将会使用 3。</p>
<h4>Date.setSeconds()</h4>
<p>setSeconds() 方法根据本地时间设置一个日期对象的秒数。</p>
<p>如果一个参数超出了合理范围, setSeconds 方法会相应地更新日期对象的时间信息。例如,为 secondsValue 指定 100,则日期对象的分钟数会相应地加 1,秒数将会使用 40。</p>
<h4>Date.setTime()</h4>
<p>setTime() 方法以一个表示从1970-1-1 00:00:00 UTC计时的毫秒数为来为 Date 对象设置时间。</p>
<blockquote><a href="https://link.segmentfault.com/?enc=1e5KyGa1Nf1v6sotDOhGUA%3D%3D.S2AwbkXJyMsKqAHs3bDSGvEgnMFne87I9aSjz41yt4BSF9IlUWSBnL33sTzQPrrgbbSsKnlMTttPlxIma11hDByL6Ce8ldt%2BcnboHE0afPcJxuDN2JrIExl0TV2zw9VR" rel="nofollow">https://developer.mozilla.org...</a></blockquote>
axios请求、和返回数据拦截,统一请求报错提示_012
https://segmentfault.com/a/1190000017721123
2019-01-02T22:56:34+08:00
2019-01-02T22:56:34+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
4
<h2>axios请求、和返回数据拦截,统一请求报错提示</h2>
<h3>官方文档</h3>
<h4>
<a href="https://link.segmentfault.com/?enc=ZXznCjg3xCjCuoo%2FlqZU8Q%3D%3D.h0T37Cxpd2Nz%2BAMJlX1fuWcmgKlxwIYfTDFgvws%2Fyik%3D" rel="nofollow">https://github.com/axios/axios</a> 英文文档</h4>
<h4>
<a href="https://link.segmentfault.com/?enc=NakJeswT7gK1K%2Bz1uq6geA%3D%3D.%2BDdPlaSHqGYDGXvHDc3npmVOu%2BFYJf%2FyPHT90twA%2FgZRoPCHXwpAPS0f%2FdLq6rd5" rel="nofollow">https://www.kancloud.cn/yunye...</a> 中文文档</h4>
<h3>请求和返回拦截,添加统一的报错信息</h3>
<p>请求的配置可以通过阅读官方文档来进行配置。axios api也很简介,多看看再自己尝试一下就会了<br>下面是我写的一个在react中的应用,UI用的阿里的Antd 框架,所以报错信息直接用全局弹窗来提示了。比较简陋。<br>写好之后,在写发送请求的文件中引用request 就可以了。</p>
<pre><code class="js">import axios from "axios";
import { message } from "antd";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import qs from "qs";
// 拦截所有有请求与回复
// Add a request interceptor
axios.interceptors.request.use(
config => {
NProgress.start();
if (config.method != "get") {
config.data = qs.stringify(config.data);
}
// withCredentials=true
config.headers["Content-Type"] = "application/x-www-form-urlencoded";
return config;
},
error => {
message.error("请求错误,请重试");
return Promise.reject(error);
}
);
// Add a response interceptor
axios.interceptors.response.use(
response => {
NProgress.done();
if (response.data.RetCode === 101) {
message.error(response.data.Message);
return response;
}
if (response.data.RetCode === 100) {
message.error(response.data.Message);
return response;
}
return response;
},
error => {
message.error("请求错误,请重试");
NProgress.done();
return Promise.reject(error);
}
);
export default axios;
</code></pre>
<blockquote><a href="https://link.segmentfault.com/?enc=4WF9aQO%2BBlyoMUTiK8TX6g%3D%3D.v0zZW5ebP0Cqo2mDhBefj%2FybbcrXNsDQkH7d%2B5nV0aU%3D" rel="nofollow">https://github.com/axios/axios</a></blockquote>
JavaScript数组学习记录_11
https://segmentfault.com/a/1190000017452296
2018-12-20T00:27:03+08:00
2018-12-20T00:27:03+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>JavaScript数组学习记录</h2>
<h3>Array.length</h3>
<p>Array.length 是Array的实例属性。返回或设置一个数组中的元素个数。该值是一个无符号 32-bit 整数,并且总是大于数组最高项的下标。</p>
<ul><li>length 属性的值是一个 0 到<code>$2^{32}$</code>-1 的整数</li></ul>
<pre><code class="js">let arr = ['1', '2', '3', '4','5'];
console.log(arr.length);
//output: 5</code></pre>
<ul><li>可以设置 length属性的值来截断任何数组。当通过改变length属性值来扩展数组时,实际元素的数目将会增加。例如:将一个拥有 2 个元素的数组的 length 属性值设为 3 时,那么这个数组将会包含3个元素,并且,第三个元素的值将会是 undefined 。</li></ul>
<pre><code class="js">let numbers = [1, 2, 3, 4, 5];
let length = numbers.length;
for (let i = 0; i < length; i++) {
numbers[i] *= 2;
}
console.log(numbers);
//[2, 4, 6, 8, 10]
</code></pre>
<pre><code class="js">let numbers = [1, 2, 3, 4, 5];
if (numbers.length > 3) {
numbers.length = 3;
}
console.log(numbers); // [1, 2, 3]
console.log(numbers.length); // 3
</code></pre>
<ul><li><strong>length属性不一定表示数组中定义值的个数。了解更多:长度与数值下标属性之间的关系。</strong></li></ul>
<p>Array.length 属性的属性特性:</p>
<table>
<thead><tr>
<th>属性</th>
<th>说明</th>
<th>备注</th>
</tr></thead>
<tbody>
<tr>
<td>writable</td>
<td>true</td>
<td>如果设置为false,该属性值将不能被修改</td>
</tr>
<tr>
<td>enumerable</td>
<td>false</td>
<td>如果设置为false,删除或更改任何属性都将会失败。</td>
</tr>
<tr>
<td>configurable</td>
<td>false</td>
<td>如果设置为 true ,属性可以通过迭代器for或for...in进行迭代。</td>
</tr>
</tbody>
</table>
<pre><code class="js">let fruits = [];
fruits.push('banana', 'apple', 'peach');
console.log(fruits.length);
// 3
fruits[5] = 'mango';
console.log(fruits[5]);
// 'mango'
console.log(Object.keys(fruits));
// ['0', '1', '2', '5']
console.log(fruits.length);
// 6
//减少length属性会删除元素
fruits.length = 2;
console.log(Object.keys(fruits));
// ['0', '1']
console.log(fruits.length);
// 2
</code></pre>
<h3>Array.prototype</h3>
<p>Array.prototype 属性表示 Array 构造函数的原型,并允许向所有Array对象添加新的属性和方法。<br>Array实例继承自 Array.prototype 。与所有构造函数一样,可以更改构造函数的原型对象,以对所有 Array 实例进行更改。例如,可以添加新方法和属性以扩展所有Array对象。这用于 polyfilling, 例如:</p>
<p>鲜为人知的事实:Array.prototype 本身也是一个 Array。</p>
<pre><code class="js">Array.isArray(Array.prototype);
// true</code></pre>
<ul><li>Array.prototype 属性</li></ul>
<table>
<thead><tr>
<th>属性</th>
<th>说明</th>
</tr></thead>
<tbody>
<tr>
<td>writable</td>
<td>false</td>
</tr>
<tr>
<td>enumerable</td>
<td>false</td>
</tr>
<tr>
<td>configurable</td>
<td>false</td>
</tr>
</tbody>
</table>
<ul><li>Array.prototype.constructor</li></ul>
<p>所有的数组实例都继承了这个属性,它的值就是 Array,表明了所有的数组都是由 Array 构造出来的。</p>
<ul><li>Array.prototype.length</li></ul>
<p>上面说了,因为 Array.prototype 也是个数组,所以它也有 length 属性,这个值为 0,因为它是个空数组。</p>
<h4>会改变自身的方法</h4>
<p>下面的这些方法会改变调用它们的对象自身的值:</p>
<ul><li>Array.prototype.copyWithin()</li></ul>
<p>在数组内部,将一段元素序列拷贝到另一段元素序列上,覆盖原有的值。</p>
<ul><li>Array.prototype.fill()</li></ul>
<p>将数组中指定区间的所有元素的值,都替换成某个固定的值。</p>
<ul><li>Array.prototype.pop()</li></ul>
<p>删除数组的最后一个元素,并返回这个元素。</p>
<ul><li>Array.prototype.push()</li></ul>
<p>在数组的末尾增加一个或多个元素,并返回数组的新长度。</p>
<ul><li>Array.prototype.reverse()</li></ul>
<p>颠倒数组中元素的排列顺序,即原先的第一个变为最后一个,原先的最后一个变为第一个。</p>
<ul><li>Array.prototype.shift()</li></ul>
<p>删除数组的第一个元素,并返回这个元素。</p>
<ul><li>Array.prototype.sort()</li></ul>
<p>对数组元素进行排序,并返回当前数组。</p>
<ul><li>Array.prototype.splice()</li></ul>
<p>在任意的位置给数组添加或删除任意个元素。</p>
<ul><li>Array.prototype.unshift()</li></ul>
<p>在数组的开头增加一个或多个元素,并返回数组的新长度。</p>
<h4>不会改变自身的方法</h4>
<p>下面的这些方法绝对不会改变调用它们的对象的值,只会返回一个新的数组或者返回一个其它的期望值。</p>
<ul><li>Array.prototype.concat()</li></ul>
<p>返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组。</p>
<ul><li>Array.prototype.includes()</li></ul>
<p>判断当前数组是否包含某指定的值,如果是返回 true,否则返回 false。</p>
<ul><li>Array.prototype.join()</li></ul>
<p>连接所有数组元素组成一个字符串。</p>
<ul><li>Array.prototype.slice()</li></ul>
<p>抽取当前数组中的一段元素组合成一个新数组。</p>
<ul><li>Array.prototype.toSource()</li></ul>
<p>返回一个表示当前数组字面量的字符串。遮蔽了原型链上的 Object.prototype.toSource() 方法。</p>
<ul><li>Array.prototype.toString()</li></ul>
<p>返回一个由所有数组元素组合而成的字符串。遮蔽了原型链上的 Object.prototype.toString() 方法。</p>
<ul><li>Array.prototype.toLocaleString()</li></ul>
<p>返回一个由所有数组元素组合而成的本地化后的字符串。遮蔽了原型链上的 Object.prototype.toLocaleString() 方法。</p>
<ul><li>Array.prototype.indexOf()</li></ul>
<p>返回数组中第一个与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1。</p>
<ul><li>Array.prototype.lastIndexOf()</li></ul>
<p>返回数组中最后一个(从右边数第一个)与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1。</p>
<h4>遍历方法</h4>
<p><strong>在下面的众多遍历方法中,有很多方法都需要指定一个回调函数作为参数。在每一个数组元素都分别执行完回调函数之前,数组的length属性会被缓存在某个地方,所以,如果你在回调函数中为当前数组添加了新的元素,那么那些新添加的元素是不会被遍历到的。此外,如果在回调函数中对当前数组进行了其它修改,比如改变某个元素的值或者删掉某个元素,那么随后的遍历操作可能会受到未预期的影响。总之,不要尝试在遍历过程中对原数组进行任何修改,虽然规范对这样的操作进行了详细的定义,但为了可读性和可维护性,请不要这样做。</strong></p>
<ul><li>Array.prototype.forEach()</li></ul>
<p>为数组中的每个元素执行一次回调函数。</p>
<ul><li>Array.prototype.entries()</li></ul>
<p>返回一个数组迭代器对象,该迭代器会包含所有数组元素的键值对。</p>
<ul><li>Array.prototype.every()</li></ul>
<p>如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false。</p>
<ul><li>Array.prototype.some()</li></ul>
<p>如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。</p>
<ul><li>Array.prototype.filter()</li></ul>
<p>将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回。</p>
<ul><li>Array.prototype.find()</li></ul>
<p>找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined。</p>
<ul><li>Array.prototype.findIndex()</li></ul>
<p>找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1。</p>
<ul><li>Array.prototype.keys()</li></ul>
<p>返回一个数组迭代器对象,该迭代器会包含所有数组元素的键。</p>
<ul><li>Array.prototype.map()</li></ul>
<p>返回一个由回调函数的返回值组成的新数组。</p>
<ul><li>Array.prototype.reduce()</li></ul>
<p>从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。</p>
<ul><li>Array.prototype.reduceRight()</li></ul>
<p>从右到左为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。</p>
<ul><li>Array.prototype.values()</li></ul>
<p>返回一个数组迭代器对象,该迭代器会包含所有数组元素的值。</p>
<ul><li>Array.prototype[@@iterator]()</li></ul>
<p>和上面的 values() 方法是同一个函数。</p>
<h4>通用方法节</h4>
<p>在 JavaScript 中,很多的数组方法被故意设计成是通用的。也就是说,那些看起来像是数组的对象(类数组对象),即拥有一个 length 属性,以及对应的索引属性(也就是数字类型的属性,比如 obj[5])的非数组对象也是可以调用那些数组方法的。其中一些数组方法,比如说 join 方法,它们只会单纯的读取当前对象的 length 属性和索性属性的值,并不会尝试去改变这些属性的值。而另外一些数组方法,比如说 reverse 方法,它们会尝试修改那些属性的值,因此,如果当前对象是个 String 对象,那么这些方法在执行时就会报错,因为字符串对象的 length 属性和索引属性都是只读的。</p>
<h3>数组方法</h3>
<h4>Array.from()</h4>
<p>Array.from()方法从一个类似数组或可迭代对象中创建一个新的数组实例。</p>
<pre><code>let arr= Array.from('Array')
console.log(arr);
// ["A", "r", "r", "a", "y"]
console.log(Array.from([1, 2, 3], x => x*x));
// [1, 4, 9]
</code></pre>
<p>Array.from() 可以通过以下方式来创建数组对象:</p>
<ul>
<li>伪数组对象(拥有一个length属性和若干索引属性的任意对象)</li>
<li>可迭代对象(可以获取对象中的元素,如 Map和 Set 等)</li>
</ul>
<p>Array.from() 方法有一个可选参数<br>mapFn,让你可以在最后生成的数组上再执行一次 map 方法后再返回。也就是说 Array.from(obj, mapFn, thisArg) 就相当于 Array.from(obj).map(mapFn, thisArg), 除非创建的不是可用的中间数组。 这对一些数组的子类,如 typed arrays 来说很重要, 因为中间数组的值在调用 map() 时需要是适当的类型。</p>
<p>from() 的 length 属性为 1 ,即Array.from.length = 1。</p>
<p>在 ES2015 中, Class 语法允许我们为内置类型(比如 Array)和自定义类新建子类(比如叫 SubArray)。这些子类也会继承父类的静态方法,比如 SubArray.from(),调用该方法后会返回子类 SubArray 的一个实例,而不是 Array 的实例。</p>
<p><strong>数组去重</strong></p>
<pre><code class="js">function combine(){
let arr = [].concat.apply([], arguments); //没有去重复的新数组
return Array.from(new Set(arr));
}
var m = [1, 2, 2], n = [2,3,3];
console.log(combine(m,n)); // [1, 2, 3]</code></pre>
<h4>Array.isArray()</h4>
<p>Array.isArray() 用于确定传递的值是否是一个 Array。</p>
<pre><code class="js">// 下面的函数调用都返回 true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
// 鲜为人知的事实:其实 Array.prototype 也是一个数组。
Array.isArray(Array.prototype);
// 下面的函数调用都返回 false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray({ __proto__: Array.prototype });</code></pre>
<p><strong>instanceof 和 isArray</strong></p>
<p>当检测Array实例时, Array.isArray 优于 instanceof,因为Array.isArray能检测iframes.</p>
<h4>Array.of()</h4>
<p>Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。</p>
<p>Array.of() 和 Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个长度为7的空数组(注意:这是指一个有7个空位的数组,而不是由7个undefined组成的数组)</p>
<pre><code class="js">
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
Array(7); // [ , , , , , , ]
Array(1, 2, 3); // [1, 2, 3]
</code></pre>
<h4>Array.concat()</h4>
<p>concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组</p>
<h4>Array.copyWithin()</h4>
<p>copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,而不修改其大小</p>
<p><strong> arr.copyWithin(target[, start[, end]])</strong></p>
<ul><li>target</li></ul>
<p>0 为基底的索引,复制序列到该位置。如果是负数,target 将从末尾开始计算。<br>如果 target 大于等于 arr.length,将会不发生拷贝。如果 target 在 start 之后,复制的序列将被修改以符合 arr.length。</p>
<ul><li>start</li></ul>
<p>0 为基底的索引,开始复制元素的起始位置。如果是负数,start 将从末尾开始计算。<br>如果 start 被忽略,copyWithin 将会从0开始复制。</p>
<ul><li>end</li></ul>
<p>0 为基底的索引,开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数, end 将从末尾开始计算。<br>如果 end 被忽略,copyWithin 将会复制到 arr.length。</p>
<pre><code class="js">[1, 2, 3, 4, 5].copyWithin(-2);
// [1, 2, 3, 1, 2]
[1, 2, 3, 4, 5].copyWithin(0, 3);
// [4, 5, 3, 4, 5]
[1, 2, 3, 4, 5].copyWithin(0, 3, 4);
// [4, 2, 3, 4, 5]
[1, 2, 3, 4, 5].copyWithin(-2, -3, -1);
// [1, 2, 3, 3, 4]
[].copyWithin.call({length: 5, 3: 1}, 0, 3);
({0:undefined,1:undefined,2:undefined,3: 1,4:undefined,5:undefined,length: 5}).copyWithin(0,3,5);
结果为:
{0:1,1:undefined,2:undefined,3: 1,4:undefined,5:undefined,length: 5};
也就是
{0:1,3:1,length:5}
// {0: 1, 3: 1, length: 5}
// ES2015 Typed Arrays are subclasses of Array
var i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]
// On platforms that are not yet ES2015 compliant:
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]</code></pre>
<p>参数target,start和end 必须为整数。</p>
<p>如果start为负,则其指定的索引位置等同于length+start,length为数组的长度。end也是如此。</p>
<p>copyWithin方法不要求其this值必须是一个数组对象;除此之外,copyWithin是一个可变方法,它可以改变this对象本身,并且返回它,而不仅仅是它的拷贝。</p>
<p>copyWithin 就像 C 和 C++ 的 memcpy 函数一样,且它是用来移动 Array 或者 TypedArray 数据的一个高性能的方法。复制以及粘贴序列这两者是为一体的操作;即使复制和粘贴区域重叠,粘贴的序列也会有拷贝来的值。</p>
<p>copyWithin 函数是设计为通用的,其不要求其 this 值必须是一个数组对象。</p>
<p>The copyWithin 是一个可变方法,它不会改变 this 的 length,但是会改变 this 本身的内容,且需要时会创建新的属性。</p>
<h4>Array.entries()</h4>
<p>entries() 方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对。</p>
<p>一个新的 Array 迭代器对象。Array Iterator是对象,它的原型(__proto__:Array Iterator)上有一个next方法,可用用于遍历迭代器取得原数组的[key,value]。</p>
<pre><code class="js">var arr = ['a', 'b', 'c'];
var iteratorArr = array1.entries();
console.log(iteratorArr.next().value);
// expected output: Array [0, "a"]
console.log(iteratorArr.next().value);
// expected output: Array [1, "b"]
</code></pre>
<ul><li>iterator.next方法</li></ul>
<pre><code class="js">var arr = ["a", "b", "c"];
var iter = arr.entries();
var a = [];
// for(var i=0; i< arr.length; i++){ // 实际使用的是这个
for(var i=0; i< arr.length+1; i++){ // 注意,是length+1,比数组的长度大
var tem = iter.next(); // 每次迭代时更新next
console.log(tem.done); // 这里可以看到更新后的done都是false
if(tem.done !== true){ // 遍历迭代器结束done才是true
console.log(tem.value);
a[i]=tem.value;
}
}
console.log(a); // 遍历完毕,输出next.value的数组</code></pre>
<ul><li>二维数组排序</li></ul>
<pre><code class="js">function sortArr(arr) {
var goNext = true;
var entries = arr.entries();
while (goNext) {
var result = entries.next();
if (result.done !== true) {
result.value[1].sort((a, b) => a - b);
goNext = true;
} else {
goNext = false;
}
}
return arr;
}
var arr = [[1,34],[456,2,3,44,234],[4567,1,4,5,6],[34,78,23,1]];
sortArr(arr);
/*(4) [Array(2), Array(5), Array(5), Array(4)]
0:(2) [1, 34]
1:(5) [2, 3, 44, 234, 456]
2:(5) [1, 4, 5, 6, 4567]
3:(4) [1, 23, 34, 78]
length:4
__proto__:Array(0)
*/</code></pre>
<ul><li>使用 for...of循环</li></ul>
<pre><code class="js">var arr = ["a", "b", "c"];
var iterator = arr.entries();
// undefined
for (let e of iterator) {
console.log(e);
}
// [0, "a"]
// [1, "b"]
// [2, "c"]</code></pre>
<h4>Array.every()</h4>
<p>every() 方法测试数组的所有元素是否都通过了指定函数的测试</p>
<pre><code class="js">function isBelowThreshold(currentValue) {
return currentValue < 40;
}
var array1 = [1, 30, 39, 29, 10, 13];
console.log(array1.every(isBelowThreshold));
// expected output: true
function isBigEnough(element, index, array) {
return (element >= 10);
}
var passed = [12, 5, 8, 130, 44].every(isBigEnough);
// passed is false
passed = [12, 54, 18, 130, 44].every(isBigEnough);
// passed is true
</code></pre>
<ul>
<li>every 方法为数组中的每个元素执行一次 callback 函数,直到它找到一个使 callback 返回 false(表示可转换为布尔值 false 的值)的元素。如果发现了一个这样的元素,every 方法将会立即返回 false。否则,callback 为每一个元素返回 true,every 就会返回 true。callback 只会为那些已经被赋值的索引调用。不会为那些被删除或从来没被赋值的索引调用。</li>
<li>callback 被调用时传入三个参数:元素值,元素的索引,原数组。</li>
</ul>
<p>如果为 every 提供一个 thisArg 参数,则该参数为调用 callback 时的 this 值。如果省略该参数,则 callback 被调用时的 this 值,在非严格模式下为全局对象,在严格模式下传入 undefined。</p>
<ul>
<li>every 不会改变原数组。</li>
<li>every 遍历的元素范围在第一次调用 callback 之前就已确定了。在调用 every 之后添加到数组中的元素不会被 callback 访问到。如果数组中存在的元素被更改,则他们传入 callback 的值是 every 访问到他们那一刻的值。那些被删除的元素或从来未被赋值的元素将不会被访问到。</li>
<li>every 和数学中的"所有"类似,当所有的元素都符合条件才返回true。另外,空数组也是返回true。(空数组中所有元素都符合给定的条件,注:因为空数组没有元素)。</li>
</ul>
<h4>Array.fill()</h4>
<p>fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。</p>
<pre><code class="js">arr.fill(value,[start, end])</code></pre>
<pre><code class="js">[1, 2, 3].fill(4); // [4, 4, 4]
[1, 2, 3].fill(4, 1); // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2); // [1, 4, 3]
[1, 2, 3].fill(4, 1, 1); // [1, 2, 3]
[1, 2, 3].fill(4, 3, 3); // [1, 2, 3]
[1, 2, 3].fill(4, -3, -2); // [4, 2, 3]
[1, 2, 3].fill(4, NaN, NaN); // [1, 2, 3]
[1, 2, 3].fill(4, 3, 5); // [1, 2, 3]
Array(3).fill(4); // [4, 4, 4]
[].fill.call({ length: 3 }, 4); // {0: 4, 1: 4, 2: 4, length: 3}
// Objects by reference.
var arr = Array(3).fill({}) // [{}, {}, {}];
arr[0].hi = "hi"; // [{ hi: "hi" }, { hi: "hi" }, { hi: "hi" }]</code></pre>
<ul>
<li>fill 方法接受三个参数 value, start 以及 end. start 和 end 参数是可选的, 其默认值分别为 0 和 this 对象的 length 属性值。</li>
<li>如果 start 是个负数, 则开始索引会被自动计算成为 length+start, 其中 length 是 this 对象的 length 属性值。如果 end 是个负数, 则结束索引会被自动计算成为 length+end。</li>
<li>fill 方法故意被设计成通用方法, 该方法不要求 this 是数组对象。</li>
<li>fill 方法是个可变方法, 它会改变调用它的 this 对象本身, 然后返回它, 而并不是返回一个副本。</li>
<li>当一个对象被传递给 fill方法的时候, 填充数组的是这个对象的引用。</li>
</ul>
<h4>Array.filter()</h4>
<p>filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。</p>
<ul>
<li>filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或 等价于 true 的值 的元素创建一个新数组。callback 只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过 callback 测试的元素会被跳过,不会被包含在新数组中。</li>
<li>callback 被调用时传入三个参数:</li>
</ul>
<p>1.元素的值</p>
<p>2.元素的索引</p>
<p>3.被遍历的数组</p>
<ul>
<li>如果为 filter 提供一个 thisArg 参数,则它会被作为 callback 被调用时的 this 值。否则,callback 的 this 值在非严格模式下将是全局对象,严格模式下为 undefined。</li>
<li>callback 最终观察到的this值是根据通常函数所看到的 "this"的规则确定的。</li>
<li>filter 不会改变原数组,它返回过滤后的新数组。</li>
<li>filter 遍历的元素范围在第一次调用 callback 之前就已经确定了。在调用 filter 之后被添加到数组中的元素不会被 filter 遍历到。如果已经存在的元素被改变了,则他们传入 callback 的值是 filter 遍历到它们那一刻的值。被删除或从来未被赋值的元素不会被遍历</li>
<li>过滤JSON中的无效条目</li>
</ul>
<pre><code class="js">var arr = [
{ id: 15 },
{ id: -1 },
{ id: 0 },
{ id: 3 },
{ id: 12.2 },
{ },
{ id: null },
{ id: NaN },
{ id: 'undefined' }
];
var invalidEntries = 0;
function isNumber(obj) {
return obj !== undefined && typeof(obj) === 'number' && !isNaN(obj);
}
function filterByID(item) {
if (isNumber(item.id) && item.id !== 0) {
return true;
}
invalidEntries++;
return false;
}
var arrByID = arr.filter(filterByID);
console.log('Filtered Array\n', arrByID);
// Filtered Array
// [{ id: 15 }, { id: -1 }, { id: 3 }, { id: 12.2 }]
console.log('Number of Invalid Entries = ', invalidEntries);
// Number of Invalid Entries = 5</code></pre>
<ul><li><strong>搜索</strong></li></ul>
<pre><code class="js">const fruits = ['apple', 'banana', 'grapes', 'mango', 'orange'];
/**
* Array filters items based on search criteria (query)
*/
const filterItems = (query) => {
return fruits.filter((el) =>
el.toLowerCase().indexOf(query.toLowerCase()) > -1
);
}
console.log(filterItems('ap')); // ['apple', 'grapes']
console.log(filterItems('an')); // ['banana', 'mango', 'orange']</code></pre>
<h4>Array.find()</h4>
<p>find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。</p>
<p>如果你需要找到一个元素的位置或者一个元素是否存在于数组中,使用Array.prototype.indexOf() 或 Array.prototype.includes()。</p>
<ul><li>用对象的属性查找数组里的对象</li></ul>
<pre><code class="js">var inventory = [
{name: 'apples', quantity: 2},
{name: 'bananas', quantity: 0},
{name: 'cherries', quantity: 5}
];
function findCherries(fruit) {
return fruit.name === 'cherries';
}
console.log(inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }
寻</code></pre>
<ul><li>寻找质数</li></ul>
<pre><code class="js">function isPrime(element, index, array) {
var start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) {
return false;
}
}
return element > 1;
}
console.log([4, 6, 8, 12].find(isPrime)); // undefined, not found
console.log([4, 5, 8, 12].find(isPrime)); // 5</code></pre>
<h4>Array.findIndex()</h4>
<p>findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。</p>
<pre><code class="js">function isPrime(element, index, array) {
var start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) {
return false;
}
}
return element > 1;
}
console.log([4, 6, 8, 12].findIndex(isPrime)); // -1, not found
console.log([4, 6, 7, 12].findIndex(isPrime)); // 2</code></pre>
<h4>Array.forEach()</h4>
<p>forEach() 方法对数组的每个元素执行一次提供的函数</p>
<pre><code class="js">//<!--复制对象的函数-->
function copy(obj) {
var copy = Object.create(Object.getPrototypeOf(obj));
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
Object.defineProperty(copy, name, desc);
});
return copy;
}
var obj1 = { a: 1, b: 2 };
var obj2 = copy(obj1); // obj2 looks like obj1 now</code></pre>
<ul><li>如果数组在迭代时被修改了,则其他元素会被跳过。节</li></ul>
<p>下面的例子输出"one", "two", "four"。当到达包含值"two"的项时,整个数组的第一个项被移除了,这导致所有剩下的项上移一个位置。因为元素 "four"现在在数组更前的位置,"three"会被跳过。 forEach()不会在迭代之前创建数组的副本</p>
<pre><code class="js">var words = ["one", "two", "three", "four"];
words.forEach(function(word) {
console.log(word);
if (word === "two") {
words.shift();
}
});
// one
// two
// four</code></pre>
<h4>Array.includes()</h4>
<p>includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。</p>
<pre><code class="js">[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false</code></pre>
<ul><li>fromIndex 大于等于数组长度节<br>如果fromIndex 大于等于数组长度 ,则返回 false 。该数组不会被搜索</li></ul>
<pre><code class="js">var arr = ['a', 'b', 'c'];
arr.includes('c', 3); //false
arr.includes('c', 100); // false</code></pre>
<ul><li>includes() 作为一个通用方法节</li></ul>
<p>includes() 方法有意设计为通用方法。它不要求this值是数组对象,所以它可以被用于其他类型的对象 (比如类数组对象)。下面的例子展示了 在函数的arguments对象上调用的includes() 方法。</p>
<pre><code class="js">(function() {
console.log([].includes.call(arguments, 'a')); // true
console.log([].includes.call(arguments, 'd')); // false
})('a','b','c');</code></pre>
<h4>Array.indexOf()</h4>
<p>indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。</p>
<pre><code class="js">arr.indexOf(searchElement)
arr.indexOf(searchElement, fromIndex = 0)</code></pre>
<ul><li>找出指定元素的所有索引位置</li></ul>
<pre><code class="js">var indices = [];
var array = ['a', 'b', 'a', 'c', 'a', 'd'];
var element = 'a';
var idx = array.indexOf(element);
while (idx != -1) {
indices.push(idx);
idx = array.indexOf(element, idx + 1);
}
console.log(indices);
// [0, 2, 4]</code></pre>
<h4>Array.join()</h4>
<p>join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。</p>
<pre><code class="js">str = arr.join()
// 默认为 ","
str = arr.join("")
// 分隔符 === 空字符串 ""
str = arr.join(separator)
// 分隔符</code></pre>
<h4>Array.keys()</h4>
<pre><code class="js">var array1 = ['a', 'b', 'c'];
var iterator = array1.keys();
for (let key of iterator) {
console.log(key); // expected output: 0 1 2
}</code></pre>
<ul><li>会包含没有对应元素的索引</li></ul>
<pre><code class="js">var arr = ["a", , "c"];
var sparseKeys = Object.keys(arr);
var denseKeys = [...arr.keys()];
console.log(sparseKeys); // ['0', '2']
console.log(denseKeys); // [0, 1, 2]</code></pre>
<h4>Array.lastIndexOf()</h4>
<p>lastIndexOf() 方法返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始</p>
<pre><code class="js">arr.lastIndexOf(searchElement, fromIndex = arr.length - 1)</code></pre>
<p>从此位置开始逆向查找。默认为数组的长度减 1,即整个数组都被查找。如果该值大于或等于数组的长度,则整个数组会被查找。如果为负值,将其视为从数组末尾向前的偏移。即使该值为负,数组仍然会被从后向前查找。如果该值为负时,其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。。</p>
<h4>Array.map()</h4>
<p>map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。</p>
<pre><code class="js">var kvArray = [{key: 1, value: 10},
{key: 2, value: 20},
{key: 3, value: 30}];
var reformattedArray = kvArray.map(function(obj) {
var rObj = {};
rObj[obj.key] = obj.value;
return rObj;
});
// reformattedArray 数组为: [{1: 10}, {2: 20}, {3: 30}],
// kvArray 数组未被修改:
// [{key: 1, value: 10},
// {key: 2, value: 20},
// {key: 3, value: 30}]</code></pre>
<h4>Array.pop()</h4>
<p>pop()方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。</p>
<pre><code class="js">let myFish = ["angel", "clown", "mandarin", "surgeon"];
let popped = myFish.pop();
console.log(myFish);
// ["angel", "clown", "mandarin"]
console.log(popped);
// surgeon</code></pre>
<h4>Array.push()</h4>
<p>push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度</p>
<ul><li>push 方法有意具有通用性。该方法和 call() 或 apply() 一起使用时,可应用在类似数组的对象上。push 方法根据 length 属性来决定从哪里开始插入给定的值。如果 length 不能被转成一个数值,则插入的元素索引为 0,包括 length 不存在时。当 length 不存在时,将会创建它。</li></ul>
<h4>Array.reduce()</h4>
<p>reduce() 方法对数组中的每个元素执行一个提供的reducer函数(升序执行),将其结果汇总为单个返回值</p>
<pre><code class="js">const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15</code></pre>
<p>reducer 函数接收4个参数:</p>
<p>Accumulator (acc) (累计器)<br>Current Value (cur) (当前值)<br>Current Index (idx) (当前索引)<br>Source Array (src) (源数组)<br>您的 reducer 函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。</p>
<pre><code class="js">var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) {
return accumulator + currentValue;
}, 0);
// 和为 6</code></pre>
<pre><code class="js">var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(function (accumulator, currentValue) {
return accumulator + currentValue.x;
},initialValue)
console.log(sum) // logs 6</code></pre>
<pre><code class="js">var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
function(a, b) {
return a.concat(b);
},
[]
);
// flattened is [0, 1, 2, 3, 4, 5]</code></pre>
<pre><code class="js">var people = [
{ name: 'Alice', age: 21 },
{ name: 'Max', age: 20 },
{ name: 'Jane', age: 20 }
];
function groupBy(objectArray, property) {
return objectArray.reduce(function (acc, obj) {
var key = obj[property];
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(obj);
return acc;
}, {});
}
var groupedPeople = groupBy(people, 'age');
// groupedPeople is:
// {
// 20: [
// { name: 'Max', age: 20 },
// { name: 'Jane', age: 20 }
// ],
// 21: [{ name: 'Alice', age: 21 }]
// }</code></pre>
<pre><code class="js">let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4];
let result = arr.sort().reduce((init, current)=>{
if(init.length===0 || init[init.length-1]!==current){
init.push(current);
}
return init;
}, []);
console.log(result); //[1,2,3,4,5]
</code></pre>
<h4>Array.reduceRight()</h4>
<p>reduceRight() 方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值</p>
<p>reduceRight 为数组中每个元素调用一次 callback 回调函数,但是数组中被删除的索引或从未被赋值的索引会跳过。回调函数接受四个参数:初始值(或上次调用回调的返回值)、当前元素值、当前索引,以及调用 reduce 的数组</p>
<pre><code class="js">var a = ['1', '2', '3', '4', '5'];
var left = a.reduce(function(prev, cur) { return prev + cur; });
var right = a.reduceRight(function(prev, cur) { return prev + cur; });
console.log(left); // "12345"
console.log(right); // "54321"</code></pre>
<h4>Array.reverse()</h4>
<p>reverse() 方法将数组中元素的位置颠倒。</p>
<pre><code class="js">var myArray = ['one', 'two', 'three'];
myArray.reverse();
console.log(myArray) // ['three', 'two', 'one']</code></pre>
<h4>Array.shift()</h4>
<p>shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度</p>
<p>shift 方法移除索引为 0 的元素(即第一个元素),并返回被移除的元素,其他元素的索引值随之减 1。如果 length 属性的值为 0 (长度为 0),则返回 undefined。</p>
<p>shift 方法并不局限于数组:这个方法能够通过 call 或 apply 方法作用于类似数组的对象上。但是对于没有 length 属性(从0开始的一系列连续的数字属性的最后一个)的对象,调用该方法可能没有任何意义。</p>
<pre><code class="js">let myFish = ['angel', 'clown', 'mandarin', 'surgeon'];
console.log('调用 shift 之前: ' + myFish);
// "调用 shift 之前: angel,clown,mandarin,surgeon"
var shifted = myFish.shift();
console.log('调用 shift 之后: ' + myFish);
// "调用 shift 之后: clown,mandarin,surgeon"
console.log('被删除的元素: ' + shifted);
// "被删除的元素: angel"</code></pre>
<h4>Array.slice()</h4>
<p>slice() 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。</p>
<pre><code class="js">arr.slice();
// [0, end]
arr.slice(begin);
// [begin, end]
arr.slice(begin, end);
// [begin, end)</code></pre>
<h4>Array.some()</h4>
<p>some() 方法测试数组中的某些元素是否通过由提供的函数实现的测试</p>
<p>some() 为数组中的每一个元素执行一次 callback 函数,直到找到一个使得 callback 返回一个“真值”(即可转换为布尔值 true 的值)。如果找到了这样一个值,some() 将会立即返回 true。否则,some() 返回 false。callback 只会在那些”有值“的索引上被调用,不会在那些被删除或从来未被赋值的索引上调用。</p>
<p>callback 被调用时传入三个参数:元素的值,元素的索引,被遍历的数组。</p>
<p>将会把它传给被调用的 callback,作为 this 值。否则,在非严格模式下将会是全局对象,严格模式下是 undefined。</p>
<p>some() 被调用时不会改变数组。</p>
<p>some() 遍历的元素的范围在第一次调用 callback. 时就已经确定了。在调用 some() 后被添加到数组中的值不会被 callback 访问到。如果数组中存在且还未被访问到的元素被 callback 改变了,则其传递给 callback 的值是 some() 访问到它那一刻的值。</p>
<pre><code class="js">function isBiggerThan10(element, index, array) {
return element > 10;
}
[2, 5, 8, 1, 4].some(isBiggerThan10); // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true</code></pre>
<h4>Array.sort()</h4>
<p>sort() 方法用原地算法对数组的元素进行排序,并返回数组。排序算法现在是稳定的。默认排序顺序是根据字符串Unicode码点。</p>
<h4>Array.splice()</h4>
<p>splice()方法通过删除现有元素和/或添加新元素来修改数组,并以数组返回原数组中被修改的内容。</p>
<pre><code class="js">var myFish = ["angel", "clown", "mandarin", "surgeon"];
//从第 2 位开始删除 0 个元素,插入 "drum"
var removed = myFish.splice(2, 0, "drum");
//运算后的 myFish:["angel", "clown", "drum", "mandarin", "surgeon"]
//被删除元素数组:[],没有元素被删除</code></pre>
<h4>Array.toLocaleString()</h4>
<p>toLocaleString() 返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ",")隔开。</p>
<pre><code class="js">var prices = ['¥7', 500, 8123, 12];
prices.toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' });
// "¥7,¥500,¥8,123,¥12"</code></pre>
<h4>Array.toString()</h4>
<h4>Array.unshift()</h4>
<p>unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度。</p>
<h4>Array.values()</h4>
<p>values() 方法返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值</p>
React生命周期-踩坑记_10
https://segmentfault.com/a/1190000017198326
2018-11-29T22:19:49+08:00
2018-11-29T22:19:49+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
0
<h2>React生命周期</h2>
<h3>生命周期概览</h3>
<h4>生命周期的状态</h4>
<p>组件的生命周期可分成三个状态:</p>
<ul>
<li>Mounting:已插入真实 DOM</li>
<li>Updating:正在被重新渲</li>
<li>Unmounting:已移出真实 DOM</li>
<li>componentWillMount 在渲染前调用,在客户端也在服务端。</li>
</ul>
<h4>生命周期介绍</h4>
<h5>componentDidMount :</h5>
<p>在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。</p>
<h5>componentWillReceiveProps</h5>
<p>在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。</p>
<h5>shouldComponentUpdate</h5>
<p>返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 <br>可以在你确认不需要更新组件时使用。</p>
<h5>componentWillUpdate</h5>
<p>在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。</p>
<h5>componentDidUpdate</h5>
<p>在组件完成更新后立即调用。在初始化时不会被调用。</p>
<h5>componentWillUnmount</h5>
<p>在组件从 DOM 中移除之前立刻被调用。</p>
<h5>代码示意</h5>
<pre><code class="js">class Content extends React.Component {
componentWillMount() {
console.log('Component WILL MOUNT!')
}
componentDidMount() {
console.log('Component DID MOUNT!')
}
componentWillReceiveProps(newProps) {
console.log('Component WILL RECEIVE PROPS!')
}
shouldComponentUpdate(newProps, newState) {
return true;
}
componentWillUpdate(nextProps, nextState) {
console.log('Component WILL UPDATE!');
}
componentDidUpdate(prevProps, prevState) {
console.log('Component DID UPDATE!')
}
componentWillUnmount() {
console.log('Component WILL UNMOUNT!')
}
</code></pre>
<h3>React16.3生命周期</h3>
<h4>安装</h4>
<p>在创建组件的实例并将其插入DOM时,将按以下顺序调用这些方法:</p>
<h5><strong>constructor()</strong></h5>
<p>React组件的构造函数在安装之前被调用。在实现React.Component子类的构造函数时,应该super(props)在任何其他语句之前调用。否则,this.props将在构造函数中未定义,这可能导致错误。</p>
<p>通常,在React中,构造函数仅用于两个目的:</p>
<p>通过分配对象来初始化本地状态this.state。<br>将事件处理程序方法绑定到实例。<br>不应该打电话setState()给constructor()。相反,如果您的组件需要使用本地状态,请直接在构造函数中指定初始状态this.state。</p>
<p><strong>构造函数是his.state直接分配的唯一位置。在所有其他方法中,需要使用this.setState()。</strong></p>
<h5>static getDerivedStateFromProps()</h5>
<p>getDerivedStateFromProps在调用render方法之前调用,无论是在初始安装还是后续更新。它应该返回一个更新状态的对象,或者返回null以不更新任何状态。</p>
<h5><strong>render()</strong></h5>
<p>render()方法是类组件中唯一必需的方法。</p>
<p>调用时,它应检查this.props并this.state返回以下类型之一:</p>
<ul>
<li>React elements。通常通过JSX创建。</li>
<li>Arrays and fragments。让您从渲染中返回多个元素。有关更多详细信息,请参阅片段文档。</li>
<li>Portals。</li>
<li>字符串和数字。它们在DOM中呈现为文本节点。</li>
<li>布尔或null。什么都没有。</li>
</ul>
<p>该render()函数应该无状态的,这意味着它不会修改组件状态,每次调用时都返回相同的结果,并且它不直接与浏览器交互。</p>
<p>如果您需要与浏览器进行交互,请执行componentDidMount()或其他生命周期方法。保持render()纯粹使组件更容易思考。</p>
<p><em>如果shouldComponentUpdate()返回false,则render()不会被调用</em></p>
<h5><strong>componentDidMount()</strong></h5>
<ul>
<li>componentDidMount()在安装组件(插入树中)后立即调用。需要DOM节点的初始化应该放在这里。<strong><em>如果需要从远程端点加载数据</em>,这是实例化网络请求的好地方。</strong>
</li>
<li>此方法是设置任何订阅的好地方。如果您这样做,请不要忘记取消订阅componentWillUnmount()。</li>
<li>您可以在componentDidMount()立即使用this.setState()。它将触发额外的渲染,但它将在浏览器更新屏幕之前发生。这保证即使render()在这种情况下将被调用两次,用户也不会看到中间状态。请谨慎使用此模式,因为它通常会导致性能问题。在大多数情况下,您应该能够分配初始状态constructor()。但是,当您需要在渲染依赖于其大小或位置的东西之前测量DOM节点时,可能需要对模态和工具提示等情况进行处理。</li>
</ul>
<p>这些方法被认为是遗留的,应该在新代码中避免它们:</p>
<p><em>UNSAFE_componentWillMount()</em></p>
<h4>更新</h4>
<p>props or state 的更改可能导致更新。重新渲染组件时,将按以下顺序调用这些方法:</p>
<h5>static getDerivedStateFromProps()</h5>
<h5>render()</h5>
<h5>getSnapshotBeforeUpdate()</h5>
<p>getSnapshotBeforeUpdate(prevProps, prevState)</p>
<p>getSnapshotBeforeUpdate()在最近呈现的输出被提交到例如DOM之前调用。它使得组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。</p>
<p>此用例并不常见,但它可能出现在需要以特殊方式处理滚动位置的聊天线程等UI中。</p>
<p><strong><em>官网的例子</em></strong></p>
<pre><code class="js">class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the scroll position so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
// (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}</code></pre>
<h4>componentDidUpdate()</h4>
<p>componentDidUpdate()更新发生后立即调用。初始渲染不会调用此方法。</p>
<p>将此作为在更新组件时对DOM进行操作的机会。只要您将当前道具与之前的道具进行比较(例如,如果道具未更改,则可能不需要网络请求),这也是进行网络请求的好地方。</p>
<pre><code class="js">componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}</code></pre>
<p>componentDidUpdate()但要注意,必须在一个条件下被包裹就像上面的例子中,否则会导致无限循环。它还会导致额外的重新渲染,虽然用户不可见,但会影响组件性能。</p>
<p><strong>componentDidUpdate():如果shouldComponentUpdate()返回false,则不会被调用。</strong></p>
<p>这些方法被认为是遗留的,您应该在新代码中避免它们:</p>
<p><em>UNSAFE_componentWillUpdate()</em></p>
<p><em>UNSAFE_componentWillReceiveProps()</em></p>
<h4>卸载</h4>
<p>从DOM中删除组件时调用此方法:</p>
<h5>componentWillUnmount()</h5>
<p>componentWillUnmount()在卸载和销毁组件之前立即调用。在此方法中执行任何必要的清理,例如使计时器无效,取消网络请求或清除在其中创建的任何订阅componentDidMount()。</p>
<p>不应该调用setState(),componentWillUnmount()因为组件永远不会被重新呈现。卸载组件实例后,将永远不会再次安装它。</p>
<h4>错误处理</h4>
<p>在渲染期间,生命周期方法或任何子组件的构造函数中发生错误时,将调用这些方法。</p>
<h5>static getDerivedStateFromError()</h5>
<p>static getDerivedStateFromError(error)</p>
<p>在后代组件抛出错误后调用此生命周期。它接收作为参数抛出的错误,并应返回值以更新状态。</p>
<h5>componentDidCatch()</h5>
<p>componentDidCatch(error, info)</p>
<p>在后代组件抛出错误后调用此生命周期。它接收两个参数:</p>
<p>error - 抛出的错误。<br>info- componentStack包含键的对象,其中包含有关哪个组件引发错误的信息。</p>
<p>如果发生错误,可以componentDidCatch()通过调用呈现回退UI setState,但在将来的版本中将不推荐使用。使用static getDerivedStateFromError()处理回退,而不是渲染。</p>
<p>componentDidCatch()在“提交”阶段被调用,因此允许副作用。它应该用于记录错误之类的事情:</p>
<pre><code>class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
logComponentStackToMyService(info.componentStack);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
</code></pre>
<p><img src="/img/remote/1460000017198329" alt="image" title="image"></p>
<p><img src="/img/remote/1460000017198330" alt="image" title="image"></p>
<blockquote>
<a href="https://link.segmentfault.com/?enc=P%2FC93pR%2FbkK6xK%2BBMFW3Wg%3D%3D.uIgbwhBU5zsw9%2Be0b5EZk%2Bi2Sh8ClWCEDamPSpp9NkPN%2B4XVMs960SBp8n3IJrLqWta5YEGJ2FuWKtmM2isd0A%3D%3D" rel="nofollow">http://projects.wojtekmaj.pl/...</a><p><a href="https://link.segmentfault.com/?enc=k2XrIL0nqOHH9%2FLgKGqgew%3D%3D.cY0SArznvgW3v3bDSNtaB%2BjrwZ4BKrFuMKDxFygCbsey3ulJX3HzVnGLHkP0PXZ45G7ceXRqaqB9ZHGJoLAa7xzUn2MIcC2H3EdEcghLQJw%3D" rel="nofollow">https://reactjs.org/blog/2018...</a></p>
<p><a href="https://link.segmentfault.com/?enc=z4id2aBfhOmZnY3%2BwjqjnA%3D%3D.SdTLWNx%2FfTylmRqCAIOCpXgsfg29Nm4OdI2SdCLQYdHWB8RkmrfaZZGxrmiVrnGF9WStEKbrA2x7MMLhRSL3DA%3D%3D" rel="nofollow">https://reactjs.org/docs/reac...</a></p>
</blockquote>
sessionStorge和localStorage的使用_09
https://segmentfault.com/a/1190000017147116
2018-11-26T16:29:03+08:00
2018-11-26T16:29:03+08:00
xiaoping
https://segmentfault.com/u/xiaoping_5b9bd7cd1b5c6
1
<h2>sessionStorge的使用</h2>
<p>sessionStorage 属性允许你访问一个 session Storage 对象。它与 localStorage 相似,不同之处在于 localStorage 里面存储的数据没有过期时间设置,而存储在 sessionStorage 里面的数据在页面会话结束时会被清除。页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话。在新标签或窗口打开一个页面时会在顶级浏览上下文中初始化一个新的会话,这点和 session cookies 的运行方式不同</p>
<h3>语法</h3>
<pre><code class="js">// 保存数据到sessionStorage
sessionStorage.setItem('key', 'value');
// 从sessionStorage获取数据
var data = sessionStorage.getItem('key');
// 从sessionStorage删除保存的数据
sessionStorage.removeItem('key');
// 从sessionStorage删除所有保存的数据
sessionStorage.clear();</code></pre>
<h3>例子</h3>
<pre><code class="js">// 获取文本输入框
var field = document.getElementById("field");
// 检测是否存在 autosave 键值
// (这个会在页面偶然被刷新的情况下存在)
if (sessionStorage.getItem("autosave")) {
// 恢复文本输入框的内容
field.value = sessionStorage.getItem("autosave");
}
// 监听文本输入框的 change 事件
field.addEventListener("change", function() {
// 保存结果到 sessionStorage 对象中
sessionStorage.setItem("autosave", field.value);
});</code></pre>
<h2>localStorage的使用</h2>
<p>只读的localStorage 允许你访问一个Document 的远端(origin)对象 Storage;数据存储为跨浏览器会话。 localStorage 类似于sessionStorage。区别在于,数据存储在 localStorage 是无期限的,而当页面会话结束——也就是说当页面被关闭时,数据存储在sessionStorage 会被清除</p>
<h3>语法</h3>
<pre><code class="js">//设定
localStorage.setItem('myCat', 'Tom')
//读取
let cat = localStorage.getItem('myCat');
//移除
localStorage.removeItem('myCat');
// 移除所有
localStorage.clear();</code></pre>
<h3>例子</h3>
<pre><code class="js">function setStyles() {
var currentColor = localStorage.getItem('bgcolor');
var currentFont = localStorage.getItem('font');
var currentImage = localStorage.getItem('image');
document.getElementById('bgcolor').value = currentColor;
document.getElementById('font').value = currentFont;
document.getElementById('image').value = currentImage;
htmlElem.style.backgroundColor = '#' + currentColor;
pElem.style.fontFamily = currentFont;
imgElem.setAttribute('src', currentImage);
}</code></pre>