vue2-3笔记

26.vue源码分析
    1.用到的js知识
        1)伪数组如何转成真数组
            const list2 = [...list1]
            const list3 = Array.from(list1)
            const list4 = Array.prototype.slice.call(list1) //改变数组方法的调用者,并将伪数组的元素全部截取并返回
!!            const list5 = [].slice.call(list1)
        2)节点类型
            console.log(eleNode.nodeType)  // 1 标签
            console.log(attrNode.nodeType)  // 2 属性
            console.log(txtNode.nodeType)  // 3 文本
        3) Object.definePrototype()方法---为对象添加属性并配置属性特性
            const person = {
              firstName: '东方',
              lastName: '华哥'
            }
            Object.defineProperty(person, 'fullName', {
              configurable: true, // 默认是false,不能被重设设置,是否可以被删除
              enumerable: false, // 默认是false,是否可以枚举
              // value:'哈_哈', // 该属性的默认值
              // writable:false , // 默认是false, 是否可以被重写
!!              get() {// 要使用get()和set()就不能设置默认值和writable
                // console.log(person.fullName) 就会进入到get
                return this.firstName + '_' + this.lastName
              },
              set(val) {
                // 什么时候会进入到set方法:   person.fullName='西门_华哥'
                const name = val.split('_')
                this.firstName = name[0]
                this.lastName = name[1]
              }
            })
        4) Object.keys()方法:将对象的key取出来放在数组中并返回
            const keys = Object.keys(person)
        5) 对象.hasOwnProperty()方法,判断当前对象中是否包含这个私有属性
            console.log(person.hasOwnProperty('fullName'))  // true
            console.log(person.hasOwnProperty('toString'))  // false
        6) 文档碎片对象模型----DoucmentFragment---(高效的批量处理多个节点)
            // 要求.通过文档碎片对象,把html标签容器中的内容更新
        
            // 1. 创建文档碎片对象
            var fragment = document.createDocumentFragment()
            // 2. 获取容器对象
            var divObj = document.getElementById('demo')
            // 3. 把容器对象中的所有的节点全部的存放在文档碎片对象中
            var child
            while (child = divObj.firstChild) { // 相当于剪切操作
              fragment.appendChild(child)
            }
            // 4. 遍历文档碎片对象中的节点,进行内容更新
            fragment.childNodes.forEach(node=>{
              node.innerHTML='我才是最帅的'
            })
            // 5. 把文档碎片对象放在容器中即可
            divObj.appendChild(fragment)
    2.vue源码文件
        1.mvvm.js文件
            1)MVVM:创建vue实例的构造函数
                1.调用方法实现数据代理:调用Object.keys()/原型上的_proxyData()方法将data对象的属性交给vue实例来代理
                2.调用方法实现数据劫持:模板解析前调用observer.js文件中的observe()方法劫持data对象的属性
                3.调用方法实现模板解析:调用原型上的$compile()方法解析模板
            2)MVVM.prototype原型
                1._proxyData():通过Object.defineProperty()方法把data对象中的每个属性添加到Vue的实例对象上,,并重写了Object.defineProperty()方法的set和get方法
        2.observer.js文件
            1)observe函数:判断传递过来的配置对象的data属性有没有值,或者是不是对象,创建Observer实例对象
            2)Observer构造函数
                1.把data对象的属性添加到Observer实例对象上
                2.调用原型的wall()方法,开始劫持数据,创建dep对象,并且当Dep.target中如果有值,就创建dep对象和Watcher实例对象的依赖关系
            3)Observer.prototype原型
                1.walk()方法:遍历data对象的属性的key
                2.convert()方法:内部调用劫持数据的方法
                3.defineReactive()方法
                    1)调用Dep构造函数创建dep实例对象,每个dep实例对象都有唯一的id标识和一个专门用来保存watcher实例对象的空数组subs
                    2)实现劫持数据,并重写了Object.defineProperty()方法的set和get方法
            4)Dep构造函数:
                1.创建dep实例对象,每个dep实例对象都有唯一的id标识,和一个专门用来保存watcher实例对象的空数组subs
                2.有一个target属性专门用来储存Watcher的实例对象,来建立dep对象和Watcher实例对象的依赖关系
            5)Dep.prototype原型
                1.depend():建立dep对象和Watcher实例对象的依赖关系
                2.addSub():建立dep对象和Watcher实例对象的依赖关系
                3.notify(): data对象属性发生变化通知watcher对象更新数据
        3.watcher.js文件
            1)Watcher构造函数:将表达式的值添加到watcher实例对象上
            2)Watcher.prototype原型
                1.parseGetter():返回获取表达式的值的函数
                2.get():调用获取表达式的值的函数来获取表达式的值
                3.addDep():建立dep对象和Watcher实例对象的依赖关系(把dep的id和dep对象以键值对的方式添加到watcher对象的depIds对象中)
                4.update(): data对象属性发生变化,watcher对象更新数据操作
        4.compile.js文件
            1)Compile编译对象构造函数:
                1.创建文档碎片对象,并把容器对象中所有的节点全都存放在文档碎片对象
                2.模版解析
                3.把模版解析后的文档碎片对象放在容器对象中
            2)Compile.prototype原型
                1.node2Fragment():创建文档碎片对象,并把容器对象中所有的节点全都存放在文档碎片对象,返回文档碎片对象
                2.init():内部调用真正解析模板的方法
                3.compileElement():真正解析模板的方法
                4.isElementNode():判断当前的节点是不是标签
                5.compile():判断标签属性是否是指令,指令是事件指令还是普通指令
                6.isDirective():判断是否是指令
                7.isEventDirective():判断当前的指令是不是事件指令
                8.isTextNode():判断当前的节点是不是文本            
                9.compileText():调用compileUtil方法来解析文本            
            3)compileUtil工具对象
                1.text属性:执行v-text指令的准备工作,内部会调用v-bind指令
                2.html属性:执行v-html指令的准备工作,内部会调用v-bind指令
                3.class属性:执行v-class指令的准备工作,内部会调用v-bind指令
                4.model属性:执行v-model指令的准备工作,内部会调用v-bind指令
                5.bind属性:获取表达式的值,创建Watcher实例对象
                6.eventHandler属性:给标签节点添加事件
                7._getVMVal:获取表达式的值,展示在页面上
            4)updater更新对象
                1.textUpdater属性:执行v-text指令
                2.htmlUpdater属性:执行v-html指令
                3.classUpdater属性:执行v-class指令
                4.modelUpdater属性:执行v-model指令
    3.剖析vue功能
        1)数据代理: 某个对象的属性,可以通过其他对象来访问,在Vue中是有数据代理,Vue的实例对象代理了data对象的属性
            创建Vue的实例对象的时候,把data对象中所有数据通过Object.keys()进行遍历,内部调用Object.defineProperty()把data对象中所有的数据一个一个添加到vm实例对象上,
            vm对象可以直接访问data对象中的数据了,vm代理了data,data是被代理者
        2)数据劫持    
            当Vue中数据代理结束后,就开始数据劫持,通过observe()方法开始进行数据的劫持,判断data是一个对象后,创建劫持的实例对象,内部遍历vm的data对象,然后把vm的data对象中所有的数据一个一个添加到劫持对象的data对象上,当前在正式添加之前,创建dep对象(id,subs数组),
            只要vm中的data对象有多少个属性就会创建多少个dep对象(将来和watcher建立关系)
        3)模板解析:把页面中html模版里面使用到的表达式(插值语法/事件指令/一般指令),解析为真正的数据的操作,并渲染界面
            1)在创建Vue实例对象时候,数据代理和数据劫持后,开始模版解析,会在MVVM的对象中实例化Compile对象
            2)Compile内部会把当前的Vue实例对象控制的容器对象中所有的节点全都存放在文档碎片对象中(文档碎片对象可以高效的批量操作DOM节点,在内存中进行节点的操作,这是所谓的虚拟DOM)
            3)取出文档碎片对象所有的子节点进行遍历,如果是文本节点,并且符合插值语法的正则,就要调用CompileUtil对象中的相关方法进而调用bind方法,
                 然后在调用updater对象中相关的方法把当前节点用到的表达式进行数据的替换,最后渲染页面即可
                 
            4)取出文档碎片对象所有的子节点进行遍历,如果当前的节点是标签节点,取出当前标签节点的所有属性,遍历所有属性
                 然后判断每个属性是不是Vue中的指令(以v-开头),然后判断当前的指令是事件指令(v-后面是:on)还是一般指令
            5)如果是事件指令,就把当前这个指令进行字符串切割,获取事件类型,还要拿着事件回调函数名去vue实例对象的methods属性中找对应的回调函数,
                 然后通过addEventListener方法为当前的标签节点绑定对应的事件,并将事件回调函数的this指向vue实例对象,最后在通过removeAttribute()移除当前标签节点的所有属性,最后渲染页面
            6)如果是普通指令,调用CompileUtil中的相关方法,进而调用bind方法,然后在调用updater对象中相关的方法把当前节点用到的表达式进行数据的替换,最后渲染页面即可
                1.updater对象中如何执行v-text和v-html指令?
                    通过文本节点的textContent和innerHTML属性替换表达式值
                2.updater对象中如何执行v-class指令?
                    获取当前标签节点的类样式的名字,如果有类名就添加一个空格在拼接上表达式的值,在把最终值添加给当前标签节点的类样式
                    
            7)模板解析中,当在内存中成功替换表达式的值之后,bind方法内部会创建watcher对象,会根据表达式的个数来创建对应个数的watcher对象    
                进入到watcher内部后,会调用get方法,进而完成开始建立dep对象和watcher对象的关系,进来会监视data对象属性的变化    
                dep和watcher的关系类型:
                    1对1的关系:1个dep对应一个watcher,data中只有一个属性,模版中只用了一个表达式
                    1对多的关系:1个dep对应多个watcher,data中只有一个属性,模版中用了多个表达式
                          多对1的关系:多个dep对应1个watcher,data中有多个属性,模版中用了一个表达式(data属性是对象,表达式:对象.属性)
                          多对多的关系:多个dep对应多个watcher,data中有多个属性,模版中用了多个表达式(data属性是对象,表达式:对象.属性)                                                                      
        4)双向数据绑定
                  创建Vue的实例的时候,除了数据代理和数据劫持以外,会进入到compile模版解析中,在内存中创建文档碎片对象,把html容器中所有的子级节点全部的存放在文档碎片对象中,
                  遍历所有的节点判断当前的节点是不是标签,然后获取当前节点标签中所有的属性,判断当前的属性是不是指令,然后再判断当前的指令是不是普通指令v-model,是的话就把把表达式的值赋值给节点value属性,再然后为当前的节点标签绑定input事件
                  如果标签中的数据发生变化,此时触发input事件,判断表达式之前的数据和现在输入的数据是否不同,之后会进入到MVVM的set方法内部再进入到observer.js的set方法内部,
                  根据当前的这个属性的dep对象通知当前dep对象中subs数组中的watcher进行数据的更新操作    
            (vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的)
            
