Vue学习笔记

最近在学习Vue,下面是学习过程中总结了一些基本的知识点

Vue

一. 基本知识

1. 实例化

var app = new Vue({

    el: '#app',  //绑定到元素上
    data: {
        message:'hello world'
    },
    filters:{
        filterName1:function(){},
        filterName2:function(){}
    },
    methods:{
        methodName1:function(){},
        methodName2:function(){}
    },
    //生命周期钩子
    created:function(){
        //未挂载到dom之前触发
    },
    mounted:function(){
        //挂载到dom后触发
    },
    beforeDestroy:function(){
        
    }
    
})

2. 过滤器

{{ data中的变量名 | filter中的过滤器名 }}

3. 指令和事件

  • v-text:解析文本,和{{ }}作用一样

  • v-html:解析html

  • v-bind:动态更新HTML元素上的属性

  • v-on:绑定事件监听器

  • v-bind语法糖:冒号 :

  • v-on语法糖:@

  • v-bind的作用,以及v-bind的变量语法,数组语法,对象语法:

    1. v-bind通常用来绑定属性的,格式是v-bind:属性名 = "值",简写:属性名 = "值"
    2. 变量语法:v-bind:class = "变量",变量形式 ,这里的变量的值,通常是在css定义好的类名;
    3. 数组语法:v-bind:class= "[变量1,变量2]" ,数组形式,其实跟上面差不多,只不过可以同时绑定多个class名;
    4. 对象语法:v-bind:class = {classname1:boolean,classname2:boolean},对象形式,这里的classname1(2)其实就是样式表中的类名,这里的boolean通常是一个变量,也可以是常量、计算属性等,这种方法也是绑定class最常用的方式。

4. VUE内置指令

  • v-cloak : 解决页面没加载完的时候出现类似{{ msg }}等变量名的现象,一般和display:none一起使用

    <style>
        [v-cloak]:{
            display-none;
        }
    </style>
    -------------------------
    <p v-cloak>
        {{ msg }}
    </p>
    
  • v-once:只在页面中渲染一次,改变变量的值也不会重新渲染

    <p v-once>
        {{ msg }}
    </p>
    
  • 条件指令:v-if

    • v-if : 后面接的是等号,等号后面的内容必须是布尔值,布尔值为true则渲染,否则不渲染

    • v-else

    • v-else-if

    • 例:

      <p v-if = '6>3'>{{ apple }}</p>
      <p v-else-if = '9<6'> {{ banana}} </p>
      <p v-else> {{ orange }} </p>
      
    • v-if的弊端:Vue在渲染元素时,出于效率考虑,会尽可能的复用已有的元素而非重新渲染,因
      此会出现乌龙

    • 解决方法:加key,唯一,提供key值可以来决定是否复用该元素

  • v-show:只改变display属性

    • v-if和v-show的区别:

      v-if:实时渲染:页面显示就渲染,不显示就移除

      v-show:元素始终存在于页面中,只是改变了display属性

  • v-for

    • 遍历多个对象:data 里面是一个数组

      <div id='app'>
          <ul>
              <!-- intm in items -->
              <li v-for="fruit in fruits">{{ fruit.name }}</li>
          </ul>
          
          <ul>
            <!-- 带索引的写法 -->
            <li v-for="(fruit,index) in fruits">第{{ index }}个 {{ fruit.name }}</li>
          </ul>
      </div>
      
      <script>
          var app = new Vue({
              el: '#app',
              data: {
                  //这里是一个数组
                  fruits: [
                      {name: 'apple'},
                      {name: 'banana'},
                      {name: 'orange'}
                  ]
              }
          })
      </script>
      
  • 遍历一个对象的多个属性

    <div id='app'>
        <span v-for='value in fruits'>{{ value }}</span>
        
        <!-- 带v-k-i的写法:value key index (外开)  -->
        <p v-for="(value,key,index) in fruits2">
          value:{{value}}<br>
          key:{{key}}<br>
          index:{{index}}
        </p>
    </div>
    
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                //这里是一个对象
                fruits: {
                    fruit1: 'apple',
                    fruit2: 'banana',
                    fruit3: 'orange'
                }
            }
        })
    </script>
    
  • v-model表单

