Vue3 tool function shared

1, Foreword

This article is the first article in the source code series since the author has been engaged in the front-end for two years. Usually, when building the infrastructure of the project, it is essential to add utils tool functions. How does Vue source code write tool functions? With questions, the essay walks into the first tool function of Vue source code

2, Environmental preparation

In order to reduce the difficulty, we can choose to package according to the method in the contribution guide and convert ts into js

node -v
#v16.9.0

# Clone project
git clone https://github.com/vuejs/vue-next.git
cd vue-next

# Global install yarn
npm install --global yarn
yarn install

# Package build code
yarn build

You can get Vue next / packages / shared / dist / shared.esm-bundler.js

Or directly see Vue next / packages / shared / SRC / index.ts

3, Tool function

1. babelParserDefaultPlugins babel resolves the default plug-in

/**
 * List of @babel/parser plugins that are used for template expression
 * transforms and SFC script transforms. By default we enable proposals slated
 * for ES2020. This will need to be updated as the spec moves forward.
 * Full list at https://babeljs.io/docs/en/next/babel-parser#plugins
 */
const babelParserDefaultPlugins = [
  "bigInt",
  "optionalChaining",
  "nullishCoalescingOperator",
];

Here are some default plug-ins. If you are interested, you can see

2. EMPTY_OBJ empty object

const EMPTY_OBJ =
  process.env.NODE_ENV !== "production" ? Object.freeze({}) : {};
// Object.freeze() is used to freeze objects and cannot be added, modified or deleted (including prototypes)
// The outermost layer of a frozen object cannot be modified.
// example:
const EMPTY_OBJ_1 = Object.freeze({});
EMPTY_OBJ_1.name = "Tool function";
console.log(EMPTY_OBJ_1.name); // undefined

const EMPTY_OBJ_2 = Object.freeze({ info: { name: "Tool function" } });
EMPTY_OBJ_2.info.nick = "function";
EMPTY_OBJ_2.obj = "props2";
console.log(EMPTY_OBJ_2.info.name); // 'tool function '
console.log(EMPTY_OBJ_2.obj); // undefined
console.log(EMPTY_OBJ_2);

/**
 * {
 *  info: { name: "Tool function ", nick:" function "}
 * }
 */

process.env.NODE_ENV is an environment variable in a node project. It is generally defined as development and production. Write code according to the environment. For example, the development environment has information such as error reports, but the production environment does not need these error warnings.

3. EMPTY_ARR empty array

const EMPTY_ARR =
  process.env.NODE_ENV !== "production" ? Object.freeze([]) : [];

// example:
EMPTY_ARR.push(1); // An error is reported, that is, the production environment freezes the array
EMPTY_ARR.length = 3;
console.log(EMPTY_ARR.length); // 0

4. NOOP null function

// Usage scenario: 1. Easy to judge, 2. Easy to compress

const NOOP = () => {};

// 1. Condition judgment
const dev = true;
if (dev) {
  instance.render = function () {
    console.log("render");
  };
}

// Can be used as a judgment.
if (instance.render === NOOP) {
  console.log("i");
}

// 2. Convenient compression. If it is function() {}, it is not convenient to compress code

5. NO function that always returns false

/**
 * Always return false.
 */
const NO = () => false;

// Advantages of compressed code

6. isOn judges that it starts with on, and the first letter after on is not a lowercase letter

const onRE = /^on[^a-z]/;
const isOn = (key) => onRE.test(key);

// example:
isOn("onChange"); // true
isOn("onchange"); // false
isOn("on1"); // true

onRE is regular, ^ on is the beginning of on, ^ a-z is not a-z lowercase letter

7. isModelListener listener

const isModelListener = (key) => key.startsWith("onUpdate:");

// example:
isModelListener("onUpdate:change"); // true
isModelListener("1onUpdate:change"); // false

Determine whether the string starts with onUpdate:

The startsWith() method detects whether the string starts with the specified prefix.

8. extend consolidation

const extend = Object.assign;

// example:
const data = { name: "tool", age: 30 };
const data2 = extend(data, { mp: "Front stream", name: "Tool function" });
console.log(data); // {name: "tool function", mp: "front stream", age: 30}
console.log(data2); // {name: "tool function", mp: "front stream", age: 30}
console.log(data === data2); // true

Merge two objects, such as the same attribute, the latter overrides the former, and there is an inheritance relationship

9. remove deletes an item in the array

const remove = (arr, el) => {
  const i = arr.indexOf(el);
  if (i > -1) {
    arr.splice(i, 1);
  }
};

// example:
const arr = ["a", 2, "c"];
remove(arr, "c");
console.log(arr); // ['a', 2]

10. Is hasown an attribute owned by itself

const hasOwnProperty = Object.prototype.hasOwnProperty;
const hasOwn = (val, key) => hasOwnProperty.call(val, key);

