前端面试之道② Vue

329 阅读4分钟

基础

  • 组件树

         Vue.component('todo-item', {
           prop: ['todo'],             //自定义特性
           template: '<li> {{ todo.text }}</li>'
       })
    
  • 当一个实例被创建时,它将data对象中所有的属性加入到 Vue的 【响应式系统】中,即双向绑定

  • 设置对象为只读属性 Object.freeze() , 冻结一个对象,不能进行操作

  • 生命周期 created mounted ... 禁止在选项属性或回调上使用 箭头函数: created: () => console.log(this.a) undefined

  • 命令

  1. v-html: 原始html,双大括号只会解析为数据 你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。 请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。

  2. xxx: null undefined false 类似 为false。

  3. {{ ok ? "true" : "false" }} 只能设置单个表达式

  4. 计算属性

                    computed: {
                        reverseMessage: function(val){      //val === this : true
                            return this.message.split("") ; //原理: get set
                        },                                  // 该属性添加到this上
                        fll_name: {
                            get(){} ,   //
                            set(){}
                        }
                    }
                    methods: {
                        reverseMessage: function (){
                             return this.message.split("")
                        }
                    }
                    两种方式获取的结果一致,但是【计算属性是基于它们响应式依赖进行-----缓存----的】
                    只在相关响应式依赖发生改变时它们才会重新求值。
                    这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,
                    而不必再次执行函数。
    
  5. 监听属性

            watch:{
                        first_name: function (val, old_val){
                            val;新值
                            old_val: 旧值
                        }
    
                    }
    
  6. 计算、监听属性,本质区别: 一个新添加一个变量, 一个监听一个变量

  7. 侦听器

    • 当数据变化时执行异步或者开销较大的操作时,这个方式最有用
    • _.debounce 是一个通过 Lodash 限制操作频率的函数。
    • _.throttle
        https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js
        created: function(){
            this.debounceGetAnswer = _.debounce(this.getAnswer, 500) ; //函数防抖
        }
        methods:{
            getAnswer(){}
        } 
    
  8. 列表渲染

    • v-for,必要提供key

    • 注意事项

                不能检测到以下 事件, 其他可以
                1.  当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
                2.  当你修改数组的长度时,例如:vm.items.length = newLength
                3.  不是响应式的
                    arr[0] = "xxx"
                    arr.length = x
                4,解决方法
      
                        this.$set(value, index,  newValue) ;  //Vue set
                        this.value.splice(0 , 1, newValue) ;  //修改原数组
        3,对象变更机制
                vm.b = "cc"  //后添加的属性  不是响应式的
      
    
        
    
  9. 组件之间相互不影响

    注册组件:全局注册、局部注册
                    全局注册
                Vue.component('xx', {
                    data: function(){} //必须是一个函数

                })
                在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以相互使用。可以被嵌套
                    局部注册
                let a = {
                    template: '<div></div'  //有且仅有一个父元素
                },b = {} ;
                new Vue({
                    components: {
                        a, b
                    }
                })
                局部注册的组件在其子组件中不可用
    

15,父子组件之间传递数据

1,props 传递, 自定义属性
        单向数据流  父 --> 子

    <v-title  title="xx"/>
        Vue.com....{
            props: ['title']
        }
    props: {
      title: String,
      likes: Number,
      isPublished: Boolean,
      commentIds: Array,
      author: Object,
      callback: Function,
      contactsPromise: Promise // or any other constructor
    }
    不能 更改 prop的值
    1,这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。
        在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
        props: ['initialCounter'],
        data: function () {
              return {
                counter: this.initialCounter
              }
        }
    2,这个 prop 以一种原始的值传入且需要进行转换。
        在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
        props: ['size'],
        computed: {
              normalizedSize: function () {
                return this.size.trim().toLowerCase()
              }
        }
    3,prop验证
            props:{
                data: [Number,String ...] ,
                data_t: {
                    type: String,
                    required: true,
                    default: 100
                }
            }
    4, 非prop的特性
            一个非prop特性指向一个组件,该属性加入到根元素上

    6,prop的双向绑定
        1,发布者订阅者模式
                父组件:订阅者  <component @my-event="change()"/>
                子组件:发布者  this.$emit('my-event', 参数)
                        change($event){
                            $event //子组件传过来的参数
                        }
        2,.sync 操作符
             不能和表达式一起使用::title.sync = "doc.title =  'xxx' " ;
             子组件:
                     this.$emit('update:title', 'Son') ;
             父组件:
                    <component :title.sync = "title"  />
                    原因: :title.sync 类似语法糖---> @update:title = "data = > title = data" :title = "title"