5. 计算属性

  • 基础例子

    <div id="example">
      <p>Original message: "{{ message }}"</p>
      <p>Computed reversed message: "{{ reversedMessage }}"</p>
    </div>
    var vm = new Vue({
      el: '#example',
      data: {
        message: 'Hello'
      },
      computed: {
        // 计算属性的 getter
        reversedMessage: function () {
          // `this` 指向 vm 实例
          return this.message.split('').reverse().join('')
        }
      }
    })
    
  • 计算属性的 setter

    计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter :

    在赋值的时候会触发set函数,把新赋的值以参数的形式传递进去

    // ...
    computed: {
      fullName: {
        // getter
        get: function () {
          return this.firstName + ' ' + this.lastName
        },
        // setter
        set: function (newValue) {
          var names = newValue.split(' ')
          this.firstName = names[0]
          this.lastName = names[names.length - 1]
        }
      }
    }
    // ...
    

    现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstNamevm.lastName也会相应地被更新。

6. 数组更新、过滤与排序

改变数组的一系列方法:

  • push() 在末尾添加元素
  • pop() 将数组的最后一个元素移除
  • shift() 删除数组的第一个元素
  • unshift():在数组的第一个元素位置添加一个元素
  • splice() :可以添加或者删除函数—返回删除的元素
    三个参数:
    • 第一个参数 表示开始操作的位置
    • 第二个参数表示:要操作的长度
    • 第三个为可选参数:
      • sort():排序
      • reverse()

两个数组变动vue检测不到:

  1. 改变数组的指定项
  2. 改变数组长度
    过滤:filter

解决方法:

改变指定项: Vue.set(app.arr,1,”car”);
app.arr.splice(1): 改变数组长度

二. 组件

组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。

1. 组件用法

  • 全局注册

    Vue.component('component-name',{
      template: '<div>组件内容</div>'
    })
    //优点:所有Vue实例都可以使用
    //缺点:权限太大,容错率低
    
  • 局部注册

    var app = new Vue({
      el: '#app',
      data:{},
        components: {
            'component-name': {
                template: '<div>组件内容</div>'
            }
        }
    })
    
  • 注意事项:

    • 组件名用横杆命名法,不能用驼峰命名法

    • template中的内容必须被一个DOM元素包起来

    • 在组件的定义中,除了template,还可以有data、methods、computed选项

    • data必须是一个方法

      data: function(){
          return {
              message: 'hello world'
          }
      }
      

2. props父级向子级传递数据

  • 在组件中使用props从父组件接收参数,props中定义的属性,在组件送可以直接使用

  • props来自父级,而data中return的数据是自己组件本身的数据,两种数据的作用域都是组件本身,可以在template、methods、computed中直接使用

  • props的值有两种,一种是字符串数组,一种是对象

  • 可以使用v­bind动态绑定父组件来的内容

    <div id="app">
        ---用v-bind传递数据(需要回车)-------------------------<br>
        <input type="text" v-model.lazy="parentMsg">
        <bind-component v-bind:msg='parentMsg'></bind-component>
    </div>
    
    var app = new Vue({
        el: '#app',
        data: {
            count: 0,
            parentMsg: '这是来自父级的数据'
        },
        components: {
            'bind-component': {
                props:['msg'],
                template: '<div>{{ msg }}</div>'
            }
        }
    })
    

3. 单项数据流

  • props传递数据是单向的,父级数据的变化会传递给子级,反之就行不通了

  • 单向数据流可以避免子组件无意中修改父组件状态

  • 应用情景:

    • 第一种:父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域
      下可以随意使用和修改。

      <div id="app">
          <my-component msg="来自父级的数据"></my-component>
      </div>
      ---javascript---------------------
      <script>
          var app = new Vue({
              el: '#app',
              data: {},
              components: {
                  'my-component': {
                      props: ['msg'],
                      template: '<p>{{ newMsg }}<p>',
                      data: function () {
                          return {
                              //在data中保存下来
                              newMsg: this.msg
                          }
                      }
                  }
              }
          })
      </script>
      
    • 第二种:需要被转变的原始值传入,通过props获取,通过计算属性进行转换

      <div id="app">
          <input type="number" v-model="width">
          <computed-component :wth='width'></computed-component>
        </div>
      
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script>
          var app = new Vue({
            el: '#app',
            data: {width:10},
            components: {
              'computed-component': {
                props: ['wth'],
                template:'<div :style="style"></div>',
                //用计算属性转换传入的值
                computed: {
                  style: function(){
                    return {
                      width: this.wth + 'px',
                      height: '100px',
                      background: 'red'
                    }
                  }
                }
              }
            }
          })
        </script>
      

