2022-09-26Webpack00

参考解决方式

  • 使用 vue-router 路由懒加载
  • 使用 gzip 压缩
  • 使用 CDN 引入 js 和 css 文件
  • 配置 webpack 的 external,不打包第三方库
  • 配置 DllPlugin 和 DllReferencePlugin 将引用的依赖提取

webpack 打包 vue 速度慢,可以通过 webpack-bundle-analyzer 进行可是化分析,主要看依赖和 chunks 打包的时间。

  • 减少文件依赖嵌套的深度
  • 使用尽可能少的处理(loader、plugin)
  • DLL 处理第三方的包
  • 多线程打包(HappyPack)
  • 关闭 sourcemap
  • 减少代码体积、压缩代码
  • 优化 resolve.extensions 配置
  • 优化 resolve.modules 配置
  • 优化 resolve.alias 配置
  • 使用 include 和 exclude
  • 设置 babel-loader 缓存

另外打包慢,是一个综合因素,和 Vue 关系不大

  • 确保 Webpack 、Npm、Node 及主要库版本要新,比如 4.x 比 3.x 提升很多,5.x 比 4.x 又提升很多。

  • loader 范围缩小到 src 项目文件,一些不必要的 loader 能关就关了

  • eslint 代码校验其实一个很费时间的步骤

    • 可以把 eslint 范围缩小到 src,且只检查 *.js,*.vue文件
    • 生产环境不开启 lint,使用 per-commit 或者 husky 在提交前校验
  • 其它还是前边提到的,多进程运行,动态链接库(DllPlugin)

2022-09-26React00

Props

React 官方文档说法:

React is pretty flexible but it has a single strict rule: all React components must act like pure functions with respect to their props.

props 是组件的只读属性,组件内部不能直接修改 props,要想修改 props,只能在该组件的上层组件中修改

在 React 中,props 是上层组件传递进来的值,这个值是父类的值,同 Vue 一样,props 的传值是单项数据流,也就是不会让影响父类的值,如果需要改值,可以先让 props 赋值给一个变量,在修改这个变量。

其实就是保证 React 单向数据流的设计模式,使状态可预测。

如果允许子组件修改,那么一个父组件将状态传递给好几个子组件,这几个子组件随意修改,就完全不可预测,不知道在什么地方修改了状态。

2022-09-26React00

Portals

插槽(Portals)能将子节点渲染到父组件的 DOM 层次之外。

应用场景

当父组件有 overflow: hidden 或 z-index 样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。

具体使用

ReactDOM.createPortal(child, container);
  • 第一个参数 child 是任何可渲染的 React 子元素,例如一个元素,字符串或片段(fragment)。
  • 第二个参数 container 则是一个 DOM 元素
render() {
  // React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
  // `domNode` 是一个可以在任何位置的有效 DOM 节点。
  return ReactDOM.createPortal(
    this.props.children,
    domNode
  );
}

具体应用示例

实现一个全局浮层:

  • html
<body>
  <div id="app-root"></div>
  <div id="mask-root"></div>
</body>

-css

#modal-root {
  position: relative;
  z-index: 999;
}
.modal {
  color: #fff;
  background-color: rgba(0, 0, 0, 1);
  position: fixed;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
  • react
const appRoot = document.getElementById('app-root');
const maskRoot = document.getElementById('mask-root');
class Mask extends React.Component{
  constructor(props){
    super(props);
    this.el = document.createElement('div');
  }
  componentDidMount()){
    maskRoot.appendChild(this.el);
  }
  componentWillUnmount(){
    maskRoot.removeChild(this.el);
  }
  render(){
    return ReactDom.createPortal(
      this.props.children,
      this.el
    )
  }
}
class App extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      showMask:false
    }
    this.handler = this.handler.bind(this);
  }
  handler(){
    let showMask = this.state.showMask;
    this.setState({showMask:true})
  }
  render(){
    let showMask = this.state.showMask;
    let mask = showMask ?
      <Mask>
        <div className="modal">
            测试
            <button onClick={this.handler}>hidden</button>
        </div>
      </Mask>
      :null;
    return (
      <div>
        this div has overflow:hidden.
        <button onClick={this.handler}>show</button>
        {mask}
      </div>
    )
  }
}
ReactDOM.render(<App />,appRoot);
2022-09-26TypeScript00

