Git Product home page Git Product logo

notes's Introduction

Hi 👋, I'm Echo

No code no bugs

Top Langs Anurag's GitHub stats

notes's People

Contributors

echozhaoh avatar

notes's Issues

vue 2.x 迁移 typescript

依赖

dependencies

vue-class-component
vue-property-decorator
vue-tsx-support
vuex-class

vue-tsx-support 是用来增强 VuePropsEvent 等类型的

不使用 vue-tsx-support

class Demo extends Vue {
    @Prop() demo: string
}

在外部是无法感知这个 props 类型的
使用 vue-tsx-support

interface Props {
    demo: string
}
class Demo extends tsx.Component<Props> implements Props {
    @Prop() demo: string
}

这种写法外部是可以感知需要传入的 Props 的类型的

devDependencies

@typescript-eslint/eslint-plugin
@typescript-eslint/parser
@vue/eslint-config-typescript
eslint-plugin-import
eslint-plugin-typescript
ts-loader
typescript

还有一些 @types 需要添加可自行查找(缺什么 types 会报错提示)

eslint

eslint 中需要添加对 ts、tsx 的支持,同时在迁移的过程中还会存在 js 和 vue
主要这两个地方需要注意(添加对 ts 的支持)

plugins: [ 'typescript', '@typescript-eslint' ],
parserOptions: { parser: "@typescript-eslint/parser", "ecmaFeatures": { "jsx": true }, "ecmaVersion": 2018, "sourceType": "module", }

同时需要在你的 workspace 的 vscode setttings.json 中增加对 ts 和 tsx 的验证

