preface

With the increase of front-end projects in the company, we will find that many resources in each project are more or less the same, which leads to a topic, how to share front-end resources across projects. Resources here generally refer to all static resources involved in the front-end, such as HTML/CSS/JS/ pictures and so on.

The so-called shared front-end resources, is to extract the common front-end resources, such as common style/common logic/common components/public picture resources and so on, so that multiple projects to reference, to avoid duplication, to avoid repeated development, unified management and maintenance. As long as the common resources are updated, other referenced projects can be updated at the same time, improving development efficiency and reducing development costs.

Barriers to front-end resource sharing

The blueprint is grand, but it is difficult to realize it. To realize it, we must first recognize the obstacles that exist in reality.

The sharing of front-end resources involves the following aspects, the key is how to use and update the problem

  • Extracting common resources: How to modularize? Modular granularity control?
  • Store/manage common resources: Where should common resources be uniformly located? How do you manage dependencies?
  • Public resource release update: How to publish a public resource, and how to update the version?
  • Coexistence of multiple versions of a common resource: How do I indicate which version of a common resource I need?
  • Use (quote) common resources: How can common resources be used in a project, with packaging optimizations in mind?

Front-end resources are composed of a variety of different types of resources, and the dependency relationship is complex, involving different resource paths, reference method is a headache, has been causing front-end resources inconvenient to share a big obstacle. And the front-end didn’t even have the concept of a dependency package manager (like Java’s Maven) early on, all dependencies were copied and pasted manually and overwritten with updates. When a new project starts, it’s copied and pasted again.

Do you remember how many copies of jquery.js you made? Can you still figure out which version of jQuery is used for each project? What should you do if you need to upgrade the jQuery version for your entire project? Common resources are shared in the form of multiple copies, which is bound to become difficult to maintain, and may eventually lead to different versions of common resources.

JAVA programmers remember the days when JAVA didn’t have Maven to manage projects and jar dependencies were copied and pasted across multiple projects?

In order to avoid the pain caused by copying and pasting and uniformly maintain public resources, we can publish public resources in fine granularity to CDN without considering optimization.

So in each project, we can use that

<! -- CSS for common components --> <link rel="stylesheet" href="//cdn.com/lib/component/component.css"> <! <img SRC = public static resources: for example, a company LOGO --> <img SRC ="//cdn.com/lib/logo.png"> <! -- public component JS --> <script SRC ="//cdn.com/lib/component/component.js"></script>
Copy the code

But what if the common resource is updated? Because of the browser cache issues involved, you need to search for all references to common resources for each item, and then change the timestamp of the reference resources one by one to force the browser cache to be invalidated.

Don’t forget to do the same for references to other public resources, such as CSS references to images that are updated as follows:

<link rel="stylesheet" href="//cdn.com/lib/component/component.css?v1">
<img src="//cdn.com/lib/logo.png?v2">
<script src="//cdn.com/lib/component/component.js?v3"></script>
Copy the code

But what if a common resource needs multiple versions to coexist? We can use the version folder to distinguish between them. When the referenced public resource is updated, we need to change the version number:

<link rel="stylesheet" href="/ / cdn.com/lib/component/1.1.0/component.css">
<img src="/ / cdn.com/lib/logo/1.2.0/logo.png">
<script src="/ / cdn.com/lib/component/1.1.0/component.js"></script>
Copy the code

But which project uses which version is still a global search. And when the common resource version is updated, how can projects be notified to update it? Is it by Shouting?

But what if the CDN domain name needs to be changed? Once again, you have to do a global search. This global search is painful and primitive. It’s easy to deal with a small number of projects, but it’s painful to manage more and more projects. You might think of workarounds for unifying references to common resources, such as configuring them all with the help of something like a back-end configuration management system.

But what if you reference too many common resources and want to optimize the number of requests and merge resources? You can’t solve this by searching globally. At first, in order to facilitate the on-demand use of public resources, we planned public resources in fine granularity, which is bound to increase the number of page requests. To optimize, we can use the Combo mechanism to dynamically merge resources on the server side.

The development phase allows for fine-grained references to common resources