16,插槽 ( 内容分发的API,将slot元素作为承载分发内容的出口 )

1,<slot > :提供给子组件使用
    v-slot  slot和slot-scope  已被丢弃

    <component>
        <div>插入的内容</div>
    </component>

    <template>
        <slot></slot>
    </template>
2,编译作用域
    父级模板里的所有内容都是在父级作用域中编译的;
    子模板里的所有内容都是在子作用域中编译的。
    <div :data = "父"/>
3,后备内容
    <slot>
            <div>当父组件没传入时</div>
    </slot>
4,具名插槽
        子组件: <slot name="slot_name" />
        父组件:
                <component>
                    <template slot="slot_name">
                            内容
                    </template>
                </component>
5,作用域插槽
    子组件给父组件传值,父组件决定渲染方式时,用作用域插槽,父组件决定子组件中的元素以何种形式渲染
        子组件:<slot :rainy="age"></slot>
        父组件:
                <component>
                    <template slot-scope="props" slot="slot_name">
                            {{ props.rainy }}
                    </template>
                    类似:
                        <template v-slot: [slot_name] = "props">

                        </template>
                </component>
6,将插槽转化为可复用的模板
7,实战:插件封装、组件封装

17,动态组件

         1, <component is="组件名" />
                动态切换 is的值.......
             <keep-alive></keep-alive>  缓存组件
         2,异步组件
                只有该组件需要被渲染的时候才能 触发工厂函数,且会把结果缓存起来
                Vue.component('async-example', function (resolve, reject) {
                  setTimeout(function () {
                    // 向 `resolve` 回调传递组件定义
                    resolve({
                      template: '<div>I am async!</div>'
                    })
                  }, 1000)
                })

                Vue.component(
                  'async-webpack-example',
                  // 这个 `import` 函数会返回一个 `Promise` 对象。
                  () => import('./my-async-component')
                )

                new Vue({
                  // ...
                  components: {
                    'my-component': () => import('./my-async-component')
                  }
                })

19,自定义事件

 v-on 事件监听器在 DOM 模板中会被自动转换为全小写 ,html对大小写不敏感
    事件名
    this.$emit("myEvent",)
    <component @myEvent="" ></component>
    @myEvent 自动转化为 @myevent
    始终使用 kebab-case 的事件名。

20,处理边界情况

1,$root $parent $children
2, ref
    <component  ref="component_name"/>
    this.$refs  //数据传递,事件
    缺点:尽量不用,并不是响应式的
3,依赖注入
        provide inject
            多层嵌套组件使用,勿滥用,耦合了
        1, provide: 一个对象或返回一个对象的函数,该对象包含可注入起子孙的属性,可以使用ES6的Symbols作为key
        2, inject {  一个字符串数组或一个对象 }
             字符串数组,  provide对象里哪些属性可用
            一个对象
                    key是本地的绑定名,value是provide里对应的对象名,也可以是一个对象,
                    此时from属性是provide里对应的对象名,default属性是不存在时的默认值
        父组件:
            provide: function () {
              return {
                getMap: this.getMap,
                message: 'hello world'
              }
            }
        子组件:
            inject: ['getMap','message']

        实际上,你可以把依赖注入看作一部分“大范围有效的 prop”,除了:

        祖先组件不需要知道哪些后代组件使用它提供的属性
        后代组件不需要知道被注入的属性来自哪里

        3,强制更新 this.$forceUpdate()

19,混入

         1, 提供一种灵活的方式,分发Vue组件的可复用功能。一个混入对象可以包含任意组件选项。
            当组件使用混入对象时,所有混入对象的选项将被“复合”进入改组织本身的选项
            // 定义一个混入对象
            var myMixin = {
              created: function () {
                this.hello()
              },
              methods: {
                hello: function () {
                  console.log('hello from mixin!')
                }
              }
            }

            // 定义一个使用混入对象的组件
            var Component = Vue.extend({
              mixins: [myMixin]
            })

            var component = new Component() // => "hello from mixin!"
         2,选项合并
                当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。
                数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
                        mixin  Vue

                1,  data优先选择 Vue,覆盖mixin

                2,  钩子函数:不进行合并,优先运行mixin    合并为数组

                3,值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。
                        两个对象键名冲突时,取组件对象的键值对。

                        Vue.extend() 同样的规则
         3,全局混入
                Vue.mixin({
                    created: function(){

                    }
                })  //谨慎使用

