随身笔记
随身笔记

vue.js笔记

目前在vue 2.5.9测试学习

顺便推荐调试工具:vue-devtools,直接去chrome商城安装即可。在引入vue.js时,不能使用min版本不然无法使用。

创建vue视图
var vm=new Vue({
     name:'root', //给这里实例化对象取一个名字,组件想知道是哪个实力化对象调用的就使用this.$parent.$options.name
     el:'#app’,   //不能将容器设置在html和body标签上,如果el没声明也可以使用 vm.$mount('#app')来实现
     parent:baba, //该属性一般用在new Vue对象中,
     data:{  //存放数据
         str:'字符串数据',
         arr:[ //json数据
             {name:'名字',age:123},
             {name:'名字',age:123},
         ],
         obj:{age:22,name:'小么'}
     },
     methods:{  //存放事件
        click:function(e){
            console.log(this);
        }
     },
     components:{},
     render:...,
     directives:...,
     filters:...,
     computed:{
        myname:{
           get(){ return this.xx  },
           set(newValue){ this.$refs.xxx.src = newValue }   //在别的地方修改 this.myname = 'baidu.con',newValue就能获取到值
        } 
     },
     watch:{ //监听的数据有变为就触发回调
         //遇到复合型数据推荐结构是[{},{}]
        str:function(new_value,old_value){
        },
        arr:{
            handler: function (val, oldVal) { /* ... */ }, //不要使用箭头函数,否则this无法指向vue对象
            deep: true  //遇到对象或者复合结构要开启深度监听,简单数组不需要开启
        },
        'obj.age':function (val, oldVal) { /* ... */ },
         obj:[
            function handle1 (val, oldVal) { /* ... */ },
            function handle2 (val, oldVal) { /* ... */ }
         ]
     },


   
     //生命周期
     beforeCreate:function(){
          //vue实例化前执行,data数据和dom还没有数据
     },
     created:function(){
          //vue实例化后执行,已经有data数据,但还没加载dom
     },
     beforeMount:function(){
          //组件内容渲染到页面前执行,data数据有了,dom还没在页面中
     },  
     render:function(){
          //render执行顺序是在beforeMount和mounted之间,它的作用就是把DOM插入到页面上,所以到了mounted执行时我们才能看见数据
     },
     mounted:function(){
          //组件内容渲染到页面后执行,data和dom都在页面中了
     },
     beforeUpdate:function(){
          //只有当data数据发生变化后,但数据还没插入到dom的时候执行
     },
     updated:function(){
          //data数据变化完成并插入到dom之后执行
     },
     beforeDestroy:function(){
          //销毁之前回调,使用vm.$destroy()即是销毁了vm组件且先触发beforeDestroy之后是触发destroyed
         
     },
     destroyed:function(){
          //销毁后回调,这里的销毁后数据还是存在的,只是修改数据已经无法驱动修改视图变化了而已,
          //之前绑定在dom的事件例如click mouseenter等等还是有效的,仅仅是无法驱动视图变化而已,之前有的还是有
     },

     //activated和deactivated,只针对开启keep-alive的组件触发
     activated:function(){
        console.log("进页面")
     },
     deactivated:function(){
        console.log("离开页面")
     },

     //组件生命周期
     beforeRouteEnter(to, from, next) {
       //不能获取到this
       // do someting
       // 在渲染该组件的对应路由被 confirm 前调用
     },
     beforeRouteUpdate(to, from, next) {
       // do someting
       // 在当前路由改变,但是依然渲染该组件是调用
     },
     beforeRouteLeave(to, from ,next) {
       //能获取到this
       // do someting
       // 导航离开该组件的对应路由时被调用
       next()
     }
});

vue对象嵌套、加载顺序问题

对于新手或者是要投入项目的最好先搞清楚,方便之后的架构设计

1,情况:父对象id=xx,里面嵌套着子对象xx1,并且里面有相同元素{{a}}

https://sdeno.com/wp-content/uploads/2017/12/vue1.jpg

修改后

https://sdeno.com/wp-content/uploads/2017/12/vue2.jpg

2,情况2:父元素里面嵌套着子元素,但子元素里面的{{a1}},父元素没有

https://sdeno.com/wp-content/uploads/2017/12/vue3.jpg

修改后

https://sdeno.com/wp-content/uploads/2017/12/vue4.jpg

总结:

1,子元素有的属性,父元素一定要有;父元素有的属性,子元素可以不声明。

2,子元素要加载在父元素前面

遮丑
//放在公共样式的最最前面
[v-cloak] {
   display: none !important;
}

<div id=“app” v-cloak>  </div>
对外直接调用vue属性
vm.str  //字符串数据

vue数据的增、删、改
vue不允许添加额外属性,只能在已有的基础上修改,我们可以自定义一个空对象,在其基础上添加属性也能达到这样的效果。

var vm1= new Vue({
  el: '#repeat',
  data:{
    str:'',  //预留空的
    obj:{}   //预留空的
    obj1:{a:''}  //如果模板中要调用obj1.a,就要在vue实例中预先设置好,否则会报错。
 }
})

在修改数组、对象时也需要vue内置的方法去修改,如果还想有数据双向绑定效果就要用以下方法去修改。否则修改后的视图也不会改变。

对象

修改:
  Vue.set(vm1.obj,'age',123) //添加了age属性
  this.$set(this.someObject,'b',2)  //临时补加a属性且赋值,如果要删除使用this.$delete(this.someObject,'b')
   或
  vm1.str=xxxx //直接修改

添加:
  this.obj = Object.assign({}, this.obj, { a: 1, b: 2 }); //在obj对象上添加了a属性和b属性
  调用:{{obj.age}}

数组

data:{
 arr:['我的名字','是谢尔顿']
}

修改:
Vue.set(this.arr, 1, '陈陈')

修改长度
example1.items.splice(newLength)
vue操作数组
- push()      //末尾加
- pop()       //末尾减
- shift()      //首减
- unshift()  //首加 ie8不支持
- splice()    //推荐使用,可增、删、改
- sort()       //升序
- reverse()  //降序
//以上方法会修改元数组


filter()
concat()
slice()
//以上方法,不会修改原数组,返回一个新数组
var example2 = new Vue({
  el: '#example-2',
  data: {
      items: [
        { message: 'Foo' },
        { message: 'Bar' }
      ]
  }
})
example2.items.push({ message: 'Baz123' })  //在items数组里插入对象