<link rel="stylesheet" href="/ / cdn.com/lib/component1/1.0.0/component1.css">
<link rel="stylesheet" href="/ / cdn.com/lib/component2/1.1.0/component2.css">
<link rel="stylesheet" href="/ / cdn.com/lib/component3/1.2.0/component3.css">
<link rel="stylesheet" href="/ / cdn.com/lib/component4/1.3.0/component4.css">
<img src="/ / cdn.com/lib/logo/1.0.0/logo.png">
<script src="/ / cdn.com/lib/component1/1.0.0/component1.js"></script>
<script src="/ / cdn.com/lib/component2/1.1.0/component2.js"></script>
<script src="/ / cdn.com/lib/component3/1.2.0/component3.js"></script>
<script src="/ / cdn.com/lib/component4/1.3.0/component4.js"></script>
Copy the code

In the launching stage, we can use the Combo mechanism to combine resources and reduce the number of requests to optimize the front-end performance

<link rel="stylesheet" href="/ / cdn.com/combo?lib/component1/1.0.0/component1.css, lib/component2 1.1.0 / component2. CSS, lib/component3/1.2.0 / component3 CSS, lib/component4 1.3.0 / component4. CSS">
<img src="/ / cdn.com/lib/logo/1.0.0/logo.png">
<script src="/ / cdn.com/combo?lib/component1/1.0.0/component1.js, lib/component2 1.1.0 / component2, js, lib/component3 1.2.0 / component3. J S, lib/component4 1.3.0 / component4. Js. ""></script>
Copy the code

Such a long URL, like an old woman’s foot-binding smelly and long, it is quite a hot eye to maintain.

And have you noticed that the public component reference way, also a bit troublesome, is in two steps, first reference CSS, then reference JS, this CSS is clearly a component dependency. So the question is, why should I care about its dependencies when I use a component? What if the component’s CSS or component’s JS has other dependencies? Does it also need to be handled by the user? Visible references to front-end resources are best handled automatically, and the user only needs to focus on the resource (component) layer he wants to use.

Front-end resource dependencies

In order to simplify the way to reference front-end resources, we must deal with front-end resource dependencies. Let’s take a good look at the various dependencies of front-end resources.

First, the page is the entry point of all resource references, that is, the starting point (source) of dependencies. There are two main types of dependencies

  • Static dependencies: Explicit dependencies that can be found by statically analyzing code, possibly involving multiple layers of dependencies
  • Dynamic dependencies: Dependencies that code loads during execution The following examples illustrate the various dependencies that can exist on the front end
Index.html -- Page entry, Starting point for all dependencies ├─ <link rel="stylesheet" href="index.css"> - CSS rely on (may also depends on other CSS and other static resources) | | ─ ─ @ import url (a.c ss); Reference resources of the path of the relative path is relative to the current CSS | | | ─ ─. A {background: url (res/Amy polumbo ng)} - rely on resources and involves a dependent on | | ─ ─ the index {background: Url (res/index. PNG)} | ├ ─ ─ < img SRC ="res/foo.png"> - static resource dependency | ├ ─ ─ < script SRC ="index.js"> < / script > -- JS preview (maybe (dynamic) rely on other CSS/JS and other static resources) | | ─ ─. Img SRC ='res/bar.png'; Reference resources of the path of the relative path is relative to the current HTML | | ─ ─ link. The href ='b.css'; | | | ─ ─... | | ─ ─ js. SRC ='a.js'; | | | ─ ─...Copy the code

How do you deal with so many dependencies? It’s not practical to do it manually. We need a Module Bundler to help us analyze and package these dependencies.

In the face of these heavy obstacles and problems, how to achieve front-end resource sharing this blueprint? Inspired by senior technical director Long, learn from the ideas of domestic cattle…….

Implementation scheme

Based on the above analysis results, to share front-end resources, the common front-end resources must be managed in the form of packages to form a common warehouse, declare the dependent front-end resource packages in the project, and download the dependent specific resource files through tools.

During development, the module packer is used to analyze the specific dependency files and package all the front-end resources that the project depends on.

