Vue学习

1.Vue简介

  • JavaScript框架
  • 简化DOM操作
  • 响应式数据驱动

在es6中,用let定义变量,const定义常量

Vue实例的作用范围:管理el选项命中元素及其内部的后代元素
可以使用其他的选择器,但是建议使用ID选择器
可以使用其他的双标签,不能使用HTML和BODY

<div id="app">
   {{ message }}
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"> --></script>
<script src="./vue.js"></script>
<script>
    // 编程范式:声明式编程
    var app = new Vue({
        el: "#app",  // 用于挂载要管理的元素(建议使用id选择器)
        data: {  // 定义数据
             message: "Hello Vue!"
            }
     })
</script>

计时器

methods属性用于在Vue对象中定义方法
@click:该指令用于监听某个元素的点击事件,并且需要指定发生点击时,执行的方法

<div id="app">
   <h2>当前计数:{{counter}}</h2>
   <!--<button v-on:click="counter++">+</button> -->
   <!--<button v-on:click="counter--">-</button> -->
   <button v-on:click="add">+</button>
   <button @click="sub">-</button>
</div>
<script src="./vue.js"></script>
<script>
   const app = new Vue({
       el: "#app",
       data: {
           counter:0
       },
       methods: {
           add: function(){
               this.counter++;
           },
           sub: function(){
               this.counter--;
           }
        }  
   })
</script>

MVVM
在这里插入图片描述
View层

  • 视图层
  • 在前端开发中,通常就是DOM层
  • 主要的作用是给用户展示各种信息

Model层

  • 数据层
  • 数据可能是固定的死数据,更多的是来自服务器从网络上请求下来的数据

VueModel层

  • 视图模型层
  • 视图模型层是View和Model沟通的桥梁
  • 一方面它实现了数据绑定,将Model的改变实时的反应到View中
  • 另一方面它实现了DOM监听,当DOM发生一些事件(点击、滚动、touch)时,可以监听到,并在需要的情况下改变对应的data

创建Vue实例的时候,传入了一个对象options
el
类型:String | HTMLElement
作用:决定之后Vue实例会管理哪一个DOM
data
类型:Object | Function(组件当中data必须是一个函数)
作用:Vue实例对应的数据对象,可以写复杂类型的数据(渲染时,遵循js 的语法即可)
methods
类型:{ [key: string]: Function }
作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用

Mustache语法

<div id="app">
   <h2>{{message}}</h2>
   <h2>{{message}},李银河</h2>
   <!-- mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式 -->
   <h2>{{firstName + lastName}}</h2>
   <h2>{{firstName + '' + lastName}}</h2>  // kobe bryant
   <h2>{{firstName}} {{lastName}}</h2>    // kobe bryant
   <h2>{{counter * 2}}</h2>  // 200
</div>
<script src="./vue.js"></script>
<script>
   const app = new Vue({
       el: "#app",
       data: {
           message: "你好啊",
           firstName: "kobe",
           lastName: "bryant",
           counter: 100
        }
   })
</script>

2.Vue指令

1.v-text指令

作用与Mustache比较相似:都是用于将数据显示在界面中(设置标签的内容),通常情况下,v-text接受一个string类型。默认写法会替换全部内容,使用插值表达式{{}}可以替换指定的内容;内部支持写表达式

2.v-html指令

该指令后面往往会跟上一个string类型,设置元素的innerHTML
会将string的html解析出来并且进行渲染,而v-text指令无论内容是什么,只会解析为文本

<div id="app">
   <h2 v-html="url"></h2>  // 百度一下
</div>
<script src="./vue.js"></script>
<script>
   const app = new Vue({
       el: "#app",
       data: {
           message: "你好",
           url: "<a href="http://wwww.baidu.com">百度一下</a>"
        }
   })
</script>

3.v-on指令

为元素绑定事件
绑定的方法定义在methods属性中,方法内部通过this关键字访问定义在data中的数据;事件绑定的方法写成函数调用的形式,可以传入自定义参数;定义方法时需要定义形参来接收传入的实参;事件的后面跟上 .修饰符 可以对事件进行限制.enter可以限制触发的按键为回车;事件修饰符有多种(.stop,完成响应之后,不再进行其他响应,阻止冒泡、.prevent阻止默认事件、.once只触发一次回调)
手动获取浏览器参数的event对象:$event

<div id="app">
  <input type="button" value="事件绑定" v-on:click="方法">
  <input type="button" value="事件绑定" v-on:mouseenter="方法">
  <input type="button" value="事件绑定" v-on:dbclick="方法">
  <input type="button" value="事件绑定" @dbclick="方法">
  <input type="button" value="点击" @click="doIt(666,'老铁')">
  <input type="button" @keyup.enter="sayHi">  
</div>
<script>
   const app = new Vue({
       el: "#app",
       methods:{
          doIt:function(p1,p2){
              console.log(p1);
              console.log(p2);
          },
          sayHi:function(){
              alert("吃了没");
          }
       }
   })
</script>

4.v-show指令

根据表达值的真假,切换元素的显示状态;原理是修改元素的display,实现显示隐藏;指令后面的内容,最终都会解析为布尔值;值为true元素显示,false隐藏;数据改变后,对应元素的显示状态会同步更新

<div id="app">
  <img src="地址" v-show="true">
  <img src="./img/monkey.gif" v-show="isShow">
  <input type="button" value="切换显示状态" @click="changeIsShow">
  <input type="button" value="累加年龄" @click="addAge">
  <img src="./img/monkey.gif" v-show="age>=18">
</div>
<script>
   const app = new Vue({
       el: "#app",
       data: {
           isShow: false,
           age: 16
        },
        methods: {
          changeIsShow:function(){
             this.isShow != this.isShow;
          },
          addAge:function(){
             this.age++;
          }
        }
   })
</script>

5.v-if指令

根据表达值的真假,切换元素的显示和隐藏(操作dom元素);表达式的值为true,元素存在于dom树中,为false,从dom树中移除;频繁的切换使用v-show,反之使用v-if,前者的切换消耗小

6.v-bind指令

动态设置元素的属性(如src、class、title)
完整写法是 v-bind:属性名
简写可以直接省略v-bind,只保留:属性名
需要动态的增删class,建议使用对象的方式:class="{类名:布尔值}"
:class="{active:isActive}"(active是否生效,取决于isActive的值)

<style>
   .active {
      color:red;
   }
</style>
<div id="app">
  <h2 :class="{key1:value1,key2:value2}"></h2>
  <h2 :class="{类名:布尔值}"></h2>
  <h2 :class="{active:isActive,line:isLine}"></h2>
</div>
<script>
const app = new Vue({
  el:"#app",
  data:{
    message:"你好",
    isActive:true,
    isLine:true
  }
})
</script>

动态绑定style
(1)对象语法:对象的value可以是具体的值,也可以是来自data中的属性

