shopify主题开发
There are certain things that do not change over time: all dev tutorials start with a “hello world” example, junior developers want senior salaries and the world of e-commerce stands on the shoulders of JQuery.
有些事情并不会随着时间的推移而改变:所有开发教程都以“ hello world”示例开始,初级开发人员想要高级薪水,而电子商务世界站在JQuery的肩膀上。
Some time ago my employer decided to start using Shopify as a new e-commerce platform for our existing and new e-shop projects. “OK for me”, I thought, probably, it’s a good chance to learn something new, practice React or Vue and add several fancy words to my CV. Bring it on! But, as you might have guessed, few moments later life gave me a good lesson not to take anything for granted.
前一段时间,我的雇主决定开始将Shopify用作我们现有和新的电子商店项目的新电子商务平台。 我想,“对我来说还行”,这可能是学习新知识,练习React或Vue并在我的简历中添加一些花哨单词的好机会。 来吧! 但是,正如您可能已经猜到的那样,过了一会儿,生活给了我一个很好的教训,不要把任何事情视为理所当然。
As a good mannered developer I started to explore the new platform by reading documentation and breaking apart available free themes. Little by little I opened to myself the “wonderful” world of SaaS with its upsides and downsides. With that being said I leave you the choice to scroll down the page until code examples or to stay here with me and read some of my observations about Shopify as a platform for theme developers.
作为一个有礼貌的开发人员,我开始通过阅读文档并分解可用的免费主题来探索新平台。 我一点一点地向自己敞开了SaaS的“奇妙”世界的面,它有其利弊。 话虽这么说,我让您可以选择向下滚动页面直到找到代码示例,或者与我呆在一起,阅读我对Shopify作为主题开发者平台的一些看法。
The first thing that got my attention was data management which was absolutely different to what I used to work with before. There is no direct access to all the data on your site so you can not create a full back up or roll back to a previous version in case of any trouble. Also you no longer have exclusive rights on site’s data. You must give your consent to share some part of it with third parties like app developers when you want to add non default functionality to your site. And you should do it quite often as the default configuration is limited to a bare minimum. That’s very reassuring right? Definitely not for me but a SaaS platform comes as a package and you either take it or leave it. I guess we should take it and move on to the bright side.
引起我注意的第一件事是数据管理,它与我以前使用的完全不同。 无法直接访问您站点上的所有数据,因此如果遇到任何麻烦,您将无法创建完整备份或回滚到以前的版本。 此外,您不再拥有站点数据的专有权。 当您要向网站添加非默认功能时,必须同意与应用程序开发人员之类的第三方共享其中的一部分。 而且您应该经常这样做,因为默认配置被限制为最低限度。 那很让人放心吧? 绝对不适合我,但SaaS平台是作为一个软件包提供的,您可以选择使用它或离开它。 我想我们应该接受它,继续前进。
What does Shopify have to offer to a front-end developer? For me the most valuable asset is a front-end API. It gives us an opportunity to build nice, clean, dynamic interfaces and helps us to deliver exactly what customers want at the right place and at the right time. Shopify does have a large API ecosystem that reaches every single piece of data that you may need to create your site or application. API comes in two major groups: “admin API” for back-end changes and app development plus “storefront API” to use on the client side. These groups are represented in three flavours: AJAX, REST, GraphQL. At first glance everything looks very promising. Right? With these API at hand there is no visible constrains on what can be done on the front-end, thus we should observe a considerable number of Shopify sites based on popular js frameworks. But wait a minute. You’ve probably already guessed that this is not the case.
Shopify必须为前端开发人员提供什么? 对我而言,最有价值的资产是前端API。 它使我们有机会构建美观,干净,动态的界面,并帮助我们在正确的位置和正确的时间准确交付客户想要的东西。 Shopify确实有一个庞大的API生态系统,可以覆盖创建站点或应用程序可能需要的每条数据。 API分为两大类:用于后端更改和应用程序开发的“管理员API”以及要在客户端使用的“店面API”。 这些组以三种形式表示:AJAX,REST,GraphQL。 乍看起来,一切看起来都非常美好。 对? 有了这些API,在前端可以做什么方面就没有明显的限制,因此我们应该观察到大量基于流行js框架的Shopify网站。 等一下 您可能已经猜到情况并非如此。
If you randomly pick any Shopify store, open developer console and type “JQuery” there is a good chance that you find this object defined.
如果您随机选择任何Shopify商店,请打开开发人员控制台并键入“ JQuery”,则很有可能会找到定义的对象。

