整理了一些面试官常问的面试题

11,194 阅读22分钟

面试题整理

数组篇

数组去重有哪些方法,那原生js使用for循环如何实现的

1.利用ES6中的 Set 方法去重 ( Array.from是将set对象转为数组)

1      let arr = [1,0,0,2,9,8,3,1];
2           function unique(arr) {
3                 return Array.from(new Set(arr))
4           }
5           console.log(unique(arr));   // [1,0,2,9,8,3]
  or
6      console.log(...new Set(arr)); // [1,0,2,9,8,3]

2.利用数组的indexOf方法去重

注:array.indexOf(item,statt) 返回数组中某个指定的元素的位置,没有则返回-1

 1      var arr =[1,-5,-4,0,-4,7,7,3];
 2                 function unique(arr){
 3                    var arr1 = [];       // 新建一个数组来存放arr中的值
 4                    for(var i=0,len=arr.length;i<len;i++){
 5                        if(arr1.indexOf(arr[i]) === -1){
 6                            arr1.push(arr[i]);
 7                        }
 8                    }
 9                    return arr1;
10                 }
11                 console.log(unique(arr));    // 1, -5, -4, 0, 7, 3

3.利用数组的sort方法去重(相邻元素对比法)

注:array.sort( function ) 参数必须是函数,可选,默认升序

        var arr =  [5,7,1,8,1,8,3,4,9,7];
                function unique( arr ){
                    arr = arr.sort();
                    console.log(arr);
​
                    var arr1 = [arr[0]];
                    for(var i=1,len=arr.length;i<len;i++){
                        if(arr[i] !== arr[i-1]){
                            arr1.push(arr[i]);
                        }
                    }
                    return arr1;
                }
                console.log(unique(arr))l;   //  1, 1, 3, 4, 5, 7, 7, 8, 8, 9

4.利用数组的includes去重

注:arr.includes(指定元素(必填),指定索引值(可选,默认值为0) ),有值返回true,没有则返回false

 1          var arr = [-1,0,8,-3,-1,5,5,7];
 2                 function unique( arr ){
 3                     var arr1 = [];
 4                     for(var i=0,len=arr.length;i<len;i++){
 5                         if( !arr1.includes( arr[i] ) ){      // 检索arr1中是否含有arr中的值
 6                             arr1.push(arr[i]);
 7                         }
 8                     }
 9                     return arr1;
10                 }
11                 console.log(unique(arr));      //  -1, 0, 8, -3, 5, 7

5.利用数组的filter方法去重

注:filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,array.filter(function(currentValue,index,arr), thisValue)

    currentValue:当前元素的值(必选参数)、index:当前元素的索引值(可选)、arr:当前元素属于的数组对象(可选)、thisValue:对象作为该执行回调时使用,传递给函数,用作 "this" 的值,默认undefined(可选)

 1              var arr = [1,2,8,9,5,8,4,0,4];
 2                 /*
 3                     模拟: 原始数组:[1,2,8,9,5,8,4,0,4]
 4                             索引值:0,1,2,3,4,5,6,7,8
 5                           伪新数组:[1,2,8,9,5,8,4,0,4]
 6                     使用indexOf方法找到数组中的元素在元素在中第一次出现的索引值
 7                             索引值:0,1,2,3,4,2,6,7,6
 8                         返回前后索引值相同的元素:
 9                           新数组:[1,2,8,9,5,4,0]        
10                 */
11                 function unique( arr ){
12                     // 如果新数组的当前元素的索引值 == 该元素在原始数组中的第一个索引,则返回当前元素
13                     return arr.filter(function(item,index){
14                         return arr.indexOf(item,0) === index;
15                     });
16                 }
17                 console.log(unique(arr));    //  1, 2, 8, 9, 5, 4, 0

6.利用函数递归去重

 1           var arr = [1,1,5,6,0,9,3,0,6]
 2                  function unique( arr ){
 3                      var arr1 = arr;
 4                       var len = arr1.length;
 5                       arr1.sort((a,b)=>{
 6                           return a-b
 7                       })
 8                       function loop(index){
 9                           if(index >= 1){
10                              if(arr1[index] === arr1[index-1] ){
11                                  arr1.splice(index,1);
12                             }
13                              loop(index - 1);  // 递归loop,然后数组去重
14                         }
15                     }
16                     loop(len-1);   
17                      return arr1
18                  }
19                  console.log(unique(arr));    //   0, 1, 3, 5, 6, 9

