Giter VIP home page Giter VIP logo

blog's People

Contributors

yuanyuanbyte avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

Vue系列之对 Vue 的理解

本系列的主题是 Vue,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

对 Vue 的理解

  • Vue 是一套用于构建用户界面的渐进式 JavaScript 框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
  • 轻量级的框架,只关注视图层,是一个构建数据的视图集合,包含了 Vuex + Vue Router 的 Vue 项目 gzip 之后 也只有 30kB
  • 双向数据绑定,数据操作较为简单。
  • Vue 在不同组件间强制使用单向数据流。这使应用中的数据流更加清晰易懂。
  • 组件式开发,可复用,低耦合,使用小型、独立和可复用的组件构建应用
  • 模板语法:Vue.js 使用了基于 HTML 的模板语法。DOM操作是十分耗费性能的,在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
  • 基于 HTML 的模板使得将已有的应用逐步迁移到 Vue 更为容易
  • 单页面应用不利于seo,可以通过SSR服务端渲染来做seo,如nuxt.js

常用的指令

  • 条件渲染指令 v-if
  • 列表渲染指令v-for
  • 属性绑定指令v-bind 缩写:
<!-- 绑定一个 attribute -->
<img v-bind:src="imageSrc">

<!-- 缩写 -->
<img :src="imageSrc">

<!-- 动态 attribute 名缩写 (2.6.0+) -->
<button :[key]="value"></button>
  • 事件绑定指令v-on 缩写@
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>

<!-- 缩写 -->
<button @click="doThis"></button>

<!-- 动态事件缩写 (2.6.0+) -->
<button @[event]="doThis"></button>
  • 双向数据绑定指令v-model
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

CSS系列之px/em/rem的区别,详解em和rem是如何转换为像素值

三者的区别

  • px是固定的像素,是相对于显示器屏幕分辨率而言的,一旦设置了就无法因为适应页面大小而改变。

为了解决px的问题,提出了em。它是相对于当前对象内文本的字体尺寸,若没有设置行内文本的字体尺寸,则使用浏览器默认字体尺寸,一般为16px。

em使用起来依旧有诸多不便,复杂的嵌套逻辑使得更改了部分元素会产生字体大小变化的连锁反应。

remCSS3新增加的相对单位,即root em,是相较于根元素(html)。rem的根据根节点使得计算更清晰。

  • emrem相对于px更具有灵活性,他们是相对长度单位,意思是长度不是定死了的,更适用于响应式布局,场景如移动端。emrem都是由浏览器转换为像素值,具体大小取决于您的设计中的字体大小设置。

em 和 rem 单位之间的区别是浏览器根据谁来转化成px值,理解这种差异是决定何时使用哪个单元的关键。

我们要通过复习 rem 和 em 单位如何工作,来理解em和rem之间的区别。

em 单位如何转换为像素值

当使用em单位时,像素值将是em值乘以使用em单位的元素的字体大小,他们转换为像素大小取决于应用在当前元素的字体尺寸。

例如,如果一个 div 有 18px 字体大小,10em 将等同于 180px,即 10 × 18 = 180。

在这里插入图片描述

CSS padding设置为 10em

在这里插入图片描述

计算结果为 180px (或接近它)

rem 单位如何转换为像素值

当使用 rem 单位,他们转化为像素大小取决于页根元素的字体大小,即 html 元素的字体大小。 根元素字体大小乘以你 rem 值。

例如,根元素的字体大小 16px,10rem 将等同于 160px,即 10 x 16 = 160。

在这里插入图片描述

CSS padding设置为 10rem

在这里插入图片描述

计算结果为160px

重点理解

有一个比较普遍的误解,认为 em 单位是相对于父元素的字体大小。 事实上,根据W3标准 ,它们是相对于使用em单位的元素的字体大小。

父元素的字体大小可以影响 em 值,但这种情况的发生,纯粹是因为继承。 让我们看看为什么以及如何起作用。

em 单位的遗传效果

使用 em 单位存在继承的时候,情况会变得比较棘手,因为每个元素将自动继承其父元素的字体大小。 继承效果只能被明确的字体单位覆盖,比如px,vw

使用 em 单位的元素字体大小根据它们来定。 但该元素可能继承其父元素的字体大小,而父元素又继承其父元素的字体大小,等等。 因此,以 em 为单位的元素字体大小可能会受到其任何父元素的字体大小影响。

再看rem的优点

rem这个单位与em有什么区别呢?区别在于使用rem为元素设定字体大小时,仍然是相对大小,但相对的只是HTML根元素。这个单位可谓集相对大小和绝对大小的优点于一身,通过它既可以做到只修改根元素就成比例地调整所有字体大小,又可以避免字体大小逐层复合的连锁反应

参考:

HTML系列之src和href的区别

什么是src

src是source的缩写,表示外部文件的引用,代表了指向外部资源的位置,把引用的文件加载到html页面的指定位置中去。

src属性是页面内容中不可缺少的一部分,常用到 js脚本,img图片和frame等元素。例如:

<script src="script.js">
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js">
<img src="2.png" />
<img src="https://apps.bdimg.com/img/2.png" />

什么是href

href是Hypertext Reference的简写,表示超文本引用,代表了指向网络资源的所在位置,它与页面直接的关系为链接的关系。

<link href="reset.css" rel=”stylesheet“/>
<a href="https://www.php.cn/">

区别

  • src是指向外部资源的位置,指向的内容会嵌入到文档中当前标签所在的位置,在请求src资源时会将其指向的资源下载并应用到文档内,如js脚本,img图片和frame等元素。当浏览器解析到该元素时,会暂停其他资源的下载和处理,知道将该资源加载、编译、执行完毕,所以一般js脚本会放在底部而不是头部。
  • href是指向网络资源所在位置(的超链接),用来建立和当前元素或文档之间的连接,当浏览器识别到它他指向的文件时,就会并行下载资源,不会停止对当前文档的处理。

HTML系列之语义化的理解

前言

很多面试官会问:

谈谈你对 HTML5语义化标签的理解。

那么本篇博客可以对你理解HTML5语义化标签有所帮助。

一:什么是语义元素?

语义元素清楚地向浏览器和开发者描述其意义。

非语义元素的例子:<div><span> - 无法提供关于其内容的信息。

语义元素的例子:<form><table> 以及 <img> 清晰地定义其内容。

二:为什么要语义化?

  • 代码结构: 使页面没有css的情况下,也能够呈现出很好的内容结构
  • 有利于SEO: 爬虫依赖标签来确定关键字的权重,因此可以和搜索引擎建立良好的沟通,帮助爬虫抓取更多的有效信息
  • 提升用户体验: 例如title、alt可以用于解释名称或者解释图片信息,以及label标签的灵活运用。
  • 便于团队开发和维护: 语义化使得代码更具有可读性,让其他开发人员更加理解你的html结构,减少差异化。
  • 方便其他设备解析: 如屏幕阅读器、盲人阅读器、移动设备等,以有意义的方式来渲染网页。

三:常用的语义元素

HTML5提供了新的语义元素来定义网页的不同部分,它们被称为“切片元素”,如图所示
在这里插入图片描述

四:文档结构标签

在这里插入图片描述

  • <main> 定义文档的主体部分
  • <nav> 定义导航链接

HTML 标签列表(功能排序) | 菜鸟教程 https://www.runoob.com/tags/ref-byfunc.html

五:参考

HTML系列之什么是HTML5?HTML5和HTML的区别

什么是HTML5

HTML5HTML的新标准,其主要目标是无需任何额外的插件如FlashSilverlight等,就可以传输所有内容。它囊括了动画、视频、丰富的图形用户界面等。

区别

从文档声明类型上看:

  • HTML是很长的一段代码,很难记住。如下代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • HTML5却只有简简单单的声明,方便记忆。如下:
<!DOCTYPE html>

从语义结构上看:

  • HTML4.0:没有体现结构语义化的标签,通常都是这样来命名的<div id="header"></div>,这样表示网站的头部。
  • HTML5:在语义上却有很大的优势。提供了一些新的标签,比如:<header><article><footer>

从语法的处理上看:

HTML无法处理不准确的语法;HTML5能够处理不准确的语法。

CSS系列之为什么要初始化CSS样式

因为浏览器的兼容问题,不同浏览器对一些标签的默认值是不同的,如果没对CSS初始化往往会出现浏览器之间的页面显示差异。

CSS系列之浏览器如何解析CSS选择器

CSS选择器的解析是从右向左解析的,为了避免对所有元素进行遍历。

  • 若从左向右的匹配,发现不符合规则,需要进行回溯,会损失很多性能。
  • 若从右向左匹配,先找到所有的最右节点,对于每一个节点,向上寻找其父节点直到找到根元素或满足条件的匹配规则,则结束这个分支的遍历。

两种匹配规则的性能差别很大,是因为从右向左的匹配在第一步就筛选掉了大量的不符合条件的最右节点(叶子节点),而从左向右的匹配规则的性能都浪费在了失败的查找上面。

而在 CSS 解析完毕后,需要将解析的结果与 DOM Tree 的内容一起进行分析建立一棵 Render Tree,最终用来进行绘图。

在建立 Render Tree 时(WebKit 中的「Attachment」过程),浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果(Style Rules)来确定生成怎样的 Render Tree。

CSS系列之媒体查询

媒体查询可以针对不同的屏幕尺寸设置不同的样式,特别是如果你需要设置设计响应式的页面。

最简单的媒体查询语法看起来是像这样的:

@media media-type and (media-feature-rule) {
  /* CSS rules go here */
}

它由以下部分组成:

  • 一个媒体类型,告诉浏览器这段代码是用在什么类型的媒体上的(例如印刷品或者屏幕);
  • 一个媒体表达式,是一个被包含的CSS生效所需的规则或者测试;
  • 一组CSS规则,会在测试通过且媒体类型正确的时候应用。

一个例子:

@media screen and (width: 600px) {
    body {
        color: red;
    }
}

CSS系列之png、jpg、gif 和 webp 图片格式的理解

  • png是便携式网络图片(Portable Network Graphics)是一种无损数据压缩位图文件格式.优点是:压缩比高,色彩好。 大多数地方都可以用。
  • jpg是一种针对相片使用的一种失真压缩方法,是一种破坏性的压缩,在色调及颜色平滑变化做的不错。在www上,被用来储存和传输照片的格式。
  • gif是一种位图文件格式,以8位色重现真色彩的图像。可以实现动画效果。
  • webp格式是谷歌在2010年推出的图片格式,压缩率只有jpg的2/3,大小比png小了45%。缺点是压缩的时间更久了,兼容性不好,目前谷歌和opera支持。

Webpack性能优化系列之 HMR 热模块替换

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

webpack性能优化

webpack性能优化主要指两个环境:

  • 开发环境性能优化
  • 生产环境性能优化

除了前面的开发环境和生产环境配置,还有以下性能优化处理:

开发环境性能优化包括:

  • 优化打包构建速度
    • HMR
  • 优化代码调试
    • source-map

生产环境性能优化包括:

  • 优化打包构建速度
    • oneOf
    • babel缓存
    • 多进程打包
    • externals
    • dll
  • 优化代码运行的性能
    • 缓存(hash-chunkhash-contenthash)
    • tree shaking
    • code split
    • 懒加载/预加载
    • pwa

HMR 热模块替换

为什么要使用HMR 热模块替换功能?

我们回看一下前面总结的开发环境配置:

/*
  开发环境配置:能让代码运行
    运行项目指令:
      webpack 打包运行项目
      npx webpack-dev-server 启动webpack-dev-server
*/

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
      {
        // 处理less资源
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 处理css资源
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          // 关闭es6模块化
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        // 处理html中img资源
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        // 处理其他资源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true
  }
};

下面我们看一下这种配置存在什么问题

运行指令:npx webpack-dev-server ,启动webpack-dev-server

然后在index.js文件中打印一段话:console.log('index.js文件被加载了~');

在这里插入图片描述

只要index.js文件重新被加载,就会打印上面那段话

现在去修改样式文件

在这里插入图片描述

#box {
  width: 400px;// 200px修改为400px
  height: 200px;
  background-image: url('../imgs/angular.jpg');
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

修改之后,很明显整体页面被刷新,内容被重新打印了一次

在这里插入图片描述

意思就是:当我们修改css文件时,明明js文件没有变化,但js文件也被重新加载了一次,所以打包的时候,看似只修改了css文件,实际上是把js文件也重新打包了一次,这是一个问题。

再写一个模块:

在这里插入图片描述

在index.js中引入上面那个print模块,并且在下面调用一下:

在这里插入图片描述

效果:

在这里插入图片描述

发现模块被加载,并且模块的print方法被调用执行了

