Coder Social home page Coder Social logo

laclys / front-end_practice Goto Github PK

View Code? Open in Web Editor NEW
5.0 5.0 0.0 77.99 MB

Daily practice

HTML 12.26% CSS 1.39% JavaScript 83.80% Java 0.14% Objective-C 0.47% Vue 0.19% TypeScript 0.31% Batchfile 0.01% Starlark 0.18% SCSS 1.20% Less 0.04% Stylus 0.01%
css javascript practice

front-end_practice's Introduction

Hi, I'm Lac! 👋

  • Frontend Developer

  • [ React, TypeScript ] Lover.

Anurag's GitHub stats

front-end_practice's People

Contributors

dependabot[bot] avatar laclys avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

front-end_practice's Issues

Docker Note

Docker Note

容器技术的一个实现

container

快速打包技术

  • 标准化
  • 轻量化
  • 易移植

Linux Container(2008)

Namespace和Cgroup(负责资源管理控制)两大机制保障

标准化

docker != container

  • runtime spec(基本规范 eg:下载、创建容器)
  • image spec(镜像的基本格式)

Image VS Container

Image

  • Docker image是一个 read-only 文件
  • 理解成一个模板
  • 包含文件系统、源码、库文件还有依赖
  • Image有分层的概念

Container

”运行中的Docker Image“

  • 实质是复制image并在image最上层加上一层read-write的层 (称之为container layer,容器层)
  • 一个Image可以创建多个Container

获取

  • 自己
  • 从registry拉取(docker hub)

basic

docker container run nginx
可以去创建一个容器,如果没有nginx会去下载

docker container ls
容器的列出(up)

docker container stop (ID可以只写前面)
容器的停止

docker container ps -a or docker container ls -a
容器的列出(up和exit)(停止的也会展示)

docker container rm
容器的删除

tips

docker container stop $(docker container ps -qa)
批量停止 docker container ps -qa打印出所有container ID传入$

docker container rm $(docker container ps -qa)
同理批量删除 docker container ps -qa打印出所有container ID传入$、

  • 不能直接删除正在运行的容器(先停止再删除或者 加-f强制)

容器两种模式attached和detached模式

(win不是完整的attached模式,ctrl+c不能把容器停掉)

detached模式 > docker container run -d -p 80:80 nginx (后台运行)

docker attach
detached -> attached

(* 创建容器尽量使用detached模式)

容器交互

docker container logs
查看log

docker container logs -f
实时查看log

docker container run -it ubuntu sh
创建一个容器并进入交互式模式 (这里创建了ubuntu sh)

docker container exec -it
在一个已经运行的容器里执行一个额外的command

eg:

docker container run -d -p 80:80 nginx

docker exec -it <ID> sh

*exit退出

Container vs VM

截屏2021-06-29 下午11 04 59

容器并不是mini的虚拟机:container其实就是进程。容器中的进程被限制了对CPU内存等资源的访问。当进程停止后,容器就退出

docker container top
显示container运行了那些进程

docker container run

  • 在本地查找是否有nginx这个image镜像,但是没有发现

  • 去远程的image registry查找nginx镜像(默认的registry是Docker Hub)

  • 下载最新版本的nginx镜像 (nginx:latest 默认)

  • 基于nginx镜像来创建一个新的容器,并且准备运行

  • docker engine分配给这个容器一个虚拟IP地址

  • 在宿主机上打开80端口并把容器的80端口转发到宿主机上

  • 启动容器,运行指定的命令(这里是一个shell脚本去启动nginx)

Vue3 note

Vue3 note

Vue3支持Vue2大部分属性

Vue3代码被重新编译成分开的实现不同功能的moudle

性能提升:打包大小减少41%;初次渲染大小快了55%,更新快了133%;内存使用减少了54% (得益于重写虚拟dom和tree shaking实现)

Composition API (Composition:组合)
(复杂组件代码变得越来越难以维护) (解决两个问题:1.把相关的feature组合到一起, 不要分散在多个地方;2.可以非常容易滴重用比mixin有更高的灵活度)

包括:

  • ref & reactive
  • computed & watch
  • 新的生命周期函数
  • hook函数

新特性

Teleport - 瞬移组件位置

Suspense - 异步加载组建的新福音

全局API的修改和优化

更好的TS支持

Vue2遇到的问题

  • 随着功能增长,复杂组件的代码变得难以维护

Mixin的缺点:命名冲突; 不清楚暴露出来的变量的作用;重用到其他Componnent经常会遇到问题

  • 对Ts支持非常有限 (Vue2就没有考虑TS,类型不友好)
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <h1>{{ count }}</h1>
  <h1>{{ double }}</h1>
  <button @click="increase">👆+1</button>