7.利用ES6中的Map方法去重

 1              /*
 2                   创建一个空Map数据结构,遍历需要去重的数组,把数组的每一个元素作为key存到Map中。由于Map中不会出现相同的key值,所以最终得到的就是去重后的结果。
 3                 */
 4      let arr = [1, 0, 8, 3, -9, 1, 0, -9, 7]
 5            function unique(arr) {
 6                 let map = new Map();
 7                 console.log(map)
 8                 //let arr1 = new Array();      // 数组用于返回结果
 9                 let arr1 = []
10                     for (let i = 0, len = arr.length; i < len; i++) {
11                         if (map.has(arr[i])) {      // 判断是否存在该key值
12                             map.set(arr[i], true);
13                         }
14                         else {
15                             map.set(arr[i], false);
16                             arr1.push(arr[i]);
17                         }
18                     }
19                     return arr1;
20                 }
21          console.log(unique(arr)); // 1, 0, 8, 3, -9, 7

8.双重for循环+splice()

let arr = [1, 5, 3, 3, 5]
for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < i + arr.length; j++) {
        if (arr[i] === arr[j]) {
          // 第一个等同于第二个,splice方法删除第二个
            arr.splice(j, 1)
            j--
        }
    }
​
}
console.log(arr);  // [1, 5, 3]

9.js双重for循环

var arr=[1,2,2,3,5,4,5]
let result=[]
for(let i=0;i<arr.length;i++){
  // console.log(i); 每个数的下标
  for(var j=0;j<result.length;j++){
    // 如果arr的下标的数与result的数相等,那就退出循环
    if(arr[i]===result[j]){
      break
    }
  }
  console.log(j);
  // 如果j===result.length相等,就把对应的元素添加到result数组里
  if(j===result.length){
    result.push(arr[i])
  }
}
console.log(result); // [ 1, 2, 3, 5, 4 ]

Set / Map的区别

Set类似于数组,但是它里面每一项的值是唯一的,没有重复的值,Set是一个构造函数,用来生成set的数据结构 .数组去重(利用扩展运算符)

Map对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数Map可以接受一个数组作为参数,数组的成员是一个个表示键值对的数组。注意Map里面也不可以放重复的项。

综上所述,主要有一下几个区别

1.Map是键值对,Set是值的集合,当然键和值可以是任何的值

2.Map可以通过get方法获取值,而set不能因为它只有值;

3.都能通过迭代器进行for…of遍历;

4.Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储

5.map和set都是stl中的关联容器,map以键值对的形式存储,key=value组成pair,是一组映射关系。set只有值,可以认为只有一个数据,并且set中元素不可以重复且自动排序。

ES6的理解

0.ES6是ECMAScript 6的缩写简称,2015 年 6 月,ECMAScript 6 正式通过,

1.变量声明let与const (1).变量不会提升 (2).有块级作用域

2.解构赋值语法 : 其实就是变量赋值语法的简写形式 (取出 对象的属性 赋值 给变量)

3.箭头函数 其实是 function 关键字的简写形式

4.拓展运算符: ... ( 作用:类似于 对象遍历的一种简写形式 )

5.数组迭代方法

数组排序

1.sort():对数组的元素进行从小到大来排序(会改变原来的数组) 默认排序顺序是在将元素转换为字符串按照Unicode 编码,从小到大进行排序

//一维数组排序
var arr=[1,5,7,9,16,2,4];
arr.sort(function(a,b){
    return b-a;  //降序排列,return a-b; —>升序排列
})  //括号里不写回调函数,则默认按照字母逐位升序排列,结果为[1,16,2,4,5,7,9]

2.冒泡排序(每一趟找出最大的)

两个相邻的数比较大小,将两个数中较大的数往右边放,小的往左边放

//性能一般
let arr=[1,5,7,9,16,2,4];
//冒泡排序,每一趟找出最大的,总共比较次数为arr.length-1次,每次的比较次数为arr.length-1次,依次递减
let len = arr.length;
for(let k = 0; k < len - 1; k++) {
    for(let m = 0; m < len - k - 1; m++) {
        if(arr[m] > arr[m+1]){
            let val = arr[m];
            arr[m] = arr[m+1];
            arr[m+1] = val;
        }
    }
}
 
