It takes about 10 minutes to read a total of 9,000 words.

Written in the book of the former

For the front-end, Github is a treasure trove. Do anything, be professional, a lot of knowledge can be found, especially in the front, there are a lot of good things in front of you. Good component source code, good design mode, good test scheme and good code structure are all at your fingertips. Therefore, do not think you are not capable of coding just API. What you need to master is the idea and thinking of programming.

In fact, this article also has something to do with Ant Design eggs. Because someone said, who let you not read NPM package source, may be a lot of people feel that reading NPM package source is a very difficult thing, but I want to tell you, NPM package is a treasure for the front end. You can see the truth of a lot of things in NPM packages, you can see the programming ideas of the best NPM packages in the world.

For example, you can see their code structure, their dependencies, how their code interacts, their code writing specifications, and so on. For now, I’ll show you how to analyze the code for a CLI-generated NPM package using taro, the most popular multi-purpose unified framework. A piece of article can not do too detailed analysis, I will be a piece of advice, tell you, don’t be frightened by node_modules that string of packages, afraid to read, afraid to understand. It’s not as hard to read as you might think, the code structure of the well-known NPM package is very friendly, and it’s no harder to understand than reading your colleague’s code (you know). And in the process of reading the NPM package, you will find a lot of surprises, find a lot of inspiration. Is not very excited, is not very happy, well, then take my hand, follow me, I take you to unlock the mysterious and beautiful NPM bag veil.

What happened to Taro Init

After implementing taro init XXX, the package.json dependencies are shown in the following figure

You’ll find that when you initialize a CLI, you install a lot of dependencies, and then if you go to node_modules, it will be very uncomfortable because there are a lot of dependencies installed, which is why many people click on node_modules and immediately close it. If you don’t open it, it will get stuck 😂. Taro Init’s package.json package is installed in taro Init’s package.json package.

Analysis of @ tarojs/components

Take a screenshot of node_modules as follows:

From the picture, we can see that there are many dependencies installed. The package that is directly related to us is @tarojs. Open @tarojs and you can see:

In fact, you will find nothing, let’s take a look at the SRC directory:

Analysis of thesrc/index.jsfile

The index.js file code is as follows:

import 'weui'
export { default as View } from './view'
export { default as Block } from './block'
export { default as Image } from './image'
export { default as Text } from './text'
export { default as Switch } from './switch'
export { default as Button } from './button'
// Other components are omitted
Copy the code

You will find that this is a place to focus on exporting various components. From the code here, we can see why components are introduced in the following form in Taro.

import { View, Text, Icon } from '@tarojs/components'
Copy the code

For example, the reason why uppercase is used is because uppercase is what we export above, and we put all the components in one object. Now, if you think about it a little bit, why capitalized? Taro (first taro) is originally written in both lowercase letters, so how can you tell them apart? When you look at the source code here, it seems quite natural for you to introduce the capitalization rule for Taro’s component. In the meantime, we should take a look at taro’s idea of exporting a component. The more frequent but inconspicuous the operation, the more we should appreciate its excellent ideas.

Let’s take a look at the structure of a component. For example, a Button component has the following structure:

From the diagram above, we can see the code structure of one of the basic components of TARO. Here we can learn several things:

First point: Each component was unit tested using Jest and the __test__ directory

Second point: Each component has index.md, which describes the documentation for the component

Third point: the style is stored separately with the directory style, at the same time the entry file name unified use index

4. Index. D. ts files were set in the Types directory to make the code prompt more friendly

Summary after analyzing @tarojs/ Components

Since Taro is a rising and promising framework, can we learn some ideas from the @tarojs/ Components source code? For example, when we go to design a component library of our own, can we borrow this idea? In fact, this form of component code structure is very popular at present, such as the use of this year’s most popular framework Jest framework as a component of the unit test, using TS to do code hints. If you look at the github source code, you can see that it uses the latest Lerna package publishing tool, the lightweight rollup package publishing tool, and @xxx as namespace. This is why I chose the Taro framework for analysis. Taro was only open sourced in June 2018, so it must have borrowed from the latest front-end technologies and best practices without historical baggage. In fact, after looking at the source of Taro, you will find that some design ideas in Taro are better than other famous frameworks.

Analysis of @ tarojs/taro

You’ll notice that this is still installed in the @tarojs directory, with no additional dependencies. Taro’s directory structure is shown below

From the code structure in the figure, we can roughly know:

First: there is an index.d.ts file in the types directory. This file is a TS file that is used to write code hints. This will give you very friendly code specification tips as you write code. For example, index.d.ts contains the following code (a random snippet) :

  interfacePageConfig { navigationBarBackgroundColor? :string, backgroundTextStyle? :'dark' | 'light', enablePullDownRefresh? :boolean, onReachBottomDistance? :numberdisableScroll? :boolean
  }

Copy the code

The purpose of this code is to give you a friendly reminder of what the data type of this field is when you write the corresponding configuration. Seeing this, we actually thought, we can also customize our own projects to add this hint, which is a good optimization for the project.

Second: we see the dist directory, and we can almost guess that this is the output directory that was packaged by the packaging tool.

