vue6

vue

什么vue?

它是一套用于构建用户界面的渐进式框架,采用MVVM思想,Vue 的核心库只关注视图层,Vue也完全能够为复杂的单页应用提供驱动。

安装vue

下载地址

起步

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>起步</title>
  <script src="js/vue.js"></script>
</head>
<body >

<div id="app">
  {{1+2*4}}
</div>

<script>
var app = new Vue({
  el:'#app'
})
</script>

</body>
</html>

实例

创建根实例

var app = new Vue({
  el: '#app',
  data: {

  }
})
  • el:目标容器对象
  • data:初始化数据,里面存在一些变量,这些变量是被监测,并且可以响应的。

初始化一些数据

var app = new Vue({
  el:'#app',
  data: {
    show: false,
    price: 60,
    msg: '文本',
    arr: ['首页','关于','联系'],
    obj: {
      name: '张三',
      age: 20
    },
    bool: true
  },

})

也可以使用以下方式初始化一个根实例(new Vue

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

<script>
var app = new Vue({
  template: `
  <div>
    <h1>{{msg}}</h1>
  </div>
  `,
  data: {
    msg: '星期一'
  }
}).$mount('#app');

生命周期钩子函数

var app = new Vue({
  el:'#app',
  data: {
    name: '张三'
  },
  beforeCreate(){
    console.log('创建之前');
  },
  created(){
    console.log('创建之后');
  },
  beforeMount(){
    console.log('挂载之前');
  },
  mounted(){
    console.log('挂载之后');
  },
  beforeUpdate(){
    console.log('更新之前');
  },
  updated(){
    console.log('更新之后');
  },
  beforeDestroy(){
    console.log('销毁之前');
  },
  destroyed(){
    console.log('销毁之后');
  }
})

一打开页面就能执行的生命周期有那些?

  • beforeCreate 创建之前

创建之前不能获取data中的变量值,这样我们可以让loading显示或者检测页面授权。

  • created 创建之后

创建之后可以获取data中的变量,因此我们可以在这里发送http请求接口,从后端拿到数据,然后同步到页面中去。

var app = new Vue({
  el:'#app',
  data: {
    list: []
  },
  created(){
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = () =>{
      if(xhr.readyState==4 && xhr.status==200){
        var info = JSON.parse(xhr.responseText);
        this.list = info;
      }
    }
    xhr.open('GET','http://localhost:8282/list');
    xhr.send();
    console.log('创建之后', this.name);
  }
})
  • beforeMount 挂载之前

挂载之前,模板中的插值{{name}}或者一些指令v-forv-text等还没有被解析出来(替换),所以如果生成的是一个html代码,那么你无法获取这个dom对象。

  • mounted挂载之后

挂载之后,如果你的节点是动态添加的,那么你至少要在挂载之后才能获取的到。所以此钩子通常用于dom操作

模版语法

  • v-text:相当于{{}}另一种写法,可以防止因为网速慢,加载会有闪烁情况。
  • v-once:执行一次性绑定,接下来不再响应数据
  • v-html:可以让插值处输出html代码,注意此名称存在一定的风险,比如XSS攻击
  • v-bind:动态绑定html特性(比如: class、style、id、checked),特别注意:大胡子语法不能用于html特性上
  • v-on:动态绑定事件
  • {{}}:插值,可以进行运算,但是注意每个绑定都只能包含单个表达式,还要小心造成数据更新死循环
  • v-for: 循环
    布尔特性,它们的存在即暗示为 true,有那些默认为假?
  1. 数字 0
  2. 空字符串 ''
  3. null
  4. NaN
  5. undefined
  6. false

修饰符

修饰符 (Modifiers) 是以半角句号. 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。

  • stop:阻止冒泡
  • prevent:阻止默认动作
<div id="app" >
  <p v-on:click="fn">
    <a href="http://www.163.com" v-on:click.stop.prevent="subfn">单击</a>
  </p>
</div>

<script>
var app = new Vue({
  el: '#app',
  data: {
    msg: '文本'
  },
  methods: {
    fn(){
      console.log( 'p',this.msg );
    },
    subfn(){
      console.log( 'a',this.msg );
    }
  }
});
</script>

计算属性和侦听器

计算属性

var app = new Vue({
  el:'#app',
  data: {
    xing: '张',
    ming: '三'
  },
  computed:{
    getName(){
      return this.xing + this.ming;
    }
  }
})

计算属性和方法有什么区别?

  • 计算属性调用不需要括号(立即执行函数),方法需要括号
  • 计算属性只是引用值,无法进行参数传递,方法可以传参
  • 计算属性会缓存值,方法不会缓存值
  • 当需要在数据变化时执行异步或开销较大时应该使用计算属性
  • 当需要的数据是实时变化的,那么请使用方法

哪个生命周期最早可以获取计算属性和方法?

答:created()钩子函数

计算属性和侦听器有什么区别?

  • 计算属性需要返回值,并且会缓存
  • 侦听器不需要返回值,并且可以的得到上一个值和最新值
  • 侦听器支持深度监听,而计算属性不可以
<div id="app" >
  <input v-model.lazy="val" type="text"> <br>
  {{val}}
</div>
<script>
//计算属性和方法有什么区别?
var app = new Vue({
  el:'#app',
  data: {
    min: 1,
    max: 20,
    val: 1
  },
  watch: {
    val( now, old ){
      if( isNaN(now) || now < this.min || now > this.max){
        this.val = old || 1;
      }
    }
  }
})
</script>

计算属性默认情况下只要getter(取值),如果想要设置值(setter),代码如下:

<div id="app" >
  {{age}} 
  {{xueli}} 
</div>

<script>
//计算属性和方法有什么区别?
var app = new Vue({
  el:'#app',
  computed:{
    age(){
      return 20;
    },
    xueli: {
      get(){
        return '本科';
      },
      set(val){
        console.log( val );
      }
    }
  }
})
</script>

注意watch默认是浅侦听,如果需要深度监听则需要加上deep:true参数

<div id="app" >
  <input v-model="age" type="text" placeholder="年龄">
  {{age}} <br><br>
  <input v-model="info.xueli" type="text" placeholder="学历">
   {{info.xueli}}
</div>
<script>
//计算属性和方法有什么区别?
var app = new Vue({
  el:'#app',
  data: {
    age: 20,
    info: {
      xueli: '本科'
    }
  },
  watch: {
    age( now, old ){
      console.log( '年龄',now );
    },
    info: {
      handler(now, old) {
        console.log( '信息', now );
      },
      immediate: true, //true立即执行,false发生改变后执行
      deep: true //深度监听
    }
  }
})
</script>

Class 与 Style 绑定

绑定class

对象语法
<div v-bind:class="{类名:布尔特性}"> </div>

例如

<div id="app" >
  <button class="btn" v-bind:class="btnClass">按钮</button>
  <button v-on:click="primary=!primary">切换</button>
</div>

<script>
var app = new Vue({
  el:'#app',
  data:{
    primary: false,
    success: false
  },
  computed:{
    btnClass(){
      return {
        primary: this.primary,
        success: this.success
        }
    }
  }
})
</script>

数组语法

我们可以把一个数组传给 v-bind:class,以应用一个 class 列表

<div v-bind:class="[activeClass, errorClass]"></div>

绑定style

对象语法
<div v-bind:style="{属性:}"> </div>

例如:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

示例:vue拖拽效果

<style>
#box{
  position: fixed;
  background-color: red;
  width: 300px;
  height: 200px;
  left: 150px;
}
</style>
<div id="app">
  <div
  id="box"
  :style="boxStyle"
  v-on:mousedown = "mousedown"
  ></div>