console.log(arr)

3.插入排序

var arr=[45,1,32,21,56,87,43,12,34,45];
    for(var i=0;i<arr.length;i++){
    var n=i;
    while(arr[n]>arr[n+1] && n>=0){
        var temp=arr[n];
        arr[n]=arr[n+1];
        arr[n+1]=temp;
        n--;
    }
}

4.希尔排序(性能最好的排序)

function xier(arr){
    var interval = parseInt(arr.length / 2);  //分组间隔设置
    while(interval > 0){
        for(var i = 0 ; i < arr.length ; i ++){
            var n = i;
            while(arr[n] < arr[n - interval] && n > 0){
                var temp = arr[n];
                arr[n] = arr[n - interval];
                arr[n - interval] = temp;
                n = n - interval;
            }
        }
        interval = parseInt(interval / 2);
    }
    return arr;
}
xier([12,9,38,44,7,98,35,59,49,88,38]);

数组合并说出最少三种方法

1.es6 利用 展开运算符 “…”

2.利用 数组方法 concat,合并两个或多个数组 (会造成内存浪费,不能处理嵌套数组) **

改变原数组

        var arr1 = [0, 1, 2];
        var arr2 = [3, 4, 5];
        arr1 = arr1.concat(arr2)
​

不改变原数组

    var arr1 = [0, 1, 2];
    var arr2 = [3, 4, 5];
    var arr3 = arr1.concat(arr2)

使用扩展操作符或array.concat()执行的合并将创建一个新数组。但是,有时不想创建一个新数组,而是想将它合并到一些现有的数组中。下面的方法执行一种可变的合并方法。

3.使用array.push()方法进行合并

array.push(item)方法将一个项加入到数组的末尾,改变了调用该方法的数组:

const heroes = ['Batman'];
​
heroes.push('Superman');
​
heroes; // ['Batman', 'Superman']

数组 push ,pop , map, unshift?

1.push: 数组尾部添加 push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度

2.pop:数组尾部删除 pop() 方法用于删除并返回数组的最后一个元素

3.unshift数组头部添加 unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度

4.map:map()方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

vue篇

Vue2 中 v-for 和 v-if 为什么不建议放一行?为什么?怎么处理?Vue3 呢

官网回答:vuejs.bootcss.com/style-guide…

为什么 :当 Vue 处理指令时,v-forv-if 具有更高的优先级,这意味着 v-if 将分别重复运行于 每个 v-for 循环中,即先运行 v-for 的循环,然后在每一个 v-for 的循环中,再进行 v-if 的条件对比,而v-if是通过创建和销毁dom元素来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,会造成性能问题,影响速度

处理: 为了避免这个问题,可以用计算属性代替.样只会在users发生改变时才会执行这段遍历的逻辑,和之前相比,避免了不必要的性能浪费。( 或者:在v-for的外层或内层包裹一个元素来使用v-if ) 如果条件出现再循环内部,可通过计算属性 computed 提前过滤掉那些不需要显示的项

好处: 过滤后的列表只会在 users 数组发生相关变化时才被重新运算,过滤更高效。

使用 v-for="user in activeUsers" 之后,我们在渲染的时候只遍历活跃用户,渲染更高效。

解耦渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强。

vue3: vue3中v-if的优先级高于v-for,如果写在同一个标签中会先使用v-if,而v-if的数据来自v-for的循环子项会导致出错,因为数据还未声明.

Vue 中的 data 为什么要求是一个函数?

多个组件复用时,每次调用data函数的时候都会return一个新的对象,它们的内存地址都是不一样的,这样就不会相互影响。为了保证组件的独立性和可复用性

computed 和 watch 的区别

computed:计算属性为了简化计算复杂度,具有缓存不能执行异步代码,必须同步执行,适用于计算比较消耗性能的计算场景、必须要有一个返回值

watch: 侦听器,检测属性值,只要属性值改变,就会触发监听。

echarts怎么挂载

一、安装并全局引入

1、通过npm获取echarts

npm install echarts --save

2、在 main.js 中全局配置

首先在main.js中引入echarts,将其绑定到vue原型上:

  1. import echarts from 'echarts'
  2. Vue.prototype.$echarts = echarts;

3.创建一个dom容器

<div id="main" style="width750pxheight400px"></div>

// 基于准备好的dom,初始化echarts实例

