Introduce a,

Swiper is a javascript plugin designed for mobile devices such as mobile phones and tablets. Swiper can realize common effects such as touchscreen focus image, touchscreen Tab switching, and touchscreen wheel casting image switching.

Swiper is widely used in the industry. Last year, the company’s basic data department needed to add image rotation function to Swiper, so I took the opportunity to read the source code of Swiper and expand the Rotate component.

This article, from how to read [email protected] source, to start with custom development, to introduce my ideas, I hope to give you some inspiration and thinking.

How to analyze the source code step by step

Usually when I read source code, I follow the following steps:

  1. Readme.md
  2. package.json
  3. Packaging tools
  4. Entrance to the file
  5. Minimum development unit

2.1 the Readme. Md

A Readme is usually an overview of a project. It tells us how to install, compile, package, and use the project, as well as common apis and simple code examples. By reading this, we can get a general understanding of the project, so that we will not get lost in the details later.

Here is part of the readme.md Swiper project:

Install the NPM package

$ npm install --global gulp Install the gulp tool globally
$ npm install Install dependencies for this project
Copy the code

Packaged development releases

$ npm run build:dev 
Copy the code

Package results in build/ folder.

Packaged production version

$ npm run build:prod
Copy the code

Package results in package/ folder.

😭😭😭, unfortunately, there is no introduction to how to launch the development environment locally, so next, let’s take a look at package.json…

2.2 package. Json

In package.json, focus first on scripts, dependencies, and devDependencies.

From scripts we can infer locally developed commands because it contains gulp Server, packaged commands;

# [gulp build package]; [Gulp Playground demo environment]; [gulp server local service];
"dev": "cross-env NODE_ENV=development gulp build && cross-env NODE_ENV=development gulp playground && cross-env NODE_ENV=development gulp server".# Local development scripts
npm run dev | yarn dev
Copy the code

There are also other commands, such as lint to execute eslint, test to package dev after lint, etc.

Swiper is a clean project with only two libraries. Most functions are completed independently.

"dependencies": {
    "dom7": "^ 2.1.3".# DOM operation encapsulation
    "ssr-window": "^" 1.0.1
}
Copy the code

DevDependencies provides a list of engineering tools, compilation tools, and other tools for the project.

"devDependencies": {
    "eslint": "^ 6.4.0".// Check the code style
    "gulp": "^ 4.0.2." ".// Package the compiler
    "less": "^ 3.10.3"./ / less compilation
    "postcss": "^ 7.0.18"./ / CSS
    "rollup": "^ 1.21.4".// JavaScript module wrapper
},
Copy the code

2.3 Packing Tools

By analyzing package.json, we find gulp, rollup and less. It is easy to imagine that the project uses gulp as an engineering tool, rollup to package JS code, and less to compile less files.

Let’s take a look at gulpfile.js and see, alas, only one line of code

require('./scripts/gulpfile');
Copy the code

All the code related to packaging is arranged into the scripts directory.

The idea of gulp is similar to Grunt, that is, tasks, and then a series of tasks are executed in order to compile and package the code and output the results.

The following is the analysis of gulP tasks in this project: the core server and build tasks.

  1. serverThe goal of the task is to start a Web service locallyconnectAnd then by monitoring the native codewatch, there are code changes, will go to repackage JS and CSS, finally openplaygroundAddress of the online testopen.
  2. buildThe goal of the task is to package the project code, and the task to go isjsandcssPackaging.
graph TB
A[tasks] --> B[playgroud]
A[tasks] --> C[server] 
A[tasks] --> D[build]
C[server] --> G[watch]
C[server] --> H[connect]
C[server] --> I[open]
D[build] --> K[styles]
D[build] --> J[js]
J[js] --> L[es]
J[js] --> M[umd]
K[styles] --> N[less]
G[watch] --> O[js]
G[watch] --> P[styles]

2.3.1 task of js

scripts/build-js.js

We found that two packaging methods of ES and UMD are supported. Esm is the standard Module system proposed by ES6, and UMD stands for Universal Module Definition, which is a scheme combining CommonJS, AMD and CMD, namely, to write a set of code. It can run in different scenarios such as browser and server.

Rollup ->plugins->replace, where ‘//INSTALL_COMPONENTS’ is inserted into the following code string, pure string substitution.

replace({
    delimiters: [' '.' '].'process.env.NODE_ENV': JSON.stringify(env),
    '//IMPORT_COMPONENTS': components.map((component) = > `import ${component.capitalized} from './components/${component.name}/${component.name}'; `).join('\n'),
    '//INSTALL_COMPONENTS': components.map((component) = > `${component.capitalized}`).join(',\n '),
    '//EXPORT': 'export default Swiper',}).Copy the code