Hence the site one way or another uses JQuery library under the hood. Don’t you find this odd? How could that happen that the Shopify community has made a choice in favor of an obsolete js library? You may not like the answer.
因此,该站点以某种方式在后台使用JQuery库。 你不觉得奇怪吗? Shopify社区如何选择支持过时的js库,这怎么可能呢? 您可能不喜欢答案。
为什么选择JQuery? (Why JQuery?)
At the beginning of the 20th century Ford Motor Company made a car available for masses. Since then the car ceased to be a toy in hands of rich enthusiasts and became a tool, an instrument to do business and make money. Of cause those cars had a simple construction, were made of cheap materials and were not very reliable but that was a trade off to make them affordable and produce in large quantities. Also don’t forget that an average driver should be able to handle this beast without much problems as well as a mechanic should not struggle a lot while fixing it in the middle of a corn field.
20世纪初,福特汽车公司向大众提供了一辆汽车。 从那时起,这辆车就不再是富裕发烧友手中的玩具,而成为一种做生意和赚钱的工具。 原因是这些汽车的结构简单,用便宜的材料制成并且不是很可靠,但这是要使它们能够负担得起并大量生产的折衷方案。 同样不要忘记,普通驾驶员应该能够在没有太多问题的情况下处理这头野兽,并且机械师在将其固定在玉米田中时也不会费劲。
Great minds think alike. Looks like Shopify has chosen a similar path. It offers fully functional ready to use e-shop to everybody. A shop owner may not have any special knowledge or expertise in IT, all he should do is tweak a few settings here and there and he is good to go. But what if something goes wrong or does not work right in the “car”? The shop owner pulls over, lifts the hood to check the engine, looks aimlessly for a couple of seconds, realizes that he has not a slightest idea how it works and then starts to squeeze and pull different tubes and wires at random hoping that it will help get the car fixed. A driver who does not know how to fix a car can do more harm rather than fix anything.
英雄所见略同。 看起来Shopify选择了类似的路径。 它为所有人提供了功能齐全的随时可以使用的网上商店。 店主可能没有IT方面的专门知识或专业知识,他所要做的只是在这里和那里调整一些设置,他很乐意去做。 但是,如果出现问题或在“汽车”中无法正常工作怎么办? 店主停下来,抬起引擎盖检查发动机,几秒钟漫不经心地看了一下,意识到自己丝毫不知道它是如何工作的,然后开始随机挤压并拉动不同的管子和电线,希望它会帮助修理汽车。 不知道如何修理汽车的驾驶员可能造成更大的伤害,而不是修理任何东西。
So Shopify stepped up with a couple of protective measures — a rigid template system based on a proprietary language called Liquid with a web editor to work with the code from an administration panel. By the way Liquid is very similar to Twig. You may be familiar with it if you worked with Drupal for example. So with these measures in place, if the shop owner needs to fix something, he may access the code via a web editor and modify it. This time if something goes wrong, just use built-in version control feature and roll back to a previous file version. Quite simple. Obviously this is not a very convenient way to work with the code all the time and more complex cases require a dev environment on a local machine but in most simple cases a web editor is more than enough. Thus we came to the first major reason to favour JQuery.
因此,Shopify采取了一些保护措施,即基于名为Liquid的专有语言的刚性模板系统,以及一个Web编辑器来使用管理面板中的代码。 顺便说一下,Liquid与Twig非常相似。 例如,如果您与Drupal合作,您可能会熟悉它。 因此,采取这些措施后,如果店主需要修理某些东西,他可以通过网络编辑器访问代码并进行修改。 这次如果出现问题,只需使用内置的版本控制功能并回滚到以前的文件版本。 非常简单。 显然,这并不是一直使用代码的便捷方式,更复杂的情况需要在本地计算机上使用开发环境,但在大多数简单情况下,Web编辑器绰绰有余。 因此,我们得出了支持JQuery的第一个主要原因。
Excessive code exposure
过多的代码暴露
This means that any employee with access to the admin panel is able to change the code on the live site without notifying anybody. Say a content manager may change a home page structure and no one will be aware of this until somebody opens that page and notices that something has been changed. I’m sure such feature was added with good intentions but do you know where best intentions lead us to? Right…
这意味着任何有权访问管理面板的员工都可以在不通知任何人的情况下更改实时站点上的代码。 假设内容管理员可能会更改主页结构,并且直到有人打开该页面并注意到某些内容已被更改之前,没人会意识到这一点。 我确定这种功能是出于良好的意愿而添加的,但是您知道最佳意图将我们引向何处吗? 对…
Code exposure has several implications on the js side: if you want to change the code in the web editor, it should not be compiled. Therefore many theme developers don’t consider using ES6 and webpack perks for theirs projects.
代码暴露在js方面有几个含义: 如果要在Web编辑器中更改代码,则不应对其进行编译。 因此,许多主题开发人员不考虑在其项目中使用ES6和Webpack特权。
The second major reason to go for JQuery is:
选择JQuery的第二个主要原因是:
Ambiguous / not clear documentation
模棱两可/文档不清晰
If you ever decide to create a new theme from scratch, think twice because it’s going to cost you a lot of time and nerves. When I was reading theme documentation, I could not get rid off a feeling that it was there not to help me understand how everything works and how internal components are connected to each other, but to give me an impression of what I may find in the theme as a developer without any detailed explanation why it has been done so. It’s like if we agreed that a car should have four wheels to run and one engine to spin them but it’s not said where and how the wheels should be attached to the car. As long as the vehicle is able to move forward, everything is fine. For example, the entire JS coverage is limited to three or four JS section functions, plus there was some information about where we can find JS files in a theme directory and how to add JS code in sections and templates. In somebody’s opinion that should be quite enough to create your own theme. But that’s not the case, because you can’t build a house without a clear blueprint. That’s why sometimes it is more reasonable to find a theme which is very close to what you are looking for and then modify it. Most likely such theme will be based on JQuery.
如果您决定从头开始创建新主题,请三思而后行,因为这将花费您大量的时间和精力。 当我阅读主题文档时,我无法摆脱一种感觉,那就是那不是在帮助我理解一切如何工作以及内部组件如何相互连接,而是让我印象深刻。作为开发人员的主题,但没有做任何详细解释。 这就好比我们是否同意一辆汽车应该有四个车轮要运转,而一个发动机要使它们旋转时,却没有说车轮应在何处以及如何连接到汽车上。 只要车辆能够向前行驶,一切都很好。 例如,整个JS覆盖范围仅限于三个或四个JS区域功能,此外还有一些有关我们可以在主题目录中找到JS文件以及如何在区域和模板中添加JS代码的信息。 在某些人看来,这足以创建您自己的主题。 但是事实并非如此,因为没有清晰的蓝图就无法盖房子。 这就是为什么有时找到与您要查找的主题非常接近的主题然后进行修改更为合理的原因。 这种主题很可能将基于JQuery。
The third major reason and at the same time the pillar-stone of the whole Shopify economy is:
同时也是整个Shopify经济的基石的第三个主要原因是:
Marketplace
市场
Sooner or later a shop owner comes to the conclusion that basic configuration is not enough and that it would be nice to have more features like: AJAX shopping cart, user-friendly filters at the collection pages, enhanced analytics, sale management system, wishlist, social share buttons, Instagram integration etc. In that case all the roads lead to the marketplace where thousands of developers are glad to sell their solutions or expertise to help out our guy with his problem. Developers create extra functionality in the form of applications. Some of them may inject their own Liquid, js or css code into the theme files to make a new feature available for an end user. That gives us another constraint on js side: in order to use applications we should not generate entire DOM structure with js. Otherwise we lose our advantage over other shops as it’s more beneficial to pay a reasonable amount of money and have the feature right away than pay a higher price for a custom solution with unclear time of delivery.
商店老板迟早得出的结论是,基本配置还不够,可以拥有更多功能,例如:AJAX购物车,收集页面上的用户友好过滤器,增强型分析,销售管理系统,愿望清单,在这种情况下,所有的道路都通向市场,成千上万的开发人员很高兴出售他们的解决方案或专业知识,以帮助我们解决这个问题。 开发人员以应用程序的形式创建额外的功能。 他们中的一些人可能会将自己的Liquid,js或css代码注入主题文件,以使新功能可供最终用户使用。 这给我们带来了js方面的另一个约束: 为了使用应用程序,我们不应该使用js生成整个DOM结构 。 否则,我们将失去与其他商店的竞争优势,因为与支付时间不明确的定制解决方案相比,支付合理数量的钱并立即拥有该功能比支付更高的价格更有利。
Let’s sum up our observations and then pass to the next chapter where I explain the cases where Vue.js may be a better alternative to JQuery.
让我们总结一下我们的观察,然后转到下一章,在其中解释Vue.js可能是JQuery更好的替代方案的情况。
List of js constraints:
js约束列表:
js code should not be compiled
js代码不应该编译
the entire DOM structure should not be generated with js
整个DOM结构不应该用js生成
These constraints are hard to digest for any mainstream js framework but if we push these boundaries to some limits, we can find edge cases where the above-mentioned constraints will be turned in our favour. To make a quick assessment of your particular situation consider the following questions:
对于任何主流js框架来说,这些约束都是很难理解的,但是如果我们将这些边界推到某个极限,我们会发现一些极端的情况,这些约束将对我们有利。 为了快速评估您的特定情况,请考虑以下问题:
- do you maintain more than 3 shops? 您是否拥有3家以上的商店?
- can you afford a custom theme development? 您可以负担得起自定义主题开发吗?
- is a site’s speed a priority for you? 网站的速度是您的优先事项吗?
- do you have a js developer in your team? 您的团队中有js开发人员吗?
- is it ok for you that some applications may not be compatible with your theme? 您可以确定某些应用程序可能与您的主题不兼容吗?
If the answer is ‘yes’ to all questions — welcome on board! For those who did not qualify I hope the rest of the article will also be interesting. It may give you some thoughts about alternatives to JQuery.
如果对所有问题的回答都是“是”,欢迎光临! 对于那些没有资格的人,我希望本文的其余部分也会很有趣。 它可能会给您一些有关JQuery替代方案的想法。
为什么选择Vue.js? (Why Vue.js?)
Before I answer this question, it would be better to ask another one. Why should we bother to replace JQuery with something else? Simple answer is: it became obsolete long time ago, but developers continue to use it by inertia as many popular JS libraries depend on it. At some point it should be stopped to prevent us from falling into a vicious cycle when there is no other choice but JQuery. Switching to native JS is not as difficult as it may seem. For most simple use-cases there is an equivalent function in “vanilla js” though the syntax may be a bit different. Don’t worry, you will get used to it in no time.
在我回答这个问题之前,最好再问一个问题。 我们为什么要麻烦用其他东西代替JQuery? 一个简单的答案是:它很久以前就已经过时了,但是由于许多流行的JS库都依赖它,因此开发人员继续惯用它。 在某些时候,应该停止它,以防止我们在JQuery别无选择的情况下陷入恶性循环。 切换到本地JS并不像看起来那样困难。 对于大多数简单用例,“ vanilla js”中都有一个等效函数,尽管语法可能有所不同。 不用担心,您会很快适应它。
Every time we call JS API via JQuery we pass by an extra layer of code and that impacts site’s performance because of CPU overhead and memory usage.
每次我们通过JQuery调用JS API时,我们都会经过一层额外的代码,由于CPU开销和内存使用情况,这会影响网站的性能。

