yuanyuanbyte / blog Goto Github PK
View Code? Open in Web Editor NEW圆圆的博客,预计写七个系列:JavaScript深入系列、JavaScript专题系列、网络系列、Webpack系列、Vue系列、JavaScript基础系列、HTML&CSS应知应会系列。
圆圆的博客,预计写七个系列:JavaScript深入系列、JavaScript专题系列、网络系列、Webpack系列、Vue系列、JavaScript基础系列、HTML&CSS应知应会系列。
本系列的主题是 Vue,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
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>
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
为了解决px的问题,提出了em。它是相对于当前对象内文本的字体尺寸,若没有设置行内文本的字体尺寸,则使用浏览器默认字体尺寸,一般为16px。
但em使用起来依旧有诸多不便,复杂的嵌套逻辑使得更改了部分元素会产生字体大小变化的连锁反应。
rem是CSS3新增加的相对单位,即root em
,是相较于根元素(html)。rem的根据根节点使得计算更清晰。
em 和 rem 单位之间的区别是浏览器根据谁来转化成px值,理解这种差异是决定何时使用哪个单元的关键。
我们要通过复习 rem 和 em 单位如何工作,来理解em和rem之间的区别。
当使用em单位时,像素值将是em值乘以使用em单位的元素的字体大小,他们转换为像素大小取决于应用在当前元素的字体尺寸。
例如,如果一个 div 有 18px 字体大小,10em 将等同于 180px,即 10 × 18 = 180。
CSS padding设置为 10em
计算结果为 180px (或接近它)
当使用 rem 单位,他们转化为像素大小取决于页根元素的字体大小,即 html 元素的字体大小。 根元素字体大小乘以你 rem 值。
例如,根元素的字体大小 16px,10rem 将等同于 160px,即 10 x 16 = 160。
CSS padding设置为 10rem
计算结果为160px
有一个比较普遍的误解,认为 em 单位是相对于父元素的字体大小。 事实上,根据W3标准 ,它们是相对于使用em单位的元素的字体大小。
父元素的字体大小可以影响 em 值,但这种情况的发生,纯粹是因为继承。 让我们看看为什么以及如何起作用。
使用 em 单位存在继承的时候,情况会变得比较棘手,因为每个元素将自动继承其父元素的字体大小。 继承效果只能被明确的字体单位覆盖,比如px,vw
使用 em 单位的元素字体大小根据它们来定。 但该元素可能继承其父元素的字体大小,而父元素又继承其父元素的字体大小,等等。 因此,以 em 为单位的元素字体大小可能会受到其任何父元素的字体大小影响。
rem这个单位与em有什么区别呢?区别在于使用rem为元素设定字体大小时,仍然是相对大小,但相对的只是HTML根元素。这个单位可谓集相对大小和绝对大小的优点于一身,通过它既可以做到只修改根元素就成比例地调整所有字体大小,又可以避免字体大小逐层复合的连锁反应。
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是Hypertext Reference的简写,表示超文本引用,代表了指向网络资源的所在位置,它与页面直接的关系为链接的关系。
<link href="reset.css" rel=”stylesheet“/>
<a href="https://www.php.cn/">
很多面试官会问:
谈谈你对 HTML5语义化标签的理解。
那么本篇博客可以对你理解HTML5语义化标签有所帮助。
语义元素清楚地向浏览器和开发者描述其意义。
非语义元素的例子:<div>
和 <span>
- 无法提供关于其内容的信息。
语义元素的例子:<form>
、<table>
以及 <img>
清晰地定义其内容。
HTML5提供了新的语义元素来定义网页的不同部分,它们被称为“切片元素”,如图所示
<main>
定义文档的主体部分<nav>
定义导航链接HTML 标签列表(功能排序) | 菜鸟教程 https://www.runoob.com/tags/ref-byfunc.html
HTML5
是HTML
的新标准,其主要目标是无需任何额外的插件如Flash
、Silverlight
等,就可以传输所有内容。它囊括了动画、视频、丰富的图形用户界面等。
从文档声明类型上看:
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 解析完毕后,需要将解析的结果与 DOM Tree 的内容一起进行分析建立一棵 Render Tree,最终用来进行绘图。
在建立 Render Tree 时(WebKit 中的「Attachment」过程),浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果(Style Rules)来确定生成怎样的 Render Tree。
媒体查询可以针对不同的屏幕尺寸设置不同的样式,特别是如果你需要设置设计响应式的页面。
最简单的媒体查询语法看起来是像这样的:
@media media-type and (media-feature-rule) {
/* CSS rules go here */
}
它由以下部分组成:
一个例子:
@media screen and (width: 600px) {
body {
color: red;
}
}
本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
webpack性能优化主要指两个环境:
除了前面的开发环境和生产环境配置,还有以下性能优化处理:
开发环境性能优化包括:
生产环境性能优化包括:
为什么要使用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功能。
比如修改入口文件下面的打印代码:
保存,可以发现所有东西都要重新打包构建:
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
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' //开发环境
};
打包 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-loader
和css-loader
less文件要用less-loader
,style-loader
和css-loader
具体这三个loder的作用见上面代码注释。
loader执行顺序(非常重要,切记!!!):
use数组中loader执行顺序:从右到左,从下到上 依次执行
其实所谓的从下到上就是把数组里的每个元素换行展示了,本质还是数组从右至左。
打包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-loader
和file-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.js,url-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]'
对图片资源最终的配置如下:
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
、.woff
、ttf
和.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'
};
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的目录下,要修改output
的filename
属性,在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
}
};
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
圣杯布局如图:
而且要做到左右宽度固定,中间宽度自适应。
<!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>
<!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,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
css有五种定位方式,分别是:static
, relative
,absolute
,fixed
和 sticky
。
HTML 元素的默认值,指定元素使用正常的布局行为。
静态定位的元素 top
, right
, bottom
, left
和 z-index
属性无效。
相对定位是相对于默认位置(即static时的位置)进行偏移。
相对定位元素经常被用来作为绝对定位元素的容器块。
绝对定位元素会被移出正常文档流,并不为元素预留空间。
相对于上级元素(一般是父元素)进行偏移。
通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置,如果元素没有已定位的父元素,那么它的位置相对于<html>
。
固定定位元素会被移出正常文档流,并不为元素预留空间。
相对于视口(viewport,浏览器窗口)进行偏移。
元素的位置相对于浏览器窗口是固定位置。即使窗口是滚动的它也不会移动,打印时,元素会出现在的每页的固定位置。
粘性定位跟前面四个属性值都不一样,它会产生动态效果,很像relative
和fixed
的结合。
粘性定位的元素是依赖于用户的滚动,在 position:relative
与 position:fixed
定位之间切换。
当元素距离页面视口(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>
效果:
同一容器中多个粘贴定位元素独立偏移,因此可能重叠;位置上下靠在一起的不同容器中的粘贴定位元素则会鸠占鹊巢,挤开原来的元素,形成依次占位的效果(这个下面会具体讲到)。
第一个特性源自粘性定位的定义,这么设计主要是为了避免当多个滚动互相嵌套的时候,粘性定位混乱。
第二个特性原因就是本文提到的粘性定位的计算规则,具体解释:
当我们的粘性定位元素都在一个容器的时候,大家都公用一个巨大的粘性约束矩形,因此,滚动的时候会一个一个往上重叠。
当我们的粘性定位元素属于不同容器的时候,就会有多个不同的粘性约束矩形,这些粘性约束矩形正好一个一个排列得很整齐,于是视觉上达成一个巧合一般的约定,即上一个粘性定位元素被滚走,下一个粘性定位元素正好开始有粘性效果。
先看看这两种方式都是如何使用的:
<!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
是html的标签,不仅可以加载 CSS 文件,还可以定义 RSS、rel(一些)连接属性等;而@import
是css的语法,只有导入样式表的作用。link
会和html同时被加载;而@import
引入的 CSS 将在页面加载完毕后被加载。link
是 HTML 标签,所以不存在兼容性问题;而@import
是 CSS2.1 才有的语法,所以只能在 IE5以上 才能识别。link
标签引入的样式,而@import
的样式不是dom可以控制的。link
方式的样式权重高于@import
的权重。@import
必须写在样式顶部,在html中 <style>@import url(引入的css路径);</style>
这个@import
必须要写在style的第一行。
本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
缓存我们会从两个点出发:一个是对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'
};
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
通过thread-loader
开启多进程打包
npm install --save-dev thread-loader
使用时,需将此 loader 放置在其他 loader 之前。放置在此 loader 之后的 loader 会在一个独立的 worker 池中运行。
注意:
请仅在耗时的操作中使用此 loader!因为每个 worker 都是一个独立的 node.js 进程,其开销大约为 600ms 左右。
我们一般给bable
用thread-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
多进程打包,用的好的时候可以极大的提升我们的打包速度,用的差的时候,就会导致打包速度更慢
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
* {}
body,div,p,ul,li {}
#name{}
.center {}
.first span{}
.food>li{}
<p style=""></p>
!important
!important
。它会覆盖页面内任何位置定义的元素样式。style="color:red;"
,权值为1000.(该方法会造成css难以管理,所以不推荐使用)#heade
r,权值为0100..bar
, 权值为0010.div
::first-line
权值为0001.*
,>
,+
, 权值为0000.内联样式 > ID选择器 > 类选择器 = 属性选择器 = 伪类选择器
> 元素选择器 = 关系选择器 = 伪元素选择器
> 通配符选择器
可继承的属性:font-size, font-family, color
不可继承的样式:border, padding, margin, width, height
本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
渐进式网络应用程序(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.js
。service-worker.js
是 Service Worker
文件,precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.js
是 service-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导流的意义下老板并没有意思让你花时间在这种事情上面
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
我们在写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'
};
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
display:none
不显示对应的元素,在文档布局中不再分配空间(回流+重绘)visibility:hidden
隐藏对应元素,在文档布局中仍保留原来的空间(重绘)本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。
在 webpack 看来, 前端的所有资源文件(js/json/css/img/less/...)都会作为模块处理。
它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。
入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
模式(Mode)指示 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]
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
本系列的主题是 Vue,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
JavaScript中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。
当 data 的值是一个对象时,它会在这个组件的所有实例之间共享。
而在Vue中,我们更多的是希望复用组件,这时就会产生问题。因为每个组件的实例都引用了相同的数据对象,更改其中一个实例的数据,就会改变其它实例中的数据。那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。
我们希望每个组件实例都管理其自己的数据。为了做到这一点,每个实例必须生成一个独立的数据对象。在 JavaScript 中,在一个函数中返回这个对象就可以了,这样就可以避免组件复用时的数据污染
:
data: function () {
return {
listTitle: '',
todos: []
}
}
🔸 为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?
因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
块格式化上下文(Block Formatting Context,BFC),它是一个独立的渲染区域,与区域外部毫不相干。
下列方式会创建块格式化上下文(BFC):
创建新的 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的效果。
<!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;
}
这也是清除浮动的一种方式。
前端常用的本地存储方式有三种:cookie
、localStorage
、sessionStorage
HTML5 提供了 sessionStorage (会话存储) 和 localStorage(本地存储)两个存储对象来对网页的数据进行添加、删除、修改、查询操作。
sessionStorage
用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据,在浏览器打开期间存在,包括页面重新加载
localStorage
用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去除。
三者的具体对比如下:
cookie使用起来麻烦需要自行封装。
localStorage和sessionStorage使用相同的API:
本系列的主题是 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已经被加载好了,
点击按钮的时候,读取的其实是缓存
加载方式的对比
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
tree shaking
是一个术语,用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 import
和 export
。
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"
],
// ...
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
flex(弹性盒子)是一种更为简洁灵活的布局方式。
容器默认存在两根轴:横轴和纵轴。
使用弹性盒子,要在父盒子先设置display:flex
。
常用于:
:) 规定容器中的项目是按行排列还是按列排列
:) 定义了项目在主轴上的对齐方式
效果图展示:
:) 定义了项目在“纵轴”上的对齐方式
语法:
align-items: flex-start | flex-end | center | baseline | stretch
各个值解析:
用一个容器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: 1 1 0
的缩略形式。
flex 值为 1,这表示每个元素占用空间都是相等的,占用的空间是在设置 padding 和 margin 之后剩余的空间。
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。
<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,用户代理检查每个
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,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
MVVM
是是Model-View-ViewModel
的缩写,Model
代表数据模型,定义数据操作的业务逻辑,View
代表视图层,负责将数据模型渲染到页面上,ViewModel
通过双向绑定把View
和Model
进行同步交互,不需要手动操作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
指的是Model-View-Controller
,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范
MVC的**:Controller负责将Model的数据用View显示出来,换句话说就是在Controller里面把Model的数据赋值给View。
MVC的特点:实现关注点分离,即应用程序中的数据模型与业务和展示逻辑解耦。就是将模型和视图之间实现代码分离,松散耦合,使之成为一个更容易开发、维护和测试的客户端应用程序。
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
<!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
元素定义的元数据的类型包括以下几种:
另外, 在同一个 <meta>
标签中,name , http-equiv 或者 charset 三者中任何一个属性存在时,itemprop 属性不能被使用。
本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
本文讲一下开发环境下如何调试代码,解决开发环境下调试代码问题,这个技术叫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(性能更好)
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
清除浮动是指清除由于子元素浮动带来父元素高度塌陷的影响。
导致背景不能正常显示,如果对父级设置了背景属性,导致父级不能撑开,会影响到背景图片不能正常打开。
边框不能撑开,由于子级使用了浮动效果,并且已经产生了浮动,父级不能撑开,所以影响边框不会随着内容的变化而变化。
<!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>
如果注释掉最后一个用来清除浮动标签,我们可以看到浮动带来的影响(高度塌陷):
优点:通俗易懂,方便
缺点:添加无意义标签,语义化差
不建议使用。
通过触发BFC方式,实现清除浮动
.fahter{
width: 400px;
border: 1px solid deeppink;
overflow: hidden;
}
优点:代码简洁
缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素
不推荐使用
.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.
推荐使用
.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.
推荐使用
两种!一种是过渡动画,一种是关键帧动画。
过度动画只能定义「开始」和「结束」两个状态。
关键帧动画可以定义「多个状态」。
使用 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;
}
}
伪类用于定义元素的特殊状态。
例如,它可以用于:
伪类开头为单冒号 :
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 /* 占位符。用于设置占位符的样式 */
在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中两个类选择器之间有的有空格,有的没空格。有空格的是后代选择器,那没有空格的是什么呢?
没有空格的选择器有两种:多类选择器
和结合元素选择器
。
元素选择器可以结合类选择器来使用。
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,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
code splitting(代码分割)
是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。
作用:代码分割可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。
常用的代码分割方法有三种:
entry
配置多入口手动地分离代码。这是迄今为止最简单直观的分离代码的方式,如何从 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
这种方式存在一些隐患:
上述隐患将造成重复引用。
解决方法:
配置 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
插件可以将公共的依赖模块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() 语法 来实现动态导入。
先从上述示例的配置中移除掉多余的 entry
和 optimization.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));
构建结果如下:
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
在打开一个页面,页面首要内容出现在屏幕的时间影响着用户的体验,而通过内联css关键代码能够使浏览器在下载完html后就能立刻渲染
而如果外部引用css代码,在解析html结构过程中遇到外部css文件,才会开始下载css代码,再渲染
所以,CSS内联使用使渲染时间提前
注意:但是较大的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 );
利用webpack、gulp/grunt、rollup等模块化工具,将css代码进行压缩,使文件变小,大大降低了浏览器的加载时间
虽然文件压缩能够降低文件大小。但CSS文件压缩通常只会去除无用的空格,这样就限制了CSS文件的压缩比例。那是否还有其他手段来精简CSS呢?答案显然是肯定的,如果压缩后的文件仍然超出了预期的大小,我们可以试着找到并删除代码中无用的CSS。
一般情况下,会存在这两种无用的CSS代码:一种是不同元素或者其他情况下的重复代码,一种是整个页面内没有生效的CSS代码。
手动删除这些无用CSS是很低效的。我们可以借助Uncss7库来进行。Uncss可以用来移除样式表中的无用CSS,并且支持多文件和JavaScript注入的CSS。
CSS选择器的匹配是从右向左进行的,这一策略导致了不同种类的选择器之间的性能也存在差异。相比于#markdown-content-h3,显然使用#markdown .content h3时,浏览器生成渲染树(render-tree)所要花费的时间更多。
所以我们在编写选择器的时候,可以遵循以下规则:
在页面发生重绘的时候,昂贵属性如box-shadow/border-radius/filter/透明度/:nth-child等,会降低浏览器的渲染性能
重排会导致浏览器重新计算整个文档,重新构建渲染树,这一过程会降低浏览器的渲染速度。如下所示,有很多操作会触发重排,我们应该避免频繁触发这些操作。
当元素的外观(如color,background,visibility等属性)发生改变时,会触发重绘。
在网站的使用过程中,重绘是无法避免的。不过,浏览器对此做了优化,它会将多次的重排、重绘操作合并为一次执行。
css样式文件有两种引入方式,一种是link元素,另一种是@import
@import会影响浏览器的并行下载,使得页面在加载时增加额外的延迟,增添了额外的往返耗时
而且多个@import可能会导致下载顺序紊乱
比如一个css文件index.css包含了以下内容:@import url("reset.css")
那么浏览器就必须先把index.css下载、解析和执行后,才下载、解析和执行第二个文件reset.css
DOCTYPE(document type
)的简写,它是一种标记语言的文档类型声明,即告诉浏览器当前 HTML 是用什么版本编写的。
文档类型定义Document Type Definition
) 进行引用,因为 HTML 4.01 基于 SGML(标准通用标记语言 国际上定义电子文档和内容描述的标准
)。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,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
因为 Vue 处理指令时,v-for
比 v-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>
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
在 HTML 中会遇到以下三类 script:
<script src='xxx'></script>
<script src='xxx' async></script>
<script src='xxx' defer></script>
那么这三类 script 有什么区别呢?
浏览器在解析 HTML 的时候,如果遇到一个没有任何属性的 script 标签,就会暂停解析,先发送网络请求获取该 JS 脚本的代码内容,然后让 JS 引擎执行该代码,当代码执行完毕后恢复解析。整个过程如下图所示:
可以看到,script 阻塞了浏览器对 HTML 的解析,如果获取 JS 脚本的网络请求迟迟得不到响应,或者 JS 脚本执行时间过长,都会导致白屏,用户看不到页面内容。这也是我们平时都把<script>
标签放到底部的原因。
async 表示异步,例如七牛的源码中就有大量的 async 出现:
当浏览器遇到带有 async 属性的 script 时,请求该脚本的网络请求是异步的,不会阻塞浏览器解析 HTML,一旦网络请求回来之后,如果此时 HTML 还没有解析完,浏览器会暂停解析,先让 JS 引擎执行代码,执行完毕后再进行解析,图示如下:
当然,如果在 JS 脚本请求回来之前,HTML 已经解析完毕了,那就啥事没有,立即执行 JS 代码,如下图所示:
所以 async 是不可控的,因为执行时间不确定,你如果在异步 JS 脚本中获取某个 DOM 元素,有可能获取到也有可能获取不到。而且如果存在多个 async 的时候,它们之间的执行顺序也不确定,完全依赖于网络传输结果,谁先到执行谁。这导致async属性下的脚本是乱序的,对于script有先后依赖关系的情况,并不适用。
defer 表示延迟,例如掘金的源码中就有大量的 defer 出现:
当浏览器遇到带有 defer 属性的 script 时,获取该脚本的网络请求也是异步的,不会阻塞浏览器解析 HTML,一旦网络请求回来之后,如果此时 HTML 还没有解析完,浏览器不会暂停解析并执行 JS 代码,而是等待 HTML 解析完毕再执行 JS 代码,图示如下:
如果存在多个 defer script 标签,浏览器(IE9及以下除外)会保证它们按照在 HTML 中出现的顺序执行,不会破坏 JS 脚本之间的依赖关系。
最后,根据上面的分析,不同类型 script 的执行顺序及其是否阻塞解析 HTML 总结如下:
XML 被设计用来传输和存储数据。
HTML 被设计用来显示数据。
HTML 是用来描述和定义网页内容的标记语言,是构成网页的最基本的东西。
所谓超文本,就是说它除了能标记文本,还能标记其他的内容,比如:图片,链接,音频,视频等。
XML 可以自己“发明”标签————这也是“可扩展的”一个含义。
XML 没什么特别的。它仅仅是纯文本而已。有能力处理纯文本的软件都可以处理 XML。
HTML 和 XML 一结合,就产生了 XHTML。是更严谨更纯净的 HTML 版本。
从继承关系上讲,HTML是一种基于标准通用标记语言(SGML)的应用,是一种非常灵活的置标语言,而XHTML则基于可扩展标记语言(XML),XML是SGML的一个子集。
宽高设为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>
效果图如下:
opacity:0
:本质上是将元素的透明度将为0,就看起来隐藏了,但是依然占据空间且可以交互visibility:hidden
: 与上一个方法类似的效果,占据空间,但是不可以交互了overflow:hidden
: 这个只隐藏元素溢出的部分,但是占据空间且不可交互display:none
: 这个是彻底隐藏了元素,元素从文档流中消失,既不占据空间也不交互,也不影响布局z-index:-9999
: 原理是将层级放到底部,这样就被覆盖了,看起来隐藏了transform: scale(0,0)
: 平面变换,将元素缩放为0,但是依然占据空间,但不可交互CSS 函数 scale()
用于修改元素的大小。可以通过向量形式定义的缩放值来放大或缩小元素,同时可以在不同的方向设置不同的缩放值。
scale() 仅适用于在欧几里德平面(二维平面)上的变换。如果需要进行空间中的缩放,必须使用 scale3D() 。
代码实现如下:
.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,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
生产环境的特点是能让代码优化上线运行的环境。
我们要做哪些事情呢?
样式经过css-loader
的处理被整合在js中,如果样式在js中的话,会让js体积非常大,下载就会慢;
同时因为是先加载js才能通过创建style
标签插入到页面中,这里就会出现闪屏现象。
所以,我们就需要将css从js中提取出来;
当然还需要对代码进行压缩和兼容性处理,这里包括html、css和js。
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兼容性处理需要用到postcss
这个库,postcss
在webpack中使用需要用到一个postcss-loader
和一个插件postcss-preset-env
就是这个东东
下载postcss-loader
和postcss-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.json
中browserslist
里面的配置,通过配置加载指定的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.js
webpack配置里的模式 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需要用到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文件越小,请求速度就会越快,加载速度就会越快,用户看到的效果也会更快一些,用户体验就会更好。所以我们在上线之前一定要给代码压缩
语法检查最常用的工具就是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.json
中eslintConfig
中设置
语法检查规则推荐使用airbnb
规则,为什么呢?
我们打开github,在探索中找到JavaScript
这个主题,下面就是js最受欢迎的仓库了
这里可以看到排名第二的是vuejs
第三个是facebook的react
第四个是bootstrap
第五个是一本书:你不知道的js
第六个就是我们推荐的airbnb这个风格指南
airbnb这个风格指南详细的介绍了我们应该怎么写js代码,不应该怎么写js代码,是一个很优秀的库。
我们现在要把这个库应用在eslint上,搜索npm,搜eslint,可以看到一个插件: eslint-config-airbnb
,这个就是让airbnb风格指南在eslint中生效
airbnb有 eslint-config-airbnb
和 eslint-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
版本啦,从下面的图片能看到我们还需要下载eslint
和eslint-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
}
},
先做个测试,写一些es6的语法
const add = (x, y) => {
return x + y;
};
console.log(add(2, 5));
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
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压缩非常简单,只需要把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压缩搞定
前面我们给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'
};
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
使用弹性盒子布局,只需要给待处理的块状元素的父元素添加属性:
/* 弹性盒子布局 */
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>
position: relative
;子元素设置为绝对定位:position: absolute
top: 50%; left: 50%;
margin-left
和margin-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>
只需将上面例子中的:子元素设置margin-left
和margin-top
为自身宽高一半的负值
替换为:transform: translate(-50%,-50%);
即:
position: relative
;子元素设置为绝对定位:position: absolute
top: 50%; left: 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
position: relative
;子元素设置为绝对定位:position: absolute
top: 0; bottom: 0; left: 0; right: 0;
margin: 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>
盒模型由content(内容)、padding(内边距)、border(边框)、margin(外边距)组成。
我们目前所学习的知识中,以标准盒子模型为准。
标准盒子模型:
IE盒子模型:
IE盒子模型(怪异盒子模型)
区别:
标准盒子模型中,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总的大小。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.