[TOC]

Vue脚手架中结构的分析

脚手架的文件们
.
├── babel.config.js // babel配置文件, 用于ES6->ES5语言的转变
├── node_modules // 依赖包
├── package-lock.json // 包版本控制文件
├── package.json // 包的说明书
├── public // 页面代码
├── favicon.ico // 网页图标
└── index.html // 页面文件
├── README.md // README 介绍文件
└── src // 逻辑代码文件
├── App.vue // 管理者 App.vue 文件
├── assets // 放置静态资源的文件
└── logo.png
├── components // 放置组件的文件
└── HelloWorld.vue // 组件文件
└── main.js // 入口文件
需要细讲的文件
main.js

// 引入Vue
import Vue from ‘vue’

// 引入 App管理组件
import App from ‘./App.vue’

// 关闭提示
Vue.config.productionTip = false

// 创建 vm
new Vue({
el: ‘#app’, // 挂载的容器
render: h => h(App), // 怎么将app组件挂载到网页 ,
});

index.html
<!DOCTYPE html>








<br /> <%= htmlWebpackPlugin.options.title %><br />






当使用单文件组件文件,替换初始化文件
image.png

render 函数

在main.js 中创建的vm进行挂载app时候,使用了 render() 函数
render: h => h(App),
因为在main.js中引入的 vue是属于在创建项目中的 vue.runtime.esm.js 是没有模板解析功能的 ; 解析不了 template, 所以使用了 render() 函数

render() 函数工作原理

// 用法
render(createElement) { // 接收到的参数createElement(创建), createElement是function类型
return createElement(‘h2’, ‘你好vue’)
}
// 简写
render: (createElement) => createElement(‘h2’, ‘你好vue’); //可以在页面中显示

// 在Vue中使用
render: (h) => h(App); // 直接使用组件名 app , 表示会创建
在项目中使用引入vue的文件
image.png
image.png

