Skip to content

基本指令使用

v-model 指令

  • 能实现页面数据动态改变
html
<div class="demo">
  <input type="text" v-model="msg" />
  <p>显示:{{msg}}</p>
</div>

<script>
  new Vue({
    //配置对象:属性名是一些特定的名称,每个属性都是一个选项
    el: '.demo', //选择器字符串---用于指定模板的根元素
    data: {
      //包含一些数据属性的对象----为模板提供数据
      msg: ''
    }
  })
</script>

v-bind 强制数据绑定

html
<div class="demo">
  <!-- 当属性值是一个动态的值时可以使用这个指令 -->
  <a v-bind:href="url">去B站</a>
  <!-- 简捷写法 -->
  <a :href="url">去B站</a>
</div>

<script>
  new Vue({
    el: '.demo',
    data: {
      url: 'https://t.bilibili.com'
    }
  })
</script>

v-if 指令

html
<div class="demo">
  <!-- 只有当满足条件的dom元素才会渲染 (通过删除dom元素进行显示隐藏)-->
  <p v-if="ok">表白成功</p>
  <p v-else>表白失败</p>

  <!-- 只有当满足条件的dom元素才会显示(通过display:none进行显示隐藏) -->
  <p v-show="ok">求婚成功</p>
  <p v-show="!ok">求婚失败</p>
  <!-- 如果需要频繁切换 v-show较好 -->
  <button @click="ok = !ok">切换</button>
</div>

<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    data: {
      ok: true
    }
    //另一种实现方法
    // data(){
    // 	return {
    // 		ok:true
    // 	}
    // },
  })
</script>

v-for 指令

html
<div class="demo">
  <ul>
    <!-- p代表persons数组的每一项,index表示每一项的下标,当需要删除功能时添加一个key可以提高性能 -->
    <li v-for="(p,index) in persons" :key="p.id">
      {{p.id}}---{{p.name}}---{{p.age}}
      <button @click="del(index)">删除</button>
      <button @click="upd(index,{id:Date.now(),name:'cat',age:17})">
        修改
      </button>
    </li>
  </ul>
</div>
<script src="vue.js"></script>
<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    data: {
      persons: [
        { id: 1, name: 'Tom', age: 15 },
        { id: 2, name: 'Jery', age: 17 },
        { id: 3, name: 'Tem', age: 157 }
      ]
    },
    methods: {
      del(index) {
        this.persons.splice(index, 1) //这里使用的不是js原生方法,其实是vue重写后的方法
      },
      upd(index, obj) {
        this.persons.splice(index, 1, obj) //这里使用的不是js原生方法,其实是vue重写后的方法
      }
    }
  })
  /* vue在内部如何监视数据的变化
			1.对象中属性数据
				给属性添加setter方法

			2.数组中的元素数据
				重写(重新定义)数组一系列更新数组元素的方法
				1).调用原生数组对应的方法,对数组元素进行处理
				2).更新界面
	*/
</script>

修改数据

  • 这样修改的数据也具有响应式

v-for 和 v-if 可以一起使用

  • 这种写法性能比较低,应该尽量避免这种用法
  • 因为 v-for 对比 v-if 有着更高的优先级,会先执行 v-for
html
<div id="demo">
  <p
    v-if="post.id==3"
    v-for="(post,index) in posts"
    :key="post.id"
    :title="post.title"
  >
    {{post.id+'...'+post.title}}
  </p>
</div>
<script src="vue.js"></script>
<script>
  new Vue({
    el: '#demo',
    data: {
      posts: [
        { id: 1, title: '博客1' },
        { id: 2, title: '博客2' },
        { id: 3, title: '博客3' }
      ]
    }
  })
</script>

v-for 指令遍历对象数据

html
<div class="demo">
  <p v-for="(value,key,index) in obj" :key="value">
    {{value + '----' + key +'---'+ index}}
  </p>
</div>

<script>
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    data: {
      obj: {
        id: 1,
        name: 'zs',
        age: 45
      }
    }
  })
</script>

v-cloak 指令

html
<style>
  [v-cloak] {
    display: none;
  }
</style>
<div class="demo">
  <!-- 用于解决闪动问题(浏览器刚显示时会显示 {{isshow}} 然后快速的显示为值 -->
  <!-- 原理是通过先隐藏内容,然后再在内存中进行值的替换,替换好之后再显示最终的结果 -->
  <p v-cloak>{{isshow}}</p>
</div>

<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    data: {
      isshow: true
    }
  })
</script>

文本相关指令

html
<div class="demo">
  <!-- 无闪动问题 -->
  <p v-text="isshow"></p>
  <!-- 可以填充HTML片段,但有安全问题 -->
  <p v-html="html"></p>
  <p v-pre>{{html}}</p>
</div>

<script>
  Vue.config.productionTip = false
  let vm = new Vue({
    el: '.demo',
    data: {
      isshow: true,
      html: '<h1>vue</h1>'
    }
  })
</script>

v-once 指令

html
<div class="demo">
  <!-- 显示的信息不再具备响应式,不会根据data数据修改而改变 -->
  <p v-once>{{isshow}}</p>
</div>

<script>
  Vue.config.productionTip = false
  let vm = new Vue({
    el: '.demo',
    data: {
      isshow: true
    }
  })
</script>

列表渲染(案例)

html
<div class="demo">
  <input type="text" v-model="searchName" />
  <ul>
    <li v-for="(p,index) in filterPersons" :key="p.id">
      {{p.id}}---{{p.name}}---{{p.age}}
    </li>
  </ul>
  <button @click="sortType=1">按年龄升序</button>
  <button @click="sortType=2">按年龄降序</button>
  <button @click="sortType=3">原本顺序</button>
</div>

<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    data: {
      searchName: '',
      sortType: 3, //定义排序规则,1为升序,2为降序,3为默认排序
      persons: [
        { id: 2, name: 'Tom', age: 15 },
        { id: 3, name: 'Jery', age: 17 },
        { id: 4, name: 'Tem', age: 157 },
        { id: 5, name: 'Temy', age: 7 },
        { id: 1, name: 'Ope', age: 20 }
      ]
    },
    computed: {
      //由于过滤后的数组是由用户输入的数据和原数据共同决定的,所以此处使用计算属性
      //过滤后的新数组
      //实现用户模糊查询数据且不区分大小写
      filterPersons() {
        //1.取出依赖数据
        const { searchName, persons, sortType } = this
        //2.计算产生一个新数据
        let arr = persons.filter((item, index) => {
          return (
            item.name.indexOf(searchName) !== -1 ||
            item.name.indexOf(searchName.toUpperCase()) !== -1
          )
        })
        //实现按年龄排序
        if (sortType !== 3) {
          if (sortType === 1) {
            arr.sort((p1, p2) => p1.age - p2.age)
          } else {
            arr.sort((p1, p2) => p2.age - p1.age)
          }
        }
        //3.返回新数组
        return arr
      }
    }
  })
</script>
  • 实现根据用户输入模糊查询
  • 按要求进行排序

表单域修饰符

html
<div class="demo">
  <!-- 能将输入的值转换为数字类型 -->
  <input type="text" v-model.number="age" />
  <!-- 能将输入的值去掉开始和结尾的空格 -->
  <input type="text" v-model.trim="msg" />
  <!-- 能将input事件切换为change事件,降低触发频率 -->
  <input type="text" v-model.lazy="my" />
  <p>{{my}}</p>
  <button @click="send">点击</button>
