分类
typescript 源码解析

从vue3源码开始学(1)

第一次写,总感觉这没准备好,那没准备好,哪有都准备好了的,干就完事

准备工作

克隆项目 git clone https://github.com/lxchuan12/vue-next-analysis.git

计划了解的

主要要啃的是 packages\shared\src 下的函数

一个个来看下

正餐

is的用法

export const isArray: (arg: any) => arg is any[] = Array.isArray
// 首先这是TS的语法 等价于
export const isArray = Array.isArray

这种写法与 : 
export const isArray: (arg: any) => boolean = Array.isArray

有什么区别吗,看下面这两个例子

function isString(test: any): test is string{
    return typeof test === "string";
}

function example(foo: any){
    if(isString(foo)){
        console.log("it is a string" + foo);
        console.log(foo.length); // string function
        // 如下代码编译时会出错,运行时也会出错,因为 foo 是 string 不存在toExponential方法
        console.log(foo.toExponential(2));
    }
    // 编译不会出错,但是运行时出错
    console.log(foo.toExponential(2));
}
example("hello world");

// --------------------------------
function isString(test: any): boolean{
    return typeof test === "string";
}

function example(foo: any){
    if(isString(foo)){
        console.log("it is a string" + foo);
        console.log(foo.length); // string function
        // foo 为 any,编译正常。但是运行时会出错,因为 foo 是 string 不存在toExponential方法
        console.log(foo.toExponential(2));
    }
}
example("hello world");

在使用类型保护时,TS 会进一步缩小变量的类型。
例子中,将类型从 any 缩小至了 string
类型保护的作用域仅仅在 if 后的块级作用域中生效

keyof

const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
  val: object,
  key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)

这里没学过ts的同学可能有点晕,这个is刚刚上面讲过了是做类型保护的,那这个 keyof typeof 又是啥 这个val又是啥

别急,跟我一个个来

首先先看这个 keyof

keyof 操作符是在 TypeScript 2.1 版本引入的,
该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。
interface Person {
  name: string;
  age: number;
  location: string;
}
type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string | number

除了接口外,keyof 也可以用于操作类
class Person {
  name: string = "Semlinker";
}

let sname: keyof Person;
sname = "name";

keyof 操作符除了支持接口和类之外,它也支持基本数据类型
let K1: keyof boolean; // let K1: "valueOf"
let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
let K3: keyof symbol; // let K1: "valueOf"

此外 keyof 也称为输入索引类型查询,与之相对应的是索引访问类型,也称为查找类型。
在语法上,它们看起来像属性或元素访问,但最终会被转换为类型:
type P1 = Person["name"];  // string
type P2 = Person["name" | "age"];  // string | number
type P3 = string["charAt"];  // (pos: number) => string
type P4 = string[]["push"];  // (...items: string[]) => number
type P5 = string[][0];  // string

所以简单的抓住重点返回的是key

举个简单的例子
// js
function getProperty(obj, key) {
  return obj[key];
}

// 这个转成ts怎么写尼
function getProperty(obj: object, key: string) {
  return (obj as any)[key];
}

// 不太优雅,用上keyof
function getProperty<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

function getProperty<我是对象 extends object, 我是对象中的键 extends keyof 我是对象>(obj: 我是对象, key: 我是对象中的键) {
  return obj[key];
}

这时候聪明的同学就要问了 老师,这个extends你还没讲呀

这个 extends 要和 keyof 连在一起看,至少按我的理解是的

keyof T 是 T 类型的键集

extends是子集的意思

子类型的值一定也是父类型。比如“猫”是“动物”的子类型,一只具体的 “猫”一定也是“动物”

所以父类型是 ‘a’|’b’ 子类型只能是 <= 父类型的

extends keyof 和 in keyof 要区分看

function getProperty<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}
// 这里的意思是K必须是T的公共属性名称,与class的那个extends 意思不太一样甚至相反

interface Person {
  age: number;
  name: string;
}

// 每一项映射成了可选的
type Optional<T> = { 
  [K in keyof T]?: T[K] 
};

const person: Optional<Person> = {
  name: "Tobias"
};

// 看上去这个 Optional<Person> 和 Person 一模一样是吧
// 我这边的理解是 这是一种映射 从必填都变成了可选


type ReadOnly<T> = { 
  readonly [K in keyof T]: T[K] 
};
const readperson: ReadOnly<Person> = {
    name: "Tobias",
    age:18
};

readperson.age = '' // Cannot assign to 'age' because it is a read-only property.

就是这样

还不明白看看这个

type 可选的<未知> = { 
  [键 in keyof 未知]?: 未知[键] 
};

