整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

性能测试YSLOW前端调优23大规则(八)-JavaScript和CSS置于外部

谓的将JavaScript和CSS置于外部,就是我们说的外置JavaScript和CSS。关于JavaScript和CSS在前端页面中的使用通常有两种方式:一种是内联方式;另外一种是外置方式。

内联方式是指将JavaScript和CSS直接嵌入到前端页面,如以下代码:

[head]

[script type="text/javascript"]

function IsEven()

{

var number = document.getElementById("TextBox1").value;

if (number % 2 == 0)

{

alert(number + " is even number");

}

else

{

alert(number + " is odd number");

}

}

[/script]

[/head]

外置方式是指将JavaScript或CSS写在一个单独的文件中,后缀名为“.js”或“.css”格式,然后在HTML页面中调用这个单独的文件,如以下代码:

[head]

[script type="text/javascript" src="ExternalJavaScript.js"][/script]

[/head]

内联JavaScript和CSS的优点如下:

1) 有效减少 HTTP 请求次数,提升前端页面性能,缓解服务器压力。

2) 浏览器加载完CSS才能渲染页面,因此可以防止CSS文件无法读取而造成页面裸奔的现象。

内联JavaScript和CSS的缺点如下:

1) 可维护性差,每天如果有需要修改内容,必须对很多页面进行修改。

2) 内联JavaScript和CSS在每次页面加载的都必须重新加载。

3) 协同开发的能力差,不方便多名开者同步工作。

外联JavaScript和CSS的优点如下:

1) 如果JavaScript和CSS被多个页面调用时,这样修改更方便,只要修改一个文件就可以。

2) 分离HTML、CSS和Javascript可以更容易操纵它们,方便协同工作。

3) 外置Javascript文件可以被浏览器缓存。

外联JavaScript和CSS的缺点如下:

1) 外置的方式增加了HTTP的请求数。

2) 浏览器要加载完CSS才能渲染页面,因此影响页面的性能。

在现实世界中使用外部文件通常会产生更快的页面,因为浏览器会缓存JavaScript和CSS文件。每次请求HTML文档时,都会下载HTML文档中内联的JavaScript和CSS。这减少了所需的HTTP请求数量,但增加了HTML文档的大小。另一方面,如果JavaScript和CSS位于浏览器缓存的外部文件中,则HTML文档的大小会减少,而不会增加HTTP请求的数量。

关键因素是外部JavaScript和CSS组件相对于所请求的HTML文档数量的缓存频率。这个因素尽管难以量化,但可以使用各种指标进行衡量。如果您的网站上的用户每个会话有多个页面浏览量,并且许多网页重复使用相同的脚本和样式表,则缓存的外部文件可能带来更大的潜在收益。

在比较内联和外置文件时,知道用户缓存外部组件的可能性的可能性很重要,通常用户在第一次访问页面时是空缓存,之后的多次后续页面查看都是具有完整缓存的。如果在访问系统时,具有完整缓存的页面占所有查看数量的比例越多,那说明外置文件的收益越高也就越有利,当然如果具有完整缓存的页面占所有查看数量的比例很低,那么内联文件的方式更有利。

如果网站中有很多页面使用相同的JavaScript和CSS,那么使用外部文件可以更好的提高这些组件的重用率,在这种情况下使用外部文件更有优势,因为当用户在页面导航时JavaScript和CSS组件已经位于浏览器的缓存中。当然反过来也很容易理解了,如果没有任何两个页面共享相同的JavaScript和CSS,重用率就会很低,当然很少有网站会出现这两种极端的情况了。

如果主页服务器知道一个组件是否在浏览器的缓存中,那么就可以在内联或使用外部文件之间做出最佳选择,当然服务器不能查看浏览器缓存中有那些内容,但可以使用Cookies做指示器,如果Cookies不存在,就使用内联JavaScript和CSS,如果Cookies出现了,则可以使用外部组件位于浏览器的缓存中,这就是我们通常说的“动态内联”。

基于以上原因通常我们建议将JavaScript和CSS置于外部。

Vue 应用中,需要使用 el-dialog 弹出一个 iframe 页面,该页面来源于其他项目,这就要解决 Vue 父页面和 iframe 子页面的两个通信问题:

  1. Vue 父页面向 iframe 子页面传递初始化数据
  2. 子页面的关闭按钮,需要通知父页面关闭 el-dialog