27.vue-property-decorator(简化ts在vue中的写法)
    vue-property-decorator是在Vue中使用TypeScript时,非常好用的一个库,使用装饰器来简化书写。(装饰器的作用就是接收vue组件,返回处理过后的vue组件)
    该库完全依赖于vue-class-component,因此在使用该库之前,请先阅读vue-class-component库。
    1)@component装饰器(来源于vue-class-component)
        @Component({
            name:'',
            components:{},
            filters:{},
            directives:{}
        })
        @Component装饰器可以接收一个对象作为参数,可以在对象中声明name , components ,mixins,filters,directives等装饰器选项,
        也可以声明computed,watch等,但并不推荐这么做,因为在访问this时,编译器会给出错误提示
        // 为了使用TypeScript,需要在script标签上添加 lang = ts
        <script lang="ts">
            import { Component, Vue } from 'vue-property-decorator'
            import User from '@/components/User.vue'
            // 还可添加directives字段
            @Component({
              name: 'HelloWorld', // name属性可以省略,效果是一样的,因为class HelloWorld extends Vue实现了vue组件名为HelloWorld
              components: {
                User
              }
            })
            export default class HelloWorld extends Vue {
            }
        </script>   
        // 等价于
        <script>
            import User from '@/components/User.vue'
            export default {
              name: 'HelloWorld',
              components: {
                User
              }
            })
        </script> 
    2)@Prop, Data, methods, computed, watch
        1.使用props
            //前面类型大写,后面可以小写
            @Prop(options: (PropOptions | Constructor[] | Constructor) = {})
            @Prop(String)
            @Prop([String,Number])
            @Prop({type: String, default: 'Developer',required: false})
            @Prop装饰器接收一个参数,这个参数可以有三种写法(字符串、数组、对象),可以添加 type, default, required为props指定验证要求,同样也可以使用 readonly 禁止操作
            <script>
                import { Component, Prop, Vue } from 'vue-property-decorator'
                @Component
                export default class HelloWorld extends Vue {
                  //属性的ts类型后面需要加上undefined类型;或者在属性名后面加上!,表示非null 和 非undefined 的断言,否则编译器会给出错误提示
                  //@Prop()限制的是其它组件传递过来的属性,propA!: string限制的是组件内接收的属性,一般@Prop()和propA!: string的限制一样
                  @Prop() propA!: string
                  @Prop(String) propB:string|undefined;
                  @Prop([String,Number]) propC!:string|number;
                  @Prop({type: String, default: 'Developer',required: false}) readonly propD!: string                                    
                }
            </script>
            // 等价于
            export default {
              props: {
                propA,
                propB:String,
                propC:[String,Number],
                propD: {
                  type: string,
                  default: 'Developer',
                  required: false
                }
              }
            }    
        2.使用data(data数据可以声明为类属性)
            export default class HelloWorld extends Vue {
              private msg: string = "welcome to my app"
              private list: Array<object> = [
                {
                    name: 'Melody',
                    age: '20'
                },
                {
                    name: 'James',
                    age: '20'
                }
              ]
            }    
            //等价于
            export default {
              data() {
                return {
                  msg: "welcome to my app",
                  list: [
                    {
                      name: 'Melody',
                      age: '20'
                    },
                    {
                      name: 'James',
                      age: '20'
                    }
                  ]
                }
            }    
        3.使用Computed 属性(computed属性可以声明为类属性访问器)
            export default class HelloWorld extends Vue {
              get fullName(): string {
                return this.first+ ' '+ this.last
              }
            }
            // 等价于
            export default {
                computed:{
                    fullName() {
                        return this.first + ' ' + this.last
                    }
                }
            }
            
            当需要写一个稍微复杂点的涉及到setter和getter的 computed属性时,在ts中写法如下
            export default class HelloWorld extends Vue {
              get fullName(): string {
                return this.first+ ' '+ this.last
              }
              set fullName(newValue: string) {
                let names = newValue.split(' ')
                this.first = names[0]
                this.last = names[names.length - 1]
              }
            }
            //等价于
            export default {
                computed:{
                    fullName: {
                      get: function () {
                        return this.first + ' ' + this.last
                      },
                      set: function (newValue) {
                        let names = newValue.split(' ')
                        this.first = names[0]
                        this.last = names[names.length - 1]
                      }
                    }

                }
            }
        4.使用watch属性
            @Watch(path: string, options: WatchOptions = {}) 
            @Watch 装饰器接收两个参数
                path:被侦听的属性名
                options:可以包含两个属性            
                    immediate:侦听开始之后是否立即调用该回调函数
                    deep:是否开启深度监视
                    侦听开始,发生在beforeCreate勾子之后,created勾子之前
                                    
            @Watch('child')
            onChildChanged (val: string, oldVal: string) {
                if (val !== oldVal) {
                  window.console.log(val)
                }
            }
            //等价于
            watch: {
                'child': {
                    handler: 'onChildChanged',
                    immediate: false,
                    deep: false 
                }
            },
            method: {
                onChildChanged(val, oldVal) {
                    if (val !== oldVal) {
                      window.console.log(val)
                    }
                }
            }
        5.使用method属性(methods可以直接声明为类成员方法)
            export default class HelloWorld extends Vue {
              public clickMe(): void {
                console.log('clicked')
                console.log(this.addNum(4, 2))
              }
              public addNum(num1: number, num2: number): number {
                return num1 + num2
              }
            }
            //等价于
            export default {
              methods: {
                clickMe() {
                  console.log('clicked')
                  console.log(this.addNum(4, 2))
                }
                addNum(num1, num2) {
                  return num1 + num2
                }
              }
            }
        6.Lifecycle hooks(生命周期函数)
            所有Vue生命周期挂钩也可以直接声明为类成员方法
            export default class HelloWorld extends Vue {
              mounted() {}
              beforeUpdate() {}
            }
            //等价于
            export default {
              mounted() {}
              beforeUpdate() {}
            }
    3)@Emit 装饰器
        子组件触发父组件的自定义事件并传递数据,在TypeScript中使用@Emit 装饰器
        @Emit(event?: string)
        @Emit 装饰器接收一个可选参数,该参数是$Emit的第一个参数,充当事件名。如果没有提供这个参数,$Emit会将回调函数名的camelCase转为kebab-case,并将其作为事件名
        @Emit会将回调函数的返回值作为第二个参数,如果返回值是一个Promise对象,$emit会在Promise对象被标记为resolved之后触发,promise的结果值作为第二个参数
        @Emit的回调函数的参数,会放在其返回值之后,一起被$emit当做参数使用

        1.@Emit()没有指定参数,相当于触发'add-to-count',addToCount函数参数为传递给'add-to-count'的数据
            import { Vue, Component, Emit } from 'vue-property-decorator'
            @Component
            export default class YourComponent extends Vue {
                @Emit()
                addToCount(n: number) {
                  this.count += n
                }
            }            
            //等价于
            export default {
              methods: {
                addToCount(n) {
                  this.count += n
                  this.$emit('add-to-count', n)
                }
              }
            }
        2.@Emit()没有指定参数,相当于触发'return-value',returnValue函数返回值为传递给'return-value'的数据
            @Emit()
            returnValue() {
              return 10
            }
            returnValue() {
                this.$emit('return-value', 10)
            }
        3.@Emit()没有指定参数,相当于触发'on-input-change',onInputChange函数返回值和参数为传递给'return-value'的数据
            @Emit()
            onInputChange(e) {
              return e.target.value
            }
            onInputChange(e) {
              this.$emit('on-input-change', e.target.value, e)
            }
        4.@Emit()没有参数并且返回一个Promise对象,Promise对象的结果值为传递给'promise'的数据
            @Emit()
            promise() {
              return new Promise(resolve => {
                resolve(20)
              })
            }
            //等价于
            promise() {
              const promise = new Promise(resolve => {
                resolve(20)
              })
        
              promise.then(value => {
                this.$emit('promise', value)
              })
            }
        5.@Emit()有参数,那么触发'reset'
            @Emit('reset')
            resetCount() {
              this.count = 0
            }
            //等价于
            resetCount() {
              this.count = 0
              this.$emit('reset')
            }                                                        
    4)vuex
        1.首先安装
            npm install vuex-module-decorators -D
            npm install vuex-class -D
        2.如果想通过名字空间的形式来使用module, 需在@Module装饰器中添加额外的参数. 
             例如, 以下示例代码中添加一个namespaced为user的module
            import { VuexModule, Module, Mutation, Action } from 'vuex-module-decorators'
            @Module({ namespaced: true, name: 'user' })
            class User extends VuexModule {
              public name: string = ''
              @Mutation
              public setName(newName: string): void {
                this.name = newName
              }
              @Action
              public updateName(newName: string): void {
                this.context.commit('setName', newName)
              }
            }
            export default User
            注意:@Module装饰器的属性字段name值, 必须与new store({modules: {}})中注册module name名称保持一致
            //等价于
            export default {
              namespaced: true,
              state: {
                name: ''
              },
              mutations: {
                setName(state, newName) {
                  state.name = newName
                }
              },
              actions: {
                updateName(context, newName) {
                  context.commit('setName', newName)
                }
              }
            }
        3.在组件里面使用vuex
            要使用Vuex,可以使用vuex-class库。该库提供了装饰器,可以在Vue组件中绑定State,Getter,Mutation和Action。
            由于已经使用了命名空间的Vuex模块,因此我们首先从vuex-class导入命名空间,然后传递模块名称以访问该模块
            import { Component, Vue } from 'vue-property-decorator'
            import { namespace } from 'vuex-class'
            const user = namespace('user')
            @Component
            export default class User extends Vue {
              @user.State
              public name!: string
            
              @user.Getter
              public nameUpperCase!: string
            
              @user.Action
              public updateName!: (newName: string) => void
            }
            //等价于
            import { mapState, mapGetters, mapActions} from 'vuex'
            export default  {
              computed: {
                ...mapState('user', ['name']),
                ...mapGetters('user', ['nameUpperCase'])
              }  
              methods: {
                ...mapActions('user', ['updateName'])
              }
            }
    5.其它
        1)@Provide 提供 / @Inject 注入
            与 React的context组件间通信相似,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代传递数据,
            数据通过Provide传递下去,然后子组件通过Inject来获取
            import { Component, Inject, Provide, Vue } from 'vue-property-decorator'
            const symbol = Symbol('baz')            
            @Component
            export class MyComponent extends Vue {
              @Inject() foo!: string
              @Inject('bar') bar!: string
              @Inject({ from: 'optional', default: 'default' }) optional!: string
              @Inject(symbol) readonly baz!: string
            
              @Provide() foo = 'foo'
              @Provide('bar') baz = 'bar'
            }
            //等价于
            const symbol = Symbol('baz')
            export const MyComponent = Vue.extend({
              inject: {
                foo: 'foo',
                bar: 'bar',
                optional: { from: 'optional', default: 'default' },
                baz: symbol,
              },
              data() {
                return {
                  foo: 'foo',
                  baz: 'bar',
                }
              },
              provide() {
                return {
                  foo: this.foo,
                  bar: this.baz,
                }
              },
            })
        2)@Ref(refKey?: string)
            @Ref 装饰器接收一个可选参数,用来指向元素或子组件的引用信息。如果没有提供这个参数,会使用装饰器后面的属性名充当参数
            import { Vue, Component, Ref } from 'vue-property-decorator'
            import { Form } from 'element-ui'
            @Componentexport default class MyComponent extends Vue {
              @Ref() readonly loginForm!: Form
              @Ref('changePasswordForm') readonly passwordForm!: Form            
              public handleLogin() {
                this.loginForm.validate(valide => {
                  if (valide) {
                    // login...
                  } else {
                    // error tips
                  }
                })
              }
            }
            //等价于
            export default {
              computed: {
                loginForm: {
                  cache: false,
                  get() {
                    return this.$refs.loginForm
                  }
                },
                passwordForm: {
                  cache: false,
                  get() {
                    return this.$refs.changePasswordForm
                  }
                }
              }
            }
        3)@Model装饰器允许我们在一个组件上自定义v-model
            @Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})
            event:事件名。
            options:与@Prop的第一个参数一致,可以是字符串、数组、对象类型
            下面例子中指定的是change事件,所以我们还需要在template中加上相应的事件:
            <template>
              <input
                type="text"
                :value="value"
                @change="$emit('change', $event.target.value)"
              />
            </template>
            import { Vue, Component, Model } from 'vue-property-decorator'            
            @Component
            export default class YourComponent extends Vue {
              @Model('change', { type: Boolean }) readonly checked!: boolean
            }
            //等价于
            export default {
              model: {
                prop: 'checked',
                event: 'change',
              },
              props: {
                checked: {
                  type: Boolean,
                },
              },
            }
        4)Mixins
            假设当前已经有一个mixins/ProjectMixin文件 如何在其他组件里面使用方式如下
            import { Component, Vue, Mixins } from 'vue-property-decorator'
            import ProjectMixin from '@/mixins/ProjectMixin'
            @Component
            export default class Project extends Mixins(ProjectMixin) {
              get projectDetail(): string {
                return this.projName + ' ' + 'HS'
              }
            }
            //等价于
            import ProjectMixin from '@/mixins/ProjectMixin'
            export default {
              mixins: [ ProjectMixin ],
              computed: {
                projectDetail() {
                  return this.projName + ' ' + 'HS'
                }
              }
            }    
        5)@PropSync装饰器与@prop用法类似,二者的区别在于:
            @PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})装饰器接收两个参数:
            propName:表示父组件传递过来的属性名
            options:与@Prop的第一个参数一致,可以是字符串、数组、对象类型
            @PropSync 会生成一个新的计算属性。    
                
            import { Vue, Component, PropSync } from 'vue-property-decorator'
            @Component
            export default class MyComponent extends Vue {
              @PropSync('propA', { type: String, default: 'abc' }) public syncedPropA!: string
            }  
            //等价于
            export default {
              props: {
                propA: {
                  type: String,
                  default: 'abc'
                }
              },
              computed: {
                syncedPropA: {
                  get() {
                    return this.propA
                  },
                  set(value) {
                    this.$emit('update:propA', value)
                  }
                }
              }
            } 
            注意:@PropSync需要配合父组件的.sync修饰符使用 
    6.总结
        1)methods可以直接声明为类成员方法(使用public关键字)
        2)computed属性可以声明为类属性访问器(get/set)
        3)data数据可以声明为类属性(使用private关键字)
        4)render函数和所有Vue生命周期挂钩也可以直接声明为类成员方法,但不能在实例本身上调用它们。

