vue面试题详细讲解

最近在面试,总结几个重点的面试题:

一、vue父子组件之间的传值:
简单来说,子组件通过props方法接受父组件绑定传来的数组或者对象,子组件通过$emit方法来向父组件发送数据

二、vue生命周期函数:

beforeCreate(创建前)
在实例初始化之后,数据观测和事件配置之前被调用,el和data并未初始化,因此无法访问,methods,data,computed等上面的方法和数据
created(创建后)
完成data数据的初始化,没有el获取不到真是的Dom
beforeMount(挂载前)
挂在开始之前被调用,相关的 render 函数首次被调用(虚拟 DOM),实例已完成以下的配置:编译模板,把 data 里面的数据和模板生成 html,完成了 el 和 data 初始化,注意此时还没有挂在 html 到页面上。
mounted(挂载后)
挂在完成,也就是模板中的 HTML 渲染到 HTML 页面中,此时一般可以做一些 ajax 操作,mounted 只会执行一次。
beforeUpdate(更新前)
在数据更新之前被调用,发生在虚拟 DOM 重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加地重渲染过程
updated(更新后)
由于数据更改导致地虚拟 DOM 重新渲染和打补丁只会调用,调用时,组件 DOM 已经更新,该钩子在服务器端渲染期间不被调用
beforeDestroy(销毁前)
在实例销毁之前调用,实例仍然完全可用,清除掉组件中的定时器和监听的dom事件
destroyed(销毁后)
在实例销毁之后调用,事件监听器会被移出,所有的子实例也会被销毁,该钩子在服务器端渲染期间不被调用

ajax操作是在monted生命周期中完成的

三、vue自定义指令

 
1.创建局部指令
var app = new Vue({
    el: '#app',
    data: {    
    },
    // 创建指令(可以多个)
    directives: {
        // 指令名称
        dir1: {
            inserted(el) {
                // 指令中第一个参数是当前使用指令的DOM
                console.log(el);
                console.log(arguments);
                // 对DOM进行操作
                el.style.width = '200px';
                el.style.height = '200px';
                el.style.background = '#000';
            }
        }
    }
})
 
2.全局指令
Vue.directive('dir2', {
    inserted(el) {
        console.log(el);
    }
})
 
3.指令的使用
<div id="app">
    <div v-dir1></div>
    <div v-dir2></div>
</div>

四、vue动态路由传值:
vue动态路由配置,vue路由传参动态路由:
  当我们很多个页面或者组件都要被很多次重复利用的时候,我们的路由都指向同一个组件,这时候从不同组件进入一个"共用"的组件,并且还要传参数,渲染不同的数据
  这就要用到动态路由跟路由传参了!
首先我们来了解下router-link这个组件:
  简单来说,它是个导航器,利用to属性导航到目标组件,并且在渲染的时候会自动生成一个a标签,当然官方也有说明,加个tag标签属性就可以渲染不同的标签,可以浏览器端查看到
  并且当一个导航器被激活的时候,会自动加上一个css的激活样式,可以全局在路由配置中设置linkActiveClass属性,属性名就是样式css名,一般写为active
  现在基本了解了router-link,先讲一下动态路由配置吧
我们在配置路由的时候,将目标组件的路径先配置好,如:
在这里插入图片描述
比如多个路由都要进入List组件,这时候在配置路由路径后加个:id(id可随便取名,标识),这个属性id可以在$route.params.id中获取,例如:
在这里插入图片描述
这个时候,不同组件进入同一目标组件时就可以得到标识跟备注了,也可以利用这个来传递一些正常的参数
接着往下看,带参数的路由,跟获取传来的参数值
当router-link被激活点击时,会将当前的to的值push到router对象当中(路由栈),所以这个值可以是string也可以是obj
传参数的时候,我们就写成对象的形式,用到v-bind的js表达式
在这里插入图片描述
此时整个的理解可以为:我是child组件过来的,而且我还带来了我的名字,我叫child
在List组件当中去获取这个参数值跟id的值
在这里插入图片描述
在这里插入图片描述
如果是不同的组件过来的,可以设置不同的id值,只要在目标组件获取属性id的值就可以了,参数就利用query.属性值来获取

