Giter Club home page Giter Club logo

blog's People

Contributors

binperson avatar

Watchers

 avatar  avatar

blog's Issues

Promise

超丑的异步

function add(getX,getY,cb) {
  var x, y;
  getX( function(xVal){
    x = xVal;
// 两个都准备好了?
    if (y != undefined) {
      cb( x + y ); // 发送和
    }
  } );
  getY( function(yVal){
    y = yVal;
// 两个都准备好了?
    if (x != undefined) {
      cb( x + y ); // 发送和
    }
  } );
} // fetchX() 和fetchY()是同步或者异步函数
add( fetchX, fetchY, function(sum){
  console.log( sum ); // 是不是很容易?
} )

发布—订阅模式

发布—订阅自定义事件

  • 首先要指定好谁充当发布者
  • 然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者
  • 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函
var salesOffices = {}; // 定义售楼处
salesOffices.clientList = []; // 缓存列表,存放订阅者的回调函数
salesOffices.listen = function( fn ){ // 增加订阅者
  this.clientList.push( fn ); // 订阅的消息添加进缓存列表
};
salesOffices.trigger = function(){ // 发布消息
  for( var i = 0, fn; fn = this.clientList[ i++ ]; ){
    fn.apply( this, arguments ); // (2) // arguments 是发布消息时带上的参数
  }
};

salesOffices.listen( function( price, squareMeter ){ // 小明订阅消息
  console.log( '价格= ' + price );
  console.log( 'squareMeter= ' + squareMeter );
});
salesOffices.listen( function( price, squareMeter ){ // 小红订阅消息
  console.log( '价格= ' + price );
  console.log( 'squareMeter= ' + squareMeter );
});
salesOffices.trigger( 2000000, 88 ); // 输出: 200 万, 88 平方米
salesOffices.trigger( 3000000, 110 ); // 输出: 300 万, 110 平方米

必要增加一个标示 key,
让订阅者只订阅自己感兴趣的消息

var salesOffices = {}; // 定义售楼处
salesOffices.clientList = {}; // 缓存列表,存放订阅者的回调函数
salesOffices.listen = function( key, fn ){
  if ( !this.clientList[ key ] ){ // 如果还没有订阅过此类消息,给该类消息创建一个缓存列表
    this.clientList[ key ] = [];
  }
  this.clientList[ key ].push( fn ); // 订阅的消息添加进消息缓存列表
};
salesOffices.trigger = function(){ // 发布消息
  var key = Array.prototype.shift.call( arguments ), // 取出消息类型
  fns = this.clientList[ key ]; // 取出该消息对应的回调函数集合
  if ( !fns || fns.length === 0 ){ // 如果没有订阅该消息,则返回
    return false;
  }
  for( var i = 0, fn; fn = fns[ i++ ]; ){
    fn.apply( this, arguments ); // (2) // arguments 是发布消息时附送的参数
  }
};
salesOffices.listen( 'squareMeter88', function( price ){ // 小明订阅 88 平方米房子的消息
  console.log( '价格= ' + price ); // 输出: 2000000
});
salesOffices.listen( 'squareMeter110', function( price ){ // 小红订阅 110 平方米房子的消息
  console.log( '价格= ' + price ); // 输出: 3000000
});
salesOffices.trigger( 'squareMeter88', 2000000 ); // 发布 88 平方米房子的价格
salesOffices.trigger( 'squareMeter110', 3000000 ); // 发布 110 平方米房子的价格

发布- 订阅模式的通用实现

