Vue2笔记
本节代码可以从
git clone https://gitee.com/fullstacker1024/vue2-component-demo.git
下载
其中项目实例:git clone https://gitee.com/fullstacker1024/vue2-shopping.git
1、快速上手
1)概念
2)创建实例
3)插值表达式 {{}}
插值表达式是一种Vue的模板语法。
(1)作用:
利用表达式进行插值,渲染到页面中表达式:是可以被求值的代码,JS引擎会将其计算出一个结果
(2)语法:{{表达式}}
<h3>{{title}}</h3>
<p>{{nickname.toUpperCase}}</p>
<p>{{age>=18?'成年':'未成年'}}</p>
<p>{{obj.name}}</p>
(3)注意点:
①使用的数据必须存在(data)
②支持的是表达式,而非语句,比如:if for....
{{if}}//有误
③不能在标签属性中使用{{}}插值
<p title="{{username}}">我是p标签</p>//有误
3)响应式特性
(1)什么是响应式
数据改变,视图自动更新
(2)如果访问或修改数据
data中的数据,最终会被添加到实例上:
①访问数据:“实例.属性名”
②修改数据:“实例.属性名”=“值”
2、指令
Vue根据不同的【指令】,针对标签实现不同的【功能】。
指令:带有v-前缀
的特殊标签属性
普通标签属性:
<div class="box">...</div>
<div title="小张">...</div>
Vue指令:
<div v-html="str">...</div>
1)v-html
(1)作用:设置元素的innerHTML
(2)语法:v-html="表达式"
2)v-show
(1)作用:控制元素显示隐藏
(2)语法:v-show="表达式"
,表达式值true显示,false隐藏
(3)原理:切换display:none控制显示隐藏
(4)场景:频繁切换显示隐藏的场景
3)v-if
(1)作用:控制元素显示隐藏(条件渲染)
(2)语法:v-if="表达式"
,表达式值true显示,false隐藏
(3)原理:基于条件判断,是欧服创建或移除元素节点
(4)场景:要么显示,要么隐藏,不频繁切换的场景
4)v-else、v-else-if
(1)作用:辅助v-if进行判断渲染
(2)语法:v-else
和v-else-if="表达式"
(3)注意:需要紧挨着v-if一起使用
5)v-on
(1)作用:注册事件=添加监听+提供处理逻辑
(2)语法:
①v-on:事件名="内嵌语句"
②v-on:事件名="methods中的函数名"
(3)简写:@事件名="函数或内嵌语句"
<button v-on:click="count++">按钮</button>
<button v-on:click="fn">按钮</button>
<button @click="count++">按钮</button>
注意:methods函数内的this指向Vue实例
6)v-bind
(1)作用:动态的设置html的标签属性,如src、url、title...
(2)语法:v-bind:属性名="表达式"
(3)简写形式::属性名="表达式"
<img v-bind:src="url">
<img :src="url">
const app=new Vue({
el:'#app',
data:{
url:"图片路径"
}
})
(4)v-bind对于样式控制的增强-操作class
语法::class="对象/数组"
①对象:键就是类名,值是布尔值,如果值为true,有这个类,否则没有这个类
<div :class="{类名1:布尔值,类名2:布尔值}"></div>
通用场景:一个类名来回切换
②数组:数组中所有的类,都会加到盒子上,本质就是一个class列表
<div :class="[类名1,类名2]"></div>
通用场景:批量添加或者删除类
(5)v-bind对于样式控制的增强-操作style
语法::style="样式对象"
<div :style="{css属性名1:css属性值1,css属性名2:css属性值2}"></div>
7)v-for
(1)作用:基于数据循环,多次渲染整个元素。(数组、对象、数字....)
<p v-for="">内容</p>
(2)遍历数组语法:
v-for="(item,index) in 数组"
item为每一项,index为下标
省略index:v-for="item in 数组"
(3)v-for中的key
①语法:key属性="唯一标识"
②作用:给列表项添加唯一标识,便于vue进行列表项的正确排序复用。
<ul>
<li v-for="(item,index) in booksList" :key="item.id">
<span>{{item.name}}</span>
</li>
</ul>
③注意点:
key的值只能是字符串或数字类型;
key的值必须具有唯一性;
推荐使用id作为key,不推荐使用index(会变化,不对应)作为key;
7)v-model
(1)作用:给表单元素使用,双向数据绑定,可以快速获取或设置表单元素内容
①数据变化,视图自动更新
②视图变化,数据自动更新
(2)语法:v-model="变量"
(3)v-model应用于其他表单元素
常见的表单元素可以用v-model绑定关联,它会根据控件类型自动选取正确的方法来更新元素。
类型 | 关联 |
---|---|
输入框 input:text | value |
文本域 textarea | value |
复选框 input:checkbox | checked |
单选框 input:radio | checked |
下拉菜单 select | value |
8)指令修饰符
通过“."
指明一些指令后缀,不同后缀封装了不同的处理操作,简化了代码。
①按键修饰符:@keyup.enter
键盘回车监听
②v-model修饰符:
v-model.trim
去除首尾空格
v-model:number
转数字
③事件修饰符
@事件名.stop
阻止冒泡
@事件名.prevent
阻止默认行为
3、计算属性
(1)概念:基于现有的数据,计算出来的新属性,依赖的数据变化,自动重新计算。
缓存特性(提升性能)
计算属性会对计算出来的结果缓存,再次使用直接读取缓存,依赖项变化了,会自动重新计算,并再次缓存
(2)语法:
①声明在computed
配置项中,一个计算属性对应一个函数
②使用起来和普通属性一样使用{{计算属性名}}
计算属性可以将一段
求值的代码
进行封装
computed:{
计算属性名(){
//一段代码逻辑(计算逻辑)
return 结果
}
}
(3)computed计算属性和method方法
①computed计算属性:封装了一段对于数据的处理,求得一个结果
写在computed配置项中,作为属性直接使用:this.计算属性、{{计算属性}}
②methods方法:给实例提供 一个方法,调用以处理业务逻辑
写在methods配置项中,作为方法,需要调用:this.方法名()、{{方法名}}、@事件名="方法名"
(4)计算属性完整写法
计算属性默认的简写,只能读取访问,不能“修改”,如果要“修改”,需要写计算属性的完整写法。
computed:{
计算属性名:{
get(){
//一段代码逻辑(计算逻辑)
return 结果
},
set(修改的值){
//一段代码逻辑(计算逻辑)
}
}
}
4、watch侦听器(监视器)
(1)作用:监视数据变化,执行一些业务逻辑或异步操作
(2)语法:
①简单写法:简单数据类型,直接监视
data:{
words:'苹果',
obj:{words:'苹果'}
},
watch{
//该方法会在数据变化时,触发执行
数据属性名(newValue,oldValue){
//一些业务逻辑或异步操作
},
'对象.属性名'(newValue,oldValue){
//一些业务逻辑或异步操作
}
}
②完整写法:添加额外配置项
对复杂类型深度监视:deep:true
初始化立刻执行一次handler方法:immediate:true
data:{
obj:{
words:'苹果',
lang:'italy'
}
},
watch:{
数据属性名:{
deep:true,//对obj的words和lang同时监视,任意一项变化
handler(newValue){
//一些业务逻辑或操作
}
}
}
eg.
点击查看代码
<div id="app">
<div>
<span>翻译成的语言:</span>
<select v-model="lang">
<option value="italy">意大利</option>
<option value="english">英语</option>
</select>
</div>
<div>
<div>
<textarea v-model="obj.words"></textarea>
</div>
<div>
<div>{{result}}</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.js"></script>
<script>
//https://applet-base-api-t.itheima.net/api/translate
const app = new Vue({
el: "#app",
data: {
obj: {
words: '小黑',
lang: 'italy'
},
result: '',
},
watch: {
obj: {
deep: true,
immediate: true,
handler(n) {
clearTimeout(this.timer)
this.timer = setTimeout(async () => {
const res = await axios({
url: 'https://applet-base-api-t.itheima.net/api/translate',
params: 'n'
})
this.result = res.data.data
console.log(res.data.data)
})
}
}
}
})
</script>
5、生命周期
Vue生命周期:一个Vue实例从创建到销毁的整个过程。
生命周期四个阶段:创建、挂载、更新、销毁
Vue生命周期过程中,会自动运行一些函数,被称为【生命周期钩子】,让开发者可以在【特定阶段】运行自己的代码。
6、工程化开发入门
开发Vue的两种方式:
①核心包传统开发模式:基于html/css/js文件,直接引入核心包,开发Vue
②工程化开发模式:基于构建工具(例如:webpack)的环境中开发Vue
1)工程化和脚手架
Vue CLI是Vue官方提供的一个全局命令工具,可以帮助我们快速创建一个开发Vue项目的标准化基础架子。【集成了webpack配置】
好处:开箱即用,零配置;内置babel等工具;标准化;
使用步骤:
①全局安装一次:
yarn global add @vue/cli
或者npm i @vue/cli -g
②查看Vue版本:vue --version
③创建项目架子:vue create project-name
④启动项目:yarn serve
或者npm run serve
2)项目运行流程
(1)脚手架目录文件结构
(2)main.js
//main.js :文件的核心作用:导入APP.vue,基于APP.vue创建结构渲染index.html
//1、导入Vue核心包
import Vue from 'vue'
//2、导入App.vue根组件
import App from './App.vue'
//3、提示:当前处于什么环境(prod or dev)
Vue.config.productionTip = false
//4、Vue实例化,提供render方法->基于App.vue创建结构渲染index.html
new Vue({
//el:'#app',作用:和$mount('#app')作用一致,用于指定vue所管理的容器
//render:h=>h(App)等价于以下结构
render:(createElement)=>{
return createElement(App)
}
}).$mount('#app')
3)组件化和根组件
(1)组件化:一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为。好处是便于维护,利于复用,提升开发效率。
(2)组件分类:普通组件、根组件
根组件:整个应用最上层的组件,包裹所有普通小组件。
(3)App.vue文件(单个组件)的三个组成部分:结构(tempate)、样式(style,可支持less,需要装包)、行为(script)
(4)让组件支持less:
①style标签,<style lang="less">
,开启less公共
②装包:yarn add less less-loader
4)普通组件的注册
(1)局部注册:只能在注册的组件内使用
①创建.vue文件(三个组成部分)
②在使用的组件
内导入并注册
使用:当成html标签使用,<组件名></组件名>
注意:组件命令规范,要是用大驼峰命名法,如HmHeader
(2)全局注册:所有组件内都能使用
①创建.vue文件(三个部分)
②在main.js
中进行注册
使用同局部组件
5)组件的三大组成部分(结构、样式、逻辑)
(1)scoped样式冲突
默认情况下,写在组件中的样式会全局生效
,因此很容易造成多个组件之间的样式冲突问题。
①全局样式:默认组件中的样式会作用到全局
②局部样式:可以给组件加上scoped属性,可以让样式只作用于当前组件
<style scoped>
</style>
scoped原理:
①给当前组件模板的所有元素,都会被添加上一个自定义属性data-v-hash值,以区分不同的组件
②css选择器后面,被自动处理,添加上属性选择器:div[data-v-hash值]
最终效果:必须是当前组件的元素,才会有这个自定义属性,才会被这个样式作用到。
(2)data是一个函数
一个组件的data选型必须是一个函数
,保证每个组件实例,维护独立的一份数据对象。
data(){
return{
count:20
}
}
7、组件通信
组件的数据是独立的,无法直接访问其他组件的数据,组件通信就是指组件与组件
之间的数据传递。
(1)组件关系的分类:父子关系、非父子关系
(2)组件通信解决方案:
(3)父子通信流程图
①父组件通过props
将数据传递给子组件
②子组件利用$emit
通知父组件修改更新
(4)什么是prop
Prop定义:组件上注册的一些自定义属性
Prop作用:向子组件传递数据
特点:可以传递任意数量的prop,可以传递任意类型的prop
- props校验:
为组件的prop指定验证要求,不符合要求,控制台就会有错误提示,可以帮助开发者快速发现错误。(类型校验、非空校验、默认值、自定义校验)
props:{
校验的属性名:类型//Number String Boolean...
required:true,//是否必填
default:默认值,//默认值
validator(value){
return 是否通过校验//自定义校验逻辑
}
}
(5)prop&data、单项数据流
共同点:都可以给组件提供数据
区别:
①data的数据是自己的,可以随便改
②prop的数据是外部的,不能直接改,要遵循单向数据流
单向数据流:父级prop的数据更新,会向下流动,影响子组件。
1)父传子
(1)实例
2)子传父
(1)实例
3)非父子
(1)event bus事件总线
作用:非父子组件之间,进行简易消息传递。(复杂场景使用Vuex)
(2)provide & inject
作用:跨层级共享数据
8、进阶语法
1)v-model
(1)v-model原理
原理:v-model本质上是一个语法糖。例如应用在输入框上,就是value属性和input事件的合写。
作用:提供数据的双向绑定
①数据变,视图跟着变:value
②视图变,数据跟着变@input
注意:$event
用于在模板中,获取事件的形参
<template>
<div id="app">
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
(2)表单类组件封装
实现子组件和父组件数据的双向绑定
(3)v-model简化代码
实现子组件和父组件数据双向绑定
2)sync修饰符
作用:实现子组件与父组件数据的双向绑定,简化代码
特点:prop属性名可以自定义,非固定为value
场景:封装弹框类的基础组件,visible属性 true显示 false隐藏
本质:就是:属性名
和@update:属性名
合写
//父组件
<BaseDialog :visible.sync="isShow"/>
<BaseDialog :visible="isShow" @update:visible="isShow = $event"/>
//子组件
props:{
visible:Boolean
},
this.$emit('update:visible',false)
3)ref和$ref
作用:利用ref和$ref可以用于获取dom元素或组件实例
特点:查找范围是当前组件内(更精确稳定)
(1)获取dom
①目标标签-添加ref属性
<div ref="chartRef">我是渲染图表的容器</div>
②恰当的时机,通过this.$refs.xxx
获取目标标签
mounted(){
console.log(this.$refs.chartRef)
}
使用querySelector
查找的是整个页面
(2)获取组件
①目标组件-添加ref属性
<BaseForm ref=baseForm"></BaseForm>
②恰当时机,通过this.$refs.xxx
获取目标组件,就可以调用组件对象里面的方法
this.$refs.baseForm.组件方法()
4)$nextTick
Vue异步更新DOM的,想要在DOM更新完成之后做某事,可以使用$nextTick
。
$nextTick
:等DOM更新后,才会触发执行此方法里的函数体
语法:this.$nextTick(函数体)
this.$nextTick(()=>{
this.$refs.inp.focus()
})
9、自定义指令
自定义指令:自己定义的指令,可以封装一些dom操作,扩展额外功能。
1)指令注冊
(1)全局注册
Vue.directive('指令名', {
inserted (el) {
el.focus()
}
})
(2)局部注册
directives: {
指令名 : {
inserted(el){
el.focus()
}
}
}
2)指令使用
<input v-指令名 type="text">
3) 自定义指令 - 指令的值
(1)语法:
①v-指令名 = “指令值” ,通过等号可以绑定指令的值
②通过binging.value可以拿到指令的值
③在inserted钩子中,binding.value判断指令的值,设置默认状态
③通过update钩子,可以监听指令值的变化,进行dom更新操作
(2)实例代码:
点击查看代码
<template>
<div>
<h1 v-color="color1">指令的值1测试</h1>
<h1 v-color="color2">指令的值2测试</h1>
</div>
</template>
<script>
export default {
data () {
return {
color1:'red',
color2:'green'
}
},
directives: {
color: {
inserted (el, binding) {
el.style.color=binding.value
},
update (el,binding) {
el.style.color=binding.value
}
}
}
}
</script>
<style>
</style>
4)自定义指令 - v-loading指令封装
场景: 实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态 => 用户体验不好
需求: 封装一个v-loading指令,实现加载中的效果
(1)核心思路:
①准备类名Loading,通过伪元素提供遮罩层
②添加或移除类名,实现loading蒙层的添加移除
③ 利用指令语法,封装v-loading通用指令
④inserted钩子中,binding.value判断指令的值,设置默认状态
⑤update钩子中,binding.value判断指令的值,更新默认状态
(2)实例代码:
点击查看代码
<template>
<div class="main">
<div class="box" v-loading="isLoading">
<ul>
<li v-for="item in list" :key="item.id" class="news">
<div class="left">
<div class="title">{{ item.title }}</div>
<div class="info">
<span>{{ item.source }}</span>
<span>{{ item.time }}</span>
</div>
</div>
<div class="right">
<img :src="item.img" alt="" />
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
// 安装axios => yarn add axios || npm i axios
import axios from "axios";
// 接口地址:http://hmajax.itheima.net/api/news
// 请求方式:get
export default {
data() {
return {
list: [],
isLoading: true,
isLoading2: false,
};
},
directives: {
loading: {
inserted(el, binding) {
console.log('inserted',binding.value)
if (binding.value) {
el.classList.add("loading");
} else {
el.classList.remove("loading");
}
},
update(el, binding) {
console.log('update',binding.value)
if (binding.value) {
el.classList.add("loading");
} else {
el.classList.remove("loading");
}
},
},
},
async created() {
// 1. 发送请求获取数据
const res = await axios.get("http://hmajax.itheima.net/api/news");
setTimeout(() => {
// 2. 更新到 list 中,用于页面渲染 v-for
this.list = res.data.data;
this.isLoading = false;
}, 1000);
},
};
</script>
<style scoped>
.loading:before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff url("./loading.gif") no-repeat center;
}
.box2 {
width: 400px;
height: 400px;
border: 2px solid #000;
position: relative;
}
.box {
width: 800px;
min-height: 500px;
border: 3px solid orange;
border-radius: 5px;
position: relative;
}
.news {
display: flex;
height: 120px;
width: 600px;
margin: 0 auto;
padding: 20px 0;
cursor: pointer;
}
.news .left {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding-right: 10px;
}
.news .left .title {
font-size: 20px;
}
.news .left .info {
color: #999999;
}
.news .left .info span {
margin-right: 20px;
}
.news .right {
width: 160px;
height: 120px;
}
.news .right img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
10、插槽
1)默认插槽
(1)默认插槽
作用: 让组件内部的一些结构支持自定义
场景: 当组件内某一部分结构不确定,用slot占位封装
使用:
①先在组件内用slot占位
//MyDialog.vue
<template>
<div> <slot></slot> </div>
</template>
②使用组件时,传入具体标签内容插入
<MyDialog>
默认插槽
</MyDialog>
(2)后备内容(默认值)
封装组件时,可以为预留的"< slot >"插槽提供后备内容(默认内容)
使用:
①给插槽设置默认显示内容:在slot标签内,写好后备内容,
//MyDialog.vue
<template>
<div> <slot>我是后备内容</slot> </div>
</template>
②外部使用组件时,不传东西,则slot会显示后备内容:我是后备内容
<MyDialog>
</MyDialog>
or外部使用组件时,传东西了,则slot整体会被换掉:我是内容
<MyDialog>
我是内容
</MyDialog>
2)具名插槽
需求: 一个组件内有多处结构,需要外部传入标签,进行定制
语法:
(1)多个slot使用name属性区分名字
点击查看代码
<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>
(2)template配合v-slot:名字
来分发对应标签,可简写为#名字
点击查看代码
<MyDialog>
<template v-slot:head><div>我是标题</div></template>
<template v-slot:content><div>我是内容</div></template>
<template #footer>
<button>确认</button>
<button>取消</button>
</template>
</MyDialog>
3)作用域插槽
定义slot插槽的同时,是可以传值的。给插值上可以绑定数据,将来使用组件时可以用
(1)给slot标签,以添加属性的方式传值
<slot :id="item.id" msg="测试文本"></slot>
(2)所有添加的属性,都会被收集到一个对象中
{id:3, msg:'测试文本'}
(3)在template中,通过 #插槽名"=obj" 接收,默认插槽名为default
<MyTable :data="list">
<template #default="obj">
<button @click="del(obj.row.id)">删除</button>
</template>
</MyTable>
11、路由的基本使用
单页应用程序:SPA-Single Page Application
所有功能在一个html页面上实现,如网易云音乐
Vue中的路由:路径和组件的映射关系
1)VueRouter的介绍
作用:修改地址栏路径时,切换显示匹配的组件
说明:是Vue官方的一个路由插件,是一个第三方包
2)VueRouter的使用(5+2)
(1)5个基础步骤(固定)
①下载VueRouter模块到当前工程,vue2对应的版本是3.6.5
yarn add vue-router@3.6.5
②main.js引入
import VueRouter from 'vue-router'
③安装注册
Vue.use(VueRouter)
④创建路由对象
const router = new VueRouter()
⑤注入,将路由对象注入到new Vue实例中,建立关联
new Vue({
render: h=>h(App),
router
}).$mount('#app')
(2)2个核心步骤
①创建需要的组件(views目录),在main.js中配置路由规则
//在Vue..use(VueRouter);后
import ViewsParent from "@/views/ViewsParent.vue";
import viewsDialog from "@/views/ViewsDialog.vue";
const router = new VueRouter({
routes: [
{ path: "/parent", component: ViewsParent },
{ path: "/dialog", component: viewsDialog },
],
});
②配置导航,在App.vue中配置路由出口(路由匹配的组件显示的位置)
<div id="app">
<a href="#/parent">parent</a><br />
<a href="#/dialog">dialog</a>
<router-view></router-view>
</div>
3)组件存放目录问题
组件分类:.vue文件分2类,页面组件&复用组件
注意:都是.vue文件(本质无区别)
分类开来更易维护
①src/views文件夹:页面组件,用于页面展示,配合路由用
②src/components文件:复用组件,用于展示数据,常用于复用
12、路由进阶
1)路由模块封装
目标:将路由模块抽离出来。好处:拆分模块,利于维护。
新建router目录,在该目录下新建index.js,将main.js中有关路由的内容抽离到index.js中
main.js
点击查看代码
//main.js
import router from "@/router/index.js";
new Vue({
render: (h) => h(App),
router,
}).$mount("#app");
router/index.js
点击查看代码
//router/index.js
import Vue from "vue";
import VueRouter from "vue-router";
import ViewsParent from "@/views/ViewsParent.vue";
import viewsDialog from "@/views/ViewsDialog.vue";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: "/parent", component: ViewsParent },
{ path: "/dialog", component: viewsDialog },
],
});
export default router;
2)声明式导航
(1)导航链接
- vue-router提供了一个全局组件router-link(取代a标签)
①能跳转,配置to属性指定路径(必须)。本质还是a标签,to无需#
②能高亮,默认就会提供高亮类名,可以直接设置高亮样式
<router-link to="/find">发现</router-link>
//取代a
<a href="#/find">发现</a>
- 说明:
router-link自动给当前导航添加了两个高亮类名router-link-exact-active、router-link-active
①router-link-active模糊匹配:to="/my"可以匹配/my、/my/a等
②router-link-exact-active精确匹配:to="/my"仅可以匹配/my
- 定制声明式导航的两个高亮类名:
const router=new VueRouter({
router:[...],
linkActiveClass:"类名1",
linkExactActiveClass:"类名2"
})
(2)跳转传参
目标:在跳转路由时,进行传值
①查询参数传参(适合传多个参数):
- 配置导航链接:
to="/path"?参数名=值
- 对应页面组件接收传递过来的值:
$route.query.参数名
②动态路由传参(传单个参数比较方便):
- 配置动态路由:
const router=new VueRouter({
routes:[
...,
{
path:'/search/:words',
component:Search
}
]
})
- 配置导航链接:
to="/path/参数值"
- 对应页面组件接收传递过来的值:
$route.params.参数名
(3)动态路由参数可选符
/search/:words
表示,必须要传参数,如果不传参数,也希望匹配,可以加个可选符“?
”
const router=new VueRouter({
routes:[
...,
{
path:'/search/:words?',
component:Search
}
]
})
(4)路由重定向
问题 :网页打开,url默认是/路径,未匹配到组件时,会出现空白。
说明:重定向->匹配path后,强制跳转path路径
语法:{path:匹配路径,redirect:重定向到的路径}
const router=new VueRouter({
routes:[
{path:'/',redirect:'/'}
]
})
(5)Vue路由-404
作用:当路径找不到匹配时,给个提示页面
位置:配在路由最后
- 语法:
path:"*"
(任意路径)-前面不匹配就命中最后这个
const router=new VueRouter({
routes:[
{path:'*',component:NoFound}
]
})
(6)Vue路由-模式设置
问题:路由的路径看起来不自然,有
#
,能否切成真正路径形式
①hash路由(默认):http://localhost:8080/#/home
②history路由(常用):http://localhost:8080/home (上线需要服务器支持)
const router=new VueRouter({
routes,
mode:"history"
})
3)编程式导航
即用JS代码来进行跳转
(1)基本跳转
①path路径跳转(简易方便)
this.$router.push("路由路径")
this.$router.push({
path:'路由路径'
})
②name命名路由跳转(适合path路径长的场景)
//使用
this.$router.push({
name:'路由名'
})
//定义
const router=new VueRouter({
routes:[
{
name:'路由名',
path:'/path/xxxx',
component:XXXX
}
]
})
(2)跳转传参
两种传参方式:查询参数传参+动态路由传参,
上面两种跳转(path路径跳转、name命名路由跳转)方式,对于这两种传参方式都支持。
①path路径跳转传参
查询参数传参:
this.$router.push("/路径?参数名1=参数值")
this.$router.push({
path:'/路径',
query:{
参数名1:'参数值1'
}
})
- 对应页面组件接收传递过来的值:
$route.query.参数名
动态路由传参:
//先配置了动态路由
this.$router.push("/路径/参数值")
this.$router.push({
path:'/路径/参数值'
})
对应页面组件接收传递过来的值:$route.params.参数名
②name命名路由跳转传参
查询参数传参:
this.$router.push({
name:'路由名',
query:{
参数名1:'参数值1'
}
})
- 对应页面组件接收传递过来的值:
$route.query.参数名
动态路由传参:
//先配置了动态路由
this.$router.push({
name:'路由名',
params:{
参数名1:'参数值1'
}
})
- 对应页面组件接收传递过来的值:
$route.params.参数名
13、ESLint
//settings.json
{
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
},
"editor.formatOnSave": false
}
14、VueX
vuex是一个vue的状态管理工具,状态就是数据
场景:
- 某个状态在很多个组件来使用(个人信息)
- 多个组件共同维护一份数据(购物车)
优势:
- 共同维护一份数据,数据集中化管理
- 响应式变化
- 操作简洁
官方原文:
- 不是所有的场景都适用于vuex,只有在必要的时候才使用vuex
- 使用了vuex之后,会附加更多的框架中的概念进来,增加了项目的复杂度 (数据的操作更便捷,数据的流动更清晰)
1)创建仓库
安装vuex与vue-router类似,vuex是一个独立存在的插件,如果脚手架初始化没有选 vuex,就需要额外安装。
(1)安装
yarn add vuex@3 或者 npm i vuex@3
(2)新建 store/index.js 专门存放 vuex
为了维护项目目录的整洁,在src目录下新建一个store目录其下放置一个index.js文件。 (和 router/index.js 类似)
点击查看代码
//store/index.js
// 导入 vue
import Vue from 'vue'
// 导入 vuex
import Vuex from 'vuex'
// vuex也是vue的插件, 需要use一下, 进行插件的安装初始化
Vue.use(Vuex)
// 创建仓库 store
const store = new Vuex.Store()
// 导出仓库
export default store
(3)在 main.js 中导入挂载到 Vue 实例上
点击查看代码
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
}).$mount('#app')
(4)测试打印Vuex
//App.vue
created(){
console.log(this.$store)
}
2)核心概念 - state 状态
(1)提供数据
State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储。在State对象中可以添加我们要共享的数据。
state状态,即数据,类似于vue组件中的data
//store/index.js
const store = new Vuex.Store({
state: {
count: 101,
},
});
(2)使用数据
①通过store直接访问
Vue模板中获取:this.$store
JS文件中获取:import 导入store
模板中:{{$store.state.xxx}}
组件逻辑中:this.$store.state.xxx
JS模块中:store.state.xxx
②通过辅助函数(mapState获取 state中的数据)
mapstate是辅助函数,帮助我们把store中的数据自动映射到组件的计算属性
中。
3)核心概念-mutations 方法
vuex同样遵循单向数据流,组件中不能直接修改仓库的数据。
(1)严格模式:strict:true
不开启严格模式不会报错,开启严格模式后直接修改会报错:
严格模式:strict:true有利于初学者,检测不规范代码,但是上线后需要移除,因为会消耗性能
(2)mutations:(含传递一个参数,多个参数使用对象或者数组)
①定义mutations对象,对象中存放修改state的方法
//store/index.js
// 创建仓库 store
const store = new Vuex.Store({
//state省略
mutations: {
addCount(state) {
state.count += 1;
},
//传递参数:只能传递一个参数
addCount2(state,value) {
state.count += value;
},
},
});
②组件中提交调用mutations
this.$store.commit("addCount")
this.$store.commit("addCount2",10)
(3)辅助函数:mapMutations
mapMutations和mapSate很像,它是把位于mutations中的方法提取了出来,映射组件methods
中。
import {mapMutations} from 'vuex'
methods:{
...mapMutations(['addCount'])
}
//调用
this.addCount(10)
相当于:
methods:{
addCount(n){
this.$store.commit('addCount',10)
}
}
//调用
this.addCount(10)
4)核心概念 - actions 异步操作
mutations必须是同步的(便于检测数据变化,记录调试)
(1)使用方法
不能直接操作state,如果要操作state,还是需要commit mutation
// store/index.js
mutations:{
changeCount(state,newCount){
state.count=newCount
}
}
// xxx.vue
//提供actions方法
actions:{
setAsyncCount(context,num){
setTimeout(()=>{//真实场景是发请求
context.commit('changeCount',num)
},1000)
}
}
//调用
this.$store.dispatch('setAsyncCount',200)
(2)辅助函数 -mapActions
mapActions 是把位于 actions中的方法提取了出来,映射到组件
methods
中
import { mapActions } from 'vuex'
methods: {
...mapActions(['changeCountAction'])
}
//调用
<button @click="changeCountAction(200)">+异步</button>
6)核心概念 - getters状态
Getter用于对Store中的数据进行加工处理形成新的数据。
除了state之外,有时我们还需要从state中筛选出符合条件的一些数据,这些数据是依赖state的,此时会用到getters
例如,state中定义了list,为1-10的数组:
state: {
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
(1)定义:
组件中,需要显示所有大于5的数据,正常的方式,是需要list在组件中进行再一步的处理,但是getters可以帮助我们实现它:
//定义getters
getters: {
// getters函数的第一个参数是 state
// 必须要有返回值
filterList: state => state.list.filter(item => item > 5)
}
(2)使用
①原始方式-$store
<div>{{ $store.getters.filterList }}</div>
②辅助函数 - mapGetters
import { mapGetters } from 'vuex'
computed: {
...mapGetters(['filterList'])
}
//使用
<div>{{ filterList }}</div>
7)核心概念 - module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
这句话的意思是,如果把所有的状态都放在state中,当项目变得越来越大的时候,Vuex会变得越来越难以维护
由此,又有了Vuex的模块化。
(1)新建模块
在src\store\modules
中新建相应的xxx.js模块。
如:
点击查看代码
//user.js
const state = {
userInfo: {
name: 'zs',
age: 18
}
}
const mutations = {
setUser (state, newUserInfo) {
state.userInfo = newUserInfo
}
}
const actions = {}
const getters = {
// 分模块后,state指代子模块的state
UpperCaseName (state) {
return state.userInfo.name.toUpperCase()
}
}
export default {
namespaced: true,//命名空间
state,
mutations,
actions,
getters
}
(2)使用模块中的state数据
①直接通过模块名访问 $store.state.模块名.xxx
$store.state.user.userInfo.name
②通过 mapState 映射:
...mapState('user', ['userInfo']),
...mapState('setting', ['theme', 'desc']),
{{userInfo.name}}
③默认根级别的映射 mapState(['xxx'])
④子模块的映射 :mapState('模块名', ['xxx']) - 需要开启命名空间 namespaced:true
(3)获取模块内的getters数据
①直接通过模块名访问 $store.getters['模块名/xxx ']
②通过 mapGetters 映射
computed:{
...mapGetters('user', ['UpperCaseName'])
}
{{UpperCaseName}}
默认根级别的映射 mapGetters([ 'xxx' ])
子模块的映射 mapGetters('模块名', ['xxx']) - 需要开启命名空间
(4)获取模块内的mutations方法
默认模块中的 mutation 和 actions 会被挂载到全局,需要开启命名空间,才会挂载到子模块。
①直接通过 store 调用 $store.commit('模块名/xxx ', 额外参数)
②通过 mapMutations 映射
默认根级别的映射 mapMutations(['xxx'])
子模块的映射 mapMutations('模块名', ['xxx']) - 需要开启命名空间
(5)获取模块内的actions方法
①直接通过 store 调用 $store.dispatch('模块名/xxx ', 额外参数)
②通过 mapActions 映射
默认根级别的映射 mapActions(['xxx'])
子模块的映射 mapActions('模块名', ['xxx']) - 需要开启命名空间
如果你真心觉得文章写得不错,而且对你有所帮助,那就不妨小小打赏一下吧,如果囊中羞涩,不妨帮忙“推荐"一下,您的“推荐”和”打赏“将是我最大的写作动力!
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接.