五、axios和ajax的区别:
1.区别 axios是通过promise实现对ajax技术的一种封装,就像jQuery实现ajax封装一样。 简单来说: ajax技术实现了网页的局部数据刷新,axios实现了对ajax的封装。 axios是ajax ajax不止axios。
Ajax:
Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。
Ajax = 异步 JavaScript 和 XML(标准通用标记语言的子集)。
Ajax 是一种用于创建快速动态网页的技术。
Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
传统的网页(不使用 Ajax)如果需要更新内容,必须重载整个网页页面。

$.ajax({
            url: 'http://jsonplaceholder.typicode.com/users',
            type: 'get',
            dataType: 'json',
            data: {
                //'a': 1,
                //'b': 2,
            },
            success: function (response) {
                console.log(response);
            }
        }) 

axios:
用于浏览器和node.js的基于Promise的HTTP客户端

  1. 从浏览器制作XMLHttpRequests
  2. 让HTTP从node.js的请求
  3. 支持Promise API
  4. 拦截请求和响应
  5. 转换请求和响应数据
  6. 取消请求
  7. 自动转换为JSON数据
  8. 客户端支持防止XSRF

axios({
            url: 'http://jsonplaceholder.typicode.com/users',
            method: 'get',
            responseType: 'json', // 默认的
            data: {
                //'a': 1,
                //'b': 2,
            }
        }).then(function (response) {
            console.log(response);
            console.log(response.data);
        }).catch(function (error) {
            console.log(error);
        })

2.优缺点: ajax: 本身是针对MVC的编程,不符合现在前端MVVM的浪潮 基于原生的XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案 JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务 axios: 从 node.js 创建 http 请求 支持 Promise API 客户端支持防止CSRF。

六、vuex实现购物车原理:
1.store.js(公共的仓库)

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
 
export default new Vuex.Store({
    state: {
        carList: [] //购物车的商品
    },
    mutations: {
        // 加
        addCar(state, params) {
            let CarCon = state.carList;
            // 判断如果购物车是否有这个商品,有就只增加数量,否则就添加一个
            // some 只要有一个isHas为true,就为true
            let isHas = CarCon.some((item) => {
                if (params.id == item.id) {
                    item.num++;
                    return true;
                } else {
                    return false;
                }
            })
 
            if (!isHas) {
                let obj = {
                    "id": params.id,
                    "title": params.title,
                    "price": params.price,
                    "num": 1,
                }
                this.state.carList.push(obj)
            }
        },
        // 减
        reducedCar(state,params){
            let len=state.carList.length;
            for(var i=0;i<len;i++){
                if(state.carList[i].id==params.id){
                    state.carList[i].num--
                    if(state.carList[i].num==0){
                        state.carList.splice(i,1);
                        break;
                    }
                }
            }
        },
        //移出
        deleteCar(state,params){
            let len=state.carList.length;
            for(var i=0;i<len;i++){
                if(state.carList[i].id==params.id){
                    state.carList.splice(i,1);
                    break;
                }
            }
        },
 
         // 初始化购物车,有可能用户一登录直接进入购物车
        // initCar(state, car) {
        //     state.carList = car
        // },
 
    },
    actions: {
        // 加
        addCar({ commit }, params) {
            // console.log(params) //点击添加传过来的参数
            // 使用setTimeout模拟异步获取购物车的数据
            setTimeout(function () {
                let result = 'ok'
                if (result == 'ok') {
                    // 提交给mutations
                    commit("addCar", params)
                }
            }, 100)
        },
        // 减
        reducedCar({ commit }, params) {
            // console.log(params) //点击添加传过来的参数
            // 使用setTimeout模拟异步获取购物车的数据
            setTimeout(function () {
                let result = 'ok'
                if (result == 'ok') {
                    // 提交给mutations
                    commit("reducedCar", params)
                }
            }, 100)
        },
        // 移出
        deleteCar({ commit }, params) {
            // console.log(params) //点击添加传过来的参数
            // 使用setTimeout模拟异步获取购物车的数据
            setTimeout(function () {
                let result = 'ok'
                if (result == 'ok') {
                    // 提交给mutations
                    commit("deleteCar", params)
                }
            }, 100)
        }
        // initCar({ commit }) {
        //     setTimeout(function () {
        //         let result = 'ok'
        //         if (result == 'ok') {
        //             // 提交给mutations
        //             commit("initCar", [{
        //                 "id": 20193698,
        //                 "title": '我是购物车原来的',
        //                 "price": 30,
        //                 "num": 100,
        //             }])
        //         }
        //     }, 100)
        // }
    },
    getters: {
        //返回购物车的总价
        totalPrice(state) {
            let Carlen = state.carList;
            let money = 0;
            if (Carlen.length != 0) {
                Carlen.forEach((item) => {
                    money += item.price * item.num
                })
                return money;
            } else {
                return 0;
            }
        },
        //返回购物车的总数
        carCount(state) {
            return state.carList.length
        }
    },
})