var event = {
  clientList: [],
  listen: function( key, fn ){
    if ( !this.clientList[ key ] ){
      this.clientList[ key ] = [];
    }
    this.clientList[ key ].push( fn ); // 订阅的消息添加进缓存列表
  },
  trigger: function(){
    var key = Array.prototype.shift.call( arguments ), // (1);
    fns = this.clientList[ key ];
    if ( !fns || fns.length === 0 ){ // 如果没有绑定对应的消息
      return false;
    }
    for( var i = 0, fn; fn = fns[ i++ ]; ){
      fn.apply( this, arguments ); // (2) // arguments 是 trigger 时带上的参数
    }
  }
};
var installEvent = function( obj ){
  for ( var i in event ){
    obj[ i ] = event[ i ];
  }
};
var salesOffices = {};
installEvent( salesOffices );
salesOffices.listen( 'squareMeter88', function( price ){ // 小明订阅消息
  console.log( '价格= ' + price );
});
salesOffices.listen( 'squareMeter100', function( price ){ // 小红订阅消息
  console.log( '价格= ' + price );
});
salesOffices.trigger( 'squareMeter88', 2000000 ); // 输出: 2000000
salesOffices.trigger( 'squareMeter100', 3000000 ); // 输出: 3000000

取消订阅的事件

event.remove = function( key, fn ){
  var fns = this.clientList[ key ];
    if ( !fns ){ // 如果 key 对应的消息没有被人订阅,则直接返回
    return false;
  }
  if ( !fn ){ // 如果没有传入具体的回调函数,表示需要取消 key 对应消息的所有订阅
    fns && ( fns.length = 0 );
  }else{
    for ( var l = fns.length - 1; l >=0; l-- ){ // 反向遍历订阅的回调函数列表
      var _fn = fns[ l ];
      if ( _fn === fn ){
        fns.splice( l, 1 ); // 删除订阅者的回调函数
      }
    }
  }
};
var salesOffices = {};
var installEvent = function( obj ){
  for ( var i in event ){
    obj[ i ] = event[ i ];
  }
}
installEvent( salesOffices );
salesOffices.listen( 'squareMeter88', fn1 = function( price ){ // 小明订阅消息
console.log( '价格= ' + price );
});
salesOffices.listen( 'squareMeter88', fn2 = function( price ){ // 小红订阅消息
console.log( '价格= ' + price );
});
salesOffices.remove( 'squareMeter88', fn1 ); // 删除小明的订阅
salesOffices.trigger( 'squareMeter88', 2000000 ); // 输出: 2000000

webpack

Resolve

webpack在启动后会从配置的入口模块触发找出所有依赖的模块,Resolve配置webpack如何寻找模块对应的文件。webpack内置JavaScript模块化语法解析功能,默认会采用模块化标准里约定好的规则去寻找,但你可以根据自己的需要修改默认的规则。

1. alias

resolve.alias配置项通过别名来把原来导入路径映射成一个新的导入路径。如下:

//webpack alias配置
resolve: {
    alias: {
        componets: './src/components/'
    }
}

当你通过import Button from 'components/button'导入时,实际上被alias等价替换成import Button from './src/components/button'。以上alias配置的含义是把导入语句里的components关键字替换成./src/components。这样做可能会命中太多的导入语句, alias还支持$符号来缩小范围只命中以关键字结尾的导入语句:

resolve: {
    alias: {
        'react$' : '/path/to/react.min.js'
    }
}

这样react$只会命中以react结尾的导入语句,即只会把import react关键字替换成import '/path/to/react.min.js'

virtual dom

什么virtual dom

  • virtual dom , 虚拟 DOM
  • 用 JS 模拟 DOM 结构
  • DOM 变化的对比,放在 JS 层来做(图灵完备语言)
  • 提高重绘性能
<div class="list">
    <li class="item">item1</li>
    <li class="item">item2</li>
</div>
{
    tag: 'ul',
    attrs: {
        id: 'list'
    },
    children: [
        {
            tag: 'li',
            attrs: {className: 'item'},
            children: ['item1']
        },
        {
            tag: 'li',
            attrs: {className: 'item'},
            children: ['item2']
        }
    ]
}

一个需求