</div>
<script src="vue.js"></script>
<script>
  Vue.config.productionTip = false
  let vm = new Vue({
    el: '.demo',
    data: {
      age: '',
      msg: '',
      my: ''
    },
    methods: {
      send() {
        console.log(this.age + 20) //能将字符串转换为数字
        console.log(this.msg) //能将输入的值去掉开始和结尾的空格
      }
    }
  })
</script>

自定义指令

html
<div class="demo">
  <!-- 自定义一个能将数据变为大写后显示 -->
  <p v-upper-text="msg"></p>
  <!-- 自定义一个能将数据变为小写后显示 -->
  <p v-lower-text="msg"></p>
</div>

<script>
  // 定义全局指令upper-text
  // 需要传递两个参数,第一个表示指令属性所在的标签元素,第二个是一个包含指令相关数据的对象
  Vue.directive('upper-text', function (el, binding) {
    //binding.value是当前标签的文本值
    el.innerText = binding.value.toUpperCase()
  })
  const vm = new Vue({
    el: '.demo',
    data: {
      msg: 'CBA I Love This Game!'
    },
    //定义全局指令lower-text,只对当前vm管理的模板有效
    directives: {
      'lower-text'(el, binding) {
        //binding.value是当前标签的文本值
        el.innerText = binding.value.toLowerCase()
      }
    }
  })
</script>

传递参数

html
<div class="demo">
  <input type="text" v-color="msg" />
</div>

<script>
  Vue.config.productionTip = false
  Vue.directive('color', {
    bind: function (el, binding) {
      el.style.backgroundColor = binding.value.color
    }
  })
  let vm = new Vue({
    el: '.demo',
    data: {
      msg: {
        color: 'orange'
      }
    }
  })
</script>

bind 和 inserted 用法

  • bind 一开始就会执行,当被绑定的元素插入到 DOM 中时执行 inserted

  • 所以当设置样式的时候用 bind ,当和行为相关的则用 inserted

绑定事件监听

html
<div class="demo">
  <a v-bind:href="url">去B站</a>
  <!-- <button v-on:click="test">点击</button> -->
  <!-- 简捷写法 -->
  <button @click="test">点击</button>
</div>

<script>
  new Vue({
    el: '.demo',
    data: {
      url: 'https://t.bilibili.com'
    },
    methods: {
      test(e) {
        console.log(e.target.innerHTML)
      }
    }
  })
</script>
  • $event 使用
html
<div class="demo">
  <!-- 当需要传参还需要传递事件对象时,需要使用$event来传递事件对象 -->
  <button @click="test('wudi',$event)">1234</button>
</div>

<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    methods: {
      test(value, e) {
        //可以直接读取文本内容和传递过来的数据
        console.log(value + ',' + e.target.innerHTML)
      }
    }
  })
</script>

事件修饰符

html
<div class="demo">
  <!-- prevent阻止默认行为 -->
  <a href="https://www.baidu.com" @click.prevent="">去百度</a>
  <div
    style="width:200px;height: 200px;background-color: orange;"
    @click="test2"
  >
    <!-- stop阻止事件冒泡 -->
    <div
      style="width: 100px;height: 100px;background-color: red;"
      @click.stop="test3"
    ></div>
  </div>
  <!-- once只执行一次事件回调函数 -->
  <button @click.once="test4">点击</button>
  <!-- 按键修饰符 -->
  <input type="text" @keyup.13="test5" />
</div>

<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    methods: {
      test2() {
        console.log('外')
      },
      test3() {
        console.log('内')
      },
      test4() {
        console.log('once')
      },
      test5() {
        console.log('按下了回车键')
      }
    }
  })
</script>

采集表单数据

html
<div class="demo">
  性别: 女<input
    type="radio"
    id="nu"
    name="sex"
    v-model="user.person"
    value="nu"
  />
  男<input
    type="radio"
    id="nan"
    name="sex"
    v-model="user.person"
    value="nan"
  /><br />

  爱好:<input
    type="checkbox"
    name="PE"
    id="run"
    value="run"
    v-model="user.ai"
  />跑步
  <input
    type="checkbox"
    name="PE"
    id="read"
    value="read"
    v-model="user.ai"
  />阅读
  <input
    type="checkbox"
    name="PE"
    id="shop"
    value="shop"
    v-model="user.ai"
  />购物<br />

  城市:
  <select v-model="user.cityid">
    <option value="">未选择</option>
    <option :value="item.name" v-for="(item,index) in allcity" :key="item.id">
      {{item.name}}
    </option></select
  ><br />
  <textarea v-model="user.text"></textarea>
  <button @click="send">发送</button>
</div>

<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    data: {
      user: {
        //保存收集到的数据对象
        person: 'nu',
        ai: [],
        cityid: '',
        text: ''
      },
      allcity: [
        { id: 1, name: '武汉' },
        { id: 2, name: '黄冈' },
        { id: 3, name: '武昌' },
        { id: 4, name: '上海' }
      ]
    },
    methods: {
      send() {
        console.log(JSON.stringify(this.user))
      }
    }
  })
</script>

计算属性和侦听器

  • 如果要显示的数据可以根据现有的数据计算产生,此时可以使用计算属性
  • 侦听器一般用来处理异步(如 ajsx 请求)
html
<div class="demo">
  姓:<input type="text" placeholder="姓名1" v-model="name1" /><br />
  姓:<input type="text" placeholder="姓名2" v-model="name2" /><br />
  姓名1(单向):<input
    type="text"
    placeholder="姓名1"
    v-model="fullname"
  /><br />
  姓名2(单向):<input
    type="text"
    placeholder="姓名2"
    v-model="fullname3"
  /><br />
  姓名3(双向):<input
    type="text"
    placeholder="姓名3"
    v-model="fullname2"
  /><br />
</div>
<script src="vue.js"></script>
<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    data: {
      name1: 'A',
      name2: 'B',
      fullname3: 'A-B'
    },
    //计算属性
    //计算属性存在缓存,多次读取只执行一次getter计算
    //初始显示会执行一次,依赖数据发生变化会执行(这里的依赖数据是name1、name2)
    //vue控制的所有回调的this都是vue的实例
    computed: {
      fullname() {
        return this.name1 + '-' + this.name2
      },
      fullname2: {
        get() {
          return this.name1 + '-' + this.name2
        },
        //当给fullname2指定新的值时自动调用
        set(value) {
          //这里的value代表上面双向文本框新修改的值
          //更新name1/name2
          console.log(value)
          const names = value.split('-')
          this.name1 = names[0]
          this.name2 = names[1]
        }
      }
    }
    //侦听器
    // watch:{
    // 	name1(value){
    // 		//this.
    // 		this.fullname3 = value + '-' +this.name2
    // 	}
    // }
  })
  //watch的另一种用法
  vm.$watch('name1', function (value) {
    //更新fullname3
    this.fullname3 = this.name1 + '-' + value
  })
</script>

利用侦听器发送 Ajax 请求

  • 可以使用 lodash.js 完成函数防抖
html
<div class="demo">
  <input type="text" v-model.lazy="msg" /><span>{{msg2}}</span>
</div>

