Component packaging
Let’s look at an example of an e-commerce site wheel cast map. Using native JS to write an e-commerce site of the wheel broadcast map, how to achieve?
Basic method
It’s still HTML, CSS and JS. A multicast graph is a typical list structure, implemented with unordered list
-
elements. Use transition to make a transition by superimposing images in one place using absolute positioning.
Next, we design a set of apis for the behavior of the multicast graph. The API design should ensure atomic operation, single responsibility and flexibility. Including the following:
- Slider
- +getSelectedItem() gets the selected object
- +getSelectedItemIndex() gets the position of the selected node among all nodes
- +slideTo() toggles the number of cards
- +slideNext() switches to the next slide
- +slidePrevious() switches to the previous slide
class Slider{
constructor(id){
this.container = document.getElementById(id);
this.items = this.container
.querySelectorAll('.slider-list__item, .slider-list__item--selected');
}
getSelectedItem(){... }getSelectedItemIndex(){... }slideTo(idx){... }slideNext(){... }slidePrevious(){...}
}
// Create a sliding component and switch to the second one
const slider = new Slider('my-slider');
slider.slideTo(2);
Copy the code
On top of that we add control flow, which listens for events.
class Slider{
constructor(id, cycle = 3000){
this.container = document.getElementById(id);
this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
this.cycle = cycle;
// Small dot toggle
const controller = this.container.querySelector('.slide-list__control');
if(controller){
controller.addEventListener('mouseover'.evt= >{});
controller.addEventListener('mouseout'.evt= >{});
this.container.addEventListener('slide'.evt= >{})}// Switch to the next slide
const previous = this.container.querySelector('.slide-list__previous');
if(previous){
previous.addEventListener('click'.evt= > {});
}
// Switch to the previous one
const next = this.container.querySelector('.slide-list__next');
if(next){
next.addEventListener('click'.evt= >{}); }}getSelectedItem(){... }getSelectedItemIndex(){... }slideTo(idx){... }slideNext(){... }slidePrevious(){... }start(){... }stop(){...}
}
const slider = new Slider('my-slider');
slider.start();
Copy the code
pluggable
Looking at the previous version of the constructor, we saw that the code was too bloated, registering button click, switch, and slide events in addition to initialization. So we thought about isolating these control elements, extracting them into plug-ins, and establishing connections through dependency injection.
class Slider{
constructor(){}registerPlugins(. plugins){
plugins.forEach(plugin= > plugin(this)); }}function pluginController(slider){}function pluginPrevious(slider){}function pluginNext(slider){}const slider = new Slider('my-slider');
slider.registerPlugins(pluginController, pluginPrevious, pluginNext);
slider.start();
Copy the code
templated
Despite the use of plug-ins, decoupling between components and different plug-ins has been achieved. However, we found that there was room for further simplification of HTML, and that in some cases we didn’t need a part of the plug-in and had to remove it manually. But by templating, you can simplify the HTML editing process.
<div id="my-slider" class="slider-list"></div>
Copy the code
class Slider{
constructor(id, opts = {images:[], cycle: 3000}){
this.container = document.getElementById(id);
this.options = opts;
this.container.innerHTML = this.render();
this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
this.cycle = opts.cycle || 3000;
this.slideTo(0);
}
render(){
const images = this.options.images;
const content = images.map(image= > `
<li class="slider-list__item">
<img src="${image}"/>
</li>
`.trim());
return `<ul>${content.join(' ')}</ul>`;
}
registerPlugins(. plugins){... }getSelectedItem(){... }getSelectedItemIndex(){... }slideTo(idx){... }slideNext(){... }slidePrevious(){... }start(){... }stop(){...}
}
const pluginController = {
render(images){... },action(slider){...}
};
const pluginPrevious = {
render(){... },action(slider){...}
};
const pluginNext = {
render(){... },action(slider){...}
};
const slider = new Slider('my-slider', {images: ['https://p5.ssl.qhimg.com/t0119c74624763dd070.png'.'https://p4.ssl.qhimg.com/t01adbe3351db853eb3.jpg'.'https://p2.ssl.qhimg.com/t01645cd5ba0c3b60cb.jpg'.'https://p4.ssl.qhimg.com/t01331ac159b58f5478.jpg'].cycle:3000});
slider.registerPlugins(pluginController, pluginPrevious, pluginNext);
slider.start();
Copy the code
Component framework
More generally, we can abstract the generic component model so that we can inherit it from other components, not just sliders.
class Component{
constructor(id, opts = {name, data:[]}){}
registerPlugins(. plugins){}
render(data) {
/* abstract */
return ' '}}Copy the code
conclusion
Here we have templated only HTML, not CSS. Common frameworks such as React and Vue implement component-based development of HTML, CSS, and JS.
The use of component-based development pattern can avoid a lot of repetitive code, logic, function in multiple pages, avoid complex maintenance. Through functional separation and component encapsulation, the code readability is not only increased, but also the operation and maintenance cost is reduced.
The following basic principles should be followed in component design:
- encapsulation
- correctness
- scalability
- reusability
The basic steps of component implementation include: HTML structure design, CSS display effect design, JS behavior design. On this basis, through plug-in, template, abstraction, component reconstruction optimization.