解决 TS 问题的最好办法就是多练,这次解读 type-challenges Medium 难度 63~68 题。

精读

Unique

实现 Unique<T>,对 T 去重:

type Res = Unique<[1, 1, 2, 2, 3, 3]> // expected to be [1, 2, 3]
type Res1 = Unique<[1, 2, 3, 4, 4, 5, 6, 7]> // expected to be [1, 2, 3, 4, 5, 6, 7]
type Res2 = Unique<[1, 'a', 2, 'b', 2, 'a']> // expected to be [1, "a", 2, "b"]
type Res3 = Unique<[string, number, 1, 'a', 1, string, 2, 'b', 2, number]> // expected to be [string, number, 1, "a", 2, "b"]
type Res4 = Unique<[unknown, unknown, any, any, never, never]> // expected to be [unknown, any, never]

去重需要不断递归产生去重后结果,因此需要一个辅助变量 R 配合,并把 Tinfer 逐一拆解,判断第一个字符是否在结果数组里,如果不在就塞进去:

type Unique<T, R extends any[] = []> = T extends [infer F, ...infer Rest]
  ? Includes<R, F> extends true
    ? Unique<Rest, R>
    : Unique<Rest, [...R, F]>
  : R

那么剩下的问题就是,如何判断一个对象是否出现在数组中,使用递归可以轻松完成:

type Includes<Arr, Value> = Arr extends [infer F, ...infer Rest]
  ? Equal<F, Value> extends true
    ? true
    : Includes<Rest, Value>
  : false

每次取首项,如果等于 Value 直接返回 true,否则继续递归,如果数组递归结束(不构成 Arr extends [xxx] 的形式)说明递归完了还没有找到相等值,直接返回 false

把这两个函数组合一下就能轻松解决本题:

// 本题答案
type Unique<T, R extends any[] = []> = T extends [infer F, ...infer Rest]
  ? Includes<R, F> extends true
    ? Unique<Rest, R>
    : Unique<Rest, [...R, F]>
  : R

type Includes<Arr, Value> = Arr extends [infer F, ...infer Rest]
  ? Equal<F, Value> extends true
    ? true
    : Includes<Rest, Value>
  : false
2022-09-26TypeScript00

解决 TS 问题的最好办法就是多练,这次解读 type-challenges Medium 难度 57~62 题。

精读

Trim Right

实现 TrimRight 删除右侧空格:

type Trimed = TrimRight<'   Hello World    '> // expected to be '   Hello World'

infer 找出空格前的字符串递归一下即可:

type TrimRight<S extends string> = S extends `${infer R}${' '}`
  ? TrimRight<R>
  : S

再补上测试用例的边界情况,\n\t 后就是完整答案了:

// 本题答案
type TrimRight<S extends string> = S extends `${infer R}${' ' | '\n' | '\t'}`
  ? TrimRight<R>
  : S

Without

实现 Without<T, U>,从数组 T 中移除 U 中元素:

type Res = Without<[1, 2], 1> // expected to be [2]
type Res1 = Without<[1, 2, 4, 1, 5], [1, 2]> // expected to be [4, 5]
type Res2 = Without<[2, 3, 2, 3, 2, 3, 2, 3], [2, 3]> // expected to be []

该题最难的点在于,参数 U 可能是字符串或字符串数组,我们要判断是否存在只能用 extends,这样就存在两个问题:

  1. 既是字符串又是数组如何判断,合在一起判断还是分开判断?
  2. [1] extends [1, 2] 为假,数组模式如何判断?

可以用数组转 Union 的方式解决该问题:

type ToUnion<T> = T extends any[] ? T[number] : T