vue.config.js配置
vue.config.js 文件 里可以设置 端口 和 是否执行后 直接打开网页
// vue脚手架项目 - 默认的配置文件名
// webpack+node环境 - 导出配置对象
module.exports = {
devServer: {
port: 8080, // 端口号
open: true, // 浏览器自动打开
// 配置反向代理 :解决 跨域问题
proxy: {
//
// 当请求地址中包含 /api 的时候 会触发 代理 机制
// ps:一个项目可以同时 代理多个服务器 根据实际需求来。
'/api': {
target: 'http://localhost:3000/', // 要代理的服务器地址
changeOrigin: true, //是否跨越: 只有这个值为true的情况下 才表示开启跨域 才可以让本地服务器代理我们发出请求
// 重写路径 // 如果是不想要 /pi的时候才重写
// 重新路由前: localhost:8888/api/login => www.baidu.com/api/login
// 重新路由后: localhost:8888/api/login => www.baidu.com/login
pathRewrite: { 如果我不想要 /api 就把它替换掉
'^/api': ''
}
}
}
},
lintOnSave: false //关闭eslint检查 有咩有没用的变量
}
vue指令
v-bind 动态绑定 标签的 属性
<template>
<div>
<a v-bind:href="url">百度</a>
<img :src="scrurl" /> // 或者直接 : 就行
</div>
</template>
<script>
export default {
// 1. 变量在data函数 return 的对象上
data() {
return {
//...其他
url: "http://www.baidu.com", // http://是必须要写的
scrurl:"图片路径"
//...其他
};
},
};
</script>
动态变量拼接 字符串
<img :href="'http://'+URL"></img> //字符串用 '' 单引号 包裹
v-on 事件绑定
<template>
<div>
<p>您购买的商品数量:{{ num }}</p>
<!-- v-on:事件名="少量的js代码" -->
<button v-on:click="num+=1">+1</button>
<!-- v-on:事件名="方法名" -->
<button v-on:click="btn">+1</button>
<!-- 简写 @事件名="方法名"" -->
<button @click="fn(5)">+5</button>
</div>
</template>
<script>
export default {
data() {
return {
num: 1,
};
},
//方法 都写在 methods 里面 methods跟data是平级的
//this 指向 当前vue实例对象 最大的 {} [export default {}这个大扩号]
methods: {
btn() {
this.num++;
},
fn(num1) {
this.num = this.num + num1;
},
},
};
</script>
v-on 事件对象
目标: vue事件处理函数中, 拿到事件对象
-
语法:
- 无传参, 通过形参直接接收
- 传参, 通过 $event 指代事件对象传给事件处理函数
<template>
<div>
<!-- 不传值 直接写方法名就行 -->
<a @click="one" href="http://www.baidu.com">百度</a>
<hr />
<!-- 传参, 通过$event指代事件对象传给事件处理函数 -->
<a @click="two(10, $event)" href="http://www.taobao.com" target="blank">淘宝</a>
</div>
</template>
<script>
export default {
methods: {
one(e) {
// 阻止默认行为
e.preventDefault();
console.log(e);
console.log(this);
},
two(num, e) {
e.preventDefault();
},
},
};
</script>
v-on修饰符
目的: 在事件后面.修饰符名 - 给事件带来更强大的功能
-
语法:
-
@事件名.修饰符="methods里函数"
- .stop - 阻止事件冒泡
- .prevent - 阻止默认行为
- .once - 程序运行期间, 只触发一次事件处理函数
-
<button @click.stop="btn">.stop阻止事件冒泡</button>
<button @click.prevent="btn">.prevent阻止默认行为</button>
<button @click.once="btn">.once 只触发一次 事件处理函数</button>
//可以连写,及 阻止事件冒泡 又 阻止默认行为
<button @click.stop.prevent="btn">可以连写,及 阻止事件冒泡 又 阻止默认行为</button>
<button @click.stop.prevent="btn">可以连写,及 阻止事件冒泡 又 阻止默认行为
v-on按键修饰符
目标: 给键盘事件, 添加修饰符, 增强能力
-
语法:
- @keyup.enter - 监测回车按键
- @keyup.esc - 监测返回按键
- @keyup.ASCII码 - 监测对应的按键
<!-- @事件名.按键名="函数名" -->
<input @keyup.backspace="fn1" type="text" />
<!-- @事件名.按键的ASCII码="函数名"-->
<input @keyup.13="fn1" type="text" />
监听 ctrl+s同时按下
<input @keyup.ctrl.s="KeyUpfn" />
更多修饰符参考: cn.vuejs.org/v2/guide/ev…
v-model 表单 value值 的设置和修改
<template>
<div>
<!-- 文本框 和 密码框 -->
<span>姓名: </span>
<input type="text" v-model="uname" />
<br>
<!-- 下拉菜单 v-model 要绑定在select上 -->
<!-- v-model的变量值 = 选中的 option的 value值 -->
<span>来自: </span>
<select v-model="form">
<option value="长沙">长沙</option>
<option value="深圳">深圳</option>
<option value="平江">平江</option>
</select>
<br />
<span>爱好:</span>
<!-- 遇到复选框, v-model的变量值-->
<!-- 变量值:不是一个数组 -关联的是 复选框 的 checked属性 true或者false -->
<!-- ps:选中一个 所有绑定同一个的 都会被选中;-->
<!-- 变量值:是一个数组 -关联的是 复选框的value属性 ps: 每选中一个,会依次push进数组 -->
<label><input type="checkbox" v-model="aihao" value="吃饭" />吃饭</label>
<label><input type="checkbox" v-model="aihao" value="睡觉" />睡觉</label>
<label><input type="checkbox" v-model="aihao" value="敲代码" />敲代码</label>
<br />
<!-- 单选按钮: v-model-->
<input type="radio" value="女" v-model="gender" name="sxe" />女
<input type="radio" value="男" v-model="gender" name="sxe" />男
<br>
<span>个人爱好: </span><br>
<textarea v-model="wenben" ></textarea>
</div>
</template>
<script>
export default {
data() {
return {
uname: "shanpeng",
form: "长沙",
aihao: [],
gender: "",
wenben:''
};
},
};
</script>
v-model修饰符
<!-- v-model.trim 修饰符: 去除首尾空白字符 -->
用户名: <input type="text" v-model.trim="str" /> <br />
<!-- v-model.number 修饰符: 以paseFloat转成数字类型 ps:如果输入的不是数字就还是字符串-->
密码: <input type="password" v-model.number="num" /> <br />
<!-- v-model.lazy 修饰符: 在change时触发而非input时 -->
<!-- ps:就是等失去焦点时再提交,而不是每次输入时就提交 -->
个人信息: <textarea v-model.lazy="xinxi"></textarea>
v-html 和 v-text
<template>
<div>
<!-- v-html和 v-text 都会覆盖 标签里面原本的内容 -->
<!-- v-html: 能识别 html标签-->
<p v-html="str">1</p>
<!-- v-text: 不能识别html标签-->
<p v-text="str">2</p>
</div>
</template>
<script>
export default {
data() {
return {
str: "<span>我叫单鹏</span>",
};
},
};
</script>
v-show 和 v-if
<!-- v-show或v-if,给变量赋值 true/false, 显示/隐藏 -->
<!-- v-show隐藏:采用 dispaly:none // 切换 -->
<p v-show="biu1">Hello Vue</p>
<!-- v-if 隐藏: 采用从DOM树直接移除 // 移除 -->
<p v-if="biu2">Hellow ShanPeng</p>
v-if 可以配合 v-if else 或者 v-else
判断条件 可以不一样 相互不影响
<div v-if="flag">1</div>
<div v-else-if="num > 3">2</div>
<div v-else>3</div>
<script>
export default {
data() {
return {
flag: false,
num: 5,
};
},
};
</script>
v-for 遍历
<template>
<div>
<tr>
<!-- v-for 把一组数据, 渲染成一组DOM -->
<!-- 口诀: 让谁循环生成, v-for就写谁身上 -->
<!-- 遍历数组 v-for="值 in 数组名" -->
<!-- item:数组的每一项 index:每一项的索引 -->
<td v-for="(item, index) in arr" :key="index">{{ value---index }}</td>
</tr>
<!-- v-for 遍历数组中的每个对象 -->
<tr v-for="value in stuArr" :key="value.id">
<td>{{ value.id }}</td>
<td>{{ value.name }}</td>
<td>{{ value.sex }}</td>
<td>{{ value.hobby }}</td>
</tr>
<ul>
<!-- 变量一个对象 v-for="(value, key) in 对象名" -->
<li v-for="(value, keys) in tObj" :key="value">
{{ keys }}-----{{ value }}
</li>
</ul>
<!-- v-for遍历整数(了解) - 从1开始一直到变量的值 v-for="num in 变量名" -->
<p>序号</p>
<div v-for="num in count" :key="num">{{ num }}</div>
</div>
</template>
<script>
export default {
data() {
return {
arr: ["小明", "小欢欢", "大黄"],
stuArr: [
{
id: 1001,
name: "孙悟空",
sex: "男",
hobby: "吃桃子",
},
{
id: 1002,
name: "猪八戒",
sex: "男",
hobby: "背媳妇",
},
],
tObj: {
name: "小黑",
age: 18,
class: "1期",
},
count: 5,
};
},
};
</script>
<style>
th,
td {
padding: 0 10px;
}
</style>
v-for 更新监测
情况1: 数组翻转 reverse 可以改变数组的方法 v-for可以监测到
情况2: 数组截取 slice 不能改变数组的 v-for监测不到
情况3: 更新值 直接重新赋值 v-for可以监测到
数组非变更方法, 返回新数组, 就不会导致v-for更新, 可采用覆盖数组或this.$set()
<template>
<div>
<!-- v-for 更新监听 -->
<ul>
<li v-for="value in arr" :key="value">{{ value }}</li>
</ul>
<button @click="re">反转数组</button>
<button @click="del2">删除数组前两个</button>
<button @click="gaibian">改变第一个值</button>
</div>
</template>
<script>
export default {
data() {
return {
arr: [1, 2, 3, 4, 5, 6, 7],
};
},
methods: {
re() {
// 能改变 数组本身 的方法 可以让 v-for 更新
this.arr.reverse();
},
del2() {
// 2. 数组 slice方法 不会造成 v-for 更新
// this.arr.slice(0, 3)
// 不会改变数组本身的方法 都不会造成 v-for 更新
// 解决v-for 更新- 直接覆盖 原始数组
this.arr = this.arr.slice(0, 3);
},
gaibian() {
// 3. 更新某个值的时候, v-for是监测不到的
// this.arr[0]=1000; 页面上是不会改变的
// 解决 this.$set()
// 参数: 1.更新的目标结构(数组) 2.更新位置 3.更新值
this.$set(this.arr, 0, 1000); // 相当于 原生js的 arr[0]=1000
},
},
};
</script>
<style>
</style>
动态设置 class 属性
<template>
<div>
<!-- 给标签 class属性动态赋值 -->
<!-- :class="{类名:布尔值}" --> 也可以通过判断
<div :class="{bianse:bool}" @click="bian">我可以变颜色</div>
</div>
</template>
<script>
export default {
data() {
return{
bool:true
}
},
methods: {
bian(){
this.bool=!this.bool
}
}
};
</script>
<style>
.bianse{
color: red;
}
</style>
动态 style 属性
<template>
<div>
<!-- 动态style属性 -->
<!-- :style="{css属性:属性值}" -->
<div :style="{backgroundColor:color}">biubiu</div>
</div>
</template>
<script>
export default {
data () {
return {
color:'blue'
}
}
}
</script>
<style>
</style>
自定义指令
全局定义
语法
在 main.js 入口 文件里 引入 Vue 之后
Vue.directive("指令名",{
// 初始化时执行
inserted(el,binding){
//el: 当前使用 该指令的 标签
//指令功能代码.....
},
// update方法-指令对象数据/标签更新时,此方法执行
update(el, binding) { // binding是自己任意取的名字
// binding.value :是 v-指令="的值"
el.style.color = binding.value
},
// 该钩子函数会在当前指令作用的组件 更新数据完毕之后 执行
componentUpdated(el, binding){
el.style.color = binding.value
}
})
实例
// 创建一个全家"自定义指令",让输入框自定聚焦
Vue.directive("focus-g", {
inserted(el) {
el.focus()
}
})
// 自定义修改字体颜色的 指令
Vue.directive('strColor', {
// inser方法 - 指令所在标签,被插入到网页上触发(一次)
inserted(el, binding) {
el.style.color = binding.value
},
// update方法-指令对象数据/标签更新时,此方法执行
update(el, binding) {
el.style.color = binding.value
}
})
使用自定义的 指令
语法 <h1 v-指令名="值">内容</h1>
<h1 v-colorG="'red'">我是谁?</h1> 'red': 固定的值
<h1 v-colorG="color">我是谁?</h1> color:变量
<input type="text" v-focus /> //页面打开自定聚焦
局部
export default {
// 局部 自定义 指令
directives: {
colorG: {
inserted(el, binding) {
el.style.color = binding.value;
},
// update方法-指令对象数据/标签更新时,此方法执行
update(el, binding) {
console.log(binding);
el.style.color = binding.value;
},
},
focus: {
inserted(el) {
el.focus();
},
},
},
};
点击按钮 让颜色随机变化
methods: {
changColor() {
this.color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(
Math.random() * 255
)},${Math.floor(Math.random() * 255)})`;
},
},
vue 过滤器 filter
定义全局过滤器
在 main.js文件 里定义
// Vue.filter('过滤器名字',函数体) itme:就是需要过滤的值
Vue.filter('fanzhuan', itme=> itme.split('').reverse().join())
定义局部过滤器
在当前 vue文件中 定义filters
filters: {
1.过滤器名字: (值) => {return "返回处理后的值"},
2.过滤器名字(过滤的值){ return "返回处理后的值"} //val 就是过滤的值
}
export default {
// ........
// filters:{'过滤器名字':函数体}
filters: {
// 保留 小数点后两位 itme和val都指 过滤的值
biu: (item) => itme.toFixed(2),
toLower(val) {
// 小写字母 转为 大写
return val.toUpperCase();
},
},
// ........
};
使用过滤器
可同时使用多个过滤器, 或者给过滤器传参
// 插值表达式
<p>{{ msg | fanzhuan }}</p>
// v-bind表达式
<p :title="msg | bianda">提示信息</p>
<template>
<div>
{{ msg1 | biu | cheng }}
{{ msg2 | str("-") }}
</div>
</template>
<script>
export default {
data() {
return {
msg1: 1.23456789,
msg2: [1, 2, 3, 4, 5],
};
},
filters: {
// 保留 小数点后两位
cheng: (itme) => itme * 2,
biu: (itme) => itme.toFixed(2),
str(val, i) {
// 数组转 字符串
return val.join(i);
},
},
};
</script>
vue计算属性 computed
一个数据, 依赖另外一些数据计算而来的结果
export default {
computed: {
"计算属性名" () {
return "值"
}
}
}
<template>
<div>
<p>{{ num }}</p>
</div>
</template>
<script>
export default {
data(){
return {
a: 10,
b: 20
}
},
computed: {
num(){
return this.a + this.b
}
}
}
</script>
注意: 计算属性也是vue数据变量, 所以不要和data里重名, 用法和data相同
vue计算属性-完整写法
get: 当计算属性 所依赖的 变量发生变化时, 计算属性也跟着变化
set: 当修改计算属性时, 可以改变其他值
computed: {
"属性名": {
get() {
return "值"
} ,
set(值){
}
}
}
vue侦听器 watch
可以侦听data/computed属性值改变
watch: {
// newVal 最新的内容 oldVal 上一次的内容(旧的)
"被侦听的属性名(变量名)" (newVal, oldVal){
}
}
<template>
<div>
<input type="text" v-model="name" />
</div>
</template>
<script>
export default {
data() {
return {
name: "",
};
},
watch: {
// newVal 最新的内容 oldVal 上一次的内容(旧的)
name(newVal, oldVal) {
console.log(newVal, oldVal);
},
},
};
</script>
侦听器的 深度侦听 和 立即执行
深度侦听: deep: true 立即执行: immediate: true 就是打开页面一上来就执行一次
watch: {
"要侦听的属性名": {
immediate: true, // 立即执行
deep: true, // 深度侦听复杂类型内变化
handler (newVal, oldVal) {
}
}
}
<template>
<div>
<input type="text" v-model="user.name">
<input type="text" v-model="user.age">
</div>
</template>
<script>
export default {
data(){
return {
user: {
name: "",
age: 0
}
}
},
// 目标: 侦听对象
/*
语法:
watch: {
变量名 (newVal, oldVal){
// 变量名对应值改变这里自动触发
},
变量名: {
handler(newVal, oldVal){
},
deep: true, // 深度侦听(对象里面层的值改变)
immediate: true // 立即侦听(网页打开handler执行一次)
}
}
*/
watch: {
user: {
// handler 里有 最新的值,和旧的值
handler(newVal, oldVal){
// user里的对象
console.log(newVal, oldVal);
},
deep: true, // 深度监听
immediate: true, // 该回调将会在侦听开始之后被立即调用
}
}
}
</script>
vue组件
全局 - 注册使用
在入口文件:一般是 main.js里, 在new Vue之上注册
import Vue from 'vue'
// 引入 组件
import 组件对象 from 'vue文件路径'
Vue.component("组件标签名", 组件对象)
// 目标: 全局注册 (一处定义到处使用)
// 1. 创建组件 - 文件名.vue
// 2. 引入组件
import Pannel from './components/Pannel'
// 3. 全局 - 注册组件
/*
语法:
Vue.component("组件标签名", 组件对象)
*/
Vue.component("PannelG", Pannel)
在任意Vue文件中 template 标签 里用
<PannelG></PannelG> //双标签 都可以
<PannelG/> //单标签 都可以
<pannel-g></pannel-g>全部小写 - //都可以
<组件标签名></组件标签名>
在 components文件中统一注册全局组件
提供注册入口src/component/index.js
// 该 文件 负责所有的公共的组件的全局注册
import 组件名 from './组件路径'
import xxx from './xxx'
/*
* Vue.use(对象) 如果是个 对象的话 ,会调用 对象里面的 install方法 参数是 Vue的实例
*
* 这里为什么vue会在调用install函数时,把Vue构造函数作为实参传给我们?
* 就是为了保证 你用的 Vue构造函数 ,和我的Vue构造函数 是 同一个
* 就是保证 Vue构造函数 一致
*
* 还有一个好处是 :
我想开放一个 基于 vue 的 第三方包插件,并且发布到npm上
vue插件的作者不需要安装vue依赖包,减少插件的代码体积
*/
export default{
install(Vue){
// 自己的代码.......
// 还可以用来做很多事,不只是 全局注册组件
/*
* 这里是用来注册全局组件
*/
Vue.component('组件名',组件名)
Vue.component('xxx',xxx)
}
}
在main.js里面注册
import Component from '@/components'
Vue.use(Component)
Vue.use() 方法
Vue.use() 方法内部会判断 传入的实参 是一个 函数 还是 一个 对象
如果是函数,则会立即调用,并且传入Vue构造函数实参
如果是对象,则会立即调用对象.install(Vue)
局部 -注册使用
在 使用这个组件的 vue文件中 引入
<script>
// 引用组件
import 组件对象 from 'vue文件路径'
export default {
// 注册组件
components: {
"组件标签名": 组件对象
}
}
</script>
vue组件-里 style标签的 scoped作用
解决多个组件样式名相同, 冲突问题
如果不使用 scoped,那么当前组件的 css样式很有可能影响到其他组件
<style scoped>
</style>
组件name属性使用
可以用组件的name属性值, 来注册组件名字
<template>
<div>
<p>我是一个Com组件</p>
</div>
</template>
<script>
export default {
name: "ComNameHaHa" // 注册时可以定义自己的名字
}
</script>
组件通行 父向子-props
父组件
<template>
<div>
<product title="超好吃的烧鹅饭" price="16" intro="每次都送一瓶饮料" ></Product>
</div>
</template>
<script>
//引用 组件
import product from "./components/MyProduct.vue";
export default {
// 注册组件
components: {
product,
},
data() {
return {
str: "开业大酬宾, 全场5折",
};
},
};
</script>
子组件
<template>
<div class="my-product">
// 使用props定义的变量
<h3>标题:{{ title }}</h3>
<p>价格: {{ price }}元</p>
<p>{{ intro }}</p>
</div>
</template>
<script>
export default {
// 在 props:[] 里 定义 变量 //接收 父组件 传过来的值
props: ["title", "price", "intro"],
// .....
};
</script>
<style>
父向子- props 配合 循环
<template>
<div>
<product v-for="obj in list" :key="obj.id"
:title="obj.proname"
:price="obj.proprice"
:intro="obj.info"></product>
</div>
</template>
<script>
// 目标: 循环使用组件-分别传入数据
// 1. 创建组件
// 2. 引入组件
import product from "./components/MyProduct.vue";
export default {
// 3. 注册组件
components: {
product,
},
data() {
return {
list: [
{ id: 1, proname: "超级好吃的棒棒糖", proprice: 18.8, info: '开业大酬宾, 全场8折' },
{ id: 2, proname: "超级好吃的大鸡腿", proprice: 34.2, info: '好吃不腻, 快来买啊' },
{ id: 3, proname: "超级无敌的冰激凌", proprice: 14.2, info: '炎热的夏天, 来个冰激凌了' },
],
};
},
};
</script>
<style>
</style>
props完整版
props: {
item: {
type: Array, // 传过来的必须是数组
// type:[String,number] 传过来的必须是 字符串 或者 数字
default: "内容", //如果没有传值过来 默认显示的值
// default:()=>{ 如果是数组或者对象,默认的值 必须是函数return的值
// return []
// },
required: true, // 是必须传的
validator(value) { // 自定义一个验证规则
if (value.length >= 2 && value.length <= 5) {
return true;
} else {
console.log("数据源必须在2-5项");
return false;
}
},
},
},
组件通信 子向父-$emit
父组件 监听自定义事件
<template>
<div>
<Child @modification="fn"> // 自定义事件名 必须写在 子组件标签上
{{zuoping}} // 以 插槽的形式 使用传过来的 参数
</Child>
</div>
</template>
<script>
import Child from "子组件路径";
export default {
components: {
Child, // 注册 组件
},
data() {
return {
zuoping: "", //声明接收变量
};
},
methods: {
fn(value) { //vlaue: 就是子组件 传过来的参数
this.zuoping = value; // 变量= 传过来的参数
},
},
created() {
this.fn(); //在 初始化的时候 调用 函数
},
};
</script>
<style lang="less" scoped>
</style>
子组件
<template>
<div>
<slot></slot> // 插槽标签
</div>
</template>
<script>
export default {
data() {
return {
poem: "天生我材必有用,千金散尽还复来。", // 定义传过去的 变量
};
},
created() { // 在初始化的时候 传过来
// 语法 this.$emit("父组件自定义事件名",传过去的变量,.....)
this.$emit("modification", this.poem);
},
};
</script>
<style lang="less" scoped>
</style>
如果只传一个值
子
this.$emit("soom", this.num);
父
如果只有一个值得话 $event 等于 传过来的值
<dupu @soom="sum += $event" />
sync 修饰符 !!!!
多用于 第三方弹框 如果传一个值 子组件又要改这个值的时候
父组件
<comp :foo.sync="bar"></comp>
会被扩展为:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
子组件
// this.$emit('update:变量名', 新值)
this.$emit('update:foo', newValue)
组件通信_跨组件传值-EventBus(兄弟组件传值)
新建 EventBus/index.js (文件夹名和文件名都是随意定义的)- 定义事件总线bus对象(中间商)
import Vue from 'vue'
// 导出空白vue对象
export default new Vue()
传值方
1.引用 中间商 EventBus/index.js 导出的 new Vue 实例
<template>
<button @click="subFn">加1</button>
</template>
<script>
import eventBus from "./eventBus";
// 因为文件名是index.js 所以不用写全 它也会自动找到
// 如果是其他文件名 就要写全
export default{
data(){
return{
num:1
}
},
methods:{
subFn() {
this.num++;
eventBus.$emit("send", this.num); // 跨组件
},
}
}
</script>
接收方
<template>
<div>
{{shu}}
</div>
</template>
<script>
import eventBus from "./eventBus";
// 因为文件名是index.js 所以不用写全 它也会自动找到
// 如果是其他文件名 就要写全
export default{
data(){
return{
shu:1
}
},
created(){ // 在 created钩子函数中 调用 $on方法
eventBus.$on("send", (index) => {
this.shu += index;
});
},
destroyed(){ // 在 destroyed钩子函数中 移除 事件
// 移除所有事件
eventBus.$off()
// 移除这个事件
eventBus.$off('send') //事件名
}
}
</script>
组件进阶 - 动态组件
- 准备被切换的 - UserName.vue / UserInfo.vue 2个组件
- 引入到UseDynamic.vue注册
- 准备变量来承载要显示的"组件名"
- 设置挂载点 标签, 使用is属性来设置要显示哪个组件
- 点击按钮 – 修改comName变量里的"组件名"
<template>
<div>
<span>
// 5. 通过点击按钮 修改 ComName的值 来让哪个组件显示
<button @click="ComName='uname'">账号密码填写</button>
<button @click="ComName='uinfo'">个人信息填写</button>
</span>
<p>下面显示注册组件:</p>
//4. 挂载点<component>标签 is属性来设置要显示哪个组件
// ps:is属性等于哪个组件名就显示哪个组件
<component :is="ComName"></component>
</div>
</template>
<script>
// 1引入两个组件
import uname from "./UserName.vue";
import uinfo from "./UseInfo.vue";
export default {
data () {
return {
// 3 定义一个变量.默认先等于 uname 组件名
ComName:'uname'
}
},
//2 注册两个组件
components: {
uname,
uinfo,
},
}
</script>
组件进阶 - 组件缓存
组件切换会导致组件被频繁销毁和重新创建, 性能不高
使用Vue内置的keep-alive组件, 可以让包裹的组件保存在内存中不被销毁
演示1: 可以先给UserName.vue和UserInfo.vue 注册created和destroyed生命周期事件, 观察创建和销毁过程
演示2: 使用keep-alive内置的vue组件, 让动态组件缓存而不是销毁
语法:
包裹需要缓存的组件标签
Vue内置的keep-alive组件 包起来要频繁切换的组件
02_UseDynamic.vue
<div style="border: 1px solid red">
<!-- Vue内置keep-alive组件, 把包起来的组件缓存起来 -->
<keep-alive>
<component :is="ComName"></component>
</keep-alive>
</div>
属性
<keep-alive max="5">
max属性: 一共只缓存 5个组件, 如果超过了那么就把 最久没有被访问的组件清除缓存
</keep-alive>
<keep-alive :include="componentArr">
include: 只缓存指定的组件,不缓存其他的
</keep-alive>
<keep-alive :exclude="componentArr">
exclude: 跟includ相反, 不缓存指定的组件,缓存其他的
</keep-alive>
补充生命周期:
- activated - 激活
- deactivated - 失去激活状态
总结: keep-alive可以提高组件的性能, 内部包裹的标签不会被销毁和重新创建, 触发激活和非激活的生命周期方法
组件进阶 - 组件插槽 slot
默认插槽
子组件
<template>
<div id="container">
<slot>默认显示内容,如果父组件没有插入内容,默认就显示这段话</slot>
</div>
</template>
父组件
<template>
<div id="container">
<div id="app">
<h3>案例:折叠面板</h3>
<Pannel>在这里写入要显示的代码
子组件就会把 <slot></slot>站位标签 替换成 这些代码
</Pannel>
<Pannel>
<p>寒雨连江夜入吴,</p>
<p>平明送客楚山孤。</p>
<p>洛阳亲友如相问,</p>
<p>一片冰心在玉壶。</p>
</Pannel>
<Pannel>
<img src="./鸣宝1.jpg" alt="">
<span>大家好,我叫旋涡鸣人</span>
</Pannel>
</div>
</div>
</template>
<script>
// 1. 引入 子组件
import Pannel from "./chacao/zhedie.vue";
export default {
// 2. 注册子组件
components: {
Pannel,
},
};
</script>
组件进阶 - 具名插槽
当一个组件内有2处以上需要外部传入标签的地方
传入的标签可以分别派发给不同的slot位置
给 slot 标签添加 name属性 取名
<slot name="title"></slot>
<slot name="content"></slot>
父组件这边使用 template 和 v-solt 属性 传递代码
<Pannel>
<!-- <template v-slot:子组件slot标签的name名> -->
<template v-slot:title>
<h4>芙蓉楼送辛渐</h4>
</template>
<!-- v-slot: 可以缩写 # -->
<template #content>
<p>寒雨连江夜入吴,</p>
<p>平明送客楚山孤。</p>
<p>洛阳亲友如相问,</p>
<p>一片冰心在玉壶。</p>
</template>
</Pannel>
组件进阶 - 作用域插槽
子组件
<!-- 在 slot标签上 自定义属性名 -->
<!-- :自定义属性名="要传过去的变量" -->
<slot :row="flag"> //也可以传固定的值, 就不用 在前面加 :
{{ flag}}
</slot>
父组件
<!-- 使用组件,template配合v-slot="自定义变量名" -->
<!-- 自定义变量名是个对象 它接收了 子组件传过来的变量 -->
<template v-slot="flag1">
<!-- 使用方法 自定义变量名.子组件自定义属性名-->
<!-- 如果传过来的是 对象 可以 自定义变量名.子组件自定义属性名.属性 -->
{{ flag1.row }}
</template>
采用 解构 对象 简写
// 直接接受
<template v-slot="{ flag }">
<img src="./柯南惊讶.jpg" alt="" />
<span> {{ flag.name1 }}</span>
</template>
组件进阶-具名插槽和作用插槽
同时使用 采用连写的方式
<!-- v-solt:具名="作用域自定义名" -->
<template v-slot:content="ge">
<img src="./柯南惊讶.jpg" alt="" />
<span> {{ ge.flag.name1 }}</span>
</template>
vue 生命周期
从Vue实例, 创建到销毁的过程 官网文档
| 阶段 | 方法名 | 方法名 |
|---|---|---|
| 初始化 | beforeCreate | created |
| 挂载 | beforeMount | mounted |
| 更新 | beforeUpdate | updated |
| 销毁 | beforeDestroy | destroyed |
<template>
<div class="box"></div>
</template>
<script>
export default{
data(){
return{}
},
methods:{},
created(){}, // 初始化之后
mounted(){}, // 挂载之后
updated(){}, // 更新之后
destroyed(){},// 销毁之后
}
</script>
<style scrped>
</style>
nextTick知识
$refs-获取DOM 或者 组件实例
ref 和 this.$refs 可以用于获取 dom 元素
<template>
<div>
<p>1. 获取原生DOM元素</p>
<!-- ref="名" -->
<h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1>
<p>2. 获取组件实例</p>
<hello-world ref="HelloWorld"></hello-world>
</div>
</template>
<script>
import HelloWorld from './HelloWorld'
export default {
components:{
HelloWorld
},
mounted(){ // 渲染后
// this.$refs.ref名
console.log(this.$refs.myH); // h1
// 通过 $refs获取组件, 可以调用组件里面的方法 获取组件里面的属性
console.log(this.$refs.HelloWorld) // HelloWorld组件实例
}
}
</script>
<style>
</style>
$nextTick使用
语法: this.$nextTick(回调函数) //回调函数 一般使用 箭头函数
methods: {
fn() {
this.num++; // 1
// 解决: this.$nextTick()
// 过程: DOM更新完会挨个触发$nextTick里的函数体
this.$nextTick(() => console.log(this.$refs.a.innerHTML)); // 1
},
},
总结: 因为DOM更新(视图渲染)是异步的
如果nexkTick拿不到 可以试试 定时器
setTimeout(function(){},0) //0 秒就行
router
vue-router使用
学会vue官方提供的vue-router路由系统功能模块使用
- 安装
yarn add vue-router
- main.js里 导入路由
import VueRouter from 'vue-router'
- main.js里 使用路由插件
// 在vue中,使用使用vue的插件,都需要调用Vue.use()
Vue.use(VueRouter)
- main.js里 创建路由规则数组
const routes = [
{
path: "/find",
name:'find'
component: Find
},
{
path: "/my",
name:'my',
component: My
},
{
path: "/part",
path: "part",
component: () => import('@/views/part'),
}
]
- main.js里 创建路由对象 - 传入规则
const router = new VueRouter({
routes
})
- main.js里 关联到vue实例
new Vue({
router
})
- 组件.vue
<router-view></router-view>
总结: 下载路由模块, 编写对应规则注入到vue实例上, 使用router-view挂载点显示切换的路由
总结2: 一切都围绕着hash值变化为准
vue路由 - 声明式导航
可用全局组件router-link来替代a标签
- vue-router提供了一个全局组件 router-link
- router-link实质上最终会渲染成a链接 to属性等价于提供 href属性(to无需#)
- router-link提供了声明式导航高亮的功能(自带类名)
<!-- <a href="#/find">发现音乐</a> href=对应 路由路径 a标签 路由路径前要加 #
<a href="#/my">我的音乐</a>
<a href="#/part">朋友</a> -->
<!-- 声明式导航 - 基本使用 -->
<!-- 本质: vue-router提供的全局组件 "router-link" 替代a标签 -->
<!-- "router-link" 替代a标签 -->
<!-- to属性 替代 href 属性 路由路径前不用加#了 -->
<router-link to="/find">发现音乐</router-link> <!-- to =对应路由路径 不用加# -->
<router-link to="/my">我的音乐</router-link>
<router-link to="/part">朋友</router-link>
点击之后 会自动添加 类名 class: .footer_wrap .router-link-active
通过这个类名 添加高亮样式
<style scoped>
/* 省略了 其他样式 */
.footer_wrap .router-link-active{
color: white;
background: black;
}
</style>
router-link自带的2个类名的区别是什么
观察路由嵌套导航的样式
- router-link-exact-active 类名 (精确匹配) url中hash值路径, 与href属性值完全相同, 设置此类名
- router-link-active 类名 (模糊匹配) url中hash值, 包含href属性值这个路径
声明式导航 - 跳转传参
查询字符串
/path?参数名=值
传参
<template>
<button @click="$router.push('/home?name=shanpeng&age=18')">跳转路由</button>
</template>
或者
this.$router.push({
name: "home",
query:{
name:'shanpeng',
age:18
}
})
接收
<script>
export default{
created(){
console.log(this.$route.query)
console.log(this.$route.query.name)
console.log(this.$route.query.age)
}
}
</script>
动态路由传参
{
path: 'departments/:name/:id',//y 这里当二级路由的path什么都不写的时候 表示该路由为当前二级路由的默认路由
component: () => import('@/views/departments'),
parps:true,// 开启 parps后 可以在路由组件中 通过 props接收 动态路由参数
}
传参
<router-link to="/part/ShanPeng/001">朋友</router-link>
或者
this.$router.push('/part/ShanPeng/001')
或者
this.$router.push({
name:'part',
params:{
name:'ShanPeng',
id:"001"
}
})
接收
console.log(this.$route.params)
console.log(this.$route.params.name)
console.log(this.$route.params.id)
重定向和模式
匹配path后, 强制切换到目标path上
- 网页打开url默认hash值是/路径
- redirect是设置要重定向到哪个路由路径
网页默认打开, 匹配路由"/", 强制切换到"/find"上
const routes = [
{
path: "/", // 默认hash值路径
redirect: "/find" // 重定向到/find
// 浏览器url中#后的路径被改变成/find-重新匹配数组规则
}
]
总结: 强制重定向后, 还会重新来数组里匹配一次规则
路由 - 404页面
默认给一个404页面
语法: 路由最后, path匹配*(任意路径) – 前面不匹配就命中最后这个, 显示对应组件页面
-
创建NotFound页面
<template> <img src="../assets/404.png" alt=""> </template> <script> export default { } </script> <style scoped> img{ width: 100%; } </style> -
在main.js - 修改路由配置
import NotFound from '@/views/NotFound' const routes = [ // ...省略了其他配置 // 404在最后(规则是从前往后逐个比较path) { path: "/404", component: NotFound }, { path: "*", // 默认hash值路径 redirect: "/404" // 重定向到/404 // 浏览器url中#后的路径被改变成/404-重新匹配数组规则 } ]
总结: 如果路由未命中任何规则, 给出一个兜底的404页面
路由跳转传参推荐
使用 路由对象名 name 传参
this.$router.push({
name: "路由名",
params: {
"参数名": 值
},
// query: {
// "参数名": 值
// },
})
// 对应路由接收 this.$route.params.参数名 取值
// 对应路由接收 this.$route.query.参数名 取值
路由 - 模式设置
目标: 修改路由在地址栏的模式
hash路由例如: http://localhost:8080/#/home //默认是 哈西 模式
history路由例如: http://localhost:8080/home (以后上线需要服务器端支持, 否则找的是文件夹)
router/index.js
const router = new VueRouter({
routes,
mode: "history" // 打包上线后需要后台支持, 默认模式是hash
})
路由 - 路由嵌套 子路由
{
path:'/',
component:login,
children:[
{
path:'my',
component:()=>import("@/对应路由的路径")
},
{
path:'your',
component:()=>import("@/对应路由的路径")
},
{
path:'time',
component:()=>import("@/对应路由的路径")
},
]
}
全局 前置/后置 守卫
目标: 路由跳转之前, 先执行一次前置守卫函数, 判断是否可以正常跳转
在路由对象上使用固定方法beforeEach
在 项目的src文件下 新建 permission.js配置路由守卫
// 场景: 当你要对路由权限判断时
// 语法: router.beforeEach((to, from, next)=>{//路由跳转"之前"先执行这里, 决定是否跳转})
// 参数1:to 要跳转到的路由 (路由对象信息) 目标
// 参数2:from 从哪里跳转的路由 (路由对象信息) 来源
// 参数3:next 函数体 - next()才会让路由正常的跳转切换, next(false)在原地停留, next("强制修改到另一个路由路径上")
import router from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // 引入一份进度条插件
import 'nprogress/nprogress.css' // 引入进度条样式
const whiteRouterList = ['/login', '/404']
/**
* 路由前置守卫
*/
router.beforeEach((to, from, next) => {
NProgress.start() // 开启进度条
if (store.getters.token) {
if (to.path === 'login') next('/')
else next()
} else {
if (whiteRouterList.includes(to.path)) next()
else next('/login')
}
NProgress.done() // 关闭进度条
})
/**
* 路由后置守卫
*/
router.afterEach((to, from, next) => {
})
Router实例 api方法
router.app
配置了 router 的 Vue 根实例
router.mode
路由使用的 模式
router.currentRoute
当前路由对应的路由信息对象
router.replace()
跳转路由后不保留历史记录
this.$router.replace()
$route 路由信息对象
$route.path
当前路由页面的 绝对路径
$route.params
当前路由页面的 动态参数
$route.query
当前路由页面的 查询字符串参数
$rout.hash
当前路由的 hash 值 (带 #) ,如果没有 hash 值,则为空字符串
$route.fullPath
完成解析后的 URL,包含查询参数和 hash 的完整路径
$router.resolve 新窗口打开
新窗口打开
vuex 全局状态管理
第一步.下载
yarn add vuex
第二步: 在main.js中 引入
import Vuex from 'vuex'
第三步:在main.js中 注册
Vue.use(Vuex)// 调用了 vuex中的 一个install方法
第四步: 实例化
const store = new Vuex.Store({...配置项})
const store =new Vuex.Store({
state:{},
actions:{},
mutations:{},
modules:{},
getters:{}
})
第五步:在根实例配置 store 选项指向 store 实例对象
new Vue({ //挂载到 vue 实例上
store,
el: '#app'
})
vuex基础-state
state是放置所有公共状态的属性,如果你有一个公共状态数据 , 你只需要定义在 state对象中
定义 state
// 初始化vuex对象
const store = new Vuex.Store({
state: {
// 管理数据
count: 0,
UserInfo:{},
toKen:'',
}
})
使用
1.原始形式- 插值表达式
组件中可以使用 this.$store 获取到vuex中的store对象实例,可通过state属性获取count属性, 如下
<div> state的数据:{{ $store.state.count }}</div>
2.计算属性 - 将state属性定义在计算属性中
computed:{
count()
return this.$store.state.count
}
}
<div> state的数据:{{ count }}</div>
vuex基础-mutations
state数据的修改只能通过mutations,并且mutations必须是同步更新,目的是形成数据快照
定义mutations 方法
mutations: {
// 方法里参数 第一个参数是当前store的state属性
// payload 载荷 运输参数 调用mutaiions的时候 可以传递参数 传递载荷
addCount (state,payload) {
state.count += payload
}
},
使用mutations 方法
<template>
<div>
<button @click="addCount">+1</button>
</div>
</template>
<script>
export default {
methods: {
addCount() {
// 调用mutation方法, 提交 mutation
// this.$store.commit(mutation名字) 可以获取到 mutations里面的方法并执行
// commit 的第二个参数 就是要传递的 载荷 payload 就是传入的参数
this.$store.commit("addCount",1);
},
},
};
</script>
vuex基础-actions
state是存放数据的,mutations是同步更新数据,actions则负责进行异步操作
定义actions
actions: {
// 获取异步的数据 context表示当前的store的实例 可以通过 context.state 获取状态 也可以通过context.commit 来提交mutations, 也可以 context.diapatch调用其他的action
getAsyncCount (context,payload) {
setTimeout(function(){
// 一秒钟之后 要给一个数 去修改state
context.commit('addCount', payload)
}, 1000)
}
}
调用
addAsyncCount () {
this.$store.dispatch('getAsyncCount', 123)
}
Vuex中的模块化-Modules
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
定义Modules的state状态
const store = new Vuex.Store({
modules: {
user: {
namespaced: true, // 开启 命名空间
state: {
UserInfo: {},
token: 1561984891651651651
}
},
})
使用 moudles 里面的state
此时要获取子模块的状态 需要通过 $store.state.模块名称.属性名 来获取
<template>
<div>
<div>用户token {{ $store.state.user.token }}</div>
<div>网站名称 {{ $store.state.setting.name }}</div>
</div>
</template>
定义 modules里面的 mutations
const store = new Vuex.Store({
modules: {
user: {
state:{.....},
mutations:{
setToKen(state,toKen){
state.token=toKen
}
}
},
})
定义 modules里面的 actions
import {async_getToKen} from '@/api/user'
const store = new Vuex.Store({
modules: {
user: {
state:{.....},
mutations:{.....},
actions:{
async getToKen(context){
const taken = await async_getToKen()
context.commit('setToKen',taken)
}
}
},
})
调用 modules 里面的 actions
<template>
<div>
<button @click="login">登录</button>
</div>
</template>
<script>
export default{
data(){
return{}
},
methods{
login(){
this.$store.dispatch('user/getToKen')
}
},
created(){},
mounted(){}
}
</script>
<style scoped lang="scss">
</style>