28.服务器端渲染 (SSR)
    将一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
    1)为什么使用服务器端渲染
        1.更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
        2.更快的内容到达时间,无需等待所有的 JavaScript都完成下载并执行,才显示服务器渲染的标记,所以你的用户将会更快速地看到完整渲染的页面。
        3.服务端渲染, 解决首屏加载速度, 和 seo问题(总结)
    2)服务端渲染实例
        // 第 1 步:创建一个 Vue 实例
        const Vue = require('vue')
        const app = new Vue({
          template: `<div>{{myname}}-{{myage}}</div>`,
          data:{
              myname:"kerwin",
              myage:100
          }
        })
        // 第 2 步:创建一个 renderer
        const renderer = require('vue-server-renderer').createRenderer()
        // 第 3 步:将 Vue 实例渲染为 HTML
        // 旧写法
        // renderer.renderToString(app, (err, html) => {
        //   if (err) throw err
        //   console.log(html)
        //   // => <div data-server-rendered="true">Hello World</div>
        // })
        
        // 新写法,在 2.5.0+,如果没有传入回调函数,则会返回 Promise:
        renderer.renderToString(app).then(html => {
          console.log(html)
        }).catch(err => {
          console.error(err)
        })
    3)Nuxt.js
        Nuxt.js是一个做vue ssr(服务端渲染)的框架
        Nuxt.js工作流:
        Incoming Request(客户端发送请求给客户端)-->nuxtServerLint(store action)(服务端检测有没有nuxtServerLint这个配置,有的话就执行这个函数用来操作vuex)
        -->middleware(操作路由相关的功能)-->validate(路由校验)-->asyncData(vue组件数据)和fetch(vuex数据)-->render(将数据渲染到页面)-->navigate(nuxt link)(组件中调用nuxt link重新从middleware开始循环)
        npm i -g create-nuxt-app
        create-nuxt-app 项目名
        //下面这个命令等价于上面两个命令,npx会检测全局和局部有没有create-nuxt-app命令,有的话就直接创建项目,没有的
        //话就先在本地下载create-nuxt-app命令,再去创建项目
        npx create-nuxt-app 项目名
        1.项目结构
            .nuxt
            assets        放静态资源
            components    放全局公共组件
            layouts        布局组件模板
            middleware    中间件做路由拦截
            pages        放路由组件
            plugins        放插件,如elememt-ui
            server        服务端配置
            static        放静态资源,小图标等
            store        放vuex
            nuxt.config.js(nuxt配置)
        2.路由
            1)Nuxt.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置
            2)路口文件(layouts/default.vue)
            3)<nuxt-link>等价于<router-link/>,支持activeClass(通过activeClass给标签加一个类名,在样式中写触发时高亮样式),tag
              <nuxt/>等价于<router-view>
            4)嵌套路由
                1.在pages中创建一个与一级路由相同文件名的文件夹来放该路由的子路由
                2.在父组件中通过<nuxt-child>标签来显示子路由
            5)路由重定向
                1.配置nuxt.config.js文件
                    router:{
                        extendRoutes(routes){
                          routes.push({
                            path:"/",
                            redirect:'/film/nowplaying'
                          })
                        }
                      }
                2.利用中间件来处理
                    //middleware/redirect.js(名字可改)文件中
                    export default function({ isHMR,app,store,route,params,error,redirect })  {
                       if (isHMR) return
                       if(route.fullPath == '/film') {
                          return redirect('/film/nowplaying')
                       }
                    }
                    //配置nuxt.config.js文件
                    router: { 
                        middleware: 'redirect' // 即每次路由跳转会调用该中间件 ,redirect是文件名
                        //middleware: ['redirect'] //多个中间件写法
                    }
            6)动态路由
                1.创建
                    在pages创建路由文件夹,文件夹里面以下划线开头,下划线后面是路由参数
                    pages
                    ‐‐| detail(文件夹,不需要在外面创建.vue文件)
                    ‐‐‐‐‐| _id.vue
                2.获取路由参数        
                    this.$router.query.id
            7)视图
                在layout 里面 写好default.vue 可以认为这是根组件的模板了, 所有的组件都加在里面,但是有些页面 可能不一样,就可以使用 个性化定制页面。
                export default { 
                    layout: 'template' 
                }
            8)服务端渲染异步数据(ssr)
                  服务端渲染的异步数据可以通过右键查源码查到数据,而客户端渲染的异步数据则不可以
                  data() {
                    return {
                      list: []
                    }
                  },
                // 在asyncData()方法中实现服务端渲染异步数据,return回来的list会给data的list
                // 在当前页面刷新, 服务端执行此函数,从其他页面跳转过来,客户端执行此函数
                // data中可以拿到route、query、params等数据
                // 只有页面级别组件才有
                async asyncData(data) {
                    let {status, data: {list}} = await axios.get('http://localhost:3000/city/list')
                    if (status === 200) {
                      return {
                        list
                      }
                    }
                  }
                或者
                asyncData(data) {
                    return axios({
                      url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6341699
                    }).then(res=>{
                      return {
                        datalist:res.data.data.films
                      }// 状态
                    })
                  }
            9)反向代理解决跨域
                1.npm i @nuxtjs/proxy ‐D
                2.在 nuxt.config.js 配置文件中添加对应的模块,并设置代理
                    modules: [
                        '@nuxtjs/axios',
                        '@nuxtjs/proxy'
                      ],
                      axios: {
                        proxy:true
                      },
                      proxy:{
                          //拦截/ajax开头的请求,拼接到http://m.maoyan.com上
                        '/ajax':{
                          target:"http://m.maoyan.com",
                          changeOrigin:true
                        }
                      },
                3.在组件中,上面代理只能解决前端跨域问题,但是如果asyncData函数在后端执行会出现请求路径不全问题
                    asyncData(data) {
                        return axios({
                          //process.server为true说明asyncData函数在服务端执行
                          url:process.server?"http://m.maoyan.com/ajax/movieOnInfoList?token=":"/ajax/movieOnInfoList?token=",
                        }).then(res=>{
                          console.log(res.data);
                          return {
                            datalist:res.data.movieList
                          }// 状态
                        })
                      }
                10)vuex在nuxt.js中的使用
                    直接在项目中创建store文件夹,配置store,nuxt.js会自动引入store,我们可以直接在组件中使用
                    const store = () => new Vuex.Store({
                      modules: {
                        city,
                        navbar
                      },
                      // 注意store中actions不能省略,nuxt.js会执行nuxtServerInit()方法实现vuex数据的服务端渲染
                      actions: {
                        // nuxtServerInit({ commit }, { req }) {
                        //   if (req.session.user) {
                        //     commit('city', req.session.user)
                        //   }
                        // }
                      }
                    })