type 可选的<未知> = { 
  [键 in keyof 未知]?: 未知[键] 
};

const readperson: 可选的<Person> = {
    name: "Tobias",
};

总结下

A extends keyof B

表示 A 是B 的子类型

in 是遍历的意思

Record

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

export const isObject = (val: unknown): val is Record<any, any> =>
  val !== null && typeof val === 'object'

export const isFunction = (val: unknown): val is Function =>
  typeof val === 'function'

export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
  return isObject(val) && isFunction(val.then) && isFunction(val.catch)
}

又有小可爱问了:这个Record是什么意思呀

看不懂没关系下次再讲

扩展

值得注意的点

1、is 在 TS 中可以用来类型保护,缩小返回

参考链接

2、keyof , extends keyof , in keyof

泛型参考链接

分类
未分类

前端拾遗|从前端函数式编程开始

函数式编程是什么呢?是一种编程范式,比较经典的函数式语言有 Haskell ,实际上 JacaScript 本身作为一门多范式的语言也是支持函数式的。

函数式具有五个鲜明的特点:

  1. 函数是一等公民
  2. Lambda表达式
  3. 纯函数,也叫没有副作用,不影响外部变量
  4. 不修改外部状态
  5. 引用透明,只依赖于输入的参数

来看一个例子


const arguments = ['qiupu'];
const callName = name => {
  console.log(arguments[0])
}
const callRealName = function(name) {
  console.log(arguments[0])
}
callName('knight'); // qiupu
callRealName ('knight'); // knight

这就是典型的上下文透传的例子。


数组里有一些方法可以实现类似Lodash库的语句组合形式,如链式调用、函数作为参数调用,也可以做一些控制语句代替for/while等。

来看一个例子

在JSX中我们常常看到这样的用法


const menu = (
<div>
  {
    props.post.filter(item =>item.name).map(item=>{
    return <div key={item.id}>{item.name}<div>
    })
  }
</div>
)

这里常见的map、filter等就是常见的 组合子 的包装。

分类
未分类

vue3变化

vue3的变化

全局api

createApp 返回实例 替代 new Vue 使得返回实例不再共享全局配置 全局的方法也从Vue上转移到了实例上

2.x全局API 3.x实例API(app)
Vue.config app.config
Vue.config.productionTip removed
Vue.config.ignoredElements app.config.isCustomElement
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use
Vue.prototype app.config.globalProperties

config.ignoredElements 就是现在 config.isCustomElement

引入此配置选项旨在支持本机自定义元素,因此重命名可以更好地传达其功能。新选项还期望一个比旧字符串/ RegExp方法具有更大灵活性的函数:

 // before
 Vue.config.ignoredElements = ['my-el', /^ion-/]
    
 // after
 const app = Vue.createApp({})
 app.config.isCustomElement = tag => tag.startsWith('ion-')

Vue.prototype 取而代之 config.globalProperties

在Vue 2中,Vue.prototype通常用于添加所有组件都可以访问的属性。 Vue 3中的等效项是config.globalProperties。在实例化应用程序内的组件时,将复制这些属性:

// before - Vue 2
Vue.prototype.$http = () => {}
// after - Vue 3
const app = Vue.createApp({})
app.config.globalProperties.$http = () => {}

一个经过转换的例子如下

const app = createApp(MyApp)

app.component('button-counter', {
  data: () => ({
    count: 0
  }),
  template: '<button @click="count++">Clicked {{ count }} times.</button>'
})

app.directive('focus', {
  mounted: el => el.focus()
})

// button-counter 组件与 focus指令 不会污染全局 只在当前实例
app.mount('#app')

Provide / Inject

// 在父级
app.provide('guide', 'Vue 3 Guide')

// 在任意层次的子级
export default {
  inject: {
    book: {
      from: 'guide'
    }
  },
  template: `<div>{{ book }}</div>`
}

Tree-shaking

因为内部都重写了,所以组件内部也完全是支持摇树优化的,带来的后果就是一些方法需要显式导入来使用

import { nextTick } from 'vue'

nextTick(() => {
  // something DOM-related
})
//before
import { shallowMount } from '@vue/test-utils'
import { MyComponent } from './MyComponent.vue'

test('an async feature', async () => {
    const wrapper = shallowMount(MyComponent)

    // execute some DOM-related tasks

    await wrapper.vm.$nextTick()

    // run your assertions
})
//after
import { shallowMount } from '@vue/test-utils'
import { MyComponent } from './MyComponent.vue'
import { nextTick } from 'vue'