list.vue(商品列表)

<template>
  <!-- 商品列表 -->
  <div id="listBox">
    <!--  -->
    <router-link :to="{path:'/car'}" style="line-height:50px">跳转到购物车</router-link>
    <el-table :data="tableData" border style="width: 100%">
      <el-table-column fixed prop="id" align="center" label="商品id"></el-table-column>
      <el-table-column prop="title" align="center" label="商品标题"></el-table-column>
      <el-table-column prop="price" align="center" label="商品价格"></el-table-column>
      <el-table-column label="操作" align="center">
        <template slot-scope="scope">
          <el-button @click="addCar(scope.row)" type="text" size="small">加入购物车</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
 
<script>
export default {
  name: "listBox",
  data() {
    return {
      tableData: [] //商品列表
    };
  },
  methods: {
    // 初始化商品列表
    initTable(){
      this.$gAjax(`../static/shopList.json`)
        .then(res => {
          console.log(res)
          this.tableData=res;
        })["catch"](() => {});
    },
    // 加入购物车
    addCar(row){
      // console.log(row)
      // 提交给store里面actions 由于加入购物车的数据要同步到后台
      this.$store.dispatch('addCar',row)
    }
    
  },
  mounted () {
    this.initTable()
  }
};
</script>
<style>
#listBox {
  width: 900px;
  margin: 0 auto;
}
</style
  1. car.vue(购物车)
<template>
  <!-- 购物车 -->
  <div id="carBox">
    <!-- 商品总数 -->
    <h2 style="line-height:50px;font-size:16px;font-weight:bold">合计:总共{{count}}个商品,总价{{totalPrice}}元</h2>
    <p v-if="count==0">空空如也!·······</p>
    <div v-else>
      <el-table :data="carData" border style="width: 100%">
        <el-table-column fixed prop="id" align="center" label="商品id"></el-table-column>
        <el-table-column prop="title" align="center" label="商品标题"></el-table-column>
        <el-table-column prop="price" align="center" label="商品价格"></el-table-column>
        <el-table-column label="操作" align="center">
          <template slot-scope="scope">
            <el-button @click="reduceFun(scope.row)" type="text" size="small">-</el-button>
            <span >{{scope.row.num}}</span>
            <el-button @click="addCar(scope.row)" type="text" size="small">+</el-button>
 
            <el-button @click="deleteFun(scope.row)" type="text" size="small">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </div>
</template>
 
<script>
export default {
  name: "carBox",
  data() {
    return {};
  },
  computed: {
    //购物车列表
    carData() {
      return this.$store.state.carList;
    },
    //商品总数
    count() {
      return this.$store.getters.carCount;
    },
    //商品总价
    totalPrice() {
      return this.$store.getters.totalPrice;
    }
  },
  methods: {
    // 增加数量
    addCar(row){
       this.$store.dispatch('addCar',row)
    },
    // 减数量
    reduceFun(row){
       this.$store.dispatch('reducedCar',row)
    },
    // 删除
    deleteFun(row){
        this.$store.dispatch('deleteCar',row)
    }
 
    // 用户首次登录请求购物车的数据
    // initCar(){
    //   this.$store.dispatch('initCar')
    // }
  },
  created () {
    // this.initCar();
  },
  mounted() {}
};
</script>
 
<style>
#carBox {
  width: 900px;
  margin: 0 auto;
}
</style>

七、vue路由钩子函数:

路由的钩子函数总结有6个

全局的路由钩子函数:beforeEach、afterEach

单个的路由钩子函数:beforeEnter

组件内的路由钩子函数:beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate

模块一:全局导航钩子函数

1、vue router.beforeEach(全局前置守卫)

beforeEach的钩子函数,它是一个全局的before 钩子函数,

(beforeEach)意思是在 每次每一个路由改变的时候都得执行一遍。

它的三个参数:

to: (Route路由对象) 即将要进入的目标 路由对象 to对象下面的属性: path params query hash fullPath matched name meta(在matched下,但是本例可以直接用)