var myChart = echarts.init(document.getElementById("main"));

组件传值

1.父子组件传值:

父--->子 : 通过props 传递,子组件接收

子---->父 : 通过一个自定义事件,然后this.$emit传递给父组件

2.兄弟组件传值 : eventBus事件总线

在全局挂载enentBus,创建一个谁都能找到的事件总线, 通过$emit 和 $on进行传递和接收。

  1. 后代组件传值 : 通过provide 和 inject。爷爷定义一个属性 provide,是一个函数,用来给后代接收。 孙子用 inject 接收。

组件缓存

keep-alive:

语法:两个属性: include 和 exclude

1.先给对应组件设置name属性名,

2.再把名字写到 include/exclude位置

include==包含--缓存

exclude==不包含--不缓存

js篇

闭包

闭包(closure):指有权访问另外一个函数作用域中的变量的函数。简单的说就是,一个作用域可以访问另外一个函数内部的局部变量。函数+上下文

function fn() {
    var num = 10;
    function fun() {
        console.log(num); 
    }
    return fun;
}
var f = fn(); 
f();

作用:延长变量作用域、在函数的外部可以访问函数内部的局部变量,容易造成内层泄露,因为闭包中的局部变量永远不会被回收. 在实际开发中,闭包最大的作用就是用来 变量私有

深拷贝,浅拷贝

浅拷贝:基本数据类型拷贝的值,引用数据类型拷贝的是内存地址。原对象和拷贝对象还是会互相影响。两个对象指向同一内存地址。

如何实现:1.Object.assign

2.数组的 slice 和 concat 方法 Array.prototype.concat(),Array.prototype.slice()

3.扩展运算符

4.函数库Lodash的_.clone方法

深拷贝:深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存,拷贝前后两个对象互不影响

如何实现:

1.一行代码: 用 JSON.stringify 把对象转换成字符串,再用 JSON.parse 把字符串转换成新的对象。 但是这种方式存在弊端,会忽略undefinedsymbol函数 ;NaNInfinity-Infinity 会被序列化为 null;

const newObj = JSON.parse(JSON.stringify(obj))
  1. Lodash.cloneDeep 实现深拷贝
  2. 使用递归的方式实现深拷贝

作用域

作用域链

js 在执⾏过程中会创建一个个的可执⾏上下⽂ 每个可执⾏上下⽂的词法环境中包含了对外部词法环境的引⽤

简而言之: 函数内部 可以访问到 函数外部作用域的变量, 而外部函数还可以访问到全局作用域的变量,

函数内访问变量时,优先使用自己内部声明的变量;如果没有,就访问外部函数作用域的变量,如果也没有,则继续往外找。。直到找到全局。

垃圾回收

垃圾回收:使用完毕,由垃圾回收自动回收不再使用的内存===( 释放不在使用的内存 )

全局变量一般不会回收, 一般局部变量的的值, 不用了, 会被自动回收掉

两种常见的浏览器垃圾回收算法: 引用计数( IE ) 和 标记清除法

标记清除法: 从js根部,如法访问到,无法触及(谁都找不到这块空间),这块空间就是垃圾,需要被回收。

原型链

每一个实例对象上有一个proto属性,指向的构造函数的原型对象,构造函数的原型

对象也是一个对象,也有proto属性,通过这个属性可以访问对象的原型,这样一层一层往上找的过程就形成了原型链。

判断是否是数组

方法一: 使用 toString 方法 :Object.prototype.toString.call(arr)

方法二: 使用 ES6 新增的 Array.isArray 方法 : Array.isArray(arr)

this的指向

1,普通函数,定时器自调用函数中的this指向window.

2.、事件中的this指向事件的调用者 ==谁调用我,我就指向谁.

3.、 构造函数中this和原型对象中的this,都是指向构造函数new 出来实例对象

4 . 箭头函数没有this

Promise

Promise 是异步编程的一种解决方案,它可以解决回调地狱的问题。Promise 是一个对象,可以从该对象获取异步操作的消息。

promise的三个状态: pending(默认) fulfilled(成功) rejected(失败)

如何识别数组和对象

1.通过constructor ==访问构造函数,我们可通过这个属性判断变量的数据类型

2.通过Object.prototype.toString.call()

3.通过instanceof

4.ES5特地新增isArray()检测变量是否是数组

new都做了那些事