<script>
  Vue.config.productionTip = false
  let vm = new Vue({
    el: '.demo',
    data: {
      msg: '',
      msg2: ''
    },
    methods: {
      // 模拟发送Ajax请求
      change(username) {
        setTimeout(() => {
          if (username == 'admin') {
            this.msg2 = '用户名重复!'
          } else {
            this.msg2 = '用户名可用'
          }
        }, 1000)
      }
    },
    watch: {
      msg(val) {
        this.change(val)
        this.msg2 = '正在验证用户名是否可用....'
      }
    }
  })
</script>

class 和 style 绑定

html
<style>
    .demo2{
        width: 200px;height: 200px;
        background-color: #000000;
    }
    .demo3{
        width: 200px;height: 200px;
        background-color: #0000FF;
    }
</style>
</head>
<body>
    <div class="demo">
        <!-- 动态添加样式 -->
        <div :class="myclass"></div>
        <!-- 另一种用法 -->
        <div :class="{demo2:hasA,demo3:hasB}"></div>
        <button @click="change">点击</button>
        <p :style="mystyle">无敌是多么寂寞</p>
    </div>
    <script>
        //全局配置
        Vue.config.productionTip = false //关闭vue开发模式的控制台提示
        let vm = new Vue({
            el:'.demo',
            data:{
                myclass:'demo2',
                hasA:true,
                hasB:false,
                mystyle:'font-weight: 700;color: orangered;'
            },
            methods:{
                change(){
                    this.myclass = 'demo3'
                    this.hasA = !this.hasA
                    this.hasB = !this.hasB
                    this.mystyle = 'font-weight: 700;color: #0000FF;'
                }
            }
        })
    </script>
</body>

vue 双向数据绑定实现原理

html
<div class="demo">
  <input type="text" id="Input" />
  <p id="op"></p>
</div>
<script>
  const data = {
    // 一般会用下划线约定表示私有属性
    _msg: 'hello'
  }
  //这个方法也能用来设置对象属性,第一个参数对象、属性名、配置规则
  Object.defineProperty(data, 'msg', {
    //当访问 data.msg 会自动调用get 函数
    get() {
      return this._msg
    },
    set(value) {
      //这里的value就是修改后的值
      this._msg = value //同步修改后的数据
      document.querySelector('#Input').value = value
      document.querySelector('#op').innerHTML = value

      //可简写为
      // op.innerHTML = value//使输入框同步数据
      // Input.value = value//使 p 标签同步数据
    }
  })
  document.querySelector('#Input').oninput = function () {
    data.msg = this.value
  }
  //可简写为
  //Input.oninput=function(){
  //data.msg = this.value;
  // }
</script>

vue 生命周期

mounted 使用

html
<div class="demo">
  <p v-show="isshow">123456</p>
</div>

<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    data: {
      isshow: true
    },
    //界面初始显示之后立即回调,一般在此执行异步操作
    //每隔一秒会将 p 元素显示隐藏
    mounted() {
      setInterval(() => {
        this.isshow = !this.isshow
      }, 1000)
    }
  })
</script>

$destroy()

  • 完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。
html
<div class="demo">
  <button @click="cl">清除mounted</button>
  <p v-show="isshow">123456</p>
</div>

<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    data: {
      isshow: true
    },
    //在vm死亡之前调用,在此做一些收尾工作
    beforeDestroy() {
      // 从vm获取定时器ID
      clearInterval(this.times)
    },
    methods: {
      cl() {
        this.$destroy()
      }
    },
    //界面初始显示之后立即回调,一般在此执行异步操作
    //每隔一秒会将 p 元素显示隐藏
    mounted() {
      // 在vm挂载定时器ID
      this.times = setInterval(() => {
        this.isshow = !this.isshow
      }, 1000)
    }
  })
</script>

生命周期方法

  • 挂载阶段: beforeCreate() 、created()、beforeMount()、mounted()

  • 更新阶段:beforeUpdate()、updated()

  • 销毁阶段 :vm.$destory() beforeDestroy()、destroyed()

  • 常用的生命周期方法

  • mounted():发 ajax 请求,启动定时器等异步任务

  • beforeDestroy():做收尾工作,如:清除定时器

html
<div class="demo">
  <p v-show="isshow">123456</p>
</div>
<script src="vue.js"></script>
<script>
  //全局配置
  Vue.config.productionTip = false //关闭vue开发模式的控制台提示
  let vm = new Vue({
    el: '.demo',
    data: {
      isshow: true
    },
    //挂载阶段
    //此阶段不能通过vm读取data中的数据
    beforeCreate() {
      //创建实例之前
      console.log('beforeCreate')
    },
    //在此阶段实现数据代理/data数据的监视
    created() {
      //此阶段可以读取data中的数据,创建实例完毕
      console.log('created')
    },
    //此阶段不能通过ref读取页面标签对象
    beforeMount() {
      //数据渲染前
      console.log('beforeMount')
    },
    /*
		界面初始显示之后立即回调
		一般在此执行异步操作:发ajax请求/启动定时器/订阅消息/绑定自定义事件监听
		显示/渲染页面
		*/
    mounted() {
      //此阶段可以通过ref读取页面标签对象,数据渲染完毕
      console.log('mounted')
    },
    beforeUpdate() {
      //更新数据前
      console.log('beforeUpdate')
    },
    //更新阶段
    updated() {
      //更新数据后
      console.log('updated')
    },
    //销毁阶段
    beforeDestroy() {
      //销毁前
      console.log('beforeDestroy')
    },
    destroyed() {
      //销毁后
      console.log('destroyed')
    }
  })
</script>

过渡和动画

过渡

html
<style>
    /* 指定显示时过渡的样式 */
    .fade-enter-active{
        transition: 3s;
    }
    /* 指定隐藏时过渡的样式 */
    .fade-leave-active{
        transition:1s;
    }
    /* 指定隐藏显示的样式 */
    .fade-enter, .fade-leave-to{
        transform: translateX(20px);
        opacity: 0;
    }
</style>
</head>
<body>
    <div id="demo">
        <button v-on:click="show = !show">
            Toggle
        </button>
        <transition name="fade">
            <p v-show="show">hello</p>
        </transition>
    </div>

    <script>
        new Vue({
            el: '#demo',
            data: {
                show: true
            }
        })
    </script>

动画

html
<style>
    /* 显示动画的过程 */
    .bounce-enter-active {
        animation: bounce-in .5s;
    }
    /* 隐藏动画的过程 */
    .bounce-leave-active {
        animation: bounce-in .5s reverse;
    }
    @keyframes bounce-in {
        0% {
            transform: scale(0);
        }
        50% {
            transform: scale(1.5);
        }
        100% {
            transform: scale(1);
        }
    }
</style>
</head>
<body>
    <div id="example-2">
        <button @click="show = !show">Toggle show</button><br>
        <transition name="bounce">
            <p v-if="show" style='display: inline-block;'>Lorem </p>
        </transition>
    </div>

    <script>
        new Vue({
            el: '#example-2',
            data: {
                show: true
            }
        })
    </script>

过滤器

  • 对要显示的数据进行特定格式化后再显示

  • 注意:并没有改变原本的数据,可是产生新的对应的数据

html
<!-- 实现对当前时间进行指定格式显示 -->
<div class="demo">
  <h2>显示格式化的日期时间</h2>
  <p>开始时间{{startTime}}</p>
  <!-- <p>过滤后的时间:{{startTime|initTime}}</p> -->
  <!-- 传递多个参数 -->
  <p>过滤后的时间:{{startTime|initTime(456,'op')}}</p>
</div>