<h2 :style="{key(css的属性名):value(属性值)}"></h2>
<!-- '50px'必须加单引号,否则会当作一个变量去解析-->
<h2 :style="{fontSize:'50px'}">{{message}}</h2>
<!-- finalSize 当成一个变量使用-->
<h2 :style="{fontSize: finalSize}">{{message}}</h2>
<!-- finalSize + 'px'作为一个表达式,进行字符串拼接-->
<h2 :style="{fontSize: finalSize + 'px'}">{{message}}</h2>
<h2 :style="{fontSize: finalSize + 'px', color:finalColor}">{{message}}</h2>
<script>
const app = new Vue({
  el:"#app",
  data:{
    message:"你好",
    finalSize: '100px',
    finalSize:100,
    finalColor:'red'
  }
})
</script>

(2)数组语法

<div :style="[baseStyle,baseSytle1]"></div>

7.v-for指令

根据数据生成列表结构;数组经常和v-for结合使用;语法是{item,index} in 数据
变量名 in 列表 item和index可以结合其他指令一起使用;数组长度的更新会同步到页面上,是响应式
官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性(绑定)

<div id="app">
   <ul>
       <li v-for="(item,index) in movies" :key="item">{{index}}.{{item}}</li>
   </ul>
   <!-- 遍历对象-->
   <h2 v-for="items in foods">{{items.name}}</h2>
   <ul>
      <li v-for="(value,key) in info">{{value}}--{{key}}</li>
   </ul>
   <ul>
      <li v-for="(value,key,index) in info">{{value}}--{{key}}--{{index}}</li>
   </ul>
   <input type="button" value="添加数据" @click="add">
   <input type="button" value="移除数据" @click="remove">
</div>
<script src="./vue.js"></script>
<script>
   const app = new Vue({
       el: "#app",
       data: {
           movies: ["夏洛特烦恼", "速度与激情", "你好李焕英"],
           info:{
              name:why,
              age:18,
              height:1.88
           }
        },
        foods:[
           { name: "西红柿炒蛋" },
           { name: "西红柿炒蛋" }
        ],
        methods:{
          add:function(){
             this.foods.push({ name: "豆干" });
          },
          remove:function(){
             this.foods.shift();
          }
        }
   })
</script>

因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新:
push():在最后添加元素,可以传多个值
pop():删除数组中的最后一个元素
shift():删除数组中的第一个元素
unshift():在数组最前面添加元素,可以传多个值
splice():第一个参数 start 起始位置,删除(第二个参数传入要删除几个元素,如果没有传则删除后面所有的元素)、插入(第二个参数传入0,并且后面跟上要插入的元素)、替换元素(第二个参数表示要替换几个元素,后面是用于替换前面的元素)
sort():排序
reverse():翻转
Vue.set(要修改的对象,索引值,修改后的值)
toFixed(num) 方法可把 Number 四舍五入为指定小数位数的数字

通过索引值修改元素值不是响应式的

8.v-model指令

获取和设置表单元素的值(双向数据绑定);绑定的数据会和表单元素值相关联;绑定的数据⬅➡表单元素的值

它的背后本质上包含两个操作:
①v-bind绑定一个value属性
②v-on指令给当前元素绑定input事件

<div id="app">
 <input type="text" v-model="message">
 <input type="text" :value="message" @input="valueChange">
 <input type="text" :value="message" @input="message = $event.target.value">
 <input>
</div>
<script>
const app = new Vue({
   el: "#app",
   data: {
     message: '你好啊'
   },
   methods:{
     valueChange(event){
        this.message = event.target.value;
     }
   }
})
</script>

结合radio(没有v-model时,必须有name属性才能实现互斥,选一个)

<label for="male">
    <input type="radio" name="sex" id="male" value="" v-model="sex"></label>
<label for="female">
     <input type="radio" name="sex" id="female" value="" v-model="sex"></label>
<h3>您选择的性别是:{{sex}}</h3>
<script>
const app = new Vue({
   el: "#app",
   data: {
     sex: '男'
   }
})
</script>

v-model:checkbox

<div id="app">
    <!-- checkbox单选框  布尔类型 -->
    <label for="agree">
        <input type="checkbox" id="agree" v-model="isAgree"> 同意协议
    </label>
    <h2>您选择的是:{{ isAgree }}</h2>
    <button :disabled="!isAgree">下一步</button>
    <br>
    <!-- checkbook多选框  数组类型 -->
    <label><input type="checkbox" name="" id="" value="蓝球" v-model="hobbies">蓝球</label>
    <label><input type="checkbox" name="" id="" value="足球" v-model="hobbies">足球</label>
    <label><input type="checkbox" name="" id="" value="乒乓球" v-model="hobbies">乒乓球</label>
    <label><input type="checkbox" name="" id="" value="羽毛球" v-model="hobbies">羽毛球</label>
    <h2>您的爱好是:{{hobbies}}</h2>
</div>
<script src="./vue.js"></script>
<script>
    const app = new Vue({
        el: "#app",
        data: {
           message: "你好啊",
           isAgree: false,  // 单选框
           hobbies: []      // 多选框
         }
     })
</script>

v-model:select

<!-- select选择一个 字符串类型-->
<select name="abc" id="" v-model="fruit">
     <option value="苹果">苹果</option>
     <option value="香蕉">香蕉</option>
     <option value="葡萄">葡萄</option>
     <option value="西瓜">西瓜</option>
     <option value="草莓">草莓</option>
</select>
<h2>您选择的水果是:{{fruit}}</h2>
<!-- select选择多个 数组类型-->
<select name="abc" v-model="fruits" multiple>
     <option value="苹果">苹果</option>
     <option value="香蕉">香蕉</option>
     <option value="葡萄">葡萄</option>
     <option value="西瓜">西瓜</option>
     <option value="草莓">草莓</option>
</select>
<h2>您选择的水果是:{{fruits}}</h2>
<script>
     const app = new Vue({
        el: "#app",
        data: {
            message: "你好啊",
            fruit: "香蕉",
            fruits: []
          }
     })
</script>

值绑定 :value
修饰符
(1)lazy修饰符:默认情况下,在input事件中同步输入框的数据,即一旦有数据发生变化,则对应的data中的数据就会自动发生改变,lazy修饰符可以让数据在失去焦点或者回车时才会更新

<label><input type="text" v-model.lazy="message"></label>
<h2>{{message}}</h2>

(2)number修饰符:默认情况下,在输入框中无论输入字母还是数字,都会被当做字符串类型进行处理,number修饰符可以让在输入框中输入的内容自动转成数字类型

<label><input type="text" v-model.number="age"></label>
<h2>{{age}}</h2>

(3)trim修饰符:过滤内容左右两边的空格

<label><input type="text" v-model.trim="name"></label>
<h2>{{name}}</h2>

9.v-once指令

该指令后面不需要跟任何表达式
该指令表示元素和组件只渲染一次,不会随着数据的改变而改变

10.v-pre指令

跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法

<p v-pre>{{message}}</p>    // {{message}}

11.v-cloak指令

在某些情况下,浏览器可能会直接显示出Mustache标签,v-cloak就可以实现类似隐藏Mustache标签的作用,vue解析之后,div中的v-cloak属性就没了