20,自定义命令

                全局指令
         1,Vue.directive('命令',{
                inserted: function(el){
                    el //真实DOM
                }
             })
             局部指令
         2,directive:{
                focus:{
                    inserted: function(){

                    }
                }
            }
         3,钩子函数
                bind()          //初始化,仅调用一次
                inserted()      //被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
                update()        //所在组件的 VNode 更新时调用
                componentUpdated()  //指令所在组件的 VNode 及其子 VNode 全部更新后调用。
                unbind()        //解绑时,调用

                参数:
                    el:指令所绑定的元素,可以用来直接操作 DOM 。
                    binding:一个对象,包含以下属性:
                        name:指令名,不包括 v- 前缀。
                        rawName: 指令名,包含前缀
                        value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
                        oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
                        expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
                        arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
                        modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
                    vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
                    oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
         4,动态指令参数
                v-pin = "200 ..."

21,渲染函数

            render
          1,  Vue.component('anchored-heading',{
                props:{
                    level:{
                        type:Number,
                        required: true
                    }
                },
                //动态生成标题  this.$slots.default = vnode
                render: function(createElement){
                    return createElement('h' + this.level,this.$slots.default)  //  标签名称 子节点数组
                }

          });
          2,createElement 参数
                // @returns {VNode}
                createElement(
                  // {String | Object | Function}
                  // 一个 HTML 标签名、组件选项对象,或者
                  // resolve 了上述任何一种的一个 async 函数。必填项。
                  'div',

                  // {Object}
                  // 一个与模板中属性对应的数据对象。可选。
                  {
                    // (详情见下一节)
                  },

                  // {String | Array}
                  // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
                  // 也可以使用字符串来生成“文本虚拟节点”。可选。
                  [
                        '先写一些文字',
                        createElement('h1', '一则头条'),
                        createElement(MyComponent, {
                              props: {
                                someProp: 'foobar'
                              }
                        })
                  ]
            )

             this.$slots 访问静态插槽的内容,每个插槽都是一个 VNode 数组

22,插件

            通常用来为Vue添加全局功能。插件的功能范围没有严格限制
                1,添加全局方法或者属性。如: vue-custom-element

                2,添加全局资源:指令/过滤器/过渡等。如 vue-touch

                3,通过全局混入来添加一些组件选项。如 vue-router

                4,添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。

                5,一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router

            Vue.use(MyPlugin)
                1,通过全局方法 Vue.use() 使用插件。它需要在你调用 new Vue() 启动应用之前完成:
                2, 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件。
            开发插件
                1,暴露一个install方法,参数1: Vue构造器,参数2:可选的对象
                    MyPlugin.install = function (Vue, options) {
                          // 1. 添加全局方法或属性
                          Vue.myGlobalMethod = function () {
                            // 逻辑...
                          }

                          // 2. 添加全局资源
                          Vue.directive('my-directive', {
                            bind (el, binding, vnode, oldVnode) {
                              // 逻辑...
                            }
                            ...
                          })

                          // 3. 注入组件选项
                          Vue.mixin({
                            created: function () {
                              // 逻辑...
                            }
                            ...
                          })

                          // 4. 添加实例方法
                          Vue.prototype.$myMethod = function (methodOptions) {
                            // 逻辑...
                          }
                    }

23,过滤器

            【注意】: 过滤的是值,是变量的值

            Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。
            过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。
            过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:
            <!-- 在双花括号中 -->
            {{ message | capitalize }}

            <!-- 在 `v-bind` 中 -->
            <div v-bind:id="rawId | formatId"></div>
            filters: {
                capitalize: function (value) {
                    if (!value) return ''
                    value = value.toString()
                    return value.charAt(0).toUpperCase() + value.slice(1)
                }
            }
            或者 全局注册
            Vue.filter('capitalize', function (value) { //value 绑定的值
              if (!value) return ''
              value = value.toString()
              return value.charAt(0).toUpperCase() + value.slice(1)
            })