脚手架
使用
安装
npm install -g @vue/cli
创建项目
- 文件根目录下的控制台 vue create 项目名,项目名不能有大写字母,中文和特殊字符
- 选择vue的版本
- npm run serve 运行项目
配置
官方文档
https://cli.vuejs.org/zh/config/#%E5%85%A8%E5%B1%80-cli-%E9%85%8D%E7%BD%AE
服务器
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 3000,
open: true
}
})
关闭eslint代码检查
module.exports = defineConfig({
lintOnSave: false
})
单vue文件讲解
- template里只能有一个根标签
- style配合scoped属性,保证样式只针对当前template生效
- vue文件配合webpack,把他们打包起来插入到Index.html
vue项目文件结构
- node_modules -----------存放第三方npm包
- public/index.html ------------浏览运行的网页
- src/main.js ----------------项目入口,打包入口
- src/App.vue -------------页面根组件,页面入口
- package.json -------------存放项目的npm依赖包
脚手架项目初始清理
- assets和components文件下的一切都删除
- App.vue全部删除留下template,script,style框
插值表达式
语法
标签内:{{ 表达式 }}
语法示例
<template>
<div>
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
msg: "今天吃西兰花了吗",
};
},
};
</script>
v-bind
作用
给标签属性动态赋值
代码示例
<template>
<div>
//v-bind简写:
<a :href="aURL">百度</a>
</div>
</template>
<script>
export default {
data() {
return {
aURL: "https://www.baidu.com",
};
},
};
</script>
v-on
作用
给标签绑定事件
语法
- v-on事件名 = “要执行的少量代码”
- v-on事件名 = “函数名”
- v-on事件名 = “函数名(参数)”
代码示例
<template>
<div>
<h1>计数:{{ count }}</h1>
//v-on的简写@
<p><button @click="count++">加一</button></p>
<p>
<button @click="addFive">加五</button>
</p>
<p>
<button @click="addFn(15)">加15</button>
</p>
</div>
</template>
<script>
export default {
data() {
return {
count: 0,
};
},
methods: {
addFive() {
this.count += 5;
},
addFn(num) {
this.count += num;
},
},
};
</script>
Vue获取事件对象
语法
- 如果无传参,通过形参直接接收
- 如果有传参,通过$event指代事件对象传给事件处理函数
代码示例
<template>
<div>
<a href="http://www.baidu.com" @click="fn">百度</a>
<a href="http://www.baidu.com" @click="fn2(2,$event)">百度2</a>
</div>
</template>
<script>
export default {
methods: {
fn(e) {
e.preventDefault();
},
fn2(num,e) {
e.preventDefault();
},
},
};
</script>
v-on修饰符
语法
- @事件名.修饰符 = “函数”
修饰符列表
- .stop 阻止事件冒泡
- .prevent 阻止默认行为
- .once 程序运行期前,只触发一次事件处理函数
运行过程
先执行修饰符,然后执行绑定的函数
v-on按键修饰符
语法
- @keydown.enter ------监测回车按键
- @keyup.esc ------监测返回按键
v-model修饰符
语法
v-model.修饰符=‘数据变量’
修饰符列表
- .number -------以parseFloat转成数字类型
- .trim-----------去除首尾空白字符
- .lazy----------在change时触发数据改变
v-for
作用
列表渲染,按照数据数量,循环生成
语法
数组的遍历
- v-for=“值变量 in 目标数组”
- v-for=“(值变量,索引变量) in 目标结构”
对象的遍历
- v-for=“value in 目标对象”
- v-for=“(value ,key) in 目标对象”
字符串的遍历
- v-for=“值变量 in 目标字符串”
- 单独遍历每一个字符
数字的遍历
- v-for=“值变量 in 目标数字”
- 从1开始到当前数字
目标结构
可以遍历数组/对象/数字/字符串(可遍历结构)
注意
- v-for中,如果数组源发生了改变,会触发数据更新
- array[index] = value的写法 不会触发v-for的更新,$set(数组,索引,值)才可以触发v-for的更新
- v-model绑定单选框,某一个单选框被选中后,v-model绑定的变量的值等于该单选框的value.v-model的值等于某一个单选框的value,则该单选框默认被选中
v-text
作用
- 把内容作为文本直接显示,不解析内容
- 会覆盖掉插值表达式
- 在标签内渲染文本值
语法
v-text = “数据变量”
v-html
作用
- 设置标签内容为html
- 在标签内渲染html
语法
v-html = “数据变量”
v-show
作用
- 控制标签的显示与隐藏
- 通过控制display:none属性来控制显示与隐藏
语法
v-show = “表达式”
v-if
作用
- 通过控制是否插入标签来显示隐藏
语法
v-if = “表达式”
vue动态添加类
语法
- :class=“{类名:布尔值}”
- 布尔值为true,标签添加类名,为false标签删除类名
- 可以控制多个类
代码示例
<template>
<div>
<button :class="{ on: isOn, off: !isOn }" @click="fun">开关</button>
</div>
</template>
<script>
export default {
data() {
return {
isOn: false,
};
},
methods: {
fun() {
this.isOn = !this.isOn;
},
},
};
</script>
<style scoped>
.on {
background-color: pink;
}
.off {
background-color: gray;
}
</style>
vue动态设置style
语法
:style=“{css属性名:值}”
代码示例
<template>
<div>
<button :style="{ color: colorStr }" @click="fun">单机修改样式</button>
</div>
</template>
<script>
export default {
data() {
return {
colorStr: "",
};
},
methods: {
fun() {
this.colorStr = "skyblue";
},
},
};
</script>
<style scoped>
.on {
background-color: pink;
}
.off {
background-color: gray;
}
</style>
过滤器
代码示例
-
局部使用
<template> <div> 商品价格:{{ price | priceFilter }} <!-- 过滤器能使用在插值表达式和v-bind属性里 --> <p :title="'hello vue' | toUpperCase">hello vue</p> </div> </template> <script> export default { data() { return { price: 9, }; }, filters: { //表达式的值就是过滤器的第一个参数 priceFilter(num) { return `${num < 10 ? "0" + num : num}¥`; }, toUpperCase(string) { return string.toUpperCase(); }, }, }; </script>
-
全局使用
//过滤器第一个参数是传入的值,一般是表达式的值,我们传给过滤器的参数从第二个开始 //在main.js编写 Vue.filter("moneyFilter",function(value,unit,unit2){ return `${value} ${unit}${unit2 || ''}`; })
注意
-
数组过滤器的方法要这样写
arrayFilter(array) { //必须生成一个新数组,返回对新数组的处理 return array.slice(0).reverse(); }
计算属性
作用
- 根据一些数据计算出来一个属性
- 当计算属性依赖的数据发生变化的时候,计算属性会重新运算
- 计算属性是作为变量使用的,不要使用括号的语法
- 计算属性不能和data里的变量重名
代码示例
<template>
<div>
<h1>{{ sum }} = {{ a }} + {{ b }}</h1>
a: <input type="text" v-model.number="a" />
</div>
</template>
<script>
export default {
data() {
return {
a: 10,
b: 20,
};
},
computed: {
sum() {
return this.a + this.b;
},
},
};
</script>
<style lang="scss" scoped></style>
全选案列
<template>
<div>
<p>全选:<input type="checkbox" v-model="isAllCheck" /></p>
<ul>
<li v-for="item in list" :key="item.id">
<input type="checkbox" v-model="item.checked" />{{ item.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
list: [
{
id: 1,
name: "八戒",
checked: false,
},
{
id: 2,
name: "孙悟空",
checked: false,
},
{
id: 3,
name: "唐僧",
checked: false,
},
{
id: 4,
name: "白龙马",
checked: false,
},
],
};
},
computed: {
//计算属性的全写形式
isAllCheck: {
get() {
let flag = true;
this.list.forEach((item) => {
if (item.checked == false) {
flag = false;
}
});
return flag;
},
set(value) {
this.list.forEach((item) => {
item.checked = value;
});
},
},
},
};
</script>
侦听器watch
语法
-
“被监听的属性名”(newVal,oldVal) {}
watch: { name(oldVal,newVal){} }
立即深度监听
<template>
<div>
<input type="text" v-model="obj.name" />
</div>
</template>
<script>
export default {
data() {
return {
obj: {
name: "西兰花",
},
};
},
watch: {
obj: {
//可以监听到复杂数据内部的变化
deep: true,
//可以在页面进来时马上触发一次当前监听回调
immediate: true,
handler(newVal, oldVal) {
console.log("监听到了数据变化");
},
},
},
};
</script>
<style scoped></style>
组件
概念
- 组件是可复用的Vue实例,封装标签,样式和JS代码
- 一个vue文件就是一个组件
使用方式
-
创建组件
<!-- 文件名ProItem.vue --> <template> <div class="container" @click="fn">产品产品产品产品</div> </template> <script> export default { data() { return {}; }, methods: { fn() { console.log("触发组件事件"); }, }, }; </script> <style scoped> .container { border: 1px solid #ccc; } </style>
-
注册组件
//全局注册 main.js import Vue from "vue"; import App from "./App.vue"; // @表示src目录 import ProItem from '@/components/ProItem.vue' // 全局注册组件 Vue.component('ProItem',ProItem); Vue.config.productionTip = false; new Vue({ render: (h) => h(App), }).$mount("#app");
<script> //局部注册 import ProItem from "@/components/ProItem.vue"; export default { components: { ProItem }, }; </script>
-
使用组件
<template> <div> <ProItem></ProItem> <ProItem></ProItem> <ProItem></ProItem> <ProItem></ProItem> <ProItem></ProItem> </div> </template> <script> export default {}; </script> <style scoped></style>
组件通信
父传子
- 父组件作为属性把数据交给子组件
- 子组件声明一个接收的配置 props,将父组件传来的属性写入其中
- 写入组内
代码示例:
父
<template>
<div>
<!-- 第一步 -->
<ProItem
v-for="(item, index) in list"
:key="index"
:itemName="item"
></ProItem>
</div>
</template>
<script>
import ProItem from "@/components/ProItem.vue";
export default {
components: {
ProItem: ProItem,
},
data() {
return {
list: ["西兰花", "西红柿", "西柚"],
};
},
};
</script>
<style scoped></style>
子
<template>
<!-- 第三步 -->
<div class="container" @click="fn">{{ itemName }}</div>
</template>
<script>
export default {
data() {
return {};
},
//第二步
props: ["itemName"],
methods: {
fn() {
console.log("触发组件事件");
},
},
};
</script>
<style scoped>
.container {
border: 1px solid #ccc;
}
</style>
子传父
- 子组件触发自定义事件,传递参数
- 父组件监听自定义事件,绑定监听处理函数
- 编写监听处理函数。
代码示例
子
<template>
<div class="container">
{{ itemName }}
<button @click="subtract">+1</button>
</div>
</template>
<script>
export default {
data() {
return {};
},
props: ["itemName", "index"],
methods: {
fn() {
console.log("触发组件事件");
},
subtract() {
// 第一步
this.$emit("subtract", 1, this.index);
},
},
};
</script>
<style scoped>
.container {
border: 1px solid #ccc;
}
</style>
父
<template>
<div>
<!-- 第二步 -->
<ProItem
v-for="(item, index) in list"
:key="index"
:itemName="item"
:index="index"
@subtract="subtract"
></ProItem>
</div>
</template>
<script>
import ProItem from "@/components/ProItem.vue";
export default {
components: {
ProItem: ProItem,
},
data() {
return {
list: [36, 38, 40],
};
},
methods: {
// 第三步
subtract(num, index) {
this.$set(this.list, index, (this.list[index] += num));
},
},
};
</script>
<style scoped></style>
props高级
-
props数组的写法可以换成对象
props: { //设定bg的要求格式 bg: String, //继续使用对象的形式,可以使用默认值 color: { type: String, default: '#fff' }, //设置必填项 title: { type: String, required: true } }
组件通信–EventBus
作用
用于跨组件通信时使用,比如兄弟组件之间通信
语法
-
src/EventBus/index.js ------ 创建空白Vue对象并导出
import Vue from 'vue' export default new Vue()
-
在要传递值的组件(a.vue) ------- eventBus.$emit(‘事件名’,值)
-
在要接收值的组件(b.vue)-------created()中声明并监听— eventBus.$on(‘事件名’,函数体)
代码示例
A组件
<template>
<div @click="putData">我是A组件</div>
</template>
<script>
import eventBus from "@/eventBus/index";
export default {
data() {
return {
data: "A组件数据",
};
},
methods: {
putData() {
eventBus.$emit("putData", this.data);
},
},
};
</script>
<style scoped></style>
B组件
<template>
<div>我是B组件{{ dataB }}</div>
</template>
<script>
import eventBus from "@/eventBus/index";
export default {
data() {
return {
dataB: "我有自己的数据",
};
},
created() {
eventBus.$on("putData", (value) => {
this.dataB = value;
});
},
};
</script>
<style scoped></style>
axios的使用
原理
export default function (options) {
return new Promise(resolve=>{
const xhr = new XMLHttpRequest()
xhr.open(options.method, options.url)
xhr.send()
xhr.onload = function() {
// xhr.responseText 其实是获取到的结果
resolve(JSON.parse(xhr.responseText))
}
})
}
// 只要返回一个Promise 对象, 里面的回调函数就会自动带上一个 resolve 放行方法
// 返回Promise就表示可以使用.then()
$refs
概念
作用与标签选择器一样
使用方式
- 在标签设置一个ref属性
- 使用this.$refs.名字可以获取该dom元素
- 获取组件时,可以获取组件的所有属性方法
代码
<template>
<div>
<MyBox ref="myBoxRef"></MyBox>
</div>
</template>
<script>
import MyBox from "@/components/MyBox.vue";
export default {
components: {
MyBox,
},
mounted() {
console.log(this.$refs.myBoxRef);
}
};
</script>
<style lang="scss" scoped></style>
补充
- 开发时不建议通过$refs来改变组件的值,父子组件通信依然是最优解
$nextTick
作用
等待页面更新(页面跟新是异步的)完毕执行回调函数
语法
this.$nextTick(回调函数)
vue组件进阶
动态组件
作用
- 多个组件使用同一个挂载点,并动态切换
使用方式
- 使用:is给组件绑定一个变量,变量值为要显示的组件名
代码示例
<template>
<div>
<component :is="comName"></component>
<button @click="comName = 'MyComA'">显示A组件</button>
<button @click="comName = 'MyComB'">显示B组件</button>
</div>
</template>
<script>
import MyComA from "@/components/MyComA.vue";
import MyComB from "@/components/MyComB.vue";
export default {
data() {
return {
comName: "MyComA",
};
},
components: {
MyComA,
MyComB,
},
};
</script>
<style lang="scss" scoped></style>
组件缓存—keep-alive
作用
- 给需要经常切换的组件添加,将组件缓存
使用方式
- 动态组件镶嵌到即可
组件插槽
作用
使用slot标签,让组件内可以接收不同的标签结构显示
给组件插入什么标签,组件就显示什么标签
语法口诀
- 组件内使用占位
- 使用组件时 夹着的地方,传入标签替换slot
设置插槽默认显示
默认显示的内容
具名插槽
使用条件
一个组件内有2处以上需要外部传入标签的地方
语法
- slot使用name属性区分名字
- template配合v-slot:名字 来分发对应的标签
- v-slot: 简写: #
代码示例
子
<template>
<div>
<slot name="left"></slot>
<slot name="right"></slot>
</div>
</template>
<script>
export default {
};
</script>
<style scoped>
div {
display: flex;
justify-content: space-between;
}
</style>
父
<template>
<div>
<component :is="comName">
<template #left>
<button>左按钮</button>
</template>
<template #right>
<button>右按钮</button>
</template>
</component>
</div>
</template>
<script>
import MyComA from "@/components/MyComA.vue";
import MyComB from "@/components/MyComB.vue";
export default {
data() {
return {
comName: "MyComA",
left: "left",
right: "right",
};
},
components: {
MyComA,
MyComB,
},
};
</script>
<style lang="scss" scoped></style>
作用域插槽
使用条件
使用插槽时,想使用子组件内的变量
口诀
- 子组件,在slot上绑定属性和子组件的值
- 使用组件,传入自定义标签,用template和v-slot=“自定义变量名”
- scope变量名自动绑定slot上所有属性和值
代码示例
子
<template>
<div>
<!-- 作用域插槽子组件部分,直接传属性 -->
<slot name="left" :data="monthList">
{{ monthList.first }}
</slot>
<slot name="right"></slot>
</div>
</template>
<script>
export default {
data() {
return {
monthList: {
first: "一月",
second: "二月",
},
};
},
};
</script>
<style scoped>
div {
display: flex;
justify-content: space-between;
}
</style>
父
<template>
<div>
<MyComA>
<template #left="scoped">
{{ scoped.data.second }}
</template>
<template v-slot:right> </template>
</MyComA>
</div>
</template>
<script>
import MyComA from "@/components/MyComA.vue";
import MyComB from "@/components/MyComB.vue";
export default {
data() {
return {
comName: "MyComA",
left: "left",
right: "right",
};
},
components: {
MyComA,
MyComB,
},
};
</script>
<style lang="scss" scoped></style>
自定义指令
使用条件
获取标签,扩展额外的功能
使用方式–全局注册
Vue.directive("showDom",{
"inserted" (el) {
//可以对el标签扩展额外功能
}
})
使用方式–局部注册
directives: {
'showDom': {
//配置对象
//可以指定使用这个指令的元素,在不同生命周期执行的函数
//在这些钩子函数的形参中,默认第一个可以获取元素本身,第二个可以用来传参
inserted(el,options) {
console.log(el)
//在元素被插入的时候执行
},
updated(el,options) {
console.log(el)
//在元素被更新的时候执行
}
}
}
使用方式–调用
<div v-showDom>
hallo world
</div>
Vue-Router
为何使用
- 单页面应用:所有功能在一个Html页面上实现
- 前端路由作用:实现业务场景切换
优点
- 整体不刷新页面
- 数据传递容易
缺点
- 首次加载比较慢
使用方式
- npm i vue-router@3 这个版本对应vue2
- main.js----- import Router from ‘vue-router’ 引入
- main.js---- Vue.use(Router) Vue安装这个库
- main.js----- const router = new Router 创建一个实例
- main.js----- new Vue({router}) 挂在到new Vue 上
配置路由
//main.js
//1.引入组件对象
import Find from '@/views/Find.vue'
const routes = [
{
path: '/find',
component: Find
}
]
const router = new VueRouter({
//路由配置表
routes
})
使用路由
<template>
<div>
<!-- 跳转链接,控制router-view的显示 -->
<router-link to="/myview1">跳转到组件1</router-link>
<router-link to="/myview2">跳转到组件2</router-link>
<!-- 路由的挂载点,指定需要显示路由组件的地方 -->
<router-view></router-view>
</div>
</template>
router-link
概念
- vue-router提供了一个全局组件 router-link
- router-link实质上最终会渲染成a链接,to属性等价于href属性,to无需#
- router-link提供了声明式导航高亮的功能,点击自动生成类名router-link-active
声明式导航跳转传参
第一种方式
<!-- /path?参数名=值 -->
<router-link to="/myview1?name=xiancheng666">跳转到组件1</router-link>
对应页面组件接收传递过来的值
<template>
<div>
组件1
<br>
获取参数:{{$route.query}}
<!-- 返回{ "name": "xiancheng666" } -->
</div>
</template>
第二种方式
<!-- /path/值 -->
<router-link to="/myview2/6666666">跳转到组件2</router-link>
对应页面组件接收传递过来的值
//main.js
const routes = [
{
path: "/myview2/:id",
component: MyView2,
},
];
<template>
<div>
组件2
<br />
{{ $route.params.id }}
<!-- 返回6666666 -->
</div>
</template>
重定向和模式
概念
- 重定向:根据用户输入的url重新定向新的url
- 模式:url在地址栏显示的样子
重定向的实现
const routes = [
{
path: "/",
redirect: "/find"
}
]
访问的路由路径不存在处理方法
const routes = [
{
path: "*",
component: NotFound
}
]
切换路由模式
- hash路由:http:localhost:8080/#/home
- history路由:http:localhost:8080/home (以后需要服务器端支持,否者找的是文件夹)
const router = new VueRouter({
mode: "history"
})
编程式导航跳转与传参
使用JS进行跳转
//传入一个对象,其中带有name属性,需要在路由中预先配置
//方式1
handleClick() {
this.$router.push({
name: 'MyPage'
})
}
//在这里配置
const route = [
{
path:'/my',
component: MyPage,
name: 'MyPage'
}
]
//传入一个对象,其中带有path属性
//方式2
handleClick() {
this.$router.push({
path: '/my'
})
}
//直接path作为字符串传入
//方式3
handleClick() {
this.$router.push('/my')
}
使用JS进行传参
//通过path传入
handleClick() {
this.$router.push({
path: '路由路径',
query: {
'参数名':'值'
}
})
}
//通过params传入
handleClick() {
this.$router.push({
name: '路由名',
params: {
'参数名':'值'
}
})
}
对应组件接收传参
<template>
<div>
组件1
<br>
<!-- 如果对方使用query传参 -->
获取参数:{{$route.query.参数名}}
<br>
<!-- 如果对方使用params传参 -->
获取参数:{{$route.params.参数名}}
</div>
</template>
前进后退
this.$router.back()//后退
this.$router.go()//前进
路由嵌套
第一步配置路由表
const routes = [
//一级路由
{
path: '/find',
component: Find,
children:[
//二级路由
{
path: 'ranking',
component: Ranking
}
]
}
]
第二步调用
<!-- Find.vue -->
<template>
<div>
<router-link to="/find/ranking">排行榜</router-link>
<!-- 显示二级路由需要一个挂载点 -->
<router-view></router-view>
</div>
</template>
全局前置守卫
概念
路由跳转之前,会触发一个函数
语法
router.beforeEach((to,from,next)=>{})
代码示例
//main.js
const router = new VueRouter()
router.beforeEach((to,from,next)=>{
//to 目的地,from来源,next 放行回调函数
//如果你来到了我的音乐,但是没有token就要跳转到登录页
if (to.path === '/my' && !localStorage.getItem('token')) {
next('/login')
}
else {
next()
}
})