VUE2万字知识笔记

0. Vue 2 常用插件

  1. Vetur

    • 功能:提供 Vue 2 的语法高亮、智能感知、代码片段、Emmet 支持等。

    • 适用场景:Vue 2 项目开发。

    • 注意:Vetur 在 Vue 3 中已逐渐被 Volar 替代。

  2. Vue Router

    • 功能:用于 Vue 2 的路由管理。

    • 适用场景:单页面应用(SPA)的页面跳转和管理。

  3. Vuex

    • 功能:状态管理工具。

    • 适用场景:管理应用中的全局状态。

  4. Element UI

    • 功能:基于 Vue 2 的 UI 组件库。

    • 适用场景:快速搭建高质量的 Vue 2 项目界面。

  5. Axios

    • 功能:用于 HTTP 请求。

    • 适用场景:与后端 API 交互。

1. vue简介

构建用户界面,用 vue 往 html 页面中填充数据

1.1 vue 的两个特性

1.1.1 数据驱动视图
  • 数据的变化会驱动视图自动更新

  • 好处:程序员只管把数据维护好,那么页面结构会被 vue 自动渲染出来!

1.1.2 双向数据绑定

在网页中,form 表单负责采集数据,Ajax 负责提交数据

  • js 数据的变化,会被自动渲染到页面上

  • 页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中

注意:数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例)

ViewModel:new Vue()的整个块

View:script外body内

Mode:data指向的对象

1.2 vue的使用

 <!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
   <script src="./lib/vue-2.6.12.js"></script>
   <!-- 2. 创建 Vue 的实例对象 -->
   <script>
     // 创建 Vue 的实例对象
     const vm = new Vue({
       // el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
       el: '#app',
       // data 对象就是要渲染到页面上的数据
       data: {
         username: 'zhangsan'
       }
     })
   </script>

注:el挂载点可使用$mount在实例化后手动挂载vue实例

const vm = new Vue({
      data: {
        username: 'zhangsan'
      }
    })
vm.$mount('#app')    

1.3 vue 项目结构解析

  1. node_modules:这个目录包含了项目的所有依赖包,这些依赖包是通过npm(Node Package Manager)安装的。开发者通常不需要手动修改这个目录下的文件。

  2. public:存放一些公共的静态资源,如favicon.icoindex.html等。这些文件通常不会被Webpack处理。

  3. src:项目的源代码目录,包含所有的组件、页面、样式、脚本等资源。

    • assets:存放项目中需要用到的资源文件,如CSS、JavaScript、图片等。

    • components:存放Vue组件,这些组件是可复用的Vue实例,通常用于构建页面的各个部分。

    • router(如果存在):存放Vue Router的配置文件,用于定义应用的路由规则。

    • store(如果使用Vuex):存放Vuex的状态管理配置文件,用于管理应用的全局状态。

    • views(或page,取决于项目结构):存放Vue页面组件,这些组件通常对应于应用的不同页面或视图。

    • App.vue:项目的根组件,定义了整个应用的UI结构。

    • main.js:项目的入口文件,负责初始化Vue实例,并挂载到DOM上。

  4. build(在某些版本中可能不存在):用于存放Webpack相关的配置和脚本。这些配置定义了如何打包和构建项目。

  5. config(在某些版本中可能不存在):主要存放配置文件,用于区分开发环境和生产环境的不同。这些配置包括端口号、是否开启热加载、静态资源的相对路径等。

  6. dist(通过构建命令生成):默认通过npm run build命令打包生成的静态资源文件目录,用于生产部署。

  7. babel.config.js:Babel的配置文件,用于配置Babel转译JavaScript代码的规则。

  8. package.json:项目的配置文件,包含了项目的依赖信息、脚本命令、版本信息等。通过npm命令可以管理项目的依赖和脚本。

  9. README.md(可选):项目的说明文档,通常包含项目的介绍、安装方法、使用指南等信息。

  10. 其他文件:如.gitignore(指定git忽略跟踪的文件)、vue.config.js(Vue CLI的配置文件,用于自定义Webpack配置等)等。

1.4 vue项目运行流程

(1)App.vue用来编写待渲染的模板结构

(2)index.html中预留一个el区

(3)main.js把App.vue渲染到index.html所预留区域

2. vue 指令

2.1 内容渲染指令

2.1.1 v-text

缺点:会覆盖元素内部原有的内容!

{{ }} 插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!

注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!

 <div id="app">
     //表达式拼接 单双引号都可以使用
         //在标签上设置 h2内全部内容都被替换
         <h2 v-text="message+'!'"></h2>
         //插值表达式 只替换大括号的位置
         <h2>一个叫{{ message+"!" }}的人</h2>     
     </div>