test('an async feature', async () => {
    const wrapper = shallowMount(MyComponent)

    // execute some DOM-related tasks

    await nextTick()

    // run your assertions
})

####受影响的API

2.x全局API 3.x用法
Vue.nextTick 拆分
Vue.observable 替换为 Vue.reactive
Vue.version 拆分
Vue.compile 仅完整版本
Vue.set 仅在兼容版本中
Vue.delete 仅在兼容版本中

参考链接

$attrs的变化

在vue2中,classstyle会被直接设置到组件的根元素并且不会出现在 $attrs中, 当inheritAttrs:false的时候

<template>
  <label>
    <input type="text" v-bind="$attrs" />
  </label>
</template>
<script>
export default {
  inheritAttrs: false
}
</script>

当这样使用的时候

<my-component id="my-id" class="my-class"></my-component>

vue2将生成以下html

<label class="my-class">
    <input type="text" id="my-id" />
</label>

vue3中$attr包含所有属性包括class与style vue3将生成以下html

<label>
    <input type="text" id="my-id" class="my-class" />
</label>

$listeners 在vue3也被移除,现在是$attrs的一部分

自定义指令的生命周期变化

简要总结:

  • API 已重命名,以便更好地与组件生命周期保持一致
  • 自定义指令将由子组件通过 v-bind="$attrs"

对比

2.x 3.x
bind beforeMount
inserted mounted
beforeUpdate
update 已经移除 改用 updated
componentUpdated updated
beforeUnmount
unbind unmounted

###生命周期变化 TODO

###scopedSlots正式弃用 v-slot 指令自 Vue 2.6.0 起被引入,提供更好的支持 slot 和 slot-scope attribute 的 API 替代方案。 v-slot 完整的由来参见这份 RFC 。 在接下来所有的 2.x 版本中 slot slot-scopeattribute 仍会被支持, 但不会出现在 Vue 3 中。 结合具名插槽要这样写

<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>

异步组件需要显式定义

Mixin合并行为变更

当来自组件的 data() 及其 mixin 或 extends 基类被合并时,现在将浅层次执行合并:

const Mixin = {
  data() {
    return {
      user: {
        name: 'Jack',
        id: 1
      }
    }
  }
}
const CompA = {
  mixins: [Mixin],
  data() {
    return {
      user: {
        id: 2
      }
    }
  }
}

vue2中结果是

{
  user: {
    id: 2,
    name: 'Jack'
  }
}

vue3中结果是

{
  user: {
    id: 2
  }
}
分类
未分类

flex布局常用属性

写组件的时候常常遇到这种情况

设计给的一个设计图 头是固定高度的 底下是自适应的

一般情况下

div{
display:flex;
flex-direction: column;
height:100%;
}

header{
height:50px;
}

body{
height:100%;
}

对一些要求不高的已经可以实现了,但是当下面组件高度很高的时候,flex会进行缩放,导致实际的header高度与预期的不一致,这个时候显式的给header 加上这两个属性就可以让他不缩放了

header{
flex-grow:0;
flex-shrink:0;
height:50px;
}

顺带一提 flex:1 flex: 1; !== flex: 1 1 auto;

  • 第一个参数表示: flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
  • 第二个参数表示: flex-shrink 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
  • 第三个参数表示: flex-basis 给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为 auto, 即项目本身的大小

分类
未分类

下一阶段预告

接下来半年内要做的事情

1.重构一个v-viewer-vue3版本的

2.开源一个可配置页面的前后端版本

欢迎加群催更 交流技术 秋秋群号:768901972

分类
未分类

git设置导致的血案-jenkins 编译 vue项目

可以看到前面报错了两次,查看报错信息 报的是

其实代码里是有这个文件的

后来想了一下 这个文件的名字我改过 之前是 user.js 后来改成了 User.js

但是我是windows git 默认不区分大小写 这边打包的是在docker里打包的 区分大小写 所以导致了这个结果

为了避免这样的惨剧 请瞧上这个设置

git config core.ignorecase false

这样windows 下 git就区分大小写了

顺带一提 git 在windows下文件目录长度是260 git自身是4096

要想突破可以

git config –global core.longpaths true

但还是建议适当控制长度

如果你有ci/cd,或者前端方向的想法,欢迎加群交流 球球群:768901972

分类
未分类

typescript 给ws添加属性类型报错怎么解决?类型“WebSocket”上不存在属性xxx

其实很简单 我以我这边使用的ws库为例子 新建一个 shims.d.ts 文件

import ws from "ws";

declare module "ws" {
  export interface WebSocket extends ws {
    $id: string;
  }
}