<script>
  Vue.filter('initTime', function (date, a, b) {
    const time = new Date(date)
    let mytime =
      time.toLocaleDateString().split('/').join('-') +
      '  ' +
      time.toLocaleTimeString().slice(2)
    console.log(a, b) //456 "op"
    return mytime
  })

  const vm = new Vue({
    el: '.demo',
    data: {
      startTime: Date.now()
    }
  })
</script>

图书管理(案例)

html
<div class="demo">
  序号:<input type="text" v-model.number.trim="order" :disabled="flag" />
  书名:<input type="text" v-model.trim="bookName" />
  <button @click="add()" :disabled="addFlag">添加</button>
  <table>
    <tr>
      <th>序号</th>
      <th>书名</th>
      <th>时间</th>
      <th>操作</th>
    </tr>
    <tr v-for="(item,i) in info" :key="item.id">
      <td>{{item.id}}</td>
      <td>{{item.name}}</td>
      <td>{{item.time}}</td>
      <td>
        <a href="javascript:;" @click="add(i)">修改</a><span>|</span
        ><a href="javascript:;" @click="del(i)">删除</a>
      </td>
    </tr>
  </table>
</div>
<script src="vue.js"></script>
<script>
  Vue.config.productionTip = false
  let vm = new Vue({
    el: '.demo',
    data: {
      info: [
        { id: 1, name: '星球日记', time: '18:40:22' },
        { id: 2, name: '鬼吹灯', time: '18:40:22' },
        { id: 3, name: '斗破苍穹', time: '18:40:22' },
        { id: 4, name: '盗墓笔记', time: '18:40:22' }
      ],
      order: '',
      bookName: '',
      flag: false,
      addFlag: false
    },
    methods: {
      add(index) {
        if (index !== undefined) {
          this.flag = true
          this.order = this.info[index].id
          this.bookName = this.info[index].name
        } else {
          if ((this.flag = true)) {
            this.info.splice(this.order - 1, 1, {
              id: this.order,
              name: this.bookName,
              time: new Date().toLocaleTimeString()
            })
            this.order = ''
            this.bookName = ''
            this.flag = false
          } else {
            this.info.push({
              id: this.order,
              name: this.bookName,
              time: new Date().toLocaleTimeString()
            })
            this.order = ''
            this.bookName = ''
          }
        }
      },
      del(index) {
        console.log(index)
        this.info.splice(index, 1)
      }
    },
    watch: {
      bookName(val) {
        const flagName = this.info.some(item => val === item.name)
        if (flagName) {
          this.addFlag = true
        } else {
          this.addFlag = false
        }
      }
    },
    computed: {
      addFlag() {
        return (this.addFlag = isId && isname)
      }
    }
  })
</script>

组件

基础用法

html
<div id="components-demo">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

<script>
  // 定义一个名为 button-counter 的新组件
  Vue.component('button-counter', {
    //配置对象
    //使用函数的目的是每个组件实例数据互不干扰
    data: function () {
      //必须是函数
      return {
        count: 0
      }
    },
    template: '<button v-on:click="count++">你点击了{{count}}次</button>'
  })

  new Vue({ el: '#components-demo' })
</script>

用 ES6 模板字符串定义模板

html
<div class="demo">
  <mycorr></mycorr>
  <mycorr></mycorr>
  <mycorr></mycorr>
</div>

<script>
  Vue.component('mycorr', {
    data: function () {
      return {
        count: 0
      }
    },
    //这里使用模板字符串定义组件使结构更加清晰
    template: `
            <div>
            <button @click="handle">点击了{{count}}次</button>
            <button>测试</button>
                </div>
            `,
    methods: {
      handle: function () {
        this.count += 2
      }
    }
  })
  new Vue({ el: '.demo' })
</script>

组件套用

  • 如果组件 A 里使用了另一个组件 B,那么 B 组件就称为 A 组件的父组件
html
<div id="demo">
  <button-counter></button-counter>
  <blog-post
    v-for="(post,index) in posts"
    :key="post.id"
    :title="post.title"
  ></blog-post>
</div>

<script>
  // 定义一个名为 button-counter 的新组件
  Vue.component('button-counter', {
    //配置对象
    data: function () {
      //必须是函数
      return {
        count: 0
      }
    },
    template: '<button v-on:click="count++">你点击了{{count}}次</button>'
  })

  //定义文章的组件
  Vue.component('blog-post', {
    props: ['title'], //声明接收属性
    // 组件可以混用,但使用时必须只有一个根标签,如将两个或多个组件模板用一个div包裹起来
    template:
      '<div><h3>{{ title }}</h3> <button-counter></button-counter> </div>'
  })

  new Vue({
    el: '#demo',
    data: {
      posts: [
        { id: 1, title: '博客1' },
        { id: 2, title: '博客2' },
        { id: 3, title: '博客3' }
      ]
    }
  })
</script>

组件命名细节

  • 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,但是在普通的标签模板中,必须使用短横线的方式使用组件
html
<div class="demo">
  <mycorr></mycorr>
  <!-- 此时不能用驼峰式 -->
  <!-- <HellWorld></HellWorld> -->
  <!-- 必须用短横线的方式才能正常显示 -->
  <hell-world></hell-world>
</div>

<script>
  // 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,但是在普通的标签模板中,必须使用短横线的方式使用组件
  Vue.component('HellWorld', {
    data: function () {
      return {
        msg: 'hellworld'
      }
    },
    template: '<div>{{msg}}</div>'
  })

  Vue.component('mycorr', {
    data: function () {
      return {
        count: 0
      }
    },
    //
    template: `
            <div>
            <button @click="handle">点击了{{count}}次</button>
            <button>测试</button>
            <HellWorld></HellWorld>
                </div>
            `,
    methods: {
      handle: function () {
        this.count += 2
      }
    }
  })
  new Vue({ el: '.demo' })
</script>

注册全局(局部)组件

  • 在项目中注册全局组件(component 方式)
  • 使用 use 方式
  • 封装需要全局注册的组件
  • 先在项目公共组件 components 下创建 index.js 文件并引入需要注册的组件
  • 然后再 main.js 中注册
  • 局部组件只能在注册它的父组件中使用
html
<div class="demo">
  <hello1></hello1>
  <mycode111></mycode111>
  <!-- 局部组件只能在注册它的父组件中使用 -->
  <!-- <test></test> -->
</div>

<script>
  Vue.component('test', {
    template: '<div>test<hello1></hello1></div>'
  })
  //定义局部组件
  let hello = {
    data: function () {
      return {
        msg: 'hello'
      }
    },
    //局部组件只能在注册它的父组件中使用
    template: '<div>{{msg}}</div>'
  }
  let mycode = {
    data: function () {
      return {
        msg: 'mycode'
      }
    },
    template: '<div>{{msg}}</div>'
  }

  const vm = new Vue({
    el: '.demo',
    data: {},
    //定义局部组件
    components: {
      hello1: hello,
      mycode111: mycode
    }
  })
</script>

父组件向子组件传递数据

  • prop 数据是受父组件数据影响的
  • ==如果是基础数据类型(字符串、数字等)绝对不能修改,即使修改了也不会传导给父组件(父组件数据不会发生改变)==
  • ==如果是复杂数据类型(对象、数组)可以修改,如 [].push {}.xxx=xxx 这种方式能传导给父组件(父组件数据也会发生改变),但不能进行赋值操作 如 [] = [1,2,3]==