不同版本的 vue

  1. vue.js 与 vue.runtiem.xxx.js 的区别 :
    1. vue.js 是完整版的 Vue, 包含 : 核心功能 + 模板解析器
    2. vue.runtime.xxx.js 是运行版本的 Vue, 只包含:核心功能 : 没有模板解析器
  2. 因为Vue.runtime.xxx.js没有模板解析器, 所以不能使用 template 配置项, 需要使用render 函数接收到的 createElement函数去指定具体内容
  3. 好处: 减少体积 …

    Vue修改默认配置

    https://cli.vuejs.org/zh/config/#vue-config-js
    vue.config.js 是一个可选的配置文件,如果项目的 (和 package.json 同级的) 根目录中存在这个文件,那么它会被 @vue/cli-service 自动加载。
    例如修改main.js入口文件
    vue.config.js中
    module.exports = {
    pages: {
    index: {
    // page 的入口
    entry: ‘src/main.js’, // 把main.js 改了, 就是改了入口文件的
    },
    lintOnSave: false // 关闭语法错误提示
    }

    ref 属性

    定义ref

    export default {
    components: {
    School,
    Student
    },
    data(){
    return {
    msg: ‘Hello Vue’
    }
    },
    methods: {
    showInfo() {
    console.log(this); // App组件的实例对象
    // 学习 组件中具有的 $refs 属性
    console.log(this.$refs);
    console.log(this.$refs.title); // 真实DOM
    console.log(this.$refs.school); // 组件的实例对象
    }
    }
    }
    在App.vue中的实例对象 上的$refs
    image.png
    ref属性的作用
    在父组件中在子组件使用 ref 属性, 会在 父组件的实例对象中 的 vc.$refs 中出现, 可以获取到 真实的DOM元素信息 , 也可以获取到子组件的实例对象(子组件的this) , 所以有很大的空间复用子组件的数据, 方法 等
    image.png
    总结

  4. 被用来给元素或子组件注册引用信息 (id的替代者)

  5. 应用在html 标签上获取的是真实的DOM元素, 应用在组件标签上是组件的实例对象 (vc)
  6. 使用方式 :

    1. 达标识 :

    2. 获取 : this.$refs.xxx

      props配置

      为了提高代码的复用,在组件中的可以使用 props 对组件的参数自定义

      为什么要使用 props ?

      在App组件中使用student 组件, 并且 student 的数据需要动态变化 。

      image.png
      出现的问题 : 数据不会改变, 在student 组件中,数据是被写死的, 这样代码虽然复用,但是数据不是动态的
      在vc中props的显示
      直接出现在 组件对象的实例上, 在组件实例对象上可以直接使用它们 ,this.xxx , 它们是可读的,修改会发生bug
      image.png

      使用 props

      为了提高代码的复用,在组件中的可以使用 props 对组件的参数自定义 , 在单个组件中需要使用到 props 。
      在组件中定义
      export default {
      name: ‘Student’,
      // data() {
      // return {
      // name: this.name,
      // sex: this.sex,
      // age: this.age,
      // }
      // },

      // 第一种使用方法: 简单定义
      props: [‘name’, ‘sex’, ‘age’],

      // 第二种使用方法 : 限制类型
      props: {
      name: String,
      sex: String,
      age: Number
      },

      // 第三种使用方法: (限制类型, 限制必要性, 指定默认值 )
      props: {
      name: {
      type: String, // 类型
      required: true, // 需要性
      default: ‘老王’ // 默认值
      }
      }
      }
      使用组件时
      // App.vue

      这里定义Number类型的age,需要设置为 v-bind:age=’18’ 或者 :age=’18’ , 才能转为 Number 类型

      需求绑定事件让age+1

      代码如下:
      // Student.app
      export default {
      name: ‘Student’,
      props: {
      name: {
      type: String,
      required: true,
      },
      sex: { // required 必要值,在定义组件标签时, 必须要定义的属性
      type: String,
      required: true,
      },
      age: { // 当没有设置age时候,使用 default
      type: Number,
      default: 99
      }
      },

      methods: {
      add() {
      this.age++;
      }
      }
      }
      出现的问题:
      image.png
      原因 : 虽然能够实现age+1 , 但是出现bug, 因为props是只读的, Vue底层会监视你对props的修改, 如果进行了修改, 就会发出警告,若业务需求确实需要修改, 那么请复制一份props的内容,到 data 中, 然后去修改data中的数据
      解决:

      小结 配置 props
      功能: 让组件接收外部传过来的数据
      接收数据方法 :
      // 第一种使用方法: 简单定义
      props: [‘name’, ‘sex’, ‘age’],

      // 第二种使用方法 : 限制类型
      props: {
      name: String,
      sex: String,
      age: Number
      },

      // 第三种使用方法: (限制类型, 限制必要性, 指定默认值 )
      props: {
      name: {
      type: String, // 类型
      required: true, // 需要性
      default: ‘老王’ // 默认值
      }
      }
      备注: 因为props是只读的, Vue底层会监视你对props的修改, 如果进行了修改, 就会发出警告,若业务需求确实需要修改, 那么请复制一份props的内容,到 data 中, 然后去修改data中的数据

      mixin混合

      当多个组件具有相同的配置文件时候,可以定义一个 xxx.js 的混合文件,通过引入,在配置中使用混合的配置项,可以达到配置的复用
      例如 : 具有相同的配置
      // Student.vue
      methods: {
      showInfo() {
      alert(this.name)
      }
      }

// School.vue
methods: {
showInfo() {
alert(this.name)
}
}

实现复用

// mixin.js 需要创建 mixin.js 混合文件
export const hunhe = {
methods: {
showInfo() {
alert(this.name)
}
}
}
// School.vue

// Student.vue

所以具有相同的配置项, 要使用混合 mixins

全局复用

// mixin.js
export const hunhe = {
methods: {
showInfo() {
alert(this.name)
}
}
}
// 多个混合模块
export const hunhe2 = {
// 定义数据
data() {
return {
x: 1,
y: 2
}
},
mounted() {
console.log(‘@’ + “你好啊”)
}
}
// main.js 入口文件
// 引入混合组件
import { hunhe, hunhe2 } from ‘./mixin.js’;
// 全局使用混合
Vue.mixin(hunhe)
Vue.mixin(hunhe2)
所有的vc 都具有mixin.js 中的方法
image.png
image.png
具有四个vc输入四次 @ , 在每个vc中都具有 data() 中的数据
小结mixin(混入)
功能: 可以把多个组件共用的配置提取成一个混入对象
使用方法 :

  1. 第一步 , 定义混合 例如 export const _mixin = {
    data(){},
    methods: {}
    }
  2. 使用混入 // 全局混入, 在main.js中
    //导入混合文件
    Vue.mixin(xxx)

//局部混入
mixins: [‘xxx’]

Vue中的插件

插件的使用
// plug.js
// 自定义插件是一个 js文件,
export default pulg = { // 保留对象
install(Vue,option) {
//1. 添加全局过滤器
Vue.filter(xxxx);
//2. 添加全局指令
Vue.directive(xxx);
//3. 配置全局混入
Vue.mixin(xxx);
//4, 添加实例对象方法
Vue.prototype.$myMethod = function () {xxx};
Vue.prototype.$myProperty = xxx;
}
}
// main.js
// 引入插件
import plug from ‘./plug’
Vue.use(plug);
在组件中使用plug的方法
//School.vue


// Student.vue

**总结**
功能: 用于增强Vue
本质 : 包含install 方法的一个对象,install 的第一个参数是Vue , 第二个以后的参数是插件使用者传递的数据 ,
自定义插件:
// 自定义插件是一个 js文件,
export default pulg = { // 保留对象
install(Vue,option) {
//1. 添加全局过滤器
Vue.filter(xxxx);
//2. 添加全局指令
Vue.directive(xxx);
//3. 配置全局混入
Vue.mixin(xxx);
//4, 添加实例对象方法
Vue.prototype.$myMethod = function () {xxx};
Vue.prototype.$myProperty = xxx;
}
}
使用插件 :
// 在`main.js` 引入插件
//使用插件
Vue.use(plug); ## scoped样式 作用: 让样式在局部生效,防止冲入 写法 :
示例 :
// Student.vue
// School.vue

Vue组件中的
Vue使用scoped 解决冲突的原理 : 生成一串随机的数字作为标签,修改绑定样式的标签
![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078297116-3797d492-f46b-42be-bc6d-2d928908c892.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=xShXA&margin=%5Bobject%20Object%5D&name=image.png&originHeight=503&originWidth=1252&originalType=url&ratio=1&rotation=0&showTitle=false&size=106554&status=done&style=none&taskId=u5b3867bd-2dc7-4dae-903f-ff82056dcc2&title=) ## TodoList 案例 实现效果 - 添加事件 / 删除事件 - 全选 / 取消全选 - 统计的完成事件 - 清除完成功能 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078297223-dd38175e-bc24-4efb-be6d-8312fb8812b9.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=fODG0&margin=%5Bobject%20Object%5D&name=image.png&originHeight=416&originWidth=930&originalType=url&ratio=1&rotation=0&showTitle=false&size=29636&status=done&style=none&taskId=u24730b0c-00f8-4233-b64d-87638add91d&title=) ### 实现代码 页面分为三个组件, MyHeaderMyListMyFooter , 在 MyList在包含一个Item 组件 和它们的管理组件 App。 样式省略
_App.vue_
在App中需要给它管理的组件下发数据,所以是在App中定义数据的, 对App所管理的组件进行交互, 比如添加,删除等, 也会组件传上来的数据进行处理

_MyHeader.vue_
分析代码, 在Header中需要输入一个事件,接下来,逻辑部分 我们就必须拿到 **事件的值、清空输入、将事件的值包装**成一个跟原数据一样的对象,然后返回给上层 App 组件

_MyList.vue_
在List当中,对Item组件进行循环遍历,展示数据 , 和为 Item 和 App之间的传递数据 , 开启通道

_MyFooter.vue_
Footer中需要 配置数据的 全选或者全不选, 和清空已完成的事件, 和对完成事件的统计

_Item.vue_
在 Item中需要对 App进行数据的交互,比如事件选中,App中的数据要同步,删除数据

TodoList总结

组要熟悉使用单组件开发的流程,基础的事件绑定方法和处理数据 , 组件的上下级之间的传输数据,
对组件化编码的流程 :

  1. 拆分静态组件 : 组件要按照 功能 点拆分, 命名不要于HTML元素冲突
  2. 实现动态组件 : 考虑好数据存放的位置,数据是一个组件在使用,还是一些组件在使用
    1. 一个组件在使用时 : 放在组件自身即可
    2. 一些组件都在使用时 : 放在它们共同的父组件上 (状态提示)
  3. 实现交互 : 绑定事件开始 (基础)

props适用于 :

  1. 父组件 ===> 子组件通信 (:todoList=’todos’ )
  2. 子组件 ===> 父组件通信 (要求父先给子一个函数) :receive=’receive’

使用 v-model 时要切记 : v-model 绑定的值不能是 props 传过来的值, 因为 props 是不可以修改的
props传过来的若是对象类型的值, 修改对象中的属性是 Vue 不会报错, 但是不推荐这样做 ,因为props 是不可以修改的

浏览器本地存储

浏览器本地存储内容的大小一般支持5MB左右 (不同浏览器可能还不一样)
浏览器通过 window.sessionStorage 和 window.localStorage 属性实现本地存储机制
相关API

  1. xxxStorage.setItem(‘key’,’value’) : 该方法接收一个键和值对 添加到存储中,如果键名存在,则更新键值
  2. xxxStroage.getItem(‘person’) : 接受一个键名作为参数, 返回键名对应的值
  3. xxxStorage.removeItem(‘key’) : 接受一个键名作为参数, 并把键名从存储中删除
  4. xxxStorage.clear() : 清空存储中的所有数据

备注 :

  1. SessionStorage存储的内容会随着浏览器窗口关闭而消失
  2. LocalStorage存储的内容,需要手动清除才消失
  3. xxxStroage.getItem(xxx) : 如果xxx对应的value 获取不到, 那么getter的返回值是null
  4. JSON.parse(null) 的结果依然是 null

    TodoList 本地存储

    使用监视属性watch 进行对 todos 的监视, 对于 todos的数据不要写死,使用 localStorage.setItem() 写到 todos 中, 在使用 localStorage.getItem() 写到本地存储中
    App.vue
    data() {
    return {
    // JSON.parse(localStorage.getItem(‘todos’)) 将数据存储到本地
    todos: JSON.parse(localStorage.getItem(‘todos’)) || [],
    }
    },

    watch: {
    todos: {
    deep: true, // 开启深度监视
    handler(value) {
    // 监视todos数据,将todos转为JSON
    localStorage.setItem(‘todos’,JSON.stringify(value))
    }
    }
    }

    组件自定义事件

    因为开发过程中,需要子组件需要给父组件传输数据, 目前学到的一种方法 : 通过父组件给子组件传递函数类型的props实现, 子给父传递数据
    还可以使用组件自定义对象实现 子组件给父组件传递数据

    绑定

    第一种方法 : 通过父组件给子组件邦迪一个自定义事件实现 : 子给父传输数据
    App.vue

_Student.vue_

第二种方法 :
// App.vue
### 解绑 this.$off('hidie'); // 解绑一个自定义事件
this.$off(['hidie','demo']); // 解绑多个自定义事件
this.$off(); // 解绑所有自定义事件 ### 总结 1. 第一种组件间通信方式 , 适用于 : 子组件 ==> 父组件 2. 使用场景 : A是父组件, B是子组件, B想给 A传输数据, 那么就要在A种给B绑定自定义事件 (事件的回调在A中) 3. 绑定自定义事件 : 1. 第一种方式 : 在父组件中 : 2. 第二种方式 : 在父组件中 :
...
mounted() {
this.$refs.xxx.$on('xxx',this.test)
} 3. 若想让自定义事件只能触发一次, 可以使用 once 修饰符 , 或者 $once 方法 4. 触发自定义事件 : this.$emit('hidie',传输的数据) 5. 解绑自定义事件 : this.$off('hidie') 6. 组件上也可以绑定原生的DOM事件 , 需要使用 native 修饰符 7. 注意: 通过 this.$refs.xxx.$on('hidie', 回调) 绑定自定义事件时, 回调要么配置在 methods 中, 要么配置成箭头函数, 否则 this 的指向会出现问题 ## 全局事件总线 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078298377-20fcbc3d-a603-4330-b892-c973a4744888.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=bBCzK&margin=%5Bobject%20Object%5D&name=image.png&originHeight=836&originWidth=1248&originalType=url&ratio=1&rotation=0&showTitle=false&size=672556&status=done&style=none&taskId=u626df5a9-2262-4020-b9a7-88b3e198c27&title=)
兄弟组件之间需要互相传输数据, 需要一个”中间人“ 做跳板,让它们之间的数据能够传输 。 中间人的身份必须要又 $emit $on $off 等方法, 考虑到 全局组件的原型 都 指向 VM , 所以在 VM原型上定义一个 全局事件总线 ,让所有的组件都能通过 this 访问到VM的原型, 进而使用VM原型上的方法
**创建全局事件总线**
// main.js
new Vue({
el: '#app',
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this
}
});
**提供数据方**
...
methods: {
sendStudentName() {
// 使用 $bus.$emit() 自定义组件,和传递数据
this.$bus.$emit('hello', this.name)
}
}
**接收数据方**
methods: {
// 记得写接收的参数
showInfo(data) {
console.log('我是 School 组件我收到了 Student 传过来的数据 ',data)
this.msg = data
},
},
...
mounted() { // 生命周期钩子,
this.$bus.$on('hello', this.showInfo)
},
beforeDestroy() {
// 调用后,在准备销毁前
this.$bus.$off('hello')
} ### 总结 1. 一种兄弟组件之间的通信方式 ,适用于任意组件(父子, 兄弟,爷孙, 父子之间最好使用 props) 2. 安装全局事件总线 : // 在 main.js 中
new Vue({
el: '#app',
render: h => h(App),
beforeCreate() {
// 定义 $bus 作为 全局事件总线 ,值指向 VM
Vue.prototype.$bus = this
}
}); 3. 使用事件总线 1. 接收数据一方 : A想接收数据 , 则 A组件中给 $bus 绑定自定义事件, 事件的**回调**留在 A组件组时 methods: {
demo(data) {....} // 接收参数的方法
}
...
mounted() { // 生命周期钩子
this.$bus.$on('自定义事件', this.demo); // 回调函数
} // 或者将回调直接写在 mounted()中
mounted() { // 生命周期钩子
this.$bus.$on('自定义事件',(name) => {
this.name = name
});
} 2. 提供数据一方 : // 定义方法
... this.$bus.$emit('自定义事件', 需要传输的数据 ) 4. 最好在beforeDestroy钩子中, 使用 $off 去解绑当前组件用到的事件 ## 消息订阅与发布 使用引入的库实现 (pubsub-js)
接收方
mounted() {
// 使用 vue 的全局事件总线
// this.$bus.$on('hello', this.showInfo)

// 使用 pubsub , this.showInfo 回调函数
this.pid = pubsub.subscribe('hello', (pubName,data) => {
console.log('我是 School 组件我收到了 Student 传过来的数据 ',pubName,data )
this.msg = data
}) },
beforeDestroy() {
// 调用后,在准备销毁前
// this.$bus.$off('hello')
// 取消 消息订阅
pubsub.unsubscribe(this.pid)
}
发送方
methods: {
sendStudentName() {
// this.$bus.$emit('hello', this.name) // 发布消息,订阅消息的方法名 要和接收方的一致
pubsub.publish('hello', this.name)
}
} ### 总结 1. 一种组件之间通信的方式, 适用于 任意组件之间的通信 2. 使用: 1. 安装 pubsub : npm i pubsub-js 2. 组件中引入 : import pubsub from 'pubsub-js' 3. 接收数据方 : methods() {
demo(data) {...}
}
...
mounted() {
this.pubid = pubsub.subscribe('xxx', this.demo) // 订阅消息
} 4. 提供数据方 : pubsub.publish('xxx', 数据) 5. 最好在 beforeDestroy 钩子中, 用 pubsub.unsubscribe(pubid) 去 取消订阅 ## $nextTick() nextTick 所指定的回调会在DOM节点更新完毕后执行 , 所以在DOM数据发生改变后使用 nextTick()获取input框的焦点
// 使用 Vue中的 $nextTick 获取焦点
// nextTick 所指定的回调会在DOM节点更新完毕后执行 , 所以在DOM数据发生改变后使用 nextTick()获取input框的焦点
this.$nextTick(function () {
this.$refs.inputTitle.focus()
})
在Vue中 nextTick() 回经常用到, 比如在获取焦点
nextTick() 1. nextTick()语法 : this.$nextTick(回调函数) 2. 作用: 在**下一次**DOM更新结束后执行回指定的回调 3. 什么使用使用 : 当改变数据后, **要基于更新后的新DOM进行某些操作时**, 要在 nextTick 指定的回调函数中执行 也就是在Vue数据发生改变后,发生数据变化, 重新解析模板, 生成新的数据,然后执行 nextTick() 中的回调函数 ## Vue封装的过度与动画 1. 作用 : 在插入、更新或移除DOM元素时, 在合适的时候给元素添加样式类名 2. 图示![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078297787-73bab597-cac6-4a6a-b18d-048177809623.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=hzpB3&margin=%5Bobject%20Object%5D&name=image.png&originHeight=600&originWidth=1200&originalType=url&ratio=1&rotation=0&showTitle=false&size=46785&status=done&style=none&taskId=u987abea6-9f75-4045-a819-21669f31707&title=) 3. 写法: 1. 准备好样式 : - 元素进入时的样式 : 1. v-enter : 进入的起点 2. v-enter-active : 进入过程中 3. v-enter-to : 进入的终点 - 元素离的样式 : 1. v-leave : 离开的起点 2. v-leave-active : 离开的过程中 3. v-leave-to : 离开的终点 2. 使用 包裹要过度的元素, 并配置name属性