2.1.2 v-html

在标签上设置 全部内容都被替换 html标签会被解析

 <div id="app">
         <h2 v-html="content"></h2>  
     </div>

2.2 属性绑定指令

  • 在 vue 中,可以使用 v-bind: 指令,为元素的属性动态绑定值;

  • 简写是英文的 :

  • 在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:

     <div :title="'box' + index">这是一个 div</div>
     <div v-bind:title="'box' + index">这是一个 div</div>

2.3 事件绑定

v-on: 简写是 @

<button @click="add"></button>
<input type="text" @keyup.enter="Add">
methods: {
   add() {
			// 如果在方法中要修改 data 中的数据,可以通过 this 访问到
			this.count += 1
   }
}

$event 的应用场景:$event 是一个特殊变量,它代表了原生 DOM 事件对象 , 如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event。例如:

<button @click="add(3, $event)"></button>

methods: {
   add(n, e) {
			// 如果在方法中要修改 data 中的数据,可以通过 this 访问到
			this.count += 1
   }
}

2.3.1 修饰符

事件修饰符

  • .prevent阻止事件默认行为

     //阻止链接默认跳转
     <a @click.prevent="xxx">链接</a>
  • .stop阻止事件冒泡

     <div @click="xxx">
         <button @click.stop="xxx">按钮</button>
     </div>

按键修饰符.后直接跟要点击的按键

   <div id="app">
     <input type="text" @keyup.esc="clearInput" @keyup.enter="commitAjax">
   </div>

2.4 v-model 指令

获取和设置表单元素的值(双向数据绑定)

 <body>
     <div id="app">
         <input type="button" value="修改message" @click="setM">
         <input type="text" v-model="message" @keyup.enter="getM">
         <h2>{{ message }}</h2>
     </div>
     <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
     <script>
         var app=new Vue({
         el:"#app",
         data:{
             message:"你好"
          },
          methods:{
             getM:function(){
                 alert(this.message)
             }
             setM:function(){
                 this.message="你你好好"
             }
          }
         })
     </script>
 </body>

v-model修饰符

number将绑定值转换为数字类型

trim自动去除输入值的首尾空白字符

lazy只有输入框失去焦点或按下回车才会更新数据

2.5 条件渲染指令

2.5.1 v-show

原理是:动态为元素添加或移除 display: none 样式,来实现元素的显示和隐藏

  • 如果要频繁的切换元素的显示状态,用 v-show 性能会更好

2.5.2 v-if

原理是:每次动态创建或移除元素,实现元素的显示和隐藏

  • 如果刚进入页面的时候,某些元素默认不需要被展示,而且后期这个元素很可能也不需要被展示出来,此时 v-if 性能更好

在实际开发中,绝大多数情况,不用考虑性能问题,直接使用 v-if 就好了!!!

//指令值为布尔值 true 或 false
<p v-if="true">被 v-if 控制的元素</p>
<p v-if="type === 'A'">良好</p>

<div v-if="type === 'A'">优秀</div>
<div v-else-if="type === 'B'">良好</div>
<div v-else-if="type === 'C'">一般</div>
<div v-else>差</div>

2.6 循环渲染指令

v-for根据数据生成列表结构

把作为母版的标签还有其内部所有内容根据数据的个数拷贝若干份

如果数据长度有改变 网页会动态改变

key的作用:列表数据变化时vue会尽可能的复用已存在的DOM元素,这种默认的性能优化策略会导致列表无法被正确的更新,key可以让vue跟踪每个节点身份,保证正确更新下提高渲染性能。

         <!-- 官方建议:只要用到了 v-for 指令,那么一定要绑定一个 :key 属性 -->
         <!-- 没有key在.vue文件会报错 -->
         <!-- 尽量把 id 作为 key 的值 -->
         <!-- 官方对 key 的值类型,是有要求的:字符串或数字类型 -->
         <!-- key 的值是千万不能重复的,否则会终端报错:Duplicate keys detected -->
         <tr v-for="(item, index) in list" :key="item.id">
           <td>{{ index }}</td>
           <td>{{ item.id }}</td>
           <td>{{ item.name }}</td>
         </tr>
         
 ​
   <div>
     <!-- 这里使用v-for来遍历genderOptions数组 -->
     <div v-for="option in genderOptions" :key="option.value">
       <!-- 生成复选框,id和value都设置为option.value -->
       <input type="checkbox" :id="option.value" :value="option.value">
       <!-- 生成标签,for属性设置为option.value,文本内容为option.text -->
       <label :for="option.value">{{ option.text }}</label>
     </div>
   </div>

2.7 自定义指令