</div>
<script>
//计算属性和方法有什么区别?
var app = new Vue({
  el:'#app',
  data:{
    left: 20,
    top: 20,
    width: 100,
    height: 100
  },
  created(){
    var box = document.getElementById('box');
    this.left = box.offsetLeft || this.left;
    this.top = box.offsetTop || this.top;
    this.width = box.offsetWidth || this.width;
    this.height = box.offsetHeight || this.height;
  },
  computed:{
    boxStyle(){

      
      return {
        left: this.left+'px',
        top: this.top+'px',
        width: this.width+'px',
        height: this.height+'px'
      }
    }
  },
  methods: {
    mousedown(e){
      
      var startX = e.clientX;
      var startY = e.clientY;
      var startLeft = this.left;
      var startTop = this.top;
      var maxLeft = window.innerWidth - this.width;
      var maxTop = window.innerHeight - this.height;

      document.onmousemove = e => {
        var endX = e.clientX;
        var endY = e.clientY;

        var distX = endX - startX;
        var distY = endY - startY;

        var left = startLeft + distX;
        var top = startTop + distY;

        if(left <=0) left =0;
        if(top <=0) top =0;
        if(left >=maxLeft) left =maxLeft;
        if(top >=maxTop) top =maxTop;

        this.left = left;
        this.top = top;

      }

      document.onmouseup = function(){
        document.onmousemove = null;
        document.onmouseup = null;
      }

    }
  }
})
</script>

数组语法

注意:数组是有序的集合,后面的对象覆盖前面的对象

<div v-bind:style="[baseStyles, overridingStyles]"></div>

例如

<div id="app">
  <div :style="[baseStyles, overridingStyles]"></div>
