vue 知识点

vue2知识点

这里写目录标题

1.插值语法 Mustache语法

解析 标签体 的内容

{{js表达式}} 不能是js语句

  <div id='app'>
      {{msg}}
    </div>
    
修改差值写法
在配置项中
delimiters:['${','}']
 <div id='app'>
      ${msg}
    </div>

2.Vue指令

1.内容渲染指令

2.条件渲染指令

3.属性绑定指令

4.事件绑定指令

5.列表渲染指令

6.双向数据绑定

(1)内容渲染指令
1.1 v-html
  <div id='app' v-html="msg"></div>
  
  data() {
        return {
          msg: `<span style="color:red;">我是span</span>`
        }
      },

不建议用:如果用户输入个脚本,解析成html就完蛋了!

1.2 v-text

将值放到指定节点中 不识别为dom结构

(2)条件渲染指令
2.1 v-if 条件渲染
<span v-if="表达式">显示</span>

表达式会被转换成boolean值,true显示,false不显示span(浏览器压根不渲染,在dom结构中也不存在)
2.2 v-else
与v-if搭配使用

<span v-if="false">显示1 if中的</span>
<span v-else>显示2 else中的</span>
两者只能显示一个
2.3 v-else-if

多个判断分支

<span v-if="score >= 60 && score < 80">及格</span>
<span v-else-if="score >= 80">优秀</span>
<span v-else-if="score < 60">回炉重造</span>
2.4 v-show
<div v-show="bool">显示</div> 
2.5 v-if和v-show的区别

v-if是加载渲染,而v-show改变状态改的是css样式。

2.6 v-once

使用此指令的元素 只会渲染一次(一次性指令)

后续数据的更新 不会引起视图的变化
<div v-once>{{bool}}</div>

(3)属性绑定指令

v-bind 动态绑定属性
<!-- 普通dom属性绑定 -->
    <!-- <img src="01.jpg" alt=""> --> 
<img v-bind:src='imgData' v-bind:title="alt">

    <!-- 简写方式  -->
    <img :src='imgData' :title="alt">

给挂载的入口节点dom身上操作不行

基础写法
.active {
      background-color: orange;
    }
<h2 v-bind:class="activeClass">我是h2标签</h2>

activeClass: 'active'
对象写法
<h2 v-bind:class="{ active:bool }">我是h2标签</h2>
数组语法写法
<h2 :class="['active','line']">我是h2标签</h2>
<h2 :class="['active',{line:bool}]">我是h2标签</h2>
style属性的绑定
对象写法
 <p :style="{backgroundColor:bgColor,height:heightValue + 'px'}">p</p>
 
 
数组写法
<p :style="[
      {backgroundColor:bgColor,height:heightValue + 'px'},
      {opacity:opacityValue}
    ]"></p>
(4)事件绑定指令

v-on 事件绑定

v-on:type='事件函数' type 点击 拖拽 划入 滑出 键盘 鼠标…

事件函数 普通函数的定义位置 放在methods里面

函数作为事件函数使用时 不需要加 ( ) 执行

methods里面直接定义的 函数 不要写 箭头函数 要不然 this会指向window

事件函数传参情况

1.不传参数,不加括号

2.正常传参 加括号写实参 需要定义形参

<button @click="addNum(1)">1</button>
接受event事件对象
<button @click="addNum">1</button>

methods: {
        addNum(value) {
          console.log(value); //PointerEvent {.....}
          this.num += 1
        }
      }
 //定义形参,不传实参
又想传普通参,又想传事件对象
实参需要传 $event 其他一一对应即可  
<button @click="addNum(1,$event)">1</button>

methods: {
        addNum(value, event) {
          console.log(value, event);
          this.num += 1
    }
}
v-on多事件处理处理器

此时就要带括号执行了!

   <button @click="changeNum(),changeFn()">点击 ++ </button>
v-on事件修饰符

.stop阻止事件冒泡(常用)

(不想泡泡冒到父级+祖先身上 就在该dom上 阻止事件冒泡 )

.prevent 阻止默认事件 (常用)

<input type="submit" @click.prevent> (有默认提交效果)
(5)列表循环指令

v-for

数组

对象

字符串

数组

(6)双向数据绑定

v-model

语法糖写法
~	<input type="text" v-model:value="msg"> 默认绑定value 不需要 写也可以 
	<input type="text" v-model="msg">

   new Vue({
            el: '#app',
            data() {
                return {
                    msg: '123'
                }
            },
            methods: {
                changeMsg(event) {
                    console.log(event);
                    this.msg = event.target.value
                }
            }
        }) 
原生的写法

利用事件对象中的target里面的value值来手动赋值设置

<input type="text" v-model:value="msg"> 默认绑定value 不需要 写也可以 
<input type="text" v-model="msg">
data() {
        return {
          msg: '123'
        }
      },
V-model修饰词

.lazy 只有在input框内 回车 才会触发数据的更新(要不然input框就是实时更新)

<input type="text" v-model.lazy="msg"

.number 尝试将输入的值转换成数值类型

<input type="text" v-model.number="num1">
<input type="text" v-model.number="num2">

.trim 去除首尾空格

<input type="text" v-model.trim="msg">
v-model的几个应用

绑定复选框

    <span>爱好: </span>
    <label for="checkbox1">睡觉</label>
    <input type="checkbox" v-model="checkboxValue" id="checkbox1" value="sleep">

    <label for="checkbox2">花钱</label>
    <input type="checkbox" v-model="checkboxValue" id="checkbox2" value="money">

    <label for="checkbox3">吃东西</label>
    <input type="checkbox" v-model="checkboxValue" id="checkbox3" value="eat">

单选按钮

<span>性别: </span>
    <label for="checkbox1">女</label>
    <input type="radio" v-model="checkboxValue" id="checkbox1" value="woman">

    <label for="checkbox2">男</label>
    <input type="radio" v-model="checkboxValue" id="checkbox2" value="man">

    <label for="checkbox3">其他</label>
    <input type="radio" v-model="checkboxValue" id="checkbox3" value="else">

下拉选框

<select v-model="selectValue">
    <option disabled>请选择男朋友</option>
    <option>吴彦祖</option>
    <option>彭于晏</option>
    <option>胡歌</option>
</select>
    
 selectValue: ''



多选下拉
<select v-model="selectValue" multiple>
      <option disabled>请选择男朋友</option>
      <option>吴彦祖</option>
      <option>彭于晏</option>
      <option>胡歌</option>
    </select>

selectValue: [] 数组 

3.key属性

使用了v-for就需要给节点带上key值 值必须是独一无二的值 一般是对象内的id

为什么要使用key值?

vue采用的就近原则,当页面重新渲染的时候,它会找就近的位置进行渲染,使用key值就相当于给节点设置了一个身份!

v-for和v-if不要用在同一个节点上

能在父级上用v-if 就在父级上面用

不能的话 外面就包一个父级(template)

<template v-if="true">
	<span v-for="item in arr" :key="item.name">{{item.name}}</span>
</template>

4.数组的响应式监听

this.arr[4]=obj

类似于上面的赋值操作,不能引起响应式变化

解决方法
(可以修改原数组这个api)
push
shift
unshift
sort
splice
reverse
pop …

(不修改原数组的api)
map
reduce
forEach
concat
slice
filter …

5.对象增加属性和删除属性

实例内
添加属性
this. s e t ( t a r g e t O b j , k e y , v a l u e ) 删除属性 t h i s . set(targetObj,key,value) 删除属性 this. set(targetObj,key,value)删除属性this.delete(targetObj,key)

  <button @click="changeMsg">点我</button>
 
 new Vue({
            el: '#app',
            data() {
                return {
                    msg: {
                        name:'jack',
                        age:183
                    }
                }
            },
            methods: {
                changeMsg(event) {
                   this.$set(this.msg,"gonghui",1232)
                   console.log(this.msg);
                }
            }
        }) 
        
 放到外面
vm.$set(vm.obj,key,value)

