Nowadays, components are frequently used in the front end. The component-based development of the front end originated from React. With component-based development, front-end development can develop applications just like stacking wood, which improves the reusability and expansibility of components to some extent. What is inside a component? A component is simply a wrapper around data and methods. In fact, most front-end components are the view, logic, style to do the corresponding encapsulation.
Determine the component invocation style
-
One of the things you really want to do, whether it’s a component or a library, is to encapsulate it, but they encapsulate it for a specific function. For example, function is the package of code block, JS library is the package of some functions, component package integration will be stronger. In short, they are abstractions of logic. Encapsulation follows the Open Closed Principle of design principles. The open closed principle states that software entities such as classes, modules, and functions should be open for extension and closed for modification.
-
At the beginning of a component design, I should focus on two things. 1. Try to keep the interior of components closed. 2. In order to make components flexible in use, more APIS and call methods should be provided as much as possible to make components more smooth to use. Juery, for example, exposes a $sign. Various properties and methods are provided for external use. Similarly, the axios library in the front and back end interaction exposes axios(config) and also provides calls to axios.get, axios.post, axios.put and so on, in order to make developers use axios more flexibly in development. The same is true for vue. To be called through new vue (config), internal components are called through custom tags.
-
There is no absolute definition of how to determine an invocation style. The principle is to make it easier for users to call your exposed API. Today we use the MessageBox in Element UI as an example to simulate the package with native JS. Call style: 1. Call in the form of new MessaegBox(config). 2. Use a vUE custom tag to call, such as
. It looks like this:
Determine the configuration based on your requirements
- With the call as the entrance, according to the requirements to determine the current messageBox we need to write what configuration. For example, in my current MessageBox, I want to achieve click popup, click ok button to close the popup box and call a certain callback, customize the title, customize the content, and so on. Here, of course, the configuration is determined according to the needs, but also depends on how flexible you want to write the component. Based on the preceding requirements, the configuration list is as follows:
{
width: "30%".height: "250px".title: "Test title".content: "Test content".dragable: true.// Whether it can be dragged
maskable: true.// Whether there is a mask
isCancel:false // Is there a cancellation
}
Copy the code
Define component class merge configuration
- Once the component invocation and configuration items are determined, the component can be separated into a messageBox class to encapsulate the component logic. Incoming configuration. Because the component is currently invoked through instantiation:
// Call the messageBox component
new MessageBox({
width: "30%".height: "250px".title: "Test title".content: "Test content".dragable: true.// Whether it can be dragged
maskable: true.// Whether there is a mask
isCancel:false // Is there a cancellation
})
Copy the code
To make the call more flexible during an external call, all configurations may or may not be passed in as user configurations. So after we receive the configuration internally we need to give a defaultOpts and we need to merge defaultOpts with the external incoming configuration. As follows:
export default class MessageBox {
constructor(opts) {
let defaultOpts = {
width: "30%".height: "250px".title: "Test title".content: "Test content".dragable: true.// Whether it can be dragged
maskable: true.// Whether there is a mask
isCancel: false // Is there a cancellation
}
this.opts = Object.assign(defaultOpts, opts); }}Copy the code
This enables configuration consolidation
Create the component DOM structure and style
- Since we want to encapsulate HTML and styles, of course we can generate DOM and CSS structures dynamically with JS. But with webComponent it’s easy to do it natively. MessageBox dom structure and CSS style are defined as follows:
const template = document.createElement('template');
template.innerHTML = `
<style>
.k-dialog {
width: 30%;
z-index: 2001;
display: block;
position: absolute;
background: #fff;
border-radius: 2px;
box-shadow: 0 1px 3px rgba(0.0.0.3);
margin: 0 auto;
top: 15vh;
left:30%;
}
.k-wrapper {
position: fixed;
left: 0px;
top: 0px;
bottom: 0px;
right: 0px;
background: black;
opacity: 0.4;
z-index: 2000;
}
.k-header {
padding: 20px 20px 10px;
}
.k-header .k-title {
line-height: 24px;
font-size: 18px;
color: # 303133;
float: left;
}
.k-body {
padding: 30px 20px;
color: # 606266;
font-size: 14px;
}
.k-footer {
padding: 10px 20px 30px;
text-align: right;
}
.k-close {
color: # 909399;
font-weight: 400;
float: right;
cursor: pointer;
}
.k-cancel {
color: # 606266;
border: 1px solid #dcdfe6;
text-align: center;
cursor: pointer;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
font-weight: 500;
margin-right: 10px;
}
.k-cancel:hover {
color: #409eff;
background: #ecf5ff;
border-color: #c6e2ff;
}
.k-primary {
border: 1px solid #dcdfe6;
text-align: center;
cursor: pointer;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
font-weight: 500;
background: #409eff;
color: #fff;
margin-left: 10px;
}
.k-primary:hover {
background: #66b1ff;
}
.k-input{
width: 100%;
margin-left: 20px;
margin-bottom: 20px;
}
.input-inner {
-webkit-appearance: none;
background-color: #fff;
background-image: none;
border-radius: 4px;
border: 1px solid #dcdfe6;
box-sizing: border-box;
color: # 606266;
display: inline-block;
font-size: inherit;
height: 40px;
line-height: 40px;
outline: none;
padding: 0 15px;
transition: border-color .2s cubic-bezier(.645.045.355.1);
width: 100%;
margin-top: 20px;
}
</style>
<div class="k-wrapper"></div>
<div class="k-dialog">
<div class="k-header">
<span class="k-title">prompt</span><span class="k-close">X</span>
</div>
<div class="k-body">
<span>This is a text</span>
<input class="input-inner" type="text" />
</div>
<div class="k-footer">
<span class="k-cancel">cancel</span>
<span class="k-primary">determine</span>
</div>
</div>
`;
Copy the code
Define a custom component, create a custom component:
class MessageBoxele extends HTMLElement{
constructor(){
super(a);console.log(this);
this._shadowRoot = this.attachShadow({ mode: 'open' });
this._shadowRoot.appendChild(template.content);
}
}
customElements.define("message-boxele", MessageBoxele);
Copy the code
Create a DOM structure with the createDom method of the MessageBox class, and control the display of MessageBox with the open method:
export default class MessageBox {
constructor(opts) {
let defaultOpts = {
width: "30%".height: "250px".title: "Test title".content: "Test content".dragable: true.// Whether it can be dragged
maskable: true.// Whether there is a mask
isCancel: false // Is there a cancellation
}
this.opts = Object.assign(defaultOpts, opts);
this.createDom();
}
createDom(){
let MessageBoxEle = document.createElement("message-boxele");
document.body.appendChild(MessageBoxEle);
MessageBoxEle.style.display = "none";
this.MessageBoxEle = MessageBoxEle;
}
open(){
this.MessageBoxEle.style.display = "block"; }}Copy the code
Dynamically display the customized title and content based on the configuration
Passing in the configuration through custom component properties, passing in custom properties:
createDom(){
let MessageBoxEle = document.createElement("message-boxele");
MessageBoxEle.style.display = "none";
// Pass parameter configurations inside the custom component as properties
MessageBoxEle.width=this.opts.height;
MessageBoxEle.title = this.opts.title;
MessageBoxEle.content = this.opts.content;
MessageBoxEle.isCancel = this.opts.isCancel;
this.MessageBoxEle = MessageBoxEle;
document.body.appendChild(MessageBoxEle);
}
Copy the code
Set various properties inside the messageBox custom component as follows:
set width(newValue) {
this._shadowRoot.querySelector(".k-dialog").style.width = newValue;
}
set height(newValue) {
this._shadowRoot.querySelector(".k-dialog").style.height = newValue;
}
set title(newValue) {
this._shadowRoot.querySelector(".k-title").innerHTML = newValue;
}
set content(newValue) {
this._shadowRoot.querySelector(".k-body span").innerHTML = newValue;
}
set isCancel(newValue) {
if(! newValue) {this._shadowRoot.querySelector(".k-footer").removeChild(this._shadowRoot.querySelector(".k-cancel"))}}set maskable(newValue) {
console.log(newValue);
if (newValue) {
this._shadowRoot.querySelector(".k-wrapper").style.display = "block";
} else {
this._shadowRoot.querySelector(".k-wrapper").style.display = "none"; }}Copy the code
Sets whether components can be dragged and dropped
canDragable() { let dailog = this._shadowRoot.querySelector(".k-dialog"); dailog.onmousedown = e => { let x = e.clientX - dailog.offsetLeft; let y = e.clientY - dailog.offsetTop; dailog.onmousemove = e => { let xx = e.clientX; let yy = e.clientY; dailog.style.left = xx - x + "px"; dailog.style.top = yy - y + "px"; } } document.onmouseup = function () { dailog.onmousemove = ""; } } set dragable(newValue) { if (newValue) { this.canDragable(); }}Copy the code
Finally, add the event corresponding to the component
Events can be added and triggered in observer mode. HTMLElement already inherits EventTarget so we can directly observe events and trigger events, delegating events to the K-Dailog container. As follows:
this._shadowRoot.querySelector(".k-dialog").addEventListener("click".e= > {
switch (e.target.className) {
case 'k-close':
this.dispatchEvent(new CustomEvent("close"))
break;
case 'k-cancel':
this.dispatchEvent(new CustomEvent("cancel"));
break;
case 'k-primary':
let value = this._shadowRoot.querySelector(".input-inner").value;
this.dispatchEvent(new CustomEvent("primary", {
detail: value
}));
break; }})Copy the code
Event bindings can be found in the MessageBox class:
addEvent() {
this.MessageBoxEle.addEventListener("close".e= > {
this.MessageBoxEle.style.display = "none";
})
this.MessageBoxEle.addEventListener("cancel".e= > {
this.MessageBoxEle.style.display = "none";
})
this.MessageBoxEle.addEventListener("primary".e= > {
console.log(e.detail);
this.opts.success(e.detail);
this.MessageBoxEle.style.display = "none"; })}Copy the code
At this point, a native component of the base webComponent is wrapped. The prevailing era frame is sweet, but looking back at native JS, it still solves the problem for us in most cases. All in all, js really sweet!! Everyone old iron, like to follow a wave. Thank you for giving an old iron !!!!!