w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com zh-hans w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/component-communication.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><p>使用Vue构建韦德娱乐平台容易,但对于初学者要掌握Vue韦德娱乐平台中的通讯还是有一定的难度。比如说,父韦德娱乐平台如何向子韦德娱乐平台通讯?子韦德娱乐平台又是如何向父韦德娱乐平台通讯?兄弟韦德娱乐平台又是怎么通讯?这些方面都是有关于韦德娱乐平台通讯相关的知识。而且掌握Vue韦德娱乐平台之间的通讯方式还是掌握Vue韦德娱乐平台的另一种能力。</p> <p>在Vue中,Vue的韦德娱乐平台自身就很棒,因为它可以帮助我们使用重用的代码片段,我们也可以使用Vue提供的所有功能。现在我们要掌握怎么建立韦德娱乐平台之间的连接,也就是韦德娱乐平台的通讯,以便一个韦德娱乐平台中的操作可以更新应用程序中的其他韦德娱乐平台。在接下来的内容中,咱们会涉及两个部分,第一个部分是父子韦德娱乐平台怎么通讯;第二部分兄弟韦德娱乐平台怎么通讯?</p> <h2>父子韦德娱乐平台通讯</h2> <p>先来看看父子韦德娱乐平台之间如何建立通讯:<strong>将看到父韦德娱乐平台如何向子韦德娱乐平台通讯和子韦德娱乐平台如何向父韦德娱乐平台通讯</strong> ?</p> <h3>父韦德娱乐平台向子韦德娱乐平台通讯</h3> <p>要在Vue中将数据从父韦德娱乐平台传到子韦德娱乐平台,我们可以通过 <strong><code>props</code></strong>来实现。在React中也是使用类似的约定来实现韦德娱乐平台之间的数据共享。<a href="http://www.xysjxj.com/quot;//vuejs.org/v2/guide/components.html#Props"><code>props</code></a>指的是从外部设置的属性,例如来自父韦德娱乐平台。为了告诉Vue子韦德娱乐平台从自已实例的外部接收数据,需要在子韦德娱乐平台的<code>Vue</code>对象中设置<code>props</code>属性。这个属性包含一个<code>String</code>数组,每个字符串表示一个可以从父韦德娱乐平台设置的属性。请注意,<code>props</code>严格用于父韦德娱乐平台与子韦德娱乐平台之间的单向通讯,并且你不希望尝试直接在子韦德娱乐平台中更改<code>props</code>的值。否则,你将收到类似这样的错误信息“避免直接修改某个<code>prop</code>,因为当父韦德娱乐平台重新渲染时,该值将被覆盖" 这样的错误。相反,根据<code>prop</code>的值使用<code>data</code>或<code>computed</code>。</p> <h4>父韦德娱乐平台使用属性绑定</h4> <p>为了将数据从父韦德娱乐平台传到子韦德娱乐平台,在父韦德娱乐平台中设置一个属性,该属性绑定和子韦德娱乐平台的<code>prop</code>相同的名称一个属性值。请注意,我们现在位于父韦德娱乐平台内部,但是我们使用了自定义标签<code>&lt;child-card&gt;</code>来渲染子韦德娱乐平台。在这个标记上,我们设置了绑定属性。在下面的示例中,使用<code>parentMessage</code>作为属性名,所以在子韦德娱乐平台中需要一个<code>props: ['parentMessage']</code>作为一个<code>prop</code>。然后在父韦德娱乐平台中,使用<code>&lt;child-card :parentMessage = "parentMessage"&gt;&lt;/child-card&gt;</code>来传递数据。</p> <pre><code>&lt;!-- ParentCard.vue --&gt; &lt;template&gt; &lt;div class="card"&gt; &lt;div class="card-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;button @click="sendMessage" class="btn"&gt;给子韦德娱乐平台发送一个消息&lt;/button&gt; &lt;/div&gt; &lt;div class="card-body"&gt; &lt;child-card :parentMessage="parentMessage"&gt;&lt;/child-card&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import ChildCard from './ChildCard'; export default { name: 'ParentCard', data: () =&gt; ({ theCardTitle: '父韦德娱乐平台', parentMessage: '' }), components: { ChildCard }, methods: { sendMessage() { this.parentMessage = `&lt;b&gt;消息来自父韦德娱乐平台:&lt;/b&gt; (^_^)!!!` } } } &lt;/script&gt; </code></pre> <h4>子韦德娱乐平台使用props对象</h4> <p>接下来的代码中,创建一个<code>ChildCard</code>子韦德娱乐平台,它的<code>data</code>中有一个<code>props</code>数组,并且设置了一个<code>parentMessage</code>字符串。这表明<code>parentMessage</code>可以从外部设置,也可以从父韦德娱乐平台设置。这正是我们在上一节中所做的。为<code>prop</code>提供的字符串名称,在我们的示例中,<code>parentMessage</code>必须与此韦德娱乐平台中模板部分中使用的属性名相匹配。</p> <pre><code>&lt;!-- ChildCard.vue --&gt; &lt;template&gt; &lt;div class="message"&gt; &lt;div class="message-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;/div&gt; &lt;div class="message-body"&gt; &lt;p class="message-text" v-html="theCardBody"&gt;&lt;/p&gt; &lt;div v-if="parentMessage" class="alert" v-html="parentMessage"&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'ChildCard', props: ['parentMessage'], data: () =&gt; ({ theCardTitle: '子韦德娱乐平台', theCardBody: '我是一个子韦德娱乐平台!(^_^) !!!' }) } &lt;/script&gt; </code></pre> <p>上面的两小节中,分别创建了<code>ParentCard</code>和<code>ChildCard</code>两个韦德娱乐平台,而且子韦德娱乐平台<code>ChildCard</code>嵌套在父韦德娱乐平台<code>ParentCard</code>中。在子韦德娱乐平台中,使用了<code>v-if</code>指令有条件地显示来自父韦德娱乐平台<code>ParentCard</code>的消息,并且显示在<code>div.alert</code>中。如果没有消息,则不会显示<code>.alert</code>。因此,当页面首次渲染时,<code>parentMessage</code>的初始值是一个空字符串(在<code>ParentCard</code>韦德娱乐平台的<code>data</code>中设置了<code>parentMessage</code>为空字符串)。所以我们一开始渲染页面的时候,并看不到(<code>ChildCard</code>子韦德娱乐平台不显示<code>.alert</code>)。当用户点击了<code>ParentCard</code>韦德娱乐平台中的“发送消息”的按钮时,则会触发<code>ParentCard</code>韦德娱乐平台中定义的<code>sendMessage()</code>方法。这个时候,<code>parentMessage</code>的值就变成了<code>&lt;b&gt;消息来自父韦德娱乐平台:&lt;/b&gt; (^_^)!!!</code>。由于此变量使用<code>:parentMessage="parentMessage"</code>绑定到<code>&lt;child-card&gt;</code>标签上,并且子韦德娱乐平台<code>ChildCard</code>通过<code>props:['parentMessage']</code>接受该值,如此一来,子韦德娱乐平台将使用来自父韦德娱乐平台的<code>parentMessage</code>的值。</p> <p>最终效果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/qzoo33ovn6?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <p>当你点击示例中右上角的按钮,就可以看到父韦德娱乐平台向子韦德娱乐平台发送的消息,这样就完成了父韦德娱乐平台向子韦德娱乐平台的数据通讯:</p> <p><img src="/sites/default/files/blogs/2018/1809/parent-child-card-vue-1.gif" alt="" /></p> <p>简单的总结一下:</p> <blockquote> <p>在Vue中,父韦德娱乐平台向子韦德娱乐平台传递数据(通讯),可以借助<code>props</code>属性完成。</p> </blockquote> <p>可以用一张类似下面这样的图来描述父韦德娱乐平台向子韦德娱乐平台通讯的关系:</p> <p><img src="/sites/default/files/blogs/2018/1809/1_NnS55EuYE9Kks5eOXUrJJw.png" alt="" /></p> <h3>子韦德娱乐平台向父韦德娱乐平台通讯</h3> <p>事实除了从父韦德娱乐平台向子韦德娱乐平台传数据之外,有时候也要能从子韦德娱乐平台向父韦德娱乐平台传数据。那么问题来了,在Vue中如何从子韦德娱乐平台向父韦德娱乐平台传数据(通讯)?在Vue中要实现这个,我们可以在子韦德娱乐平台中发出自定义事件,并在父韦德娱乐平台中侦听发出的事件(子韦德娱乐平台中自定义的事件)。我们在上面的示例上来做一些更改,完成一个子韦德娱乐平台向父韦德娱乐平台通讯的示例。</p> <h4>子韦德娱乐平台发出自定义事件</h4> <p>首先在子韦德娱乐平台<code>ChildCard</code>的<code>&lt;template&gt;</code>中添加一个新的标签<code>button</code>。在这个<code>button</code>添加一个<code>click</code>事件:</p> <pre><code>&lt;!-- ChildCard.vue --&gt; &lt;template&gt; &lt;div class="message"&gt; &lt;div class="message-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;/div&gt; &lt;div class="message-body"&gt; &lt;p class="message-text" v-html="theCardBody"&gt;&lt;/p&gt; &lt;div v-if="parentMessage" class="alert" v-html="parentMessage"&gt;&lt;/div&gt; &lt;button v-if="parentMessage" @click="ok" class="btn"&gt;OK&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; </code></pre> <p>当我们点击“OK”按钮时,想运行名为<code>ok()</code>的方法。让我们设置该方法,以便在触发事件时发出(<code>$emit</code>)自定义的事件。我们将一个<code>finished</code>字符串传递给<code>$emit</code>函数。当然我们可以选择自己喜欢的名字,但在这个案例中我们选择了<code>finished</code>这个名,这样更具语义化。我们希望子韦德娱乐平台的信息发送到父韦德娱乐平台。</p> <pre><code>&lt;!-- ChildCard.vue --&gt; &lt;script&gt; export default { name: 'ChildCard', props: ['parentMessage'], data: () =&gt; ({ theCardTitle: '子韦德娱乐平台', theCardBody: '我是一个子韦德娱乐平台!(^_^) !!!' }), methods: { ok() { this.$emit('finished') } } } &lt;/script&gt; </code></pre> <h4>父韦德娱乐平台侦听自定义事件</h4> <p>现在我们可以回到父韦德娱乐平台<code>ParentCard中,在父韦德娱乐平台中使用自定义标签</code><child-card><code>调用子韦德娱乐平台</code>ChildCard,在这个标签中我们可以使用<code>@finished="finished"</code>侦听子韦德娱乐平台中自定义的事件。这意味着我们需要在父韦德娱乐平台中定义一个<code>finished()</code>方法。父韦德娱乐平台中定制了自定义属性的侦听器和触发它的方法。</p> <pre><code>&lt;!-- ParentCard.vue --&gt; &lt;template&gt; &lt;div class="card"&gt; &lt;div class="card-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;button @click="sendMessage" class="btn"&gt;给子韦德娱乐平台发送一个消息&lt;/button&gt; &lt;/div&gt; &lt;div class="card-body"&gt; &lt;child-card :parentMessage="parentMessage" @finished="finished"&gt;&lt;/child-card&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import ChildCard from "./ChildCard"; export default { name: "ParentCard", data: () =&gt; ({ theCardTitle: "父韦德娱乐平台", parentMessage: "" }), components: { ChildCard }, methods: { sendMessage() { this.parentMessage = `&lt;b&gt;消息来自父韦德娱乐平台:&lt;/b&gt; (^_^)!!!`; }, finished() { this.parentMessage = '' } } }; &lt;/script&gt; </code></pre> <p>在这个示例中,用户首先点击“发送消息”按钮,它将消息向下发送到子韦德娱乐平台,这个时候消息和一个新按钮会一起在子韦德娱乐平台中渲染。</p> <p><img src="/sites/default/files/blogs/2018/1809/parent-child-card-vue-2.png" alt="" /></p> <p>现在我们可点击“OK”按钮,它会向父韦德娱乐平台发出自定义的<code>finished</code>事件。在父韦德娱乐平台中,我们正在侦听该自定义事件,当侦听到<code>finished</code>自定义事件时,就会触发<code>finished()</code>方法,将<code>parentMessage</code>重置为空字符串。现在这个示例,我们实现了父韦德娱乐平台向子韦德娱乐平台和子韦德娱乐平台向父韦德娱乐平台传递数据(数据通讯)。</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/x9r1p84xmp?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <p>同样的,我们可以用张图来描述:</p> <p><img src="/sites/default/files/blogs/2018/1809/1_Nu1Jkw1hrFoKC5bUP-5TTA.png" alt="" /></p> <h3>通过回调函数实现子韦德娱乐平台向父韦德娱乐平台通讯</h3> <p>上面的示例是通过自定义事件完成子韦德娱乐平台向父韦德娱乐平台进行数据通讯。如果你不想发出自定义事件,还可以通过另一种方式将消息从子韦德娱乐平台发送到父韦德娱乐平台。和上一个示例不同之处是,我们不需要在子韦德娱乐平台中定义<code>ok()</code>方法,而是在父韦德娱乐平台中定义该方法。一旦我们在父韦德娱乐平台上定义了该方法,就可以通过<code>props</code>把信息从父韦德娱乐平台传递给子韦德娱乐平台。所以在<code>ParentCard</code>韦德娱乐平台中定义<code>ok()</code>方法,并且在<code>&lt;child-card&gt;</code>上绑定已定义该方法和<code>props</code>。</p> <pre><code>&lt;!-- ParentCard.vue --&gt; &lt;template&gt; &lt;div class="card"&gt; &lt;div class="card-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;button @click="sendMessage" class="btn"&gt;给子韦德娱乐平台发送一个消息&lt;/button&gt; &lt;/div&gt; &lt;div class="card-body"&gt; &lt;child-card :parentMessage="parentMessage" @finished="finished" :ok="ok"&gt;&lt;/child-card&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import ChildCard from "./ChildCard"; export default { name: "ParentCard", data: () =&gt; ({ theCardTitle: "父韦德娱乐平台", parentMessage: "" }), components: { ChildCard }, methods: { sendMessage() { this.parentMessage = `&lt;b&gt;消息来自父韦德娱乐平台:&lt;/b&gt; (^_^)!!!`; }, finished() { this.parentMessage = '' }, ok() { this.finished() } } }; &lt;/script&gt; </code></pre> <p>现在我们要做的是更新子韦德娱乐平台上的<code>props</code>。这样做的目的是通过<code>props</code>将回调函数从父韦德娱乐平台中传递到子韦德娱乐平台。我们可以像下面这样做:</p> <pre><code>&lt;!-- ChildCard.vue --&gt; &lt;template&gt; &lt;div class="message"&gt; &lt;div class="message-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;/div&gt; &lt;div class="message-body"&gt; &lt;p class="message-text" v-html="theCardBody"&gt;&lt;/p&gt; &lt;div v-if="parentMessage" class="alert" v-html="parentMessage"&gt;&lt;/div&gt; &lt;button v-if="parentMessage" @click="ok" class="btn"&gt;OK&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'ChildCard', props: ['parentMessage', 'ok'], data: () =&gt; ({ theCardTitle: '子韦德娱乐平台', theCardBody: '我是一个子韦德娱乐平台!(^_^) !!!' }) } &lt;/script&gt; </code></pre> <p>父韦德娱乐平台和子韦德娱乐平台之间的交互与上面的示例是相同的。</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/5v896299yk?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <p>你可以使用任何对你来说比较容易的方案,但是在从子韦德娱乐平台到父韦德娱乐平台的通讯中,发出自定义事件似乎是较为更流行的一种。</p> <p>现在我们知道如何通过<code>props</code>实现父韦德娱乐平台向子韦德娱乐平台之间的通讯以及如何通过自定义事件完成子韦德娱乐平台向父韦德娱乐平台之间的通讯。除了这两种之外,还有另外一种情形,那就是兄弟之间的韦德娱乐平台如何进行数据通讯。那么接下来,咱们就来学习这方面的知识。</p> <h2>兄弟韦德娱乐平台通讯</h2> <p>在Vue中实现兄弟韦德娱乐平台的通讯也有几种方法,其中一种方法是让父韦德娱乐平台允当两个子韦德娱乐平台之间的中间件(中继);另一种就是使用<code>EventBus</code>(事件总线),它允许两个子韦德娱乐平台之间直接通讯,而不需要涉及父韦德娱乐平台。</p> <h3>通过父韦德娱乐平台进行兄弟韦德娱乐平台之间通讯</h3> <p>先来看第一个方法,就是<strong>让兄弟韦德娱乐平台通过一个共同的父韦德娱乐平台彼此通讯</strong>。</p> <p>我们还是通过示例来学习。接下来的这个示例包含父韦德娱乐平台和两个子韦德娱乐平台,这两个子韦德娱乐平台是兄弟韦德娱乐平台。单击兄弟韦德娱乐平台上的按钮,可以看到他们之间可以相互通讯。</p> <p><img src="/sites/default/files/blogs/2018/1809/parent-child-card-vue-2.gif" alt="" /></p> <p>首先创建<code>ParentCard</code>韦德娱乐平台:</p> <pre><code>&lt;!-- ParentCard.vue --&gt; &lt;template&gt; &lt;div class="card"&gt; &lt;div class="card-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;button @click="momSaidChill" v-if="stopFighting()" class="btn"&gt;停止通讯&lt;/button&gt; &lt;/div&gt; &lt;div class="card-body"&gt; &lt;brother-card :messageSon="messageson" @brotherSaid="messageDaughter($event)"&gt;&lt;/brother-card&gt; &lt;sister-card :messageDaughter="messagedaughter" @sisterSaid="messageSon($event)"&gt;&lt;/sister-card&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import BrotherCard from './BrotherCard'; import SisterCard from './SisterCard' export default { name: 'ParentCard', data: () =&gt; ({ theCardTitle: '父韦德娱乐平台', messagedaughter:'', messageson:'' }), components: { BrotherCard, SisterCard }, methods: { messageDaughter(message) { this.messagedaughter = message; }, messageSon(message) { this.messageson = message; }, stopFighting() { if (this.messagedaughter &amp;&amp; this.messageson) { return true } return false }, momSaidChill() { this.messagedaughter = '', this.messageson = '' } } }; &lt;/script&gt; </code></pre> <p>创建<code>SisterCard</code>韦德娱乐平台:</p> <pre><code>&lt;!-- SisterCard.vue --&gt; &lt;template&gt; &lt;div class="message"&gt; &lt;div class="message-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;/div&gt; &lt;div class="message-body"&gt; &lt;p class="message-text"&gt;我是Sister韦德娱乐平台&lt;/p&gt; &lt;button @click="messageBrother" class="btn"&gt;给哥哥发消息&lt;/button&gt; &lt;div v-if="messageDaughter" class="alert" v-html="messageDaughter"&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'SisterCard', props: ['messageDaughter'], data: () =&gt; ({ theCardTitle: '子韦德娱乐平台2' }), methods: { messageBrother() { this.$emit('sisterSaid', '妈妈说,该做作业了!(^_^)!!!') } } } &lt;/script&gt; </code></pre> <p>接着创建<code>BrotherCard</code>韦德娱乐平台:</p> <pre><code>&lt;!-- BrotherCard.vue --&gt; &lt;template&gt; &lt;div class="message"&gt; &lt;div class="message-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;/div&gt; &lt;div class="message-body"&gt; &lt;p class="message-text"&gt;我是Brother韦德娱乐平台&lt;/p&gt; &lt;button @click="messageSister" class="btn"&gt;给妹妹发消息&lt;/button&gt; &lt;div v-if="messageSon" class="alert" v-html="messageSon"&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'BrotherCard', props: ['messageSon'], data: () =&gt; ({ theCardTitle: '子韦德娱乐平台1' }), methods: { messageSister() { this.$emit('brotherSaid', '妈妈说,该做作业了!(^_^)!!!') } } } &lt;/script&gt; </code></pre> <p>最终效果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/mo4m1m3zy?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <p>接下来简单看看这个实现过程。</p> <h4><code>SisterCard</code>通过<code>ParentCard</code>与<code>BrotherCard</code>通讯</h4> <p>首先来看<code>SisterCard</code>是如何与<code>BrotherCard</code>通讯的。从示例中可以看出,他们两之间的通讯是通过其父韦德娱乐平台<code>ParentCard</code>作为中间媒介来进行通讯的。</p> <p>我们在<code>SisterCard</code>韦德娱乐平台的<code>&lt;template&gt;</code>中为<code>messageBrother()</code>方法设置了一个<code>@click</code>事件来监听该事件。</p> <pre><code>&lt;button @click="messageBrother" class="btn"&gt;给哥哥发消息&lt;/button&gt; </code></pre> <p>当用户点击<code>SisterCard</code>中的“给哥哥发消息”将会触发<code>messageBrother()</code>方法。在这个方法中,将发出一个<code>sisterSaid</code>事件,并且把<code>妈妈说,该做作业了!(^_^)!!!</code>信息发送出去。</p> <pre><code>methods: { messageBrother() { this.$emit("sisterSaid", "妈妈说,该做作业了!(^_^)!!!"); } } </code></pre> <p>在<code>ParentCard</code>的<code>&lt;template&gt;</code>中定制了一个<code>@sisterSaid</code>事件侦听器,它触发了<code>messageSon()</code>方法。所以父韦德娱乐平台在这两个兄弟韦德娱乐平台之间起到了传递的作用。</p> <pre><code>&lt;sister-card :messageDaughter="messagedaughter" @sisterSaid="messageSon($event)"&gt;&lt;/sister-card&gt; </code></pre> <p>另外在<code>ParentCard</code>韦德娱乐平台中声明了<code>messageSon()</code>方法,该方法接受上面发送的自定义事件,并将其设置为<code>messageson</code>属性。</p> <pre><code>messageSon(message) { this.messageson = message; }, </code></pre> <p>这样一来,<code>ParentCard</code>韦德娱乐平台中<code>messageson</code>就由空字符串变成了<code>妈妈说,该做作业了!(^_^)!!!</code>。</p> <p>接着在<code>ParentCard</code>韦德娱乐平台自定义标签<code>&lt;brother-card&gt;</code>通过<code>:messageSon="messageson"</code>的方式将<code>messageson</code>属性绑定到<code>&lt;brother-card&gt;</code>。</p> <pre><code>&lt;brother-card :messageSon="messageson" @brotherSaid="messageDaughter($event)"&gt;&lt;/brother-card&gt; </code></pre> <p>这个时候在<code>BrotherCard</code>韦德娱乐平台中设置<code>props</code>的属性值为<code>messageSon</code>。这样就可以访问源自于<code>SisterCard</code>韦德娱乐平台的数据,并且该数据在<code>BrotherCard</code>中显示。</p> <pre><code>props: ["messageSon"], </code></pre> <p>最后在<code>BrotherCard</code>韦德娱乐平台就可以使用该数据。我们可以通过<code>v-if</code>指令来查看<code>messageSon</code>是否有任何有用的数据,如果有,那么就在<code>div.alert</code>中显示该消息:</p> <pre><code>&lt;div v-if="messageSon" class="alert" v-html="messageSon"&gt;&lt;/div&gt; </code></pre> <p>上面的描述过程也适用于<code>BrotherCard</code>通过<code>ParentCard</code>与<code>SisterCard</code>进行数据通讯。</p> <h3>通过EventBus进行兄弟间韦德娱乐平台通讯</h3> <p>随着应用程序越来越庞大,通过父韦德娱乐平台来传递所有内容会把事情变得越来越棘手。不过我们还有另一种选择,那就是使用<a href="http://www.xysjxj.com/quot;//vuetips.com/global-event-bus"><code>EventBus</code></a>架起兄弟之间通讯的桥梁。接下来看看我们是如何利用这一点一完成兄弟韦德娱乐平台之间的数据通讯。</p>" <p>我们同样基于上面的示例来做修改。接下来的示例中,<code>ParentCard</code>韦德娱乐平台包含了<code>SisterCard</code>和<code>BrotherCard</code>两个子韦德娱乐平台,而且这两个子韦德娱乐平台是兄弟韦德娱乐平台。</p> <p>首先在<code>main.js</code>文件中定义一个新的<code>eventBus</code>对象,其实他是一个全新的Vue实例:</p> <pre><code>// main.js import Vue from 'vue' import App from './App' export const eventBus = new Vue() new Vue({ el: '#app', render: h =&gt; h(App) }) </code></pre> <p>接着在新创建的<code>BrotherCard</code>韦德娱乐平台导入<code>main.js</code>:</p> <pre><code>&lt;!-- BrotherCard.vue --&gt; &lt;script&gt; import { eventBus } from '../main' &lt;/script&gt; </code></pre> <p><code>eventBus</code>实例现在将成为<code>BrotherCard</code>韦德娱乐平台中发出事件的实例。现在我们可以使用<code>eventBus.$emit</code>来替代上例中的<code>this.$emit</code>。<code>eventBus</code>是一个Vue实例,而且<code>eventBus</code>有这个<code>$emit</code>方法,这就是我们能够这么用的原因。这样做同样会触发相同的自定义事件名称和消息。</p> <pre><code>methods: { messageSister() { eventBus.$emit('brotherSaid', '妈妈说,该做作业了!(^_^)!!!') } } </code></pre> <p>同样可以在<code>SisterCard</code>韦德娱乐平台中引入<code>eventBus</code>:</p> <pre><code>&lt;script&gt; import { eventBus } from '../main' &lt;/script&gt; </code></pre> <p>将<code>created()</code>生命周期钩子添加到<code>SisterCard</code>韦德娱乐平台。在<code>created()</code>钩子中添加<code>eventBus</code>启动自定义事件的侦听器。当使用<code>SisterCard</code>韦德娱乐平台时,该侦听器将开始运行并且会保持运行。下面的代码只是侦听<code>brotherSaid</code>自定义事件,然后触发回调,将作为自定义事件有效负载传递的消息分配给<code>fromBrother</code>。</p> <pre><code>created() { eventBus.$on('brotherSaid', (message) =&gt; { this.fromBrother = message }) } </code></pre> <p>这样就可以有条件地显示来自<code>BrotherCard</code>的信息:</p> <pre><code>&lt;div v-if="fromBrother" class="alert" v-html="fromBrother"&gt;&lt;/div&gt; </code></pre> <p>上面看到的是如何通过<code>eventBus</code>实现<code>SisterCard</code>向<code>BrotherCard</code>传递数据的方式,反之,<code>BrotherCard</code>向SisterCard`传递数据也可以使用类似的方式。</p> <p>最终代码如下:</p> <pre><code>&lt;!-- SisterCard.vue --&gt; &lt;template&gt; &lt;div class="message"&gt; &lt;div class="message-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;/div&gt; &lt;div class="message-body"&gt; &lt;p class="message-text"&gt;我是Sister韦德娱乐平台&lt;/p&gt; &lt;button @click="messageBrother" class="btn"&gt;给哥哥发消息&lt;/button&gt; &lt;div v-if="fromBrother" class="alert" v-html="fromBrother"&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import { eventBus } from "../main"; export default { name: "SisterCard", data: () =&gt; ({ theCardTitle: "Sister Card", fromBrother: "" }), methods: { messageBrother() { eventBus.$emit("sisterSaid", "妈妈说,该做作业了!(^_^)!!!"); } }, created() { eventBus.$on("brotherSaid", message =&gt; { this.fromBrother = message; }); } }; &lt;/script&gt; &lt;!-- BrotherCard.vue --&gt; &lt;template&gt; &lt;div class="message"&gt; &lt;div class="message-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;/div&gt; &lt;div class="message-body"&gt; &lt;p class="message-text"&gt;我是Brother韦德娱乐平台&lt;/p&gt; &lt;button @click="messageSister" class="btn"&gt;给妹妹发消息&lt;/button&gt; &lt;div v-if="fromSister" class="alert" v-html="fromSister"&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import { eventBus } from "../main.js"; export default { name: "BrotherCard", data: () =&gt; ({ theCardTitle: "Brother Card", fromSister: "" }), methods: { messageSister() { eventBus.$emit("brotherSaid", "妈妈说,该做作业了!(^_^)!!!"); } }, created() { eventBus.$on("sisterSaid", message =&gt; { this.fromSister = message; }); } }; &lt;/script&gt; </code></pre> <p>最后创建的<code>ParentCard</code>韦德娱乐平台,我们可以像下面这样编码:</p> <pre><code>&lt;!-- ParentCard --&gt; &lt;template&gt; &lt;div class="card"&gt; &lt;div class="card-header"&gt; &lt;h5 v-text="theCardTitle"&gt;&lt;/h5&gt; &lt;/div&gt; &lt;div class="card-body"&gt; &lt;brother-card&gt;&lt;/brother-card&gt; &lt;sister-card&gt;&lt;/sister-card&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import BrotherCard from "./BrotherCard"; import SisterCard from "./SisterCard"; export default { name: "ParentCard", data: () =&gt; ({ theCardTitle: "Parent Card" }), components: { BrotherCard, SisterCard } }; &lt;/script&gt; </code></pre> <p>最终看到的效果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/50jkx4n5z4?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <h2>总结</h2> <p>在本伟德1946手机版中,我们学习了在Vue中如何实现韦德娱乐平台之间的通讯。通过实例看到了如何实现父韦德娱乐平台向子韦德娱乐平台,子韦德娱乐平台向父韦德娱乐平台以及兄弟韦德娱乐平台间的数据通讯。简单的根据为:</p> <ul> <li>通过<code>props</code>可以实现父韦德娱乐平台向子韦德娱乐平台发送数据</li> <li>通过自定义事件可以实现子韦德娱乐平台向父韦德娱乐平台发送数据</li> <li>兄弟韦德娱乐平台数据通讯除了借助共同的父韦德娱乐平台做为通讯桥梁之外,还可以通过<code>eventBus</code>来让兄弟之间韦德娱乐平台进行数据通讯</li> </ul> <p>最后用一张图来简单的描述一下:</p> <p><img src="/sites/default/files/blogs/2018/1809/parent-child-card-vue-3.jpg" alt="" /></p> <h2>参考阅读</h2> <ul> <li><a href="http://www.xysjxj.com/quot;//vegibit.com/vuejs-parent-child-communication/">VueJ" Parent Child Communication</a></li> <li><a href="http://www.xysjxj.com/quot;//vegibit.com/vue-sibling-component-communication/">Vu" Sibling Component Communication</a></li> </ul> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/640.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/vue"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/642.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue韦德娱乐平台</a></div></div></div> Sun, 09 Sep 2018 09:58:13 +0000 Airen 2455 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/managing-state-in-vue-js.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文根据<a href="http://www.xysjxj.com/quot;//medium.com/@hassan.djirdeh?source=post_header_lockup">@Hassa" Djirdeh</a>的《<a href="http://www.xysjxj.com/quot;//medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87">Managin" State in Vue.js</a>》一文所整理。</p> </blockquote> <p>Vue中管理应用程序的状态有多种不同的方法,了解状态管理也是学习Vue知识的基础部分,也是很重要的一部分。从这篇文章开始,我们来开始学习Vue应用程序中的状态管理。在这篇文章中会先简单的介绍Vue应用程序中状态管理的大多数方法。希望对Vue的学习者有所帮助。</p> <h2>状态管理</h2> <p><a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/642.html">Vue韦德娱乐平台</a>是Vue应用程序的构建中的一部分,允许我们在其中结合标记(HTML)、样式(CSS)和逻辑(JavaScript)。</p>" <p>接下来的示例<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/seven-ways-to-define-a-component-template-by-vuejs.html">将以单文件构建Vue韦德娱乐平台</a>的方式向大家呈现,该韦德娱乐平台显示<code>data</code>属性中<code>numbers</code>中的一系列数字:</p>" <pre><code>&lt;!-- NumberComponent.vue --&gt; &lt;template&gt; &lt;div&gt; &lt;h2&gt;The numbers are {{ numbers }}&lt;/h2&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'NumberComponent', data: () =&gt; ({ numbers: [1, 2, 3] }) } &lt;/script&gt; </code></pre> <p>效果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/m4o0ypzzx9?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <p>每个Vue韦德娱乐平台都包含一个<code>data()</code>函数,用于要响应的韦德娱乐平台。如果模板中使用的<code>data()</code>属性值发生更改,韦德娱乐平台视图将重新呈现以显示更改。</p> <p>在上面的示例中,<code>numbers</code>是存储在<code>data()</code>函数中的一个数组。如果另一个韦德娱乐平台要访问<code>data()</code>函数中的<code>numbers</code>,该怎么办呢?例如,我们可能需要一个韦德娱乐平台负责显示<code>numbers</code>(比如上面的示例),另一个韦德娱乐平台负责操作<code>numbers</code>的值。</p> <p>如果我们想在多个韦德娱乐平台之间共享<code>numbers</code>,那么<code>numbers</code>则不仅仅是韦德娱乐平台组别的<code>data</code>,而是应用程序级别的<code>data</code>。这就把我们带到了状态管理的主题 —— <strong>应用程序级别数据的管理</strong>。</p> <p>在我们讨论如何在应用程序中管理状态之前,我们首先要了解Vue中的<a href="http://www.xysjxj.com/quot;//vuejs.org/v2/guide/components-props.html#ad"><strong><code>props</code></strong></a>和<" href="http://www.xysjxj.com/quot;//vuejs.org/v2/guide/components.html#Sending-Messages-to-Parents-with-Events"><strong>自定义事件</strong></a>是<" href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/component-data-and-props-part1.html">如何在父韦德娱乐平台和子韦德娱乐平台之间共享数据</a>。</p>" <h2>Props和自定义事件</h2> <p>假设我们有一个应用程序,它包含父韦德娱乐平台和子韦德娱乐平台。和其他的韦德1946手机版客户端框架一样,Vue允许我们使用<code>props</code>将数据从父韦德娱乐平台传递到子韦德娱乐平台。</p> <p><img src="/sites/default/files/blogs/2018/1809/1_NnS55EuYE9Kks5eOXUrJJw.png" alt="" /></p> <p>使用<code>props</code>非常简单。我们实际上需要做的就是将一个值绑定到正在呈现的子韦德娱乐平台的<code>prop</code>属性上。下面是一个使用<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/v-bind.html"><code>v-bind</code>指令</a>向下传递一个数组值的示例:</p>" <pre><code>&lt;!-- ParentComponent.vue --&gt; &lt;template&gt; &lt;div&gt; &lt;ChildComponent :numbers="numbers" /&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import ChildComponent from './ChildComponent' export default { name: 'ParentComponent', data: () =&gt; ({ numbers: [1, 2, 3] }), components: { ChildComponent } } &lt;/script&gt; &lt;!-- ChildComponent.vue --&gt; &lt;template&gt; &lt;div&gt; &lt;h2&gt;{{ numbers }}&lt;/h2&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'ChildComponent', props: { numbers: Array } } &lt;/script&gt; </code></pre> <p><code>ParentComponent</code>韦德娱乐平台把<code>numbers</code>数组作为同名的<code>props</code>传递给<code>ChildComponent</code>韦德娱乐平台。<code>ChildComponent</code>韦德娱乐平台借助Mustache语法将<code>numbers</code>值绑定到其模板上。</p> <p>最终的效果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/n39kwrp0kp?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <blockquote> <p><strong><code>props</code></strong> 可以用于将数据从父韦德娱乐平台传递到子韦德娱乐平台!</p> </blockquote> <p>如果我们需要一个相反方向传递数据的方法(从子韦德娱乐平台传到父韦德娱乐平台),应该怎么办?比如上面的例,允许我们从子韦德娱乐平台的<code>data()</code>函数中引入一个新的<code>number</code>数组。</p> <p>我们不能再使用<code>props</code>来传递数据了,因为<code>props</code>只能单向传输数据(你从父到子到孙...等等)。为了便于让子韦德娱乐平台通知父韦德娱乐平台一些事情,我们可以使用Vue自定义事件。</p> <p><img src="/sites/default/files/blogs/2018/1809/1_Nu1Jkw1hrFoKC5bUP-5TTA.png" alt="" /></p> <p>Vue中的自定义事件与JavaScript原生的自定义事件非常相似,但有一个关键性的区别:<strong>Vue中的自定义事件主要用于韦德娱乐平台之间的通讯,而不是DOM节点之间的通讯</strong>!</p> <p>下面这个示例就是使用自定义事件,把<code>ChildComponent</code>中的<code>number</code>值传递给<code>ParentComponent</code>韦德娱乐平台,从而更改<code>ParentComponent</code>的<code>numbers</code>的示例:</p> <pre><code>&lt;!-- ParentComponent.vue --&gt; &lt;template&gt; &lt;div&gt; &lt;ChildComponent :numbers="numbers" @number-added="numbers.push($event)" /&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import ChildComponent from './ChildComponent'; export default { name: 'ParentComponent', data: () =&gt; ({ numbers: [1, 2, 3] }), components: { ChildComponent } } &lt;/script&gt; &lt;!-- ChildComponent.vue --&gt; &lt;template&gt; &lt;div&gt; &lt;h2&gt;{{ numbers }}&lt;/h2&gt; &lt;div class="form"&gt; &lt;input v-model="number" type="number" /&gt; &lt;button @click="$emit('number-added', Number(number))"&gt; Add new number&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'ChildComponent', props: { numbers: Array }, data: () =&gt; ({ number: 0 }) } &lt;/script&gt; </code></pre> <p><code>ChildComponent</code>韦德娱乐平台有一个捕获<code>number</code>值的<code>input</code>和捕获<code>number</code>值发出一个<code>number-added</code>自定义事件的按钮。</p> <p>在<code>ParentComponent</code>韦德娱乐平台上指定了由<code>@number-added</code>表示的自定义事件的监听器,其主要用于呈现子韦德娱乐平台。当该事件在子韦德娱乐平台中发出时,它将<code>number</code>的值推送到<code>ParentComponent</code>韦德娱乐平台的<code>numbers</code>数组中。最终的效果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/5kjv0l60xp?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <blockquote> <p><strong>自定义事件</strong>用于从子韦德娱乐平台到父韦德娱乐平台的通讯。</p> </blockquote> <p>我们可以<strong>使用<code>props</code>向下传递数据,使用自定义事件向上发送消息</strong>。我们如何能够传递数据和实现两个不同兄弟韦德娱乐平台之间的通讯呢?</p> <p><img src="/sites/default/files/blogs/2018/1809/1_c0v_myc_MbUdc8hFratiQg.png" alt="" /></p> <p>我们不能像上面那样使用自定义事件,因为这些事件是在特定韦德娱乐平台的接口中发出的,因此需要在韦德娱乐平台渲染的位置声明自定义事件侦听器。在两个独立的韦德娱乐平台中,一个韦德娱乐平台不会在另一个韦德娱乐平台中渲染。</p> <p>在Vue中大致有三种方式可以管理兄弟韦德娱乐平台之间的数据通讯,从而处理应用程序的状态管理:</p> <ul> <li>使用全局的EventBus</li> <li>使用简单的全局存储</li> <li>使用类似于Flux库的Vuex</li> </ul> <h2>EventBus</h2> <p>EvemtBus是一个Vue实例,用于支持独立韦德娱乐平台之间订阅和发布自定义事件。</p> <p>等等,我们不是说独立的韦德娱乐平台不能触发和监听彼此之间的自定义事件吗?他们通常不能,但是一个EventBus帮助我们实现这个目标,因为它是全局的,可以通用。</p> <p>下面的示例在<code>event-bus.js</code>创建了一个EventBus的实例:</p> <pre><code>// event-bus.js import Vue from 'vue' export const EventBus = new Vue() </code></pre> <p>我们现在可以使用<code>EventBus</code>的接口来发出事件(Emit Events)。假设我们有一个<code>NumberSubmit</code>韦德娱乐平台,它负责在单击按钮时发送自定义事件。这个自定义事件<code>number-added</code>将传递用户在<code>input</code>中输入的值:</p> <pre><code>&lt;!-- NumberSubmit.vue --&gt; &lt;template&gt; &lt;div class="form"&gt; &lt;input v-model="number" type="number" /&gt; &lt;button @click="addNumber"&gt;Add new number&lt;/button&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import { EventBus } from '../event-bus.js'; export default { name: 'NumberSubmit', data: () =&gt; ({ number: 0 }), methods: { addNumber(newNumber) { EventBus.$emit('number-added', Number(this.number)) } } } &lt;/script&gt; </code></pre> <p>现在我们可以有一个完全独立的韦德娱乐平台,比如<code>NumberDisplay</code>,它会显示一个数字值的列表,并监听<code>NumberSubmit</code>韦德娱乐平台中是否输入了一个新数值:</p> <pre><code>&lt;!-- NumberDisplay.vue --&gt; &lt;template&gt; &lt;div&gt; &lt;h2&gt;{{ numbers }}&lt;/h2&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import { EventBus } from '../event-bus.js'; export default { name: 'NumberDisplay', data: () =&gt; ({ numbers: [1, 2, 3] }), created() { EventBus.$on('number-added', number =&gt; { this.numbers.push(number) }) } } &lt;/script&gt; </code></pre> <p>我们在<code>NumberDisplay</code> 韦德娱乐平台的<code>created()</code>钩子中(它是<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/vue-instances-and-life-cycles.html">Vue生命周期</a>中的一个钩子函数)创建了一个<code>EventBus</code>监听器:<code>EventBus.$on</code>。当<code>NumberSubmit</code>韦德娱乐平台发送事件时,它将在事件对象中传递一个<code>number</code>值。<code>NumberDisplay</code>侦听并将该新<code>number</code>推送到其<code>numbers</code>数组中。</p>" <pre><code>&lt;!-- App.vue --&gt; &lt;template&gt; &lt;div id="app"&gt; &lt;NumberDisplay /&gt; &lt;NumberSubmit /&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import NumberDisplay from './components/NumberDisplay'; import NumberSubmit from './components/NumberSubmit'; export default { name: 'App', components: { NumberDisplay, NumberSubmit } } &lt;/script&gt; </code></pre> <p>最终效果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/xpov7z58jw?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <p>上面的示例回答了前面提出的问题:<strong>EventBus可以用来实现兄弟韦德娱乐平台之间的数据通讯</strong>!</p> <p><img src="/sites/default/files/blogs/2018/1809/1_WWekdHKKggw3LoKDjmQg5w.png" alt="" /></p> <p>是不是觉得设置和使用EventBus很容易,对吧?不幸的是,EventBus有一个明显的劣抛。假如我们的应用程序下面这样的:</p> <p><img src="/sites/default/files/blogs/2018/1809/1_AFvKqBuzpY6q2EezHoJ0Dw.png" alt="" /></p> <p>假设所有的白线箭头都是从父韦德娱乐平台向下传递到所有子韦德娱乐平台的<code>props</code>,而黄色的虚线箭头则是从韦德娱乐平台发出和监听事件。这些事件都没有被跟踪,并且可以在应用程序的任何地方触发。这使得维护工作变得非常困难,这可能会使代码难以工作,并且成为bug的来源。</p> <p>这是为什么Vue指南声明<a href="http://www.xysjxj.com/quot;//vuejs.org/v2/style-guide/#Non-flux-state-management-use-with-caution">EventBus不是Vue应用程序数据管理方法的主要原因之一</a>。</p>" <blockquote> <p><strong>EventBus</strong>是让所有韦德娱乐平台相互通讯的一种简单方法,但并适合中、大型的应用程序。</p> </blockquote> <h2>全局存储</h2> <p>让我们看看另一种处理应用程序数据通讯的方法。</p> <p>通过创建包含在韦德娱乐平台之间共享数据存储的存储模式,可以实现一些简单的状态管理。存储(Store)可以管理应用程序的状态以及负责更改状态的方法。</p> <p>例如,我们可以有一个像下面这样简单的存储:</p> <pre><code>// store.js export const store = { state: { numbers: [1, 2, 3] }, addNumber(newNumber) { this.state.numbers.push(newNumber) } } </code></pre> <p>在<code>store</code>中的<code>state</code>中包含了一个<code>numbers</code>数组,以及一个<code>addNumbers</code>方法,该方法接受接受有效负载并直接更新<code>state.numbers</code>的值。</p> <p>我们可以有一个韦德娱乐平台<code>NumberDisplay</code>用来显示来自<code>store</code>的<code>numbers</code>数组:</p> <pre><code>&lt;!-- NumberDisplay.vue --&gt; &lt;template&gt; &lt;div&gt; &lt;h2&gt;{{ storeState.numbers }}&lt;/h2&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import { store } from '../store.js'; export default { name: 'NumberDisplay', data: () =&gt; ({ storeState: store.state }) } &lt;/script&gt; </code></pre> <p>我们现在可以创建另一个韦德娱乐平台<code>NumberSubmit</code>,它允许用户向我们数据数组中添加一个新的数字:</p> <pre><code>&lt;!-- NumberSubmit.vue --&gt; &lt;template&gt; &lt;div class="form"&gt; &lt;input v-model="numberInput" type="number" /&gt; &lt;button @click="addNumber(numberInput)"&gt;Add new number&lt;/button&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import { store } from '../store.js'; export default { name: 'NumberSubmit', data: () =&gt; ({ numberInput: 0 }), methods: { addNumber(numberInput) { store.addNumber(Number(numberInput)) } } } &lt;/script&gt; </code></pre> <p><code>NumberSubmit</code>韦德娱乐平台中有一个<code>addNumber()</code>方法,它调用<code>store.addNumber()</code>变量并传递预期的有效负载。</p> <p><code>store</code>方法接收有效负载并直接改变<code>store.numbers</code>数组。由于<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/vue-reactivity.html">Vue的响应性</a>(Vu" reactivity),当存储状态中的<code>number</code>数组发生更改时,依赖于此值的相关DOM(<code>NumberDisplay</code>韦德娱乐平台中的<code>&lt;template&gt;</code>)会自动更新。</p> <p>当我们说韦德娱乐平台相互交互时。这些韦德娱乐平台不会对彼此做任何事情,而是通过存储相互调用更改。</p> <p><img src="/sites/default/files/blogs/2018/1809/1_UfyvxLR4pQDC4cVSnrwd3A.png" alt="" /></p> <p>然后在<code>App.vue</code>中引入刚才创建的韦德娱乐平台:</p> <pre><code>&lt;!-- App.vue --&gt; &lt;template&gt; &lt;div id="app"&gt; &lt;NumberDisplay /&gt; &lt;NumberSubmit /&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import NumberDisplay from './components/NumberDisplay'; import NumberSubmit from './components/NumberSubmit'; export default { name: 'App', components: { NumberDisplay, NumberSubmit } } &lt;/script&gt; </code></pre> <p>最终的效果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/9819zv2xxo?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <p>如果我们仔细观察所有与存储直接交互的所有部分,我们可以建立一个模式:</p> <ul> <li><code>NumberSubmit</code>中的方法有责任直接对存储方法进行操作,因此我们可以将其标记为 <strong>存储操作</strong> (Store action)</li> <li>存储方法也有一定的责任 —— 直接改变存储状态。 所以我们会说这是一个 <strong>存储变量</strong> (Store mutation)</li> <li><code>NumberDisplay</code>并不真正关心存储或<code>NumberSubmit</code>中方法类型,只关心存储中获取信息。所以我们会说韦德娱乐平台A是各种 <strong>Store getter</strong></li> </ul> <p>一个动作(Action)提交给一个变量(Mutation)。变量会改变状态,然后影响视图或韦德娱乐平台。视图或韦德娱乐平台使用 <strong><code>getter</code></strong> 检索存储数据。我们开始很接近类似Flux的状态管理。</p> <blockquote> <p>允许韦德娱乐平台依赖于外部存储,简单存储可以更易于管理应用程序的状态。</p> </blockquote> <h2>Vuex</h2> <p><a href="http://www.xysjxj.com/quot;//vuex.vuejs.org/">Vuex</a>是类似Flux的状态管理库,专门用于Vue的状态管理。</p>" <p>对于那些不熟悉的人来说,<a href="http://www.xysjxj.com/quot;//facebook.github.io/flux/docs/in-depth-overview.html">Flux</a>是Facebook创造的一种设计模式。Flux模式由四个部分组成,组成单向数据管道:</p>" <p><img src="/sites/default/files/blogs/2018/1809/1_ux5JOvTad6QUucqtw9vKgQ.png" alt="" /></p> <p>Vuex的灵感主要来自Flux和<a href="http://www.xysjxj.com/quot;//guide.elm-lang.org/architecture/">El" Architecture</a>。Vuex集成的核心是Vuex存储。</p> <pre><code>// store.js const store = new Vuex.Store({ state, mutations, actions, getters }) </code></pre> <p>Vuex存储(<strong>Vuex Store</strong>)包含四个对象:<code>state</code>、<code>mutations</code>、<code>actions</code>和<code>getters</code>。</p> <p><strong><code>state</code></strong> 只是一个包含需要在应用程序中共享的属性的对象。</p> <pre><code>// store.js const state = { numbers: [1, 2, 3] } </code></pre> <p>这个<code>state</code>对象只包含了一个<code>numbers</code>数组。</p> <p><code>mutations</code>是负责直接改变存储状态的函数。在Vuex中,<code>mutations</code>总是以<code>state</code>作为第一个参数。此外,<code>actions</code>也可以不作为第二个参数传递有效负载:</p> <pre><code>// store.js const mutations = { ADD_NUMBER(state, payload) { state.numbers.push(payload) } } </code></pre> <p>在Flux架构中,<code>mutations</code>中的函数通常用大写字母表示,以区别于其他函数,并用于工具和lint目的。在上面的示例中,创建了一个<code>ADD_NUMBER()</code>的<code>mutations</code>,它需要一个有效的<code>payload</code>并将该有效的<code>payload</code>推送到<code>state.numbers</code>数组中。</p> <p><code>actions</code>可以调用<code>mutations</code>。在提交<code>mutations</code>之前,<code>actions</code>还负责所有异步调用。<code>actions</code>可以访问<code>context</code>对象,该对象提供对<code>state</code>(使用<code>context.state</code>)、<code>getter</code>(使用<code>context.getters</code>)和<code>commit</code>函数(<code>context.commit</code>)的访问。</p> <p>下面是一个简单的<code>actions</code>的示例,它只是传递预期的有效负载时直接提交<code>mutations</code>:</p> <pre><code>// store.js const actions = { addNumber(context, number) { context.commit('ADD_NUMBER', number) } } </code></pre> <p>Vuex存储中的<code>getters</code>就像韦德娱乐平台中的<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/vue-computed-intro.html">计算属性</a>一样。<code>getters</code>主要用于执行一些计算和操作,以便在韦德娱乐平台访问这些信息之前存储状态。</p>" <p>像<code>mutations</code>一样,<code>getters</code>可以访问<code>state</code>作为第一个参数。这里有一个叫<code>getNumbers</code>的<code>getter</code>,它只返回<code>state.numbers</code>数组:</p> <pre><code>// store.js const getters = { getNumbers(state) { return state.numbers } } </code></pre> <p>最后<code>store.js</code>的代码如下所示:</p> <pre><code>import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); const state = { numbers: [1, 2, 3] }; const mutations = { ADD_NUMBER(state, payload) { state.numbers.push(payload); } }; const actions = { addNumber(context, number) { context.commit("ADD_NUMBER", number); } }; const getters = { getNumbers(state) { return state.numbers; } }; export default new Vuex.Store({ state, mutations, actions, getters }); </code></pre> <blockquote> <p>对于这样简单的一个示例,可能不一定需要Vuex存储。上面的示例只是用来向大家展示如何使用Vuex和简单的全局存储在实现上的直接区别。</p> </blockquote> <p>当Vuex存储准备好之后,Vue应用程序可以在Vue实例中声明<code>store</code>对象,可以提供给Vue应用程序使用。</p> <pre><code>// main.js import Vue from "vue"; import App from "./App"; import store from "./store"; new Vue({ el: '#app', store, components: { App }, template: '&lt;App /&gt;' }) </code></pre> <p>有了Vuex存储之后,韦德娱乐平台通常可以执行以下两种操作之一。他们要么:<strong>获取(<code>GET</code>)状态信息(通过访问<code>store</code>中<code>state</code>或<code>getters</code>)或者 调用(<code>DISPATCH</code>)<code>actions</code></strong>。</p> <p>下面创建的<code>NumberDisplay</code>韦德娱乐平台,它通过将<code>getNumbers</code>存储<code>getter</code>映射到韦德娱乐平台<code>getNumbers</code>计算属性来直接显示<code>state.numbers</code>数组。</p> <pre><code>&lt;!-- NumberDisplay.vue --&gt; &lt;template&gt; &lt;div&gt; &lt;h2&gt;{{ getNumbers }}&lt;/h2&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'NumberDisplay', computed: { getNumbers() { return this.$store.getters.getNumbers } } } &lt;/script&gt; </code></pre> <p>接着再创建一个<code>NumberSubmit</code>韦德娱乐平台,允许用户通过<code>addNumber</code>方法映射到同名的<code>actions</code>,然后将新输入的数字添加到<code>state.numbers</code>:</p> <pre><code>&lt;!-- NumberSubmit.vue --&gt; &lt;template&gt; &lt;div class="form"&gt; &lt;input v-model="numberInput" type="number" /&gt; &lt;button @click="addNumber(numberInput)"&gt;Add new number&lt;/button&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'NumberSubmit', data: () =&gt; ({ numberInput: 0 }), methods: { addNumber(numberInput) { this.$store.dispatch('addNumber', Number(numberInput)) } } } &lt;/script&gt; </code></pre> <p>最后在<code>App.vue</code>中引入前面创建的韦德娱乐平台:</p> <pre><code>&lt;!-- App.vue --&gt; &lt;template&gt; &lt;div id="app"&gt; &lt;NumberDisplay/&gt; &lt;NumberSubmit/&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import NumberDisplay from "./components/NumberDisplay"; import NumberSubmit from "./components/NumberSubmit"; export default { name: "App", components: { NumberDisplay, NumberSubmit } }; &lt;/script&gt; </code></pre> <p>最终的效果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/5xrv9p5vy4?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <p>我们可以看到,Vuex通过引入显式定义的<code>actions</code>、<code>mutations</code>和<code>getters</code> 扩展了简单的存储方法。这就是使用Vuex的最初标准和主要优势所在。此外,Vuex和vue-devtools集成在一起,提供了更易的调试功能。</p> <p>下图就是一个关于vue-devtools如何帮助我们在发生突变时观察存储信息:</p> <p><img src="/sites/default/files/blogs/2018/1809/1__KhPP7io0QJ06y_H7Ztl-Q.gif" alt="" /></p> <p>Vuex不是唯一个用来管理Vue状态的库,类似于Flux的库在社区中还有很多种,比如<a href="http://www.xysjxj.com/quot;//github.com/nadimtuhin/redux-vue"><code>redux-vue</code></a>或<" href="http://www.xysjxj.com/quot;//github.com/titouancreach/vuejs-redux"><code>vuejs-redux</code></a>,用于扩展<" href="http://www.xysjxj.com/quot;//redux.js.org/">Redux</a>。然而,由于Vuex是专门为Vue应用程序而定制的,因此它无疑是最容易与Vue应用程序集成在一起。</p>" <blockquote> <p>Vuex扩展了简单的存储方法,使我们的应用程序的状态管理变得更简单。</p> </blockquote> <h2>如何选择最合适的方法</h2> <p>很多时候,你会发现大家试图了解最佳方法是什么?我不一定相信有正确或错误的方法,因为每种方法都有其优点和缺点。</p> <h3>EventBus</h3> <ul> <li><strong>优点</strong>: 非常容易设置</li> <li><strong>缺点</strong>: 无法正确跟踪发生的变化</li> </ul> <h3>简单的存储</h3> <ul> <li><strong>优点</strong>: 相对容易建立</li> <li><strong>缺点</strong>: 状态和可能的状态变化没有明确定义</li> </ul> <h3>Vuex</h3> <ul> <li><strong>优点</strong>: 管理应用程序最强大的方法,并且与Vue开发工具集成在一起</li> <li><strong>缺点</strong>:额外的文件,需要花时间学习</li> </ul> <p>不管哪一种方法,都没有最好的方法,只有最适合的方法。我们应该根据自己的项目选择最适合项目的最佳方法。最后希望这篇文章对于想学习Vue的状态管理的同学有所帮助。</p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/translations"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">译文</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/vue"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/672.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue状态管理</a></div></div></div> Wed, 05 Sep 2018 14:46:18 +0000 Airen 2454 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/how-to-use-web-image.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><p>图片在Web上的使用占比已经非常的高,特别是在手淘互动这样的环境之下,我们每一个项目的图片使用量都非常的大。如果能把图片用好,的确是件不容易的事情,而且面对的挑战也不小。经常会碰到有关于图片如何加载,如何适配,如何优化等等。</p> <p>另外在移动端开发中,很多同学在Web上使用图片的方法一般都是通过<code>&lt;img&gt;</code>标签和CSS的<code>background-image</code>属性来处理。也正因如此,很多同学却忘记了这两者应该如何?怎么使用又是最优的。甚至有很多韦德1946手机版客户端同学都已经忘记了这两者的差异是什么?</p> <p>加上Web的技术不断革新,事实上除了前面提到的加载图片的方式之外,还有其他的方式,比如HTML5的<code>&lt;picture&gt;</code>(虽然这个元素标签曾经一度废弃过,但后来又添加回来了)。就算还是使用<code>img</code>标签,也有了新的优化,比如<code>img</code>的<code>srcset</code>属性。</p> <p>那么面对这么多的变化,以及使用的场景,我们应该怎么来选择,才是最优的选择。今天这篇文章,我们就来一起探讨一下这些东西,希望大家会喜欢。</p> <h2>Web引入图片的姿势</h2> <p>Web上如何引入图片,其实前面已经简单的提到过。这也是探索Web上图片最基础的部分。所以花点时间来简单的描述一下。一般在Web上引入图片,大体分为两种类型,其一是通过 <strong>HTML元素引入图片</strong>,其二是通过 <strong>CSS样式引入图片</strong>。</p> <h3>HTML元素引入图片</h3> <p>通过HTML元素在Web上引入图片最常见的方式是通过<a href="http://www.xysjxj.com/quot;//w3c.github.io/html/semantics-embedded-content.html#the-img-element"><code>&lt;img&gt;</code>元素</a>来引入。比如:</p>" <pre><code>&lt;img src="logo.png" alt="韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)" /&gt; </code></pre> <p><code>img</code>元素是通过<code>src</code>来引入图片资源(图片的路径),除此<code>src</code>属性之外,还有其属性,比如上例中的<code>alt</code>属性<strong>提供有意义的描述</strong>(当图片加载失败的时候,<code>alt</code>的内容将会显示在Web上。这些描述有助于提高您的网站的可访问性,因为它们能提供语境给屏幕阅读器及其他辅助性技术),<code>width</code>和<code>hegiht</code>等常用属性。在HTML5中还给<code>img</code>元素添加了两个新属性 <strong><a href="http://www.xysjxj.com/quot;//w3c.github.io/html/semantics-embedded-content.html#element-attrdef-img-srcset"><code>srcset</code></a></strong>" 和<strong><a href="http://www.xysjxj.com/quot;//ericportis.com/posts/2014/srcset-sizes/"><code>sizes</code></a></strong>。</p>" <p><code>srcset</code> 属性增强了 <code>img</code> 元素的行为,让您能够轻松地针对不同设备特性提供多种图片文件。类似于 CSS 原生的 <a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/safari-6-and-chrome-21-add-image-set-to-support-retina-images.html"><code>image-set</code> 函数</a>,<code>srcset</code> 也允许浏览器根据设备特性选择最佳图像,例如,在 <code>2x</code> 显示屏上使用 <code>2x</code> 图像,将来甚至允许在网络带宽有限时对 <code>2x</code> 设备使用 <code>1x</code> 图像。</p> <p>在不支持 <code>srcset</code> 的浏览器上,浏览器只需使用 <code>src</code> 属性指定的默认图像文件。 正因如此,无论设备能力如何,一定要始终包含一个在任何设备上都能显示的 <code>1x</code> 图像。如果 <code>srcset</code> 受支持,则会在进行任何请求之前对逗号分隔的图像/条件列表进行解析,并且只会下载和显示最合适的图像。 比如,你现在可以换个姿势使用<code>&lt;img&gt;</code>来根据不同的环境加载不同的图片:</p> <pre><code>&lt;img srcset="quilt_2/detail/large.jpg 1920w, quilt_2/detail/medium.jpg 960w, quilt_2/detail/small.jpg 480w" src="quilt_2/detail/medium.jpg" alt="Detail of the above quilt, highlighting the embroidery and exotic stitchwork."&gt; </code></pre> <p>另外,<code>img</code>的<code>sizes</code>属性也非常的强大。该属性可以使图片资源尺寸的值被用来指定图像的预期尺寸。当<code>srcset</code>使用<code>w</code>描述符时,用户代理使用当前图像大小来选择<code>srcset</code>中合适的一个图像<code>URL</code>。被选中的尺寸影响图像的显示大小。如果没有设置<code>srcset</code>属性,或者没值,那么<code>sizes</code>属性也将不起作用。来看一个简单的例子:</p> <pre><code>&lt;img src="lighthouse-200.jpg" sizes="50vw" srcset="lighthouse-100.jpg 100w, lighthouse-200.jpg 200w, lighthouse-400.jpg 400w, lighthouse-800.jpg 800w, lighthouse-1000.jpg 1000w, lighthouse-1400.jpg 1400w, lighthouse-1800.jpg 1800w" alt="a lighthouse"&gt; </code></pre> <p>在上面的例子中,渲染了一张宽度为视窗宽度一半(<code>sizes="50vw"</code>)的图像,根据浏览器的宽度及其设备像素比,允许浏览器选择正确的图像,而不考虑浏览器窗口有多大。</p> <p>上面我们看到的仅是<code>srcset</code>和<code>sizes</code>属性的冰山一角,有关于更详细的东西我们不在这里阐述,如果你感兴趣的话,可以观注后续的更新,因为我最近正在探讨如何借助这两个特性在移动端不同的设备上加载所需要的图像资源。</p> <p>在HTML5中新增了一个元素标签:<a href="http://www.xysjxj.com/quot;//html.spec.whatwg.org/multipage/embedded-content.html#the-picture-element"><code>&lt;picture&gt;</code></a>。该元素可以通过若干个<code>&lt;source&gt;</code>,而且浏览器会自动匹配<code>&lt;source&gt;</code>的<code>type</code>、<code>media</code>、<code>srcset</code>等属性,来找到最适合当前布局、视窗宽度、设备像素密度的图像资源。其次在该元素里面还可以内嵌一个<code>img</code>标签,用来作为其降级处理,一旦浏览器不支持<code>picture</code>元素,会自动引入内嵌的<code>img</code>引入的图片。比如下面这个示例:</p>" <pre><code>&lt;picture&gt; &lt;source media="(min-width: 800px)" srcset="head.jpg, head-2x.jpg 2x"&gt; &lt;source media="(min-width: 450px)" srcset="head-small.jpg, head-small-2x.jpg 2x"&gt; &lt;img src="head-fb.jpg" srcset="head-fb-2x.jpg 2x" alt="a head carved out of wood"&gt; &lt;/picture&gt; </code></pre> <p>上面的例子中,如果浏览器宽度至少为 <code>800px</code>,则将根据设备分辨率使用 <code>head.jpg</code> 或 <code>head-2x.jpg</code>。如果浏览器宽度在 <code>450px</code> 和 <code>800px</code> 之间,则将根据设备分辨率使用 <code>head-small.jpg</code> 或 <code>head-small-2x.jpg</code>。对于屏幕宽度小于 <code>450px</code>,且不支持 <code>picture</code> 元素向后兼容的情况,浏览器将渲染 <code>img</code> 元素,因此要始终包含该元素。</p> <p>是不是看上去<code>picture</code>有点类似于具备了<code>srcset</code>和<code>sizes</code>属性的<code>img</code>。</p> <h3>CSS引入图片</h3> <p>除了熟悉的<code>img</code>元素之外,在Web上还有另一种大家熟悉的方式是通过CSS的<code>background-image</code>属性来引入图片。其使用方式非常的简单:</p> <pre><code>.logo { background-image: url(logo.png); } </code></pre> <p>该属性除了可以给一个元素设置一个背景图像之外,还可以通过用逗号隔开引用多个<code>ulr()</code>,给元素设置多个背景图像。图像在绘制时,以<code>z</code>轴方向堆叠的方式进行。先指定的图像会在之后指的图像上面绘制。因此指定的第一个图像最接近用户。</p> <p>在CSS中我们可以通过一些相关的属性来控制<code>background-image</code>引入的图像的展示方式。比如<code>background-position</code>、<code>background-repeat</code>、<code>background-size</code>、<code>background-clip</code>等。除此之外,还可以使用<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/advanced-effects-with-css-background-blend-modes.html">CSS的混合模式</a>属性,比如<code>background-blend-mode</code>处理一些特殊效果。</p> <p>在CSS中除<code>background-image</code>之外,<a href="//drafts.csswg.org/css-images-4/#image-set-notation">CSS Images Module Level 4</a>提供了一个<code>image-set()</code>函数,可以帮助我们根据不同的<code>dpr</code>显示不同的图像:</p> <pre><code>.logo { background-image: image-set(url(logo@1x.png) 1x,url(logo@2x.png) 2x); } </code></pre> <p>当然,也可以在<code>image-set()</code>前面引入正常使用<code>background-image</code>的方式,给<code>image-set()</code>做降级处理:</p> <pre><code>.logo { background-image: image-set(url(logo@1x.png); background-image: image-set(url(logo@1x.png) 1x,url(logo@2x.png) 2x); } </code></pre> <p>在HTML中的<code>img</code>或者<code>picture</code>元素,我们可以通过<code>srcset</code>和<code>sizes</code>等特性为不同的状态(比如不同的视窗宽度)加载不同的图像。时至今日,CSS可以借助<a href="//www.w3.org/TR/css3-mediaqueries/">媒体查询特性</a>,显示不同的图像。比如媒体查询根据<a href="http://www.xysjxj.com/quot;//www.html5rocks.com/en/mobile/high-dpi/#toc-bg">设备像素比规则</a>,让你实现类似<code>image-set()</code>的特性,针对<code>2x</code>和<code>1x</code>显示不同的图像。</p>" <pre><code>@media (min-resolution: 2dppx),(-webkit-min-device-pixel-ratio: 2){ .logo { background-image: url(logo@2x.png) } } </code></pre> <p>Chrome、Firefox 和 Opera 都支持标准的 <code>(min-resolution: 2dppx)</code>,Safari 和 Android 浏览器则均需要不带 <code>dppx</code> 单位的旧版供应商前缀语法。请谨记,这些样式只有在设备与媒体查询匹配时才被加载,且必须为基础案例指定样式。 这样也能够确保当浏览器不支持分辨率特有的媒体查询时,一些内容仍可以得到渲染。</p> <pre><code>.logo { background-image: url(logo@1x.png); } @media (min-resolution: 2dppx), (-webkit-min-device-pixel-ratio: 2) { .logo { background-image: url(logo@2x.png); } } </code></pre> <p>您也可以使用 <code>min-width</code>(或<code>max-width</code>) 语法根据视口大小显示备用图像。 此方法的好处是,如果媒体查询不匹配,则图像不会被下载。 例如,只有在浏览器宽度等于 <code>500px</code> 或更大时,<code>bg.png</code> 才会被下载,然后应用于 <code>body</code>:</p> <pre><code>@media (min-width: 500px) { body { background-image: url(bg.png); } } </code></pre> <h2>图像处理方式</h2> <p>这里所说的图像处理方式并不像图像处理软件中一样的处理图像。这里所说的处理方式主要指的是<strong>图像样式</strong>的处理,其涵盖:效果处理和适配处理两个部分。</p> <h3>图像的效果处理</h3> <p>先不管是哪种方式引入的图像,在Web上总是会遇到图像效果的处理。最直接的,比如说处理图像的尺寸。在<code>img</code>中我们可以使用该元素自备的属性<code>width</code>和<code>height</code>来处理。当然也可以通过CSS的<code>width</code>、<code>height</code>、<code>max-width/height</code>和<code>min-width/hegiht</code>来处理。但要处理尺寸之外的一些效果,我们就只能通过CSS的属性来处理。</p> <p>到目前为止,通过CSS来处理图像的效果只要有CSS的 <a href="http://www.xysjxj.com/quot;//www.w3.org/TR/compositing-1/"><strong>混合模式</strong></a>" 和 <a href="http://www.xysjxj.com/quot;//www.w3.org/TR/filter-effects-1/"><strong>滤镜</strong></a>相关的属性。对于<code>img</code>或<code>picture</code>元素引入的图像,可以使用<code>mix-blend-mode</code>和<code>filter</code>。比如下面这个<" href="http://www.xysjxj.com/quot;//codepen.io/SaraSoueidan/full/09efabde7114d37a736525b5ab616bc5/"><code>mix-blend-mode</code>处理的图像效果</a>:</p>" <p><a href="http://www.xysjxj.com/quot;//codepen.io/SaraSoueidan/full/09efabde7114d37a736525b5ab616bc5/"><im" src="/sites/default/files/blogs/2018/1808/mix-blend-mode-example-2.png" alt="" /></a></p> <p>再来看一看<code>filter</code>处理图像的效果,<a href="http://www.xysjxj.com/quot;//twitter.com/una">@una</a>写的一" <a href="//www.cssfilters.co/">CSS滤镜工具</a>:</p> <p><a href="//www.cssfilters.co/"><img src="/sites/default/files/blogs/2018/1808/css-image-fliter-effect-1.gif" alt="" /></a></p> <p>事实上,<code>filter</code>不仅仅可以用于<code>img</code>上,还可以运用于任何的HTML元素上。另外,在CSS滤镜规范中还提供了一些<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/advanced-css-filters.html">高级滤镜的属性</a>,比如<code>backdrop-filter</code>和<code>filter()</code>属性,这些属性可以帮助我们实现一些更特殊的效果,比如iOS上的<a href="http://www.xysjxj.com/quot;//codeburst.io/ios-backdrop-effect-on-chrome-and-non-webkit-browsers-summary-of-currently-available-techniques-34b0f624ae6a">毛玻璃效果</a>:</p>" <p><img src="/sites/default/files/blogs/2015/1508/css-filters-2.jpg" alt="" /></p> <p>除此之外,CSS的混合模式的另一属性<code>background-blend-mode</code>常用于使用了背景图像的元素上,达到处理图像的效果:</p> <p><a href="http://www.xysjxj.com/quot;//bennettfeely.com/image-effects/"><im" src="/sites/default/files/blogs/2018/1808/0_pbOpSQo7_KpwEIeO.png" alt="" /></a></p> <p><a href="http://www.xysjxj.com/quot;//bennettfeely.com/image-effects">@bennettfeely</a>在<" href="http://www.xysjxj.com/quot;//codepen.io/airen/full/aaZaPP/">Codepen上写了一个很优秀的Demo</a>:</p>" <div style="margin-bottom: 20px;"><iframe id="aaZaPP" src="//codepen.io/airen/embed/aaZaPP?height=600&amp;theme-id=0&amp;slug-hash=aaZaPP&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="600" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>如果你觉得CSS的混合模式和滤镜很强大,希望深入的去探索相关的知识,建议你花点时间阅读下面相关的伟德1946手机版:</p> <ul> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/advanced-effects-with-css-background-blend-modes.html">CSS混合模式高级应用</a></li> <li><a href="//www.sarasoueidan.com/blog/compositing-and-blending-in-css/">Compositing And Blending In CSS</a></li> <li><a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/409.html">CS" 混合模式</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/ten-effects-with-css3-filter">CSS3 Filter的十种特效</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/advanced-css-filters.html">高级CSS filters</a></li> </ul> <p>上面我们看到的是CSS混合模式和滤镜怎么处理图像,但有的时候我们需要有一些特殊形状之类的效果,那么此时就需要<a href="//www.w3.org/TR/css-masking-1/">CSS Masking Module Level 1</a>提供的<code>mask-*</code>和<code>clip-path</code>属性。比如这样的效果:</p> <p><img src="/sites/default/files/blogs/2013/css-masking-3.jpg" alt="" /></p> <p>上图是使用<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/css-masking.html"><code>mask-image</code></a>实现的一个效果。<code>clip-path</code>就更强大了:</p> <p><a href="http://www.xysjxj.com/quot;//bennettfeely.com/clippy/"><im" src="/sites/default/files/blogs/2018/1808/clippy.jpg" alt="" /></a></p> <p>有关于CSS <a href="http://www.xysjxj.com/quot;//codepen.io/tag/mask/"><code>mask</code></a>和<" href="http://www.xysjxj.com/quot;//codepen.io/tag/clip-path/"><code>clip-path</code></a>效果,可以在Codepen上找到很多优秀案例。除了案例,互联网上优秀的伟德1946手机版也不少:</p>" <ul> <li><a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/339.html">CS" Masking</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/clipping-masking-css.html">CSS中的剪裁和遮罩</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/css-masking.html">如何在CSS中使用遮罩</a></li> <li><a href="http://www.xysjxj.com/quot;//www.html5rocks.com/en/tutorials/masking/adobe/">CS" Masking</a></li> <li><a href="//hacks.mozilla.org/2017/06/css-shapes-clipping-and-masking/">Shapes in clipping and masking – and how to use them</a></li> <li><a href="//css-tricks.com/clipping-masking-css/">Clipping and Masking in CSS</a></li> <li><a href="http://www.xysjxj.com/quot;//www.sarasoueidan.com/blog/svg-object-fit/">Croppin" &amp; Scaling Images Using SVG Documents</a></li> <li><a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/431.html">CS" <code>clip-path</code></a></li> <li>Using CSS Clip Path to Create Interactive Effects:<a href="//css-tricks.com/using-css-clip-path-create-interactive-effects/">Part1</a> and <a href="//css-tricks.com/using-css-clip-path-to-create-interactive-effects-part-ii/">Part2</a></li> <li><a href="//alligator.io/css/clipping-with-clip-path/">Introduction to Clipping Using clip-path in CSS</a></li> </ul> <h3>图像适配</h3> <p>图像适配估计让不少的同学头痛和蛋疼,特别是面对移动端开发的时候。在我的印象中,第一次接触图像适配的处理是在做<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/responsive">响应式网站</a>的时候。自从有了响应式网站设计,那么对于图像的处理,也有一个对应的专业术语:<strong>Responsiv" Image</strong>,对于响应式图像的处理也有很多优秀的文章:</p> <ul> <li><a href="http://www.xysjxj.com/quot;//cloudfour.com/thinks/responsive-images-101-definitions/">Responsiv" Images 101</a> (<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/509.html">译文</a>)</li>" <li><a href="http://www.xysjxj.com/quot;//cloudfour.com/thinks/responsive-images-201-client-hints/">Responsiv" Images 201: Client Hints</a></li> <li><a href="http://www.xysjxj.com/quot;//medium.freecodecamp.org/a-guide-to-responsive-images-with-ready-to-use-templates-c400bd65c433">" Guide to Responsive Images with Ready-to-Use Templates</a></li> <li><a href="http://www.xysjxj.com/quot;//docs.imgix.com/tutorials/responsive-images-srcset-imgix">Responsiv" Images with <code>srcset</code></a></li> <li><a href="http://www.xysjxj.com/quot;//www.zhangxinxu.com/wordpress/2014/10/responsive-images-srcset-size-w-descriptor/">响应式图片<code>srcset</code>全新释义<code>sizes</code>属性<code>w</code>描述符</a></li>" <li><a href="http://www.xysjxj.com/quot;//www.zhangxinxu.com/wordpress/2015/11/anatomy-of-responsive-images/">HTML5响应式图片技术中文图解</a></li>" <li><a href="http://www.xysjxj.com/quot;//efe.baidu.com/blog/responsive-images-in-practice/">实战响应式图片</a></li>" <li><a href="http://www.xysjxj.com/quot;//www.lullabot.com/articles/fundamentals-of-responsive-images">Fundamental" of Responsive Images</a></li> <li><a href="http://www.xysjxj.com/quot;//medium.com/9elements/building-a-responsive-image-e4c6229fa1f6">Buildin" a responsive image</a></li> <li><a href="http://www.xysjxj.com/quot;//responsiveimages.org/">Responsiv" images</a></li> </ul> <p>虽然响应式图像很强大,但我们平时面对的还是要想办法怎么灵活的处理图像。针对于这方面,我更喜欢把这些称为<strong><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/flexible-images.html">Flexible Images</a></strong>。而且平时也经常碰到同学会问,怎么让图像能更适合或适配容器,比如最常见的全屏图像这样的效果。</p> <p>时至今日,灵活处理图像有很多种方式,老旧一点的方案是使用<code>max-width</code>:</p> <pre><code>img { max-width: 100%; } </code></pre> <p>让一张<code>img</code>能较为灵活的适合容器。先进一点的是<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/css3-object-fit-and-object-position-properties.html"><code>object-fit</code></a>这样的属性,让你的图像能较灵活的适配容器:</p> <p><img src="/sites/default/files/blogs/2018/1808/object-fit-values.png" alt="" /></p> <p>感兴趣的同学,可以在下面的Demo体验一下下:</p> <div style="margin-bottom: 20px;"><iframe id="VGjVPy" src="//codepen.io/airen/embed/VGjVPy?height=400&amp;theme-id=0&amp;slug-hash=VGjVPy&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>前面提到过,引入图像除了<code>img</code>还有<code>background-image</code>,那么在CSS中还有一个<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/content/css3-background-size"><code>background-size</code>属性</a>类似于<code>object-fit</code>属性,可以帮助灵活的处理背景图像适配元素尺寸。</p> <p><img src="/sites/default/files/blogs/2018/1808/bZpL5.png" alt="" /></p> <blockquote> <p>有关于Flexible Images更详细的介绍,可以阅读早期整理的相关文章《<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/flexible-images.html">Flexible Images</a>》。</p> </blockquote> <p>另外一个场景是,图像能根据容器的长宽比来显示:</p> <p><img src="/sites/default/files/blogs/2014/1401/flexible-image-5.jpg" alt="" /></p> <p>在<a href="http://www.xysjxj.com/quot;//github.com/WICG/aspect-ratio">WICG的讨论</a>上,有人提出来了原生的长宽比属性<code>aspect-ratio</code>。例如,给定一个容器元素它的<code>width</code>和<code>height</code>都设置为<code>auto</code>,并且<code>aspect-ratio</code>的值为<code>2/1</code>,<code>max-height:200px</code>。一个容器宽度为<code>500px</code>时,元素首先会设置<code>width:500px</code>,然后根据<code>aspect-ratio</code>比例将<code>height</code>设置为<code>250px</code>。这个时候其实违反了<code>max-height</code>的约束。相反,容器大小会变成<code>height" 200px</code>和<code>width:400px</code>。另外,如果元素的<code>max-width是450px</code>时,长宽比将会完全被忽视,因为无法满足。</p> <p>这是未来的一个CSS属性,虽然离我们很远,但庆幸的是,有很多替代方案可以实现,最常见的就是<code>padding</code>来模拟:</p> <pre><code>.aspectration { position: relative; /*因为容器所有子元素需要绝对定位*/ height: 0; /*容器高度是由padding来控制,盒模型原理告诉你一切*/ width: 100%; } .aspectration[data-ratio="16:9"] { padding-top: 56.25%; } .aspectration[data-ratio="4:3"]{ padding-top: 75%; } </code></pre> <p>如果你要走时髦的话,你可以使用CSS自定义属性、<code>calc()</code>、<code>vw</code>,甚至CSS的Grid。我们来看其中一个示例吧:</p> <div style="margin-bottom: 20px;"><iframe id="ZMOVwo" src="//codepen.io/airen/embed/ZMOVwo?height=400&amp;theme-id=0&amp;slug-hash=ZMOVwo&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>有关于其他的方案或更详细的介绍,可以花点时间阅读下面的文章:</p> <ul> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/aspect-ratio-boxes.html">容器长宽比</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/aspect-ratio.html">CSS实现长宽比的几种方案</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/experiments-in-fixed-aspect-ratios.html">Web中如何实现纵横比</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/flexible-images.html">Flexible Images</a></li> <li><a href="//www.bram.us/2017/06/16/aspect-ratios-in-css-are-a-hack/">Aspect Ratios in CSS are a Hack</a></li> <li><a href="http://www.xysjxj.com/quot;//calculateaspectratio.com/">Aspec" Ratio Calculator</a></li> </ul> <h2>Web图片加载与渲染规则</h2> <p>前面我们花费了不少的篇幅介绍了Web中怎么加载图片,如何处理图片以及图片的适配。除了关注这几点之外,我们还应该关注图片加载与渲染的规则,因为只有掌握了这些才能更好的理解Web图像的运用。也能给用户更好的体验。那么接下来,咱们将花一点时间来了解一下图片加载和渲染规则。</p> <p>如果要对图片加载和渲染规则有较好的理解,就需要具备一些基础知识,比如说浏览器的工作原理。不过这部分已经超出这篇文章的范围,如果你感兴趣,可以花一些时间阅读下面几篇文章:</p> <ul> <li><a href="http://www.xysjxj.com/quot;//www.html5rocks.com/zh/tutorials/internals/howbrowserswork/">浏览器的工作原理:新式网络浏览器幕后揭秘</a></li>" <li><a href="http://www.xysjxj.com/quot;//coolshell.cn/articles/9666.html">浏览器的渲染原理简介</a></li>" <li><a href="http://www.xysjxj.com/quot;//www.zybuluo.com/yangfch3/note/671516">浏览器内核、J" 引擎、页面呈现原理及其优化</a></li> <li><a href="http://www.xysjxj.com/quot;//zhuanlan.zhihu.com/p/32751855">聊" JavaScript 与浏览器的那些事:引擎与线程</a></li> <li><a href="http://www.xysjxj.com/quot;//sylvanassun.github.io/2017/10/03/2017-10-03-BrowserCriticalRenderingPath/">浏览器渲染过程与性能优化</a></li>" <li><a href="http://www.xysjxj.com/quot;//lz5z.com/Web%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96-%E9%A1%B5%E9%9D%A2%E9%87%8D%E7%BB%98%E5%92%8C%E5%9B%9E%E6%B5%81/">We" 性能优化:页面重绘和回流</a></li> <li><a href="http://www.xysjxj.com/quot;//github.com/sunyongjian/blog/issues/34">从浏览器输入一" URL 到页面渲染</a></li> <li><a href="http://www.xysjxj.com/quot;//segmentfault.com/a/1190000006879700">从输入URL到页面加载发生了什么?</a></li>" </ul> <p>如果你阅读完上面这些文章,我相信你对浏览器渲染的工作原理有所了解,那么后续的内容就不会有什么难度了。</p> <p>先上一张图:</p> <p><img src="/sites/default/files/blogs/2018/1808/26868233-de8d0f06-4b9a-11e7-8b35-0c6bfbe9871b.png" alt="" /></p> <p>Web浏览器先会把获取到的HTML代码解析成一个DOM树,HTML中的每个标签都是DOM树中的一个节点,包括<code>display: none</code>隐藏的标签,还有JavaScript动态添加的元素等。浏览器会获取到所有样式,并会把所有样式解析成样式规则,在解析的过程中会去掉浏览器不能识别的样式。</p> <p>浏览器将会把DOM树和样式规则组合在一起(DOM元素和样式规则匹配)后将会合建一个渲染树(Render Tree),渲染树类似于DOM树,但两者别还是很大的:<strong>渲染树能识别样式,渲染树中每个节点(<code>NODE</code>)都有自己的样式,而且渲染树不包含隐藏的节点(比如<code>display:none</code>的节点,还有<code>&lt;/head&gt;</code>内的一些节点),因为这些节点不会用于渲染,也不会影响节点的渲染,因此不会包含到渲染树中</strong>。一旦渲染树构建完毕后,浏览器就可以根据渲染树来绘制页面了。</p> <p>简单的归纳就是浏览器渲染Web页面大约会经过六个过程:</p> <ul> <li>解析HTML,构成DOM树</li> <li>解析加载的样式,构建样式规则树</li> <li>加载JavaScript,执行JavaScript代码</li> <li>DOM树和样式规则树进行匹配,构成渲染树</li> <li>计算元素位置进行页面布局</li> <li>绘制页面,最终在浏览器中呈现</li> </ul> <p>是不是会感觉这个和我们图像加载渲染没啥关系一样,事实并非如此,因为<code>img</code>、<code>picture</code>或者<code>background-image</code>都是DOM树或样式规则中的一部分,那么咱们套用进来,图片加载和渲染的时机有可能是下面这样:</p> <ul> <li>解析HTML时,如果遇到<code>img</code>或<code>picture</code>标签,将会加载图片</li> <li>解析加载的样式,遇到<code>background-image</code>时,并不会加载图片,而会构建样式规则树</li> <li>加载JavaScript,执行JavaScript代码,如果代码中有创建<code>img</code>元素之类,会添加到DOM树中;如查有添加<code>background-image</code>规则,将会添加到样式规则树中</li> <li>DOM树和样式规则匹配时构建渲染树,如果DOM树节点匹配到样式规则中的<code>backgorund-image</code>,则会加载背景图片</li> <li>计算元素(图片)位置进行布局</li> <li>开始渲染图片,浏览器将呈现渲染出来的图片</li> </ul> <p>上面套用浏览器渲染页面的机制,但图片加载与渲染还是有一定的规则。因为,页面中不是所有的<code>&lt;img&gt;</code>(或<code>picture</code>)元素引入的图片和<code>background-image</code>引入的背景图片都会加载的。那么就引发出新问题了,什么时候会真正的加载,加载规则又是什么?</p> <p>先概括一点:</p> <blockquote> <p><strong>Web页面中不是所有的图片都会加载和渲染</strong>!</p> </blockquote> <p>根据前面介绍的浏览器加载和渲染机制,我们可以归纳为:</p> <ul> <li><code>&lt;img&gt;</code>、<code>&lt;picture&gt;</code>和设置<code>background-image</code>的元素遇到<code>display:none</code>时,图片会加载但不会渲染</li> <li><code>&lt;img&gt;</code>、<code>&lt;picture&gt;</code>和设置<code>background-image</code>的元素祖先元素设置<code>display:none</code>时,<code>background-image</code>不会渲染也不会加载,而<code>img</code>和<code>picture</code>引入的图片不会渲染但会加载</li> <li><code>&lt;img&gt;</code>、<code>&lt;picture&gt;</code>和<code>background-image</code>引入相同路径相同图片文件名时,图片只会加载一次</li> <li>样式文件中<code>background-image</code>引入的图片,如果匹配不到DOM元素,图片不会加载</li> <li>伪类引入的<code>background-image</code>,比如<code>:hover</code>,只有当伪类被触发时,图片才会加载</li> </ul> <p>感兴趣的同学,不仿自己写写Demo验证一下。</p> <h2>Web图像优化</h2> <p>Web图像通常占据了网页上下载字节的大部分,通常也占据了大量的视觉空间。因此,优化图像通常可以最大限度地减少下载的字节数以及提高网站性能:<strong>浏览器需要下载的字节越少,占用客户端的带宽就越少,浏览器下载并在屏幕上渲染有用内容的速度就越快。</strong></p> <p>图像优化既是一门艺术,也是一门科学:说它是一门艺术,是因为单个图像的压缩并不存在明确的最佳方案,说它是一门科学,则是因为有许多发展成熟的方法和算法都能够显著缩减图像的大小。找到图像的最佳设置需要在许多方面进行认真分析:格式能力、编码数据的内容、质量、像素尺寸等。</p> <p><a href="http://www.xysjxj.com/quot;//developers.google.com/web/resources/contributors/ilyagrigorik?hl=zh-cn">@Ily" Grigorik</a>在《<a href="http://www.xysjxj.com/quot;//developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/image-optimization?hl=zh-cn">图像优化</a>》一文中做了详细的介绍。下面几点是他给大家做图片优化时提供的参考意见:</p>" <ul> <li><strong>首选矢量格式</strong>:矢量图像与分辨率和缩放无关,这使它们成为多设备和高分辨率情况的完美选择。</li> <li><strong>缩小和压缩 SVG 资产</strong>: 大多数绘图应用程序生成的 XML 标记往往包含可以移除的多余元数据;确保您的服务器配置为对 SVG 资产采用 GZIP 压缩。</li> <li><strong>挑选最佳光栅图片格式</strong>:确定您的功能要求并选择适合每个特定资产的格式。</li> <li><strong>通过试验为光栅格式找到最佳质量设置</strong>:不要害怕调低“质量”设置,调低后的效果通常很不错,并且字节数的缩减很显著。</li> <li><strong>移除多余的图像元数据</strong>:许多光栅图像都包含多余的资产元数据:地理信息、相机信息等。请使用合适的工具删除这些数据。</li> <li><strong>提供缩放的图像</strong>:调整服务器上的图像尺寸,并确保图像的“显示”尺寸尽可能接近其“自然”尺寸。尤其要密切注意较大的图像,因为在调整尺寸时,它们占用的开销最大!</li> <li><strong>自动化、自动化、自动化</strong>:投资购置自动化工具和基础设施,这样可以确保您的所有图像资产始终得到优化。</li> </ul> <h2>结论</h2> <p>Web图片在Web中已经是很重要的一部分,理解或掌握了怎么在Web中使用图片是很重要的。可以让你的网站更具有体验性,或者从利益上说,他直接可以帮助你省不少银子。这篇文章,从怎么引入Web图片方式入手,以及如何处理图片,优化图片,甚至从浏览器的加载和渲染机制的基础上进行更深一步的判断,在实际使用中如何选择。比如是使用<code>img</code>、<code>picture</code>还是<code>background-image</code>。当然,使用图片不仅仅这些东西,涉及到的方方面面还是很多,只有彻底了解清楚这些东西,你才能更好的使用好图片。如果你感兴趣,不仿花一些时间阅读阅读《<a href="http://www.xysjxj.com/quot;//images.guide/">Image" guide</a>》一文。 我想你会喜欢上这篇指南的。</p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/html5"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">HTML5</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/68.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">CSS</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/228.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">image</a></div></div></div> Sun, 02 Sep 2018 06:32:01 +0000 Airen 2453 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/build-an-infinite-scroll-component-using-intersection-observer-api.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明:本文根据@Alex Jover Morales的《<a href="http://www.xysjxj.com/quot;//vueschool.io/articles/vuejs-tutorials/build-an-infinite-scroll-component-using-intersection-observer-api/">Buil" an Infinite Scroll component using Intersection Observer API</a>》一文所整理。</p> </blockquote> <p>在开发过程中,经常会遇到要处理大量数据的情况,比如列表、搜索记录等,因此你需要一种方法让用户以块状显示这些数据,以便保持应用程序性能和数据的有序性。</p> <p>你可能会使用分页韦德娱乐平台来处理,它可以轻松跳转,甚至一次跳转几个页面。</p> <p>当然,元素滚动是分页韦德娱乐平台的另一种替代方案,它可以提供更好的用户体验,特别是在移动端和可触摸设备上。当用记在页面上滚动时,它提供了一个透明的分页,给人一种没有结尾列表的感觉。</p> <p>自从<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/docs/Web/API/Intersection_Observer_API">Intersectio" Observer API</a>出现之后,构建无限滚动韦德娱乐平台变得更简单。让我们看看如何通过这个API来构建无限滚动韦德娱乐平台。</p> <h2>Intersection Observer API</h2> <p>Intersection Observer API提供了一个可订阅的模型,可以观察该模型,以便在元素进入视窗时得到通知。</p> <p>创建一个观察者实例很简单,我们只需要创建一个<code>IntersectionObserve</code>的新实例并调用<code>observe</code>方法,传递一个DOM元素:</p> <pre><code>const observer = new IntersectionObserver() const coolElement = document.querySelector('#coolElement') observer.observe(coolElement) </code></pre> <p>但是,当观察者进入<code>coolElement</code>视窗时,我们如何得到通知呢?<code>IntersectionObserver</code>构造函数接受一个回调作为参数,我们可以这样使用它:</p> <pre><code>const observer = new IntersectionObserver(entries =&gt; { const firstEntry = entries[0] if (firstEntry.isIntersecting) { // Handle intersection here... } }) const coolDiv = document.querySelector('#coolDiv') observer.observe(coolDiv) </code></pre> <p>如你所见,回调将接收<code>entries</code>作为其参数。它是一个数组,因为当你使用<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/docs/Web/API/Intersection_Observer_API">阈值</a>时,你可以有几个<code>entries</code>,但事实并非如此,所以我们只会得到第一个元素。然后我们可以使用<code>firstEntry.isIntersection</code>属性来检查它是否相交。这是进行异步请求并检索下一个页面数据的好方法。</p>" <p><code>IntersectionObserver</code>构造函数使用下面的方法接受选项对象(<code>options</code>)为其第二个参数:</p> <pre><code>const options = { root: document.querySelector('#scrollArea') rootMargin: '0px' threshold: 1.0 } const observer = new IntersectionObserver(callback, options) </code></pre> <p><code>rootMargin</code>对于我们的示例非常有用,因为它提供了一种定义<code>margin</code>的方法,观察者可以使用它来查找交集。默认情况下,它是<code>0</code>,表示观察者一进入视窗就会触发交叉事件(Intersect Event)。但是设置一个<code>400px</code>的<code>rootMargin</code>意味着交叉回调将在元素进入视窗之前<code>400px</code>位置处触发。</p> <p>因为<code>root</code>和<code>threshold</code>对于这种呢况没有什么意义(因此超出了范围),有关于这方面可以<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/docs/Web/API/Intersection_Observer_API">查阅文档</a>进行了解。</p>" <p>知道如何使用交点观察器,我们可以在列表末尾放置一个<code>Observable</code>韦德娱乐平台,以便在用户到达列表底部时添加更多数据。</p> <h2>Observer韦德娱乐平台</h2> <p>前面的例子很酷,对吧?但是对于我们来说有一个Vue韦德娱乐平台是很方便的,所以我们可以在我们的Vue应用程序中使用它。</p> <p>我们可以使用一个<code>mounted</code>钩子来创建我们需要保存在韦德娱乐平台状态变量中的观察者。使用<code>mounted</code>钩子而不是<code>created</code>钩子很重要,因为我们需要一个DOM元素来观察,而在<code>created</code>钩子中我们没有它:</p> <pre><code>// Observer.vue export default { data: () =&gt; ({ observer: null }), mounted() { this.observer = new IntersectionObserver(([entry]) =&gt; { if (entry &amp;&amp; entry.isIntersecting) { // ... } }) this.observer.observe(this.$el) } } </code></pre> <blockquote> <p>注意:我们在<code>[entry]</code> 参数上使用数组解构。这是一种速记方式,相当于获取<code>entries</code>数组并将第一个元素作为<code>entries[0]</code>访问。</p> </blockquote> <p>正如你所见,我们使用<code>this.$el</code>作为<code>root</code>元素以便观察DOM元素。</p> <p>为了使其可重用,我们需要让父韦德娱乐平台(使用<code>Observer</code>韦德娱乐平台的韦德娱乐平台)处理交叉的事件。为此,我们在它相交时发出一个自定义事件<code>intersect</code>:</p> <pre><code>export default { mounted() { this.observer = new intersectionObserver(([entry]) =&gt; { if (entry &amp;&amp; entry.isIntersecting) { this.$emit('intersect') } }) this.observer.observe(this.$el) } // ... } </code></pre> <p>根据韦德娱乐平台的模板,我们只需要任何元素,所以我们可以使用一个没有任何大小的<code>&lt;div&gt;</code>:</p> <pre><code>&lt;template&gt; &lt;div class="observer"/&gt; &lt;/template&gt; </code></pre> <p>最后,在韦德娱乐平台被销毁时清理观察者很重要,否则,我们将在应用程序中会造成内存泄漏,因为事件监听器不会被清除。我们可以在<code>destroyed</code>钩子中来调用<code>observer</code>的<code>disconnect</code>方法:</p> <pre><code>export default { destroyed() { this.observer.disconnect() } // ... } </code></pre> <p>你会发现还有<code>unobserve</code>方法。主要区别是:</p> <ul> <li><code>unobserve</code>:停止观察一个元素</li> <li><code>disconnect</code>:停止观察所有元素</li> </ul> <p>在我们的示例中,因为我们只有一个元素,所以它们都可以工作。</p> <p>我们还可以添加一个<code>options</code>属性,以便在需要使用<code>rootMargin</code>的情奖品下将<code>IntersectionObserver</code>选项传递给它。</p> <p>将所有内容放在<code>Observer.vue</code>韦德娱乐平台中:</p> <pre><code>&lt;!-- Observer.vue --&gt; &lt;template&gt; &lt;div class="observer" /&gt; &lt;/template&gt; &lt;script&gt; export default { props: ['options'], data: () =&gt; ({ observer: null }), mounted() { const options = this.options || {} this.observer = new IntersectionObserver(([entry]) =&gt; { if (entry &amp;&amp; entry.isIntersecting) { this.$emit('intersect') } }, options) this.observer.observe(this.$el) }, destroyed() { this.observer.disconnect() } } &lt;/script&gt; </code></pre> <h2>构建元限滚动</h2> <p>假设你有一个列表韦德娱乐平台,类似以下内容:</p> <pre><code>&lt;template&gt; &lt;div&gt; &lt;ul&gt; &lt;li class="list-item" v-for="item in items" :key="item.id"&gt;{{ item.name }}&lt;/li&gt; &lt;/ul&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { data: () =&gt; ({ items: [] }), async mounted() { const res = await fetch('https://jsonplaceholder.typicode.com/comment') this.items = await res.json() } } &lt;/script&gt; </code></pre> <blockquote> <p>请注意,代码中使用了<code>async</code>和<code>await</code>语法,使异步代码看起来很漂亮。有关于这方面的信息可以阅读<a href="//medium.com/@_bengarrison/javascript-es8-introducing-async-await-functions-7a471ec7de8a">这篇文章</a>。</p> </blockquote> <p>这个韦德娱乐平台使用<code>v-for</code>将<code>items</code>渲染到列表中。在<code>mounted</code>钩子中,它使用<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/docs/Web/API/Fetch_API/Using_Fetch"><code>Fetch</code>" API</a>从<code>jsonplaceholder.typicode.com</code>中获取一些模拟数据,用于填充<code>items</code>变量。</p> <h2>添加分页</h2> <p>它可以正常工作,但没有分页功能。为此,<code>jsonplaceholder.typicode.com</code>的端点允许我们使用<code>_page</code>和<code>_limit</code>来控制返回的数据。此外,我们需要一个<code>page</code>变量来跟踪,它从<code>1</code>开始。</p> <p>我来改变上面的代码,以便进行分页:</p> <pre><code>export default { data: () =&gt; ({ page: 1, items: [] }), async mounted() { const res = await fetch( `https://jsonplaceholder.typicode.com/comments?_page=${this.page}&amp;_limit=50` ) this.items = await res.json() } } </code></pre> <p>现在我们有分页功能了,每个页限制<code>50</code>个元素。</p> <h2>添加Observer韦德娱乐平台</h2> <p>我们仍然需要创建无限滚动韦德娱乐平台,接下来把<code>Observer</code>韦德娱乐平台引入进来。我们将在列表底部使用它,当它到达视窗时,它将获取下一页并增加该页。</p> <p>首先,导入<code>Observer</code>韦德娱乐平台,并将其添加到<code>InfiniteScroll</code>韦德娱乐平台中:</p> <pre><code>&lt;template&gt; &lt;div&gt; &lt;ul&gt; &lt;li class="list-item" v-for="item in items" :key="item.id"&gt;{{ item.name }}&lt;/li&gt; &lt;/ul&gt; &lt;Observer @intersect="intersected" /&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import Observer from './components/Observer' export default { // ... components: { Observer } } &lt;/script&gt; </code></pre> <p>最后,我们可以在<code>mounted</code>钩子上的代码移动<code>intersected</code>方法中。在<code>Observer</code>韦德娱乐平台的<code>intersect</code>定制事件。</p> <pre><code>export default { data: () =&gt; ({ page: 1, items: [] }), methods: { async intersected() { const res = await fetch(`https://jsonplaceholder.typicode.com/comments?_page=${this.page}&amp;_limit=50`) } this.page++ const items = await res.json() this.items = [...this.items, ...items] } } </code></pre> <p>请记信,我们必须增加页面。此外,现在我们必须将项添加到现有的<code>this.items</code>数组。我们通过在<code>this.items=[...this.items, ...items]</code>来实现。这基本上和老办法<code>this.items = this.items.concat(items)</code>是一样的。</p> <p>无限滚动韦德娱乐平台,全部代码看起来像下面这样:</p> <pre><code>&lt;!-- InfiniteScroll.vue --&gt; &lt;template&gt; &lt;div&gt; &lt;ul&gt; &lt;li class="list-item" v-for="item in items" :key="item.id"&gt;{{ item.name }}&lt;/li&gt; &lt;/ul&gt; &lt;Observer @intersect="intersected" /&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import Observer from './components/Observer' export default { data: () =&gt; ({ page: 1, items: [] }), methods: { async intersected() { const res = await fetch(`https://jsonplaceholder.typicode.com/comments?_page=${this.page}&amp;_limit=50`) this.page++ const items = await res.json() this.items = [...this.items, ...items] } }, components: { Observer } } &lt;/script&gt; </code></pre> <h2>总结</h2> <p>无限滚动韦德娱乐平台是数据分页的一种很好的展示方式,特别是在移动设备和可触摸设备上。通过添加<code>IntersectionObserver</code> API,它变得更加容易。在本文中,我们已经完成了自己构建一个无限滚动韦德娱乐平台所有步骤。</p> <p>请记住,如果你需要支持旧的浏览器,你可能需要<a href="http://www.xysjxj.com/quot;//github.com/w3c/IntersectionObserver/tree/master/polyfill">W3C的<code>IntersectionObserver</code></a>和<" href="http://www.xysjxj.com/quot;//github.com/github/fetch">Github上Fetc" Polyfill</a>。</p> <p>最终Demo效果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/yp92yr06y9?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/translations"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">译文</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/vue"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/642.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue韦德娱乐平台</a></div></div></div> Sun, 26 Aug 2018 08:54:24 +0000 Airen 2452 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/async-vuejs-components.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文根据@Alex Jover Morales的《<a href="http://www.xysjxj.com/quot;//vueschool.io/articles/vuejs-tutorials/async-vuejs-components/">Asyn" Vue.js Components</a>》一文所整理。</p> </blockquote> <p>随着应用程序越来越大,你开始考虑优化应用程序,使其变得更快。在此过程中,你可能使用了拆分代码和延迟加载这两种方法,它们通过将代码块的加截推迟到需要的时候加载,从而使应用程序的初始包变得更小。</p> <p>延迟加载对于应用程序路由有很大的意义,并且有很大的影响,因为每个路由都是应用程序的不同部分。</p> <p>延迟加载有意义的另一种情况是韦德娱乐平台延迟渲染。这些韦德娱乐平台可以是<code>tooltips</code>、<code>popover</code>、<code>modal</code>等,当然这些韦德娱乐平台也可以使用<a href="http://www.xysjxj.com/quot;//vuejs.org/v2/guide/components.html#Async-Components">异步韦德娱乐平台</a>。</p>" <p>让我们来看看如何在Vue中构建延迟加载异步韦德娱乐平台。</p> <h2>延迟加载韦德娱乐平台</h2> <p>在我们开始了解延迟加载韦德娱乐平台之前,我们先来了解通常是如何加载韦德娱乐平台的。 为此,我创建了一个<code>Tooltip.vue</code>韦德娱乐平台:</p> <pre><code>&lt;!-- Tooltip.vue --&gt; &lt;template&gt; &lt;h1&gt;Hi from Tooltip!&lt;/h2&gt; &lt;/template&gt; </code></pre> <p>这里没有什么特别之处,它就是一个简单的韦德娱乐平台。我们可以通过本地注册,导入<code>Tooltip</code>韦德娱乐平台并将其添加到<code>components</code>选项中,这样就可以在另一个韦德娱乐平台中使用它。比如,在<code>App.vue</code>中使用它:</p> <pre><code>&lt;!-- App.vue --&gt; &lt;template&gt; &lt;div id="app"&gt; &lt;Tooltip /&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import Tooltip from "./components/Tooltip" export default { name: "App", components: { Tooltip } }; &lt;/script&gt; </code></pre> <p>只要<code>App</code>被导入,就可以在初始加载时,<code>Tooltip</code>韦德娱乐平台就会被导入、使用和加载。但是想想:只有在我们要使用韦德娱乐平台时才加载该韦德娱乐平台难道没有意义吗?用户很可能在不需要工具提示的情况下浏览整个系统。</p> <p>为什么我们要在应用程序开始时花费宝贵的资源来加载韦德娱乐平台呢?我们可以应用延迟加载和代码拆分来改进它。延迟加载是在稍后的阶段加载某些内容的技术。</p> <p>虽然代码拆分是将一段代码拆分到一个单独的文件(称为<strong>chunk</strong>)中,以便减少应用程序的初始包,从而减轻初始加载。</p> <p>通过使用<a href="http://www.xysjxj.com/quot;//developers.google.com/web/updates/2017/11/dynamic-import">动态导入(Dynami" import)</a>,Vue可以轻松应用这些技术。在ES2018中也会具有这样的功能,它允许程序在运行时加载模块。我闪将有一篇文章深入探讨这些概念,但让我们从实用和简单的角度开始吧。</p> <p>现代的绑定器(Modern bundlers),比如<a href="http://www.xysjxj.com/quot;//webpack.js.org/">Webpack</a>(从版本2开始),<" href="http://www.xysjxj.com/quot;//rollupjs.org/">Rollup</a>和<" href="http://www.xysjxj.com/quot;//parceljs.org/">Parcel</a>将理解这种语法并自动为该模块创建一个单独的文件,该文件将在需要时加载。</p>" <p>我在这里认为你已经熟悉了静态方式导入模块。然而,动态导入是一个返回<code>Promise</code>的函数,其中包含模块作为其有效的加载。下面的示例展示了如何以静态导入和动态方式导入<code>utils</code>模块。</p> <pre><code>// 静态导入模块 import utils from './utils' // 动态导入 import('./utils').then(utils =&gt; { // 可以在这里使用utils模块 }) </code></pre> <p>在Vue中延迟加载韦德娱乐平台与在封装的函数中动态导入韦德娱乐平台一样容易。在前面的例子中,我们可以像下面这样延迟加载<code>Tooltip</code>韦德娱乐平台:</p> <pre><code>export default { components: { Tooltip: () =&gt; import('./components/Tooltip') } } </code></pre> <p>使用<code>() =&gt; import('./components/Tooltip')</code>替代前面示例中的<code>import Tooltip from "./components/Tooltip"</code>。Vue一旦请求渲染将会延迟加载该韦德娱乐平台。</p> <p>不仅如此,它还将应用代码拆分。你可以使用上面提到的任何绑定器运行代码来进行测试。最简单的方式就是使用<a href="http://www.xysjxj.com/quot;//github.com/vuejs/vue-cli">vue-cil</a>,但在文章的最后,你将找到一个已经构建好的Demo。运行后,打开开发者工具,在Network一栏将可可以看到一个名为<code>1.chunk.js</code>这样的JavaScript文件。</p>" <p><img src="/sites/default/files/blogs/2018/1808/Async_Vue_Components-1.png" alt="" /></p> <h2>有条件地加载一个异步韦德娱乐平台</h2> <p>在前面的示例中,尽管我们通过延迟加载来加载<code>Tooltip</code>韦德娱乐平台,但它将在需要渲染时立即加载,这在<code>App</code>韦德娱乐平台加载时就立即发生了。</p> <p>然而,在实践中,我们希望将<code>Tooltip</code>韦德娱乐平台加载能延迟到需要时加载,这通常是在触发某个事件之后有条件地进行,比如在按钮或文本上悬停时触发。</p> <p>为了简单起见,在<code>App</code>韦德娱乐平台中添加一个按钮,使用<code>v-if</code>有条件地渲染<code>Tooltip</code>韦德娱乐平台:</p> <pre><code>&lt;!-- App.vue --&gt; &lt;template&gt; &lt;div&gt; &lt;button @click="show = true"&gt;Load Tooltip&lt;/button&gt; &lt;div v-if="show"&gt; &lt;Tooltip /&gt; &lt;/div&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { data: () =&gt; ({ show: false }), components: { Tooltip: () =&gt; import('./components/Tooltip') } } &lt;/script&gt; </code></pre> <p>请记住,Vue在需要渲染之前不会使用该韦德娱乐平台。这意味着在点击之前不需要该韦德娱乐平台,并且该韦德娱乐平台将被延迟加载。</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/x2jqlll93o?view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <h2>异步韦德娱乐平台的用户体验</h2> <p>大多数情况下,异步韦德娱乐平台加载速度非常快,因为它们是从主包中拆分出来的小块代码。但是想象一下,你在一个非常慢的网络环境下缓慢的加载一个大的模态(Modal)韦德娱乐平台。这可能需要一些时间来加载和渲染。</p> <p>当然,你可以使用一些优化,比如HTTP缓存或<a href="http://www.xysjxj.com/quot;//www.machmetrics.com/speed-blog/guide-to-browser-hints-preload-preconnect-prefetch/">资源提示</a>,以低优先级预加载到内存中。事实上,新的<" href="http://www.xysjxj.com/quot;//github.com/vuejs/vue-cli">vue-cli</a>会对这些延迟加载的块预先获取。不过,在一些情况下,加载可能需要一些时间。</p>" <p>从用户体验的角度来看,<a href="http://www.xysjxj.com/quot;//www.sitepoint.com/make-users-enjoy-waiting/">如果一个任务需要超过<code>1s</code>的时间</a>,你就会开始失去用户的注意力。</p>" <p>但是,可以通过向用户提供反馈来保持注意力。为了吸引用户的注意力,我们可以在加载时使用<a href="http://www.xysjxj.com/quot;//babich.biz/progress-indicators/"><code>progress</code>(进度条)韦德娱乐平台</a>,但是在异步加载时,我们如何使用一个漂亮的<code>loading</code>或<code>progress</code>韦德娱乐平台呢?</p>" <h2>加载韦德娱乐平台</h2> <p>你还记得我们使用一个带有动态导入的函数来延迟加载异步韦德娱乐平台吗?</p> <pre><code>export default { components: { Tooltip: () =&gt; import('./components/Tooltip') } } </code></pre> <p>通过返回对象而不是动态导入的结果来定义异步韦德娱乐平台长期的方法。在该对象中,我们可以定个一个加载韦德娱乐平台:</p> <pre><code>const Tooltip = () =&gt; ({ component: import('./components/Tooltip'), loading: AwesomeSpinner }) </code></pre> <p>这样,在默认延迟<code>200ms</code>之后,韦德娱乐平台<code>AwesomeSpinner</code>就会显示出来。你也可以自定义延迟时间:</p> <pre><code>const Tooltip = () =&gt; ({ component: import('./components/Tooltip'), loading: AwesomeSpinner, delay: 500 }) </code></pre> <p>作为加载韦德娱乐平台应该使用的韦德娱乐平台必须尽可能的小,以使它几乎能立即加载</p> <h2>错误韦德娱乐平台</h2> <p>同样的,我们可以用延迟加载韦德娱乐平台的方式来定义一个错误韦德娱乐平台:</p> <pre><code>const Tooltip = () =&gt; ({ component: import('./components/Tooltip'), loading: AwesomeSpinner, error: SadFaceComponent }) </code></pre> <p>加载<code>./components/Tooltip</code>韦德娱乐平台出错时,<code>SadFaceComponent</code>韦德娱乐平台将会显示。在下面这几种情况之下可能会发生这种情况:</p> <ul> <li>网络瘫痪(连不上网)</li> <li>该韦德娱乐平台不存在(这是一种尝试它的好方法,你可以自己故意删除它)</li> <li>加载超时</li> </ul> <p>默认情况下,没有超时,但我们可以自己配置:</p> <pre><code>const Tooltip = () =&gt; ({ component: import('./components/Tooltip'), loading: AwesomeSpinner, error: SadFaceComponent, timeout: 5000 }) </code></pre> <p>现在,如果在<code>5000ms</code>之后<code>Tooltip</code>韦德娱乐平台还未加载,将会显示<code>SadFaceComponent</code>韦德娱乐平台。</p> <h2>总结</h2> <p>你已经了解了如何在自己的块文件中分割韦德娱乐平台,以及如何使用动态导入来延迟加载韦德娱乐平台。我们还通过有条件地渲染数据来延迟数据块的加载。</p> <p>虽然异步韦德娱乐平台可以通过分割和延迟的加载方式来提高应用程序的加载时间,但它们可能会对用户体验有很大的影响,尤其是当它们很大的时候。控制加载状态允许我们提供反馈,并在速度很慢的情况下让用户参与进来。</p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/translations"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">译文</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/vue"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/642.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue韦德娱乐平台</a></div></div></div> Sun, 26 Aug 2018 03:28:48 +0000 Airen 2451 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/reusing-logic-in-vue-components.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文根据@Alex Jover Morales的《<a href="http://www.xysjxj.com/quot;//vueschool.io/articles/vuejs-tutorials/reusing-logic-in-vue-components/">Reusin" Logic in Vue Components</a>》一文所整理。</p> </blockquote> <p>当你开始使用Vue创建应用程序时,你可以开始先创建韦德娱乐平台,来构建应用程序的不同部分。你应该可以感受到Vue和Web韦德娱乐平台结构体系的良好开发体验。随着项目的进行,你开始以某种方式构造应用程序韦德娱乐平台,可能是按页面和韦德娱乐平台。</p> <p>但随着项目的不断发展,你开始要在多个韦德娱乐平台之间执行重复的逻辑。我们常常说不要做重复的事情(DRY)和让一切保持它的简单。这两个原则利于我们编写和维护应用程序。</p> <p>也许你已经知道一些有助于遵循这些原则的模式、库和技术。Vuex将帮助你从韦德娱乐平台中提取状态逻辑,Vue路由器将对路由逻辑做同样的工作,但是韦德娱乐平台呢?</p> <p>我们经常遇到这样的情况,需要重用属于韦德娱乐平台的一些UI功能。例如,除了被锚定和定位到元素之外,弹出窗(Popover)和提示框(Tooltip)都可以在某个事件触发时共享打开和关闭的功能。</p> <p>在这篇文章中,我将采用一个<code>Colorful.vue</code>韦德娱乐平台的示例,该韦德娱乐平台监听滚动事件并根据滚动位置更改颜色:如果它高于窗口高度,则为蓝色,否则为红色。这是通过使用<code>:style</code>绑定<code>color</code>本地状态变量来实现:</p> <pre><code>&lt;!-- Colorful.vue --&gt; &lt;template&gt; &lt;div :style="{ background: color }"&gt;&lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { data: () =&gt; ({ color: 'red' }), methods: { handleScrollChange(status) { this.color = status === 'in' ? 'red' : 'blue' }, handleScroll() { if (window.scrollY &gt; window.innerHeight) { this.handleScrollChange('out') } else { this.handleScrollChange('in') } } }, mounted() { window.addEventListener('scroll', this.handleScroll) }, destroyed() { window.removeEventListener('scroll', this.handleScroll) } } &lt;/script&gt; </code></pre> <p>添加一点样式,方便我们看效果。这个时候你在页面中滚动鼠标时,能看到这样的效果:</p> <p><img src="/sites/default/files/blogs/2018/1808/vue-component-1.gif" alt="" /></p> <p>你可能没有在韦德娱乐平台中看到任何错误。它在<code>mounted</code>钩子中注册了<code>scroll</code>事件,并在<code>destroyed</code>钩子中删除<code>scroll</code>事件。它们调用一个<code>handleScroll</code>方法来检测滚动条位置,并调用<code>handleScrollChange</code>方法来改变颜色,具体是什么颜色取决于<code>status</code>参数。</p> <p>现在的滚动功能是在<code>Colorful</code>韦德娱乐平台中。但是,我们可以去掉<code>mounted</code>和<code>destroyed</code>的钩子和<code>handleScroll</code>方法,以便在其他韦德娱乐平台中重用它们。</p> <p>我们来看看不同的方法。</p> <h2>韦德娱乐平台继承</h2> <p>首先让我们将与滚动相关的行为移动到它自己的韦德娱乐平台<code>Scroll.js</code>中:</p> <pre><code>export default { methods: { handleScroll() { if (window.scrollY &gt; window.innerHeight) { this.handleScrollChange('out') } else { this.handleScrollChange('in') } } }, mounted() { window.addEventListener('scroll', this.handleScroll) }, destroyed() { window.removeEventListener('scroll', this.handleScroll) } } </code></pre> <p>正如你所见,这个滚动韦德娱乐平台需要有一个<code>handleScrollChange</code>,我们在子韦德娱乐平台中实现它,这意味着这个韦德娱乐平台必须被扩展并且不能单独工作。</p> <p>因为它只包含JavaScript,所以我们可以将它写在<code>.js</code>文件中,但如果需要,它也可以是<code>.vue</code>文件。请记住,只会继承<code>.vue</code>文件的JavaScript部分。</p> <p>然后,在<code>Colorful</code>韦德娱乐平台中删除我们移出的韦德娱乐平台行为,并使用<code>extends</code>韦德娱乐平台选项导入<code>Scroll</code>文件:</p> <pre><code>&lt;!-- Colorful.vue --&gt; &lt;template&gt; &lt;div :style="{ background: color }"&gt;&lt;/div&gt; &lt;/template&gt; &lt;script&gt; import Scroll from './Scroll' export default { extends: Scroll, data: () =&gt; ({ color: 'red' }), methods: { handleScrollChange(status) { this.color = status === 'in' ? 'red' : 'blue' } } } &lt;/script&gt; </code></pre> <p>请注意,韦德娱乐平台扩展不是类继承。在本例中,Vue合并父韦德娱乐平台和子韦德娱乐平台选项,从而创建一个新的混合对象。</p> <p>例如,在前面的示例中,我们最终会得到包含下面API的一个韦德娱乐平台:</p> <pre><code>{ data: () =&gt; ({ color: "red" }), methods: { handleScrollChange(status), handleScroll, }, mounted, destroyed }; </code></pre> <p>对于<code>mounted</code>和<code>destroyed</code>钩子来说,父节点和子节点都会被保留,并且它们将按照从父节点到子节点的继承顺序被调用。</p> <h2>Mixins</h2> <p>与韦德娱乐平台继承类似,我们可以使用<code>mixins</code>来共享韦德娱乐平台逻辑。但是在前面的示例中,我们只能从一个韦德娱乐平台继承,而使用<code>mixins</code>,我们可以组合它们的多个功能。</p> <p>事实上,我们不需要从上一个示例中的<code>Scroll.js</code>文件中更改任何内容。在<code>Colorful</code>韦德娱乐平台中只需使用<code>mixins</code>选项来替代<code>extends</code>选项就足够了。请记住,<code>mixins</code>需要一个数组:</p> <pre><code>&lt;!-- Colorful.vue --&gt; &lt;script&gt; import Scroll from './Scroll' export default { mixins: [Scroll], data: () =&gt; ({ color: 'red' }), methods: { handleScrollChange(status) { this.color = status === 'in' ? 'red' : 'blue' } } } &lt;/script&gt; </code></pre> <p>与韦德娱乐平台继承的主要区别在于顺序不同:在韦德娱乐平台继承中,子韦德娱乐平台中的钩子在父韦德娱乐平台之前执行,而<code>mixins</code>的钩子在韦德娱乐平台使用它之前执行它们的钩子。</p> <p>另外,<code>mixins</code>不能有任何<code>template</code>和<code>style</code>标签,它们只是普通的JavaScript。</p> <h2>编写可重用韦德娱乐平台</h2> <p>虽然继承和<code>mixins</code>看起来很容易实现,但它们在某种程度上是隐式的。在前面的示例中,当你使用<code>Scroll</code>功能时,需要知道必要的<code>handleScrollChange</code>方法。</p> <p>在这种情况下并没有那么糟糕,但是当你扩展或使用多个<code>mixins</code>时,事情可能会变得混乱,特别是开始追踪许多部分的功能时。</p> <p>另一种重用功能的方法是创建只接收<code>props</code>和<code>emit</code>,这样的方法可能是一种更明确的解决方案,既没有任何合并,也不需要共享上下文。此外,这种解决方案更加通用,因为相同的方法可以应用于任何其他基于韦德娱乐平台的技术。</p> <p>首先,我们必须使用<code>Scroll</code>的<code>.vue</code>韦德娱乐平台:</p> <pre><code>&lt;!-- Scroll.vue --&gt; &lt;template&gt; &lt;div&gt;&lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { methods: { handleScroll() { if (window.scrollY &gt; window.innerHeight) { this.$emit('scrollChange', 'out') } else { this.$emit('scrollChange', 'in') } } }, mounted() { window.addEventListener('scroll', this.handleScroll) }, destroyed() { window.removeEventListener('scroll', this.handleScroll) } } &lt;/script&gt; </code></pre> <p>在本例中,我们不是调用<code>handleScrollChange</code>方法,而是发出一个<code>scrollChange</code>事件,父韦德娱乐平台可以使用这个事件来完成它的工作。</p> <p>然后,在<code>Colorful.vue</code>中,我们必须奖其作为韦德娱乐平台导入并处理<code>scrollChange</code>事件:</p> <pre><code>&lt;!-- Colorful.vue --&gt; &lt;template&gt; &lt;scroll @scrollChange="handleScrollChange" :style="{ background:color }"&gt;&lt;/scroll&gt; &lt;/template&gt; &lt;script&gt; import Scroll from './Scroll' export default { components: { Scroll }, data: () =&gt; ({ color: 'red' }), methods: { handleScrollChange(status) { this.color = status === 'in' ? 'red' : 'blue' } } } &lt;/script&gt; </code></pre> <p>我们已经通过<code>scroll</code>替换了<code>div</code>标签,但是<code>Colorful</code>韦德娱乐平台逻辑保持不变。</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/9jpvolnj6r" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <h2>总结</h2> <p>我们已经看到三种方法来提取滚动功能并重用它。根据你自己的情况,你将选择其中的任何一种方法。</p> <p>韦德娱乐平台继承和<code>mixins</code>为我们提供了一种分离韦德娱乐平台逻辑的一部分并将它们合在一起的方法,这种方法在某些情况下适用,前提条件是它不会使用你的项目管理变得太混乱。特别是<code>mixins</code>,它更强大,因为它允许将多个<code>mixins</code>组合在一起。</p> <p>使用韦德娱乐平台组合是一种更清晰,更明确的解决方案。同样的技术也可以用于使用相同技术的其他框架。但在某些情况下,它可能不如<code>mixins</code>那么方便。</p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/translations"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">译文</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/vue"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/642.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue韦德娱乐平台</a></div></div></div> Fri, 24 Aug 2018 14:17:27 +0000 Airen 2450 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/trigonometry-you-must-know.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文转载<a href="http://www.xysjxj.com/quot;//weibo.com/joueu">@Helkyle</a>的《<" href="http://www.xysjxj.com/quot;//w3ctrain.com/2018/08/20/trigonometry-you-must-know/">三角函数在韦德1946手机版客户端动画中的应用</a>》一文。</p>" </blockquote> <p>我是个很懒的人,开发过程中经常有意无意地刻意避开数学相关的知识,你也知道解数学题非常枯燥无趣。平时写<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/532.html">动画</a>也尽量使" <a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/29.html">CSS" 来实现</a>,<code>timing-function</code> 随意选用,最多也就调一下 <a href="http://www.xysjxj.com/quot;//pomax.github.io/bezierinfo/"><code>cubic-bezier</code></a>,找到看着舒服的就行。但是怎样让动画更顺滑,写出更贴近自然的动画,说实话以前我没怎么考虑过。</p>" <p>每次当动效设计师提出,能不能这样那样的时候,我会理所当然地予以否决。所以有很长一段时间,我非常羡慕那些能用 <a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/604.html"><code>canvas</code></a>" 绘制很酷炫的动画的程序员。</p> <div style="margin-bottom: 20px;"><iframe id="RYWbrJ" src="//codepen.io/airen/embed/RYWbrJ?height=400&amp;theme-id=0&amp;slug-hash=RYWbrJ&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <blockquote> <p>先这样吧,又不是不会动。</p> </blockquote> <p>今天来分享一下三角函数相关的内容,<strong>如果刚学韦德1946手机版客户端的时候有人教我这些,我会很开心</strong>。</p> <h2>三角函数</h2> <p><a href="http://www.xysjxj.com/quot;//zh.wikipedia.org/wiki/%E4%B8%89%E8%A7%92%E5%87%BD%E6%95%B0">三角函数</a>已经是老生常谈了(街舞圈称之" <strong>Old School</strong>),它伴随我们从初中到大学,太多的公式定理,光是应付考试就花了不少时间。先简单回顾一下,确保你还记得基础知识。</p> <h3>勾股定理</h3> <p>最开始学三角函数的时候就是从背勾三股四弦五开始,<a href="http://www.xysjxj.com/quot;//zh.wikipedia.org/zh-hans/%E5%8B%BE%E8%82%A1%E5%AE%9A%E7%90%86">勾股定理</a>描述的是对于直角三角形,直角两条边的平方和等于斜边的平方:</p>" <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-2.png" alt="" /></p> <h3>常用三角函数</h3> <p>印象中教科书里面只保留了 <code>sin</code>, <code>cos</code>, <code>tan</code>,其他可以通过变换得到。</p> <pre><code>sinθ = a / h cosθ = b / h tanθ = a / b </code></pre> <h3>极坐标系和单位圆</h3> <p>在<a href="http://www.xysjxj.com/quot;//zh.wikipedia.org/zh/%E7%AC%9B%E5%8D%A1%E5%B0%94%E5%9D%90%E6%A0%87%E7%B3%BB">笛卡尔直角坐标</a>系中,任一" <code>(x, y)</code> 都可以转化成<a href="http://www.xysjxj.com/quot;//zh.wikipedia.org/zh-hans/%E6%9E%81%E5%9D%90%E6%A0%87%E7%B3%BB">极坐标</a>表" <code>(r, θ)</code>,其中:</p> <pre><code>r = Math.sqrt(x^2 + y^2) θ = Math.atan2(y, x) </code></pre> <p>单位圆的定义是半径为单位长度的圆,圆上任意一点的横坐标就是对应角度的余弦值,任意点的纵坐标就是对应角度的正弦值。</p> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-3.gif" alt="" /></p> <h3>简单的图像变换</h3> <p>以<a href="http://www.xysjxj.com/quot;//zh.wikipedia.org/zh/%E6%AD%A3%E5%BC%A6%E6%9B%B2%E7%B7%9A">正弦曲线</a>为例,对函数进行简单的变换,得到不一样的结果。</p>" <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-4.png" alt="" /></p> <p>正弦曲线公式:<code>y = A sin(Bx + C) + D</code></p> <ul> <li><code>A</code> 控制振幅,<code>A</code> 值越大,波峰和波谷越大,<code>A</code> 值越小,波峰和波谷越小;</li> <li><code>B</code> 值会影响周期,<code>B</code> 值越大,那么周期越短,<code>B</code> 值越小,周期越长。</li> <li><code>C</code> 值会影响图像左右移动,<code>C</code> 值为正数,图像右移,<code>C</code> 值为负数,图像左移。</li> <li><code>D</code> 值控制上下移动。</li> </ul> <p>这个公式非常有用,如果下文的代码让你不解,记得回来查看注解。</p> <p>简单得回顾一下之后,确保你还记得这些基础知识,那么这些曾经被得滚瓜烂熟的内容,和韦德1946手机版客户端代码结合会变成什么样?</p> <h2>常见的应用场景</h2> <h3>图像应用</h3> <p>最直观的应用是使用三角函数来绘制 Wave 曲线</p> <pre><code>for (let x = 0; x &lt; width; x++) { const y = Math.sin(x * a) * amplitude } </code></pre> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-5.jpeg" alt="" /></p> <pre><code>for (let x = 0; x &lt; width; x++) { const radians = x / width * Math.PI * 2 const scale = (Math.sin(radians - Math.PI * 0.5) + 1) * 0.5 const y = Math.sin(x * 0.02 + xSpeed) * amplitude * scale } </code></pre> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-6.gif" alt="" /></p> <div style="margin-bottom: 20px;"><iframe id="LJpPVj" src="//codepen.io/airen/embed/LJpPVj?height=400&amp;theme-id=0&amp;slug-hash=LJpPVj&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>之前掘金上很火的<a href="http://www.xysjxj.com/quot;//juejin.im/post/5b4ffa045188251b134e7211">一篇文章</a>,也是同样的道理,使用两层正弦函数绘制曲线,<code>fill</code>" 之后加上 <code>stagger</code> 动画,就能得到非常酷炫的水波效果。</p> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-7.gif" alt="" /></p> <p>如果再结合鼠标位置 + <code>lerp</code> 动画,就能实现<a href="http://www.xysjxj.com/quot;//www.smartisan.com/u1/#/overview">坚果首页</a>同款的动画。</p>" <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-8.gif" alt="" /></p> <div style="margin-bottom: 20px;"><iframe id="wEKwKN" src="//codepen.io/airen/embed/wEKwKN?height=400&amp;theme-id=0&amp;slug-hash=wEKwKN&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>这篇文章大部分代码都可以在我的 <a href="http://www.xysjxj.com/quot;//codepen.io/HelKyle/">Codepe" 主页</a>看到。</p> <h3>SlowInSlowOut</h3> <p>正余弦曲线有很自然地缓入缓出的特性,并且在一个周期里面从 <code>-1</code> 到 <code>1</code> 再回到 <code>-1</code>,非常适合用来模拟一些物理效果。因为真实世界里面,汽车都是缓慢启动,加速,减速,再缓慢减速直到速度变为 <code>0</code> 的,突变会让人很难受。左边的摆球是线性匀速摆动,右边是用了三角函数优化的结果。显然左边的效果设计师会打人。</p> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-9.gif" alt="" /></p> <div style="margin-bottom: 20px;"><iframe id="qMOWNX" src="//codepen.io/airen/embed/qMOWNX?height=400&amp;theme-id=0&amp;slug-hash=qMOWNX&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>只需使用 <code>sin</code> 或 <code>cos</code> 乘以最大角度,就可以得到在摆动最大角度之间的 <code>SlowInSlowOut</code>。</p> <pre><code>ctx.rotate(Math.cos(t / 180 * Math.PI) * Math.PI * 0.25) </code></pre> <h3>角度控制</h3> <p>在开发过程中,我们常常需要跟角度打交道,比如在头像左上角(<code>45deg</code>)显示 <code>Notification</code> 红点,用鼠标控制 <code>rotation</code> 等等。</p> <p>韦德1946手机版客户端 JS 里面 <code>Math.atan2(y, x)</code> 可以用来计算 <code>(x, y)</code> 和 <code>x</code> 轴正方向的夹角弧度值。</p> <pre><code>function getCurrentDegree () { const deltaX = mouse.x - window.innerWidth * 0.5 const deltaY = mouse.y - window.innerHeight * 0.5 return Math.atan2(deltaY, deltaX) * 180 / Math.PI } </code></pre> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-10.gif" alt="" /></p> <div style="margin-bottom: 20px;"><iframe id="GXpKNY" src="//codepen.io/airen/embed/GXpKNY?height=400&amp;theme-id=0&amp;slug-hash=GXpKNY&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>插一句,三角函数相关的动画并不一定需要用 JS 来写,比如下面的 DEMO,使用 <code>compass</code> 依赖,同样可以做到灵活控制在特定角度的动画(千万不要手写各个点的坐标!!!后期没办法维护)</p> <pre><code>@import "compass"; .checkbox:checked { ~ button { $per: 180 / 4; @for $i from 1 through 6 { &amp;:nth-of-type(#{$i}) { $angle: $per * ($i - 1) * 1deg + 180deg; $x: cos($angle) * $d; $y: sin($angle) * $d; transform: translate($x, $y) rotate(0deg) ; } } } } </code></pre> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-11.gif" alt="" /></p> <div style="margin-bottom: 20px;"><iframe id="EeVYWm" src="//codepen.io/airen/embed/EeVYWm?height=400&amp;theme-id=0&amp;slug-hash=EeVYWm&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>Case Study</h2> <p>经常用到的场景大概就这些吧,再以一个案例分析来复习一下。</p> <p>前两天在 Codepen 首页看到热门推荐,作者用存 CSS 动画来实现一个行走的动画,挺新颖的,然而仔细一看,脚步的动画真心觉得别扭,于是想用三角函数优化一下。</p> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-1.gif" alt="" /></p> <p>绘制头部:</p> <pre><code>drawHead (t) { ctx.save() ctx.beginPath() ctx.translate(0, Math.sin(t) * 4) ctx.arc(80, -35, 35, 0, 2 * Math.PI) ctx.fill() ctx.closePath() ctx.restore() } </code></pre> <p>我会给每个方法传入周期参数 <code>t</code>, <code>t</code> 从 <code>0</code> 到 <code>2PI</code> , 这样能保证所有的周期运动时间同步。</p> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-12.gif" alt="" /></p> <p>身体和阴影的绘制都差不多,直接跳过看脚步动画。</p> <p>脚有两只,按道理应该是抬脚到落脚的动作完成时,其他部位都完成了一个完整的周期,所以在绘制脚的时候,<code>t</code> 需要除以 <code>2</code>。然后第一只脚和第二只脚相差半个脚自身的周期,可以直接将 <code>t</code> 替换成 <code>t + Math.PI</code> 就是第二脚的动画。</p> <pre><code>drawFeet (t) { t = t / 2 ctx.translate(Math.cos(t) * -50, 0) // 另一只脚 ctx.translate(Math.cos(t + Math.PI) * -50, 0) } </code></pre> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-13.gif" alt="" /></p> <p>脚步动画自身周期的一半是在地面上的,可以通过判断一下 <code>sin</code> 值,小于 <code>0</code> 则不做 <code>y</code> 纵轴方向上的变化。</p> <pre><code>ctx.translate(Math.cos(t) * -50, Math.sin(t) &gt; 0 ? Math.sin(t) * -35 : 0) </code></pre> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-14.gif" alt="" /></p> <p>还没完,为了让脚更加逼真,同样在前半个周期做一下 <code>rotate</code> 。</p> <pre><code>if (t &lt; Math.PI) { ctx.rotate(Math.sin(t) * Math.PI / 180 * -5) } </code></pre> <p><img src="/sites/default/files/blogs/2018/1808/trgonometry-15.gif" alt="" /></p> <p>最终得到的效果是这样的:</p> <div style="margin-bottom: 20px;"><iframe id="RYWbrJ" src="//codepen.io/airen/embed/RYWbrJ?height=400&amp;theme-id=0&amp;slug-hash=RYWbrJ&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>总结</h2> <p>现如今韦德1946手机版客户端发展速度非常迅速,刚入门的同学刚学完 jQuery 就被告知,You Dont Need jQuery。追新的脚本根本停不下来,在学习新框架新技能的同时,也别忘了基础知识的重要性。</p> <p>好了,今天就分享到这里,希望一次汇集这么多效果,能让你下次使用三角函数更得心应手。</p> <p>以上大部分代码都可以在我的 <a href="http://www.xysjxj.com/quot;//codepen.io/HelKyle/">Codepe" 主页</a>看到。</p> <h2>相关链接</h2> <ul> <li><a href="http://www.xysjxj.com/quot;//www.desmos.com/calculator/sa6pki00id">三角函数图像</a></li>" <li><a href="http://www.xysjxj.com/quot;//juejin.im/post/5b4ffa045188251b134e7211">水波图实现原理</a></li>" </ul> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/624.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">转载</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/532.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Web动画</a></div></div></div> Thu, 23 Aug 2018 14:12:04 +0000 Airen 2449 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/how-to-build-the-simple-camera-component.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文根据<a href="http://www.xysjxj.com/quot;//twitter.com/_davideast">@Davi" East</a>的《<a href="http://www.xysjxj.com/quot;//frontendnews.io/editions/2018-08-15-simple-camera-component">HO" TO BUILD A SIMPLE CAMERA COMPONENT</a>》一文所整理。</p> </blockquote> <p>要构建一个<code>camera</code>韦德娱乐平台,我们首先要了解所需的浏览器API。</p> <ul> <li>使用<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/MediaDevices"><code>MediaDevices</code></a>" API获取相机访问权限</li> <li>使用<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/HTML/Element/video"><code>video</code></a>元素播放<" href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/MediaStream"><code>MediaStream</code></a></li>" <li>使用<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement"><code>canvas</code></a>元素以<code>blob</code>或<code>base64</code>形式拍照</li>" </ul> <p>让我们构建一具自定义的<code>camera</code>元素,这样你就不必再担心把这些代码连接起来。</p> <h2>使用自定义元素构建可跨框架重用的韦德娱乐平台</h2> <p>这篇文章并没有指定在哪个框架构建摄像头韦德娱乐平台。叶节点(Leaf Node)韦德娱乐平台应该是可重用的。自定义元素是一种新的浏览器标准,允许你构建可在大多数JavaScript框架中移植的可重用元素。如果你不熟悉自定义元素(Custom Elements)并不重要。因为接下来的示例都是一些简单的示例,所以使用自定义元素并不复杂。在高级情况下,它会变得复杂,但我们将会避开这些。这是一个简单的例子:</p> <pre><code>class HelloElement extends HTMLElement { constructor() { // 调用构造函数不是必须的。如果你这样做,一定要确认调用了`super()` super(); } // 当元素连接到DOM时调用这个函数 connectedCallback() { // 附上一个shadow,这样任何人都不会弄乱你的样式 const shadow = this.attachShadow({ mode: 'open' }); shadow.textContent = 'Hello world!'; } } // 定义标签名,它必须有一个破折号 customElements.define('hello-element', HelloElement); </code></pre> <p>在HTML中你可以像下面这样调用自定义的元素<code>hello-element</code>:</p> <pre><code>&lt;hello-element&gt;&lt;/hello-element&gt; </code></pre> <p>你在浏览器运行上面的代码之后,将看到的效果如下图所示:</p> <p><img src="/sites/default/files/blogs/2018/1808/custom-element-1.png" alt="" /></p> <p>这是自定义元素的简单用法。就像我说的,它也可以变得更复杂,但我们这里将要构建的是一个摄像头韦德娱乐平台,而且是一个简单的摄像头韦德娱乐平台,所以会尽量让它保持简单。</p> <h2>摄像头韦德娱乐平台需要一个video元素和一个隐藏的canvas元素</h2> <p>让我们从简单的<code>camera</code>韦德娱乐平台开始。</p> <pre><code>class SimpleCamera extends HTMLElement { constructor() { super(); } connectedCallback() { const shadow = this.attachShadow({ mode: 'open' }) this.videoElement = document.createElement('video') this.canvasElement = document.createElement('canvas') this.videoElement.setAttribute('playsinline', true) this.canvasElement.style.display = 'none' shadow.appendChild(this.videoElement) shadow.appendChild(this.canvasElement) } } customElements.define('simple-camera', SimpleCamera) </code></pre> <p>该韦德娱乐平台只添加了两个元素:一个是<code>video</code>元素和一个隐藏的<code>canvas</code>元素。</p> <p>在 iOS 10 Safari 中,通过 <code>playsinline</code> 可以让视频内联播放。设置了 <code>playsinline</code> 属性的视频在播放时不会自动全屏,但用户可以点击全屏按钮来手动全屏;没有设置 <code>playsinline</code> 的视频会在播放时自动全屏。无论是否设置 <code>playsinline</code> 属性,退出全屏后视频都会继续播放。</p> <p><code>playsinline</code> 属性在 iOS 10 之前需要写成 <code>webkit-playsinline</code>,它的浏览器厂商前缀在 iOS 10 中被移除。但是目前 iOS 微信还不支持去掉前缀的写法,两个属性最好都加上。</p> <p>显然,<code>&lt;video&gt;</code>的 <code>autoplay</code> 必须和 <code>playsinline</code> 属性一起使用。也就是说,只有默认内联播放的视频才有可能自动播放,这一点很容易理解。</p> <p>然后在HTML中像下面这样调用自定义好的元素:</p> <pre><code>&lt;simple-camera&gt;&lt;/simple-camera&gt; </code></pre> <p>这样就可以为摄像机创建一个元素。也可以开始播放一些视频。</p> <p><img src="/sites/default/files/blogs/2018/1808/custom-element-2.png" alt="" /></p> <p>好像啥也没有一样,是不。不急,咱们继续往下。</p> <h2>通过<code>MediaDevices</code> API授权访问摄像头</h2> <p>使用<code>navigator.mediaDevices.getUserMedia()</code>方法,授权用户访问摄像头。</p> <pre><code>navigator.mediaDevices.getUserMedia(constraints).then((mediaStream) =&gt; { }) </code></pre> <p>请注意,<code>getUserMedia()</code>会返回一个<code>Promise</code>。如果返回成功,<code>Promise</code>会解析<code>MediaStream</code>。此流(Stream)将会用于<code>video</code>元素。如果<code>Promise</code>拒绝(<code>rejects</code>),表示用户未授权访问摄像头。然而!<code>Promise</code>有可能永远不会解决(<code>resolve</code>)或拒绝(<code>reject</code>)。用户可以决定永远不对权限弹出框执行操作。那不是很好玩吗?</p> <h2>浏览器对<code>MediaDevices</code>的支持很强大,但很奇怪</h2> <p><code>MediaDevices</code> API得到浏览器强大的支持。它可以在所有现代浏览器中使用。然而,在IE中没有得到支持,所以你需要对该特性做一个检查。</p> <pre><code>if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia(constraints).then((mediaStream) =&gt; { }) } </code></pre> <p>然而,一些浏览器版本对<code>MediaDevices</code> 的API只有部分支持,有些则需要添加浏览器供应商的韦德1946手机版客户端才能实现。<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia">MDN文章</a>中有一个关于设置Polyfills的部分有介绍到这方面的知识。幸运的是,这些Polyfill应该应用在元素之外,所以我们不需要在元素中考虑这个。</p>" <h2>为mediaStream的audio和video设置相应的约束</h2> <p><code>getUserMedia()</code>方法接受一组约束。这些限制有助于在用户接受权限后配置流。它们具有<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints"><code>MediaStreamConstraints</code></a>的类型。你可以指定两个主要属性:<code>audio</code>和<code>video</code>。</p>" <pre><code>if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia({ audio: false, video: { facingMode: 'user' } }).then((mediaStream) =&gt; { }) } </code></pre> <p><code>audio</code>属性是一个简单的布尔值。你要么请求用户的音频,要么不请求。<code>video</code>属性要复杂得多。视频约束,也称为<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints"><code>MediaTrackConstraints</code></a>,指定了视频流可能需要的所有内容:<code>echoCancellation</code>、<code>latency</code>" <code>sampleRate</code>、 <code>sampleSize</code>、 <code>volume</code>、 <code>noiseSuppression</code>、 <code>frameRate</code>、 <code>aspectRatio</code>、 <code>facingMode</code>,当然还有<code>width</code>和<code>height</code>。</p> <p>有很多约束。然而,除非你正开发一个摄像头应用程序,否则你只需要几个。即:<code>height</code>、<code>width</code>和<code>facingMode</code>。</p> <h2>将MediaStream分配给video元素</h2> <p>现在已经配置了<code>MediaStream</code>,就可以将其分配给<code>video</code>元素。</p> <pre><code>open(constraints) { return navigator.mediaDevices.getUserMedia(constraints) .then((mediaStream) =&gt; { // 分配MediaStream this.videoElement.srcObject = mediaStream // 加载时播放流 this.videoElement.onloadedmetadata = (e) =&gt; { this.videoElement.play() } }) } </code></pre> <p><code>video</code>元素有一个<code>srcObject</code>。它在分配<code>MediaStream</code>时从设置的摄像头流式输出。上面的代码片段在元素上添加了一个<code>open</code>方法。自定义元素具有可调用方法。如果用户调用这个<code>open</code>方法,它将启动视频流。</p> <pre><code>&lt;script&gt; (async function() { const camera = document.querySelector('simple-camera') await camera.open({ video: { facingMode: 'user' } }) }()) &lt;/script&gt; </code></pre> <p>现在我们可以播放视频,让我们拍照。</p> <p><img src="/sites/default/files/blogs/2018/1808/custom-element-3.gif" alt="" /></p> <h2>使用canvas将照片作为blob拍摄</h2> <p><code>canvas</code>元素能够从<code>video</code>元素中绘制帧。使用此功能,你可以在不可见的<code>canvas</code>上绘制,然后将图像导出为<code>blob</code>。</p> <pre><code>_drawImage() { const imageWidth = this.videoElement.videoWidth const imageHeight = this.videoElement.videoHeight const context = this.canvasElement.getContext('2d') this.canvasElement.width = imageWidth this.canvasElement.height = imageHeight context.drawImage(this.videoElement, 0, 0, imageWidth, imageHeight) return { imageHeight, imageWidth } } </code></pre> <p>这个私有的<code>_drawImage()</code>方法将不可见的<code>canvas</code>的<code>height</code>和<code>width</code>设置为<code>video</code>的大小。然后在上下文(<code>context</code>)中使用<code>drawImage()</code>方法。提供<code>video</code>元素的<code>x</code>、<code>y</code>位置,<code>width</code>和<code>height</code>。这将在不可见的<code>canvas</code>上绘图,并将相关设置创建为一个<code>blob</code>。</p> <pre><code>takeBlobPhoto() { const {imageHeight, imageWidth} = this._drawImage() return new Promise((resolve, reject) =&gt; { this.canvasElement.toBlob((blob) =&gt; { resolve({blob, imageHeight, imageWidth}) }) }) } </code></pre> <p><code>canvas</code>元素有一个<code>toBlob()</code>方法。由于它是异步的,所以你可以将它转换为一个<code>Promise</code>,这样它就更容易使用。</p> <p>现在你可以开始控制这个相机了:</p> <pre><code>&lt;simple-camera&gt;&lt;/simple-camera&gt; &lt;button id="btnPhoto"&gt;Take Blob&lt;/button&gt; &lt;script&gt; (async function(){ const camera = document.querySelector('simple-camera') const btnPhoto = document.querySelector('#btnPhoto') await camera.open({ video: { facingMode: 'user' } }) btnPhoto.addEventListener('click', async event =&gt; { const photo = await camera.takeBlobPhoto() }) }()) &lt;/script&gt; </code></pre> <p>当你需要上传一个文件时,<code>blob</code>是最好的。但是有时候,在<code>image</code>标签中插入一个<code>base64</code>编码的字符串会更好。<code>canvas</code>有相应的解决方案。</p> <p><img src="/sites/default/files/blogs/2018/1808/custom-element-4.png" alt="" /></p> <h2>使用canvas把拍摄的图片转换为base64</h2> <p><code>canvas</code>元素有现代战争<code>toDataURL()</code>方法。该方法获取<code>canvas</code>的当前内容,并将其输出成<code>base64</code>编码的图像。</p> <pre><code>takeBase64Photo({type, quality} = {type: 'png', quality: 1}) { const {imageHeight, imageWidth} = this._drawImage() const base64 = this.canvasElement.toDataURL('image/' + type, quality) return {base64, imageHeight, imageWidth} } </code></pre> <p><code>takeBase64()</code>方法调用<code>toDataURL()</code>方法并返回它的<code>base64</code>值。注意,你可以指定图像类型和图像质量。</p> <pre><code>&lt;simple-camera&gt;&lt;/simple-camera&gt; &lt;button id="btnBlobPhoto"&gt;Take Blob&lt;/button&gt; &lt;button id="btnBase64Photo"&gt;Take Base64&lt;/button&gt; &lt;script&gt; (async function() { const camera = document.querySelector('simple-camera') const btnBlobPhoto = document.querySelector('#btnBlobPhoto') const btnBase64Photo = document.querySelector('#btnBase64Photo') await camera.open({video: {facingMode: 'user'}}) btnBlobPhoto.addEventListener('click', async event =&gt; { const photo = await camera.takeBlobPhoto() }) btnBase64Photo.addEventListener('click', async event =&gt; { const photo = camera.takeBase64Photo({type: 'jpeg', quality: 0.8}) }) }()) &lt;/script&gt; </code></pre> <p><img src="/sites/default/files/blogs/2018/1808/custom-element-5.png" alt="" /></p> <p>把所有代码结合到一起:</p> <pre><code>&lt;script&gt; class SimpleCamera extends HTMLElement { constructor() { super(); } connectedCallback() { const shadow = this.attachShadow({ mode: 'open' }) this.videoElement = document.createElement('video') this.canvasElement = document.createElement('canvas') this.videoElement.setAttribute('playsinline', true) this.canvasElement.style.display = 'none' shadow.appendChild(this.videoElement) shadow.appendChild(this.canvasElement) } open(constraints) { return navigator.mediaDevices.getUserMedia(constraints).then((mediaStream) =&gt; { this.videoElement.srcObject = mediaStream console.log(mediaStream) this.videoElement.onloadedmetadata = (e) =&gt; { this.videoElement.play() } }) } _drawImage() { const imageWidth = this.videoElement.videoWidth const imageHeight = this.videoElement.videoHeight const context = this.canvasElement.getContext('2d') this.canvasElement.width = imageWidth this.canvasElement.height = imageHeight context.drawImage(this.videoElement, 0, 0, imageWidth, imageHeight) return { imageHeight, imageWidth } } takeBlobPhoto() { const {imageHeight, imageWidth} = this._drawImage() this.canvasElement.style.display="block" const card = document.createElement('div') card.classList.add('card') document.querySelector('.wrapper').appendChild(card) card.appendChild(this.canvasElement) return new Promise((resolve, reject) =&gt; { this.canvasElement.toBlob((blob) =&gt; { resolve({blob, imageHeight, imageWidth}) }) }) } takeBase64Photo({type, quality} = {type: 'png', quality: 1}) { const {imageHeight, imageWidth} = this._drawImage() const base64 = this.canvasElement.toDataURL('image/' + type, quality) this.canvasElement.style.display="block" const card = document.createElement('div') card.classList.add('card') document.querySelector('.wrapper').appendChild(card) card.appendChild(this.canvasElement) return {base64, imageHeight, imageWidth} } } customElements.define('simple-camera', SimpleCamera) &lt;/script&gt; &lt;div class="wrapper"&gt; &lt;div class="card"&gt; &lt;simple-camera&gt;&lt;/simple-camera&gt; &lt;div class="active"&gt; &lt;button id="btnBlobPhoto"&gt;Take Blob&lt;/button&gt; &lt;button id="btnBase64Photo"&gt;Take Base64&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;script&gt; (async function() { const camera = document.querySelector('simple-camera') const btnBlobPhoto = document.querySelector('#btnBlobPhoto') const btnBase64Photo = document.querySelector('#btnBase64Photo') await camera.open({video: {facingMode: 'user'}}) btnBlobPhoto.addEventListener('click', async event =&gt; { const photo = await camera.takeBlobPhoto() }) btnBase64Photo.addEventListener('click', async event =&gt; { const photo = camera.takeBase64Photo({type: 'jpeg', quality: 0.8}) }) }()) &lt;/script&gt; </code></pre> <p>Demo效果如下:</p> <div style="margin-bottom: 20px;"><iframe id="EeaqMZ" src="//codepen.io/airen/embed/EeaqMZ?height=400&amp;theme-id=0&amp;slug-hash=EeaqMZ&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>移植到你最喜欢的框架中</h2> <p>现代JavaScript框架能够使用自定义元素。这使得自定义元素成为构建通用韦德娱乐平台的一个极有吸引力的选择。如果你的公司使用多个框架来开发应用程序,你可以轻松地把该韦德娱乐平台移植到你的框架中。无处不在的自定义元素显示了每个框架与自定义元素的兼容性。</p> <h2>扩展阅读</h2> <ul> <li><a href="http://www.xysjxj.com/quot;//w3c.github.io/mediacapture-main/">Medi" Capture and Streams</a></li> <li><a href="http://www.xysjxj.com/quot;//frontendnews.io/editions/2018-08-15-simple-camera-component">HO" TO BUILD A SIMPLE CAMERA COMPONENT</a></li> <li><a href="//www.zcfy.cc/article/choosing-cameras-in-javascript-with-the-mediadevices-api">JavaScript 使用 <code>mediaDevices</code> API 选择摄像头</a></li> <li><a href="http://www.xysjxj.com/quot;//www.html5rocks.com/zh/tutorials/getusermedia/intro/">Capturin" Audio &amp; Video in HTML5</a></li> <li><a href="http://www.xysjxj.com/quot;//medium.com/@sebastianpatron/an-intro-to-webrtc-and-accessing-a-users-media-devices-76ca2e2edc73">A" Intro to WebRTC and Accessing a User’s Media Devices</a></li> <li><a href="http://www.xysjxj.com/quot;//blogs.bytecode.com.au/glen/2018/02/28/mediadevices-specific-camera.html">Selectin" a specific camera with the MediaDevices API</a></li> <li><a href="http://www.xysjxj.com/quot;//developers.google.com/web/fundamentals/media/capturing-images/?hl=zh-cn">采集用户的图像</a></li>" <li><a href="http://www.xysjxj.com/quot;//denzel.netlify.com/js/camera_in_js_trial.html">JS控制设备摄像头初探</a></li>" <li><a href="http://www.xysjxj.com/quot;//segmentfault.com/a/1190000010826909"><code>getUserMedia</code>" API的两个使用案例</a></li> <li><a href="http://www.xysjxj.com/quot;//www.toobug.net/article/capture_video_on_web.html">如何使用Web录制视频</a></li>" <li><a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements">Usin" custom elements</a></li> <li><a href="http://www.xysjxj.com/quot;//medium.com/dev-channel/the-case-for-custom-elements-part-1-65d807b4b439">Th" Case for Custom Elements: Part 1</a></li> <li><a href="http://www.xysjxj.com/quot;//medium.com/dev-channel/the-case-for-custom-elements-part-2-2efe42ce9133">Th" Case for Custom Elements: Part 2</a></li> <li><a href="http://www.xysjxj.com/quot;//medium.com/@hectorlorenzo/vue-as-web-components-custom-elements-91fbb962608a">Vu" as Web Components: Custom Elements</a></li> <li><a href="http://www.xysjxj.com/quot;//marcelpociot.de/blog/2017-12-08-using-custom-vuejs-elements">Creat" custom, distributable web components with VueJS</a></li> </ul> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/520.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Web Components</a></div><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/668.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">MediaDevices</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/669.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">自定义元素</a></div></div></div> Thu, 23 Aug 2018 12:54:53 +0000 Airen 2448 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/advanced-effects-with-css-background-blend-modes.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><p>首先要声明的是,我们在这篇文章中要了解的不是CSS混合模式如何使用,而是来一起探索CSS混合模式的一些高级运用以及它能做些什么,又会带来什么样的效果。操作过像Photoshop这样的图像处理软件的同学,对于图层混合模式一定不会感到陌生,但对于CSS中的混合模式,估计还是有不少的同学会感到陌生或者好奇。如果你从未接触过CSS混合模式相关的知识,那么建议你先花点时间阅读下面这几篇文章:</p> <ul> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/css-blend-modes-could-be-the-next-big-thing-in-web-design.html">Web设计中的CSS混合模式</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/getting-to-know-css-blend-modes.html">开始了解CSS混合模式</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/css-blend-modes.html">实战CSS混合模式</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/compositing-and-blending-in-css.html">CSS中的合成与混合模式</a></li> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/image-effects-with-css.html">使用CSS处理图像效果</a></li> <li><a href="http://www.xysjxj.com/quot;//github.com/chokcoco/iCSS/issues/16">不可思议的颜色混合模" <code>mix-blend-mode</code></a></li> <li><a href="http://www.xysjxj.com/quot;//github.com/chokcoco/iCSS/issues/31">不可思议的混合模" <code>background-blend-mode</code></a></li> <li><a href="http://www.xysjxj.com/quot;//github.com/chokcoco/iCSS/issues/32">两" CSS 代码实现图片任意颜色赋色技术</a></li> <li><a href="//www.zhangxinxu.com/wordpress/2015/05/css3-mix-blend-mode-background-blend-mode/">CSS3混合模式<code>mix-blend-mode</code>和<code>background-blend-mode</code>简介</a></li> <li><a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/409.html">CS"混合模式</a></li> <li><a href="//codepen.io/search/pens?q=css%20blend%20mode&amp;page=1&amp;order=popularity&amp;depth=everything&amp;show_forks=false">CSS混合模式DEMO @codepen</a></li> </ul> <p>如果你了解过CSS的混合模式,那就知道,其实他非常的简单,有关于CSS的混合模式相关的属性有:</p> <ul> <li><strong><code>background-blend-mode</code></strong>:用于元素的背景图像、渐变和背景颜色的混合</li> <li><strong><code>mix-blend-mode</code></strong>:用于元素的内容应该与元素的直系父元素的内容和元素的背景的混合</li> <li><strong><code>isolation</code></strong>:隔离<code>isolation</code>的作用是创建一个堆叠上下文(Stacking Context),主要用于与<code>mix-blend-mode</code>属性一起使用时,将混合模式只应用于某一个元素或某一组元素;当和<code>background-blend-mode</code>属性一起使用时,可以只混合一个指定元素栈的背景,它允许使一组元素从它们后面的背景中独立出来,只混合这组元素的背景</li> </ul> <blockquote> <p>有关于<code>isolation</code>相关的介绍,可以阅读@张鑫旭老师的《<a href="//www.zhangxinxu.com/wordpress/2016/01/understand-css3-isolation-isolate/">理解CSS3 <code>isolation: isolate</code>的表现和作用</a>》一文。</p> </blockquote> <p>当然,这个时候很多同学难免会问到,浏览器对其兼容性如何?事实上,现代浏览器对他们的支持度已经非常的不错了。</p> <div style="margin-bottom: 20px;"><iframe src="//caniuse.com/css-backgroundblendmode/embed" scrolling="no" frameborder="0" height="300" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <div style="margin-bottom: 20px;"><iframe src="//caniuse.com/css-mixblendmode/embed" scrolling="no" frameborder="0" height="300" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>文章最开始我们就提到了,在这篇文章并不会介绍CSS的混合模式相关的属性怎么使用,而会探讨CSS的混合模式的一些高级运用,以及对应的效果。</p> <h2>渐变混合模式</h2> <p><code>background</code>中的<code>background-image</code>可以使用CSS的渐变属性,言外之意,CSS渐变绘制的图形其实类似于背景图像。我们可以使用<a href="//www.w3.org/TR/css-images-4/#linear-gradients"><code>linear-gradient()</code></a>、<a href="//www.w3.org/TR/css-images-4/#radial-gradients"><code>radial-gradient()</code></a>、<a href="//www.w3.org/TR/css-images-4/#funcdef-repeating-linear-gradient"><code>repeating-linear-gradient()</code></a>、<a href="//www.w3.org/TR/css-images-4/#funcdef-repeating-radial-gradient"><code>repeating-radial-gradient()</code></a>、<a href="//www.w3.org/TR/css-images-4/#conic-gradients"><code>conic-gradient()</code></a>和<a href="//www.w3.org/TR/css-images-4/#funcdef-repeating-conic-gradient"><code>repeating-conic-gradient()</code></a>等函数来绘制背景图。这些属性除了<code>conic-gradient()</code>和<code>repeating-conic-gradient()</code>之外都得到了较好的支持。但庆幸的是,这两个属性也进入了W3C的<a href="//www.w3.org/TR/css-images-4/">CSS Image Values and Replaced Content Module Level 4</a>中,而且通过<a href="//css-houdini.rocks/conic-gradient/">CSS Houdini</a>能得到部分浏览器的支持。</p> <p>如果你对CSS的<code>conic-gradient</code>属性感兴趣,可以花点时间阅读下面的伟德1946手机版:</p> <ul> <li><a href="//www.w3.org/TR/css-images-4/#conic-gradients">Conic Gradients: the <code>conic-gradient()</code> notation</a></li> <li><a href="http://www.xysjxj.com/quot;//lea.verou.me/2015/06/conical-gradients-today/">Conica" gradients, today!</a></li> <li><a href="//css-houdini.rocks/conic-gradient/">CSS Houdini: Conic gradient</a></li> <li><a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/657.html">Coni" gradient伟德1946手机版和案例</a></li> <li><a href="http://www.xysjxj.com/quot;//www.cnblogs.com/coco1s/p/7079529.html">神奇" <code>conic-gradient</code> 圆锥渐变</a></li> <li><a href="//www.zhangxinxu.com/wordpress/2017/11/pure-css-colorful-circle/">3种纯CSS实现中间镂空的12色彩虹渐变圆环方法</a></li> </ul> <p>而我们要聊的不是<code>conic-gradient</code>,还是回到我们的正题中来吧。</p> <p>CSS中的<code>background</code>属性是可以接受多背景的,也就是说,我们可以使用多个渐变属性来绘制多个背景,并且运用到同一个元素上。如此一来,我们使用渐变等特性,可以绘制一些特殊的,而且复杂的图案背景。最为出名的是<a href="//lea.verou.me/css3patterns/">@Lea Verou使用这种技术设计的背景图案</a>。</p> <p><a href="//lea.verou.me/css3patterns/"><img src="/sites/default/files/blogs/2018/1808/0_G-mavF51iOebWwp8.png" alt="" /></a></p> <blockquote> <p>有关于相关原理,建议阅读@Lea的<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/502.html">CS" Secrets</a>一书中的<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/css-secrets/random-backgrounds.html">随机背景</a>和<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/css-secrets/complex-background-pattern.html">复杂背景图案</a>这两部分内容。</p> </blockquote> <p>现在我们有了<code>background-blend-mode</code>属性,那么实现CSS Patterns Gallery这样的效果更是如虎添翼。比如下面这个示例:</p> <pre><code>@mixin pattern($bg-primary,$bg-secondary) { background: repeating-linear-gradient( -45deg, transparent, transparent 1em, rgba($bg-secondary,0.4) 0, rgba($bg-secondary,0.1) 2em, transparent 0, transparent 1em, rgba($bg-secondary,0.3) 0, rgba($bg-secondary,0.2) 4em, transparent 0, transparent 1em, rgba($bg-primary,0.6) 0, rgba($bg-primary,0.2) 2em ), repeating-linear-gradient( 45deg, transparent, transparent 1em, rgba($bg-secondary,0.4) 0, rgba($bg-secondary,0.1) 2em, transparent 0, transparent 1em, rgba($bg-secondary,0.3) 0, rgba($bg-secondary,0.2) 4em, transparent 0, transparent 1em, rgba($bg-primary,0.4) 0, rgba($bg-primary,0.1) 2em ), #FFF; background-blend-mode: multiply; } html { height: 100%; @include pattern(#c0ebfa, #7FD7F5); } </code></pre> <div style="margin-bottom: 20px;"><iframe id="xabZxV" src="//codepen.io/airen/embed/xabZxV?height=400&amp;theme-id=0&amp;slug-hash=xabZxV&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>这仅仅是其中一个示例,事实上你可以发挥你自己的想象,可以创建出更多有意思的效果。比如@Yoksel、@Una Kravets和@Bennett Feely提供了<a href="http://www.xysjxj.com/quot;//bennettfeely.com/gradients/">24个有关于<code>background-blend-mode</code>结合渐变属性实现的一些有意思的背景图案效果</a>。</p>" <p><a href="http://www.xysjxj.com/quot;//bennettfeely.com/gradients/"><im" src="/sites/default/files/blogs/2018/1808/0_Bti236XTjirmwhhK.png" alt="" /></a></p> <p>除了CSS的渐变属性可以帮助我们设计一些复杂而且有创意的背景图案之外,其实还有一个非常优秀的CSS库也能做到这一点,即:<strong><a href="//css-doodle.com/">@yuanchuan的<code>&lt;css-doodle /&gt;</code></a></strong>也能做到:</p> <p><a href="//css-doodle.com"><img src="/sites/default/files/blogs/2018/1806/css-doodle-1.png" alt="" /></a></p> <blockquote> <p>如果你对<code>&lt;css-doodle /&gt;</code>感兴趣的话,可以阅读早期整理过的一篇文章《<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/create-patterns-with-css-doodle.html">一个制作Web图案的韦德娱乐平台:<code>css-doodle</code></a>》。</p> </blockquote> <p>在这个库写效果的时候,也可以添加<code>background-blend-mode</code>,可以创造出一些神奇的效果,比如下面这个案例:</p> <div style="margin-bottom: 20px;"><iframe id="eLmJGJ" src="//codepen.io/airen/embed/eLmJGJ?height=400&amp;theme-id=0&amp;slug-hash=eLmJGJ&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>当然,除了<code>background-blend-mode</code>之外,还可以在<code>css-doodle</code>中使用<code>mix-blend-mode</code>属性:</p> <div style="margin-bottom: 20px;"><iframe id="pOvgQj" src="//codepen.io/airen/embed/pOvgQj?height=400&amp;theme-id=0&amp;slug-hash=pOvgQj&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>混合模式的图像效果</h2> <p>前面提到过<code>background-image</code>允许在元素上设置多个渐变,其实类似的,我们也可以使用多个<code>url()</code>引用多张背景图像。当引入多张背景图像的时候,借助<code>background-blend-mode</code>的特性,我们可以实现一些特殊的图像效果。</p> <p><img src="/sites/default/files/blogs/2018/1808/photo-effect-with-background-blend-mode.png" alt="" /></p> <p>如此强大的功能,助推你在背景图片上做更多的意义的事情:只有你想不到的,没有你做不到的:</p> <p><a href="http://www.xysjxj.com/quot;//bennettfeely.com/image-effects/"><im" src="/sites/default/files/blogs/2018/1808/0_pbOpSQo7_KpwEIeO.png" alt="" /></a></p> <p>@Una很早前就通过这些CSS特性写了一个系列的伟德1946手机版,这些伟德1946手机版演示了如何使用CSS的<code>background-blend-mode</code>实现一些图像效果:</p> <ul> <li><a href="http://www.xysjxj.com/quot;//una.im/vintage-washout">Par" 1: The Vintage Washout Effect</a></li> <li><a href="http://www.xysjxj.com/quot;//una.im/3d-effect">Par" 2: 3D Glasses Effect</a></li> <li><a href="http://www.xysjxj.com/quot;//una.im/vignettes">Par" 3: Vignettes in 3 Ways</a></li> <li><a href="http://www.xysjxj.com/quot;//una.im/bokeh">Par" 4: Bokeh Textures</a></li> <li><a href="http://www.xysjxj.com/quot;//una.im/lomo">Par" 5: Lomography Filters</a></li> <li><a href="http://www.xysjxj.com/quot;//una.im/infrared">Par" 6: Infrared Photo Effect</a></li> </ul> <p>当然,有的时候你还可以借助CSS的<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/ten-effects-with-css3-filter"><code>filter</code>属性</a>来实现一些效果,而且效果也是非常不错的。</p> <h2>混合模式的HTML视频效果</h2> <p>前两个示例,看到的是背景和一些图像使用混合模式得到的效果。其实我们在视频上也是可以运用混合模式,让视频有一些独特的效果。传统的视频效果(比如,滤镜、淡入淡出等)一般是通过一些视频处理软件来处理的,比如Adobe Premiere或Apple的Final Cut。虽然这些软件有自身的一些优势,但是具有一定的编辑成本,特别是视频成形之后,要修改就具有非常高的成本了。</p> <p>我们可以尝试使用CSS的<code>filter</code>和混合模式<code>mix-blend-mode</code>属性可以处理一些简单的视频效果。因为<code>&lt;video&gt;</code>元素也和<code>img</code>元素一样,CSS的滤镜和混合模式可以用在它身上。同时配合其他元素,就轻易的实现了<a href="http://www.xysjxj.com/quot;//thenewcode.com/1020/HTML5-Video-Effects-with-CSS-Blend-Modes">混合模式的视频效果</a>。比如下面这个示例:</p>" <div style="margin-bottom: 20px;"><iframe id="zJxqaa" src="//codepen.io/airen/embed/zJxqaa?height=400&amp;theme-id=0&amp;slug-hash=zJxqaa&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>改变图标颜色</h2> <p>在平常的工作中,如果需要改变图标的颜色通常会更改变图标来做。这里我们向大家演示另一种改变图标颜色的技巧,即:<strong>使用CSS混合模式修改图标颜色</strong>。@ChokCoco 的《<a href="http://www.xysjxj.com/quot;//www.cnblogs.com/coco1s/p/8080211.html">两" CSS 代码实现图片任意颜色赋色技术</a>》一文中详细阐述了这种技术。</p> <p>眼见为实,还是上个@ChokCoco写的示例吧:</p> <div style="margin-bottom: 20px;"><iframe id="MqYepq" src="//codepen.io/airen/embed/MqYepq?height=400&amp;theme-id=0&amp;slug-hash=MqYepq&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>动画中的混合模式</h2> <p><a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/532.html">Web动画</a>已经是一种非常成熟的技术了。在大家的世界中,如果要做一些优秀的动效,都需要借助于JavaScript来完成。事实也不一定全部如此,比如下面这样的一个示例,我们可以借助CSS的一些优秀特性,比如这里所说的CSS混合模式,我们就可以使用纯CSS的技术实现一个火焰的动效:</p>" <div style="margin-bottom: 20px;"><iframe id="yxyJvR" src="//codepen.io/airen/embed/yxyJvR?height=400&amp;theme-id=0&amp;slug-hash=yxyJvR&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>文本淡化效果</h2> <p>在移动端开发的时候,我们有时候需有类似下图这样的需求:</p> <p><img src="/sites/default/files/blogs/2018/1808/fading-text-with-css-blend-modes.gif" alt="" /></p> <p>很多时候我们通过在<code>::before</code>或<code>::after</code>这样的伪元素上使用<code>linear-gradient</code>做一个渐化的背景图,遮盖在文本上。但往往这样做的效果会比较生硬。不过幸运的,咱们借助CSS的混合模式,会让整个效果变得细腻,如果配上一些动效,那交互效果就会更完美。比如@Giana在Codepen上写的一个示例:</p> <div style="margin-bottom: 20px;"><iframe id="dqPXgX" src="//codepen.io/airen/embed/dqPXgX?height=400&amp;theme-id=0&amp;slug-hash=dqPXgX&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>优雅降级</h2> <p>值得庆幸的是,<code>background-blend-mode</code>属性在Firefox、Chrome和Opera中得到了全面的支持,而且Safari也在紧跟步伐,现在也支持<code>saturation</code>、<code>hue</code>、<code>color</code>和<code>luminosity</code>几种模式。不幸的是,如果你的项目中要兼容IE或Edge浏览器的话,那你只能放弃使用CSS的混合模式特性。</p> <p>当然,如果你的用户对这些追求不是很高,而你又想给使用现代浏览器的用户有一个更好的体验。那么咱们可以使用CSS 的<code>@supports</code>属性给CSS混合模式做优雅的降级。比如像下面这样的一个示例:</p> <pre><code>.spectrum-background { background: gray; @supports (background-blend-mode: screen) { background: linear-gradient(red, transparent), linear-gradient(to top left, lime, transparent), linear-gradient(to top right, blue, transparent); background-blend-mode: screen; } } </code></pre> <p>上面的代码在不支持的浏览器中显示<code>gray</code>颜色,而支持的浏览器就会使用渐变色,并且还会有CSS混合模式的效果。</p> <blockquote> <p>有关于CSS的<code>@supports</code>属性的详细介绍,<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css3/css3-supports.html">可以点击这里进行了解</a>。</p> </blockquote> <h2>总结</h2> <p>CSS混合模式特性是CSS中很优秀也是很强大的特性,这些特性能帮助我们直接在客户端实现类似图像处理软件,比如Photoshop软件处理图片的一些效果(图层混合效果)。这篇文章并不是对CSS的混合模式做相应的科普,而是通过几个示例,向大家展示了CSS混合模式的强大之处,以及可使用的场景。</p> <p>当然,除了文章中提到的几个场景之外,大家还可以发挥自己的想象,去创造一些更有意思的东西。因为CSS的混合模式和Photoshop的图层混合模式非常相似,这也就是其能实现一些特殊效果的另一原因。如果你有相关的案例,欢迎在下面的评论中与我们一起分享。</p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/CSS3"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">CSS3</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/68.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">CSS</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/409.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">CSS混合模式</a></div><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/408.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">css blend modes</a></div></div></div> Tue, 21 Aug 2018 16:22:24 +0000 Airen 2447 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/event-capturing-bubbling-javascript.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><p>通过前面的<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/dom-model.html">DOM事件模型</a>和<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/DOM-event-binding.html">事件绑定的姿势</a>两节的学习,我们对JavaScript中的DOM事件有了一定的基础。但忽略了有关事件如何被触发的重要细节。</p> <h2>事件传播</h2> <p>让我们从事件传播开始。事件传播是<strong>事件冒泡</strong>和<strong>事件捕获</strong>的总称。先来看一张图,这张图在前面的文章中有也出现过,但没有深入的介绍。先上图吧!</p> <p><img src="/sites/default/files/blogs/2018/1808/dom-event-1.png" alt="" /></p> <p>W3C规范中定义了三个事件阶段,依次是<strong>捕获阶段</strong>、<strong>目标阶段</strong>和<strong>冒泡阶段</strong>。事件对象按照上图的传播路径依次完成这些阶段。如果某个阶段不支持或事件对象的传播被终止,那么该阶段就会被跳过。详细的阐述,我们后面会通过示例来向大家描述,这样会更易于理解。这里先来了解这三个事件阶段的概念:</p> <ul> <li><strong>捕获阶段</strong>:在事件对象到达事件目标之前,事件对象必须从<code>window</code>经过目标的祖先节点传播到事件目标。这个阶段被我们称之为捕获阶段。在这个阶段注册的事件监听器在事件到达其目标前必须先处理事件</li> <li><strong>目标阶段</strong>:事件对象到达其事件目标。这个阶段被我们称为目标阶段。一旦事件对象到达事件目标,该阶段的事件监听器就要对它进行处理。如果一个事件对象类型被标志为不能冒泡。那么对应的事件对象在到达此阶段时就会终止传播</li> <li><strong>冒泡阶段</strong>:事件对象以一个与捕获阶段相反的方向从事件目标传播经过其祖先节点传播到<code>window</code>。这个阶段被称之为冒泡阶段。在此阶段注册的事件监听器会对相应的冒泡事件进行处理</li> </ul> <p>理解概念总是很累的。为了更好的了解事件及其工作细节,我们来创建一个简单的示例,通过示例来帮助我们理解事件的工作细节,比如前面提到的事件传播。</p> <p>先来创建示例所需要的HTML结构:</p> <pre><code>&lt;body id="theBody" class="item"&gt; &lt;div id="one_a" class="item"&gt; &lt;div id="two" class="item"&gt; &lt;div id="three_a" class="item"&gt; &lt;button id="buttonOne" class="item"&gt;one&lt;/button&gt; &lt;/div&gt; &lt;div id="three_b" class="item"&gt; &lt;button id="buttonTwo" class="item"&gt;two&lt;/button&gt; &lt;button id="buttonThree" class="item"&gt;three&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div id="one_b" class="item"&gt; &lt;/div&gt; &lt;/body&gt; </code></pre> <p>正如你所看到的,这里没有什么特别之处。HTML结构很简单,我们用前面学到的知识,把上面的HTML结构用DOM树描绘出来,将会像下面这样:</p> <p><img src="/sites/default/files/blogs/2018/1808/dom_ec_1_72.png" alt="" /></p> <p>假设我们点击了<code>buttonOne</code>元素。从前面学到的知识中我们知道,这将会触发一个<code>click</code>事件。<code>click</code>事件实际上并不源于你与之交互的元素。因为事件会从你的文档开始:</p> <p><img src="/sites/default/files/blogs/2018/1808/dom-event-2.png" alt="" /></p> <p>从根开始,事件从上一级一级往下,并在触发事件的元素<code>buttonOne</code>(事件目标)处停止:</p> <p><img src="/sites/default/files/blogs/2018/1808/dom_capturing_72.png" alt="" /></p> <p>如图所示,<code>click</code>事件所采用的路径是直接的,他会通知该路径中的每个元素。这也意味着,你要监听<code>body</code>、<code>one_a</code>、<code>two</code>、<code>three_a</code>上的<code>click</code>事件,也就会触发相关的事件处理程序。这是一个很重要的细节。稍后我们再详细来介绍。</p> <p>现在,一旦你的事件达到目标,它就不会停止。它会重新追溯它的步骤,回到根源:</p> <p><img src="/sites/default/files/blogs/2018/1808/dom_bubbling_72.png" alt="" /></p> <p>就像以前一样,在事件向上移动时,事件路径上的每个元素都会被通知它的存在。</p> <p>其实这两个过程,前者通常被称为事件捕获,而后者被称为事件冒泡。接下来我们来聊聊事件捕获和事件冒泡的一些细节。</p> <h2>事件捕获</h2> <p>有一个细节我们需要注意,在DOM中启动事件的位置并不重要。因为事件始终从根开始,沿着路径直到达到目标,然后再重新追溯根源,然后回到根。而其中启动事件的部分和事件从根向下阻止DOM称为事件捕获阶段:</p> <p><img src="/sites/default/files/blogs/2018/1808/capture_phase_72.png" alt="" /></p> <p>在此阶段,仅调用捕获监听器,也就是<code>addEventListener</code>的第三个参数为<code>true</code>:</p> <pre><code>element.addEventListener('click', listener, true) </code></pre> <p>如果省略此参数,则其默认值为<code>false</code>,并且监听器不是捕获器,而变成冒泡。因此,在此阶段期间,仅调用从<code>window</code>到事件目标父级的路径上找到的捕获器。</p> <p>回到我们的示例中:</p> <pre><code>Array.from(document.querySelectorAll('.item')).forEach(item =&gt; { var id = item.id; item.addEventListener('click',function(e){ console.log(`${e.type}: ${id}`) }, true); }) </code></pre> <p>点击不同的元素,可以看到事件捕获的过程:</p> <p><img src="/sites/default/files/blogs/2018/1808/dom-event-3.gif" alt="" /></p> <p>具体的Demo,<a href="http://www.xysjxj.com/quot;//codepen.io/airen/full/oPNxxJ/">可以点击这里</a>,打开浏览器开发者工具,你点击不同的元素时,在<code>console</code>项中会输出对应的事件捕获过程。</p>" <h2>事件冒泡</h2> <p>上面看到的是事件传播的第一阶段,即事件捕获阶段。接下来是看另一个阶段,即事件冒泡阶段:</p> <p><img src="/sites/default/files/blogs/2018/1808/bubble_phase_72.png" alt="" /></p> <p>在事件冒泡阶段,只会调用非捕获者。也就是说,只有<code>addEventListener</code>的第三个参数的值为<code>false</code>的事件监听器。比如上面的示例,把<code>addEventListener</code>的第三个参数设置为<code>false</code>,或者不显式的设置(因为其默认值为<code>false</code>):</p> <p><img src="/sites/default/files/blogs/2018/1808/dom-event-4.gif" alt="" /></p> <p>具体的Demo,<a href="http://www.xysjxj.com/quot;//codepen.io/airen/full/YOzGxv/">可以点击这里</a>,打开浏览器开发者工具,你点击不同的元素时,在<code>console</code>项中会输出对应的事件冒泡过程。</p>" <h2>事件中断</h2> <p>现实中,很多时候我们并不希望目标元素的事件结束之后还去追溯其根源(冒泡)。也就是想在需要的地方可以结束事件的生命。在JavaScript中可以在事件对象上使用<code>stopPropagation</code>方法:</p> <pre><code>function handleClick(e) { e.stopPropagation(); // do something } </code></pre> <p>也就是说,<code>stopPropagation</code>方法可以阻止事件在各个阶段中运行。来看一个示例,比如你在<code>three_a</code>元素有一个<code>click</code>事件,并且希望阻止事件传播。比如像下面这样:</p> <pre><code>Array.from(document.querySelectorAll('.item')).forEach(item =&gt; { var id = item.id; item.addEventListener('click',function(e){ console.log(`${e.type}: ${id}`) }, true); }) Array.from(document.querySelectorAll('.item')).forEach(item =&gt; { var id = item.id; item.addEventListener('click',function(e){ console.log(`${e.type}: ${id}`) }, false); }) var theElement = document.querySelector("#three_a"); theElement.addEventListener("click", doSomething, true); function doSomething(e) { e.stopPropagation(); } </code></pre> <p>当你点击<code>buttonOne</code>元素时,事件的路径变成下面这样:</p> <p><img src="/sites/default/files/blogs/2018/1808/event_stopped_72.png" alt="" /></p> <p>按理说,<code>click</code>事件捕获会从<code>window</code>一级一级向下移动DOM树,并且会通知到路径上的每个DOM元素,直到目标元素<code>buttonOne</code>停止。但上面的代码,我们在<code>three_a</code>元素的<code>click</code>事件的捕获监听事件<code>doSomething</code>时调用了<code>stopPropagation</code>方法。事件的捕获到<code>three_a</code>将会停止。来看一下添加<code>stopPropagation</code>方法前后的效果:</p> <p><img src="/sites/default/files/blogs/2018/1808/dom-event-3.png" alt="" /></p> <p>从上图我们可以看到,此时你虽然点击了<code>buttonOne</code>元素,但程序却无法捕获到该元素的上的<code>click</code>事件。而且事件也不会再冒泡到根元素。</p> <h2>停止即时传播</h2> <p>正如其名称的含义,<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation"><code>stopImmediatePropagation</code></a>会立即停止,甚至阻止了当前监听器的兄弟姐妹接收事件。</p>" <p>如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。如果其中某个监听函数执行了 <code>event.stopImmediatePropagation()</code> 方法,则当前元素剩下的监听函数将不会被执行。</p> <p>来看下个简单的示例:</p> <pre><code>let buttonOneEle = document.getElementById('buttonOne') // buttonOne 绑定的第一个监听函数 buttonOneEle.addEventListener("click", (event) =&gt; { console.log(`第一 ${event.type}: ${event.target.id}`); }, false); // buttonOne 绑定的第二个监听函数 buttonOneEle.addEventListener("click", (event) =&gt; { console.log(`第二 ${event.type}: ${event.target.id}`); //执行stopImmediatePropagation方法,阻止click事件冒泡,并且阻止p元素上绑定的其他click事件的事件监听函数的执行 event.stopImmediatePropagation(); }, false); // 该监听函数排在上个函数后面,该函数不会被执行 buttonOneEle.addEventListener("click",(event) =&gt; { console.log(`${event.type}: ${event.target.id}`); }, false); // buttonOne 元素的click事件没有向上冒泡,该函数不会被执行 Array.from(document.querySelectorAll('.item')).forEach(item =&gt; { item.addEventListener('click',function(event){ console.log(`${event.type}: ${event.target.id}`) }, false); }) </code></pre> <p>在浏览器<a href="http://www.xysjxj.com/quot;//codepen.io/airen/full/qMEWmM/">打开Demo</a>,点击<code>buttonOne</code>元素或者其他元素时,在<code>console</code>中输出的结果像下面这样:</p>" <p><img src="/sites/default/file/blogs/2018/1808/dom-event-5.gif" alt="" /></p> <h2>阻止默认行为</h2> <p><code>preventDefault</code>它是事件对象(<code>Event</code>)的一个方法,作用是取消一个目标元素的默认行为。既然是默认行为,那就说元素必须要有默认行为才能被取消,如果元素自身没有默认行为,调用该方法就会无效。</p> <p>下例演示了如何使用<code>preventDefault</code>方法来阻止一个<code>input</code>元素内非法字符的输入。</p> <pre><code>&lt;body&gt; &lt;p&gt;请输入一些字母,只允许小写字母.&lt;/p&gt; &lt;form&gt; &lt;input type="text" id="my-textbox"/&gt; &lt;/form&gt; &lt;script type="text/javascript"&gt; function checkName(evt) { var charCode = evt.charCode; if (charCode != 0) { if (charCode &lt; 97 || charCode &gt; 122) { evt.preventDefault(); alert("只能输入小写字母." + "\n" + "charCode: " + charCode + "\n" ); } } } document.getElementById('my-textbox').addEventListener( 'keypress', checkName, false ); &lt;/script&gt; &lt;/body&gt; </code></pre> <p>另外,在监听器中调用事件对象的<code>preventDefault</code>方法,可以避免使用事件取消执行默认操作。你可以查看 <a href="http://www.xysjxj.com/quot;//developer.mozilla.org/zh-cn/DOM/event.cancelable"><code>event.cancelable</code></a>" 属性来判断一个事件的默认动作是否可以被取消. 在<code>cancelable</code>属性为<code>false</code>的事件上调用 <code>preventDefault</code> 方法没有任何效果.</p> <p><code>preventDefault</code> 方法不会阻止该事件的进一步冒泡。 <code>event.stopPropagation</code> 方法才有这样的功能.</p> <h2>总结</h2> <p>在这篇文章中,我们深入的了解了JavaScript中DOM事件传播机制。JavaScript的DOM事件会经历<strong>捕获阶段</strong>、<strong>目标阶段</strong>和<strong>冒泡阶段</strong>三个阶段。通过示例介绍事件捕获和事件冒泡机制,以及如何中断事件、取消事件和阻止事件冒泡等。</p> <h2>扩展阅读</h2> <ul> <li><a href="//www.kirupa.com/html5/event_capturing_bubbling_javascript.htm">Event Capturing and Bubbling in JavaScript</a></li> <li><a href="http://www.xysjxj.com/quot;//www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow">Documen" Object Model (DOM) Level 2 Events Specification</a></li> <li><a href="//www.sitepoint.com/event-bubbling-javascript/">What Is Event Bubbling in JavaScript? Event Propagation Explained</a></li> <li><a href="http://www.xysjxj.com/quot;//www.w3.org/TR/dom/#events">W3" DOM4 – Events</a></li> <li><a href="http://www.xysjxj.com/quot;//dom.spec.whatwg.org/#events">DO" – Living Standard – Events</a></li> <li><a href="http://www.xysjxj.com/quot;//www.w3.org/TR/DOM-Level-3-Events/#dom-event-architecture">W3" UI Events – DOM Event Architecture</a></li> <li><a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Events">MS" – Events and the DOM</a></li> </ul> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/660.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">DOM</a></div><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/666.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">DOM事件</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/663.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">DOM系列</a></div></div></div> Tue, 21 Aug 2018 13:14:03 +0000 Airen 2446 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com