Vue构造函数上也有这个方法 
Vue.set(vm.obj, 'hobby', '花钱')       

上述代码是可以发生响应式变化的

// this.obj.hobby = '花钱'
// delete this.obj.age
无法引起响应式变化 (数据变了 但是页面dom上不发生更新)

6.计算属性computed

是一个加工厂,用来加工data中的数据(当然其他地方的数据也可以加工)

用的时候 不要加括号 因为不是函数

getter和setter
computed: {
        // handleName() {
        //   return this.myname + '~~~~~'
        // }

        handleName: {
          get: function () {
            console.log('-----get------');
            return this.firstname + ' ' + this.lastname
          },
          set: function (newValue) {
            console.log('-----set------', newValue);
            this.firstname = newValue.split(" ")[0]
            this.lastname = newValue.split(" ")[1]
          }
        }

      }
getter和setter触发的时机

首次获取(模板内使用了该计算属性 或者你在其他地方使用 )这个计算属性 触发getter
因为有缓存 其他多次获取不会触发

当计算属性 依赖的数据(本身是响应式数据==> 比如data中的数据 )发生变化时 会再次触发 getter

当 对计算属性 有赋值操作时 会触发 setter ( 一般情况很少用到 )

7.watch侦听 / 监听

监听 顾名思义就是监督

num: { 
          handler(newValue, oldValue) {
            console.log('新值', newValue);
            console.log('旧值', oldValue);
          },
          // 不管num发没发生变化 先执行回调函数 
          immediate: true,
          // 深度监听 默认是false
          deep: true // 视情况使用 
        }
对对象的监听 非深度

( 一层监听 只有 = 赋值修改时 才会触发监听 ) 对内层属性的修改不能监听

obj: {
    handler(newV, oldV) {
    console.log(newV, oldV);
    },
    // 开启深度监听
    deep: true
}

只想监听 对象的某个属性 (如果属性是基础值类型 不需要deep true 如果属性 还是对象类型  deep:true加上才能深度监听 )
"obj.name": {
          handler(newV, oldV) {
            console.log(newV, oldV);
          }
        }

8.生命周期(钩子)

img

9.注册全局组件

分成三步

第一步 
  // 在这个配置项中  vm的配置对象内的配置属性 几乎都可以使用 除了 el 因为子组件不需要挂载
    // 先配置子组件配置对象 然后先注册  再生成vm实例对象 
    const child = Vue.extend({
      // 给child定义的数据 
      data() {
        return {
          msg: '我是儿子'
        }
      },
      mounted() {
        console.log(this);
      },
      template: `
        <h1>{{msg}}</h1>
      `
    })
    
    
    第二步 
     // 全局注册 
    Vue.component('child', child) 
    
    第三步 使用组件
    
    <div id='app'>
    <!-- 单文件组件XXX.vue(一个文件对应一个组件)  非单文件组件 .html (可以创建多个组件)-->
    <child></child>
  </div>

注意点

1.vue2中只允许一个顶节点

2.child 构造函数 解析到有child组件使用时 new 构造函数 生成 vc实例对象

10.vm vc vm 管理 vc

<script src='https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js'></script>
 Vue.prototype.$axios = axios
 
 const child = Vue.extend({
      // 给child定义的数据 
      data() {
        return {
          msg: '我是儿子'
        }
      },
      mounted() {
        // this.fn()
        console.log(this.$axios);
      },
      template: `
      <div>
        <h1>{{msg}}</h1>
        <h1>{{msg}}</h1>
      </div>
      `
    })

11.单文件写法,为脚手架做准备了

<template>
  <div>组件的dom<son></son></div>
</template>

<script>
// 导入 son的配置对象
import son from "./son.vue";
export default {
  data() {
    return {
      msg: "我是儿子",
    };
  },
  components: {
    son: son,
  },
};
</script>

<style></style>

12.下载脚手架工具

全局安装 

npm i -g @vue/cli

查看版本
@vue/cli 4.5.14  你们的 最新的应该是 5 开头的 
vue -V
搭建基础项目
cd到对应文件夹  在命令行中 初入  vue create 07vue-demo--- 

Vue CLI v5.0.4
? Please pick a preset:
  Default ([Vue 3] babel, eslint) 
  Default ([Vue 2] babel, eslint) 
> Manually select features  (!) 自己选择版本和配置 回车确认 
                             
                             
                             
要使用哪些功能到你的项目中     (空格操作取消 和 勾选)                        
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to      
invert selection, and <enter> to proceed)
>(*) Babel  // 兼容打包成(ES5以外的)低版本的js 
 ( ) TypeScript  // js的超集 
 ( ) Progressive Web App (PWA) Support // 是否要把项目添加到桌面图标
 ( ) Router // 配置路由
 ( ) Vuex // 状态管理
 ( ) CSS Pre-processors // css预处理工具
 ( ) Linter / Formatter // 代码检查工具
 ( ) Unit Testing // 测试工具 不需要
 ( ) E2E Testing  // 测试工具 不需要                            

选择vue的版本 
? Choose a version of Vue.js that you want to start the project with 
  3.x
> 2.x

配置是放到单独的文件 还是 package.json  
? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
> In dedicated config files
  In package.json

不要保存为 预设项 
Save this as a preset for future projects? (y/N) N


等待即可 

各部分功能解析
public 
index.html 整个项目的入口html文件 在浏览器上看到的页面   div#app 整个项目最大的容器 

src资源 项目的99%的内容都在这里  
	assets 存放静态资源 图片资源 共用的js 公用的css   sas  less
 	components router  存放组件 .vue 
	App.vue  根组件 有一个 配置对象{} 传给 new Vue({}) 生成vm实例对象 
	main.js 入口js文件 
.browserslistrc  浏览器版本控制 
.gitignore 上传到远程仓库 省略一些什么文件 
babel.config.js babel的配置文件 
jsconfig.json  项目的配置项 

vue.config.js  可以配置 webpack的一些设置  
main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false



// 底层 之前 运行     自动模板渲染成dom 
// 手动的调用 渲染函数 模板渲染成dom

// 生成的项目中 我们所使用的的vue 不是完整的vue 砍掉了 渲染功能 
// 渲染功能 只在 开发阶段可以使用   上线之后 这个功能对于项目而言是无用的 

new Vue({
  render: h => h(App),
}).$mount('#app')
单页面应用的逻辑
虽然路由在换 但实际上 都是同一个页面---public里面的index.html   切换了展示的组件  
<style scoped>  scoped 带作用域  限制里面的css只服务于 当前组件 

<img src="@/assets/logo.png" alt="" /> 可以不需要考虑文件的相对关系 只需要考虑资源在 src目录中存储的位置 

@/ 相当于 src/----方便绝对路径,不用考虑相对路径



export default {
  name: "HelloWorld",
  data() {
    return {
      src: require("@/assets/logo.png"), 如果在data中要用@ 就要用require请求一下资源 
    };
  },
};

13.组件ref属性–父组件来修改子组件的值

作用:在vue中获取dom的方法

ref在 普通dom上使用 就是获取的普通dom

在组件上用 获取到的是helloworld组件的vc对象

<template>
  <div id="app">
    <son ref="son"></son>
    <button @click='changeMsg'>修改儿子的值</button>
  </div>
</template>

<script>
import son from "./components/son.vue";
export default {
  name: "App",
  components: {
    son,
  },
  methods: {
    changeMsg(){
    //  console.log( this.$refs);
    console.log(this.$refs.son);
    this.$refs.son.msg='456'
    }
  },
};
</script>

<style>
</style>

14.单文件组件需要注意的几个点:

1.组件配置项中 name的名字都是要配置的 因为name就是在开发工具里出现的名字。

2.组件名字的命名一般才用大驼峰命名法。

3.template中只能有一个节点 (vue3中忽略不计,因为vue3中会自动地帮我们去找虚拟的节点)

15.动态组件

根据条件,动态的切换组件。

<component :is="componentName"></component>
				组件名

16.缓存动态组件