html
<div class="demo">
  <div>{{pmsg}}</div>
  <!-- 绑定静态的值 -->
  <mymenu title="来自父组件的值"></mymenu>
  <!-- 绑定动态的值,可以传递多个值 -->
  <mymenu :title="ptitle" :title2="ptitle2"></mymenu>
</div>

<script>
  Vue.component('mymenu', {
    //这里是父组件传递过来的值 props 是固定写法,如果传递多个值,这里需要定义多个值接收,名字也要一一对应
    props: ['title', 'title2'],
    data: function () {
      return {
        msg: '子组件本身的数据'
      }
    },
    template: '<div>{{msg+"----"+title+"---"+title2}}</div>'
  })

  const vm = new Vue({
    el: '.demo',
    data: {
      pmsg: '父组件的内容',
      ptitle: '动态绑定的属性',
      ptitle2: '第二个动态绑定的属性'
    }
  })
</script>

父子组件传递数据一些细节

  • 我们可以通过对象形式来规定子组件接收的数据类型
  • 可以规定默认值
  • 也可以自定义校验规则
  • 接收非 prop 属性,父组件传递的没有被子组件接收的数据会出现在当前组件的根节点上
  • 如果不想出现这种结果,可以通过 inheritAttrs 禁止继承
  • ==class 和 style 作为 props 的保留属性,不能被 props 接收,也不能通过 inheritAttrs 禁用,就是会作用于组件的根节点==
  • 可以通过 $listeners 拿到父组件的所有方法,从而修改父组件的数据,实现子传父
  • $parent 能获取父组件的实例,通过实例拿到父组件的所有属性和方法
  • $children 能获取子组件的实例,通过实例拿到子组件的所有属性和方法(获取到的是一个包含子组件的一个数组)
  • 通过自定义事件进行子传父

ref 传值

祖先传值

  • 无论层级结构多深,祖先组件都可以作为其后代组件的依赖(数据)提供者,祖先组件通过 provide 提供数据,后代组件通过 inject 接收数据

  • 后代组件写法

  • 祖先组件写法
  • 如果想将祖先组件的 data 里的数据传递给后代组件 provide 必须为一个函数

使用频率

propt 接收的数据类型

html
<div class="demo">
  <!-- :pnum 加冒号那么子组件接收到的就是一个数值 -->
  <!-- :pnum 如果不加冒号那么子组件接收到的就是一个字符串 -->
  <!-- :pboo 加冒号那么子组件接收到的就是一个布尔值 -->
  <!-- :pboo 如果不加冒号那么子组件接收到的就是一个字符串 -->
  <mymenu
    :title="ptitle"
    :pnum="12"
    :pboo="false"
    :parr="parr"
    :pobj="pobj"
  ></mymenu>
</div>
<script src="vue.js"></script>
<script>
  Vue.component('mymenu', {
    props: ['title', 'pnum', 'pboo', 'parr', 'pobj'],
    template: `
            <div>
            <div>{{title}}</div>
            <div>{{pnum + 12}}</div>
            <div>{{pboo}}</div>
            <ul>
            <li :key='index' v-for="(item,index) in parr">{{item}}</li>
                </ul>
            <span>{{pobj.name}}</span><span>{{pobj.age}}</span><span>{{pobj.sex}}</span>
                </div>
            `
  })

  const vm = new Vue({
    el: '.demo',
    data: {
      ptitle: '动态绑定的属性',
      parr: ['apple', 'orange', 'banan'],
      pobj: {
        name: 'zs',
        age: 45,
        sex: '男'
      }
    }
  })
</script>

子组件向父组件传递信息

html
<div class="demo">
  <p :style="{fontSize:fontsize+'px'}">{{msg}}</p>
  <!-- 父组件需要通过@ 监听自定义事件 -->
  <!-- 通过 $event 接收子组件传递的数据 -->
  <mymenu :parr="parr" @enlargetext="hander($event)"></mymenu>
</div>
<script src="vue.js"></script>
<script>
  Vue.component('mymenu', {
    props: ['parr'],
    //子组件需要通过 $emit 自定义事件
    template: `
            <div>
                <ul>
                <li :key='index' v-for="(item,index) in parr">{{item}}</li>
                    </ul>
                <button @click="parr.push('lemon')">添加</button>
                <button @click="$emit('enlargetext',5)">扩大父组件文字</button>
            </div>
`
  })

  const vm = new Vue({
    el: '.demo',
    data: {
      msg: '父组件文字',
      parr: ['apple', 'orange', 'banan'],
      fontsize: 10
    },
    methods: {
      //这里可以通过形参接收到上面 $event 也就是子组件传递过来的值
      hander: function (val) {
        //扩大字体大小
        this.fontsize += val
      }
    }
  })
</script>
  • 子组件向父组件传递多个信息
html
<div class="demo">
  <p :style="{fontSize:fontsize+'px'}">{{msg}}</p>
  <!-- 如果父组件回调 hander 不加括号,那么可以快捷接收多个数据 -->
  <mymenu :parr="parr" @enlargetext="hander"></mymenu>
</div>

<script>
  Vue.component('mymenu', {
    props: ['parr'],
    template: `
			<button @click="$emit('enlargetext',5,'hjshdj',false)">扩大父组件文字</button>
		`
  })

  const vm = new Vue({
    el: '.demo',
    data: {
      msg: '父组件文字',
      parr: ['apple', 'orange', 'banan'],
      fontsize: 10
    },
    methods: {
      //这里直接定义多个形参接收子组件传递的数据
      hander: function (val, msg, flag) {
        //扩大字体大小
        this.fontsize += val
        console.log(msg)
        console.log(flag)
      }
    }
  })
</script>
  • 子组件用 $attrs 向父子间传值

在子组件定义

vue
<template>
  <div>
    {{ $attrs.pa }}
  </div>
</template>

<script>
// $attrs 里会存放从父子间传递过来且没有在子组件 porps 中定义的值
export default {
  props: {
    bb: {
      type: String,
      default: ''
    }
  },
  created() {
    console.log(this.$attrs)
  }
}
</script>

父组件

vue
<template>
  <div id="app">
    <Father pa="xxx" bb="ddd" aa="jjj"></Father>
    <Er></Er>
  </div>
</template>

兄弟组件之间的数据通信

html
<div class="demo">
  <div>父组件</div>
  <button @click="clear">清除</button>
  <son></son>
  <son2></son2>
</div>

<script>
  //提供事件中心,兄弟组件之间需要通过它来实现数据通信
  const hub = new Vue()

  Vue.component('son', {
    data: function () {
      return {
        num: 0
      }
    },
    template: `
            <div>
                <div>TOM:{{num}}</div>
                <button @click='handle'>点击</button>
            </div>
            `,
    methods: {
      handle: function () {
        //借助事件中心和$emit触发兄弟组件事件
        hub.$emit('son2event', 1)
      }
    },
    mounted: function () {
      //监听事件
      hub.$on('sonevent', val => {
        this.num += val
      })
    }
  })

  Vue.component('son2', {
    data: function () {
      return {
        num: 0
      }
    },
    template: `
            <div>
                <div>jerry:{{num}}</div>
                <button @click='handle'>点击</button>
            </div>
			`,
    methods: {
      handle: function () {
        //借助事件中心和$emit触发兄弟组件事件
        hub.$emit('sonevent', 2)
      }
    },
    mounted: function () {
      //监听事件
      hub.$on('son2event', val => {
        this.num += val
      })
    }
  })

  const vm = new Vue({
    el: '.demo',
    methods: {
      clear() {
        //清除组件事件
        hub.$off('son2event')
        hub.$off('sonevent')
      }
    }
  })