你好啊!


3. 备注 : 若有多个元素需要过度, 则需要使用 : 且每个元素都要指定 key 值 4. 使用第三方库 : [https://animate.style/](https://animate.style/) 1. 安装 : npm install animate.css --save 2. 导入 : import 'animate.css'; 3. 使用 :


appear
name='animate__animated animate__bounce'
enter-active-class='animate__rubberBand'
leave-active-class='animate__backOutDown'
>

你好啊!


## Vue配置代理 **方式一**
在vue.config.js 中添加如下配置 ([https://cli.vuejs.org/zh/config/#devserver](https://cli.vuejs.org/zh/config/#devserver))
module.exports = {
devServer: {
proxy: 'http://localhost:8080' // 代理的端口
}
}
说明 : 1. 优点 : 配置简单, 请求资源时, 直接发给前端 8080 即可 2. 缺点: 不能配置多个代理,不能灵活的控制请求是否走代理 3. 工作方式 : 若按照上述配置请求了代理, 当请求了前端不存在的资源时, 那么该请求就会转发给服务器(优先匹配给前端资源) **方式二**
在vue.config.js 中添加如下配置 ([https://cli.vuejs.org/zh/config/#devserver](https://cli.vuejs.org/zh/config/#devserver))
module.exports = {
devServer: {
proxy: {
'/api': { // 匹配 所以 以 `/api` 开头的请求路径
target: 'http://localhost:8080', // 代理目标的基础路径
ws: true, // 支持开启 WebSocks
changeOrigin: true, // 请求的 host 请求ip 是否为反向代理的
pathRewrite: { '^/api': '' }, // 路径重写; 匹配以api开头的路径,重写为 '' 空
},
}
}
} 1. 优点 : 可以配置多个代理,而且可以灵活控制请求是否走代理, 2. 缺点: 配置略为繁琐, 请求资源时必须加前缀 ## github Search案例 实现内容
![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078298147-78a80104-615a-4a19-9771-e3c926c3e173.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=kWD8B&margin=%5Bobject%20Object%5D&name=image.png&originHeight=690&originWidth=1423&originalType=url&ratio=1&rotation=0&showTitle=false&size=145798&status=done&style=none&taskId=u88380a79-c9be-49db-bae2-2864ae74a08&title=)
分析 :可分为两个单个组件 : Search List
search 发送请求获取数据, List负责接收数据,数据渲染 , 涉及到, 两个单个组件中的数据间通信和 axios 请求
写一个 axios请求
import axios from 'axios'; // 使用
axios.get(`url ${参数}`).then(
response => {}, // 请求成功
error => {} // 请求失败
)
**实现代码 :**
_Search.vue_
methods: {
searchUser() {

// 使用全局事件总线 $bus
// 正在加载中
this.$bus.$emit('getUsers',{isFirst:false, isLoading: true, errorMsg: '', users: []})
// 请求数据
axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
// 请求成功时
this.$bus.$emit('getUsers',{ isLoading: false, errorMsg: '', users:response.data.items})
},
error => {
// 请求失败时
this.$bus.$emit('getUsers',{isLoading: false, errorMsg: error.message, users: []})
}
)
}
}
_List.vue_

vue-resource 发送请求

vue-resource : 发送请求库,在vue 1.0版本用的比较多 ,目前使用最多的是 axios , 需要了解一下 vue-resource ,
安装 : npm install vue-resource
在main.js 中引入(插件进入和使用方法) : import vueResource from ‘vue-resource’ 使用 : Vue.user(vueResource) ; 在所有的 vm 或者 vc 身上都多出了一个 $http
组件中使用 : this.$http.get() | this.$http.post()

Vue插槽

默认插槽

// 在组件中定义默认插槽

// 使用插槽

具名插槽

// 定义具名插槽

// 使用具名插槽

作用域插槽

理解 : 数据在组件自身,但是根据数据生成的结构需要组件的使用者来决定。 (games数据在Category组件中, 但使用数据所遍历出来的结构由 App组件来决定)
作用域插槽,当数据没有在使用组件者里 时,可以使用作用域插槽进行数据的传输,改变使用相同组件,不同的样式
// 子组件中


// 父组件中
### 总结 作用 : 让父组件可以向子组件指定的位置插入 **html** 结构, 也是一种组件间的通信方式 , 适用于 父组件==> 子组件
分类 : 默认插槽、 具名插槽 、 作用域插槽
使用方式 : 1. 默认插槽 : //父组件中

HTML结构

// 子组件中
2. 具名插槽 //父组件中


Vue - 图15
更多美食
// 子组件中
3. 作用域插槽理解 : 数据在组件自身,但是根据数据生成的结构需要组件的使用者来决定。 (games数据在Category组件中, 但使用数据所遍历出来的结构由 App组件来决定)// 父组件中






// 子组件中 ## Vuex 官网 : [https://vuex.vuejs.org/zh/](https://vuex.vuejs.org/zh/)
概念 : 在vue中实现集中式状态(数据) 管理的一个Vue插件,对Vue应用中多个组件共享状态进行集中式的管理 (读写) , 也是一种组件间的通信方式 ,适合用于任意间的组件通信
**何时使用** : 当多个组件需要共享数据时 ### Vuex 工作原理 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078298189-c3ad61d9-dbf2-429c-bd0b-6b8d8d80ce71.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=YGGP1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=551&originWidth=701&originalType=url&ratio=1&rotation=0&showTitle=false&size=30472&status=done&style=none&taskId=ud3fd99a4-c8e1-4934-9758-d2a21e79942&title=)
分析 : Vuex主要的核心功能为 stateactionsmutations , 它们之间通信使用了的api有 dispatchcommit 。 state 用于存放数据的 , actions 用于处理逻辑 和 接受网络上传输的参数,或者vc提供的参数 , mutations 用于对数据加工,然后返回给 state 的 。
在图中 actions 上方的 backend API 是表示actions 接收到的数据不仅仅是vc通过dispatch 提供的, 也可以是由 backend API 通过网络请求返回得到的数据 。
mutations 旁边的 Devtools 表示 vue提供的插件,调试工具
Vuex工作流程 : 需要建立文件 /store/store.js 因为vc引入Vuex后 会多出$store , store 相当于Vuex的组件,使用Vuex的Api都要经过 $store。 在 store.js 中需要定义三个对象,也就是Vuex中的三个核心部分 : actionsmutationsstate 。 然后在 main.js 中引入 store.js 文件,添加store 到vm中 ### 搭建Vuex环境 1. 安装 : npm install vuex 2. 创建 : src/store/index.js// 引入Vue核心库
import Vue from 'vue'; // 引入 Vuex
import Vuex from 'vuex'; // 应用 Vuex
Vue.use(Vuex) // 准备好 actions 对象 --- 相应组件中用户的动作
const actions = {} // 准备好 mutations 对象, 修改 store 中的数据
const mutations = {} // 准备好一个 state 对象 , 用于保存具体的数据
const state = {} // 创建Store :Vuex.Store({}) 并暴露 Store
export default new Vuex.Store({
// 当 key 和 value 同名时,使用简写
actions,
mutations,
state
}) 3. 在 main.js中引入storeimport Vue from 'vue'
import App from './App.vue' Vue.config.productionTip = false // 引入 Store
import store from './store/index' //创建VM
new Vue({
el: '#app',
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this
}, // 使用 Vue中的Store
store, // 引入 store
}); 4. 在组件中查看 storemounted() {
console.log(this)
}![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078298661-e6e86181-d958-41cd-8a9c-bf222e185710.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=iEdcd&margin=%5Bobject%20Object%5D&name=image.png&originHeight=529&originWidth=930&originalType=url&ratio=1&rotation=0&showTitle=false&size=72294&status=done&style=none&taskId=uf50307dc-ec6c-4ddb-86bc-9948ed4fa0f&title=) ### 基本使用 1. 初始化数据, 配置 actions 配置mutations 操作文件 store.jsstore.js// 引入Vue核心库
import Vue from 'vue'; // 引入 Vuex
import Vuex from 'vuex'; // 应用 Vuex
Vue.use(Vuex) // 准备好 actions 对象 --- 相应组件中用户的动作
const actions = {
// 相应组件中的动作
incrementOdd(context, value) {
if (context.state.sum % 2) {
context.commit('INCREMENTODD', value)
}
},
incrementWite(context, value) {
setTimeout(() => {
context.commit('INCREMENTWite', value)
}, 500)
}
} // 准备好 mutations 对象, 修改 store 中的数据
const mutations = {
INCREMENT(state, value) { // 收到的是 state中的sum的数据对象具有getter/setter方法, 和actions传过来的 value
// console.log('mutations 收到的参数 :', store, value);
state.sum += value;
},
DECREMENT(state, value) {
state.sum -= value
},
INCREMENTODD(state, value) {
state.sum += value
},
INCREMENTWite(state, value) {
state.sum += value
}
} // 准备好一个 state 对象 , 用于保存具体的数据
const state = {
sum: 0
} // 创建Store :Vuex.Store({}) 并暴露 Store
export default new Vuex.Store({
// 当 key 和 value 同名时,使用简写
actions,
mutations,
state
})Count.vue 2. 组件中读取 vuex 中的数据 : $store.state.sum 3. 组件中修改vuex中的数据 : $store.dispatch('actions中的方法名',数据) 或 $store.commit('mutations中的方法名',数据) 4. 备注 : 若没有发送网络请求或者其他业务逻辑, 组件中也可以越过actions, 即不写 dispatch 直接写 commit ### getters 的基本使用 1. 概念 :当state中的数据需要经过加工后再使用时, 可以通过 getters 对state 中的数据进行加工 2. 在 store.js 中 追加getters 配置 ...
// 准备好一个 getters 对象 , 用于对数据的加工
const getters = {
// 定义 bigSum
bigSum(state) {
return state.sum * 10
}
}
export default new Vuex.Store({
...
getters, // 配置getters
}) 3. 在组件中读取getters 的数据 : $store.getters.bigSum ### Vuex中四个map方法的使用 #### mapSrate 和 mapGetter 当页面上展示出更多的 state 的数据 ,和 getter 数据时, 出现的问题 : $store.state.sum不符合 简单的模板语法
解决问题方法 : 1. 通过计算属性 computed 简写函数 2. 通过Vuex中的mapState和 mapGetter方法进行简写
_Count.vue_
#### mapActions 和 mapMutations mapActions和 mapMutations 一般用于在 methods 方法中使用
没有使用 map方法前
methods: {
increment() {
this.$store.commit('INCREMENT',this.n)
},
decrement(){
this.$store.commit('DECREMENT',this.n)
},
incrementOdd() {
this.$store.dispatch('incrementOdd',this.n)
},
incrementWite() {
this.$store.dispatch('incrementWite',this.n)
}
},
当$store使用 commit时, 也可使用 Vuex提供的map 进行简写因为commit是和 Mutations 进行对话的,使用 mapMutations() 能够简写$store.commit()
methods: {
// 当$store 使用 commit时, 也可使用 Vuex提供的map 进行简写
// 因为commit是和 Mutations 进行对话的,使用 mapMutations() 能够简写 $store.commit() // 使用 mapMutations() 传参必须要写在模板中, 不然在mutations中的INCREMENT收到的是 evtent ,而不是参数
// ...mapMutations({'INCREMENT':'INCREMENT'}), // 对象式 ...mapMutations(['INCREMENT']), // 数组式 // 同理
// ...mapMutations({'decrement':'DECREMENT'}), // 对象式
// key 要和模板中的事件一致, value 需要和 mutations 中的方法一致 ...mapMutations(['DECREMENT']), // 模板中使用的方法名要一致才能使用数组式简写 // *************************************** **
// dispatch 使用 mapActions() // 使用 mapActions() 对使用 $store.dispatch() 方法进行简写 ,
// 使用方法和上边一样, mapActions()直接跟actions进行对话 // ...mapActions({'incrementOdd':'incrementOdd'}), // 对象式
...mapActions(['incrementOdd']), // 数组式 // ...mapActions({'incrementWite':'incrementWite'}),
...mapActions(['incrementWite']), // 数组式
},
使用 mapMutations() 和 mapActions() 传参必须要写在模板中, 不然在mutations中的INCREMENT收到的是 evtent ,而不是参数



#### 总结 1. mapState 方法 : 用于帮助我们映射 state 中的数据为计算属性 computed: {
// 使用 mapState 方法 (对象式)
// 使用 ...mapState() 是ES6中 对象中,写入多个对象使用 ... 映射
...mapState({'sum':'sum', 'name': 'name', 'age':'age',}), // 使用 mapState 方法 (数组式)
// 数组式,必须式定义映射的变量名 必须要和 state中的变量名一致
...mapState(['sum', 'name', 'age']),
}, 2. mapGetters 方法 : 用于帮助我们映射 getters 中的数据为计算属性 computed: {
// 对象式 {}
...mapGetters({'bigSum':'bigSum'}), //数组式
...mapGetters(['bigSum'])
} 3. mapActions 方法 : 用于帮助我们生成与 actions 对话的方法, 即: 包含 $store.dispatch(xxx) 的函数methods: {
...mapActions({'incrementWite':'incrementWite'}), // 对象式
...mapActions(['incrementWite']), // 数组式
} 4. mapMutations 方法 : 用于帮助我们生成与 mutations 对话的方法, 即: 包含 $store.commit(xxx) 的函数methods: {
...mapMutations({'INCREMENT':'INCREMENT'}), // 对象式 ...mapMutations(['INCREMENT']), // 数组式
} 备注: mapActions 与 mapMutations 使用时, 若需要传递参数需要: 在模板中绑定事件时传递好参数,否则参数是事件对象 ### Vuex实现数据共享 实现过程 : 把需要共享的数据放在vuex中的 state 中, 让两方组件都能使用 this.$store.stare.xxx 能够访问到数据,组件中逻辑引起 state 中的数据发生变化, 另一方的定义的数据也会发生变化
//index.js 定义vuex中的数据
// 准备好一个 state 对象 , 用于保存具体的数据
const state = {
sum: 0, // 需要共享的数据
name: 'Yellowsea',
age: 20,

// 让person组件显示数据
personList: [{ id: '001', name: '张三' }]
} // person.vue 中


// Count.vue


//Person添加数据时调用的 mutations() 方法
Add_Person(state, value) { // state vuex中的state, value person中传过来的 value
state.personList.unshift(value) // 添加数据
}
![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078298890-2fcccc8c-d34b-4f5f-bb52-99759c7fb9b8.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=zvJ4A&margin=%5Bobject%20Object%5D&name=image.png&originHeight=706&originWidth=609&originalType=url&ratio=1&rotation=0&showTitle=false&size=47981&status=done&style=none&taskId=u742fa1b9-7b3a-4dae-8c0c-d9b16f9a525&title=) ### Vuex 模块化 namespace 模块化和命名空间 : 在 store 定义的关于组件的数据,可以机型分开 ,各自具有各自的 方法
//index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex)
// Count 组件相关的数据对象
const countAbout = {
namespaced: true,
state: {
sum: 0,
name: 'Yellowsea',
age: 20,
},
actions: {
incrementOdd(context, value) {
if (context.state.sum % 2) {
context.commit('INCREMENTODD', value)
}
},
incrementWite(context, value) {
setTimeout(() => {
context.commit('INCREMENTWite', value)
}, 500)
}
},
mutations: {
INCREMENT(state, value) {
state.sum += value;
},
DECREMENT(state, value) {
state.sum -= value
},
INCREMENTODD(state, value) {
state.sum += value
},
INCREMENTWite(state, value) {
state.sum += value
},
},
getters: {
bigSum(state) {
return state.sum * 10
}
},
}
// person组件相关的数据
const personAbout = {
namespaced: true,
state: {
personList: [{ id: '001', name: '张三' }]
},
actions: {},
mutations: {
Add_Person(state, value) {
state.personList.unshift(value) // 添加数据
}
},
getters: {},
} export default new Vuex.Store({
// 在Vuex.$store 中使用 模块 modules
modules: {
// 导入定义的模块
countAbout,
personAbout,
}
})
// 查看组件中的 this 出现的方法 $store.state 中 出现了这两个模块
![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078299044-00fb222b-ded1-4cf8-a2cb-74e35bd8f56f.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=TB9j1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=772&originWidth=958&originalType=url&ratio=1&rotation=0&showTitle=false&size=114567&status=done&style=none&taskId=u8613ba85-bde7-4ac1-8ca6-59a99a473ea&title=)
**在组件中获取到模块中的数据**
// Count.vue 中
computed: { // 使用 模块化命名空间时, 用mpa时候,获取模板数据中的方法
// ...mapState('模板名',['获取的数据']), ...mapState('countAbout',['sum', 'name', 'age',]),
...mapState('personAbout',['personList']), // 这个是 person中的共享数据
...mapGetters('countAbout',['bigSum'])
},
methods: {
// 使用模板中的方法
// ...mapMutations('模板名',{'方法名':'方法名'}),
...mapMutations('countAbout',{'INCREMENT':'INCREMENT'}),
...mapMutations('countAbout',{"DECREMENT":'DECREMENT'}),
...mapActions('countAbout',{'incrementOdd':"incrementOdd"}),
...mapActions('countAbout',{'incrementWite':'incrementWite'}),
},
//pseron.vue 中
computed: {
// 由自己写的获取数据, 在使用模块化和命名空间时候 的获取数据
//使用方法 : this.$store.state.模块名.state中的数据
personList() {
return this.$store.state.personAbout.personList
},
sum() {
return this.$store.state.countAbout.sum
},
// getter
firstPersonName() {
// 自己写的计算属性获取 getters中的数据 比较特殊 是使用 [模块名/数据] 获取
return this.$store.getters['personAbout/firstPersonName']
}
},
methods: {
Add() {
const personObj = {
"id": nanoid(),
"name": this.name
}
this.name = '',
// 提交数据给 mutations
// 使用 commit : this.$store.commit('模块名/mutations中的方法,传输的数据)
this.$store.commit('personAbout/Add_Person',personObj)
},
AddWang() {
const personObj = {
"id": nanoid(),
"name": this.name
}
this.name = '',
// 提交数据给 mutations
// this.$store.dispatch('模块名/actons中的方法',需要传输的数据)
this.$store.dispatch('personAbout/AddWang',personObj)
},
// 网络请求
addMsg() {
this.$store.dispatch('personAbout/addMsg')
}
},
// index.js
import Vue from 'vue';
import Vuex from 'vuex';
import countAbout from './count';
import personAbout from './person';
Vue.use(Vuex)
export default new Vuex.Store({
// 在Vuex.$store 中使用 模块
modules: {
countAbout,
personAbout,
}
})
// 将index.js 中的模块分割为单个文件 , 并且保留,在 index.js 中引入
count.js
// Count 组件相关的数据对象
export default { // export default
namespaced: true,
...
}
// person.js
// person组件相关的数据
import axios from 'axios';
import { nanoid } from 'nanoid';
export default {
namespaced: true,
actions: {
// 处理逻辑
AddWang(context, value) {
if (value.name.indexOf('王') === 0) {
context.commit('Add_Person', value);
} else {
alert('要求姓王')
}
},
// 使用 api 返回的数据
addMsg(context) {
axios.get('https://autumnfish.cn/api/joke').then(
response => {
context.commit('Add_Person', { id: nanoid(), name: response.data })
console.log(response.data)
},
error => {
console.log(error.message)
})
}
},
getters: {
firstPersonName(state) {
return state.personList[0].name
}
},
... // 省略以下
} #### Vuex模块化 + 命名空间总结 1. 目的: 让代码更好维护, 让数据分类更加明确 2. 修改 : store.jsconst counAbout = {
namespaced: true, // 开启命名空间
state: {},
actions: {},
mutations: {},
getters: {}
} const personAbout = {
namespaced: true, // 开启命名空间
state: {},
actions: {},
mutations: {},
getters: {}
} export default new Vuex.Store({
modules: {
counAbout,
personAbout
}
}) 3. 开启命名空间后, 组件中读取 state 数据 // 方式一
this.$store.state.personAbout.personList
// 方式二 使用了 mapState模块
...mapState('countAbout',['sum','name','age']) 4. 开启命名空间后, 组件中读取 getter数据 // 方式一
this.$store.getter['personAbout/personList']
// 方式二
...mapGetter('countAbout',['bigSum']) 5. 开启命名空间后, 组件中调用 dispatch // 方式一
this.$store.dispatch('personAbout/AddWang',personObj) // 提交数据给 actions
// 方式二
...mapActions('countAbout',{'incrementOdd':"incrementOdd"}), 6. 开启命名空间后, 组件中调用 commit//方式一
this.$store.commit('personAbout/Add_Person',personObj) // 提交数据给 mutations
//方式二
...mapMutations('countAbout',{"DECREMENT":'DECREMENT'}), ## Vue路由 ### 认识Vue中的路由 vue-router理解 : vue的一个插件库, 专门用来实现SPA应用
SPA应用 : - 单页 Web应用 - 整个应用只有一个完整的页面 - 点击页面的导航链接不会 **刷新** 页面, 只会做页面的局部更新 - 数据需要通过 ajax请求获取 路由的理解 : - 什么是路由 : 一个路由就是一组映射关系 (key ---- value) - key 为路径 , value 可能是 function 或 component 路由的分类 : - 后端路由 : - 理解 : value 是 function, 用于处理客户端提交的请求 - 工作过程 : 服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求,返回响应数据 - 前端路由 : - 理解 : value 是 component , 用于展示页面内容 - 工作过程: 当浏览器的路径改变时, 对应的组件就会展示 ### 路由的基本使用 1. 安装vue-router : npm install vue-router 2. 引入 : import VueRouter from 'vue-router' 3. 应用插件 : Vue.use(VueRouter)// 该文件专门用于创建整个应用的 路由器
import VueRouter from 'vue-router';
//引入组件
import About from '../components/About'
import Home from '../components/Home'
// 创建一个路由器, 并暴露出去
export default new VueRouter({
// 路由的对象
// 匹配的路径 注意这里 : routes 而不是 routers
routes: [{
path: '/about', //路由匹配的路径
component: About // 展示的组件
},
{
path: '/home',
component: Home
}
]
}) 1. 编写router配置项 : 创建 /src/router/index.js 4. 实现切换(active-class可配置高亮样式) 5. 指定展示位置
### 几个注意点 1. 路由组件通常存放在 pages 文件夹 , 一般组件放在 components 文件夹 2. 通过切换, "隐藏" 了 **路由组件**, 默认是销毁的, 需要的时候再去挂载。 3. 每个路由组件都有自己的$route属性, 里面存储着自己的路由信息 4. 整个应用只有一个router , 可以通过组件的 $router 属性获取到 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078299478-2e58f878-8bef-45f3-93cd-775585e9c140.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=tra8q&margin=%5Bobject%20Object%5D&name=image.png&originHeight=680&originWidth=829&originalType=url&ratio=1&rotation=0&showTitle=false&size=62940&status=done&style=none&taskId=ufb4e0724-25c1-4e57-a1f0-193434c920c&title=) ### 多级路由(嵌套路由) 1. 路由配置项规则 : 使用 children 配置项 routes: [
{ // 匹配的路径 注意这里 : routes 而不是 routers
path: '/about',
component: About
},