</div>
<script>
var app = new Vue({
  el:'#app',
  data:{
    baseStyles: {
      color: 'red',
      fontSize: '26px' 
    },
    overridingStyles: {
      color: 'green',
      'line-height': 3
    }
  }
 
})
</script>

条件渲染

v-if

Vue 中,我们使用 v-if 指令实现同样的功能:

它惰性解析,只有条件为true,才会创建节点,重建事件监听,条件为false,则什么也不错。

<h1 v-if="true">Yes</h1>
<h1 v-if="1>2">Yes</h1>

v-else 否则

<h1 v-if="1 > 2"> 1大于2 </h1>
<h1 v-else> 1小于2 </h1>

v-else-if 多条件

<div id="app">

  <p v-if="score < 60">不及格</p>
  <p v-else-if="score >= 60  && score < 80">良好</p>
  <p v-else-if="score >= 80  && score < 90">优秀</p>
  <p v-else>极好</p>

</div>
<script>
var app = new Vue({
  el:'#app',
  data: {
    score: 100
  }
})
</script>


注意:v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。

template

template模板标签不会被解析出来,这样就不用被迫额外加一个标签。

<div id="app">

  <template v-if="show">
    你好
  </template>

</div>
<script>
var app = new Vue({
  el:'#app',
  data: {
    show: true
  }
})
</script>

key的意义

Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做使 Vue 变得非常快。

但这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们,那就是定义key

<div id="app">
  <h1>登录方式</h1>

  <button v-on:click="type = 'email'">邮箱</button>
  <button v-on:click="type = 'mobile'">手机</button> <br><br>

  <input v-if="type=='email'" type="email" placeholder="请使用邮箱登录" key="email">
  <input v-else type="text" placeholder="请使用手机登录" key="mobile">

</div>
<script>
var app = new Vue({
  el:'#app',
  data: {
    type: 'email'
  }
})
</script>

v-show

另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:

实际上就是控制style.display的值

<h1 v-show="ok">显示</h1>

不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display

v-show 不支持 <template> 元素,也不支持 v-else

v-if 和 v-show 的区别

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

v-if和v-for一起使用

v-ifv-for 一起使用时,v-for 具有比 v-if 更高的优先级。

  <div id="app">

    <ul>
      <template v-if="arr.length">
        <li v-for="item in arr">
          {{item}}
        </li>
      </template>
      <li v-else>
        抱歉,暂时没有数据
      </li>
    </ul>

  </div>

  <script>
  var app = new Vue({
    el: '#app',
    data: {
      arr:[]
    }
  })
  
 </script>


列表渲染

v-for 数组

  
  <div id="app">

    <button 
    v-for="p in total"
    :class="{active: p==page}"
    v-on:click="goPage(p)"
    >{{p}}</button>

  </div>

  <script>
  var app = new Vue({
    el: '#app',
    data: {
      page: 5,
      total: 10
    },
    methods:{
      goPage(p){
        this.page = p;
      }
    }
  })
  </script>


v-for 对象

  <div id="app">
    <p v-for="( val, key, index ) in person">
        {{index}} -- {{key}} --  {{val}}
    </p>
  </div>

  <script>
  var app = new Vue({
    el: '#app',
    data: {
      person: {
        name: '张三',
        age: 20
      }
    }
  })
  </script>

按指定的字段排序

  <div id="app">
    <h1>{{flag}}</h1>
    <button v-on:click="orderBy">按价钱排序</button>
    <ul>
      <li v-for="item in news">
        {{item.title}}  ---- {{item.price}}
      </li>
    </ul>
  </div>

<script>
  var news = [
      { id:1, title:'标题1', price: 10 },
      { id:2, title:'标题2', price: 30 },
      { id:3, title:'标题3', price: 25 },
      { id:4, title:'标题4', price: 32 },
      { id:5, title:'标题5', price: 40 }
  ];
  var app = new Vue({
    el: '#app',
    data: {
      flag: 'def',
      news: [...news],
      onews: [...news]
    },
    methods: {
      orderBy(){

        if(this.flag=='def'){
          this.flag = 'asc';
          this.news.sort(function( a, b ){
            return a.price - b.price;
          })
        }else if(this.flag=='asc'){
          this.flag = 'desc';
          this.news.sort(function( a, b ){
            return  b.price - a.price;
          })
        }else if(this.flag=='desc'){
          this.flag = 'def';
          this.news = [...this.onews];
        }

      }
    }
  })
  </script>

数组更新检测

数组变异方法(破坏原始数组)

  1. pop 删除末尾成员
  2. shift 删除首个成员
  3. push 向数组末尾追加成员
  4. unshift 向数组开头追加成员
  5. reverse 反转数组
  6. sort 数组排序
  7. splice 集修改删除添加一体的数组操作

数组非变异方法(产生新数组,不破坏原始数组)

  1. concat 连接多个数组
  2. slice 分割数组
  3. map 数组映射
  4. filter 数组过滤
  5. reduce 数组叠加

