一、vue入门以及学习总结

原创
02/17 11:27
阅读数 65

vue cli3

vue ui

一、语法部分

Hellow vue!

 <div id="app">
        <div>{{msg}}</div>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
        /*
              Vue的基本使用步骤
              1、需要提供标签用于填充数据
              2、引入vue.js库文件
              3、可以使用vue的语法做功能了
              4、把vue提供的数据填充到标签里面
            */
        var vm = new Vue({
            //el元素挂载的位置
            el: '#app',
            //模型数据
            data: {
                msg: 'Hello Vue'
            }
        });
    </script>

指令
1. 什么是指令?
⚫ 什么是自定义属性

⚫ 指令的本质就是自定义属性

⚫ 指令的格式:以v-开始(比如:v-cloak)

2. v-cloak指令用法

⚫ 插值表达式存在的问题:“闪动”

⚫ 如何解决该问题:使用v-cloak指令

⚫ 解决该问题的原理:先隐藏,替换好值之后再显示最终的值

<style type="text/css">
  [v-cloak]{
    display: none;
  }
</style>

<div v-cloak>{{msg}}</div>
//背后的原理:先通过样式隐藏内容,然后在内存中进行值的替换,替换好之后再显示最终的结果

3. 数据绑定指令
⚫ v-text  填充纯文本

① 相比插值表达式更加简洁

⚫ v-html  填充HTML片段

① 存在安全问题

② 本网站内部数据可以使用,来自第三方的数据不可以用

⚫ v-pre  填充原始信息

① 显示原始信息,跳过编译过程(分析编译过程)
4. 数据响应式
⚫ 如何理解响应式

① html5中的响应式(屏幕尺寸的变化导致样式的变化)

② 数据的响应式(数据的变化导致页面内容的变化)

⚫ 什么是数据绑定

① 数据绑定:将数据填充到标签中

⚫ v-once  只编译一次

① 显示内容之后不再具有响应式功能
 

双向数据绑定指令

  //v-model双向绑定指令,即div内容跟输入框内容同步
      <div>{{msg}}</div>
      <div>
        <input type="text" v-model='msg'>
      </div>

事件函数的调用方式 

  <div>
            <!-- 方法1 直接写点击事件执行的内容 -->
            <button v-on:click='num++'>点击</button>
            <!-- 方法2 @替代v-on -->
            <button @click='num++'>点击1</button>
            <!-- 方法3  如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数 -->
            <button @click='handle'>点击2</button>
            <!-- 方法4  如果事件要传参数,那么事件对象必须作为最后一个参数显示传递,
                 并且事件对象的名称必须是$event -->
            <button @click='handle(p, $event)'>点击3</button>
        </div>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                num: 0
            }, // 注意点: 这里不要忘记加逗号 
            // methods  中 主要是定义一些函数
            methods: {
                // 默认会传递event对象
                handle: function(event) {
                    //这里的this是Vue的实例对象+
                    console.log(this === vm);
                    //在函数中 想要使用data里面的数据 一定要加this 
                    this.num++;
                }
            }
        });
    </script>

 事件修饰符 

<!-- .stop 阻止冒泡 -->
<button v-on:click.stop='handle1'>点击1</button>
<!-- .prevent 阻止默认行为  -->
<a v-on:click.prevent="handle">跳转</a> 

<!-- js原生写法 -->
 methods: {
        handle0: function(){
          this.num++;
        },
        handle1: function(event){
          // 阻止冒泡
          // event.stopPropagation();
        },
        handle2: function(event){
          // 阻止默认行为
          // event.preventDefault();
        }
      }
  • .stop - 调用 event.stopPropagation()
  • .prevent - 调用 event.preventDefault()
  • .capture - 添加事件侦听器时使用 capture 模式。
  • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
  • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
  • .native - 监听组件根元素的原生事件。
  • .once - 只触发一次回调。
  • .left - (2.2.0) 只当点击鼠标左键时触发。
  • .right - (2.2.0) 只当点击鼠标右键时触发。
  • .middle - (2.2.0) 只当点击鼠标中键时触发。
  • .passive - (2.3.0) 以 { passive: true } 模式添加侦听器

按键修饰符 

<!-- 按回车键触发submit提交 -->
<input v-on:keyup.enter='submit'>
<!-- 按delete键触发删除内容 -->
<input v-on:keyup.delete='handle'> 

methods: {
        clearContent:function(){
        // 按delete键的时候,清空用户名
          this.uname = '';
        },
        handleSubmit: function(){
          console.log(this.uname,this.pwd)
        }
}
自定义按键修饰符 
<!-- 只有a可以触发 -->
<input type="text" v-on:keyup.a='handle' v-model='info'>

Vue.config.keyCodes.a = 65

属性绑定

v-bind:属性名="值"

