前端面试题

  1. CSS选择器样式优先级

2. CSS实现三列布局(左右固定宽度,中间自适应)

(1)CSS浮动

第一个float:left,第二个float:right,第三个设置margin-left和margin-right

(2)绝对定位法

第一个定位到left,第二个定位到right,第三个设置margin-left和margin-right

(3)flex布局

.left{
  width:200px;
  或者
  flex:0 0 200px;
}
.right{
  width:200px;
  或者
  flex:0 0 200px;
}
.center{
  flex:1;
}

  1. vue双向绑定原理是什么?里面的关键点在哪里?

原理

Vue采用数据劫持结合发布者-订阅者模式的方法,通过Object.defineProperty()来劫持各个属性的setter,getter属性,在数据变动时,通知订阅者,触发更新回调函数,重新渲染视图

关键要素:

  • observer 实现对vue各个属性进行监听

function observer(obj, vm){
      Object.keys(obj).forEach(function(key){
          defineReactive(vm, key, obj[key])
  })
}
// Object.defineProperty改写各个属性
function defineReactive( obj, key, val ) {
    // 每个属性建立个依赖收集对象,get中收集依赖,set中触发依赖,调用更新函数 
    var dep = new Dep();
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function() {
          // 收集依赖  Dep.target标志
          Dep.target && dep.addSub(Dep.target)
          return val
        },
        set: function(newVal){
            if(newVal === val) return
            // 触发依赖
            dep.notify()
            val = newVal
      }
  })
}

-Dep实现

function Dep(){
    this.subs = []
}
Dep.prototype = {
    constructor: Dep,
    addSub: function(sub){
        this.subs.push(sub)
    },
    notify: function(){
        this.subs.forEach(function(sub){
            sub.update() // 调用的Watcher的update方法
      })
    }
}
  • compiler 实现对vue各个指令模板的解析器,生成AST抽象语法树,编译成Virtual Dom,渲染视图

// 编译器
function compiler(node, vm){
    var reg = /\{\{(.*)\}\}/;
    // 节点类型为元素
    if(node.nodeType ===1){
        var attr = node.attributes;
        // 解析属性
        for(var i=0; i< attr.length;i++){
                if(attr[i].nodeName == 'v-model'){
                var  _value = attr[i].nodeValue
                 node.addEventListener('input', function(e){
                    //给相应的data属性赋值,触发修改属性的setter
                    vm[_value] = e.target.value
                })
                node.value = vm[_value] // 将data的值赋值给node
                node.removeAttribute('v-model')
            }
        }
        new Watcher(vm,node,_value,'input')
    }
    // 节点类型为text
    if(node.nodeType ===3){
       if(reg.test(node.nodeValue)){
           var name = RegExp.$1; 
            name = name.trim()
            new Watcher(vm,node,name,'input')
       }
    }
}
  • Watcher 连接observer和compiler,接受每个属性变动的通知,绑定更新函数,更新视图

function Watcher(vm,node,name, nodeType){
    Dep.target = this; // this为watcher实例
    this.name = name
    this.node = node 
    this.vm = vm
    this.nodeType = nodeType
    this.update() // 绑定更新函数
    Dep.target = null //绑定完后注销 标志
}
Watcher.prototype = {
    get: function(){
        this.value = this.vm[this.name] //触发observer中的getter监听
  },
   update: function(){
      this.get()
      if(this.nodeType == 'text'){
        this.node.nodeValue = this.value
      }   
      if(this.nodeType == 'input') {
          this.node.value = this.value
    }
  }
}

完整实现

function Vue(options){
    this.date = options.data
    var data = this.data
    observer(data, this) // 监测
    var id = options.el
    var dom = nodeToFragment(document.getElmentById(id),this) //生成Virtual Dom
  // 编译完成后,生成视图
    document.getElementById(id).appendChild(dom)    
 }
function nodeToFragment(node, vm){
    var flag = document.createDocumentFragment()
    var child
    while(child = node.firstChild){
        compiler(cild, vm)
        flag.appendChild(child)
  }
  return flag
}

// 调用
  var vm = new Vue({
    el: "app",
    data: {
        msg: "hello word"
  }
})
  1. 实现水平垂直居中的方式?

1、利用margin:auto

元素有宽度和高度时,利用margin:auto设置元素水平垂直居中:

HTML代码如下:

<div class="div1">
    <div class="center"></div>
</div>

CSS代码如下:

