如何創(chuàng)建與框架無關的JavaScript插件
JavaScript中的插件使我們能夠擴展語言,以實現(xiàn)所需的某些強大(或不夠強大)的功能。插件/庫本質上是打包的代碼,可以使我們免于一遍又一遍地編寫相同的東西(功能)。
在JavaScript生態(tài)系統(tǒng)中,有數(shù)百個框架,這些框架中的每一個都為我們提供了一個創(chuàng)建插件的系統(tǒng),以便為框架添加新的東西。
如果你看一下NPM注冊表,幾乎所有的JavaScript插件都是在那里發(fā)布的,你會看到有超過一百萬個插件以簡單庫和框架的形式發(fā)布。
為每個框架創(chuàng)建插件的方式可能會有很大不同。例如,Vue.js有自己的插件系統(tǒng),這與你為React.js創(chuàng)建插件的方式不同。然而,這一切都歸結為相同的JavaScript代碼。
因此,使用普通JavaScript創(chuàng)建插件,讓你有能力創(chuàng)建一個無論在哪個框架下都能使用。
“與框架無關的JavaScript插件是無需框架上下文即可工作的插件,您可以在任何框架(甚至沒有框架)中使用插件”
構建庫時要記住的事項
- 您應該對插件有一個目標——這是插件要實現(xiàn)的關鍵
- 您的插件應易于使用以達到預期用途
- 您的插件應在很大程度上可定制
- 您的插件應該有一個文檔,指導將要使用該插件的開發(fā)人員
現(xiàn)在,讓我們著眼于上述幾點。
我們將創(chuàng)造什么
在本文中,我將向您展示如何創(chuàng)建與框架無關的插件。在本教程中,我們將創(chuàng)建一個旋轉木馬/幻燈片插件——該插件的目標。
這個插件會暴露一些方法,可以被插件的用戶調用 .next() 和 .prev()
起步
讓我們創(chuàng)建一個新的文件夾來存放我們的插件代碼和任何其他必要的文件,我將把我的文件夾稱為 TooSlidePlugin。
在這個文件夾中,在你喜歡的編輯器中創(chuàng)建一個新的JavaScript文件。這個文件將包含插件的代碼,我把我的文件叫做 tooSlide.js。
有時,我什至想像在開始創(chuàng)建插件之前(從最終開發(fā)人員的角度)如何使用插件。
- var slider = new ToolSidePlugin({
- slideClass: ".singleSlide",
- container: ".slideContainer",
- nextButton: ".next",
- previousButton: ".prev"
- })
上面的代碼假定有一個名為 TooSlides 的構造函數(shù),該構造函數(shù)接收具有某些屬性的對象作為參數(shù)。對象的屬性是 slidesClass、container、nextButton 和 previousButton,這些是我們希望用戶能夠自定義的屬性。
我們將從將插件創(chuàng)建為單個構造函數(shù)開始,以便其本身具有名稱空間。
- function ToolSidePlugin() {
- }
Options
由于我們的插件,TooSlides 需要一個選項參數(shù),所以我們會定義一些默認的屬性,這樣如果我們的用戶沒有指定自己的屬性,就會使用默認的屬性。
- function ToolSidePlugin(options) {
- let defaultOptions = {
- slideClass: ".singleSlide",
- container: ".slideContainer",
- nextButton: ".nextSlide",
- previousButton: ".previousSlide"
- }
- options = { ...defaultOptions, ...options};
- let _this = this;
- let slides = [];
- let currentSlideIdex = 0;
- }
我們創(chuàng)建了一個 defaultOptions 對象來保存一些屬性,我們還使用JavaScript擴展操作符將傳入的選項與默認選項合并。我們將 this 分配給另一個變量,因此我們?nèi)匀豢梢栽趦?nèi)部函數(shù)中對其進行訪問。
我們還創(chuàng)建了兩個變量 slides,它將保存所有我們想要用作幻燈片的圖片,以及 currentSlideIndex,它保存當前正在顯示的幻燈片的索引。
接下來,由于我們的幻燈片需要有一些控制,可以用來向前和向后移動幻燈片,我們將在構造函數(shù)中添加下面的方法。
- this.prepareControls = function() {
- const nextButton = document.createElement("button");
- const previousButton = document.createElement("button");
- nextButton.setAttribute("class", "next");
- nextButton.InnerHTML = "Next";
- previousButton.setAttribute("class", "prev");
- nextButton.InnerHTML = "Prev";
- let controleContainer = document.createElement("div");
- controleContainer.setAttribute("class", "too-slide-control-container");
- controleContainer.appendChild(previousButton);
- controleContainer.appendChild(nextButton);
- document.querySelector(options.container).appendChild(controleContainer);
- nextButton.addEventListener("click", function() {
- _this.next();
- });
- previousButton.addEventListener("click", function() {
- _this.previous();
- });
- }
在 .prepareControls() 方法中,我們創(chuàng)建了一個容器DOM元素來保存控件按鈕,我們自己創(chuàng)建了下一個和上一個按鈕,并將它們附加到 controlContainer。
然后我們給兩個按鈕附加事件監(jiān)聽器,分別調用 .next() 和 .previous() 方法。別擔心,我們很快就會創(chuàng)建這些方法。
接下來,我們將添加兩個方法:.goToSlide()和 .hideOtherSlides()。
- this.goToSlide = function(index) {
- this.hideOtherSlides();
- if(index > slides.length - 1) {
- index = 0;
- }
- if(index < 0) {
- index = slides.length - 1;
- }
- slides[index].style = "display:block";
- currentSlideIndex = index;
- }
- this.hideOtherSlides = function() {
- document.querySelectorAll(options.slidesClass).forEach((slide, index) => {
- slides[index].style = "display: none"
- })
- }
.goToSlide() 方法采用參數(shù) index,這是我們要顯示的幻燈片的索引,此方法首先隱藏當前正在顯示的任何幻燈片,然后僅顯示我們要顯示的幻燈片。
接下來,我們將添加 .next() 和 .previous() 輔助方法,分別幫助我們向前一步,或者向后一步(還記得我們之前附加的事件監(jiān)聽器嗎?
- this.next = function() {
- this.goToSlide(currentSlideIndex + 1);
- }
- this.previous = function() {
- this.goToSlide(currentSlideIndex - 1);
- }
這兩個方法基本上調用 .goToSlide() 方法,并將 currentSlideIndex 移動 1。
現(xiàn)在,我們還將創(chuàng)建一個 .init() 方法,該方法將在實例化構造函數(shù)時幫助我們進行設置。
- this.init = function() {
- document.querySelectorAll(options.container).className += " too-slide-slider-container";
- document.querySelectorAll(options.slidesClass).forEach((slide, index) => {
- slides[index] = index;
- slides[index].style = "display:none";
- slides[index].className = " too-slide-single-slide too-slide-fade";
- });
- this.goToSlide(0);
- this.prepareControls();
- }
如您所見,.init() 方法獲取所有幻燈片圖像并將其存儲在我們之前聲明的 slides 數(shù)組中,并默認隱藏所有圖像。
然后,它通過調用 .goToSlide(0) 方法顯示幻燈片中的第一張圖像,并且還通過調用 .prepareControls() 設置我們的控制按鈕。
為了收尾我們的構造函數(shù)代碼,我們將在構造函數(shù)中調用 .init() 方法,這樣每當構造函數(shù)被初始化時,.init() 方法總是被調用。
最終代碼如下所示:
- function ToolSidePlugin(options) {
- let defaultOptions = {
- slidesClass: ".singleSlide",
- container: ".slideContainer",
- nextButton: ".nextSlide",
- previousButton: ".previousSlide"
- }
- options = { ...defaultOptions, ...options};
- let _this = this;
- let slides = [];
- let currentSlideIdex = 0;
- this.init = function() {
- document.querySelectorAll(options.container).className += " too-slide-slider-container";
- document.querySelectorAll(options.slidesClass).forEach((slide, index) => {
- slides[index] = index;
- slides[index].style = "display:none";
- slides[index].className = " too-slide-single-slide too-slide-fade";
- });
- this.goToSlide(0);
- this.prepareControls();
- }
- this.prepareControls = function() {
- const nextButton = document.createElement("button");
- const previousButton = document.createElement("button");
- nextButton.setAttribute("class", "next");
- nextButton.InnerHTML = "Next";
- previousButton.setAttribute("class", "prev");
- nextButton.InnerHTML = "Prev";
- let controleContainer = document.createElement("div");
- controleContainer.setAttribute("class", "too-slide-control-container");
- controleContainer.appendChild(previousButton);
- controleContainer.appendChild(nextButton);
- document.querySelector(options.container).appendChild(controleContainer);
- nextButton.addEventListener("click", function() {
- _this.next();
- });
- previousButton.addEventListener("click", function() {
- _this.previous();
- });
- }
- this.goToSlide = function(index) {
- this.hideOtherSlides();
- if(index > slides.length - 1) {
- index = 0;
- }
- if(index < 0) {
- index = slides.length - 1;
- }
- slides[index].style = "display:block";
- currentSlideIndex = index;
- }
- this.hideOtherSlides = function() {
- document.querySelectorAll(options.slidesClass).forEach((slide, index) => {
- slides[index].style = "display: none"
- })
- }
- this.next = function() {
- this.goToSlide(currentSlideIndex + 1);
- }
- this.previous = function() {
- this.goToSlide(currentSlideIndex - 1);
- }
- this.init();
- }
添加CSS
在存放我們的插件項目的文件夾中,我們將添加一個CSS文件,其中包含我們的滑塊的基本樣式。我將把這個文件稱為 tooSlide.css。
- * {box-sizing: border-box}
- body {font-family: Verdana, sans-serif; margin:0; padding: 30px;}
- .too-slide-single-slide {
- display: none;
- max-height: 100%;
- width: 100%;
- }
- .too-slide-single-slide img{
- height: 100%;
- width: 100%;
- }
- img {vertical-align: middle;}
- /* Slideshow container */
- .too-slide-slider-container {
- width: 100%;
- max-width: 100%;
- position: relative;
- margin: auto;
- height: 400px;
- }
- .prev, .next {
- cursor: pointer;
- position: absolute;
- top: 50%;
- width: auto;
- padding: 10px;
- margin-right: 15px;
- margin-left: 15px;
- margin-top: -22px;
- color: white;
- font-weight: bold;
- font-size: 18px;
- transition: 0.6s ease;
- border-radius: 0 3px 3px 0;
- user-select: none;
- border-style: unset;
- background-color: blue;
- }
- .next {
- right: 0;
- border-radius: 3px 0 0 3px;
- }
- .prev:hover, .next:hover {
- background-color: rgba(0,0,0,0.8);
- }
- .too-slide-fade {
- -webkit-animation-name: too-slide-fade;
- -webkit-animation-duration: 1.5s;
- animation-name: too-slide-fade;
- animation-duration: 1.5s;
- }
- @-webkit-keyframes too-slide-fade {
- from {opacity: .4}
- to {opacity: 1}
- }
- @keyframes too-slide-fade {
- from {opacity: .4}
- to {opacity: 1}
- }
- /* On smaller screens, decrease text size */
- @media only screen and (max-width: 300px) {
- .prev, .next,.text {font-size: 11px}
- }
測試我們的插件
為了測試我們的插件,我們將創(chuàng)建一個HTML文件,我將其命名為 index.html。我們還將添加4張圖片用作幻燈片,所有圖片都與我們的插件JavaScript代碼位于同一目錄中。
我的HTML文件如下所示:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>測試幻燈片</title>
- <link rel="stylesheet" href="tooSlide.css">
- </head>
- <body>
- <div class="contaoner">
- <div class="slideContainer">
- <div class="singleSlide"><img src="slide1.png" alt="slide1" class="slideImage"></div>
- <div class="singleSlide"><img src="slide2.png" alt="slide2" class="slideImage"></div>
- <div class="singleSlide"><img src="slide3.png" alt="slide3" class="slideImage"></div>
- <div class="singleSlide"><img src="slide4.png" alt="slide4" class="slideImage"></div>
- </div>
- </div>
- <script src="tooSlide.js"></script>
- <script>
- var slider = new ToolSidePlugin({
- slideClass: ".singleSlide",
- container: ".slideContainer",
- nextButton: ".next",
- previousButton: ".prev"
- })
- </script>
- </body>
- </html>
在HTML文件的頭部分,我調用了 tooSlide.css 文件,而在文件的末尾,我調用了 tooSlide.js 文件。
完成此操作后,我們將能夠實例化我們的插件構造函數(shù):
- var slider = new ToolSidePlugin({
- slideClass: ".singleSlide",
- container: ".slideContainer",
- nextButton: ".next",
- previousButton: ".prev"
- })
最后的效果:
為您的JavaScript項目編寫文檔
文檔是你教人們?nèi)绾问褂媚愕牟寮?。因此,它需要你花一些心思?/p>
對于我們新創(chuàng)建的插件,我將從在項目目錄中創(chuàng)建README.md文件開始。
發(fā)布你的插件
在編寫了您的插件之后,您很可能希望其他開發(fā)人員從您的新創(chuàng)建中受益,因此我將向您展示如何做到這一點。
你可以通過兩種方式讓你的插件對其他人可用:
- 在Github上托管它,當您這樣做時,任何人都可以下載倉庫,包括文件(.js和.css),并立即使用您的插件。
- 發(fā)布在npm上,請查看官方的npm文檔來指導您。
就是這樣。
結束
在本文中,我們構建了一個插件來完成一件事:幻燈片圖像。
它也是零依賴的,現(xiàn)在我們可以開始用我們的代碼來幫助別人,就像我們也得到了幫助一樣。
該插件教程的代碼可在我的github上找到:https://github.com/dunizb/CodeTest/tree/master/JavaScript/TooSlidePlugin
原文:https://blog.logrocket.com/how-to-create-a-framework-agnostic-javascript-plugin/
作者:Sodeeq Elusoji
本文轉載自微信公眾號「前端全棧開發(fā)者」,可以通過以下二維碼關注。轉載本文請聯(lián)系前端全棧開發(fā)者公眾號。