场景:用户在切换这个组件的时候,希望保存这个组件的值。input输入框。被缓存的组件 不会销毁 而是暂时失去活力

<keep-alive>
      <component :is="componentId"></component>
    </keep-alive>
缓存动态组件的两个非主流生命周期
activited(){
//激活时
}
deactivited(){
//失活时
}
被缓存的组件 二次激活时 不会触发created mounted
 <!-- include 对应的组件的name值 定义可以被缓存的组件 -->
     
    <!-- exclude 对应的组件的name值 定义不缓存的组件 -->
    <keep-alive exclude="Singer">
      <component :is="componentId"></component>
    </keep-alive>
    
    vue3中 (不能定义组件的name 也就是 不能使用 exclude include)

17.在style中控制元素—用于修改css框架的默认样式

vue2深度控制子元素
.singer /deep/ li {
  list-style: none;
}  
.singer 组件的顶节点  /deep/ 控制到 组件内的 子元素的css 

常用于  修改 css框架---element UI的默认样式  

/deep/  li  所有子组件内部的li都会修改,遵循css原理

18.父子组件通信

父组件传值给子组件

子组件用props接收,父组件以动态属性的形式传递!

props内的数据 爸爸内发生变化 儿子也会发生变化

一定要注意 不要在 子组件中修改props值!!如果有使用场景需要对props里的值进行加工 请使用computed!

子组件中    props: ["title", "list"],//传递数据的标签属性
 父组件中		<SelectList :title="title" :list="singerList"></SelectList>

19.props的接受写法

数组写法:props: [“title”, “list”]

对象写法:主要是进行一些类型的检查

key为属性 value为规则

props: {
    // 基础的类型检查
    title: String,
    msg: String,
    // 可以接收多个数据类型
    // msg: [String, Number],

    // 接收字符串型 并且必传值   Missing required prop: "msg" 缺失必须的prop msg
    // msg: {
    //   type: String,
    //   required: true,
    // },

    // 接收字符串型 但如果没有传递 使用default默认值  (用户可配置项 可以用default)
    // msg: {
    //   type: String,
    //   default: "我是默认值",
    // },

20.props未接受的属性

对于未接收的list 将会出现在 this.$attrs 对象内 在 selectList的顶节点上 也有(标签属性)

21.深究js中导入导出

import export
(1)默认(加入了default)导出(完全由使用者去命名—在import的时候由使用者命名)
export default {
	name:'东星耀阳'
}
(2)导入的时候–只有默认导出导入不需要加上{}
import obj from './01'
console.log(obj); 
(3)命名导出
export const arr = [1, 2]
//主人去设置名字,我引入的时候必须就要解构赋值了
//按需求去导入
导入 

import { arr } from './01'   arr要和导出文件 对应
console.log(arr);
(4)统一导出
// 统一导出
const arr = [1, 2]
const obj = { name: 1 }

export {
  arr,
  obj
}

导入
import { arr } from './01'
console.log(arr);

22.父子传值— 自定义事件 子组件改变父组件中的值

思路

在子组件中,不是直接修改爸爸的值,而是自定义事件通知爸爸去修改爸爸的值

在父组件中使用自定义事件,子组件中去触发这个自定义事件

子组件

<template>
  <div class='son'>
    我是儿子里面的值{{msg}}
    <button @click='change'>xiugai</button>
  </div>
</template>
<script>
export default {
  name: "son",
  data () {
    return {
      msg:'123'
    };
  },
  methods: {
    change(){
        this.$emit('change')
    }
  },
}
</script>
<style lang="scss" scoped>
</style>

父组件

<template>
  <div class='App'>
    msg:{{msg}}
    <son @change='changemsg'></son>
  </div>
</template>
<script>
import son from '@/components/son'
export default {
  name: "App",
  data () {
    return {
      msg:'我是爸爸的值'
    };
  },
  components:{
    son
  },
  methods: {
    changemsg(){
      this.msg+=1111
    }
  },
}
</script>
<style lang="scss" scoped>
</style>
另一种注册自定义事件的方法
父组件  // 利用ref属性 
mounted() {
    // 给 selectList 实例对象注册上 change自定义事件
    this.$refs.selectListRef.$on("change", this.changeContent); mounted() {
   //一次性的
    this.$refs.selectListRef.$once("change", this.changeContent);
  },
  destroyed() {
      // 解绑事件
    this.$refs.selectListRef.$off("change", this.changeContent);
  },
  },

23.插槽

用于 父组件 给 子组件传递 html 内容-----用于element UI

如何将dom放在组件中去渲染,插槽帮我们解决了这个问题。

​ Vnode是个虚拟节点

默认插槽

第一步
<SelectList :msg="msg">
  <span style="background: red">这是父亲传来的dom</span>
</SelectList>
    
    第二步
子组件内
<div class="select-list">
    <h2>msg:{{ msg }}</h2>
	<slot></slot>
</div>
具名插槽
目的:将不同的slot分开,区分结构

先给 槽位安排 名字

 <div class="header"><slot name="header"></slot></div>
 <div class="content"><slot name="content"></slot></div>
 <div class="footer"><slot name="footer"></slot></div>
     
父组件中

 <SelectList :msg="msg">
      <!-- slot值对应到 slot 的name值 才能放到对应的坑位 -->
      <template slot="header">
        <span style="background: red">头部</span>
      </template>
      <template slot="content">
        <span style="background: blue">内容</span>
      </template>
      <template slot="footer">
        <span style="background: orange">尾部</span>
      </template>
    </SelectList>
作用域插槽(传数据 子传到父)
第一步: 传数据 

 <slot name="header" :dataList="arr"></slot>

第二步 接收 并使用 (使用范围仅在 接收的插槽dom内 )
 <SelectList :msg="msg">
      <template slot="header" scope="{ dataList }">
        <span>{{ dataList }}</span>
      </template>
或者   abc 随便命名
      <template slot="header" scope="abc">
        <span>{{ abc.dataList }}</span>
       
      </template>
    </SelectList>



scope接收的是一个对象。

24.自定义指令

全局注册指令

directive有两种写法,一种是回调函数,一种是对象写法,对象写法里面是常用的三个生命周期的函数

全局注册
回调函数
Vue.directive(指令名,cb) 

使用 <div v-指令名='value'></div>
//指令是等到模板渲染的时候才会执行
main.js中

Vue.directive('abc', () => {
  console.log(1111);
})

组件中
使用指令
Singer.vue
<template>
  <div class="singer">
     //  解析到了这 才会去执行指令 
    <h1 v-abc="456">{{ content }}</h1>
    <SelectList :msg="msg"></SelectList>
    <hr />
  </div>
</template>

对象写法

为什么要使用对象写法呢?

下面先来看一个例子

小bug
Vue.directive('focus', (el, binding) => {
  // 操作dom 确保dom已经渲染完毕
  console.log(el);
  el.focus() // 这里没有效果时因为 此时 input还没渲染好 
})

解决方法

解决方案  写完整写法 
Vue.directive('focus', {
  // 钩子函数 
  bind(el, binding) {
    // 指令和元素成功绑定时 

  },
  inserted(el, binding) {
    // 指令所在元素被插入页面时调用  dom被渲染完毕 
    el.focus()
  },
  update(el, binding) { 
      // 节点更新时调用 绑定值发生变化时调用  
    console.log(binding.value);
  }
})
小demo
实现一个加载指令
<ul style="height: 100px; background-color: red" v-loading="bool">
      <li v-for="item in arr" :key="item.num">{{ item.num }}</li>
    </ul>
    
    bool: true,
      arr: null,
      
     mounted() {
    setTimeout(() => {
      this.arr = [{ num: 1 }, { num: 2 }, { num: 3 }];
      this.bool = false;
    }, 1000);
  },
  
  
  Vue.directive('loading', {
  inserted(el, binding) {
    if (binding.value) {
      el.img = document.createElement("img")
      el.img.src = require('./assets/images/loading.gif')
      el.appendChild(el.img)
    }
  },
  update(el, binding) {
    // false的时候  
    if (!binding.value) {
      el.removeChild(el.img)
    }else {
      el.appendChild(el.img)
    }
  },
}) 
局部注册指令

在配置对象中 只能用在 配置对象 对应的组件中

 directives: {
    // bind时机
     //只想在bind时机上用,直接写组件名
    // abc(el, binding) {
    //   console.log(el);
    // },

    // 完整写法
    abc: {
      bind(el, binding) {},
      inserted() {},
      update() {},
    },
  },
      
      两个单词  使用的时候 v-abc-a
      "abc-a":{
      }

25.全局事件总线-任意组件通信(利用自定义事件**)**

好处:任意组件之间可以通讯,不用组件!

main.js   把vm在Vue的原型对象上存一下

const vm = new Vue({
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus = this  // this就是vm
  }
}).$mount('#app')

