欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前端面试题—vue部分详解

程序员文章站 2022-04-19 16:31:19
...

vue

基本概念区分

  • 单页面应用:在一个页面跳来跳去,不重新加载页面
  • vue 特点:渐进式 框架 双向数据绑定
    • 双向数据绑定:视图改变 数据自动更新;数据更新 视图自动改变
    • 渐进式:vue vue-router路由 vuex axios
    • 框架:自己写的代码被框架调用(库:自己调用库的代码)
  • 声明式

安装vue

cmd命令

npm i vue

yarn add vue

vue核心实现方法

var obj={}
Object.defineProperty(obj,'name',{
     value:'xhufeng',
     configurable:true,//是否可删
     writable:true,//是否可写
     enumerable:true,//是否可枚举(循环)
     set(val){
        //只要外界给name赋值,就会触发该函数
        //形参val就是外界赋予的值
     },
     get(){
        return 123
     }
  })
复制代码

vue指令

指令都是行内属性
v-model放在input、textarea、select>option上的,实现双向数据绑定
v-text 展示对应的文本
v-once 对应的标签只渲染一次
v-show=布尔 是否能显示,true能显示,false不能显示(存在隐式转化)
v-html 把值中的标签渲染出来

v-for

循环显示元素
可以循环数组、对象、数字、字符串
最好加:key='a+i'
v-for='item in ary'

v-bind

用于绑定行内属性 简写成:

v-if

控制是否渲染该元素
值是true,则渲染该元素;false则不渲染
v-else v-else-if连着使用
可以使用template标签,就不会出现多余标签

<body>
    <div id="app">
        <h1>{{name}}</h1>
        <button @click='flag=!flag'>按钮</button>
        <h2 v-show='flag'>hello</h2>
        <h2 v-show='!flag'>world</h2>
        
        <button @click='n=1'>1</button>
        <button @click='n=2'>2</button>
        <button @click='n=3'>3</button>
        <h3 v-if='n===1'>hello</h3>
        <h3 v-else-if="n===2">hahaha</h3>
        <h3 v-else>world</h3>
    </div>
</body>

</html>
<script src="./node/node_modules/vue/dist/vue.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            name: "珠峰",
            flag:true,
            n:1
        },
        methods: {
            fn() {

            }
        }
    })
</script>
复制代码
v-cloak

需要配合css使用:解决小胡子显示问题

v-pre

跳过有这个指令的标签及其子元素的编译,按照原生代码编译

vue对象

data中的属性最终都添加到了实例上 属性需要有get set,才能触发视图更新

     let vm = new Vue({
         el: '#app',
         data: {
             name: 'liu',
             age:21,
             q:'<h1>haha</h1>',
             obj:{
                 a:123,//对于对象来说,新增一个属性不会触发视图更新,只有改变属性时才会触发视图更新
                 b:undefined,
             }
         }
     }).$mount('#app')
     //处理方式
     //1.正常写全要用到的属性;先预留要用到的属性
     //2.整个对象的重新赋值
     //3.$set()方法
     vm.$set(vm.obj,'c','456')
     //4.增加一个无关变量t,每次修改完数据之后;重置t就可以了
复制代码

vue数组

    let vm = new Vue({
        el: '#app',
        data: {
            ary: [1, 2, 3, 4]
        }
    })
    vm.ary.length--;
    //能改变原有数组,但不能触发视图更新,只有数组原型上的变异方法可以触发更新
    //数组变异方法:pop shift unshift push reverse splice sort
复制代码

vue 事件

<body>
    <div id="app">
        {{ary}}
        <button @click='fn'>按钮1</button>
        <button @click='fn(name,$event)'>按钮2</button>
        <!-- $event是固定写法,代表传参事件对象 -->
    </div>
</body>

</html>
<script src="./node/node_modules/vue/dist/vue.js"></script>
<script>
    //事件绑定,用v-on:事件类型=‘函数’  或者  @事件类型=‘函数’
    //函数一般是在methods中定义的
    //对应的函数一般不带小括号,默认传参事件对象e
    //当我们需要传参时,需要加小括号,小括号里写需要传的参数;当只有小括号没有传参时,为undefined
    //el data methods 都是VUE规定死的属性名 
    let vm = new Vue({
        el: '#app',
        data: {
            ary: [1, 2, 3, 4],
            name:'liu'
        },
        methods: {
            fn: function (val,e) {
                console.log(val,e,this)
            }
        }
    })
    //@kryup.13='fn'在按下回车的时候才会触发该函数