<!-- v-bind指令用法  url的值写在vue的data里,-->
<a v-bind:href="url">百度</a>
<!-- 简写方式 -->
<a :href="url">百度1</a>
<!-- 也可以通过函数替换属性值 -->
<button v-on:click='handle'>切换</button>

 v-model的低层实现原理分析

 <!-- 通过v-bind:value="msg"传值,但这不是动态绑定,需要用v-on:input="msg=$event.target.value" -->
<input v-bind:value="msg" v-on:input="msg=$event.target.value">

对象语法 

class和style等属性用法类似

1、<div v-bind:class="{ 样式里的类名: 布尔值}"></div> 

2、<div v-bind:class="{ 对象名/数组名}"></div>   (推荐使用),因为更简洁,具体的类名或者样式可以放在vue的data内写。

 <style type="text/css">
    .active {
      border: 1px solid red;
      width: 100px;
      height: 100px;
    }
    .error {
      background-color: orange;
    }
  </style>

<!-- 将样式类名写成对象的形式 -->
<div v-bind:class="{active: isActive,error: isError}">测试</div>
<button v-on:click='handle'>切换</button>

 <script type="text/javascript">
    var vm = new Vue({
      el: '#app',
      data: {
        isActive: true,
        isError: true,
        //如果类名较多,可以将所有类名放到一个对象中,数组也一样
        classArray{"active","error"}
      },
      methods: {
        handle: function(){
          // 控制isActive的值在true和false之间进行切换
          this.isActive = !this.isActive;
          this.isError = !this.isError;
        }
      }
    });

数组语法

<div v-bind:class='[activeClass, errorClass]'>测试样式</div>

//vue对象内的data
data: {
        //通过值的方式传递类名
        activeClass: 'active',
        errorClass: 'error'
      },

分支结构 

<div v-if='score>=90'>优秀</div>
<div v-else-if='score<90&&score>=80'>良好</div>
<div v-else-if='score<80&&score>60'>一般</div>
<div v-else>比较差</div>
<div v-show='flag'>测试v-show</div>

 <script type="text/javascript">
    //v-if控制元素是否渲染到页面 
    // v-show的原理:控制元素样式是否显示 display:none,(已经渲染到了页面)
    var vm = new Vue({
      el: '#app',
      data: {
        //通过score值的大小来控制谁显示
        score: 10,
        flag: false
      },
      methods: {
        handle: function(){
          this.flag = !this.flag;
        }
      }
    });
  </script>

v-for遍历数组

 <!-- item是自定义的,in是关键字,fruits是数组 -->
      <li v-for='item in fruits'>{{item}}</li>
      <!-- index是索引值 -->
      <li v-for='(item, index) in fruits'>{{item + '---' + index}}</li>
      <!-- 如果数组比较复杂,可以使用key值提高性能item.id中id是数组里面的数据,如果没有id可以使用index -->
      <li :key='item.id' v-for='(item, index) in myFruits'>
        <span>{{item.ename}}</span>
        <span>-----</span>
        <span>{{item.cname}}</span>
      </li>

 v-for遍历对象

<div v-for='(value, key, index) in object'></div>
<!--  v-if和v-for结合使用,只会输出value等于12的键值对,这个对象里面其他的键值对不会被输出 -->
<div v-if='value==12' v-for='(value, key, index) in object'>{{v + '---' + k + '---' + i}}</div>
<!-- 输出的结果是12---age---1 -->

vue里data的对象
var obj = {
      uname: 'lisi',
      age: 12,
      gender: 'male'
    }

表单修饰

.number 转换为数值 

注意:当开始输入非数字的字符串时,因为Vue无法将字符串转换成数值 所以属性值将实时更新成相同的字符串。即使后面输入数字,也将被视作字符串

.trim 自动过滤用户输入的首尾空白字符 

注意:只能去掉首尾的 不能去除中间的空格 

.lazy 将input事件切换成change事件,在失去焦点 或者 按下回车键时才更新

注意:.lazy 修饰符延迟了同步更新属性值的时机。即将原本绑定在 input 事件的同步逻辑转变为绑定在 change 事件上 

<input type="text" v-model.number='age'>
<input type="text" v-model.trim='info'>
<input type="text" v-model.lazy='msg'>

自定义指令 

Vue.directive 注册全局指

<!--   使用自定义的指令,只需在对用的元素中,加上'v-'的前缀形成类似于内部指令'v-if','v-text'的形式。 --> 
<input type="text" v-focus> 

<script>
// 注意点: 
//   1、 在自定义指令中  如果以驼峰命名的方式定义 如  Vue.directive('focusA',function(){}) 
//   2、 在HTML中使用的时候 只能通过 v-focus-a 来使用  
// 注册一个全局自定义指令 v-focus 
Vue.directive('focus', {    
    // 当绑定元素插入到 DOM 中。 其中 el为dom元素    
    inserted: function (el) {            
    // 聚焦元素,获取焦点            
    el.focus();    
    } 
});
</script> 