It’s probably not a big deal for desktop users, but think about mobile users who do not have either fast CPU or stable broadband connection, nor unlimited power supply.
对于台式机用户来说,这可能不是什么大问题,但请考虑没有快速CPU或稳定宽带连接,也没有无限电源的移动用户。
Imagine that we have already reduced CPU usage and made our js code base a bit lighter by switching to native JS. Can we improve this even more? Yes, we can :) We can reduce the size of the document that the browser downloads on each page request and we can generate device specific DOM structure on the fly. That means that mobile users will get a lightweight document optimized for their device.
想象一下,我们已经减少了CPU使用率,并通过切换到本机JS使我们的js代码库更轻了。 我们可以进一步改善吗? 是的,我们可以:)我们可以减少浏览器在每个页面请求上下载的文档的大小,并且可以动态生成特定于设备的DOM结构。 这意味着移动用户将获得针对其设备优化的轻量级文档。
All this is possible with the help of one of the popular front-end frameworks such as Angular, React or Vue. Let’s have a look at what happens when we open any page in our shop.
借助Angular,React或Vue等流行的前端框架之一,所有这些都是可能的。 让我们看看打开商店中的任何页面时会发生什么。

The first thing that catches attention is the proportion between the initial DOM content size and the total size of js files. In case of frameworks usage (on the left) the resulting js size is noticeably bigger than the JQuery version (on the right) but the initial DOM structure is rather small as it contains a bare minimum of elements which represent a page structure without any details. Those details are being added to DOM later by js framework. That also explains why js files on the left side are larger — they contain a portion of the DOM structure missing in the initial DOM version. In other words we store a part of the DOM structure in js files. Is it the right thing to do? Let’s look at what happens during second page load.
引起注意的第一件事是初始DOM内容大小与js文件总大小之间的比例。 在使用框架的情况下(左侧),所产生的js大小明显大于JQuery版本(在右侧),但是初始DOM结构相当小,因为它只包含表示页面结构的最少元素,没有任何细节。 这些细节稍后将由js框架添加到DOM中。 这也解释了为什么左侧的js文件更大-它们包含初始DOM版本中缺少的DOM结构的一部分。 换句话说,我们将DOM结构的一部分存储在js文件中。 这是正确的做法吗? 让我们看看第二页加载期间发生了什么。