4. 组件命名注意事项

  • 在html中, myMessage 和 mymessage 是一致的,因此在组件中的html
    中使用必须使用kebab­case(短横线)命名方式。在html中不允许使用驼
    峰!!!!!!
  • 在组件中, 父组件给子组件传递数据必须用短横线。在template中,必
    须使用驼峰命名方式,若为短横线的命名方式。则会直接保错。
  • 在组件的data中,用this.XXX引用时,只能是驼峰命名方式。若为短横线
    的命名方式,则会报错。

5. 数据类型验证

  • 验证的 type 类型可以是:

    • String
    • Number
    • Boolean
    • Object
    • Array
    • Function
    Vue.component ( ‘ my-compopent ’, {
        props : {
            //必须是数字类型
            propA : Number ,
            //必须是字符串或数字类型
            propB : [String , Number] ,
            //布尔值,如果没有定义,默认值就是 true
            propC: {
                type : Boolean ,
                    default : true
            },
            //数字,而且是必传
            propD: {
                type: Number ,
                required : true
            },
            //如果是数组或对象,默认值必须是一个函数来返回
            propE: {
                type : Array,
                default : function () {
                    return [] ;
                }
            },
            //自定义一个验证函数
            propF: {
                validator : function (value) {
                    return value > 10;
                }
            }
        }
    });
    

三. 组件通信

1. 自定义事件

用于:子组件给父级传递数据

子组件用$emit ( '父组件中事件名' , 要传递的数据 ) 来触发事件 ,父组件用@'父组件中事件名'='触发的函数'来 监昕子组件的事件,触发的函数的参数即为子组件传递来的数据

第一步:自定义事件
第二步:在子组件中用$emit触发事件,第一个参数是事件名,后边的参数是要传递的数据
第三步:在自定义事件中用一个参数来接受

<div id="app">
    您的余额是:{{ acount }}<br>
    <my-component :acount="acount" @change-acount='handleAcount'></my-component>
</div>

<script>
    var app = new Vue({
        el: '#app',
        data: {
            acount: 2000
        },
        methods:{
            handleAcount:function(value){
                this.acount = value
            }
        },
        components: {
            'my-component': {
                props:['acount'],
                template: `<div>
                            <button @click="addAcount">+1000</button> 
                            <button @click="reduceAcount">-1000</button>
                         </div>`,
                data: function(){
                    return {
                        count: this.acount
                    }
                },
                methods:{
                    addAcount: function(){
                        this.count += 1000
                        //注意这里$emit的参数
                        this.$emit('change-acount',this.count)
                    },
                    reduceAcount: function(){
                        this.count -= 1000
                        this.$emit('change-acount',this.count)
                    }
                }
            }
        }
    })

2. 在组件中使用v-model

在组件中使用v­-model 其实是一个语法糖,这背后其实做了两个操作

  • v-­bind 绑定一个 value 属性
  • v-­on 指令给当前元素绑定 input 事件

$emit('input',value)的代码,这行代码实际上会触发一个 input事件, ‘input’后的参数就是传递给v­-model绑定
属性的值

<div id="app">
    您的余额是:{{ acount }}<br>
    <!-- <my-component :acount="acount" @change-acount='handleAcount'></my-component> -->
    <!-- 下面使用v-model -->
    <my-component :acount="acount" v-model='acount'></my-component>
</div>

<script>
    var app = new Vue({
        el: '#app',
        data: {
            acount: 2000
        },
        components: {
            'my-component': {
                props:['acount'],
                template: `<div>
                            <button @click="addAcount">+1000</button> 
                            <button @click="reduceAcount">-1000</button>
                          </div>`,
                data: function(){
                    return {
                        count: this.acount
                    }
                },
                methods:{
                    addAcount: function(){
                        this.count += 1000
                        // 这里触发的是input事件
                        this.$emit('input',this.count)
                    },
                    reduceAcount: function(){
                        this.count -= 1000
                        this.$emit('input',this.count)
                    }
                }
            }
        }
    })
</script>

3. 非父组件之间的通信---bus中介

用于两个组件之间的通信,(非父子关系),可以在根组件中用一个空的Vue对象作为中介,一个组件中监听该中介的自定义事件,另一个组件中触发该自定义事件并传递数据

<div id="app">
    <acomponent></acomponent>
    <bcomponent></bcomponent>