1.创建一个空对象

2.this指向这个对象 (指向了构造函数的prototype属性)

3.给这个对象赋值

4.返回这个对象

防抖和节流

1.防抖:指在一段时间内,无论触发了多少次回调,都只执行最后一次。

原理:利用定时器,函数在第一次执行时设定一个定时器,并且通过闭包缓存起来,之后调用时发现已经设定过定时器就清空之前的定时器,并重新设定一个新的定时器,如果存在没有被清空的定时器,当定时器计时结束后触发函数执行。

/*
 * 防抖函数
 * @param fn 事件触发的操作
 * @param delay 多少毫秒内连续触发事件,不会执行
 * @returns {Function}
*/
function debounce(fn,delay) {
    let timer = null; //通过闭包缓存了一个定时器
    return function () {
        const args = [...arguments];
        const that = this
        timer && clearTimeout(timer);
        timer = setTimeout(function () {
            fn.apply(that,args);
        },delay);
    }
}
window.onscroll = debounce(function () {
    let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
    console.log('滚动条位置:' + scrollTop);
},200)
​
​

2.节流:在一定时间间隔内,只执行第一次。函数节流非常适用于函数被频繁调用的场景,例如:window.onresize() 事件、mousemove 事件、上传进度等情况

原理: 实现原理就是通过一个布尔类型变量来判断是否可执行回调,当变量为true时,生成一个定时器,同时将变量取反通过闭包保存起来,当定时器执行完回调后,再将变量变为true,在变量为false期间,调用节流函数不会生成定时器。

/**
 * 节流函数
 * @param fn 事件触发的操作
 * @param delay 间隔多少毫秒需要触发一次事件
 * @returns {Function}
 */
function throttle(fn, delay) {
    let flag = true;
    return function () {
        if (!flag) {
            return;
        }
        const that = this
        const args = [...arguments];
        flag = false;
        setTimeout(() => {
            fn.apply(that, args);
            flag = true;
        }, delay);
    }
}
window.onscroll = debounce(function () {
    let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
    console.log('滚动条位置:' + scrollTop);
},200)
​

使用场景:1.滚动事件 2.输入的模糊搜索 3.轮播图切换 4.点击操作

事件循环机制 Eventloop

JavaScript是一个单线程的脚本语言。异步任务交给浏览器处理,

微任务(Microtasks)、宏任务(task)皆是异步任务,执行顺序如下:

同步任务===>执行单个宏任务===>先执行宏任务里的微任务===>宏任务

宏任务: 1.主线程上的执行栈中所有的代码块

2.setTimeout

3.setInterval

4.Ajax

5.事件

微任务: 1.Promise.then

2.process.nextTick(Node.js 环境)

arguments

1.什么是arguments?

在函数调用的时候,浏览器每次都会传递进两个隐式参数:

一个是函数的上下文对象this,另一个则是封装实参的类数组对象arguments。arguments 是一个类数组对象,是函数实参的集合,是一个伪数组。

2.arguments方法:

一:转数组 Array.from() , [...arguments]

function fun2() {
 let len = arguments.length;
 let newArr = Array.from(arguments);
 newArr.push(1);
 console.log(newArr)
 }
fun2("a", 0, { name: "cao" });
输出:['a',0,{ foo: "Hello, arguments" },1]
​
function fun2() {
 let newArr = [...arguments];
 newArr.push(1)
 console.log(newArr)
}
fun2("a", 0, { name: "cao" });
输出:['a',0,{ foo: "Hello, arguments" },1]

二:调用原型方法: Array.prototype.slice.call(arguments) ,[].slice.call(arguments)

function fun2() {
 let len = arguments.length;
 let newArr = Array.prototype.slice.call(arguments);//用的数组原型
 newArr.push(1)
 console.log(newArr)
}
fun2("a", 0, { name: "cao" });
输出:['a',0,{ foo: "Hello, arguments" },1]
​
function fun2() {
  let len = arguments.length;
  let newArr = [].slice.call(arguments);//用的数组
  newArr.push(1);
  console.log(newArr)
}
fun2("a", 0, { name: "cao" });
输出:['a',0,{ foo: "Hello, arguments" },1]
​

3.箭头函数没有arguments。 使用剩余参数表示法...rest

Html

水平垂直居中

一: 绝对定位和负magin值

二: 绝对定位 + transform