Song中
<template>
  <div class="song">
    <h1>{{ content }}</h1>
  </div>
</template>
<script>
export default {
  name: "Song",
  data() {
    return {
      content: "Song",
    };
  },
  components: {},
  mounted() {
    // 注册 形参 value
    this.$bus.$on("toSong", (value) => {
      console.log(value);
      this.content += value;
    });
  },
};
</script>
<style scoped></style>



Singer中
<template>
  <div class="singer">
    <h1>{{ content }}</h1>
    <button @click="fn">点击发送数据给Song</button>
  </div>
</template>
<script>
export default {
  name: "Singer",
  data() {
    return {
      content: "Singer",
    };
  },
  methods: {
    fn() {
      // Singer 传值(实参 触发事件) 给 Song (形参接收实参 注册事件)
      this.$bus.$emit("toSong", "这是Singer给Song的数据");
    },
  },
};
</script>

//重点就是搞清楚 哪个注册哪个执行
//传值的那个触发执行,
//被传值的那个是注册事件

this. b u s . bus. bus.on注册事件

this. b u s . bus. bus.emit 触发事件,跟自定义事件的emit一样用法

26.利用自定义指令实现图片的懒加载

在视口范围内的img src正常 url 请求资源
非视口范围内的img src 赋值为默认图片 

img进入的视口了 src 替换为正常url 正常请求资源 


main.js

let scrollHTML = document.documentElement.scrollTop
let windo3wHeight = window.innerHeight
const refreshScroll = () => {
  scrollHTML = document.documentElement.scrollTop
}
const refreshHeight = () => {
  windowHeight = window.innerHeight
}

window.addEventListener('scroll', refreshScroll);
window.addEventListener('resize', refreshHeight);

let defaultImg = require('./assets/images/default.png')

Vue.directive('lazy', {
  inserted(el, binding) {
    const observe = new IntersectionObserver((changes) => {
      // console.log(changes[0].boundingClientRect.y);
      const ImgTop = changes[0].boundingClientRect.y
      // 图片上方到html顶部的距离 > 浏览器高度 + html的滚动距离   视口外
      // 图片上方到html顶部的距离 < 浏览器高度 + html的滚动距离   视口内
   
      if (ImgTop > windowHeight + scrollHTML) {
        // 视口外
        el.src = defaultImg
      } else {
        //视口内
        setTimeout(() => {
          el.src = binding.value 
          // 监听解绑 
          observe.unobserve(el)
        }, 500)
      }

    }, {})
    // 监听的操作 
    observe.observe(el)
  }
})

Singer.vue

<ul>
      <li v-for="(item, index) in arr" :key="index">
        <img alt="" v-lazy="item.srcImage" />
      </li>
    </ul>

27.按钮节流操作

// 事件捕获 事件冒泡 定时器 
Vue.directive('throttle', {
  inserted(el, binding) {
    let throttleTime = binding.value
    //当用户没有传  默认2s 
    if (!throttleTime) {
      throttleTime = 2000
    }
    let cbFun
    // 捕获 后绑定的事件  先执行 
    el.addEventListener('click', (event) => {
      if (!cbFun) { //第一次执行  上
        cbFun = setTimeout(() => {
          cbFun = null
        }, throttleTime)// 
      } else { //下
        event && event.stopImmediatePropagation()
      }
    }, true)

  }
})

<button @click="sayBye" v-throttle="3000">11111</button>

methods: {
    sayBye() {
      console.log("bye ~~~"); 
    },
  },

28.mixin混入 复用 配置项

目的:抽离公共项,复用

原始情况:

原始情况
----目的是抽离公共项,复用


<template>
  <div class="song">
    <h1>{{ contentCom }}</h1>
  </div>
</template>
<script>
export default {
  name: "Song",
  data() {
    return {
      content: "Song",
    };
  },
  computed: {
    contentCom() {
      return (this.content += "~~~~");
    },
  },
  methods: {
    fn() {
      console.log(1111);
    },
  },
  mounted() {
    console.log(2222);
  },
};
</script>
<style scoped></style>


singer
<template>
  <div class="singer">
    <h1>{{ contentCom }}</h1>
  </div>
</template>
<script>
export default {
  name: "Singer",
  data() {
    return {
      content: "Singer",
    };
  },
  computed: {
    contentCom() {
      return (this.content += "~~~~");
    },
  },
  methods: {
    fn() {
      console.log(1111);
    },
  },
  mounted() {
    console.log(2222);
  },
};
</script>





抽离公共项

mixin.js   //文件的名字是自由定义的,也可以定义多个,最后只要在mixins:[]
//引入即可
export const mixin = {
  computed: {
    contentCom() {
      return (this.content += "~~~~");
    },
  },
  methods: {
    fn() {
      console.log(1111);
    },
  },
  mounted() {
    // console.log(this.$el);
  },
}


混入公共项

混入公共项。使用配置项mixins:[]接受  //文件的名字是自由定义的
全局使用:Vue.mixin(mixin)

注意点:

1.如果和自己身上的 生命周期同时使用(不会覆盖) 先运行mixin中的 再运行组件内部的

2.其他配置项中 出现同一个定义项 函数名 数据名 优先使用自己的

29.plugin插件

目的:增加vue的全局功能

plugin/index.js 或者 plugin.js

export default {
  install(Vue, options) {
    // 在这里 全局功能的添加  
    Vue.directive('throttle', {
      inserted(el, binding) {
        let throttleTime = binding.value
        //当用户没有传  默认2s 
        if (!throttleTime) {
          throttleTime = 2000
        }
        let cbFun

        // 捕获 后绑定的事件  先执行 
        el.addEventListener('click', (event) => {
          if (!cbFun) { //第一次执行  上
            cbFun = setTimeout(() => {
              cbFun = null
            }, throttleTime)
          } else { //下
            event && event.stopImmediatePropagation()
          }
        }, true)

      }
    })

    // Vue.component(...)

    // Vue.mixin(....)

    // Vue.prototype.$bus = xxxx 
  }
}
加载插件
在main.js中
import plugins from './plugin'
Vue.use(plugins)

30.生命周期中nextTick

情景问题:由于定时器不知道多少秒能够渲染完毕,所以使用nextTick,下次dom更新结束后来执行回调!

<div class="singer">
    <h1>{{ content }}</h1>
    <div v-if="isShow">
      <input type="text" ref="ipt" />
    </div>
    <button @click="changeIsShow">触发变化</button>
  </div>


isShow: false,


changeIsShow() {
      this.isShow = !this.isShow;

      // setTimeout(() => {
      //   if (this.isShow) {
      //     // 得等 input渲染完毕之后才能 获取
      //     console.log(this.$refs.ipt);
      //   }
      // }, 500);

      // 等下一次dom更新结束后 执行回调
      this.$nextTick(() => {
        if (this.isShow) {
          // 得等 input渲染完毕之后才能 获取
          console.log(this.$refs.ipt);
          this.$refs.ipt.focus();
        }
      });
      // if (this.isShow) {
      //   // 得等 input渲染完毕之后才能 获取
      //   console.log(this.$refs.ipt);
      //   this.$refs.ipt.focus();
      // }
    },
        
        
 //应用情景:需求要在一个盒子里面渲染一个数组的内容,我要获取外层盒子的高度
 
 //异步获取的情况