.div1 {
  background-color: #eee;
  width: 200px;
  height: 200px;
  position: relative;
}
.div1 .center {
  width: 50px;
  height: 50px;
  background-color: forestgreen;
  position: absolute;
  margin: auto;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

2、利用position: absolute

HTML代码:

<div class="div2">
    <div class="center"></div>
</div>

已知元素宽度和高度时,可以设置position: absolutemargin为负的宽高的一半,CSS代码如下:

.div2 {
  background-color: #eee;
  width: 200px;
  height: 200px;
  position: relative;
  margin-top: 20px;
}
.div2 .center {
  width: 50px;
  height: 50px;
  background-color: rgb(34, 71, 139);
  position: absolute;
  left: 50%;
  top: 50%;
  margin-left: -25px;
  margin-top: -25px;
}

已知元素宽度和高度时,也可以利用calc计算属性设置topleft,CSS代码如下:

.div2 {
  background-color: #eee;
  width: 200px;
  height: 200px;
  position: relative;
  margin-top: 20px;
}
.div2 .center {
  width: 50px;
  height: 50px;
  background-color: rgb(34, 71, 139);
  position: absolute;
  left: 50%;
  top: 50%;
  margin-left: -25px;
  margin-top: -25px;
}

已知元素宽度和高度时,也可以利用calc计算属性设置topleft,CSS代码如下:

.center {
  width: 50px;
  height: 50px;
  background-color: rgb(34, 71, 139);
  position: absolute;
  left: calc(50% - 25px);
  top: calc(50% - 25px);
}

元素宽度和高度未知时,可以设置position: absolutetransform: translate(-50%, -50%),CSS代码如下:

.center {
  width: 50px;
  height: 50px;
  background-color: rgb(34, 71, 139);
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

3、弹性盒子

通过为其父元素设置display: flex来实现居中。

HTML代码如下:

<div class="div4">
    <div class="center"></div>
</div>

css代码:

.div4 {
  background-color: #eee;
  width: 200px;
  height: 200px;
  position: relative;
  margin-top: 20px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.div4 .center {
  width: 50px;
  height: 50px;
  background-color: rgb(240, 248, 166);
}

4、利用水平对齐和行高

设置text-alignline-height 实现单行文本水平垂直居中。

HTML代码如下:

<div class="div5">
    <p class="center">我要居中</p>
</div>

css代码:

.div5 {
  background-color: #eee;
  width: 200px;
  height: 200px;
  margin-top: 20px;
}
.div5 .center {
  text-align: center;
  line-height: 200px;
}

5、grid

HTML代码如下:

<div class="div6">
    <p class="center">我要居中</p>
</div>

在网格项目中设置justify-selfalign-self或者margin: auto,CSS代码如下:

.div6 {
  background-color: #eee;
  width: 200px;
  height: 200px;
  margin-top: 20px;
  display: grid;
}
.div6 .center {
  align-self: center;
  justify-self: center;
  /* margin: auto; */
}

在网格容器上设置justify-itemsalign-itemsjustify-contentalign-content,CSS代码如下:

.div6 {
  background-color: #eee;
  width: 200px;
  height: 200px;
  margin-top: 20px;
  display: grid;
  align-items: center;
  justify-items: center;
  /* align-content: center;
  justify-content: center; */
}

  1. 常用的伪元素有哪些?

::before 伪元素可用于在元素内容之前插入一些内容。

h1::before {
  content: url(smiley.gif);
}

::after 伪元素可用于在元素内容之后插入一些内容。

h1::after {
  content: url(smiley.gif);
}

::selection 伪元素匹配用户选择的元素部分。

<!DOCTYPE html>
<html>
<head>
<style>
::-moz-selection { /* 针对 Firefox 的代码 */
  color: red;
  background: yellow;
}

::selection {
  color: red;
  background: blue;
}
</style>
</head>
<body>

<h1>请选择本页中的文本:</h1>

<p>这是一个段落。</p>
<div>这是 div 元素中的文本。</div>

<p><b>注释:</b>Firefox 支持可供替代的 ::-moz-selection 属性。</p>

</body>
</html>

::first-line 伪元素用于向文本的首行添加特殊样式。

<!DOCTYPE html>
<html>
<head>
<style>
p::first-line {
  color: #ff0000;
  font-variant: small-caps;
}
</style>
</head>
<body>

<p>您可以使用 ::first-line 伪元素将特殊效果添加到文本的第一行。一些更多的文字。越来越多,越来越多,越来越多,越来越多,越来越多,越来越多,越来越多,越来越多,越来越多,越来越多。</p>

</body>
</html>

::first-letter 伪元素用于向文本的首字母添加特殊样式。

<!DOCTYPE html>
<html>
<head>
<style>
p::first-letter {
  color: #ff0000;
  font-size: xx-large;
}
</style>
</head>
<body>

<p>您可以使用 ::first-letter 伪元素为文本的第一个字符添加特殊效果!</p>

</body>
</html>
  1. 移动端如何适配不同屏幕尺寸?

1、flex布局:主流的布局方式,不仅适用于移动Web,网页上也表现良好,这也是现在工作中用的最多的布局方式,因此我们的项目尽量采用flex+rem的方式进行布局和完成移动端的适配。

rem是相对长度单位,可以做到一样的取值,在不同尺寸的屏幕上的大小按比例缩放。

rem的定义:rem(font size of the root element)是相对于根元素(即html元素)font-size计算值的倍数。

例如html标签设置font-size:16px,同时div设置width:1.2rem。那么这个div的宽度就是1.2rem=16px*1.2=19.2px。

因此这种方法的适配原理是:根据不同屏幕的宽度,以相同的比例动态修改html的font-size适配,并将px替换成rem,它可以很好的根据根元素的字体大小来进行变化,从而达到各种屏幕基本一直的效果体验。

明白了REM的原理后,我们就可以使用这个特点来进行适应布局了,这也是现在比较主流的移动端web适配方案。

//获得屏幕大小
let htmlwidth = document.documentElement.clientWidth || document.body.clientWidth;  //浏览器兼容
console.log("屏幕宽度:"+htmlwidth) //iphone5:320 iphone6:375
 
//获得html DOM元素
let htmlDom = document.getElementsByTagName('html')[0];
 
//给DOM元素设置样式
htmlDom.style.fontSize = htmlwidth/20 + 'px';  
//以iphone5为基础 iphone5默认字体大小为16px 320/16=20 即1rem字体大小是屏幕宽度的1/20

2、使用js修改rem值的大小

3、使用媒体查询适配

  1. 本地储存有哪些?他们三者有什么区别?

存储三种方式:cookie、sessionStorage、localStorage

cookie可以设置过期时间 路径 domin

1.cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。

2.存储大小限制也不同,

cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,

如会话标识。sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。

3.数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;

localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie在设置的cookie过期时间之前一直有效

即使窗口或浏览器关闭。

  1. JS的数据类型?如何判断js的数据类型?

基本数据类型:string、number、 boolean、 undefined、 null

引用数据类型:object、function

判断js数据类型:

  1. typeof

typeof null ---> "object"

  typeof undefined ---> "undefined"

  typeof true | false ---> 'boolean'

  typeof 42 ---> 'number'

  typeof "42" ---> 'string'

  typeof { name : '1'} | [] ---> 'object'

  typeof Symbol ---> 'symbol'

  typeof ()=>{} ---> 'function'

  typeif void 0 ---> 'undefined'
  1. instanceof

  1. Object.prototype.toString.call()

Object.prototype.toString.call(null) ---> [object Null]

  Object.prototupe.toString.call(undefined) ---> [object Undefined]

  Object.prototype.toString.call(123) ---> [object Number]

  Object.prototype.toString.call(true) ---> [object Boolean]

  Object.prototype.toString.call('123') ---> [object String]

  Object.prototype.toString.call({}) ---> [object Object]

  Object.prototype.toString.call([]) ---> [object Array]

  Object.prototype.toString.call(Math) ---> [object Math]

  Object.prototype.toString.call(function(){}) ---> [object Function]

  Objdec.prototype.toString.call(new Date) ---> [object Date]

  Object.prototype.toString.call(Symbol()) ---> [object Symbol]

4.constructor 判断对象的构造函数

1. null 是js 原型链的起点,没有构造函数

  2. undefined 没有构造函数

  3. [].constructor === Array ---> true

  4. [string].constructor === String

  5. [object].constructor === object

  6. [number].constructor === Number

  7. [symbol].constructor === Symbol

  8. [function].constructor === Function

  9. [new Date].constructor === Date

  10. [RegExp].constructor === RegExp
  1. let、const、var三者有哪些区别?

  1. vue的生命周期有哪些?说一下它们每个阶段做什么操作?

⽣命周期就是vue实例从创建到销毁的整个过程我们把它称之为vue的生命周期,

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期的函数,这给了用户在不同阶段添加自己的代码的机会。

1. beforeCreate(创建前)

2. created (创建后)

3. beforeMount (载入前)

4. mounted (载入后)

5. beforeUpdate (更新前)

6. updated (更新后)

7. beforeDestroy( 销毁前)

8. destroyed (销毁后)

Vue生命周期函数就是vue实例在某一个时间点会自动执行的函数

当Vue对象创建之前触发的函数(beforeCreate)

Vue对象创建完成触发的函数(Created)

当Vue对象开始挂载数据的时候触发的函数(beforeMount)

当Vue对象挂载数据的完成的时候触发的函数(Mounted)

Vue对象中的data数据发生改变之前触发的函数 (beforeUpdate)

Vue对象中的data数据发生改变完成触发的函数(Updated)

Vue对象销毁之前触发的函数 (beforeDestroy)

Vue对象销毁完成触发的函数(Destroy)

vue生命周期的作用是什么?

Vue 所有的功能的实现都是围绕其生命周期进行的,在生命周期的不同阶段调用对应的钩子函数可以实现组件数据管理和DOM渲染两大重要功能。

Vue每个生命周期的详细介绍:

beforeCreate(){}:Vue创建前,此阶段为实例初始化之后,this指向创建的实例,数据观察,数据监听事件机制都未形成,不能获得DOM节点。data,computed,watch,methods 上的方法和数据均不能访问,注:date和methods的数据都还未初始化。

Created(){}: Vue创建后,此阶段为实例初始化之后,data、props、computed的初始化导入完成, 注:要调用methods中的方法,或者操作data中的数据,最早只能在Created中操作

能访问 data computed watch methods 上的方法和数据,初始化完成时的事件写这个里面,

此阶段还未挂载DOM。

beforeMount(){}: Vue载入前,阶段执行时, 模板已经在内存中编译好了,但是未挂载到页面中,(页面还是旧的)

注:这个阶段是过渡性的,一般一个项目只能用到一两次。

Mounted(){}:Vue载入后,(完成创建vm.$el,和双向绑定); 只要执行完mounted,就表示整个Vue实例已经初始化完成了,此时组件已经脱离里了创建阶段, 进入到了运行阶段。

beforeUpdate(){}:Vue更新前, 当执行beforeUpdate的时候,页面中显示的数据还是旧的,此时date数据是最新的,页面尚未和最新数据数据保持同步。但是DOM中的数据会改变,这是vue双向数据绑定的作用,可在更新前访问现有的DOM,如手动移出添加的事件监听器。

Updated(){}:Vue更新后, Updated执行时数据已经保持同步了,都是最新的,

完成虚拟DOM的重新渲染和打补丁。

组件DOM已完成更新,可执行依赖的DOM操作。

注意:不要在此函数中操作数据(修改属性),否则就会陷入死循环。

beforeDestroy(){}:(Vue销毁前,可做一些删除提示,比如:您确定删除****吗?)

当执行beforeDestroy的时候,Vue实例就已经从运行阶段进入到销毁阶段了。实例上的所有date和methods以及过滤器和指令都是处于可用状态,此时还没有真正的执行销毁过程。

Destroyed(){}:Vue销毁后, 当执行到destroted函数的时候,组件已经完全销毁(渣都不剩),此时组件中的所有的数据,方法,指令,过滤器...都已经销毁(不可用了)。

————————————————

★用的最多的是created,用它来发送http请求还可以获取本地存储里的数据

★created()最早操作Data数据的钩子函数,mounted()最早操作Dom元素的钩子函数。

★created和mounted的区别是created执行的时机更早,mounted 可以获取到 dom 元素,如果想在created中获取到dom元素的话可以使用nextTick

父子组件执行的顺序:

父组件 beforeCreate

父组件 created

父组件 beforeMount

子组件 beforeCreate

子组件 created

子组件 beforeMount

子组件 mounted

父组件 mounted

  1. 组件通讯方式有哪些?

  1. 父组件向子组件传递数据 props

父组件通过 props 向子组件传递数据,子组件通过 $emit 和父组件通信

props的特点:

  • props只能是父组件向子组件进行传值,props使得父子组件之间形成一个单向的下行绑定。子组件的数据会随着父组件的更新而响应式更新。

代码演示

父组件:

// 父组件
<template>
    <div id="father">
        <son :msg="msgData" :fn="myFunction"></son>
    </div>
</template>
 
<script>
import son from "./son.vue";
export default {
    name: father,
    data() {
        msgData: "父组件数据";
    },
    methods: {
        myFunction() {
            console.log("vue");
        }
    },
    components: {
        son
    }
};
</script>

子组件:

// 子组件
<template>
    <div id="son">
        <p>{{msg}}</p>
        <button @click="fn">按钮</button>
    </div>
</template>
<script>
export default {
    name: "son",
    props: ["msg", "fn"]
};
</script>

  1. 子组件向父组件传递数据($emit的用法)

$emit的特点:

  • $emit 绑定一个自定义事件,当这个事件被执行的时候就会将参数传递给父组件,而父组件通过v-on监听并接收参数

用法:

父组件:

// 父组件
<template>
  <div class="section">
    <com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article>
    <p>{{currentIndex}}</p>
  </div>
</template>
 
<script>
import comArticle from './test/article.vue'
export default {
  name: 'comArticle',
  components: { comArticle },
  data() {
    return {
      currentIndex: -1,
      articleList: ['红楼梦', '西游记', '三国演义']
    }
  },
  methods: {
    onEmitIndex(idx) {
      this.currentIndex = idx
    }
  }
}
</script>

子组件:

//子组件
<template>
  <div>
    <div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{{item}}</div>
  </div>
</template>
 
<script>
export default {
  props: ['articles'],
  methods: {
    emitIndex(index) {
      this.$emit('onEmitIndex', index) // 触发父组件的方法,并传递参数index
    }
  }
}
</script>
  1. 兄弟组件通信

兄弟组件通信可以借助父组件交互

A组件 B组件

1. A组件数据传递给共同的父组件 再有父组件接受后传递给B组件

2. 通过 $parent + $refs以父组件为中间人来获取到兄弟组件,也可以进行通信。

  1. ref / $refs

这种方式也是实现父子组件之间的通信

ref:这个属性用在子组件上,它的用用就指向了子组件的实例,可以通过实例来访问组件的数据和方法

用法:

在子组件中:

export default {
  data () {
    return {
      name: 'JavaScript'
    }
  },
  methods: {
    sayHello () {
      console.log('hello')
    }
  }
}

父组件:

<template>
调用子组件使用ref 获取子组件实例
  <child ref="child"></child>
</template>
<script>
  import child from './child.vue'
  export default {
    components: { child },
    mounted () {
      console.log(this.$refs.child.name);  // JavaScript 获取子组件数据
      this.$refs.child.sayHello();  // hello  调用子组件的方法
    }
  }
</script>
  1. eventBus事件总线($emit / $on)

eventBus事件总线适用于父子组件、非父子组件等之间的通信,使用步骤如下:

vue事件总线(eventBus),通过创建一个空的vue实例作为全局事件总线,用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件

(1)创建事件中心管理组件之间的通信

// main.js
Vue.prototype.$bus = new Vue()
// child2
    this.$bus.$emit('bus', 'bus传值,组件2传过来的')
// child2
    this.$bus.$on('bus', (msg)=> {
      console.log('msg', msg)
    })

(2)发送事件 假设有两个组件home About

(3)接收事件

6

  1. vuex

  1. Vuex有几个属性以及作用?

Vuex的五个核心概念:state、getters、mutations、actions、modules

1、state: vuex的基本数据,用来存储变量;

用法:可以通过this.$store.state 获得Vuex的state

//    src/store/index
const store = new Vuex.Store({
    state: {
        number:66
    }
})
const app = new Vue({
    //..
    store,
    computed: {
        count: function(){
            return this.$store.state.number
        }
    },
    // this.$store.state.number
})
 
// 每当 store.state.number 发生变化, 都会重新求取计算属性,并且触发更新相关联的 DOM。

mapState辅助函数

// 在需要使用的文件里
import { mapState } from 'vuex'
 
export default {
  // ...
  methods:{
 ...mapState({ myNumber: 'number' }),
} 
}    

2、getters: 从基本数据(state)派生的数据,相当于state的计算属性;

用法:this.$store.grtters.sidber , this.$store.grtters.device ,以此类推

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
 
 
Vue.use(Vuex)

 const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token,
  username: state => state.user.username,
  empno: state => state.user.empno
}
const store = new Vuex.Store({
  getters
})
 
export default store

mapGetters辅助函数

import { mapGetters } from 'vuex'
// mapGetters的作用:把getters映射为计算属性
computed: {
    ...mapGetters(['getPartList']),
    // ...mapGetters({
    //   calcList: 'getPartList'
    // }),
    // calcList () {
    //   // 注意:获取getters的值,不需要加括号(当属性使用)
    //   return this.$store.getters.getPartList
    // },
}

3、mutations: 提交mutation是更改Vuex中的store中的状态的唯一方法。mutation必须是同步的,如果要异步需要使用action。

每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数。(提交荷载在大多数情况下应该是一个对象),提交荷载也可以省略的。

const store = new Vuex.Store({
  state: {
    number: 1
  },
  mutations: {
    //无提交载荷
    Submit(state) {
        state.number++
    }
    //提交载荷
    SubmitN(state, payload) {
      state.number += payload.num
    }
  }
})

使用方法:

你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 Submit 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:

//无提交载荷
this.$store.commit('Submit')
//提交载荷
this.$store.commit('SubmitN', {
    num: 66
    })

mapMutations 辅助函数

与其他辅助函数类似,你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用。

// 在需要使用的文件里
import { mapMutations } from 'vuex'
 
export default {
    methods:{
    ...mapMutations({ mySubmit: 'Submit', mySubmitN: 'SubmitN'}),
}

4、action: 和mutation的功能大致相同,不同之处在于 ①Action提交的是mutation,而不是直接变更状态,②Action可以包含任意异步操作。

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。

  • Action 可以包含任意异步操作。

const store = new Vuex.Store({
  state: {
    number: 0
  },
  mutations: {
    submit (state) {
      state.number++
    }
  },
  actions: {
    submit (context) {
      setInterval(function(){
        context.commit('submit')
      }, 1000)
    }
  }
})

注意:Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。

使用方法:

分发actions,即Action 通过 store.dispatch 方法触发:

this.$store.dispatch('increment')

mapActions辅助函数

你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用

import { mapActions } from 'vuex'
 
export default {
  //..
  methods: {
    ...mapActions([
      'submit' 
    ]),
    ...mapActions({
      add: 'submit' 
    })
  }
}

5、modules: 模块化vuex,可以让每一个模块拥有自己的 state、mutation、action、 getters,使得结构非常清晰,方便管理。

使用单一状态树,导致应用的所有状态集中到一个很大的对象。但是,当应用变得很大时,store 对象会变得臃肿不堪。

为了解决以上问题,Vuex 允许我们将 store 分割到模块(module)。每个模块拥有自己的 state、mutation、action、getters、甚至是嵌套子模块——从上至下进行类似的分割:

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import app from './modules/app'
import settings from './modules/settings'
import user from './modules/user'
 
Vue.use(Vuex)
 
const store = new Vuex.Store({
  modules: {
    app,
    settings,
    user
  },
  getters
})
 
export default store

使用模块化的state,mutations,action,需在使用方法前加模块名

如:

this.$store.state.user.number

this.$store.commit(‘user/xxx’)

this.$store.dispatch(‘user/xxx’)

  1. Vue的监听属性和计算属性有什么区别?

computed(计算属性):

1,监听值未在data中定义,以return返回值形式;

2,计算属性的值会被缓存,只有实例中相关依赖值改变时,才重新计算,性能好但不适合做异步请求;

3,计算属性默认只有get来读取,手动修改计算属性时,会触发手写的set函数。

watch(监听器):

1,监听值要在data中先定义,可以不写return返回值;

2,不支持缓存,可以做异步操作;

3,监听值改变,回调函数自动调用。

  1. Vue导航守卫有哪些?

什么是路由守卫?

路由守卫就是路由跳转过程中的一些钩子函数 ,在路由跳转的时候,做一些判断或其它的操作。 类似于组件生命周期钩子函数 。

分类

1.全局路由守卫

beforeEach(to, from, next) 全局前置守卫,路由跳转前触发

beforeResolve(to, from, next) 全局解析守卫 在所有组件内守卫和异步路由组件被解析之后触发

afterEach(to, from) 全局后置守卫,路由跳转完成后触发

2.路由独享守卫

beforeEnter(to,from,next) 路由对象单个路由配置 ,单个路由进入前触发

3.组件路由守卫

beforeRouteEnter(to,from,next) 在组件生命周期beforeCreate阶段触发

beforeRouteUpdadte(to,from,next) 当前路由改变时触发

beforeRouteLeave(to,from,next) 导航离开该组件的对应路由时触发

4.参数

to: 即将要进入的目标路由对象

from: 即将要离开的路由对象

next(Function):是否可以进入某个具体路由,或者是某个具体路由的路径

详解

  1. 路由前置守卫 beforeEach(to, from, next)
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

在路由跳转前触发,在实际项目中应用最多,主要是登陆验证和跳转权限判断

  1. 全局解析守卫 beforeResolve(to, from, next)
router.beforeResolve((to, from, next) => {
  // ...
})

类似于路由前置守卫 beforeEach(to, from, next),也是路由跳转前触发,但它是同时在所有组件内守卫和异步路由组件被解析之后触发的

调用时机:在 beforeEach(to, from, next)和组件内beforeRouteEnter(to, from, next)之后,afterEach(to, from)之前调用

  1. 全局后置守卫 afterEach(to, from, next)
router.afterEach((to, from) => {
  // ...
})

于路由前置守卫 beforeEach(to, from, next)相对,路由跳转后触发,但它是同时在所有组件内守卫和异步路由组件被解析之后触发的

调用时机:在 beforeEach(to, from, next)和组件内beforeResolve (to, from, next)之后, beforeRouteEnter(to, from)之前调用

  1. 路由独享守卫 beforeEnter(to, from, next)
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

于路由前置守卫 beforeEach(to, from, next)相同,但在beforeEach(to, from, next)后触发

  1. 组件路由守卫 beforeRouteEnter(to, from, next)
const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 不能获取组件实例 
    // 因为当守卫执行前,组件实例还没被创建
  }

因为该守卫在组件创建之前阶段触发,那个时候组件还没有创建成功,所以这个守卫内不能使用this获取组件实例

调用时机:在全局守卫beforeEach(to, from, next)和独享守卫beforeEnter(to, from, next)之后,全局beforeResolve(to, from, next)和全局afterEach(to, from)之前调用

  1. 组件路由守卫 beforeRouteUpdate(to, from, next)
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 
  },

调用时机:在当前路由复用时

  1. 组件路由守卫 beforeRouteLeave(to, from, next)
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例
  }
}