Search the code and you will find that ‘//INSTALL_COMPONENTS’ exists in the following code, so a batch of component names will be inserted into this location, as will //IMPORT_COMPONENTS, etc.

// Path: SRC /swiper.js
const components = [
  Device,
  Support,
  Browser,
  Resize,
  Observer,
  //INSTALL_COMPONENTS
];
Copy the code

scripts/build-config.js

Package configuration files that record constants, such as Components, which record all components in swiper. When adding new components, add their names here.

The styles of 2.3.2 task

scripts/build-styles.js

The main thing is to use less, to compile and package less files, which I won’t go into here.

2.4 Import File

Let’s take a look at how Swiper is used, usually with two parameters:

var swiper = new Swiper(container,options);

  • One is thecontainerIs a selector (string orHTML ElementObject);
  • One is configuration itemsoptionsIs an object:
/ / for
var swiper = new Swiper('.swiper-container', {
  pagination: {
    el: '.swiper-pagination'.dynamicBullets: true,}});Copy the code

So we initialize a series of components (such as pagination) with New Swiper(), so we need to understand the source code from the Swiper class.

2.4.1 Start from swiper.js

/build-js.js/Swiper/Swiper/Swiper /build-js.js/Swiper

input: './src/swiper.js'.Copy the code

src/swiper.js

Find swiper. Js and the first line of code looks like this. You may have noticed that this is the swiper class definition:

// Swiper Class
import Swiper from './components/core/core-class';
Copy the code

src/components/core/core-class.js

import SwiperClass from '.. /.. /utils/class';
class Swiper extends SwiperClass {
// ...
}
Copy the code

We found core-class, which is derived from SwiperClass, utils/class, so we stopped by.

src/utils/class.js

Alas, SwiperClass has finally arrived. Here is a class diagram of the two classes, listing the main methods and properties.

Figure 2: Swiper class diagram

Next, we will focus on analyzing these three JS in combination with the swimlane diagram below.

Figure 3: Swiper function call

2.4.2 After loading swiper.js, construct the swiper class

Let’s take a look at swiper.js.

// Swiper Class
import Swiper from './components/core/core-class';

//IMPORT_COMPONENTS
const components = [
  Device,
  Support,
  Browser,
  Resize,
  Observer,
  //INSTALL_COMPONENTS
];
 
if (typeof Swiper.use === 'undefined') {
  Swiper.use = Swiper.Class.use;
  Swiper.installModule = Swiper.Class.installModule;
}

// The use method is executed, and the component is processed. Let's see what the use method does
Swiper.use(components);
Copy the code

Using utils/class is a static method, line by line: utils/class