<style>
 [v-cloak] {
   display: none;
 }
</style>
<div id = "app" v-cloak>
    <h2 v-cloak>{{message}}</h2>    // {{message}}
</div>

计算属性
计算属性会进行缓存,如果多次使用时,计算属性只会调用一次;
一般是没有set方法的,是一个只读属性

<div id="app">
  <!--1.直接拼接:语法过于繁琐-->
  <h2>{{firstName + '' + lastName}}</h2>
  <h2>{{firstName}} {{lastName}}</h2>
  <!--2.通过定义methods-->
  <h2>{{getFullName()}}</h2>
  <!--3.计算属性不需要加小括号,命名 名词-->
  <h2>{{fullName}}</h2>
</div>
<script>
 const app = new Vue({
   el:"#app",
   data:{
     firstName:"Lebron",
     lastName:"James"
   },
   // computed计算属性
   computed:{
     fullName:function(){
       return this.firstName + '' + this.lastName;
     }
   },
   methods:{
     getFullName:function(){
       return this.firstName + '' + this.lastName;
     }
   }
 })
</script>

ES5之前的var是没有块级作用域的(if/for);
ES6中的let是有块级作用域的(if/for)

const的使用

当修饰的标识符不会被再次赋值时,就可以使用const来保证数据的安全性;再开发中,优先使用const,只有需要改变某一个标识符时使用let(ES6中)
注意:

  • 一旦给const修饰的标识符被赋值后,不能修改
  • 在使用const定义标识符,必须进行赋值
  • 常量的含义是指向的对象不能修改,但可以改变对象内部的属性
<script>
const name = "why",
const age = 18,
const height = 1.88
// ES5的写法
const obj = {
 name:name,
 age:age,
 height:height,
 run:function(){},
 eat:function(){}
}
//ES6 的写法
const obj = {
 name,
 age,
 height,
 run(){},
 eat(){}
}
</script>

Vue在进行DOM 渲染时,出于性能考虑,会尽可能的复用以及存在的元素,而不是重新创建新的元素。若不希望Vue出现重复利用的问题,可以给对应的input添加key,并且保证key的不同

编程范式:命令式编程、声明式编程
面向对象编程(第一公民:对象)、函数式编程(第一公民:函数)

<script>
let nums = [10,20,222,111,444,50,60]
let newNums = nums.filter(function(n){
   return n < 100
})
console.log(newNums)

let new2Nums = newNums.map(function(n) {
   return n*2
})
console.log(new2Nums)

let total = new2Nums.reduce(function(prevValue,n){
   return prevValue + n
},0)
console.log(total)
-----------------------------------------------------------------
// 函数式编程
let total = nums.filter(function(n){
  return n < 100
}).map(function(n){
  return n * 2
}).reduce(function(prevValue, n){
  return prevValue + n
},0)
-----------------------------------------------------------------
// 箭头函数
let total = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n)
</script>

filter() 过滤器回调函数要求:必须返回一个boolean值:true or false
当返回true时,函数内部会自动将这次回调的值n加入到新的数组中,
返回false时,函数内部会过滤掉这次的n

map() 映射,返回的是一个数组
reduce():对数组中所有的内容进行汇总

3.组件化

它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造应用,任何应用都会被抽象成一颗组件树。
在这里插入图片描述

3.1 组件化的步骤

(1)创建组件构造器 Vue.extend()
传入template代表自定义组件的模板(在使用到组件的地方,要显示的HTML代码)
(2)注册组件 Vue.component()
(3)使用组件

<div id="app">
    <!-- 3.使用组件 -->
    <cpn></cpn>
</div>

<script src="../vue.js"></script>
<script>
     // 1.创建组件构造器对象
     const cpnC = Vue.extend({
         template: `
            <div>
                <h2>我是标题</h2>
                <p>我是内容,哈哈哈哈</p>
                <p>我是内容,呵呵呵呵</p>
            </div>`
        })

     // 2.注册组件  Vue.component('组件标签名', 组件构造器) 全局组件
     // Vue.component('cpn', cpnC)
     const app = new Vue({
         el: "#app",
         data: {
            message: "你好啊",
         },
       // cpn为使用组件时的标签名:组件构造器(局部组件,挂载在某个vue实例中)
         components: {
             cpn: cpnC
         }
      })
</script>

3.2 子组件与父组件

子组件是只能在父组件中识别的

<div id="app">
    <cpn2></cpn2>
    <!--报错-->
    <cpn1></cpn1> 
</div>
<script>
// 1.创建第一个组件构造器(子组件)
    const cpnC1 = Vue.extend({
        template: `
         <div>
             <h2>我是标题1</h2>
             <p>我是内容,哈哈哈哈</p>    
         </div> `
     })
// 2.创建第二个组件构造器(父组件)
    const cpnC2 = Vue.extend({
        template: `
        <div>
            <h2>我是标题2</h2>
            <p>我是内容,呵呵呵呵</p>    
            <cpn1></cpn1>
        </div> `,
        components: {
           cpn1: cpnC1
         }
     })
     const app = new Vue({
         el: "#app",
         components: {
             cpn2: cpnC2
         }
     })
</script>

语法糖:主要是省去了Vue.extend()的步骤,直接使用一个对象来代替

<div id="app">
    <cpn1></cpn1> 
    <cpn2></cpn2>
</div>
<script>
  // const cpnC = Vue.extend()  创建组件构造器
  // 全局组件注册
  Vue.component('cpn1',{
    template: `
    <div>
       <h2>我是标题1</h2>
       <p>我是内容,哈哈哈哈</p>
    </div>`
  })
// 注册局部组件
const app = new Vue({
    el: "#app",
    data:{
       message:'hello'
    },
    components: {
       'cpn2': {
          template: `
            <div>
               <h2>我是标题2</h2>
                <p>我是内容,哈哈哈哈</p>
            </div>`
       }
     }
 })
</script>

3.3 组件模板分离

<div id="app">
    <cpn></cpn>
</div>
    <!-- 1.script标签,注意:类型必须是text/x-template -->
    <!-- <script type="text/x-template" id="cpn">
        <div>
            <h2>我是标题</h2>
            <p>我是内容,哈哈哈哈</p>
        </div>  
    </script> -->
    
    <!-- 2.template标签 -->
    <template id="cpn">
        <div>
            <h2>我是标题</h2>
            <p>我是内容,哈哈哈哈呵呵呵呵</p>
        </div>
    </template>

    <script src="../vue.js"></script>
<script>

        // 注册一个全局组件
        Vue.component('cpn', {
            template: '#cpn'
        })
 </script>

组件内部不能访问vue实例里面的数据。
组件是一个单独功能模块的封装,这个模块有属于自己的HTML模板,也有属于自己的数据 data属性:必须是一个函数,且函数返回一个对象,对象内部保存着数据

<template id="cpn">
     <div>
         <h2>我是标题</h2>
         <p>我是内容,哈哈哈</p>
         <h2>{{title}}</h2>
     </div>