通常用来禁止用户在还未保存修改前突然离开

调用时机:导航离开该组件的对应路由时调用

完整的导航解析流程

1.触发进入其它路由

2.调用要离开路由的组件守卫beforeRouteLeave

3.调用全局的前置守卫beforeEach

4.在重用的组件里调用 beforeRouteUpdate

5.在路由配置里的单条路由调用 beforeEnter

6.解析异步路由组件

7.在将要进入的路由组件中调用beforeRouteEnter

8.调用全局的解析守卫beforeResolve

9.导航被确认

10.调用全局的后置钩子afterEach

11.触发 DOM 更新mounted

12.执行beforeRouteEnter守卫中传给 next的回调函数

实际应用

router.beforeEach((to, from, next) => {
  NProgress.start(); // NProgress实现显示加载进度条效果
  console.log("routemgr to", to.path);
  if ("这里判断是不是开发环境") {
    //开发环境下,直接路由
    next();
  } else {
    if (to.path == "/login") {
      //登录页面
      session.set("isOpen", "ok");
      next();
    } else if ("这里判断如不是生产环境下录页面需要判断权限") {
      //非生产环境下
      next();
    } else {
      //非登录页面需要判断权限
      console.log("routemgr user", lu.userinfo);
      if (gadget.isEmptyObject(lu.userinfo)) {
        //首次打开页面的时候,不需要弹出错误页面提示,直接跳转至登录页面即可
        let ret = session.get("isOpen");
        if (ret == "ok") {
          //vuex用户信息判断,如果不存在,则重新登录
          MessageBox.alert("用户未登录,需要重新登录.", "错误", {
            confirmButtonText: "确定",
            type: "error",
          }).then(() => {
            console.log("重新登录");
            //next(`/procmgr/login?redirect=${to.path}`);
            next(`/login`);
            NProgress.done();
          });
        } else {
          next(`/login`);
          NProgress.done();
        }
      } else {
        //权限判断
      }
    }
  }
});
  1. 登录拦截怎么实现?

  1. 闭包是什么?怎么实现?