{
path: '/home',
component: Home,

// 注意 children 下的 path 不能使用 "/" "/news" , 因为路由器已经默认加上了
children: [{ // 通过 chidren 配置子级路由
path: 'news', // 注意此处一定不要写成 `/news`
component: News
},
{
path: 'message', // 注意此处一定不要写成 `/message`
component: Message
}
]
}
] 2. 跳转(要写完整路径 )
News ### 路由的 query 参数 参数的写法 : ?key=value&key=value
查看路由接收的参数

image.png
使用方法:

  1. 传递参数
    {{m.title}}

>{{m.title}}
2. 接收参数 : $route.query.id
$route.query.title ### 命名路由 作用简化路径,只负责写定义时路径就行
// 路由文件
routes: [{
name: ‘guanyu’, // 命名路由 ,直接加上 name 参数就行
path: ‘/about’,
component: About
},
{
path: ‘/home’,
component: Home,
children: [{
path: ‘news’,
component: News
},
{
path: ‘message’,
component: Message,
children: [{
name: ‘xiangqing’, // 命名路由
path: ‘detail’,
component: Detail
}]
}
]
}
]
//使用命名路由


{{m.title}}

About ### 路由的 params 参数 params : 传递的参数在url后面,而且不是和query的 ?key=value ,是 http://localhost:8080/#/home/message/detail/003/消息003 这样 , 跟在路径后面
在this种的 params
image.png 1. 配置路由, 使用占位符接收参数{
path: ‘message’,
component: Message,
children: [{
name: ‘xiangqing’, // 命名路由
path: ‘detail/:id/:title’, //使用占位符声明接收 params参数
component: Detail
}]
} 2. 传递params参数
{{m.title}}
:to=”{
name: ‘xiangqing’, // 使用命名路由 简化路径
params: { // 将接收数据的参数 写为 params
id: m.id,
title: m.title
}
}”>
{{m.title}}
3. 备注: 特别注意, 路由携带params 参数时,若使用to的对象式写法, 则不能使用path 配置项, 必须使用 name 配置 4. 接收参数 $route.params.id
$route.params.title ### 路由的 props 配置 作用 : 让路由组件能够更方便的接收到参数
// 路由配置 index.js
{
path: ‘message’,
component: Message,
children: [{
name: ‘xiangqing’, // 命名路由
// path: ‘detail/:id/:title’, //使用占位符声明接收 params参数, 只能由于 params

path: ‘detail’, // 在定义 query 时使用
component: Detail, // 使用路由的 props 参数
//第一种写法: props值为对象,该对象中所有的key-value的组合最终都会通过 props 传给 Detail组件 ,劣: a 和 b 的数据都是写死的
// props: { a: ‘000’, b: ‘hello’ },


// 第二种写法: 布尔值写法, 若 props: true, 则把路由收到的所有 params 参数通过 props 传给 Detail组件
//
// props: true,

// 第三种写法: 函数式,使用路由的回调函数收到的 $route, 使用 $route 根据返回数据定义 props 接收的数据,可以用在 params, 和 query
props($route) {
return {
id: $route.query.id,
title: $route.query.title
}
}
}]
}
一般使用 第三种方法 函数式写法用得多
在message.vue 中