注意事项

由于 JavaScript 的限制,Vue 不能检测以下变动的数组:

  1. 当你利用索引直接设置一个项时,例如:
vm.items[1] = newValue

  1. 当你修改数组的长度时,例如:
vm.items.length = 4

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

针对上面的问题我们可以使用以下方式解决

// Vue.set
Vue.set(要响应的数组, 数组索引值, 更新的值)

也可以使用

// Array.prototype.splice
要响应的数组.splice(数组索引值, 成员个数, 更新的值)

使用vue数据分页和删除

  <style>
    .active{
      background-color: red;
    }
    </style>
  <div id="app">
    <input type="search" placeholder="搜索" v-model="keyword">
    <ul>
      <li v-if="!pnews.length">抱歉,没有搜索到数据!</li>
      <li v-else v-for="(item,index) in pnews">
        {{item.title}} <button v-on:click="delItem(item.id)">删除</button>
      </li>
    </ul>
    <button 
    v-for="p in totalpage"
    v-bind:class="{active: p==page }"
    v-on:click="goPage(p)"
    >{{p}}</button>
  </div>

<script>
  var app = new Vue({
    el: '#app',
    data: {
      keyword: '',
      page: 1,
      perpage: 2,
      news: [
        { id:1, title:'标题1', price: 10 },
        { id:2, title:'标题2', price: 30 },
        { id:3, title:'标题1', price: 25 },
        { id:4, title:'标题4', price: 32 },
        { id:5, title:'标题5', price: 40 },
        { id:6, title:'标题1', price: 24 },
        { id:7, title:'标题7', price: 36 }
      ]
    },
    computed: {
      total(){
        return this.search.length;
      },
      totalpage(){
        return Math.ceil( this.total / this.perpage );
      },
      search(){
        return this.news.filter( item => {
          return item.title.indexOf(this.keyword) != -1;
        })
      },
      pnews(){
        var start = (this.page - 1) * this.perpage;
        var end = start + this.perpage;
        return this.search.slice( start, end);
      }
    },
    methods: {
      goPage( p ){
        this.page = p;
      },
      delItem( id ){
        if(confirm('您确定要删除此行信息吗?')){
          this.news = this.news.filter( item =>{
            return item.id != id;
          })

          //防止因为整页数据删除完,页码不同步
          if( this.page > this.totalpage ){
            this.page -= 1;
          }

        }
      }
    }
  })
  </script>

v-for 和 template

<template>不会被解析出来

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider"></li>
  </template>
</ul>

事件处理

<div id="app">

  <button v-on:click="alert($event,'张三','李四')">{{count}}</button>

</div>

<script>
  var app = new Vue({
    el: '#app',
    data: {
      count: 0
    },
    methods: {
      alert( event, str1,str2 ){
        console.log(event, str1, str2 )
        this.count += 1;
      }
    }
 
  })
  </script>

.capture将事件改为捕获模式?

<ul v-on:click.capture="alert('ul')">
  <li v-on:click.capture="alert('li')">
    <button v-on:click="alert('btn')">按钮</button>
  </li>
</ul>

.self只有单击到绑定的元素对象才能触发,冒泡过来的事件不会触发。

  <ul v-on:click.self="alert('ul')">
    <li>
      <button >按钮</button>
    </li>
  </ul>

事件修饰符

鼠标修饰符
  • left:左键,默认
  • middle:中键,滚轮
  • right: 右键
<button v-on:click.middle="count += 1">{{count}}</button>

键盘修饰符
  • .enter:回车键
  • .tab:换行键
  • .delete:退格键
  • .esc:取消键
  • .space:空格键
  • .up:方向键上
  • .down:方向键下
  • .left:方向键左
  • .right:方向键右

表单输入绑定

v-model双向绑定,不过是下面的代码的语法糖

  <div id="app">
    <input type="text" v-bind:value="name" v-on:input="update">
    {{name}}
  </div>
 
  <script>
  var app = new Vue({
    el:'#app',
    data: {
      name: '张三'
    },
    methods: {
      update(e){
        this.name = e.target.value;
      }
    }
  })
  </script>

单行文本框、多行文本框、单选下拉列表框

  <div id="app">

    <input v-model="form.username" type="text"> <br><br>
    <select v-model="form.xueli">
      <option value="">请选择</option>
      <option value="本科">本科</option>
      <option value="大专">大专</option>
    </select> <br><br>
    <textarea v-model="form.msg"></textarea>
    <button v-on:click="submit()">提交</button>

  </div>
 
  <script>
  var app = new Vue({
    el:'#app',
    data: {
      form: {
        username: '张三',
        xueli: '大专',
        msg: '没有任何信息'
      }
    },
    methods: {
      submit(){
        console.log(this.form )
      }
    }
  })
  </script>