三: flex布局

内联元素居中布局:

  1. 水平居中
  • 行内元素可设置:text-align: center;
  • flex布局设置父元素:display: flex; justify-content: center;
  1. 垂直居中
  • 单行文本父元素确认高度:height === line-height
  • 多行文本父元素确认高度:disaply: table-cell; vertical-align: middle;

块级元素居中布局

  1. 水平居中
  • 定宽: margin: 0 auto;
  • 不定宽: 参考上诉例子中不定宽高例子。
  1. 垂直居中
  • position: absolute设置left、top、margin-left、margin-to(定高);
  • position: fixed设置margin: auto(定高);
  • display: table-cell;
  • transform: translate(x, y);
  • flex(不定高,不定宽);
  • grid(不定高,不定宽),兼容性相对比较差;

绝对定位和相对定位的区别

相对定位: 是相对于它本身所在的文档流的位置进行定位,不会脱离文档流,定位前的位置依然保留。

绝对定位: 相对于它最近的设置过position某些属性(如:relative、obsolute、fixed、sticky )的父级结点来进行定位。如果祖先节点中没有设置,默认相对于浏览器窗口定位。会脱离文档流,文档中不会保留其定位前的位置

  1. relation 相对定位,相对于自己的文档流的位置定位,不会脱离文档流
  2. absolute 绝对定位,相对于具有relative、obsolute、fixed、sticky 的最近的父容器来定位,会脱离文档流
  3. fixed 绝对定位(通常叫做:固定定位),相对于window位置定位,会脱离文档流

http

http的缓存

HTTP 缓存分为 2 种,一种是强缓存,另一种是协商缓存。主要作用是可以加快资源获取速度,提升用户体验,减少网络传输,缓解服务端的压力。

强缓存 (验证缓存是否过期) :(进行判断缓存是否有效, 就是判断资源是否过期, 如果未过期, 直接用缓存)

协商缓存:若未命中强缓存(强缓存过期了),则浏览器会将请求发送至服务器。

服务器根据http头信息中的Last-Modify/If-Modify-SinceEtag/If-None-Match来判断是否命中协商缓存。

如果命中,则http返回码为304 (你本地之前加载的资源是有效的),浏览器从缓存中加载资源。

css

css怎么做动画

css 实现动画的三种方式总结:

  1. transition 过渡
  2. transform 变形 。
  3. animation 关键帧动画

1.transition 过渡动画:

1. 语法:
     1. transition: 属性是个复合属性 。
     2. transition: property duration timing-function delay
     3. 默认值为: transition: all 0 ease 0;
​
2. 属性介绍:
     1. transition-property: 规定设置过渡效果的 css 属性名称 。
     2. transition-duration: 规定完成过渡效果需要多少秒或毫秒 。
     3. transition-timing-function: 指定过渡函数, 规定速度效果的速度曲线 。
     4. transition-delay: 指定开始出现的延迟时间 。
   
3. 子属性详解:
     1. transition-property: none |all |property;
        1. 值为 none 时, 没有属性会获得过渡效果
        2. 值为 all 时, 所有属性都将获得过渡效果
        3. 值为指定的 css 属性应用过渡效果, 多个属性用逗号隔开
        4. css 属性实际使用时的设置:
           1. color: background-color, border-color, color, outline-color ;
           2. length: 真实的数字 如:word-spacing,width,vertical-align,top,right,bottom,left,padding,outline-width,margin,min-width,min-height,max-width,max-height,line-height,height,border-width,border-spacing,
           3. integer: 离散步骤(整个数字), 在真实的数字空间, 以及使用 floor() 转换为整数时发生 如: outline-offset,z-index 。
           4. number: 真实的(浮点型)数值, 如:zoom, opacity, font-weight 。
           5. rectangle: 通过 x, y, width 和 height(转为数值)变换,如: crop 。
           6. visibility: 离散步骤, 在01数字范围之内, 0表示“隐藏”, 1表示完全"显示"; 如: visibility 。
           7. shadow: 作用于 color, x, y 和 blur(模糊)属性; 如:text-shadow 。
           8. background-image: 通过每次停止时的位置和颜色进行变化 。 它们必须有相同的类型(放射状的或是线性的)和相同的停止数值以便执行动画 。