// API related to prototype operation
// Object.getPrototypeOf
// Object.setPrototypeOf
// Object.isPrototypeOf

// . call is used to redefine this object
// . call example:
// Cats eat fish, dogs eat meat,
// One day the dog wanted to eat fish
// Cat. Eat fish. Call (dog, fish)
// The dog ate the fish

// example:
hasOwn({ name: "Front stream" }, "name"); // true
hasOwn({ name: "Front stream" }, "a"); // false
hasOwn({ name: "Front stream" }, "toString"); // false
hasOwn({ __proto__: { name: "Front stream" } }, "name"); // false

hasOwn can determine whether its own attributes are, excluding the methods on the prototype

11. isArray determines whether it is an array

const isArray = Array.isArray;

// example:
isArray([1, 2, 3, 4]); // true
var obj = {
  a: 1,
  b: 2,
};
isArray(obj); // false
isArray(new Array()); // true
isArray("Array"); // false

const fakeArr = { __proto__: Array.prototype, length: 0 };
isArray(fakeArr); // false
fakeArr instanceof Array; // true
// So instanceof is not accurate

12. isMap determines whether it is a Map object

const isMap = (val) => toTypeString(val) === "[object Map]";

// example:
const map = new Map();
const o = { p: "Hello World" };
map.set(o, "content");
map.get(o); // 'content'
isMap(map); // true

ES6 adds Map and Set. For details, please see

13. isSet determines whether it is a Set object

const isSet = (val) => toTypeString(val) === "[object Set]";

// example:
const set = new Set();
isSet(set); // true

More Set objects are used in array de duplication

14. isDate determines whether it is a Date object

const isDate = (val) => val instanceof Date;

// example:
isDate(new Date()); // true

//  `instanceof ` will look up according to the prototype chain
isDate({ __proto__: new Date() })(
  // true
  { __proto__: [] }
) instanceof Array; // true
// Array.isArray should be used when judging arrays

15. isFunction determines whether it is a function

const isFunction = (val) => typeof val === "function";

// example:
isFunction(() => {}); // true

16. isString determines whether it is a string

const isString = (val) => typeof val === "string";

// example
isString(12); // false
isString(""); // true
isString("12"); // true

17. isSymbol judge whether it is a Symbol

const isSymbol = (val) => typeof val === "symbol";

// example:
let s = Symbol();

typeof s; // "symbol"

Symbol represents a unique value

18. isObject determines whether it is an object

const isObject = (val) => val !== null && typeof val === "object";

// example:
isObject({}); // true
isObject(null); // false
// The reason why it is not null is that typeof null is actually an object

19. isPromise judge whether it is Promise

const isPromise = (val) => {
  return isObject(val) && isFunction(val.then) && isFunction(val.catch);
};

// example
const p1 = new Promise(function (resolve, reject) {
  resolve("Ruokawa");
});
isPromise(p1); // true

Judge whether the parameter is an object, and there are. then function and. catch function

20. objectToString object to string

const objectToString = Object.prototype.toString;

21. toTypeString object to string

const toTypeString = (value) => objectToString.call(value);

// example:
toTypeString({ name: "Zhang San" }); // "[object Object]"

22. The torawtype object is converted to the last few bits of string interception

const toRawType = (value) => {
  // extract "RawType" from strings like "[object RawType]"
  return toTypeString(value).slice(8, -1);
};

// example:
toRawType({ name: "Zhang San" }); // "Object"

23. Isplanobject determines whether it is a pure object

const isPlainObject = (val) => toTypeString(val) === "[object Object]";

isPlainObject({}); // true
isPlainObject([]); // false

const Person = function () {
  this.name = "Front stream";
};
isPlainObject(new Person()); // The true constructor is an instance object

24. isIntegerKey determines whether it is a numeric string Key

const isIntegerKey = (key) =>
  isString(key) &&
  key !== "NaN" &&
  key[0] !== "-" &&
  "" + parseInt(key, 10) === key;

// The second parameter of parseInt is hexadecimal.
// example:
isIntegerKey(""); // false
isIntegerKey("12"); // true
isIntegerKey("012"); // false
isIntegerKey("a"); // false

Judge the string type, and it is not NaN, and it does not start with '-', and can be parseInt

25. Makemap & & judge whether it is a pure object

makeMap is to generate a map (key value pair) object from a comma separated string, and return a function to detect whether the key value exists. The second parameter is whether to convert the parameter to lowercase

/**
 * Make a map and return a function for checking if a key
 * is in that map.
 * IMPORTANT: all calls of this function must be prefixed with
 * \/\*#\_\_PURE\_\_\*\/
 * So that rollup can tree-shake them if necessary.
 */
function makeMap(str, expectsLowerCase) {
  const map = Object.create(null);
  const list = str.split(",");
  for (let i = 0; i < list.length; i++) {
    map[list[i]] = true;
  }
  return expectsLowerCase
    ? (val) => !!map[val.toLowerCase()]
    : (val) => !!map[val];
}