本文将整理 Vue 中嵌入 iframe 并进行双向通信的完整过程。

子页面直接调用父页面方法的跨域问题

理论上,如果 iframe 的 src 跟父页面同源,是可以直接用 window.parent.func 方法调用父类的 close 方法,完成 el-dialog 弹框关闭的。

但是,如果不同源,就会报错:

DOMException: Blocked a frame with origin "http://localhost:8085" 
from accessing a cross-origin frame.

网上搜索到的解决办法是,使用 iframe 的 contentWindow.postMessage 方法传递数据给子页面。但是有一个问题,什么时候调用能得到真正的 iframe 对象呢?

父组件传递数据的时机

经过反复测试,笔者发现:如果直接在 Vue 父组件的 created 方法中向 iframe 传递数据,此时 iframe 因为并没有被访问,导致无法传数据,无论是通过 id 还是 $refs 都不能获取到这个 iframe 对象。

看这段网上搜到的通用方法:

<el-dialog :visible="showIFrame" :close-on-click-modal="false"
               @close="closeSelf" title="弹框配置" >
      <iframe id="myFrame"  ref="myframe" src="http://localhost:8085/myIFrame.html" 
      frameborder="0" scrolling="auto" marginheight="0" marginwidth="0" 
      width="100%" height="726px;">
      </iframe>
    </el-dialog>

此时,如果在 created 方法中传递数据:

created() {
    // 监听子组件的关闭事件消息
    window.addEventListener('message', () => {
      this.closeSelf();
    }, false);

    // 向子组件传递初始化数据
    this.$refs.myframe.contentWindow.postMessage(data, '*'); // 取到的是 undefined
    const iframe = document.getElementById('myframe');  // 为 null
    iframe.contentWindow.postMessage(data,"*");
  },

代码运行会报两种错误。

错误一,$refs 中没有该组件:

错误一

错误二,id 为 myframe 的元素为 null:

错误二

解决办法:在 iframe 的 load 事件中向子页面传递数据,上面两种用法就不会报错了。

完整流程

父组件的 created 中监听子组件消息,调用关闭逻辑;同时,为 iframe 提供一个 @load="loaded" 事件,在该事件中调用 postMessage 向子组件发送初始化数据。

首先,弹框 Vue 组件 PopupIframe.vue 代码如下:

<template>
  <div id="app">
    <el-dialog :visible="showIFrame" :close-on-click-modal="false"
               @close="closeSelf" title="Ifram配置">
      <iframe v-show="src!==''"  
      id="myframe" 
      @load="loaded" 
      ref="myframe" 
      :src="src" frameborder="0" scrolling="auto" marginheight="0" marginwidth="0" width="100%" height="726px;"></iframe>
    </el-dialog>
  </div>
</template>
<script>
export default {
  name: 'PopupIframe',
  props: ['showIFrame'],
  data() {
    return {
      src: '',
      data: {
      },
    };
  },
  created() {
    // 设置 src
    this.src = 'http://localhost:8087/myIfram.html";
    // 监听子组件的关闭事件
    window.addEventListener('message', () => {
      this.closeSelf();
    }, false);
  },
  methods: {
    loaded() {
      // 只有在 iframe 加载时传递数据给子组件,$refs 才是非空的
      this.$refs.myframe.contentWindow.postMessage({
        type: 'myData',
        data: this.data,
      }, '*');
    },
    closeSelf() { // 关闭当前弹框
      this.$emit('closeIframe');
    },
  },
};
</script>

第二步,iframe 子页面添加监听数据的方法:

window.addEventListener('message', (e) => {
         console.log(e.data);
         if (e.data.type === 'myData') {
            // TODO 使用父组件传递的初始化数据
          }
});

第三步,iframe 的页面的关闭按钮向父组件发送消息,通知关闭:

window.parent.postMessage('close', '*');

PopupIframe.vue 被其他组件引用就可以了。

postMessage 数据类型

测试发现,this.$refs.myframe.contentWindow.postMessage 向 iframe 子组件发送数据后,子组件收到的数据除了第一步定义的数据外,还有其他数据,它的监听方法里面会执行两次:

postMessage 的内容

这说明,框架也向子组件发送了一些数据,类型为打包的告警数据。

因此,父组件传递数据时,也可以按照这个格式添加一个 type 标识我们的数据,前面第二步操作中,子组件使用时再根据它处理自己需要的数据。