replace
:to=”{
// path: ‘/home/message/detail’, // 保留完整的路径
name: ‘xiangqing’, // 使用命名路由 简化路径
query: { // 使用 query时用query ,使用
id: m.id,
title: m.title
}
}”>
{{m.title}}

//路由组件配置 接收数据 props
// Deatil.vue
// 接收路由参数
// 查看传递过来的参数

// props: [‘a’, ‘b’], // 接收路由传递的参数 方法一
// props: [‘id’, “title”], // 方法二
props: [‘id’, “title”]

的 replace 属性

  1. 作用: 控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器历史记录有两种写入方式: 分别为 push 和 replace , push是追加历史记录, replace 是替换当前记录, 路由跳转的时候默认为 push
  3. 如何开启replace 模式 : News

    编程式路由导航

    因为使用 最终都会转为 标签 , 在开发过程中不仅仅是 标签能够跳转,比如使用
  • go : 连续跳转到路由哪里,接收参数 number类型 , 比如前进几步, 后退几步 1, -1
  • push : 正常的访问,不会替换掉上一条路由路径,以追加的方式添加路由
  • replace : 以替换上一条路由路径方式访问路由
  • forward : 路由向前进一步 (浏览器的向前向后)
  • back : 路由向后退一步 (浏览器的向前向后)