</template>
<script>
Vue.component('cpn', {
     template: '#cpn',
     data: function () {
          return {
               title: "abc"
           }
      }
})
</script>

3.4 父子组件的通信

子组件不能引用父组件或者Vue实例的数据,但在开发中,往往一些数据需要从上层传递到下层:比如在一个页面中,我们从服务器请求到了很多数据,其中一部分数据并非是整个页面的大组件来展示的,而是需要下面的子组件进行展示,这时,并不会让子组件再次发送一个网络请求,而是直接让父组件(大组件)将数据传递给子组件(小组件)
在这里插入图片描述
(1) 通过props向子组件传递数据
使用props来声明需要从父级接收到的数据
props的值有两种方式:
① 字符串数组,数组中的字符串就是传递时的名称
对象:对象可以设置传递时的类型,也可以设置默认值

<div id="app">
  <cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
   <div>
      <!--<p>{{cmovies}}</p>-->
      <ul>
         <li v-for="item in cmovies">{{item}}</li>
      </ul>
      <h2>{{cmessage}}</h2>
   </div>
</template>
<script>
const cpn = {
  template: "#cpn",
  props:['cmovies','cmessage']
}

const app = new Vue({
  el:"#app",
  data:{
     message:"hello",
     movies:['海贼王', '哈姆雷特', '海王']
  },
  components:{
    'cpn':cpn
  }
})
</script>

props数据验证

当需要对props进行类型等验证时,需要对象写法
验证支持的数据类型:
String、Number、Boolean、Array、Object、Date、Function、Symbol
当有自定义构造函数时,验证也支持自定义的类型

<div id="app">
  <cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
   <div>
      <!--<p>{{cmovies}}</p>-->
      <ul>
         <li v-for="item in cmovies">{{item}}</li>
      </ul>
      <h2>{{cmessage}}</h2>
   </div>
</template>
<script>
// 父传子:cpn
const cpn = {
  template: "#cpn",
  props:{
    // 1.类型限制
    cmovies:Array,
    cmessage:String,
    // 2.提供一些默认值、必传值
    cmessage: {
      type:String,
      default:"aaaaaaa",
      required:true
    },
    // 类型是对象或者数组时,默认值必须是一个函数
    cmovies:{
      type:Array,
      default:function(){
         return []
      }
    }
  },
  data(){
     return {}
  }
}

const app = new Vue({
  el:"#app",
  data:{
     message:"hello",
     movies:['海贼王', '哈姆雷特', '海王']
  },
  components:{
    cpn   // 增强写法(等同于'cpn':cpn  cpn:cpn)
  }
})
</script>

若props是驼峰命名的,则在使用时用“-”分割

<div id="app">
    <cpn :cinfo="info" :child-my-message="message"></cpn>
</div>
<script>
 const cpn = {
      template: '#cpn',
      props: {
          cinfo: {
              type: Object,
              default() {
                  return {}
              }
          },
          childMyMessage: {
              type: String,
              default: ''
          }
      }
}
</script>

(2) 通过事件向父组件发送消息

子组件向父组件传递—>自定义事件来完成
v-on不仅可以用于监听DOM事件,还可以用于组件间的自定义事件
自定义事件流程:
① 在子组件中,通过$emit()来触发事件
② 在父组件,通过v-on来监听子组件事件

<!-- 父组件模板 -->
<div id="app">
   <!--父组件监听子组件传过来的事件-->
   <cpn @itemclick="cpnClick"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn">
    <div>
       <button v-for="item in categories" @click="btnClick(item)">
                {{item.name}}
       </button>
    </div>
</template>
<script>
   // 1.子组件
   const cpn = {
            template: "#cpn",
            data() {
                return {
                    categories: [
                        { id: 'aaa', name: '热门推荐' },
                        { id: 'bbb', name: '手机数码' },
                        { id: 'ccc', name: '家用电器' },
                        { id: 'ddd', name: '电脑办公' },
                    ]
                }
            },
            methods: {
                btnClick(item) {
                    // 发射事件(自定义事件--'事件名称')
                    this.$emit('itemclick', item)
                }
            }
     }
        // 2.父组件
        const app = new Vue({
            el: "#app",
            data: {
                message: 'Hello'
            },
            components: {
                cpn
            },
            methods: {
                cpnClick(item) {
                    console.log('cpnClick', item);
                }
            }
        })
</script>

3.5 父子组件的访问方式

(1)父组件访问子组件:$children$refs(reference引用 重要)
(2)子组件访问父组件:$parent$root

3.6 slot

组件的插槽是为了让组件更加具有扩展性
最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽,一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容:搜索框、文字、菜单…,由调用者自己决定

<!-- 
        1.插槽的基本使用 <slot></slot>
        2.插槽的默认值 <slot><button>按钮</button></slot>
        3.如果有多个值同时放入到组件进行替换时,一起作为替换元素
     -->
<div id="app">
    <cpn><button>按钮</button></cpn>
    <cpn><span>呵呵呵呵</span></cpn>
    <cpn>
        <i>啦啦啦</i>
        <div>我是div元素</div>
        <p>我是p元素</p>
    </cpn>
    <cpn></cpn>
    <cpn></cpn>
    <cpn></cpn>
</div>

<template id="cpn">
    <div>
       <h2>我是组件</h2>
       <p>哈哈哈哈哈哈哈</p>
       <slot><button>按钮</button></slot>
    </div>
</template>

<script src="../vue.js"></script>
<script>
        const cpn = {
            template: "#cpn"
        }
        const app = new Vue({
            el: "#app",
            data: {
                message: 'Hello'
            },
            components: {
                cpn
            }
        })
</script>

具名插槽

<div id="app">
    <cpn><span slot="center">标题</span></cpn>
    <cpn><button slot="left">返回</button></cpn>
</div>
<template id="cpn">
    <div>
        <slot name="left"><span>左边</span></slot>
        <slot name="center"><span>中间</span></slot>
        <slot name="right"><span>右边</span></slot>
     </div>
</template>

编译作用域

准则:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在自己作用域内编译

作用域插槽

父组件替换插槽的标签,但是内容本身由子组件提供

<div id="app">
    <cpn></cpn>
    <cpn>
       <!-- 目的:获取子组件中的pLanguages -->
       <template slot-scope="slot">
           <span v-for="item in slot.data">{{item}} -</span>
       </template>
    </cpn>
    <cpn>
       <template slot-scope="slot">
           <!-- <span v-for="item in slot.data">{{item}} *</span> -->
           <span>{{slot.data.join(' * ')}}</span>
       </template>
    </cpn>
</div>

<template id="cpn">
    <div>
       <slot :data="pLanguages">
           <ul>
              <li v-for="item in pLanguages">{{item}}</li>
           </ul>
       </slot>
    </div>
</template>

<script src="../vue.js"></script>
<script>
    const app = new Vue({
    el: "#app",
    data: {
        message: 'Hello'
    },
    components: {
         cpn: {
            template: "#cpn",
            data() {
              return {
                 pLanguages: ['JavaScript', 'Java', 'C++', 'C#', 'Python']
              }
            }
        }
   }
 })