2.7.1 私有自定义指令

directives节点下声明

 directives: {
     color: {
         //为绑定的HTML元素设置颜色
         bind(el) {
             el.style.color='red'
         }
     }
 }

使用自定义指令需要加上v-前缀

为自定义指令动态绑定参数:template使用自定义指令,通过等号方式

 data(){
     return{
         color:'red'
     }
 }
 <h1 v-color="color">内容</h1>

通过binding获取指令的参数值:自定义指令形参的第二个参数

bind函数只调用一次:当指令第一次被绑定到元素时,DOM更新时bind不被触发

update会在每次DOM更新时被调用

directives: {
	color: {
		bind(el,binding) {
			//通过binding对象的.value属性,获取动态的参数值
			el.style.color=binding.value
		},
		update(el,binding) {
			el.style.color=binding.value
		}
	}
}

如果bind和update函数中的逻辑完全相同,则对象格式的自定义指令可以简写为函数格式

directives: {
	color:(el,binding) {
			el.style.color=binding.value
		}
}
2.7.2 全局自定义指令

通过Vue.directive()声明

Vue.directive('color',function(el,binding){
	el.style.color=binding.value
})

3. 过滤器Filters

常用于文本的格式化,本质是一个js函数,定义于js尾部,由管道符’|‘调用

  1. 在过滤器函数中,一定要有 return 值

  2. 如果全局过滤器和私有过滤器名字一致,此时按照“就近原则”,调用的是”私有过滤器“

3.1 私有过滤器

定义

     const vm = new Vue({
       el: '#app',
       data: {
         message: 'hello vue.js'
       },
       //私有过滤器
       filters: {
      //在过滤器的形参中,可以获取到“管道符”前面待处理的那个值
         capi(val) {
           // 字符串有 charAt 方法,这个方法接收索引值,表示从字符串中把索引对应的字符,获取出来
           // val.charAt(0)
           const first = val.charAt(0).toUpperCase()
           // 字符串的 slice 方法,可以截取字符串,从指定索引往后截取
           const other = val.slice(1)
           return first + other
         }
       }
     })

使用方法

 //方法1:插值表达式
 <p>message 的值是:{{ message | capi }}</p>
 //方法2:v-bind属性绑定
 <div v-bind:id="message | capi"></div>

串联调用

 <p>message 的值是:{{ message | capi | cap }}</p>

应用于日期格式化时可以js库的day.js

3.2 全局过滤器

 // 使用 Vue.filter() 定义全局过滤器
     Vue.filter('capi', function (str) {
       const first = str.charAt(0).toUpperCase()
       const other = str.slice(1)
       return first + other + '~~~'
     })

4. watch 侦听器

允许开发者监视数据的变化从而针对数据变化做特定操作

侦听器本质上是一个函数,要监视哪个数据的变化,就把数据名作为方法名即可 ,侦听器参数新值在前,旧值在后

带参的侦听器第一个参数固定是侦听的数据,其余参数项才是传递的参数

4.1 方法格式的侦听器

  • 缺点1:无法在刚进入页面的时候,自动触发!!!

  • 缺点2:如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器!!!

 const vm = new Vue({
       el: '#app',
       data: {
         username: 'admin',
         info: {age: 18}
       },
       
       // 所有的侦听器,都应该被定义到 watch 节点下
       
       watch: {     
         //1.数据侦听-方法格式
         username(newVal,oldVal) {
                 console.log(newVal)
         }    
       
     })

4.2 对象格式的侦听器

  • 好处1:可以通过 immediate 选项,让侦听器自动触发!!!

  • 好处2:可以通过 deep 选项,让侦听器深度监听对象中每个属性的变化!!!

	  //2.对象侦听-对象格式
      //handler是固定写法,表示当username值变化时自动调用handler处理函数
      watch:
          info: {
            handler(newVal){
                console.log(newVal.age)
            },
            //表示页面初次渲染好之后,立即触发当前watch侦听器
            immediate: true
            //不加此属性侦听不到对象值的变化
            deep: true
     	 }
      }
      
      //3.侦听对象单个属性的变化
      watch:{
           'info.age': {
            handler(newVal){
                console.log(newVal.age)
            }
          }
         }

5. 计算属性computed

在method和template都可以使用,需要return一个计算结果

特点:

  1. 定义的时候,要被定义为“方法”, 但本质是一个属性

  2. 在使用计算属性的时候,当普通的属性使用即可

  3. 计算属性会缓存计算的结果,只有其依赖数据变化时,才会重新进行运算

