vue基础详解(全)

一,VUE的核心

1.数据驱动(mvvm模型,只要改变model的数据,视图层就会自动更新)

2.视图组件: 把整一个网页的拆分成一个个区块,每个区块我们可以看作成一个组件。网页由多个组件拼接或者嵌套组成。

二,使用vue

1.引入vue之后就可以进行下面的步骤

2.创建语法(创建 ,设置数据,挂载,渲染)

var vm = new Vue({})//参数是一个对象

 

三、定义一个vue实例的4个常用选项

1.过滤器 filter属性:

例如我们从后台取出来的数据都是带有小数位的,但是需求上面要求的是只展示整数部分,这个时候我们就可以用过滤器属性了。我们先创建一个实例,数据中的格式都是带有小数点的,此时我们创建filters属性(记住此属性的值是一个对象,所以":"之后要带上大括号“{}”,在filters里面我们写一个转换函数,这个函数的功能就是将传参转换为整数后返回,此处用的是es6的语法),写好函数后要怎么使用呢?我们在div里面,将参数用管道符号"|"传给toInt函数,这样就可以了!

    数字1:{{num1 | toInt}}     数字2:{{num2 | toInt}}     数字3:{{num3 | toInt}}
let app6 = new Vue({     el:'#app6',     data:{         num1:123.4,         num2:520.1314,         num3:1314.520     },     filters:{         toInt(value){                 return parseInt(value);              }     } });

4.计算属性computed

 

有时候,我们拿到一些数据,需要经过处理计算后得到的结果,才是我们要展示的内容。

比如:我们有三个数,但是需要展示的是三个数字之和。这种情况,就适合用我们的计算属性computed。(可以直接调用计算属性里面的值。)

let app6 = new Vue({

    el:'#app6',

    data:{

        num1:123.4,

        num2:520.1314,

        num3:1314.520

    },

    filters:{

        toInt(value){

                return parseInt(value);

             }

    },

    computed:{

        sum(){

            return parseInt(this.num1+this.num2+this.num3);

        }

    }

});

计算属性computed的定义语法和过滤器filters类似,但是用法不一,如下:

总和是{{sum}}

我们提供的函数将用作属性app6.sum 的 getter 函数:

计算属性拥有getter和setter两个属性,也就是我们可以设置这两个属性,例如

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]

    }

  }

}

那么每次运行fullname的时候,就是使用getter,返回一个值,而每次执行fullname = ""的时候,就会执行set里面的函数,将firstname和lastname都进行改变。

 

需要注意的是,sum的值是依赖data中的数据num1,num2,num3的,一旦它们发生了变化,sum的值也会自动更新

 

5.methods  方法

顾名思义,在methods中,我们可以定义一些方法,供组件使用。

比如,我们定义一个数据a,当用户点击按钮的时候,a的值加1。这种情况,我们可以利用methods来实现。

 

定义一个plus( )的方法在methods中,下面我们把plus( ) 绑定在按钮的点击事件上

let vm = new Vue({

    //挂载元素

  el:'#app',

    //实例vm的数据

  data:{

         a:0

    },

    //方法

  methods:{

        plus(){

            return this.a++;

        }

    }

});

    {{ a }}        

  

 

1. 计算属性computed和方法methods都能用于计算,说说两者的区别?

答: 假设我们计算一个值sum,它是经过a和b相加得到的结果。

      如果我们使用计算属性的方式来实现得到sum,由于计算属性computed是基于它的依赖进行缓存的,也就是sum的值只有在a或者b的值发生变化的时候才会重新计算求值。这就意味着,倘若a和b的值不发生改变,即使重新渲染或者多次访问计算属性中的sum,都无需重新计算,节省运算开销。

        反观,如果我们用methods方法来定义一个求和函数sum( )来计算a和b的求和,如果多个地方需要渲染a和b的和,我们则需要多此调用sum( )方法,这样的后果就是多次重复执行函数,造成不必要的运算开销。

 

 

6.watch 观察

watch选项是Vue提供的用于检测指定的数据发生改变的api,也就是说,当这个数据发生变化的时候会触发对应的函数

上面点击按钮a的值加1的例子,不就是数据发生变化了吗?我们就用watch选项来监听数字a是否发生了变化,如果了监听到了变化,我们就在控制台输入以下a的最新值。

在上个例子的代码基础上,我们加上watch部分的代码

let vm = new Vue({

    //挂载元素

  el:'#app',

    //实例vm的数据

  data:{

         a:0

    },

    //方法

  methods:{

        plus(){

            return this.a++;

        }

    },

    //观察

  watch:{

        a(){

          console.log(`有变化了,最新值:`);

          console.log(this.a);

        }

    }

});

最后一部分watch就是我们新加的代码,a( ) 表示我们要观察监听的就是数据a,

vue基础详解(全)_第1张图片

四.vue实例的生命周期:

不要在选项属性或者回调函数上面使用箭头函数,因为箭头函数是和父级上下文绑定在一起的。

vue基础详解(全)_第2张图片

1. beforeCreate(还没创建实例)

此阶段为实例初始化之后,此时的数据观察和事件配置都没好准备好。

我们试着console一下实例的数据data和挂载元素el,代码如下:

  

{{name}}
  

 

得到的结果是:

此时的实例中的data和el还是undefined,不可用的。

 

2. created(创建实例)

beforeCreate之后紧接着的钩子就是创建完毕created,我们同样打印一下数据data和挂载元素el,看会得到什么?

在上一段代码的基础上,加上下面这段代码:

created(){

    console.log('创建完毕');

    console.log(this.$data);

    console.log(this.$el);

}

我们看到打印的结果:

此时,我们能读取到数据data的值,但是DOM还没生成,所以属性el还不存在,输出$data为一个Object对象,而$el的值为undefined。

 

3. beforeMount(生成dom)

上一个阶段我们知道DOM还没生成,属性el还为undefined,那么,此阶段为即将挂载,我们打印一下此时的$el是什么?

增加一下代码:

beforeMount(){

    console.log('即将挂载');

    console.log(this.$el);

}

我们看到打印结果:

此时的$el不再是undefined,而是成功关联到我们指定的dom节点

但此时{{ name }}还没有被成功地渲染成我们data中的数据。没事,我们接着往下看。

4. mounted(将实例与dom相连)

mounted也就是挂载完毕阶段,到了这个阶段,数据就会被成功渲染出来,我们编写mounted的钩子,打印$el 看看:

 

mounted(){

    console.log('挂载完毕');

    console.log(this.$el);

}

打印结果

 

如我们所愿,此时打印属性el,我们看到{{ name }}已经成功渲染成我们data.name的值:“krys”。

5. beforeUpdate(实例更新前)

我们知道,当修改vue实例的data时,vue就会自动帮我们更新渲染视图,在这个过程中,vue提供了beforeUpdate的钩子给我们,在检测到我们要修改数据的时候,更新渲染视图之前就会触发钩子beforeUpdate。

html片段代码我们加上ref属性,用于获取DOM元素。

    {{name}}
Vue实例代码加上beforeUpdate钩子代码: beforeUpdate(){   console.log('=即将更新渲染=');   let name = this.$refs.app7.innerHTML;   console.log('name:'+name); },

我们试一下,在控制台修改一下实例的数据name,在更新渲染之前,我们打印视图中文本innerHTML的内容会是多少:

vue基础详解(全)_第3张图片

 

我们在控制台把app.name的值从原来的 “krys” 修改成 “斑马”,在更新视图之前beforeUpdate打印视图上的值,结果依然为原来的值:“krys”,表明在此阶段,视图并未重新渲染更新。

6. updated(实例更新完成之后)

此阶段为更新渲染视图之后,此时再读取视图上的内容,已经是最新的内容,接着上面的案例,我们添加钩子updated的代码,如下:

updated(){

  console.log('==更新成功==');

  let name = this.$refs.app7.innerHTML;

  console.log('name:'+name);

}

结果如下:

大家注意两个不同阶段打印的name的值是不一样,updated阶段打印出来的name已经是最新的值:“斑马”,说明此刻视图已经更新了。

7. beforeDestroy

调用实例的destroy( )方法可以销毁当前的组件,在销毁前,会触发beforeDestroy钩子。

8. destroyed

成功销毁之后,会触发destroyed钩子,此时该实例与其他实例的关联已经被清除,它与视图之间也被解绑。

案例:我们通过在销毁前通过控制台修改实例的name,和销毁之后再次修改,看看情况。

beforeDestroy(){

   console.log('销毁之前');

},

destroyed(){

   console.log('销毁成功');

}

效果如下图:

vue基础详解(全)_第4张图片

 

销毁之前,修改name的值,可以成功修改视图显示的内容为:“更新视图”,一旦效用实例的$destroy( )方法销毁之后,实例与视图的关系解绑,再修改name的值,已于事无补,视图再也不会更新了,说明实例成功被销毁了。

 

9. actived

keep-alive组件被激活的时候调用。

10. deactivated

keep-alive 组件停用时调用。

关于keep-alive组件的激活和停用

 

以后最为常用的钩子是:created 成功创建。

 

 

五.在html中绑定数据

1.Mustache语法:{{}}的写法,在里面写入需要渲染的数据

{{name}}
let app7 = new Vue({     el:"#app7",     data:{         name:"krys"     } }

 

2.绑定纯html

如果上面的例子中,name的值是一些html语句,如果单纯的是像上面这样写的话,会原样输出,不会转化为html语句,例如

    {{name}}

并不能得到我们想要的效果,这时候需要用到vue提供的v-html指令,

    {{name}}

 

vue基础详解(全)_第5张图片

3.绑定属性:attr或者v-bind:attr,其中attr是属性名, 需要注意的是:当渲染的属性值是布尔值的时候(true和false),效果就不一样了,并不是简单地将true或者false渲染出来,而是当值为false的时候,属性会被移除

   baidu
let app9 = new Vue({     'el':"#app9",     data:{         link:"https://www.baidu.com"     } })

vue基础详解(全)_第6张图片

 

 

 

4.支持javascript表达式

值得注意的是,只能包含单个表达式,多个表达式组成的不会生效!

上面讲到的都是将数据简单地绑定在视图上,但事实上,vue还支持我们对数据进行简单的运算:javascript表达式支持。

{{ num+3 }}

vue基础详解(全)_第7张图片

 

vue基础详解(全)_第8张图片

 

 

六,必须要掌握的指令

1.v-text指令

v-text指令用于更新标签包含的文本,他的作用跟双大括号{{}}一样,

    

vue基础详解(全)_第9张图片

2.v-html指令(执行html)

这个指令帮助我们绑定一些包含html代码的数据在视图上。

    

vue基础详解(全)_第10张图片

 

3.v-show指令

控制元素的css中display属性,v-show指令的取值为true/false,分别对应着显示/隐藏。

    

我是true

    

我是false

vue基础详解(全)_第11张图片

4.v-if(其实就相当于if)

v-if指令的取值也是true或者false,它控制元素是否需要被渲染出来,设置为true的

标签,成功渲染出来,而设置为false的

标签,直接被一行注释代替了,并没有被解析渲染出来。

那么如何区别v-show和v-if这两个指令呢?就是一个会被渲染出来,一个不会。记住一个很重要的点如果需要频繁切换显示/隐藏的,就用 v-show ;如果运行后不太可能切换显示/隐藏的,就用 v-if 。

    

我是true

    

我是true

 

vue基础详解(全)_第12张图片

5.v-else指令(相当于else)

 

1) if和else指令,在一般的变成语言都是结对出现,在vue里也不例外,它没有对应的值,但是要求前一个兄弟节点必须使用v-if指令。(没有if哪来的else),如果v-if的值为true那么else里面的东西将不会渲染出来

    

我是if

    

我是else

vue基础详解(全)_第13张图片

vue基础详解(全)_第14张图片

 

 

3)v-else-if

  A
  B
  C
  Not A/B/C

v-else,v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。

 

4)使用key来管理可复用的元素

Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:









new Vue({

    el: '#app',

    data: {

        loginType:"username"

    },

    methods:{

        change:function () {

            this.loginType == "username" ? this.loginType = "email":this.loginType = "username";

        }

    }

});

那么在上面的代码中切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder。

 

这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可。









new Vue({

    el: '#app',

    data: {

        loginType:"username"

    },

    methods:{

        change:function () {

            this.loginType == "username" ? this.loginType = "email":this.loginType = "username";

        }

    }

});

现在,每次切换时,输入框都将被重新渲染。

6.v-for指令

v-for指令用来进行循环,不但可以迭代数组,还可以迭代对象和整数,下面例子中,index是索引,item是值(有先后顺序,第一个是值,第二个是索引)。可以看到生成了4个div

   
{{index}}.我们的成员有:{{item}}

vue基础详解(全)_第15张图片

 

用for in或者for of遍历对象的时候,第一个参数是值,第二个参数是键名,第三个参数是索引。在遍历对象时,是按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下是一致的。

        
  •        {{key}}-- {{value}}     
new Vue({     el:"#v-for-object",     data:{         object:{             firstName:"liang",             lastName:"krys",             fullName:"krys liang",             age:23         }     } });

vue基础详解(全)_第16张图片

尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。因为它是 Vue 识别节点的一个通用机制。

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index" 。

这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有的唯一 id。这个特殊的属性相当于 Vue 1.x 的 track-by ,但它的工作方式类似于一个属性,所以你需要用 v-bind 来绑定动态值 (在这里使用简写):

  

 

 

当对数组进行下面两个操作的时候,数组不会进行响应式的变化,也就是说,即使进行了下面的两个操作,数据也不会重新渲染

1)利用索引直接设置一个值

2)修改数组的长度

 

var vm = new Vue({

  data: {

    items: ['a', 'b', 'c']

  }

})

vm.items[1] = 'x' // 不是响应性的

vm.items.length = 2 // 不是响应性的

 

为了解决第一类问题,改用下面两个方法来实现同样的效果

Vue.set(vm.items, indexOfItem, newValue)

 

vm.items.splice(indexOfItem, 1, newValue)

 

vm.$set(vm.items, indexOfItem, newValue)

 

 

为了解决第二类问题,可以改用下面的方法

vm.items.splice(newLength)

 

 

还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:

var vm = new Vue({

  data: {

    a: 1

  }

})

// `vm.a` 现在是响应式的

 

vm.b = 2

// `vm.b` 不是响应式的

对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:

var vm = new Vue({

  data: {

    userProfile: {

      name: 'Anika'

    }

  }

})

你可以添加一个新的 age 属性到嵌套的 userProfile 对象:

Vue.set(vm.userProfile, 'age', 27)

你还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:

vm.$set(vm.userProfile, 'age', 27)

有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性

vm.userProfile = Object.assign({}, vm.userProfile, {

  age: 27,

  favoriteColor: 'Vue Green'

})

 

 

