“This article has participated in the call for good writing activities, click to view: the back end, the big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!”

preface

When developing THE JS SDK, the “native OR framework” has different options for different people and different scenarios. This article, based on actual project scenarios, explains how to deal with some problems that are “not going well” in the native environment.

1. Modular development

// index.js // add an entry button // button click to add sidebar // Click blank to hide sidebar // sidebar click to pop up //...Copy the code

So write down to think bald, like a running account composition.

  • Modularity can be used (using webpack, gulp, etc.) to help readers quickly locate the code they are looking for.
  • To weaken the chronological order of organizational logic, object orientation can be used, such as splitting classes (the granularity of splitting can be borrowed from the use of components in the framework).
//index.js Class Entry {} // Entry button // dropdown.js Class dropdown {// Bind sidebar show() {} // show sidebar hidden() {} // hide sidebar Select () {} // Select open dialog}Copy the code

The component development experience is applied to the native environment, and the code structure is clearer.

2. Keep the innerHTML in order

<ul class="list">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
Copy the code

Generating the above section of DOM dynamically with JS might look something like this.

const ul = document.createElement('ul');
ul.className = 'list';
ul.innerHTML = [1.2.3].map(i= > `<li>${i}</li>`).join(' ');
Copy the code

InnerHTML accepts fragments of HTML serialization, so you can dynamically generate the DOM using template strings. But there are limitations to this approach:

  1. You need an existing node as a container and cannot manipulate the DOM before inserting the container;
  2. Container node properties still need to be added manually (such as class);

By encapsulating innerHTML, you can overcome these limitations and generate a dom out of the container.

const wrap = document.createElement('div');
export const template2dom = <T extends HTMLElement>(template: string): T => {
    wrap.innerHTML = template;
    return wrap.children[0] as T;
};

const ul = template2dom(`
<ul class="list">${
    [1, 2, 3].map(i => `<li>${i}</li>`).join('')
}</ul>`);
Copy the code

Combined with module splitting, DOM operations can be divided into initial rendering and dynamic modification. Use the encapsulated Template2DOM to get the root node of the module, and querySelector to get dynamically modified nodes under the root node, using the native DOM API.

3. Clear events

When using a framework, we don’t have to worry about whether the events we’re listening for should be removed; the framework usually takes care of it for us. Now we need to solve:

  • Remove events bound to the DOM when it leaves the document;
  • Use event delegate when there are more frequent nodes in the DOM that need to bind events.

It is easier said than done. The location of the removal event is usually different from that of the registered event, and many attributes shared in the class are added to obtain listener functions and dom. It is possible to miss a removeEventlistener due to negligence. Encapsulating an event management class as a base class is a good solution.

export abstract class EventCleaner {
    private readonly eventMap = new Map <HTMLElement, SetThe < {name: keyof HTMLElementEventMap; cb(e: HTMLElementEventMap[keyof HTMLElementEventMap]): unknown; } > > (); addEventListener<Textends keyof HTMLElementEventMap>(
        el: HTMLElement,
        name: T,
        cb: (e: HTMLElementEventMap[T]) = > unknown
    ): void {
        el.addEventListener(name, cb);
        const events = this.eventMap.get(el);
        if (events) {
            events.add({name, cb});
        }
        else {
            this.eventMap.set(el, new Set([{name, cb}]));
        }
    }
    clearEvent(): void {
        for (const [dom, events] of Array.from(this.eventMap)) {
            for (const {name, cb} ofevents) { dom.removeEventListener(name, cb); }}this.eventMap.clear(); }}Copy the code

All you need to do is write an unload method on each derived class and call clearEvent to clear all the events listened on within that instance.

4. Style isolation

Style isolation is a problem that the SDK can’t get around. CSS Modules can change the id and class forms at build time so that styles do not cause accidental contamination.

  • Hash: CSS Module default class,Semantic lossandModify with style (Bad for downstream overlay style);
  • Local + name[MD5] : takes into account semantics, uniqueness and does not change with style.

Local + name\[md5\] is not a form supported by CSS-Loader and requires hash at build time

const md5 = require('md5');
const affix = md5(path.parse(packageName)).slice(0.5); // use md5 as the suffix of the CssModule

/ / webpack configuration
{
    test: /\.css$/i,
    use: [
        'style-loader',
        {
            loader: 'css-loader'.options: {
                modules: {
                    localIdentName: `[local]_${affix}`,}}}]}Copy the code

These are my thoughts on using native JS to develop SDK. It’s all about sorting out the order of the code, and the native API is powerful enough that I don’t need to extend anything. Front-end frameworks make it cheaper for developers to get into the habit of being logical, and they create some inertia. Hope to be able to take its essence and discard its dross, fearless forward!

Source code reference: github.com/anyblue/blu…