单选框、单选复选框、复选框、多选下拉列表框

  <div id="app">

    <input type="radio" value="" v-model="form.sex"><input type="radio" value="" v-model="form.sex"><br><br>

    <input type="checkbox" v-model="form.check" true-value="1" false-value="0"> 审核 <br><br> 

    <input type="checkbox" value="篮球" v-model="form.habby"> 篮球 
    <input type="checkbox" value="足球" v-model="form.habby"> 足球 
    <input type="checkbox" value="排球" v-model="form.habby"> 排球 <br><br>

    <select v-model="form.skill" multiple>
      <option value="">请选择</option>
      <option value="武术">武术</option>
      <option value="跳舞">跳舞</option>
      <option value="编程">编程</option>
    </select> <br><br>
    <button v-on:click="submit()">提交</button>

  </div>
 
  <script>
  var app = new Vue({
    el:'#app',
    data: {
      form: {
        sex: '女',
        check: true,
        habby: ['篮球','排球'],
        skill: ['武术','编程']
      }
    },
    methods: {
      submit(){
        console.log(this.form )
      }
    }
  })
  </script>

值绑定

  <div id="app">

    <input type="text" v-bind:value="form.username"> <br><br>
    <textarea v-bind:value="form.msg"></textarea> <br>
    <select>
      <option value="">请选择</option>
      <option v-bind:value="item.value" v-for="item in form.xueli">{{item.label}}</option>
    </select>

  </div>
 
  <script>
  var app = new Vue({
    el:'#app',
    data: {
      form: {
        username: '张三',
        msg: '个人说明',
        xueli: [
          {label:'本科',value:'benke'},
          {label:'大专',value:'dazhuan'},
        ]
      }
    }
  })
  </script>

单选框的值绑定

  <div id="app">

    <template v-for="item in form.gender">
      <input 
      type="radio"
      v-model="form.sex"
      v-bind:value="item.value"
      > {{item.label}}
    </template>

  </div>
 
  <script>
  var app = new Vue({
    el:'#app',
    data: {
      form: {
        sex: 'women',
        gender: [
          {label:'男',value:'men'},
          {label:'女',value:'women'}
        ]
      }
    }
  })
  </script>

复选框绑定值

<div id="app">

  <template v-for="item in form.hobbyOptions">
    <input 
    type="checkbox"
    v-model="form.hobby"
    v-bind:value="item.value"
    > {{item.label}}
  </template>
  <br>  <br>
  {{form.hobby}}

</div>

<script>
var app = new Vue({
  el:'#app',
  data: {
    form: {
      hobby: ['篮球'],
      hobbyOptions: [
        {label:'篮球',value:'篮球'},
        {label:'足球',value:'足球'},
        {label:'排球',value:'排球'}
      ]
    }
  }
})
</script>

v-model的修饰符

  • .number:转换为数字
  • .trim:去除首尾空格
  • .lazy:默认input事件改为change事件

组件系统

组件是构建web系统界面中的最小单元。

组件类型

  • 原生组件:<button>
  • 自定义组件:<gx-button>

自定义组件类型

  • 全局组件
  • 局部组件

全局组件

怎么创建一个全局组件?

<div id="app">

  <gx-count></gx-count>
  <gx-count></gx-count>
  
</div>

<script>
Vue.component('gx-count',{
  data: function(){
    return {
      total: 0
    };
  },
  template:`<button @click="total += 1">{{total}}</button>`
})

var app = new Vue({
  el:'#app'
})
</script>

每个子组件有自己独立的作用域,数据不共享。

注意:组件内部的data属性值一定要是一个函数,并且这个函数返回一个对象

注意:组件内部的模板template,必须要有一个根元素,并且这个根元素不能为template

props 向组件内部传参

从父作用域向组件内部传参,通过组件内部的props属性注册

<div id="app">
  <gx-page :page="1"></gx-page>
</div>

<script>
Vue.component('gx-page',{
  props: ['page']
})
</script>

props 注意事项

  • 从父作用域传进子组件内部的属性(变量),你可以读取,但是不要设置它的值。否则会引起数据流混乱。你应该在data中或者在computed拷贝复制一份。
  • 你通过props传进来的属性不能和本地data或者computed中的变量重名

示例

<div id="app">
  <gx-page :page="1" :totalpage="5"></gx-page>
</div>