Vue.directive 注册全局指令 带参

<input type="text" v-color='msg'>
<script type="text/javascript">    
//自定义指令-带参数      bind - 只调用一次,在指令第一次绑定到元素上时候调用
 Vue.directive('color', {      
    // bind声明周期, 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置      
    // el 为当前自定义指令的DOM元素        
    // binding 为自定义的函数形参   通过自定义属性传递过来的值 存在 binding.value 里面      
    bind: function(el, binding){        
    // 根据指令的参数设置背景色        
    // console.log(binding.value.color)        
    el.style.backgroundColor = binding.value.color;      
    }    
});
</script>

自定义指令局部指令 

1、局部指令,需要定义在 directives 的选项 用法和全局用法一样

2、局部指令只能在当前组件里面使用

3、当全局指令和局部指令同名时以局部指令为准

var vm = new Vue({ 
//局部指令,需要定义在  directives 的选项      
    directives: {        
        color: {          
            bind: function(el, binding){            
            el.style.backgroundColor = binding.value.color;          
        }        
    }
})

计算属性 computed 

1、模板中放入太多的逻辑会让模板过重且难以维护 使用计算属性可以让模板更加的简洁

2、计算属性是基于它们的响应式依赖进行缓存的

3、computed比较适合对多个变量或者对象进行处理后返回一个结果值,也就是数多个变量中的某一个值发生了变 化则我们监控的这个值也就会发生变化

     //方法,不会缓存,再次调用会重新执行方法
      methods: {
        reverseMessage: function(){
          console.log('methods')
          return this.msg.split('').reverse().join('');
        }
      },
      //计算属性,再次调用如果依赖(值,这里msg)不改变,该方法不会重新计算,而是将缓存的结果输出,提高性能
      computed: {
        reverseString: function(){
          console.log('computed')
          return this.msg.split('').reverse().join('');
        }


 侦听器 watch 

1、使用watch来响应数据的变化

2、一般用于异步或者开销较大的操作

3、watch 中的属性 一定是data 中 已经存在的数据

4、当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够 监听到变化,此时就需要deep属性对对象进行深度监听
 

<input type="text" v-model.lazy='uname'>

//监听器
watch: {
    //当input失去焦点时触发监听器
    uname: function(val){
    // 调用后台接口验证用户名的合法性
    this.checkName(val);
    // 修改提示信息
    this.tip = '正在验证...';
    }
}

过滤器 

1、Vue.js允许自定义过滤器,可被用于一些常见的文本格式化。

2、过滤器可以用在两个地方:双花括号插值和v-bind表达式。

3、过滤器应该被添加在JavaScript表达式的尾部,由“管道”符号指示

4、支持级联操作 

5、过滤器不改变真正的 data ,而只是改变渲染的结果,并返回过滤后的版本

6、全局注册时是filter,没有s的。而局部过滤器是filters,是有s的

<!-- 数据|过滤器 -->
<div>{{msg | upper}}</div>
<!-- 数据|过滤器|再次过滤 -->
<div>{{msg | upper | lower}}</div>
<!-- 属性名过滤 -->
<div :abc='msg | upper'>测试数据</div>

 //全局过滤器  第一个字母大写
    Vue.filter('upper', function(val) {
      return val.charAt(0).toUpperCase() + val.slice(1);
    });


//局部过滤器
      filters: {
        upper: function(val) {
          return val.charAt(0).toUpperCase() + val.slice(1);
        }
      }

----------------------------------------------------------

<div>{{date | format('yyyy-MM-dd hh:mm:ss')}}</div>
    
// 这是一个时间格式转换函数
// 带参过滤器
    Vue.filter('format', function(value, arg) {
      function dateFormat(date, format) {
          if (typeof date === "string") {
              var mts = date.match(/(\/Date\((\d+)\)\/)/);
              if (mts && mts.length >= 3) {
                  date = parseInt(mts[2]);
              }
          }
          date = new Date(date);
          if (!date || date.toUTCString() == "Invalid Date") {
              return "";
          }
          var map = {
              "M": date.getMonth() + 1, //月份 
              "d": date.getDate(), //日 
              "h": date.getHours(), //小时 
              "m": date.getMinutes(), //分 
              "s": date.getSeconds(), //秒 
              "q": Math.floor((date.getMonth() + 3) / 3), //季度 
              "S": date.getMilliseconds() //毫秒 
          };

          format = format.replace(/([yMdhmsqS])+/g, function(all, t) {
              var v = map[t];
              if (v !== undefined) {
                  if (all.length > 1) {
                      v = '0' + v;
                      v = v.substr(v.length - 2);
                  }
                  return v;
              } else if (t === 'y') {
                  return (date.getFullYear() + '').substr(4 - all.length);
              }
              return all;
          });
          return format;
      }
      return dateFormat(value, arg);
    })

生命周期 

<script type="text/javascript">
    /*
      Vue实例的生命周期
      
    */
    var vm = new Vue({
      el: '#app',
      data: {
        msg: '生命周期'
      },
      methods: {
        update: function(){
          this.msg = 'hello';
        },
        destroy: function(){
          this.$destroy();
        }
      },
      // 挂载
      beforeCreate: function(){
        console.log('beforeCreate');
      },
      created: function(){
        console.log('created');
      },
      beforeMount: function(){
        console.log('beforeMount');
      },
      //重点!到此即说明页面元素加载完成
      mounted: function(){
        console.log('mounted');
      },

      // 更新
      beforeUpdate: function(){
        console.log('beforeUpdate');
      },
      updated: function(){
        console.log('updated');
      },

      // 销毁
      beforeDestroy: function(){
        console.log('beforeDestroy');
      },
      destroyed: function(){
        console.log('destroyed');
      }
    });
  </script>

变异方法

Vue 将被侦听的数组的变异方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

替换数组 

变异方法,顾名思义,会改变调用了这些方法的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如 filter()concat()  slice() 。它们不会改变原始数组,而总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:

动态数组响应式数据 

    // 通过数组索引改变值不是响应式数据,页面数据不会更新
    vm.list[1] = 'lemon';
    // 响应式写法一   (对象数组,索引,新值)
    Vue.set(vm.list, 2, 'lemon');
    // 响应式写法二
    vm.$set(vm.list, 1, 'lemon');
    // 对象也相同(添加属性)
    vm.$set(vm.info, 'gender', 'female');

vue实现图书管理案例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .grid {
            margin: 0 auto;
            width: 550px;
            /* background-color: skyblue; */
            text-align: center;
        }
        
        .submit {
            padding-top: 10px;
            height: 40px;
            background-color: rgb(248, 182, 77);
            border-bottom: 1px solid rgb(255, 255, 255);
        }
        
        .sum {
            height: 30px;
            line-height: 30px;
            background-color: rgb(248, 182, 77);
            border-bottom: 1px solid rgb(255, 255, 255);
        }
        
        .tb table {
            width: 100%;
            border-collapse: collapse;
        }
        
        thead {
            border: 1px dashed rgb(248, 182, 77);
            background-color: rgb(248, 182, 77);
        }
        
        .tb td {
            background-color: #fff;
            border: 1px dashed rgb(248, 182, 77);
        }
        
        .tb th,
        td {
            padding: 10px;
        }
    </style>

</head>

<body>
    <div class="grid">
        <div class="head">
            <h1>图书馆管理</h1>
        </div>
        <div class="submit">
            <label for="id">编号:</label>
            <input type="text" name="" id="" v-focus v-model='id' :disabled='flag'>
            <label for="id">名称:</label>
            <input type="text" name="" id="" v-model='name'>
            <input type="button" value="提交" @click='add'>
            <input type="button" value="清除" @click='clear'>

        </div>
        <div class="sum">
            <span>图书总数:</span>
            <span>{{sum}}</span>
        </div>
        <div class="tb">
            <table>
                <thead>
                    <tr>
                        <th>编号</th>
                        <th>名称</th>
                        <th>时间</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    <tr :key='item.id' v-for='item in books'>
                        <td>{{item.id}}</td>
                        <td>{{item.name}}</td>
                        <td>{{item.date|format('yyyy-MM-dd hh:mm:ss')}}</td>
                        <td>
                            <a href="" @click.prevent='edit(item.id)'>修改</a>|
                            <a href="" @click.prevent='dele(item.id)'>删除</a>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>

    <script src="vue.js"></script>
    <script>
        //时间格式转换
        Vue.filter('format', function(value, arg) {
            function dateFormat(date, format) {
                if (typeof date === "string") {
                    var mts = date.match(/(\/Date\((\d+)\)\/)/);
                    if (mts && mts.length >= 3) {
                        date = parseInt(mts[2]);
                    }
                }
                date = new Date(date);
                if (!date || date.toUTCString() == "Invalid Date") {
                    return "";
                }
                var map = {
                    "M": date.getMonth() + 1, //月份 
                    "d": date.getDate(), //日 
                    "h": date.getHours(), //小时 
                    "m": date.getMinutes(), //分 
                    "s": date.getSeconds(), //秒 
                    "q": Math.floor((date.getMonth() + 3) / 3), //季度 
                    "S": date.getMilliseconds() //毫秒 
                };
                format = format.replace(/([yMdhmsqS])+/g, function(all, t) {
                    var v = map[t];
                    if (v !== undefined) {
                        if (all.length > 1) {
                            v = '0' + v;
                            v = v.substr(v.length - 2);
                        }
                        return v;
                    } else if (t === 'y') {
                        return (date.getFullYear() + '').substr(4 - all.length);
                    }
                    return all;
                });
                return format;
            }
            return dateFormat(value, arg);
        })

        //自动获取焦点
        Vue.directive('focus', {
            inserted: function(el) {
                el.focus();
            }
        })

        //创建vue实例
        var vm = new Vue({
            el: '.grid',
            data: {
                flag: false,

                id: '',
                name: '',
                books: []
            },
            methods: {
                //清楚数据
                clear: function() {
                    this.id = '';
                    this.name = '';
                    this.flag = false;
                },
                //查找数据
                findByid: function(id) {

                },
                //判断ID是否存在
                booleanId: function(id) {
                    var existId = false;
                    this.books.some(function(item) {
                        if (id == item.id) {
                            existId = true;
                        }
                    })
                    return existId;
                },
                add: function() {
                    if (this.id.length == 0 || this.name.length == 0) {
                        alert('编号或者名称未填写!')
                        return true;
                    }
                    if (this.flag) {
                        //修改
                        if (this.books[this.id - 1].name == this.name) {
                            alert('内容未修改!')
                            return true;
                        } else {
                            this.books[this.id - 1].name = this.name;
                            this.books[this.id - 1].date = new Date().getTime();
                            this.clear();
                        }
                    } else {
                        //添加
                        if (this.booleanId(this.id)) {
                            alert('编号重复,请修改!')
                        } else {
                            var book = {};
                            book.id = this.id;
                            book.name = this.name;
                            book.date = new Date().getTime();
                            this.books.push(book);
                            this.clear();
                        }
                    }
                    this.flag = false;
                },
                edit: function(id) {
                    var book = [];
                    this.books.some(function(item) {
                        if (id == item.id) {
                            book.push(item);
                        }
                    });
                    this.id = book[0].id;
                    this.name = book[0].name;
                    this.flag = true;
                },
                dele: function(id) {
                    this.books.splice(this.id - 1, 1);
                }
            },
            //计算属性
            computed: {
                sum: function() {
                    return this.books.length
                }
            },
            mounted: function() {
                var data = [{
                    id: 1,
                    name: '西游记',
                    date: 2525609975000
                }, {
                    id: 2,
                    name: '三国演义',
                    date: 2525609975000
                }, {
                    id: 3,
                    name: '红楼梦',
                    date: 2525609975000
                }, {
                    id: 4,
                    name: '水浒传',
                    date: 2525609975000
                }];
                this.books = data
            }
        })
    </script>
</body>

</html>

全局组件注册语法

<!-- 如果组件是驼峰命名,需要使用短横线隔开,否之无法使用 -->
<button-counter></button-counter>

------------js----------------
 //  组件注册注意事项
        //  1、组件参数的data值必须是函数
        //  2、组件模板必须是单个根元素,即外面被一个div包裹
        //  3、组件模板的内容可以是模板字符串
        // 驼峰命名例如:ButtonCounter,这种写法只能在字符串模板中用驼峰的方式使用组件,但是在普通的标签模板中,必须使用短横线的方式使用组件
        Vue.component('button-counter', {
            data: function() {
                return {
                    count: 0
                }
            },
            //模板
            template: '<div><button @click="handle">点击了{{count}}次</button><button>测试</button></div>',
            //此处可以使用模板字符串,模板字符串需要浏览器提供支持(ES6语法),如:
            // `(反引号)
            //  <div>
            //     <button @click="handle">点击了{{count}}次</button>
            //     <button>测试123</button>
            //     <HelloWorld></HelloWorld>
            //  </div>
            // `
        })


局部组件注册

<hello-world></hello-world>

-------------js--------------
var HelloWorld = {
      data: function(){
        return {
          msg: 'HelloWorld'
        }
      },
      template: '<div>{{msg}}</div>'
    };
    
var vm = new Vue({
      el: '#app',
      //在vue实例里面的局部组件components多了个s
      components: {
        'hello-world': HelloWorld,
      }
    });

组件间数据交互- 父组件向子组件传值

vm.$emit( 事件对象, [参数] )   触发当前实例上的事件。附加参数都会传给监听器回调。

vm.$on( 事件对象, 回调函数)  监听事件

vm.$off( 事件对象, 回调函数 )  移除自定义事件监听器

vm.$once( event, callback )  监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除。

<body>
    <div id="app">
        <!-- 父组件通过属性将值传递给子组件 -->
        <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
        <!-- 父组件监听子组件的事件  $event是固定的,否则拿不到值-->
        <menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
        // 子组件向父组件传值-携带参数
        Vue.component('menu-item', {
            //组件内部通过props接收传递过来的值
            // props属性名规则   1在props中使用驼峰形式,模板中需要使用短横线的形式   2字符串形式的模板中没有这个限制
            props: ['parr'],
            //字符串模板
            template: `
        <div>
          <ul>
            <li :key='index' v-for='(item,index) in parr'>{{item}}</li>
          </ul>
          <!-- 子组件通过自定义事件向父组件传递信息 -->
          <!-- $emit(触发)是固定的,enlarge-text自定义名称 5是传的参数 -->
          <button @click='$emit("enlarge-text", 5)'>扩大父组件中字体大小</button>
          <button @click='$emit("enlarge-text", 10)'>扩大父组件中字体大小</button>
        </div>
      `
        });
        //父组件
        var vm = new Vue({
            el: '#app',
            data: {
                pmsg: '父组件中内容',
                parr: ['apple', 'orange', 'banana'],
                fontSize: 10
            },
            methods: {
                handle: function(val) {
                    // 扩大字体大小
                    this.fontSize += val;
                }
            }
        });
    </script>
</body>

非父子组件间传值

// 提供事件中心
    var hub = new Vue();

    Vue.component('test-tom', {
      data: function(){
        return {
          num: 0
        }
      },
      template: `
        <div>
          <div>TOM:{{num}}</div>
          <div>
            <button @click='handle'>点击</button>
          </div>
        </div>
      `,
      methods: {
        handle: function(){
          //触发'jerry-event'组件的监听事件,并传参
          hub.$emit('jerry-event', 2);
        }
      },
      mounted: function() {
        // 监听事件
        hub.$on('tom-event', (val) => {
          this.num += val;
        });
      }
    });
    Vue.component('test-jerry', {
      data: function(){
        return {
          num: 0
        }
      },
      template: `
        <div>
          <div>JERRY:{{num}}</div>
          <div>
            <button @click='handle'>点击</button>
          </div>
        </div>
      `,
      methods: {
        handle: function(){
          //触发'tom-event'组件的监听事件,并传参
          hub.$emit('tom-event', 1);
        }
      },
      mounted: function() {
        // 监听事件
        hub.$on('jerry-event', (val) => {
          this.num += val;
        });
      }
    });

具名插槽用法
 

<base-layout>
    <!-- 这里的p会被header标签包裹 -->
    <p slot='header'>标题信息</p>
    <!-- 这里的p会被main标签包裹 -->
    <p>主要内容1</p>
    <!-- 这里的p会被footer标签包裹 -->
    <p slot='footer'>底部信息信息</p>
    <!-- template页面渲染后不会出现,变成header标签 -->
    <template slot='header'>
        <p>标题信息88</p>
    </template>
</base-layout>

-------------js--------------
 // 插槽 <slot></slot>,写在template模板内
    // 具名插槽<slot name='header'></slot>,就是有name属性
    Vue.component('base-layout', {
      template: `
        <div>
          <header>
            <slot name='header'></slot>
          </header>
          <main>
            <slot></slot>
          </main>
          <footer>
            <slot name='footer'></slot>
          </footer>
        </div>
      `
    });

作用域插槽
 应用场景:父组件对子组件的内容进行加工处理,可以获取父亲元素绑定的数据

<!-- 父组件 :list='list'传数据到子组件-->
<fruit-list :list='list'>
      <!-- slot-scope='slotProps'是自定义属性,通过该属性可以访问子组件传递的值 -->
      <!-- slot-scope='slotProps中的slotProps.row可以获取父亲元素绑定的数据 -->
      <template slot-scope='slotProps'>
        <!-- 对符合条件的标签添加效果 -->
        <strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>
        <span v-else>{{slotProps.info.name}}</span>
      </template>
</fruit-list>

--------------js------------
 // 子组件
    Vue.component('fruit-list', {
      props: ['list'],
      template: `
        <div>
          <li :key='item.id' v-for='item in list'>
            <slot :info='item'>{{item.name}}</slot>
          </li>
        </div>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {
        list: [{
          id: 1,
          name: 'apple'
        },{
          id: 2,
          name: 'orange'
        },{
          id: 3,
          name: 'banana'
        }]
      }
    });

购物车案例总结

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style type="text/css">
    .container {
    }
    .container .cart {
      width: 300px;
      margin: auto;
    }
    .container .title {
      background-color: lightblue;
      height: 40px;
      line-height: 40px;
      text-align: center;
      /*color: #fff;*/  
    }
    .container .total {
      background-color: #FFCE46;
      height: 50px;
      line-height: 50px;
      text-align: right;
    }
    .container .total button {
      margin: 0 10px;
      background-color: #DC4C40;
      height: 35px;
      width: 80px;
      border: 0;
    }
    .container .total span {
      color: red;
      font-weight: bold;
    }
    .container .item {
      height: 55px;
      line-height: 55px;
      position: relative;
      border-top: 1px solid #ADD8E6;
    }
    .container .item img {
      width: 45px;
      height: 45px;
      margin: 5px;
    }
    .container .item .name {
      position: absolute;
      width: 90px;
      top: 0;left: 55px;
      font-size: 16px;
    }

    .container .item .change {
      width: 100px;
      position: absolute;
      top: 0;
      right: 50px;
    }
    .container .item .change a {
      font-size: 20px;
      width: 30px;
      text-decoration:none;
      background-color: lightgray;
      vertical-align: middle;
    }
    .container .item .change .num {
      width: 40px;
      height: 25px;
    }
    .container .item .del {
      position: absolute;
      top: 0;
      right: 0px;
      width: 40px;
      text-align: center;
      font-size: 40px;
      cursor: pointer;
      color: red;
    }
    .container .item .del:hover {
      background-color: orange;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    
    var CartTitle = {
      props: ['uname'],
      template: `
        <div class="title">{{uname}}的商品</div>
      `
    }
    var CartList = {
      props: ['list'],
      template: `
        <div>
          <div :key='item.id' v-for='item in list' class="item">
            <img :src="item.img"/>
            <div class="name">{{item.name}}</div>
            <div class="change">
              <a href="" @click.prevent='sub(item.id)'>-</a>
              <input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
              <a href="" @click.prevent='add(item.id)'>+</a>
            </div>
            <div class="del" @click='del(item.id)'>×</div>
          </div>
        </div>
      `,
      methods: {
        changeNum: function(id, event){
          this.$emit('change-num', {
            id: id,
            type: 'change',
            num: event.target.value
          });
        },
        sub: function(id){
          this.$emit('change-num', {
            id: id,
            type: 'sub'
          });
        },
        add: function(id){
          this.$emit('change-num', {
            id: id,
            type: 'add'
          });
        },
        del: function(id){
          // 把id传递给父组件
          this.$emit('cart-del', id);
        }
      }
    }
    var CartTotal = {
      props: ['list'],
      template: `
        <div class="total">
          <span>总价:{{total}}</span>
          <button>结算</button>
        </div>
      `,
      computed: {
        total: function() {
          // 计算商品的总价
          var t = 0;
          this.list.forEach(item => {
            t += item.price * item.num;
          });
          return t;
        }
      }
    }
    Vue.component('my-cart',{
      data: function() {
        return {
          uname: '张三',
          list: [{
            id: 1,
            name: 'TCL彩电',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          },{
            id: 2,
            name: '机顶盒',
            price: 1000,
            num: 1,
            img: 'img/b.jpg'
          },{
            id: 3,
            name: '海尔冰箱',
            price: 1000,
            num: 1,
            img: 'img/c.jpg'
          },{
            id: 4,
            name: '小米手机',
            price: 1000,
            num: 1,
            img: 'img/d.jpg'
          },{
            id: 5,
            name: 'PPTV电视',
            price: 1000,
            num: 2,
            img: 'img/e.jpg'
          }]
        }
      },
      template: `
        <div class='cart'>
          <cart-title :uname='uname'></cart-title>
          <cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
          <cart-total :list='list'></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      },
      methods: {
        changeNum: function(val) {
          // 分为三种情况:输入域变更、加号变更、减号变更
          if(val.type=='change') {
            // 根据子组件传递过来的数据,跟新list中对应的数据
            this.list.some(item=>{
              if(item.id == val.id) {
                item.num = val.num;
                // 终止遍历
                return true;
              }
            });
          }else if(val.type=='sub'){
            // 减一操作
            this.list.some(item=>{
              if(item.id == val.id) {
                item.num -= 1;
                // 终止遍历
                return true;
              }
            });
          }else if(val.type=='add'){
            // 加一操作
            this.list.some(item=>{
              if(item.id == val.id) {
                item.num += 1;
                // 终止遍历
                return true;
              }
            });
          }
        },
        delCart: function(id) {
          // 根据id删除list中对应的数据
          // 1、找到id所对应数据的索引
          var index = this.list.findIndex(item=>{
            return item.id == id;
          });
          // 2、根据索引删除对应数据
          this.list.splice(index, 1);
        }
      }
    });
    var vm = new Vue({
      el: '#app',
      data: {

      }
    });

  </script>
</body>
</html>

学习vue拓展的知识

Promise、Fetch、Axios入门以及学习总结,涉及Express搭建接口    https://my.oschina.net/u/4139437/blog/3168201

vue路由

路由的本质就是一种对应关系,比如说我们在url地址中输入我们要访问的url地址之后,浏览器要去请求这个url地址对应的资源。 那么url地址和真实的资源之间就有一种对应的关系,就是路由。

路由分为前端路由和后端路由

1).后端路由是由服务器端进行实现,并完成资源的分发

