面试第五篇

  

HTML、CSS

canvas与svg的区别

都是有效的图形工具,对于数据量较小的情况都有很高的性能,都是用JavaScript和html,都遵守w3c标准

svg优点:

矢量图,不依赖于像素,无限方法后不会失真;

以dom形式展示,事件绑定由浏览器直接分发到节点上

svg缺点:

svg以dom形式展现,涉及到动画时需要更新dom,性能较低

canvas优点:

定制性更强,可以绘制自己想要的东西;

非dom结构形式,即基于JavaScript绘制,涉及到动画时较高

canvas缺点:

事件分发有canvas处理,绘制内容的事件需要自己处理

依赖于像素,无法高效保真,画布较大时由于绘制的过程时一个一个像素去绘制,像素较多时性能就会比较低。

可以继承的CSS属性

可以继承的:font-size、font-weight、line-height、color、cursor等

不可继承的一般是会改变盒子的:display、margin、border、padding、height等

图片较多的页面优化

1.设置懒加载。进入屏幕范围再加载。

2.压缩图片。尽量使图片小于2m

3.设置CDN。

4.按屏幕分辨率加载图片。

5.加大网络带宽。

HTML XML XHTML JSON

HTML(HyperText Markup Language / 超文本标记语言),用来描述和定义 网络内容的标记语言,超文本的意思是说,除了能标记本文,还能标记 图片,视频,链接 等其他内容

XML(Extensible Markup Language / 可扩展标记语言),表现就是给一堆文档加上标签,说明里面的数据是什么意思,方便存储、传输、分享数据。和 HTML 的区别是 HTML 的标签就预定义的,XML 是可扩展的 XHTML: Extensible Hypertext Markup Language / 可扩展超文本标记语,其实就是 HTML 的严格语法形式,约定了 属性名必需小写,空元素必需关闭,元素名小写,属性名必需加引号,布尔类型必需加属性值

JSON(Javascript Object Notation)比较轻量级的数据交换格式,由键值对组成,数据格式比较简单, 易于读写, 格式都是压缩的, 占用带宽小

CSS Sprites

CSS Sprites在国内很多人叫css精灵,也叫雪碧图,是一种网页图片应用处理方式。它允许将一个页面涉及到的所有零星图片都包含到一张大图中, 利用CSS的“background-image”,“background- repeat”,“background-position”的组合进行背景定位, 访问页面时避免图片载入缓慢的现象。

优点:

(1)CSS Sprites能很好地减少网页的http请求,从而大大的提高页面的性能,这是CSS Sprites最大的优点,也是其被广泛传播和应用的主要原因;

(2)CSS Sprites能减少图片的字节;

(3)CSS Sprites解决了网页设计师在图片命名上的困扰,只需对一张集合的图片命名,不需要对每一个小图片进行命名,从而提高了网页制作效率。

(4)CSS Sprites只需要修改一张或少张图片的颜色或样式来改变整个网页的风格。

缺点:

(1)图片合并麻烦:图片合并时,需要把多张图片有序的合理的合并成一张图片,并留好足够的空间防止版块出现不必要的背景。

(2)图片适应性差:在高分辨的屏幕下自适应页面,若图片不够宽会出现背景断裂。

(3)图片定位繁琐:开发时需要通过工具测量计算每个背景单元的精确位置。

(4)可维护性差:页面背景需要少许改动,可能要修改部分或整张已合并的图片,进而要改动css。在避免改动图片的前提下,又只能(最好)往下追加图片,但这样增加了图片字节。

Margin塌陷与合并

父组件内部套了自组件,如果父组件没有设置边框(border),也没有padding-top (这三个条件中没有一个满足的),当我们给子组件设置margin-top的时候,会发现父组件会跟着一起往下掉,这个现象就叫做margin值穿透,又叫边界重叠。

正常情况下,父级元素应该相对浏览器进行定位,子级相对父级定位.但由于margin的塌陷,父级相对浏览器定位.而子级没有相对父级定位,子级相对父级,就像坍塌了一样.父子嵌套元素在垂直方向的margin,父子元素是结合在一起的,他们两个的margi会取其中最大的值

解决方法;

触发bfc(块级格式上下文),改变父级的渲染规则,具体有四种方法

overflow:hidden;

float:left/right;

display:inline-block;

position:absolute/fixed

根据具体情况选择没有影响的来解决margin塌陷

margin合并

有两个元素是上下关系,当我们给上面的元素设置margin-bottom:value1; 又给下面的元素设置了margin-top:value2; 那它俩直接的距离并不是距离之和,而是选择最大的那个距离。

用bfc解决

rem、em、px区别

px像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。

em是相对长度单位。相对于当前对象内文本的字体尺寸,

rem也是相对长度单位。相对于根元素的尺寸,比如说浏览器的默认字体是16px,那么1rem=16px

实现0.5px的直线/边框

如果直接使用border:0.5px,chrome不能识别,会四舍五入为1px,此外chrome会把小于0.5px的当成0处理,因此有三种方法:

1.使用transform:scale和transform-origin,最完美,既不复杂实现效果也好

.hr .scalr-half{
  height:1px;
  transform:scaleY(0.5);
  transform-origin:50% 100%;
}

2.指定背景,使用线性渐变linear-gradient

.hr .gradient{
  height:1px;
  background:linear-gradient(0deg,#fff,#000)
}
<html>
<div class="hr gradient"></div>
</html>

有点发虚

3.使用boxshadow

.hr .boxshadow{
  height:1px;
  background:none;
  box-shadow: 0 0.5px 0 #000
}
<div class="hr boxshadow"></div>

同样有点发虚

4.设置背景使用svg,有点复杂

<svg xmlns='https://www.w3.org/2000/svg' width='100%' height='1px'>
   <line x1='0' y1='0' x2='100%' y2='0' stroke="#000"></line>
</svg>

BEM命名规范

Bem 是块(block)、元素(element)、修饰符(modifier)的简写,由 Yandex 团队提出的一种前端 CSS 命名方法论。

- 中划线 :仅作为连字符使用,表示某个块或者某个子元素的多单词之间的连接记号。

_ 单下划线:单下划线用来描述一个块或者块的子元素的一种状态

__ 双下划线:双下划线用来连接块和块的子元素

BEM 是一个简单又非常有用的命名约定。让你的前端代码更容易阅读和理解,更容易协作,更容易控制,更加健壮和明确,而且更加严密。

CSS加载原理

浏览器拉取到CSS后会进行解析,根据选择器的不同类型(比如element、class等)把他们分类到不同的桶中,浏览器基于它找到不同的选择器,将不同的规则应用在对应的DOM节点,并添加节点依赖的样式(称为渲染树阶段)

浏览器遇到无法解析的CSS选择器或者声明时会发生什么?

浏览器什么也不会做,会继续解析下一个CSS样式

Webpack

webpack配置项

webpack.config.js

const path = require('path');
module.exports={
  //配置打包模式
  mode:'development',
  //配置入口文件
  entry:{
    
  }//配置输出目录
  output:{
	
	}
}

配置es6/7/8代码转es5代码

npm install babel-loader @babel/core @babel/preset-env

新建bable.config.js

module.exports ={
  presets:[
    "@babel/preset-env"
  ]
}

配置webpack打包文件、图片、字体、媒体等

bundle、module与chunk的区别

官方解释:

Module提供比较完整程序接触面(surface area)更小的离散功能块。精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。 模块解析(Module Resolution)\一个模块可以作为另一个模块的依赖模块,resolver 是一个库(libary)用于帮助找不到模块的绝对路径,模块将在**resolve.modules**中指定的所有目录内搜索。

Chunk这是 webpack 特定的术语被用在内部来管理 building 过程。bundle 是由 chunk 组成,其中有几种类型(例如,入口 chunk(entry chunk)和子 chunk(child chunk))。通常 chunk 会直接对应所输出的 bundle,但是有一些配置并不会产生一对一的关系。 代码分离(Code Splitting)指将代码分离到每个 bundles/chunks 里面,你可以按需加载,而不是加载一个包含全部的 bundle。 配置(Configuration)webpack 的配置文件是一个普通的 JavaScript 文件,它导出为一个对象。然后由 webpack 根据这个对象定义的属性进行处理。

Bundle是由多个不同的模块生成,bundles 包含了早已经过加载和编译的最终源文件版本。 Bundle 分离(Bundle Splitting):这个流程提供了一个优化 build 的方法,允许 webpack 为应用程序生成多个 bundle。最终效果是,当其他某些 bundle 的改动时,彼此独立的另一些 bundle 都可以不受到影响,减少需要重新发布的代码量,因此由客户端重新下载并利用浏览器缓存。

简单来说:

  1. 对于一份同逻辑的代码,当我们手写下一个一个的文件,它们无论是 ESM 还是 commonJS 或是 AMD,他们是一个个 module
  2. 当我们写的 module 源文件传到 webpack 进行打包时,webpack 会根据文件引用关系生成 chunk 文件,webpack 会对这个 chunk 文件进行一些操作;
  3. webpack 处理好 chunk 文件后,最后会输出 bundle 文件,这个 bundle 文件包含了经过加载和编译的最终源文件,所以它可以直接在浏览器中运行。

一般来说,在不开启source-map的情况下,chunk和bundle的关系是一对一的。但是在开始source-map后,chunk和bundle就是一对多的关系,因为生成的bundle还包括了.map文件。

modulechunkbundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字:

我们直接写出来的是 module,webpack 处理时是 chunk,最后生成浏览器可以直接运行的 bundle。

产生chunk的三种方式
  • entry配置入口
  • 异步加载模块
  • 代码分割

配置入口

entry: {
    index: './src/js/index.js',
    login: './src/js/login.js'
},
output: {
    path: path.join(__dirname, './dist'),
    filename: '[name].js'
}

在entry对象中,每个字段都会产生一个chunk,如果将output选项中的filename写死名称,那么系统会报错,因为生成了多个chunk。如上所示配置,最终生成了两个bundle。

异步加载模块也会产生chunk

{
    entry: {
        'index': 'src/index.js'
    },
    output: {
        filename: '[name].bundle.js',
        chunkFilename: '[name].bundle.js'
    }
}
const Page = r => require.ensure([], () => r(require('./index.vue')), 'Page');

代码分割也会产生chunk

module.exports = {
    entry: {
        index: path.join(__dirname, './src/js/index.js'),
        login: path.join(__dirname, './src/js/login.js')
    },
    output: {
        path: path.join(__dirname, './dist'),
        filename: '[name].bundle.js',
        chunkFilename: '[name].bundle.js'
    },
    optimization: {
        runtimeChunk: "single",
        splitChunks: {
            cacheGroups: {
                commons: {
                    chunks: "initial",
                    minChunks: 2,
                    maxInitialRequests: 5, // The default limit is too small to showcase the effect
                    minSize: 0 // This is example is too small to create commons chunks
                },
                vendor: {
                    test: /node_modules/,
                    chunks: "initial",
                    name: "vendor",
                    priority: 10,
                    enforce: true
                }
            }
        }
    }
}

hash、chunkhash、contenthash区别

hash一般是结合CDN缓存来使用,通过webpack构建之后生成的文件名自动带上对应的MD5值,如果文件内容改变的话hash值也会改变,对应的html中引入的url地址也会变化。这种变化可以出发CDN服务区从源服务器上拉取对应数据,进而更新本地缓存。

具体区别在于:

hash:hash跟整个项目的构建有关,只有项目里有文件更改,整个项目构建的hash值就会更改,并且全部文件都共用相同的hash值。

chunkhash:采用hash计算时每次构建生成的hash值都不一样,即使文件内容没有改变。chunkhash和hash不一样,她能根据不同的入口文件进行依赖文件解析,构建对应的chunk,生成对应的哈希值。我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成hash值,只要不改动公共库的代码,就可以保证其哈希值不受影响。

contenthash:contenthash一般是在处理css样式文件时使用的.chunkhash是按模块是否改变进行重新生成hash,但是如果index.js更改了代码,css文件就算内容没有改变,由于该模块发生了改变,导致CSS文件会重复构建。

使用extra-text-webpack-plugin里的contenthash值,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,就不会重复构建

var extractTextPlugin = require('extract-text-webpack-plugin'),
    path = require('path')

module.exports = {
     output:{
       path:path.join(_dirname,'/dist/js')
       filename:'bundel.[name].[chunkhash].js'
     }
     plugins:[
        new extractTextPlugin('./css/bundle.[name].[contenthash].css')
    ]
}

webpack打包流程

串行流程:

初始化:启动构建,读取与合并配置参数,加载plugin,实例化Compiler

编译:从Entry入口发出,针对每个Module调用对应的loader去翻译文件内容,再找到该module对应的module,递归进行编译处理

输出:对编译后的module组合成chunk,把chunk转换成文件,输出到文件系统中

  • 读取文件,分析模块依赖
  • 对模块进行解析执行(深度遍历)
  • 针对不同的模块使用不同的 loader
  • 编译模块,生成抽象语法树(AST)
  • 遍历 AST,输出 JS

loader与plugin

loader主要用于预处理源文件,类似于构建工具中的任务概念,plugin主要完成loader无法完成的事情。loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件! 因为 webpack 本身只能处理 JavaScript,如果要处理其他类型的文件,就需要使用 loader 进行转换,loader 本身就是一个函数,接受源文件为参数,返回转换的结果。

plugin的主要执行apply方法, 在plugin中存在很多hook钩子,即生命周期。Plugin 是用来扩展 Webpack 功能的,通过在构建流程里注入钩子实现,它给 Webpack 带来了很大的灵活性。 通过plugin(插件)webpack可以实 loader 所不能完成的复杂功能,使用 plugin 丰富的自定义 API 以及生命周期事件,可以控制 webpack 打包流程的每个环节,实现对 webpack 的自定义功能扩展。

打包优化

构建优化

1.减少编译体积。ContextReplacementPlugin、Ignoreplugin、babel-plugin-import、babel-plugin-transform-runtime。

2.并行编译。happy pack、thread-loader、uglifyjsWebpackPlugin开启并行

3.缓存.cache-loader、hard-source-webpack-plugin、uglifyjsWebpackPlugin开启缓存、babel-loader开启缓存

4.预编译.dllWebpackPlugin&&DllReferencePlugin、auto-dll-webpack-plugin

性能优化

1.减少编译体积 tree-shaking、scope hosting

2.hash缓存。webpack-md5-plugin

3.拆包 splitChunksPlugin、import、require.ensure

如果你觉得我的文章对你有帮助的话,希望可以推荐和交流一下。欢迎關注和 Star 本博客或者关注我的 Github