Giter VIP home page Giter VIP logo

fe-note's Introduction

Blog

世界很大, 多踏出一步, 就进步一步。✊!!欢迎技术交流。

fe-note's People

Contributors

nuohui avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

fe-note's Issues

Vue源码: vm.$set原理分析

文中写到“索引是可以劫持get/set的, 但是如果监听索引的话, 如果你push一个元素进来, 那个元素的索引就没有被劫持, 那么就不会是响应式的”,那么如果push的时候也做了劫持处理呢?

手动实现一个bind函数

 Function.prototype.bind = function (ctx) {
        const _otherParam = Array.prototype.slice.call(arguments, 1)
        const _this = this
        return function customBind () {
          const _params = Array.prototype.slice.call(arguments)
          _this.apply(ctx, [].concat(_otherParam, _params))
        }
      }

Vue源码: Vue拦截变异数组方法的思路

  • array.js
/*
 * not type checking this file because flow doesn't play well with
 * dynamically accessing methods on Array prototype
 */

import { def } from '../util/index'
// 数组原型
const arrayProto = Array.prototype
// 数组代理原型对象
export const arrayMethods = Object.create(arrayProto)

// 变异数组方法:执行后会改变原始数组的方法
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // cache original method 缓存原始的数组原型上的方法
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    // 返回的value还是通过数组原型方法本书执行的结果
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted // 存储调用执行变异数组方法导致数组本身值改变的数组,主要指的是原始数组增加的那部分(需要重新Observer)
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    // 观察新增加的数组元素
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})

  • Observer类