</div>

<script>
    Vue.component('acomponent', {
      template: '<button @click="handle">点击向B组件传递数据</button>',
      data: function () {
        return {
          msg: '我是来自A组件的数据'
        }
      },
      methods: {
        handle: function () {
          // 触发根组件的bus中的method1事件
          this.$root.bus.$emit('method1', this.msg)
        }
      }
    })
    Vue.component('bcomponent', {
      template: '<div>{{ msg }}</div>',
      data: function () {
        return {
          msg: '这是B组件'
        }
      },
      created: function () {
        // 对根组件bus监听method1事件
        this.$root.bus.$on('method1',  (value)=> {
          this.msg = value
        })
      }
    })
    var app = new Vue({
      el: '#app',
      data: {
        bus: new Vue()
      }
    })
</script>

4. 父链和子链

父链:this.$parent

子链:this.$refs.索引
提供了为子组件提供索引的方法,用特殊的属性ref为其增加一个索引

<div id="app">
    <button @click='clickA'>A子组件</button>
    <button @click='clickB'>B子组件</button>
    <son-a ref='a'></son-a>
    <son-b ref='b'></son-b>
</div>

<script>
    var app = new Vue({
        el: '#app',
        data: { msg: '来自父组件的msg' },
        methods: {
            clickA: function(){
                alert(this.$refs.a.name)
            },
            clickB: function(){
                alert(this.$refs.b.name)
            }
        },
        components: {
            'son-a': {
                template: '<div>这是子组件:{{ msg }}</div>',
                data: function () {
                    return {
                        msg: this.$parent.msg,
                        name: 'son-a'
                    }
                }
            },
            'son-b': {
                template: '<div><div>',
                data: function () {
                    return {
                        name: 'son-b'
                    }
                }
            }
        }
    })
</script>

四. 插槽slot标签

1. 单个插槽

用于混合父组件的内容与子组件的模板

在子组件中加入<slot></slot>标签,当父组件中没有传入内容时,默认显示slot标签中的内容,反之,则将传入内容替换掉原来slot标签中的内容

<div id="app">
    <my-component>
        <p>{{ msg }}</p>
    </my-component>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
    Vue.component('my-component', {
        template: `<div>
                        <slot>
                            如果父元素没传内容进来,就默认显示我吧!
                        </slot>
                   </div>`
    })
    var app = new Vue({
        el: '#app',
        data: {msg:'父元素的内容'}
    })
</script>

2. 具名插槽

把父组件要传入的标签命名slot='名称',在子组件的slot标签中添加name属性

<div id="app">
    <my-component>
        <div slot='header'>标题</div>
        <div slot='container'>正文</div>
        <div slot='footer'>底部</div>
    </my-component>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
    Vue.component('my-component', {
      template: `<div>
                  <slot name='header'>
                    如果父元素没传内容进来,就默认显示我吧!
                  </slot>
                 </div>`
    })
    var app = new Vue({
      el: '#app',
      data: {msg:'父元素的内容'}
    })
</script>

3. 作用域插槽

从子组件中获取数据:在父组件中添加<template></template>标签,slot属性的值与子元素<slot>标签的name属性一样,用slot-scope属性获取传来的数据(除了name)

<div id="app">
    <my-component>
        <!-- 这里的prop是变量名,随便取一个名字都可以
             slot的值要跟子组件的name相同
             template标签不会被渲染
             template标签用其他例如<p>、<span>等都可以 -->
        <template slot-scope='prop' slot='abc'>
            <!-- 可以获取除了name以外传来的内容 -->
            {{ prop.text }}
        </template>
    </my-component>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>    
    Vue.component('my-component', {
      template: `<div>
                  <slot name='abc' text='子组件中传来的内容'>
                    如果父元素没传内容进来,就默认显示我吧!
                  </slot>
                 </div>`
    })
    var app = new Vue({
      el: '#app',
      data: {msg:'父元素的内容'}
    })
</script>

4. 访问slot

通过this.$slots.name访问

Vue.component('my-component2', {
      template: `<div>
                  <slot name='header'>
                    如果父元素没传内容进来,就默认显示我吧!
                  </slot>
                  <slot name = 'footer'></slot>
                 </div>`,
      mounted:function(){
        var header = this.$slots.header
        var footer = this.$slots.footer
        console.log(header[0].elm.innerText)
        console.log(footer[0].elm.innerText)
      }
})