好处:

  1. 实现了代码的复用

  2. 只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值!

     var vm = new Vue({
           el: '#app',
           data: {
             // 红色
             r: 0,
             // 绿色
             g: 0,
             // 蓝色
             b: 0
           },
           methods: {
             // 点击按钮,在终端显示最新的颜色
             show() {
               console.log(this.rgb)
             }
           },
           // 所有的计算属性,都要定义到 computed 节点之下
           // 计算属性在定义的时候,要定义成“方法格式”
           computed: {
             // rgb 作为一个计算属性,被定义成了方法格式
             rgb() {
               return `rgb(${this.r}, ${this.g}, ${this.b})`
             }
           }
         })

6. vue-cli 的使用

vue-cli脚手架是Vue.js开发的标准工具,简化基于webpack创建工程化的Vue项目的过程

安装vue-cli&创建vue项目

在终端下运行如下的命令安装

 npm install -g @vue/cli

创建指定名称的项目:

 vue create 项目的名称

注:因系统上禁止运行脚本,此时可能报错,此时以管理员身份打开powershell

 执行语句:get-ExecutionPolicy,得到回复是Restricted,表示状态是禁止的。
 执行:set-ExecutionPolicy RemoteSigned回车后,输入Y。
 执行:set-ExecutionPolicy RemoteSigned回车后,输入A
 若执行set-ExecutionPolicy RemoteSigned回车后,输入A报错,
 然后按照提示输入:Set-ExecutionPolicy -Scope CurrentUser
 然后再输入:RemoteSigned ,就会出现如上图的选择,最后输入A则成功。

7. vue组件

组件后缀名.vue

7.1 安装与使用

注册私有组件:a的components内注册了c,那么c就不能用在除a以外的其他组件

//组件App.vue

 <template>
   <div>
     111
     <!-- 3.使用组件 -->
     <HelloWorld/>
   </div>
 </template>
 ​
 <script>
 // 1.导入子组件
  import HelloWorld from './components/HelloWorld.vue'
 ​
 // 2.注册子组件
 export default {
    components: {
      HelloWorld
    }
 }
 </script>
 ​
 <style>
 ​
 </style>

注册全局组件

//main.js

 ​
 import Vue from 'vue'
 import App from './App.vue'
 // 1.引入组件
 import HelloWorld from './components/HelloWorld.vue'
 ​
 Vue.config.productionTip = false
 // 2.Vue.component()方法注册全局组件
 //参数1:字符串格式 表示组件的注册名称
 //参数2:需要被全局注册的那个组件
 Vue.component('HelloWorld',HelloWorld)
 ​
 new Vue({
   render: h => h(App),
 }).$mount('#app')
 ​

//组件App.vue

 <template>
   <div>
     111
     <!-- 3.使用组件 -->
     <HelloWorld/>
   </div>
 </template>
 ​
 <script>
 export default {
 ​
 }
 </script>
 ​
 <style>
 ​
 </style>

7.2 组成

template(模板结构)、script(组件js行为)、style(组件样式)

  • template:容器标签,只有包裹性质作用,不会被渲染成真正的DOM元素,template只能包含唯一的根节点

  • script:封装组件的js业务逻辑,vue中的data必须是方法

  • style:可选,编写美化组件的UI解构,此标签上添加lang="less"可用less语法编写

7.3 属性

7.3.1 props

组件自定义属性,提高组件复用性

 //数组格式定义
 props:['init']
 //对象格式定义
 props: {
     init: {
         //default定义属性默认值
         //type定义属性值类型,传来的值若不符则会在终端报错
         //required属性设置为必填项,强制用户必须传递属性的值
         default: 0,
         type: Number,
         required: true
     }
 }

父组件为props提供值

 //1.静态值
 <ChildComponent title="Hello, World!" />
 ​
 //2.动态值
 <template>
   <div>
     <ChildComponent :title="parentTitle" />
   </div>
 </template>
 ​
 <script>
 export default {
   data() {
     return {
       parentTitle: 'Dynamic Title from Parent'
     };
   }
 };
 </script>
 ​
 //3.传递对象,数组
 <template>
   <div>
     <ChildComponent :user="{ name: 'John Doe', age: 30 }" />
   </div>
 </template>
 ​
 //4.传递计算属性
 <template>
   <div>
     <ChildComponent :isActive="isActiveComputed" />
   </div>
 </template>
 ​
 <script>
 export default {
   computed: {
     isActiveComputed() {
       // 根据某些条件返回true或false
       return this.someCondition;
     }
   }
 };
 </script>

注:此属性可读,不能直接修改,可通过转存到data中,data可读可写

 props: {'init'},
 data(){
     return {
         count: this.init
     }
 }

7.3.2 scoped

为style节点提供,可以解决样式冲突问题

