作为开发人员,我们可能会遇到想要在某些外部应用程序中注入某些组件或应用程序(或应用程序的一部分)的情况。这样的组件称为小部件。小部件基本上是可以嵌入到第三方网站或您自己的网站中的组件。它们通常为用户提供第三方应用程序访问其他网站资源的权限。Google Adsense就是小部件的案例代表。有时,我们在网页上也看到了一个“聊天帮助”按钮,它也是一个小部件。
在本文中,我们将尝试制作一个小部件,该小部件将嵌入到使用Vue制作的外部应用程序中。我们也可以使用React。
因此,让我们开始吧。
开始
我知道我说过我们将要创建一个小部件,但是那个小部件将是什么呢?在开始某些事情之前,我们应该知道问题陈述是什么。我们希望在外部网站中拥有一个独立的组件,该组件允许用户与该组件进行交互并将控制权交给我们的主应用程序,而所有这些操作都无需过多地操纵外部应用程序的现有代码即可。
为了解决这个问题,我们将创建一个小部件(该术语将在整个LOT中使用)。接下来,我们需要一个场景。我们将有一个名为Geeky Glasses的外部应用程序,这是一家制造酷眼镜😎的公司的应用程序。我们的主要应用程序将是一家名为BLAH的示例电子商务公司,该公司将向用户提供一些详细信息,以方便为这些眼镜进行预预订。我们的小部件将是BLAH创建的一个小表格,并将被注入Geeky Glasses的主页中。
我们将按照以下顺序进行开发:
一. 外部网页
为简单起见,我们将创建一个简单的网页,使用纯HTML和CSS来制作此页面。让我们向页面正文中添加一些内容:
<body>
<div class="main-div">
<div class="left-panel">
<div class="main-text">
<span style="font-size: 90px; color: #099ba3;">Geeky</span>
<span style="font-size: 40px;">GLASSES</span>
<div class="subtext">Coming soon...</div>
</div>
<img src="./product.png" width="100%" />
</div>
<div class="right-panel">
<div id="widget" class="widget">
OUR WIDGET WILL BE RENDERED HERE
</div>
</div>
</div>
</body>
我们的外部网页将如下所示,并且小部件将呈现在空白区域中。
稍后,我们将在此网页中添加一个脚本,该脚本将获取我们将创建的小部件。
二. 主要应用程式
我们将设置一个Vue(或React)项目,这将是BLAH的电子商务网站,并创建一个多步骤表单,允许用户输入其个人和地址详细信息以进行Geeky Glasses的预预订。
要创建vue项目,请运行:
vue create vue-widge
选择默认或手动设置所需的功能,然后设置应用程序。现在,我们可以创建具有基本验证和成功屏幕的表单。表单的个人详细信息将使用用户将使用我们的小部件在外部应用中输入的详细信息填充。
现在看起来像这样:
好的,现在我们已经准备好外部和主应用程序。现在最有趣的部分是,我们将Geeky Glasses和BLAH连接起来。
三. 小部件
在开始实施之前,让我们了解小部件的工作方式。如前所述,我们将在外部网页中包含一个脚本,以呈现该小部件。该脚本将附加在文件的head标记中html
。该脚本实际上作为静态资产驻留在我们的主应用程序中,可以使用该应用程序的绝对URL对其进行访问。让我们在外部网页中添加脚本。
<script>
var js = document.createElement("script");
js.async = true;
// Path to the script that loads the widget
js.src = "https://vuewidget.herokuapp.com/widget-loader.js"; // This is where the app is currently hosted
document.getElementsByTagName("head")[0].appendChild(js);
</script>
在上面的代码片段中,我们可以看到它widget-loader.js
是作为静态资源访问的。这是因为我们将其保存在公用文件夹中,并且不会通过webpack。我们可以通过绝对路径引用静态资源。
来到widget-loader.js,它是一个自调用JS函数,它加载CSS文件(widget.css
)和JS文件(widget.js
)并将其分别附加到外部网页的head和script标签。JS文件将为小部件指定实际的HTML代码,而CSS文件将为其设置样式。widget-loader.js
:
// Self invoking function
(function(w, d, link, script, p) {
window.onload = function() {
// Load css
var css = "https://vuewidget.herokuapp.com/css/widget.css";
// Load js
var js = "https://vuewidget.herokuapp.com/js/widget.js";
link = d.createElement("link");
link.rel = "stylesheet";
link.href = css;
// Appending stylesheet in the head tag
d.getElementsByTagName("head")[0].appendChild(link);
script = d.createElement("script");
script.async = 1;
script.src = js;
// Adding the script in the script tag
p = d.getElementsByTagName("script")[0];
p.parentNode.insertBefore(script, p);
};
})(window, document);
请注意,widget.css
和widget.js
也被添加到公用文件夹中,并可用作静态资产,可以使用绝对路径进行引用。
Widget.js
也是一种自调用功能。我们将创建一个名为Widget的对象构造函数,它将具有以下属性:
html
代码作为字符串。containerID
和formName
。该containerID
是的标识div
将包含小部件。
<div id="widget" class="widget">
OUR WIDGET WILL BE RENDERED HERE
</div>
接下来,我们调用两个函数initializeWidget
和initializeEvents
。InitializeEvent
在的innerHTML
对象中添加html属性,containerID
并InitializeEvents
添加提交和按钮点击事件。Widget.js
看起来像这样:
(function() {
this.Widget = function() {
// HTML source code for the widget
this.html =
`<div class='container'>` +
`<div class='discount-text'>Get 10% Cashback on early bookings</div>` +
`<div class='logo'><span>b</span><span>l</span><span>a</span><span>h</span>.com</div>` +
`<form id='form' name="widget-form" action="" target="_blank" style="margin-top:5%">` +
` <div class="widget-row" >` +
` <div class="widget-cell">` +
` <label class="widget-label" id="widget-firstname-label" for="widget-firstname">Firstname</label>` +
` <input type="text" id="widget-firstname" class="widget-input" name="firstname" />` +
` </div>` +
` <div class="widget-cell">` +
` <label class="widget-label" id="widget-lastname-label" for="widget-lastname">Lastname</label>` +
` <input type="text" id="widget-lastname" class="widget-input" name="lastname" />` +
` </div>` +
` </div>` +
` <div class="widget-row">` +
` <div class="widget-cell">` +
` <label class="widget-label" id="widget-email-label" for="widget-email">Email ID</label>` +
` <input type="text" id="widget-email" class="widget-input" name="email" />` +
` </div>` +
` <div class="widget-cell">` +
` <label class="widget-label" id="widget-phone-label" for="widget-phone">Phone</label>` +
` <input type="number" id="widget-phone" class="widget-input" name="phone" />` +
` </div>` +
` </div>` +
` <button id='submit-button'>BOOK NOW</button>` +
`</form>` +
`</div>`;
var defaults = {
containerId: "widget",
formName: "widget-form",
};
this.options = defaults;
initializeWidget(this);
initialiseEvents(this);
};
function initializeWidget(self) {
var container = document.getElementById(self.options.containerId);
if (container) {
// Appending the widget html code to the block which has the id "widget" in the demo page
container.innerHTML = self.html;
}
}
function initialiseEvents(self) {
// Adding event listener to the "Book Now" button
var button = document.getElementById("submit-button");
if (button) {
button.addEventListener("click", submitClicked.bind(self));
}
var form = document.getElementById("form");
if (form) {
form.addEventListener("submit", submitClicked.bind(self));
}
}
function validateFields(field, value) {
var regex = /[a-z]{2,}/i;
// Validate first and last name
if (field === "firstname" || field === "lastname") {
return regex.test(value);
}
// Validate email address
if (field === "email") {
regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regex.test(value);
}
// Validate phone number
if (field === "phone") {
regex = /^[0-9]{10}$/;
return regex.test(value.toString());
}
}
function submitClicked(event) {
if (event) {
event.preventDefault();
}
var fields = ["firstname", "lastname", "email", "phone"],
form = document.forms[this.options.formName],
field,
label;
for (var i = 0; i < fields.length; i++) {
field = form[fields[i]];
field.className = field.className.replace(" widget-error", "");
label = document.getElementById("widget-" + fields[i] + "-label")
.innerText;
if (!field.value) {
field.className += " widget-error";
alert("Please enter your " + label);
field.focus();
return false;
}
if (!validateFields(fields[i], field.value)) {
field.className += " blinker-error";
alert("Please enter a valid " + label);
field.focus();
return;
}
}
// Opening the vue app with all the details
window.open(
`https://vuewidget.herokuapp.com/?firstname=${form["firstname"].value}&lastname=${form["lastname"].value}&email=${form["email"].value}&phone=${form["phone"].value}`,
);
}
})();
new Widget({});
成果
我们可以看到外部网页现在在空白区域中呈现了小部件。我们可以输入我们的详细信息,然后单击提交按钮。它会将我们重定向到主应用,并预先填写了一些字段。