五. 动态组件

VUE给我们提供 了一个元素叫component
作用是: 用来动态的挂载不同的组件
实现:使用is特性来进行实现的

<div id="app">
    <!-- 绑定is属性 -->
    <component :is='thisView'></component>
    <button @click="handleView('A')">页面一</button>
    <button @click="handleView('B')">页面二</button>
    <button @click="handleView('C')">页面三</button>
    <button @click="handleView('D')">页面四</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            thisView: 'compA'
        },
        methods:{
            'handleView':function(value){
                this.thisView = 'comp' + value
            }
        },
        components:{
            'compA':{template:'<div>页面一</div>'},
            'compB':{template:'<div>页面二</div>'},
            'compC':{template:'<div>页面三</div>'},
            'compD':{template:'<div>页面四</div>'}
        }
    })
</script>

六. 自定义指令

自定义指令的基本用法

和组件类似分全局注册和局部注册,区别就是把component换成了derective

  • 钩子函数

    指令定义函数提供了几个钩子函数(可选):

    • bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时 执行一次的初始化动作。
    • inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。 update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的 绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。
    • componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。 unbind: 只调用一次, 指令与元素解绑时调用。
  • 钩子函数的参数有:

    • el: 指令所绑定的元素,可以用来直接操作 DOM 。
    • binding: 一个对象,包含以下属性:
      • name: 指令名,不包括 v­ 前缀。
      • value: 指令的绑定值, 例如: v­my­directive=”1 + 1”, value 的值是 2。
      • oldValue: 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
      • expression: 绑定值的字符串形式。 例如 v­my­directive=”1 + 1” , expression 的值是 “1 + 1”。
      • arg: 传给指令的参数。例如 v­my­directive:foo, arg 的值是 “foo”。
      • modifiers: 一个包含修饰符的对象。 例如: v­my­directive.foo.bar, 修饰符对象,,,modifiers 的值是 { foo: true, bar: true }。
    • vnode: Vue 编译生成的虚拟节点。
    • oldVnode: 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。 自定义的指令

七、render函数

1. 基本用法

在自定义组件时,template属性内容有时会很长,而且,在切换组件内的内容时,不显示的内容可能也会渲染一遍再隐藏,而在组件中用render函数可以解决这个问题

render函数的参数必须是createElement,这是在源码中已经定义好的函数

render函数要记得把createElement函数执行结果返回出去

<div id="app">
    <comp :thisview='thisView'>
        <slot slot='red'>red</slot>
        <slot slot='blue'>blue</slot>
    </comp>
    <button @click='clickRed'>red</button>
    <button @click='clickBlue'>blue</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
    Vue.component('comp', {
        props: ['thisview'],
        //这里的参数必须是createElement,是源码中已经定义好的函数
        render: function (createElement) {
            return createElement('div', this.$slots[this.thisview])
        },
        data: function () {
            return {
                view: this.thisview
            }
        }
    })
    var app = new Vue({
        el: '#app',
        data: { thisView: 'red' },
        methods: {
            clickRed: function () {
                this.thisView = 'red'
            },
            clickBlue: function () {
                this.thisView = 'blue'
            }
        }
    })
</script>

2. createElement函数的参数

render函数返回createElement函数的执行结果

  • 第一个参数 :createElement函数的第一个参数必选,可以是String、Object、Function

    • String:html标签,比如'div'、'h1'等等

      Vue.component('child',{
          render:function(createElement){
              return createElement('div')
          }
      })
      
      
  • Object:含有数据选项的对象,比如:

    Vue.component('child',{
        render:function(createElement){
            return createElement({
                template: '<div>文字内容</div>'
            })
        }
    })
    
    
  • Function:返回含有数据选项的对象,比如:

    Vue.component('child',{
        render:function(createElement){
            var domFn = function(){
                return {
                    template: '<div>文字内容</div>'
                }
            }
            return createElement(domFn())
        }  
    })
    
    
  • 第二个参数:数据对象(可选)——只能是Object

    下面是一些常见的选项:style、class、attrs、domProps等

    Vue.component('child',{
        render:function(createElement){
            return createElement({
                template: '<div>文字内容</div>'
            },{
                'class': {
                    //表示类名class,true为有该类名,false则相反
                    //class在js中是关键字,最好用引号包起来,不加也没事
                    foo: true,
                    baz: false
                },
                style: {
                    color: 'red',
                    //css中的横杆命名在这里都要写成驼峰命名
                    fontSize: '20px'
                },
                attrs: {
                    //attributes,正常的HTML属性,大部分可以写进来
                    id: 'red',
                    src: '../XXX.jpg'
                },
                domProps: {
                    //用来写原生的DOM属性
                    innerHTML: '<span style="color:red;">文字内容</span>'
                }
            })
        }
    })
    
  • 第三个参数:代表子节点,可选——可以是String 或 Array(Array常用)

    因为第二个参数可选且必须为Object,所以,如果第二个参数为String或Array,那么意味着第二个参数为空,这个String或Array是第三个参数,子节点

    其实第三个参数存的就是VNODE虚拟节点

    Vue.component('child',{
        render: function(createElement){
            return createElement({
                template: '<div>内容</div>'
            },{
                class:{
                    red: true
                }
            },[
                //这里的内容时字符串,不是对象,所以也是子节点(第三个参数)
                createElement('span','内容'),
                createElement('span','内容')
            ])
        }
    })
    