定义:在JavaSeript中,内嵌函数可以访问定义在外层函数中的所有变量和函数,并包括其外层函数能访问的所有变量和函数。但是在函数外部则不能访问函数的内部变量和嵌套函数。

所谓“闭包”指的就是有权访问另一个函数作用域内变量(局部变量)的函数。 它最主要的用途是以下两点。

1.可以在函数外部读取函数内部的变量。

2.可以让变量的值始终保持在内存中。

闭包函数的实现

function fn() {
    var times = 0;
    var c = function() {
        return ++times;
    };
    return c;
}
var count = fn();   //保存fn()返回的函数,此时count就是一个闭包
//访问测试
console.log(count());    //输出结果: 1
console.log(count());    //输出结果: 2
console.log(count());    //输出结果: 3
console.log(count());    //输出结果: 4
console.log(count());    //输出结果: 5
  1. opacity和rgba的区别?

opacity和rgba都是css中设置透明度的方式。

opacity 属性设置元素的不透明级别。

rgba() 函数使用红( R)、绿(G)、蓝(B)、透明度(A)的叠加来生成各式各样的颜色。

opacity和rgba的区别:

opacity

opacity是一个属性。opacity属性的值,可以被其子元素继承,给父级div设置opacity属性,那么所有子元素都会继承这个属性,并且,该元素及其继承该属性的所有子元素的所有内容透明度都会改变。