"typescript.tsdk": "node_modules/typescript/lib",
"eslint.validate": [
    "javascript",
    "javascriptreact",
    {
        "language": "typescript",
        "autoFix": true
    },
    {
        "language": "typescriptreact",
        "autoFix": true
    }
],
"eslint.options": { //指定vscode的eslint所处理的文件的后缀 "extensions": [ ".js", ".ts", ".tsx", ".vue" ] }

同时存在 vue 和 jsx

在我们改造过程中不可避免的会遇到在写 jsx 的时候会应用到之前的一些 vue 的组件,而这些组件有没有被全局 install,这个时候我们可以先如下处理

@Component({
 components: {
    DemoVue,
    },
})
class Demo extends Vue {
    render() {
        return (<demo-vue />)
    }
}

对于 template 中的一些修饰符,我们在 jsx 中该怎么处理,比 xxx.sync 修饰符,sync 只是一个语法糖,我们可以在组件的 on 中进行监听,如下

@Component({
    components: {
        DemoVue,
    },
})
class Demo extends Vue {
  render() {
    return (<demo-vue on={{'update:Click': () => {}}} />)
  }
}

同时我们需要区分 attrs 和 props , props 可以直接在组件上进行传递,attrs 需要放到 attrs 属性下,如下

@Component({
    components: {
        DemoVue,
    },
})
class Demo extends Vue {
    render() {
        return (<demo-vue on={{'update:Click': () => {}}} propsA attrs={{attrsA: 'xxx'}} />)
    }
}

对于一些第三方库没有 decorator ,该怎么办,我们可以借助 vue-class-component 中的 createDecorator 来造一个简易的轮子,如下 apollo 和 metaInfo

export function ApolloDes() {
    return function(_t: Vue, key: string, desc: any) {
        createDecorator(options => {
            options.apollo = desc.value()
        })(_t, key)
    }
}

export function MetaInfoDes() {
    return function (_target: Vue, key: string, desc: any) {
        createDecorator((options) => {
            options.metaInfo = desc.value
        })(_target, key)
    }
}
使用方式
class Demo extends Vue {
@MetaInfoDes()
metaInfo() {
    return {
        title: 'xxx',
    }
}

@ApolloDes
apollo() {
    return {
        reqA: {
            query: xxxx
        }
    }
}
render() {
    return (<demo-vue on={{'update:Click': () => {}}} propsA attrs={{attrsA: 'xxx'}} />)
}
}

一些·奇怪的 bug

如下例

class Demo extends Vue {
    test = {name: 'xxx'}
    render() {
        const { test } = this
        return (
            <test props={test}/>
        )
    }

}

上述代码会在编译的运行的时候将组件 test 编译为 <xxxx /> ,理解之后也不奇怪,因为 test 也是一个变量,只不过 这个组件被全局注册我们没在这 应用,刚好组件和变量 test 重名,而且 test 变量具有 name 属性,运行时,test 的 name 就会被编译成组件名了,组件和 test 都是变量,只不过组件这个变量在 Vue 中被全局注册了,而且这个组件名也是小写,lint 工具不会去检查,就造成了一种错觉。

webpack4 打包优化

plugin: TerserPlugin,OptimizeCSSAssetsPlugin

splitChunks

runtimeChunk: 用来共享 runtime

// flag 自己多实践

重力缓冲回到顶部

~function goTop(){
 let delta = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
 delta && window.scrollBy(0, - delta / 9.8)
 delta && requestAnimationFrame(goTop)
}()

tapable 助你解析 webpack 插件系统

tapable

tapable 导出了 9 个 hooks

  • SyncHook
  • SyncBailHook
  • SyncWaterfallHook
  • SyncLoopHook
  • AsyncParallelHook
  • AsyncParallelBailHook
  • AsyncSeriesHook
  • AsyncSeriesBailHook
  • AsyncSeriesWaterfallHook

上述 9 个 hooks 都继承自 Hook 这个 class

tapable Hook 解析

hook 对外提供了 isUsed call promise callAsync compile tap tapAsync tapPromise intercept 这些方法

其中 tap 开头的方法是用来订阅事件的,call promise callAsync 是用来触发事件的,isUsed 返回了一个 boolean 值用来标记当前 hook 中注册的事件是否被执行完成。

isUsed 源码

isUsed() {
  	return this.taps.length > 0 || this.interceptors.length > 0;
}

tap tapAsync tapPromise 这三个方法第一个参数传入可以支持传入 string(一般是指 plugin 的名称) 或者一个 Tap 类型,第二个参数是一个回调用来接收事件被 emit 时的调用。

export interface Tap {
    name: string; // 事件名称,一般就是 plugin 的名字
    type: TapType; // 支持三种类型 'sync' 'async' 'promise'
    fn: Function;
    stage: number;
    context: boolean;
}

call promise callAsync 这三个方法在传入参数的时候是依赖于 hook 被实例化的时候传入的 args 数组占位符的数量的,如下示例:

const sync = new SyncHook(['arg1', 'arg2']) // 'arg1' 'arg2' 为参数占位符
sync.tap('Test', (arg1, arg2) => {
  console.log(arg1, arg2) // a2
})
sync.call('a', '2')

其中 promise 调用会返回一个 PromisecallAsync 默认支持传入一个 callback

Sync 开头的 hook 不支持使用 tapAsynctapPromise,可以看下述的以 SyncHook 的源码为例

const TAP_ASYNC = () => {
	throw new Error("tapAsync is not supported on a SyncHook");
};

const TAP_PROMISE = () => {
	throw new Error("tapPromise is not supported on a SyncHook");
};

const COMPILE = function(options) {
	factory.setup(this, options);
	return factory.create(options);
};

function SyncHook(args = [], name = undefined) {
	const hook = new Hook(args, name);
	hook.constructor = SyncHook;
	hook.tapAsync = TAP_ASYNC;
	hook.tapPromise = TAP_PROMISE;
	hook.compile = COMPILE;
	return hook;
}

SyncHook.prototype = null;

在这里面我们可以看到 tapAsynctapPromise 是被重写了直接 throw error

一个简单的使用示范

下面的例子会给大家带来一个简单地示范

class TapableTest {
  constructor() {
    this.hooks = {
      sync: new SyncHook(['context', 'hi']),
      syncBail: new SyncBailHook(),
      syncLoop: new SyncLoopHook(),
      syncWaterfall: new SyncWaterfallHook(['syncwaterfall']),
      asyncParallel: new AsyncParallelHook(),
      asyncParallelBail: new AsyncParallelBailHook(),
      asyncSeries: new AsyncSeriesHook(),
      asyncSeriesBail: new AsyncSeriesBailHook(),
      asyncSeriesWaterfall: new AsyncSeriesWaterfallHook(['asyncwaterfall']) 
    }
  }
  emitSync() {
    this.hooks.sync.call(this, err => {
        console.log(this.hooks.sync.promise)
        console.log(err)
    })
  }
  emitAyncSeries() { 
    this.hooks.asyncSeries.callAsync(err => {
        if (err) console.log(err)
    })
  }
}

const test = new TapableTest()
test.hooks.sync.tap('TestPlugin', (context, callback) => {
  console.log('trigger: ', context)
  callback(new Error('this is sync error'))
})
test.hooks.asyncSeries.tapAsync('AsyncSeriesPlugin', callback => {
    callback(new Error('this is async series error'))
})
test.emitSync()
test.emitAyncSeries()

上述的运行结果可以这查看 runkit

下面来聊一聊 webpack 中的插件是如何依赖 tapable 的

webpack 插件被注入的时机

当我们定义了 webpack 的配置文件后,webpack 会根据这些配置生成一个或多个 compiler ,而插件就是在创建 compiler 时被添加到 webpack 的整个运行期间的, 可以看下述源码:(相关源码可以在 webpack lib 下的 webpack.js 中找到)

const createCompiler = rawOptions => {
	const options = getNormalizedWebpackOptions(rawOptions);
	applyWebpackOptionsBaseDefaults(options);
	const compiler = new Compiler(options.context);
	compiler.options = options;
	new NodeEnvironmentPlugin({
		infrastructureLogging: options.infrastructureLogging
	}).apply(compiler);
	if (Array.isArray(options.plugins)) {
		for (const plugin of options.plugins) {
			if (typeof plugin === "function") {
				plugin.call(compiler, compiler);
			} else {
				plugin.apply(compiler);
			}
		}
	}
	applyWebpackOptionsDefaults(options);
	compiler.hooks.environment.call();
	compiler.hooks.afterEnvironment.call();
	new WebpackOptionsApply().process(options, compiler);
	compiler.hooks.initialize.call();
	return compiler;
};

我们可以看到遍历 options.plugins 这一段,这一段分了两种情况来进行插件的插入

  • 我们的 plugin 可以以函数的方式被 webpack 调用,也就是说我们可以用函数来写插件,这个函数的作用域是当前的 compiler,函数也会接收到一个 compiler
  • 可以传入一个包含 apply 方法的对象实例,apply 方法会被传入 compiler

所以这也就解释了为什么我们的插件需要 new 出来之后传入到 webpack

进入 Compiler 一探究竟

上一个中我们了解到了 plugins 是何时被注入的,我们可以看到在 plugin 的注入时传入了当前被实例化出来的 Compiler,所以现在我们需要了解下 Compiler 中做了什么

进入 Compiler.js (也在 lib 中)我们可以第一时间看到 Compilerconstructor 中定义了一个庞大的 hooks

this.hooks = Object.freeze({
			/** @type {SyncHook<[]>} */
			initialize: new SyncHook([]),

			/** @type {SyncBailHook<[Compilation], boolean>} */
			shouldEmit: new SyncBailHook(["compilation"]),
			/** @type {AsyncSeriesHook<[Stats]>} */
			done: new AsyncSeriesHook(["stats"]),
			/** @type {SyncHook<[Stats]>} */
			afterDone: new SyncHook(["stats"]),
			/** @type {AsyncSeriesHook<[]>} */
			additionalPass: new AsyncSeriesHook([]),
			/** @type {AsyncSeriesHook<[Compiler]>} */
			beforeRun: new AsyncSeriesHook(["compiler"]),
			/** @type {AsyncSeriesHook<[Compiler]>} */
			run: new AsyncSeriesHook(["compiler"]),
			/** @type {AsyncSeriesHook<[Compilation]>} */
			emit: new AsyncSeriesHook(["compilation"]),
			/** @type {AsyncSeriesHook<[string, AssetEmittedInfo]>} */
			assetEmitted: new AsyncSeriesHook(["file", "info"]),
			/** @type {AsyncSeriesHook<[Compilation]>} */
			afterEmit: new AsyncSeriesHook(["compilation"])
      ...
})

看到这些 hook 是不是很熟悉,全是 tapable 中的 hook,webpack 正是依赖于这些复杂的构建 hook 而完成了我们的代码构建,所以在我们编写 plugin 时就可以利用这些 hook 来完成我们的特殊需求。

比如我们经常用到的 HtmlWebpackPlugin ,我们可以看下他是如何运行的,在 HtmlWebpackPluginapply 中我们可以找到这样一段代码:

compiler.hooks.emit.tapAsync('HtmlWebpackPlugin', (compiler, callback) => {
  ...
})

说明 HtmlWebpackPlugin 是利用了 Compileremithook 来完成的

通过深入了解,webpack 是在庞大的插件上运行的,他自己内置了很多插件

上述内容如有错误,请指正

js 函数实现

currying

function currying() {
    const arr = Array.from(arguments) 
    const fn = arr.shift()
    let rest = arr
    if (rest.length === fn.length) {
        return fn.apply(this, rest)
    }
    return function loop() {
        const args = rest.concat(...Array.from(arguments))
        if (args.length === fn.length) {
           return fn.apply(this, args)
        } else {
               return loop.apply(this, args)
        }
    }
}

new 的实现过程

function NewInstance(parent) {
    const obj = Object.create({})
    obj.__proto__ = Object.create(parent.prototype)
    const result = parent.apply(obj)
    return result instanceof Object ? result : obj 
}

instanceof 实现过程

function instanceofClone(child, parent) {
    let a = child.__proto__
    while(a) {
        if (a === parent.prototype) return true
         a = a.__proto__
    }
    return false
}

go 实现一个简易的 http server

package main

import (
  "fmt"
  "net/http"
  "strings"
  "log"
)

func sayHelloName(w http.ResponseWriter, r *http.Request) {
  r.ParseForm() // 解析参数
  fmt.Println(r.Form) // 输出到服务端的打印信息
  fmt.Println("path", r.URL.Path)
  fmt.Println("scheme", r.URL.Scheme)
  fmt.Println(r.Form["url_long"])
  for k, v := range r.Form {
    fmt.Println("key: ", k)
    fmt.Println("val: ", strings.Join(v, ""))
  }
  fmt.Fprintf(w, "Hello World") // 写入到 w 输出到客户端
}

func main() {
  http.HandleFunc("/", sayHelloName) // 设置访问路由
  err := http.ListenAndServe(":9090", nil) // 设置监听端口
  if err != nil {
    log.Fatal("ListenAndServer: ", err)
  }
}

echozh 的 webpack 源码解读第一步 ===> Parser

Parser.js 中,我们可以看到两个比较核心的库:

  • acorn 用于解析 js 的 ast 语法🌲
  • tapable webpack 的一个用于 plugin 的小型事件处理的库

接下来的东西我也不知道怎么写了,先看看吧

svg 可内联作为 background-img

想到 background-image 我们第一时间想到的应该是图片,但是图片会有很多因素的影响,比如大小、网络等,今天说的是将 svg 内联作为 background-image,以下是格式:

background-image: url('data:image/svg+xml,<svg>xxxx</svg>');

如果我们直接这样将 svg 放入,有可能会无法显示,比如 svg 中有颜色的属性 #000000,其中 # 就回导致 svg 无法正常显示,我猜测的原因不是 svg 没有显示,而是已经显示了,但是颜色失效了,所以我们需要将 svg 的代码字符串放入 encodeURIComponent 函数处理才行,将 < > # " 等特殊字符转义。

go 实现二分法

package main

import (
   "fmt"
)

func binarySearch(arr []int, target int) int {
   left, right, mid := 0, len(arr) - 1, 0
   for left <= right {
   	mid = (left + right) >> 1
   	if arr[mid] > target {
   		right = mid - 1
   	} else if arr[mid] < target {
   		left = mid + 1
   	}else {
   		return mid
   	}
   }	
   return -1
}

func main() {	
   arr := make([]int, 0, 10)
   for i := 0; i < 10; i++ {
   	arr = append(arr, i)
   }
   fmt.Println(arr)
   mid := dichotomy(arr, 0)
   fmt.Printf("selected value is: %d", mid)
}

react 中如何解决作用域问题

在没有使用 es6 之前解决作用域都是手动的

  • example
    bindcallapply
    有时候我们想要改变一些事件函数的作用域,
    我们可能会这样做
    onClick.bind(ctx)
    这样做
    onClick.call(ctx, arg)
    还有这样做
    onClick.apply(ctx, args)

然而使用 es6 之后我们还可以有👇的操作

1 () => {onClick()}
2 在 class 中我们可以自定义箭头函数来代替一些事件函数

   class Test extends React.Component {
         render () {
             return (
                 <input onChange={this.handleChange} />
              )
         }
         handleChange = (event) => {
              console.log(event.target.value)
         }
  }

20200726 阶段总结

webpack

  • 开始下面内容前,需熟悉 webpack 各个模块 entryloaderplugin
  • webpack 实现的机制,webpack 整个流程是靠各个 hook 完成 源码
  • webpack 是如何实现 hook 的,主要使用 tapable

图解 http (tcp/ip、http 这些内容很重要)

微前端

qiankun

qiankun 是基于 single-spa 实现,qiankun 中主要做了对 window 的 proxy 以及很重要的一步,将 single-spa 中需要开发自己开发的大部分内容都实现了配置化,其中支持配置化的很要的一个库 import-html-entry

路由是微前端实现的关键

目前的微前端的实现都是基于对路由的划分来加载不同的子应用,所以理解前端路由很重要,其中也要搞清楚前端路由和后端路由的区别

  • history 路由
  • hash 路由

monorepo

分包(我的直白翻译)

将同一仓库下的不同模块的代码单独打包,目前很多 ui 库都是使用 monorepo 来处理分包的,blueprintjs 是我 monorepo 的启蒙,这个库的源码值得一读。

monorepo 目前主流的工具是 lerna

阅读源码

阅读源码是必经的一步,但也不是鼓励什么准备都没有,上来直接看,这样是没有任何收益的

阅读源码之前,你需要熟悉这个库的大部分功能以及如何使用并且能够基本无障碍的使用,接下来就是将模块拆解,分解自己的目标,一步一步去理解源码中的每一个功能。

css escape

function cssEscape(str) {
  const length = str.length;
  let index = -1;
  let codeUnit;
  let result = '';
  const firstCodeUnit = str.charCodeAt(0);
  while (++index < length) {
    codeUnit = str.charCodeAt(index);
    // Note: there’s no need to special-case astral symbols, surrogate
    // pairs, or lone surrogates.

    // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
    // (U+FFFD).
    if (codeUnit === 0x0000) {
      result += '\uFFFD';
      continue;
    }

    // Comma
    if (codeUnit === 44){
      result += '\\2c ';
      continue;
    }

    if (
      // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
      // U+007F, […]
      (codeUnit >= 0x0001 && codeUnit <= 0x001f) ||
      codeUnit === 0x007f ||
      // If the character is the first character and is in the range [0-9]
      // (U+0030 to U+0039), […]
      (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
      // If the character is the second character and is in the range [0-9]
      // (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
      (index === 1 &&
        codeUnit >= 0x0030 &&
        codeUnit <= 0x0039 &&
        firstCodeUnit === 0x002d)
    ) {
      // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
      result += '\\' + codeUnit.toString(16) + ' ';
      continue;
    }

    if (
      // If the character is the first character and is a `-` (U+002D), and
      // there is no second character, […]
      index === 0 &&
      length === 1 &&
      codeUnit === 0x002d
    ) {
      result += '\\' + str.charAt(index);
      continue;
    }

    // If the character is not handled by one of the above rules and is
    // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
    // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
    // U+005A), or [a-z] (U+0061 to U+007A), […]
    if (
      codeUnit >= 0x0080 ||
      codeUnit === 0x002d ||
      codeUnit === 0x005f ||
      (codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
      (codeUnit >= 0x0041 && codeUnit <= 0x005a) ||
      (codeUnit >= 0x0061 && codeUnit <= 0x007a)
    ) {
      // the character itself
      result += str.charAt(index);
      continue;
    }

    // Otherwise, the escaped character.
    // https://drafts.csswg.org/cssom/#escape-a-character
    result += '\\' + str.charAt(index);
  }
  return result;
}

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.