</script>
复制代码

vue过滤器

全局用filter,实例局部使用filters 全局过滤器要放在需要使用的实例的前面

vue计算属性computed

  • 于data同级别
  • 语法同methods一样
  • 计算属性的名字 不能跟 data 或 methods 中的名字重复
  • 完全依赖于函数体中出现的属性名,只在最初加载时和其中属性名改变时运行,并不是像methods中的函数只要页面更新就运行一次
  • 不能传参
  • 异步的无法处理
  • 为了提高性能而存在

vue监听属性

  • 当且仅当监听的属性(例如:name)发生变化时会执行函数
  • 可以处理异步
  • 下面这种写法不能监听引用数据类型的内部变化
 watch:{
            name(newV,oldV){
                clearTimeout(this.timer)
                this.timer=setTimeout(() => {
                    if (newV.length > 5) {
                        this.msg2 = '名字太长'
                    }else{
                        this.msg2 = ''
                    }
                }, 500)
            }
        }
复制代码
  • 深度监听需要用下面这种语法
  • 这种深度监听在有get和set属性时才会触发监听
        watch:{
            obj:{
                handler(){
                    console.log(1111)
                },
                deep:true
             }
        }
复制代码

directives自定义指令

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

vue动画

transition
  • 把需要实现动画的标签用transition标签包起来
  • 只能对根元素有动效

transition-group
  • tag指定外层包裹元素的tag类型;不写,默认span
  • duration控制vue设置的类名存在的时间

事件修饰符

  • .self只点元素本身时才触发事件
  • .stop阻止冒泡事件
  • .prevent阻止默认事件
  • .once对应函数只触发一次
  • .capture在捕获阶段触发二级绑定事件
  • .passive优先执行默认事件(滚动行为)

表单修饰符

  • .number转化为数字,类似parse转化
  • .trim去字符串前后空格

生命周期

    let vm = new Vue({
        el:'#app',
        // template:'<h1>哈哈哈</h1>',
        data:{
            name:"liu"
        },
        // 生命周期钩子函数 就是VUE规定的一些在固定阶段执行的函数
        // 语法上 跟 data和el、methods等 同级
        beforeCreate(){
            // 创造之前
            // 不能获取data和methods里的数据
            console.log('beforeCreate');
            debugger;
        },
        created(){            
           // 能获取data和methods里的数据
           // 一般把ajax请求的发送写到这里
        },
        beforeMount() {},
        mounted(){
           // 在此时DOM已经渲染完成
        },
        beforeUpdate(){
           // 在视图更新时会触发其中函数
           // 函数在视图更新前执行
        },
        updated(){
           // 在视图更新时会触发其中函数
           // 函数在视图更新后执行
           // 不要写会触发视图更新的代码
        },
        beforeDestroy(){},
        destroyed(){}
    })
    vm.$destory();//手动销毁该实例,双向数据绑定没了
复制代码

组件

  • 划分比较明细,复用率比较高
  • 功能性组件 页面级组件
  • 全局组件 局部组件
  • template模板中只能有一个根元素

全局组件的注册

  • 必须写到根组件(根实例)的前面
    //全局组件的定义
    Vue.component('qqq',{
        template:'<i>hello 小聪子</i>'
    })
复制代码
<body>
    <div id="app">
        <h1>{{name}}</h1>
        <aaa></aaa>
        <aaa></aaa>
        <my-name></my-name>
        <!-- 不支持驼峰式命名 -->
        
    </div>
</body>
   <template id='qqq'>
        <div>
            <input type="text" v-model='name'>
            <h1><i>{{name}}</i></h1>
        </div>
    </template>

<script>
    //全局组件的定义
    //使用时只要把组件名当作标签使用即可
    Vue.component('aaa', {
        //没有el属性,有template属性,data是个函数,并且返回一个对象
        //template就是qqq标签要去展示的内容
        //template有且只能有一个根元素
        template: '#qqq',
        data() {
            return {
                name: 'liu'
            }
        },
        created() {
            console.log(this.name)
        }
    })
    Vue.component('myName', {
        template: `<h2>My name is </h2>`,
        data() {
            return {
            }
        },
    })
    let vm = new Vue({
        el: '#app',
        data: {
            name: "liu"
        }
    })