rgba

rgba是一个属性值。rgba设置的元素,只对该元素的背景色有改变,并且,该元素的后代不会继承该属性。

  1. css中四大定位模式

参考:https://blog.csdn.net/M_emory_/article/details/123842619

(一) 静态定位 static (了解一下)

静态定位是元素的默认定位方式,无定位的意思

语法:

选择器{ position : static; }

静态定位按照标准流特性摆放位置,它没有边偏移

静态定位在布局时很少用到

(二) 相对定位 relative (非常重要)

相对定位是元素在移动位置的时候,是相对于它原来的位置来说的 (自恋型)

语法:

选择器{ position : relative; }

相对定位的特点∶(务必记住)

1.它是相对于自己原来的位置来移动的(移动位置的时候参照点是自己原来的位置)。

2.原来在标准流的位置继续占有,后面的盒子仍然以标准流的方式对待它。(不脱标,继续保留原来位置)因此,相对定位并没有脱标。它最典型的应用是给绝对定位当爹的。。。

(三) 绝对定位 absolute (非常重要)

绝对定位是元素在移动位置的时候,是相对于它祖先元素来说的 (拼爹型)

语法:

选择器{ position : absolute; }

绝对定位的特点︰(务必记住)

1.如果没有祖先元素或者祖先元素没有定位,则以浏览器为准定位(Document文档)。

