让我们构建 Web 组件!第 4 部分:Polymer 库
Polymer 应用工具箱 - 入门套件
如今,基于组件的 UI 设计风靡一时。你知道吗?Web 本身就拥有原生组件模块,无需任何库的依赖。千真万确!你可以编写、发布和复用单文件组件,它们可以在任何 主流 浏览器和 任何框架 (如果你喜欢的话)中运行。
在 上一篇文章 中,我们学习了如何仅使用 JavaScript 和 DOM API 来编写单文件组件。
今天我们将深入探索最初的 Web 组件库:Polymer。我们将重构 <lazy-image>上次构建的组件,以充分利用 Polymer 的强大功能。我们还将学习如何使用 Polymer 富有表现力的模板系统和双向绑定,从基于 Polymer 的组件构建完整的应用程序。我们将了解 Polymer 团队发布的一些优秀的现成文档元素。最后,我们将概述 Polymer 项目的一些实用工具,并了解它们如何应用于任何 Web 组件项目,而不仅仅是 Polymer 应用程序。
聚合物项目
Polymer 项目 早在 2012/2013 年就已启动,目标是提升网络平台的功能。 据说, 在谷歌总部深处,一群 Chrome 浏览器工程师与一群网络开发人员秘密举行了一次通灵会,共同规划整个网络的未来发展方向。
浏览器工程师询问 Web 开发人员,他们希望五年后的 Web 开发是什么样子,然后着手构建它。最终成果是 Polymer 库的第一个版本,也是现代 Web 组件故事的开端。
自那时起,Polymer 项目经历了一个完整的循环,如今完全 无需使用 Polymer 库即可编写 Web 组件 。但 Polymer 项目依然活跃。他们维护着各种 Web 平台提案,并倡导一种比当前流行的更基于标准的 Web 开发方式。
另一方面, Polymer 库 如今 已沦为众多用于构建 Web 组件和基于组件的应用程序的替代方案之一。
所以不要混淆这两者。“ 项目” 指的是整个平台, “库”指 的是帮助你构建组件。
重构 <lazy-image>
那么,让我们开始吧!既然我们已经开发出了 <lazy-image>基础组件,那就以此为基础来探索 Polymer 吧。
重构的第一步 <lazy-image>是安装并导入 Polymer 库。
npm i -S @polymer/polymer
Enter fullscreen mode
Exit fullscreen mode
我们还会稍微更改一下组件的名称,以帮助我们理清思路:
import { PolymerElement , html } from ' @polymer/polymer '
const tagName = ' polymer-lazy-image ' ;
class PolymerLazyImage extends PolymerElement {
/* ... */
}
customElements . define ( tagName , PolymerLazyImage )
Enter fullscreen mode
Exit fullscreen mode
Polymer 3.0 和 paper-elements 要求我们对所有模块规范进行转换,可以在构建步骤中进行,也可以在服务器运行时进行。我们将使用 polymer serve,它可以为我们动态转换原始规范。
npm i -D polymer-cli
npx polymer serve
Enter fullscreen mode
Exit fullscreen mode
在我们进行任何其他操作之前,现在应该采取的另一个重要步骤是调用 super我们所有生命周期回调的版本。
connectedCallback () {
super . connectedCallback ();
// ...
}
disconnectedCallback () {
super . disconnectedCallback ();
// ...
}
Enter fullscreen mode
Exit fullscreen mode
如果不这样做,就会出现问题,因为 PolymerElement基类需要在生命周期事件发生时执行一些工作。例如处理 polyfill,而我们现在不再需要手动执行这些工作了……
connectedCallback () {
super . connectedCallback ();
this . setAttribute ( ' role ' , ' presentation ' );
if ( ' IntersectionObserver ' in window ) this . initIntersectionObserver ();
else this . intersecting = true ;
}
Enter fullscreen mode
Exit fullscreen mode
现在 我们可以完全摆脱所有与 shadowRoot`-` 和 ` -` 相关的代码了,包括 `-`,因为 Polymer 会帮我们处理这些。太棒了!使用库让我们省去了维护 polyfill 的一项压力。 ShadyCSSupdateShadyStyles
特性
Polymer 允许你静态地声明元素的属性,这里的“静态”指的是在 static get编写代码时就已声明。当你在该代码块中声明属性时,Polymer 会自动处理属性的同步。这意味着,当 src元素上的某个属性被设置时,Polymer 会自动更新 src元素实例上的相应属性。
现在我们可以删除我们的 attributeChangedCallback、 safeSetAttribute以及所有的 getter 和 setter,并用带有某些 Polymer 特有描述符的静态属性映射来替换它们。
static get properties () {
return {
/** Image alt-text. */
alt : String ,
/**
* Whether the element is on screen.
* @type {Boolean}
*/
intersecting : {
type : Boolean ,
reflectToAttribute : true ,
notify : true ,
},
/** Image URI. */
src : String ,
};
}
Enter fullscreen mode
Exit fullscreen mode
Polymer 默认绑定的是属性(properties),而不是特性(attributes)。这意味着,如果您在宿主元素的 Polymer 模板中绑定到某个元素的属性,它不一定会作为特性出现在该元素上。 reflectToAttribute在属性描述符上设置布尔值可以确保每当属性发生更改时,Polymer 也会在元素上设置相应的特性。不过不用担心,即使您使用类似 `property.property.isAttribute('property')` 的构造函数声明属性 propName: String,特性更改也始终会更新关联的属性,无论您是否设置了 `isAttribute('property')` reflectToAttribute。
注意 :Polymer 会将驼峰式属性名称转换为短横线式属性名称,反之亦然。顺便一提,这也是 Polymer 库在某些“自定义元素无处不在”测试中失败的 原因。
这个 notify布尔值可以让你的元素在属性每次更改时触发一个自定义事件。例如,该事件会 针对某个属性调用 property-name-changed`except` 函数 ,其属性会是一个对象,该对象包含一个 指向属性新值的 键。 intersecting-changedintersectingdetailvalue
lazyImage . addEventListener ( ' intersecting-changed ' , event => {
console . log ( event . detail . value ) // value of 'intersecting';
})
Enter fullscreen mode
Exit fullscreen mode
这是 Polymer 双向绑定系统的基础。虽然在这里并非绝对必要,但我们不妨暴露这些事件,以防用户想要将图像 intersecting状态绑定到其所在的组件中。
现在我们也可以删除该 setIntersecting方法了,因为借助我们的属性映射和 Polymer 的模板系统,我们不再需要它了。
稍后 我们将详细介绍 Polymer 的属性描述符 。
数据绑定模板
我们使用静态 getter 定义 Polymer 3 元素的模板, template该 getter 返回一个带标签的模板字面量。
static get template () {
return html `
I'm the Template!
` ;
}
Enter fullscreen mode
Exit fullscreen mode
Polymer模板采用了一种特殊的语法,类似于车把或胡须。单向(数据向下)绑定使用双方括号[[]],双向(数据向上)绑定使用双 {{花括号 }}。
<some-input input= "{{myInput}}" ></some-input>
<some-element
some-property= "[[myInput]]"
some-attribute $="[[ myAttribute ]]"
></some-element>
Enter fullscreen mode
Exit fullscreen mode
在这个例子中,每当 <some-input>触发一个 input-changed事件时,宿主元素都会更新其 someProperty属性 <some-element>。用 JS 术语来说,这是一个简单的赋值操作: someElementInstance.someProperty = this.myInput。
如果要绑定到属性而不是特性,请将字符附加 $到绑定:每当 myOtherProp更改时, some-attributeon <some-element>都会更新: someElementInstance.setAttribute('some-attribute', this.myOtherProp)。
同样地,每当 input-changed自定义事件触发时 <some-input>, myInput宿主组件上的属性将被设置为事件的 detail.value属性。
在我们的 <polymer-lazy-image>模板中,我们没有使用任何双向绑定,所以我们将坚持使用方括号。
这个 aria-hidden属性带来了一点挑战。Polymer 使用 ` setAttribute(name, '')and`将布尔值绑定到属性 removeAttribute(name)。但由于 ` and` aria-hidden必须接受字符串字面量 "true"`or` "false",我们不能直接将其绑定到 `or` 的布尔值 intersecting。`src` 属性 <img/> src也同样有趣。实际上,我们希望仅在元素与图像相交后才设置它。为此,我们需要根据 `src` 属性的状态来计算图像的 `src` 属性 intersecting。
Polymer模板可以包含 计算绑定 。这些绑定与所选方法的返回值绑定。
<img id= "image"
aria-hidden $="[[ computeImageAriaHidden ( intersecting )]]"
src= "[[computeSrc(intersecting, src)]]"
alt $="[[ alt ]]"
/>
Enter fullscreen mode
Exit fullscreen mode
绑定表达式中这种类似函数的语法是做什么用的?它告诉 Polymer 要运行哪个元素方法以及何时运行。每当检测到其依赖项(即绑定表达式中传递的“参数”)发生变化时,它就会触发,并使用返回值更新绑定。
另请注意,我们绑定的是图像的 src 属性 ,而不是其 特性 。这是为了避免尝试从 URL 加载图像 "undefined"。
computeSrc ( intersecting , src ) {
// when `intersecting` or `src` change,
return intersecting ? src : undefined ;
}
computeImageAriaHidden ( intersecting ) {
// when `intersecting` changes,
return String ( ! intersecting );
}
Enter fullscreen mode
Exit fullscreen mode
不过,别被误导了,这些不是 JavaScript 表达式,所以你不能随意传入任何值:` [[computeImageAriaHidden(!intersecting)]]is work` 和 `is does` 都不起作用。 [[computeImageAriaHidden(this.getAttribute('aria-hidden'))]]
现在我们只需稍微调整一下属性映射和样式,以适应元素 API 的变化:
static get properties () {
return {
// ...
/** Whether the element is intersecting. */
intersecting : Boolean ,
/**
* Whether the image has loaded.
* @type {Boolean}
*/
loaded : {
type : Boolean ,
reflectToAttribute : true ,
value : false ,
},
};
}
Enter fullscreen mode
Exit fullscreen mode
<style>
/* ... */
#placeholder ::slotted (*),
:host ([ loaded ]) #image {
opacity : 1 ;
}
#image ,
:host ([ loaded ]) #placeholder ::slotted (*) {
opacity : 0 ;
}
</style>
<div id= "placeholder" aria-hidden $="[[ computePlaceholderAriaHidden ( intersecting )]]" >
<slot name= "placeholder" ></slot>
</div>
<img id= "image"
aria-hidden $="[[ computeImageAriaHidden ( intersecting )]]"
src= "[[computeSrc(intersecting, src)]]"
alt $="[[ alt ]]"
on-load= "onLoad"
/>
Enter fullscreen mode
Exit fullscreen mode
因此,我们能够大幅 减少 组件中的样板代码,并通过将其包含在模板中来精简一些多余的逻辑,尽管其中包含一些略显繁琐的计算绑定辅助函数。
这是我们完成的 <polymer-lazy-image>模块:
import { PolymerElement , html } from ' @polymer/polymer ' ;
const isIntersecting = ({ isIntersecting }) => isIntersecting ;
const tagName = ' polymer-lazy-image ' ;
class PolymerLazyImage extends PolymerElement {
static get template () {
return html `
<style>
:host {
position: relative;
}
#image,
#placeholder ::slotted(*) {
position: absolute;
top: 0;
left: 0;
transition:
opacity
var(--lazy-image-fade-duration, 0.3s)
var(--lazy-image-fade-easing, ease);
object-fit: var(--lazy-image-fit, contain);
width: var(--lazy-image-width, 100%);
height: var(--lazy-image-height, 100%);
}
#placeholder ::slotted(*),
:host([loaded]) #image {
opacity: 1;
}
#image,
:host([loaded]) #placeholder ::slotted(*) {
opacity: 0;
}
</style>
<div id="placeholder" aria-hidden$="[[computePlaceholderAriaHidden(intersecting)]]">
<slot name="placeholder"></slot>
</div>
<img id="image"
aria-hidden$="[[computeImageAriaHidden(intersecting)]]"
src="[[computeSrc(intersecting, src)]]"
alt$="[[alt]]"
on-load="onLoad"
/>
` ;
}
static get properties () {
return {
/** Image alt-text. */
alt : String ,
/** Whether the element is on screen. */
intersecting : Boolean ,
/** Image URI. */
src : String ,
/**
* Whether the image has loaded.
* @type {Boolean}
*/
loaded : {
type : Boolean ,
reflectToAttribute : true ,
value : false ,
},
};
}
constructor () {
super ();
this . observerCallback = this . observerCallback . bind ( this );
}
connectedCallback () {
super . connectedCallback ();
// Remove the wrapping `<lazy-image>` element from the a11y tree.
this . setAttribute ( ' role ' , ' presentation ' );
// if IntersectionObserver is available, initialize it.
if ( ' IntersectionObserver ' in window ) this . initIntersectionObserver ();
// if IntersectionObserver is unavailable, simply load the image.
else this . intersecting = true ;
}
disconnectedCallback () {
super . disconnectedCallback ();
this . disconnectObserver ();
}
/**
* Loads the img when IntersectionObserver fires.
* @param {Boolean} intersecting
* @param {String} src
* @return {String}
*/
computeSrc ( intersecting , src ) {
return intersecting ? src : undefined ;
}
/**
* "true" when intersecting, "false" otherwise.
* @protected
*/
computePlaceholderAriaHidden ( intersecting ) {
return String ( intersecting );
}
/**
* "false" when intersecting, "true" otherwise.
* @protected
*/
computeImageAriaHidden ( intersecting ) {
return String ( ! intersecting );
}
/** @protected */
onLoad () {
this . loaded = true ;
}
/**
* Sets the `intersecting` property when the element is on screen.
* @param {[IntersectionObserverEntry]} entries
* @protected
*/
observerCallback ( entries ) {
if ( entries . some ( isIntersecting )) this . intersecting = true ;
}
/**
* Initializes the IntersectionObserver when the element instantiates.
* @protected
*/
initIntersectionObserver () {
if ( this . observer ) return ;
// Start loading the image 10px before it appears on screen
const rootMargin = ' 10px ' ;
this . observer = new IntersectionObserver ( this . observerCallback , { rootMargin });
this . observer . observe ( this );
}
/**
* Disconnects and unloads the IntersectionObserver.
* @protected
*/
disconnectObserver () {
this . observer . disconnect ();
this . observer = null ;
delete this . observer ;
}
}
customElements . define ( tagName , PolymerLazyImage );
Enter fullscreen mode
Exit fullscreen mode
对比一下 原版 和 Polymer 版本,看看组件是如何工作的:
更多聚合物特性
Polymer 的功能远不止我们这个简单的示例元素所能展示的。举个简单的例子,Polymer 可以 id将模板中所有 `<div>` 元素映射到一个名为 `<object>` 的对象 $:
<paper-button id= "button" > Button!</paper-button>
<paper-input id= "input" label= "Input!" ></paper-input>
Enter fullscreen mode
Exit fullscreen mode
connectedCallback () {
console . log ( this . $ . button . textContent ) // "Button!"
this . $ . input . addEventListener ( ' value-changed ' , breakTheInternet );
}
Enter fullscreen mode
Exit fullscreen mode
高级数据绑定
Polymer 还可以使用特殊语法绑定来自非 Polymer 元素事件的宿主属性:
<video current-time= "{{videoTime::timeupdate}}" />
Enter fullscreen mode
Exit fullscreen mode
这意味着“当 timeupdate事件触发时,将本地 videoTime属性分配给视频元素 currentTime”。
在后续版本中 <polymer-lazy-image>,我们可能会使用这类绑定来将内部 <img>属性与我们自己的属性同步。
想要深入了解 Polymer 的数据绑定系统,请 阅读 相关文档。
观察者和计算属性
计算属性和绑定是 Polymer 观察者 的特殊情况。一个简单的观察者如下所示:
static get properties () {
return {
observed : {
type : String ,
observer : ' observedChanged ' ,
},
};
}
observedChanged ( observed , oldVal ) {
console . log ( ` ${ observed } was ${ oldVal } ` );
}
Enter fullscreen mode
Exit fullscreen mode
您还可以定义具有多个依赖项或深度观察对象或数组的复杂观察者。
static get properties () {
return {
observed : Object ,
message : {
type : String ,
value : ' A property of observed has changed ' ,
},
};
}
static get observers () {
return [
// careful: deep observers are performance intensive!
' observedChanged(message, observed.*) '
],
}
observedChanged ( message , { path , value , base }) {
// path: the path through the object where the change occurred
// value: the new value at that path
// base: the root object e.g. `observed`
console . log ( message , path + ' : ' + value );
}
Enter fullscreen mode
Exit fullscreen mode
您还可以设置计算属性,类似于计算绑定:
static get properties () {
return {
theString : String ,
theLength : {
type : Number ,
computed : ' computeTheLength(theString) ' ,
},
};
}
computeTheLength ( theString ) {
return theString . length ;
}
Enter fullscreen mode
Exit fullscreen mode
在这种情况下, theLength会根据变化 computeTheLength情况进行更新 theString。
然后,可以将这些计算属性像任何普通属性一样绑定到模板上。
<span> [[theString]] has [[theLength]] characters</span>
Enter fullscreen mode
Exit fullscreen mode
请阅读文档 了解有关 Polymer 观察者的全部内容 。
属性描述符
我们已经了解了如何设置值 reflectToAttribute并 notify在值更新时影响外部世界,以及如何使用 observer描述符设置简单的观察者。
您还可以使用 来设置默认值 value,该值可以接受字面值或函数。
static get properties () {
return {
prop : {
type : String ,
value : ' 🚣♂️ '
},
things : {
type : Array ,
value : () => [],
},
};
}
Enter fullscreen mode
Exit fullscreen mode
注意! 当您想要使用引用类型(例如 `<value>` 或 `<value>`)设置默认值时 Array, Object请务必传递一个函数,否则 您的元素的每个实例 将共享同一个引用。
value组件初始化时会设置属性值,之后不会再更新。如果需要在连接后动态设置属性,请使用 计算属性 或观察者。
辅助元素
Polymer 提供了一些辅助元素,您可以在模板中使用它们来减少需要编写的命令式 JavaScript 代码量。其中最常用的两个元素分别是 <dom-repeat>用于遍历列表和输出 DOM,以及 <dom-if>用于条件渲染:
<!-- Will output a new article with h2 and img for each post -->
<dom-repeat items= "[[posts]]" as= "post" >
<template>
<article>
<h2> [[post.title]]</h2>
<img src $="[[ post.picture ]]" >
</article>
</template>
</dom-repeat>
<!-- Will only render it's template if conditionDepending(someProp, another) is truthy -->
<dom-if if= "[[conditionDepending(someProp, another)]]" >
<template>
I'm a very lucky textNode to have [[someProp]] and [[another]] on my side.
</template>
</dom-if>
Enter fullscreen mode
Exit fullscreen mode
要使用这些辅助函数,请确保导入它们。
import ' @polymer/polymer/lib/elements/dom-repeat.js ' ;
import ' @polymer/polymer/lib/elements/dom-if.js ' ;
Enter fullscreen mode
Exit fullscreen mode
有关辅助元素的更多信息,请参阅 Polymer 文档 。
聚合物应用
Polymer 在重构整个应用程序方面表现出色。Polymer 项目开创了一种相当先进且非常独特的(抱歉用了个双关)声明式应用程序结构,这种结构主要基于 HTML 元素构建。Polymer 的方法将“一切皆元素”,充分利用了 HTML 内置的可组合性。例如,`<div>` 元素 <iron-ajax>可以获取资源并将其暴露给 Polymer 的数据绑定。
<iron-ajax auto
url= "/api/posts"
handle-as= "json"
last-response= "{{posts}}" ></iron-ajax>
<dom-repeat items= "[[posts]]" as= "post" >
<template>
<article>
<h2> [[post.title]]</h2>
<img hidden $="[[! post.cover ]]" src $="[[ post.cover ]]" >
[[post.body]]
</article>
</template>
</dom-repeat>
Enter fullscreen mode
Exit fullscreen mode
但依我拙见,这种方法的最佳例证体现在 封装路由 <app-route>的元素和理念上 :
<!-- <app-shell> template -->
<!-- Capture and expose address-bar changes -->
<app-location route= "{{route}}" ></app-location>
<app-route route= "[[route]]"
data= "{{routeData}}"
tail= "{{pageTail}}"
pattern= "/:page" ></app-route>
<!-- Composed routing! -->
<app-route route= "[[tail]]"
data= "{{itemData}}"
tail= "{{itemTail}}"
pattern= "/:itemId" ></app-route>
<iron-pages selected= "{{routeData.page}}" attr-for-selected= "name" >
<app-master name= "master" ></app-master>
<app-detail name= "detail"
item-id= "[[itemData.itemId]]"
route= "[[itemTail]]" ></app-detail>
</iron-pages>
Enter fullscreen mode
Exit fullscreen mode
通过使用 app-route 和 iron-pages 元素,我们拥有一个完整的路由解决方案,可以根据 URL 隐藏和显示内容,甚至可以将与路由相关的数据传递给这些视图组件。
由于 <app-route>它将 route属性视为数据,而不是直接绑定到 window.location,因此您可以将路由的部分内容传递给子视图,并让它们管理自身及其子 <app-route>视图的内部状态。真棒!
<!-- <app-detail> template -->
<app-route route= "[[route]]"
data= "{{routeData}}"
pattern= "/:editing" ></app-route>
<item-detail hidden $="[[ routeData.editing ]]" ></item-detail>
<item-editor hidden $="[[! routeData.editing ]]" ></item-editor>
<paper-checkbox checked= "{{routeData.editing}}" > Editing</paper-checkbox>
Enter fullscreen mode
Exit fullscreen mode
真是个很棒的想法!
**注意** 为了简洁起见,本示例中我们直接绑定到 `routeData` 的子属性,但在实际项目中,我们会添加一些辅助方法来从 `routeData` 计算中间的 `page` 属性。
要查看此类应用程序架构的完整示例,请参阅 GitHub 上著名的 Polymer Starter Kit 。
Polymer 应用工具箱 - 入门套件
此模板是使用抽屉式布局构建应用程序的起点。布局由 app-layout元素构成。
该模板及其 polymer-cli工具链还演示了“PRPL 模式”的使用。该模式允许在用户请求的初始路由上快速交付内容并与之交互,并通过预先缓存应用程序所需的其余组件,并在用户浏览应用程序时按需逐步加载它们,从而实现快速的后续导航。
PRPL模式简述:
初始路线所需的 推送组件
尽快渲染 初始路线
剩余路由的 预缓存组件
按需延迟加载 并逐步升级后续路线
设置
先决条件
使用 npm 安装 Polymer CLI (我们假设您已预先安装了 node.js )。
npm install -g polymer-cli
从模板初始化项目
mkdir my-app
cd my-app
polymer init polymer-3-starter-kit
启动开发服务器
此命令的作用是……
纸元素
如果一篇关于 Polymer 的博客文章没有提到 Paper Elements ,那它就不完整了。Paper Elements 是由 Polymer 项目发布的 Material Design UI 组件集。但如果我们没有把一件事说清楚,那我们也犯了一个巨大的错误:
PaperElements != Polymer ;
Enter fullscreen mode
Exit fullscreen mode
即使不使用纸张元素,您也可以很好地使用聚合物库;同样,即使不使用聚合物库,您也可以很好地使用纸张元素!
<head>
<script type= "module" src= "https://unpkg.com/@polymer/paper-checkbox/paper-checkbox.js?module" ></script>
<script type= "module" src= "https://unpkg.com/@polymer/paper-card/paper-card.js?module" ></script>
<script type= "module" src= "https://unpkg.com/@polymer/paper-button/paper-button.js?module" ></script>
</head>
<body>
<paper-card heading= "Am I Checked?" >
<div class= "card-content" >
Output: <span id= "output" > Not Checked</span>
</div>
<div class= "card-actions" >
<paper-checkbox id= "input" > Check me!</paper-checkbox>
<paper-button raised disabled id= "button" > Reset</paper-button>
</div>
</paper-card>
<script>
const onClick = () => input . checked = false ;
const onInput = ({ detail : { value }}) => {
output . textContent = value ? ' Checked ' : ' Not Checked ' ;
button . disabled = ! value ;
}
input . addEventListener ( ' checked-changed ' , onInput );
button . addEventListener ( ' click ' , onClick );
</script>
</body>
Enter fullscreen mode
Exit fullscreen mode
我们在这里失去的只是使用 Polymer 数据绑定系统的能力。但是——你猜对了——有一个元素可以做到这一点,它叫做 <dom-bind>
如果您想轻松构建基于 Material Design 的 UI,不妨试试 paper-elements。
聚合物工具
除了倡导工作、JS 和组件库以及标准提案之外,Polymer 项目还发布了各种工具,帮助您构建、发布和部署应用程序和组件。
prpl-server
Chrome 团队开发了 PRPL 模式 ,作为编写和交付高性能 Web 应用的最佳实践。 prpl-server它能够轻松地为功能强大的浏览器提供最小的有效包,同时还能兼容运行较大包的旧版浏览器。PRPL 模式提供了一个现成的二进制文件和一个 Express 中间件库。 不妨试用一下 。
聚合物 CLI
Vue CLI 帮助你开发 Vue 应用。Angular CLI 帮助你开发 Angular 应用。 create-react-app帮助你开发 React 应用。
Polymer CLI 可帮助您开发 Web 应用程序。
没错,它提供了 Polymer 3 元素和应用程序的模板,但这并非全部。`build` polymer build和 ` polymer serveapp` 命令可以构建并运行任何 Web 组件应用程序。转译是可选的。实际上,CLI 对代码所做的几乎唯一操作就是将裸模块说明符(例如 `<module>`)替换 import { PolymerElement } from '@polymer/polymer';为浏览器可以直接加载的相对 URL。
什么?!你是说不用 Webpack?不用 Babel?不用花几个小时跟那些跟我的应用代码无关的配置文件和 API 较劲?
没错,我说的就是这个。下次你开发应用项目时,不妨考虑使用 Web 组件和 Polymer CLI 进行重构。
但如果您 想 为旧版浏览器进行转译(见 prpl-server上文),您可以定义一个 builds部分 polymer.json:
{
"root" : "~/projects/my-project" ,
"entrypoint" : "index.html" ,
"shell" : "src/my-project.js" ,
"sources" : [
"src/my-project.js" ,
"manifest/**" ,
"package.json"
],
"builds" : [{
"name" : "es5prod" ,
"preset" : "es5-bundled" ,
"addServiceWorker" : true
}, {
"name" : "es6prod" ,
"preset" : "es6-unbundled" ,
"addServiceWorker" : true
}, {
"name" : "dev" ,
"addServiceWorker" : false ,
"js" : { "minify" : false , "compile" : false },
"css" : { "minify" : false },
"html" : { "minify" : false },
"bundle" : false ,
"addPushManifest" : false
}]
}
Enter fullscreen mode
Exit fullscreen mode
然后你只需配置 prpl-server它以支持 es6prod现代浏览器以及 es5prodIE 等浏览器,就可以开始使用了。
医生,看看这些文件!
WebComponents.org
在你着手实现脑海中的想法之前,不妨先去 webcomponents.org <super-button>看看 ,这是最大的 Web 组件目录。 每个组件都附有文档、公共 API 和安装方法。你还可以找到 npm 和 GitHub 的链接。 如果你是组件作者,那就别犹豫了! 发布你的组件, 让其他人也能从中受益。
结论
Polymer库无疑是超前的。它采取的方法是要求Web平台做得更好,然后将这种要求变为现实,而不是仅仅绕过平台的局限性。
既然 Web 组件已被广泛支持,Polymer 库在我们的 Web 开发工具箱中是否还有一席之地?答案是肯定的!有些项目天生就适合 Polymer 的声明式风格。有些团队会发现,设计师和文档编写者可以利用 Polymer 富有表现力的绑定系统来完成开发人员的工作。
不过 ,并非一切都 尽如人意 。 随着平台和更广泛的网络社区的发展,Polymer 项目的优先级也随之改变。Polymer 3 很可能是该库的最后一个主要版本,同样,3.0 系列也将是 paper-elements 的最后一个版本。
那么,让我们回顾一下 Polymer 库的一些优点和缺点:
优点
缺点
富有表现力的模板系统
无法直接将 JS 传递给模板
观察者和计算属性,声明式事件监听器
庞大的依赖链会促使开发更大的纯 Polymer 应用。
非常酷炫且独特的声明式应用程序结构方法
无论好坏,这种独特的声明式风格都不如其他架构流行。
一套成熟的库和组件集。经过实践检验,值得信赖。
Polymer.js 几乎已被弃用,除非进行 fork 操作,否则不会再获得新功能。
这是否意味着 Web Components 的末日? 当然 不是!Polymer 远非唯一的选择。一个名为 的轻量级声明式 JS 模板库 lit-html,以及一个利用该库的自定义元素基类, LitElement才是当下最热门的技术。如果一切顺利,我们将在下一期中介绍它们。
到时候见😊
您是否想就这里涵盖的任何主题进行一对一指导?
致谢
感谢 Pascal Schilp 和@ruphin 的建议和更正( 排名不分先后) 。
请阅读本系列的下一篇文章。
文章来源:https://dev.to/bennypowers/lets-build-web-components-part-4-polymer-library-4dk2