</template>

<script lang="ts">
import { defineComponent, ref, computed,  } from 'vue';

export default defineComponent({
  name: 'App',
  setup() {
    const count = ref(0) 
    const double = computed(() => {
      return count.value * 2
    })
    const increase = () => {
      count.value++
    }
    return {
      count,
      increase,
      double
    }
  }
});
</script>

Vue2/3 响应式

Vue2是通过Object.definedProperty实现的。对于对象新增的属性无能为力,对于数组则需要拦截他的原型方法实现响应式

Vue3使用Proxy实现响应式。Proxy在更高维度上进行属性拦截修改

Vue3生命周期

在 setup 中使用的 hook 名称和原来生命周期的对应关系

beforeCreate -> 不需要
created -> 不需要
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeUnmount -> onBeforeUnmount
unmounted -> onUnmounted
errorCaptured -> onErrorCaptured

// 调试用
renderTracked -> onRenderTracked
renderTriggered -> onRenderTriggered

TS对Vue3的加持

Vue2对ts支持非常有限,Vue2依赖this上下文对象,往外暴露属性。vue的this比普通的JS object多了很多黑魔法。Vue2在推出中就没有考虑对ts的集成

vue2 都是用class的方式

Vue3有了defineComponent

setup(props,context) // props可以自动推论, context提供了三个this中最常用的属性(setup无法访问vue2最常用的this)

Teleport

问题: eg: Dialog被包裹在其他组件之中,容易被干扰

(类似react 传送门)

<teleport to="#modal">
....

</teleport>

Suspense

问题: 异步困境

  <Suspense>
    <template #default >
      <async-show></async-show>
    </template>
    <template #fallback >
      <h1>loadinng....</h1>
    </template>
  </Suspense>


// AsyncShow

<template>
  <h1>{{result}}</h1>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  setup() {
    return new Promise((resolve) => {
      setTimeout(() => {
        return resolve({
          result: 42
        })
      }, 3000)
    })
  }
})
</script>

全局配置修改:

1.Vue.config -> app.config

2.全局注册API

3.行为扩展API

vite note

构建工具的高阶封装,最核心的是rollup 和Vue3一起发布的。2.0之后完全独立的框架

vitejs.dev

目标

  • 使用简单,能封装的都封装了。集成了dev-server
  • 便于扩展。兼容rollup插件

类似产品

  • Snowpack
  • WMR
  • @web/dev-server

(官网对比)

Vite做的最好的一个

和传统构建工具对比

vite:

  • High Level Api;
  • 本身不具有编译能力,不参与编译。只是集成了rollup的功能。启动dev-server。帮我们在中间进行一个串联。底层的编译能力来自rollup
  • 完全基于ESM加载方式进行开发

vs webpack vs rollup

看webpack的官网 documentation 非常非常复杂。功能非常非常多,也非常强大。它希望把它能考虑加载的东西全部加载进去。同时它也希望它的核心功能帮助其他功能进行实现。比如我们可以基于webpack 实现一个webpack-dev-server。其实也是围绕webpack核心功能进行构建的。 它有各种各种构建的方式

rollup更加单一专一的工具。rollup诞生的目的就是build ES Module。同时它只专注于build js而不考虑平台。

对于webpack 我们编译出来代码会有很多webpack本身的工具函数。帮助我们加载模块。rollup不会有什么它专有的函数在里面。它会遵循esm的标准。它build出来的代码就符合common.js的标准或者是像umd的标准。没有自己的工具在里面,比较纯粹。只是符合这个规范。rollup初衷不是前端项目,而是工具类库。

vite:目标是工程、项目开发。不像webpack和rollup他们是工具,更加focus或者说执行的是”构建“。vite更加上层。High Level,初衷就是更好的体验,一开始设计就是为了去解决项目开发构建。学习成本比webpack、rollup低很多。

总结:webpack更全面,rollup更专一,vite为项目而生而不是为构建而生。

  • 减少了很多配置项
  • 减少的工作(dev-server/各类loader/内置build命令(类库、项目)) 做好了很多提前预设

Vite: 自身插件系统 生态兼容rollup

1.0 vue3

2.0 跨框架

React

FastRefresh: (官方插件)相比react-hot-loader。解决了很多rhl无法解决的问题;速度更快;以及支持局部更新

React.StrictMode

当我们使用CRA创建React App。挂在的APP节点默认被React.StrictMode严格模式包裹了(react16.3加上的)

React.StrictMode: 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告

简单说就是会检测意外的副作用。
例如
170b9f2f75a2c7d5

state.id值为多少?

大部分人都会说1。当然生产环境确实如此。但包裹React.StrictMode下,开发环境答案是2