//1.将该数据展示成一个表格。2.随便修改一个信息,表格也跟着修改
[
    {
        name: '张三',
        age: '20',
        address: '北京'
    },
    {
        name: '李四',
        age: '21',
        address: '上海'
    },
    {
        name: '王五',
        age: '22',
        address: '广州'
    }
]
<body>
    <div id="container"></div>
    <button id="btn-change">change</button>

    <script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.2.0/jquery.js"></script>
    <script type="text/javascript">
        var data = [
            {
                name: '张三',
                age: '20',
                address: '北京'
            },
            {
                name: '李四',
                age: '21',
                address: '上海'
            },
            {
                name: '王五',
                age: '22',
                address: '广州'
            }
        ]

        // 渲染函数
        function render(data) {
            var $container = $('#container')

            // 清空容器,重要!!!
            $container.html('')

            // 拼接 table
            var $table = $('<table>')

            $table.append($('<tr><td>name</td><td>age</td><td>address</td>/tr>'))
            data.forEach(function (item) {
                $table.append($('<tr><td>' + item.name + '</td><td>' + item.age + '</td><td>' + item.address + '</td>/tr>'))
            })

            // 渲染到页面
            $container.append($table)
        }

        $('#btn-change').click(function () {
            data[1].age = 30
            data[2].address = '深圳'
            // re-render  再次渲染
            render(data)
        })

        // 页面加载完立刻执行(初次渲染)
        render(data)

    </script>
</body>

遇到的问题

var div = document.createElement('div');
var item, result = '';
for(item in div){
    result += '|' + item;
}
console.log(result);
/*
|align|title|lang|translate|dir|dataset|hidden|tabIndex|accessKey|draggable|spellcheck|contentEditable|isContentEditable|offsetParent|offsetTop|offsetLeft|offsetWidth|offsetHeight|style|innerText|outerText|onabort|onblur|oncancel|oncanplay|oncanplaythrough|onchange|onclick|onclose|oncontextmenu|oncuechange|ondblclick|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|ondurationchange|onemptied|onended|onerror|onfocus|oninput|oninvalid|onkeydown|onkeypress|onkeyup|onload|onloadeddata|onloadedmetadata|onloadstart|onmousedown|onmouseenter|onmouseleave|onmousemove|onmouseout|onmouseover|onmouseup|onmousewheel|onpause|onplay|onplaying|onprogress|onratechange|onreset|onresize|onscroll|onseeked|onseeking|onselect|onstalled|onsubmit|onsuspend|ontimeupdate|ontoggle|onvolumechange|onwaiting|onwheel|onauxclick|ongotpointercapture|onlostpointercapture|onpointerdown|onpointermove|onpointerup|onpointercancel|onpointerover|onpointerout|onpointerenter|onpointerleave|nonce|click|focus|blur|namespaceURI|prefix|localName|tagName|id|className|classList|slot|attributes|shadowRoot|assignedSlot|innerHTML|outerHTML|scrollTop|scrollLeft|scrollWidth|scrollHeight|clientTop|clientLeft|clientWidth|clientHeight|onbeforecopy|onbeforecut|onbeforepaste|oncopy|oncut|onpaste|onsearch|onselectstart|previousElementSibling|nextElementSibling|children|firstElementChild|lastElementChild|childElementCount|onwebkitfullscreenchange|onwebkitfullscreenerror|setPointerCapture|releasePointerCapture|hasPointerCapture|hasAttributes|getAttributeNames|getAttribute|getAttributeNS|setAttribute|setAttributeNS|removeAttribute|removeAttributeNS|hasAttribute|hasAttributeNS|getAttributeNode|getAttributeNodeNS|setAttributeNode|setAttributeNodeNS|removeAttributeNode|closest|matches|webkitMatchesSelector|attachShadow|getElementsByTagName|getElementsByTagNameNS|getElementsByClassName|insertAdjacentElement|insertAdjacentText|insertAdjacentHTML|requestPointerLock|getClientRects|getBoundingClientRect|scrollIntoView|scrollIntoViewIfNeeded|animate|before|after|replaceWith|remove|prepend|append|querySelector|querySelectorAll|webkitRequestFullScreen|webkitRequestFullscreen|scroll|scrollTo|scrollBy|createShadowRoot|getDestinationInsertionPoints|ELEMENT_NODE|ATTRIBUTE_NODE|TEXT_NODE|CDATA_SECTION_NODE|ENTITY_REFERENCE_NODE|ENTITY_NODE|PROCESSING_INSTRUCTION_NODE|COMMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE|DOCUMENT_FRAGMENT_NODE|NOTATION_NODE|DOCUMENT_POSITION_DISCONNECTED|DOCUMENT_POSITION_PRECEDING|DOCUMENT_POSITION_FOLLOWING|DOCUMENT_POSITION_CONTAINS|DOCUMENT_POSITION_CONTAINED_BY|DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC|nodeType|nodeName|baseURI|isConnected|ownerDocument|parentNode|parentElement|childNodes|firstChild|lastChild|previousSibling|nextSibling|nodeValue|textContent|hasChildNodes|getRootNode|normalize|cloneNode|isEqualNode|isSameNode|compareDocumentPosition|contains|lookupPrefix|lookupNamespaceURI|isDefaultNamespace|insertBefore|appendChild|replaceChild|removeChild|addEventListener|removeEventListener|dispatchEvent
*/
  • DOM 操作是“昂贵”的,js 运行效率高,将 DOM 对比操作放在 JS 层,提高效率
  • 尽量减少 DOM 操作,而不是“推倒重来”
  • 项目越复杂,影响就越严重
  • vdom 即可解决这个问题