const isReservedProp = /*#__PURE__*/ makeMap(
  // the leading comma is intentional so empty string "" is also included
  ",key,ref," +
    "onVnodeBeforeMount,onVnodeMounted," +
    "onVnodeBeforeUpdate,onVnodeUpdated," +
    "onVnodeBeforeUnmount,onVnodeUnmounted"
);

// example:
isReservedProp("key"); // true
isReservedProp("ref"); // true
isReservedProp("onVnodeBeforeMount"); // true
isReservedProp("onVnodeMounted"); // true
isReservedProp("onVnodeBeforeUpdate"); // true
isReservedProp("onVnodeUpdated"); // true
isReservedProp("onVnodeBeforeUnmount"); // true
isReservedProp("onVnodeUnmounted"); // true

26. cacheStringFunction cache

const cacheStringFunction = (fn) => {
  const cache = Object.create(null);
  return (str) => {
    const hit = cache[str];
    return hit || (cache[str] = fn(str));
  };
};

let add = (a, b) => a + b;
let calculate = cacheStringFunction(add);
calculate(10, 20); //30
calculate(10, 20); // With the same parameters, the data is fetched from the cache instead of being recalculated the second time

var count = 0;
var fn = function (n) {
  count++;
  return n < 2 ? n : fn(n - 1) + fn(n - 2);
};
fn = cacheStringFunction(fn);
for (var i = 0; i <= 10; i++) {
  fn(i);
}
console.log(count); // 12
// The count without cache function is 453

Cache function refers to caching the last calculation result. If the same parameter is encountered in the next call, it will directly return the data in the cache

27. camelize hyphen to small hump

// \w is a 0-9a-zA-Z_; number consisting of upper and lower case letters and underscores
const camelizeRE = /-(\w)/g;
/**
 * @private
 */
const camelize = cacheStringFunction((str) => {
  return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ""));
});

camelize("on-click"); // onClick

28. hyphenate small hump to hyphen

const hyphenateRE = /\B([A-Z])/g;
/**
 * @private
 */
const hyphenate = cacheStringFunction((str) =>
  str.replace(hyphenateRE$1, "-$1").toLowerCase()
);

// example:
hyphenate("onClick"); // on-click

29. capitalize string to capitalize after on

// Initial to capital
const capitalize = cacheStringFunction(
  (str) => str.charAt(0).toUpperCase() + str.slice(1)
);
/**
 * @private
 */
const toHandlerKey = cacheStringFunction((str) =>
  str ? `on${capitalize(str)}` : ``
);

// example:
toHandlerKey("click"); // onClick

30. hasChanged judge whether there is a change

const hasChanged = (value, oldValue) => !Object.is(value, oldValue);

// Object.is a new method added in ES6 to compare whether two values are strictly equal
// example:
hasChanged(1, 2); // true has changed

31. invokeArrayFns executes the functions in the array

const invokeArrayFns = (fns, arg) => {
  for (let i = 0; i < fns.length; i++) {
    fns[i](arg);
  }
};

// example:
let arr = [
  function (val) {
    console.log(`I am ${val}`);
  },
  function (val) {
    console.log(`I'm 18 years old`);
  },
];
invokeArrayFns(arr, "Front stream");
/**
 * I'm the front stream
 * I'm 18 years old
 */

32. def defines object attributes

const def = (obj, key, value) => {
  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: false,
    value,
  });
};

// configurable: can this attribute be deleted
// enumerable: whether the attribute will be enumerated in the for in loop.
// Value: the value returned when getting the property.

33. To number to number

If parseFloat returns NaN, it returns itself. NaN returns true. Otherwise, it returns true

const toNumber = (val) => {
  const n = parseFloat(val);
  return isNaN(n) ? val : n;
};

// example:
toNumber("12"); // 12
toNumber("a12"); // 'a12'
parseFloat("a12"); // NaN
isNaN(NaN); // true

34. getGlobalThis global object

let _globalThis;
const getGlobalThis = () => {
  return (
    _globalThis ||
    (_globalThis =
      typeof globalThis !== "undefined"
        ? globalThis
        : typeof self !== "undefined"
        ? self
        : typeof window !== "undefined"
        ? window
        : typeof global !== "undefined"
        ? global
        : {})
  );
};

Get the global this point

The first execution of getGlobalThis is undefined, and the following assignment statement is executed

self is the Web Worker global object

window is the browser global object

Global is the Node global object

4, Summary

We have learned functions such as empty object, empty array, empty function, isMap, isSet and isPromise, which complement and expand the native JavaScript. Functions such as camelize, hyphenate and toHandlerKey are the processing of Vue related event names and declaration cycle names

When building the basic framework of our project, we can learn from the tool functions of the source code to make our tool functions more reusable and extensible

Keywords: Javascript Vue.js

Added by TomNomNom on Sat, 20 Nov 2021 07:47:05 +0200