Vue
插值表达式:Vue的一种模板语法
作用:利用 表达式 进行插值渲染
语法:{{ 表达式 }}
注意点:
-
使用的数据要存在
-
支持的是表达式,不是语句 if for
-
不能在标签属性中使用 {{ }}
-
data里面的数据是响应式数据
v-html : 设置元素的innerHTML <div v-html="msg"></div>
v-show 通过css当中display :none来隐藏 适用于频繁操作
v-if 根据判断条件 控制元素的创建与移除 (条件渲染)
v-else必须配合v-if使用
<p v-if="score>=90">成绩评定A:奖励电脑一台</p>
<p v-else-if="score>=70">成绩评定B:奖励周末郊游</p>
<p v-else-if="score>=60">成绩评定C:奖励零食礼包</p>
<p v-else>成绩评定D:惩罚一周不能玩手机</p>
v-on:事件名 = '内联语句' v-on: 可以替换为@
<button @click = 'count=count-2'>-</button>
<button v-on:mouseenter = 'count = count+2'>+</button>
methods:{ } 提供处理逻辑函数
methods中的所有函数,this指向都指当前实例
v-bind:src(标签属性)
简写 :src
<img v-bind:src="imgUrl" v-bind:title="msg" alt="">
<img :src="imgUrl" :title="msg" alt="">
操作class
<div :class="{类名1:布尔值,类名2:布尔值}"></div>如果布尔值为true就添加上去,false就不添加
<div :class="[类名1,类名2]"></div> 数组当中的所有类都会添加到盒子上去
v-for="(item,index) in list"
<li v-for="(item,index) in list">水果{{item}}-{{index}}</li>
一个参数括号可以省略
<li v-for="item in list">水果{{item}}-</li>
v-for中的key:
给列表项添加唯一标识,便于Vue进行列表项的正确排序使用
v-for的行为会尝试原地修改元素
v-model="变量" 实现双向数据绑定
<div id="app"> <h3>v-model修饰符 .trim .number</h3> 姓名:<input v-model.trim="username" type="text"><br> 年纪:<input v-model.number="age" type="text"><br> <h3>@事件名.stop → 阻止冒泡</h3> <div @click="fatherFn" class="father"> <div @click.stop="sonFn" class="son">儿子</div> </div> <h3>@事件名.prevent → 阻止默认行为</h3> <a @click.prevent href="http://www.baidu.com">阻止默认行为</a> </div>
计算属性: computed:{}计算属性:有缓存的,一旦算出结果,就会立刻缓存。下一次读取 直接读取缓存就行了 性能特别高
1.计算属性的默认简写,只能读取不能修改computed:{
计算属性名(){一段代码逻辑
}
return 结果
}
2.计算属性的完整写法
computed: { fullName:{ get(){ return this.firstName+this.lastName }, set(value){ //value传入到fullName的值 console.log(value); this.firstName = value.slice(0,1) this.lastName = value.slice(1) } } },
set(修改的值)
watch监视器:监视数据变化,执行一些逻辑变化和异步操作
1.简单写法
watch:{
数据属性名(newValue,oldValue){
一些业务逻辑
}
}
or
watch:{
'对象属性名'(newValue,oldValue){
一些业务逻辑
}
}
2.完整写法
watch:{ 数据属性名:{ deep:true, //深度监视 immediate:true //进入页面立刻执行一次handler方法 handler(newValue){ console.log(newValue) } } }
deep:true 对复杂数据类型进行深度监视immediate: true, // 立刻执行,一进入页面handler就立刻执行一次
生命周期八大函数
// 1. 创建阶段(准备数据) beforeCreate () { console.log('beforeCreate 响应式数据准备好之前', this.count) }, created () { console.log('created 响应式数据准备好之后', this.count) // this.数据名 = 请求回来的数据 // 可以开始发送初始化渲染的请求了 }, // 2. 挂载阶段(渲染模板) beforeMount () { console.log('beforeMount 模板渲染之前', document.querySelector('h3').innerHTML) }, mounted () { console.log('mounted 模板渲染之后', document.querySelector('h3').innerHTML) // 可以开始操作dom了 }, // 3. 更新阶段(修改数据 → 更新视图) beforeUpdate () { console.log('beforeUpdate 数据修改了,视图还没更新', document.querySelector('span').innerHTML) }, updated () { console.log('updated 数据修改了,视图已经更新', document.querySelector('span').innerHTML) }, // 4. 卸载阶段 beforeDestroy () { console.log('beforeDestroy, 卸载前') console.log('清除掉一些Vue以外的资源占用,定时器,延时器...') }, destroyed () { console.log('destroyed,卸载后') }
scoped
1.style中的样式 默认是作用到全局的
2.加上scoped可以让样式变成局部样式
组件都应该有独立的样式,推荐加scoped(原理)
scoped原理:
1.给当前组件模板的所有元素,都会添加上一个自定义属性
data-v-hash值
data-v-5f6a9d56 用于区分开不通的组件
2.css选择器后面,被自动处理,添加上了属性选择器
div[data-v-5f6a9d56]
工程化状态下data就是一个函数
// data() {
// console.log('函数执行了')
// return {
// count: 100,
// }
// },
组件通信
父组件向子组件传值
<div class="app" style="border: 3px solid #000; margin: 10px"> 我是APP组件 <!-- 1.给父组件标签,添加属性方式 赋值 --> <Son :title="myTitle"></Son> </div> <script> import Son from './components/Son.vue' export default { name: 'App', data() { return { myTitle: '学前端,就来黑马程序员', } }, components: { Son, }, } </script>
<template> <div class="son" style="border:3px solid #000;margin:10px"> <!-- 3.直接使用props的值 --> 我是Son组件 {{title}} </div> </template> <script> export default { name: 'Son-Child', // 2.通过props来接受 props:['title'] } </script> <style> </style>
子组件向父组件传值
<template> <div class="son" style="border: 3px solid #000; margin: 10px"> 我是Son组件 {{ title }} <button @click="changeFn">修改title</button> </div> </template> <script> export default { name: 'Son-Child', props: ['title'], methods: { changeFn() { // 1.通过this.$emit() 向父组件发送通知 this.$emit('changTitle','传智教育') }, }, } </script> <style> </style>
<template> <div class="app" style="border: 3px solid #000; margin: 10px"> 我是APP组件 <!-- 2.父组件对子组件的消息进行监听 --> <Son :title="myTitle" @changTitle="handleChange"></Son> </div> </template> <script> import Son from './components/Son.vue' export default { name: 'App', data() { return { myTitle: '学前端,就来黑马程序员', } }, components: { Son, }, methods: { // 3.提供处理函数,提供逻辑 handleChange(newTitle) { this.myTitle = newTitle }, }, } </script> <style> </style>
props完整写法
<template> <div class="base-progress"> <div class="inner" :style="{ width: w + '%' }"> <span>{{ w }}%</span> </div> </div> </template> <script> export default { // 1.基础写法(类型校验) // props: { // w: Number, // }, // 2.完整写法(类型、默认值、非空、自定义校验) props: { w: { type: Number, required: true, default: 0,//默认值 validator(val) { // console.log(val) if (val >= 100 || val <= 0) { console.error('传入的范围必须是0-100之间') return false } else { return true } }, }, }, } </script> <style scoped> .base-progress { height: 26px; width: 400px; border-radius: 15px; background-color: #272425; border: 3px solid #272425; box-sizing: border-box; margin-bottom: 30px; } .inner { position: relative; background: #379bff; border-radius: 15px; height: 25px; box-sizing: border-box; left: -3px; top: -2px; } .inner span { position: absolute; right: 0; top: 26px; } </style>
validator固定的 验证器
单项数据流
prop的数据是外部传过来的不能直接修改,
要遵循单项数据流
even bus事件总线(相当于一个桥梁)
作用:非父子通信,进行简易消息传递(复杂->Vuex)
1.创建一个都可以访问到的事件总线(空vue实例 -->utils/EvenBus.js)
import Vue from 'vue' const Bus = new Vue() export default Bus
2.组件接收方,监听Bus实例事件
<template> <div class="base-a"> 我是A组件(接受方) <p>{{msg}}</p> </div> </template> <script> import Bus from '../utils/EventBus' export default { data() { return { msg: '', } }, created() { Bus.$on('sendMsg', (msg) => { // console.log(msg) this.msg = msg }) }, } </script> <style scoped> .base-a { width: 200px; height: 200px; border: 3px solid #000; border-radius: 3px; margin: 10px; } </style>
3.组件发送方,触发Bus实例事件
<template> <div class="base-b"> <div>我是B组件(发布方)</div> <button @click="sendMsgFn">发送消息</button> </div> </template> <script> import Bus from '../utils/EventBus' export default { methods: { sendMsgFn() { Bus.$emit('sendMsg', '今天天气不错,适合旅游') }, }, } </script> <style scoped> .base-b { width: 200px; height: 200px; border: 3px solid #000; border-radius: 3px; margin: 10px; } </style>
非父子通信provide&inject
provide&inject作用:跨层级共享数据
1.父组件provide提供数据
<template> <div class="app"> 我是APP组件 <button @click="change">修改数据</button> <SonA></SonA> <SonB></SonB> </div> </template> <script> import SonA from './components/SonA.vue' import SonB from './components/SonB.vue' export default { provide() { return { // 简单类型 是非响应式的 color: this.color, // 复杂类型 是响应式的-推荐 userInfo: this.userInfo, } }, data() { return { color: 'pink', userInfo: { name: 'zs', age: 18, }, } }, methods: { change() { this.color = 'red' this.userInfo.name = 'ls' }, }, components: { SonA, SonB, }, } </script> <style> .app { border: 3px solid #000; border-radius: 6px; margin: 10px; } </style>
2.子孙组件inject取值使用
<template> <div class="grandSon"> 我是GrandSon {{ color }} -{{ userInfo.name }} -{{ userInfo.age }} </div> </template> <script> export default { inject: ['color', 'userInfo'], } </script> <style> .grandSon { border: 3px solid #000; border-radius: 6px; margin: 10px; height: 100px; } </style>
v-model原理
v-model本质上是一个语法糖 是value和input事件的和写
作用提供数据的双向绑定
<template> <div class="app"> <input type="text" v-model="msg1" /> <br /> <!-- v-model的底层其实就是:value和 @input的简写 --> <input type="text" :value="msg2" @input="msg2 = $event.target.value" /> <!-- $event 参数可以访问原生事件对象 --> </div> </template> <script> export default { data() { return { msg1: '', msg2: '', } }, } </script> <style> </style>
v-model简化代码
1.双向绑定数据
2.子组件中props通过value接收,事件触发input
prop属性名固定value
<template> <div> <!-- 我们不能直接使用v-model去绑定 --> <!-- 我们使用change事件监听 --> <select :value="value" @change="handleChange"> <option value="1">北京</option> <option value="2">上海</option> <option value="3">广州</option> <option value="4">深圳</option> </select> </div> </template> <script> export default { name: 'VueBaseSelect', props: { // 我们直接使用value去接收父组件中的selectId value: String }, methods: { handleChange(e) { // 子传父 用到this.$emit() this.$emit("input", e.target.value) } } }; </script> <style scoped></style>
3.父组件 v-model给组件直接绑数据
<template> <div> <!-- 我们使用v-model v-model的原理采用了 :value + @input="?=$event.target.value" --> <BaseSelect v-model="selectId"></BaseSelect> </div> </template> <script> import BaseSelect from "./components/BaseSelect.vue" export default { name: 'VueApp', data() { return { selectId: '2', }; }, components: { BaseSelect } }; </script> <style scoped></style>
.sync修饰符
作用:可以使用子组件与父组件的双向数据绑定,简化代码
特点:prop属性名,可以自定义,非v-model的固定value
1.父组件属性传参
:isShow.sync="isShow 等价于 :isShow+@updata:isShow
<template> <div class="app"> <button @click="openDialog">退出按钮</button> <!-- isShow.sync => :isShow="isShow" @update:isShow="isShow=$event" --> <BaseDialog :isShow.sync="isShow"></BaseDialog> </div> </template> <script> import BaseDialog from './components/BaseDialog.vue' export default { data() { return { isShow: false, } }, methods: { openDialog() { this.isShow = true // console.log(document.querySelectorAll('.box')); }, }, components: { BaseDialog, }, } </script> <style> </style>
2.子组件向父组件传递值
this.$emit('update:isShow',false)
<template> <div class="base-dialog-wrap" v-show="isShow"> <div class="base-dialog"> <div class="title"> <h3>温馨提示:</h3> <button class="close" @click="closeDialog">x</button> </div> <div class="content"> <p>你确认要退出本系统么?</p> </div> <div class="footer"> <button>确认</button> <button>取消</button> </div> </div> </div> </template> <script> export default { props: { isShow: Boolean, }, methods:{ closeDialog(){ this.$emit('update:isShow',false) } } } </script> <style scoped> .base-dialog-wrap { width: 300px; height: 200px; box-shadow: 2px 2px 2px 2px #ccc; position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); padding: 0 10px; } .base-dialog .title { display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid #000; } .base-dialog .content { margin-top: 38px; } .base-dialog .title .close { width: 20px; height: 20px; cursor: pointer; line-height: 10px; } .footer { display: flex; justify-content: flex-end; margin-top: 26px; } .footer button { width: 80px; height: 40px; } .footer button:nth-child(1) { margin-right: 10px; cursor: pointer; } </style>
利用ref和$refs获取DOM元素标签
1.给目标标签-添加ref属性(也可以获取组件)
<div class="base-chart-box" ref="baseChartBox">子组件</div>
2.恰当时机,通过this.$refs.xxx,获取目标标签(mounted(){} //页面渲染完成)
mounted(){ console.log(this.$refs.baseChartBox) }
document.querySelector 会查找项目中所有的元素
Vue异步更新的
$nextTick:等DOM更新完后才会执行方法里面的函数体
语法this.$nextTick(函数体)
定义指令**
1.全局注册指令(在main.js)
// // 1. 全局注册指令 // Vue.directive('focus', { //focus指令名 // // inserted 会在 指令所在的元素,被插入到页面中时触发 // inserted (el) { // // el 就是指令所绑定的元素 // // console.log(el); // el.focus() // } // })
在标签使用v-focus 就可以获取焦点了
2.局部注册指令
// 2. 局部注册指令 directives: { // 指令名:指令的配置项 focus: { inserted (el) { el.focus() } } }
指令的值
binding.value 就是指令的值
inserted 提供的是元素被添加到页面中时的逻辑
<template> <div> <h1 v-color="color1">指令的值1测试</h1> <h1 v-color="color2">指令的值2测试</h1> </div> </template> <script> export default { data () { return { color1: 'red', color2: 'orange' } }, directives: { color: { // 1. inserted 提供的是元素被添加到页面中时的逻辑 inserted (el, binding) { // console.log(el, binding.value); // binding.value 就是指令的值 el.style.color = binding.value }, // 2. update 指令的值修改的时候触发,提供值变化后,dom更新的逻辑 update (el, binding) { console.log('指令的值修改了'); el.style.color = binding.value } } } } </script> <style> </style>
*v-loading**
默认插槽:让组件内部自定义
1.组件内需要定制的部分,改用<slot></slot>
<template> <div class="dialog"> <div class="dialog-header"> <h3>友情提示</h3> <span class="close">✖️</span> </div> <div class="dialog-content"> <!-- 1. 在需要定制的位置,使用slot占位 --> <slot></slot> </div> <div class="dialog-footer"> <button>取消</button> <button>确认</button> </div> </div> </template>
2.使用组件时在 <MyDialog>
<div>你确认要删除么</div>
</MyDialog>传入结构替换slot
<template> <div> <!-- 2. 在使用组件时,组件标签内填入内容 --> <MyDialog> <div>你确认要删除么</div> </MyDialog> <MyDialog> <p>你确认要退出么</p> </MyDialog> </div> </template>
插槽-默认值 -后备内容
插槽后备内容:封装组件时可以为预留的<slot>插槽提供默认内容
1.往slot标签内部,编写内容,可以作为后备内容(默认值)
<slot>我是默认的文本内容 </slot>
<template> <div class="dialog"> <div class="dialog-header"> <h3>友情提示</h3> <span class="close">✖️</span> </div> <div class="dialog-content"> <!-- 往slot标签内部,编写内容,可以作为后备内容(默认值) --> <slot> 我是默认的文本内容 </slot> </div> <div class="dialog-footer"> <button>取消</button> <button>确认</button> </div> </div> </template>
<template> <div> <MyDialog></MyDialog> //触发插槽默认值 <MyDialog> 你确认要退出么 </MyDialog> </div> </template>
具名插槽
1.多个slot使用name属性区分名字
<slot name="名字"></slot>
<template> <div class="dialog"> <div class="dialog-header"> <!-- 一旦插槽起了名字,就是具名插槽,只支持定向分发 --> <slot name="head"></slot> </div> <div class="dialog-content"> <slot name="content"></slot> </div> <div class="dialog-footer"> <slot name="footer"></slot> </div> </div> </template>
2.template配合v-slot:名字,来分发对应的标签,v-slot:名字简写#插槽名
<template> <div> <MyDialog> <!-- 需要通过template标签包裹需要分发的结构,包成一个整体 --> <template v-slot:head> <div>我是大标题</div> </template> <template v-slot:content> <div>我是内容</div> </template> <template #footer> <button>取消</button> <button>确认</button> </template> </MyDialog> </div> </template>
插槽作用域
-
给slot标签,添加属性的方式传值
<slot :row="item" msg="测试文本"></slot>
-
将所有的属性,添加到一个对象中
<!-- 2. 将所有的属性,添加到一个对象中 --> <!-- { row: { id: 2, name: '孙大明', age: 19 }, msg: '测试文本' }
-
通过template #插槽名="变量名" 接收
<template> <div> <MyTable :data="list"> <!-- 3. 通过template #插槽名="变量名" 接收 --> <template #default="obj"> <button @click="del(obj.row.id)"> 删除 </button> </template> </MyTable> <MyTable :data="list2"> <template #default="{ row }"> <button @click="show(row)">查看</button> </template> </MyTable> </div> </template>
-
路由
1.什么是单页面应用程序? 所有功能都在一个html页面
2.单页面的优缺点? 优点:按需更新性能高,开发效率好。缺点:学习成本,首屏加载慢,不利于SEO
3.应用场景:系统类网站、内部网站、
vue当中的路由就是路径和组件的映射关系
VueRouter的使用(5+2)v2对应VueRouter3.x Vuex3.x v3对应VueRouter4.x Vuex4.x**
5个基础步骤:
1.下载VueRouter模块到当前工程,版本3.6.5
yarn add vue-router@3.6.5
2.在main.js中引入
import VueRouter from 'vue-router'
3.安装注册
Vue.use(VueRouter) // VueRouter插件初始化
4.创建路由对象
const router = new VueRouter({ // routes 路由规则们 // route 一条路由规则 { path: 路径, component: 组件 } routes: [ { path: '/find', component: Find }, { path: '/my', component: My }, { path: '/friend', component: Friend }, ] })
5.将路由对象创建到new Vue实例当中去,建立关联
new Vue({ render: h => h(App), router }).$mount('#app')
2个核心步骤
1.创建views目录,在main.js中配置路由规则
const router = new VueRouter({ // routes 路由规则们 // route 一条路由规则 { path: 路径, component: 组件 } routes: [ { path: '/find', component: Find }, { path: '/my', component: My }, { path: '/friend', component: Friend }, ] })
2.配置导航,配置路由出口
<template> <div> <div class="footer_wrap"> <a href="#/find">发现音乐</a> <a href="#/my">我的音乐</a> <a href="#/friend">朋友</a> </div> <div class="top"> <!-- 路由出口 → 匹配的组件所展示的位置 --> <router-view></router-view> </div> </div> </template>
<!-- 路由出口 → 匹配的组件所展示的位置 -->
<router-view></router-view>
告诉组件名字是多个单词
<template> <div> <p>发现音乐</p> <p>发现音乐</p> <p>发现音乐</p> <p>发现音乐</p> </div> </template> <script> export default { name: 'FindMusic'//告诉组件名字是多个单词 } </script> <style> </style>
组件分类:(页面组件&复用组件)便于维护
页面组件-views文件夹=>配合路由,页面展示
复用组件-components文件夹=>封装复用
路由的封装抽离
1.src新建文件夹router当中新建index.js
@相当与src
import Find from '@/views/Find' import My from '@/views/My' import Friend from '@/views/Friend' //@类似src import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) // VueRouter插件初始化 // 创建了一个路由对象 const router = new VueRouter({ // routes 路由规则们 // route 一条路由规则 { path: 路径, component: 组件 } routes: [ { path: '/find', component: Find }, { path: '/my', component: My }, { path: '/friend', component: Friend }, ] }) export default router
2.在main.js当中导入
import Vue from 'vue' import App from './App.vue' import router from './router/index' Vue.config.productionTip = false new Vue({ render: h => h(App), router }).$mount('#app')
使用vue-router当中的router-link(取代a标签)
1.router-link必须配置to属性,本质还是a标签,to无需#
<template> <div> <div class="footer_wrap"> <router-link to="/find">发现音乐</router-link> <router-link to="/my">我的音乐</router-link> <router-link to="/friend">朋友</router-link> </div> <div class="top"> <!-- 路由出口 → 匹配的组件所展示的位置 --> <router-view></router-view> </div> </div> </template>
2.能高亮,默认就会提供高亮类名,可以直接设置高亮样式(自带激活时的类名)
声明式导航-两个类名
router-link会自动给当前导航添加两给高亮类名
1.router-link-exact-active 精确匹配
to="/my" 仅可以匹配 /my
2.router-link-active 模糊匹配(用的多)
to="/my"可以匹配 /my /my/a /my/b .......
自定义匹配的名字
1.在router的index.js中link自定义高亮类名
在router外面
import Find from '@/views/Find' import My from '@/views/My' import Friend from '@/views/Friend' import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) // VueRouter插件初始化 // 创建了一个路由对象 const router = new VueRouter({ // routes 路由规则们 // route 一条路由规则 { path: 路径, component: 组件 } routes: [ { path: '/find', component: Find }, { path: '/my', component: My }, { path: '/friend', component: Friend }, ], // link自定义高亮类名 linkActiveClass: 'active', // 配置模糊匹配的类名 linkExactActiveClass: 'exact-active' // 配置精确匹配的类名 }) export default router
声明式导航-跳转传参
目标:在跳转时可以进行传参
1.查询参数传参(适合多个参数传参)
1.1语法格式 to="/path?参数名=值"
<router-link to="/search?key=黑马程序员">黑马程序员</router-link>
1.2.对应页面组件接收传递过来的值 $route.query.参数名
<template> <div class="search"> <p>搜索关键字: {{ $route.query.key }} </p> <p>搜索结果: </p> <ul> <li>.............</li> <li>.............</li> <li>.............</li> <li>.............</li> </ul> </div> </template> <script> export default { name: 'MyFriend', created () { // 在created中,获取路由参数 // this.$route.query.参数名 获取 console.log(this.$route.query.key); } } </script>
在created中,获取路由参数 this.$route.query.参数名 获取
2.1动态路由传参(传单个参数比较方便)
1.配置动态路由
/path/:参数名? /path/:参数名,表示必须传,如果不传参也希望匹配可以加一个可选符?
import Home from '@/views/Home' import Search from '@/views/Search' import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) // VueRouter插件初始化 // 创建了一个路由对象 const router = new VueRouter({ routes: [ { path: '/home', component: Home }, { path: '/search/:words', component: Search }//动态路由传参 ] }) export default router
2.配置导航链接 to="/path/参数值"‘
<template> <div class="home"> <div class="logo-box"></div> <div class="search-box"> <input type="text"> <button>搜索一下</button> </div> <div class="hot-link"> 热门搜索: <router-link to="/search/黑马程序员">黑马程序员</router-link> <router-link to="/search/前端培训">前端培训</router-link> <router-link to="/search/如何成为前端大牛">如何成为前端大牛</router-link> </div> </div> </template>
3.对应页面组件接收传递过来的值 $route.params.参数名
<template> <div class="search"> <p>搜索关键字: {{ $route.params.words }} </p> <p>搜索结果: </p> <ul> <li>.............</li> <li>.............</li> <li>.............</li> <li>.............</li> </ul> </div> </template> <script> export default { name: 'MyFriend', created () { // 在created中,获取路由参数 // this.$route.query.参数名 获取查询参数 // this.$route.params.参数名 获取动态路由参数 console.log(this.$route.params.words); } }
Vue路由-重定向
问题:网站打开,url默认/路径,未匹配到组件是,会出现空白
说明:重定向->匹配path后,强制跳转path的路径
语法:{ path:匹配路径 ,redirect :重定向的路径 }
import Home from '@/views/Home' import Search from '@/views/Search' import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) // VueRouter插件初始化 // 创建了一个路由对象 const router = new VueRouter({ routes: [ { path: '/', redirect: '/home' },//页面默认跳转到/home { path: '/home', component: Home }, { path: '/search/:words?', component: Search } ] }) export default router
Vue路由 -404
作用:在找不到匹配时,给个提示页面
位置:配在路由最后
语法:在router文件当中index.js当中配置path:"*"(任意路径)-前面不匹配就最后这个
import Home from '@/views/Home' import Search from '@/views/Search' import NotFound from '@/views/NotFound' import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) // VueRouter插件初始化 // 创建了一个路由对象 const router = new VueRouter({ routes: [ { path: '/', redirect: '/home' }, { path: '/home', component: Home }, { path: '/search/:words?', component: Search }, { path: '*', component: NotFound } ] }) export default router
在views当中创建一个NotFound.vue 组件
<template> <div> <h1>404 Not Found</h1> </div> </template> <script> export default { } </script> <style> </style>
Vue模式设置
hash路由(默认) 例如http://localhost:8081/search#/search
history路由(常用)例如http://localhost:8081/search/search
模式切换 在路由对象里面添加 mode:"history" 一旦采用history,地址栏就没有#,需要后台配置访问规则
import Home from '@/views/Home' import Search from '@/views/Search' import NotFound from '@/views/NotFound' import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) // VueRouter插件初始化 // 创建了一个路由对象 const router = new VueRouter({ // 注意:一旦采用了 history 模式,地址栏就没有 #,需要后台配置访问规则 mode: 'history', //路由转化为history模式 routes: [ { path: '/', redirect: '/home' }, { path: '/home', component: Home }, { name: 'search', path: '/search/:words?', component: Search }, { path: '*', component: NotFound } ] }) export default router
编程式导航-2种路由的切换
1.path跳转(1)简写 this.$router.push('路由路径')
(2)完整写法 this.$router.push({path:'路由路径'})
// 1. 通过路径的方式跳转 // (1) this.$router.push('路由路径') [简写] // this.$router.push('/search') // (2) this.$router.push({ [完整写法] // path: '路由路径' // }) // this.$router.push({ // path: '/search' // })
2.name 命名跳转(适合path路径长的场景)
给页面加一个名字 name:"名字"
import Home from '@/views/Home' import Search from '@/views/Search' import NotFound from '@/views/NotFound' import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) // VueRouter插件初始化 // 创建了一个路由对象 const router = new VueRouter({ // 注意:一旦采用了 history 模式,地址栏就没有 #,需要后台配置访问规则 mode: 'history', routes: [ { path: '/', redirect: '/home' }, { path: '/home', component: Home }, { name: 'search', path: '/search/:words?', component: Search }, { path: '*', component: NotFound } ] }) export default router
this.$router.push({ name:'名字'})
// 2. 通过命名路由的方式跳转 (需要给路由起名字) 适合长路径 // this.$router.push({ // name: '路由名' // })
3.1.path路径跳转查询参数传参(query传参)
(1.1)简写 this.$router.push('路由路径?参数名=参数值')
// 1. 通过路径的方式跳转 // (1) this.$router.push('路由路径') [简写] // this.$router.push('路由路径?参数名=参数值') // this.$router.push('/search') // this.$router.push(`/search?key=${this.inpValue}`) //
(1.2)完整写法 this.$router.push({path:'路由路径', query:{参数名:参数值,参数名:参数值}})
// (2) this.$router.push({ [完整写法] 更适合传参 // path: '路由路径' // query: { // 参数名: 参数值, // 参数名: 参数值 // } // }) // this.$router.push({ // path: '/search', // query: { // key: this.inpValue // } // }) // this.$router.push({ // path: `/search/${this.inpValue}` // })
(2)获取参数 $route.query.参数名
在created中,获取路由参数 // this.$route.query.参数名 获取查询参数
3.2path动态路由跳转传参
(1)简写 this.$router.push('/路径名称/参数值')
this.$router.push(`/search/${this.inpValue}`)
(2)完整写法 this.$router.push({
path:'/路径名称/参数值'
})
this.$router.push({ // path: `/search/${this.inpValue}` // })
(3)获取参数 $route.params.参数名
// this.$route.params.参数名 获取动态路由参数
4.1name命明查询参数传参(query传参)
(1)
// 2. 通过命名路由的方式跳转 (需要给路由起名字) 适合长路径 // this.$router.push({ // name: '路由名' // query: { 参数名: 参数值 }, // })
(2)获取参数 $route.query.参数名
// this.$route.params.参数名 获取动态路由参数
4.2name命名路由传参(动态路由传参)
(1)
// 2. 通过命名路由的方式跳转 (需要给路由起名字) 适合长路径 // this.$router.push({ // name: '路由名' // params: { 参数名: 参数值 } // })
(2)获取参数 $route.params.参数名
// this.$route.params.参数名 获取动态路由参数
$router.back()返回
二级路由
routes: [ { path: '/', component: Layout, redirect: '/article', children: [{ path: '/article', component: Article }, { path: '/collect', component: Collect }, { path: '/like', component: Like }, { path: '/user', component: User } ] }, { path: '/detail/:id', component: ArticleDetail } ]
组件名:如果没有配置name属性,才回去找文件名
export default { name: 'CollectPage' }
<keep-alive> </keep-alive>保存缓存
keep-alive是vue内置组件,会缓存不活动的组件实例,而不是销毁他们
他是一个抽象组件:他本身不会渲染成一个DOM元素,也不会出现在父组件链中。
keep-alive的优点:把切换出去的组件保存在内存当中,防止重复渲染DOM
减少加载时间及性能消耗,提高用户体验。
1.include:组件名数组,只有匹配的才会缓存
2.exclude:组件名数组,任何匹配的组件不被缓存
3.max:最多可以缓存多少给组件实例
<template> <div class="h5-wrapper"> <keep-alive :include="['LayoutPage']"> <router-view></router-view> </keep-alive> </div> </template>
一旦组件缓存了就不会触发 created ,mounted ,destroyed。但会提供2个函数activated (看到页面)组件激活时触发,deactivated组件失活时触发(离开页面)
自定义创建项目
vue create 项目名称
vuex概述
vuex是一个vue的状态管理工具,状态就是数据。
场景: 1.某个状态 在很多个组件来使用(个人信息)
2.多个组件共同维护一份数据(购物车)
优势: 1.共同维护一份数据,数据集中化管理
2.响应式变化
3.操作简单(vuex提供了一些辅助函数)
如果创建项目的时候没有选择vuex,需要装包
yarn add vuex@3
1.在store文件夹下的index.js插件安装
// 插件安装 Vue.use(Vuex)
2.创建仓库
const store = new Vuex.Store({ // 通过state提供数据(所有组件共享的数据) state: { title: '大标题', count: 100 } })
3.导出给main.js使用
// 导出给main.js使用 export default store
4.在main.js当中导入挂载
import Vue from 'vue' import App from './App.vue' import store from '@/store/index' Vue.config.productionTip = false new Vue({ render: h => h(App), store }).$mount('#app')
使用数据
1.获取store
模板中 : {{ $store.state.xxx }}
组件逻辑中: this.$store.state.xxx
created () { console.log(this.$router)// undefined 没有配置路由 console.log(this.$store.state.count) },
js模块中: store.state.xxx
2.通过辅助函数(简化)
1.导入 import { mapState } from 'vuex'
2.数组方式引入
computed: { ...mapState(['count', 'title']) },
3.页面直接使用stata里面的数据
<template> <div id="app"> <h1> 根组件 - {{title}} -{{count}} </h1> <input type="text"> <Son1></Son1> <hr> <Son2></Son2> </div> </template>
修改**数据**
strict:true 可以开启严格模式 (可检查是不是修改了长裤的数据,上线需关闭)
vuex同样遵循单项数据流,组件中不能直接修改仓库里面的数据
mutation
定义mutation对象,对象中放修改state的方法
在1.vuex实例对象里添加mutations方法
// 这里存放的是vuex 的相关代码 import Vue from 'vue' import Vuex from 'vuex' // 插件安装 Vue.use(Vuex) // 创建仓库(空仓库) const store = new Vuex.Store({ // 严格模式 strict: true, // 1.通过state提供数据(所有组件共享的数据) state: { title: '大标题', count: 100 }, // 2.通过mutation 可以提供修改数据的方法 mutations: { // 所有mutation函数,第一个参数就是state addCount (state) { state.count += 1 }, changeTitle (state) { } } }) console.log(store.state.count) // 导出给main.js使用 export default store
2.组件调用mutation
this.$store.commit('mutation函数')
<template> <div class="box"> <h2>Son1 子组件</h2> 从vuex中获取的值: <label>{{ $store.state.count }}</label> <br /> <button @click="handleAdd">值 + 1</button> </div> </template> <script> export default { name: 'Son1Com', methods: { handleAdd () { // 错误代码(vue默认不会执行,检测需要成本) // this.$store.state.count++ // 需要提交调用mutation this.$store.commit('addCount') } } } </script> <style lang="css" scoped> .box { border: 3px solid #ccc; width: 400px; padding: 10px; margin: 20px; } h2 { margin-top: 10px; } </style>
mutation(同步)传递参数(不支持传递多个参数,单可以传递对象,数组)
// 这里存放的是vuex 的相关代码 import Vue from 'vue' import Vuex from 'vuex' // 插件安装 Vue.use(Vuex) // 创建仓库(空仓库) const store = new Vuex.Store({ // 严格模式 strict: true, // 1.通过state提供数据(所有组件共享的数据) state: { title: '大标题', count: 100 }, // 2.通过mutation 可以提供修改数据的方法 mutations: { // 所有mutation函数,第一个参数就是state addCount (state, n) {//n 叫提交载荷 state.count += n }, changeTitle (state, str) { state.title = str } } }) console.log(store.state.count) // 导出给main.js使用 export default store
辅助函数:mapMutation
导入 import { mapMutations} from 'vuex'
mapMutation和mapState很像,他是把位于mutation的方法提出来,映射到组件的methods当中去
1.在组件导入import { mapMutations} from 'vuex'
2.在methods当中做映射
methods: { ...mapMutations(['delCount']), del (n) { this.$store.commit('delCount', n) } }
3.组件直接调用函数
<button @click="delCount(1)">值 - 1</button> <button @click="delCount(5)">值 - 5</button>
actions(异步)处理异步操作
1.提供action方法
// 3.actions 处理异步 // 注意不能直接操作state ,操作state还是 commit mutation actions: { // context 上下文(这里没有分模块,可以当初state仓库) // context.commit('mutation',额外参数) changeCountAsync (context, num) { // setTimeout模拟异步,以后大部分场景发请求 setTimeout(() => { context.commit('changeCount', num) }, 1000) } }
2.页面中dispatch调用
this.$store.dispatch('action函数名', 额外参数)
handleChange () { // 调用action this.$store.dispatch('changeCountAsync', 666) }
辅助函数mapActions
作用:把位于action的方法提取出来
1.导入包import { mapActions } from 'vuex'
2.在methods当中做映射
...mapActions(['action函数名']),
<script> import { mapMutations, mapActions } from 'vuex' export default { name: 'Son2Com', methods: { ...mapActions(['changeCountAsync']), ...mapMutations(['delCount', 'changeTitle']), del (n) { this.$store.commit('delCount', n) } } } </script>
页面直接调用action函数
<template> <div class="box"> <h2>Son2 子组件</h2> 从vuex中获取的值:<label>{{ $store.state.count }}</label> <br /> <button @click="delCount(1)">值 - 1</button> <button @click="delCount(5)">值 - 5</button> <button @click="del(10)">值 - 10</button> <button @click="changeCountAsync(999)">一秒后改成999</button> <button @click="changeTitle('崔陈志')">改标题</button> </div> </template> <script> import { mapMutations, mapActions } from 'vuex' export default { name: 'Son2Com', methods: { ...mapActions(['changeCountAsync']), ...mapMutations(['delCount', 'changeTitle']), del (n) { this.$store.commit('delCount', n) } } } </script> <style lang="css" scoped> .box { border: 3px solid #ccc; width: 400px; padding: 10px; margin: 20px; } h2 { margin-top: 10px; } </style>
getters
除了state之外,有时候我们还需要从state中派生处一些状态,这些状态是依赖state,此时我们就会用到getters
1.定义getters
// 4.getters 类似于计算属性 getters: { // 注意点 // 1.形参第一参数就是 state // 2.必须有返回值,返回值就是getters的值 filterList (state) { return state.list.filter(item => item > 5) } }
2.访问getters
2.1通过store访问getters
{{$store.getters.filterList}}
<div>{{$store.getters.filterList}}</div>
2.2通过mapGetters访问
computed: { ...mapGetters(['filterList']), },
直接使用 <div>{{filterList}}</div>
vuex的模块化
在store文件下创建modules,模块就写在modules下,例如user.js ,setting.js
1.创建user.js
// user 模块 const state = { userInfo: { name: 'zs', age: 18 }, score: 80 } const mutation = {} const actions = {} const getters = {} export default { state, mutation, actions, getters }
setting.js
// user 模块 const state = { them: 'light', // 主题色 desc: '测试模块demo' } const mutation = {} const actions = {} const getters = {} export default { state, mutation, actions, getters }
2.在根模块导入子模块
// 5.moduler 模块 modules: { user, setting }
3.在组件当中使用子模块的数据
测试访问模块中的state -- 原生 --
$store.state.模块名.xxx 来访问
<!-- 测试访问模块中的state -- 原生 -- --> <div>{{$store.state.user.userInfo.name}}</div>
利用辅助函数mapstate
1...mapState('模块名', ['属性名'])
computed: { ...mapState('user', ['userInfo']) },
2.开启命名空间
在模块名的导出里面添加 namespaced: true,
export default { namespaced: true, //开启命名空间 state, mutation, actions, getters }
3.组件当中使用
<div>{{userInfo}}</div>
**访问模块当中的getters **
-
原生访问
-
<div>{{$store.getters['user/UpperCaseName']}}</div>
-
特殊字符不能直接对象.访问 的对象【'属性名'】
// user 模块 const state = { userInfo: { name: 'zs', age: 18 }, score: 80 } const mutation = {} const actions = {} const getters = { // 分模块后state指代子模块的state UpperCaseName (state) { return state.userInfo.name.toUpperCase()//toUpperCase() 转化为大写 } } export default { namespaced: true, state, mutation, actions, getters }
mapGetters辅助函数访问
...mapGetters('模块名', ['属性名'])
...mapGetters('user', ['UpperCaseName'])
直接使用
<div>{{UpperCaseName}}</div>
调用模块当中的mutation
1.直接通过store调用$store.commit('模块名/xxx',额外参数)
2.通过mapMutations映射
2.1默认根级别的映射mapMutation(['xxx'])
2.2子模块的映射 maoMutation('模块名',['xxx']) 需要开启命名空间
调用模块当中的action
1.直接通过store调用 $store.dispath('模块名/xxx',额外参数)
2通过mapActions
2.1默认跟级别的映射 mapActions(['xxx'])
2.2子模块的映射mapActions('模块名',['xxx']) 需要开启命名空间