3. this.$slots在render函数中的使用

插槽的用法

第三个 参数存的就是VNODE

createElement(‘header’,header), 返回的就是VNODE

var header = this.$slots.header; //这返回的内容就是含有VNODE的数组

<div id="app">
    <child>
        <span slot='header'>我是标题</span>
        <span slot='main'>我是正文</span>
        <span slot='main'>我是正文</span>
        <span slot='footer'>我是结尾</span>
    </child>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
    Vue.component('child', {
        render: function (createElement) {
            var header = this.$slots.header
            var main = this.$slots.main
            var footer = this.$slots.footer
            return createElement('div', {
                'class': {
                    red: true
                }
            }, [
                createElement('header', header),
                createElement('main', main),
                createElement('footer', footer)
            ])
        }
    })
    var app = new Vue({
        el: '#app',
        data: {}
    })

4. 在 render中使用props传递数据

5. 在render函数中使用v-model

<div id="app">
    <my-component :inputvalue='inputvalue' @change='changevalue'></my-component><br>
    <!-- 这里的v-model是语法糖,绑定input事件,当触发input事件时,把传来的值付给inputvalue -->
    <my-component :inputvalue='inputvalue' v-model='inputvalue'></my-component><br>
    {{ inputvalue }}
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            inputvalue: '111'
        },
        methods: {
            changevalue: function (value) {
                this.inputvalue = value
            }
        },
        components: {
            'my-component': {
                props: ['inputvalue'],
                render: function (createElement) {
                    return createElement('input', {
                        attrs: {
                            value: this.inputvalue
                        },
                        on: {
                            input: (event) => {
                                this.$emit('change', event.target.value)
                                this.$emit('input', event.target.value)
                            }
                        }
                    })
                }
            }
        }
    })
</script>

6. 作用域插槽

向组件的插槽中传递数据

$scopedSlots.default

<div id="app">
    <my-component>
        <template scope='abc'>
            {{ abc.text }}<br>
            {{ abc.name }}
        </template>
    </my-component>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {},
        components: {
            'my-component': {
                render: function (createElement) {
                    return createElement('div', this.$scopedSlots.default({
                        text: '我是子组件传来的数据',
                        name: '陈秋钿'
                    }))
                }
            }
        }
    })
</script>

7. 函数化组件

functional:true //表示当前Vue实例无状态、无实例

无实例:组件内部没有this,可以通过context上下文来解决,render函数的第一个参数是createElement,第二个参数是context,这里this是window

context.parent

context.props

context.props.text ---- this.text

context.children ----- this.$slots.default

八. vue-cli脚手架

1. 安装步骤

首先电脑上要安装最新版的nodeJS.官网下载,安装完之后安装淘宝npm镜像

npm install -g cnpm --registry=https://registry.npm.taobao.org

  • 安装五部走:

    • 全局安装vue­cli

      npm install -g vue-cli

    • 进入目录–初始化项目

      vue init webpack my-project

    • 进入项目

      cd my-project

    • 安装依赖

      npm install

    • 启动项目

      npm run dev

  • 部署项目:

    npm run build

2. 目录结构