vue组件中的样式会全局生效,会发生多个组件样式冲突问题

 //解决方法1:可以为子组件分配唯一的自定义属性,通过属性选择器控制样式作用域
 <template>
     <div class="container" data-v-001>
         <h3 data-v-001>轮播图组件</h3>
     </div>
 </template>
 ​
 <style>
     .container[data-v-0001]{
         border: 1px solid red;
     }
 </style>
 //解决方法2:子组件添加scoped
 <template>
     <div class="container">
         <h3>轮播图组件</h3>
     </div>
 </template>
 ​
 <style scoped>
     .container {
         border: 1px solid red;
     }
 </style>

/deep/样式穿透:当父组件需要修改子组件内部的样式,但子组件的样式被封装在组件内部,无法直接通过父组件的样式进行修改时,可以使用样式穿透技术

因为node-sass的版本过高或者sass-loader的版本不匹配导致的/deep/会报错,这时换成vue3的::v-deep就可以了

 <style lang="less" scoped>
 //不加时,生成的选择器样式为.title[data-v-052242de]
     .title {
         color:blue
     }
 ​
 //加时,生成的选择器样式为[data-v-052242de] .title
 /deep/ .title {
     color:blue
 }
 </style>

7.4 组件生命周期

生命周期:一个组件从创建->运行->销毁的整个阶段,强调的是一个时间段

生命周期函数:vue框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行,强调的是一个时间点

生命周期函数分类:

组件生命周期的第一个阶段(组件创建阶段):beforeCreate->created->beforeMount->mounted

组件生命周期的第二个阶段(组件运行阶段):beforeUpdate->updated

组件生命周期的第三个阶段(组件销毁阶段):beforeDestroy->destroyed

Created最早发Ajax请求,Mounted最早操纵DOM节点,Updated数据和结构都是最新的

一个组件创建阶段只执行一次,运行阶段0-n次,销毁阶段也只执行一次生命周期函数与data、method同级

7.5 组件之间的数据共享

组件常见关系:父子关系、兄弟关系

7.5.1 父子组件之间的数据共享

父->子:使用自定义属性

 //父组件
 //2.通过动态绑定属性,在父组件新值传给子组件的属性
 <son :msg="message" :user="userinfo"><son>
 data(){
     return{
         message:'hello vue.js',
         userinfo:{name:'zs',age:20}
     }
 }
 ​
 //子组件
 <template>
     <div>
         <h5>son</h5>
         <p>{{ msg }}</p>
         <p>{{ user }}</p>
     </div>
 </template>
 //1.在子组件自定义属性,并在template放入
 props:['msg','user']

子->父:使用自定义事件

 //子组件
 //1.子组件通过$emit提交值
 export default {
     data(){
         return {count:0}
     },
     methods:{
         add(){
             this.count +=1
             //修改数据时通过$emit触发自定义事件
             this.$emit('numchange',this.count)
         }
     }
 }
 //父组件
 //2.父组件自定义事件,通过参数获取值
 <Son @numchange="getNewCount"></son>
 export default {
     data(){
         return {countFromSon:0}
     },
     methods:{
         getNewCount(Val){
             this.countFromSon=val
         }
     }
 }
7.5.2 兄弟组件之间的数据共享

vue2中共享方案为EventBus

1.创建EventBus.js模块。并向外共享一个Vue实例对象

2.在数据发送方,调用bus.$emit(‘事件名称’,要发送的数据)方法触发自定义事

3.在数据接收方,调用bus.$on(‘事件名称’,事件处理函数)方法注册一个自定义事件

 //兄弟组件A(数据发送方)
 //2.引入eventBus.js,通过emit触发自定义事件
 import bus from './eventBus.js'
 export default{
     data(){
         return {
             msg:'hello vue.js'
         }
     },
     methods:{
         sendMsg(){
             bus.$emit('share',this.msg)
         }
     }
 }
 //eventBus.js
 //1.创建eventBus.js
 import Vue from 'vue'
 //向外共享vue的实例对象
 export default new Vue()
 //兄弟组件C(数据接收方)
 //3.引入eventBus.js,created生命周期函数接收数据
 import bus from './eventBus.js'
 export default{
     data(){
         return {
             msgFromLeft:''
         }
     },
     created() {
         bus.$on('share',val=>{
             this.msgFromLeft=val
         })
     }
 }

7.6 ref引用

在不依赖jQuery情况下获取DOM元素或组件的引用

每个vue组件实例都包含一个$refs对象,里面存着对应的DOM元素或组件的引用,默认指向一个空对象

 <h3 ref="myh3">组件</h3>
 <button @click="getRef">获取</button>
 <my-counter ref="counterRef">组件</my-counter>
 ​
 methods: {
     getRef(){
         //引用页面上的DOM元素
         console.log(this.$refs.myh3)
         //引用页面上的组件实例
         console.log(this.$refs.counterRef)
     }
 }