</script>

4.模块化开发

在网页开发早期,js制作作为一种脚本语言,做一些简单的表单验证或动画实现等,那时代码还是很少的。随着ajax异步请求的出现,慢慢形成了前后端的分离,客户端需要完成的事情越来越多,代码量也与日俱增,为了应对代码量的剧增,通常会将代码组织在多个js文件中进行维护,但是这种维护方式,依然不能避免一些灾难性的问题:
① 全局变量同名问题
② 对js文件的依赖顺序几乎是强制性的

我们可以使用将需要暴露到外面的变量,使用一个模块作为出口:
在匿名函数内部,定义一个对象;给对象添加各种需要暴露到外面的属性和方法(不需要暴露的直接定义即可);最后将这个对象返回,并且外面使用一个MoudleA接收
常见的模块化规范:CommonJSAMDCMDES6的Modules

export(导出)基本使用

// info.js
export let name = 'why'
export let age = 18
export let height = 1.88

let name = 'why'
let age = 18
let height = 1.88
export {name, age, height}

// 导出函数/类
export function mul(num1,num2) {
    return num1 + num2
}
export class Person {
   run() {}
}
export {mul, Person}
<script src="info.js" type="module"></script>
import {name,age} form "info.js"  // 导入

某些情况下,一个模块中包含的某个功能,并不希望给这个功能命名,而且让导入者可以自己来命名,可以使用export default在同一个模块中,不允许同时存在多个

// info.js
export default function(){
   console.log('default function');
}

// main.js
import myFun from './info.js'  //myFun是自己命名的
myFun()

若我们希望某个模块中所有的信息都导入,可以通过导入模块中所有的export变量,通常情况下,给起一个别名

import * as aaa from './info.js'
console.log(aaa.name)

5.Webpack

现代的JavaScript应用的静态模块打包工具
Webpack其中一个核心是让我们可以进行模块化开发,且处理模块间的依赖关系,不仅仅是JavaScript文件,CSS、图片、json文件等都可以被当做模块来使用
地址:官网https://www.webpack.js.org

5.1 webpack的打包方式

在这里插入图片描述
mathUtils.js

function add(num1,num2) {
return num1 + num2
}
function mul(num1,num2) {
return num1 * num2
}

module.exports = {
add,
mul
}

main.js

// 1.使用commonjs的模块化规范
const {add,mul} = require('./mathUtils.js')

console.log(add(20,30))
console.log(mul(10,2))

// 2.使用ES6的模块化规范
import {name,age,height} from "./info"

console.log(name);
console.log(age);
console.log(height);

info.js

export const name="why";
export const age = 18;
export const height = 1.88;

打包成bundle.js文件
在这里插入图片描述
在这里插入图片描述
直接在index.html文件中引用

<script src="./dist/bundle.js" type=""></script>

打包方式二:

webpack.config.js配置文件

// 动态获取路径
const path = require('path')

module.exports = {
    // 入口
    entry: './src/main.js',
    // 出口
    output: {
       path: path.resolve(__dirname,'dist'), // 绝对路径
       filename: 'bundle.js'
    },
}


在这里插入图片描述
package.json

{
  "name": "meetwebpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

在这里插入图片描述
打包方式三:
package.json

{
  "name": "meetwebpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
  "author": "",
  "license": "ISC"
}

在这里插入图片描述
局部安装webpack

npm install webpack@3.6.0 --save-dev
{
  "name": "meetwebpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
  "author": "",
  "license": "ISC",
  "devDependencies:" {
      "webpack": "^3.6.0"
  }
}

通过node_modules/.bin/webpack启动webpack打包
package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置:
首先,会寻找本地的node_modules/.bin路径中对应的命令
如果没找到,会去全局的环境变量中寻找
执行build指令:npm run build

5.2 loader

webpack主要用来处理js代码,且webpack会自动处理js之间相关的依赖。但在开发中,也需要加载css、图片、高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue转成js文件等。webpack扩展对应的loader就支持这些转化

5.2.1 loader使用过程

一、通过npm安装需要使用的loader

npm install --save-dev css-loader@2.0.2
npm install --save-dev style-loader@0.23.1

二、在webpack.config.js中的module关键字下进行配置

// 动态获取路径
const path = require('path')
module.exports = {
// 入口
entry: './src/main.js',
// 出口
output: {
path: path.resolve(__dirname,'dist'), // 绝对路径
filename: 'bundle.js'
},
module:{
  rules:[
    {
      test:/\.css$/,
      // css-loader只负责将css文件进行加载
      // style-loader负责将样式添加到DOM中
      // 使用多个loader时,从右向左
      use:['style-loader','css-loader']
    }
  ]
}
}

5.2.2 less文件

special.less

@fontSize: 50px;
@fontColor: orange;

body {
  font-size: @fontSize;
  color: @fontColor;
}
npm install --save-dev less-loader@4.1.0 less@3.9.0

webpack.config.js

// 动态获取路径
const path = require('path')
module.exports = {
// 入口
entry: './src/main.js',
// 出口
output: {
path: path.resolve(__dirname,'dist'), // 绝对路径
filename: 'bundle.js'
},
module:{
  rules:[
    {
      test:/\.css$/,
      // css-loader只负责将css文件进行加载
      // style-loader负责将样式添加到DOM中
      // 使用多个loader时,从右向左
      use:['style-loader','css-loader']
    },
    {
      test: /\.less$/,
      use: [{
        loader: "style-loader"
      },{
        loader: "css-loader"
      },{
        loader: "less-loader"
      }]
    }
  ]
}
}

5.2.3 图片文件处理url-loader

npm install --save-dev url-loader  (图片小于8kb)
npm install --save-dev file-loader  (图片大于8kb)

配置webpack.config.js

{
        test: /\.(png|jpg|gif|jpeg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              // 当加载的图片小于limit时,会将图片编译成base64字符串形式
              // 当加载的图片大于limit时,需要使用file-loader模块进行加载
              limit: 8196,
            },
          },
        ],
      },

打包后,会发现dist文件夹下多了一个图片文件(名:哈希hash值32位,防止重名)

在这里插入图片描述
在这里插入图片描述
在真实开发中,可能对打包的图片名字有一定的要求,比如:将所有图片放在同一个文件夹中,跟上图片原来的名字,同时也要防止重复 img/name.hash:8.ext(截取8位)
在这里插入图片描述
然后打包 npm run build
在这里插入图片描述

5.2.4 ES6语法处理

webpack打包的js文件里面写的ES6语法并没有转成ES5,意味着可能一些对ES6还不支持的浏览器没办法很好的运行代码。
若将ES6语法转成ES5,需要babel

npm install --save-dev babel-loader@7 babel-core babel-preset-es2015

webpack.config.js

{
        test:/\.js$/,
        exclude:/(node_modules|bower_components)/,
        use:{
          loader:'babel-loader',
          options:{
            presets:['es2015']
          }
        }
},

