2021前端个人总结

Html5

语义化标签

header
footer
nav
article
section

文档类型定义

在h5中只需要写DOCTYPE HTML,因为h5不基于SGML,所以不需要引用dtd,但是需要使用DOCTYPE HTML来规范浏览器的行为;
HTML4.01基于SGML所以需要对DTD进行引用,来告诉浏览器文档所使用的文档类型。

history

window.location.hash

hash

url中的hash指的是从#开始的一串字符。
通过a标签中的href属性可以将hash值添加到url中。
<a href="#test"></a>
点击链接,url后面就会加上#test,同时window.history会增加一条浏览记录。
window.history.hash可以访问到该记录,可以通过onhashchange事件监听hash值的变化
http请求不包括#号后面的内容,

Css

display属性

  1. inline 行内元素,默认不换行,不能设置宽高
  2. block 块级元素,默认换行,能设置宽高
  3. inline-block 可以设置宽高,而且可以不换行显示
  4. flex 弹性盒子

flex弹性盒子

原理:通过给父元素设置display:flex;来控制子元素的位置和排列方式。
基本概念:

  1. 容器:父元素; 项目:所有子元素;
  2. 主轴,交叉轴,默认沿主轴排列;

容器的属性:
flex-direction/flex-wrap/flex-flow/justify-content/align-items/align-content

  1. flex-direction 主轴的方向 row/row-reverse/column/column-reverse
  2. flex-wrap 项目换行方式 wrap/no-wrap/wrap-reverse
  3. flex-flow 前两个值的简写
  4. justify-content 定义项目在主轴的对齐方式
  5. align-items 定义项目在交叉轴的对齐方式
  6. align-content 定义多根轴线的对齐方式,一根轴线的时候不起作用

项目的属性:
order/flex-grow/flex-shrink/flex-basis/flex/align-self

伪元素

附加至选择器末尾的关键词,允许你对被选择元素的特定部分修改样式
按照规定应该使用双冒号,但由于旧版的W3C规范并未对此进行区分,所以两者都可
window.getComputedStyle可以获取伪元素的content信息
:after/before
:first-line/first-letter
:placeholder
:marker
:cue

伪类

添加到选择器的关键字,制定要选择的元素的特殊状态
可以添加多个伪类
使用单冒号
:link
:visited
:hover
:active
:focus()
:checked
:disabled
:first-child/last-child
:first/last
:first-of-type/last-of-type
:not()
:nth-child()

@media 媒体查询

在响应式设计的时候很有用,对不同尺寸的或类型的设备显示不同的样式
格式:
@media mediaType and|not|only (media feature) { css code; }

或者
<link media="mediaType and|not|only (media feature)" stylesheet="css1.css">

@support

用于检测浏览器是否支持某个属性(即浏览器对某个css属性是否兼容),如果支持就使用某套方案,可以在顶层用,也可嵌套使用

calc()

css中的计算函数,可以计算任意值,格式:width:calc(100% - 100px)
支持 + - * / ;运算符前后都要有空格,符合数学中的四则运算;

css中的居中方法

1.水平居中

行内元素:text-align:center;
块状元素:margin:0 auto;

2.垂直居中

line-height:height;

3.水平垂直居中

一:
position:absolute; top:50%; left:50%; transform: translate(-50%,-50%);
二:
display: flex; align-items: center; justify-content:center;

rem,em,px,vw/vh的区别

rem:长度是根据根元素的font-size来的,一般设html;
em:长度根据父元素的font-size,字体也是根据font-size;
vw/vh:即viewport width/height,视窗的宽度和高度,1vh即1%视口高度;
百分比:最近的父元素的宽高度;

移动端的适配

基本概念

  1. 屏幕尺寸:屏幕对角线的长度,单位是英寸,1英寸=2.54cm;
  2. 屏幕分辨率:屏幕横纵方向上的像素点数,单位是px,1px=1个像素点,一般表示为:纵向乘横向,例如:1960*1080
  3. 物理像素/设备像素(dp):屏幕能显示的最小粒度,设备/物理像素固定
  4. css像素(dip):与设备无关的像素,在标准显示密度下(普通屏),一个css像素对应一个设备像素。苹果的视网膜屏幕(Retina)的分辨率是普通屏的两倍,一个css像素就对应两个设备像素。如果用户进行缩放,则一个css像素对应的设备像素值也会随之变化。

css像素与物理像素的关系依赖于scale

对于web开发者而言,我们使用的每个css 和JavaScript定义的像素本质上都是css像素,在开发过程中我们并不关心一个css像素跨越了多少个设备像素,我们将这个依赖于屏幕特性和用户缩放程度的复杂计算交给了浏览器。

  1. 像素比(dpr):物理像素/独立像素(css像素),retina屏的dpr为2,即2个物理像素对应一个css像素,3倍屏的dpr为3。viewport中的scale和dpr是倒数关系。js中获取dpr: window.devicePixelRatio,部分浏览器不支持
  2. 布局视口:layout viewport = document.documentElement.clientWidth,比实际视口要宽
  3. 真实视口:visual viewport = window.innerWidth;部分机型不能正常获取
  4. 理想视口:ideal viewport,没有固定的尺寸,最适合移动端,不同的设备拥有不同的理想视口,它的意义在于,无论分辨率为多少,都可以完美的呈现给用户。所有的iphone的理想视口都是640px*320px

适配方案:

  1. 设置meta标签
    <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
  2. 使用rem,根据设备宽度动态设置根元素的font-size
  3. 设置viewport的scale为1/dpr,可以画出1px的线条
  4. 使用vm/vh

关于移动端适配,参照https://www.jb51.net/article/149140.htm, https://www.jianshu.com/p/b13d811a6a76

rem

基准设备宽度baseWidth(iphone678):375
基准根元素baseFontSize: 16px
baseWidth/baseFontSize = width/font-size
获取当前设备的设备宽度,根据以上基准值,算出根元素的font-size

0.5px的线

transform:scale(0.5);

盒模型

分为IE盒模型和w3c盒模型
box-sizing:content-box; // 默认值,w3c盒模型
box-sizing:border-box; // IE盒模型
IE盒模型:width=border+padding+content;
w3c盒模型:width=content;

清除浮动

clear:both;
::after 伪元素;
overflow:hidden;
父级元素设置高度;

BFC(Block Formatting Context)

块级格式化上下文,独立的渲染区域,与其他部分互不干扰;
触发条件:
根元素
position:absolute/fixed;
display:inline-block/table;
float元素;
overflow!==visible;
规则:
属于同一个BFC的两个相邻盒子垂直排列,并且margin会重叠;
不会与float的元素区域重叠;
计算其高度时float元素也会参与计算;
文字层不会被浮动层覆盖,会环绕周围;

Javascript

JavaScript组成部分

dom+bom+ECMAScript(核心)

深拷贝和浅拷贝