总结:本文的核心知识点是 iframe 的 load 事件,要想顺利传递数据给 iframe ,必须在这里调用才能得到 iframe 对象。

是展示广告或为自己的内容添加促销块的完美方式。它还为您的布局提供了一个有趣的视觉突破,从重复的网格中脱颖而出。

为了更好地理解我们将要构建的内容,有一个随附的演示。但是,由于此演示使用一些PHP代码,因此需要服务器才能运行。

可以从此 GitHub 存储库下载项目文件(https://github.com/tutsplus/How-to-Embed-Random-Content-Inside-a-Grid-Layout)。

通过服务器运行演示,请注意两件事:

  • 嵌入式横幅
  • 重新加载页面时,横幅图像会发生变化。

了解布局

在前面的教程中,我们使用自己的标记重新创建了一个这样的教程列表。

标记

我们使用简单的HTML来构建结构。自动化并将数据内容存储在 PHP 数组中。在这里,我们将使用 PHP,但无论语言或 CMS/框架如何,逻辑都将保持不变。我们应该遍历源代码并检索帖子。

我们的数据内容格式如下所示:

$articles      = array(
  array(
    'title'      => 'Quick Tip: Create a Very Simple Parallax Effect With CSS & JavaScript',
    'image'      => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/parallax-pre.jpg',
    'link'       => 'https://webdesign.tutsplus.com/tutorials/quick-tip-how-to-build-a-dead-simple-parallax-effect-with-css-javascript--cms-33061',
    'categories' => array(
      'CSS',
      'JavaScript',
    ),
  ),
  array(
    'title'      => 'How to Build a Static Portfolio Page With CSS & JavaScript',
    'image'      => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/chart-pre.png',
    'link'       => 'https://webdesign.tutsplus.com/tutorials/how-to-build-a-portfolio-page-with-css-javascript--cms-32933',
    'categories' => array(
      'CSS',
      'JavaScript',
    ),
  ),
    
  // more articles here 
);

这是我们的循环逻辑:

<?php if ( ! empty( $articles ) ) : ?>
  <div class="container">
    <ol class="posts">
      <?php foreach ( $articles as $key => $article ) : ?>
        <li class="post">
          <article>
            <figure>
              <a href="<?php echo $article['link']; ?>" target="_blank">
                <img width="300" height="208" src="<?php echo $article['image']; ?>" alt="<?php echo $article['title']; ?>">
              </a>
              <figcaption>
                <ol class="post-categories">
                  <?php foreach ( $article['categories'] as $cat ) : ?>
                    <li>
                      <a href="">
                        <?php echo $cat; ?>
                      </a>
                    </li>
                  <?php endforeach; ?>
                </ol>
                <h2 class="post-title">
                  <a href="<?php echo $article['link']; ?>" target="_blank">
                    <?php echo $article['title']; ?>
                  </a>
                </h2>
              </figcaption>
            </figure>
          </article>
        </li>
      <?php endforeach; ?>
    </ol>
  </div>
<?php endif; ?>

如前所述,根据你将要使用的语言或CMS,情况会发生变化。例如,WordPress有一个用于所有主要查询的内置循环

<!-- Start the Loop. -->
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
 
  <!-- Add post info using WP built-in functions -->
<?php endwhile; else : ?>
  <!-- The very first "if" tested to see if there were any Posts to -->
  <!-- display. This "else" part tells what do if there weren't any. -->
  <p><?php esc_html_e( 'Sorry, no posts matched your criteria.' ); ?></p>
  <!-- REALLY stop The Loop. -->
<?php endif; ?>

风格

除了标记之外,我们还将保留上一教程中的大多数样式。我们只会进行一些更改以使布局响应。

以下是所有样式:

:root {
  --black: #3a3a3a;  --white: #fff;  --green: #49b293;
}
* {
  margin: 0;  padding: 0;
}
img {
  display: block;  max-width: 100%;  height: auto;
}
ol {
  list-style: none;
}
a {
  text-decoration: none;  color: inherit;
}
body {
  margin: 50px 0;  color: var(--black); 
  font: 1rem/1.3 sans-serif;
}
.container {
  max-width: 1200px;  padding: 0 15px; 
  margin: 0 auto;
}
h1 {
  text-align: center;  margin-bottom: 2rem;
}
h1 a {
  text-decoration: underline;
}
.posts {
  display: grid;  grid-gap: 1.5rem;
}
.posts .post {
  width: 300px;  margin: 0 auto;  
  border: 1px solid rgba(0, 0, 0, 0.1);
}
.posts > li {
  background: #fafafa;
}
.posts .post-title {
  font-size: 1.3rem;
}
.posts .post-title:hover {
  text-decoration: underline;
}
.posts figcaption {
  padding: 1rem;
}
.posts .post-categories {
  margin-bottom: 0.75rem;  font-size: 0.75rem;
}
.posts .post-categories * {
  display: inline-block;
}
.posts .post-categories li {
  margin-bottom: 0.2rem;
}
.posts .post-categories a {
  padding: 0.2rem 0.5rem;  border-radius: 1rem; 
  border: 1px solid;  line-height: normal;  transition: all 0.1s;
}
.posts .post-categories a:hover {
  background: var(--green);  color: var(--white);
}
@media (min-width: 500px) {
  .posts {
    grid-template-columns: repeat(2, 1fr);
  }
  .posts .post {
    width: auto;
  }
}
@media (min-width: 600px) {
  .posts {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (min-width: 900px) {
  .posts {
    grid-template-columns: repeat(4, 1fr);
  }
}

现在让我们假设我们要在网格内放置横幅。在这种情况下,我们只会使用图片横幅,但在您的情况下,您可以插入来自不同来源的广告、轮播、视频或您喜欢的任何其他内容。

我们的横幅必须满足以下要求:

  • 它们应该在每五列之后出现。在我们的例子中,有 12 个帖子,所以我们的网格将包含两个横幅。当然,在您的情况下,您可以拥有更多。
  • 在每个页面加载时,它们应该随机出现,这意味着某些横幅不会有任何固定的位置。
  • 此外,嵌入的横幅应该是唯一的,这意味着单个横幅不会在同一页面中出现两次。

下面是我们想要生成的布局示例:

如前所述,横幅将随机出现,因此在另一个页面加载时,我们可能会看到不同的横幅,如下所示:

为了实现此行为,我们将使用不同的 PHP 数组函数(、array_diffarray_push)。array_keysarray_rand

让我们记下我们将遵循的步骤:

  1. 将所有横幅(来自 Unsplash)存储在数组内,并从该数组中获取密钥。$images
  2. 初始化数组,我们将在其中添加要添加到网格的每个横幅的键。默认情况下,它将为空。$exclude_images_keys
  3. 在循环中,我们将检查当前元素的索引是否不为 0 并且能被 5 整除。
  4. 如果发生这种情况,我们将比较 和 数组。我们将返回它们的唯一值(如果有),并将它们存储在数组中。$images_keys$exclude_images_keys$in_items
  5. 从数组中获取一个随机密钥。$in_items
  6. 将此键添加到数组中,以从将来的选择中排除关联的横幅。$exclude_images_keys
  7. 使用此键选择横幅并将其放置在网格中。

以下是负责此功能的 PHP 代码:

<?php 
// 1 
$images              = array(
  'banner1.jpg',
  'banner2.jpg',
  'banner3.jpg',
  'banner4.jpg',
  'banner5.jpg',
  'banner6.jpg',
);
$images_keys         = array_keys( $images );
$exclude_images_keys = array();
foreach ( $articles as $key => $article ) :
  // 3 
  if ( 0 !== $key && 0 === $key % 5 ) :
    // 4 
    $in_items         = array_diff( $images_keys, $exclude_images_keys );
    // 5 
    $random_image_key = array_rand( $in_items );
    // 6 
    array_push( $exclude_images_keys, $random_image_key );
    // 7 
    $random_image = $images[ $random_image_key ];
    ?>
    <li class="banner">
      <img width="800" height="533" src="img/<?php echo $random_image; ?>" alt="banner">
    </li>
	<?php
  endif;
  ...
 endforeach;

以及我们横幅的附加 CSS:

.posts .banner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
  
@media (min-width: 900px) {
  .posts .banner {
    grid-column: span 2;
  }
}

结论

就是这样,伙计们!我希望你和我一样喜欢这个小练习,它给了你一些关于如何在网格布局中嵌入广告项目的想法。此外,通过以我们在此处介绍的方式使用模运算符 (),您可以创建具有动态模式的多列布局。

与往常一样,非常感谢您的阅读!