2).前端路由是依靠hash值(锚链接)的变化进行实现

后端路由性能相对前端路由来说较低

前端路由的基本概念:根据不同的事件来显示不同的页面内容,即事件与事件处理函数之间的对应关系

前端路由主要做的事情就是监听事件并分发执行事件处理函数

模拟路由案例

<a href="#/zhuye">主页</a> 
<a href="#/keji">科技</a> 
<!-- 根据 :is 属性指定的组件名称,把对应的组件渲染到 component 标签所在的位置 -->
<!-- 可以把 component 标签当做是【组件的占位符】 -->
<component :is="comName"></component>


   <script>
      // 主页组件
      const zhuye = {
        template: '<h1>主页信息</h1>'
      }

      // 科技组件
      const keji = {
        template: '<h1>科技信息</h1>'
      }
     // #region vue 实例对象
      const vm = new Vue({
        el: '#app',
        data: {
          comName: 'zhuye'
        },
        // 注册私有组件
        components: {
          zhuye,
          keji
        }
      })

    // 监听 window 的 onhashchange 事件,根据获取到的最新的 hash 值,切换要显示的组件的名称
      window.onhashchange = function() {
        // 通过 location.hash 获取到最新的 hash 值
        console.log(location.hash);
        switch(location.hash.slice(1)){
          case '/zhuye':
            vm.comName = 'zhuye'
          break
          case '/keji':
            vm.comName = 'keji'
          break
        }
</script>

Vue Router简介

它是一个Vue.js官方提供的路由管理器。是一个功能更加强大的前端路由器,推荐使用。

Vue Router和Vue.js非常契合,可以一起方便的实现SPA(single page web application,单页应用程序)应用程序的开发。

Vue Router依赖于Vue,所以需要先引入Vue,再引入Vue Router

Vue Router的特性:

支持H5历史模式或者hash模式

支持嵌套路由 支持路由参数

支持编程式路由

支持命名路由

支持路由导航守卫

支持路由过渡动画特效

支持路由懒加载

支持路由滚动行为

Vue Router的使用步骤

第一步引入vue-router.js文件

<!--第二步 添加路由链接 -->
<router-link to="/user">User</router-link>
<router-link to="/register">Register</router-link>
 <!-- 根据命名路由跳转 -->
<router-link :to="{ name: 'user', params: {id: 3} }">User3</router-link>

<!--第三步 路由占位符 -->
<router-view></router-view>

<script>
      // 第四步 定义路由组件
      const User = {
        template: '<h1>User 组件</h1>'
      }

      const Register = {
        template: '<h1>Register 组件</h1>'
      }

//第五步 配置路由规则并创建路由实例
      const router = new VueRouter({
        // 所有的路由规则
        routes: [
          {
          // 命名路由
          name: 'user',
          path: '/user/:id',},
          //至少包含path、component
          //path设置为/表示页面最初始的地址 / ,redirect表示要被重定向的新地址,设置为一个路由即可
          { path: '/', redirect: '/user'},  // 路由重定向:可以通过路由重定向为页面设置默认展示的组件
          { path: '/user', component: User },
          { path: '/register', component: Register }
        ]
      })

//第六步 将路由挂载到Vue实例中
      const vm = new Vue({
        // 指定控制的区域
        el: '#app',
        data: {},
        // 挂载路由实例对象 键和值一样可以简写
        // router: router
        router
      })

嵌套路由意思是在显示的路由上嵌套一个子路由

1、在父组件中添加路由连接

2、
<!-- 子路由的占位符 -->
<router-view />

3、在路由实例添加规则

 // children 数组表示子路由规则
          { path: '/register', component: Register, children: [
            { path: '/register/tab1', component: Tab1 },
            { path: '/register/tab2', component: Tab2 }
          ] }

动态路由匹配

<router-link to="/user/1">User1</router-link>
<router-link to="/user/2">User2</router-link>

<script>
      const User = {
        template: '<h1>User 组件 -- 用户id为: {{$route.params.id}}</h1>'
      }
      // 创建路由实例对象
      const router = new VueRouter({
        routes: [
          //这里的:id就是路由连接user/后面的值
          { path: '/user/:id', component: User },
        ]
      })
</script>

页面导航的两种方式:
A.声明式导航:通过点击a链接的方式实现的导航
B.编程式导航:调用js的api方法实现导航

编程式导航
this.$router.push("hash地址");
this.$router.push("/login");
//根据路由名跳转并传参
this.$router.push({ name:'user' , params: {id:123} });
this.$router.push({ path:"/login" });
this.$router.push({ path:"/login",query:{username:"jack"} });

this.$router.go( n );//n为数字,n=1(向前1) n=-1(退后1)

 

 

 

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部