https://www.jianshu.com/p/1c142ec2ca45
浅拷贝:for-in,Object.assign(),直接赋值
深拷贝:
递归,
SON.Stringify()和JSON.parse(),
jQuery的extend(),
lodash库的cloneDeep(),
用slice(),concat()实现数组的深拷贝,
扩展运算符,
Object.create()

本地存储

localStorage:5M,用户手动清理,所有同源窗口共享,仅存储在浏览器,用于长期登陆
sessionStorage:5M,浏览器标签页关闭,储存结束,同一个浏览器窗口共享,仅存储在浏览器,用于敏感登陆
cookie:容量不超过4k,请求中会带上,设置过期时间,同源窗口共享,用于记录是否登陆过,以便实现下次自动登陆

ECMAScript

定义了语法、类型、语句、关键字、保留字、操作符、对象
类型运算符:typeof instanceof

js中的引用类型

Object Array Date Function RegExp 基本包装类型(String Number Boolean) 单体内置对象(Global Math)

Object

创建方式: 对象字面量(不会调用构造函数),构造函数。
所有Object的实例都有的方法和属性(不包括宿主对象):

constructor:构造函数

hasOwnProperty(propertyName):检测传入的属性名是否是该对象的实例属性

isPrototypeOf(object):检测该对象是否是传入对象的原型

propertyIsEnumerable(propertyName): 检查属性是否可以枚举(使用for-in遍历)

valueOf():获取该对象的字符串值

toString():同valueOf

toLocaleString():根据地区不同返回该对象的字符串值。

Array

  1. 数组长度可动态调整;
  2. 数组的每一项都可以是任意数值;

创建方式:

  1. 使用构造函数
    var arr1 = new Array(3)
    var arr1 = Array(3)
    var arr1 = new Array('123')
  2. 使用数组字面量(不会调用构造函数)
    var arr1 = ['1', 's', 'tr']

length属性:可读可写

检测数组
Array.isArray() // 推荐使用
arr instanceof Array // 当有多个全局执行环境时会有问题

转换方法

valueOf():隐式调用toString()方法,返回值同toString()

toString():返回数组中每个值的字符串形式拼接而成的以逗号分割的字符串

toLocaleString():调用每一项的toLocaleString()方法,返回逗号拼接的字符串

join():使用不同的分割符来拼接字符串

栈方法
特点:后进先出(Last-In-First-Out)

push(): 接收推入的值,返回数组长度
pop():移除末尾项,返回移除的项

队列方法
特点:先进先出(First-In-First-Out)

push(): 接收推入的值,返回数组长度
shift():移除起始项,返回移除的项

或者

pop(): 移除末尾项,返回移除的项
unshift():在数组起始端添加,返回数组长度

重排序方法

reverse():倒序
sort():默认升序,会调用toString()方法比较字符串而不是数值,可传入一个比较函数,返回排序后的数组

操作方法

concat():拼接

slice(startIndex[,endIndex]):分割数组,返回分割好的数组,不会影响原数组,当参数为负数时,会与length相加,当endIndex小于startIndex,返回空数组

splice(index, 删除的个数, 替换的值):可以删除、插入、替换 ,返回值为被删除的数组成的数组,如果没有则返回[]

位置方法

indexOf(val[,startIndex]):查找val在数组的索引值,如果没有返回-1
lastIndexOf():从数组尾部开始查

迭代方法
接收两个参数,第一个是回调函数,第二个是作用域对象,第二个可选。
回调函数有三个参数:function(item, index, arr)
迭代方法不会影响原数组

forEach(): 没有返回值

map():返回回调函数调用的结果组成的数组

some():回调函数中有一项返回true,结果就为true

every():回调函数中每项返回true,结果才为true

filter():返回回调函数返回true的项组成的数组

归并方法
迭代数组每一项,构建一个最终数组
接收两个参数,第一个是回调函数,第二个是初始值,第二个可选。
回调函数有四个参数:function(before, now, index, arr)

reduce(): 没有返回值
reduceRight(): 没有返回值

数组去重
https://segmentfault.com/a/1190000016418021

  1. es6 Set,无法去掉空对象
  2. indexOf,includes,新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。
  3. sort,先排序,再对比相邻元素
  4. Map,利用map的key不能重复的特性去重
  5. reduce+includes

Date

使用自UTC(Coordinated Universal Time,国际协调时间)1970年1月1日零时开始经过的毫秒数来保存日期。
创建方式:new Date()
参数:

  1. 不传参,获得当前时间和日期
  2. 传毫秒数:获得指定时间和日期
  3. 传代表日期的字符串:调用Date.Parse(),返回日期

将指定时间和日期转化为毫秒数:Date.Parse()和Date.UTC()

Date.Parse(): 接收一个表示日期的字符串参数,尝试返回对应的毫秒数,结果因地区而异,如果该字符串不能表示日期则会返回NaN。

Date.UTC(): 作用同上,区别在于:参数(年份,月份(0-11),日期(1-31),小时(0-23),分钟,秒,毫秒)只有前两个是必须的

获取当前时间:Date.now(),返回毫秒数

继承的方法:toString(),toLocaleString(),valueOf()

toString():返回带有时区信息的日期和时间
toLocaleString(): 根据浏览器设置的地区相适应的格式返回日期和时间
valueOf():返回日期的毫秒表示,多用于比较日期。

日期格式化方法:

toDateString():以特定于实现的格式显示星期几、月、日和年
toLocaleDateString():以特定于实现的格式显示星期几、月、日和年

toTimeString():以特定于实现的格式显示时分秒和时区
toLocaleTimeString(): 以特定于实现的格式显示时分秒

toUTCString():以特定于实现的格式显示完整的UTC日期

日期时间组件方法

getTime()
setTime(毫秒)

getFullYear()
setFullYear()
getUTCFullYear()
setUTCFullYear()

getMonth()
setMonth()
getUTCMonth()
setUTCMonth()

getDate()
setDate()
getUTCDate()
setUTCDate()

getDay()
getUTCDay()

getHours()
setHours()
getUTCHours()
setUTCHours()

getMinutes()
setMinutes()
getUTCMinutes()
setUTCMinutes()

getMinutes()
setMinutes()
getUTCMinutes()
setUTCMinutes()

js中的面向对象

理解对象

简单来说,对象是键值对的集合。
属性类型:数据属性,访问器属性。

  1. 数据属性:Configurable,Enumerable,Writable,Value
    configurable:是否能通过delete删除该属性,对象上定义的属性默认为true.
    enumerable:是否可通过for-in枚举,对象上定义的属性默认为true.
    writable:是否可修改,对象上定义的属性默认为true。
    value:这个属性的值,默认undefined。
    修改属性默认的特性:Object.defineProperty(对象,属性,描述符对象)

    Object.defineProperty(person, 'name', {
      writable: false,
      value: 'Li'
    })
    

    注意:将configurable配置为false之后,不能delete它,也不能修改 它,并且不能将它在改为true了。在调用object.defineProperty()方法,如果不指定configurable、writable、enumerable的值,默认为false。

  2. 访问器属性:getter,setter(非必须)
    configurable: 同数据属性。
    enumerable: 同数据属性。
    get:在读取访问器属性的时候会调用这个函数。
    set:在写入访问器属性的时候会调用这个函数。

        var book = {
          _year:2004,
          edition: 1
        }
        Object.defineProperty(book, "year", {
          get: function() {
            return this._year
          },
          set: function(val) {
            if (val > 2004) {
              this._year = val;
              this.edition += val - 2004
            }
          }
        })
    

    注:_year前面的下划线是一种常用的记号,表示只能通过对象方法访问的属性

    Object.defineProperties(book, 对应属性的描述符对象)
    Object.getOwnPropertyDescriptor(book, "_year"),该方法只能用于实例属性