</script>

兄弟组件一些细节

  • vue3 中不支持这种写法,作者推荐使用第三方库来实现事件中心功能

匿名插槽

  • 可以实现父组件向子组件传递模板内容
html
<div class="demo">
  <!-- 当模板当中有值时会显示值 -->
  <alert>有bug发生</alert>
  <alert>有一个警告</alert>
  <!-- 如果模板中没有值的时候会显示默认写入的值 -->
  <alert></alert>
</div>

<script>
  //组件插槽
  Vue.component('alert', {
    template: `
            <div>
                <strong>ERROR:</strong>
                <slot>默认内容</slot>
            </div>
       `
  })

  const vm = new Vue({
    el: '.demo'
  })
</script>

具名插槽

html
<div class="demo">
  <alert>
    <p slot="header">标题信息</p>
    <p>主体信息</p>
    <p>主体信息2</p>
    <p slot="footer">底部信息</p>
  </alert>

  <p>-------另一种用法-------</p>

  <alert>
    <!-- 这种写法可以将多条数据插入插槽当中 -->
    <template slot="header">
      <p>标题信息</p>
    </template>

    <template>
      <p>主体信息</p>
      <p>主体信息2</p>
    </template>

    <template slot="footer">
      <p>底部信息</p>
    </template>
  </alert>
</div>
<script src="vue.js"></script>
<script>
  //组件插槽
  Vue.component('alert', {
    template: `
            <div>
                <header>
                <slot name='header'></slot>
                </header>
                <main>
                    <slot></slot>
                </main>
                <footer>
                    <slot name='footer'></slot>
                </footer>
            </div>
        `
  })

  const vm = new Vue({
    el: '.demo'
  })
</script>

作用域插槽

  • 可以在父组件中拿到子组件中的数据,并且进行加工处理
html
<div class="demo">
  <alert :list="list">
    <!-- 父组件对子组件的内容进行加工处理 -->
    <!-- 父组件通过 slot-scope='solt2' 接收子组件传递过来的数据 -->
    <!-- template不会渲染出来,可以用来临时包裹标签-->
    <template slot-scope="solt2">
      <strong class="current" v-if="solt2.info.id==2"
        >{{solt2.info.name}}</strong
      >
      <span v-else>{{solt2.info.name}}</span>
    </template>
  </alert>
</div>

<script>
  //组件插槽
  Vue.component('alert', {
    props: ['list'],
    // 在子组件定义插槽,通过 :info='item' 向父组件传递数据
    template: `
            <div>
                <li v-for="(item,index) in list" :key='item.id'>
                <slot :info='item'>{{item.name}}</slot>
                </li>
            </div>
            `
  })

  const vm = new Vue({
    el: '.demo',
    data: {
      list: [
        { id: 1, name: 'apple' },
        { id: 2, name: 'orange' },
        { id: 3, name: 'banana' }
      ]
    }
  })
</script>

购物车综合(案例)

html
<style>
    *{
        margin: 0;padding: 0;
    }
    .demo{
        width: 300px;height: 350px;
        margin: 50px auto;
    }
    .herder{
        height: 50px;
        line-height: 50px;
        text-align: center;
        background-color: cornflowerblue;
    }
    .body{
        height: 250px;
    }
    li{
        height: 50px;
        list-style: none;
        line-height: 50px;
        border-bottom: #007AFF solid 1px;
        text-align: center;
    }
    .foot{
        height: 50px;
        background-color: orange;
        line-height: 50px;
        text-align: center;
    }
    a{
        text-decoration: none;
        font-size: 20px;
        color: #000000;
        margin: 0 10px;
    }
    input{
        width: 50px;
        text-align: center;
    }
    button{
        padding: 5px 10px;
        margin-left: 20px;
    }
</style>
</head>
<body>
    <div class="demo">
        <mycart></mycart>
    </div>

    <script>
        const cartitle = {
            //标题组件
            props:['uname'],
            template:`
				<div class="herder"><h1>{{uname}}的商品</h1></div>
			`,
        }
        const cartlist = {
            // 主体内容组件
            props:['list'],
            template:`
                <ul>
                    <li v-for="(item,index) in list" :key='item.id'>
                    <span>{{item.name}}</span><a href="javascript:;" @click='$emit("numplus",index)'>+</a><input type="text" @blur="$emit('change',index,$event)" :value='item.num'><a href="javascript:;"@click='$emit("numsub",index)'>-</a><a href="javascript:;" @click='$emit("del",index)'>删除</a>
                    </li>
                </ul>
			`,
        }
        const carttotal = {
            props:['list'],
            // 底部结算组件
            template:`
                <div class="foot">
                	<span>总价{{total}}</span><button>结算</button>
                </div>
			`,
            computed:{
                total:function(){
                    //计算商品总价
                    let t = 0
                    this.list.forEach((item)=>t += item.price * item.num)
                    return t
                }
            }
        }
        Vue.component('mycart',{
            //定义父组件
            data:function(){
                return {
                    uname:'张三',
                    list:[
                        {id:1,name:'TCL',price:1000,num:0},
                        {id:2,name:'haier',price:2000,num:0},
                        {id:3,name:'小米',price:3000,num:0},
                        {id:4,name:'华为',price:4000,num:0},
                        {id:5,name:'PPTV',price:5000,num:0},
                    ]
                }
            },
            // 监听自定义函数接收子组件传递的数据
            template:`
                <div>
                    <cartitle :uname='uname'></cartitle>
                    <cartlist :list='list' @del='del' @change='change'@numplus='numplus' @numsub='numsub'></cartlist>
                    <carttotal :list='list'></carttotal>
                </div>
                `,
            components:{
                //定义三个局部子组件
                'cartitle':cartitle,
                'cartlist':cartlist,
                'carttotal':carttotal
            },
            methods:{
                //删除数据
                del(i){
                    this.list.splice(i,1)
                },
                //修改数据
                //根据事件对象读取用户在文本框输入的值和索引修改商品数量
                change(i,e){
                    console.log('第'+i +'个,值:'+e.target.value);
                    this.list[i].num = parseInt(e.target.value)
                },
                //累加1
                //用户点击+号将当前商品数量加1
                numplus(i){
                    this.list[i].num +=1
                },
                //累减1
                //用户点击-号将当前商品数量减1
                numsub(i){
                    //阻止当商品数量为零时继续减1
                    if(this.list[i].num){
                        console.log('减法'+i+'----'+this.list[i].num);
                        this.list[i].num -=1
                    }
                }
            }
        })
        const vm = new Vue({el:'.demo'})
    </script>

案例细节

  • 在计算总价时可以使用 reduce 方法快速计算和
js
// 计算数组元素相加后的总和
const arr11 = [
  { name: 'zs', num: 45 },
  { name: 'zs', num: 45 },
  { name: 'zs', num: 45 }
]
/*acc=>累计值
cur => 当前项
curindex => 当前项的索引
arr11 =>数组本身
最后的 0 表示 累计值(acc)的默认值*/
let sum = arr11.reduce((acc, cur, curindex, arr11) => acc + cur.num, 0)
console.log(sum) //135