</script>
复制代码

局部组件的注册

  • 局部组件只能使用在此之前声明的局部组件作为子组件

<body>
    <div id="app">
        <h1>{{name}}</h1>
        <son></son>

        <parent></parent>
    </div>
</body>

<template id="opp">
    <h2>ninininini</h2>
</template>

<template id="o">
    <h2>小聪子<son></son></h2>
</template>

<script>
    let son = {
        template: '#opp',
        data() {
            return {

            }
        }
    }
    let parent = {
        template: '#o',
        data() {
            return {

            }
        },
        components: {
            son
        }
    }
    let vm = new Vue({
        el: '#app',
        data: {
            name: "liu"
        },
        components: {
            son,
            parent: parent
        }
    })
</script>
复制代码

组件的数据传输

父传子

过程

  • 1.通过v-bind绑定属性,把相应的数据传递给子组件
  • 2.子组件通过props接收传进来的数据

注意事项

  • 父传子是单向数据流,不能从子组件修改父组件的数据
  • 但如果是引用数据类型,不修改地址,只改变内容,可以改变
  • props可以是数组,也可以对象
  • props中自定义属性的参数
  • - 1.default:默认值
    复制代码
  • - 2.type:规定此属性的数据类型
    复制代码
  • 可以通过实例this.$parent调用父组件的数据和方法(不推荐)
<body>
    <div id="app">
        <h1>{{name}}</h1>
        <input type="text" v-model='name'>
        <son :name='name'></son>
    </div>
</body>

<template id="o">
    <div>
        <h3>{{name}}</h3>
        <button @click='fn'> 这个按钮点击了{{n}}次</button>
    </div>
</template>

<script>
    let son = {
        template: '#o',
        data() {
            return {
                n: 0
            }
        },
        methods: {
            fn() {
                this.n++
            }
        },
        props:['name']
    }
    let vm = new Vue({
        el: '#app',
        data: {
            name: "liu"
        },
        components: {
            son,
        }
    })
</script>
复制代码
子传父

本质上是父组件使用子组件的数据

官方过程

  1. 通过自定义事件触发父组件中的方法
  2. 再由子组件方法的执行通过this.$emit('自定义事件名',this.qqq)传递子组件的数据

也可以在父组件中通过this.$children[i]调用子组件的数据及方法 也可以在父组件中通过this.$refs.son调用子组件的方法

插槽

在template模板中写入<slot></slot>,可显示在使用组件时组件名标签内部内容

具名插槽

在slot标签中的name属性是控制要去显示哪一部分的一个功能 不写时,默认default,全部显示 name对应的的是slot='qqq'的部分

模板中若有与name值对应的元素,则slot标签包含的内容不会显示出来;反之,可以显示

子父组件的mounted

执行顺序

动态组件component

keep-alive

-在没有keep-alive时,component组件是销毁旧的,重新渲染新的 -加上keep-alive后,动态组件有缓存机制,不会销毁旧的

  <keep-alive>
      <component :is='son'></component>
  </keep-alive>

复制代码

vue-router

vue-router是vue的路由插件

基本用法

  • 步骤
  • 1、声明组件
  • 2、编写路由映射表
  • 3、把编辑好的映射表注入到router实例中
  • 4、把router实例注入到根实例中
  • router-link 控制跳转的链接和显示的文字
  • router-view 控制显示的组件内容
  • active-class 控制选中对应路径的类名
  • tag 控制渲染成什么标签
<body>
    <div id="app">
       <router-link to='/home' active-class='current'>首页</router-link>
       <router-link to='/list' tag='div'>列表</router-link>
       <router-view></router-view>
    </div>
</body>

</html>
<template id="home">
    <div>home</div>
</template>
<template id="list">
    <div>list</div>
</template>
<script src="../node/node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script>

    let home = {
        template: '#home',

    }
    let list = {
        template: '#list',

    }

    //路由映射表
    let routes = [{
            path: '/home',
            component: home
        },
        {
            path: '/list',
            component: list
        }
    ]

    let router=new VueRouter({
        routes:routes,
    })
    let vm = new Vue({
        el: '#app',
        data: {
            name: "liu"
        },
        router,
    })
</script>
复制代码

传参

  • 提供了两种传参方式
  • 1、query传参(问号传参)
  • 路由映射表不用改动 :to={path:'',query:{}}或者:to={name:'',query:{}}
  • 2、params传参(路径传参)
  • 在映射表中添加 /:变量 的形式; :to={name:'',params:{变量:''}}}