2.如果祖先元素有定位(相对、绝对、固定定位),则以最近一级的有定位祖先元素为参考点移动位置。

3.绝对定位不再占有原先的位置。(脱标)

(四) 固定定位 fixed (重要)

固定定位是元素固定于浏览器可视区的位置,主要使用场景 : 可以在浏览器页面滚动时元素的位置不会改变

语法:

选择器{ position : flxed; }

固定定位的特点∶(务必记住)

1.以浏览器的可视窗口为参照点移动元素。

跟父元素没有任何关系

不随滚动条滚动。

2.固定定位不在占有原先的位置。

固定定位也是脱标的,其实固定定位也可以看做是一种特殊的绝对定位。

固定定位小技巧︰固定在版心右侧位置。

小算法︰

1.让固定定位的盒子left: 50%.走到浏览器可视区(也可以看做版心)的一半位置。

2.让固定定位的盒子margin-left:版心宽度的一半距离。多走版心宽度的一半位置就可以让固定定 位的盒子贴着版心右侧对齐了。

  1. CSS3新增了哪些新特性?

一、选择器

css3中新增了一些选择器,主要为如下图所示:

二、新样式

1.边框

css3新增了三个边框属性,分别是:

border-radius:创建圆角边框

box-shadow:为元素添加阴影