vue 路由

路由传参

query 传参

params 传参

监听 hash 变化事件

vue 内置组件

vue 路由基本使用

html
<div class="demo">
  <router-link to="/user">user</router-link>
  <router-link to="/login">login</router-link>

  <router-view></router-view>
</div>

<script>
  const user = {
    template: '<h1>user组件</h1>'
  }
  const login = {
    template: '<h1>login组件</h1>'
  }

  const router = new VueRouter({
    routes: [
      { path: '/user', component: user },
      { path: '/login', component: login }
    ]
  })

  let vm = new Vue({
    el: '.demo',
    router
  })
</script>

重定向和路由嵌套

html
<div class="demo">
  <router-link to="/user">user</router-link>
  <router-link to="/login">login</router-link>

  <router-view></router-view>
</div>

<script>
  const user = {
    // 子路由嵌套
    template: `
            <div>
            <h1>user组件</h1>
            <router-link to="/login/tab1">tab1</router-link>
            <router-link to="/login/tab2">tab2</router-link>

            <router-view></router-view>
                </div>
            `
  }
  const login = {
    template: '<h1>login组件</h1>'
  }

  const Tab1 = {
    template: '<h3>tab1 子组件</h3>'
  }

  const Tab2 = {
    template: '<h3>tab2 子组件</h3>'
  }

  const router = new VueRouter({
    routes: [
      //路由重定向,path 表示需要被重定向的原地址,redirect 表示要被重定向到的新地址
      //这样页面就能默认显示 user 组件了
      { path: '/', redirect: '/user' },
      // children 数组表示子路由规则
      {
        path: '/user',
        component: user,
        children: [
          { path: '/login/tab1', component: Tab1 },
          { path: '/login/tab2', component: Tab2 }
        ]
      },
      { path: '/login', component: login }
    ]
  })
  let vm = new Vue({
    el: '.demo',
    // 挂载router
    router
  })
</script>

动态路由匹配与参数接收

html
<div class="demo">
  <router-link to="/user">user</router-link>
  <router-link to="/user1">user</router-link>
  <router-link to="/user2">user</router-link>
  <router-link to="/user3">user</router-link>
  <router-link to="/login">login</router-link>

  <router-view></router-view>
</div>

<script>
  const user = {
      //路由组件中通过 $route.params.id 获取动态路由的参数
      template:`<h1>user组件{{$route.params.id}}</h1>`
      //在 created 钩子函数中可以通过 this.$route.params.id 来获取动态路由的参数
      created(){
          console.log(this.$route.params.id);
      }
  }
  const login = {
      template:'<h1>login组件</h1>'
  }

  const router = new VueRouter({
      routes:[
          {path:'/',redirect:'/user'},
          //动态路径参数,以冒号开头
          {path:'/user:id',component:user},
          {path:'/login',component:login}
      ]
  })
  let vm = new Vue({
      el:'.demo',
      // 挂载router
      router
  })
</script>

通过 props 来接收参数

html
<div class="demo">
  <router-link to="/user">user</router-link>
  <router-link to="/user1">user</router-link>
  <router-link to="/user2">user</router-link>
  <router-link to="/user3">user</router-link>
  <router-link to="/login">login</router-link>

  <router-view></router-view>
</div>

<script>
  const user = {
    //通过 props 接收传递过来的路由参数
    props: ['id'],
    template: `<h1>user组件{{id}}</h1>`
  }
  const login = {
    template: '<h1>login组件</h1>'
  }

  const router = new VueRouter({
    routes: [
      { path: '/', redirect: '/user' },
      //开启 props 接收
      { path: '/user:id', component: user, props: true },
      //如果 props 是一个对象,它会被按原样设置为组件属性,需要在子组件里的 props 添加对应名称来接收
      // {path:'/user:id',component:user,props:{name:'zs',age:45}},
      //如果既想传递路由参数邮箱传递 id 就可以使用函数传递,需要在子组件里的 props 添加对应名称来接收
      // {path:'/user:id',component:user,props:route =>({name:'zs',age:45,id:route.params.id})},
      { path: '/login', component: login }
    ]
  })
  let vm = new Vue({
    el: '.demo',
    // 挂载router
    router
  })
</script>

命名路由

html
<div class="demo">
  <router-link to="/user">user</router-link>
  <router-link to="/user1">user</router-link>
  <router-link :to="{ name: 'myuser', params: {id: 3} }">user5</router-link>
  <router-link to="/user3">user</router-link>
  <router-link to="/login">login</router-link>

  <router-view></router-view>
</div>

<script>
  const user = {
    props: ['id'],
    template: `<h1>user组件{{id}}</h1>`
  }
  const login = {
    template: '<h1>login组件</h1>'
  }

  const router = new VueRouter({
    routes: [
      { path: '/', redirect: '/user' },
      {
        // 命名路由
        name: 'myuser',
        path: '/user:id',
        component: user,
        props: true
      },
      { path: '/login', component: login }
    ]
  })
  let vm = new Vue({
    el: '.demo',
    router
  })
</script>

编程式导航

  • 能通过代码跳转
html
<div class="demo">
  <router-link to="/user">user</router-link>
  <router-link to="/user1">user</router-link>
  <router-link to="/user2">user</router-link>
  <router-link to="/user3">user</router-link>
  <router-link to="/login">login</router-link>

  <router-view></router-view>
</div>

<script>
  const user = {
    props: ['id'],
    template: `
            <div>
            <h1>user组件{{id}}</h1>
            <button @click="goRegister">跳转到登录页面</button>
                </div>
            `,
    methods: {
      goRegister() {
        //用编程的方式控制路由跳转
        this.$router.push('/login')
      }
    }
  }
  const login = {
    template: '<h1>login组件</h1>'
  }

  const router = new VueRouter({
    routes: [
      { path: '/', redirect: '/user' },
      { path: '/user:id', component: user, props: true },
      { path: '/login', component: login }
    ]
  })
  let vm = new Vue({
    el: '.demo',
    // 挂载router
    router
  })
</script>

编程式导航传参

html
<div class="demo">
  <router-link to="/user">user</router-link>
  <router-link to="/user1">user</router-link>
  <router-link to="/user2">user</router-link>
  <router-link to="/user3">user</router-link>
  <router-link to="/login">login</router-link>

  <router-view></router-view>
</div>

<script>
  const user = {
    //通过 props 接收传递过来的路由参数
    props: ['id'],
    template: `
<div>
<h1>user组件{{id}}</h1>
<button @click="goRegister">跳转到登录页面</button>
    </div>
`,
    methods: {
      goRegister() {
        //用编程的方式控制路由跳转
        //this.$router.push('/login?uname = zs123')
        //还可以用对象传参
        this.$router.push({
          path: '/login',
          //编程式导航传参,通过查询参数进行
          query: {
            uname: 'zs123'
          }
        })
      }
    }
  }
  const login = {
    template: '<h1>login组件{{$route.query.uname}}</h1>'
  }

  const router = new VueRouter({
    routes: [
      { path: '/', redirect: '/user' },
      { path: '/user:id', component: user, props: true },
      { path: '/login', component: login }
    ]
  })
  let vm = new Vue({
    el: '.demo',
    // 挂载router
    router
  })
</script>

$route 和 router 的差异

  • $router 是路由实例,一般用来进行跳转(编程式导航),​route 是一个对象,一般用来接收和路由器相关的参数

后台管理(案例)