31.过渡

1.过渡中的四点两过

要过渡的dom要transition标签给他包裹起来

四点:

v-enter

v-enter-to

v-leave

v-leave-to

两过:

v-enter-active

v-leave-active

原理:

Vue底层给他们添加类名

<template>
  <div class="app">
    <transition>
      <Singer v-show="bool"></Singer>
    </transition>

    <button @click="bool = !bool">切换Singer是否出现</button>
  </div>
</template>

 /*
    两个状态 进场(进来)   出场(出去)
    进来的起点 translate 100% 0 (v-enter)       
    进来的终点 translate 0 0 (v-enter-to)     

    出去的起点  translate 0 0 (v-leave)
    出去的终点  translate 100% 0 (v-leave-to) 

    transition:transform 2s;  active  (运动的过程中) 进来的active 出去的active 
    进来的active (v-enter-active)
    出去的active (v-leave-active)
  */ 
默认的 transition 标签 
.v-enter {
  transform: translate(100%, 0);
}
.v-enter-to {
  transform: translate(0, 0);
}
/* 出去 */
.v-leave {
  transform: translate(0, 0);
}
.v-leave-to {
  transform: translate(100%, 0);
}
/* 进来过程 */
.v-enter-active {
  transition: transform 2s;
}
/* 出去过程 */
.v-leave-active {
  transition: transform 2s;
}
四个点,两个过程
四点两过
2.指定name名字

相较于默认的变化,

transition上面增加了一个name属性

在样式书写当中,将v改写成name所对应的属性值

<transition name="demo">
      <Singer v-show="bool"></Singer>
    </transition>

/* 进来的起点和离开的终点 */
.demo-enter,
.demo-leave-to {
  opacity: 0;
  transform: translate(0, -100%);
}
/* 进来的终点和离开的起点 */
.demo-enter-to,
.demo-leave {
  opacity: 1;
  transform: translate(0, 0);
}

/* 进来过程和出去过程 */
.demo-enter-active,
.demo-leave-active {
  transition: 2s;
}
3.appear属性

目的:想要出场效果就有

<!-- appear 如果dom本身就会渲染 第一次渲染就会具备过渡效果 默认不会 -->
    <transition name="demo" appear>
      <Singer v-show="bool" class="center"></Singer>
    </transition>
4.transition-group

1.想要多个组件都拥有过渡效果

2.小要求:给每个组件中都要用key值

3.里面有个tag属性,会将渲染其属性值渲染成对应的标签

 <transition-group>
      <Singer v-if="bool" key="1"></Singer>
      <Singer v-else class="center" key="2"></Singer>
    </transition-group>

tag会把 transition-group 渲染会一个ul标签   transition标签没有 
 <transition-group tag="ul">
      <li v-for="item in arr" :key="item">{{ item }}</li>
    </transition-group>
<button @click="spliceArr">3移除</button>

data() {
    return {
      bool: true,
      arr: [1, 2, 3, 4, 5],
    };
  },
methods: {
    spliceArr() {
      this.arr.splice(2, 1);
    },
  },
5.动画的写法

就不用写四点了

<transition name="move">
      <div class="box" v-show="bool"></div>
    </transition>
 <button @click="bool = !bool">切换Singer是否出现</button>

.box {
  position: absolute;
  top: 0;
  left: 0;
  width: 200px;
  height: 200px;
  border-radius: 50%;
  background-color: yellowgreen;
}

.move-enter-active,
.move-leave-active {
  animation: move 2s;
}

@keyframes move {
  0% {
    top: 0;
    left: 0;
  }
  50% {
    top: 50%;
    left: 50%;
  }
  100% {
    top: 0;
    left: 0;
  }
}

32.v-model用到组件

自定义事件是要处理很复杂的逻辑

而用v-model只是单纯的想在子组件中修改值

<AlertBox ref="alertBoxRef"  v-model="data"></AlertBox>
 data() {
    return {
      data: "",
    };
  },
  
  
 子组件中
 <button @click="fn"></button>
 model:{
 	prop:['data'],
 	event:'change'
 },
 props:['data']
 
methods:{
fn(){
this.$emit('change',123)
}
}

33.vuex

1.vuex的作用:如果一个数据需要被很多数据共享,一个数据需要被很多组件修改,这个时候,我们就要用vuex,因为那个时候父子组件传值,全局总线之间传值已经满足不了我们的需求了
2.context就是一个简化版本的store
3。在actions里定义的函数都是经过了特殊的处理,有一个参数context,是一个简化版本的store
4.mutations里面的函数的参数是state

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
// Store对象 统一管理    
export default new Vuex.Store({
  state: {
    // 数据存这里 
  },
  mutations: {
    // 真实的修改数据 
  },
  actions: {
    // 修改数据之前你要做的一些逻辑 .. 
  },
  getters: {
  },
  modules: {
  }
})

main.js中引入
import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')
app中的组件
<template>
  <div id="app">
    <PageA> </PageA>
    <hr />
    <ControlBtn></ControlBtn>
    <hr />
    <InputAdd></InputAdd>
  </div>
</template>
<script>
import PageA from "./components/PageA.vue";
import ControlBtn from "./components/ControlBtn.vue";
import InputAdd from "./components/InputAdd.vue";
export default {
  name: "App",
  data() {
    return {};
  },
  components: {
    PageA,
    ControlBtn,
    InputAdd,
  },
};
</script>
list定义到state中
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
// Store对象 统一管理    
export default new Vuex.Store({
  state: {
    // 数据存这里
    list: ['周杰伦', '蔡依林', '陈奕迅']
  },
  mutations: {
    // 真实的修改数据 
  },
  actions: {
    // 数据之前你要做的一些逻辑 .. 
  },
  getters: {
  },
  modules: {
  }
})

pageA需要用到数据
<template>
  <div class="page-a">
    <ul>
      <li v-for="item in list">{{ item }}</li>
    </ul>
  </div>
</template>
<script>
export default {
  name: "component_name",
  data() {
    return {
      list: [],
    };
  },
  mounted() {
    this.list = this.$store.state.list;
  },
};
</script>


//使用computed保持响应式 
computed: {
    list() {
      return this.$store.state.list;
    },
  },
      

注意点:

如果要使用到数据,直接使用到computed里面定义一个函数然后返回,因为只有这样具有响应式

vuex基础总结:

需要被很多组件共享的数据放到vuex中

需要触发actions==>dispatch

需要触发mutations==>commit

this.$store.dispatch

context.commit(‘deleteListFn’, arr)

规律:

在vuex以外,一般使用this.$store

在vuex中,一般用context

34.获取state数据,actions函数,mutations函数的其他方式

1.获取state数据

mapState 帮助映射state中的数据计算属性

所以在计算属性中使用

import { mapState } from "vuex";

在计算属性中使用 
  computed: {
    // list() {
    //   return this.$store.state.list;
    // },
    // arr() {
    //   return this.$store.state.arr;
    // },
	// 等价于上面的写法
    ...mapState(["list", "arr"]),
  },
      
      第一个: 数组中的数组项 就是state中数据的名称 
      第二个 必须写在 computed 必须要使用 ....
      
      
对象写法

对象写法和数组写法的区别:

// listPage() {
    //   return this.$store.state.list;
    // },
    // 等价于上面的写法 
    // listPage 定义的计算属性   'list' state中的数据
    ...mapState({ listPage: "list" }),
        
数组写法 +> 组件内计算属性名 与 state中数据 名一致
对象写法 +> 组件内计算属性名 与 state中数据 名不一致
2.actions函数
import { mapActions } from "vuex";

使用的位置 methods中  