Well, that looks much more promising. Once we have our js in the cache we reduce traffic consumption to the minimum. From now on we can adapt the DOM structure to a particular device we’ve opened this site on. If one element on the page has a different DOM structure for desktop and mobile versions, there is no more need to load them both and hide one of them later as we did with JQuery approach. We just add the right version of the element and keep the DOM structure as slim as it may be.
好吧,这看起来更有希望。 将我们的js放入缓存后,我们便可以将流量消耗降至最低。 从现在开始,我们可以使DOM结构适应于打开此站点的特定设备。 如果页面上的一个元素在台式机和移动版中具有不同的DOM结构,则不再需要像在JQuery方法中那样同时加载它们和隐藏其中一个。 我们只是添加了正确的元素版本,并保持DOM结构尽可能细。
It’s nice to have js files in the cache but what if we change something in the code? In that case we will have to reload the entire js bundle. Taking into account its size, this is not a pleasant perspective. A famous rule “divide and conquer” comes to the rescue.
缓存中包含js文件很好,但是如果我们更改代码中的内容怎么办? 在这种情况下,我们将不得不重新加载整个js包。 考虑到它的大小,这不是令人愉快的观点。 一项著名的“分而治之”法则应运而生。

In general any js code consists of two main components: your custom code marked as yellow and its dependencies marked as green. When we add a new functionality or fix a bug, we make changes in the custom code only. So it would be reasonable to divide our js bundle in two parts and load them independently. Thus if we change something in the code, we will reload only a portion that was affected by these changes, leaving all the dependencies untouched in the cache. Also this pattern reduces loading time during the first page load as Shopify supports HTTP/2 protocol and loads multiple files in parallel.
通常,任何js代码都由两个主要组件组成:标记为黄色的自定义代码和标记为绿色的依赖项。 当我们添加新功能或修复错误时,仅在自定义代码中进行更改。 因此,将我们的js包分为两部分并分别加载它们是合理的。 因此,如果我们更改代码中的某些内容,我们将仅重新加载受这些更改影响的部分,而在缓存中保留所有相关性。 另外,由于Shopify支持HTTP / 2协议并并行加载多个文件,因此该模式减少了首页加载期间的加载时间。
At this point we had a quick glance at a conceptual difference between modern js frameworks and a classic approach with JQuery or native js. It’s time to say why I have picked Vue.js among other alternatives.
此时,我们快速浏览了现代js框架与JQuery或本机js的经典方法之间的概念差异。 现在该说为什么我在其他选择中选择了Vue.js。
I would like to have a small, compact and well supported library with a clear code structure and fast learning curve to make the transition from JQuery or native js to a new framework as painless as possible. It is also worth mentioning that I narrowed the list of candidates to three well-known frameworks which are supported by a large community of developers and would not disappear from the radars in the near future: Angular, React and Vue.
我希望有一个小巧,紧凑且受良好支持的库,具有清晰的代码结构和快速的学习曲线,以使从JQuery或本机js到新框架的过渡尽可能轻松。 还值得一提的是,我将候选人的范围缩小到三个知名框架,这些框架得到了大型开发人员社区的支持,并且在不久的将来不会从雷达中消失:Angular,React和Vue。