html
<style>
  html,
  body,
  #app {
    margin: 0;
    padding: 0px;
    height: 100%;
  }
  .header {
    height: 50px;
    background-color: #545c64;
    line-height: 50px;
    text-align: center;
    font-size: 24px;
    color: #fff;
  }
  .footer {
    height: 40px;
    line-height: 40px;
    background-color: #888;
    position: absolute;
    bottom: 0;
    width: 100%;
    text-align: center;
    color: #fff;
  }
  .main {
    display: flex;
    position: absolute;
    top: 50px;
    bottom: 40px;
    width: 100%;
  }
  .content {
    flex: 1;
    text-align: center;
    height: 100%;
  }
  .left {
    flex: 0 0 20%;
    background-color: #545c64;
  }
  .left a {
    color: white;
    text-decoration: none;
  }
  .right {
    margin: 5px;
  }
  .btns {
    width: 100%;
    height: 35px;
    line-height: 35px;
    background-color: #f5f5f5;
    text-align: left;
    padding-left: 10px;
    box-sizing: border-box;
  }
  button {
    height: 30px;
    background-color: #ecf5ff;
    border: 1px solid lightskyblue;
    font-size: 12px;
    padding: 0 20px;
  }
  .main-content {
    margin-top: 10px;
  }
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }
  ul li {
    height: 45px;
    line-height: 45px;
    background-color: #a0a0a0;
    color: #fff;
    cursor: pointer;
    border-bottom: 1px solid #fff;
  }

  table {
    width: 100%;
    border-collapse: collapse;
  }

  td,
  th {
    border: 1px solid #eee;
    line-height: 35px;
    font-size: 12px;
  }

  th {
    background-color: #ddd;
  }
</style>
<body>
  <div class="demo">
    <router-view></router-view>
  </div>

  <script>
    const app = {
      template: `
                <div>
                <header class="header">后台管理系统</header>
                <div class="main">
                <div class="content left">
                <ul>
                <li><router-link to="/users">用户管理</router-link></li>
                <li><router-link to="/rights">权限管理</router-link></li>
                <li><router-link to="/goods">商品管理</router-link></li>
                <li><router-link to="/orders">订单管理</router-link></li>
                <li><router-link to="/settings">系统设置</router-link></li>
                        </ul>
                        </div>
                <div class="content right"><div class="main-content">
                <router-view></router-view>
                        </div></div>
                        </div>
                <footer class="footer">版权信息</footer>
                        </div>
                `
    }
    // 定义用户管理组件
    const users = {
      template: `
                <div>
                <h3>用户管理区域</h3>
                <table>
                <thead>
                <tr><th>编号</th><th>姓名</th><th>年龄</th><th>操作</th></tr>
                        </thead>
                <tbody>
                <tr v-for="item in userlist" :key="item.id">
                <td>{{item.id}}</td>
                <td>{{item.name}}</td>
                <td>{{item.age}}</td>
                <td>
                <a href="javascript:;" @click="goDetail(item.id)">详情</a>
                        </td>
                        </tr>
                        </tbody>
                        </table>
                        </div>
                `,
      data() {
        return {
          userlist: [
            { id: 1, name: '张三', age: 10 },
            { id: 2, name: '李四', age: 20 },
            { id: 3, name: '王五', age: 30 },
            { id: 4, name: '赵六', age: 40 }
          ]
        }
      },
      methods: {
        goDetail(id) {
          console.log(id)
          this.$router.push('/userinfo/' + id)
        }
      }
    }
    //定义用户信息详情组件
    const UserInfo = {
      props: ['id'],
      template: `
                <div>
                <h5>用户详情页{{id}}</h5>
                <button @click="goback()">后退</button>
                        </div>
                `,
      methods: {
        goback() {
          // 实现后退功能
          this.$router.go(-1)
        }
      }
    }
    // 定义权限管理组件
    const rights = {
      template: `
        <h1>权限管理</h1>
        `
    }

    // 定义商品管理组件
    const goods = {
      template: `
<h1>商品管理</h1>
`
    }
    // 定义订单管理组件
    const orders = {
      template: `
<h1>订单管理</h1>
`
    }
    // 定义系统设置组件
    const settings = {
      template: `
<h1>系统设置</h1>
`
    }

    const router = new VueRouter({
      routes: [
        {
          path: '/',
          component: app,
          redirect: '/users',
          children: [
            { path: '/users', component: users },
            { path: '/UserInfo/:id', component: UserInfo, props: true },
            { path: '/rights', component: rights },
            { path: '/goods', component: goods },
            { path: '/orders', component: orders },
            { path: '/settings', component: settings }
          ]
        }
      ]
    })

    let vm = new Vue({
      el: '.demo',
      router
    })
  </script>
</body>

前端模块化开发

ES6 模块化开发

  • 需要暴露的模块(需要 node14 版本才支持 ES6 语法)
js
let a = 0
let b = 2
function show() {
  console.log('show函数')
}
//ES6暴露语法
//一个模块中只允许出现一个 export default (只允许暴露一次)
export default {
  a,
  b,
  show
}
  • 引入模块
js
//ES6引入语法
import obj from './demo'
console.log(obj)

组件缓存(Keep-Alive)

  • 用 Keep-Alive 组件包裹的组件不会因为组件的切换而销毁,会保存组件状态

依赖注入

当我们需要给后代组件传值时,传统父传子非常麻烦,这时可以使用,但是不要滥用

  • 在祖先组件传递数据
js
  // 给所有的后代组件提供数据
  //provide 也可以为一个对象,这里是哟个函数的话就可以访问到当前组件的 data 和props里的数据
  provide: function () {
    return {
      articleId: this.articleId
    }
  },
  props: {
    articleId: {
      type: [Number, String],
      required: true
    }
  },
  • 在后代组件接收
js
//这样写之后任意后代组件都能接收到数据
// 接收祖先组件传递过来的文章ID articleId
  inject: {
    articleId: {
      type: [Number, String, Object],
      default: null
    }
  },

配置模板(vue.config.js)

js
'use strict'
const path = require('path')
module.exports = {
  dev: {
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    // 配置跨域
    proxyTable: {
      '/apil': {
        target: 'http://10.10.49.157:32766', //(本地测试)
        changeOrigin: true, //允许跨域
        pathRewrite: {
          '^/apil': ''
        }
      },
      // 改进获取 token 机制接口
      '/myapi': {
        // target: 'http://10.10.48.241:8888', // 生产环境本地地址
        // target: 'http://10.10.163.222:8888', // 生产环境打包地址
        target: 'http://10.10.48.211:8888', // 调试环境本地地址
        // target: 'http://10.10.139.43:8888', // 调试环境打包地址
        changeOrigin: true, //允许跨域
        pathRewrite: {
          '^/myapi': ''
        }
      }
    },
    // Various Dev Server settings
    host: '10.126.10.151', //项目地址
    port: 8090, // 项目端口号
    autoOpenBrowser: true,
    errorOverlay: true,
    notifyOnErrors: true,
    poll: false,
    devtool: 'cheap-module-eval-source-map',
    cacheBusting: true,
    cssSourceMap: true
  },
  build: {
    index: path.resolve(__dirname, '../dist/index.html'),
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/26/', // 打包后的资源请求路径
    productionSourceMap: true,
    devtool: '#source-map',
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],
    bundleAnalyzerReport: process.env.npm_config_report
  }
}