React.StrictMode,它在开发环境中将 constructor 函数调用了两次。

为了检测意外的副作用,通过调用两次的方式将一些隐藏地比较深的副作用放大
再比如如下例子

let cache

export default () => {
  const [name, setname] = useState('')
  const [age, setAge] = useState(18)

  // dept name变化,userInfo值变化.
  const userInfo = useMemo(() => {
    return {
      fullname: `my name is ${name}`,
    }
  }, name)

  if (!cache) {
    cache = userInfo
  }

  console.log('!!!', {
    cache,
    userInfo,
    equal: cache === userInfo,
  })

  return (
    <div>
      <input type="text" value={age} onChange={e => setAge(e.target.value)} placeholder='age'/>
      {JSON.stringify({
        name,
        age,
        userInfo,
      })}
    </div>
  )
}

name没有变化userInfo不需要重新计算, 重新渲染。但开启StrictMode之后userInfo每次返回的都是全新的对象。不受dept影响

运行代码:这里第一次打印 equal: true,没问题。这里修改input输入框age的值。按道理useMemo的dept只依赖name。但是我们每次修改age,打印出来的都是equal:false,证明userinfo还是render了,渲染出来了新对象

去掉严格模式就正常


使用严格模式开发 要慎重。不光是useMemo、useState、useCallback都不是返回同一个对象

官方说法是:通过在开发环境下故意重复调用,帮助你发现它们,使它们更具确定性。我是觉得挺坑的。

Proxy里为什么要配合Reflect使用

  • HTML:
<body>
  <div>
    普通对象操作:
    <span id="common"></span>
  </div>

  <div>
    Reflect函数操作:
    <span id="reflect"></span>
  </div>
</body>
  • JS
 let user = {
    _name: "Guest",
    get name() {
      return this._name;
    }
  };

  let userProxy = new Proxy(user, {
    get(target, prop, receiver) {
      return target[prop];
    }
  });

  let admin = {
    __proto__: userProxy,
    _name: "Admin"
  };
  document.getElementById('common').innerHTML = admin.name

  let user2 = {
    _name: "Guest",
    get name() {
      return this._name;
    }
  };

  let userProxy2 = new Proxy(user2, {
    get(target, prop, receiver) {
      return Reflect.get(target, prop, receiver);
    }
  });

  let admin2 = {
    __proto__: userProxy2,
    _name: "Admin"
  }

  document.getElementById('reflect').innerHTML = admin2.name
  • Result:
截屏2023-09-09 23 14 31

直接使用普通对象: target[prop]这里target实际上还是user。this 指向的是user,而不是admin。故html显示Guest

使用Reflect.get(target, prop, receiver),第三个参数虽然没有用到,但是它代表的是 Proxy 实例或者继承 proxy 的那个对象。即:代理对象本身,同时也有可能是继承了代理对象的对象,具体区别于调用方

如果使用Reflect.get(target, prop)相当于 target[prop]


**1. Proxy 中接受的 receiver 形参,表示代理对象本身 或者 继承了代理对象的对象。

  1. Reflect 中传入的 recriver 实参,表示修改执行原始操作时的 this 指向。**

TS函数返回类型怎样根据传入参数值的不同而改变

工作上遇到一个问题,是这样的

假如一个函数test(p)
type p = 't' | 'h'

当test('t')时 返回值类型为 string;

当test('p')时 返回值类型为 number;

这里给出两种解答

  1. 重载
type p = 't' | 'h'
function test(p: 't'): number

function test(p: 'h'): string

function test(p: 't' | 'h'): number | string {
    if (p == "t") {
        return 123
    } else {
        return "123"
    }
}
  1. 引入泛型,进行类型推断

现阶段的TS,无法做到 p === 't'或者 p extends 't' 来推断出就是type p = 't' | 'h'中的't'(ts 的自动推断主要依赖 typeofinstanceof、对象类型的属性判断等)
所以我们显式的声明返回值类型_(借助interface)_。 并且通过mapping type得到返回类型

interface TestReturn  {
    t: number
    h: string
    [key: string]: unknown
}

// types util
function isKeys(key: unknown): key is keyof TestReturn {
    return key === 'a' || key === 'b'
}

function test<T extends keyof TestReturn>(p: T): TestReturn[Key]

// 重载了一下防止未知字段传入
function test(key: string): unknown {
    const store: Values = {
         t: 123
         h: '123'
    }

    if (isKeys(key)) {
        return store[key]
    }

    return store[key]
}

个人比较喜欢第二种方式

react hook开发的一些纠结点

官方eslint