5.2.5 webpack-使用vue的配置过程

1.安装:npm install --save vue
2.导入 main.js
在这里插入图片描述
3.挂载vue实例

const app = new Vue({
	el: '#app',
	data: {
		message: 'Hello Word!',
	},
});

<div id="app">
    <h2>{{message}}</h2>
 </div>

4.打包:npm run build
在webpack.config.js中配置
在这里插入图片描述

版本
1.runtime-only:代码中,不可以有任何的template
2.runtime-compiler:代码中,可以有template,因为compiler可以用于编译template

5.2.6 el和template区别

正常运行之后,若我们希望将data中的数据显示在界面中,就必须修改index.html,如果后面自定义了组件,也必须修改index.html来使用组件,但是html模板在之后的开发中,不希望手动频繁修改,怎么做?
定义template属性:在Vue实例中,我们定义了el属性,用于和index.html中的#app进行绑定,让Vue实例之后可以管理它其中的内容。我们可以将div元素值中的{{message}}内容删掉,只保留一个基本的id为div的元素,若依然希望在其中显示{{message}}的内容,我们可以再定义一个template属性

new Vue({
	el: '#app',
  template:`
    <div>
        <h2>{{message}}</h2>
        <button @click="btnClick"></button>
        <h2>{{name}}</h2>
     </div>
  `
  ,
	data: {
		message: 'Hello Word!',
    name:'codewhy'
	},
  methods:{
    btnClick(){}
  }
});
npm install -D vue-loader vue-template-compiler 
const {VueLoaderPlugin} = require('vue-loader')

module: {
     {
        test:/\.vue$/,
        use:['vue-loader']
     }
},
plugins:[
      new VueLoaderPlugin()
  ],

5.3 plugin插件

通常用于对某个现有的架构进行扩展。webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化、文件压缩等

loader和plugin的区别

  • loader主要用于转换某些类型的模块,它是一个转换器
  • plugin是插件,它是对webpack本身的扩展,是一个扩展器

plugin的使用过程

  • 通过npm安装需要使用的plugins
  • 在webpack.config.js中配置插件

5.3.1 打包html的plugin

在真实发布项目时,发布的是dist文件夹中的内容,若dist文件夹中没有index.html
,那么打包的js等文件也就没有意义了。我们需要将index.html文件打包到dist文件夹中,这时就可以使用HtmlWebpackPlugin插件–自动生成一个index.html文件(可以指定模板来生成);将打包的js文件,自动通过script标签插入到body中

npm install html-webpack-plugin@3.2.0 --save-dev

使用插件,修改webpcak.config.js文件中plugin部分的内容:
template表示根据什么模板来生成index.html
删除之前在output中添加的publicPath属性,否则插入的script标签中的src可能会有问题

const HtmlWebpackPlugin = require('html-webpack-plugin')

plugins:[
      new webpack.BannerPlugin('最终版权归...所有'),
      new VueLoaderPlugin(),
      new HtmlWebpackPlugin({
        template:'index.html'
      })
  ],

5.3.2 js压缩的plugin

在项目发布之前,我们必须对js等文件进行压缩处理

npm install uglifyjs-webpack'-plugin@1.1.1 --save-dev

在webpack.config.js中进行配置

const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');

plugins:[
      // new webpack.BannerPlugin('最终版权归...所有'),
      new VueLoaderPlugin(),
      new HtmlWebpackPlugin({
        template:'index.html'
      }),
      new UglifyjsWebpackPlugin()
  ],

5.3.3 搭建本地服务器

webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的浏览器自动刷新显示我们修改后的结果

npm install webpack-dev-server@2.9.1 --save-dev 

devserver也是作为webpack中的一个选项,选项本身可以设置如下属性:

  • contentBase:为哪一个文件提供本地服务,默认是根文件夹,这里要填写./dist
  • port:端口号
  • inline:页面实时刷新
  • historyApiFallback:在SPA页面中,依赖HTML5的history模式
    webpack.config.js文件配置修改如下:
devServer:{
   contentBase:'./dist',
   inline:true
}

在package.json中我们还可以再配置另外一个scripts:–open参数表示直接打开浏览器

"dev":"webpack-dev-server --open"

运行 npm run dev打开网页

ctrl+c终止

5.3.4 webpack配置分离

将webpack.config.js分离成三个文件:base.config.jsdev.config.jsprod.config.js
合并

npm install webpack-merge@4.1.5 --save-dev

prod.config.js(生产环境)

const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')

module.exports = webpackMerge(baseConfig, {
  plugins:[
       new UglifyjsWebpackPlugin()  
  ]
})

dev.config.js(开发环境)

const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')

module.exports = webpackMerge(baseConfig, {
  devServer:{
     contentBase:'./dist',
     inline:true
   }
})

修改脚本文件package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config ./build/prod.config.js",
    "dev": "webpack-dev-server --open --config ./build/dev.config.js"
  }

npm run build打包到dist
base.config.js修改路径

	output: {
		path: path.resolve(__dirname, '../dist'), // 绝对路径
		filename: 'bundle.js',
	},

6.Vue CLI脚手架

使用前提–Node、Webpack
安装:npm install -g @vue/cli
拉取 2.x模板
npm install -g @vue/cli-init

在终端建立项目(下载模板)

6.1 Vue CLI2

1、创建项目

npx vue init webpack 项目名

2、创建过程

在这里插入图片描述
项目结构
在这里插入图片描述

3、运行

npm run dev

4、runtime-only和runtime-compiler区别

在这里插入图片描述
Vue程序的运行过程
在这里插入图片描述
runtime-compiler:
template 解析–> ast(抽象语法树) 编译–> render --> virtual dom -->UI(真实DOM)
runtime-only:
render --> virtual dom -->UI
所以,runtime-only性能更高,代码量更少

const cpn = {
   template:`<div>{{message}}</div>`,
   data() {
      return {
        message: '我是组件message'
      }
   }
}
new Vue({
  el: '#app',
  render = function(createElement) {
    // 1.普通用法:createElement('标签',{标签的属性},[])
    return createElement('h2',
      {class: 'box'},
      ['Hello World'],createElement('button',['按钮']))
    // 2.传入组件对象
    return createElement(cpn)
  }
})

参数1:标签名 创建的h2标签会替换掉挂载的app <h2></h2>
参数2:对象 标签属性 <h2 class="box"></h2>
参数3:数组 可以跟标签的内容 <h2 class="box">内容</h2>

在这里插入图片描述
在这里插入图片描述

6.2 Vue CLI3

1、创建项目

npx vue create 项目名

2、创建过程

①手动选择
在这里插入图片描述
②用空格键选中
在这里插入图片描述

③选择配置文件
在这里插入图片描述
④保存 作为将来创建项目时的预置
在这里插入图片描述
项目结构
在这里插入图片描述

3、运行

npm run serve

C:\Users\dell.vuerc (可以删除)
在这里插入图片描述
配置可视化(图形化界面)

npx vue ui

6.3 箭头函数