这样就可以实现扩展了,成功的在ws 上扩展了$id属性

另外注意一点 d.ts文件不要和.ts文件一样命名 不推荐通过后缀区分文件

还有疑问可以加群讨论 秋秋群:768901972

分类
未分类

leetcode刷题记录

随缘更新,记录下自己的刷题记录

刷题要有思路的刷题,这轮刷题的目的就是熟悉题型,没事造造火箭。所以我选择的刷题思路是按照tag进行刷题。每个tag刷一个月。刷题的频率暂定是每个工作日刷一题,不论简单或难,总之要有一题,纯粹当爱好。

更新-2020-06-09

//这题是考常见的排序,冒泡,快排,插入排序等常见的排序都可以
// https://leetcode-cn.com/problems/kth-largest-element-in-an-array/submissions/
/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var findKthLargest = function(nums, k) {
    return nums.sort(compareNumbers)[nums.length - k]
};

function compareNumbers(a, b) {
  return a - b;
}

sort 的源码实现目前有新旧两版 旧版的是根据数组的长度,内部调用的不同的算法来实现的,数组小于10的时候调用的是插入排序 数组大于10的时候 使用的是快排

具体可以看这里https://github.com/v8/v8/blob/ad82a40509c5b5b4680d4299c8f08d6c6d31af3c/src/js/array.js

新版的看这里 https://github.com/v8/v8/blob/master/third_party/v8/builtins/array-sort.tq

更新-2020-06-08

// 这题是leetcode第一题 https://leetcode-cn.com/problems/two-sum/
// 可以使用暴力穷举法进行,也可以采用HashMap,这里采用的是HashMap来实现
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    let myMap = Object.create({});
    var index = 0;
    for (var value of nums) {
        if (myMap[target - value] !== undefined) {
            return [myMap[target - value], index]
        }
        myMap[value] = index;
        index += 1;
    }
};

执行用时 :60 ms, 在所有 JavaScript 提交中击败了97.56%的用户

内存消耗 :37.9 MB, 在所有 JavaScript 提交中击败了5.08%的用户

分类
未分类

GIS基础科普1-坐标系与地图投影

北京54坐标系 是参心坐标系 因为原点离国内太远,所以国内使用实际误差较大

西安80坐标系 是参心坐标系 换了一个大地基准面,大地原点设在陕西省泾阳县永乐镇 基准面采用的是青岛验潮站1952年到1979年确定的黄海平均海水面 也叫85高程

国家CGCS2000的坐标系原点 是整个地球的质量中心  也叫地心坐标系  参考椭球参数基本与WGS84椭球体差不多 两个坐标系的坐标精度要求不高的情况下可以直接使用

两个不同的三维空间直角坐标系之间转换时,通常使用七参数模型,需要知道7个未知参数

3个坐标的平移量 3个坐标轴的旋转角度 加上一个尺度因子K 一共七个参数

分类
未分类

deno的初体验-环境安装与第一个程序

denoDeno 的目标是为现代程序员提供一个高效、安全的脚本环境。

它始终以单个可执行文件形态,作为分发文件,并且该可执行文件,足够运行任何 deno 程序。

给定一个 deno 程序的 URL,您应该能够用不超过 50MB 的 Deno 可执行文件,来执行它。

Deno 明确承担运行时,和包管理器的角色。它用标准的浏览器兼容协议,来加载模块:URLs。

Deno 为提供了程序访问系统的安全保证,默认情况下,它是最严格的安全沙盒。Deno 提供一套经过评审(审核)的标准模块这保证了与 Deno 的合作。
windows 下安装Deno 在 OSX、Linux 和 Windows 上工作。deno 是单个二进制可执行文件。它没有外部依赖关系。deno_install提供方便的脚本,来下载和安装二进制文件。

使用 Shell:

$ curl -fsSL https://deno.land/x/install/install.sh | sh

使用 PowerShell:

> iwr https://deno.land/x/install/install.ps1 | iex

使用Scoop(Windows):

scoop install deno


也可以通过github.com/denoland/deno/releases下载 tarball 或 zip 文件,手动安装 deno。这些包只包含一个可执行文件。您必须在 Mac 和 Linux 上,设置可执行的环境变量(PATH)。

安装完成之后初体验
新建一个 helloworld.ts在里面写下

console.log("hello world");

终端中运行

deno run .\helloworld.ts

新建一个 a.ts

export const a = {  name: "i am a",};

在helloworld.ts中引入

import { a } from "./a.ts";
console.log("hello world",a.name);

注意a.ts 的后缀目前版本并不能省略,需要明确写出

先体验到这里,后续在更新