<script>
Vue.component('gx-page',{
  props: ['page','total','totalpage','perpage'],
  data(){
    return {
      page_: this.page || 1,
      perpage_: this.perpage || 10,
      disabled: false
    }
  },
  computed: {
    totalpage_(){
      return this.totalpage || Math.ceil(this.total/this.perpage_);
    }
  },
  template:`
  <ul class="gxpage">
    <li 
    v-for="p in totalpage_"
    :class="{ active: p == page_, disabled }"
    @click="goPage(p)"
    >{{p}}</li>
  </ul>
  `,
  methods: {
    goPage(p){
      if(this.disabled)return;
      this.page_ = p;
    }
  }
})

var app = new Vue({
  el:'#app'
})

</script>

emit 子组件向父组件发送消息

emit其实利用js冒泡的原理才实现

<div id="app">
  {{page}}
  <gx-page :page="page" @change="change"></gx-page>
</div>

<script>
Vue.component('gx-page', {
  data(){
    return {
      page_: this.page || 1
    }
  },
  template: `
  <ul class="gxpage">
    <li 
    v-for="p in totalpage_"
    :class="{ active: p == page_ }"
    @click="goPage(p)"
    >{{p}}</li>
  </ul>
  `,
  methods: {
    goPage(p){
      this.page_ = p;
      this.$emit( 'change', p );
    }
  }
})
var app = new Vue({
  el:'#app',
  data: {
    page: 1
  }
  methods: {
    change(p){
      this.page = p;
    }
  }
})

sync修饰符

不使用sync的写法

<div id="app">
  {{page}}
  <gx-page :page="page" @update:page="page=$event"></gx-page>
</div>

<script>
Vue.component('gx-page', {
  data(){
    return {
      page_: this.page || 1
    }
  },
  template: `
  <ul class="gxpage">
    <li 
    v-for="p in totalpage_"
    :class="{ active: p == page_ }"
    @click="goPage(p)"
    >{{p}}</li>
  </ul>
  `,
  methods: {
    goPage(p){
      this.page_ = p;
      this.$emit( 'update:page', p );
    }
  }
})
var app = new Vue({
  el:'#app',
  data: {
    page: 1
  }
})

使用sync的写法,可以看到节省了 @update.page

<div id="app">
  {{page}}
  <gx-page :page.sync="page"></gx-page>
</div>

<script>
Vue.component('gx-page', {
  data(){
    return {
      page_: this.page || 1
    }
  },
  template: `
  <ul class="gxpage">
    <li 
    v-for="p in totalpage_"
    :class="{ active: p == page_ }"
    @click="goPage(p)"
    >{{p}}</li>
  </ul>
  `,
  methods: {
    goPage(p){
      this.page_ = p;
      this.$emit( 'update:page', p );
    }
  }
})
var app = new Vue({
  el:'#app',
  data: {
    page: 1
  }
})

props属性验证

组件中使用props数组形式在一些简单功能还好,但是这种方式以下场景存在不足:

  • 不能明确属性的类型 type
  • 没有必填选项 required
  • 没有默认值选项 default
  • 没有校验选项 validator
{
  props: ['page','perpage']
}

在开发一些功能强大的组件props应该使用对象的形式:

Vue.component('gx-list',{
  props: {
    page: {
      type: Number,
      default: 1
    },
    perpage: {
      type: Number,
      default: 10
    },
    totalpage: {
      type: Number
    },
    total: {
      type: Number
    }
  },
  template: `
  <div>验证</div>
  `
})

非props属性

组件之插槽

匿名插槽(默认插槽)

匿名插槽支持多个,但是多个会被重复匹配

<slot></slot>
<slot></slot>

插槽都是支持默认值

<slot>默认</slot>

<div id="app">

  <gx-button>
    <img src="images/4.png" width="10" height="20">
    按钮
  </gx-button>

  <gx-button label="我是朴素按钮"/>

</div>

<script>
Vue.component('gx-button', {
  props: ['label'],
  template: `
  <div>
    <button>
      <slot>{{label}}</slot>
    </button>
  </div>
  `
})

var app = new Vue({
  el:'#app',
  data:{
    msg: ''
  }

})
</script>


命名插槽

顾名思义,就给插槽去一个名字

在组件内部

{
  template: `
  <div>
    <slot name="header">
      命名插槽
    </slot>
    <slot>
      默认插槽
    </slot>
    <slot name="header">
      命名插槽
    </slot>
  </div>
  `
}


父组件调用

<div id="app">

  <gx-dialog>

    <template slot="header">
      命名插槽内容
    </template>

  </gx-dialog>

</div>


示例代码

<div id="app">

  <gx-dialog>

    <div slot="header">
      会员登陆
    </div>

    我是内容

    <div slot="footer">
      <button>取消</button>
      <button>确定</button>
    </div>

  </gx-dialog>

</div>

<script>
Vue.component('gx-dialog',{
  template: `
  <div class="gx-dialog">
    <slot name="header">
      头部
    </slot>

    <slot>主体</slot>

    <slot name="footer">
      脚部
    </slot>
</div>
  `
})
var app = new Vue({
  el: '#app'
})

