# 第 2 章 框架设计的核心要素

# 1. 提升用户的开发体验

友好的警告、错误提示信息,让我们能够清晰快速地定位问题。

源码中 initCustomFormatter 函数用于在开发环境设置浏览器自定义输出的格式

开启浏览器自定义输出格式:

  • Settings -> Preferences
  • Console -> Enable custom formatters

# 2. 控制框架代码的体积

vue.js 使用 rollup.js 进行构建,会输出两种版本:

  • 开发版
  • 生产版

打印语句:

// __DEV__ 构建工具预定义常量
if (__DEV__ && !res) {
  warn(
    `Failed to mount app: mount target selector "${container}" returned null`
  );
}

开发环境时:

/*
  这段永远不会执行的代码为 dead code,编译时会被移除,这就减少了代码的体积
*/
if (false && !res) {
  warn(
    `Failed to mount app: mount target selector "${container}" returned null`
  );
}

总结:

  • 构建工具设置预定义常量 __DEV__,在生产环境不包含打印语句,从而使得框架自身的代码量不随着警告信息的增加而增加。

# 3. 框架要做到良好的 tree-shaking

说明:

  • tree-shaking 因 rollup.js 而普及
  • 作用:消除那些永远不会执行的代码,即排除 dead code

条件:

  • 针对 ESModule(依赖 ESM 的静态结构)
  • 移除没有副作用的 dead code

标记为没有副作用的代码:

// utils.js
export function add(num1, num2) {
  return num1 + num2;
}

// main.js
import { add } from './utils';

add(1, 2);

/*#__PURE__*/ console.log( add(1, 2) );

tree-shaking 后,上面三行代码都会被移除:

npx rollup main.js -f esm -o bundle.js

# 4. 框架应该输出怎样的构建产物

-browser 的都是给浏览器用的,如 vue.esm-browser.js

-bundle 的是给构建工具用的:

if (__DEV__) {
  // ...
}

// => __DEV__ 会被替换
if (process.env.NODE_ENV !== 'production') {
  // ...
}

webpack、rollup.js 寻找资源时,会优先使用 package.json 中的 module 属性:

{
  "main": "index.js",
  "module": "dist/vue.runtime.esm-bundle.js"
}

# 5. 特性开关

通过预定义常量来控制是否包含某特新的代码

源码:

// support for 2.x options
if (__FEATURE_OPTIONS_API__) {
  // ...
}

-bundle 的代码:

if (__VUE_OPTIONS_API__) {
  // ...
}

在 webpack 中设置值:

new webpack.DefinePlugin({
  __VUE_OPTIONS_API__: JSON.stringify(true)
})

# 6. 错误处理

统一的错误处理接口:

  • utils.js

    let errorHandler = (e) => console.log(e);
    
    export default {
      fn1(callback) {
        callWithErrorHandling(callback);
      },
    
      fn2(callback) {
        callWithErrorHandling(callback);
      },
    
      registerErrorHandler(fn) {
        errorHandler = fn;
      }
    }
    
    function callWithErrorHandling(fn) {
      try {
        fn && fn();
      } catch(e) {
        console.log(e);
      }
    }
    
  • 使用:

    import utils from './utils';
    
    utils.registerErrorHandler((e) => {
      console.log(e.message);
    })
    
    utils.fn1(() => {/* ... */});
    utils.fn2(() => {/* ... */});
    

在 vue 中注册统一的错误处理函数:

app.config.errorHandler = () => {
  // ...
}

# 7. 良好的 TS 类型支持

用 TS 编写框架,与对 TS 类型支持友好 是两码事

# 8. 总结

提供友好的警告信息很重要,有助于开发者快速定位问题

但警告信息越详细,框架体积越大

利用 tree-shaking 和 预定义常量 __DEV__ 排除掉提升开发体验的代码以减小包的体积

tree-shaking 是一种排除机制,基于 ESM,可利用 /*#__PURE__*/ 注释来辅助构建工具

本章目录