应用

通过布尔值inputVisible来控制组件中的文本框与按钮的按需切换

文本框自动获取焦点可添加ref引用,并调用原生DOM的focus()

this.$nextTick(cb)方法会把cb回调推迟到下一个DOM更新周期之后执行,即等组件的DOM更新完成后在执行cb回调函数,从而保证cb回调函数可以操作到最新DOM元素上

 <template>
     <input type="text" v-if="inputVisible" ref="ipt">
     <button v-else @click="showInput">显示input输入框</button>
 </template>
 <script>
     export default{
         data(){
             return{
                 inputVisible:false
             }
         },
         methods:{
             showInput(){
                 this.inputVisible=true
                 //把对input的操作推迟到下次DOM更新之后,否则页面上根本不存在文本框内元素
                 this.$nextTick(()=>{
                     this.$refs.ipt.focus()
                 })  
             }
         }
     }
 </script>

7.7 动态组件

动态切换组件的显示与隐藏

7.7.1 动态组件渲染

vue提供内置的component组件来实现

默认情况下,切换动态组件无法保持组件状态,此时可以使用vue内置keep-alive组件来保持动态组件状态(假设每个组件 都有自增一的效果,每次切换组件时,计数器都会重置为0)

keep-alive的include属性用来指定只有名称匹配的组件会被缓存,多个组件用英文逗号隔开

keep-alive生命周期函数:

组件被缓存时自动触发deactived生命周期函数

组件被激活时自动触发activited生命周期函数

 <template>
   <div>
     <!--2.当前要渲染的组件名-->
     <button @click="currentComponent = 'ComponentA'">显示组件 A</button>
     <button @click="currentComponent = 'ComponentB'">显示组件 B</button>
     <!-- 使用 is 属性动态绑定组件 -->
     <!--1.通过is属性动态指定要渲染的组件,component起到占位符的作用-->
     <component :is="currentComponent"></component>
   </div>
 </template>
 ​
 <script>
 import ComponentA from './ComponentA.vue';
 import ComponentB from './ComponentB.vue';
 ​
 export default {
   data() {
     return {
       // 3.当前要显示的组件名称
       currentComponent: 'ComponentA' // 默认显示组件 A
     };
   },
   components: {
     // 注册局部组件
     ComponentA,
     ComponentB
   }
 };
 </script>

7.8 插槽slot

封装组件时希望把不确定的,由用户指定的部分定义为插槽,可以把插槽认为组件封装期间,为用户预留的内容的占位符

若封装组件没有预留任何<slot>插槽,则用户提供的自定义内容会被丢弃

组件使用者若没有为插槽提供内容,则后备生效

 //组件
 <template>
     <p>内容1</p>
     <slot>后备内容</slot>
     <p>内容n</p>
 </template>
 ​
 //使用页面
 <my-com-1>
     <p>用户自定义内容</p>
 </my-com-1>
7.8.1 具名插槽

封装组件时需要预留多个插槽节点,则需要为每个插槽指定具体的name名称

 //子构件
 <div class="container">
     <header>
     <slot name="header"></slot>
     </header>
     <main>
     <slot></slot>
     </main>
     <footer>
     <slot name="footer"></slot>
     </footer>
 </div>