border-image:使用图片来绘制边框

box-shadow

设置元素阴影,设置属性如下:

水平阴影

垂直阴影

模糊距离(虚实)

阴影尺寸(影子大小)

阴影颜色

内/外阴影

其中水平阴影和垂直阴影是必须设置的

2.背景

新增了几个关于背景的属性,分别是background-clip、background-origin、background-size和background-break

background-clip

用于确定背景画区,有以下几种可能的属性:

background-clip: border-box; 背景从border开始显示

background-clip: padding-box; 背景从padding开始显示

background-clip: content-box; 背景显content区域开始显示

background-clip: no-clip; 默认属性,等同于border-box

通常情况,背景都是覆盖整个元素的,利用这个属性可以设定背景颜色或图片的覆盖范围

background-origin

当我们设置背景图片时,图片是会以左上角对齐,但是是以border的左上角对齐还是以padding的左上角或者content的左上角对齐? border-origin正是用来设置这个的

background-origin: border-box; 从border开始计算background-position

background-origin: padding-box; 从padding开始计算background-position

background-origin: content-box; 从content开始计算background-position

默认情况是padding-box,即以padding的左上角为原点

background-size

background-size属性常用来调整背景图片的大小,主要用于设定图片本身。有以下可能的属性:

background-size: contain; 缩小图片以适合元素(维持像素长宽比)

background-size: cover; 扩展元素以填补元素(维持像素长宽比)

background-size: 100px 100px; 缩小图片至指定的大小

background-size: 50% 100%; 缩小图片至指定的大小,百分比是相对包 含元素的尺寸

background-break

元素可以被分成几个独立的盒子(如使内联元素span跨越多行),background-break 属性用来控制背景怎样在这些不同的盒子中显示

background-break: continuous; 默认值。忽略盒之间的距离(也就是像元素没有分成多个盒子,依然是一个整体一样)

background-break: bounding-box; 把盒之间的距离计算在内;

background-break: each-box; 为每个盒子单独重绘背景

3.文字

word-wrap

语法:word-wrap: normal|break-word

normal:使用浏览器默认的换行

break-all:允许在单词内换行

text-overflow

text-overflow设置或检索当当前行超过指定容器的边界时如何显示,属性有两个值选择:

clip:修剪文本

ellipsis:显示省略符号来代表被修剪的文本

text-shadow

text-shadow可向文本应用阴影。能够规定水平阴影、垂直阴影、模糊距离,以及阴影的颜色

text-decoration

CSS3里面开始支持对文字的更深层次的渲染,具体有三个属性可供设置:

text-fill-color: 设置文字内部填充颜色

text-stroke-color: 设置文字边界填充颜色

text-stroke-width: 设置文字边界宽度

4.颜色

css3新增了新的颜色表示方式rgba与hsla

rgba分为两部分,rgb为颜色值,a为透明度

hala分为四部分,h为色相,s为饱和度,l为亮度,a为透明度

三、transition 过渡

transition属性可以被指定为一个或多个CSS属性的过渡效果,多个属性之间用逗号进行分隔,必须规定两项内容:

  • 过度效果

  • 持续时间

语法如下:

transition: CSS属性,花费时间,效果曲线(默认ease),延迟时间(默认0)

四、transform 转换

transform属性允许你旋转,缩放,倾斜或平移给定元素

transform-origin:转换元素的位置(围绕那个点进行转换),默认值为(x,y,z):(50%,50%,0)

使用方式:

transform: translate(120px, 50%):位移

transform: scale(2, 0.5):缩放

transform: rotate(0.5turn):旋转

transform: skew(30deg, 20deg):倾斜

五、animation 动画

动画这个平常用的也很多,主要是做一个预设的动画。和一些页面交互的动画效果,结果和过渡应该一样,让页面不会那么生硬