29性能优化
    1)开发过程
        1.优先使用vIf
            v-if是将元素删除来达到隐藏的效果,v-show是将元素display:none来达到隐藏的效果,如果需要频繁切换才使用v-show
        2.vFor key避免使用index作为标识
            当index作为标识的时候,插入一条数据的时候,列表中它后面的key都发生了变化,那么当前的 vFor 都会对key变化的 Element 重新渲染,
            但是其实它们除了插入的 Element 数据都没有发生改变,这就导致了没有必要的开销。
        3.释放组件资源
            每创建出一个事物都需要消耗资源,资源不是凭空产生的,是分配出来的。所以说,当组件销毁后,尽量把我们开辟出来的资源块给销毁掉,
            比如 setInterval , addEventListener等
        4.长列表
            长列表渲染的时候,建议将DOM移除掉,类似于图片懒加载的模式,只有出现在视图上的DOM才是重要的DOM。网络上有一些很好的解决方案,如 vue-virtual-scroller 库等等
        5.图片合理的优化方式
            图片应该都不陌生吧,在网页中,往往存在大量的图片资源,这些资源或大或小。当我们页面中DOM中存在大量的图片时,
            难免不会碰到一些加载缓慢的问题,导致图片出现加载失败的问题。网络上大部分都在使用 懒加载 的使用方式,
            只有当 存在图片的DOM 出现在页面上才会进行图片的加载,无形中起到了分流的作用,下面就说一套实践的方案吧
            1)小图标使用 SVG 或者字体图标
            2)通过 base64 和 webp  的方式加载小型图片
            3)能通过cdn加速的大图尽量用cdn
            4)大部分框架都带有懒加载的图片
        6.使用路由懒加载
            component: () => import('@/components/HelloWorld')
        7.第三方模块(UI/工具插件)按需导入
        8.SPA 页面采用keep-alive缓存组件
        9.防抖、节流
        10.首屏优化
            众所周知,第一次打开Vue的时候,如果你的项目够大,那么首次加载资源时,会非常的久。由于资源没有加载完毕,
            界面的DOM也不会渲染,会造成白屏的问题。用户此时并不知道是加载的问题,所以会带来一个不好的体验。
            因此通常会在public下写一个加载动画,告诉用户,网页在加载中这个提示。当页面加载成功后,页面渲染出来的这一个体验比白屏等开机要好太多了。
        11.静态模板尽量使用函数组件替代普通组件
        12.render函数只要数据一改变就会触发,所以数据的处理尽量放在render函数外面,render函数里面的方法提前定义在外面
                因为方法是执行render函数临时创建的。消耗性能    
        13.结构显示隐藏加动画
    2)webpack
        1.优化打包构建速度
            1) HMR 热模块替换
            2) cache 缓存(针对js)
            3) oneOf            
            4) 多进程打包        
        2.优化打包代码体积和性能
            1) 兼容性处理    
            2) tree shaking 树摇
            3) code split 代码分割 / lazy loading 懒加载    
            4) preload 和 prefetch 预加载             
            5) cache 缓存(浏览器缓存)        
            6) PWA 渐进式网络应用程序(离线加载技术)
    3)其它
        1.服务端渲染SSR
            1)vuex中
                actions: {
                    //app相当于实例对象
                    nuxtServerInit({commit}, {req, app}) {
                    }
                }
            2)组件内
                //ctx相当于实例对象
                asyncData(ctx){}
        2.还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。
            //熊健静态服务器/utils/compress.js文件
            const { createGzip, createDeflate } = require("zlib");
            // 设置响应头,告诉客户端,内容经过了压缩
            res.setHeader("Content-Encoding", "gzip");
            // rs可读流会将数据读取传给gzip压缩,返回值还是一个rs(可读流中的数据已经压缩了~)
            rs = rs.pipe(createGzip());
        3.依赖库CDN加速
            看到有小伙伴使用CDN的方式引入一些依赖包,觉得非常的 Nice ,然后我也开始使用了。我将 Vue Axios Echarts 等等都分离了出来,
            在正式环境下,通过CDN,确实有了一些明显的提升,所以说大家可以进行尝试。
            // 在html引入script标签后。在vue的配置中,进行声明
            configureWebpack: {
              externals: {
                'echarts': 'echarts' // 配置使用CDN
              }
            }
            