</script>

作用域插槽

可以把组件内部的数据,抛出到父作用域,父作用域可以拿到内部数据,进行逻辑处理

示例

  <style>
  li{
    display: inline;
    margin: 0 10px;
  }
  a{
    color: inherit;
  }
  .active{
    color: red;
  }
  </style>
  <script src="js/vue.js"></script>
</head>
<body>

<div id="app">

  <gx-nav :data="navs">
    <template slot-scope="row">
      <a :href="row.url">{{row.title}}</a>
    </template>
  </gx-nav>

</div>

<script>
Vue.component('gx-nav',{
  props: {
    index: {
      type: Number,
      default: 0
    },
    data: {
      type: Array,
      required: true
    },
  },
  template: `
  <ul>
    <li 
    :class="{active: index == i }"
    v-for="( item, i ) in data"
    >
      <slot v-bind="item">
        {{item.title}}
      </slot>
    </li>
  </ul>
  `
})
var app = new Vue({
  el: '#app',
  data: {
    navs: [
      {id: 1, icon: '@', title:'首页', url:'#' },
      {id: 2, icon: '$', title:'关于我们', url:'#' },
      {id: 3, icon: '%', title:'联系我们', url:'#' },
    ]
  }
})

</script>

vue全局组件和局部组件

怎么注册一个全局组件?

Vue.component('gx-page', {
  template: `<div>我是全局组件</div>`
})
Vue.component('gx-list', {
  template: `<div>我是全局组件</div>`
})



怎么注册一个局部组件?

const GxPage =  {
  template: `<div>我是局部组件</div>`
}

const GxList =  {
  template: `<div>我是局部组件</div>`
}

new Vue({
  components: {
    'gx-page': GxPage,
    'gx-list': GxList
  }
})


全局组件一经注册,可以再不同的根实例new Vue()使用,也可以在组件的内部使用,无须在components注册。

但是因为是全局的,所以全局组件一打边常驻内存,内存开销要大,所以应该要合理的注册为全局组件,比如一些基础组件(按钮、单选框、复选框)可以注册为全局组件,而页面组件,不适合,因为页面组件通常是不会复用的。

局部组件,通常需要先定义或者导入,然后再组件选项或者根实例选项的components属性注册,才可以使用

<div id="app1">
  <gx-page></gx-page>
  <gx-list></gx-list>
</div>

<div id="app2">
  <gx-page></gx-page>
  <gx-list></gx-list>
</div>

<script>
const GxPage = {
  template: `<div>我是组件1</div>`
}
const GxList = {
  template: `<div>
    我是组件2
    <gx-page></gx-page>
  </div>`,
  components: {
    GxPage
  }
}

//注册全局组件
// Vue.component('gx-page', GxPage )
// Vue.component('gx-list', GxList )

new Vue({
  el:'#app1',
  //注册局部组件
  components: {
    GxPage,
    GxList
  }
})

// new Vue({
//   el:'#app2'
// })

</script>

在单页面应用中安装其他第三方框架

通常在main.js中导入,以下安装方法为全局全部导入组件

  1. 先安装所有的框架依赖
cnpm install element-ui -S

cnpm install iview -S

cnpm install vant -S

  1. main.js中导入
//main.js

import Vue from 'vue';
import App from './App.vue';

//载入gxUI
import gxUI from './components';
Vue.use( gxUI);

//载入饿了么框架
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use( ElementUI );

//载入美团前端框架
import iView from 'iview';
import 'iview/dist/styles/iview.css';
Vue.use( iView );

//载入有赞Vant移动端框架
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);

new Vue({
  render: h => h(App)
}).$mount('#app')


使用vue官方脚手架

一、先全局装vue命令行工具

cnpm install @vue/cli -g

或者使用yarn安装

yarn global add @vue/cli

二、测试安装版本

vue --version

三、快速原型开发

cnpm install @vue/cli-service-global -g

这使得可以直接运行.vue文件,但是需要注意的事,至少需要一个index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
</head>
<body>
  <div id="app"></div>
</body>
</html>

运行命令

-o:浏览器自动打开

vue serve 要运行的文件.vue -o

示例

vue serve 1.vue -o

四、使用vue-cli3创建项目

vue create 项目名称

示例

vue create hello

步骤:

  1. 创建项目
vue create hello
  1. 选择自定义配置
Manually select features
  1. 选择需要安装的模块

space 选择模块,a 全选 i 反选

Babel
Router(待定,还没有学到)
Vuex(待定,还没有学到)
CSS Pre-processors
  1. 选择完毕之后,输入enter确认
  2. 进入css模块选择,我们选择less