它们接收参数的方法也是和 :to={‘’} 接收参数是一样的
methods: {
pushShow(m) {
// console.log(this.$router)
this.$router.push({ // 使用 路由的追加方式添加路由, $router.push({})
name: ‘xiangqing’,
query: {
id: m.id,
title: m.title
}
})

    },

    replaceShow(m) {<br />            this.$router.replace({   // // 使用 路由的 替换方式添加路由, $router.push({})<br />                name: 'xiangqing',  <br />                query: {<br />                    id: m.id,<br />                    title: m.title<br />                }<br />            })<br />        }<br />}<br />实现功能 <br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078300590-a5fad0c6-0a25-4122-b51b-4c22fe6b1fe0.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=shkFR&margin=%5Bobject%20Object%5D&name=image.png&originHeight=761&originWidth=1344&originalType=url&ratio=1&rotation=0&showTitle=false&size=81559&status=done&style=none&taskId=u08a77445-40a4-4d8c-867c-8c0b7da5aa1&title=)<br />**小结**
  1. 不借助 实现路由跳转, 让路由跳转更加灵活
  2. 具体编码 methods: {
    pushShow(m) {
    // console.log(this.$router)
    this.$router.push({ // 使用 路由的追加方式添加路由, $router.push({})
    name: ‘xiangqing’,
    query: {
    id: m.id,
    title: m.title
    }
    })

     },<br />        replaceShow(m) {<br />            this.$router.replace({   // // 使用 路由的 替换方式添加路由, $router.push({})<br />                name: 'xiangqing',  <br />                query: {<br />                    id: m.id,<br />                    title: m.title<br />                }<br />            })<br />        }<br />}
    

    缓存路由组件

  • : 保持状态
  • include : 参数写组件名, 保持那个组件的活跃状态

在需要显示组件中写具有
// Home.vue






小结

  1. 作用: 让不展示的路由组件保持挂载状态, 不被销毁
  2. 具体编码 :

    引入两个声明周期钩子

    出现问题 : 在 News 组件中, 使用定时器和input 组件,并且在上层组件使用 ,当切走 News组件时候,定时器一直在开启着没有关闭
    image.png
    //news.vue
    mounted() {
    this.timer = setInterval(() => {
    console.log(‘@’)
    this.opacity -= 0.01
    if (this.opacity <= 0 ) {
    this.opacity = 1
    }
    }, 16)
    },
    beforeDestroy() {
    console.log(‘News组件即将被销毁了’)
    clearInterval(this.timer);
    },
    引出两个声明周期钩子, 路由组件常用 : activated 和 deactivated 钩子
    activated 当组件被激活时候 触发函数
    deactivated 当组件失活时候触发函数
    // 当组件被激活状态
    activated() {
    // console.log(‘News组件被激活了’)
    this.timer = setInterval(() => {
    console.log(‘@’)
    this.opacity -= 0.01
    if (this.opacity <= 0 ) {
    this.opacity = 1
    }
    }, 16)
    },

     // 当前组件失活状态 <br />        deactivated() {<br />            console.log('News组件失活了')<br />            clearInterval(this.timer);<br />        },<br />生命周期钩子: 在Vue的生命周期钩子图中体现出的8个钩子, 在图中没有的钩子还有三个 , activateddeactivated$nextTick<br />**小结** : 两个生命周期钩子 
    
  3. 作用 : 路由组件所独有的两个钩子, 用于捕获路由激活组件的激活状态 。

  4. 具体名字 :
    1. activated : 路由组件被激活时触发
    2. deactivated : 路由组件失活时触发

      全局前置-路由守卫

      全局路由守卫

      全局路由守卫前置
      beforeEach((to, from, next)
      初始化时执行、 每次路由切换前执行 , 控制访问地址权限
      定义规则
  • to : 要去哪里 url , 去哪里
  • from : 当前的url 来自哪里
  • next: 使用 next() 执行要跳转的 url

router.beforeEach((to, from, next) => { // 收到的参数
/*
to : 要去哪里 url , 去哪里
from : 当前的url 来自哪里
next: 使用 next() 执行要跳转的 url
*/
// console.log(‘这是to : ‘, to)
// console.log(‘这是 from ‘, from)

// 使用逻辑判断 是否放行  url<br />    // if (to.path === '/home/news' || to.path === '/home/message') {  // 写to 去到的路径, 也可以写name <br />    if (to.name === 'xinwen' || to.name === 'xinxi') { // 写to 去到的路径, 也可以写name <br />        if (localStorage.getItem('user') === 'yellowsae') {<br />            next()<br />        } else {<br />            alert('请登录')<br />        }<br />    } else {<br />        next()<br />    }<br />})<br />问题<br />if (to.name === 'xinwen' || to.name === 'xinxi') <br />因为这样判断不好,可以在路由配置中自定义信息,然后进行判断。 在路由中定义自定义信息是 **路由元信息**meta<br />**学习路由元信息** : 元信息, 也就是开发过程中自定义的信息 <br />定义元信息 : 在路由对象中配置 meta: {isAuth: true}<br />使用 : to.meta.isAuth<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/25614690/1650078301061-496975d8-4706-44b4-9690-c606144a29bd.png#clientId=uc580999e-777a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=OGP7k&margin=%5Bobject%20Object%5D&name=image.png&originHeight=508&originWidth=529&originalType=url&ratio=1&rotation=0&showTitle=false&size=32429&status=done&style=none&taskId=u4c0cf51e-5d13-407d-9d7c-740a4368ab8&title=)<br />路由中使用 <br />if (to.meta.isAuth) { // 直接判断 isAuth ,能简写很多<br />    if (localStorage.getItem('user') === 'yellowsae') {<br />        next()<br />    } else {<br />        alert('请登录')<br />    }<br />} else {<br />    next()<br />}  <br />**全局路由守卫后置**<br />能够获取到路由的 to | from 的信息 <br />afterEach(to,from) //没有 next() , 因为它是跳转后再执行的 <br />router.afterEach((to, from) => { //  每次路由切换之后执行<br />    // 作用 , 可以在访问后执行一些DOM操作 , 比如修改标题 <br />    // console.log(to, from)<br />    if (to.meta.value) {<br />        document.title = to.meta.value  // 修改网页标题<br />    } else {<br />        document.title = 'vue-test'<br />    }<br />})

独享路由守卫

就是单独路由的规则 , 使用 api , beforEnter(to,from,next) , 直接写在路由规则里
独享路由只有beforEnter(to,from,next) 没有 afterEnter(to,from,next)
定义
{
name: ‘xinwen’,
path: ‘news’,
component: News,
meta: {
isAuth: true,
value: ‘新闻’
},
beforeEnter(to, from, next) { //使用独享路由守卫
if (to.meta.isAuth) { //使用路由元信息
if (localStorage.getItem(‘user’) === ‘yellowsae’) {
next()
} else {
alert(‘请登录’)
}
} else {
next()
}
},
},

组件内路由守卫

分为
进入组件时守卫 beforRouteEnter(to,from,next)
离开组件时路由守卫 : beforeRouteLeave(to,from,next)
使用
About.vue
// 组件内路由守卫
// 在进入组件时候调用
beforeRouteEnter (to, from, next) {
if (to.meta.isAuth) { //使用路由元信息
if (localStorage.getItem(‘user’) === ‘yellowsae’) {
next()
} else {
alert(‘请登录’)
}
} else {
next()
}
},

// 在离开当前组件时候调用 <br />    beforeRouteLeave (to, from, next) {<br />        console.log('About组件路由要离开了')<br />        next()<br />    }<br />**小结**
  1. 作用 : 对路由进行权限的控制
  2. 分类 : 全局守卫 , 独享守卫 ,组件内守卫
  3. 全局守卫 :// 全局前置守卫 : 初始化时执行、 每次路由切换前执行
    router.beforeEach((to, from, next) => { // 收到的参数
    // 使用逻辑判断 是否放行 url
    // if (to.path === ‘/home/news’ || to.path === ‘/home/message’) { // 写to 去到的路径, 也可以写name
    // if (to.name === ‘xinwen’ || to.name === ‘xinxi’)
    if (to.meta.isAuth) {
    if (localStorage.getItem(‘user’) === ‘yellowsae’) {
    next()
    } else {
    alert(‘请登录’)
    }
    } else {
    next()
    }
    })

// 全局路由守卫, 初始化时执行、 每次路由切换之后执行
router.afterEach((to, from) => { // 每次路由切换之后执行
// 作用 , 可以在访问后执行一些DOM操作 , 比如修改标题
// console.log(to, from)
if (to.meta.value) {
document.title = to.meta.value // 修改网页标题
} else {
document.title = ‘vue-test’
}
})

  1. 独享路由守卫beforeEnter(to, from, next) { //写在路由配置里
    if (to.meta.isAuth) { //使用路由元信息
    if (localStorage.getItem(‘user’) === ‘yellowsae’) {
    next()
    } else {
    alert(‘请登录’)
    }
    } else {
    next()
    }
    },

  2. 组件内路由守卫 beforeRouteEnter (to, from, next) // 在进入组件时候调用
    beforeRouteLeave (to, from, next) // 在离开当前组件时候调用

路由器的两种工作模式

定义路由器的工作模式 :
src/router/index.js
const router = new VueRouter({
mode: ‘hash’, // 默认就是 hash
// mode: ‘history’, // history 工作模式
})
代码实现
将前端人员写好的代码进行打包 : npm run build
打包后生成了 dist 文件 , 就是前端生成的静态资源文件
image.png
发送给后端服务器使用部署上线
小结

  1. 对于一个url来说, 什么是 hash 值 ? —— # 及其后面的内容就是hash 值
  2. hash值不会包含在 HTTP请求中,即 : hash值不会带给服务器
  3. hash模式 :
    1. 地址中永远带着#号 , 不美观
    2. 若以后将地址通过第三方手机 APP分享链接, 若APP校验严格,则地址会被标记为不合法
    3. 兼容性好
  4. history模式
    1. 地址干净, 美观
    2. 兼容性和 hash模式相比略差
    3. 应用部署上线时需要后端人员的支持,解决刷新页面服务端的404问题

      Vue使用ElementUI组件库

      前端常用的UI组件库
      移动端 :

PC端 :

// 使用
Vue.use(ElementUI)
//在组件中使用
App.vue

image.png
完整版引入出现的体积过大
image.png
按需引入 在ElementUI 中有按需引入的教程
安装 : npm install babel-plugin-component -D
然后,将.babelrc 修改为:(在生成的.babelrc 已经改为babel.config.js) 了,需要修改配置
babel.config.js
presets: [
‘@vue/cli-plugin-babel/preset’,
[“@babel/preset-env”, { “modules”: false }]
],
plugins: [
[
“component”,
{
“libraryName”: “element-ui”,
“styleLibraryName”: “theme-chalk”
}
]
]
//main.js
// 按需引入
import { Button, Row, DatePicker } from ‘element-ui’;

// 注册全局组件 , 不需要引入样式
// Vue.component(自定义组件名, 需要注册的组件)
Vue.component(‘Hidie-button’, Button)
Vue.component(‘Hidie-row’, Row)
Vue.component(‘Hidie-date-picker’, DatePicker)
//App.vue

按需引入的效果 , 和全部引入的一致
image.png
占用大小
image.png
总结 :
在使用组件库中, 尽量使用按需引入, 而且不需要背文档,使用到直接查看文档,直接复制粘贴就行