Therefore, we need a package manager and a module packer to achieve the desired sharing of front-end resources.

  • Package manager: NPM

npm is the package manager for JavaScript

manage dependencies in your projects

Manage project-level dependencies and set up an NPM private server if necessary

  • Module packer: Webpack

webpack is a module bundler for modern JavaScript applications

it recursively builds a dependency graph that includes every module your application needs, then packages all of those modules into a small number of bundles – often only one – to be loaded by the browser.

Build tools analyze and package dependencies, and now you can feel free to modify and delete static resource files left over from your project history

  • Any resource can be regarded as a dependency, and reference relationship of resources can be separated out during construction
  • If a resource is a zombie resource, deleting it does not cause a build failure because there are no files that reference (depend on) it
  • In the days before build tools relied on analytics, you could only do a global search to determine if a file was still useful but there was probably no way to make a decision to delete it (what if other projects referenced it?).

Next, the key to sharing front-end resources is to publish common modules to NPM, and then rely on actual use in individual project declarations.

The actual way to use it comes down to: how do I use webpack to reference NPM modules (the node_modules folder) in HTML/CSS/JS files, or files in NPM modules

  • HTML
<! -- Reference the file from NPM module ionicons --> <img SRC ="~ionicons/dist/svg/ios-sunny.svg" width="50" height="50">
Copy the code
  • CSS
*/ @import url(~normalize.css); */ @import url(~normalize.css); .test-npm-res {/* Reference the file from NPM module ionicons */ background-image: url(~ionicons/dist/ SVG /ios-partly-sunny-outline.svg); }Copy the code
  • JS:
// Import SVG from the NPM module ionicons'ionicons/dist/svg/ios-sunny-outline.svg';
Copy the code

The sample

We only need to focus on the module we use, and no longer need to focus on module dependencies

  • Sharing static resource files: Referencing common company Logo images across projects

Publish public static resources on NPM as a company-common-res module package

Company - common - res / ├ ─ ─ SRC / | | ─ ─ logo. The PSD | └ ─ ─... | ├ ─ ─ dist / | | ─ ─ logo. The PNG | └ ─ ─... | └ ─ ─ package. JsonCopy the code

Declare dependencies on this module in your project’s package.json

"Dependencies" : {" company - common - res ":" ^ "1.0.0,}Copy the code

The next step is to reference the static resource in an HTML/CSS/JS file, for example

<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2017/12/21/1607716e35b27686~tplv-t2oaga2asx-image.image" width="50" height="50">
Copy the code
  • Common CSS components: Reference common CSS styles across projects

Publish the common CSS base styles on NPM as a company-component-base module package, declaring dependencies on this module in the project’s package.json

The package.json of the company-component-base module should declare main as the component’s CSS file

"main": "company-component-base.css"
Copy the code

Use this component in your project, for example in a CSS file

@import url(~company-component-base);
Copy the code

Publish common CSS components on NPM as a company-component-button module package

@import url(~company-component-button);
Copy the code
  • Common JS modules (purely logical modules, without styles): Reference common JS utility methods in various projects

Publish the common JS utility methods on NPM as a company-util module package, declaring dependencies on this module in the project’s package.json

import util from 'company-util';
util.log('test');
Copy the code
  • Common JS Components (including styles): Reference the common TOAST components in each project

Publish the Toast component on NPM as a company-component-toast module package and declare dependencies on this module in your project’s package.json

import Toast from 'company-component-toast';

new Toast({
    content: 'Prompt content'
}).show();
Copy the code

conclusion

Barriers based on front-end resource sharing

  • Many different kinds of resources
  • Complex resource dependencies (HTML dependencies /CSS dependencies /JS dependencies/image dependencies/component dependencies)
  • There is no package manager to unify the dependencies of the maintenance project
  • How are dependencies referenced and packaged

To implement front-end resource sharing, we need a package manager and module packer to address these issues.

The next focus is, how to form a component platform, so that we can easily know what components, and how to use these components

  • Normalized package names: such as component package namescomponentAt the beginning
  • Normalized project directory structure
  • Component documentation and showable usage examples