数组写法 
 <button @click="deleteList">删除最后一项</button>
 methods: {
  
    // deleteList() {
    //   this.$store.dispatch("deleteList");
    // },
    //等价于上面的写法
    ...mapActions(["deleteList"]),
  },
  
  
  对象写法
  methods: {
  // deleteOne() {
    //   // 第一个参数 是actions里面定义的函数名 就可以触发actions内的函数了
    //   this.$store.dispatch("deleteList");
    // },
    // 等价于上面的写法
    ...mapActions({ deleteOne: "deleteList" }),
  }

数组写法 +> 组件内函数名 与 actions 中函数名一致
对象写法 +> 组件内函数名 与 actions 中函数名不一致 


如果要传参  那就是 deleteOne(参数)  deleteList(参数)
3.mutations函数
import { mapMutations } from "vuex";

写在 methods中 

数组写法
// addListFn() {
    //   this.$store.commit("addListFn", this.query);
    // },
    ...mapMutations(["addListFn"]),
        
        
对象写法 
 // fn() {
    //   this.$store.commit("addListFn", this.query);
    // },

    // 等价于上面的写法
    ...mapMutations({ fn: "addListFn" }),
        
数组写法 +> 组件内函数名 与 Mutations 中函数名一致
对象写法 +> 组件内函数名 与 Mutations 中函数名不一致 


如果要传参  那就是 addListFn(参数)  fn(参数)

35.getters

拿state中的数据,跟计算属性差不多

getters的其他获取方式

写在computed

import { mapState, mapGetters } from "vuex";

<span>{{ hobby }}</span>
<span>{{ hobby }}</span>

数组写法
 computed: {
hobbyFirst() {
      return this.$store.getters.hobbyFirst;
    },//等价于上面的写法 
    ...mapGetters(["hobbyFirst"]),
    }

对象写法 
 hobby() {
      return this.$store.getters.hobbyFirst;
    },
    ...mapGetters({ hobby: "hobbyFirst" }),

36.modules

分了模块之后的问题?

获取state中数据不一样了,先进入模块名,再进入变量的名称。

而且在利用map获取的时候,只能一个一个地去获取

index.js
index.js

import Vue from 'vue'
import Vuex from 'vuex'

import list from './modules/list'
import listOther from './modules/listOther'
import person from './modules/person'
import getters from './getters'


Vue.use(Vuex)


// Store对象 统一管理    
export default new Vuex.Store({
  getters,
  modules: {
    list,
    listOther,
    person
  }
})

./modules/list.js
export default {
  // 开启命名空间 
  namespaced: true,
  state() {
    return {
      list: ['周杰伦', '蔡依林', '陈奕迅'],
      abs: 1
    }
  },
  actions: {
    deleteList(context) {
      // 假设很多的逻辑 ........
      if (context.state.list.length) {
        // 删除的真实操作
        let arr = [...context.state.list]
        arr.pop()
        context.commit('deleteListFn', arr)
      }
    },
    addList(context, value) {
      if (!value) return
      if (!context.state.list.includes(value)) {
        context.commit('addListFn', value)
      }
    },
  },
  mutations: {
    // 真实的修改数据 
    deleteListFn(state, value) {
      state.list = value
    },
    // 添加数组项
    addListFn(state, value) {
      state.list.push(value)
    },
  }
}

./modules/listOther.js
export default {
  // 开启命名空间 
  namespaced: true,
  state() {
    return {
      listOther: ['稻花香', '爱情七十二变', '好好说话'],
    }
  },
  actions: {},
  mutations: {
    reverseListOther(state) {
      state.listOther = state.listOther.reverse()
    },
    handleListOther(state, value) {
      state.listOther = state.listOther.map(item => item + value)
    }
  }
}


state的获取
list() {
      return this.$store.state.list.list; // 	第一个list模块名.	第二个list数据名
    },
        
        
        
// 第一个参数 是 模块名  第二个 [模块中的数据名]
    ...mapState("list", ["list"]),
    ...mapState("listOther", ["listOther"]),   
        
        
        对象写法 
  ...mapState("listOther", { listOther1: "listOther" }),
      
      
      变化就是在 mapState函数第一个参数位置 添加模块名  
     
在getters中获取
export default {
  hobbyFirst(state) {
    // state.模块名.数据名
    return state.person.person.info.hobby[0] + '~'
  }
}
actions,mutations的触发
 deleteOne() {
      // 模块名/actions中的函数名
      this.$store.dispatch("list/deleteList");
    },
    
// 模块名 , 正常接收
...mapActions("list", { deleteOne: "deleteList" }),

数组写法 
 ...mapActions("list", ["deleteList"]),
 
 
 总结 普通 就是 模块/函数名 
	  map形式 第一个参数要填 模块名  后面参数正常填写
总结:

获取时,相对于以前,要增加模块名下面去取数据!!!

如果是要获取多个模块,写多个map就行

37.在一个模块中想要触发或访问 其他模块 中数据或函数时

1.获取其他模块中的state数据 context.rootState.数据名

2.获取其他模块中的getter rootGetters 中

3.开启root:true 才能在顶层 去寻找其他模块的actions函数 默认情况是只找当前模块中的actions函数,mutations里面是同理的!!

在listOther.js中


changeListOther(context, value) {
      console.log(value);
      // 获取其他模块中state中数据  cpmtext.rootState.数据名 

      // 获取其他模块中的getter  rootGetters 中
      // console.log(context);

      // 开启root:true 才能在	顶层	去寻找其他模块的actions函数  默认情况是只找当前模块中的actions函数
    //在context中有root可以传递
      // context.dispatch('list/deleteList', null, { root: true })

      // 去触发其他模块中的 mutations中的函数 
      context.commit('list/testFn', null, { root: true })
    }

38.router(现在没怎么使用router了)

router 路由器 route路由

4版本的 vue-router和 vue2不兼容 所以一定要下载3版本

 "vue-router": "^3.5.1",
 "vuex": "^3.6.2"
(1)router和route

// router 路由对象 路由的跳转,回退,前进都是由路由对象进行管理的!!!

import Vue from 'vue'
import VueRouter from 'vue-router'


Vue.use(VueRouter)

// 路由规则   /  ==>  首页.vue    /404  ==> 404.vue
const routes = [

]
// router 路由对象 路由的跳转,回退,前进都是由路由对象进行管理的!!!
const router = new VueRouter({
  mode: 'history',
  routes
})

export default router


//一定要记住在main.js中导入!!!
main.js
import router from './router'

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

(2)hash history 两种模式
hash  http://localhost:8081/#/index     /index 
 	# 后面的内容 不会发送给服务器 只在前端使用  
    兼容性比较好
    不美观
    若地址通过第三方分享(手机app),如果地址校验比较严格 可能会被标记为不合法 
    
history http://localhost:8081/index
	会发送给服务器 和 后端的路由发生冲突  (后端可以处理这个误会)
	兼容性比hash差一点
	美观
    
// router 路由对象 
const router = new VueRouter({
  mode: 'hash', / history 直接改值可以切换 
  routes
})
(3)配置路由规则

1.每一条路由规则都是一个对象

2.根据路由规则匹配的组件叫路由组件 放到views文件中

3.前端的单页面展示不是跳转到某个页面,而是在app.vue中某个位置换一个路由组件展示。

const routes = [
  {
    path: '/home',// 路由
    name: 'Home', // 路由名----可选项,一般首字母大写啊
    component: Home // 路由组件
  }
]
(4)路由跳转的显示位置

分为一级路由,二级路由,三级路由

显示路由的标签

点我跳转首页路由跳转的标签

a标签在vue中是很少见的

1.1-一级路由展示的位置
<template>
  <div id="app">
    <!-- 一级路由组件展示的位置 -->
    <router-view></router-view>
 <router-link to="/home">点我跳转首页</router-link>路由跳转的标签
  </div>
</template>

当访问 /home路由时 就会在app中的 router-view的位置展示 对应的路由组件
(5)一般组件 路由组件的区别
一般组件

import导入到父组件中===>注册====>在模板中使用===>一般组件通常存放在components文件夹中

路由组件

在router/index.js中 导入 在路由表中 使用

访问对应路由 展示对应 路由组件