├── build // 项目构建(webpack)相关代码 记忆:(够贱) 9个
│ ├── build.js // 生产环境构建代码
│ ├── check­versions.js // 检查node&npm等版本
│ ├── dev­client.js // 热加载相关
│ ├── dev­server.js // 构建本地服务器
│ ├── utils.js // 构建配置公用工具
│ ├── vue­loader.conf.js // vue加载器
│ ├── webpack.base.conf.js // webpack基础环境配置 //entry程序的入口
│ ├── webpack.dev.conf.js // webpack开发环境配置
│ └── webpack.prod.conf.js // webpack生产环境配置
二、
├── config// 项目开发环境配置相关代码 记忆: (环配) 3个
│ ├── dev.env.js // 开发环境变量(看词明意)
│ ├── index.js //项目一些配置变量
│ └── prod.env.js // 生产环境变量
三、
├──node_modules// 项目依赖的模块 记忆: (依赖) *个
四、
├── src// 源码目录5
1
│ ├── assets// 资源目录
│ │ └── logo.png
2
│ ├── components// vue公共组件
│ │ └── Hello.vue
3
│ ├──router// 前端路由
│ │ └── index.js// 路由配置文件
4
│ ├── App.vue// 页面入口文件(根组件)
5
│ └── main.js// 程序入口文件(入口js文件)
五、
└── static// 静态文件,比如一些图片,json数据等
│ ├── .gitkeep
剩余、
├── .babelrc// ES6语法编译配置
├── .editorconfig// 定义代码格式
├── .gitignore// git上传需要忽略的文件格式
├── index.html// 入口页面
├── package.json// 项目基本信息
├── README.md// 项目说明

3. 其他

以 .vue 结尾的是组件文件,组件文件中的样式只对组件中的标签有用

九. 前端路由和vuex状态管理

访问不同的路由,加载不同的组件(输入不同的网址,显示不同的内容)

watch: {
    // 如果路由有变化,会再次执行该方法
    $route: "getUserData"
}

1. vue­-router路由基本加载

  • 安装

    npm install --save vue-router
    
    • 引用
    import router from 'vue-router'
    Vue.use(router)
    
  • 配置路由文件,并在vue实例中注入

    var rt = new router({
        routes:[{ //这里是routes,不是routers啊
            path:'/',//指定要跳转的路径
            component:HelloWorld//指定要跳转的组件
        }]
    })
    new Vue({
        el: '#app',
        router: rt,
        components: { App },
        template: ''
    })
    
  • 确定视图加载的位置

    <router-view></router-view>
    

2. 路由的跳转

用router-link

<router-link to="/"></router-link>

<template>
    <ul>
        <li>
            <router-link to="/helloworld">HELLO WORLD</router-link>
        </li>
        <li>
            <router-link to="/helloearth">HELLO EARTH</router-link>
        </li>
    </ul>
</template>

3. 通过路由传递参数

  • 在路由中加入name属性
  • 在path后面加 "/ : 要传递的参数 "
  • router-link标签中绑定toname 和 params结合使用,在组件中用 $route.params.XXX 获取传来的参数
<!-- list.vue -->
<template>
    <ul>
        <li>
            <router-link :to="{name: 'helloworld',params:{worldmsg: '你好世界'}}">
                HELLO WORLD
            </router-link>
        </li>
        <li>
            <router-link :to="{name:'helloearth',params:{earthmsg:'你好地球'}}">
                HELLO EARTH
            </router-link>
        </li>
    </ul>
</template>

<!-- HelloEarth.vue -->
<template>
    <div class="hello">
        <h1>{{ msg }}</h1>
        <h2>Essential Links</h2>
        <h3>{{ $route.params.earthmsg }}</h3>
    </div>
</template>

<script>
    export default {
        name: 'HelloEarth',
        data () {
            return {
                msg: 'HELLO EARTH'
            }
        }
    }
</script>
// index.js(router)
export default new router({
  routes: [{
    name: 'helloworld',
    path: '/helloworld/:worldmsg',
    component: HelloWorld
  },{
    name: 'helloearth',
    path: '/helloearth/:earthmsg',
    component: HelloEarth
  }]
})

4. Axios —— get请求

this.$http.get( 'url ', {params:{}})

axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:

  • 从浏览器中创建 XMLHttpRequest
  • 从 node.js 发出 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防止 CSRF/XSRF
  • 使用方法

    1. 安装

      npm install axios

    2. 引入加载

      import axios from 'axios'

    3. 将axios挂载到全局Vue上(不用每个文件都引入加载)

      Vue.prototype.$http = axios

      这里的http是一个变量名,换成其他的也可以,用this.http.get()函数就能发送请求,(这里this是指当前Vue实例)

    4. 发出get请求

      axios基于promise,有.then()、.catch()

      在请求链接上加参数的两种方法:

      ​ 直接在链接后面加 ?page=1&limit=10

      ​ 或者,get函数加第二个参数 {params:{ page:1;limit:10}}

      <template>
          <div>
              <button @click='getData'>点击请求数据</button>
              <ul>
                  <li v-for="item in items">{{item.title}}</li>
              </ul>
          </div>
      </template>
      
      <script>
          export default {
              name: "HelloWorld",
              data() {
                  return {
                      msg: "Welcome to Your Vue.js App",
                      items: []
                  };
              },
              methods: {
                  getData() {
                 // this.$http.get('https://cnodejs.org/api/v1/topics?page=1&limit=10')
                      this.$http
                          .get("https://cnodejs.org/api/v1/topics", {
                          params: {
                              page: 1,
                              limit: 10
                          }
                        })
                          .then(res => {
                             this.items = res.data.data;
                                 console.log(res.data.data);
                         })
                          .catch(function(err) {
                              console.log(err);
                          });
                  }
              }
          };
      </script>
      
      

5. Axios —— post请求

  • 与get相似,把get换成post

  • POST传递数据有两种格式:

    form­data格式: ?page=1&limit=48

    x­www­form­urlencoded格式: { page: 1,limit: 10 }

  • 在axios中,post请求接收的参数必须是form­data格式,可以使用qs插件把我们写的内容转换为formdata格式

    qs插件—­qs.stringify

    npm install qs

import qs from'qs'
postData() {
    this.$http.post(url, qs.stringify({
        params: {
            page: 1,
            limit: 10
        }
    }))
    .then(res => {
        this.items = res.data.data;
        console.log(res.data.data);
    })
    .catch(function(err) {
        console.log(err);
    });
}

6.vuex的store用法

子组件和父组件之间的通信可以通过props以及$emit来完成,非父组件之间的通信需要通过他们之间的共同父组件来完成,当文件较多时,就会很乱很麻烦,所以就用到了Vuex的store

简而言之,vuex可以用来管理状态,共享数据,在各个组件之间管理外部状态

应用场景:多个页面间共享的登录状态

  1. 安装vuex,引入并通过use方法使用它

    npm i vuex

    import Vuex from 'vuex'

    Vue.use(Vuex)

  2. 创建状态仓库

    //这里的Store和state是不能改的!
    var store = new Vuex.Store({
      //state 状态
      state: {
        num: 100
      }
    })
    //在入口文件中引入store,跟引入router一样
    new Vue({
      el: '#app',
      router,    
      store, //就是这一句
      components: { App },
      template: '<App/>'
    })
    
    
  1. 在任意组件中,都可以通过 this.$store.state.XXX 直接拿到共享的数据

    computed:{
        getNum:function(){
            return this.$store.state.num
        }
    }
    
    

7. vuex的相关操作及核心用法

vuex状态管理的流程

view———­>actions———–>mutations—–>state————­>view

  • 方法1:在mutations选项中定义状态改变事件,在组件中通过this.$store.commit('事件名')触发转台的改变

  • 方法2:actions,用来操作mutations的,可有可无,但是actions可以进行异步操作而mutations不能,

    通过this.$store.dispatch('事件名')

    注意:actions提交的是mutation,而不是直接变更状态

    actions可以包含异步操作,但是mutation只能包含同步操作

  • 方法三:getters,我觉得这个跟计算属性有点像

    var store = new Vuex.Store({
        state: {
            num: 100
        },
        mutations:{
            addNum(state){
                state.num ++
            },
            reduceNum(state){
                state.num --
            }
        },
        actions:{
            //传入的参数为上下文
            ruduce(context){
                context.commit('reduceNum')
            }
        },
        getters:{
            getCount(state){
                return state.num > 0 ? state.num : 0
            }
        }
    })
    
    //在组件中
    this.$store.commit('addNum')
    this.$store.dispatch('ruduce')
    this.$store.getters('getCount')
    
    

十. 过渡

<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>
new Vue({
  el: '#demo',
  data: {
    show: true
  }
})
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}

在进入/离开的过渡中,会有 6 个 class 切换。

  1. v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
  2. v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
  3. v-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
  4. v-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
  5. v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
  6. v-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容