​
     2. transition-duration
        1. transition-duration: time;
        2. 该属性主要用来设置一个属性过渡到另一个属性所需的时间, 也就是从旧属性过渡到新属性花费的时间长度, 俗称持续时间
​
     3. transition-timing-function: linear| ease| ease-in| ease-out| ease-in-out| cubic-bezier(n,n,n,n);
        1. 该属性指的是过渡的 “缓动函数” 。 主要用来指定浏览器的过渡速度, 以及过渡期间的操作进展情况 。
        2.  注意: 值 cubic-bezier(n,n,n,n) 可以定义自己的值, 如 cubic-bezier(0.42,0,0.58,1) 。
        3. 各个子属性详细解析:
                 1. linear: 匀速 (约等于)== cubic-bezier(0,0,1,1) 。
                 2. ease: 慢快慢  (约等于)== cubic-bezier(0.25,0.1,0.25,0.1) 。
                 3. ease-in: 慢速开始的过渡 (约等于)== cubic-bezier(0.45,0.,1,1) 。
                 4. ease-out: 慢速结束的过渡 (约等于)== cubic-bezier(0,0.,0.58,1) 。
                 5. ease-in-out: 慢速开始和结束的过渡 (约等于)== cubic-bezier(0.45,0.,0.58,1) 。
                 6. cubic-bezier(n,n,n,n): 在 cubic-bezier 函数中定义自己的值; 可能的值是0~1之间的数值 。
            
        4. transition-delay
            1. 这个属性没什么说的了, 就是过渡效果开始前的延迟时间, 单位秒或者毫秒
​
​

2.transform 变形

1. 可以利用 transform 功能来实现文字或图像的 旋转、缩放、倾斜、移动 这四种类型的变形处理
     1. 旋转 rotate
            1. 用法: transform: rotate(45deg);
            2. 提供一个参数 “角度”, 单位 deg 为度的意思, 正数为顺时针旋转, 负数为逆时针旋转, 上述代码作用是顺时针旋转45度
​
     2. 缩放 scale
            1. 用法: transform: scale(0.5)  或者  transform: scale(0.5, 2);
            2. 一个参数时: 表示水平和垂直同时缩放该倍率
            3. 两个参数时: 第一个参数指定水平方向的缩放倍率, 第二个参数指定垂直方向的缩放倍率 。
​
     3. 倾斜 skew
            1. 用法: transform: skew(30deg)  或者 transform: skew(30deg, 30deg);
            2. 一个参数时: 表示水平方向的倾斜角度 。
            3. 两个参数时: 第一个参数表示水平方向的倾斜角度, 第二个参数表示垂直方向的倾斜角度 。
            4. skew 的默认原点 transform-origin 是这个物件的中心点
​
     4. 移动 translate
            1. 用法: transform: translate(45px)  或者 transform: translate(45px, 150px);
            2. 一个参数时: 表示水平方向的移动距离;
            3. 两个参数时: 第一个参数表示水平方向的移动距离, 第二个参数表示垂直方向的移动距离 。
​
2. 基准点 transform-origin
     1. 在使用 transform 方法进行文字或图像的变形时, 是以元素的中心点为基准点进行的 。 使用 transform-origin 属性, 可以改变变形的基准点 
     2. 用法: transform-origin: 10px 10px;
     3. 表示相对左上角原点的距离, 单位 px, 第一个参数表示相对左上角原点水平方向的距离, 第二个参数表示相对左上角原点垂直方向的距离;
     4. 两个参数除了可以设置为具体的像素值, 其中第一个参数可以指定为 left、center、right, 第二个参数可以指定为 top、center、bottom。
​
3. 多方法组合变形
     1. 用法: transform: rotate(45deg) scale(0.5) skew(30deg, 30deg) translate(100px, 100px);
     2. 这四种变形方法顺序可以随意, 但不同的顺序导致变形结果不同, 原因是变形的顺序是从左到右依次进行
​
​

3.animation 关键帧动画