官方eslint配置之后,副作用函数有props或者state变量未存在于依赖数组中,会有 exhaustive-deps 规则的提醒。
然而一些场景下又与这些规则不符。
比如时间类函数类似componentDidMount的功能一般会这么写:

useEffect(() => {
// ....
    console.log('var1', var1)
    console.log('var12, var2)
}, [])

假设var1 & var2 是我们通过 useState或者props传进来的变量。再或者,里面有函数执行,比如一些请求异步函数。然而这里我们没有在依赖数组中加入这些变量(deps)。就触发了 exhaustive-deps 规则的提醒。黄色的小warning,大可不必理会。(自动lint --fix一股脑把依赖添加到依赖数组里有时会出错)。强迫症出于对eslint的规则遵守。给出的解决方案就是 通过useRef来回避这个问题。对副作用函数中的每个变量都创建对应的useRef值。这样怎么说又麻烦有不合理。比如我们封装一个useMount

function useMount(fn) {
  const fnRef = useRef()
  fnRef.current = fn;
  useEffect(() => {
    fnRef.current();
  }, [fn]);
}

warning解除

再举个栗子

function Modal({visible,  value}) {
   useEffect(() => {
     visible && console.log('value', value)
   }, [visible])

}

这样一个简单的例子。依旧触发了就触发了 exhaustive-deps 规则的提醒,有黄线。
但确确实实有这样的需求和问题。例子中Modal组件需要根据visible变量变化执行 ·”xxxxxx“一系列操作。操作中会引用其他变量比如这里的value。但是我们确实不希望value加在deps中,因为我们并不关心它的变化。(需要引用到其它的 props 或 state 变量)如果把所有变量都加入deps中带来的副作用可能会造成重复执行,这是非预期的也是没必要的事情(为了解决eslint小黄线而重复执行一次。这里 我们又迫于无奈需要一个辅助变量来记录 visible 变量的前一状态值,用来在副作用函数中判断是否因为 visible 变量变动触发的函数执行。我们又要用useRef封装一个前值函数。

const usePrevious = (value) => {
  const ref = useRef()
  useEffect(() => {
    ref.current = value
  })
  return ref.current
}
function Modal({visible,  value}) {
  const prev = usePrevious(visible)
   useEffect(() => {
     prev !==visible &&  visible && console.log('value', value)
   }, [visible, prev, value])

}

希望 useEffect 的依赖数组中是与副作用函数更有效的变量,而不是副作用函数中全部引用的变量。

再说useCallback

在 类组件中,传递给子组件的props变量中函数大多数都是this.xxx 形式,即引用都是稳定的。useCallback 函数,只要依赖数组保持稳定,会返回一个引用稳定的函数。useCallback在项目中使用有时候会耗尽心思,一单一单 组件树上层一个不小心出了问题,有是前功尽弃
比如

function Button({ child, disabled, onClick }) {
  const handleBtnClick = useCallback(() => {
  !disabled && onClick?.()
  }, [disabled, onClick]);

  return (
    <button onClick={handleBtnClick}>{child}</button>
  );
}

function App() {
  const onBtnClick = () => {}
  return (
    <Button onClick={onBtnClick} />
  )
}

这么一个App组件包裹着Button组件。我们希望_disabled 和 onClick 变量的引用变动时才返回一个新的引用的函数_。但是 APP组件这一层 没有用useCallback包裹onBtnClick保持函数稳定,每次传递给Button的都是新函数(或者说全新的函数引用)子组件接收触发deps,生成一个全新引用的 handleBtnClick 函数。怎么办,这里我们再在子组件Button中用useRef解决问题

function Button({ child, disabled, onClick }) {
  const handleBtnClickRef = useRef()
  handleBtnClickRef.current = () => {
    !disabled && onClick?.()
  };

  const handleBtnClick = useCallback(() => {
    handleBtnClickRef.current()
  }, [handleBtnClickRef])

  return (
    <button onClick={handleBtnClick}>{child}</button>
  );
}

我们用useRef- handleBtnClickRef保存最新的函数。变量引用是固定的,所以 handleBtnClick 函数的引用也是固定的。触发 onClick 回调函数也能拿到最新的 disabled 和 onClick 值

这里再举一个例子:需要得到一个不变的函数引用,但这个不变的函数执行的时候,执行的是传递的最新函数。这个实在wukong这各项目里用的。直接复制过来

export function useCallbackRef(fn) {
  const fnRef = useRef(fn)
  fnRef.current = fn

  return useCallback(((...args) => fnRef.current(...args)), [])
}

useRef

这里useRef成为了解决问题的关键。很像class里面的this.xxxmutable的方式去解决问题。副作用、依赖数组真的增加了很多心智负担

rollup note

vs webpack

开源类库优先选择,更加是个打包独立的类库;以ESM为目标的构建工具;Tree Shaking

Rollup插件

(大部分可以直接在Vite使用)

截屏2021-09-26 下午11 31 17

常用插件

  • Commonjs
  • Babel
  • TS
  • Replace
  • Node resolve
  • eslint
  • image
  • strip(去除一些没必要的代码eg: console.log····)
  • wasm

前端框架模板引擎

前端框架,核心能力--模板引擎实现

页面正常工作需要实现的功能流程:

  • 监听操作
  • 获取数据变量
  • 使用数据变量拼接成HTML模板
  • 将HTML内容塞到页面对应的地方
  • 将HTML片断内需要监听的点击时间进行绑定

如果使用前端框架(数据驱动!!!)

  • 使用将数据变量绑定到HTML模板的方式,来控制展示内容
  • 配合一些条件判断、条件循环等逻辑,控制交互具体逻辑
  • 通过改变数据变量,框架会自动更新页面内容

Vue为例页面渲染分4步

  1. 解析语法生成 AST 对象;
  2. 根据生成的 AST 对象,完成data数据初始化;
  3. 根据 AST 对象和data数据绑定情况,生成虚拟 DOM 对象;
  4. 将虚拟 DOM 对象生成真正的 DOM 元素插入到页面中,此时页面会被渲染。
  • 为什么要经过AST这一步?
  • 将 HTML 模板解析成 AST 对象,再根据 AST 对象生成 DOM 节点,在这个过程中前端框架可以实现以下功能:
    • 排除无效 DOM 元素(非自定义组件、也非默认组件的 DOM 元素),在构建阶段可及时发现并进行报错;
    • 可识别出自定义组件,并渲染对应的组件;
    • 可方便地实现数据绑定、事件绑定等功能;
    • 为虚拟 DOM Diff 过程打下铺垫;
    • HTML 转义(预防 XSS 漏洞)。

hooks note

function Test1(props: { onChange: (res: string) => void }) {
  return (
    <input
      onChange={(e) => {
        props.onChange(e.target.value);
      }}
    />
  );
}

function Test1Wrapper() {
  let a = "";
  const [b, setB] = useState(0);
  useEffect(() => {
    const i = setInterval(() => {
      setB((res) => ++res);
    }, 1000);
    return () => {
      clearInterval(i);
    };
  }, []);
  return (
    <>
      <Test1
        onChange={(res: string) => {
          a = res;
        }}
      />
      {b}
      <button
        onClick={() => {
          console.log(a);
        }}
      >
        show a
      </button>
    </>
  );
}

直接写变量 - 时有时无
-> 因为 React 中,类型为 Dispatch 的方法(useReducer 的 dispatch 和 useState 的 setState),都会导致当前函数重新运行

而此处的 a 是个变量,它会在函数运行时重新赋值为 “”,导致之前的状态丢失

因此,如果 log 按钮点击前发生了任何其他依赖的状态变化(包括 context,state,props),a 都会被重置

typescript tips

工具类

  • Required
type Required<T> = {
    [P in keyof T]-?: T[P];
};

-? 来标注属性为必填的属性,- 用来清除可选性实现 Required。。
+?也是有效的,Partial可知 +? 的 +`是可以省略的

eg:

interface Props {
  a?: string
  b?: string
  c?: string
}

// 仅保留b,c属性并转为必填
type NewProps1 = Required<Pick<Props, 'b' | 'c'>>

// 需要保留Props的所有属性,但是b,c需要必填
type NewProps2 = Partial<Props> & Required<Pick<Props, 'b' | 'c'>>
  • Record<Keys, Type>
    构造一个类型,这个类型的键的类型是 Keys,值的类型 Type
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

eg:

// 简单的限定键和值的类型
type Obj1 = Record<string, string>

// 基于其他类型生成新的类型
type FruitTypes = 'apple' | 'banana' | 'pear'

interface FruitInfo {
  name: FruitTypes
  price: number
}

type Fruits = Record<FruitTypes, FruitInfo>

const fruits: Fruits = {
  apple: {
    name: 'apple',
    price: 10
  },
  banana: {
    name: 'banana',
    price: 11
  },
  pear: {
    name: 'pear',
    price: 12
  }
}
  • Omit
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Exclude<T, U> = T extends U ? never : T;

eg:

// 工具类型 SelectRequired<T, K in keyof T> 
interface Props {
  a?: string
  b?: string
  c?: string
  d: string
}

type NewProps = SelectRequired<Props, 'b' | 'c'>;    // { a?: string, b: string, c: string, d: string }

// 实现
type SelectRequired<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T, K>
  • Exclude<Type, ExcludeUnion>
type Exclude<T, U> = T extends U ? never : T;

eg:

// 排除一个具体的值

type T0 = Exclude<"a" | "b" | "c", "a">  // "b" | "c"

// 排除一种类型

type T1 = Exclude<string | number | (() => void), Function>
  • Extract<Type, Union>
/**

 * Extract from T those types that are assignable to U

 */

type Extract<T, U> = T extends U ? T : never;
  • NonNullable
    过滤掉 Type 中的 null 和 undefined,剩余的类型作为一个新类型返回。其实就是 Exclude 的一种特殊情况。
type NonNullable<T> = T extends null | undefined ? never : T
  • Parameters
/**

 * Obtain the parameters of a function type in a tuple

 */

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
  • ReturnType
/**

 * Obtain the return type of a function type

 */

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

Typescript4.0之后的新特性

4.0 元组元素标签

type LabeledTupleType = [id: number, name: string];

可变元组

在元组类型的语法中,我们可以对泛型使用展开运算符 & 可以在元组中的任何位置使用剩余元素

const TupleA = ['A'] as const;
const TupleB = ['B'] as const;
type TupleType = readonly any[];
function concat<T extends TupleType, U extends TupleType>(arr1: T, arr2: U): [...T, ...U] { //  4.0版本之前会报错:ts(1256)
return [...arr1, ...arr2]; //  4.0版本之前会报错:ts(2741)
}
const TupleC = concat(TupleA, TupleB); // ['A', 'B'] // 合并两个元组为一个新的元组的函数!!! 从而得到了新的元组类型 ['A', 'B'] 
type ConcatedTuple = [ ...(typeof TupleA), ...(typeof TupleB)];

极大地提升函数式编程的类型体验和可能性

可以在函数组合中使用可变元组约束高阶函数入参和返回值的类型,比如对 JavaScript 内置 bind 方法更好地进行类型检测支持

4.1 模板字面量

Break Change🤓 使得字符串类型也具备了可变可运算的能力。

type PrefixType<P extends string, N extends string> = `${P}/${Capitalize<string & N>}`;
type UserLoginAction = PrefixType<'User', 'login'>; // 'User/Login'

可以对字符串类型进行拼接等操作 更加灵活,比如我们在定义路由的类型以及redux action type的类型都可以用到

// 比如将 { id: string; name: string} => “user/:id/:name/”

type RoutePath<P extends string, N extends string> = `user/${P}/${N}/`
type a = RoutePath<'123', 'Lac'> // "user/123/Lac/"

实现 lodash get

type PropType<T, Path extends string> =
    string extends Path ? unknown :
    Path extends keyof T ? T[Path] :
    Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropType<T[K], R> : unknown :
    unknown;

declare function get<T, P extends string>(obj: T, path: P): PropType<T, P>;

const obj = { a: { b: {c: 42, d: 'hello' }}};

const value = get(obj, "a.b.c")

4.1 映射类型键名重新映射

在映射类型中,我们可以使用 as 操作符对键名重新映射(可以理解为针对类型的类型断言)

type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserInfoGetters = Getters<{ id:number; name: string; }> // { getId: () => number; getName: () => string; }

4.2元组头部/中间剩余元素

可以在元组的任何地方使用剩余元素表达式,而不再仅仅局限于元组的尾部

const prefixRestTuple: [...rest: string[], number] = ['a', 'b', 1];
const middleRestTuple: [boolean, ...rest: string[], number] = [true, 'a', 'b', 1];

4.2yield 表达式提示 noImplicitAny 错误

必须显式注解 yield 表达式的返回值类型,否则会提示 noImplicitAny 错误

这个还没有测试,如果使用Redux-saga管理effect副作用 yield这块要注意

4.3增加了关键字 overrride

以保证基础类中的方法不会被覆盖

性能分析

性能分析:页面白屏过久、操作卡顿

  • 通常前端性能分析两个角度:时间和空间

    • 时间:常见耗时:页面加载耗时、渲染耗时、网络耗时、脚本执行耗时等

    • 空间资源占用,包括CPU占用、内存占用、本地缓存占用等

  • Chrome DevTools

    • lighthouse面板:该面板用于自动化分析网站加载时,存在的性能问题,并提出推荐的优化方案
    • performance面板: 该面板用于记录、分析网站在运行时的性能数据
  • lighthouse:前身是Chrome DevTools的Audits。优势在于自动化、成本低,会收集网站加载时的性能数据,并根据最佳实践给每一项进行打分,同时针对最低分给出对应的优化方案

    • 三个主要功能
      • 在一系列的测试下运行网站,比如不同尺寸的设备和不同的网络速度
      • 检查页面对辅助功能指南的一致性,例如颜色对比度、ARIA最佳实践
      • 生成网页运行报告(网页性能、常见统计耗时、网页优化方向)
      • 报告五个方面:性能、无障碍、最佳做法、SEO、PWA

image

   - 四个:驱动(Driver)、收集器(Gatherers)、审查器(Audits)和报告(Report)
   -  过程:
       - 当网站页面开始加载之后,Lighthouse 会使用驱动(Driver)通过 Chrome DevTools Protocol 获取页面的性能数据;
       - 驱动(Driver)获取到的数据会被收集器(Gatherers)进行收集,并输出被称为 Artifact 的结果;
       - Artifact 会作为审查器(Audits)的输入,审查器会对其运行测试,然后分配通过/失败/得分的结果;
       - 审查的结果给到报告(Report),对各个部分进行加权和统计得到面向用户的报告(如最佳实践),并将该报告渲染给用户。

(但是lighthouse不能用于运行时的性能分析,无法给到最佳实践以外的数据/建议)

  • performance:前身 Timeline面板,常用在页面运行时使用,比如用户点击操作之后的逻辑执行、页面滚动时的页面渲染情况
    (对页面进行录制最好打开隐身模式 确保Chrome不被扩展插件影响)
    image-1

  • Monitor: 对CPU占用率、内存使用大小、挂在Dom数···

  • 面板可以看到详细的性能数据报考函数调用的情况、各类事件的顺序和耗时、CPU占用情况···我们可以看这些定位性能问题
    (用performanc进行分析,分析过程比较繁琐。上手成本不低,除了页面加载耗时、网络耗时。定位具体问题需要看FPS、CPU占用以及火焰图)

    • 性能分析自动化
    • 性能分析处理不是一蹴而就的事情 需要不断分析、优化、处理。需要自动化
    • 方案
      • 通过集成测试、自动化测试等技术让项目运行在浏览器中
      • 根据脚本本来运行相应的功能
      • 需要过去网页加载和运行的性能数据
  • ighouse自动化: 通过自动化任务跑脚本的方式,使用lighthouse跑分析报告。通过对比以往的数据来进行功能变更、性能优化等场景的性能回归(成本低)

  • Chrome-Dev-Protocol: 允许第三方对Chrome网站进行检查、调试、分析等操作。可以自行开发工具 通过Chrome-Dev-Protocol获取Chrome中网站数据

  • 其实就是一个WEB应用。当我们打开 Chrome DevTools 的时候,浏览器内核 Chromium 本身会作为一个服务端,它会通过 WebSocket 与 Chrome DevTools 进行通信

  • 过程如下:

    • DevTools 将作为客户端,与作为服务端的 Chromium 建立连接;
    • DevTools 通过 HTTP 获取 HTML、JavaScript 和 CSS 资源,并进行加载;
    • 资源加载后,DevTools 会建立与浏览器的 WebSocket 连接;
    • Chrome DevTools Protocol 基于 WebSocket,它利用 WebSocket 建立连接 DevTools 和浏览器内核的快速数据通道。
  • Chrome-Dev-Protocol 不管是win、mac、手机端调试都用的该协议。提供的内容:具有与浏览器不同交互(页面、SW、扩展程序)的API。

  • 该协议把不同操作分为不同的域(就是Devtools中的不同功能模块)

  • 具体几个:

    • Performance域:可获取运行时性能指标,包括页面 DOM 节点数量、Javascript 栈数量、页面布局耗时等。
    • Tracing域:可获取页面加载的 DevTools 性能跟踪,可以使用Tracing.start和Tracing.stop创建可在 Chrome DevTools 或时间轴查看器中打开的跟踪文件。
    • Runtime域:通过远程评估和镜像对象暴露 JavaScript 的运行时,可以获取 JavaScript 栈的使用情况。
    • Network域:可以分析网络相关的性能。
    • 其他涉及 DOM 节点、JS 执行等相关数据的域。
  • 通过使用Chrome-Dev-Protocol 我们可以拿到很多数据包括网络数据、性能数据(JS Runtime像window.performance、window.chrome.loadTimes())

  • 如何使用协议:

    • 这个协议封装出不同语言的库,包括 Node.js、Python、Java 等,可以在awesome-chrome-devtools这个项目中找到
    • Puppeter的CDPSession(封装了Chrome-Dev-Protocol 能力)
  • Chrome-Dev-Protocol可以模拟浏览器环境 通过一系列工具、规则运行你的页面,最后最后的得到审计报告。那么也可以根据业务的需要对这些数据进行自动化的分析,结合前端工程化在项目上线前进行自动化任务的执行、检测、生成对比报告等,实现自动化性能监控

Next.js 一些记录

Next.js
• 直观的、 基于页面 的路由系统(并支持 动态路由)
• 预渲染。支持在页面级的 静态生成 (SSG) 和 服务器端渲染 (SSR)
• 自动代码拆分,提升页面加载速度
• 具有经过优化的预取功能的 客户端路由
• 内置 CSS 和 Sass 的支持,并支持任何 CSS-in-JS 库
• 开发环境支持 快速刷新
• 利用 Serverless Functions 及 API 路由 构建 API 功能
• 完全可扩展
static-generation
server-side-rendering

When to Use Static Generation v.s. Server-side Rendering
We recommend using Static Generation (with and without data) whenever possible because your page can be built once and served by CDN, which makes it much faster than having a server render the page on every request.
You can use Static Generation for many types of pages, including:
• Marketing pages
• Blog posts
• E-commerce product listings
• Help and documentation
You should ask yourself: "Can I pre-render this page ahead of a user's request?" If the answer is yes, then you should choose Static Generation.
On the other hand, Static Generation is not a good idea if you cannot pre-render a page ahead of a user's request. Maybe your page shows frequently updated data, and the page content changes on every request.
In that case, you can use Server-side Rendering. It will be slower, but the pre-rendered page will always be up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate frequently updated data.
When would you use Server-side rendering?
A: When the data needs to be up-to-date with every request

Webpack打包优化

打包流程

  1. 初始化
  2. loader解析(loader解析耗时)
  3. 搜索依赖文件(搜索耗时)
  4. 打包生成chunk(打包耗时)

缩短解析时间

  1. rules.include exclude
  2. 多进程加速loader的解析时间(thread-loader(预热worker池))
  3. 加缓存,尽可能多加(babel、html...)

第一次构建生成文件缓存,第二次构建看文件有没有变化,没有变化就是用缓存

三个问题:

  1. thread-loader/cache-loader哪个在前?
    > normal:从右往左、pich: 从左往右可通过return阻断后面的流程
A: thread-loader
  1. babel-loader、cache-loader缓存区别、是否取其一?
    > catch-loader:根据mtime来缓存,轻量(控制整个loader流程的缓存)
    > babel-loader:根据文件内容来缓存

屏幕快照 2021-04-01 11 23 24

屏幕快照 2021-04-01 11 23 36

A: 最好都加
  1. 其他loader是否需要缓存

A:使用缓存有代价,所以必须是很重的任务才有效果,否则适得其反

缩短搜索时间

  1. resovle.module
  2. resolve.alias
  3. resolve.extension

缩短打包耗时

  1. 缓存生成的chunk
  2. smaller = faster(使用数量更少/体积更小的lib)
    屏幕快照 2021-04-01 15 47 02
splitChunks: {
  minSize
  maxInitialRequests

  chunks
  minChunks
}

其他

  • DLL(预编译资源模块 把不会修改的资源模块分包出来。很容易配置错,webpack4性能提升,帮助作用不大)
  • noParse(完全不需要webpack解析的模块)
  • IgnorePlugin(moment库)

SvgIcon

  • 支持多色图标
  • 可以控制图标不同部分
  • 是矢量图标,图标锐利,体积更小

制作平台:IconPark

qiankun notes

特点

实现基于 single-spa
HTML Entry 接入方式:接入子应用像iframe一样简单

只配置子应用的首页地址
根据地址来请求整个页面,从页面中提出 script 和 style 资源
script 通过 eval 来执行

资源预加载

配合 HTML Entry ,在空闲时候加载子应用的首页的 style 和 script, style 写入 html,script 放入缓存

样式隔离:子应用之间的样式互不干扰
JS 沙箱:子应用之间的 全局变量/事件 不冲突

mount 生命周期时 proxy 劫持 window
unmount 生命周期时恢复 window 属性和被劫持的方法

prefetch 预加载

requestIdleCallback 时机
请求所有注册的app,匹配 style 和 script 资源,预先下载缓存

接入

添加 package.json 的 name 属性,作为唯一标记,应用之间不重复
打包出来的配置需要更改
需要提供一个不带侧边栏(头)的版本

CSS 设计模式

OOCSS

OO:面向对象

原则一:容器与内容分离
原则二:结构与皮肤分离
原则三:面向设计开发

应用:Grid栅格系统

BEM

块(Block)、 元素(Element)、修饰符(Modifier)

作用:命名规范、让页面结构更清晰
进阶版oocss

SMACSS

分类:Base、Layout、 Modules、State、Theme
好处:易维护、易复用、易扩展。。
命名规范:.l-header .is-hidden .theme-nav

ITCSS

ACSS

一个样式属性一个类
好处:极强的复用性、维护成本低
坏处:破坏了CSS命名语义化

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.