为具名参数提供内容可以在一个<template>元素上使用v-slot(简写为#),并以v-slot的参数的形式提供其名称

默认值default,只能用在template身上

//父构件
<my-com-2>
	<template #header>
		<h1></h1>
	</template>
	
	<template #default>
		<h2></h2>
	</template>
	
	<template #footer>
		<h3></h3>
	</template>
</my-com-2>
7.8.2 作用域插槽

为预留的插槽绑定props数据的为作用域插槽

<tbody>
	<slot v-for="item in list" :user="item"></slot>
</tbody>

使用:v-slot接收作用域插槽对外提供的数据

<my-com-3>
	//1.接收作用域插槽对外提供的数据
	<template #default="data">
		//2.使用作用域插槽的数据
		<h2>{{ data.user }}</h2>
	</template>
</my-com-3>

作用域插槽对外提供的数据对象,使用解构赋值简化数据接收过程

//在Vue中,如果有多个默认插槽,会导致无法正确分发内容,因为默认插槽只能有一个。这可能导致了父组件的作用域插槽绑定到了错误的插槽上。

<my-com-3>

	<template #default="{user}">
		<h2>{{ user.id }}</h2>
		<h2>{{ user.name }}</h2>
		<h2>{{ user.state }}</h2>
	</template>
	
</my-com-3>

8.路由

8.1 前端路由的概念与原理

路由:对应关系

前端路由:Hash地址与组件之间的对应关系

SPA:一个web网站只有唯一的一个HTML页面,所有组件的展示与切换都在这个唯一的页面完成,此时不同组件的切换要通过前端路由来实现

SPA与前端路由:SPA项目中,不同功能的切换要通过前端路由来实现

8.1.1 前端路由的工作方式

1.用户点击了页面上的路由链接

2.导致URL地址栏中的Hash值发生变化

3.前端路由监听到了Hash地址的变化

4.前端路由把当前Hash地址对应的组件渲染到浏览器中

8.1.2 实现简单的前端路由

<template>
  <div class="box">
  <!--2.在App.vue组件中,为a链接添加对应的hash值-->
    <a href="#/home">首页</a>
    <a href="#/movie">电影</a>
    <a href="#/Small">关于</a>
    <!--1.通过conponent标签,结合comName动态渲染组件-->
    <component :is='comName'></component>
  </div>
</template>

<script>
import Small from './components/Small.vue'
import Home from './components/Home.vue'
import Movie from './components/Movie.vue'
export default {
    data(){
        return{
            comName:'Home'
        }
    },
    components:{
        Small,
        Home,
        Movie
    },
    //3.在created生命周期函数中监听到浏览器地址栏中hash地址的变化
    created(){
        window.addEventListener('hashchange',()=>{
            switch(location.hash){
                case '#/home':
                    this.comName='Home'
                    break;
                case '#/movie':
                    this.comName='Movie'
                    break;
                case '#/Small':
                    this.comName='Small'
                    break;
            }
        })
    }

}
</script scoped>

<style>

</style>

8.2 vue-router的基本使用

vue.js的官方路由解决方案,结合VUE管理SPA项目中组件的切换

文档:Vue Router | Vue.js 的官方路由

8.2.1 安装与配置

1.安装vue-router包

npm i vue-router@3.5.2 -s

2.创建路由模块:新建src/router/index.js路由模块

//1.导入Vue和VueRouter的包
import Vue from 'vue'
import VueRouter from 'vue-router'

//2.调用Vue.use()函数,把VueRouter安装为Vue插件
Vue.use(VueRouter)

//3.创建路由的实例对象
const router=new VueRouter()

//4.向外共享路由的实例对象
export default router

3.导入并挂载路由模块:在src/main.js入口文件中

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

//1.导入路由模块
import router from '@/router'

new Vue({
	render:h=>(App),
	//2.挂载路由模块
	router:router
}).$mount('#app')

4.声明路由链接和占位符:src/App.vue组件中使用vue-router提供的<router-link>和<router-view>

<template>
	<div class="app-container">
		<h1>App组件</h1>
		//1.定义路由链接
		<router-link to="/home">首页</router-link>
		<router-link to="/movie">电影</router-link>
		<router-link to="/about">关于</router-link>
		
		//2.定义路由的占位符
		<router-view></router-view>
	</div>
</template>
8.2.2 声明路由的匹配规则

src/router/index.js中通过routes数组声明

//1.导入需要使用路由切换展示的组件
import Home from '@/components/Home.vue'
import Movie from '@/components/Movie.vue'
import About from '@/components/About.vue'

//2.创建路由的实例对象
const router = new VueRouter({
	routes:[
		{path:'/home',component:Home},
		{path:'/movie',component:Movie},
		{path:'/about',component:About}
	]
})

8.3 vue-router的常见用法

8.3.1 路由重定向

用户在访问地址A时强制用户跳转到地址C,通过路由规则的redirect属性指定一个新的路由地址

const router = new VueRouter({
	routers:[
		{path:'/',redirect:'/home'},
		{path:'/home',component:Home}
	]
})

8.3.2 嵌套路由

通过路由实现组件的嵌套

例如:点击父级路由<router-link>显示模板内容<router-view>,模板内容中又有子级路由链接,点击子级路由链接显示子级模板内容

8.3.3 声明子路由链接和子路由占位符

1.About.vue组件中声明子路由链接和子路由占位符

<template>
 <div class="about-container">
 	<h3>about组件</h3>
 	//在关于页面中,声明两个子路由链接
 	<router-link to="/about/tab1">tab1</router-link>
 	<router-link to="/about/tab2">tab2</router-link>
 	//在关于页面中,声明子路由占位符
 	<router-view></router-view>
 </div>
</template>

在src/router/index.js路由模块中导入需要的插件,使用children属性声明子路由规则

import Tab1 from '@/components/Tab1.vue'
import Tab2 from '@/components/Tab2.vue'


const router = new VueRouter({
	routes:[{
		//about的路由规则(父级)
		path:'/about',
		component:About,
		children:[
			{path:'tab1',component:Tab1},
			{path:'tab2',component:Tab2}
			//通配符路由 应该放在所有明确的路由定义之后,否则它会捕获所有路径,导致其他路由无法正确匹配。
			{path:'*',redirect:'/about/tab2'}
			
		]
	}]
})

8.3.4 动态路由匹配

路由规则的复用性差

概念:把Hash地址中的可变的部分定义为参数项,从而提高路由规则的复用性

vue-router中使用(:)来定义路由参数项

{path:'/movie/1',component:Movie},
{path:'/movie/2',component:Movie},
{path:'/movie/3',component:Movie},
//合并
{path:'/movie/:id',component:Movie}
<router-link to="/movie/1">子组件n</router-link>

1.$route.params参数对象:访问动态匹配的参数值

//index.js

{path:'newMovie/:id',component:NewMovieAll}

//NewMovieAll.vue

<template>
  <div class="box">
    <h1>看新电影n</h1>
     {{ this.UrlId }}
  </div>
</template>

<script>
export default {
    data(){
        return{
            UrlId: 0
        }
    },
    created(){
        this.UrlId = this.$route.params.id
    }


}
</script>

2.使用props接受路由参数:在路由规则中开启props传参

//1.index.js中在定义路由规则时声明props:true选项
//即可在Movie组件中以props形式接收到路由规则匹配到的参数项
{path:'/movie/:id',component:Movie,props:true}

<template>
	//3.直接用props中接收的路由参数
	<h3>{{ id }}</h3>	
</template>

<script>
export default {
	props:'id'//2..vue中使用props接收路由规则中匹配到的参数项
}
</script>

8.3.5 声明式导航&编程式导航

在浏览器中,点击链接实现导航的方式,叫声明式导航。

  • 如普通网页点击<a>链接,vue项目中点击<router-link>

  • 常用于静态页面跳转,导航栏侧边栏构建,列表页面的链接

在浏览器中,调用API方法实现导航的方式,叫编程式导航。

  • 如普通网页中调用location.href跳转到新页面

  • 表单提交后的跳转,登录注册的跳转,用户输入或动态数据决定跳转路径,返回上下页,在跳转前执行某些逻辑,例如权限检查、表单验证

vue-router中的编程式导航API

1.this.$router.push(‘hash 地址’):跳转到指定的hash地址,并增加一条历史记录

<template>
	<div class="home-container">
		<h3>Home组件</h3>
		<button @click="gotoMovie">跳转到Movie页面</button>
	</div>
</template>

<script>
export default {
	methods:{
		gotoMovie(){this.$router.push('/movie/1')}
	}
}
</script>

2.this.$router.replace(‘hash 地址’):跳转到指定的hash地址,并替换当前的历史记录

  • 与push的区别:push会增加一条历史记录,replace不会增加历史记录,而是替换掉当前的历史记录

3.this.$router.go(数字 n):实现导航历史前进、后退

  • $router.back()历史记录中后退到上一个页面

  • $router.forward()历史记录中前进到下一个页面

    <template>	
    	<h3>{{ id }}</h3>
    	<button @click="goBack">后退</button>	
    </template>
    
    <script>
    export default {
    	props:['id'],
    	methods:{
    		goBack(){this.$router.go(-1)}
    	}
    }
    </script>

8.3.6 导航守卫

控制路由的访问权限,如登录

全局前置守卫:导航跳转时触发,此时可以进行访问权限控制

//创建路由实例对象
const router = new VueRouter({...})

//调用路由实例对象的beforeEach方法声明全局前置守卫
//每次路由导航跳转时自动触发fn回调函数
router.beforeEach(fn)

1.守卫方法的3个形参:router.beforeEach(to,from,next)

  • to是将要访问的路由的信息对象

  • from是将要离开的路由的信息对象

  • next是一个函数,调用next()表示放行,允许这次路由导航

2.next函数的3种调用方式

  • 当前用户拥有后台主页的访问权限,直接放行:next()

  • 当前用户没有后台主页的访问权限,强制其跳转到登陆页面:next(‘/login’)

  • 当前用户没有后台主页的访问权限,不允许跳转到后台页面:next(false)

3.控制后台主页的访问权限

router.beforeEach((to,from,next)=>{
	if(to.path === '/main'){
		const token = localStorage.getItem('token')
		if(token){
			next()//访问的是后台主页,且具有token值
		}else{
			next('/login')//访问的是后台主页,且没有token值
		}
	{}}else{
		next()//访问的不是后台主页,直接放行
	}
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值