1. 在 CSS3 中创建动画, 您需要学习 @keyframes 规则 。
​
2. @keyframes 规则用于创建动画 。 在 @keyframes 中规定某项 CSS 样式, 就能创建由当前样式逐渐改为新样式的动画效果 。
​
3. 必须定义动画的名称和时长 。 如果忽略时长, 则动画不会允许, 因为默认值是 0。
​
4. 请用百分比来规定变化发生的时间, 或用关键词 "from" 和 "to", 等同于 0% 和 100% 。
​
5. 语法: animation: name duration timing-function delay iteration-count direction;
     1. animation-name    规定需要绑定到选择器的 keyframe 名称*
     2. animation-duration   规定动画完成一个周期所花费的秒或毫秒。默认是 0。
     3. animation-timing-function    规定动画的速度曲线。 默认是 "ease"。
        1. linear   动画从头到尾的速度是相同的。
        2. ease 默认。动画以低速开始,然后加快,在结束前变慢。
        3. ease-in  动画以低速开始。
        4. ease-out 动画以低速结束。
        5. ease-in-out  动画以低速开始和结束。
        6. cubic-bezier(n,n,n,n)    在 cubic-bezier 函数中自己的值。可能的值是从 0 到 1 的数值。
     4. animation-delay    规定动画何时开始 。 默认是 0。
     5. animation-iteration-count    规定动画被播放的次数 (from到to算作一次, to到from算作一次; 注意计算方式) 。
        1. 默认是 1 。
        2. infinite规定动画应该无限次播放。
     6. animation-direction    规定动画是否在下一周期逆向地播放 。 默认是 "normal"; alternate (轮流),。
        1. normal   默认值。动画按正常播放。
        2. reverse  动画反向播放。
        3. alternate    动画在奇数次(1、3、5...)正向播放,在偶数次(2、4、6...)反向播放。
        4. alternate-reverse    动画在奇数次(1、3、5...)反向播放,在偶数次(2、4、6...)正向播放。
        5. initial  设置该属性为它的默认值 。
​
6. 子属性详解
     1. alternate(轮流):
            1. alternate (轮流): 动画播放在第偶数次向前播放, 第奇数次向反方向播放 (animation-iteration-count 取值大于1时设置有效
            2. 语法: animation-direction: alternate;
​
     2. animation-play-state 规定动画是否正在运行或暂停 。 默认是 "running" 播放; paused 暂停播放 。
            1. 语法: animation-play-state: paused;
                                                    
     3. animation-fill-mode   属性规定动画在播放之前或之后, 其动画效果是否可见; 规定对象动画时间之外的状态; none | forwards | backwards | both 。
            1. none:       不改变默认行为 (默认, 回到动画没开始时的状态) 。
            2. forwards:   当动画完成后,保持最后一个属性值(在最后一个关键帧中定义) (动画结束后动画停留在结束状态) 。
            3. backwards:  在 animation-delay 所指定的一段时间内, 在动画显示之前, 应用开始属性值 (在第一个关键帧中定义) (动画回到第一帧的状态)。
            4. both:       向前和向后填充模式都被应用 (根据 animation-direction 轮流应用 forwards 和 backwords 规则)。
            5. 语法:        animation-fill-mode: forwards
               1. 0% 是动画的开始, 100% 是动画的完成。
​
​

移动端rem适配

Vant 中的样式默认使用 px 作为单位,如果需要使用 rem 单位,推荐使用以下两个工具:

一、使用 lib-flexible 动态设置 REM 基准值(html 标签的字体大小)

1、安装

# yarn add amfe-flexible
npm i amfe-flexible

2、然后在 main.js 中加载执行该模块

import 'amfe-flexible'

最后测试:在浏览器中切换不同的手机设备尺寸,观察 html 标签 font-size 的变化。

二、使用 postcss-pxtorempx 转为 rem

1、安装

# yarn add -D postcss-pxtorem
# -D 是 --save-dev 的简写
npm install postcss-pxtorem -D

2、然后在项目根目录中创建 .postcssrc.js 文件

module.exports = {
  plugins: {
    'autoprefixer': {
      browsers: ['Android >= 4.0', 'iOS >= 8']
    },
    'postcss-pxtorem': {
      rootValue: 37.5,
      propList: ['*']
    }
  }
}
​

3、配置完毕,重新启动服务

最后测试:刷新浏览器页面,审查元素的样式查看是否已将 px 转换为 rem

(1)PostCSS 介绍

PostCSS 是一个处理 CSS 的处理工具,本身功能比较单一,它主要负责解析 CSS 代码,再交由插件来进行处理,它的插件体系非常强大,所能进行的操作是多种多样的,例如:

目前 PostCSS 已经有 200 多个功能各异的插件。开发人员也可以根据项目的需要,开发出自己的 PostCSS 插件。