animation也有很多的属性

animation-name:动画名称

animation-duration:动画持续时间

animation-timing-function:动画时间函数

animation-delay:动画延迟时间

animation-iteration-count:动画执行次数,可以设置为一个整数,也可以设置为infinite,意思是无限循环

animation-direction:动画执行方向

animation-paly-state:动画播放状态

animation-fill-mode:动画填充模式

六、渐变

颜色渐变是指在两个颜色之间平稳的过渡,css3渐变包括

  • linear-gradient:线性渐变

background-image: linear-gradient(direction, color-stop1, color-stop2, …);
  • radial-gradient:径向渐变

linear-gradient(0deg, red, green);

七、其他

关于css3其他的新特性还包括flex弹性布局、Grid栅格布局,这两个布局在以前就已经讲过,这里就不再展示

除此之外,还包括多列布局、媒体查询、混合模式等等…

  1. promise的简单介绍

1. promise是什么?

promise是解决异步问题的一种解决方案(但是它本身不是异步的),它本质上是一个对象,用于获取异步的一些消息。

promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

2.promise的特点

2.1 promise的三种状态

一个 Promise 必然处于以下3种状态之一:

待定状态(pending): 初始状态,不是成功状态也不是失败状态。
成功状态(fulfilled):  意味着操作成功完成。
失败状态(rejected): 意味着操作失败。

2.2 状态改变情况

当promise状态从pending,变成其他两种状态中的一种时,就不会再变,任何时候都可以得到这个结果。所以,Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected。

3.promise语法

基本语法

 var promise = new Promise(function(resolve, reject) {
        // 异步语句
        // 异步语句执行结束后、调用resolve 或 reject
});

Promise 对象的创建语法中,包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。

在回调中执行一些操作(例如异步),如果成功,则调用 resolve,它把promise的状态修改为fullfiled。失败则调用 reject,,他把promise的状态修改为rejected。

4.一些基本用法

4.1 then 的用法

链式操作,简化层层回调的写法。

  const pro = new Promise((resolve, reject) => {
            setTimeout(function(){
                var num = Math.ceil(Math.random()*10); //生成1-10的随机数
                resolve(num);
      }, 1000);
           
        })
        pro.then(function(data){
            console.log('随机数字是'+data);
            return data*2
        }).then(function(data1){
            console.log('第一个数字的两倍是'+data1);
            return data1*2
        }).then(function(data2){
            console.log('第二个数字的两倍是'+data2);
            return data2*2
        }).then(function(data3){
            console.log('第三个数字的两倍是'+data3);
        });

利用定时器异步获取一个数字,1秒后执行完成。第一个then接收传过来的数字,然后输出,并对该数据进行乘2的处理,接着传给下一个then,依次执行上述操作。就得到了下面的运行结果。

运行结果如下:

4.2 reject的用法

reject的作用就是把Promise的状态置为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。看下面的代码。

下面举一个简单的例子,当小花手中的苹果数大于5时,就不送人,如果小于大于5,就把苹果送给小红。小花手中的苹果数是随机的。

const pro1 = new Promise((resolve, reject) => {
            var num = Math.ceil(Math.random()*10); //生成1-10的随机数
            console.log('小花有'+num+'个苹果');
            if(num<=5){
                resolve(num);
            }
            else{
                reject(num);
            }
        })

        const pro2 = pro1.then(data => {
            console.log('小花送给小红'+data+'个苹果');
        }, err => {
            console.log(err+'个苹果,太重了,拿不起,不送了');
        })

利用定时器异步获取一个数字,1秒后执行完成,如果数字小于等于5,我们认为是“成功”了,调用resolve修改Promise的状态。否则我们认为是“失败”了,调用reject并传递一个参数,作为失败的原因。

结果1

结果2

4.3 catch的用法

在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。

还有一个作用是用来指定reject的回调。

 const pro = new Promise((resolve, reject) => {
            var num = Math.ceil(Math.random()*10); //生成1-10的随机数
            console.log(num);
            if(num<=5){
                resolve(num);//成功
            }
            else{
                reject(num);//失败
            }
        })
       
        pro.then(function(data){
            console.log('resolved'+num);
            console.log(data);
        })
        .catch(function(reason){
            console.log('rejected'+num);
            console.log(reason);
        });

如果数字小于等于5,我们认为是“成功”了,调用resolve修改Promise的状态,并进到这个then方法中。否则我们认为是“失败”了,调用reject并传递一个参数,作为失败的原因,并进到这个catch方法中。

结果1

结果2

  1. promise同时提交几个异步请求,谁先返回就用谁的返回值

Promise.race([]);接受一个参数,由promise组成的一个数组;它的返回结果是promise对象;它的结果和状态由什么去决定呢?由第一个改变Promise状态的对象去决定;若是返回的是成功,那么race就是成功;若是失败,那么race就是失败

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok1')
    }, 1000)
})

let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok2')
    }, 1300)
})
let p3 = new Promise((resolve, reject) => {
    resolve('ok3')
})

const result = Promise.race([p1, p2, p3]);
console.log(result)

  1. 弹性布局一行两列,一行固定宽,如何实现

htmL:
<div class="father_div">
      <div class="c1">子1</div>
      <div class="c2">子2</div>
</div>

css:
<style scoped>
.father_div{
  display: flex;
}
.c1{
  width: 400px;
  background-color: antiquewhite;
}
.c2{
  flex: 1;
  background-color: aqua;
}
</style>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值