现在我们修改一下print.js文件,'hello print'改为`'hello webpack'

console.log('print.js被加载了~');

function print() {
  const content = 'hello webpack';
  console.log(content);
}

export default print;

保存查看效果,可以发现整个页面被重新刷新了,所有代码重新执行:

在这里插入图片描述

这个意思就代表:假设以后我们有100个js模块,100个样式模块,如果只修改其中某一个模块,这整200个模块就需要重新打包,这个速度可以想想是非常慢的,更不要说以后的模块会越来越多,那打包情况只会越来越慢。

所以我们想实现这样的功能:如果只有一个模块发生变化,那么只需要修改这一个模块代码就足够了,其他模块理应是不变的

做这个功能要使用webpack的HMR 热模块替换

HMR: hot module replacement 热模块替换 / 模块热替换

作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度

HMR功能的使用非常简单,只需要在devServer里配置hot:true即可:

  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
    // 开启HMR功能
    // 当修改了webpack配置,新配置要想生效,必须重新webpack服务
    hot: true
  }

修改配置后,运行指令:npx webpack-dev-server ,重新启动webpack-dev-server

开启HMR功能跟之前更新有什么区别?

首先打印结果这里已经显示HMR功能已经开启:

在这里插入图片描述

下面修改样式文件,保存,可以发现只更新一个样式文件,js文件并不会重新加载:

在这里插入图片描述

可以发现:样式文件可以使用HMR功能,之所以能使用是因为style-loader内部实现了~

这也是为什么我们在开发环境要使用style-loader,生产环境要用MiniCssExtractPlugin.loader提取成单独文件,因为开发环境借助style-loader能让性能更好,打包速度更快,但是上线的时候考虑性能优化,所以不能用

前面样式文件可以使用HMR功能,js文件行不行呢?

修改print.js文件,保存,发现整个页面被重新刷新了一遍,这一点可以知道js文件默认不能使用HMR功能

html文件可以使用HMR功能吗?

在html文件中修改一段代码:

在这里插入图片描述

保存后发现当开启HMR功能后,html文件改动后并没有任何变化:

在这里插入图片描述

html文件: 默认不能使用HMR功能,同时会导致问题:html文件不能热更新了~

通过修改entey入口,改成一个数组,把html文件引入就可以解决上面的问题了:

entry: ['./src/js/index.js', './src/index.html'],

修改配置后,打包运行项目

在这里插入图片描述

测试发现html文件也是没有HMR功能的,一旦html文件发生变化,整体页面就会重新刷新

那么html文件需要HMR功能吗?

html不像js文件,一个项目里会有很多个js模块,而运行的html只会有一个,一旦发生变化,就一定要变化,它只需要变化一个文件,既然一定要变化,那就没办法优化,所以html文件不用做HMR功能

前面讲到了js默认不能使用HMR功能,这并不代表js就不需要HMR功能,如何对js文件使用HMR功能?

用法:

if (module.hot) {
  // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
  module.hot.accept('./print.js', function() {
    // 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
    // 会执行后面的回调函数
    print();
  });
}

在这里插入图片描述

修改print.js,查看效果

在这里插入图片描述

可以发现,print.js会重新更新,但index.js不会变,即index.js不会重新加载,样式和结构也不会重新加载,这就是js文件的HMR功能的使用

注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。

为什么呢?

因为入口文件会将其他文件全部引入,一旦入口文件变化,其他文件重新引入,就会重新加载,这是没办法阻止的,所以入口文件是做不了HMR功能的,只能对入口文件引入的一些依赖或者其他文件做HMR功能。

比如修改入口文件下面的打印代码:

在这里插入图片描述

保存,可以发现所有东西都要重新打包构建:

在这里插入图片描述

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

Webpack系列之开发环境配置(打包样式、html、图片和其他资源,配置devServer)

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

webpack.config.js

webpack 开箱即用,可以无需使用任何配置文件。然而,webpack 会假定项目的入口起点为 src/index.js,然后会在 dist/main.js 输出结果,并且在生产环境开启压缩和优化

通常你的项目还需要继续扩展此能力,为此你可以在项目根目录下创建一个 webpack.config.js 文件,然后 webpack 会自动使用它。

webpack.config.js是webpack的配置文件。 作用是:指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)

所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs。

下面开始介绍webpack的这个配置文件

创建配置文件

1. 创建文件 webpack.config.js

2. 配置内容如下

const { resolve } = require('path'); // node 内置核心模块,用来处理路径问题。

module.exports = {
	entry: './src/js/index.js', // 入口文件
	output: { // 输出配置
		filename: './built.js', // 输出文件名
		path: resolve(__dirname, 'build/js') // 输出文件路径配置
	},
	mode: 'development' //开发环境
};

如何打包样式资源以及loader执行顺序是什么

打包 CSS 资源

1. 下载安装 loader 包

npm i css-loader style-loader less-loader less -D

2. 修改配置文件

/*
  webpack.config.js  webpack的配置文件
    作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)

    所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs。
*/

// resolve用来拼接绝对路径的方法
const { resolve } = require('path');

module.exports = {
  // webpack配置
  // 入口起点
  entry: './src/index.js',
  // 输出
  output: {
    // 输出文件名
    filename: 'built.js',
    // 输出路径
    // __dirname nodejs的变量,代表当前文件的目录绝对路径
    path: resolve(__dirname, 'build')
  },
  // loader的配置
  module: {
    rules: [
      // 详细loader配置
      // 不同文件必须配置不同loader处理
      {
        // 匹配哪些文件
        test: /\.css$/,
        // 使用哪些loader进行处理
        use: [
          // use数组中loader执行顺序:从右到左,从下到上 依次执行
          // 创建style标签,将js中的样式资源插入进行,添加到head中生效
          'style-loader',
          // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
          'css-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          // 将less文件编译成css文件
          // 需要下载 less-loader和less
          'less-loader'
        ]
      }
    ]
  },
  // plugins的配置
  plugins: [
    // 详细plugins的配置
  ],
  // 模式
  mode: 'development', // 开发模式
  // mode: 'production'
}

模块loader可以链式调用。链中的每个loader都将对资源进行转换。链会逆序执行。第一个loader将其结果(被转换后的资源)传递给下一个loader,依此类推。最后,webpack期望链中的最后的loader 返回 JavaScript。

应保证 loader 的先后顺序:'style-loader' 在前,而 'css-loader' 在后。如果不遵守此约定,webpack 可能会抛出错误。

不同文件必须配置不同loader处理

css文件要用style-loadercss-loader

less文件要用less-loaderstyle-loadercss-loader

具体这三个loder的作用见上面代码注释。

loader执行顺序(非常重要,切记!!!):

use数组中loader执行顺序:从右到左,从下到上 依次执行

其实所谓的从下到上就是把数组里的每个元素换行展示了,本质还是数组从右至左。

如何打包html资源

打包html资源需要用到html-webpack-plugin这个插件,下载安装 plugin 包

npm install --save-dev html-webpack-plugin

实例文件目录:

在这里插入图片描述

loader使用的一个区别是,插件plugin下载后需要引入后才能使用:

// 引入
const HtmlWebpackPlugin = require('html-webpack-plugin');

默认配置:

/*
  loader: 1. 下载   2. 使用(配置loader)
  plugins: 1. 下载  2. 引入  3. 使用
*/
const { resolve } = require('path');
// 引入
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
    ]
  },
  plugins: [
    // plugins的配置
    // html-webpack-plugin
    // 功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
    new HtmlWebpackPlugin()
  ],
  mode: 'development'
};

html-webpack-plugin插件的功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)

在这里插入图片描述

实际开发中我们的需求:需要有结构的HTML文件

我们可以通过对HtmlWebpackPlugin添加配置选项template,添加目标html文件的路径即可,具体配置如下:

/*
  loader: 1. 下载   2. 使用(配置loader)
  plugins: 1. 下载  2. 引入  3. 使用
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
    ]
  },
  plugins: [
    // plugins的配置
    // html-webpack-plugin
    // 功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
    // 需求:需要有结构的HTML文件
    new HtmlWebpackPlugin({
      // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

目标html文件:

在这里插入图片描述

打包出来的文件:

在这里插入图片描述

html-webpack-plugin插件设置template配置属性的功能:复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)

如何打包图片资源

为什么需要对webpack做打包图片的配置?我们先看一个在样式中引入图片(background-image)的实例

实例目录结构如下:

在这里插入图片描述

index.html:

在这里插入图片描述

index.js:

在这里插入图片描述

index.less:

在这里插入图片描述

如果我们不对图片资源做配置的话,基础的webpack.config.js配置如下:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.less$/,
        // 要使用多个loader处理用use
        use: ['style-loader', 'css-loader', 'less-loader']
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

对项目进行打包后,我们发现图片资源处理不了,在打包图片的时候是报错的:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

所以我们需要对图片进行单独的处理

我们使用url-loader来处理图片资源,这里需要下载url-loaderfile-loade两个loader:

npm i url-loader file-loade -D

url-loader有相应的优化配置属性limit,写在options里,

limit属性的作用:图片大小小于指定数值时,就会被base64处理

  • 优点:减少请求数量(减轻服务器压力)
  • 缺点:图片体积会更大(文件请求速度更慢)

配置如下:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.less$/,
        // 要使用多个loader处理用use
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 问题:默认处理不了html中img图片
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        // 使用一个loader
        // 下载 url-loader file-loader
        loader: 'url-loader',
        options: {
          // 图片大小小于8kb,就会被base64处理
          // 优点: 减少请求数量(减轻服务器压力)
          // 缺点:图片体积会更大(文件请求速度更慢)
          limit: 8 * 1024
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

配置完成后再次打包运行:

在这里插入图片描述

图片资源没有报错。

这里我们备注一下上面代码里我们用到的三张图片资源:

angular.jpg,12kb大小:

在这里插入图片描述

react.png,74kb大小:

在这里插入图片描述

vue.jpg,4kb大小:

在这里插入图片描述

再来看一下输出的资源:

在这里插入图片描述

我们发现打包后的资源输出了两张图片,一张74kb,一张12kb,但我们实际上引入了三张图片,还有以这样图片去哪里呢?

我们看build:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我们发现vue.jpg经过了base64处理,变成了一个非常长的编码字符串,变成了字符串的形式,而不会输出成一张图片,所以我们输出的结果就少了一张图片。这正是前面url-loader做优化配置属性limit后的效果。

我们打开build文件下的index.html文件验证一下效果:

在这里插入图片描述

三张图片正常显示

我们检查一下html结构,可以发现box1盒子背景图片(vue.jpg)的url是一个base64的地址:

在这里插入图片描述

到这里我们处理了样式里的图片资源,只是这样还不够,因为除了在样式中引入图片以外,我们还可能在html中通过<img>标签的方式引入图片:<img src="./angular.jpg" alt="angular">

在这里插入图片描述

在html中通过<img>标签的方式引入图片后,在原有的配置中我们再打包一次,发现没有报错,输出的结果和之前一样,打开build下的index.html:

在这里插入图片描述

img的路径还是src="./angular.jpg"./是相对路径,但是相对路径下没有angular.jpg这张图片:

在这里插入图片描述

我们打开build下的index.html,发现<img src="./angular.jpg" alt="angular">这张图片是找不到的:

在这里插入图片描述

这是因为上面url-loader的配置有一个问题:默认处理不了html中img图片

这里我们还要再加一个loader:html-loader作用是:处理html文件的img图片(负责引入img,从而能被url-loader进行处理)

下载html-loader:

npm i html-loader -D

配置:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.less$/,
        // 要使用多个loader处理用use
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 问题:默认处理不了html中img图片
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        // 使用一个loader
        // 下载 url-loader file-loader
        loader: 'url-loader',
        options: {
          // 图片大小小于8kb,就会被base64处理
          // 优点: 减少请求数量(减轻服务器压力)
          // 缺点:图片体积会更大(文件请求速度更慢)
          limit: 8 * 1024
        }
      },
      {
        test: /\.html$/,
        // 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
        loader: 'html-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

上面的配置打包后发现还是没有报错,但是build下的index.html文件里img的src变成了[object Module]

在这里插入图片描述

这是为什么呢?

这是 因为url-loader默认使用es6模块化解析,而html-loader引入图片是common.jsurl-loader以es6模块去解析common.js模块,解析不了。

解决方法:关闭url-loader的es6模块化,使用commonjs解析

{
  // 问题:默认处理不了html中img图片
  // 处理图片资源
  test: /\.(jpg|png|gif)$/,
  // 使用一个loader
  // 下载 url-loader file-loader
  loader: 'url-loader',
  options: {
    // 图片大小小于8kb,就会被base64处理
    // 优点: 减少请求数量(减轻服务器压力)
    // 缺点:图片体积会更大(文件请求速度更慢)
    limit: 8 * 1024,
    // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
    // 解析时会出问题:[object Module]
    // 解决:关闭url-loader的es6模块化,使用commonjs解析
    esModule: false
  }
},

关闭url-loader的es6模块化,再次打包,没有报错

查看build下的index.html文件

在这里插入图片描述

img的src变成[object Module]的问题已经解决,变成了完整的绝对路径,上面的长字符串是根据图片内容生成的唯一hash值。

打开build下的index.html文件,html里的图片显示正常:

在这里插入图片描述

我们发现打包后图片的名字有点长,这是根据图片内容生成的唯一hash值:

在这里插入图片描述

不想让图片名字这么长可以通过url-loader里的name属性来给图片进行重命名:name: '[hash:10].[ext]'

  • [hash:10]取图片的hash的前10位
  • [ext]取文件原来扩展名

对图片资源最终的配置如下:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.less$/,
        // 要使用多个loader处理用use
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 问题:默认处理不了html中img图片
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        // 使用一个loader
        // 下载 url-loader file-loader
        loader: 'url-loader',
        options: {
          // 图片大小小于8kb,就会被base64处理
          // 优点: 减少请求数量(减轻服务器压力)
          // 缺点:图片体积会更大(文件请求速度更慢)
          limit: 8 * 1024,
          // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
          // 解析时会出问题:[object Module]
          // 解决:关闭url-loader的es6模块化,使用commonjs解析
          esModule: false,
          // 给图片进行重命名
          // [hash:10]取图片的hash的前10位
          // [ext]取文件原来扩展名
          name: '[hash:10].[ext]'
        }
      },
      {
        test: /\.html$/,
        // 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
        loader: 'html-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

打包后的图片名称已经变成取图片的hash的前10位

在这里插入图片描述

另外我们还可以发现webpack打包的一个优势:

上面的例子中html里引用了一次angular.jpg,在样式文件里也引用了一次angular.jpg,但打包之后其实只生成了一张angular.jpg的图片文件

所以在这里就是当webpack在解析的时候发现我们使用了同一个文件,它不会重复打包,它只会输出一次,这也是webpack的一个优势,它不会重复打包某一个文件。

如何打包其他资源

什么是其他资源呢?比如说字体图标icon

字体图标的特点就是我们不需要做任何处理,只需要原封不动的输出出去就ok了,这就是其他资源,不需要做优化,也不需要做压缩啊等等,这类资源统一归类于其他资源。

我们先下载一个字体图标,这里使用iconfont图标,随便选择一个,直接点击下载代码

在这里插入图片描述

下载完之后解压一下,这里包含了字体图标的所有配置

在这里插入图片描述

我们这里使用font-class 引用

在这里插入图片描述

我们新建文件夹,src/index.html,使用字体图标看一看效果

在这里插入图片描述

在这里插入图片描述

再写一个入口js文件,index.js,作用就是负责引入字体图标样式文件

在这里插入图片描述

iconfont.css文件里又引用了.eot.woffttf.svg资源,我们也需要放入项目中,具体看前面的实例项目文件目录

在这里插入图片描述

打包其他资源我们可以使用exclude排除已经处理过或者不需要处理的资源,然后使用file-loader对其他资源进行处理,一个小的优化就是可以对打包后的文件名称做一个缩短优化

具体配置:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      // 打包其他资源(除了html/js/css资源以外的资源)
      {
        // 排除css/js/html资源
        exclude: /\.(css|js|html|less)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

打包运行项目,字体图标显示正常
sa

如何配置devServer

webpack-dev-server具有实时重新加载 (live reloading) 的功能。用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)

特点:只会在内存中编译打包,不会有任何输出

启动devServer指令为npx webpack-dev-server

webpack devServer启动gzip压缩:compress: true

配置如下:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      // 打包其他资源(除了html/js/css资源以外的资源)
      {
        // 排除css/js/html资源
        exclude: /\.(css|js|html|less)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',

  // 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
  // 特点:只会在内存中编译打包,不会有任何输出
  // 启动devServer指令为:npx webpack-dev-server
  devServer: {
    // 项目构建后路径
    contentBase: resolve(__dirname, 'build'),
    // 启动gzip压缩
    compress: true,
    // 端口号
    port: 3000,
    // 自动打开浏览器
    open: true
  }
};

配置好devServer,启动devServer,打开浏览器,输入:localhos:3000,这就是我们使用devServer搭建的服务器,这个服务器启动的项目。

开发环境配置总结——一个完整的开发环境配置

除了之前的配置,我们还要做一些处理,之前编写的实例目录有点乱

在这里插入图片描述

我们划分一下目录

在这里插入图片描述

把原来的路径修改好后,输入命令webpack打包项目,看一下输出结果:

在这里插入图片描述

我们发现所有的资源都输出到一块了,没有任何的差别

如果希望打包后的资源和原来的结构一样的,可以一个一个调

首先让先js文件输出到js的目录下,要修改outputfilename属性,在built.js前加js/

  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },

重新构建一下项目,可以发现build下面就多了一个js目录

在这里插入图片描述

图片资源修改构建路径要在url-loader中添加outputPath配置属性:outputPath: 'imgs'

 {
   // 处理图片资源
   test: /\.(jpg|png|gif)$/,
   loader: 'url-loader',
   options: {
     limit: 8 * 1024,
     name: '[hash:10].[ext]',
     // 关闭es6模块化
     esModule: false,
     outputPath: 'imgs'
   }
 },

这样图片资源就会构建在build下面的imgs文件夹下

在这里插入图片描述

其他资源修改构建路径也是添加outputPath配置属性:outputPath: 'media'

  {
    // 处理其他资源
    exclude: /\.(html|js|css|less|jpg|png|gif)/,
    loader: 'file-loader',
    options: {
      name: '[hash:10].[ext]',
      outputPath: 'media'
    }

在这里插入图片描述

把build目录删掉,重新打包一次,就可以看到清晰的结构

在这里插入图片描述

有人可能会问,为什么构建后的资源没有看到之前写的css文件?这是因为css-loader把css文件打包到js当中了(复习环节),它是跟js文件融为一体的,所以没有被单独打包出去

  {
     // 匹配哪些文件
     test: /\.css$/,
     // 使用哪些loader进行处理
     use: [
       // use数组中loader执行顺序:从右到左,从下到上 依次执行
       // 创建style标签,将js中的样式资源插入进行,添加到head中生效
       'style-loader',
       // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
       'css-loader'
     ]
   },

在这里插入图片描述

完整的开发环境配置:

/*
  开发环境配置:能让代码运行
    运行项目指令:
      webpack 会将打包结果输出出去
      npx webpack-dev-server 只会在内存中编译打包,没有输出
*/

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
      {
        // 处理less资源
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 处理css资源
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          // 关闭es6模块化
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        // 处理html中img资源
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        // 处理其他资源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true
  }
};

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

CSS系列之圣杯布局和双飞翼布局的实现和理解

圣杯布局

圣杯布局如图:

在这里插入图片描述

而且要做到左右宽度固定,中间宽度自适应。

1. 利用flex布局

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .header,
        .footer {
            height: 40px;
            width: 100%;
            background: red;
        }

        .container {
            display: flex;
        }

        .left {
            width: 200px;
            background: yellow;
        }

        .middle {
            flex: 1;
            background: pink;
        }

        .right {
            width: 250px;
            background: blue;
        }
    </style>
</head>

<body>
    <div class="header">header</div>
    <div class="container">
        <div class="left">left</div>
        <div class="middle">middle</div>
        <div class="right">right</div>
    </div>
    <div class="footer">footer</div>
</body>

</html>

在这里插入图片描述

2. float布局

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .header,
        .footer {
            height: 40px;
            width: 100%;
            background: red;
        }

        .footer {
        	/* footer 清除浮动 */
            clear: both;
        }

        .container {
            padding-left: 200px;
            padding-right: 250px;
        }

        .container div {
            position: relative;
            float: left;
        }

        .middle {
            width: 100%;
            background: yellow;
        }

        .left {
            width: 200px;
            background: pink;
            margin-left: -100%;
            left: -200px;
        }

        .right {
            width: 250px;
            background: blue;
            margin-left: -250px;
            left: 250px;
        }
    </style>
</head>

<body>
    <div class="header">这里是头部</div>
    <div class="container">
        <div class="middle">中间部分</div>
        <div class="left">左边</div>
        <div class="right">右边</div>
    </div>
    <div class="footer">这里是底部</div>
</body>

</html>

在这里插入图片描述

这种float布局是最难理解的,主要是浮动后的负margin操作,下面我们详细解析:

首先要注意我们实现该布局的三个盒子的父盒子宽度不是100%,左右是添加了padding的.container {padding-left: 200px;padding-right: 250px;} ,而左右的padding值正是left和right的宽度。

其次,非常重要的一个概念,子盒子里的margin值的百分比是基于父盒子的。

另外,要注意,最终实现好的圣杯布局里的“中间部分”的那个盒子,其实是第一个盒子,放在最前面的,并不是在三个盒子中间,顺序是这样的:先放“中间部分”,然后再依次放“左边”和“右边”。使用 float 实现圣杯布局就是在这个结构上实现的,这很重要。

F12打开控制台,我们去掉left和right最难理解的样式:

在这里插入图片描述

在这里插入图片描述

激活left的margin-left(注意:实践时逐渐改变这些值可以非常直观的理解原理),因为margin的100%是基于父盒子的,所以我们发现left逐渐往左移动,超出父盒子边界(margin的负值边界是基于父盒子的边界),当margin-left: -200px;时,left就顶到上层,处于最右边的位置了。

在这里插入图片描述

margin-left: -100%;时,处于最左边的位置了。

在这里插入图片描述

这时候,再让left往左移动一个自身的宽度:添加left: -200px;(left值也是基于父盒子的边界),就到我们的目标位置了。

在这里插入图片描述

同理,对于right:当margin-left: -250px;时,right就顶到上层,处于最右边的位置。

在这里插入图片描述

这时候,再让right往右移动一个自身的宽度:添加left: 250px;(注意:向右移动是正值哦,上面的是向左移动,所以是负值left: -200px;,不要记混了),就到我们的目标位置了。

left: 250px;换成right: -250px;也可以实现向右移动一个自身宽度。

在这里插入图片描述

到此,实现圣杯布局并且对他的原理进行了解析。

双飞翼布局

在这里插入图片描述

有了圣杯布局的铺垫,双飞翼布局也就问题不大啦。这里采用经典的float布局来完成。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .container {
            min-width: 600px;
        }

        .left {
            float: left;
            width: 200px;
            height: 400px;
            background: red;
            margin-left: -100%;
        }

        .center {
            float: left;
            width: 100%;
            height: 500px;
            background: yellow;
        }

        .center .inner {
            margin: 0 200px;
        }

        .right {
            float: left;
            width: 200px;
            height: 400px;
            background: blue;
            margin-left: -200px;
        }
    </style>
</head>

<body>
    <article class="container">
        <div class="center">
            <div class="inner">双飞翼布局</div>
        </div>
        <div class="left"></div>
        <div class="right"></div>
    </article>
</body>

</html>

双飞翼布局实现的原理,参考上面圣杯布局的解析。

注意:双飞翼布局的left和right是在主盒子里面,而不是在外面,所以我们只需要通过marin-left的负值就可以实现,不需要通过设置left再往左或者往右移动。

为什么center中间还要加一个inner呢?而且inner还要设置margin: 0 200px;

   <div class="center">
       <div class="inner">双飞翼布局</div>
   </div>
    .center .inner {
        margin: 0 200px;
    }

这也算是双飞翼布局的一点要点,添加的inner以及他的margin: 0 200px;正是双飞翼布局中间部分布局的核心,如果没有这点,就不是完整的双飞翼布局。这个inner就是我们以后在该布局中间区域添加内容的父盒子(或者说是顶层盒子)。

Vue系列之 Vue 和 JQuery 的区别

本系列的主题是 Vue,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

Vue 和 JQuery 的区别在哪?为什么放弃 JQuery用 Vue?

  1. jQuery是直接操作DOM,Vue不直接操作DOM,Vue的数据与视图是分开的,Vue只需要操作数据即可
  2. jQuery的操作DOM行为是频繁的,而Vue利用虚拟DOM的技术,大大提高了更新DOM时的性能
  3. Vue中不倡导直接操作DOM,开发者只需要把大部分精力放在数据层面上
  4. Vue集成的一些库,大大提高开发效率,比如Vuex,Router等

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

CSS系列之定位方式有哪几种?详解sticky粘性定位

css有五种定位方式,分别是:staticrelativeabsolutefixedsticky

1. static 静态定位

HTML 元素的默认值,指定元素使用正常的布局行为。

静态定位的元素 top, right, bottom, leftz-index 属性无效。

2. relative 相对定位

相对定位是相对于默认位置(即static时的位置)进行偏移。

相对定位元素经常被用来作为绝对定位元素的容器块。

3. absolute 绝对定位

绝对定位元素会被移出正常文档流,并不为元素预留空间。

相对于上级元素(一般是父元素)进行偏移。

通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置,如果元素没有已定位的父元素,那么它的位置相对于<html>

4. fixed 固定定位

固定定位元素会被移出正常文档流,并不为元素预留空间。

相对于视口(viewport,浏览器窗口)进行偏移。

元素的位置相对于浏览器窗口是固定位置。即使窗口是滚动的它也不会移动,打印时,元素会出现在的每页的固定位置。

5. sticky 粘性定位

粘性定位跟前面四个属性值都不一样,它会产生动态效果,很像relativefixed的结合。

粘性定位的元素是依赖于用户的滚动,在 position:relativeposition:fixed 定位之间切换。

sticky 粘性定位 详解

当元素距离页面视口(Viewport,也就是fixed定位的参照)顶部距离大于 50px 时,元素以 relative 定位表现,而当元素距离页面视口小于 50px 时,元素表现为 fixed 定位,也就会固定在顶部。

代码:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>菜鸟教程(runoob.com)</title>
  <style>
    .aaa {
      padding: 100px;
    }

    div.sticky {
      position: -webkit-sticky;
      position: sticky;
      top: 50px;

      padding: 5px;
      background-color: #cae8ca;
      border: 2px solid #4CAF50;
    }
  </style>
</head>

<body>

  <p>尝试滚动页面。</p>
  <p class="aaa">注意: IE/Edge 15 及更早 IE 版本不支持 sticky 属性。</p>

  <div class="sticky">我是粘性定位!</div>

  <div style="padding-bottom:2000px">
    <p>滚动我</p>
    <p>来回滚动我</p>
    <p>滚动我</p>
    <p>来回滚动我</p>
    <p>滚动我</p>
    <p>来回滚动我</p>
  </div>

</body>

</html>

效果:

在这里插入图片描述

表现方式如下:
距离页面顶部大于20px,表现为 position:relative;

在这里插入图片描述

距离页面顶部小于20px,表现为 position:fixed;

在这里插入图片描述

粘性定位的注意事项

先看一个例子,代码如下:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			*{			/*重置样式*/
				margin: 0;
				padding: 0;
			}
			dl{			/*dl是父元素*/
				padding-top: 24px;/*设置字距离盒子距离*/
			}
			dt{			/*设置粘性定位元素*/
				background: orange;/*背景颜色*/
				color:#fff;/*字体颜色*/

				position: sticky;/*设置粘性定位*/
				top:10px;/*设置粘性定位达到偏移量固定位置*/
			}
		</style>
		
	</head>
	<body>
		<div>
			<dl>
				<dt>A</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>B</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>C</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>D</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>E</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F1</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F2</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F3</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F4</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F5</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F6</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F7</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
            <dl>
				<dt>F</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F1</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F2</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F3</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F4</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F5</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F6</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
			<dl>
				<dt>F7</dt>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
				<dd>1111111</dd>
			</dl>
		</div>
	</body>
</html>

效果:

在这里插入图片描述

同一容器中多个粘贴定位元素独立偏移,因此可能重叠;位置上下靠在一起的不同容器中的粘贴定位元素则会鸠占鹊巢,挤开原来的元素,形成依次占位的效果(这个下面会具体讲到)。

第一个特性源自粘性定位的定义,这么设计主要是为了避免当多个滚动互相嵌套的时候,粘性定位混乱。

第二个特性原因就是本文提到的粘性定位的计算规则,具体解释:

当我们的粘性定位元素都在一个容器的时候,大家都公用一个巨大的粘性约束矩形,因此,滚动的时候会一个一个往上重叠。

当我们的粘性定位元素属于不同容器的时候,就会有多个不同的粘性约束矩形,这些粘性约束矩形正好一个一个排列得很整齐,于是视觉上达成一个巧合一般的约定,即上一个粘性定位元素被滚走,下一个粘性定位元素正好开始有粘性效果。

参考

CSS系列之页面导入样式时,使用link和@import的区别

link和@import的使用

先看看这两种方式都是如何使用的:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <!-- 实例1. link标签引入yellow.css,内联样式引入green.css -->
    <link rel="stylesheet" href="yellow.css">
    <style type="text/css">
        @import url("green.css");
    </style>
</head>
<body>
    <div style="width: 50px; height: 50px;"></div>
</body>
</html>

link和@import有什么区别?

  1. 从属关系link是html的标签,不仅可以加载 CSS 文件,还可以定义 RSS、rel(一些)连接属性等;而@import是css的语法,只有导入样式表的作用。
  2. 加载顺序:页面被加载时,link会和html同时被加载;而@import引入的 CSS 将在页面加载完毕后被加载。
  3. 兼容性link是 HTML 标签,所以不存在兼容性问题;而@import是 CSS2.1 才有的语法,所以只能在 IE5以上 才能识别。
  4. DOM:javascript只能控制dom去改变link标签引入的样式,而@import的样式不是dom可以控制的。
  5. link方式的样式权重高于@import的权重。

@import必须写在样式顶部,在html中 <style>@import url(引入的css路径);</style>

这个@import必须要写在style的第一行。

Webpack性能优化系列之缓存 (详解如何做babel缓存和文件资源缓存)

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

缓存

缓存我们会从两个点出发:一个是对babel进行缓存,另一个是对整体的文件资源进行缓存

babel缓存

日常开发中,我们永远是js代码最多,像结构和样式要么少要么很少,即使多也没有办法做更多的处理。

为什么要对babel进行缓存呢?因为babel要对我们写的js代码做编译处理,编译成一种浏览器能识别的语法,即所谓的js兼容性处理。在编译过程中,假设有100个js模块,只改动其中1个js模块,不可能把全部的模块都重新编译一遍,其他99个应该是不变的,这一点跟前面文章讲过的HMR功能一样:一个模块变,只变这一个模块,其他模块不变。而生产环境下又不能使用HMR功能,因为HMR是基于devServer,生产环境是不需要devServer的。

开启babel缓存只需要在babel-loader的配置里加一个参数即可:cacheDirectory: true

 {
   test: /\.js$/,
   exclude: /node_modules/,
   loader: 'babel-loader',
   options: {
     presets: [
       [
         '@babel/preset-env',
         {
           useBuiltIns: 'usage',
           corejs: { version: 3 },
           targets: {
             chrome: '60',
             firefox: '50'
           }
         }
       ]
     ],
     // 开启babel缓存
     // 第二次构建时,会读取之前的缓存
     cacheDirectory: true
   }
 },

开启babel缓存,之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程(recompilation process)。

文件资源缓存

写个例子来演示一下文件资源缓存

为了演示缓存,我们需要写一个服务器代码

server.js:

/*
  服务器代码
  启动服务器指令:
    npm i nodemon -g
    nodemon server.js 
    
    不需要下载nodemon
    node server.js
  访问服务器地址:
    http://localhost:3000

*/
const express = require('express');

const app = express();
// express.static向外暴露静态资源
// maxAge 资源缓存的最大时间,单位ms
app.use(express.static('build', { maxAge: 1000 * 3600 }));

app.listen(3000);

在这里插入图片描述

关于上述服务器代码疑问参考:Express细节探究(1)——app.use(express.static) - ADAM亚当 - 博客园

node server.js命令, 启动服务器

注意,上面的服务器设置了maxAge,即开启Cache-Control

通过network查看资源,刷新页面,可以看到资源来自cache,即来自缓存

在这里插入图片描述

查看请求可以发现请求头设置了Cache-Control,max-age=3600,即有效期为3600s,一个小时,意思就是这个资源会被强制缓存一个小时

在这里插入图片描述

这个缓存会带来新的问题:

假如我们修改代码,修改一下js代码

index.js:

import '../css/index.css';

function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

// eslint-disable-next-line
// console.log(sum(1, 2, 3, 4));// 修改前

// eslint-disable-next-line
console.log(sum(1, 2, 3, 4, 5));// 修改后

再次构建,打开浏览器刷新,会发现结果没有变化

在这里插入图片描述

修改background-color: deeppink;,原来为pink,再次构建,发现样式同样的有没有变化

在这里插入图片描述

这是为什么呢?

这是因为当前资源在强制缓存期间,它是不会访问服务器的,直接读取本地缓存

这就带来了一个问题,假使我们的资源在强缓存期间出现了严重的bug,开发人员需要紧急修复,但因为资源被强制缓存,就算修复了,也会因为还在强缓存期间而无效。

我们可以通过修改资源名称来解决这个问题

有三种方法可以修改资源名称:

hash: 每次wepack构建时会生成一个唯一的hash值。

问题: 因为js和css同时使用一个hash值。

如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)

chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样

问题: js和css的hash值还是一样的

因为css是在js中被引入的,所以同属于一个chunk

contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样

这三种方法,可以发现只有contenthash是没有副作用的,所以最后的结论就是使用contenthash来修改资源名称

使用方式:

入口文件

 entry: './src/js/index.js',
 output: {
   filename: 'js/built.[contenthash:10].js',
   path: resolve(__dirname, 'build')
 },

样式文件

 new MiniCssExtractPlugin({
   filename: 'css/built.[contenthash:10].css'
 }),

完整的配置:

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

/*
  缓存:
    babel缓存
      cacheDirectory: true
      --> 让第二次打包构建速度更快
    文件资源缓存
      hash: 每次wepack构建时会生成一个唯一的hash值。
        问题: 因为js和css同时使用一个hash值。
          如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)
      chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
        问题: js和css的hash值还是一样的
          因为css是在js中被引入的,所以同属于一个chunk
      contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样    
      --> 让代码上线运行缓存更好使用
*/

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常来讲,一个文件只能被一个loader处理。
            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
              先执行eslint 在执行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: { version: 3 },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ],
              // 开启babel缓存
              // 第二次构建时,会读取之前的缓存
              cacheDirectory: true
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.[contenthash:10].css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

Webpack性能优化系列之多进程打包 (极大的提升项目打包构建速度)

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

多进程打包

通过thread-loader开启多进程打包

npm install --save-dev thread-loader

使用时,需将此 loader 放置在其他 loader 之前。放置在此 loader 之后的 loader 会在一个独立的 worker 池中运行。

注意:

请仅在耗时的操作中使用此 loader!因为每个 worker 都是一个独立的 node.js 进程,其开销大约为 600ms 左右。

我们一般给bablethread-loader,即给bable开启多进程打包

用法:

  {
    test: /\.js$/,
    exclude: /node_modules/,
    use: [
+      /* 
+        开启多进程打包。 
+        进程启动大概为600ms,进程通信也有开销。
+        只有工作消耗时间比较长,才需要多进程打包
+      */
+      {
+        loader: 'thread-loader',
+        options: {
+ 		   // 产生的 worker 的数量,默认是 (cpu 核心数 - 1)
+          workers: 2 // 进程2个
+        }
+      },
      {
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                useBuiltIns: 'usage',
                corejs: { version: 3 },
                targets: {
                  chrome: '60',
                  firefox: '50'
                }
              }
            ]
          ],
          // 开启babel缓存
          // 第二次构建时,会读取之前的缓存
          cacheDirectory: true
        }
      }
    ]
  },

构建项目:

在这里插入图片描述

可以看到此次构建用了5.4s

我们看一下没有启用多进程打包是什么情况:

在这里插入图片描述

正常构建项目时间4.1s,可以看到我们开启多进程打包后构建的更慢了,主要原因是测试的几乎是一个空项目,js代码非常少,如果我们对一个正常的项目使用多进程打包,就可以看到正面的增益效果了。

对于thread-loader多进程打包,用的好的时候可以极大的提升我们的打包速度,用的差的时候,就会导致打包速度更慢

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

CSS系列之选择器的优先级

css基本选择器有哪些?

  1. 通配符选择器 * {}
  2. 标签选择器 body,div,p,ul,li {}
  3. id选择器 #name{}
  4. 类选择器(class选择器).center {}
  5. 后代选择器 .first span{}
  6. 子代选择器 .food>li{}
  7. 内联选择器 <p style=""></p>
  8. !important

权重计算规则:

  1. 第一优先级:无条件优先的属性只需要在属性后面使用!important。它会覆盖页面内任何位置定义的元素样式。
  2. 第一等:内联样式,如:style="color:red;",权值为1000.(该方法会造成css难以管理,所以不推荐使用)
  3. 第二等:ID选择器,如:#header,权值为0100.
  4. 第三等:类、伪类、属性选择器如:.bar, 权值为0010.
  5. 第四等:标签、伪元素选择器,如:div ::first-line 权值为0001.
  6. 通配符,子选择器,相邻选择器等。如*>,+, 权值为0000.
  7. 继承的样式没有权值。

css选择器优先级顺序

内联样式 > ID选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 元素选择器 = 关系选择器 = 伪元素选择器 > 通配符选择器

哪些属性可以继承?

可继承的属性:font-size, font-family, color

不可继承的样式:border, padding, margin, width, height

Webpack性能优化系列之 PWA (使用渐进式网络应用程序为我们的项目添加离线体验)

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

PWA

渐进式网络应用程序(progressive web application - PWA)可以为我们的项目添加离线体验,使用名为 Workbox 的 Google 项目,帮助我们更简单地为 web app 提供离线支持。

正常情况下,如果停止 server 然后刷新,则网页不再可访问。

比如掘金https://juejin.cn/,有网络时正常访问:

在这里插入图片描述

No throttling改为Offline关闭网络

在这里插入图片描述

再次刷新,网页不可访问:

在这里插入图片描述

淘宝使用了PWA技术,访问淘宝网关闭网络,刷新

在这里插入图片描述

可以看到关闭网络后网页依然可以访问,从network中能看到没有问题的资源基本上都是来自于ServiceWorker,就是来自PWA技术提供的资源

PWA的使用:

添加 workbox-webpack-plugin 插件,然后调整 webpack.config.js 文件:

npm install workbox-webpack-plugin --save-dev

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const WorkboxPlugin = require('workbox-webpack-plugin');

  module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js',
    },
    plugins: [
      new HtmlWebpackPlugin({
+       title: 'Progressive Web Application',
      }),
+     new WorkboxPlugin.GenerateSW({
+       //1. 帮助serviceworker快速启动
+       //2. 删除旧的 serviceworker

+       //生成一个 serviceworker 配置文件~
+       clientsClaim: true,
+       skipWaiting: true,
+     }),
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist'),
      clean: true,
    },
  };

完成这些设置,构建项目

...
                  Asset       Size  Chunks                    Chunk Names
          app.bundle.js     545 kB    0, 1  [emitted]  [big]  app
        print.bundle.js    2.74 kB       1  [emitted]         print
             index.html  254 bytes          [emitted]
precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.js  268 bytes          [emitted]
      service-worker.js       1 kB          [emitted]
...

现在你可以看到,生成了两个额外的文件:service-worker.js 和名称冗长的 precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.jsservice-worker.jsService Worker 文件,precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.jsservice-worker.js 引用的文件,所以它也可以运行。你本地生成的文件可能会有所不同;但是应该会有一个 service-worker.js 文件。

我们现在已经创建出一个 Service Worker。

注册 Service Worker

通过在入口文件添加以下注册代码来注册 Service Worker:

index.js

import { mul } from './test';
import '../css/index.css';

function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

// eslint-disable-next-line
console.log(mul(2, 3));
// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));

+ /*
+   1. eslint不认识 window、navigator全局变量
+     解决:需要修改package.json中eslintConfig配置
+       "env": {
+         "browser": true // 支持浏览器端全局变量
+       }
+    2. sw代码必须运行在服务器上
+       --> nodejs
+       -->
+         npm i serve -g
+         serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
+ */
+ // 注册serviceWorker
+ // 处理兼容性问题
+ if ('serviceWorker' in navigator) {
+   window.addEventListener('load', () => {
+     navigator.serviceWorker
+       .register('/service-worker.js')
+       .then(() => {
+         console.log('sw注册成功了~');
+       })
+       .catch(() => {
+         console.log('sw注册失败了~');
+       });
+   });
+ }

再次构建项目

现在来进行测试。停止 server 并刷新页面。如果浏览器能够支持 Service Worker,应该可以看到你的应用程序还在正常运行。

在这里插入图片描述

2021/11/19 update

目前来看淘宝已经下线了pwa (简单测试了一下,不严谨),搜了网上以前支持pwa的项目都已经不支持了🙄

2019 年 PWA(Progressive Web App) 凉了吗? - 知乎 (zhihu.com)

来自知乎 作者:张宇昂
确实挺凉的,去年年初给业务加了pwa之后由于各种原因没上本来还以为会掉队,因为那时候pwa已经挺成熟了,结果到现在都没听说有几个应用深度使用pwa。感觉pwa的用处不是很大,网速快的情况那些cache有没有都无所谓。pwa的高级形态还是得像微博的pwa一样可以做成类App的那种体验。然而在历史遗留产物不同页面都是完全不同的应用架构里只能重新编写之前的应用逻辑。在h5只有为app导流的意义下老板并没有意思让你花时间在这种事情上面

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

Webpack性能优化系列之 oneOf

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

oneOf

我们在写loader的时候,rules里面有非常非常多的loader规则,但是每个文件只能匹配一个 loader,被一个 loader 处理,因此可以使用 oneOf 唯一匹配,不需要每个文件把所有的 loader 都询问一遍,这样可以提高 loader 的执行效率

webpack.config.js写法配置如下:

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',//正常的,一个文件只能被一个loader处理,当一个文件要被多个loader处理,
        			   //一定要指定loader执行的先后顺序,先执行eslint再执行babel
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件,所以eslint-loader放在oneOf匹配之前执行
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常来讲,一个文件只能被一个loader处理。
            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
              先执行eslint 在执行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: {version: 3},
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ]
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

Webpack系列之简介与五个核心概念

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

webpack 是什么

webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。

在 webpack 看来, 前端的所有资源文件(js/json/css/img/less/...)都会作为模块处理。

它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。

在这里插入图片描述

webpack 五个核心概念

1. Entry

入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。

2. Output

输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。

3. Loader

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。

4. Plugins

插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。

5. Mode

模式(Mode)指示 webpack 使用相应模式的配置。

在这里插入图片描述

一个小的梳理:

  1. webpack能处理js/json资源,不能处理css/img等其他资源
  2. 能将 es6 的模块化语法转换成浏览器能识别的语法
  3. 不能将 js 的 es6 基本语法转化为 es5 以下语法
  4. 生产环境比开发环境多一个压缩js代码

基本的webpack配置结构

webpack.config.js是webpack的配置文件,下面是一个打包样式资源的例子:

注意:webpack的五个核心概念除了loader,其他四个都能在下面webpack.config.js导出对象的属性中体现,而loader配置的属性是:module

/*
  webpack.config.js  webpack的配置文件
    作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)

    所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs。
*/

// resolve用来拼接绝对路径的方法
const { resolve } = require('path');

module.exports = {
  // webpack配置
  // 入口起点
  entry: './src/index.js',
  // 输出
  output: {
    // 输出文件名
    filename: 'built.js',
    // 输出路径
    // __dirname nodejs的变量,代表当前文件的目录绝对路径
    path: resolve(__dirname, 'build')
  },
  // loader的配置
  module: {
    rules: [
      // 详细loader配置
      // 不同文件必须配置不同loader处理
      {
        // 匹配哪些文件
        test: /\.css$/,
        // 使用哪些loader进行处理
        use: [
          // use数组中loader执行顺序:从右到左,从下到上 依次执行
          // 创建style标签,将js中的样式资源插入进行,添加到head中生效
          'style-loader',
          // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
          'css-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          // 将less文件编译成css文件
          // 需要下载 less-loader和less
          'less-loader'
        ]
      }
    ]
  },
  // plugins的配置
  plugins: [
    // 详细plugins的配置
  ],
  // 模式
  mode: 'development', // 开发模式
  // mode: 'production'
}

环境参数

看一下自己的环境

查看node版本

node -v

node版本为:v14.15.4

查看webpack版本

npm info webpack

webpack版本为:[email protected]

在这里插入图片描述

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

Vue系列之组件的 data 为什么必须是一个函数

本系列的主题是 Vue,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

Vue 组件的 data 为什么必须是一个函数

JavaScript中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。

当 data 的值是一个对象时,它会在这个组件的所有实例之间共享。

而在Vue中,我们更多的是希望复用组件,这时就会产生问题。因为每个组件的实例都引用了相同的数据对象,更改其中一个实例的数据,就会改变其它实例中的数据。那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。

我们希望每个组件实例都管理其自己的数据。为了做到这一点,每个实例必须生成一个独立的数据对象。在 JavaScript 中,在一个函数中返回这个对象就可以了,这样就可以避免组件复用时的数据污染

data: function () {
  return {
    listTitle: '',
    todos: []
  }
}

🔸 为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?

因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

CSS系列之什么是BFC?如何形成?应用场景有哪些

什么是BFC?

块格式化上下文(Block Formatting Context,BFC),它是一个独立的渲染区域,与区域外部毫不相干。

如何形成BFC?

下列方式会创建块格式化上下文(BFC)

在这里插入图片描述

应用场景有哪些?

1. 防止外边距塌陷

创建新的 BFC 避免两个相邻 <div> 之间的 外边距合并 问题

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .blue,
        .red-inner {
            height: 50px;
            margin: 10px 0;
        }

        .blue {
            background: blue;
        }

        .red-outer {
            /* 通过overflow: hidden创建新的BFC */
            overflow: hidden;

            background: red;
        }
    </style>
</head>

<body>
    <div class="blue"></div>
    <div class="red-outer">
        <div class="red-inner">red inner</div>
    </div>
</body>

</html>

在这里插入图片描述

如果我们取消BFC,注释掉 overflow: hidden; 这行代码

  .red-outer {
      /* 通过overflow: hidden创建新的BFC */
      /* overflow: hidden; */

      background: red;
  }

在这里插入图片描述

从上图可以发现外边距塌陷了,这就是BFC的效果。

2. 防止浮动导致父元素高度塌陷

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .container {
            border: 10px solid red;
        }

        .inner {
            background: #08BDEB;
            height: 100px;
            width: 100px;
        }
    </style>
</head>

<body>
    <div class="container">
        <div class="inner"></div>
    </div>
</body>

</html>

上面的代码效果如下:

在这里插入图片描述

接下来将inner元素设为浮动:

.inner {
    float: left;
    
    background: #08BDEB;
    height: 100px;
    width: 100px;
  }

会产生这样的塌陷效果:

在这里插入图片描述

但如果我们对父元素设置BFC后, 这样的问题就解决了:

.container {
    border: 10px solid red;

	/* 通过overflow: hidden讲父盒子设置为BFC */
    overflow: hidden;
}

在这里插入图片描述

这也是清除浮动的一种方式。

HTML系列之前端存储

前端常用的本地存储方式有三种:cookielocalStoragesessionStorage

  • cookie:在HTML5标准前本地储存的主要方式,优点是兼容性好,缺点是大小只有4k,自动请求头加入cookie浪费流量,使用起来麻烦需要自行封装;

HTML5 提供了 sessionStorage (会话存储) 和 localStorage(本地存储)两个存储对象来对网页的数据进行添加、删除、修改、查询操作。

  • sessionStorage 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据,在浏览器打开期间存在,包括页面重新加载

  • localStorage 用于长久保存整个网站的数据,保存的数据没有过期时间直到手动去除

三者的具体对比如下:

在这里插入图片描述

cookie使用起来麻烦需要自行封装。

localStorage和sessionStorage使用相同的API:

在这里插入图片描述

Webpack性能优化系列之懒加载和预加载

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

懒加载

懒加载或者按需加载,会在文件需要使用时才加载,是一种很好的优化网页或应用的方式。

懒加载的使用加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

project

在这里插入图片描述

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpack</title>
</head>

<body>
  <h1>hello lazy loading</h1>
  <button id="btn">按钮</button>
</body>

</html>

test.js

console.log('test.js文件被加载了~');

export function mul(x, y) {
  return x * y;
}

export function count(x, y) {
  return x - y;
}

index.js

console.log('index.js文件被加载了~');

import { mul } from './test';

document.getElementById('btn').onclick = function() {
    console.log(mul(4, 5));
};

构建打开项目

在这里插入图片描述

可以发现,还没有点击按钮,test.js文件就已经加载了,这是没有做懒加载的效果

现在对test.js文件做懒加载:点击按钮的时候才加载test.js文件资源

懒加载其实就是用到了code splitting文章中动态导入的方法

懒加载写法:

index.js

console.log('index.js文件被加载了~');

// import { mul } from './test';

document.getElementById('btn').onclick = function() {
  // 懒加载~:当文件需要使用时才加载~
  import(/* webpackChunkName: 'test'*/'./test').then(({ mul }) => {
    console.log(mul(4, 5));
  });
};

构建结果:

在这里插入图片描述

现在的构建结果还有一个test,表示已经做了代码分割,因为前面已经讲到了,懒加载其实就是用到了code splitting文章中动态导入的方法

所以懒加载内部实现的前提条件就是先进行代码分割,先分割成单独的js文件,再对这个单独的js文件进行懒加载

打开项目:可以明显的看到,一开始这个test.js文件并没有加载

在这里插入图片描述

在这里插入图片描述

点击按钮:test.js文件才被加载

在这里插入图片描述

在这里插入图片描述

这里有一个问题,重复点击按钮会重复加载资源吗?

不会,资源第一次加载后,第二次及后续就会直接读取缓存,而不会重复加载资源

预加载

在声明 import 时,使用webapck的内置指令/* webpackPrefetch: true */就可以对指定资源进行预加载

index.js

console.log('index.js文件被加载了~');

// import { mul } from './test';

document.getElementById('btn').onclick = function() {
  // 预加载 prefetch:会在使用之前,提前加载js文件 
  // 正常加载可以认为是并行加载(同一时间加载多个文件)  
  // 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
    console.log(mul(4, 5));
  });
};

构建结果:

在这里插入图片描述

可以发现test.js资源用到了prefetch技术,即预加载

打开页面看到test.js已经被加载好了,

在这里插入图片描述

点击按钮的时候,读取的其实是缓存

在这里插入图片描述

加载方式的对比

  • 正常加载:可以认为是并行加载(同一时间加载多个文件)
  • 懒加载:当文件需要使用时才加载~
  • 预加载 prefetch:会在使用之前,提前加载js文件
    等其他资源加载完毕,浏览器空闲了,再偷偷加载资源

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

CSS系列之如何理解z-index和层叠上下文

如何理解z-index?

z-index 属性指定一个元素的堆叠顺序。

当元素之间重叠的时候, z-index 较大的元素会覆盖较小的元素在上层进行显示。

如何理解层叠上下文?

层叠上下文是HTML元素的三维概念,这些HTML元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的z轴上延伸。

在这里插入图片描述

在层叠上下文中,子元素同样也按照上面解释的规则进行层叠。 重要的是,其子级层叠上下文的 z-index 值只在父级中才有意义。子级层叠上下文被自动视为父级层叠上下文的一个独立单元。

Webpack性能优化系列之 tree shaking (去除未引用代码,减少代码体积)

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

tree shaking

tree shaking 是一个术语,用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 importexport

tree shaking 可以去除未引用代码,减少代码体积。

前提是:必须使用ES6模块化

使用方法:开启production环境就会自动启动tree shaking

问题:

tree shaking不同版本会有一点点差异,这些差异会无意之间将我们的css代码当作未引用代码而把它干掉

为了解决这个问题,我们需要在 package.json 中添加 "sideEffects" 属性。

"sideEffects": false 代表:所有代码都没有副作用(都可以进行tree shaking),这是sideEffects的默认值,这可能会把css / @babel/polyfill (副作用)等文件干掉

我们可以通过配置sideEffects解决上述问题:

package.json

// ...
"sideEffects": [
  "**/*.css",
  "**/*.scss",
  "./esnext/index.js",
  "./esnext/configure.js"
],
// ...

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

CSS系列之对flex的理解

flex

flex(弹性盒子)是一种更为简洁灵活的布局方式。

容器默认存在两根轴:横轴和纵轴。

使用弹性盒子,要在父盒子先设置display:flex

常用于:

  • 在父内容里面垂直居中一个块内容。
  • 分布对齐的场景等。

经常用到的属性

1. flex-direction

:) 规定容器中的项目是按行排列还是按列排列
  • row:横向从左到右排列(左对齐),默认的排列方式。
  • row-reverse:反转横向排列(右对齐,从后往前排,最后一项排在最前面。
  • column:纵向排列。
  • column-reverse:反转纵向排列,从后往前排,最后一项排在最上面。

2. justify-content

:) 定义了项目在主轴上的对齐方式
  • flex-start:弹性项目向行头紧挨着填充。这个是默认值。
  • flex-end:弹性项目向行尾紧挨着填充。
  • center:弹性项目居中紧挨着填充。(常用)
  • space-between:弹性项目平均分布在该行上。(常用)
  • space-around:弹性项目平均分布在该行上,两边留有一半的间隔空间。

效果图展示:

在这里插入图片描述

3. align-items

:) 定义了项目在“纵轴”上的对齐方式

语法:

align-items: flex-start | flex-end | center | baseline | stretch

各个值解析:

  • flex-start:侧轴(纵轴)起始位置的边界紧靠住该行的侧轴起始边界。
  • flex-end:侧轴(纵轴)起始位置的边界紧靠住该行的侧轴结束边界。
  • center:弹性盒子元素在该行的侧轴(纵轴)上居中放置。(常用)

题目: 请用flex实现三栏布局,高度已知,左右栏宽度300px,中间自适应。

用一个容器container包裹三栏,设置comtainer容器的display属性为flex,左右栏设置宽度为300px,中间栏设置flex:1,这里的1表示宽度比例,具体数值取决于其它盒子的flex值,由于这里其它盒子宽度固定,所以中间栏会自动填充。代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>三栏布局</title>
</head>
<style type="text/css">
    * {
        margin: 0;
        padding: 0;
    }

    .container {
        display: flex;
        /*只设置这一属性就OK*/
    }

    .left {
        background-color: aqua;
        width: 300px;
        height: 100px;
    }

    .center {
        height: 100px;
        background: #f296ff;

        flex: 1;
    }

    .right {
        height: 100px;
        background-color: #6ee28d;
        width: 300px;
    }
</style>

<body>
    <!-- 已知高度,写出三栏布局,左右宽度300px,中间自适应-->
    <div class="container">
        <div class="left"></div>
        <div class="center"></div>
        <div class="right"></div>
    </div>
</body>

</html>

效果图如下:

在这里插入图片描述
中间的盒子会随着浏览器窗口的放大缩小而变化。

题目:内容宽度等分

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>三栏布局</title>
</head>
<style type="text/css">
    .box {
        display: flex;
    }

    .box div {
        flex: 1;
        border: 1px solid red;
    }
</style>

<body>
    <div class="box">
        <div>1</div>
        <div>2</div>
        <div>3</div>
    </div>
</body>

</html>

效果图如下:

在这里插入图片描述

题目:flex: 1 代表什么意思

flex: 1flex: 1 1 0 的缩略形式。

flex 值为 1,这表示每个元素占用空间都是相等的,占用的空间是在设置 padding 和 margin 之后剩余的空间。

HTML系列之img的srcset的作用

img的srcset的作用

img 元素的 srcset 属性用于浏览器根据宽、高和像素密度来加载相应的图片资源。

属性格式:图片地址 宽度描述w 像素密度描述x,多个资源之间用逗号分隔。例如:

<img src="small.jpg " srcset="big.jpg 1440w, middle.jpg 800w, small.jpg 1x" />

上面的例子表示浏览器宽度达到 800px 则加载 middle.jpg ,达到 1400px 则加载 big.jpg。注意:像素密度描述只对固定宽度图片有效。

img 元素的 size 属性给浏览器提供一个预估的图片显示宽度。

属性格式:媒体查询 宽度描述(支持px),多条规则用逗号分隔。

<img src="images/gun.png" alt="img元素srcset属性浅析"
     srcset="images/bg_star.jpg 1200w, images/share.jpg 800w, images/gun.png 320w"
     sizes="(max-width: 320px) 300w, 1200w"/>

上面的例子表示浏览器视口为 320px 时图片宽度为 300px,其他情况为 1200px。

还有哪一个标签能起到跟srcset相似作用?(追问)

<picture>:picture 元素
HTML <picture> 元素通过包含零或多个 <source> 元素和一个 <img> 元素来为不同的显示/设备场景提供图像版本。浏览器会选择最匹配的子 <source> 元素,如果没有匹配的,就选择 <img> 元素的 src 属性中的URL。然后,所选图像呈现在<img>元素占据的空间中。

<!--Change the browser window width to see the image change.-->

<picture>
    <source srcset="/media/cc0-images/surfer-240-200.jpg"
            media="(min-width: 800px)">
    <img src="/media/cc0-images/painted-hand-298-332.jpg" alt="" />
</picture>

要决定加载哪个URL,用户代理检查每个 的 srcset、media 和 type 属性,来选择最匹配页面当前布局、显示设备特征等的兼容图像。

还有什么能起到跟srcset相似作用?(追问)

css image-set()

body {
    background-image: -webkit-image-set( url(../images/pic-1.jpg) 1x, url(../images/pic-2.jpg) 2x, url(../images/pic-3.jpg) 600dpi);
    background-image:         image-set( url(../images/pic-1.jpg) 1x, url(../images/pic-2.jpg) 2x, url(../images/pic-3.jpg) 600dpi);
}

上述代码将会为普通屏幕使用 pic-1.jpg,为高分屏使用 pic-2.jpg,如果更高的分辨率则使用 pic-3.jpg,比如印刷。

Vue系列之 MVVM 是什么,和 MVC 的区别

本系列的主题是 Vue,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

什么是 MVVM?

MVVM 是是Model-View-ViewModel的缩写,Model代表数据模型,定义数据操作的业务逻辑,View代表视图层,负责将数据模型渲染到页面上,ViewModel通过双向绑定把ViewModel进行同步交互,不需要手动操作DOM的一种设计**。

MVVM 的核心是 ViewModel 层,它就像是一个中转站,负责转换 Model 中的数据对象,让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。如下图所示:

在这里插入图片描述

一张图来说明 MVVM 的各个组成部分:

在这里插入图片描述

  • View 层

    View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建,为了更方便地展现 ViewModel 或者 Model 层的数据,已经产生了各种各样的模板语言,各大 MVVM 框架如 Vue 都有自己用来构建用户界面的内置模板语言

  • Model 层

    Model 是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开

  • ViewModel 层

    ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 View 层的数据模型是只包含状态的,比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示),而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。由于实现了双向绑定,ViewModel 的内容会实时展现在 View 层,这是激动人心的,因为前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新,真正实现数据驱动开发。看到了吧,View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。

什么是 MVC?

MVC 指的是Model-View-Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范

  • Model(模型):处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据
  • View(视图):处理数据显示的部分。通常视图是依据模型数据创建的
  • Controller(控制器):数据模型和视图之间通信的桥梁,通常控制器负责从事图读取数据,控制用户输入,并向模型发送数据

MVC的**:Controller负责将Model的数据用View显示出来,换句话说就是在Controller里面把Model的数据赋值给View。

MVC的特点:实现关注点分离,即应用程序中的数据模型与业务和展示逻辑解耦。就是将模型和视图之间实现代码分离,松散耦合,使之成为一个更容易开发、维护和测试的客户端应用程序。

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

HTML系列之常用的meta标签

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
</body>

</html>

<meta>:文档级元数据元素

meta 元素定义的元数据的类型包括以下几种:

  • 如果设置了 name 属性,meta 元素提供的是文档级别(document-level)的元数据,应用于整个页面。
  • 如果设置了 http-equiv 属性,meta 元素则是编译指令,提供的信息与类似命名的HTTP头部相同。
  • 如果设置了 charset 属性,meta 元素是一个字符集声明,告诉文档使用哪种字符编码。
  • 如果设置了 itemprop 属性,meta 元素提供用户定义的元数据。

另外, 在同一个 <meta> 标签中,name , http-equiv 或者 charset 三者中任何一个属性存在时,itemprop 属性不能被使用。

Webpack性能优化系列之 source-map

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

source-map

本文讲一下开发环境下如何调试代码,解决开发环境下调试代码问题,这个技术叫source-map

source-map: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)

在webpack中启动source-map只需要在配置中添加:devtool: 'source-map'

在这里插入图片描述

启动source-map后,打包运行就可以在build目录下看到built.js.map,这个文件就是source-map文件,它提供了源代码和构建后代码的映射关系

在这里插入图片描述

在这里插入图片描述

上面的写法是source-map最基本的写法

devtool的模式写法可以总结为:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

具体就是有以下几种模式:

1.source-map:外部

错误代码准确信息 和 源代码的错误位置

2.inline-source-map:内联

只生成一个内联source-map

错误代码准确信息 和 源代码的错误位置

3.hidden-source-map:外部

错误代码错误原因,但是没有错误位置

不能追踪源代码错误,只能提示到构建后代码的错误位置

4.eval-source-map:内联

每一个文件都生成对应的source-map,都在eval

错误代码准确信息 和 源代码的错误位置

5.nosources-source-map:外部

错误代码准确信息, 但是没有任何源代码信息

6.cheap-source-map:外部

错误代码准确信息 和 源代码的错误位置

只能精确的行

7.cheap-module-source-map:外部

错误代码准确信息 和 源代码的错误位置

module会将loader的source map加入


上述这些模式,其中一些值适用于开发环境,一些适用于生产环境。对于开发环境,通常希望更快速的 source map,需要添加到 bundle 中以增加体积为代价,但是对于生产环境,则希望更精准的 source map,需要从 bundle 中分离并独立存在。

我们看一下效果:

devtool: 'inline-source-map'

构建后没有built.js.map文件

在这里插入图片描述

但是会在built.js文件下面生成base64编码的source-map文件

在这里插入图片描述

这种方法是嵌到js中的,所以叫内联,像前面生成外部built.js.map文件的叫外联

内联 和 外部的区别:1. 外部生成了文件,内联没有 2. 内联构建速度更快

应用场景

前面讲了source-map的很多种配置方案,到底怎么用呢?

这里就需要考虑两种环境:开发环境和生产环境

开发环境:速度快,调试更友好

速度快(eval>inline>cheap>...):

  • eval-cheap-souce-map(首推)
  • eval-source-map

调试更友好:

  • souce-map(首推)
  • cheap-module-souce-map
  • cheap-souce-map

最终平衡速度和调试,开发环境推荐的方案:

  • eval-source-map(调试最友好)
  • eval-cheap-module-souce-map(性能更好)

如果我们使用vue或者react框架开发,都会有对应的脚手架,而脚手架的配置默认是:eval-source-map

生产环境:源代码要不要隐藏? 调试要不要更友好?

内联会让代码体积变大,所以在生产环境不用内联

需要隐藏源代码的方案:

  • nosources-source-map(全部隐藏)
  • hidden-source-map(只隐藏源代码,会提示构建后代码错误信息)

生产环境推荐方案:

  • source-map(调试最友好)
  • cheap-module-souce-map(性能更好)

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

CSS系列之清除浮动有哪些方法?为什么要清除浮动

什么是清除浮动?

清除浮动是指清除由于子元素浮动带来父元素高度塌陷的影响。

为什么要清除浮动?

  1. 导致背景不能正常显示,如果对父级设置了背景属性,导致父级不能撑开,会影响到背景图片不能正常打开。

  2. 边框不能撑开,由于子级使用了浮动效果,并且已经产生了浮动,父级不能撑开,所以影响边框不会随着内容的变化而变化。

清除浮动有哪些方法?

1. 额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)(不推荐)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .fahter {
            width: 400px;
            border: 1px solid deeppink;
        }

        .big {
            width: 200px;
            height: 200px;
            background: darkorange;
            float: left;
        }

        .small {
            width: 120px;
            height: 120px;
            background: darkmagenta;
            float: left;
        }

        .footer {
            width: 900px;
            height: 100px;
            background: darkslateblue;
        }

        .clear {
            clear: both;
        }
    </style>
</head>

<body>
    <div class="fahter">
        <div class="big">big</div>
        <div class="small">small</div>
        <div class="clear">额外标签法</div>
    </div>
    <div class="footer">footer</div>
</body>

</html>

此时:

在这里插入图片描述

如果我们清除了浮动,父元素自动检测子盒子最高的高度,然后与其同高。

<body>
    <div class="fahter">
        <div class="big">big</div>
        <div class="small">small</div>
        <!-- <div class="clear">额外标签法</div> -->
    </div>
    <div class="footer">footer</div>
</body>

如果注释掉最后一个用来清除浮动标签,我们可以看到浮动带来的影响(高度塌陷):

在这里插入图片描述

优点:通俗易懂,方便

缺点:添加无意义标签,语义化差

不建议使用。

2. 父级添加overflow属性(父元素添加overflow:hidden)(不推荐)

通过触发BFC方式,实现清除浮动

.fahter{
    width: 400px;
    border: 1px solid deeppink;
    
    overflow: hidden;
}

优点:代码简洁

缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素

不推荐使用

3. 使用after伪元素清除浮动(推荐使用)

    .clearfix:after{/*伪元素是行内元素 正常浏览器清除浮动方法*/
        content: "";
        display: block;
        height: 0;
        clear:both;
        visibility: hidden;
    }
    .clearfix{
        *zoom: 1;/*ie6清除浮动的方式 *号只有IE6-IE7执行,其他浏览器不执行*/
    }
 
<body>
    <div class="fahter clearfix">
        <div class="big">big</div>
        <div class="small">small</div>
        <!--<div class="clear">额外标签法</div>-->
    </div>
    <div class="footer"></div>
</body>

优点:符合闭合浮动**,结构语义化正确

缺点:ie6-7不支持伪元素:after,使用zoom:1触发hasLayout.

推荐使用

4. 使用before和after双伪元素清除浮动

     .clearfix:after,.clearfix:before{
        content: "";
        display: table;
    }
    .clearfix:after{
        clear: both;
    }
    .clearfix{
        *zoom: 1;
    }
 
 <div class="fahter clearfix">
        <div class="big">big</div>
        <div class="small">small</div>
 </div>
 <div class="footer"></div>

优点:代码更简洁

缺点:用zoom:1触发hasLayout.

推荐使用

参考

CSS系列之动画和过渡

CSS 中动画有几种?

两种!一种是过渡动画,一种是关键帧动画。

过度动画只能定义「开始」和「结束」两个状态。

关键帧动画可以定义「多个状态」。

如何写「过渡动画」?

使用 transition 属性即可撰写过渡动画。比如看下面的例子:

<html>
  <body>
    <div class="section">hello</div>
  </body>
</html>
.section{
  width: 100px;
  background-color: black;
  color: white;
  transition: 2s;
}

.section:hover{
  width: 300px;
}

上面代码的含义:

div 在平时状态下宽度为 100 像素,在鼠标悬浮其上的时候,宽度为 300 像素。

但是可以看到,我们通过加入了transition: 2s;这个属性,让宽度的变化不是瞬间完成的,而是在 2s 内逐步完成的。

这就是 transition 动画——也就是我们的过渡动画。

在这里插入图片描述

如何写关键帧动画?

使用 @keyframes 关键字定义动画。

下面的例子将在动画完成 25%,完成 50% 以及动画完成 100% 时更改 <div> 元素的背景颜色::

<!DOCTYPE html>
<html>
<head>
<style>
div {
  width: 100px;
  height: 100px;
  background-color: red;
  animation-name: example;
  animation-duration: 4s;
}

@keyframes example {
  0%   {background-color: red;}
  25%  {background-color: yellow;}
  50%  {background-color: blue;}
  100% {background-color: green;}
}
</style>
</head>
<body>

<p><b>注释:</b>本例在 Internet Explorer 9 以及更早的版本中无效。</p>

<div></div>

</body>
</html>

在这里插入图片描述

我们用百分比来规定变化发生的时间,或用关键词 "from" 和 "to",等同于 0% 和 100%。

0% 是动画的开始,100% 是动画的完成。

为了得到最佳的浏览器支持,我们最好定义 0% 和 100% 选择器。

使用关键字动画,需要在指定动画的元素上定义animation-name:动画名称,和animation-duration:动画完成一个周期所花费的时间。

animation-name:动画名称,将声明的动画名称和@keyframes 绑定用来描述动画;

animation-iteration-count:规定动画被播放的次数。默认是 1。该属性设置为 infinite 使动画永远持续下去。

animation-direction:指定是否应该轮流反向播放动画。该属性设置为 alternate 可实现来回运动(动画在奇数次(1、3、5...)正向播放,在偶数次(2、4、6...)反向播放)

一个来回运动的例子:

<div class="test">1</div>
.test {
    background-color: rgb(50, 188, 131);
    animation-name: slidein;
    animation-duration: 3s;
    animation-iteration-count: infinite;
    animation-direction: alternate;
}

@keyframes slidein {
    from {
        margin-left: 100%;
        width: 0;
    }

    to {
        margin-left: 0%;
        width: 500px;
    }
}

CSS系列之伪类和伪元素的区别

什么是伪类?

伪类用于定义元素的特殊状态。

例如,它可以用于:

  • 设置鼠标悬停在元素上时的样式
  • 为已访问和未访问链接设置不同的样式
  • 设置元素获得焦点时的样式

伪类开头为单冒号

a:link {color:#FF0000;} /* 未访问的链接 */
a:visited {color:#00FF00;} /* 已访问的链接 */
a:hover {color:#FF00FF;} /* 鼠标划过链接 */
a:active {color:#0000FF;} /* 已选中的链接 */

什么是伪元素?

CSS 伪元素用于设置元素指定部分的样式。

例如,它可用于:

  • 设置元素的首字母、首行的样式
  • 在元素的内容之前或之后插入内容

伪元素开头为双冒号 ::

::before /* 元素内容前插入新内容 */
::after /* 元素内容之后插入新内容 */
::first-letter /* 文本首字母。常用于对文本首字母设置样式 */
::first-line /* 文本首行。常用于对文本首行设置样式。仅用于块级元素 */
::selection /* 选中的内容。常用于文本 */
::placeholder /* 占位符。用于设置占位符的样式 */

HTML系列之data-属性

在HTML5中我们可以使用data-*(自定义数据属性)为前缀来设置我们需要的自定义属性,来进行一些数据的存取。

如何使用?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="div1" class="div1" data-id="myId" data-id-and-class="Hello">test2</div>
    <div id="div2" myName="Hello">test</div>
    <script>
        var div1 = document.getElementById("div1");
        //获取自定义的值
        var myId = div1.getAttribute("data-id");
        var my = div1.getAttribute("data-id-and-class");
        console.log(myId); // myId
        console.log(my); // Hello
        
        //设置自定义的值
        div1.setAttribute("data-name", "nicai")

        var div = document.getElementById("div2");
        var myName = div.getAttribute("myName");
        console.log(myName); //Hello
        </script>
</body>
</html>

getAttribute方法能在所有现代浏览器中正常工作,但它不是HTML5的自定义data-*属性被使用的目的,这个方法也用到我们以前使用自定义属性。

CSS系列之多类选择器和结合元素选择器

日常开发中我们会碰到css中两个类选择器之间有的有空格,有的没空格。有空格的是后代选择器,那没有空格的是什么呢?

没有空格的选择器有两种:多类选择器结合元素选择器

结合元素选择器

元素选择器可以结合类选择器来使用。

p.important {color:red;}

选择器 p.important 解释为:“其 class 属性值为 important 的所有段落”。我更想把其解释为:“段落中类名(class属性)为important的元素”,或者“是段落,同时类名也是important的”。

多类选择器

<html>
<head>
<style type="text/css">
.important {font-weight:bold;}
.warning {font-style:italic;}
.important.warning {background:red;}
</style>
</head>

<body>
<p class="important">This paragraph is very important.</p>
<p class="warning">This is a warning.</p>
<p class="important warning">This paragraph is a very important warning.</p>

<p>This is a paragraph.</p>

<p>...</p>
</body>
</html>

在这里插入图片描述
在 HTML 中,一个标签可能有多个类名(class),每个类名之间用空格分隔。

通过把两个类选择器链接在一起,中间不加空格,仅可以选择同时包含这些类名的元素(类名的顺序不限)。

Webpack性能优化系列之 code splitting (代码分割,优化资源加载)

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

code splitting

code splitting(代码分割)是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。

作用:代码分割可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。

常用的代码分割方法有三种:

  • 入口起点:使用 entry 配置多入口手动地分离代码。
  • 防止重复:使用 SplitChunksPlugin 去重和分离 chunk。
  • 动态导入:通过模块的内联函数调用来分离代码。

多入口手动分离代码

这是迄今为止最简单直观的分离代码的方式,如何从 main bundle 中分离 another module(另一个模块),即配置多入口:

project

webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
+ |- another-module.js
|- /node_modules

another-module.js

import _ from 'lodash';

console.log(_.join(['Another', 'module', 'loaded!'], ' '));

webpack.config.js

 const path = require('path');

 module.exports = {
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
+    filename: '[name].[contenthash:10].bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
 };

这将生成如下构建结果:

...
[webpack-cli] Compilation finished
asset index.bundle.js 553 KiB [emitted] (name: index)
asset another.bundle.js 553 KiB [emitted] (name: another)
runtime modules 2.49 KiB 12 modules
cacheable modules 530 KiB
  ./src/index.js 257 bytes [built] [code generated]
  ./src/another-module.js 84 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 245 ms

这种方式存在一些隐患:

  • 如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中。
  • 这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来。

上述隐患将造成重复引用。

解决方法:

配置 dependOn option 选项,这样可以在多个 chunk 之间共享模块:

webpack.config.js

 const path = require('path');

 module.exports = {
   mode: 'development',
   entry: {
+    index: {
+      import: './src/index.js',
+      dependOn: 'shared',
+    },
+    another: {
+      import: './src/another-module.js',
+      dependOn: 'shared',
+    },
+    shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
 };

如果我们要在一个 HTML 页面上使用多个入口时,还需设置 optimization.runtimeChunk: 'single',否则还会遇到这里所述的麻烦。

webpack.config.js

 const path = require('path');

 module.exports = {
   mode: 'development',
   entry: {
     index: {
       import: './src/index.js',
       dependOn: 'shared',
     },
     another: {
       import: './src/another-module.js',
       dependOn: 'shared',
     },
     shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
+  optimization: {
+    runtimeChunk: 'single',
+  },
 };

构建结果如下:

...
[webpack-cli] Compilation finished
asset shared.bundle.js 549 KiB [compared for emit] (name: shared)
asset runtime.bundle.js 7.79 KiB [compared for emit] (name: runtime)
asset index.bundle.js 1.77 KiB [compared for emit] (name: index)
asset another.bundle.js 1.65 KiB [compared for emit] (name: another)
Entrypoint index 1.77 KiB = index.bundle.js
Entrypoint another 1.65 KiB = another.bundle.js
Entrypoint shared 557 KiB = runtime.bundle.js 7.79 KiB shared.bundle.js 549 KiB
runtime modules 3.76 KiB 7 modules
cacheable modules 530 KiB
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
  ./src/another-module.js 84 bytes [built] [code generated]
  ./src/index.js 257 bytes [built] [code generated]
webpack 5.4.0 compiled successfully in 249 ms

由上可知,除了生成 shared.bundle.js,index.bundle.js 和 another.bundle.js 之外,还生成了一个 runtime.bundle.js 文件。

SplitChunksPlugin

SplitChunksPlugin 插件可以将公共的依赖模块node_modules提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。让我们使用这个插件,将之前的示例中重复的 lodash 模块去除:

webpack.config.js

  const path = require('path');

  module.exports = {
    mode: 'development',
    entry: {
      index: './src/index.js',
      another: './src/another-module.js',
    },
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist'),
    },
+   optimization: {
+     splitChunks: {
+       chunks: 'all',
+     },
+   },
  };

使用 optimization.splitChunks 配置选项之后,现在应该可以看出,index.bundle.js 和 another.bundle.js 中已经移除了重复的依赖模块。需要注意的是,插件将 lodash 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了大小。

总结:

1.可以将node_modules中代码单独打包一个chunk最终输出
2.自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk

构建结果如下:

...
[webpack-cli] Compilation finished
asset vendors-node_modules_lodash_lodash_js.bundle.js 549 KiB [compared for emit] (id hint: vendors)
asset index.bundle.js 8.92 KiB [compared for emit] (name: index)
asset another.bundle.js 8.8 KiB [compared for emit] (name: another)
Entrypoint index 558 KiB = vendors-node_modules_lodash_lodash_js.bundle.js 549 KiB index.bundle.js 8.92 KiB
Entrypoint another 558 KiB = vendors-node_modules_lodash_lodash_js.bundle.js 549 KiB another.bundle.js 8.8 KiB
runtime modules 7.64 KiB 14 modules
cacheable modules 530 KiB
  ./src/index.js 257 bytes [built] [code generated]
  ./src/another-module.js 84 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 241 ms

动态导入

当涉及到动态代码拆分时,可以使用符合 ECMAScript 提案 的 import() 语法 来实现动态导入。

先从上述示例的配置中移除掉多余的 entryoptimization.splitChunks,因为接下来的演示中并不需要它们:

webpack.config.js

 const path = require('path');

 module.exports = {
   mode: 'development',
   entry: {
     index: './src/index.js',
-    another: './src/another-module.js',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
-  optimization: {
-    splitChunks: {
-      chunks: 'all',
-    },
-  },
 };

project

在这里插入图片描述

index.js

function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

/*
  通过js代码,让某个文件被单独打包成一个chunk
  import动态导入语法:能将某个文件单独打包
*/
import(/* webpackChunkName: 'test' */'./test')
  .then(({ mul, count }) => {
    // 文件加载成功~
    // eslint-disable-next-line
    console.log(mul(2, 5));
  })
  .catch(() => {
    // eslint-disable-next-line
    console.log('文件加载失败~');
  });

// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));

构建结果如下:

在这里插入图片描述

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

CSS系列之性能优化总结

CSS性能优化的方式有那些?

  1. 内联首屏关键CSS
  2. 异步加载CSS
  3. 资源压缩
  4. 去除无用CSS
  5. 合理使用选择器
  6. 减少使用昂贵的属性
  7. 减少重排与重绘
  8. 不要使用@import

1. 内联首屏关键CSS

在打开一个页面,页面首要内容出现在屏幕的时间影响着用户的体验,而通过内联css关键代码能够使浏览器在下载完html后就能立刻渲染

而如果外部引用css代码,在解析html结构过程中遇到外部css文件,才会开始下载css代码,再渲染

所以,CSS内联使用使渲染时间提前

注意:但是较大的css代码并不合适内联(初始拥塞窗口、没有缓存),而其余代码则采取外部引用方式

2. 异步加载CSS

在CSS文件请求、下载、解析完成之前,CSS会阻塞渲染,浏览器将不会渲染任何已处理的内容

前面加载内联代码后,后面的外部引用css则没必要阻塞浏览器渲染。这时候就可以采取异步加载的方案,比如下面的方案:

使用javascript将link标签插到head标签最后

// 创建link标签
const myCSS = document.createElement( "link" );
myCSS.rel = "stylesheet";
myCSS.href = "mystyles.css";
// 插入到header的最后位置
document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );

3. 资源压缩

利用webpack、gulp/grunt、rollup等模块化工具,将css代码进行压缩,使文件变小,大大降低了浏览器的加载时间

4. 去除无用CSS

虽然文件压缩能够降低文件大小。但CSS文件压缩通常只会去除无用的空格,这样就限制了CSS文件的压缩比例。那是否还有其他手段来精简CSS呢?答案显然是肯定的,如果压缩后的文件仍然超出了预期的大小,我们可以试着找到并删除代码中无用的CSS。

一般情况下,会存在这两种无用的CSS代码:一种是不同元素或者其他情况下的重复代码,一种是整个页面内没有生效的CSS代码。

手动删除这些无用CSS是很低效的。我们可以借助Uncss7库来进行。Uncss可以用来移除样式表中的无用CSS,并且支持多文件和JavaScript注入的CSS。

5. 合理使用选择器

CSS选择器的匹配是从右向左进行的,这一策略导致了不同种类的选择器之间的性能也存在差异。相比于#markdown-content-h3,显然使用#markdown .content h3时,浏览器生成渲染树(render-tree)所要花费的时间更多。

所以我们在编写选择器的时候,可以遵循以下规则:

  • 不要嵌套使用过多复杂选择器,最好不要三层以上
  • 使用id选择器就没必要再进行嵌套
  • 通配符和属性选择器效率最低,避免使用

6. 减少使用昂贵的属性

在页面发生重绘的时候,昂贵属性如box-shadow/border-radius/filter/透明度/:nth-child等,会降低浏览器的渲染性能

7. 减少重排与重绘

减少重排

重排会导致浏览器重新计算整个文档,重新构建渲染树,这一过程会降低浏览器的渲染速度。如下所示,有很多操作会触发重排,我们应该避免频繁触发这些操作。

  • 改变font-size和font-family
  • 改变元素的内外边距
  • 通过JS改变CSS类
  • 通过JS获取DOM元素的位置相关属性(如width/height/left等)
  • CSS伪类激活
  • 滚动滚动条或者改变窗口大小

避免不必要的重绘

当元素的外观(如color,background,visibility等属性)发生改变时,会触发重绘。

在网站的使用过程中,重绘是无法避免的。不过,浏览器对此做了优化,它会将多次的重排、重绘操作合并为一次执行。

8. 不要使用@import

css样式文件有两种引入方式,一种是link元素,另一种是@import

@import会影响浏览器的并行下载,使得页面在加载时增加额外的延迟,增添了额外的往返耗时

而且多个@import可能会导致下载顺序紊乱

比如一个css文件index.css包含了以下内容:@import url("reset.css")

那么浏览器就必须先把index.css下载、解析和执行后,才下载、解析和执行第二个文件reset.css

参考

HTML系列之DOCTYPE的作用

DOCTYPE(document type)的简写,它是一种标记语言的文档类型声明,即告诉浏览器当前 HTML 是用什么版本编写的。

HTML 4.01 和 HTML5 中 DOCTYPE的区别

  • HTML 4.01 中的 doctype 需要对 DTD(文档类型定义Document Type Definition) 进行引用,因为 HTML 4.01 基于 SGML(标准通用标记语言 国际上定义电子文档和内容描述的标准)。
  • HTML 5 不基于 SGML,因此不需要对 DTD 进行引用,但是需要 doctype 来规范浏览器的行为(html 5简化了这种声明,意在告诉浏览器使用统一的标准即可)。

两种版本的声明方式

HTML 5:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
</html>

HTML 4

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

Vue系列之为什么不建议同时使用 v-if 和 v-for

本系列的主题是 Vue,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

为什么不建议把 v-if 和 v-for 用在同一个元素上

因为 Vue 处理指令时,v-forv-if 具有更高的优先级,所以导致每循环一次就会去v-if一次,而v-if是通过创建和销毁dom元素来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,造成页面卡顿,性能下降。

解决办法:

  • v-for的外层或内层包裹一个元素来使用v-if
  • computed处理

看个例子:

<div v-for="item in [1, 2, 3, 4, 5, 6, 7]" v-if="item !== 3">
    {{item}}
</div>

上面的写法是v-for和v-if同时存在,会先把7个元素都遍历出来,然后再一个个判断是否为3,并把3给隐藏掉,这样的坏处就是,渲染了无用的3节点,增加无用的dom操作,建议使用computed来解决这个问题:

<div v-for="item in list">
    {{item}}
</div>

computed() {
    list() {
        return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3)
    }
}

我们也可以把:

<ul>
  <li
    v-for="user in users"
    v-if="shouldShowUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

更新为:

<ul v-if="shouldShowUsers">
  <li
    v-for="user in users"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

HTML系列之 script 标签中 defer 和 async 的区别

在 HTML 中会遇到以下三类 script:

<script src='xxx'></script>
<script src='xxx' async></script>
<script src='xxx' defer></script>

那么这三类 script 有什么区别呢?

script

浏览器在解析 HTML 的时候,如果遇到一个没有任何属性的 script 标签,就会暂停解析,先发送网络请求获取该 JS 脚本的代码内容,然后让 JS 引擎执行该代码,当代码执行完毕后恢复解析。整个过程如下图所示:

在这里插入图片描述

可以看到,script 阻塞了浏览器对 HTML 的解析,如果获取 JS 脚本的网络请求迟迟得不到响应,或者 JS 脚本执行时间过长,都会导致白屏,用户看不到页面内容。这也是我们平时都把<script>标签放到底部的原因。

async script

async 表示异步,例如七牛的源码中就有大量的 async 出现:

在这里插入图片描述

当浏览器遇到带有 async 属性的 script 时,请求该脚本的网络请求是异步的,不会阻塞浏览器解析 HTML,一旦网络请求回来之后,如果此时 HTML 还没有解析完,浏览器会暂停解析,先让 JS 引擎执行代码,执行完毕后再进行解析,图示如下:

在这里插入图片描述

当然,如果在 JS 脚本请求回来之前,HTML 已经解析完毕了,那就啥事没有,立即执行 JS 代码,如下图所示:

在这里插入图片描述

所以 async 是不可控的,因为执行时间不确定,你如果在异步 JS 脚本中获取某个 DOM 元素,有可能获取到也有可能获取不到。而且如果存在多个 async 的时候,它们之间的执行顺序也不确定,完全依赖于网络传输结果,谁先到执行谁。这导致async属性下的脚本是乱序的,对于script有先后依赖关系的情况,并不适用。

defer script

defer 表示延迟,例如掘金的源码中就有大量的 defer 出现:

在这里插入图片描述

当浏览器遇到带有 defer 属性的 script 时,获取该脚本的网络请求也是异步的,不会阻塞浏览器解析 HTML,一旦网络请求回来之后,如果此时 HTML 还没有解析完,浏览器不会暂停解析并执行 JS 代码,而是等待 HTML 解析完毕再执行 JS 代码,图示如下:
在这里插入图片描述
如果存在多个 defer script 标签,浏览器(IE9及以下除外)会保证它们按照在 HTML 中出现的顺序执行,不会破坏 JS 脚本之间的依赖关系。

最后,根据上面的分析,不同类型 script 的执行顺序及其是否阻塞解析 HTML 总结如下:

在这里插入图片描述

原文:https://juejin.cn/post/6894629999215640583
作者:乔珂力

HTML系列之HTML、XHTML和XML之间的区别

  • HTML:HyperText Markup Language / 超文本标记语言
  • XML: Extensible Markup Language / 可扩展标记语言
  • XHTML: Extensible Hypertext Markup Language / 可扩展超文本标记语

XML 被设计用来传输和存储数据。

HTML 被设计用来显示数据。

HTML

HTML 是用来描述和定义网页内容的标记语言,是构成网页的最基本的东西。

所谓超文本,就是说它除了能标记文本,还能标记其他的内容,比如:图片,链接,音频,视频等。

XML

XML 可以自己“发明”标签————这也是“可扩展的”一个含义。

XML 没什么特别的。它仅仅是纯文本而已。有能力处理纯文本的软件都可以处理 XML。

XHTML

HTML 和 XML 一结合,就产生了 XHTML。是更严谨更纯净的 HTML 版本。

从继承关系上讲,HTML是一种基于标准通用标记语言(SGML)的应用,是一种非常灵活的置标语言,而XHTML则基于可扩展标记语言(XML),XML是SGML的一个子集。

CSS系列之手写三角形

宽高设为0,border设置宽度,三边透明transparent ,三角形底部方向设置颜色,就可以实现三角形了

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .triangle {
            width: 0;
            height: 0;
            border-style: solid;
            border-width: 50px;
            border-color: transparent transparent red transparent;
        }

        /* 简写版 */
        .triangle2 {
            width: 0;
            height: 0;
            border: 50px solid transparent;
            border-top-color: orange;
        }
    </style>
</head>

<body>
    <div class="triangle"></div>
    ————
    <div class="triangle2"></div>
</body>

</html>

效果图如下:

在这里插入图片描述

CSS系列之隐藏页面元素的方式

透明

  • opacity:0:本质上是将元素的透明度将为0,就看起来隐藏了,但是依然占据空间且可以交互
  • visibility:hidden: 与上一个方法类似的效果,占据空间,但是不可以交互了

溢出

  • overflow:hidden: 这个只隐藏元素溢出的部分,但是占据空间且不可交互

文档流

  • display:none: 这个是彻底隐藏了元素,元素从文档流中消失,既不占据空间也不交互,也不影响布局

层级

  • z-index:-9999: 原理是将层级放到底部,这样就被覆盖了,看起来隐藏了

欧几里德平面(二维平面)上的缩放

  • transform: scale(0,0): 平面变换,将元素缩放为0,但是依然占据空间,但不可交互

CSS 函数 scale() 用于修改元素的大小。可以通过向量形式定义的缩放值来放大或缩小元素,同时可以在不同的方向设置不同的缩放值。

在这里插入图片描述

scale() 仅适用于在欧几里德平面(二维平面)上的变换。如果需要进行空间中的缩放,必须使用 scale3D() 。

CSS系列之禁止用户选中

代码实现如下:

.unselectable {
    user-select: none;

    -moz-user-select: -moz-none;
    -khtml-user-select: none;
    -webkit-user-select: none;
    /*
      Introduced in IE 10.
      See http://ie.microsoft.com/testdrive/HTML5/msUserSelect/
    */
    -ms-user-select: none;
 }

Webpack系列之生产环境配置(提取css为单独文件,js语法检查eslint,html、css和js的压缩与兼容性处理)

本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

生产环境

生产环境的特点是能让代码优化上线运行的环境

我们要做哪些事情呢?

样式经过css-loader的处理被整合在js中,如果样式在js中的话,会让js体积非常大,下载就会慢;

同时因为是先加载js才能通过创建style标签插入到页面中,这里就会出现闪屏现象。

所以,我们就需要将css从js中提取出来;

当然还需要对代码进行压缩和兼容性处理,这里包括html、css和js。

提取css成单独文件

mini-css-extract-plugin这个插件会将 CSS 提取到单独的文件中

下载这个插件:

npm install --save-dev mini-css-extract-plugin

在配置文件webpack.config.js中引用:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

和开发环境中不同的一点,这里我们会用MiniCssExtractPlugin.loader这个loader取代style-loader

MiniCssExtractPlugin.loader作用:提取js中的css成单独文件。

因为这个loade提取js中的css成单独文件,所以不需要style-loader再创建style标签,放入样式了。

具体的webpack.config.js配置如下:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 创建style标签,将样式放入
          // 'style-loader', 
          // 这个loader取代style-loader。作用:提取js中的css成单独文件
          MiniCssExtractPlugin.loader,
          // 将css文件整合到js文件中
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin()
  ],
  mode: 'development'
};

实例项目目录和文件内容:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

使用当前配置打包运行一下,会发现打包后的build下面多了一个main.css文件

在这里插入图片描述

这个main.css会把我们之前的样式全都加载进来:

在这里插入图片描述

再看index.html,它会自动引入打包生产的资源

在这里插入图片描述

打开build下的index.html,样式正常

在这里插入图片描述

如果我们希望打包后的资源和之前的项目目录结构是一样的,可以在插件使用的地方配置filename属性,重命名即可。

配置如下:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 创建style标签,将样式放入
          // 'style-loader', 
          // 这个loader取代style-loader。作用:提取js中的css成单独文件
          MiniCssExtractPlugin.loader,
          // 将css文件整合到js文件中
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      // 对输出的css文件进行重命名
      filename: 'css/built.css'
    })
  ],
  mode: 'development'
};

删掉前面打包的build文件,重新打包运行,可以看到build目录下面多了css目录,以及目录下面被我们重命名的built.css

在这里插入图片描述

因为这样方式加载的样式是通过link标签引入,而不是style标签,所以不会产生闪屏现象;并且css文件和js文件分割开了,js文件体积也没那么大,解析速度也会更好一些。

在这里插入图片描述

css兼容性处理

css兼容性处理需要用到postcss这个库,postcss在webpack中使用需要用到一个postcss-loader和一个插件postcss-preset-env

就是这个东东

在这里插入图片描述

在这里插入图片描述

下载postcss-loaderpostcss-preset-env:

npm install --save-dev postcss-loader postcss-preset-env

有两种使用方式,第一种是使用postcss-loader的默认配置(就是直接把loader名以字符串的形式加到处理css loader的数组里):

  {
    test: /\.css$/,
    use: [
      MiniCssExtractPlugin.loader,
      'css-loader',
      // 使用loader的默认配置
      'postcss-loader'
    ]
  }

但是现在不能使用默认配置,我们需要修改loader的配置,那就得写成对象的形式,在options属性里修改loader的配置

第二种方式:

   {
     test: /\.css$/,
     use: [
       MiniCssExtractPlugin.loader,
       'css-loader',
       // 使用loader的默认配置
       // 'postcss-loader',
       // 修改loader的配置
       {
         loader: 'postcss-loader',
         options: {
           ident: 'postcss',// 固定写法
           plugins: () => [
             // 使用我们前面下载的postcss插件
             require('postcss-preset-env')()
           ]
         }
       }
     ]
   }

前面的配置里我们在postcss-loader里配置了要使用的插件postcss-preset-env,这个插件的作用是帮postcss找到package.jsonbrowserslist里面的配置,通过配置加载指定的css兼容性样式。

所以我们现在还需要做一步,就是写这个browserslist

package.json里加一个browserslist属性,值为对象,对象里面可以写两个参数,development(代表开发环境配置) 和 production(代表生产环境配置)

具体写法:

  "browserslist": {
	// 开发环境
    "development": [
      "last 1 chrome version",//兼容最近的chrome浏览器版本
      "last 1 firefox version",
      "last 1 safari version"
    ],
    // 生产环境
    "production": [
      ">0.2%",// 大于99.8%的浏览器,基本上全部的浏览器
      "not dead",// 不要已经死的浏览器,比如ie10
      "not op_mini all"// 不要op_mini所有的,因为op_mini早都死完了,所以不要它,而且在国内基本没有人使用这种东西,所以全都不要
    ]
  },

开发环境不需要做太多,兼容一些主要浏览器的调试版本就足够了;但是生产环境就要多写一点,具体看上面生产环境配置

更多的配置大家可以去github搜关键字browserslist,上面有一个仓库,里面详细介绍了我们可以在browserslist写哪些参数,以及参数详细的配置。

现在验证一下前面的配置靠不靠谱,改动一下样式,添加一些有兼容性问题的样式

  display: flex;
  backface-visibility: hidden;

在这里插入图片描述

postcss-preset-env这个插件默认情况下就是找生产环境配置,也就是说默认使用browserslist里的production配置,跟webpack.config.jswebpack配置里的模式 mode: 'development'是没有关系的。

我们做实例演示,想要变成开发环境,需要设置node环境变量:

// 设置nodejs环境变量
process.env.NODE_ENV = 'development';

css兼容性处理最终配置:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 设置nodejs环境变量
process.env.NODE_ENV = 'development';

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          /*
            css兼容性处理:postcss --> postcss-loader postcss-preset-env

            帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式

            "browserslist": {
              // 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
              "development": [
                "last 1 chrome version",
                "last 1 firefox version",
                "last 1 safari version"
              ],
              // 生产环境:默认是看生产环境
              "production": [
                ">0.2%",
                "not dead",
                "not op_mini all"
              ]
            }
          */
          // 使用loader的默认配置
          // 'postcss-loader',
          // 修改loader的配置
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: () => [
                // postcss的插件
                require('postcss-preset-env')()
              ]
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    })
  ],
  mode: 'development'
};

上面的配置修改了环境变量,postcss-preset-env插件会以开发环境的配置做兼容性处理,开发环境只做了主要浏览器最近的版本,我们打包运行实例验证一下,打开build里面的样式代码

在这里插入图片描述

我们发现,它对backface-visibility做了兼容性处理,flex没有做处理

接着我们测试生产环境的兼容性配置,注释掉修改环境变量,没有设置环境变量,默认就是找生产环境:

// 设置nodejs环境变量
// process.env.NODE_ENV = 'development';

重新打包运行

在这里插入图片描述

这时候我们发现,生产环境还多了一个dlisplay:-webkit-box;,这个就是做了一个兼容性处理

通过postcss的处理,我们在写样式的时候就不用太过操心兼容性问题,可以把它都交给工具,让工具自动去完成,这样就大大解放了开发人员的一些复杂的操作,只需要考虑最简单的东西,通过工具帮我们自动做兼容性处理。

压缩css

压缩css需要用到optimize-css-assets-webpack-plugin这个插件

下载插件:

npm install --save-dev optimize-css-assets-webpack-plugin

这个插件的使用非常简单,引入后直接在plugins中new调用即可

引入插件:

const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

plugins中new调用:

// 压缩css
new OptimizeCssAssetsWebpackPlugin()

这个插件只要调用就可以压缩css,内部的默认配置已经足够将css进行压缩了,所以我们不需要再修改它的配置。

具体配置如下:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

// 设置nodejs环境变量
// process.env.NODE_ENV = 'development';

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: () => [
                // postcss的插件
                require('postcss-preset-env')()
              ]
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    // 压缩css
    new OptimizeCssAssetsWebpackPlugin()
  ],
  mode: 'development'
};

打包运行,找到build下的css

在这里插入图片描述

我们发现css整体已经被压缩成一行了

压缩后的文件体积为204kb

在这里插入图片描述

这里为了对比,我们注释掉插件的调用,关闭css压缩

// 压缩css
// new OptimizeCssAssetsWebpackPlugin()

打包运行查看一下结果

没有压缩构建出的css代码:

在这里插入图片描述

没有压缩构建出的css代码体积:

在这里插入图片描述

没有压缩的的css体积大小为273kb,前面压缩后的是204kb,这个压缩的还是比较少的,因为样式不多,总共才14行代码,所以压缩的效果不是特别明显,实际开发中的样式会非常多,那时css压缩后的体积就会非常小,效果很明显。css文件越小,请求速度就会越快,加载速度就会越快,用户看到的效果也会更快一些,用户体验就会更好。所以我们在上线之前一定要给代码压缩

js语法检查eslint

语法检查最常用的工具就是eslint,在webpack中对应的要使用eslint-loader,这个loader依赖eslint这个库,也需要下载

语法检查js文件:

test: /\.js$/,

注意:只检查自己写的源代码,第三方的库是不用检查的,排除node_modules

exclude: /node_modules/,
 /*
   语法检查: eslint-loader  eslint
     注意:只检查自己写的源代码,第三方的库是不用检查的
 */
 {
   test: /\.js$/,
   exclude: /node_modules/,
   loader: 'eslint-loader',
   options: {}
 }

设置检查规则,在package.jsoneslintConfig中设置

语法检查规则推荐使用airbnb规则,为什么呢?

我们打开github,在探索中找到JavaScript这个主题,下面就是js最受欢迎的仓库了

在这里插入图片描述

这里可以看到排名第二的是vuejs

在这里插入图片描述

第三个是facebook的react

在这里插入图片描述

第四个是bootstrap

在这里插入图片描述

第五个是一本书:你不知道的js

在这里插入图片描述

第六个就是我们推荐的airbnb这个风格指南

在这里插入图片描述

airbnb这个风格指南详细的介绍了我们应该怎么写js代码,不应该怎么写js代码,是一个很优秀的库。

我们现在要把这个库应用在eslint上,搜索npm,搜eslint,可以看到一个插件: eslint-config-airbnb,这个就是让airbnb风格指南在eslint中生效

在这里插入图片描述

airbnb有 eslint-config-airbnbeslint-config-airbnb-base两个插件,其中 eslint-config-airbnb会包含react的风格建议,我们现在不写react代码,所以不用这个插件,用eslint-config-airbnb-base

在这里插入图片描述

eslint-config-airbnb-base插件又有两个版本,eslint-config-airbnb-base(支持ECMAScript 6+)和eslint-config-airbnb-base/legacy(仅支持ES5及以下),看到legacy(遗产,传统)这个词了吧,我们当然要使用支持ECMAScript 6+的eslint-config-airbnb-base版本啦,从下面的图片能看到我们还需要下载eslinteslint-plugin-import这两个库

在这里插入图片描述

在这里插入图片描述

下载需要的库和插件:

npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import

package.json里的eslintConfig中设置检查规则:

找到package.json,在里面加一个字段叫eslintConfig,值为一个对象,通过extends来继承airbnb-base就OK了。

  "eslintConfig": {
    "extends": "airbnb-base",
    "env": {
      "browser": true
    }
  },

在这里插入图片描述

如果要指定配置文件中的环境,请使用env键,并通过设置每个环境来指定要启用的环境true。例如,以下启用浏览器和Node.js环境:

{
    "env": {
        "browser": true,
        "node": true
    }
}

或者在一个package.json文件中

{
    "eslintConfig": {
        "env": {
            "browser": true,
            "node": true
        }
    }
}

配置eslint语法检查后,运行项目会发现后很多代码不规范的,我们一个个去修复会很麻烦,所以我们在配置的时候可以再加一个选项:fix: true,自动修复eslint的错误,不用我们手动去改

 {
   test: /\.js$/,
   exclude: /node_modules/,
   loader: 'eslint-loader',
   options: {
     // 自动修复eslint的错误
     fix: true
   }
 }

如果我们在代码里使用console,eslint也会发出警告:不建议使用console

在这里插入图片描述

但是我们是为了调试,这里就可以忽略这个规则,有一个选项叫// eslint-disable-next-line,就是下一行eslint所有规则都失效(下一行不进行eslint检查)

在这里插入图片描述

写法就是在需要忽略的代码上面写一行// eslint-disable-next-line,跟注释的写法一样

这样重新打包运行就不会再有警告了。

最终配置:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      /*
        语法检查: eslint-loader  eslint
          注意:只检查自己写的源代码,第三方的库是不用检查的
          设置检查规则:
            package.json中eslintConfig中设置~
              "eslintConfig": {
                "extends": "airbnb-base"
              }
            airbnb --> eslint-config-airbnb-base  eslint-plugin-import eslint
      */
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
        options: {
          // 自动修复eslint的错误
          fix: true
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

package.json

  "eslintConfig": {
    "extends": "airbnb-base",
    "env": {
      "browser": true
    }
  },

js兼容性处理bable、core-js

先做个测试,写一些es6的语法

const add = (x, y) => {
  return x + y;
};
console.log(add(2, 5));

在这里插入图片描述
使用webpack的内置配置看一下效果

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: []
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

打包运行后,看built.js

在这里插入图片描述

可以发现webpack的内置配置是没有做兼容性处理的,正常我们打开chrome浏览器是没有问题的,输出结果是正常的

在这里插入图片描述

但是要注意,一旦我们打开ie浏览器就会报语法错误,因为它根本不认识这些语法

在这里插入图片描述

所以我们就必然需要做兼容性处理

做兼容性处理需要用到bable,在webpack中对应使用bable-loader,使用时我们需要做具体的配置,有一个参数叫presets预设:指示babel做怎么样的兼容性处理,默认情况下我们会传入'@babel/preset-env',这样相当于做一个预设环境的兼容性处理,一个非常基本的兼容性处理

{
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    options: {
        // 预设:指示babel做怎么样的兼容性处理
        presets: ['@babel/preset-env']
    }
}

下载需要的插件:

npm install --save-dev babel-loader @babel/core @babel/preset-env

以上面的兼容性配置打包运行,打开built.js

在这里插入图片描述

我们发现此时的es6语法已经做了兼容性处理

刷新查看ie浏览器效果

在这里插入图片描述

结果打印出来了,正常显示。此时我们已经有了基本的兼容性处理。

为什么说只是有了基本的兼容性处理呢?是因为在开发过程中的兼容性问题不止上面这些,比如说我们写一个Promise,上面的配置能够处理吗?

在这里插入图片描述

以前面的配置打包运行,查看built.js

在这里插入图片描述

看到这个Promise还是原封不动的promise,只有有箭头函数做了兼容性处理(基础的兼容性配置做的处理)

刷新查看ie浏览器效果

在这里插入图片描述

ie浏览器报错:promise未定义

总结这种方法:

基本的js兼容性处理 --> @babel/preset-env,存在的问题:只能转换基本语法,如promise高级语法不能转换

所以我们就要解决这个兼容性问题

第二种解决方法:做全部js兼容性处理 --> @babel/polyfill

下载@babel/polyfill这个库

npm i @babel/polyfill -D

这个不是bable插件,下载之后只需引入即可

比如直接在js代码里用import引入

import '@babel/polyfill';

在这里插入图片描述

引入后我们再来构建一次

刷新ie浏览器,查看效果,可以发现ie浏览器正常运行打印了

在这里插入图片描述

下面我们看这第二种做全部兼容性处理的方法有什么问题:

之前构建的代码体积为4kb

在这里插入图片描述

现在我们通过import '@babel/polyfill',引入这个库后打包的代码体积为441kb

在这里插入图片描述

查看built.js可以发现,里面包含了各式各样的兼容性处理,总结一下就是,@babel/polyfill这个库会把js全部的兼容性处理纳进来,不管你做什么兼容性,它一次性全部搞定

相当于我饿了,只需要吃一顿饭,它却把一年内的粮食都运进来

@babel/polyfill是怎么做的呢?其实它就是把那些浏览器不识别的方法,全部定义好,直接挂载在对应的顶层对象上,这样不管它识不识别你都可以直接用了。所以这种是一种相对比较暴力的解决方式。

总结第二种方法:

全部js兼容性处理 --> @babel/polyfill ,存在的问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~

第三种解决方法:需要做兼容性处理的就做:按需加载 --> core-js

下载core-js:

npm i core-js -D

配置core-js

在这里插入图片描述

如上图,我们稍微改动一下presets参数的写法,给'@babel/polyfill'外再套个数组,在下面写相应的配置:

{
   test: /\.js$/,
   exclude: /node_modules/,
   loader: 'babel-loader',
   options: {
     // 预设:指示babel做怎么样的兼容性处理
     presets: [
       [
         '@babel/preset-env',
         {
           // 按需加载
           useBuiltIns: 'usage',
           // 指定core-js版本
           corejs: {
             version: 3
           },
           // 指定兼容性做到哪个版本浏览器
           targets: {
             chrome: '60',
             firefox: '60',
             ie: '9',
             safari: '10',
             edge: '17'
           }
         }
       ]
     ]
   }
}

上面就是我们第三种方案的配置,要注意,使用第三种方案就不能再使用第二种方案,所以要把前面引入的import '@babel/polyfill';注释掉:

// import '@babel/polyfill';

再次打包运行,我们发现代码的体积更小了,只有104kb

在这里插入图片描述

总结一下

js兼容性处理:babel-loader @babel/core

  1. 基本js兼容性处理 --> @babel/preset-env
    问题:只能转换基本语法,如promise高级语法不能转换
  2. 全部js兼容性处理 --> @babel/polyfill
    问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
  3. 需要做兼容性处理的就做:按需加载 --> core-js

js兼容性处理的最终配置(按需加载core-js):

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      /*
        js兼容性处理:babel-loader @babel/core 
          1. 基本js兼容性处理 --> @babel/preset-env
            问题:只能转换基本语法,如promise高级语法不能转换
          2. 全部js兼容性处理 --> @babel/polyfill  
            问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
          3. 需要做兼容性处理的就做:按需加载  --> core-js
      */  
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          // 预设:指示babel做怎么样的兼容性处理
          presets: [
            [
              '@babel/preset-env',
              {
                // 按需加载
                useBuiltIns: 'usage',
                // 指定core-js版本
                corejs: {
                  version: 3
                },
                // 指定兼容性做到哪个版本浏览器
                targets: {
                  chrome: '60',
                  firefox: '60',
                  ie: '9',
                  safari: '10',
                  edge: '17'
                }
              }
            ]
          ]
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

js压缩

js压缩非常简单,只需要把mode改为production,即生产环境下webpack会自动压缩代码。

生产环境下webpack会加载一些不一样的配置,加载了很多插件,其中UglifyJsPlugin这个插件就会去压缩js代码,所以我们就不需要自己去写插件了,webpack内部已经实现好了

在这里插入图片描述

我们用一个最基础的webpack配置,修改mode改为production,测试一下效果

  mode: 'production'
};

实例项目的js代码:

const add = (x, y) => {
  return x + y;
};
console.log(add(2, 5));

const promise = new Promise(resolve => {
  setTimeout(() => {
    console.log('定时器执行完了~');
    resolve();
  }, 1000);
});

console.log(promise);

打包运行后的built.js代码:

在这里插入图片描述

可以看到代码已经压缩成一行了,js压缩搞定

压缩html

前面我们给css和js都做了兼容性处理,html要不要做兼容性处理呢?答案是不需要,html是没办法做兼容性处理的,它的标签认识就是认识,不认识就是不认识,所以html是不需要做兼容性处理的。我们只需要对html代码进行压缩。

通过HtmlWebpackPlugin对html代码进行压缩,HtmlWebpackPlugin插件配置中有一个选项叫minify,使用这个选项做html压缩。

配置如下:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // 压缩html代码
      minify: {
        // 移除空格
        collapseWhitespace: true,
        // 移除注释
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

生产环境配置总结——一个完整的开发环境配置

1.代码复用

前面css文件做的配置,对于less文件我们也需要做:

{
    test: /\.less$/,
    use: [
        MiniCssExtractPlugin.loader,
        'css-loader',
        {
            // 还需要在package.json中定义browserslist
            loader: 'postcss-loader',
            options: {
                ident: 'postcss',
                plugins: () => [require('postcss-preset-env')()]
            }
        },
        'less-loader'
    ]
},

可以发现对less文件使用的后三个loader,css文件也使用了,所以这里我们就可以复用一下

提取公用的loader:

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

用扩展运算符展开:

  {
    test: /\.css$/,
    use: [...commonCssLoader]
  },
  {
    test: /\.less$/,
    use: [...commonCssLoader, 'less-loader']
  },

这样就不用去写太多重复代码,代码更加精简一些。

2.指定loader执行的先后顺序

正常来讲,一个文件只能被一个loader处理。当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序。

对于js文件loader执行的先后顺序:先执行eslint 在执行babel

通过对loader设置enforce属性指定该loader优先执行:

// 优先执行
enforce: 'pre',
  /*
    正常来讲,一个文件只能被一个loader处理。
    当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
      先执行eslint 在执行babel
  */
  {
    // 在package.json中eslintConfig --> airbnb
    test: /\.js$/,
    exclude: /node_modules/,
    // 优先执行
    enforce: 'pre',
    loader: 'eslint-loader',
    options: {
      fix: true
    }
  },
  {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    options: {
      presets: [
        [
          '@babel/preset-env',
          {
            useBuiltIns: 'usage',
            corejs: {version: 3},
            targets: {
              chrome: '60',
              firefox: '50'
            }
          }
        ]
      ]
    }
  },

3.一个完整的生产环境配置

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [...commonCssLoader]
      },
      {
        test: /\.less$/,
        use: [...commonCssLoader, 'less-loader']
      },
      /*
        正常来讲,一个文件只能被一个loader处理。
        当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
          先执行eslint 在执行babel
      */
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                useBuiltIns: 'usage',
                corejs: {version: 3},
                targets: {
                  chrome: '60',
                  firefox: '50'
                }
              }
            ]
          ]
        }
      },
      {
        test: /\.(jpg|png|gif)/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          outputPath: 'imgs',
          esModule: false
        }
      },
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(js|css|less|html|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

CSS系列之如何让一个元素水平垂直居中(方案总结+代码实例)

1. flex

使用弹性盒子布局,只需要给待处理的块状元素的父元素添加属性:

 /* 弹性盒子布局 */
 display: flex;
 /* 水平居中 */
 justify-content: center; 
 /* 垂直居中 */
 align-items: center;

该方案在实际开发中应用颇多,也比较简单直观,不需要确定子盒子宽高就可以实现。

实例代码,复制即可查看效果:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box {
            height: 300px;
            width: 300px;
            background-color: orangered;

            /* 弹性盒子布局 */
            display: flex;
            /* 水平居中 */
            justify-content: center; 
            /* 垂直居中 */
            align-items: center;
        }

        .test-div {
            height: 100px;
            width: 100px;
            background-color: blue;
        }
    </style>
</head>

<body>
    <section class="box">
        <div class="test-div"></div>
    </section>
</body>

</html>

效果:
在这里插入图片描述

2. position (需要确定子元素宽高)

  • 元素设置为相对定位position: relative元素设置为绝对定位position: absolute
  • 元素设置top和left为50%top: 50%; left: 50%;
  • 元素设置margin-leftmargin-top自身宽高一半的负值margin-left: -50px; margin-top: -50px; (自身宽高为height: 100px; width: 100px;

实例代码,复制即可查看效果:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box {
            height: 300px;
            width: 300px;
            background-color: rgb(201, 211, 58);

            /* 父盒子 相对定位 */
            position: relative;
        }

        .test-div {
            height: 100px;
            width: 100px;
            background-color: rgb(211, 18, 98);

            /* 子盒子 绝对定位 */
            position: absolute;
            top: 50%;
            left: 50%;
            margin-left: -50px;
            margin-top: -50px;
        }
    </style>
</head>

<body>
    <section class="box">
        <div class="test-div"></div>
    </section>
</body>

</html>

效果:
在这里插入图片描述

3. position + transform 元素平移

只需将上面例子中的:元素设置margin-leftmargin-top自身宽高一半的负值
替换为transform: translate(-50%,-50%);

即:

  • 元素设置为相对定位position: relative元素设置为绝对定位position: absolute
  • 元素设置top和left为50%top: 50%; left: 50%;
  • 元素设置transform为 translate(-50%,-50%)transform: translate(-50%,-50%);

这个方法比较特殊,虽说只改了一行代码,但是原理是什么?只是背一行代码就会显得十分草率。

所以再放实例代码之前,我们先来看看这个方法如何实现了水平垂直居中。

transform: translate(x,y);的语法定义了元素的2D转换,也就是元素移动(平移),讲完这个我们再来看,子元素设置绝对定位,top和left各移动50%后,子元素左上角正好在父盒子的中心,此时还没有实现子元素的水平垂直居中(即子元素的中心点在父盒子的中心,而不是左上角在父盒子中心)。接着往下看:

translate(x,y) 括号里填百分比数据的话,会以本身的长宽做参考,注意,不是以父盒子宽高为参考,这就是该方法最需要理解的一个点。比如,本身的宽为100px,高为100px. 那填translate(50%,50%)就是向右和向下各移动50px,添加负号就是向着相反的方向移动。移动后的效果就是子元素的中心点在父元素的中心,实现了水平垂直居中。

实例代码,复制即可查看效果:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box {
            height: 300px;
            width: 300px;
            background-color: rgb(231, 69, 20);

            /* 父盒子 相对定位 */
            position: relative;
        }

        .test-div {
            height: 100px;
            width: 100px;
            background-color: rgb(67, 33, 218);

            /* 子盒子 绝对定位 */
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
    </style>
</head>

<body>
    <section class="box">
        <div class="test-div"></div>
    </section>
</body>

</html>

效果:
在这里插入图片描述
http://www.imooc.com/qadetail/129282

4. position + margin:auto

  • 元素设置为相对定位position: relative元素设置为绝对定位position: absolute
  • 元素设置top、bottom、left和right为0top: 0; bottom: 0; left: 0; right: 0;
  • 元素设置margin为automargin: auto;

实例代码,复制即可查看效果:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box {
            height: 300px;
            width: 300px;
            background-color: rgb(74, 199, 43);

            /* 父盒子 相对定位 */
            position: relative;
        }

        .test-div {
            height: 100px;
            width: 100px;
            background-color: rgb(20, 38, 117);

            /* 子盒子 绝对定位 */
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            margin: auto;
        }
    </style>
</head>

<body>
    <section class="box">
        <div class="test-div"></div>
    </section>
</body>

</html>

效果:
在这里插入图片描述

参考

CSS系列之盒模型的理解,标准盒模型和怪异盒模型有什么区别

什么是盒模型?

盒模型由content(内容)、padding(内边距)、border(边框)、margin(外边距)组成。

image

标准盒模型和怪异盒模型有什么区别?

我们目前所学习的知识中,以标准盒子模型为准。

标准盒子模型:

image

IE盒子模型:

IE盒子模型(怪异盒子模型)

image

区别:

标准盒子模型中,width 和 height 指的是内容区域的宽度和高度。增加内边距、边框和外边距不会影响内容区域的尺寸,但是会增加元素框的总尺寸。

标准盒模型声明的元素宽度width = content(宽度)

IE盒子模型中,width 和 height 指的是内容区域+border+padding的宽度和高度。元素内容的宽度和高度是由已设定的宽度和高度分别减去边框和内边距后得到的。

IE盒模型声明的元素宽度width = content(宽度) + padding(左右宽度) + border(左右宽度)

两者的声明使用:

现代浏览器默认使用W3C的标准盒模型,但是在不少情况下怪异盒模型更好用,于是W3C在css3中加入box-sizing

box-sizing: content-box // 标准盒模型

box-sizing: border-box // 怪异盒模型

一句话总结就是:

标准盒模型的内容大小就是content的大小,而ie盒模型的大小则是content+padding+border总的大小。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.