Third: The whole catalog is very simple, so what is the role of Taro? Actually, Taro is a runtime.

Let’s take a look at package.json, as shown below:

I found a field, which is

"PeerDependencies ": {" nervJS ": "^1.2.17"}Copy the code

The most common ones are dependencies and devDependencies. What consciousness does peerDependencies express? Let’s go to Google Translate, as shown here:

Translate the whole field into English. Translate the whole field into English.

This dependency does not require NPM install in its own directory. Simply place NPM install in the root directory. In the spirit of not building wheels, please see the following blog for specific awareness:

Discuss peerDependencies of NPM dependency management

Let’s look at index.js, just two lines of code:

module.exports = require('./dist/index.js').default
module.exports.default = module.exports
Copy the code

But I was a little surprised by the way it was written. Why write it like this, can’t you do it in one line, more decoupled? Probably for something.

PS: After writing this article, I thought about this question and found that this method is the same as one of the following index.js:

export {}
export default {}
Copy the code

Instantly I understood the author’s purpose.

Analysis of thetaro/src

As shown in the figure:

Let’s take a look at env.js

export const ENV_TYPE = {
  WEAPP: 'WEAPP'.WEB: 'WEB'.RN: 'RN'.SWAN: 'SWAN'.ALIPAY: 'ALIPAY'.TT: 'TT'
}

export function getEnv () {
  if (typeofwx ! = ='undefined' && wx.getSystemInfo) {
    return ENV_TYPE.WEAPP
  }
  if (typeofswan ! = ='undefined' && swan.getSystemInfo) {
    return ENV_TYPE.SWAN
  }
  if (typeofmy ! = ='undefined' && my.getSystemInfo) {
    return ENV_TYPE.ALIPAY
  }
  if (typeoftt ! = ='undefined' && tt.getSystemInfo) {
    return ENV_TYPE.TT
  }
  if (typeofglobal ! = ='undefined' && global.__fbGenNativeModule) {
    return ENV_TYPE.RN
  }
  if (typeof window! = ='undefined') {
    return ENV_TYPE.WEB
  }
  return 'Unknown environment'
}

Copy the code

In the above code, we can use the getEnv function to obtain the running environment of the current program, such as appellate, swan, OR TT. In fact, at this point we should feel the idea of multi-terminal unity, genEnv did something very important:

After writing code using the Taro framework, how do I convert it to multiterminal? In fact, at runtime, according to the environment to switch to the corresponding compilation environment, which is converted to the specified side of the code. The getEnv function visualizes the transformation process.

Let’s continue to look at index.js with the following code:

import Component from './component'
import { get as internal_safe_get } from './internal/safe-get'
import { set as internal_safe_set } from './internal/safe-set'
import { inlineStyle as internal_inline_style } from './internal/inline-style'
import { getOriginal as internal_get_original } from './internal/get-original'
import { getEnv, ENV_TYPE } from './env'
import Events from './events'
import render from './render'
import { noPromiseApis, onAndSyncApis, otherApis, initPxTransform } from './native-apis'
const eventCenter = new Events()
export {
  Component, Events, eventCenter, getEnv, ENV_TYPE, render, internal_safe_get, internal_safe_set, internal_inline_style, internal_get_original, noPromiseApis, onAndSyncApis,
  otherApis, initPxTransform
}

export default {
  Component, Events, eventCenter, getEnv, ENV_TYPE, render, internal_safe_get, internal_safe_set, internal_inline_style, internal_get_original, noPromiseApis, onAndSyncApis,
  otherApis, initPxTransform
}
Copy the code

As you can see, the same collection of modules is exported with export and export Default, respectively. What’s the reason for doing this? I personally think it’s for the robustness of the code. You can mount all exports in one context, or you can destruct them to import the specific exports you want. Seeing this, I wonder if we can do this in our own projects.

Without further ado, let’s take a look at two of the more important but less coded files called render.js and component.js. The code is as follows:

render.js :

export default function render () {}
Copy the code

component.js :

class Component {
  constructor (props) {
    this.state = {}
    this.props = props || {}
  }
}
export default Component
Copy the code

The amount of code is very small, an empty render function, a few functions of the Componet class, think about what is doing.

Analyze the taro global messaging mechanism event.js

If we look at events.js, the pseudocode (shorthand) looks like this:

class Events {
  constructor() {
    // ...
  }
  on() {}
  once() {}
  off() {}
  trigger() {}
}

export default Events
Copy the code

You’ll find this file completes taro’s global message notification mechanism. It has on, once, off, trigger methods, events.js have corresponding complete code implementation. The official documents are as follows:

Taro Message Mechanism

If you think about it, do you realize that this is where the API originally came from? It’s not so hard to understand, and you don’t have to memorize it.

Analysis of theinternaldirectory

The readme. md directory in the internal directory can be read as follows: It is an internal method that exports functions named with internal_, which users do not need to care about and will not use. At compile time, each taro-CLI compiled file will automatically add its dependencies and use them. Such as:

import { Component } from 'taro'
class C extends Component {
  render () {
    const { todo } = this.state
    return (
      <TodoItem
        id={todo[0].list[123].id}
      />)}}Copy the code

Will compile to:

import { Component, internal_safe_get } from 'taro'
class C extends Component {
  $props = {
    TodoItem() {
      return {
        $name: "TodoItem".id: internal_safe_get(this.state, "todo[0].list[123].id"),}}}... }Copy the code

Each taro-CLI compiled file is automatically assigned its dependencies and used at compile time. What is the meaning of this sentence? Taro – CLI may need to process files in this way during compilation. At present, I understand it like this temporarily. It is normal that I cannot understand it temporarily. Let’s continue to analyze it below.

Analysis of thetarojs/taroThe summary of the

Tarojs/Taro has already done the analysis. From the analysis, we can get a general idea of how a runtime connects multiple interfaces at a macro level and how to add friendly hints to the code through TS files. Since there is internal, it means that not all files in the internal directory can provide external methods, such as events.js, which is also instructive. How to define internal and external code, how to split.

Analyze several conscious function files

Install dependencies first:

yarn add @tarojs/taro-weapp && nervjs && nerv-devtools -S
Copy the code

Then let’s look at the latest package structure

The corresponding package.json is as follows:

{
  "dependencies": {
    "@tarojs/components": "^ 1.2.1." "."@tarojs/router": "^ 1.2.2." "."@tarojs/taro": "^ 1.2.1." "."@tarojs/taro-weapp": "^ 1.2.2." "."nerv-devtools": "^ 1.3.9"."nervjs": "^ 1.3.9"}}Copy the code

That’s how much more stuff goes into node_modules after we install these dependencies. Let’s take a quick look at indirectly related packages and pick a few

Analysis of theomit.js

Let’s see: omit. Js

import _extends from "babel-runtime/helpers/extends";
function omit(obj, fields) {
  var shallowCopy = _extends({}, obj);
  for (var i = 0; i < fields.length; i++) {
    var key = fields[i];
    delete shallowCopy[key];
  }
  return shallowCopy;
}

export default omit;
Copy the code

We know from readme.md that it generates a shallow-copy object with the specified fields removed.

Analysis of theslash.js

The code is as follows:

'use strict';
module.exports = input= > {
	const isExtendedLengthPath = / ^ \ \ \ \ \? \ \ /.test(input);
	const hasNonAscii = /[^\u0000-\u0080]+/.test(input);
	if (isExtendedLengthPath || hasNonAscii) {
		return input;
	}
	return input.replace(/\\/g.'/');
};
Copy the code

We know this from Slash’s Readme.md

This was created since the path methods in Node outputs \\ paths on Windows.

Specific consciousness, self-analysis, not difficult.

Analysis of thevalue-equal.js

The main contents of value-equal are as follows:

var _typeof = typeof Symbol= = ="function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol= = ="function" && obj.constructor === Symbol&& obj ! = =Symbol.prototype ? "symbol" : typeof obj; };

function valueEqual(a, b) {
  if (a === b) return true;
  if (a == null || b == null) return false;
  if (Array.isArray(a)) {
    return Array.isArray(b) && a.length === b.length && a.every(function (item, index) {
      return valueEqual(item, b[index]);
    });
  }
  var aType = typeof a === 'undefined' ? 'undefined' : _typeof(a);
  var bType = typeof b === 'undefined' ? 'undefined' : _typeof(b);
  if(aType ! == bType)return false;
  if (aType === 'object') {
    var aValue = a.valueOf();
    var bValue = b.valueOf();
    if(aValue ! == a || bValue ! == b)return valueEqual(aValue, bValue);
    var aKeys = Object.keys(a);
    var bKeys = Object.keys(b);
    if(aKeys.length ! == bKeys.length)return false;
    return aKeys.every(function (key) {
      return valueEqual(a[key], b[key]);
    });
  }
  return false;
}
export default valueEqual;
Copy the code

The value of each object’s key is only compared to the value of the key. Take a look at the idea of writing code this way.

Analysis of theprop-types.js

Let’s look at prop-types, so I won’t list the source code here. Look at readme.md, we know

Runtime type checking for React props and similar objects.

The React framework supports props type checking

XxxComponent.propTypes = {
  xxProps: PropTypes.xxx
}
Copy the code

Analysis of js – tokens

Let’s look at JS-Tokens and see the following code:

Object.defineProperty(exports, "__esModule", {
  value: true
})
exports.default = (/ (([' "])? : (? ! 2 \ | \ \). | \ \ (? :\r\n|[\s\S]))*(\2)? | ` (? :[^`\\$]|\\[\s\S]|\$(? ! \ {) | \ $\ {(? : [^ {}] | \ {\} [^}] *?) * \}? * (`)? | | (\ \ /. *)/(\ \ * (? : | \ [^ *] * (? ! \ \ /)) * (\ * /)?) | (\ /? ! (\ *)? : \ [(? : (?! [\] \ \]). | \ \.) * \] | (? ! [\ / \] \ \]). | \ \.) + / /? : (? ! \s*(? :\b|[\u0080-\uFFFF$\\'"~({]|[+\-!] (? ! =) | \.? \ | d)) [gmiyus] {1, 6} \ b? ! [\u0080-\uFFFF$\\]|\s*(? : \ [+ - * % & | ^ < >! =? ({] | \ / (? ! [\/*])))))|(0[xX][\da-fA-F]+|0[oO][0-7]+|0[bB][01]+|(? :\d*\.\d+|\d+\.?) (? :[eE][+-]? \d+)?) | ((? ! \d)(? : (? ! \s)[$\w\u0080-\uFFFF]|\\u[\da-fA-F]{4}|\\u\{[\da-fA-F]+\})+)|(--|\+\+|&&|\|\||=>|\.{3}|(? : [+ \ \ / % & | ^] {1, 2} | | \ * < {1, 2} | > {1, 3} |! =? | = {1, 2}) =? |[?~.,:;[\](){}])|(\s+)|(^$|[\s\S])/g
Copy the code

In combination with readme.md, we can see that it uses re to turn JS syntax into tokens, so cool.

Example is as follows:

var jsTokens = require("js-tokens").default
var jsString = "var foo=opts.foo; \n..."
jsString.match(jsTokens)
// ["var", " ", "foo", "=", "opts", ".", "foo", ";", "\n", ...]
Copy the code

Let you write can write out this kind of contrarian regular 😂.

A summary of various small functions

Do you feel that these function files are quite conscious, if you want to see how specific implementation, you can continue to look at the source code, you will find a lot of things are specific implementation, there is no need to memorize. Js-token, value-equal, prop-types omit, slash, etc., are all very good functions, which can give us a lot of inspiration in programming. We can learn from the ideas and implementation methods of these functions. Thus better improve our JS programming ability, which is also in the process of reading NPM package source code is a very important harvest.

Analysis of @ tarojs/taro – weapp

This package is used to compile the code compiled by Taro into wechat applet code. The code structure is shown in the figure below:

First of all, from readme.md, we can’t see what this package actually does, just a one-sentence, multiterminal solution applet side infrastructure. Therefore, I think the Taro team should complement this point accordingly. The readme.md here is written too succinctly.

But we can get a sense of what taro-eappp does by reading the code. First, let’s look at the structure of the code. There’s dist, SRC, etc., and node_modules. After thinking about the package described above, we wondered why there was a node_modules directory. What is its purpose? Can’t you use peerDependencies above? To this, temporarily cannot understand this matter, encounter this kind of problem how to do? At this point, we can stop thinking about this problem, do not block, and continue to analyze the other code.

We’ll start with readme.md as usual, but readme.md is a one-sentence, multiterminal solutions applet side infrastructure. So what? Don’t get discouraged! The eight-year war of Resistance continues to be analyzed.

Let’s take a look at package.json, which looks like this:

"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "rollup -c rollup.config.js", "watch": "Rollup - c rollup. Config. Js - w"}, "dependencies" : {" @ tarojs/taro ":" 1.2.2 ", "@ tarojs/utils" : "1.2.2", "lodash" : "^ 4.17.10 prop -", "types" : "^ 15.6.1"}Copy the code

There are two main things we can see from package.json. The first is the dependencies required by this package. We can see dependencies @tarojs/taro, @tarojs/utils, Lodash, prop-types. Then we looked at node_modules and found only @tarojs/taro. Everything else is installed outside, like Lodash, prop-types can use packages in the root directory, where @tarojs/utils is newly installed. Under the taro directory. With all this information in hand, let’s combine it with the above understanding and consider a few questions:

  1. Why doesn’t it workpeerDependencies
  2. Why the@tarojs/taroInstall thetaro-weappThe inside of the bag.
  3. whytaro-weappThere is notypes/index.d.tsThis kind of file

Question Mark, throw the question out first and think about it further later. One thing to remember is that it is not necessary to read the source code at a level of complete understanding. It is not realistic or necessary. All we need to do is throw out the question and move on. Now let’s read index.js, which looks like this:

module.exports = require('./dist/index.js').default
module.exports.default = module.exports
Copy the code

The dist directory is obviously a packaged directory. Now let’s analyze the SRC directory. The index file in SRC is as follows:

/* eslint-disable camelcase */
import {
  getEnv, Events, eventCenter, ENV_TYPE, render,
  internal_safe_get, internal_safe_set,
  internal_inline_style, internal_get_original
} from '@tarojs/taro'

import Component from './component'
import PureComponent from './pure-component'
import createApp from './create-app'
import createComponent from './create-component'
import initNativeApi from './native-api'
import { getElementById } from './util'

export const Taro = {
  Component, PureComponent, createApp, initNativeApi,
  Events, eventCenter, getEnv, render, ENV_TYPE,
  internal_safe_get, internal_safe_set,
  internal_inline_style, createComponent,
  internal_get_original, getElementById
}
export default Taro
initNativeApi(Taro)
Copy the code

From index.js, we can see that some methods of @tarojs/taro have been imported. This article has already analyzed @tarojs/taro. If you think about it together, you can find that @tarojs/ taro-appellate package is necessary to implement transformations when implementing @tarojs/taro appellate programs.

Taro-appellate P effect is generally known. Now let’s analyze the dependent external files in index.js as follows:

Analysis of SRC/components. Js

To indent the code, let’s take a look at the approximate code, as shown below:

As you can see from the picture, the BaseComponent class is exported. As you can see from the name, this is a basic component class. Since there is not too much code, I will post it directly.

import { enqueueRender } from './render-queue'
import { updateComponent } from './lifecycle'
import { isFunction } from './util'
import {
  internal_safe_get as safeGet
} from '@tarojs/taro'
import { cacheDataSet, cacheDataGet } from './data-cache'
const PRELOAD_DATA_KEY = 'preload'
class BaseComponent {
  // when _createData is generated, the applet is accessed through data.__createData
  __computed = {}
  // this. Props, accessed via data.__props
  __props = {}
  __isReady = false
  // will set componentDidMount to true
  __mounted = false
  // Cut it down a bit
  $componentType = ' '
  $router = {
    params: {},
    path: ' '
  }
  constructor (props = {}, isPage) {
    this.state = {}
    this.props = props
    this.$componentType = isPage ? 'PAGE' : 'COMPONENT'
  }
  _constructor (props) {
    this.props = props || {}
  }
  _init (scope) {
    this.$scope = scope
  }
  setState (state, callback) {
    enqueueRender(this)
  }
  getState () {
    const { _pendingStates, state, props } = this
    const queue = _pendingStates.concat()
    queue.forEach((nextState) = > {
      if (isFunction(nextState)) nextState = nextState.call(this, stateClone, props)
      Object.assign(stateClone, nextState)
    })
    return stateClone
  }
  forceUpdate (callback) {
    updateComponent(this)
  }
  $preload (key, value) { / / omit}
  // will be called by an anonymous function
  __triggerPropsFn (key, args) {}
}
export default BaseComponent
Copy the code

If we look at the code above, we know from the name that this is a component’s base class, which means that all components inherit from BaseComponent. Let’s look at the code above, starting with the first point, why are there so many underline variables? In fact, these variables are for their own use, let’s look at the following code:

class BaseComponent {
  // when _createData is generated, the applet is accessed through data.__createData
  __computed = {}
  // this. Props, accessed via data.__props
  __props = {}
  __isReady = false
  // will set componentDidMount to true
  __mounted = false
  // Cut it down a bit
  $componentType = ' '
  $router = { params: {}, path: ' '}}Copy the code

First of all, I remember that ES6 does not support writing variables directly into classes, which should be supported by Babel. The function of this variable is basically known from the comments in the code, such that __props can be accessed from data.__props. That is the value of this.props, which is also using the proxy mode. Just like access in VUE. OK, we know that, so let’s move on to the following code:

class BaseComponent {
  constructor (props = {}, isPage) {
    this.state = {}
    this.props = props
    this.$componentType = isPage ? 'PAGE' : 'COMPONENT'
  }
  _constructor (props) {
    this.props = props || {}
  }
}
Copy the code

Look what we found. Constructor () constructor () But below _constructor function is what the devil, also conducted inside this. Props props of = | | {}, is what the devil, if you look at the taro official document, you may see this tip:

If you don’t write this. Props = props, it’s ok, because taro needs to use props to do something during runtime.

But you may not understand why, because it feels like the text is not as real as the code, so when you see the code above, does it feel real because you see the code? Is actually the taro _constructor to use its own internal methods are enclosing props = props | | {}. So the documentation will say: it is ok not to write props.

Other things like setState, getState, and so on, you can analyze it for yourself, it’s the same way. Anyway, as long as you analyze it, you can basically have a deeper understanding of it. You may forget what’s in the official documentation at this point, but you won’t forget the meaning of this line of code.

Analysis of SRC/native – API. Js

The code in this file is important, why is it called native API? If you look at the official documentation, you’ll see this page:

In fact, the native api.js here is the introduction of the above figure, which can be understood as Taro’s encapsulation of the native API of wechat applet.

What is the output of native-api.js

export default function initNativeApi (taro) {
  processApis(taro)
  taro.request = request
  taro.getCurrentPages = getCurrentPages
  taro.getApp = getApp
  taro.requirePlugin = requirePlugin
  taro.initPxTransform = initPxTransform.bind(taro)
  taro.pxTransform = pxTransform.bind(taro)
  taro.canIUseWebp = canIUseWebp
}
Copy the code

Here we export an initNativeApi method. Look at the code above, do you know the general picture of the entire entrance. This exported method is executed in the entry to complement Taro. If you do not implement the initNativeApi(taro), do you agree to do so. If you do not implement the initNativeApi, do you agree to do so?

const Taro = {
  Component, PureComponent, createApp, initNativeApi, Events,
  eventCenter, getEnv, render, ENV_TYPE, internal_safe_get,
  internal_safe_set, internal_inline_style,
  createComponent, internal_get_original, getElementById
}
Copy the code

Taro is just like CTX in KOA, which mounts many methods in the form of binding contexts. However, an optimization has been made to mount more methods to Taro through the initNativeApi(Taro) method. InitNativeApi (Taro) initNativeApi(Taro)

const Taro = {
  // The above export still exists
  request,
  getCurrentPages,
  getApp,
  requirePlugin,
  initPxTransform,
  pxTransform,
  canIUseWebp,
}
Copy the code

Forget about processApis(taro).

We look at the code above and find many methods. We can understand that by implementing initNativeApi(Taro), Taro can mount some local apis of wechat applet. You’ll notice that some of these aren’t native apis, but let’s put it this way: Request, getCurrentPages, getApp. I personally understand that the reason the author does this is to separate native and non-native methods for decoupling.

Analysis of SRC/pure – component. Js

import { shallowEqual } from '@tarojs/utils'
import Component from './component'
class PureComponent extends Component {
  isPureComponent = true
  shouldComponentUpdate (nextProps, nextState) {
    return! shallowEqual(this.props, nextProps) || ! shallowEqual(this.state, nextState)
  }
}
export default PureComponent
Copy the code

Let’s look at the code for pure-Componnet.js. The PureComponent class inherits from Component. At the same time, I implement a shouldComponentUpdate method. The code for this method looks like this:

shouldComponentUpdate (nextProps, nextState) {
    return! shallowEqual(this.props, nextProps) || ! shallowEqual(this.state, nextState)
}
Copy the code

You can see that the input parameter is nextProps, nextState. And then compare the props and state with the shallowEqual method, which sounds like a shallow comparison. The code is found in the SRC directory of the @taro/util directory (shallow-equal.js) as follows:

Object.is = Object.is || function (x, y) {
  if (x === y) returnx ! = =0 || 1 / x === 1 / y
  returnx ! == x && y ! == y }export default function shallowEqual (obj1, obj2) {
  if (obj1 === null && obj2 === null) return true
  if (obj1 === null || obj2 === null) return false
  if (Object.is(obj1, obj2)) return true
  const obj1Keys = obj1 ? Object.keys(obj1) : []
  const obj2Keys = obj2 ? Object.keys(obj2) : []
  if(obj1Keys.length ! == obj2Keys.length)return false

  for (let i = 0; i < obj1Keys.length; i++) {
    const obj1KeyItem = obj1Keys[i]
    if(! obj2.hasOwnProperty(obj1KeyItem) || !Object.is(obj1[obj1KeyItem], obj2[obj1KeyItem])) {
      return false}}return true
}
Copy the code

Look at the code, found to be shallow comparison. PureComponent isn’t as abstract as you’d expect. React’s PureComponent is similar. There is no need to memorize the lifecycle of a framework and various professional names. In fact, when you remove the veil and see the truth of it, you will find that the framework is not so profound. But if you just don’t have the courage to uncover it, to face it, then you will always be in the imagination, unaware of the truth.

Analysis of SRC/create – componnet. Js

Let’s take a look at it

  const weappComponentConf = {
    data: initData,
    created (options = {}) {
      this.$component = cacheDataGet(preloadInitedComponent, true)
      this.$component = new ComponentClass({}, isPage)
      this.$component._init(this)
      this.$component.render = this.$component._createData
      this.$component.__propTypes = ComponentClass.propTypes
      Object.assign(this.$component.$router.params, options)
    },
    attached () {},
    ready () {
      componentTrigger(this.$component, 'componentDidMount')
    },
    detached () {
      componentTrigger(this.$component, 'componentWillUnmount')}}Copy the code

As can be seen from the above code, this is to compile the components compiled by Taro into the native component instance in wechat applet. CacheDataGet and cacheDataHas are used in the Attached methods. Why are they used, what is their purpose, and what is the meaning behind them? Need to combine with the meaning of the life cycle of the component of wechat small program, to think and analyze. This.$component.render = this.$component._createData.

Analysis of SRC/create – app. Js

function createApp (AppClass) {
  const app = new AppClass()
  const weappAppConf = {
    onLaunch (options) {
      app.$app = this
      app.$app.$router = app.$router = {
        params: options
      }
      if (app.componentWillMount) app.componentWillMount()
      if (app.componentDidMount) app.componentDidMount()
    },
    onShow (options) {},
    onHide () {},
    onError (err) {},
  }
  return Object.assign(weappAppConf, app)
}
export default createApp
Copy the code

Look at the if statement above, you can get a sense of the purpose behind it. Look at how taro follows the react concept of data immutable programming.

Analysis of SRC/next – tick. Js

const nextTick = (fn, ... args) = > {
  fn = typeof fn === 'function' ? fn.bind(null. args) : fnconst timerFunc = wx.nextTick ? wx.nextTick : setTimeout
  timerFunc(fn)
}
export default nextTick
Copy the code

This code is easy to understand, and is executed at the next phase of the loop by placing the code in wx.nexttick or setTimeout.

Analysis of SRC/render – queue. Js

import nextTick from './next-tick'
import { updateComponent } from './lifecycle'
let items = []
export function enqueueRender (component) {
  if(! component._dirty && (component._dirty =true) && items.push(component) === 1) {
    nextTick(rerender)
  }
}
export function rerender () {
  let p
  const list = items
  items = []
  while ((p = list.pop())) {
    if (p._dirty) {
      updateComponent(p, true)}}}Copy the code

The idea of nextTick rendering is known by naming it.

Analysis of SRC/lifecycle. Js

updateComponent

Analysis of SRC/data – cache. Js

const data = {}
export function cacheDataSet (key, val) {
  data[key] = val
}
export function cacheDataGet (key, delelteAfterGet) {
  const temp = data[key]
  delelteAfterGet && delete data[key]
  return temp
}
export function cacheDataHas (key) {
  return key in data
}
Copy the code

From the code we can know that this is to do data caching. I’m going to cache it, and every time I fetch a value, I’m going to delete that value. So why do we do it this way, what are the reasons behind it or what are the advantages of it? You can think about it carefully afterwards, which is also a good programming idea.

Analysis of the@tarojs/taro-weappAfter a summary of the

When implementing tarojs/ taro-eappp, Taro uses getEnv to implement taro-eappp compilation. Then we analyzed the taro – how weapp compile processing, such as how to solve the problem of frequently involves API is different. Through analysis, we have a deeper understanding of taro’s entire architecture and part of its internal implementation. These ideas are worth putting into practice in our daily projects. Actually see what is the purpose of the source code, for example, I analyze taro init analysis to the present, if you have finished, you will find that there are a lot of cool ideas, may be in your world, wrote several projects are simply couldn’t remember also can use, see the source of the purpose is to let you to contact the world excellent open source project is how to design. So that I can absorb these ideas and use them for my own growth.

Analysis of therollup-plugin-alias

From readme.md, we can see that one thing it does is abstract the inbound path of the package, which has a lot of benefits. / this symbol, and can be changed centrally. What inspired us is that we can actually learn from rollup-plugin-alias how to manage our own NPM packages. We need to absorb this idea.

Analysis of theresolve-pathname

readme.md
URL

Weyl and Warning are all tools for handling hints, so read the source code and analyze it yourself.

Analysis of @ tarojs/router

The code directory structure screenshot is as follows:

We’ll see that under the Router directory, there are dist and types directories. There is no SRC directory, but why do some packages have SRC and some don’t? This is a problem that needs further careful analysis.

How to find something more interesting

How to find more interesting things in node_modules. As an example, let’s look at how bind is implemented in different packages: the following image shows the implementation of bind in modules in core-js

var aFunction = require('./_a-function');
var isObject = require('./_is-object');
var invoke = require('./_invoke');
var arraySlice = [].slice;
var factories = {};

var construct = function (F, len, args) {
  if(! (lenin factories)) {
    for (var n = [], i = 0; i < len; i++) n[i] = 'a[' + i + '] ';
    factories[len] = Function('F,a'.'return new F(' + n.join(', ') + ') ');
  } return factories[len](F, args);
};

module.exports = Function.bind || function bind(that /* , ...args */) {
  var fn = aFunction(this);
  var partArgs = arraySlice.call(arguments.1);
  var bound = function (/* args... * /) {
    var args = partArgs.concat(arraySlice.call(arguments));
    return this instanceof bound ? construct(fn, args.length, args) : invoke(fn, args, that);
  };
  if (isObject(fn.prototype)) bound.prototype = fn.prototype;
  return bound;
};
Copy the code

Here’s a look at the bind implementation in LoDash:

var baseRest = require('./_baseRest'),
    createWrap = require('./_createWrap'),
    getHolder = require('./_getHolder'),
    replaceHolders = require('./_replaceHolders');
    
var WRAP_BIND_FLAG = 1,
    WRAP_PARTIAL_FLAG = 32;

var bind = baseRest(function(func, thisArg, partials) {
  var bitmask = WRAP_BIND_FLAG;
  if (partials.length) {
    var holders = replaceHolders(partials, getHolder(bind));
    bitmask |= WRAP_PARTIAL_FLAG;
  }
  return createWrap(func, bitmask, thisArg, partials, holders);
});
bind.placeholder = {};
module.exports = bind;
Copy the code

Comparing the two codes, we can see that the implementation form of the two codes is not the same. Maybe we can generally understand the first way to write, almost all articles are written in the first way, easy to understand. But the second way is a little harder to understand, because it’s more abstract and decoupled than the first way. For example, it’s more functional. In fact, if you’re comfortable with functional programming, bind is essentially an implementation of partial functions. The second version is already named partials. For example, in an interview, if you are asked how bind is implemented, you can write both implementations. May you write, the interviewer can not understand it 😂. So here’s an example, and there are a lot of them, so explore for yourself. (Core-JS and LoDash packages were introduced.)

Understanding of ant Design Egg events

The recent Ant Design Easter egg event, this egg is exciting enough that people react so strongly. It shows the popularity of Ant Design. In the vernacular, Ant Design used to be “loved but not hated”, but now it is “loved and hated by everyone”.

There is a problem, how to solve it, how to solve it, but forced to tear, who took the pot.

The story goes like this:

For example, when you work in a company, a co-worker or someone else gets in trouble and reset your code. This must affect your work. What do you do at this point? You’re gonna be pissed. You’re gonna be pissed. Especially if you don’t take the blame for the mess and the impact on someone else’s work, you just have to get the code back. That’s the kind of person you’re going to get upset about, and you’re going to pick a fight with. After all, you have affected my work, do not act as if the pot is not your own, you carry the pot, I will solve the problem you brought me, don’t do it again.

Ant Design, just like the colleague who made the trouble above, affected everyone, but Ant also took the initiative to admit his mistake, took the initiative to take the blame, and immediately provided a solution.

In fact, for those who cause unemployment because of this thing, I personally think it is more uncomfortable. But for those of you who speak vehement (harsh) language, several of which rise to the level of resentment because of the front-end framework, the purpose of the harsh language is to implicitly lash out at the Ant team. I think Ant realizes that he’s never going to do anything like this again.

I sincerely hope that you will:

Since we have chosen to trust Ant Design from the very beginning, we should be more tolerant and tolerant of this mistake of Ant Design. We should not deny all of it just because of one mistake.

In fact you in the company, also is such, you make a mistake, affecting many colleagues, you realize the gravity of the situation, you are very bad, very regret, you find that you made a very stupid thing, you really want to go to make up for, but time can’t back, time can not return, you can do is to ensure that the next time won’t make mistakes again, You really want everyone’s forgiveness and trust. Although you sincerely admit your mistake, I hope everyone can trust you as before, but if you make a mistake, they will not trust you so much in their behavior and speech. That, at this time your heart, also must be extremely lost and gray cold bar.

So I’m asking you to continue to trust Ant Design and embrace it for once, and embrace the right-leaning people who have done so much for open source.

In fact, in life, sometimes, we will find that tolerance does not need many times, one tolerance is ok. Because tolerance once can make a thing never happen again. Is not, nag so much, actually the answer is in the words.

All right, enough with the personal spin.

note

The article is a little long

Since the article was a bit long, I did a few things to the code I posted, for example, cut some of the code and write it as a three-line if statement, and write it as one line. Put import and export together as much as possible without line breaks. So if you want to see no cut version of the article, you can go to my lot, making connections: https://github.com/godkun/blog/issues/30

What should I do if I don’t understand the NPM package

For NPM package source code, I myself when reading, will also be on some places do not understand, this is very normal for us (NB big guy except), but I will not because a paragraph, a file do not understand and block my understanding of the whole package, I will add my own understanding, even if it is wrong, But as long as I can fluently understand the whole package as I want to understand it, it is enough. Don’t try to fully understand unless you’ve talked to the author of the NPM package.

You will find that there are some problems in the analysis of this article, and I do not have an exact answer, just like those uploaded videos of LOL teaching, as long as they are uploaded, they are all classic moves, predictions and coquetty operations. But in real life, there may have been a dozen or so. Speaking of this, IT suddenly occurred to me that there was a post on Zhihu, which seemed to ask what kind of scene the program usually writes code in, and also posted a picture of the Matrix. Is it really like this? And then there was a video response, which made me laugh. In fact, derivation, you know that when looking at NPM package source code, it can not be plain sailing. There must be some incomprehensible, and the source of the NPM package and github corresponding NPM package source is not the same. The NPM package is like the github NPM source code after the package management tool, build output. You can see this from the dist directory, such as github taro source with rollup into small packages.

It’s normal to encounter areas that you don’t understand. All you have to do is understand the whole and ignore the parts.

Summary at the end of the article

At this point, you’ll notice that I haven’t analyzed all the dependencies downloaded from Taro Init, because if I had, short stories would have been born, and they wouldn’t have made any sense. I just play a role, I hope you read my article, there are some harvest, don’t be afraid of NPM package, NPM package is also written by people.

When analyzing, I recommend downloading one package at a time, and then downloading one package to take a look at the directory. It helps to understand that many people just dump NPM I or YARN install, open node_modules, and don’t know which package to look for. So, when you want to understand something, the best way is to download it package by package, look at it bit by bit, look at the changes in the code structure before and after, the changes in the package. Then you will notice that the number of bags gradually increases, but you will not panic at all, because you already know their general function and content.

Finally according to the primary school Language teacher taught me the operation, make a beginning and end echo.

Front-end is the industry that benefits most on Github, because the most advanced open source technology, the source code is on Github, Github is the front-end treasure, inexhaustible, endless. React, vue, Angular, webpack, Babel, Node, RXJS, three. Js, TypeScript, Taro, Ant-Design, Egg, Jest, KOa, Lodash, Parcel, Rollup, D3, Redux , Flutter, CAx, Lerna, HApi, JSX, ESLint, etc., etc. The treasure is there. Would you like to unveil it and see the truth?

Refer to the link

  • Official Documents:

Excitation of moment

The nuggets series can be found on github.

https://github.com/godkun/blog

Feel good, can point a star and praise praise, encourage encourage.

Expose my most mysterious dating website account for the first time (dive escape)

Behind-the-scenes look

2018 is coming to an end. I wish you all a happy family and a successful career in 2019.

There will probably be some mistakes, but there will also be some good ones.

So…

Happy New Year’s day ya!