from: (Route路由对象) 当前导航正要离开的路由

next: (Function函数) 一定要调用该方法来 resolve 这个钩子。 调用方法:next(参数或者空) ***必须调用

next(无参数的时候): 进行管道中的下一个钩子,如果走到最后一个钩子函数,那么 导航的状态就是 confirmed (确认的)

next(’/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。

应用场景:可进行一些页面跳转前处理,例如判断需要登录的页面进行拦截,做登录跳转!!


router.beforeEach((to, from, next) => {
    if (to.meta.requireAuth) {
        //判断该路由是否需要登录权限
        if (cookies('token')) {
            //通过封装好的cookies读取token,如果存在,name接下一步如果不存在,那跳转回登录页
            next()//不要在next里面加"path:/",会陷入死循环
        }
        else {
            next({
                path: '/login',
                query: {redirect: to.fullPath}//将跳转的路由path作为参数,登录成功后跳转到该路由
            })
        }
    }
    else {
        next()
    }
})

2、vue router.afterEach(全局后置守卫)

router.beforeEach 是页面加载之前,相反router.afterEach是页面加载之后

模块二:路由独享的守卫(路由内钩子)

你可以在路由配置上直接定义 beforeEnter 守卫:


const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]

这些守卫与全局前置守卫的方法参数是一样的。

模块三:组件内的守卫(组件内钩子)
1、beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave


const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当钩子执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
} 

八、vue解决跨域问题的方法:

方法1.后台更改header

header('Access-Control-Allow-Origin:*');//允许所有来源访问 
header('Access-Control-Allow-Method:POST,GET');//允许访问的方式  

方法2.使用JQuery提供的jsonp

methods: { 
  getData () { 
    var self = this 
    $.ajax({ 
      url: 'http://f.apiplus.cn/bj11x5.json', 
      type: 'GET', 
      dataType: 'JSONP', 
      success: function (res) { 
        self.data = res.data.slice(0, 3) 
        self.opencode = res.data[0].opencode.split(',') 
      } 
    }) 
  } 
} 

方法3.使用http-proxy-middleware 代理解决(项目使用vue-cli脚手架搭建)
例如请求的url:“http://f.apiplus.cn/bj11x5.json”

1、打开config/index.js,在proxyTable中添写如下代码

proxyTable: { 
  '/api': {  //使用"/api"来代替"http://f.apiplus.c" 
    target: 'http://f.apiplus.cn', //源地址 
    changeOrigin: true, //改变源 
    pathRewrite: { 
      '^/api': 'http://f.apiplus.cn' //路径重写 
      } 
  } 
}

2、使用axios请求数据时直接使用“/api”:

getData () { 
 axios.get('/api/bj11x5.json', function (res) { 
   console.log(res) 
 })

通过这中方法去解决跨域,打包部署时还按这种方法会出问题。解决方法如下:let serverUrl = '/api/' //本地调试时 // let serverUrl = 'http://f.apiplus.cn/' //打包部署上线时 export default { dataUrl: serverUrl + 'bj11x5.json' }

方法二引入jq
1.下载依赖

cnpm install jquery --save-dev

2.在webpack.base.conf.js文件中加入

plugins: [
    new webpack.ProvidePlugin({
        $: "jquery",
        jQuery: "jquery"
    })
 ],

3.在需要的temple里引入也可以在main.js里全局引入

import $ from 'jquery'

eg:

<template>
  <div class="source">
      source{{data}}
  </div>
</template>

<script>
import $ from 'jquery'
  export default({
    name:"source",
    data(){
      return{
        data:""
      }
    },
    created(){
      this.getData()
    },
    methods:{
      getData () {
        var self = this
        $.ajax({
          url: '你要请求的url',
          type: 'GET',
          dataType: 'JSONP',
          success: function (res) {
            self.data = res.result
          }
        })
      }
    }
  })
</script>

<style>

</style>

九、什么方法可以替换v-model?

v-model动态监听输入的值{{value1}}

使用v-model实现的监听:

v-bind:value和v-on:input动态监听输入的值{{value2}}

模拟v-model的方法:

**十、如何给vue自定义组件添加点击事件?**
需要在@click后面加上.native,官方对于native的解释为:

.native -——监听组件根元素的原生事件

正确写法:`<my-button  @click.native="alert1()" names="删除" v-bind:item2="btdata"></my-button>`
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值