export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)
    if (Array.isArray(value)) {
      if (hasProto) {
        // 如果支持__proto__属性: 直接把原型志向代理原型对象
        protoAugment(value, arrayMethods)
      } else {
        // 不支持就在数组实例上定义同名的变异方法(且不可枚举)进行拦截
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

  /**
   * Walk through all properties and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}

function copyAugment (target: Object, src: Object, keys: Array<string>) {
  // target: 需要被Observe的对象
  // src: 数组代理原型对象
  // keys: const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
  // keys: 数组代理原型对象上的几个编译方法名
  // const methodsToPatch = [
  //   'push',
  //   'pop',
  //   'shift',
  //   'unshift',
  //   'splice',
  //   'sort',
  //   'reverse'
  // ]
  for (let i = 0, l = keys.length; i < l; i++) {
    const key = keys[i]
    def(target, key, src[key])
  }
}

排序算法

function bubbleSort(list) {
    if (Array.isArray(list)) {
        if (list.length <= 1) return list;
        // 外层循环控制总的遍历轮数,理论上等于数组长度
        for(let i = 0; i < list.length; i++) {
            // j才是真正对比数据的轮,- i是因为需要排除之前每轮对比出来的数
            let flag = false // 标示是否有数据发生交换
            for(let j = 0; j < list.length - 1 - i; j++) {
                if (list[j + 1] < list[j]) {
                    const temp = list[j];
                    list[j] = list[j + 1];
                    list[j + 1] = temp;    
                    flag = true;
                }
            }
            // 没有数据发生交换说明已排序完成
            if (!flag) {
                break
            }
        }
    }
    return list;
}

Vue的进阶小知识<2>: 虚拟DOM的Diff算法

DOM DIFF

比较两棵DOM树的差异是Virtual DOM算法最核心的部分.简单的说就是新旧虚拟dom 的比较,如果有差异就以新的为准,然后再插入的真实的dom中,重新渲染。借网络一张图片说明:

屏幕快照 2019-03-31 上午11 09 13

比较只会在同层级进行, 不会跨层级比较。
比较后会出现四种情况:

  • 1、此节点是否被移除 -> 添加新的节点
  • 2、属性是否被改变 -> 旧属性改为新属性
  • 3、文本内容被改变-> 旧内容改为新内容
  • 4、节点要被整个替换 -> 结构完全不相同 移除整个替换

常见的Diff常见

  • 节点间的移动

demo1-节点移动

结果: 在经过Diff之后, BC节点互相换了位置。并没有节点的删除与新建。

demo2

  • 节点间的删除与新建

demo2-删除再新建

删除CEF节点再新建CEF节点。

demo2

demo3-删除新建

删除CEF新建GEF节点。

demo2

  • 更新删除新建(无key)

屏幕快照 2019-03-31 下午12 16 03

B1/B2是更新了, E/F是先删除后新建

demo2

  • 移动(有key)

屏幕快照 2019-03-31 下午12 23 10

仅仅B2节点更新了

demo2

  • 插入(有key)

屏幕快照 2019-03-31 下午12 25 13

有key值只需要插入一个B4。

demo2

  • 插入(无key)

屏幕快照 2019-03-31 下午12 28 58

B2更新为B4, B3更新为B2, 插入B4。

demo2

Vue的进阶小知识<1>: 双向绑定与单向数据流

双向绑定与单向数据流

  • 双向绑定

屏幕快照 2019-03-31 上午9 36 58

  • 单向数据流

屏幕快照 2019-03-31 上午9 39 04

Vue.js是单向数据流, 不是双向绑定, 双向绑定知识语法糖(.sync)
.sync修饰符实际上还是通过事件emit包装的语法糖(缩写模式)。

demo

// PersonInfo.vue

<template>
  <div>
    <select
      :value="phoneInfo.areaCode"
      placeholder="区号"
      @change="handleAreaCodeChange"
    >
      <option value="+86">+86</option>
      <option value="+60">+60</option>
    </select>
    <input
      :value="phoneInfo.phone"
      type="number"
      placeholder="手机号"
      @input="handlePhoneChange"
    />
    <input
      :value="zipCode"
      type="number"
      placeholder="邮编"
      @input="handleZipCodeChange"
    />
  </div>
</template>
<script>
export default {
  name: "PersonalInfo",
  model: {
    prop: "phoneInfo", // 默认 value
    event: "change" // 默认 input
  },
  props: {
    phoneInfo: Object,
    zipCode: String
  },
  methods: {
    handleAreaCodeChange(e) {
      this.$emit("change", {
        ...this.phoneInfo,
        areaCode: e.target.value
      });
    },
    handlePhoneChange(e) {
      this.$emit("change", {
        ...this.phoneInfo,
        phone: e.target.value
      });
    },
    handleZipCodeChange(e) {
      this.$emit("update:zipCode", e.target.value);
    }
  }
};
</script>

// App.vue

<template>
  <div>
    <PersonalInfo v-model="phoneInfo" :zip-code.sync="zipCode" />

    <PersonalInfo
      :phone-info="phoneInfo"
      :zip-code="zipCode"
      @change="val => (phoneInfo = val)"
      @update:zipCode="val => (zipCode = val)"
    />

    phoneInfo: {{ phoneInfo }}
    <br />
    zipCode: {{ zipCode }}
  </div>
</template>
<script>
import PersonalInfo from "./page/PersonaInfo.vue";
export default {
  components: {
    PersonalInfo
  },
  data() {
    return {
      phoneInfo: {
        areaCode: "+86",
        phone: ""
      },
      zipCode: ""
    };
  }
};
</script>
<style lang="scss" scoped>
html, body{
  height: 100%;
}
body{
  width: 100%;
}
</style>

效果

1

原理

v-model="phoneInfo"

实际上等同于

:phone-info="phoneInfo"
@change="val => (phoneInfo = val)"

另外在自组件可以自定义model
model: {
    prop: "phoneInfo", // 默认 value
    event: "change" // 默认 input
 }
:zip-code.sync="zipCode"
实际上等同于
:zip-code="zipCode"
 @update:zipCode="val => (zipCode = val)"

node

npm config set sass_binary_site https://npm.taobao.org/mirrors/node-sass/
npm config set sharp_dist_base_url https://npm.taobao.org/mirrors/sharp-libvips/
npm config set electron_mirror https://npm.taobao.org/mirrors/electron/
npm config set puppeteer_download_host https://npm.taobao.org/mirrors/
npm config set phantomjs_cdnurl https://npm.taobao.org/mirrors/phantomjs/
npm config set sentrycli_cdnurl https://npm.taobao.org/mirrors/sentry-cli/
npm config set sqlite3_binary_site https://npm.taobao.org/mirrors/sqlite3/
npm config set python_mirror https://npm.taobao.org/mirrors/python/

Koa2源码学习

第一篇: 图解Hello World学习源码

初衷

个人觉得学习源码需要带着目的去看, 才能达到效果, 但是公司又没有上Node, 没有实践怎么办呢?最近发现通过调试Koa2源码也是个不错的法子。

准备工作

- 安装node
- 安装vscode
- 学习如何在vscode下调试

关于Node下调试推荐阅读下:《Node.js 调试指南》

我们这里只需要学习如何在vscode下调试即可。
具体就不详情说了, 见链接, 有问题我们可以讨论。

从Hello World开始学习

// 安装koa2, nodemon等后, 来个入门的hello world

const Koa = require('koa')
const app = new Koa()
const port = 3000

app.use(async (ctx, next) => {
    await next()
    ctx.response.status = 200
    ctx.response.body = 'hello world'
})

app.listen(port, () => {
    console.log(`server is running on the port: ${port}`)
})

是的没错,通过上述这个入门代码也能学习到Koa2的源码知识。

首先观察上面用到了的一些API。
new Koa()
app.use()
app.listen()

我们现在开始进入node_modules目录下找到Koa。

koa2

通过package.json得知Koa的入口文件为

"main": "lib/application.js"
// lib目录下模块
- application.js  实例
- context.js      上下文对象
- request.js      请求对象
- response.js     响应对象

现在我们当然从入口文件application.js开始。我们的实例代码第一行是new koa(); 我们肯定是有一个类,这里是开始的突破点。

// 构造函数继承node的EventEmitter类 
// http://nodejs.cn/api/events.html
module.exports = class Application extends Emitter {
    ...
}

然后我们去打三个断点, 分别如下:

构造器

app.use()

app.listen()

之所以在这三个地方打断点是对应前面提到的执行了Koa2的三个api.通过这三个断点我们一步步去了解Koa2内部是怎么执行的。

最开始肯定是执行constructor();部分注释见上述截图。

this.middleware = []; 这个是用来存放通过app.use()注册的中间件的。

重点接下来看app.use()。

app.use()

很明显fn指的就是:

async (ctx, next) => {
    await next()
    ctx.response.status = 200
    ctx.response.body = 'hello world'
}

在use(fn)方法中主要做了以下事情:

1. 错误校验, fn必须是函数, 否则给出错误提示
2. fn不推荐使用生成器函数, v2版本Koa2会进行转化, 但是v3就会不支持生成器函数了, 这里主要是对koa1的向下兼容。
3. 存储注册的中间件
3. return this. 支持链式调用

这个时候你可以看懂this大概有这些属性:

Application {
    _events:Object {}
    _eventsCount:0
    _maxListeners:undefined
    context:Object {}
    env:"development"
    middleware:Array(1) []
    proxy:false
    request:Object {}
    response:Object {}
    subdomainOffset:2
    Symbol(util.inspect.custom):inspect() { … }
    __proto__:EventEmitter
}

然后进入listen(), 这里有一段this.callback(), 我们需要去这个方法下打断点看执行了什么。

// 其实就是http.createServer(app.callback()).listen(...)的语法糖
listen(...args) {
    debug('listen');
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }

callback()

// callback()做了以下几件事:
1. 通过compose合并中间件
2. 为应用注册error事件的监听器
3. 返回一个请求处理函数handleRequest

接下来我们看看this.createContext()和this.handleRequest(),分别打断点看代码。

note: 提一个小问题, node应该经常会发生端口占用问题。

每次请求都会创建一个上下文对象。

handleRequest(ctx, fnMiddleware) {
    const res = ctx.res;
    res.statusCode = 404;
    // 错误处理
    const onerror = err => ctx.onerror(err);
    const handleResponse = () => respond(ctx);
    // 通过第三方库on-finished监听http response,当请求结束时执行回调,这里传入的回调是context.onerror(err),即当错误发生时才执行。
    onFinished(res, onerror);
    // 即将所有中间件执行(传入请求上下文对象ctx),之后执行响应处理函数(respond(ctx)),当抛出异常时同样使用onerror(err)处理。
    return fnMiddleware(ctx).then(handleResponse).catch(onerror);
  }

对respond打断点

/**
 * Response helper.
 * 在所有中间件执行完之后执行
 */

function respond(ctx) {
  // allow bypassing koa
  // 通过设置ctx.respond = false来跳过这个函数,但不推荐这样子
  if (false === ctx.respond) return;

  const res = ctx.res;
  // 上下文对象不可写时也会退出该函数
  if (!ctx.writable) return;

  let body = ctx.body;
  const code = ctx.status;

  // ignore body
  // 当返回的状态码表示没有响应主体时,将响应主体置空:
  if (statuses.empty[code]) {
    // strip headers
    ctx.body = null;
    return res.end();
  }
  // 当请求方法为HEAD时,判断响应头是否发送以及响应主体是否为JSON格式,若满足则设置响应Content-Length:
  if ('HEAD' == ctx.method) {
    if (!res.headersSent && isJSON(body)) {
      ctx.length = Buffer.byteLength(JSON.stringify(body));
    }
    return res.end();
  }

  // status body
  // 当返回的状态码表示有响应主体,但响应主体为空时,将响应主体设置为响应信息或状态码。并当响应头未发送时设置Content-Type与Content-Length:
  if (null == body) {
    if (ctx.req.httpVersionMajor >= 2) {
      body = String(code);
    } else {
      body = ctx.message || String(code);
    }
    if (!res.headersSent) {
      ctx.type = 'text';
      ctx.length = Buffer.byteLength(body);
    }
    return res.end(body);
  }
  // 对不同的响应主体进行处理
  // responses
  if (Buffer.isBuffer(body)) return res.end(body);
  if ('string' == typeof body) return res.end(body);
  if (body instanceof Stream) return body.pipe(res);

  // body: json
  body = JSON.stringify(body);
  if (!res.headersSent) {
    ctx.length = Buffer.byteLength(body);
  }
  res.end(body);
}

错误处理

onerror(err) {
    // 当err不为Error类型时抛出异常。
    if (!(err instanceof Error)) throw new TypeError(util.format('non-error thrown: %j', err));
    // 当 err.status 是 404 或 err.expose 是 true 时默认错误处理程序也不会输出错误
    if (404 == err.status || err.expose) return;
    // 默认情况下,将所有错误输出到 stderr,除非 app.silent 为 true
    if (this.silent) return;

    const msg = err.stack || err.toString();
    console.error();
    console.error(msg.replace(/^/gm, '  '));
    console.error();
  }

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.