当v-for和v-if处于同一个节点的时候,v-for的优先级比v-if要高,这就是说v-if将分别运行于每个v-for循环中。

  •   {{ todo }}

  •  

    当在自定义组件中使用 v-for 时,key 现在是必须的。然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要用 props 

      v-for="(item, index) in items"

      v-bind:item="item"

      v-bind:index="index"

      v-bind:key="item.id">

     

     

     

     

     

    7.v-bind指令

    v-bind指令用于动态绑定DOM元素的属性(例如href,src等),其中:href等价于v-bind:href

        百度官网

    vue基础详解(全)_第17张图片

     

    8.v-on指令

     

    v-on指令相当于绑定事件的监听器,绑定的事件触发了,可以指定事件的处理函数。

       

    vue基础详解(全)_第18张图片

    在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节

    为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

     

    ...

    • .stop ==stopPropagation->阻止事件继续传播

    • .prevent == preventDefault ->去除默认行为(form提交不再重载页面)

    • .capture ->添加事件监听器时使用事件捕获模式,也就是元素自身出发的事件先在此处理,然后才交给内部元素进行处理

    • .self ->只在event.target是当前元素自身时才触发处理函数

    • .once->点击事件只会触发一次

    • .passive->passive是不拦截默认事件

    使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

    Vue 还对应 addEventListener 中的 passive 选项提供了 .passive 修饰符。

    ...

    这个 .passive 修饰符尤其能够提升移动端的性能。

    不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。

     

    按键修饰符

     

    在监听键盘事件的时候,经常需要检查常见的键值。

     

     

    上面的keycode是按键的编码,等同于下面

     

     

     

    所有的按键别名

    • .enter

    • .tab

    • .delete (捕获“删除”和“退格”键)

    • .esc

    • .space

    • .up

    • .down

    • .left

    • .right

     

    可以通过全局 config.keyCodes 对象自定义按键修饰符别名:

    // 可以使用 `v-on:keyup.f1`

    Vue.config.keyCodes.f1 = 112

     

    9、v-model指令(在input、textarea、select这三个元素上创建双向数据绑定。)

    v-model会忽略所有表单元素的value、checked、selected特性的初始值而总是将vue实例的数据作为数据来源。应该在data中声明初始值,

    这个指令是非常重要且非常常用,一般用在表单输入,它帮助我们轻易的实现表单控件和数据的双向绑定,相对于之前的手动更新DOM,简直就是我们的天使。要注意的是v-model中的值,data中变量的名字,p标签中的{{}}里面的值,三者要一致。因为就是iniput输入的值被绑定到了data中,然后输出到p中。

            

    上面输入的值是:{{msg}}

     

    vue基础详解(全)_第19张图片

    10.v-once指令

    这个指令的特点是只渲染一次,后面元素中的数据再更新变化,都不会重新渲染。

             

    你输入:{{ msg }}

    vue基础详解(全)_第20张图片

     

    select元素中,如果表达式的初始值没有匹配任何选项(也就是没有指定默认值),select元素将会被渲染为未被选中的状态。这个状况下面再ios就会引发一些bug。所以,在option中提空一个空的禁用的选项。

         Selected: {{ selected }}
    new Vue({   el: '#example-5',   data: {     selected: ''   } })

    多选的时候,象下面那样绑定到一个数组

            Selected: {{ selected }}
    new Vue({   el: '#example-6',   data: {     selected: []   } }) Selected: {{ selected }} new Vue({   el: '...',   data: {     selected: 'A',     options: [       { text: 'One', value: 'A' },       { text: 'Two', value: 'B' },       { text: 'Three', value: 'C' }     ]   } })

     

     

     

     

    data: {

        picked:this.value,

        toggle:"false",

        selected:this._value

    },

    vue基础详解(全)_第21张图片

     

     

    修饰符

    1).lazy,只有当值改变的时候才进行同步

    2).number

    将用户的输入值自动转换为数值类型

    3).trim

    自动过滤用户输入的首尾空白字符,可以给v-model添加.trim修饰符

     

     

     

     

     

     

    七.vue中的组件

    1)一个组件的data选项必须是一个函数,所有的数据放在return里面。只有这样,每个实例才可以维护一份被返回对象的独立的拷贝。

    data: function () {

      return {

        count: 0

      }

    }

     

    2)注册组件

    • 全局注册:Vue.component.这样全局注册之后,可以用在任何新创建的vue实例中

    • 局部注册:先声明组件,然后再实例中的components中使用组件。

    局部注册中可以通过一个不同的JavaScript对象来定义组件

    var ComponentA = { /* ... */ }

    var ComponentB = { /* ... */ }

    var ComponentC = { /* ... */ }

     

    然后再components选项中定义想要使用的组件

    new Vue({
    
      el: '#app',
    
      components: {
    
        'component-a': ComponentA,
    
        'component-b': ComponentB
    
      }
    
    })

    对于 components 对象中的每个属性来说,其属性名就是自定义元素的名字,其属性值就是这个组件的选项对象。

    注意局部注册的组件在其子组件中不可用。

     

    例如,如果你希望 ComponentA 在 ComponentB 中可用,则你需要这样写:

    var ComponentA = { /* ... */ }
    
    
    
    var ComponentB = {
    
      components: {
    
        'component-a': ComponentA
    
      },
    
      // ...
    
    }

     

    在模块系统中局部注册

    在这些情况下,我们推荐创建一个 components 目录,并将每个组件放置在其各自的文件中。

    然后你需要在局部注册之前导入每个你想使用的组件。例如,在一个假设的 ComponentB.js 或 ComponentB.vue 文件中:

    import ComponentA from './ComponentA'
    
    import ComponentC from './ComponentC'
    
    
    
    export default {
    
      components: {
    
        ComponentA,
    
        ComponentC
    
      },
    
      // ...
    
    }

    这样就可以在conponentB中的模板使用componentA和c了。

     

     

     

    在模块中,基础组件的自动化全局注册

    1)什么是基础组件

    许多组件只是包裹了一个输入框或者按钮这类的元素,是相对通用的,把它们称之为基础组件,它们会在各个组件中被频繁的使用。

    这样就会导致很多包含基础组件的长列表

    import BaseButton from './BaseButton.vue'
    
    import BaseIcon from './BaseIcon.vue'
    
    import BaseInput from './BaseInput.vue'
    
    
    
    export default {
    
      components: {
    
        BaseButton,
    
        BaseIcon,
    
        BaseInput
    
      }
    
    }
    
    

    所以在webpack中,使用require.context只在全局注册这些通用的基础组件。

    在入口文件中(src/main.js中全局导入基础组件的实例代码)

    import Vue from 'vue'
    
    import upperFirst from 'lodash/upperFirst'
    
    import camelCase from 'lodash/camelCase'
    
    
    
    const requireComponent = require.context(
    
      // 其组件目录的相对路径
    
      './components',
    
      // 是否查询其子目录
    
      false,
    
      // 匹配基础组件文件名的正则表达式
    
      /Base[A-Z]\w+\.(vue|js)$/
    
    )
    
    
    
    requireComponent.keys().forEach(fileName => {
    
      // 获取组件配置
    
      const componentConfig = requireComponent(fileName)
    
    
    
      // 获取组件的 PascalCase 命名
    
      const componentName = upperFirst(
    
        camelCase(
    
          // 剥去文件名开头的 `./` 和结尾的扩展名
    
          fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
    
        )
    
      )
    
    
    
      // 全局注册组件
    
      Vue.component(
    
        componentName,
    
        // 如果这个组件选项是通过 `export default` 导出的,
    
        // 那么就会优先使用 `.default`,
    
        // 否则回退到使用模块的根。
    
        componentConfig.default || componentConfig
    
      )
    
    })

    记住全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生。

     

    3)通过props向子组件传递数据

    HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名

    Vue.component('blog-post', {
    
      // 在 JavaScript 中是 camelCase 的
    
      props: ['postTitle'],
    
      template: '

    {{ postTitle }}

    ' })

    如果你使用字符串模板,这个限制就不存在。

     

    Prop 是你可以在组件上注册的一些自定义特性。当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样

    Vue.component('blog-post', {
    
      props: ['title'],
    
      template: '

    {{ title }}

    ' })

     

    一个 prop 被注册之后,你就可以像这样把数据作为一个自定义特性传递进来:

     

    此外还可以定义传进来的prop数据的类型

    props: {

      title: String,

      likes: Number,

      isPublished: Boolean,

      commentIds: Array,

      author: Object

    }

     

    当子组件需要对prop传入的数据进行处理的时候,最好要有一个本地的数据来进行接收,而不要直接改变prop传进来的数据。

    我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。

    为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:

    Vue.component('my-component', {

      props: {

        // 基础的类型检查 (`null` 匹配任何类型)

        propA: Number,

        // 多个可能的类型

        propB: [String, Number],

        // 必填的字符串

        propC: {

          type: String,

          required: true

        },

        // 带有默认值的数字

        propD: {

          type: Number,

          default: 100

        },

        // 带有默认值的对象

        propE: {

          type: Object,

          // 对象或数组默认值必须从一个工厂函数获取

          default: function () {

            return { message: 'hello' }

          }

        },

        // 自定义验证函数

        propF: {

          validator: function (value) {

            // 这个值必须匹配下列字符串中的一个

            return ['success', 'warning', 'danger'].indexOf(value) !== -1

          }

        }

      }

    })

    当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。

    注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 在 default 或 validator 函数中是不可用的(这些是在实例创建之后才可以使用)。

    非prop的特性

    一个非prop特性是值传向一个组件,但是该组件并没有相应prop定义的特性。但是组件可以接收这样的特性,并且添加到这个组件的跟元素上。

    替换/合并已有的特性

    如果组件内部已经设置好的属性,再从组件的父级传入进来,就会替换掉组件内部设置好的值。但是,class和style这两个特性会只能一些,会把组件内部和外部的值合并起来,得到最终的值。

    禁用特性继承(就是不要让组件的根元素,继承父级的所有属性,将父元素的元素拆开来,赋予给不同的组件元素)

     

            v-model="username"

            class="username-input"

            placeholder="Enter your username"

            label = "123"

    >

     

    Vue.component('base-input', {

          inheritAttrs: false,

          props: ['label', 'value'],

          template: `

      

        {{ label }}

        

          v-bind="$attrs"

          v-bind:value="value"

          v-on:input="$emit('input', $event.target.value)"

        >

      

    `

      })

     

      new Vue({

          el: '#todo-list-example',

          data: {

              username:""

          }

      })

    vue基础详解(全)_第22张图片

    可以看到父元素的label属性赋予了组件中的label标签,而其他的属性李如玉placeholder、class等则绑定在了$attrs上面,赋予给了input元素

    4)通过事件向父级组件发送消息

    在下面的例子中blog-post是父组件,父组件中的postFontSize是用来控制博文的字号

    new Vue({

      el: '#blog-posts-events-demo',

      data: {

        posts: [/* ... */],

        postFontSize: 1

      }

    })

     

    然后在模板中添加一个按钮用来放大字号(利用自定义事件,将enlarge-text事件传给父组件)

    Vue.component('blog-post', {

      props: ['post'],

      template: `

        

          

    {{ post.title }}

          

          

        

      `

    })

     

    然后再父组件中的模板上面监听这个事件

      

        

          v-for="post in posts"

          v-bind:key="post.id"

          v-bind:post="post"

            v-on:enlarge-text="postFontSize += 0.1"

        >

      

     

    此外,还可以通过这个自定义的事件给父组件传数据

    $emit的第二个参数中填写的是这个数据

    例如在上面的例子中,我们由子组件来决定放大多少倍,在模板中的button改成下面

     

    可以看到,这里的参树为0.1,也就是每次点击都放大0.1em

     

    然后在父级组件监听这个事件的时候,访问这个数据

      ...

      v-on:enlarge-text="postFontSize += $event"

    >

     

    如果这个处理函数是一个方法,那么这个值将作为参数被传入

    
    
    
    
    methods: {
    
      onEnlargeText: function (enlargeAmount) {
    
        this.postFontSize += enlargeAmount
    
      }
    
    }

     

    自定义事件

    • 事件名

    不同于组件和prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。

    v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。

    因此,我们推荐你始终使用 kebab-case 的事件名。

     

    • 自定义组件的v-model

    一个组件上的v-model会利用value的prop和input的事件,但是像单选框,复选框等类型的输入控件可能会将value特性用在不同的地方(亲测,当勾选单选框或者复选框的时候,值都为undefined)。

    所以如果我们想要获得这个值(就是究竟有没有被选中的时候,就可以利用组件的这个model属性)

    首先给组件添加model属性,可以看到下面的model含有checked这个属性以及change的这个事件,当组件被改变状态的时候,就会触发这个change事件,并且将自己的checked的值传给父元素,也就是base-checkbox里面的lovingVue这个值绑定了组件中的chencked所对应的值,当base-checkbox触发一个change事件并且附带一个新的值的时候,lovingVue就会被更新。需要注意的是,仍然需要在组件的props里面声明checked这个属性。

    Vue.component('base-checkbox', {

      model: {

        prop: 'checked',

        event: 'change'

      },

      props: {

        checked: Boolean

      },

      template: `

        

          type="checkbox"

          v-bind:checked="checked"

          v-on:change="$emit('change', $event.target.checked)"

        >

      `

    })

     

     

    • 将原生事件绑定到组件(.native)

    组件根元素直接监听事件(亲测,不添加.native是不生效的),

        

     

    Vue.component("myinput",{

        template:""

    })

    let vm = new Vue({

        el:"#test",

        data:{

     

        },

        methods:{

            console:function () {

                console.log("focus");

            }

        }

    });

     

    但有的时候组件的根元素不具有这些原生事件的时候,这个事件触发函数就不会生效,例如

      {{ label }}

      

        v-bind="$attrs"

        v-bind:value="value"

        v-on:input="$emit('input', $event.target.value)"

      >

     

    于是vue提供了一个$listeners 属性,里面包含了所有作用在这个组件的监听器

    {

      focus: function (event) { /* ... */ }

      input: function (value) { /* ... */ },

    }

     

    于是上面修改为

    Vue.component('base-input', {

      inheritAttrs: false,

      props: ['label', 'value'],

      computed: {

        inputListeners: function () {

          var vm = this

          // `Object.assign` 将所有的对象合并为一个新对象

          return Object.assign({},

            // 我们从父级添加所有的监听器

            this.$listeners,

            // 然后我们添加自定义监听器,

            // 或覆写一些监听器的行为

            {

              // 这里确保组件配合 `v-model` 的工作

              input: function (event) {

                vm.$emit('input', event.target.value)

              }

            }

          )

        }

      },

      template: `

        

          {{ label }}

          

            v-bind="$attrs"

            v-bind:value="value"

            v-on="inputListeners"

          >

        

      `

    })

     

     

    5)在自定义组件里面使用v-model,一定要做一下两件事

    • 将其value属性绑定在一个value的props上

    • 在其input事件被触发的时候,将新的值通过自定义的input事件抛出

     

    Vue.component('custom-input', {

      props: ['value'],

      template: `

        

          v-bind:value="value"

          v-on:input="$emit('input', $event.target.value)"

        >

      `

    })

     

     

    6)对于特殊的html元素中,其内部元素为指定的元素,所以当我们自定义的元素出现在这里的时候会被视为无效的内容。

    此时使用is属性

      

     

    7)组件名

    全局注册的时候第一个参数就是组件名

    Vue.component('my-component-name', { /* ... */ })

     

    定义组件名的方式有两种:

    • 使用 kebab-case  

    Vue.component('my-component-name', { /* ... */ })

    • 使用 PascalCase

    Vue.component('MyComponentName', { /* ... */ })

    当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 都是可接受的。注意,尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。

    因为HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

        

        

    {{searchText}}

     

    Vue.component('CustomInput', {

          props: ['value'],

          template: `

      

        v-bind:value="value"

        v-on:input="$emit('input', $event.target.value)"

      >

    `

      })

     

      new Vue({

          el: '#todo-list-example',

          data: {

              searchText:""

          }

      })

    vue基础详解(全)_第23张图片

     

    但是,使用首字母大写的情况下就会出错

        

        

    {{searchText}}

    vue基础详解(全)_第24张图片

     

     

     

    8)插槽

    所谓插槽,就是在自定义组件中包含的内容,例如

    something intresting

    那么上面的something..就是插槽

     

    官网中写道,如果不在自定义组件中添加元素,那么所有被放进来的元素将会被抛弃

    例如上面的myelement模板实现可能如下

      v-bind:href="url"

      class="nav-link"

    >

      

     

    这样写,当组件渲染的时候,这个slot元素就会代替上面的something intresting

     

    插槽中可以包含任何模板代码,包括html

      

      

      Your Profile

     

     

    • 具名插槽

    结合父组件上使用slot从而指定插槽的位置,例如

    模板中的内容可能如下,每个位置拥有不一样的元素

      
           
      
           
      
           
    那么我们在使用父组件的时候      

    A paragraph for the main content.

      

    And another one.

      

    可以看到父组件里面的模板就会出现在slot里面name属性所对应的位置下面

    vue基础详解(全)_第25张图片

    如果不像上面,用template元素以及slot属性包裹,直接用在一个普通元素上但是结合slot属性使用,也可以

      

    Here might be a page title

     

      

    A paragraph for the main content.

      

    And another one.

     

      

    Here's some contact info

     

    我们还是可以保留一个未命名插槽,这个插槽是默认插槽,也就是说它会作为所有未匹配到插槽的内容的统一出口

     

    • 插槽的默认内容

    有的时候为插槽提供的内容是很有用的,例如,个 组件可能希望这个按钮的默认内容是“Submit”,但是同时允许用户覆写为“Save”、“Upload”或别的内容。

    你可以在组件模板里的 标签内部指定默认的内容来做到这一点。

    如果父组件为这个插槽提供了内容,那么这个默认的内容就会被替换掉

     

     

     

     

     

    9)动态组件&异步组件

    在一个多标签的界面中使用is特性来切换不同的组件

    Vue.component('tab-home', {

        template: '

    Home component
    '

    })

    Vue.component('tab-posts', {

        template: '

    Posts component
    '

    })

    Vue.component('tab-archive', {

        template: '

    Archive component
    '

    })

     

    new Vue({

      el: '#dynamic-component-demo',

      data: {

        currentTab: 'Home',

        tabs: ['Home', 'Posts', 'Archive']

      },

      computed: {

        currentTabComponent: function () {

          return 'tab-' + this.currentTab.toLowerCase()

        }

      }

    })

     

     

     

      

        v-for="tab in tabs"

        v-bind:key="tab"

        v-bind:class="['tab-button', { active: currentTab === tab }]"

        v-on:click="currentTab = tab"

      >{{ tab }}

     

      

        v-bind:is="currentTabComponent"

        class="tab"

      >

     

    这样实现的话,每次切换,都会重新渲染的。

    当在这些组件进行切换的时候,有时会想保持这些组件的状态,以免反复重新渲染所导致的性能问题,为了解决这个问题,用一个keep-alive元素将动态组件包裹起来

     

        v-bind:is="currentTabComponent"

        class="tab"

      >

     

    • 异步组件

    所谓异步组件就是等到需要的时候,才会去服务器加载这个模块。vue用工厂函数的的方式定义组件,这个函数会异步的解析运行,也就是只有在需要被渲染的时候才会触发这个函数。

    Vue.component('async-example', function (resolve, reject) {

      setTimeout(function () {

        // 向 `resolve` 回调传递组件定义

        resolve({

          template: '

    I am async!
    '

        })

      }, 1000)

    })

     

     

     

    我们来写一个类似于公众号历史记录这样的东西(就是div结构,样式都一样,仅仅是内容不一样的多个div)

        
            

    这是文章的标题

            
                2018年11月14日             原创         
        
        
    .unit{     height: 200px;     width: 90%;     overflow: hidden;     margin: 0 auto;     background-color: #ffffff; } .content{     width: 60%;     height: 100%;     float: left; } img{     width: 40%;     height: 100%;     float: left; } .info{     font-size: 16px;     color: #cccccc; } .info span{     margin: 0 15px; } .info span:nth-child(2){     border: 1px solid #cccccc;     border-radius: 7px; } 接

    其中基本的css代码和html的代码如下:下来定义一个组件,代码如下:,这个组件名叫myarticle,拥有props属性和template属性,其中pros属性用来接收参数,这些参数会在模板里面用到。

    Vue.component('myarticle',{
    
        props:['detail'],
    
        template :' 
    \n' +     '        
    \n' +     '            

    {{detail.title}}

    \n' +     '            
    \n' +     '                {{detail.date}}\n' +     '                原创\n' +     '            
    \n' +     '        
    \n' +     '        \n' +     '    
    ' });

     

    然后创建一个vue实例,需要注意的是,一定要确保实例vm在创建之前,组件已经成功注册。

    let vm = new Vue({
    
        el:"#app",
    
        data:{
    
            articles:[
    
                {
    
                    title:"这是第一个标题",
    
                    date:"2018年11月14日",
    
                    is_oringinal:true,
    
                    imgurl:"img/logo.jpg"
    
                },
    
                {
    
                    title:"这是第二个标题",
    
                    date:"2018年11月15日",
    
                    is_oringinal:true,
    
                    imgurl:"img/logo.jpg"
    
                },
    
                {
    
                    title:"这是第三个标题",
    
                    date:"2018年11月16日",
    
                    is_oringinal:true,
    
                    imgurl:"img/logo.jpg"
    
                }
    
            ]
    
        }
    
    })

    注册完组件之后,就开始使用这个自定的组件

        

     

    vue基础详解(全)_第26张图片

     

     

    八.组件之间的通信

    组件实例的作用域都是孤立的,也就是子组件在模板中不能引用父组件的数据

    1.父->子,父组件将数据传递给子组件

    先创建一个实例

    实例中含有数据msg。

    注册子组件

    Vue.component('son',{

        template:"

    "

    })

    并将子组件插入到父组件下面:

        

     

    接下来我们开始传递数据,父组件向子组件传递参数,用组件提供的props属性,在下面的代码中,父组件app向son组件的:message属性传输了它自己的值msg。子组件message接收了这个参数,然后在组件的定义里面,用props来接收这一个值,并将这个值在div中输出。这样就完成了父组件传递给子组件了。props选项声明了它要接受的参数是message,而接收到的对应的值是父组件的数据msg。我们在子组件顺利地把message展示出来。

        

    Vue.component('son',{

        props:['message'],

        template:"

    {{message}}
    "

    })

    vue基础详解(全)_第27张图片

     

    而props是单向绑定的,也就是说子组件接收到这个值之后无论怎么修改,父组件中也不会受到影响。因为vue为了防止子组件无意修改了父组件的数据和状态,如果多个子组件任意地对父组件进行修改,会让数据流难以阅读。

     

    2.子->父,子组件传数据给父组件。

    父组件监听子组件的事件,并接收子组件传过来的数据

    vue实例都有一个事件的接口,这个接口就是$emit(eventname)来触发一个事件

    1.注册一个新组件

    Vue.component('son',{

       template:``,

     

       methods:{

           send(){

               this.$emit('connect');

     

           }

       }

    });

    一个按钮button,点击它的时候,会触发组件内部的send( )方法,而方法体里面会触发一个事件,事件名取名为:“connect”。

    然后我们就去父组件监听这个事件‘connect’,监听方式跟普通原生的事件一模一样,也是用 v-on 指令,缩写用@符号。 我们采用缩写来监听:

        

    const app = new Vue({

        el:"#app",

        methods:{

            say(){

                console.log(`大家好,我监听到了

      事件connect的触发`);

            }

        }

    });

     

    这样,当点击按钮的时候,就成功调用了父组件的say函数。这时,我们只需要把子组件的数据,通过这个事件connect传递给父组件,就可以实现子->父的通信。在data函数里面返回需要返回的数据,并在$emit里面讲msg一并发射出去,在父组件中的say中接收

    Vue.component('son',{

        props:['message'],

        template:"",

        data(){

            return{

                msg:'大家好,我是儿子的数据'

            }

        },

        methods:{

            send(){

                this.$emit('connect',this.msg);

            }

        }

    })

    const  app = new Vue({

        el:"#app",

        methods:{

            say(msg){

                console.log("hello,我监听到了子组件的connect事件的触发,儿子的数据是"+msg);

            }

        }

    })

    vue基础详解(全)_第28张图片

    这样就完成了子->父组件的数据传输

     

     

    九.动态绑定class和style

    1.class的绑定

    1)对象法:动态绑定的class的值是一个对象{},键是一个样式名,而值是是否显示这个样式。在对象中,若该属性对应的value为true,则该属性会被渲染出来,为false,则不会渲染出来。并且这写法可以与单独的class属性可以共存

    这是文字

    这是文字

    let  vm = new Vue({

        el:"#app",

        data:{

            isActive:true,

            error:"active",

            active:"erro",

            color:"red",

            ifshow:"block",

        }

    })

     

    2)数组法,数组语法中也可以使用对象语法:

    hello

    数组中的值在实例中对应的值是一个属性样式,也就是说数组法中html中数组的值可以不是一个样式名,只要它在vue实例中data中对应的键值的值是一个样式名。

    vue基础详解(全)_第29张图片

     

     

     

    2.style的绑定(绑定内联样式style)

    CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) 来命名:

    1)对象语法,需要注意的是这样绑定的属性之间要用逗号隔开,不要用分号

    world

    vue基础详解(全)_第30张图片

     

    2)数组语法

    v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:

     

    v-bind:style 使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀

     

    你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:

    这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。

     

    3.用在组件上

    当在一个自定义组件上使用class属性的时候,这些类将被添加到该组件的根元素上面。这个元素上已经存在的类不会被覆盖

    例如,如果你声明了这个组件:

    Vue.component('my-component', {

      template: '

    Hi

    '

    })

    然后在使用它的时候添加一些 class:

    HTML 将被渲染为:

    Hi

    对于带数据绑定 class 也同样适用:

    当 isActive 为 truthy 时,HTML 将被渲染成为:

    Hi

     

     

     

     

     

     

    十.demo,做一个TODOlist

    确定T

     

     

     

    十一.用transition组件轻松实现过渡效果

    1.transition过度原理分析

    vue提供的transition组件会在以下四种情况下起作用

    • 条件渲染(v-if)

    • 条件展示(v-show)

    • 动态组件

    • 组件根节点

    在上述的任意一种情况发生的时候(比如v-show的值本来为true的时候,当发生某种变化导致值变为false的时候),就可以给transition组件包含的节点元素添加enter或leave过度动画元素。

    当插入或删除包含在transition组件中的元素时,vue将会做以下的处理

    • 自动嗅探目标元素是否应用了css过渡或动画,如果是,在适当的时候添加或者删除css类名

    • 如果过渡组件提供了JavaScript钩子函数,这些钩子函数将会在恰当的时机被调用

    • 如果没有找到JavaScript钩子也没有检测到css过渡/动画,dom操作在下一帧立即执行

     

    下面的v在实际应用改成transition中name属性的值

    • v-enter:定义过渡的开始状态,在元素被插入之前生效,在元素被插入之后的下一帧移除。

    • v-enter-active:定义过渡生效时的状态,在整个进入过渡的阶段都会应用,在元素被完全插入之前生效,在过渡或者动画完成之后被移除。(就是这个类来定义过渡的时间等)

    • v-enter-to:定义过渡的结束状态,在元素被插入之后下一帧生效,在过渡或者动画完成之后被移除。

    • v-leave:离开过渡的开始的那一刻(用在从显示到隐藏的时候)

    • v-leave-active:离开过渡的的持续状态,在整个离开过渡的阶段中应用(在这里定义过渡的时间等)

    • v-leave-to离开过渡的结束状态,在离开过渡被触发之后下一帧生效。

    vue基础详解(全)_第31张图片

    需要注意的是

    节点一定要被transition元素包含

    transition的name属性一定要与css类名开始的地方相同(如果没有使用name属性,那么类名前缀为v)

    2.例子

       

        

            

    hello

        

    .fade-enter-active, .fade-leave-active {

        /*定义过渡的状态*/

        transition: all 1.5s ease;

    }

    .fade-enter, .fade-leave-to  {

        /*定义进入动画的状态,以及离开动画最后结束的状态*/

        transform: translateX(20px);

    }

     

     

    (2)css动画

    在动画中,v-enter类名在节点插入dom后不会立即删除,而是在animationend时间触发时删除。

    (也就是说,直到触发动画事件的时候才会删除这个类名)

     

    .animate-enter-active{

        animation: bounze 1s;

    }

    .animate-leave-active{

         animation: bounze 1.5s reverse;

    }

    @keyframes bounze {

       0%{

       transform: translateX(0px);

       }

         50%{

             transform: translateX(20px);

         }

         100%{

       transform: translateX(40px);

         }

    }

     

    (3)自定义过渡类名

    可以通过自定义类名来使用其他第三方css动画库里面的类名(在transition元素上面指定自定义的类名)

    • * enter-class

    • * enter-active-class

    • * enter-to-class (2.1.8+)

    • * leave-class

    • * leave-active-class

    • * leave-to-class (2.1.8+)

     

      

      

        name="custom-classes-transition"

        enter-active-class="animated tada"

        leave-active-class="animated bounceOutRight"

      >

        

    hello

      

     

    (4)JavaScript钩子

    就是在transition 过渡或者动画中使用JavaScript的的处理函数进行处理,有一下八个钩子

    • before-enter

    • enter

    • after-enter

    • enter-cancelled

    • before-leave

    • leave

    • after-leave

    • leave-cancelled

     

      v-on:before-enter="beforeEnter"

      v-on:enter="enter"

      v-on:after-enter="afterEnter"

      v-on:enter-cancelled="enterCancelled"

     

      v-on:before-leave="beforeLeave"

      v-on:leave="leave"

      v-on:after-leave="afterLeave"

      v-on:leave-cancelled="leaveCancelled"

    >

      

     

     

    // ...

    methods: {

      // --------

      // 进入中

      // --------

     

      beforeEnter: function (el) {

        // ...

      },

      // 当与 CSS 结合使用时

      // 回调函数 done 是可选的

      enter: function (el, done) {

        // ...

        done()

      },

      afterEnter: function (el) {

        // ...

      },

      enterCancelled: function (el) {

        // ...

      },

     

      // --------

      // 离开时

      // --------

     

      beforeLeave: function (el) {

        // ...

      },

      // 当与 CSS 结合使用时

      // 回调函数 done 是可选的

      leave: function (el, done) {

        // ...

        done()

      },

      afterLeave: function (el) {

        // ...

      },

      // leaveCancelled 只用于 v-show 中

      leaveCancelled: function (el) {

        // ...

      }

    }

    当仅仅使用JavaScript过渡的时候,需要有两个注意事项

    • 在enter和leave中必须使用done来进行回调

    • 在transition中使用v-bind:css="false",这样vue会跳过css的检测。

     

    (5)初始渲染的过渡

    可以通过appear特性来设置节点在初始渲染的过渡

     

    (6)多个元素的过渡

    就是多个元素之间只显示其中一个,当状态发生改变的时候,显示的元素不一样,就会出现元素之间的过渡。值得注意的是,官网推荐在相同标签名的元素之间进行切换的时候,需要给这些元素设置key特性,并拥有不一样的值,否则vue为了效率只会替换相同标签内部的内容。

     

    此外,在vue中,当两个元素进行过度的时候,transition的默认行为是进入和离开同时发生,那么此时两个元素就都会同时发生过度效果,这会影响效果,所以vue中有一个过渡模式

    这个模式有两个值,分别是

    • out-in 离开元素先过渡然后再到进入的元素过渡

    • in-out 与上面相反

        

        

     

    (6)多个组件的过渡

    多个组件的过渡使用动态组件

     

      Vue.component("tab-home",{

          template:`

    this is home

    `

      });

      Vue.component("tab-aboutus",{

          template:`

    this is aboutus

    `

      });

      Vue.component("tab-vechicle",{

          template:`

    this is vechicle

    `

      });

    let vm = new Vue({

        el:"#test",

        data:{

            buts:["home","aboutus","vechicle"],

            currentTab:"home"

        },

        computed:{

            currentActiveTab:function () {

                return "tab-"+this.currentTab;

            }

        }

    });

     

            

        

           

               

           

        

     

    (7)列表过渡

    使用transition-group组件包含列表元素

    使用transition-group的特点

    • 不同于transition,它会以一个真实的元素呈现,默认为一个span,可以通过tag特性来更改为其他元素

    • 过渡模式不可以用,因为不再切换其他元素

    • 内部元素总是需要有一个唯一的key属性

    new Vue({

        el: '#list-demo',

        data: {

            items: [1,2,3,4,5,6,7,8,9],

            nextNum: 10

        },

        methods: {

            randomIndex: function () {

                return Math.floor(Math.random() * this.items.length)

            },

            add: function () {

                this.items.splice(this.randomIndex(), 0, this.nextNum++)

            },

            remove: function () {

                this.items.splice(this.randomIndex(), 1)

            },

        }

    })

     

        

        

        

        

          {{ item }}

        

        

     

    .mutiple-item {

        display: inline-block;

        margin-right: 10px;

    }

    .mutiple-enter-active,.mutiple-leave-active{

       transition: all 1s ;

    }

    .mutiple-enter,.mutiple-leave-to{

        transform: translateY(10px);

    }

     

    列表的排序过渡,transition-group组件还有一个特殊之处,不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需要了解新增的v-move特性,他会在元素的改变定位的过程中应用。

    css的类名可以使用name属性自定义前缀,也可以通过move-class属性手动设置。

     

     

     

     

    十二.vue-router

    1.vue-router是vue官方的路由插件,适合用于构建单页面应用。

    2.先引入vue-router

    3.准备组件,vue-route给我们提供了两个新组件,组件,其中link是用于帮助用户进行视图导航,也就是传统页面中a标签所做的事情,用to来指定组件的位置。而view组件负责渲染匹配到的视图组件,也就是渲染link指向的目标地址,

        

        

            

        

     

    接下来定义视图组件以及与导航地址关联起来。创建一个router实例,创建实例的时候我们需要传参数routes来进行配置。

    const hins = {

        template:`

    这是张敬轩的页面
    `

    };

    const ste = {

        template:`

    这是孙燕姿
    `

    };

    const  adele = {

        template:`

    这是Adele
    `

    };

    const router = new VueRouter({

        routes:[

            {

                path:"/hins",

                component:hins

            },

            {

                path:"/ste",

                component:ste

            },

            {

                path:"/adele",

                component:adele

     

            }

        ]

    });

     

     

    最后,创建一个vue实例,创建的时候通过配置router参数来注入我们刚定义好的router路由

     

    //创建vue实例,注入路由router

    const app = new Vue({

        el:"#app",

        router //此处是ES6语法,

     

     

          //相当于router:router

    });

     

     

     

    vue基础详解(全)_第32张图片

     

    vue基础详解(全)_第33张图片

    vue基础详解(全)_第34张图片

     

    十三.vuex

    1.vuex是什么?

    vuex是一个专为vue开发的应用程序的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex采用类似全局对象的形式来管理所有组件的公用数据,如果你想想修改这个全局对象的数据?是可以的,但没那么简单,你得按照Vuex提供的方式来修改,不能自己随意用自己的方式来修改

     

    (1)vue的状态存储是响应式的,方你的组件使用到了这个vuex的状态,一旦它改变了,所有关联的组件都会自动更新相对应的数据。

    (2)不能直接修改vuex的状态,想修改就得使用vuex提供的唯一途径:显示地提交mutations来实现修改,

     

    2.vuex的3个核心概念

    (1)要使用vuex,就要创建一个实例store,我们称之为仓库,利用这个仓库对状态进行管理

    const store = new Vuex.Store({});

     

    (2)State

    vuex使用单一状态树,用一个对象state包含了整个应用层级的所有状态。在下面代码中,假如我们有一个全局状态count,值为5,就可以将其定义为state对象里面的一对键值对,作为全局状态供我们使用。

    const store = new Vuex.Store({

        state:{

            count:5

        }

    });

     

    (2)Getters

    当我们需要从state中派生出一些状态的时候,就会使用到getters,相当于getters是state的计算属性。

    const store = new Vuex.Store({

        state:{

            count:5

        },

        getters:{

            newCount:state =>state.count*3

        }

     

    });

     

    (3)mutations

    vuex给我们提供修改仓库store中的状态的唯一方法就是通过提交mutation。

    定义一个mutations:

    const store = new Vuex.Store({

        state:{

            count:5

        },

        getters:{

            newCount:state =>state.count*3

        },

        mutations:{

            increment(state){

                state.count ++;

            }

        }

     

    });

     

    定义好了后就必须要提交mutation,语法如下:

    store.commit('increment');

     

    4.例子

    需求:做一个计算器,那么运算的结果result和输入的值enter就是会跟每一个组件(按键)

    相关联的,所以这里我们将result和enter作为应用层的状态(全局数据)来处理,这里就会简单的使用到vuex提供的仓库store。

    vue基础详解(全)_第35张图片

    首先完成上面界面的实现。

    一共有3个主要区域,一个运算的结果,一个输入的结果,还有按键区域。

    按键区域的使用可以使用组件来实现,定义一个keyboard组件,根据传入的参数来定义不一样的按键。

        

        

        

            

                

            

        

     

    #app{

        width: 100%;

        height: 100%;

    }

    .result{

        width: 324px;

        height: 56px;

        font-size: 34px;

        margin-left: 178px;

    }

    .enter{

        width: 324px;

        height: 50px;

        font-size: 24px;

        margin-left: 218px;

    }

    .keys{

        width: 324px;

        height: 324px;

    }

    .list div{

        width: 80px;

        height: 80px;

        line-height: 80px;

        background-color: #efefef;

        border: 0.5px solid #cccccc;

        border-bottom: none;

        border-right: none;

        float: left;

        text-align: center;

        font-size: 18px;

        vertical-align: center;

    }

    .list div:first-child{

        color: #ff8282;

    }

    .list div:last-child{

        color: white;

        background-color: #ff8282;

    }

     

     

    界面实现了,接下来就是数据的显示以及如何进行数据绑定,当按键被按下的时候,显示输入的值以及运算的结果。这时候,组件们就共用到了result和enter这两个值,所以我们首先需要建立仓库来管理它们,并初始化为空字符串

    const store = new VUex.Store({

        state:{

            result:"",

            enter:""

        }

    })

     

    vuex提供了store选项,允许我们将仓库store引入到根组件中,并且此跟组件的所有子组件都可以使用仓库store,而且子组件无需显示的引入。

    首先我们在vue实例下面引入我们的仓库store

     

    const app = new Vue({

        el:"#app",

        data:{

            keys:[

                'clear','+','-','*',

                '7','8','9','/',

                '4','5','6','0',

                '1','2','3','=',

            ]

        },

        store,

     

    }

    引入之后我们就来使用,那么怎么使用仓库的result和enter呢?我们这里用到vue的computed计算属性。通过this.$store来回去仓库,然后this.$store.state来获取属性,最后用this.$store.state.result来获取仓库里的result。同理获得enter

    computed:{

        result(){

            return this.$store.state.result;

        },

        enter(){

            return this.$store.state.enter;

        }

    }

     

    将result和enter显示在html代码里面

    {{result}}

    {{enter === ""?0:enter}}

     

    最后,绑定按键的点击事件,当我们点击键盘的时候,对仓库的result和enter进行修改。但是vuex中不能任意修改应用层的状态,所以就通过它提供的唯一的途径:通过commit提交mutation。当用户点击键盘的时候我们就提交一个mutation,并把当前按的键对应的值传过去。所以我们要重新定义keyboard组件的事件。

    Vue.component("keyboard",{

        props:['value'],

        template:`

            @click="getKeyboardValue"

           :data-value="value">

            {{value}}

         

    `,

        methods:{

            getKeyboardValue(event){

                let value=event.target.dataset.value;

                this.$store.commit('calculate',value)

            }

        }

    });

     

    增加了一个监听点击事件的处理函数getKeyboardValue,我们会提交体格名为calculate的mutation给仓库,并将当前按键的值一起提交过去。这个calculate的mutation要在仓库中定义

    const store = new Vuex.Store({

        state:{

            result:"",

            enter:""

        },

        mutations:{

            calculate(state,value){

                if (value === '='){

                    state.result = eval(state.enter);

                    state.enter+=value;

                }else if (value === 'clear'){

                    state.result = state.enter = "";

                }else {

                    state.enter +=value;

                }

            }

        }

    });

     

     

     

    十四.使用axios

    (1)get请求

     

    axios.get('detail.html?id=10').then(function (res) {

        console.log(res);

    }).catch(function (err) {

        console.log(err);

    });

     

     

     

    axios.get('detail.html',{

        params:{

            id :10

        }

    }).then(function (res) {

        console.log(res);

    }).catch(function (err) {

        console.log(err)

    })

     

     

    (2)post请求

     

     

    axios.post('detail.html',{

        name:"krys",

        age:22

    }).then(function (res) {

        console.log(res)

    }).catch(function (err) {

        console.log(err);

    })

     

    (3)多个请求并发

     

    function getProfile(){

        //请求1

        return axios.get('vuex.html');

    }

    function getUser(){

        //请求2

        return axios.get('todo.html');

    }

     

    //并发请求

    axios.all([

        getProfile(),

        getUser()

    ]).then(axios.spread((res1, res2)=>{

        //两个请求现已完成

        console.log(res1);

        console.log(res2);

    }));

     

     

    我们事先定义了两个方法getProfile()和getUser()帮我们实现get请求。在某个业务场景中,我们需要同时产生以上两个get请求,并需要等待它们都请求完毕后再做逻辑处理,你也许会想到回调嵌套,但现在你可以使用axios的并发请求API: axios.all()

    当这两个请求都完成的时候会触发 axios.spread() 函数,res1和res2两个参数分别代表返回的结果,相当实用的API。

     

    上面的写法当中是axios提供给我们的别名写法,也可以写成下面这样

    axios({

        method: 'post',

        url: 'detail.html',

        data: {

            name: 'krys',

        }

    });

     

     

     

    十五、官网上看到的一些总结

    1.使用object.freeze()会阻止修改现有的属性,也就是说,当代码中使用了Object.freeze(obj),那么在vue实例中将不会再追踪这个对象的变化。(仅仅针对data属性中的值是外部的一个对象)

      

    {{ foo }}

      

      

     

    var obj = {

      foo: 'bar'

    }

    Object.freeze(obj);

    new Vue({

      el: '#app',

      data: obj

    })

     

     

    2.混入

    (1)什么是混入?

    混入(mixins)是一种分发vue组件中可以重复使用的功能的方式。

    混入对象可以包含任意组件选项。当组件使用混入对象是,所有混入独享的选项将被混入该组件本身的选项。(其实就是多个组件中相同功能的部分)

     

    组件在引用之后相当于在父组件内开辟了一块单独的空间,来根据父组件props过来的值进行相应的操作,单本质上两者还是泾渭分明,相对独立。

     

    而mixins则是在引入组件之后,则是将组件内部的内容如data等方法、method等属性与父组件相应内容进行合并。相当于在引入后,父组件的各种属性方法都被扩充了。

     

     

     

    (2)当组件和混入对象含有同名选项的时候,以下面这些方式进行混合

    • 数据对象在内部会进行递归合并,在和组件的数据发生冲突的时候以组件数据为优

    • 同名钩子函数将混为同一个数组,因此两个都会被调用,但是混入对象的钩子将会在组件自身钩子之前被调用

    • 值为对象的选项,例如methods,components等将会被混合为同一个对象,当两个对象里面属性名冲突的时候,取组件对象的键值对

    var mixin = {

         data: function () {

             return {

                 message: 'hello',

             }

         },

         created: function () {

             console.log('混入对象的钩子被调用')

         },

         methods: {

             foo: function () {

                 console.log('foo')

             },

             conflicting: function () {

                 console.log('from mixin')

             }

         }

    };

     

    let vm =  new Vue({

         el:"#list-demo",

         mixins: [mixin],

         data: function () {

             return {

                 message: 'goodbye',

             }

         },

         created: function () {

             console.log('组件的钩子被调用')

         },

         methods:{

             bar: function () {

                 console.log('bar')

             },

             conflicting: function () {

                 console.log('from self')

             }

         }

    });

    vm.foo() // => "foo"

    vm.bar() // => "bar"

    vm.conflicting() // => "from self"

     

    (3)全局混入

     

    全局声明混入对象,在全局混入对象中声明的属性,会影响所有的vue实例。

    全局声明混入对象的代码

    // 为自定义的选项 'myOption' 注入一个处理器。

    Vue.mixin({

      created: function () {

        var myOption = this.$options.myOption

        if (myOption) {

          console.log(myOption)

        }

      }

    })

     

    let vm =  new Vue({

        el:"#list-demo",

        data: function () {

            return {

                message: 'goodbye',

            }

        },

        myOption:"hello"

    });

    这样控制台会立即打印出hello

    大多数情况下,只应当应用于自定义选项,就像上面示例一样。

     

     

    3.自定义指令

    自定义全局指令

    Vue.directive("myfirst",{

        inserted:function (el) {

            el.focus();

        }

    });

    自定义局部指令

    directives: {

      focus: {

        // 指令的定义

        inserted: function (el) {

          el.focus()

        }

      }

    }

    然后可以在模板中的任何元素上面使用

     

     

    此外,指令定义对象有如下几个钩子函数(均为可选)

    • bind:只调用一次,指令第一次绑定到元素时调用

    • inserted:被绑定你元素插入父节点时调用(也就是dom生成的时候调用)

    • update:所在组件爱你的vnode更新时调用

    • componentUpdated所在组件的vnode及其子vnode全部更新后调用

    • unbind:只调用一次,指令与元素解绑时调用

     

    这些钩子函数会被传入下面这些参数

     

    * el:指令所绑定的元素,可以用来直接操作 DOM 。

    * binding:一个对象,包含以下属性:

        * name:指令名,不包括 v- 前缀。

        * value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。

        * oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。

        * expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1"中,表达式为 "1 + 1"。

        * arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。

        * modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。

    * vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。

    * oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

     

    4、渲染函数以及JSX

    在vue中使用render生成html代码

    Vue.component('anchored-heading', {

        render: function (createElement) {

            return createElement(

                'h' + this.level,   // 标签名称

                this.$slots.default // 子元素数组

            )

        },

        props: {

            level: {

                required: true

            }

        }

    });

     

    (2)虚拟DOM

    虚拟节点:包含的信息会告诉vue页面上需要渲染什么样的节点,以及其子节点。如下面会返回一个虚拟及诶单

    createElement('h1', this.blogTitle)

     

    createElement(

      // {String | Object | Function}

      // 一个 HTML 标签字符串,组件选项对象,或者

      // 解析上述任何一种的一个 async 异步函数。必需参数。

      'div',

     

      // {Object}

      // 一个包含模板相关属性的数据对象

      // 你可以在 template 中使用这些特性。可选参数。

      {

        // (详情见下一节)

      },

     

      // {String | Array}

      // 子虚拟节点 (VNodes),由 `createElement()` 构建而成,

      // 也可以使用字符串来生成“文本虚拟节点”。可选参数。

      [

        '先写一些文字',

        createElement('h1', '一则头条'),

        createElement(MyComponent, {

          props: {

            someProp: 'foobar'

          }

        })

      ]

    )

     

     

    你可能感兴趣的:(前端,js,vue)