路由组件 通常存放在 views/pages 文件夹中

当访问路由组件的时候 ,生命周期会重新被执行一次

(6)$router $route

每个组件都有自己的 r o u t e 里面存储的是自己的路由信息每个组件也能访问到 route 里面存储的是自己的路由信息 每个组件也能访问到 route里面存储的是自己的路由信息每个组件也能访问到router 指向同一个router对象 可以进行路由的操作

(7)二级路由

注意点:

1.在children里面写path不带/

2.二级路由在一级路由里面写组件展示的位置

import Singer from '@/views/Singer'

{
    path: '/about',// 路由
    name: 'About', // 路由名
    component: About, // 路由组件  /about/singer
    children: [
      {
        // 二级路由 要在 上级路由组件中 开一个 router-view展示二级路由组件
        path: 'singer', // 不带 / 不带 / 不带 /---注意这个写法
        name: 'Singer',
        component: Singer
      }
    ]
  }

一级路由组件中的写法

 <div class="about">
    <h1>关于</h1>
    <router-link to="/about/singer">点我跳到singer</router-link> |
    <router-link to="/about/song">点我跳到song</router-link>

    <!-- about二级路由组件展示位置 -->
    <router-view></router-view>
  </div>
(8)路由传参
1.1 传递query参数

接受时,在对应路由组件中 this.route.query中 获取

http://localhost:8081/about/song/detail?id=001&name=%E6%AD%8C%E6%9B%B21

查询字符串

方式一 

<router-link to="/about/song/detail?id=001&name=歌曲1">
{{item.name}}</router-link>
 
 接收时 在对应路由组件中 
 this.route.query中 获取 
 detail中
 <template>
  <div class="detail">
    歌曲名: {{ info.name }}
    <br />
    编号:{{ info.id }}
  </div>
</template>
<script>
export default {
  name: "Detail",
  data() {
    return {
      info: {},
    };
  },
  mounted() {
    this.info = this.$route.query;
  },
};
</script>
<style scoped></style>


 

可以采用对象的写法

<router-link
          :to="{
            path: '/about/song/detail',
            query: {
              id: item.id,
              name: item.name,
            },
          }"
          >{{ item.name }}</router-link>
              
detail中  可以完美的接收数据 
(9)要写路由命名跳转,只能写在对象里面
children: [
          {
            path: 'detail',
            name: "Detail",  //这就是路由命名 
            component: Detail
          }
        ]




根据路由命名进行跳转 / 只能写在 对象写法里  !!! 
<router-link
          :to="{
            name: 'Detail',
            query: {
              id: item.id,
              name: item.name,
            },
          }"
          >{{ item.name }}</router-link
        >

              
path: '/about/song/detail',
name: 'Detail',    写法相对更加简单              
(10)动态路由
传递params参数
在路由表中配置 
children: [
          {
            path: 'detail/:id/:name',   // detail/001/歌曲1
            name: "Detail",
            component: Detail
          }
        ]

换成 活数据

 <router-link :to="`/about/song/detail/${item.id}/${item.name}`">{{
          item.name
        }}</router-link>

换成美观写法(常用)

<router-link
          :to="{
            name: 'Detail', // 不能使用path 只能用 name 
            params: {
              id: item.id,
              name: item.name,
            },
          }"
          >{{ item.name }}</router-link
        >
接收
 <div class="detail">
    歌曲名: {{ $route.params.name }}
    <br />
    编号:{{ $route.params.id }}
  </div>

小tip:当用params对象写法时,只能写name。

(11)路由的props配置
第一个写法–写死了
先配置好
//第一个方法:写死了
 children: [
          {
            path: 'detail/:id/:name',   // detail/001/歌曲1
            name: "Detail",
            component: Detail,
            props: {
              id: '001',
              name: '歌曲1'
            }
          }
        ]
//需要接收数据的路由组件中 可以 通过props取到对应的值 
export default {
  name: "Detail",
  props: ["id", "name"],
  
};





第二个写法–接受布尔值
//第二个写法 ---接收布尔值

props: true 值为布尔值 把路由组件收到的所有的params参数 以props的形式 传给detail组件 detail就可以用props接收了  ( query参数无法识别 ) 
第三个写法—函数写法
//第三个写法
//函数写法 -----这种写法可以接受query模式的写法

//query的传递
//定义路由时 
children: [
          {
            path: 'detail',   // detail/001/歌曲1
            name: "Detail",
            component: Detail,
            props($route) {
              return {
                id: $route.query.id,
                name: $route.query.name,
              }
            }
          }
//传递数据时
  <router-link
          :to="{
            name: 'Detail',
            query: {
              id: item.id,
              name: item.name,
            },
          }"
          >{{ item.name }}</router-link
        >  
              
//接收数据时 
 props: ["id", "name"],
     
     
//params的传递
//定义路由时
 children: [
          {
            path: 'detail/:id/:name',   // detail/001/歌曲1
            name: "Detail",
            component: Detail,
            props($route) {
              return {
                id: $route.params.id,
                name: $route.params.name,
              }
            }
          }
     
//传递数据时
  <router-link
          :to="{
            name: 'Detail',
            params: {
              id: item.id,
              name: item.name,
            },
          }"
          >{{ item.name }}</router-link
        >
//接收 
 props: ["id", "name"],
(12)路由懒加载

底层性能的一些优化

替换成 这种写法 component: () => import(/* webpackChunkName: “Detail” */‘@/views/Detail’),

等用到组件的时候再引入,这样性能会更优化一点!!!

替换成 这种写法 component: () => import(/* webpackChunkName: "Detail" */'@/views/Detail'),


import Vue from 'vue'
import VueRouter from 'vue-router'
// 只到文件夹 那就回去找文件夹中 index.vue
import Home from '@/views/Home'

将直接导入的方式换成懒加载的方式,底层性能的一种优化!!!
// import About from '@/views/About'
// import Singer from '@/views/Singer'
// import Song from '@/views/Song'
// import Detail from '@/views/Detail'