Vue.set(this.items,2,{name:’111’})   //找到数组this.items   在索引2的位置上添加数据  {name:’111’}
或者
this.arrys.splice(2,0,'吗132’)   //在数组中索引2的位置,插入一个数组内容为'吗132’
数组替换
filter()
concat()
slice()
example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})
修改数组内容
example2.items.$set(0, { childMsg: 'Changed!'})
删除数组
this.arrys.splice(0,1)  //从第0个索引开始包括第0个索引,选1个元素开始删除
注意:$remove已经被弃用

$用法
vm.$data //获取到vm里面data设置的数据
vm.$el //获取到此id的原生DOM
vm.$options //vm.$options.render=(a)=>{ return a(‘div’,{},’文字’); } ,data属性里面的数字有更新就会触发该函数
还有更多$的方法

表达式

{{str}} //输出变量
表达式指出js语法
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }} //只能使用三元运算,不能使用if else
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>

不解析表达式

<div v-pre>{{name}} </div> //直接输出 {{name}}

计算属性
计算属性主要是弥补解决{{}}表达式不能解决复杂的问题。
vue初始化时,有些属性需要在vue初始化后进行计算后才显示
什么时候需要计算属性?当一个值需要依赖另一个值变化而变化的时候就需要,尤其是对大数据的时候

<div id="example">
 a={{ a }}, b={{ b }}
</div>

var vm = new Vue({
  el: '#example',
  data: {
    a: 1
  },
  computed: { //计算属性里面的属性名调用时,直接写变量,如:{{b}}, 不能{{b()}}
    b: function () { //在计算属性里面定义的属性名可以直接在表达式中显示,b值依赖a,需要初始化时计算出b值这时候就需要计算属性
    // `this` 指向 vm 实例
       return this.a + 1 //a值变化,b值也会自动更新
    }

    //另一种写法:
    myname:{
       get(){ return this.xx  },
       set(newValue){ this.$refs.xxx.src = newValue }   //在别的地方修改 this.myname = 'baidu.con',newValue就能获取到值
    } 
  }
})

结果:
a=1,b=2  (只有变量b被调用的时候,计算属性里面的函数才会去运行)

***注意:当使用v-model监听某个值时,页面中正好有一个表达式是类似是<p>{{xx()}}</p>,如果xx()如果是做了很复杂耗时的计算,推荐把xx方法写入到computed,中不然页面性能会大大降低。案例:indexaaa

https://sdeno.com/wp-content/uploads/2017/12/QQ20181124-171200@2x-285x300.png

https://cn.vuejs.org/v2/guide/computed.html#%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E7%9A%84-setter

还额外提供一个set属性

computed:{
    fullName:{
        get: function (){
            return this.firstName + ' ' + this.lastName
        },
        set: function (newValue) {
            //一旦this.fullName = '1122'属性有改变就会触发set
            this.firstName = newValue
            this.lastName = '改了'
        }
    }
},

表单

案例:test

模板
//需要控制大部分html显示、隐藏的 就把html都放入<template>里面
//v-show 不能用在 <template>上 也不支持 v-else

<template v-if="message">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>
<p v-else>No</p>


取代v-else的方法:
<p v-show="condition">YES</p>
<p v-show="!condition">NO</p>

(提示:频繁切换推荐使用v-show,因为v-if每次显示的时候都需要耗性能)

过滤器表达式

过滤器只能放在{{ }} 和 v-bind 中