创建对象

创建单个对象: 对象字面量/构造函数
创建多个对象

  1. 工厂模式

    function createPerson(name, age) {
      var o = new Object();
      o.name = name;
      o.age = age;
      o.sayName = function() {
        alert(this.name)
      }
      return o
    }
    var person1 = createPerson('Li', 23);
    var person2 = createPerson('Di', 13);
    

    优点:可以快速的创建多个相似的对象;
    缺点:无法知道一个对象的类型(对象识别)。

  2. 自定义构造函数

    function Person(name, age) {
      this.name = name;
      this.age = age;
      this.sayName = function() {
        alert(this.name)
      }
    }
    var person1 = new Person('Li', 23);
    var person2 = new Person('Di', 13);
    

    new操作符
    a. 创建一个新对象;
    b. 设置原型链(将新对象的__proto__指向函数的prototype) ;
    c. 将构造函数的作用域赋给新对象(绑定this);
    d. 执行构造函数的代码(为新对象添加属性);
    e. 返回新对象。

    可以用instanceof检测person1和person2的类型
    person1 instanceof Person => true
    person1 instanceof Object => true

    优点:对象类型识别
    缺点:每个对象都有单独的sayName函数实例,这个不必要。

  3. 原型模式

    function Person() {
    }
    Person.prototype.name = 'Li';
    Person.prototype.age = 31;
    Person.prototype.sayName = function() {
      alert(this.name)
    }
    
    var person1 = new Person();
    var person2 = new Person();
    
    

    person1和person2共享所有的属性和方法。

    关于prototype和__proto
    Person.prototype.constructor 指向 Person
    person1.__proto__ 指向 Person.prototype
    Person.prototype.isPrototypeOf(person1) 指向 true
    Object.getPrototypeOf(person1) == Person.prototype
    Object.getPrototypeOf(person1).name 值为:"Li"

    读取对象的某个属性的时候,先在对象实例本身读取,如果没有找到,则去原型对象上去找,如果还没有找到,则沿着原型链继续往上找。
    可以通过person1.hasOwnProperty("age")来检测该属性是否是实例上的属性。
    操作符in: `alert("name" in person1) 不管是实例属性还是原型上的属性都返回true.
    in配合hasOwnProperty()可以检测属性是否是原型属性:

    function hasProtoProperty(object, name) {
      return !object.hasOwnProperty(name) && (name in object)
    }
    

    for-in:返回所有可枚举的属性(enumerable为true),包括实例上的以及原型对象上的。属性没有顺序。如果属性值为undefined或是null会抛出错误或不执行,所以建议先判断。

    Object.keys(person1)获取对象上所有可枚举的实例属性。返回数组。
    Object.getOwnPropertyNames(person1)获取对象上的所有实例属性,不论是否可以枚举。返回数组。

    调用构造函数会为实例添加一个指向最初原型(prototype)的指针(__proto__),如果在调用构造函数之后重新定义prototype,则会切断这个指针,即person.proto不再指向Person.prototype了。

    可以在原生对象(String/Number/Array/……)的原型上添加方法,但是不推荐。

    缺点:所有实例共享属性和方法,属性值不够灵活(当属性值为引用值的时候)

  4. 组合使用自定义构造函数和原型模式
    通过自定义构造函数定义实例属性,原型模式定义共享的属性和方法

    function Person(name, age) {
      this.name = name;
      this.age = age;
    }
    Person.prototype.sayName = function() {
      alert(this.name)
    }
    
    var person1 = new Person("Li", 12);
    var person2 = new Person("Di", 24);
    
  5. 动态原型模式

    function Person(name, age) {
     this.name = name;
     this.age = age;
     // 一下这段代码只在初次调用构造函数的时候执行
     if(typeof this.sayName != "function") {
       Person.prototype.sayName = function() {
         alert(this.name)
       }
     }
    }
    
    var person1 = new Person("Li", 12);
    var person2 = new Person("Di", 24);
    
  6. 寄生构造函数模式
    类似工厂模式,建议在特殊情况下使用。

  function Person(name, age) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function() {
      alert(this.name)
    }
    return o
  }

  var person1 = new Person("Li", 12);
  var person2 = new Person("Di", 24);
  1. 稳妥构造函数模式
    稳妥对象:没有公共属性,方法也不引用this对象。在安全的环境中使用。

    function Person(name, age){
      var o = new Object();
      o.sayName = function() {
        alert(name)
      }
      return o
    }
    

js继承

  1. 原型链继承

    function Person(name, age) {// 父
      this.name = name;
      this.age = age;
    }
    function Student() { // 子
    
    }
    Student.prototype = new Person();
    

    每个构造函数(Person)都有原型对象(prototype),指向Object的实例上的proto属性,Object.Prototype为null
    子类构造函数的prototype指向父类的实例,而父类构造函数的prototype指向Object的实例,通过这种关系,形成了原型链。
    确定原型和实例之间的关系
    a. instanceof操作符
    b. isPrototypeOf()
    Object.prototype.isPrototypeOf(instance) true
    缺点:父子之间共享引用类型的值,不能给父类构造函数传递参数。

  2. 借用构造函数
    在子类的构造函数中调用父类的构造函数,可以避免原型链继承方式中的引用类型值共享的问题,以及不方便向父类构造函数中传递参数的问题(因为可能会影响父类的实例)
    通过call或者apply实现

    function Person(name, age) {// 父
      this.name = name;
      this.age = age;
    }
    function Student(name, age, score) { // 子
      Person.call(this, name, age)
      this.score = score
    }
    

    缺点:在构造函数中定义方法不能实现方法复用

  3. 组合继承
    原型链继承(继承原型上的属性和方法)+借用构造函数(继承实例上的属性)

    function Person(name, age) {// 父
      this.name = name;
      this.age = age;
    }
    function Student(name, age, score) { // 子
      Person.call(this, name, age)
      this.score = score
    }
    Student.prototype = new Person();
    Student.prototype.constructor = Student;
    Student.prototype.sayScore = function() {
      alert(this.score)
    }
    

    缺点:会调用两次父类的构造函数

  4. 原型式继承
    由道格拉斯.克罗克福德提出,基本思想:借助原型可以基于已有的对象创建新对象,同时也不用创建自定义类型。

    function object(o) {
      function F() {}
      F.prototype = o;
      return F()
    }  
    

    ECMAScript5新增Object.create()规范了原型式继承,Object.create(proto[,propertiesObject]),使用现有的对象来提供新创建的对象的proto(来自MDN),返回继承了proto的对象。第二个参数与Object.defineProperties()的第二个参数相同。

    缺点:同原型链继承一样,引用类型的值会共享

  5. 寄生式继承
    同样由道格拉斯.克罗克福德提出,思路与原型式继承紧密相关:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回对象。

    function createAnother(original) {
      // 也可以用其他方法来创建clone,比如Object.create()
      var clone = object(original);
      clone.sayName = function() {
        alert('hi')
      }
      return clone
    }
    

    缺点:不能函数复用,与借用构造函数类似

  6. 寄生组合式继承(理想的调用方式)
    通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
    不用调用两次父类的构造函数

function inheritPrototype(subType, superType) {
  var prototype = Object(superType.prototype); // 创建对象
  prototype.constructor = subType;  // 增强对象
  subType.prototype = prototype; // 指定对象
}

function Person() {}
function Student() {
  Person.call(this)
}
inheritPrototype(Student, Person);

call()方法
apply()方法
Object.assign()
// todo

js浅拷贝及深拷贝
// todo

dom(文档对象模型)

script标签

async:
defer:
src:
type: 编写代码使用的脚本语言的内容类型(MIME类型),默认text/javascript

基本数据类型
string boolean null undefined number object(引用类型) symbol(es6)

原生js操作DOM

document.getElementById()
document.getElementsByTagName() 伪数组
document.getElementsByClassName()
document.querySelector()
document.querySelectorAll()

window.onload()

ele.style.width = '234px';
ele.innerHtml = '内容';
ele.innerText

ele.className 获取标签的属性名
ele.value 获取表单元素的value值
ele.disabled
ele.checked
ele.selected

document.getComputedStyle() ie低版本不支持
ele.currentStyle()

onfocus()
onclick()
onblur()
onkeyup()
ondblclick()

nodeType:节点类型

  1. 元素节点
  2. 属性节点
  3. 文本节点

nodeName:节点名
nodeValue: 节点值

  1. 元素节点:null
  2. 文本节点:文本值
  3. 属性节点:属性值

节点属性
ele.parentNode
ele.childNodes 包含文本节点,空节点
ele.children
ele.firstChild/lastChild
ele.previousSibling/nextSibling

节点方法
parentEle.appendChild()
parentEle.insertBefore()
ele.cloneNode()
parentEle.replaceChild()

创建节点
document.createElement()
ele.innerHtml()
document.write()

删除节点
parentEle.removeChild()

属性
ele.getAttribute()
ele.setAttribute()
ele.removeAttribute()

Math.random()

bom(浏览器对象模型)

浏览器渲染页面的大致过程

  1. 渲染引擎启动html解释器(htmlParser)解析html源码,根据DOM API创建dom tree,Browser进程并行下载网络资源(css/image/js...)。
    在dom树中,每个html标签都有一个对应的节点,每个文本也有对应的文本节点,根节点就是documentElement,对应的是html节点。当遇到script节点时,将启动js引擎解析脚本,此时会阻塞DOM树的构建。
    当DOM树构建完成时,DOMContentLoaded事件会被触发。
    当DOM树构建完成,并且页面依赖的资源(图片、视频等) 都下载完了,onload事件会被触发。

  2. 渲染引擎启动css解释器(cssParser,cssGrammer)处理css源码,计算出最终样式,根据CSSOM API(css对象模型)构建cssRuleTree(css规则树),忽略其中的非法语法。

  3. 渲染引擎将domTree和cssRuleTree 合成rendering tree(渲染树),计算各个节点在页面上的布局或排版,忽略head、display:none的元素,每行都是独立的文本节点,每个节点都有对应的css。

  4. 当渲染树创建后,浏览器就会绘制(paint)页面到屏幕上。

这个过程并不是一次完成,实际上css和js会多次修改dom或者cssom。

重排/回流以及重绘

重排(reflow):元素的宽高引起布局的改变,就需要重排,重排后一定会重绘;
重绘(repaint):元素的颜色、透明度、字体样式变化会重绘,重绘不一定引起重排;

引起重排:

  1. 添加删除可见的dom元素;
  2. 元素位置、大小的变化;
  3. 内容改变;
  4. 页面渲染器初始化;
  5. 浏览器窗口大小改变;

避免重排和重绘:

  1. 使用类名来切换样式;
  2. 多个样式写在一起;
  3. 批量修改样式(先将需要改变的部分隐藏起来(display:none),修改后再展示)
  4. 缓存布局信息(offsetLeft,clientTop);当需要多次获取这些信息时,将其保存在一个临时变量中,通过访问变量得到这些值。
  5. 使用visible:hidden;代替display:none;

闭包

调用外部函数内(外部作用域)的变量。
缺点是常驻内存会增大内存使用量,并且使用不当容易造成内存泄漏
好处是减少全局变量的使用,使用闭包模块化代码
立即调用函数有,匿名函数

js跨域

解决方案:

  1. JSONP(动态创建scripts标签加载其他域的js文件,在当前页面调用加载后的文件。具体操作:向其他域传入一个callback的参数,其他域的后台将callback参数值和json串包装成js函数返回至当前域,将其解析成为可执行的js文件,从而实现域与域之间的通信)
  2. iframe
    通过document.domain实现(前提是属于同一个顶级基础域和同一个协议)
  3. 后台代理
    通过后台转发http请求,Nginx反向代理,
  4. 后端设置响应头
  5. h5的postMessage,使用Window.onmessage来获取
  6. window.name
  7. CORS:是一种新标准,支持同源通信也支持跨域通信。fetch是实现cors通信的。
  8. websocket

ajax原理

Asynchronous Javascript And XML;异步的js和xml;
创建XmlHttpRequest对象(浏览器自带),简称xhr;
优点:局部刷新,提高用户体验
缺点:不利于seo优化

var xhr = null;  
// 兼容性检查  
if(window.XmlHttpRequest) {
  xhr = new XmlHttpRequest();
}else{
  xhr = new ActiveXObject('Microsoft XmlHttp');
}
// get请求
xhr.open('GET',url?data,true); // 异步  
xhr.send();  
// POST请求
xhr.open('POST',url,true);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send(data);
// 接收响应
xhr.onreadystatechange = function() {
  if(xhr.readyStatus === 4) {
    if (xhr.status === 200) {
      // 获取响应结果 xhr.responseText
    }
  }
}

浏览器的进程(process)与线程(thread)

进程:CPU资源分配的最小单位
线程:程序执行的最小单位
进程与线程的关系:一个进程有多个线程共同协作完成任务,同一个进程下的线程共享程序的内存空间(代码段,数据集,堆等)。

浏览器内的进程

  1. 有多个进程,当浏览器每打开一个标签页就相当于创立了一个独立的浏览器进程(不绝对,浏览器会将多个空白标签页合成一个进程)
  2. 辅助进程: 辅助标签页进程的其他进程
    a. Browser进程:浏览器的主进程,负责协调主控,只有一个。作用有:负责浏览器界面显示,与用户交互(前进后退等);负责各个页面的管理,创建和销毁其他进程;网络资源的管理,下载等。
    b. 第三方插件进程:每种类型的插件对应一种进程,仅当使用该插件的时候创建。
    c. GPU进程:最多一个,用于3D绘制等。
    d. 浏览器渲染进程(浏览器内核),Renderer进程,内部是多线程的,即每个标签页所拥有的进程,互不影响,负责渲染页面,脚本执行,事件处理等。

浏览器内核(渲染进程,Renderer进程,渲染引擎)的常驻线程

  1. GUI(Graphical User Interface/图形用户界面)渲染线程
    a. 负责渲染浏览器界面,包括解析html,css,构建Dom树,Render树,布局与绘制等。
    b. 重绘(repaint)或者重排/回流(reflow),执行该线程
  2. JS引擎线程(js内核),例如V8引擎,负责处理js脚本,等待任务队列的到来,然后加以处理,浏览器不论什么时候都只有一个js引擎在运行js程序。
  3. 事件触发线程,控制事件循环,鼠标点击,滚动页面等等,会将对应任务添加到事件触发线程中,当对应的事件被触发时,该线程负责将其添加到待处理事件队列的末尾,等待js引擎空闲时处理。
  4. 定时触发线程,setTimeOut和setInterval所在的线程。由于定时器计时并不是由js引擎计时的(因为js引擎是单线程的,如果该线程堵塞了,会影响计时的准确性),当计时完被触发,事件会被添加到待处理事件队列的末尾等待js引擎空闲时处理。
  5. 异步HTTP请求线程,在XmlHttpRequest连接后启动一个新的线程,如果线程检测到请求的状态变更,如果设置有回调函数,该线程会把回调函数添加到事件队列中等js引擎空闲时处理。
  6. EventLoop轮询处理线程,负责在异步代码执行成功后,取相应的回调函数。

浏览器内核中线程之间的关系

  1. GUI渲染线程与js引擎互斥。
    理由:如果js引擎正在修改某个dom解构,GUI渲染线程又正在渲染页面,那么会导致两者处理的结果不一致。也可以由此推出,当js引擎正在进行复杂计算时,js引擎将会阻塞,页面长时间得不到渲染,就会很卡顿。
  2. js引擎与事件触发线程、定时触发线程,异步HTTP请求线程。
    在浏览器内核中有个事件队列。事件触发线程、定时触发线程、异步HTTP请求线程 这三个线程的回调函数会被放入事件队列中,当满足触发条件时,并且js引擎空闲时,就会去执行事件队列中事件。
    事件队列中的任务被称为宏任务,js引擎中的任务被称为微任务。当执行完一个事件队列中的事件后,js引擎会去检测是否有没有执行的微任务,如果有,就先执行微任务。

微任务和宏任务

  1. 微任务:语言标准提供的,promise,process.nextTick(),Mutation Observe
  2. 宏任务:宿主对象提供的,setTimeout,setInterval,setImmediate,requestAnimationFrame,I/O操作(点击事件,输入事件,异步http请求),UI渲染。

js代码->process.nextTick()->promise->setTimeout->setInterval->setImmediate->I/O->UI渲染

process.nextTick():在当前执行栈的尾部,当下一次event loop询问时执行。
setImmediate():相当于setTimeout(fn,0),当前执行栈所有事件执行完毕之后执行。比setTimeout先执行。

任务队列中,每当执行完一个异步回调函数后,event loop就会去执行栈(即主程序)中询问是否有微任务需要执行,这个时候如果有process.nextTick(),会执行它,然后再去执行微任务,此时多个微任务是一起执行的。

为什么js是单线程的?

因为js是用来处理用户与界面的交互,即对dom的操作,如果设计成多线程的,那么会带来很复杂的同步问题,比如一个线程需要修改某个节点,另一个线程又要删除这个节点,那么这个时候该以哪个线程为主就是个问题了。

h5的新标准中允许使用 new Worker 的方式来开启一个新的线程,去运行一段单独的js文件脚本,但是它严格的限制了可使用的功能,比如智能用ECMAScript,不能使用BOM和DOM。

Web Worker

// todo

模块化

  1. 全局function
  2. 简单对象(namespaced)
  3. IIFE(匿名闭包)
  4. AMD规范,require.js这个工具库遵循了该规范,(define()/require()),异步加载,用于浏览器端
  5. CMD规范,整合了AMD和CommonJs规范,sea.js这个工具库遵循了该规范,使用define()定义模块,export暴露模块,require引入模块,异步加载,用于浏览器端
  6. Node.js的CommonJs(module.exports/require),同步
  7. es6的import/export,编译时确定依赖,而不是运行时确定,所以是静态的,可用于前后端

ES6新增

1.es6模块

import/export

2.let/const

3.symbol

独一无二的值

4.函数参数的默认值,数组解构,对象解构

5.class(语法糖)

6.promise 异步函数

异步编程的解决方案,解决了回调地狱的问题(es7的async和await也可以解决)。
缺陷:

  1. 无法取消Promise,一旦创建它就会立即执行,无法中途取消;
  2. 如果不设置回调,promise内部的错误不会反应到外部;
  3. 当处于pending状态时,无法得知进展到哪个阶段。

状态(pending,fulfilled,rejected)一旦改变就不会再变,一般是pending到fulfilled或者pending到rejected,最后的状态为resolved。
构造函数:Promise(fn)

7.Set和Map

8.箭头函数

与普通函数的区别

  1. this的指向
  2. 没有arguments绑定,可访问最近的非箭头函数的arguments对象。
  3. 不能作为构造函数(不支持new操作符,没有prototype)
  4. 不能使用重复命名的参数,在非严格模式下,普通函数是支持的。
  5. 语法更简洁。

9.数组的方法

forEach()
map()
filter()
reducer()
some()
every()
find()

数组扁平化
flat()

10.模板字符串

11.对象字面量简写

12.展开运算符(...)

13.剩余参数(...)

将不确定数量的元素表示为数组,一般用于定义参数

HTTP(Hyper Text Transfer Protocol)

一次完整的http请求

1.域名解析
2.TCP三次握手
3.建立TCP请求后发起http请求
4.服务器响应请求,返回html代码
5.浏览器解析html代码并请求其中的资源(css/js/image)
6.浏览器渲染页面呈现给用户
7.连接结束

DNS解析

将网址翻译成IP地址,本地域名服务器=》根域名服务器

TCP协议

三次握手,四次挥手
状态码(100-500)
1:信息,服务器收到请求
2
:成功,操作被成功接收并处理
3:重定向
4
:客服端错误
5**:服务器端错误

GET请求和POST请求的区别

1.传参方式:get通过url,有长度限制(url限制),post在请求体里面传参,没有长度限制
2.后退会导致post请求重新请求
3.get请求可以被缓存
4.get请求只支持URL编码
5.get请求会留在历史记录中
6.get请求只支持ASCII字符他,post没有限制

content-type

上传文件:multipart/form-data
表单提交:application/x-www-form-urlencoded
Json格式:application/json
html文件:text/html

http请求

参考https://www.cnblogs.com/linliquan/p/11362336.html
https://www.cnblogs.com/xuxinstyle/p/9813654.html
特点:无状态(对事物处理没有记忆能力),无连接(每次只处理一个请求,完成之后就会断开),灵活(可传递多种类型的数据),简单快速,支持客户/服务器模式

HTTP 请求报文:请求行,请求头,空行,请求体

请求行: http协议版本字段,请求方法字段,URL字段,中间用空格隔开
请求头: 由关键字/值对组成,常见的有User-Agent,Accept,Host……
空行:包含回车符和换行符,告诉服务端不再有请求头
请求体: get方法没有请求体,post方法常用于表单传输数据,经常使用Content-Type和Content-Length

HTTP 响应报文:状态行,响应头,空行,响应体

状态行: http协议版本字段,状态码,状态码的描述字段,中间用空格隔开
状态码由3位数组成,首位表示响应的类型
1:表示服务器已经接受请求,客户端可继续发送请求;
2:表示服务器已经成功收到请求并进行处理;
3:重定向;302
4:表示客服端请求有非法内容,跨域:403
5:服务器端出错

响应头: Location,Server,Vary,Connection
空行:包含回车符和换行符,告诉客服端不再有响应头
响应体: 服务器返回的文本信息

前端优化方案

  1. 合理缓存
  2. 压缩文件(JavaScript:UglifyPlugin,MiniCssExtractPlugin,HTML:HtmlWebpackPlugin)
  3. 图片懒加载
  4. 事件委托
  5. 防抖节流
  6. 减少http请求
  7. 合理设置缓存
  8. 静态资源使用cdn
  9. 字体图标

防抖和节流

优化高频率执行js代码的一种手段。
防抖(debounce):持续触发某个事件,给定时间内没有再触发就执行一次;如果还没有到给定时间又触发了,则重新开始计时。
应用:输入验证,搜索提示

节流(throttle):持续触发某个事件,一定时间内只执行一次。类似眼睛一段时间内只眨一次。
应用:监听页面元素滚动

区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。

Vue

diff算法
object.defineProperty
生命周期:create mount update destroy

vue-router

  1. 钩子函数
    全局钩子:beforeEach,afterEach
    全局解析守卫:beforeResolve
    路由钩子:beforeEnter,beforeLeave
    路由组件钩子:beforeRouteEnter beforeRouteUpdate beforeRouteLeave
  2. 动态路由
    /:id
    this.$route.params.id
  3. 路由组件
    router-view
    router-link
  4. 路由跳转
    router.push()
    router.replace()
    router.go()
  5. 路由模式
    hash(默认):基于onhashchange事件
    history(需要后端支持): h5的replaceState和pushState

vuex

vue中管理数据的仓库
state,getter,mutation(同步,dispatch),action(异步,commit),module
应用场景:跨组件通信

vue中的组件传值方式

props
emit,on
event bus
$refs:获取vue实例,可以调用其中的方法和属性

vue中注册组件的方法
vue.extend()
vue.component()

vue中static和assets的区别
static:webpack不会打包里面的文件,必须使用绝对路径引用里面的文件
assets:webpack会打包里面的文件,重新编译

React

使用

  1. 项目中使用react全家桶: Create React App
  2. 服务端渲染react:next.js
  3. ……

Class组件

函数组件

没有this,不能读取this.state
常用作展示类的组件

render prop

高阶组件

新特性(react16.8):hook

加强版的函数组件,在函数组件中,需要什么功能就添加对应的hook,将对应的功能勾进来,可以用官方提供的一些钩子,也可以自定义钩子

定义:Hook是一个特殊的函数,它可以让你勾入react的特性
使用场景: 当在编写函数式组件并且意识到需要向其添加一些state,以前是需要将其转换成class,现在可以使用hook
使用规则:不要在循环、条件、嵌套中使用;仅在react的函数组件中和自定义hook中调用

useState Hook/State Hook


定义一个state变量
使用方法:
const [count, setCount] = useState(0)
参数:初始state的值
返回值:返回当前的state和setState,需成对获取

useEffect Hook/Effect Hook


数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用,可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
React 组件中有两种常见副作用操作:需要清除的(例如定时器,订阅好友的在线状态)和不需要清除的,前者只需要将清除的函数通过return返回回去,react将会自动的在组件卸载时调用。注意:组件每次重新渲染的时候也会调用清除函数,也正是这样规避了使用class时没有在componentDidMount的生命周期函数中处理而出现的bug。
使用:
useEffect(() => {} [, state]),第二个可选参数为state,用于在react重新渲染之前比较前后两次的state的值是否一样,如果相同,则跳过此次操作,否则就会重新渲染。当第二个参数为[]时,react只会执行一次effect(仅在组件挂载和卸载时使用)
好处:
关注点分离,即useEffect()允许用户以代码的用途来分割它们,而不是通过生命周期函数来分割它们

useReducer


自定义hooks


用于共享组件之间的逻辑
只需要函数名为use开头

虚拟dom diff算法
生态完整
属于MVC框架中的view层,数据单向流动
共享组件之间的状态逻辑: render props 和高阶组件
生命周期:
componentWillMount() componentDidMount()
componentWillReceiveProps() shouldComponentUpdate() componentDidUpdate()
componentWillUnmount()

react-router

安装:react-router-dom
模式:history:BrowserRouter,hash:HashRouter
主要的组件有:
BrowserRouter、HashRouter: 包裹路由组件,在最底层
Switch、Route:匹配路由的组件
Link、NavLink:导航组件,两者都会渲染成a标签,NavLink可以设置activeClassName,当导航到这个组件时,就会应用这个样式
Redirect:重定向

搭建react全家桶单页应用步骤

初始化一个项目:npm init
创建项目文件夹,配置webpack
配置入口页面
在入口页面添加路由(react-router-dom)

Axios

基于promise的HTTP库,可以在浏览器中和nodejs中使用
特性:

  1. 在浏览器中创建XmlHttpRequest;
  2. 在node.js中创建http请求;
  3. 支持promise API
  4. 拦截请求和响应
  5. 取消请求
  6. 自动转换JSON数据
  7. 客户端支持防御XSRF

拦截器

axios.interceptor.response/request
修改请求头的一些配置
添加请求中的加载动画
添加参数

常用方法

axios(config)
axios(url[,config])

请求方法的别名

axios.get()
axios.post()

封装

  1. 安装并引用axios
  2. 创建axios的实例:axios.create(配置对象)
  3. 请求拦截:axios.interceptors.request.use(),处理请求数据
  4. 响应拦截:axios.interceptors.response.use(),处理数据,超时
  5. get请求封装,设置content-type(form-data或application/json)
  6. post请求封装,设置content-type(form-data或application/json)
  7. 暴露模块

Fetch

Webpack

webpack面试:https://juejin.cn/post/6844904094281236487#heading-0

模块打包工具,可压缩,优化代码格式
支持大部分模块规范:COMMONJS、AMD(require.js)、CMD(sea.js)、ES6模块

核心

entry:一个或多个入口,一般是js文件,通过递归找到依赖文件,多入口文件的entry通过键值对的形式输入

output:出口文件及位置

loader:webpack本身只能解析js/json文件,loader使webpack可以解析打包其他格式的文件,在module.rules中配置,test指定匹配规则,use指定使用的loader

常见的loader
css-loader: 支持.css文件的加载和解析,转换成commonjs对象
style-loader: 将样式通过style标签插入到head中
url-loader: 内部使用了file-loader,可以自动将较小文件打包成base64
file-loader: 图片、字体的打包
raw-loader: 将文件以字符串的形式导入(base64)
babel-loader: 转换es5以上版本的新特性语法
vue-loader
postcss-loader
image-loader
sass-loader/less-loader: 将sass/less文件转换成css文件
sass-resources-loader: 提取sass中的全局变量
ts-loader: 将typeScript转换成js
thread-loader: 多进程打包css和js

plugin:插件,扩展功能,资源管理,环境变量注入,作用于整个构建过程,放到plugins数组中

常见的plugin
define-plugin:定义环境变量
commons-chunk-plugin:提取公共代码
clean-webpack-plugin:清理构建目录
extract-text-webpack-plugin:将css从bundle文件里提取成一个独立的css文件
mini-css-extract-plugin:将css提取成一个单独的css文件,支持webpack4,使用link标签的方式插入到HTML中
copy-webpack-plugin:将文件或文件夹拷贝到构建的输出目录
html-webpack-plugin:创建html文件去承载输出的bundle
uglify-js-webpack-plugin:压缩混淆js代码
zip-webpack-plugin:将打包的资源生成zip文件
web-webpack-plugin:为单页应用输出html,比前者好用
hot-module-replacement-plugin:热更新,webpack自带,在devServer中配置hot:true会自动引入这个插件,具体作用:将HMR runtime注入到webpack打包的bundle.js中,使其可以与HMR server建立websocket通信,从而实现热更新

mode(webpack4新增字段)
可选值

  1. development: 设置process.env.NODE_ENV: development,开启NamedChunksPlugin和NamedModulesPlugin
  2. production: 设置process.env.NODE_ENV: production,开启FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin, TerserPlugin
  3. none: 不开启任何优化项

webpack中的文件监听

  1. webpack --watch 需要手动刷新浏览器
  2. 在webpack.config.js中设置watch:true
    原理:轮询(poll)判断文件的最后编辑时间是否变化,某个文件发生了变化并不会立刻告诉监听者,而是先缓存起来,等待一段时间(aggregateTimeout:默认300ms)再告诉监听者,可以设置ignored(不监听的文件或文件夹)来提高性能
module.exports = {
  watch: true, // 默认false
  watchOptions: {
    ignored: /node_modules/, 
    aggregateTimeout: 300,
    poll: 1000
  }
}
  1. 热更新WDS(webpack-dev-server)
    wds不输出文件,不刷新浏览器(可添加--open),是放在内存中,一般配合HotModuleReplacementPlugin(webpack自带)使用,在webpack-dev-server中配置hot:true,会自动引入这个plugin
  2. 使用webpack-dev-middleware
    将webpack打包的文件传输给服务器,适用于灵活的定制场景
    const express = require("express");
    const webpack = require("webpack");
    const webpackDevMiddleware = require("webpack-dev-middleware");
    
    const app = express();
    const config = require('./webpack.config');
    const compiler = webpack(config);
    
    app.use(webpackDevMiddleware(compiler, {
      publicPath: config.output.publicPath
    }))
    
    app.listen(3000, function() {
      console.log('Example app is listening on port 3000!\n')
    })
    

热更新原理分析:
HMR runtime
HMR server
Bundle server
websocket

使用webpack优化前端代码

  1. 代码压缩
    压缩js:UglifyJsWebpackPlugin(webpack4内置)和ParallelUglifyPlugin
    压缩css:OptimizeCssAssetsWebpackPlugin,同时使用cssnano(css预处理器)
    压缩html: htmlWebpackPlugin, 设置压缩参数minify
  2. 自动清理构建目录
    clean-webpack-plugin
  3. 增强css
    自动补全浏览器前缀:autoprefixer插件(后置处理器),配合postcss-loader使用
    px自动转化成rem: px2rem-loader配合手淘的lib-flexible库(自动计算根元素font-size的值)
  4. 静态资源内联

意义:
a. 页面框架初始化脚本
b. 上报相关打点
c. css内联避免页面闪动
d. 减少http网络请求数(小图片或字体内联:url-loader)

 html和js的内联:使用raw-loader   

a. html: ${require('raw-loader!babel-loader!./metal.html')}
b. js: <script>${require('raw-loader!babel-loader!../node_modules/lib-flexible.js')}</script$>
c. css: 借助style-loader或者使用html-inline-css-webpack-plugin

  1. 多页面打包
    多页面: 每次页面跳转的时候,后台服务器都会返回一个新的html文档
    页面解耦,利于seo
    webpack中的实现: 利用glob.sync,配合html-webpack-plugin插件
    entry; glob.sync(path.join(__dirname, './src/*/index.js'))

  2. source map
    通过source map 定位到源代码,开发环境开启,生产环境关闭
    关键字:
    eval: 使用eval包裹代码块
    source map: 产生.map文件
    cheap: 不含列信息
    inline: 将.map作为DataURI嵌入,不单独生产.map文件
    module: 包含loader的source map

  3. 提取页面公共资源
    基础库的分离:
    将react、react-dom通过cdn引入,不打入bundle中
    使用html-webpack-externals-plugin
    或者:
    使用splitChunksPlugin(webpack4内置,代替CommonsChunkPlugin)进行公共脚本分离
    sass-resource-loader

  4. 摇树优化(tree shaking)
    概念: 一个模块中可能有多个方法,只要某个方法用到了,整个模块都会被引入进来,tree shaking则是将用到的方法打包,其他的方法会在uglify的阶段擦除掉
    webpack默认支持,在.babelrc里面设置modules: false
    当mode设置为production的时候默认开启
    要求: 必须是es6的语法,cjs不支持
    es6模块特点:
    a. 只能作为模块顶层的语句出现
    b. import 的模块名只能是字符串常量
    c. import binding是 immutable(不可变的)

  5. scope hoisting(作用域提升)
    webpack打包分析
    a. 打包出来是一个IIFE(匿名闭包)
    b. modules是一个数组,每一项是一个模块初始化函数
    c. __webpack_require用来加载模块,返回module.exports
    d. 通过WEBPACK_REQUIRE_METHOD(0)启动程序
    scope hoisting可以减少函数声明代码和内存开销
    webpack4中mode设为production会自动引用,同样只支持es6语法,不支持cjs

  6. 代码分割和动态引入
    代码分割:webpack将代码库分割成chunks,当代码需要运行的时候才会被加载
    适用场景:脚本懒加载,使初始脚本更小; 抽离相同代码块到一个共享块
    CommonJS: require.ensure
    ES6: 动态import(目前还没有原生支持,需要babel转换)

  7. 结合Eslint使用
    Airbnb: eslint-config-airbnb, eslint-config-airbnb-base

  8. 打包库和组件
    可通过多种方式引用:CJS/AMD/ES-MODULE
    有压缩版和未压缩版
    示例:

{
  mode: 'production',
  entry: {
    'large-number': './src/index.js',
    'large-number.min': './src/index.js'
  },
  output: {
    filename: '[name].js',
    library: 'largeNumber',
    libraryExport: 'default',
    libraryTarget: 'umd'
  }
}
  1. 服务器端渲染(减少http请求,减少白屏时间,利于seo)
    客服端:
    a. 使用react-dom/server的renderToString将react组件渲染成字符串;
    b. 服务端路由返回对应的模板
    c. 打包出针对服务端的组件
    服务器端渲染会出现的问题:
    浏览器的全局变量(window,document)node端不支持——添加判断语句
    某些组件不兼容——根据打包环境单独打包适配
    不支持fetch——使用isomorphic-fetch或者改用axios
    nodejs无法解析css——将style-loader替换成isomorphic-style-loader或者服务端打包时通过ignore-loader忽略掉css解析
    将打包后的html模板作为服务器端返回的HTML模板,将打包后的react组件通过占位符的形式放入合适位置

  2. 优化构建时命令行显示的日志
    production => stats: errors-only
    development => 在devServer中设置 stats: errors-only
    注意:引入的方法不能有副作用(即相同的输入得到不同的输出,引用了全局变量就会有副作用),否则会失效

webpack构建速度和体积优化

  1. 初级优化: 使用webpack内置的stats
  2. 使用speed-measure-webpack-plugin分析速度
  3. 使用webpack-bundle-analyzer分析体积
  4. 使用高版本的webpack和nodejs,高版本意味着高性能(但也存在问题,酌情使用)
  5. 多进程多实例构建(thread-loader/parallel-webpack/happy-pack(没有维护了推荐第一个))
  6. 多进程并行压缩代码(parallel-uglify-plugin/uglifyJs-webpack-plugin/terser-webpack-plugin/)
  7. 进一步分包:预编译资源模块(DLLPlugin进行分包,DllReferencePlugin对manifest.json)
  8. 缓存:提升二次构建速度(babel-loader/terser-webpack-plugin/cache-loader/hard-source-webpack-plugin)
  9. 缩小构建目标:合理配置resolve
  10. 使用tree-shaking 擦除无用的js和css: purgecss-webpack-plugin配合mini-css-extract-plugin
  11. 图片压缩:配置image-webpack-loader(基于Node库的imagemin或者tinypng API)
  12. 动态polyfill:polyfillServices(根据不同的机型(userAgent)下载不同版本的polyfill)

webpack构建原理分析

  1. 命令行输入指令,npm会让命令行工具进入node_modules.bin目录查找webpack.sh/webpack.cmd文件,进一步执行node_modules\webpack\bin\webpack.js
  2. 分析webpack.js(启用子进程运行命令,安装依赖,判断webpack-cli是否安装)
  3. 分析webpack-cli所做的工作(引入yargs,对命令行进行定制;分析命令行参数,转换参数,组成编译配置项;引用webpack,根据配置项进行编译和构建)
  4. 分析webpack-cli源码(处理命令行参数,生成最终配置项options,实例化webpack对象,执行构建流程)

手写loader

本质:导出为函数的JavaScript模块,函数接收资源,返回处理后的资源
执行顺序:从右往左串行执行(函数式编程中的compose组合方式)
开发和调试:使用loader-runner,允许在不安装webpack的情况下运行loader
辅助功能:loader-utils

手写插件

本质:导出一个类,包含有apply()和hooks

文件指纹

打包后输出文件名的后缀,用于版本管理,发布时仅需要发布更改过的内容,没有更改过的使用缓存
生成:
hash: 只要项目有改动,hash就会变
contentHash:根据文件内容定义,文件内容变动,他就会变,常见的场景:一个js文件引用了css文件,css文件一般使用contentHash
chunkHash:不同的entry会有不同的chunkHash,chunk内容改变,他就会变

占位符名称:
[ext]: 资源后缀名
[name]: 文件名
[path]: 文件的相对路径
[folder]: 文件夹
[contenthash]: hash值,md5加密,长度值为32位,[chunkhash:8]表示取前8位
[hash]: hash值

js的文件指纹设置:
output.filename: '[name]_[chunkhash:8].js'
css的文件指纹设置:
使用MiniCssExtraPlugin提成单独的文件再设置,与style-loader功能互斥,不能同时使用
new MiniCssExtractPlugin({
filename: [filename]_[contenthash:8].css
})
图片的文件指纹设置:在file-loader中设置
loader: "file-loader",
options: {
name: 'img/[name][hash:8].[ext]'
}

名词解释:
bundle:打包的文件
chunk:代码块,由多个模块组成
module:模块,一个文件

AST

抽象语法树(Abstract Syntax Tree),源代码的抽象语法结构的树状表现形式

babel

presets

每一项都对应一部分功能的集合

plugins

每一项都对应一个功能

微信公众号

openId

当用户关注了公众号,公众号可获得该用户的openId(加密的微信号),不同的公众号openId不同,

unionId

同一个开放平台账号下的微信公众号和应用(app)对应每个用户都有个共同的unionId

网页授权(scope参数)

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

参数 是否必须 说明
appid 公众号的唯一标识
redirect_uri 授权后重定向的回调链接地址,用urlEcode进行编码
response_type code
scope 应用授权作用域
state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
#wechat_redirect 返无论直接打开还是做页面302重定向时候,必须带此参数回国家地区语言版本
  1. 静默授权 snsapi_base 不弹出授权页面,直接跳转,只能获取用户openid
  2. 非静默授权 snsapi_userinfo 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息

用户授权后跳转页面

redirect_uri/?code=CODE&state=STATE。

调用凭证

当用户授权给公众号之后,公众号可以获得一个网页授权特有的access_token,后期可通过这个access_token进行接口调用(获取用户基本信息)

相关接口

获取用户基本信息(openId和unionId通用)

GET https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
get请求 https协议

参数 是否必须 说明
access_token 调用接口凭证
openId 普通用户的标识,对当前公众号唯一
lang 返回国家地区语言版本
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容