Vue.use(VueRouter)
const routes = [
  {
    path: '/home',// 路由
    name: 'Home', // 路由名
    component: Home // 路由组件
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "About" */'@/views/About'), //  访问到组件才去加载
      等价于()=>{
          return import(```)
      }
    children: [
      {
        path: 'singer', 
        name: 'Singer',
        component: () => import(/* webpackChunkName: "Singer" */'@/views/Singer')
      },
      {
        path: 'song', 
        name: 'Song',
        component: () => import(/* webpackChunkName: "Song" */'@/views/Song'),
        children: [
          {
            path: 'detail/:id/:name',
            name: "Detail",
            component: () => import(/* webpackChunkName: "Detail" */'@/views/Detail'),
            props($route) {
              return {
                id: $route.params.id,
                name: $route.params.name,
              }
            }
          }
        ]
      }
    ]
  }
]
// router 路由对象 
const router = new VueRouter({
  mode: 'history',
  routes
})

export default router 

(13)replace属性 和 push属性

这两种属性都是用于历史记录的!!

默认 我们在地址栏访问地址 历史记录的添加方式 是追加push的

push追加历史记录(默认)

replace 替换当前记录

 <router-link to="/Home" replace>首页</router-link>
(14)命名视图–展示多个路由组件

同时(同级)展示多个视图

{
    path: '/about',
    name: 'About',
    components: {
      default: () => import(/* webpackChunkName: "About" */'@/views/About'),
      PageA: () => import(/* webpackChunkName: "PageA" */'@/views/PageA')
    },

        
展示位置
 <router-view></router-view>
 <router-view name="PageA"></router-view>
(15)路由的别名

目的:访问到其他的名字,也能匹配到这个路由组件

{
    path: '/home',
    name: 'Home',
    alias: ['/home1', '/home2'], // 别名  访问 数组中的路由 也是访问 /home
    component: Home
  },
(16)重定向

场景:如果登录不成功,就会重新跳转,一般用于条件判断语句当中。

{
    path: '/home',
    name: 'Home',
    alias: ['/home1', '/home2'], // 别名  访问 数组中的路由 也是访问 /home
    redirect: '/', 或者
	// redirect: { name: 'About' },
    component: Home
  },
      
      访问 /home 在地址栏会跳到 / 跟路由 
(17)编程式导航

目的:不用router-link发生路由跳转

使用this.$route.push直接跳转

this.$route.replace

this.$route.go(1)前进

this.$route.go(-1)后退

this.$route.forward()前进

this.$route.back()后退

(18)缓存路由组件
<!-- 缓存路由组件  -->
    <keep-alive>
      <router-view></router-view>
    </keep-alive>
    
缓存有两个生命周期
activited
deactivited
(19)导航守卫

理解为路由跳转前和跳转后提供的钩子

所有路由跳转之前都会执行beforeEach

afterEach作为路由最后一个钩子

全局前置钩子
所有的路由跳转 之前 都会执行 beforeEach 

let token = '' // 假设 空是未登录  有值似乎登录
const whitePath = ['/', '/login', '/401', '/404',]

router.beforeEach((to, from, next) => {
  // to  跳转去哪个路由
  // from 从哪个路由发生跳转 
  console.log(to);
  if (whitePath.includes(to.path)) {
    next()
  } else {
    // 判断 是否登录 
    // token 
    // 获取token 
    if (token) { //登录了 
      next()
    } else {
      //假设跳到login
      next('/login')
    }
  }
  // next() // 执行下一步 如果没有就会卡死在这 路由不会成功
})

全局后置钩子
router.afterEach((to, from) => {
  // 作为 路由最后一个钩子  不需要next 

  if (to.meta.title) {
    document.title = to.meta.title
  }

})
独享守卫–路由规则独享的
放到路由规则中来 

{
    path: '/about',
    name: 'About',
    meta: {
      title: '关于页面',
    },
    // 独享守卫 只对/about起作用  只有 访问 / about才会触发 独享守卫 
    beforeEnter(to, from, next) {
      console.log(to);
      next()
    },
    components: {
      default: () => import(/* webpackChunkName: "About" */'@/views/About'),
      PageA: () => import(/* webpackChunkName: "PageA" */'@/views/PageA')
    },
    children: [..............]
路由组件守卫

beforeRouteEnter 加载之前

beforeRouteLeave 离开路由组件是

beforeRouteUpdate 复用路由组件时

export default {
  name: "Detail",
  props: ["id", "name"],

  // 访问路由组件时 加载之前
  beforeRouteEnter(to, from, next) {
    console.log("组件中的守卫 进来");
    next();
  },
  // 离开路由组件时
  beforeRouteLeave(to, from, next) {
    console.log("组件中的守卫 离开");
    next();
  },
  // 复用路由组件时
  beforeRouteUpdate(to, from, next) {
    console.log("组件中的复用");
    next();
  },
};

er-link to=“/Home” replace>首页


#### (14)命名视图--展示多个路由组件

> 同时(同级)展示多个视图

```js
{
    path: '/about',
    name: 'About',
    components: {
      default: () => import(/* webpackChunkName: "About" */'@/views/About'),
      PageA: () => import(/* webpackChunkName: "PageA" */'@/views/PageA')
    },

        
展示位置
 <router-view></router-view>
 <router-view name="PageA"></router-view>
(15)路由的别名

目的:访问到其他的名字,也能匹配到这个路由组件

{
    path: '/home',
    name: 'Home',
    alias: ['/home1', '/home2'], // 别名  访问 数组中的路由 也是访问 /home
    component: Home
  },
(16)重定向

场景:如果登录不成功,就会重新跳转,一般用于条件判断语句当中。

{
    path: '/home',
    name: 'Home',
    alias: ['/home1', '/home2'], // 别名  访问 数组中的路由 也是访问 /home
    redirect: '/', 或者
	// redirect: { name: 'About' },
    component: Home
  },
      
      访问 /home 在地址栏会跳到 / 跟路由 
(17)编程式导航

目的:不用router-link发生路由跳转

使用this.$route.push直接跳转

this.$route.replace

this.$route.go(1)前进

this.$route.go(-1)后退

this.$route.forward()前进

this.$route.back()后退

(18)缓存路由组件
<!-- 缓存路由组件  -->
    <keep-alive>
      <router-view></router-view>
    </keep-alive>
    
缓存有两个生命周期
activited
deactivited
(19)导航守卫

理解为路由跳转前和跳转后提供的钩子

所有路由跳转之前都会执行beforeEach

afterEach作为路由最后一个钩子

全局前置钩子
所有的路由跳转 之前 都会执行 beforeEach 

let token = '' // 假设 空是未登录  有值似乎登录
const whitePath = ['/', '/login', '/401', '/404',]

router.beforeEach((to, from, next) => {
  // to  跳转去哪个路由
  // from 从哪个路由发生跳转 
  console.log(to);
  if (whitePath.includes(to.path)) {
    next()
  } else {
    // 判断 是否登录 
    // token 
    // 获取token 
    if (token) { //登录了 
      next()
    } else {
      //假设跳到login
      next('/login')
    }
  }
  // next() // 执行下一步 如果没有就会卡死在这 路由不会成功
})

全局后置钩子
router.afterEach((to, from) => {
  // 作为 路由最后一个钩子  不需要next 

  if (to.meta.title) {
    document.title = to.meta.title
  }

})
独享守卫–路由规则独享的
放到路由规则中来 

{
    path: '/about',
    name: 'About',
    meta: {
      title: '关于页面',
    },
    // 独享守卫 只对/about起作用  只有 访问 / about才会触发 独享守卫 
    beforeEnter(to, from, next) {
      console.log(to);
      next()
    },
    components: {
      default: () => import(/* webpackChunkName: "About" */'@/views/About'),
      PageA: () => import(/* webpackChunkName: "PageA" */'@/views/PageA')
    },
    children: [..............]
路由组件守卫

beforeRouteEnter 加载之前

beforeRouteLeave 离开路由组件是

beforeRouteUpdate 复用路由组件时

export default {
  name: "Detail",
  props: ["id", "name"],

  // 访问路由组件时 加载之前
  beforeRouteEnter(to, from, next) {
    console.log("组件中的守卫 进来");
    next();
  },
  // 离开路由组件时
  beforeRouteLeave(to, from, next) {
    console.log("组件中的守卫 离开");
    next();
  },
  // 复用路由组件时
  beforeRouteUpdate(to, from, next) {
    console.log("组件中的复用");
    next();
  },
};
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue.js是一种用于构建用户界面的JavaScript框架,由Evan You于2014年创建。它采用了响应式的数据绑定和组件化的开发方式,使得开发者能够更轻松地构建高效、可维护的前端应用程序。 在Vue知识点中,首先需要了解Vue的基本概念和核心特性。Vue的核心是响应式数据绑定,它允许开发者将数据与DOM元素进行绑定,使得数据的变化能够自动反映到界面上。此外,Vue还提供了指令、计算属性、监听器等功能,方便开发者处理复杂的数据逻辑和交互行为。 另外,Vue的组件化开发模式也是非常重要的知识点。通过将应用程序拆分成一系列独立的组件,开发者可以更好地管理和复用代码。Vue提供了组件的定义、组件通信、组件生命周期等功能,方便开发者构建可扩展的应用程序。 除了基本的概念和核心特性,Vue知识点还包括路由管理、状态管理、动画效果等方面。路由管理允许开发者通过URL的变化来实现页面之间的切换和导航,使得应用程序可以呈现更好的用户体验;状态管理用于管理应用程序的全局状态,方便各个组件之间的数据通信和共享;动画效果可以为应用程序增加交互性和视觉效果。 综上所述,一个完整的Vue知识点的PDF分享应当包括Vue的基本概念、响应式数据绑定、组件化开发模式、路由管理、状态管理和动画效果等方面的内容。通过学习这些知识点,开发者可以更好地掌握Vue的使用方法,提高前端开发的效率和质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值