参考解决方式
- 使用 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)
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 单向数据流的设计模式,使状态可预测。
如果允许子组件修改,那么一个父组件将状态传递给好几个子组件,这几个子组件随意修改,就完全不可预测,不知道在什么地方修改了状态。
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);
解决 TS 问题的最好办法就是多练,这次解读 type-challenges Medium 难度 63~68 题。
实现 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
配合,并把 T
用 infer
逐一拆解,判断第一个字符是否在结果数组里,如果不在就塞进去:
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
解决 TS 问题的最好办法就是多练,这次解读 type-challenges Medium 难度 57~62 题。
实现 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<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] extends [1, 2]
为假,数组模式如何判断?可以用数组转 Union 的方式解决该问题:
type ToUnion<T> = T extends any[] ? T[number] : T