<router-link :to='{path:"/list",query:{id:123,e:name}}' tag='div'>列表</router-link>

重定向redirect

let routes = [
        {
            path:'/',
            redirect:'/son2/222'
        },
        {
            path:'/son1',
            // redirect:'/son2/5555',
            name:'son1',
            component:son1,
            redirect:'/son1/sz',
            children:[
                {
                    path:'/son1/sz',
                    component:sz
                }
            ]
        },
        {
            path:'/son2/:bl1234',
            name:'son2',
            component:son2
        },
        {
            path:'/*',
            redirect:'/son1'
        }
    ]
复制代码

vuex

是一个能方便vue实例及其组件传输数据的插件 方便传输数据,作为公共存储数据的一个库

基本用法

  • 步骤
  • 1、创建一个vuex实例
  • 2、在根组件中注册一下
  • 3、注册后,根组件及其一下的局部或全局组件可使用vuex实例中的数据方法,多个组件使用同一套规则时,我们可以把这套规则单独拎出来写在vuex实例中通用

方法汇总

state

类似vue实例中的data,存储数据 在vue实例中用this.$store.state调用state里的数据 使用...Vuex.mapStore(['count'])将其数据放入computed中

mutations

官方提供的唯一改数据的方法 其中必须是同步函数 通过commit来调用mutations中的函数 其中函数最少有一个参数state,最多两个参数另加val(调用时的传参) 使用...Vuex.mapMutations(['mu_add'])将其函数放入methods中

actions

可以写异步函数,一般多用于触发ajax请求 不能直接用于修改state中的数据,需要通过其中函数默认的第一个实参obj,用obj.commit('mu_')来调用mutations中的函数来修改数据 使用...Vuex.mapActions(['ac_add'])将其函数放入methods中

getters

类似vue实例中的计算属性,存储数据 使用...Vuex.mapGetters(['remove'])将其函数放入computed中

    //1、创造一个vuex实例
    //2、把创造的实例放到根实例中
    //使用this.$store.state
    let son1 = {
        template: `<div>
                <button @click='add'>增加</button>
                <button @click='mu_add(100)'>增加</button>
                <button @click='add2(100)'>2增加</button>
                <h2>{{count}}</h2>
                <h2>{{qqq}}</h2>
            </div>`,
        data() {
            return {

            }
        },
        computed: {
            //两种监听vuex数据的写法
            // myCount(){
            //     return this.$store.state.count
            // }
            ...Vuex.mapState(['count', 'qqq'])
        },
        methods: {
            add() {
                // this.$store.state.count++
                // this.$store.commit('mu_add', 1)
                this.mu_add(10)
            },
            ...Vuex.mapMutations(['mu_add']),
            add2() {
                this.$store.dispatch('ac_add', 22)
                //dispatch 触发的是actions里的函数
                //commit   触发的是mutations里的函数
            }
        }
    }
    let son2 = {
        template: `<div>
                <button @click='remove'>减少</button>
                <h2>{{$store.state.count}}</h2>
            </div>`,
        methods: {
            remove() {
                this.$store.commit('mu_remove', 1)
            }
        },
    }
    let store = new Vuex.Store({
        state: {
            count: 0,
            qqq: 12
        },
        
        mutations: {
            mu_add(state, val = 1) {
                console.log(arguments)
                //第一个实参是默认传的state
                //第二个实参是自己传的参数
                //一共只有一个或两个实参
                state.count += val
            },
            mu_remove(state, val = 1) {
                console.log(arguments)
                //第一个实参是默认传的state
                //第二个实参是自己传的参数
                //一共只有一个或两个实参
                state.count -= val
            }
        },
        actions: {
            ac_add(obj,n) {
                //obj是vuex封装好的一个对象,里边提供了commit方法
                obj.commit('mu_add',n)
                console.log(arguments)
            }
        }
        //mutations中必须是同步函数,actions同步异步都行
        //想要修改state里的数据只能用commit来调用mutations里的函数
    })
    let vm = new Vue({
        el: '#app',
        store,
        data: {
            name: "liu"
        },
        components: {
            son1,
            son2
        },
        created() {
            console.log(this)
        },
    })
复制代码

==========================================================

前端小白,第一次发文,欢迎评论指正!