We can see from the table above that Angular is not an option due to its complexity and overwhelming size. React looks much better but not good enough. It is 30% heavier than Vue and uses JSX syntax for templates, which sets the bar of entry requirements for developers a bit higher. The winner is Vue. It’s compact, it has familiar HTML syntax for templates and it allows you to do pretty much the same things that you would do with Angular or React. Here are two Vue features that will be very useful for theme developers:
从上表我们可以看到,由于Angular的复杂性和庞大的规模,它不是一个选择。 React看起来好多了,但还不够好。 它比Vue重30%,并使用JSX语法作为模板,这对开发人员设置了更高的入门要求。 胜利者是Vue。 它结构紧凑,具有熟悉的模板HTML语法,并且允许您执行与Angular或React几乎相同的操作。 这是两个Vue功能,对于主题开发人员将非常有用:
- single file components 单个文件组件
- declarative rendering 陈述式渲染
The first feature will help us to create embedded apps inside liquid templates. The second feature will be of help if you want to run a quick test of some functionality or idea without setting up a new working environment. Just open codepen or jsfiddle, and you are good to go. No compilation is needed.
第一个功能将帮助我们在液体模板中创建嵌入式应用程序。 如果您想对某些功能或想法进行快速测试而无需设置新的工作环境,则第二个功能将很有帮助。 只需打开codepen或jsfiddle,就可以了。 无需编译。
让我们编码吧! (Let’s code!)
I assume that Node.js and Yarn have already been installed on your machine. If not, it’s a good time to install them. Also you should be familiar with the Shopify theme structure. We will use Gulp to watch for changes in the source files then to compile them with the help of webpack and finally to deploy them to the Shopify server.
我假设您的计算机上已经安装了Node.js和Yarn 。 如果没有,现在是安装它们的好时机。 另外,您应该熟悉Shopify主题结构。 我们将使用Gulp监视源文件中的更改,然后在webpack的帮助下对其进行编译,最后将它们部署到Shopify服务器。
To make my task a bit easier I will take a free Shopify theme “Debut” and then step by step I will explain how to add Vue components into it. For those who don’t want to waste time on reading and prefer to play with the code right away I have prepared a link to my github repository with a ready to use project which you may clone and play with on your local environment. I tried to leave comments along the code to make it more clear and readable, but if you find that some areas are not clear or confusing, you may always get back to this article for some hints or guidance. For the rest of you who decided to stick to the narrative I advise to have a look at the project on github as well to have at hand a full file version with code snippets that I will use as examples later.
为了使我的任务更容易一些,我将使用免费的Shopify主题“ Debut”,然后逐步介绍如何向其中添加Vue组件。 对于那些不想浪费时间阅读并喜欢立即使用代码的人,我准备了指向我的github存储库的链接,其中包含一个随时可用的项目,您可以在本地环境中进行克隆和使用。 我试图在代码中留下注释,以使其更清晰易读,但是,如果您发现某些区域不清楚或令人困惑,则可以随时返回本文以获取一些提示或指导。 对于决定坚持叙述的其他人,我建议您也看看github上的项目,并准备一个带有代码片段的完整文件版本,我将在后面用作示例。
First of all I downloaded the Debut theme files from Shopify and put them to the project’s src folder:
首先,我从Shopify下载了Debut主题文件,并将它们放在项目的src文件夹中:
../src/
assets
config
layout
locales
sections
snippets
templates
I keep all the config and script files in the root folder:
我将所有配置和脚本文件保存在根文件夹中:
../
.gitignore
babel.config.js
config.yml
gulpfile.js
package.json
webpack.config.js
The Debut theme has a “testimonials” section with a list of random testimonials on the home page. I’ll replace a liquid version of that list with Vue.js version.
首次登台主题有一个“推荐”部分,主页上有随机推荐的列表。 我将用Vue.js版本替换该列表的临时版本。
Let’s modify theme.liquid file and rename original js scripts to keep them aside. The link to theme.js file stays unchanged as we will generate a new theme.js file with webpack later.
让我们修改theme.liquid文件并重命名原始js脚本以将它们放在一边。 指向theme.js文件的链接保持不变,因为稍后我们将使用webpack生成一个新的theme.js文件。
...
<script>
var theme = {
vue: {
availableApps: []
},
breakpoints: {
medium: 750,
large: 990,
widescreen: 1400
},
strings: {
addToCart: {{ 'products.product.add_to_cart' | t | json }},
...
<!--script src="{{ 'lazysizes.js' | asset_url }}" async="async"></script-->
<!--script src="{{ 'vendor.js' | asset_url }}" defer="defer"></script-->
<script src="{{ 'theme.js' | asset_url }}" defer="defer"></script>
...
Extend the global “theme” object by adding a new property that contains a list of all the Vue applications available on the current page.
通过添加新属性来扩展全局“主题”对象,该属性包含当前页面上所有可用的Vue应用程序的列表。
Add initial div element as a mount point for our future Vue component. To initialize and run this component we will need two things: a unique identifier to find it among other components on the page and input data to render the list.
添加初始div元素作为我们将来的Vue组件的安装点。 要初始化和运行此组件,我们需要做两件事:在页面上的其他组件中找到它的唯一标识符,以及输入数据以呈现列表。
...
{%- assign app_type = 'vue-quotes' -%}
<div class="vue-quote-app"
data-app-type="{{ app_type }}"
data-app-id="{{ section.id }}">
<script id="{{ section.id }}-{{ app_type }}">
window.theme.vue.availableApps.push({
type: "{{ app_type }}",
id: "{{ section.id }}",
data: {
"settings": {{ section.settings | json }},
"blocks":[
{% for block in section.blocks %}
{{ block.settings | json }},
{% endfor %}
]
}
});
</script>
</div>
...
Two parameters, “app_type” and “section.id”, form an app key. “section.id” is generated by the platform so it’s unique for each section, whereas “app_type” value is defined manually. Thus we may have multiple Vue applications of different types in the same section. “Json” Liquid filter converts section’s settings and section’s block data into a json representation that we store in the global js object associated with the application.
两个参数“ app_type”和“ section.id”形成一个应用程序密钥。 “ section.id”是由平台生成的,因此它对于每个部分都是唯一的,而“ app_type”值是手动定义的。 因此,我们在同一节中可能有多个不同类型的Vue应用程序。 “ Json” Liquid过滤器将节的设置和节的块数据转换为json表示形式,我们将其存储在与应用程序关联的全局js对象中。
import './vue-section-quotes.scss';
import Vue from 'vue';
import Quotes from './quotes.vue';
export class QuotesApp {
constructor(id, data) {
this._sectionId = id;
this._appType = 'vue-quotes';
this._appInstance = null;
this._appData = data;
this._mountingNode = null;
}
getSectionId() {
return this._sectionId;
}
setMountingNode() {
this._mountingNode = document.querySelector(`div[data-app-id="${this._sectionId}"][data-app-type="${this._appType}"]`);
}
// required
kill() {
this._appInstance.kill();
}
// required
init() {
this.setMountingNode();
this._appInstance = new Vue({
el: this._mountingNode,
render: h => h(Quotes, {
props: {
...this._appData
}
}),
methods: {
kill() {
this.$destroy();
}
}
});
}
};
The next step is to write a service class QuotesApp for our application type. It will handle app initialization and destruction plus it links scss file with styles being used in the app. Inside the init function we mount a single file component with main app logic to the div element created earlier in the quotes.liquid template.
下一步是为我们的应用程序类型编写服务类QuotesApp 。 它将处理应用程序的初始化和销毁操作,并将它与应用程序中使用的样式链接到scss文件。 在init函数内部,我们将具有主应用逻辑的单个文件组件安装到先前在quotes.liquid模板中创建的div元素上。
<template>
<div class="vue-quote-app">
<div v-if="settings.title.length > 0" class="section-header text-center">
<h2>{{ settings.title }}</h2>
</div>
<div class="quotes-wrapper">
<div class="quotes-slider" :data-count="`${blocks.length}`">
<div v-for="(block, index) in blocks" :key="`block-id-${index}`">
<blockquote class="quotes-slider__text text-center">
<span class="quote-icon">
<svg aria-hidden="true" focusable="false" role="presentation" class="icon icon-quote" viewBox="0 0 41 35"><path d="M10.208 17.711h6.124v16.332H0V21.684C0 8.184 5.444.956 16.332 0v6.125c-4.083 1.14-6.124 4.414-6.124 9.82v1.766zm24.498 0h6.124v16.332H24.498V21.684C24.498 8.184 29.942.956 40.83 0v6.125c-4.083 1.14-6.124 4.414-6.124 9.82v1.766z" fill="#000" fill-rule="evenodd"/></svg>
</span>
<div v-html="block.quote" class="rte-setting rte"></div>
<cite>{{ block.author }}</cite>
</blockquote>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
settings: Object,
blocks: Array
}
}
</script>
The code of the “quotes.vue” component is simple and straightforward. It takes a blocks array and renders a list of quotes. That’s it.
“ quotes.vue”组件的代码非常简单明了。 它采用一个blocks数组并呈现一个引号列表。 而已。
The last touch is to handle cases where we use the Shopify design editor. Among a variety of customization options the editor allows to add or remove any dynamic section on the home page. That means that we should be able to initialize our components after insertion and remove all component references when we delete a section from the page. Whenever one of these actions takes place, the editor emits a js event. In my case I added two callback functions for “shopify:section:load” and “shopify:section:unload” events.
最后一点是处理使用Shopify设计编辑器的情况。 在各种自定义选项中,编辑器允许添加或删除主页上的任何动态部分。 这意味着我们应该能够在插入后初始化组件,并在从页面中删除部分时删除所有组件引用。 只要执行这些操作之一,编辑器就会发出js事件。 就我而言,我为“ shopify:section:load”和“ shopify:section:unload”事件添加了两个回调函数。
...
if (Shopify.designMode) {
// editor mode helper function
const registerNewApps = function(event) {
const eventSectionId = event.detail.sectionId;
event
.target
.querySelectorAll(`div[data-app-id="${eventSectionId}"]`)
.forEach(appElement => {
const appType = appElement.getAttribute('data-app-type');
eval(document.getElementById(`${eventSectionId}-${appType}`).innerHTML);
});
}
// Handle theme editor events
document.addEventListener("shopify:section:load", event => {
const eventSectionId = event.detail.sectionId;
registerNewApps(event);
window.theme.vue.availableApps.forEach(app => {
// check if a new section has got apps
if (app.id == eventSectionId) {
// create instances for apps in the new section
if (appTypeClass[app.type]) {
const newApp = new appTypeClass[app.type](app.id, app.data);
activeApps.push(newApp);
newApp.init();
} else {
console.log(`App "${app.type}" was not registered`);
}
}
});
});
document.addEventListener("shopify:section:unload", event => {
const eventSectionId = event.detail.sectionId,
newActiveApps = [],
newAvailableApps = [];
activeApps.forEach(app => {
// check if a unloaded section had apps
if (app.getSectionId() == eventSectionId) {
// turn off and unmount apps from unloaded section
app.kill();
} else {
// save untouched apps aside
newActiveApps.push(app)
}
});
window.theme.vue.availableApps.forEach(app => {
// check if a unloaded section had apps
if (app.id != eventSectionId) {
// save untouched apps aside
newAvailableApps.push(app)
}
});
// reinit global app lists
window.theme.vue.availableApps = newAvailableApps;
activeApps = newActiveApps;
});
}
The only tricky and potentially dangerous part here is the code evaluation on the line 12. Unfortunately it’s a necessary evil, because when a new section is being added to the DOM tree, the inline javascript block is not executed, so I should do it manually via eval function.
唯一棘手且潜在危险的部分是第12行的代码评估。不幸的是,这是必不可少的,因为在将新的部分添加到DOM树时,不会执行内联javascript块,因此我应该手动执行通过eval函数。
At this point there is a bunch of js files which I should compile into a single theme.js file and then upload it to the server. Shopify provides a command line tool for working with theme files on a local environment called “theme-kit”. It watches for changes in theme files and uploads them to the server if something has been changed. In other words it keeps a remote theme synchronized with a local one. It works fine until you need to compile multiple source files into a js bundle. From the performance perspective it’s better to pre-compile all Vue.js components and send to the client a code already prepared for rendering. Also if we use ES6 syntax with features that are not yet supported by majority of browsers, we need to transpile the code into ES5 format.
此时,有一堆js文件,我应该将其编译为单个theme.js文件,然后将其上传到服务器。 Shopify提供了一个命令行工具,用于在称为“ theme-kit”的本地环境中处理主题文件。 它监视主题文件中的更改,如果有更改,则将其上载到服务器。 换句话说,它使远程主题与本地主题保持同步。 在您需要将多个源文件编译成js捆绑包之前,它可以正常工作。 从性能的角度来看,最好预先编译所有Vue.js组件,并将已经准备好进行渲染的代码发送给客户端。 另外,如果我们将ES6语法用于大多数浏览器尚不支持的功能,则需要将代码转换为ES5格式。
I use Gulp as an orchestration tool that watches for changes, compiles changed files with help of webpack and finally uploads the result to the server using a “node-themekit”. To start working on the project just run “gulp watch” and that’s it. Gulp will take care of everything else.
我将Gulp用作编排工具,该工具可以监视更改,借助webpack编译更改的文件,最后使用“ node-themekit”将结果上传到服务器。 要开始从事该项目,只需运行“ gulp watch”即可。 Gulp会照顾其他一切。
You may find the source code for this pet project on the github at the following link. Don’t hesitate to play around with the code and write in the comments if you find a better way to add Vue.js into Shopify themes.
您可以在以下链接的github上找到此宠物项目的源代码。 如果您找到将Vue.js添加到Shopify主题中的更好方法,请不要犹豫地使用代码并在注释中编写。
本文的重点 (Takeaway from this article)
- Classic Shopify themes are not a friendly environment for JS frameworks 经典Shopify主题不是JS框架的友好环境
- JQuery is still a default weapon of choice but can be easily replaced with native JS to improve performance jQuery仍然是默认选择的武器,但可以轻松地用本机JS替换以提高性能
- In large projects some data intensive elements like cart, product pages, collection filters can be implemented as Vue.js components 在大型项目中,一些数据密集型元素(例如购物车,产品页面,集合过滤器)可以实现为Vue.js组件
翻译自: https://medium.com/@mdyuzhinov/vue-js-components-in-shopify-themes-720eaefb7a2b
shopify主题开发