/** * is similar to Vue's use method **@static
 * @param {*} Module Component or module *@param {*} params 
 * @returns
 * @memberof SwiperClass* /
static use(module. params) {
  const Class = this; // Class is SwiperClass
  if (Array.isArray(module)) { // If an array is passed in, the registration is iterated
    module.forEach((m) = > Class.installModule(m));
    return Class;
  }
  // If a single component is registered directly, does it also provide an opportunity to register components on the run?
  return Class.installModule(module. params); }/** * Register a single component **@static
 * @param {*} module
 * @param {*} params
 * @returns
 * @memberof SwiperClass* /
static installModule(module. params) {
  const Class = this;
  if(! Class.prototype.modules) Class.prototype.modules = {};// Module name. The default is name
  const name = module.name || (`The ${Object.keys(Class.prototype.modules).length}_${Utils.now()}`);
  // Add the component to modules in SwiperClass
  Class.prototype.modules[name] = module;
  
  // Bind methods on module.proto to swiperClass.prototype
  if (module.proto) {
    Object.keys(module.proto).forEach((key) = > {
      Class.prototype[key] = module.proto[key];
    });
  }
  
  // Class
  // Bind methods on module.static to SwiperClass
  if (module.static) {
    Object.keys(module.static).forEach((key) = > {
      Class[key] = module.static[key];
    });
  }
  
  // Call the install method
  if (module.install) {
    module.install.apply(Class, params);
  }
  return Class;
}
Copy the code

So, after use and installModule method, SwiperClass. Prototype. The modules object contains all of the component object, SwiperClass. The prototype and SwiperClass binding a number of methods and properties.

2.4.3 New Swiper() example

With the use method, Swiper is ready to initialize the Swiper instance.

var swiper = new Swiper('.swiper-container', {
  pagination: {
    el: '.swiper-pagination'.dynamicBullets: true,}});Copy the code

The create method for each component is executed, and the events under each component on are collected to the eventsListeners on swiper.emit(‘init’). Triggers the initialization of all components.

The important thing is that two methods are executed


// Install Modules
swiper.useModules();

// Init
if (swiper.params.init) {
  swiper.init();
}
Copy the code
useModules
useModules(modulesParams = {}) {
  / / swiper instance
  const instance = this;
  if(! instance.modules)return;
  / / traverse modules
  Object.keys(instance.modules).forEach((moduleName) = > {
    const module = instance.modules[moduleName];
    const moduleParams = modulesParams[moduleName] || {};

    // Extend instance methods and props
    // If the component has an instance object, it binds the instance property traversal to instance and this object to. Here module. Instance is slightly different from instance.
    if (module.instance) {
      Object.keys(module.instance).forEach((modulePropName) = > {
        const moduleProp = module.instance[modulePropName];
        if (typeof moduleProp === 'function') {
          instance[modulePropName] = moduleProp.bind(instance);
        } else{ instance[modulePropName] = moduleProp; }}); }// Add event listeners
    // Iterate through the events and handlers on the component's ON property, call the on method on swiper instance, and categorically bind to eventsListeners
    if (module.on && instance.on) {
      Object.keys(module.on).forEach((moduleEventName) = > {
        instance.on(moduleEventName, module.on[moduleEventName]);
      });
    }

    // Module create callback
    // Execute create if the component has a create method and the object is a swiper instance.
    if (module.create) {
      module.create.bind(instance)(moduleParams); }}); }Copy the code
init
init() {
  const swiper = this;
  if (swiper.initialized) return;

  // Triggers the beforeInit event
  swiper.emit('beforeInit');

  / /... Omit code

  // According to the browser environment, configure events such as touch and mouse.
  swiper.attachEvents();

  // Init Flag
  swiper.initialized = true;

  // Notify all init events on eventsListeners to be executed
  swiper.emit('init');
}
Copy the code

So far, we have examined the process of component registration, class and instance initialization in Swiper. Now we can look at how to design and develop a component based on a single component.

2.4.4 summary

  1. When swiper.js is loaded via CDN, code construction is performed firstSwiperClass, the main process is to register the component toSwiperOf the classprototypeObject, the core method isuse,installModule;
  2. new SwiperAfter that, it will executeSwiperOf the classconstructorMethod to start based on the specifiedcontainerDe-instantiate the multicast, merge parameters, register events, and then initialize the component to get the multicast running. The core method isuseModules,useModulesParams,initAnd so on.

2.5 Analyzing a Component

Take Pagination as an example, as shown in the figure below. Pagination encapsulated some common methods. The exported object contained four attributes: Name, params, Create, ON, Create and ON, which would be called when new Swiper was created. Name and params names and initialization parameters.

2.5.1 Pagination object

The create method is bound to the Pagination object, which will be called later, including the registration of listening events, and corresponding processing methods. The core functions of the component are implemented in these methods.

2.5.2 params

The default pager parameters defined in the useModulesParams method are merged into the params property of swiper instance objects.

2.5.3 the create

The create method, called on useModules, initializes the pagination property of Swiper instances;

2.5.4 on

All methods of the ON attribute are classified as useModules and stored in eventsListeners that emit specific events. These provide a global notification, such as the init event mentioned earlier, on swiper instances. Then initialize the Swiper components.

Known events, documented, some of which you should also use when customizing a new component.

init,touchStart,touchMove,touchEnd,slideChangeTransitionStart,
slideChangeTransitionEnd,imagesReady,transitionStart,transitionEnd,
touchMoveOpposite,sliderMove,click,tap,doubleTap,progress,reachBeginning,
beforeDestroy,reachEnd,setTransition,resize,setTranslate,
slideNextTransitionStart,slideNextTransitionEnd,slidePrevTransitionStart,
slidePrevTransitionEnd,fromEdge,toEdge,slideChange,autoplayStart,
autoplayStop,autoplay,beforeLoopFix,loopFix,observerUpdate,breakpoint
Copy the code

Develop a new component

Referring to Pagination, we hope to add a rotation button. When clicked, the current image can be rotated. The code structure is as follows, which is basically consistent with pagination.

Fourth, the ending

So far, the simple source code analysis is complete, the swiper source structure how to organize, code execution logic has a certain understanding of how to extend a component scheme, of course, there are a lot of details, you can go to read in detail.


Nanjing 300 Cloud Information Technology Co., LTD. (CHE 300) was founded on March 27, 2014. It is a mobile Internet enterprise rooted in Nanjing and currently located in Nanjing and Beijing. After 7 years of accumulation, the cumulative number of valuation has reached 5.2 billion times, and won the favor of many high-quality investment institutions at home and abroad, such as Sequoia Capital, SAIC Industry Fund. 300 Cloud is an outstanding domestic auto transaction and financial SaaS service provider with independent third party relying on artificial intelligence and standardization of auto transaction pricing and auto financial risk control as its core products.

Welcome to join 300 cloud, witness the booming development of the automobile industry together, look forward to walking with you hand in hand! Official website: www.sanbaiyun.com/ Resume: [email protected], please note from nuggets 😁