Less
  1. 问你是将配置文件独立出来,还是集成到package.json?(自由选择,任意都可以)

  2. 问你是否要保存这个选择,以便以后项目可以使用?

Save this as a preset for future projects?
  1. 当你选择yes,它提示让你去一个名字,可以写中文
Save preset as
  1. 当你取完名称之后,它便开始安装项目所需的所有依赖文件,等待…
  2. 安装完毕依赖之后,会有提示

cd hello:进入项目
yarn serve:运行项目

cd hello
yarn serve
  1. 开始运行项目,浏览器会生成ip地址供你访问
  2. 配置浏览器自动打开

package.json中加入--open

{
  "scripts": {
    "serve": "vue-cli-service serve --open",
    "build": "vue-cli-service build"
  }
}

渲染函数render

vue的模板解析器非常好用,里面包含v-for,v-bind但是不是适用所有的场景,以下使用render函数,自己写编译器,让你回到原生js熟悉的写法。

  <div id="app">

    <gx-list :data="news"></gx-list>

  </div>

  <script>
  Vue.component('gx-list',{
    props:{
      data: Array
    },
    render: function( h ){
      let lis = this.data.map( item => {
        return h('li', item.id + '----' + item.title  );
      });
      return h('ul', lis );
    }
  })

  new Vue({
    el:'#app',
    data: {
      news: [
      {id:1,title:'标题1'},
      {id:2,title:'标题2'}
    ]}
  })
  
  </script>

动态组件

is:显示某组件,下方About是组件名称

<component v-bind:is="'About'"></component>
keep-alive

失活的组件将会被缓存!

vue官方路由组件

vue-routervue全家桶的一员

vue全家桶:

  • vue 核心框架
  • vue-router 路由管理
  • vue-vuex 状态管理
  • axios http请求

vue-router路由

安装
cnpm install vue-router -S
部署vue路由
基础部署
  1. main.js中载入路由
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
  1. 接上代码,创建一个路由实例
const router = new VueRouter();
  1. 接上代码,给实例配置路由规则

规则通常是写在下方数组内

import Home from './views/home'
import About from './views/about'
import Product from './views/product'
import Contact from './views/contact'
const router = new VueRouter({
  routes: [
    { path: '/home', component: Home },
    { path: '/about', component: About },
    { path: '/product', component: Product },
    { path: '/contact', component: Contact },
  ]
});
  1. 将路由实例挂载vue根实例中
new Vue({
  render: h => h(App),
  router
})
  1. vue-router提供了2个全局组件
  • router-link:类似于超级链接a,但是比超级链功能要强
  • router-view:路由和组件匹配成功后,那么组件的内容映射到那里?可以理解为默认插槽

声明式导航和编程式导航的区别

声明式导航

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

编程式导航

<template>
  <button @click="$router.push('/home')">联系页面</button>
</template>

或者

{
  methods: {

    checkLogo(){
      //假设条件为真,就跳转页面
      if(data.code==200){
        this.$router.push('/home')
      }
    }

  }
}

命名路由、路由别名、重定向

命名路由,就是给路由规则取一个名字

var router = new VueRouter({
  routes: [
    { path: '', redirect: { name: 'home' }  },
    { name: 'home', path: '/home', component: Home }
  ]
})

路由别名,就是给路由取一个简短的名称,方便引用的不需要写过长的路径

var router = new VueRouter({
  routes: [
    { path: '', redirect: '/shouye' },
    { alias: '/shouye', path: '/home', component: Home }
  ]
})

动态路由

param形式传参

一、在视图层传入实际参数值

<router-link to="/product/10/蔬菜水果">产品中心</router-link>

以上实际传入2个参数,分部是 10 和 蔬菜水果

二、在路由中配置路由映射规则,冒号表示后面的值为变量

var router = new VueRouter({
  routes: [
    { path:'/product/:id/:name', component: Product }
  ]
})

三、在匹配到的组件(Product)中,接收来自地址栏中传来的参数

Product.vue配置如下

<template>
  <div class="product">
    {{$route.params.id}} --- {{$route.params.name}} 
  </div>
</template>
query形式传参

一、在视图层传入实际参数值

<router-link to="/product?id=10&name=蔬菜水果">产品中心</router-link>

二、在匹配到的组件(Product)中,接收来自地址栏中传来的参数

Product.vue配置如下

<template>
  <div class="product">
    {{$route.query.id}} --- {{$route.query.name}} 
  </div>
</template>
可以同时param和query传参吗?

可以这样传参,形式如下

视图层

<router-link to="/product/10?name=蔬菜水果">产品中心</router-link>

路由规则

var router = new VueRouter({
  routes: [
    { path:'/product/:id', component: Product }
  ]
})

组件接收

<template>
  <div class="product">
    {{$route.params.id}} --- {{$route.query.name}} 
  </div>
</template>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值