群组信息 私有

administrators

 

成员列表

  • React16后的新功能

    软件版本通常分3级:

    • 第一个数字代表大版本
    • 第二个数字代表小版本
    • 第三个数字代表小打小闹 - bug fixes等

    V15 到 V16 是大版本升级,惊心动魄的新功能不少:

    1. fragments - 曾几何时,render()必须且只能返回一个node,因此,你不得不加上一些不必要的<div>把多个node包起来。fragments 让你可以直接render一个数组。这个配上map真是太完美了。注意:key是需要的,这个有点烦。所以v16.2.0后,我们可以用特殊字符<>, 例如:<><li /><li /><>,这样可以就可省略了。

    2. 对了,你可以直接返回字符串了 - 不由再次感叹:曾几何时,render()必须且只能返回一个node

    3. contextAPI - 就是说createContext之后,可以在一个component里提供provider,例外一个component里通过consumer来读取
      祝:这个我基本上没用过 - 因为我要吗直接传props,或者用其他state管理,例如:redux或者mobx等。

    4. createRef API - 这个在不受控制的form还是很有用的。受控制指通过react state来控制form的字。

    5. 新的lifecycles

    • getDerivedStateFromProps
    • getSnashotBeforeUpdate

    注意:几个will的lifecycles, 到V17后就没有life了。所以,大家有空还是看看上面两个新的lifecyles吧。

    1. StrictMode - 用了发现下面几个潜在问题
    • Identifying components with unsafe lifecycles
    • Warning about legacy string ref API usage
    • Detecting unexpected side effects
    1. React.memo - 用来替代PureComponent 或者 shouldComponentUpdate, memo让component变存, 或者说只有props变才重新render。如只是父级component变了,memo子component不会重新render

    2. React.lazy - 用来实现code-split - 基本是react loadable的复制版。

    下面几个还没出:

    1. Hooks - 让functional component 可以使用 state 通过 useState()

    2. suspend

    3. concurrent rendering

    10和11 基本上是用了提供弱CPU和慢网速用户的用户体验。大概意思:hold住render,知道数据ready,这样对网速快的用户少了loading icon,对慢用户仍然有repsond。这个概念有点难,等真正开始用了,我们再深入!

    发布在 React
  • 让footer黏在页面最下方的最佳方案

    这个问题困扰我很久,每次我都要Google找答案,虽然每次都可以搞定,但是回头又忘记当时是怎么做的了。几天又上网搜了搜,就得这个一定是最佳方案了!记下,记下!

    HTML

    <!doctype html>
    <html lang="en">
    
    <head>
      <!-- head content -->
    </head>
    
    <body>
      <header>
        <!-- header content -->
        <h1>Hello header!</h1>
      </header>
      <main>
        <!-- page content -->
        <p>Hello main content!</p>
      </main>
      <footer>
        <!-- footer content -->
        <p>
          I like sitting at the bottom :-)</p>
      </footer>
    </body>
    

    CSS

    body {
      display: flex;
      flex-direction: column;
      min-height: 100vh;
      margin: 0 /** this is super important **/
    }
    footer {
      margin: auto auto 0 auto;
    }
    

    演示

    https://codepen.io/adamcai/pen/YBXKMQ?editors=1100

    发布在 笔记
  • RE: 推荐阅读 发布在 笔记
  • 鼠标选择加亮 发布在 笔记
  • Promise - Part 7 of 函数式编程

    Promise (承诺) 函数用于实现callback的功能,即在一个函数结束后调用另外一个函数。
    比如下面这个例子:

    function do_thing1(){
      setTimeout( function(){
        console.log( 'do thing1' );
      }, 1000 );
    }
     
    function do_thing2(){
      console.log( 'do thing2' );
    }
     
    do_thing1();
    do_thing2();
    

    这里我们先调用do_thing1因为我们希望do_thing2do_thing1结束后再执行,但是do_thing2没有等!

    我们可以用传统的callback来解决这个问题如下:

    function do_thing1(callback){
      setTimeout( function(){
        console.log( 'do thing1' );
        callback && callback()
      }, 1000 );
    }
     
    function do_thing2(){
      console.log( 'do thing2' );
    }
    do_thing1(do_thing2);
    

    我把do_thing2当做一个变量传给do_thing1,等do_thing1结束后来调用do_thing2。

    接下来,我们用Promise来解决这个问题:

    const do_thing1b = new Promise((resolve, reject) =>
      setTimeout(() => {
        console.log("do_thing1b");
        resolve("done");
      }, 1000)
    );
    
    const do_thing2b = () => {
      console.log("do thing2b");
    };
    
    do_thing1b.then(() => do_thing2b());
    

    因为do_thing1b现在是个Promise,所以我们可以用then来串接其他函数,即do_thing2,这样就确保do_thing2b一定在do_thing1b结束后才被调用。这就"承诺"!

    发布在 香草Javascript🌿
  • 微软浪费我不少的时间

    用Mac快4年了,偶尔到PC上除了一点东西,我擦,那个慢啊!不比不知道,我们在Windows上浪费了多少时间!

    发布在 笔记
  • Recursion - Part 6 of 函数式编程

    递归函数指自己调用自己直到被停止。
    说起Recursion - 递归,你如果学过计算机科学, 那么一定会想起斐波那契。(1,1,2,3,5,8,...)

    function feibo(n) {
      if (n === 1) {
        return 1;
      } else if (n < 1) {
        return 0;
      } else {
        return feibo(n - 1) + feibo(n - 2);
      }
    }
    

    其实在我的前端工作中,很少涉及斐波那契这样的算法题。那么需要前端需要递归吗?看看下面这个例子,

    const categories = [
      { id: "animals", parent: null },
      { id: "mammals", parent: "animals" },
      { id: "cats", parent: "mammals" },
      { id: "dogs", parent: "mammals" },
      { id: "aHuang", parent: "dogs" },
      { id: "aMi", parent: "cats" },
      { id: "aTu", parent: "dogs" },
      { id: "aMao", parent: "cats" }
    ];
    
    const makeTree = (categories, parent) => {
      let node = {}; //这个let很重要,它是block scoped的,也就是说下一次进入这个block,新的node被产生
      categories
        .filter(c => c.parent === parent)
        .forEach(item => (node[item.id] = makeTree(categories, item.id)));
      return node;
    };
    
    console.log(JSON.stringify(makeTree(categories, null), null, 2));
    

    结果如下:

    {
      "animals": {
        "mammals": {
          "cats": {
            "aMi": {},
            "aMao": {}
          },
          "dogs": {
            "aHuang": {},
            "aTu": {}
          }
        }
      }
    }
    

    这里我们把一组数据转换成树状结构,按照父系相同归类。这个如果用for loop,代码一定很乱,makeTree这么写还是挺酷的吧?

    总而言之,递归的作用是简化代码 - 这也是函数式编程的中心思想!

    看Pen: https://codepen.io/adamcai/pen/LMEdgR

    发布在 香草Javascript🌿
  • Curry - Part 5 of 函数式编程

    Curry (咖喱)函数 只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。如果你不知道我在说什么,没关系,一步一步来:
    首先看一个普通函数:

    function person = (name, height, weight) {
      return name + '身高' + height + ', 体重' + weight;
    }
    

    用ES6箭头函数可以简写为:

    const person = (name, height, weight) =>
      name + '身高' + height + ', 体重' + weight;
    
    console.log(person('Tom', '1米78', '150公斤'));
    //Tom身高1米78, 体重150公斤
    

    如果你不知道为什么可以这么写,请查看[这里]。(https://j4fun.com/topic/48/map-arrow函数的简写-part-2-of-函数式编程)
    接下来,我们把以上函数用咖喱函数来表示:

    function curryPersonFunction (name) {
      return function personHeightWeight(height, weight) {
        return name + '身高' + height + ', 体重' + weight;
      }
    }
    

    注意:curryPersonFunction()函数现在只接一个参数name,然后返回一个函数personHeightWeight()。这么一来,我们可以用下面的函数调用来实现上面普通函数需要实现的结果:

    const tom = curryPersonFunction('Tom'); //tom 在这里是一个函数
    console.log(tom('1米78', '150公斤'));
    //Tom身高1米78, 体重150公斤
    

    以上的例子或许不能解释为什么要用Curry函数,但是发挥点想象力,你可能注意到,我们可以用Curry函数把一部分函数单独分离出来,这样就可以重复使用它来提高代码的可读性。

    比如下面这个例子:

    const data = {
        "user": "adam",
        "posts": [
            { "title": "why curry?", "contents": "curry is cool" },
            { "title": "why functional", "contents": "functional is clean" }
        ]
    };
    
    function getObjProp(propName, obj){
      return obj[propName];
    }
    
    const titles = getObjProp("posts", data).map(post => {
      return getObjProp("title", post)
    });
    
    console.log(titles);
    //["why curry?","why functional"]
    

    上面例子里,getObjProp是一个普通函数,它接受两个参数,一个是属性名,另外一个是object,然后返回这个属性的值。如果把它写成curry函数,在配上“级联”函数,比如map,我们就不用每次都传入object了。

    const get = prop => obj => obj[prop];
    const titles = get('posts')(data).map(get('title'));
    

    本章对于codePen:
    例子一:Curry基础
    例子二:Curry和Map的妙用

    发布在 香草Javascript🌿
  • Reduce - Part 3 of 函数式编程

    虽然reduce, map, filter常常被放在一起介绍,但是reduce的用法比较特殊,也比较难掌握,虽然在函数式编程的第一章,我们介绍过reduce,但是我还是觉得有必要在这里更加深入的介绍一下。

    基本上我可以用下面表达式来描述reduce

    const arr=[1,2,3];
    function f (a, b) { return a + b; }
    
    arr.reduce (f) = f(f(arr[0], arr[1]), arr[2]);
    

    分解开来,就是:

    const result = f(arr[0], arr[1]) = f(1, 2) = 3;
    result = f(result, arr[2]) = f(3,3) = 6;
    

    其实reduce还有第二个参数,也就是所谓的初始值,如果我们定义了这个值,那么,第一次传入给处理函数的参数就是不是数组的前两个成员,而是,这个初始值和数组的第一个成员。表达式如下:

    arr.reduce(f, INIT) = f(f(f(INIT, arr[0]), arr[1]), arr[2]);
    

    如果数组成员不是简单的数值或者字符串,那是这个初始值就变得很有用。

    看下面这个数组:

    const orders =[
      {amount: 1},
      {amount: 2},
      {amount: 3},
      {amount: 4}
    ]
    

    如果你用上面的reduce来计算总和就行不通了:

    const total = orders.reduce((x,y)=>x.amount + y.amount);
    

    虽然第一次调用求和函数,等到结果 = orders[0].amount + orders[1].amount = 3, 但是第二次我们把3 和 orders[2] 传给求和函数,我们就无法得到正确结果,因为3.amount是不存在的。
    解决这个问题,我们需要用到初始值,如下:

    const result2 = arr.reduce(
      (x,y) => {
        return x + y.amount;
      }, 1
    );
    

    看Pen: https://codepen.io/adamcai/pen/aQXEqB?editors=0010

    发布在 香草Javascript🌿
  • Map+Arrow函数的简写 - Part 2 of 函数式编程

    上一章节,我们学习了高阶函数,其中map就是一个高阶函数,一个非常常用非常实用的高阶函数。
    让我们重温一下这个函数的用法:

    function double (x) {
      return x*2;
    }
           
    const arr = [1,2,3];
    
    //map - return array
    const newArr1 = arr.map(double);
    console.log(newArr1);
    //[2,4,6]
    

    以上写法直观,但是有点"土",特别在这个ES6普遍流行的日子里。以上的函数可以通过Arrow函数,简化为:

    const newArr1 = arr.map(x=>x*2);
    

    什么玩意?是的,对于初学者来说,这个写法有点懵。下面我来解释一下:

    function double(x){
       return x*2;
    }
    //用arrow函数表示:
    const double = (x) => {
       return x*2;
    }
    //因为只有一个参数,所以括号可以省略:
    const double = x => {
       return x*2;
    }
    //又因为,函数返回值可以用一个return搞定,所以花括号又可以省略
    const double = x=> x*2;
    // 注意:在高级函数里,函数可以当做一个值,所以我们可以吧double直接传给map函数
    const newArr1 = arr.map(x=>x*2);
    

    现在应该清楚了吧?重复一下重点

    • 如果arrow函数只有一个参数,那么括号可以省略
    • 如果arrow函数返回值可以用一个return搞定,那么花括号又可以省略
    发布在 香草Javascript🌿