30.项目结构分析
    gshop(脚手架2)
        |-- build : webpack 相关的配置文件夹(基本不需要修改) 
        |-- config: webpack 相关的配置文件夹(基本不需要修改) 
            |-- index.js: 指定后台服务的端口号和静态资源文件夹 
        |-- node_modules 
        |-- src 
            |-- components------------非路由组件文件夹 
                |-- FooterGuide---------------底部组件文件夹 
                    |-- FooterGuide.vue--------底部组件 vue |-- 
            pages-----------------路由组件文件夹 
                |-- Msite---------------首页组件文件夹 
                    |-- Msite.vue--------首页组件 vue 
                |-- Search----------------搜索组件文件夹 
                    |-- Search.vue---------搜索组件 vue 
                |-- Order--------------订单组件文件夹 
                    |-- Order.vue-------订单组件 vue 
                |-- Profile--------------个人组件文件夹 
                    |-- Profile.vue-------个人组件 vue |-- 
            App.vue---------------应用根组件 vue 
            |-- main.js---------------应用入口 js
        |-- static: 静态资源文件夹 
        |-- .babelrc: babel 的配置文件 
        |-- .editorconfig: 通过编辑器的编码/格式进行一定的配置 
        |-- .eslintignore: eslint 检查忽略的配置 
        |-- .eslintrc.js: eslint 检查的配置 
        |-- .gitignore: git 版本管制忽略的配置 
        |-- index.html: 主页面文件 
        |-- package.json: 应用包配置文件 
        |-- README.md: 应用描述说明的 readme 文件
    shop-client(脚手架3)
        |-- node_modules
        |-- public: 任何放置在 public 文件夹的静态资源都会被简单的复制,而不经过 webpack。你需要通过绝对路径来引用它们。
           |-- index.html: 主页面文件
        |-- src
           |-- main.js: 应用入口js
        |-- babel.config.js: babel的配置文件
        |-- vue.config.js: vue的配置文件
        |-- .gitignore: git版本管制忽略的配置
        |-- package.json: 应用包配置文件 
        |-- README.md: 应用描述说明的readme文件