{{ message | capitalize }}    //message的值传入到capitalize函数里面进行过滤处理后在返回
{{ message | filterA | filterB }}    //过滤条件可以叠加
{{ message | filterA (‘arg1', arg2) }}   //过滤传参数,默认第一个参数是message,‘arg1', arg2分别是第二、第三个参数
<li v-for="(value,index) in scroll_img" :class="value.xx | filterA">
   {{index}}
</li>

自定义过滤器

<p>{{name | time_style | add_year}}</p>  //name是数据,name数据会经过time_style、add_year的过滤

data:{
  name:new Date(),  //原始数据
},
filters:{
  time_style:function (value) {
    console.log(value); //这里的value就是new Date()
    return value.getHours()+':'+value.getMinutes()+':'+value.getSeconds()
  },
  add_year:function (value) {
    console.log(value); //这里的value是经过time_style处理过后返回来的值
    return "2017-"+value
  }
}

条件语句 (显隐)

//v-if是直接删除DOM来控制显隐
<p v-if="greeting">Hello!</p>
<p v-else>No</p>

条件可以这么写
<h1 v-if="yes">Yes!</h1>
<h1 v-if="no">No!</h1>
<h1 v-if="age >= 25">Age: {{ age }}</h1>
<h1 v-if="name.indexOf('jack') >= 0">Name: {{ name }}</h1>

var vm = new Vue({
  el: '#app',
  data: {
    yes: true,
    no: false,
    age: 28,
    name: 'keepfool'
  }
})


//v-show根据display: none;来控制显隐
<p v-show="message">YES</p>
<p v-else>No</p>


2.1.0 新增
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>

元素属性attr

data: {
 text:'text',
 value:'值',
 url:'http://baidu.com',
 a_title:'a连接'
}

<a v-bind:href="url" v-bind:title="a_title">aaa</a>
//<a href="http://baidu.com" title="a连接">aaa</a>

<input v-bind:type="text" v-bind:value="value">
//<input type="text" value="值">

<a v-bind:href="url"></a>
缩写:
<a :href="url"></a>
动态属性

data(){
 msg:'内容'
 name:'titlexx',
 event:'click'
},
template:`<div :[name]="msg" @[event]="handle"></div>`,
methods:{
 handle(){
 }
}

效果:
<div titlexx="内容"></div>

class动态添加
在vue中添加class推荐使用v-bind:class来添加

对象方法:

(1)
 <div class="static" v-bind:class="{ 'class-a': isA, 'class-b': isB }"></div>
data: {
 isA: true,
 isB: false
}
结果:<div class="static class-a"></div>
 

(2)
<div v-bind:class="classObject"></div>
data: {
  classObject: {
   'class-a': true,
   'class-b': false
  }
}

数组方法:

<div v-bind:class="[classA, classB]">
data: {
  classA: 'class-a',
  classB: 'class-b'
}

<div class="class-a class-b"></div>

数组和对象

<div v-bind:class="[{'active':a},iserror]"> fasdfasdf </div>

data:{
  a:true,
  iserror:'name_class'
},

结果:<div class="active name_class"> fasdfasdf </div>


另一种表达式
<div v-bind:class="[classA, { classB: isB, classC: isC }]">

三元表达式

<div v-bind:class="[classA, isB ? classB : '']">

如果遇到负责判断推荐使用计算属性,如:

<input type="text" v-model="name" :class="class_array">

var app=new Vue({
  el:"#app",
  data:{
    a:true,
    b:false
  },
  computed:{
    class_array:function () {
                   return {
                      class_1: this.a && !this.b
                   }
                 }
  }
});

css样式

(推荐)对象方法:
<div v-bind:style="styleObject"></div>
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

数组方法:
<div v-bind:style="[styleObjectA, styleObjectB]">fff</div>
data: {
  styleObjectA:{color:'red'},
  styleObjectB:{fontSize:'13px'},
}


常规:
<img alt="" :style="'background:url('+val.cover_picture+') no-repeat center;border:0'">

结果都是:<div style="color: red; font-size: 13px;">fff</div>

都类似要学会举一反三

DOM事件

v-on:input  缩写 @input

<input type="text" @input="fn">  //fn写需要的逻辑判断

类似于jquery写法
$('.tx').bind('input propertychange', function() {    
    document.title=$(this).val();
});

图片监听事件

<img :src="item.iconUrl" @error="errorImgfn" >

methods: {
    errorImgfn(e){
      e.target.src=require('@/assets/imgs/dbsx.png')
    },
}

或者
<img :src="item.iconUrl" :onerror="errorImg" >

 data () {
    return {
      errorImg: 'this.src="' + require('@/assets/imgs/dbsx.png') + '"'
    };
 },
同样也有@onload=""

案例:

methods:{
  click:function(e){
    console.log(this);
  }
}

<button v-on:click="click($event)">点</button>
缩写:<button @click="click($event)">点</button>

解除事件绑定:

vue不像jquery有unbind和off,但是可以利用一些小技巧去实现,例如:

<div id="app">
 <button @click="status && fn($event)">xxx</button>
 <p><button @click="bian">change</button></p>
</div>

new Vue({
 el:'#app',
 data:{
    status:true
 },
 methods:{
    fn:function (e) {
        console.log(e)
    },
    bian:function () {
        this.status=false;
    }
 }
});

//通过改变status变量去实现

常用:

@focus  //input 获取焦点
@blur   // 失去焦点

touch事件

长按,点击事件

<div @touchstart="gotouchstart()" @touchmove="gotouchmove" @touchend="gotouchend()"></div>
methods:{
       gotouchstart(img){
            touch_time=new Date()/1000; //记录手指放在屏幕上的时间
            temp_time=0;
            run_fn=null;
            clearTimeout(run_fn);
           run_fn= setTimeout(function () {
               Tools.dialog.strHtml('<div><img style="display: block;width: 70%;" src="'+img+'"> </div>');
            },800);
        },
        //手释放,如果在800毫秒内就释放,则取消长按事件,此时可以执行onclick应该执行的事件
        gotouchend(num){
            var self=this;
            if(  Math.ceil( ((new Date()/1000)-touch_time)*100  )/100>0.8  ){
               // console.log('长按');

            }else{
                clearTimeout(run_fn);
                self.gokill(num);
            }
        },
        //如果手指有移动,则取消所有事件,此时说明用户只是要移动而不是长按
        gotouchmove(){
            clearTimeout(run_fn);
        },
}

指令修饰符

<a v-bind:href.literal="url" v-bind:title="a_title">aaa</a>
直接显示 <a href="url" title="a连接">aaa</a> //href直接显示属性名

禁止默认行为
<button v-on:submit.prevent="onSubmit"></button> //相当于加了event.preventDefault()

<a @click.stop=“"> </a> //阻止事件冒泡
<div @click.self></div> //只有该元素触发,不包括子元素

1,事件修饰符

.stop //阻止事件冒泡
.prevent //阻止默认行为,例如点击a会自动跳页面,点击<input type=“submit”>会刷新提交数据
.capture //一般事件都是在冒泡时候被执行的,addEventListener(‘click’,fn,true) ,添加此修饰符相当于改成了true,默认是false
.self //例如当父元素绑定了click,父元素里面又有一个子元素,当点击了子元素时候,其实同时也点击了父元素因为父元素包裹着子元素嘛,click触发。当给父元素添加.self时,在点击子元素时候,就不会出发click了。也有阻止冒泡的效果,相当于e.currentTarget
.once //2.1.4 新增,只触发一次事件
.native // v-on:click.native,这样触发的就是js原生的click事件了而不是vue封装好的事件

<a v-on:click.stop="doThis"></a> //阻止默认行为
<a v-on:click.stop.prevent="doThat"></a> //同时阻止默认行为和事件冒泡
<form v-on:submit.prevent></form> //只添加修饰符 ,不添加其他参数

2,键盘修饰符

<!-- 同上 -->
<input v-on:keyup.enter=“fn”> //当input获取焦点时候,按回车就会触发fn事件,对外调用就是
vm.fn()事件

<!-- 缩写语法 -->
<input @keyup.enter=“fn">
或者
<input @keyup.13="fn" type="text”>

按键组合
<input @keyup.ctrl.67="fn" type="text”> //获取input焦点后同时按住ctrl+c 就能触发fn函数


键盘别名
enter
tab
delete
esc
space
up
down
left
right
ctrl
alt
shift
meta (mac是command,win是windows)

自定义别名
Vue.config.keyCodes.huiche=13

3,数据同步修饰符

lazy //效果都是同步,但是只有在input失去焦点时候才同步数据
number //将字符串的数字转化为数字类型,如果是NaN直接显示此数据,例如是abc123,说明是NaN,那就直接显示abc123
trim //自动去掉 首尾空格


举例:
<input v-model.lazy="msg">
<input v-model.number="msg">
<input v-model.trim="msg">

属性监听$watch

https://cn.vuejs.org/v2/api/#watch

var vm = new Vue({
   el:'#app',
   data:{
       str:'你好',
       arr:[],
       obj:{
           a:''
       }
   },
   watch:{
      str:function(new_value,old_value){
      },
      'obj.a':{
         handler: function (new_value,old_value) { /* ... */ }
      }
      //或者
      'obj':{
         handler: function (new_value,old_value) { new_value.a },
         deep: true, //深度监听
         immediate: true //true时,默认执行一次handler函数
      }
   }
});

或者

https://cn.vuejs.org/v2/api/#vm-watch

vm.$watch('str', function (new_value,old_value) { //属性值发生改变就会触发
 
},{
  deep: true
});

//注意如果直接监听整个对象arr或者obj,old_value返回来的值跟new_value是一样的

input监听

两种方法

1,v-on:input

<input type="text" v-on:input ="inputFunc($event)">

methods:{
   inputFunc:function (e) {
      document.title=e.target.value;
   },
}

2,watch

<input type="number" :value="buy_num" v-model="buy_num">

data:{
   buy_num:0
},
watch:{
   buy_num:function(){
         console.log(this.buy_num);
   }
}

循环、遍历

数据:

data:{
   array:['陈陈',20,'男'],
   obj:{
     xx1:'数字1',
     xx2:'数字2',
     xx3:'数字3'
   },
}

数组:

<ul>
 <li v-for="(value,index) in array">
    索引:{{index}}----值:{{value}}
 </li>
</ul>
https://sdeno.com/wp-content/uploads/2017/12/for_array.jpg

对象:

<ul>
 <li v-for="(value,key,index) in obj">
   属性名:{{key}}--- 
   值是:{{value}}---
   索引是{{index}} 
 </li>
</ul>
https://sdeno.com/wp-content/uploads/2017/12/for_obj.jpg

复合数据:

data:{
    scroll_img:[
     {img_name:1},
     {img_name:2},
     {img_name:3},
     {img_name:4}
    ]
},
methods:{
    remove_li:function (index) {
       this.scroll_img.splice(index,1)
    }
}

对象和数组:

<ul>
 <li :class="'xx-'+index" :title="value.img_name" v-for="(value,index) in scroll_img">
   值是:{{value.img_name}}---
   索引是{{index}} ----
   <button @click="remove_li(index)">删除该项</button>
 </li>
</ul>
https://sdeno.com/wp-content/uploads/2017/12/obj_array1.jpg

v-for互嵌

data:{
  scroll_img:[
    {img_name1:1},
    {img_name2:2},
    {img_name3:3},
    {img_name4:4},
  ]
}


<ul>
 <li v-for="(value,index) in scroll_img">
   索引:{{index}}---
   <span v-for=" (val,key,index) in value"> 属性:{{key}} </span>---
   <span v-for=" (val,key,index) in value"> 值:{{val}} </span>
 </li>
</ul>
https://sdeno.com/wp-content/uploads/2017/12/forandfor.jpg

v-for 筛选、过滤

在遍历的同时不希望有些数据显示出来

1,//v-bind:class方法,给不需要显示的数据添加指定class名隐藏或者添加test自定义过滤方法
<li v-for="(value,index) in scroll_img" :class="!!value.xx?'gg':'mm' | test">
   索引:{{index}}---
</li>
2,//v-show方法,跟v-bind:class效果类似仅仅是隐藏,dom还是存在的
3,//v-if,直接把不希望显示的数据删除
<li v-for="(value,index) in scroll_img" v-if="!value=='xx'">  //遍历出来的value值如果等于xx字符串就不显示
  索引:{{index}}---
</li>
4,通过计算属性在过滤后才把值带入到v-for中遍历

v-for排序:升序、降序

如果希望遍历的值根据某个字段降序或者升序,要先通过计算属性排列好才带入到v-for循环中

:key重新渲染

案例:

默认情况使用v-for渲染列表时,在每个input输入内容,如下

https://sdeno.com/wp-content/uploads/2017/12/for_input1.jpg

在点击“打乱顺序”,reverse(),视图上并没有发生任何变化,其实dom结构已经发生了变化,如图:

https://sdeno.com/wp-content/uploads/2017/12/for_input2.jpg

所以需要强制在更新一次排序视图才能同步更新,给遍历input的dom结构添加一个:key=”index”属性,index必须是唯一不能重复,添加之后在点击“打乱顺序”,

https://sdeno.com/wp-content/uploads/2017/12/for_input3.jpg

如果在使用v-for遍历数据时,如果数据发生了变化但是视图没变可以尝试使用:key来解决。

https://cythilya.github.io/2017/04/27/vue-list-rendering/

生命周期

vue的一系列执行过程,其实都通过以下的钩子(回调函数)捕获到,同时也可以通过钩子在我们需要的某时刻添加我们需要的功能。

(1)第一次刷新页面加载时都会触发到以下钩子:

beforeCreate   //常用,添加类似loding效果
create   //常用,在数据显示到视图时,在额外添加数据到vm.$data中
beforeMount
mounted   //常用,有时需要获取dom,或者把接口的数据插入到dom中

(2)数据发生改变时:

beforeUpdate
updated   //常用,数据发生改变后回调

(3) 销毁(就是停止数据的双向绑定效果)vm.$destroy()

beforeDestroy
destroyed  //常用,销毁后回调

虽然生命周期经历过这几个步骤,但是常用的也就那么几个。

组件封装(重点)

自己一个经典案例:《vue树节点创建以及增删改

作用就是可以重复性的利用代码,例如选项卡组件可以在同一个页面下 多次使用。

组件分:全局 和 局部

1,全局

var myComponent = Vue.extend({
  template: '<div>This is my first component!</div>’ //这个是子组件,组件必须要有一个容器div包裹着才行
})


// 2.注册组件,并指定组件的标签,组件的HTML标签为<my-component>
Vue.component('my-component', myComponent)  <---全局组件声明方法

或者

Vue.component('my-component',{
   template: '<div>This is my first component!</div>’ //这个是子组件,组件必须要有一个容器div包裹着才行
})

//全局组件声明一定要写在new Vue实例对象的前面不然就出错

new Vue({
 el: '#app'
});

调用:
<div id="app">
  <!-- 3. #app是Vue实例挂载的元素,应该在挂载元素范围内使用组件-->
  <my-component></my-component> //调用组件,放在哪个vm容器下就在哪个vm显示,这个是父组件
</div>

2,局部

new Vue({
   el: '#app',
   components: {
      'my-component' : {
          template:'<div> <button @click="fn">按钮1</button> <p>{{value}}</p> </div>'
      }
   }
});


或者

var myComponent = Vue.extend({
  template: '<div>This is my first component!</div>'
})

new Vue({
el: '#app', components: { // 2. 将myComponent组件注册到容器#app实例下,也就只有此vm容器内部可以使用其他的容器不行 'my-component' : myComponent } });
调用:
<div id="app">
   <!-- 3. my-component只能在#app下使用-->
   <my-component></my-component>
</div>

以上发现一个问题如果在组件复杂的时候template需要很多html就很麻烦且不好写,
可以利用<script type=”text/x-template” id=”page”>

案例:
<script type="text/x-template" id="page"> 
 <ul>
   <li></li>
 </ul>
</script>

Vue.component("page",{ template:"#page"})

或者

template:'<div>\
           <h2>我是子组件的标题</h2>\
           <slot>\
             只有在没有要分发的内容时才会显示。\
           </slot>\
          </div>'

特殊dom结构  使用is
例如table的结构都是固定的,要基于这样固定结构模板来写组件要这么做

<table>
  <tbody is="my-component"></tbody>  //这里的tbody会被模版替代
</table>

Vue.component('my-component',{
 template:'<tbody><tr><td>123</td></tr></tbody>'
});

效果:

<table><tbody><tr><td>123</td></tr></tbody></table>

类似的还有ul ol select

*注意:不推荐使用table表格里面嵌套vue代码会报错,尤其是ie10以上浏览器。推荐使用div+css去模拟table表格

.vam { display: table; border-collapse:separate;border-spacing: 5px; } //table
.vam>div { display: table-row; }  //tr
.vam>div>div { display: table-cell; vertical-align: middle; }  //td

<div class="vam">
    <div>
        <div> </div>
    </div>
</div>

组件继承

//组件继承使用场景,可以把多组件使用比较多的方法独立封装成一个公共组件,其他组件在去调用,避免写重复代码。

<div id="root">

</div>

var a={
   template:`<div>123---{{xxx}}</div>`
}

var x=Vue.extend(a);

new x({
  el:'#root',
  data:{
     xxx:'aaa'
  }
});
//父组件
var a={
  template:`<div>123</div>`,
  mounted(){
     console.log('a父亲');
  }
}


//子组件
var b={
  extends:a, //子继承父
  template:`<div>456</div>`, //子模板覆盖父模板
  mounted(){
    console.log('b子');
    this.$parent.$options.name //打印出实例化对象的name属性值
  }
}

  实例化方法1:
  var c=Vue.extend(b);  //组件b插入到Vue对象中
  new c({
    el:'#root'
  });

  实例化方法2: //推荐使用这种写法
  new Vue({
      name:'root',
      el:'#root',
      components:{
          diycomponent:b
      },
      template:'<diycomponent></diycomponent>'
  });

//结果
a父亲
b子

parent案例:

var a={
  template:`
   <div>
     <div>{{haha}}---{{baba}}</div>
   123</div>
  `,
  props:['baba']
}


var b={
  extends:a, //b组件继承a组件
  data:function () {
    return {
       haha:'给父组件传参'
    }
  }
}

var xx=new Vue({
  name:'xx',
  data:{
    a1:'11',
    b2:'22'
  }
});


new Vue({
  parent:xx, //继承xx的new Vue对象
  el:'#root',

  components:{
    com:b //引入组件b
  },
  template:'<com :baba="this.$parent.a1"></com>', //将组件渲染出来
  mounted:function () {
      console.log( this.$parent.a1 ); //获取到指定父元素的值,可以获取不推荐去修改
  }
});

组件传参 (重点)

要向组件中的template模板传值,只能靠组件中的data和props
prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。

1,组件传入参数
//以下是子组件
Vue.component('todo-item', {    //组件要放在最前面不能放在new Vue后面
    template: ‘<li v-show=“num">This is a todo{{num}}------{{value1}}-----{{value2}}</li>’,   //子组件
    data:function () {  //传递参数,这里要使用function
       return {num:0}  //返回的也必须是对象
    },
    props:['value1','value2’]  //父组件要传值给子组件就必须在props里面声明变量
})
//以上是子组件

var vm = new Vue({
  el: '#app’,
  data:{
    parentMsg:’字符串'
  }
});

<div id="app">
  <ol>
    //如果要获取到vue实例对象里面的数据就要使用v-bind,以下parentMsg就是vue实例对象中data的数据
    <todo-item value1="参数1" v-bind:value2="parentMsg"></todo-item>  //<--这个是父组件
  </ol>
</div>
 2,动态参数
其实就是v-model和参数结合
<input type="text" v-model="parentMsg">
<my-dir v-bind:value1="parentMsg"></my-dir>  //value1是子组件里面的props里的属性,parentMsg是vue实例化对象中data里的数据

3,传递数据类型
用v-bind 和 不用的区别

<my-dir value1="1"></my-dir> //这时候传递的是字符串类型
<my-dir v-bind:value1="1"></my-dir> //这时候传递的是数字类型

<my-dir value1="[1,2]"></my-dir> //传递给模板的数据类型仅仅是字符串
<my-dir :value1="[1,2]"></my-dir> //传给模板的数据类型是数组

4,参数验证
限制父组件参数传入的类型,或者自定义传入参数的验证方法 。这些主要组件给别人用时做的限制。

例如:
props:{
 value1: {
   type: Number,
   default: 100
 },
 value2:Number,
 value3:[String,Number],
 value4:{
   type:Boolean,
   required:true
 },
 value5:{
   type:Array,
   default:function(){
     return [];
   }
 },
 value6:{
   validator:function(value){ //自定义验证,value6=“11” 传入的值必须大于10
      return value>10
   }
 }
}

除了以上类型还有 Object 、Function

案例:

<my-dir value1=“1″></my-dir> //这样限制了value1传入的类型,必须是数字类型,如果组件没设置value1属性,那么默认出入值为100
还有更多请参考api

5,子组件传参数给父组件
父组件可以通过props传参数给子组件,子组件可以通过以下方法做到,就必须使用自定义事件
$on(eventName) 监听事件 //适用在不同组件之间传参使用
$emit(eventName) 触发事件  //适用子组件传参给父组件。例如$emit(‘click’,data)该方法都写子组件中 ,这里的click是父组件中,data是子组件要传给父组件的数据

$once只有触发一次

直接看例子:

<my-component @on_jia="get_num" v-on:on_jian="get_num"></my-component>
<p>这里的str是vue实例化对象里面的数据:{{str}}</p>


Vue.component('my-component',{
 template:'<div><button @click="jia(num)">+</button>-----<button @click="jian(num)">-</button></div>',
 data:function () {
     return {num:0}
 },
 methods:{
   jia:function (value) {
     value=value+1;
     this.num=value;

     this.$emit('on_jia',this.num);
   },
   jian:function (value) {
     value=value-1;
     this.num=value;
     this.$emit('on_jian',this.num);
   }
 }
});



var vm=new Vue({
 el:'#app',
 data:{
   str:0
 },
 methods:{
   get_num:function (value) {
      this.str=value;
   }
 }
});

最终效果:把组件里面的参数值传给了实例化对象

过程:
1,子组件里面通过 this.$emit(自定义方法,参数) 自定义一个方法,并把参数带进去。
2,父组件通过v-on监听,子组件自定义的方法,并触发一个回调事件get_num,get_num负责把子组件参数获取过来。(中介作用)
3,在vue实例化对象中,在methods里面同样声明一个get_num并获取到,父组件传过来的数据。

$on使用

<div id="xx">
     <div @click="haha()">s</div>
</div>


<script>
    // click事件->haha->$emit->$on->diyclick->geth
    new Vue({
        el:'#xx',
        data(){
            return{
                a1:'初始数据'
            }
        },
        created(){
            //$on可以为一个自定义事件定义多个方法,遇到复杂的逻辑可以这样处理不需要都写在一起。
            //一个事件多个方法按上到下顺序执行
            this.$on('diyclick',this.geth);
            this.$on('diyclick',this.geth_1);

            //也可以在同一个函数上自定义多个自定义事件,打印看看this._events
            //this.$on(['diyclick_1','diyclick_2'],this.geth);
        },
        methods:{
            geth(e){
                console.log(this.a1,e); //this.a1 是$on自己的数据,e是顺带了$emit的数据
            },
            geth_1(e){
                console.log('这里是geth_1数据',e);
            },
            haha(){
                this.$emit('diyclick','$emit自己的数据');
            }
        }

    })
</script>

6,不同组件之间的传参(非父子关系) 跨组件传参
描述:两个组件,分别在不同的vue实例中,实现相互之间的传递参数,借助一个第三个空的vue实例和$emit、$on

//创建/src/eventbus.js
import Vue from 'vue'
export default new Vue()


//子组件1
<button @click="fn">1</button>
import Eventbus from '@/eventbus'
export default {
  name: "test1",
  methods:{
    fn(){
      Eventbus.$emit('send',{a:1})  //发送参数
    }
  }
}


//子组件2
import Eventbus from '@/eventbus'
export default {
  name: "test2",
  mounted() {
    Eventbus.$on('send',(e)=>{
      console.log(e)  //接收到{a:1}
    })
  }
}

 


7,组件数据共享
其实只要在全局环境中 声明一个对象,在组件的data里面引用即可

var obj={num:0}
data:function(){
 return obj
}

8,父组件传值给子组件里的计算属性

<my-component :value="1"></my-component> //父组件 传一个 数字类型 值为1 给子组件

Vue.component('my-component',{
 template:'<div>{{xx}}</div>’, //计算属性的结果给 模板
 props:['value'],
 computed:{ 
   xx:function () {
     return this.value+2; //获取父组件的参数 并 参数计算后在把结果传给模板
   }
 }
});

9,子组件直接获取,组件所在的vue实例对象里面的data数据

<div id="app"> <xx></xx> </div>

Vue.component('xx',{
  template:'<a> </a>',
  methods:{
      fn:function(){
           this.$parent.num   //num是实例对象里面的数据
      }
  }
});


new Vue({
   el:'#app',
   data:{
      num:0
   }
});

父组件直接获取子组件数据是用this.$children ,会遍历出所有的子组件

10, new Vue实例对象直接获取子组件的data数据,可以在父组件上用ref=””,如下案例:

<div id="app"> <xx ref="comA"></xx>  <a @click="fn"></a> </div>  //给组件取名字,方便快速定位

Vue.component('xx',{
 template:'<a> </a>',
 data:function(){
    return {num:0}
 }
});


new Vue({
 el:'#app',
 methods:{
     fn:function(){
          this.$refs.comA.num  //num是子组件的数据
     } 
 }
});

*注意如果在父组件里的子节点使用ref是获取到dom元素,如:

<xx ref="comA"> <span ref="comB">111</span>  </xx>

this.$refs.comA.num  //num是子组件的数据,打印0
this.$refs.comB //打印 dom元素 <span ref="comB">111</span>

11,孙子获取爷爷数据,provide / inject

//孙子组件
var onecomponent={
  template:`<div> 345 </div>`,
  inject:[
     'yeye',
     //'data'
  ],
  mounted(){
     console.log( this.yeye.baba );
    //console.log( data.baba );
  }
}


//儿子组件
var component_temp={
  template:`
    <div> <slot baba="baba22"></slot> <haha></haha> </div>
  `,
  data(){
    return{
      xx:'11'
    }
  },
  components:{
    haha:onecomponent //引入孙子组件
  },
  props:['value1']
}


new Vue({
  el:'#root',
  provide:function(){

    //这里代码,防止低版本的vue,孙子在获取爷爷数据时候,没有双向数据绑定效果而写的
        // var data={}
        // var _this=this;
        // Object.defineProperty(data,'baba',{
        //     get:function () {
        //         return _this.baba
        //     },
        //     enumerable:true
        // })
    return{
     yeye:this,
     // data:data
    }
  },
  components:{
     com:component_temp
  },
  template:'<com ref="x"> <p slot-scope="diy">{{diy.baba}}</p> </com>', //爷爷组件
  data:{
    baba:'baba11' //孙子要获取爷爷的数据
  }
});

父子组件方法调用

vue 父调用子方法

子:

mounted(){
  this.$on('bridge',(val)=>{
    this.childAction(val);
  });
  ///此时通过$on进行监听中间桥接函数bridge对目的方法childAction进行触发
},
methods:{
  childAction(val='hello world'){
    console.log(val) //得到爸爸传来的数据
  }
}

父:

<Category ref="category"></Category>

<el-button @click="addcate()">修改分类</el-button>
addcate(){
  this.$refs.category.$emit('bridge','这里是要给儿子的数据');
}

子调用父亲

(1)通过this.$parent.event来调用父组件的方法

父组件

methods: {
 fatherMethod() {
   console.log('测试');
 }
}

子组件

mounted() {
  this.$parent.fatherMethod();
}

(2)子组件里用$emit

父组件

<child @fatherMethod="fatherMethod"></child>

methods: {
   fatherMethod() {
     console.log('测试');
   }
}

子组件

this.$emit('fatherMethod'); //调用

(3)父组件把方法传入子组件中,在子组件里直接调用这个方法

父组件

<child :fatherMethod="fatherMethod"></child>
methods: {
      fatherMethod() {
        console.log('测试');
      }
}

子组件

props: {
 fatherMethod: {
   type: Function,
   default: null
 }
},
mounted(){
  if (this.fatherMethod) {
      this.fatherMethod();
  }
}

动态组件 (类似选项卡)

需要特殊<component v-bind:is=”str”></component>

其实也就是多个组件项目切换 显示

<div id="app">
   <a @click="fn('comA')"></a>
   <a @click="fn('comB')"></a>
   <component v-bind:is="str"></component>  //str显示哪个组件名就显示那个组件
</div>



var vm=new Vue({
  el:'#app',
  data:{
     str:'comA'
  },
  methods:{
      fn:function(value){
          this.str=value;
      }
  },
  components:{
     'comA':{
          template:'<a>1</a>'
     },
     'comB':{
          template:'<a>2</a>'
     }
  }
});
缓存
《vue中keepAlive使用》
避免组件切换回来又重新渲染,可以使用keep-alive在外包一层,更多功能http://cn.vuejs.org/v2/api/#keep-alive
<keep-alive>
   <component :is="currentView">
      <!-- 非活动组件将被缓存! -->
   </component>
</keep-alive>

异步组件

有些组件可能要从别的地方获取数据后才显示出来,这时候肯定考虑异步

<div id="app">
    <com ref="haha"></com>
    <button @click="fn">点我</button>
    <p>{{str}}<p>
</div>
var vm=new Vue({
 el:'#app',
 data:{
   str:''
 },
 methods:{
   fn:function(value){
     this.str=this.$refs.haha.num;
   }
 },
 components:{
   'com':function (a,b) {

        $.ajax({
           url:'http://localhost/test_duble_data.html',
           success:function () {
             a({
                template:'<div>xx</div>',
                data:function(){
                   return {num:'子组件传来的数据'}
                }
             });
           }
        });

    }
 }
});

强制更新,实现数据双向绑定效果

html:

<div id="root">
  <div>{{text}}--{{obj.a}}</div> //正常来说如果要在模板中使用obj.a,那么在vue实例中就要预先声明obj中的a变量即使是空值也好。
</div>
var app=new Vue({
  el:'#root',
  // template:'<div>{{text}}--{{obj.a}}</div>',
  data:{
    text:0,
    obj:{} //但是在obj中并没有预先声明a变量,就算在后面临时赋值obj.a=xxx,在模板中调用也不会有值显示。
  }
 });

 var i=0;
 setInterval(()=>{
   i++;
   app.obj.a=i; //临时给a赋值
   app.$forceUpdate(); //如果要为临时设置的也实现双向绑定就要强制更新组件。(不太推荐)
   //如果没有临时预设属性,又不想使用强制更新,可以使用app.$set(app.obj,'a',i),作用是临时补上a属性到data中
 },1000)

DOM回调函数

按理来说我们使用vue是不需要操作dom的,但是偏偏可能我们在做一些动画效果时,就是要需要等dom加载完成后才去执行

<div id="app">
   <div id="haha" v-if="num">数据</div>
   <button @click="fn">点我</button>
</div>

var vm=new Vue({
  el:'#app',
  data:{
     num:false
  },
  methods:{
     fn:function(){
          this.num=true;
          this.$nextTick(function(){ //等待#app容器下的dom加载完成后才执行以下代码
              
              $('#haha').text();
          });
     }
  }
});


slot内容分发  (插槽)

把这个理解为父组件往子组件传值的另一种方法就行。

或者理解为,在父组件里面写的html想要在子组件渲染出来就需要到slot

//在父组件的<my-component></my-component>标签里面加内容
<div id="app">
  <my-component> //这个是父组件,以下内包裹的就是要传给子组件的内容
    <p slot="header">头部1</p>
    <p slot="header">头部2</p>
    <p>主体内容1</p>
    <p>主体内容2</p>
    <p slot="foot">底部1</p>
  </my-component>
</div>

  

<script>
//要显示出父组件加的内容就需要到slot标签
Vue.component('my-component',{
  template:'<div> ' +
            '<slot name="header"></slot> ' +
            '<slot></slot> ' +
            '<slot name="foot"></slot> ' +
            '<slot name="foot2">父组件没传值就默认显示我</slot> ' +
           '</div>'
  });



 new Vue({
  el:'#app'
 });

</script>



//渲染效果
<div id="app">
  <div>
    <p>头部1</p>
    <p>头部2</p> 
    <p>主体内容1</p> 
    <p>主体内容2</p> 
    <p>底部1</p> 
    父组件没传值就默认显示我
  </div>
</div>


1,父组件my-component包裹着内容就是要传给子组件的
2,子组件要接收父组件传来的值就要使用<slot></slot>
3, 子组件的<slot name=”xx”>使用name就只能指定接收父组件的<dom slot=”xx”></dom>
父组件可以使用多次<dom slot=”xx”></dom>,子组件也可以接收多个,只要一一对应
4,如果子组件有<slot name=”xx”>设置默认值</slot>,而父组件没有传值来,子组件可以显示默认值
5,只要子组件没有为slot设置name的,父组件的其他内容就默认替换

slot传参 (作用域插槽)

slot结合template使用,子组件传参给父组件,在子组件的slot标签上自定义参数,父组件接收参数时使用scope=””

<my-component>
  <template slot="xx" scope="xxx">
     <p>123</p>
     <p>{{xxx.msg0}}</p>
     <p>{{xxx.msg1}}</p>
  </template>
</my-component>



<script>

Vue.component('my-component',{
  template:'<div> <slot name="xx" :msg1="str" msg0="内容123"></slot> </div>',
  data:function () {
    return {str:'子组件内容'}
  }
});


new Vue({
 el:'#app'
});

</script>
//渲染效果
<div id="app">
  <div>
     <p>123</p> 
     <p>内容123</p> 
     <p>子组件内容</p>
  </div>
</div>

如果slot没有结合使用<template>时,子组件传参给父组件时,父组件接收参数要使用slot-scope=””

//子组件
template:`
<div> <slot baba="baba22"></slot> </div>
`

//父组件
template:'<com> <p slot-scope="diy">{{diy.baba}}</p>  </com>',  //父组件接收参数要用slot-scope

$slots

可以利用$slots来获取到父组件传来的slot具名,例如可以获取到父组件传来的dom元素

<my-component>
  <p id="nimei" slot="xx">xxx</p>
</my-component>

Vue.component('my-component',{
  template:'<div> <slot name="xx" :msg1="str" msg0="内容123"></slot> </div>',
  data:function () {
     return {str:'子组件内容'}
  },
  mounted:function () {
    document.title=(this.$slots.xx)[0].elm.id  //获取到父传来的slot="xx"的dom元素,也就是id="nimei"的p元素
  }
});

内联模板

之前介绍slot,在父组件包裹里面写数据就默认相当于参数要传给子组件,
我们也可以不在子组件里的template里面写div,可以直接把父组件里面的
数据当做模板。

<div id="app">
 <my-component inline-template> //inline-template告诉父组件,里面内容是当成模板使用,而不是当成slot内容分发给子组件
   <div> //跟子组件的template一样写模版最外层同样也要包裹着一个div容器不然没效果
     <p>父组件里面的内容就相当于模板了</p>
     <p>{{msg}}</p>
     <p>{{message}}</p>
     <p>{{aa}}</p> //这里优先使用子组件的数据
   </div>
 </my-component>
</div>


<script>

Vue.component('my-component',{
 data:function () {
   return {msg:'子组件',aa:'子组件的aa'}
 }
});

new Vue({
  el:'#app',
  data:{
    message:'父组件',
    aa:'父组件的aa'
  }
});

</script>

动态生成vue实例
vue实例都是我们最开始使用new Vue()来实现了,但有时候我们也需要动态是生成。

<div id="app">
  <button @click="fn()">动态挂载vue对象</button>

  <div id="app2">

  </div>
</div>



<script>

var myvue=Vue.extend({
  template:'<div><input type="text" v-model="haha"><p>{{haha}}</p></div>',
  data:function () {
      return {haha:''}
  }
})


new Vue({
 el:'#app',
 data:{
 message:'父组件',
    aa:'父组件的aa'
 },
 methods:{
   fn:function () {
     new myvue().$mount('#app2');
   }
 }
});
</script>

自定义指令

<div id="app">
 <input type="text" v-focus="{haha:'笑哈哈'}">
</div>

<script>
    //以下是全局
    Vue.directive('focus',{
        bind:{},
    });


    //以下是局部 自定义指令
    new Vue({
        el:'#app',
        directives:{
            focus:{
                bind:function (el,binding) {  //仅仅执行一次,作用就是为绑定的元素,添加初始化值。
                    el.value=1
                    el.className='nihao';
                    el.addEventListener('click',function () {
                        console.log('绑定');
                    });
                    console.log(el);  //笑哈哈
                    el.xx=123;  //要想其他钩子函数都能调用到,就把要公用的属性和方法都写在el中
                },
                inserted:function (el,binding) {  //dom加载完成后,就调用
                    el.focus();
                    console.log(el.xx);  //123
                },
                update:function (el,binding,vnode,oldVnode) {

                },
                componentUpdated:function (el,binding,vnode,oldVnode) {

                },
                unbind:function (el,binding) {

                }
            }
        }
    });

</script>

render函数   虚拟dom   vue创建html

template:'<div ref="x"></div>' //template里面的html,最终还是靠render函数显示到页面上的
等价
render(createElement){
        return createElement('div',{
            ref:'x'
        });
},

具体更多用法参考:https://cn.vuejs.org/v2/guide/render-function.html

**注意:能用template创建html就尽量用,否则使用render创建html会增加难度。

使用场景是:当模板出现重复性比较高时可以考虑使用,例如:

<script type="text/x-template" id="xx"> 
  <div>      
    <h1 v-if="show">       
      <a></a>      
    </h1>

    <h2 v-if="show">       
      <a></a>      
    </h2>

     <h3 v-if="show">       
      <a></a>      
     </h3>

     <h4 v-if="show">       
      <a></a>      
     </h4> 
  </div>
</script>

如果出现类似以上的重复性出现的dom就考虑使用虚拟dom进行遍历了。

对比学习效率会比较高

template:'\
         <div id="ele"> \
           :class="show:show" \
           @click="fn">内容</div>'

比较

render:function(createElement){
   return createElement(
             'div',
             {
               class:{'show':this.show}
               attrs:{id:'ele'},
               on:{click:this.fn}
             },
             '内容'
          )
   }

可读性明显template比render好,所以要看情况使用虚拟dom不然会带来不便。

上面的案例都知道传进来的createElement参数才是最关键的,而这个参数是一个函数,
函数里面又有3个参数

createElement(
  {string|object|function}, //必选 (根据逻辑判断生成不同的dom元素标签)
  {object}, //可选 (数据填充,第二个参数玩法很多)
  {string|array} //可选 (子节点)
)

随身笔记

vue.js笔记
目前在vue 2.5.9测试学习 顺便推荐调试工具:vue-devtools,直接去chrome商城安装即可。在引入vue.js时,不能使用min版本不然无法使用。 创建vue视图 var vm=new Vue({ name:'…
扫描二维码继续阅读
2017-12-24