<script>
 // 定义函数的方式:
 // 1.function
 const aaa = function() {}
 // 2.对象字面量中定义函数
 const obj = {
   bbb: function() {},
   bbb() {}
 }
 // 3.ES6中的箭头函数
 const ccc = (参数列表) => {}
 
 // 2.参数问题
    // 2.1 放入两个参数
    const sum = (num1, num2) => {
      return num1 + num2
    }
    // 2.2 放入一个参数,可以省略括号()
    const power = num => {
      return num * num
    }
    // 3.返回值问题
    // 3.1 函数体中有多行语句
    const test = () => {
      console.log('Hello World');
      console.log('Hello Vue.js')
    }
    // 3.2 函数代码块中只有一行代码
    // const mul = (num1,num2) => {
    //   return num1 * num2
    // }
    const mul = (num1, num2) => num1 * num2
    console.log(mul(20, 30));

    // const demo = () => {
    //   console.log('Hello Demo')
    // }
    const demo = () => console.log('Hello Demo')
    console.log(demo());
</script>

箭头函数的使用:当把一个函数作为参数传入另一个函数

this的使用
箭头函数中的this如何查找?
向外层作用域中,一层层查找this,直到有this的定义
以函数的形式调用时,this永远都是window;
以方法的形式调用时,this就是调用方法的那个对象

<script>
    const obj = {
      aaa() {
        console.log(this);     // obj对象{aaa: f}
        setTimeout(function () {
          console.log(this);   // Window
        })
        setTimeout(() => {
          console.log(this);  // obj对象{aaa: f}
        })
      }
    }

    obj.aaa()
  </script>

6.4 Vue-Router 路由

路由就是通过互联的网络把信息从源地址传输到目的地址的活动。
路由器提供了两种机制:路由和转送
路由决定把数据包从来源到目的地的路径
转送将输入端的数据转移到合适的输出端

后端路由阶段

早期的网站开发整个HTML页面都是由服务器来渲染的,服务器直接生产渲染好对于的HTML页面,返回给客户端进行展示。
一个网站,那么多页面服务器如何处理?

  • 一个页面有自己对应的网址,即URL
  • URL会发送到服务器,服务器会通过正则对该URL进行匹配,并且最后交给一个Controller进行处理
  • Controller进行各种处理,最终生成HTML或者数据,返回给前端
  • 这就完成了一个IO操作
    当我们页面需要请求不同的路径内容时,交给服务器进行处理,服务器渲染好整个页面,并且将页面返回给客户端。这种情况下渲染好的页面,不需要单独加载任何的js和css,可以直接交给浏览器展示,这样也有利于SEO的优化。
    缺点:
  • 整个页面的模块由后端人员来编写和维护
  • 前端开发人员如果要开发页面,需要通过PHP和Java等语言来编写页面代码
  • HTML代码和数据以及对应的逻辑会混在一起,编写和维护非常糟糕

前后端分离阶段

  • 随着ajax的出现,有了前后端分离的开发模式
  • 后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中
  • 优点-前后端责任的清晰,后端专注于数据,前端专注于交互和可视化
  • 当移动端出现后,后端不需要及逆行任何处理,依然使用之前的一套API即可

单页富用应用阶段

SPA-在前后端分离的基础上加了一层前端路由,即由前端来维护一套路由规则
前端路由的核心:改变url,但是页面不进行整体的刷新