31.vue.config.js文件配置
    const isProduction = process.env.NODE_ENV === 'production';
    const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
    const cdn = {
        css: ["https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.8.2/theme-chalk/index.css"],
        js: [
            'https://cdn.bootcss.com/vue/2.5.17/vue.runtime.min.js',
            'https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js',
            'https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js',
            'https://cdn.bootcss.com/axios/0.18.0/axios.min.js',
                    'https://cdn.bootcss.com/element-ui/2.8.2/index.js',
                    'https://cdn.bootcss.com/echarts/3.8.5/echarts.min.js'
        ]
    }
    module.exports = {
      // 基本路径
      // '/'绝对路径,'./'或''相对路径
      publicPath: process.env.NODE_ENV === 'production' ? '/' : './',
        
      // 运行 vue-cli-service build 时生成的生产环境构建文件的目录
      // 默认构建前清除文件夹(构建时传入 --no-clean 可关闭该行为
      outputDir: 'dist',
     
      // 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录
      assetsDir: 'static',
     
      // 指定生成的 index.html 的输出路径 (相对于 outputDir),也可以是一个绝对路径
      indexPath: 'index.html',
     
      // 生成的静态资源在它们的文件名中包含了 hash 以便更好的控制缓存
      filenameHashing: true,
     
      // 当在 multi-page 模式下构建时,webpack 配置会包含不一样的插件
      // (这时会存在多个 html-webpack-plugin 和 preload-webpack-plugin 的实例)。
      // 如果你试图修改这些插件的选项,请确认运行 vue inspect
      pages: {
        index: {
          // page 的入口
          entry: 'src/pages/index/index.js',
          // 模板来源
          template: 'src/pages/index/index.html',
          // 在 dist 的输出为 index.html
          filename: 'index.html',
          // 当使用 title 选项时,
          // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
          title: '首页',
          // 在这个页面中包含的块,默认情况下会包含
          // 提取出来的通用 chunk 和 vendor chunk。
          chunks: ['chunk-vendors', 'chunk-common', 'index']
        },
        // 当使用只有入口的字符串格式时,
        // 模板会被推导为 `public/subpage.html`
        // 并且如果找不到的话,就回退到 `public/index.html`。
        // 输出文件名会被推导为 `subpage.html`。
        
        // 多入口时,接着写子页面
        //subpage: 'src/subpage/main.js'
      },
      
      // eslint-loader 是否在保存的时候检查
      lintOnSave: true,
     
      // 是否使用包含运行时编译器的Vue核心的构建
      runtimeCompiler: false,
     
      // 默认情况下 babel-loader 忽略其中的所有文件 node_modules,
      // 想要通过 Babel 显式转译一个依赖,可以在这个选项中列出来
      transpileDependencies: [],
     
      // 生产环境 sourceMap
      productionSourceMap: false,
      
      // 跨域设置 
      // 可取值参考: https://developer.mozilla.org/zh-CN/docs/Web/HTML/CORS_settings_attributes
      crossorigin: undefined,
      
      // 构建后的文件是部署在 CDN 上的,启用该选项可以提供额外的安全性, 默认false
      integrity: false,
      
      // webpack 配置,键值对象时会合并配置,为方法时会改写配置
      // https://cli.vuejs.org/guide/webpack.html#simple-configuration
      //configureWebpack: (config) => {},
     
      // webpack 链接 API,用于生成和修改 webapck 配置
      // https://github.com/mozilla-neutrino/webpack-chain
      chainWebpack: (config) => {
          if (isProduction) {
            // 删除预加载
            config.plugins.delete('preload');
            config.plugins.delete('prefetch');
            // 压缩代码
            config.optimization.minimize(true);
            // 分割代码
            config.optimization.splitChunks({
                chunks: 'all'
            })
            // 生产环境注入cdn
            config.plugin('html')
                .tap(args => {
                    args[0].cdn = cdn;
                    return args;
                });
        }
        // 因为是多页面,所以取消 chunks,每个页面只对应一个单独的 JS / CSS
        config.optimization
          .splitChunks({
            cacheGroups: {}
          });
     
        // 'src/lib' 目录下为外部库文件,不参与 eslint 检测
        config.module
          .rule('eslint')
          .exclude
          .add('/Users/maybexia/Downloads/FE/community_built-in/src/lib')
          .end()
      },
     
      configureWebpack: config => {
            if (isProduction) {
                // 用cdn方式引入
                config.externals = {
                    'vue': 'Vue',
                    'vuex': 'Vuex',
                    'vue-router': 'VueRouter',
                    'axios': 'axios',
                                    'element-ui': 'ELEMENT',
                                    'echarts': 'echarts'
                }
                // 为生产环境修改配置...
                config.plugins.push(
                    //生产环境自动删除console
                                     new UglifyJSPlugin({
                        uglifyOptions: {
                            compress: {
                                warnings: true,
                                drop_console: true,
                                drop_debugger: true,
                                pure_funcs: ['console.log']//移除console
                            }
                        },
                        sourceMap: false,
                        parallel: true
                    })
                );
            } else {
                // 为开发环境修改配置...
            }
      },
     
      // 配置高于chainWebpack中关于 css loader 的配置
      css: {
        // false 时只有 *.module.[ext] 结尾的文件才会被视作 CSS Modules 模块
        // true 时可以去掉文件名中的 .module, 并将所有的 *.(css|scss|sass|less|styl(us)?) 文件视为 CSS Modules 模块
        modules: false,
        
         // 是否使用 css 分离插件 ExtractTextPlugin,采用独立样式文件载入,不采用 <style> 方式内联至 html 文件中
        // 生产环境下是 true,开发环境下是 false  
        extract: true,
     
        // 是否构建样式地图,设置为 true 之后可能会影响构建的性能
        sourceMap: false,
     
        // css预设器配置项
        loaderOptions: {
          css: {
            // 这里的选项会传递给 css-loader
          },
     
          postcss: {
            // 这里的选项会传递给 postcss-loader
          }
        }
      },
     
      // 所有 webpack-dev-server 的选项都支持
      // https://webpack.js.org/configuration/dev-server/
      devServer: {
        open: true,
     
        host: '127.0.0.1',
     
        port: 3000,
     
        https: false,
     
        hotOnly: false,
     
        // 将任何未知请求 (没有匹配到静态文件的请求) 代理到该字段指向的地方 
        proxy: null,
        // proxy: 'http://123.206.33.109:8081',          // 配置跨域处理,只有一个代理
        proxy: {
          // 配置多个代理y
          "/api": {
            target: "http://localhost:8088",//这里改成你自己的后端api端口地址,记得每次修改,都需要重新build
            //target: "http://localhost:58427",
            //target: "http://api.douban.com",
            ws: true,
            changeOrigin: true,
            pathRewrite: {
              // 路径重写,
              "^/apb": "" // 替换target中的请求地址
            }
          }
        },
     
        before: app => {
        }
      },
      // 构建时开启多进程处理 babel 编译
      // 是否为 Babel 或 TypeScript 使用 thread-loader
      parallel: require('os').cpus().length > 1,
     
      // PWA 插件相关配置
      // https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
      pwa: {},
     
      // 第三方插件配置
      pluginOptions: {}
    };
    
    使用了CDN的方式加载了比较重的js并在index.html头部引入cdn
    <--使用CDN的JS文件-->
    <% for(var i in htmlWebpackagePlugin.options.cdn&&htmlWebpackagePlugin.options.cdn.js){
        %>
            <link href="<%=htmlWebpackagePlugin.options.cdn.js[i]%>" rel="preload" as="script">
        %>
    }<%
    <--使用CDN的JCSS文件-->
    <% for(var i in htmlWebpackagePlugin.options.cdn&&htmlWebpackagePlugin.options.cdn.css){
        %>
            <link href="<%=htmlWebpackagePlugin.options.cdn.css[i]%>" rel="preload" as="style">
            <link href="<%=htmlWebpackagePlugin.options.cdn.css[i]%>" rel="stylesheet">
        %>
    }<%
    
    访问发现确实变快了,但是服务器只有2M,我想更快居然可以压缩代码,config配置下 放在configureWebpack里
    new CompressionWebpackPlugin({
        filename: '[path].gz[query]',  // 提示 compression-webpack-plugin@3.0.0的话asset改为filename
         algorithm: 'gzip',
        test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
         threshold: 10240,
        minRatio: 0.8,
    // 配置参数详解   
   // 提示 compression-webpack-plugin@3.0.0的话asset改为filename
   // asset: 目标资源名称。 [file] 会被替换成原始资源。[path] 会被替换成原始资源的路径, [query] 会被替换成查询字符串。默认值是 "[path].gz[query]"。
   // algorithm: 可以是 function(buf, callback) 或者字符串。对于字符串来说依照 zlib 的算法(或者 zopfli 的算法)。默认值是     "gzip"。
   // test: 所有匹配该正则的资源都会被处理。默认值是全部资源。
    // threshold: 只有大小大于该值的资源会被处理。单位是 bytes。默认值是 0。
    // minRatio: 只有压缩率小于这个值的资源才会被处理。默认值是 0.8。
    
    nginx也做压缩的调整就可以更快
    # 开启gzip
    gzip on;
    # 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
    gzip_min_length 1k;
    # gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
    gzip_comp_level 2;
    # 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;
    
32.babel.config.js文件配置
    module.exports = {
      presets: [
        '@vue/app'
      ],
      // 如果需要按需引入第三方库,在下面添加
      plugins:[
          [
              "import",
              {
                  "libraryName": "vant",
                  "libraryDirectory": "es",
                  "style": true
                }
          ],
          [
              "import",
              {
                  "libraryName": "we-vue",
                  "style": "style.css"
                },
                "we-vue"
          ]
      ]
    }
    
33. .eslintrc.js文件详情
    {
        root: true, //指定配置文件根目录:表示当前文件为eslint的根配置文件,逐层查找时无需往更上一级的文件目录中进行搜索
        parser: 'babel-eslint',//指定eslint解析器:babel-eslint是围绕Babel解析器的包装器使其与ESLint兼容;可能值espree、esprima
        parserOptions: { //eslint解析器配置项
            sourceType: "module",//指定js的导入方式,module是指通过模块导入,默认值为script(表示通过script标签引入)
        },
        env: { //运行环境极其局全局变量
               browser: true, //浏览器环境
        },
        plugins: [//提供插件
               'html' //插件名称,省略了[eslint-plugin-]前缀,表示规范html
           ],
        extends: 'airbnb-base', //规则继承:airbnb-base包含了JS、Es6的语法检查要依赖于[eslint-pugin-import];另一个值standard表示使用标准的js语法规则
        settings: { //添加共享规则参数,会提供给每一个规则,但是规则使不使用,看规则的设置
            'import/resolver': {
                   webpack: { //解析webpack配置项,路径为bulid/webpack.base.conf.js
                      config: 'bulid/webpack.base.conf.js'

                  }
            }
        },
        rules:{
            "no-alert": 0,//禁止使用alert confirm prompt
            "no-array-constructor": 2,//禁止使用数组构造器
            "no-bitwise": 0,//禁止使用按位运算符
            "no-caller": 1,//禁止使用arguments.caller或arguments.callee
            "no-catch-shadow": 2,//禁止catch子句参数与外部作用域变量同名
            "no-class-assign": 2,//禁止给类赋值
            "no-cond-assign": 2,//禁止在条件表达式中使用赋值语句
            "no-console": 2,//禁止使用console
            "no-const-assign": 2,//禁止修改const声明的变量
            "no-constant-condition": 2,//禁止在条件中使用常量表达式 if(true) if(1)
            "no-continue": 0,//禁止使用continue
            "no-control-regex": 2,//禁止在正则表达式中使用控制字符
            "no-debugger": 2,//禁止使用debugger
            "no-delete-var": 2,//不能对var声明的变量使用delete操作符
            "no-div-regex": 1,//不能使用看起来像除法的正则表达式/=foo/
            "no-dupe-keys": 2,//在创建对象字面量时不允许键重复 {a:1,a:1}
            "no-dupe-args": 2,//函数参数不能重复
            "no-duplicate-case": 2,//switch中的case标签不能重复
            "no-else-return": 2,//如果if语句里面有return,后面不能跟else语句
            "no-empty": 2,//块语句中的内容不能为空
            "no-empty-character-class": 2,//正则表达式中的[]内容不能为空
            "no-empty-label": 2,//禁止使用空label
            "no-eq-null": 2,//禁止对null使用==或!=运算符
            "no-eval": 1,//禁止使用eval
            "no-ex-assign": 2,//禁止给catch语句中的异常参数赋值
            "no-extend-native": 2,//禁止扩展native对象
            "no-extra-bind": 2,//禁止不必要的函数绑定
            "no-extra-boolean-cast": 2,//禁止不必要的bool转换
            "no-extra-parens": 2,//禁止非必要的括号
            "no-extra-semi": 2,//禁止多余的冒号
            "no-fallthrough": 1,//禁止switch穿透
            "no-floating-decimal": 2,//禁止省略浮点数中的0 .5 3.
            "no-func-assign": 2,//禁止重复的函数声明
            "no-implicit-coercion": 1,//禁止隐式转换
            "no-implied-eval": 2,//禁止使用隐式eval
            "no-inline-comments": 0,//禁止行内备注
            "no-inner-declarations": [2, "functions"],//禁止在块语句中使用声明(变量或函数)
            "no-invalid-regexp": 2,//禁止无效的正则表达式
            "no-invalid-this": 2,//禁止无效的this,只能用在构造器,类,对象字面量
            "no-irregular-whitespace": 2,//不能有不规则的空格
            "no-iterator": 2,//禁止使用__iterator__ 属性
            "no-label-var": 2,//label名不能与var声明的变量名相同
            "no-labels": 2,//禁止标签声明
            "no-lone-blocks": 2,//禁止不必要的嵌套块
            "no-lonely-if": 2,//禁止else语句内只有if语句
            "no-loop-func": 1,//禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以)
            "no-mixed-requires": [0, false],//声明时不能混用声明类型
            "no-mixed-spaces-and-tabs": [2, false],//禁止混用tab和空格
            "linebreak-style": [0, "windows"],//换行风格
            "no-multi-spaces": 1,//不能用多余的空格
            "no-multi-str": 2,//字符串不能用\换行
            "no-multiple-empty-lines": [1, {"max": 2}],//空行最多不能超过2行
            "no-native-reassign": 2,//不能重写native对象
            "no-negated-in-lhs": 2,//in 操作符的左边不能有!
            "no-nested-ternary": 0,//禁止使用嵌套的三目运算
            "no-new": 1,//禁止在使用new构造一个实例后不赋值
            "no-new-func": 1,//禁止使用new Function
            "no-new-object": 2,//禁止使用new Object()
            "no-new-require": 2,//禁止使用new require
            "no-new-wrappers": 2,//禁止使用new创建包装实例,new String new Boolean new Number
            "no-obj-calls": 2,//不能调用内置的全局对象,比如Math() JSON()
            "no-octal": 2,//禁止使用八进制数字
            "no-octal-escape": 2,//禁止使用八进制转义序列
            "no-param-reassign": 2,//禁止给参数重新赋值
            "no-path-concat": 0,//node中不能使用__dirname或__filename做路径拼接
            "no-plusplus": 0,//禁止使用++,--
            "no-process-env": 0,//禁止使用process.env
            "no-process-exit": 0,//禁止使用process.exit()
            "no-proto": 2,//禁止使用__proto__属性
            "no-redeclare": 2,//禁止重复声明变量
            "no-regex-spaces": 2,//禁止在正则表达式字面量中使用多个空格 /foo bar/
            "no-restricted-modules": 0,//如果禁用了指定模块,使用就会报错
            "no-return-assign": 1,//return 语句中不能有赋值表达式
            "no-script-url": 0,//禁止使用javascript:void(0)
            "no-self-compare": 2,//不能比较自身
            "no-sequences": 0,//禁止使用逗号运算符
            "no-shadow": 2,//外部作用域中的变量不能与它所包含的作用域中的变量或参数同名
            "no-shadow-restricted-names": 2,//严格模式中规定的限制标识符不能作为声明时的变量名使用
            "no-spaced-func": 2,//函数调用时 函数名与()之间不能有空格
            "no-sparse-arrays": 2,//禁止稀疏数组, [1,,2]
            "no-sync": 0,//nodejs 禁止同步方法
            "no-ternary": 0,//禁止使用三目运算符
            "no-trailing-spaces": 1,//一行结束后面不要有空格
            "no-this-before-super": 0,//在调用super()之前不能使用this或super
            "no-throw-literal": 2,//禁止抛出字面量错误 throw "error";
            "no-undef": 1,//不能有未定义的变量
            "no-undef-init": 2,//变量初始化时不能直接给它赋值为undefined
            "no-undefined": 2,//不能使用undefined
            "no-unexpected-multiline": 2,//避免多行表达式
            "no-underscore-dangle": 1,//标识符不能以_开头或结尾
            "no-unneeded-ternary": 2,//禁止不必要的嵌套 var isYes = answer === 1 ? true : false;
            "no-unreachable": 2,//不能有无法执行的代码
            "no-unused-expressions": 2,//禁止无用的表达式
            "no-unused-vars": [2, {"vars": "all", "args": "after-used"}],//不能有声明后未被使用的变量或参数
            "no-use-before-define": 2,//未定义前不能使用
            "no-useless-call": 2,//禁止不必要的call和apply
            "no-void": 2,//禁用void操作符
            "no-var": 0,//禁用var,用let和const代替
            "no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],//不能有警告备注
            "no-with": 2,//禁用with
            "array-bracket-spacing": [2, "never"],//是否允许非空数组里面有多余的空格
            "arrow-parens": 0,//箭头函数用小括号括起来
            "arrow-spacing": 0,//=>的前/后括号
            "accessor-pairs": 0,//在对象中使用getter/setter
            "block-scoped-var": 0,//块语句中使用var
            "brace-style": [1, "1tbs"],//大括号风格
            "callback-return": 1,//避免多次调用回调什么的
            "camelcase": 2,//强制驼峰法命名
            "comma-dangle": [2, "never"],//对象字面量项尾不能有逗号
            "comma-spacing": 0,//逗号前后的空格
            "comma-style": [2, "last"],//逗号风格,换行时在行首还是行尾
            "complexity": [0, 11],//循环复杂度
            "computed-property-spacing": [0, "never"],//是否允许计算后的键名什么的
            "consistent-return": 0,//return 后面是否允许省略
            "consistent-this": [2, "that"],//this别名
            "constructor-super": 0,//非派生类不能调用super,派生类必须调用super
            "curly": [2, "all"],//必须使用 if(){} 中的{}
            "default-case": 2,//switch语句最后必须有default
            "dot-location": 0,//对象访问符的位置,换行的时候在行首还是行尾
            "dot-notation": [0, { "allowKeywords": true }],//避免不必要的方括号
            "eol-last": 0,//文件以单一的换行符结束
            "eqeqeq": 2,//必须使用全等
            "func-names": 0,//函数表达式必须有名字
            "func-style": [0, "declaration"],//函数风格,规定只能使用函数声明/函数表达式
            "generator-star-spacing": 0,//生成器函数*的前后空格
            "guard-for-in": 0,//for in循环要用if语句过滤
            "handle-callback-err": 0,//nodejs 处理错误
            "id-length": 0,//变量名长度
            "indent": [2, 4],//缩进风格
            "init-declarations": 0,//声明时必须赋初值
            "key-spacing": [0, { "beforeColon": false, "afterColon": true }],//对象字面量中冒号的前后空格
            "lines-around-comment": 0,//行前/行后备注
            "max-depth": [0, 4],//嵌套块深度
            "max-len": [0, 80, 4],//字符串最大长度
            "max-nested-callbacks": [0, 2],//回调嵌套深度
            "max-params": [0, 3],//函数最多只能有3个参数
            "max-statements": [0, 10],//函数内最多有几个声明
            "new-cap": 2,//函数名首行大写必须使用new方式调用,首行小写必须用不带new方式调用
            "new-parens": 2,//new时必须加小括号
            "newline-after-var": 2,//变量声明后是否需要空一行
            "object-curly-spacing": [0, "never"],//大括号内是否允许不必要的空格
            "object-shorthand": 0,//强制对象字面量缩写语法
            "one-var": 1,//连续声明
            "operator-assignment": [0, "always"],//赋值运算符 += -=什么的
            "operator-linebreak": [2, "after"],//换行时运算符在行尾还是行首
            "padded-blocks": 0,//块语句内行首行尾是否要空行
            "prefer-const": 0,//首选const
            "prefer-spread": 0,//首选展开运算
            "prefer-reflect": 0,//首选Reflect的方法
            "quotes": [1, "single"],//引号类型 `` "" ''
            "quote-props":[2, "always"],//对象字面量中的属性名是否强制双引号
            "radix": 2,//parseInt必须指定第二个参数
            "id-match": 0,//命名检测
            "require-yield": 0,//生成器函数必须有yield
            "semi": [2, "always"],//语句强制分号结尾
            "semi-spacing": [0, {"before": false, "after": true}],//分号前后空格
            "sort-vars": 0,//变量声明时排序
            "space-after-keywords": [0, "always"],//关键字后面是否要空一格
            "space-before-blocks": [0, "always"],//不以新行开始的块{前面要不要有空格
            "space-before-function-paren": [0, "always"],//函数定义时括号前面要不要有空格
            "space-in-parens": [0, "never"],//小括号里面要不要有空格
            "space-infix-ops": 0,//中缀操作符周围要不要有空格
            "space-return-throw-case": 2,//return throw case后面要不要加空格
            "space-unary-ops": [0, { "words": true, "nonwords": false }],//一元运算符的前/后要不要加空格
            "spaced-comment": 0,//注释风格要不要有空格什么的
            "strict": 2,//使用严格模式
            "use-isnan": 2,//禁止比较时使用NaN,只能用isNaN()
            "valid-jsdoc": 0,//jsdoc规则
            "valid-typeof": 2,//必须使用合法的typeof的值
            "vars-on-top": 2,//var必须放在作用域顶部
            "wrap-iife": [2, "inside"],//立即执行函数表达式的小括号风格
            "wrap-regex": 0,//正则表达式字面量用小括号包起来
            "yoda": [2, "never"]//禁止尤达条件
        }
    }
 
     让vscode安装eslint规范的方式去保存
     1.安装插件ESLint
     2.配置settings.json文件
         "editor.codeActionsOnSave":{
             "source.fixAll.eslint":true
         },
         "eslint.format.enable":true,
         "eslint.validate":[
             "javascript",
             "vue",
             "html"
         ],
         "[vue]":{
             "editor.defaultFormatter":"dbaeumer.vscode-eslint"
         }
        此时,编写 vue 文件时 编写时,就会自动的按照eslint标准语法进行检测,并标注错误 保存代码时,会自动按照 eslint 
        语法要求对代码进行修复

            

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值