MVVM

jQuery 实现 todo-list

<body>
    <div>
        <input type="text" name="" id="txt-title">
        <button id="btn-submit">submit</button>
    </div>
    <div>
        <ul id="ul-list"></ul>
    </div>
    
    <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
    <script type="text/javascript">
        var $txtTitle = $('#txt-title')
        var $btnSubmit = $('#btn-submit')
        var $ulList = $('#ul-list')
        $btnSubmit.click(function () {
            var title = $txtTitle.val()
            if (!title) {
                return
            }
            var $li = $('<li>' + title + '</li>')
            $ulList.append($li)
            $txtTitle.val('')
        })
    </script>
</body>

vue 实现 todo-list

<head>
    <meta charset="UTF-8">
    <title>to-do-list by vue</title>
    <script src="./vue-2.5.13.js"></script>
</head>
<body>
    <div id="app">
        <div>
            <input v-model="title">
            <button v-on:click="add">submit</button>
        </div>
        <div>
            <ul>
                <li v-for="item in list">{{item}}</li>
            </ul>
        </div>
    </div>

    <script type="text/javascript">
        // data 独立
        var data = {
            title: '',
            list: []
        }
        // 初始化 Vue 实例
        var vm = new Vue({
            el: '#app',
            data: data,
            methods: {
                add: function () {
                    this.list.push(this.title)
                    this.title = ''
                }
            }
        })
    </script>
</body>

两者的区别

  • 数据和视图的分离,解耦(开放封闭原则)
  • 以数据驱动视图,只关心数据变化,DOM 操作被封装

MVC

image

  • M - Model 数据
  • V - View 视图、界面
  • C - Controller 控制器、逻辑处理

image
image

MVVM

image

  • Model - 模型、数据
  • View - 视图、模板(视图和模型是分离的)
  • ViewModel - 连接 Model 和 View

MVVM 框架的三大要素

  • 响应式:vue 如何监听到 data 的每个属性变化?
  • 模板引擎:vue 的模板如何被解析,指令如何处理?
  • 渲染:vue 的模板如何被渲染成 html ?以及渲染过程

diff 算法

diff算法

  • DOM 操作是“昂贵”的,因此尽量减少 DOM 操作
  • 找出本次 DOM 必须更新的节点来更新,其他的不更新
  • 这个“找出”的过程,就需要 diff 算法

image

diff log1.txt log2.text

git diff xxx

patch(container, vnode)

image
image
image

function createElement(vnode) {
    var tag = vnode.tag  // 'ul'
    var attrs = vnode.attrs || {}
    var children = vnode.children || []
    if (!tag) {
        return null
    }

    // 创建真实的 DOM 元素
    var elem = document.createElement(tag)
    // 属性
    var attrName
    for (attrName in attrs) {
        if (attrs.hasOwnProperty(attrName)) {
            // 给 elem 添加属性
            elem.setAttribute(attrName, attrs[attrName])
        }
    }
    // 子元素
    children.forEach(function (childVnode) {
        // 给 elem 添加子元素
        elem.appendChild(createElement(childVnode))  // 递归
    })

    // 返回真实的 DOM 元素
    return elem
}