URL的hash
URL的hash也就是锚点(#),本质上是改变window.location的href属性
可以通过直接赋值location.hash来改变href,但是页面不发生刷新
例:location.hash='aaa'

HTML5的history模式

pushState:相当于入栈

> location.href
< "http://192.168.1.101:8080/examples/urlChange"
> history.pushState({},'','/foo')
< undefined
> location.href
< "http://192.168.1.101:8080/foo"
> history.pushState({},'','/')
< undefined
> location.href
< "http://192.168.1.101:8080/"

replaceState:不可以返回

> location.href
< "http://192.168.1.101:8080/"
> history.replaceState({},'','/foo')
< undefined
> location.href
< "http://192.168.1.101:8080/foo"
> history.replaceState({},'','/foo/bar')
< undefined
> location.href
< "http://192.168.1.101:8080/foo/bar"

go

> location.href
< "http://192.168.1.101:8080/"
> history.go(-1)
< undefined
> location.href
< "http://192.168.1.101:8080/foo"
> history.go(-1)
< undefined
> location.href
< "http://192.168.1.101:8080/examples/urlChange"
> history.go(1)
< undefined
> location.href
< "http://192.168.1.101:8080/foo"

history.back() 等价于 history.go(-1)
history.forward() 等价于 history.go(1)
这三个接口等同于浏览器界面的前进后退

6.4.1 vue-router基本使用

步骤一:安装
npm install vue-router --save
步骤二:在模块化工程中使用(因为它是一个插件,所以可以通过Vue.use()来安装路由功能)

  • 导入路由对象,并且调用Vue.use(VueRouter)
  • 创建路由实例,并且传入路由映射配置
  • Vue实例挂载创建的路由实例

router/index.js

// 配置路由相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'

// 1.通过Vue.use(插件),安装插件
Vue.use(VueRouter)

// 2.创建VueRouter路由对象
const routes = [

]
const router = new VueRouter({
  // 配置路由和组件之间的映射关系
  routes 
})

// 3.将router对象传入到vue实例
export default router

main.js

import Vue from "vue";
import App from "./App.vue";
import router from "./router";

Vue.config.productionTip = false;

new Vue({
  router,  //挂载
  render: (h) => h(App),
}).$mount("#app");

使用vue-router的步骤

  • 创建路由组件
  • 配置路由映射:组件和路径映射关系
  • 使用路由:通过<router-link><router-view>

<router-link>:该标签是一个vue-router中已经内置的组件,它会被渲染成一个<a>标签,属性如下:

  • to:用于指定跳转的路径
  • tag:指定<router-link>之后渲染成什么组件,比如<router-link to='/home'> tag="button"会被渲染成button按钮
  • replace:不会留下history记录,所以指定replace的情况下,后退键返回不能返回到上一个页面中
  • active-class:当<router-link>对应的路由匹配成功时,会自动给当前元素设置一个router-link-active的class,设置active-class可以修改默认的名称。但是通常不会修改类的属性,直接使用默认的router-link-active即可

<router-view>:该标签会根据当前的路径,动态渲染出不同的组件。网页的其它内容,比如顶部的标题、导航,或底部的一些版权信息等会和<router-view>处于同一个级别;在路由切换时,切换的是<router-view>挂载的组件,其它内容不会发生改变

Home.vue
<template>
  <div>
    <h2>我是首页</h2>
    <p>我是首页内容,哈哈哈</p>
  </div>
</template>
<script>
export default {
  name:"Home",
  data () {
    return {
    };
  },
  components: {},
  computed: {},
  mounted: {},
  methods: {}
}
</script>
<style lang='scss' scoped>
</style>

About.vue
<template>
  <div>
    <h2>我是关于</h2>
    <p>我是关于内容,呵呵呵</p>
  </div>
</template>
<script>
export default {
  name:"About",
  data () {
    return {
    };
  },
  components: {},
  computed: {},
  mounted: {},
  methods: {}
}
</script>
<style lang='scss' scoped>
</style>


index.js
// 配置路由相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'

import Home from '../components/Home.vue'
import About from '../components/About.vue'

// 1.通过Vue.use(插件),安装插件
Vue.use(VueRouter)

// 2.创建VueRouter路由对象
const routes = [
  {
    path:'/home',
    component:Home
  },
  {
    path:'/about',
    component:About
  }
]
const router = new VueRouter({
  // 配置路由和组件之间的映射关系:一个映射关系就是一个对象
  routes
})

// 3.将router对象传入到vue实例
export default router

APP.vue
<template>
  <div id="app">
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view></router-view>
  </div>
</template>

<script>
  export default {
    name: 'App'
  }
</script>
<style>
</style>

路由的默认路径

默认情况下,进入网站的首页,我们希望<router-view>渲染首页的内容,让路径默认跳转到首页,并且<router-view>渲染首页组件,只需多配置一个映射即可:path配置的是根路径,redirect是重定向,即将根路径重定向到/home路径下

{
const routers = [
    path:'/',
    // redirect重定向
    redirect:'/home'
  },

默认情况下,路径的改变使用的是URL的hash
如果希望使用HTML5的history模式,进行如下配置即可:

// 2.创建VueRouter路由对象
const router = new VueRouter({
  // 配置路由和组件之间的映射关系:一个映射关系就是一个对象
  routes,
  mode:'history'
})

动态路由
在某些情况下,一个页面的path路径可能是不确定的,比如进入用户界面时,希望是/user/aaa或/user/bbb,除了有前面的/user之外,后面还跟上了用户的ID,这种path和component的匹配关系,称之为动态路由
①新建 User.vue

<template>
  <div>
    <h2>我是用户界面</h2>
    <p>我是用户的相关信息,嘿嘿嘿</p>
    <h2>{{userId}}</h2>
    <h2>{{$route.params.userId}}</h2>
  </div>
</template>
export default {
  name:"User",
  computed:{
    userId() {
      return this.$route.params.userId
    }
  },

②在index.js中添加一个路由对象

{
    path:'/user/:userId',
    component:User
  }

③在App.vue中动态绑定

<router-link :to="'/user/' + userId">用户</router-link>
<script>
  export default {
    name: 'App',
    data(){
      return {
        userId:'lisi'
      }
    },
  }
</script>

路由的懒加载
官方解释:当打包构建应用时,JavaScript包会变得非常大,影响页面加载。若我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。
白话:首先,我们知道路由中通常会定义很多不同的页面,这个页面最后被打包到一个js文件中,但是,页面这么多都放在一个js文件中,必然会造成这个页面非常大。如果我们一次性从服务器请求下来这个页面,可能需要花费一定的时间,甚至用户的电脑上还出现了短暂空白的情况。如何避免这种情况呢?—>路由懒加载
路由懒加载做了什么?
主要作用就是将路由对应的组件打包成一个个的js代码块,只有在这个路由被访问到的时候,才加载对应的组件
懒加载的方式
方式一:结合Vue的异步组件和Webpack的代码分析

const Home = resolve => {require.ensure(['../components/Home.vue'],() => 
{resolve(require('../components/Home.vue')) })};

方式二:AMD写法

const About = resolve => require(['../components/About.vue'],resolve);

方式三:在ES6中,有更简单的写法来组织Vue的异步组件和Webpack的代码分割

const Home = () => import('../components/Home.vue');

在这里插入图片描述
在这里插入图片描述

6.4.2 vue-router嵌套路由

比如在home页面中,我们希望通过/home/news和/home/message访问一些内容。一个路径映射一个组件,访问这两个路径也会分别渲染两个组件。路径和组件的关系如下:
在这里插入图片描述
实现路由嵌套有两个步骤:
①创建对应的子组件,并且在路由映射中配置对应的子路由
②在组件内部使用<router-view>标签
1、创建news和message子组件
在这里插入图片描述
2、在index.js中加载路由、创建路由对象、配置路径和路由映射子组件
在这里插入图片描述
ng)
3、在Home.vue中显示
在这里插入图片描述

6.4.3 vue-router参数传递

传递参数主要有两种类型:paramsquery
params的类型:

  • 配置路由格式:/router/:id
  • 传递的方式:在path后面跟上对应的值
  • 传递后形成的路径:/router/123,/router/abc

query的类型

  • 配置路由格式:/router,也就是普通配置
  • 传递的方式:对象中使用query的key作为传递方式
  • 传递后形成的路径:/router/?id=123,/router/?id=abc

新建一个Profile.vue组件;
在index.js中配置一个路由对象;

const Profile = () => import('../components/Profile.vue')
const routes=[
{
    path:'/profile',
    component:Profile
  }
]

在App.vue中

<router-link :to="{path:'/profile',
    query:{name:'why',age:18,height:1.88}}">档案</router-link>

在Profile.vue中显示

<template>
  <div>
    <h2>我是profile组件</h2>
    <h2>{{$route.query}}</h2>
    <h2>{{$route.query.name}}</h2>
    <h2>{{$route.query.age}}</h2>
    <h2>{{$route.query.height}}</h2>
  </div>
</template>

在App.vue中

<template>
<button @click="userClick">用户</button>
<button @click="profileClick">档案</button>
</template>
methods:{
  userClick(){
     this.$router.push('/user/' + this.userId)
  },
  profileClick(){
     this.$router.push({
       path:'/profile',
       query:{
         name:'kobe',
         age:18,
         height:2.10
       }
     })
  }
}

6.4.4 vue-router导航守卫

vue-router提供的导航守卫主要用来监听路由的进入和离开,提供了beforeEachafterEach的钩子函数,它们会在路由即将改变前和改变后触发
我们可以利用beforeEach来完成标题的修改:
首先,在钩子中定义一些标题,用meta来定义;
其次,利用导航守卫,修改标题
导航钩子的三个参数

  • to:即将要进入的目标的路由对象
  • from:当前导航即将要离开的路由对象
  • next:调用该方法后,才能进入下一个钩子

在index.js中

{
    path:'/home',
    component:Home,
    meta:{
      title:'首页'
    },
},
{
    path:'/about',
    component:About,
    meta:{
      title:'关于'
    },
},
{
    path:'/user/:userId',
    component:User,
    meta:{
      title:'用户'
    },
 },
 // 前置守卫(guard)
router.beforeEach((to,from,next)=>{
  // 从from跳转到to
  document.title = to.matched[0].meta.title
  next()
})
// 后置钩子(hook)
router.afterEach((to,from) => {

})

keep-alive

是Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
属性

  • include:字符串或正则表达式,只有匹配的组件会被缓存
  • exclude:字符串或正则表达式,任何匹配的组件都不会被缓存

router-view也是一个组件,如果直接被包含在keep-alive里面,所有路径匹配到的视图组件都会被缓存

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值