patch(vnode, newVnode)

image
image
image
image
image
image

function updateChildren(vnode, newVnode) {
    var children = vnode.children || []
    var newChildren = newVnode.children || []

    children.forEach(function (childVnode, index) {
        var newChildVnode = newChildren[index]
        if (childVnode.tag === newChildVnode.tag) {
            // 深层次对比,递归
            updateChildren(childVnode, newChildVnode)
        } else {
            // 替换
            replaceNode(childVnode, newChildVnode)
        }
    })
}

function replaceNode(vnode, newVnode) {
    var elem = vnode.elem  // 真实的 DOM 节点
    var newElem = createElement(newVnode)

    // 替换
}

more

  • 节点新增和删除
  • 节点重新排序
  • 节点属性、样式、事件变化
  • 如何极致压榨性能
  • ...

diff 实现过程

  • patch(container, vnode) 和 patch(vnode, newVnode)
  • createElment
  • updateChildren

three.js代码结构

three.js代码

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
        typeof define === 'function' && define.amd ? define(['exports'], factory) :
            (factory((global.THREE = {})));
}(this, (function (exports) {

})));

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
        typeof define === 'function' && define.amd ? define(['exports'], factory) :
            (factory((global.THREE = {})));
}(global, factory))

global = this

factory = function (exports) {

    function WebGLRenderTargetCube( width, height, options ) {

        WebGLRenderTarget.call( this, width, height, options );

        this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5
        this.activeMipMapLevel = 0;

    }

    WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype );
    WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube;

    WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true;
    
    
    exports.WebGLRenderTargetCube = WebGLRenderTargetCube;
    //...
    exports.CanvasRenderer = CanvasRenderer;

    Object.defineProperty(exports, '__esModule', { value: true });
}

a = typeof exports === 'object' && typeof module !== 'undefined'
b = factory(exports)
c = typeof define === 'function' && define.amd
e = define(['exports'], factory)
f = (factory((global.THREE = {})))

a ? b : c ? e : f

(function (global, factory) {
    a ? b : c ? e : f
}(global, factory))

node环境下不能打印love

node环境下 if((typeof module !="undefine"&&module.exports)==true)(console.log("love"))不能打印love

首先看这句typeof module !="undefine"&&module.exports,他的运行顺序实际是这样:

((typeof module)  != "undefine") && module.exports

因此,执行过程:

'obect'! = 'undefine' && module.exports
true&&module.exports
module.exports
{}

在MDN的js文档里非严格相等 ==的情况下:

  • 对象(被比较值A)与布尔值(被比较值B)
  • ToPrimitive(A) == ToNumber(B)

因此,返回false

参考链接:

JavaScript 中的相等性判断
JavaScript中,{}+{}等于多少?

webpack+react

什么会有服务端渲染

单页应用存在的问题

  • SEO不友好
  • 首次请求等待时间较长,体验不好

React中如何使用服务端渲染

react-dom是React专门为web端开发的渲染工具。我们可以在客户端使用react-dom的render方法渲染组件,而在服务端,react-dom/server提供我们将react组件渲染成HTML的方法

项目开发时的常用配置

常用配置
  • webpack dev server
  • Hot module replacement

CommonJS规范

参考 CommonJS规范

概述

  • 所有代码都运行在模块作用域,不会污染全局作用域。
  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
  • 模块加载的顺序,按照其在代码中出现的顺序。
//example.js
var x = 5;
var addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;

//加载模块
var example = require('./example.js');
console.log(example.x); // 5
console.log(example.addX(1)); // 6

ES6模块

参考:ECMAScript 6 入门

export 命令

// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export {firstName, lastName, year};

export function multiply(x, y) {
  return x * y;
};

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.