Vue是一个用于构建用户界面的渐进式框架
一套完整的项目解决方案,提升开发效率(理解记忆规则)
规则一 官网
构建用户界面–>创建Vue实例 初始化渲染
步骤;
<script>
const app = new Vue({
el:'#app',
data:{
msg: 'He1lo黑马'
}
})
</script>
el:'#app'
插值表达式是一种Vue的模板语法
<p:title="username"> </p>
如何访问或修改数据呢?
data中的数据最终会被添加到实例上
安装Vue开发者工具:装插件调试Vue应用
v-html / v-show /v-if / v-else / v-on / v-bind /v-for /v-model
Vue 会根据不同的指令,针对标签实现不同的功能
指令: 带有 v- 前缀的特殊 标签属性
作用: 设置元素的 innerHTML
语法: v-html="表达式
<div V-html="str"></div>
1.作用控制元素显示隐藏
2.语法:V-show =“表达式” 表达式值 true 显示,false 隐藏
3.原理切换 display:none 控制显示隐藏
4.场景:频繁切换显示隐藏的场景
1.作用:控制元素显示隐藏(条件渲染)
2.语法:v-if =“表达式” 表达式值 true 显示false 隐藏
3.原理基于条件判断,是否 创建 或 除 元素节点
4.场景:要么显示,要么隐藏,不频繁切换的场景
<div id="app">
<p v-if="gender === 1">性别:♂ 男</p>
<p v-else>性别:♀ 女</p>
<hr>
<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>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
gender: 1,
score: 2
}
})
</script>
<button v-on:click="fn">按钮</button>
<button @click="fn">按钮</button>
<div id="app">
<img v-bind:src="url" alt="">
</div>
<script>
const app=new Vue({
el:'#app',
data:{
url:'./imgs/10-02.png'
}
})
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- <div id="app">
<button v-show="index > 0" @click="index--">上一页</button>
<div>
<img :src="list[index]" alt="">
</div>
<button v-show="index < list.length - 1" @click="index++">下一页</button>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
index: 0,
list: [
'./imgs/11-00.gif',
'./imgs/11-01.gif',
'./imgs/11-02.gif',
'./imgs/11-03.gif',
'./imgs/11-04.png',
'./imgs/11-05.png',
]
}
})
</script> -->
<div id="app">
<button v-show="index>0" @click="index--">上一页</button>
<img :src="list[index]" alt="">
<button v-show="index @click="index++">下一页</button>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
index: 0,
list: [
'./imgs/11-00.gif',
'./imgs/11-01.gif',
'./imgs/11-02.gif',
'./imgs/11-03.gif',
'./imgs/11-04.png',
'./imgs/11-05.png',
]
}
})
</script>
</body>
</html>
遍历数组语法
v-for =“(item,index) in 数组”
1.key 的值只能是字符串 或数字类型
2.key 的值必须具有 唯一性
3.推荐使用 id 作为 key (唯一) ,不推荐使用 index 作为 key (会变化,不对应)
<div id="app">
<!--
v-model 可以让数据和视图,形成双向数据绑定
(1) 数据变化,视图自动更新
(2) 视图变化,数据自动更新
可以快速[获取]或[设置]表单元素的内容
-->
账户:<input type="text" v-model="username"> <br><br>
密码:<input type="password" v-model="password"> <br><br>
<button @click="login">登录</button>
<button @click="reset">重置</button>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
password: ''
},
methods: {
login() {
console.log(this.username, this.password)
},
reset() {
this.username = ''
this.password = ''
}
}
})
</script>
列表渲染/删除功能/添加功能/底部统计/清空
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./css/index.css" />
<title>记事本</title>
</head>
<body>
<!-- 主体区域 -->
<section id="app">
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input placeholder="请输入任务" class="new-todo" v-model="content" @keyup.enter="addItem" />
<button class="add" @click="addItem">添加任务</button>
</header>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<li class="todo" v-for="(item, index) in list">
<div class="view">
<span class="index">{{index+1}}</span> <label>{{item.content}}</label>
<button class="destroy" @click="delItem(item.id)"></button>
</div>
</li>
</ul>
</section>
<!-- 统计和清空 -->
<footer class="footer">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> {{list.length}}</strong></span>
<!-- 清空 -->
<button class="clear-completed" @click="clear">
清空任务
</button>
</footer>
</section>
<!-- 底部 -->
<script src="../vue.js"></script>
<script>
// 1.渲染列表
// 2. 删除某一项(给按钮添加一个点击事件 通过id传参)
// 3. 添加任务(添加点击事件,双向数据绑定 ,构建对象 ,添加到list里面最前面)
// 4. 底部合计
// 5. 清空
// 6. 回车添加任务
const app = new Vue({
el: '#app',
data: {
list: [
{
id: '123456',
content: "游泳一小时"
},
{
id: '223456',
content: "跳舞一小时"
},
],
content: ' '
},
methods: {
delItem(id) {
// console.log(id)
this.list = this.list.filter(item => item.id !== id)
},
addItem() {
this.list.unshift({
id: `${new Date().valueOf()}`,
content: this.content
}),
this.content = ''
},
clear() {
this.list = []
}
}
})
</script>
</body>
</html>
通过“.”指明一些指令 后缀,不同的后缀封装了不同的处理操作 - 简化代码
<div class="box":class="{类名1: 布尔值,类名2: 布尔值}"></div>
适用场景:一个类名,来回切换
<div class="box":class="[ '类名1','类名2','类名3 ']"></div>
适用场景:批量添加或删除类
常见的表单元素都可以用 v-model 绑定关联一>快获取或设置表单元素的值
它会根据 控件类型自动选取正确的方法来更新元素
输入框 input:text value
文本域textarea value
复选框 input:checkbox checked
单选框input:radio checked
下拉菜单 select value
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
ul {
display: flex;
border-bottom: 2px solid #e01222;
padding: 0 10px;
}
li {
width: 100px;
height: 50px;
line-height: 50px;
list-style: none;
text-align: center;
}
li a {
display: block;
text-decoration: none;
font-weight: bold;
color: #333333;
}
li a.active {
background-color: #e01222;
color: #fff;
}
</style>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item,index) in list" :key="item.id" @click="activeIndex=index">
<a href="#" :class="{active:index===activeIndex}">{{item.name}}</a>
</li>
</ul>
</div>
<script src="../vue.js"></script>
<script>
// 1. 删除ul中所有的li,是用数据来渲染li
// 2. 在data中声明一个 默认选中的索引 0
// 3. 判断li在循环的时候 索引是0 1 2 哪个索引和data中声明的一样
// 4. 一样就添加active高亮
const app = new Vue({
el: '#app',
data: {
activeIndex: 0,//记录高亮
list: [
{ id: 1, name: '京东秒杀' },
{ id: 2, name: '每日特价' },
{ id: 3, name: '品类秒杀' }
]
}
})
</script>
</body>
</html>
区别:
缓存特性(提升性能):
计算属性会对计算出来的结果缓存,再次使用直接读取缓存
依赖项变化了,会自动重新计算一并再次缓存
计算属性默认的简写,只能读取访问,不能“修改
如果要“修改"一>需要写计算属性的完整写法
computed:{
计算属性名(){
一段代码逻辑(计算逻辑)
return 结果
}
}
computed:{
计算属性名:{
get(){
一段代码逻辑(计算逻辑)
return 结果
},
set(修改的值) {
一段代码逻辑(修改逻辑)
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./styles/index.css" />
<title>Document</title>
</head>
<body>
<div id="app" class="score-case">
<div class="table">
<table>
<thead>
<tr>
<th>编号</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody v-if="list.length > 0">
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ index + 1 }}</td>
<td>{{ item.subject }}</td>
<!-- 需求:不及格的标红, < 60 分, 加上 red 类 -->
<td :class="{ red: item.score < 60 }">{{ item.score }}</td>
<td><a href="#">删除</a></td>
</tr>
</tbody>
<tbody v-else>
<tr>
<td colspan="5">
<span class="none">暂无数据</span>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">
<span>总分:246</span>
<span style="margin-left: 50px">平均分:79</span>
</td>
</tr>
</tfoot>
</table>
</div>
<div class="form">
<div class="form-item">
<div class="label">科目:</div>
<div class="input">
<input
type="text"
placeholder="请输入科目"
/>
</div>
</div>
<div class="form-item">
<div class="label">分数:</div>
<div class="input">
<input
type="text"
placeholder="请输入分数"
/>
</div>
</div>
<div class="form-item">
<div class="label"></div>
<div class="input">
<button class="submit" >添加</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
list: [
{ id: 1, subject: '语文', score: 62 },
{ id: 7, subject: '数学', score: 39 },
{ id: 12, subject: '英语', score: 70 },
],
subject: '',
score: ''
}
})
</script>
</body>
</html>
data:{
words:'苹果',
obj:{
words:'苹果'
}
},
watch:{
// 该方法会在数据变化时,触发执行
数据属性名 (newValue,oldValue){
一些业务逻辑 或 异步操作.
},
'对象.属性名' (newValue,oldValue){
一些业务逻辑 或 异步操作.
}
}
data:{
words:'苹果',
obj:{
words:'苹果',
lang:'italy'
},
},
watch:{//watch完整写法
数据属性名:{
deep: true,// 深度监听
immediate:true,// 默认先执行一次 不管数据变没变
handler(newVal) {
console.log(newValue)
}
}
}
什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来)
data:{
title:'计数器',
count:100
}
<div id="app">
<h3>{{ title }}</h3>
<div>
<button>-</button>
<span>{{ count }}</span>
<button>+</button>
</div>
</div>
查看 Vue 版本: vue --version
创建项目架子: vue create project-name(项目名-不能用中文)
项目路径 C:\Users\Administrator\study-dmj
C:\Users\Administrator\study-dmj
命令yarn serve
命令npm run serve --> main.js
import AABb from "路径" //导入
components:{AABb}//注册
<AaBb></AaBb>//使用
//1.创建一个AaBb.vue
//2.注册
import AaBb from"路径”//引入
Vue.component("AaBb",AaBb)//注册
//3使用
//随便在哪里都可以使用 还是使用标签的形式使用
默认情况:写在组件中的样式会全局生效因此很容易造成多个组件之间的样式冲突问题
<style scoped>
</style>
props 和 $emit
1. provide&inject
2. eventbus
data() {
return {
msg:["1,2"]
}
}
<template>
<div id="app">
<Son :notice="msg"></Son>
</div>
</template>
<script>
export default {
props: ['notice']
}
</script>
<template>
<div class="son">{{ notice }}</div>
</template>
<script>
export default {
props: ['notice'],
methods: {
handlerFix() {
// this.$emit("自定义事件名",传给自定义事件对应的参数)
this.$emit("fatherEvent","中午吃什么,到黑马")
}
}
}
</script>
<template>
<div id="app">
<Son :notice="msg" @fatherEvent="handlerFather"></Son>
</div>
</template>
<script>
import Son from './components/Son.vue'
export default {
methods: {
handlerFather(num) {
this.msg = num
console.log("父组件的事件触发了",num);
}
}
}
</script>
类型校验
数组传参写法(不是校验)
props: ["username", "age", "isSingle", "car", "hobby"],
对象写法简单校验
//props:{
// 校验的属性名:类型 // Number String Boolean...
// }
props: {
username: String,
age: Number,
isSingle: Boolean,
car: Object,
hobby: Array
}
对象写法完整校验
//props: {
// 校验的属性名: {
// type: 类型, // Number String Boolean
// required: true,// 是否必填
// default: 默认值,// 默认值
// validator (value) {
// 自定义校验逻辑
// return 是否通过校验
// }
// }
//}
props: {
hobby: {
type: Array ,// Number String Boolean
required: true,// 是否必填
default: '小黑',// 默认值
validator :function (value) {
// 自定义校验逻辑
let res = value.includes('篮球')
return res
}
}
}
required: true,// 是否必填
default: 默认值,// 默认值
validator :function (value) {
// 自定义校验逻辑
let res = value.includes('篮球')
return res
}
需求说明:
①获取dom:
- ① 1.1 目标标签--添加ref属性
<BaseForm ref="baseForm"></BaseForm>
- ① 1.2 恰当时机 , 通过this.$refs.xxx, 获取目标组件,就可以调用组件对象里面的方法
this.$refs.baseForm.组件方法()
点击编辑,显示编辑框
问题:“显示之后”,立刻获取焦点是不能成功的!
原因: Vue 是异步更新 DOM (提升性能)
$nextTick:等 DOM 更新后,才会触发执行此方法里的函数体
this.$nextTick(()=>(
this.$refs.inputRef.focus()//获取焦点
})
mounted(){
this.$refs .inp.focus()
}
Vue.directive( '指令名',{
"inserted" (el) {
// 可以对 el 标签,扩展额外功能
el.focus()
}
})
Vue.directive("focus", {
inserted(el) {
console.log(el)
el.focus()
}
})
Vue.directive("color", {
inserted(el, binding) {
el.style.color = binding.value
}
})
Vue.directive("color", {
inserted(el, binding) {
el.style.color = binding.value
}
})
作用:非父子组件之间,进行简易消息传递。(复杂场景一 Vuex)
先在组件内用slot占位
使用组件时,传入具体标签内容插入
<template>
<div class="dialog">
<div class="dialog-content">
<slot> 默认后备内容</slot>
</div>
</div>
</template>
需求:组件内有多处不确定的结构怎么办
语法
<slot :id="item.id"> </slot>
父子组件通信,把表格用数据渲染出来
分离结构,把my-tag部分分离成组件
myTag的开发
作用:修改地址栏路径时,切换显示匹配的组件
路径改变,对应组件切换
说明: Vue 官方的一个路由插件,是一个第三方包(需要下载)
官网: https://v3.router.vuejs.org/zh/
yarn add vue-router@3.6.5
npm i vue-router@3
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter()
new Vue({
render: h => h(App),
router
}).$mount('#app')
//绝对路径:@指代src目录,可以用于快速引入组件
import Find from '@/views/Find.vue'
import My from './views/My .vue'
import Friend from './views/Friend.vue'
const router = new VueRouter({
routes: [
{ path:'/find', component: Find },
{ path:'/my', component: My },
{ path:'/friend', component: Friend },
]
})
<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>
页面组件放在vue.js中
复用组件放在components里面
路由模块化
url:localhost:8080/#/find
需求:实现导航高亮效果
//书写:
<router-link to="/find">发现音乐</router-link>
//解析:
<a href="#/find"class="router-link-active router-link-exact-active"> </a>
<!-- <a href="#/find">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/friend">朋友</a> -->
<!-- 声明式导航 - 导航链接 -->
<!-- 注意点: router-link必须具备to属性,等同于a标签必须具备href,不需要加#,仍然被解析成了a标签 -->
<!-- 导航高亮 加类名 class="router-link-exact-active router-link-active" -->
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/friend">朋友</router-link>
有模糊匹配一定有精准匹配
linkActiveClass: 'active',
linkExactActiveClass: 'exact-active'
routes: [
// 配置重定向(路由表的前面) redirect强制跳转到对应的地址
{ path: '/', redirect: '/home' },
//path重定向前的路径(无法显示页面的路径),redirect需要显示页面的路径
]
//index.js
const router = new VueRouter({
mode: "history"//路由模式
// 路由模式的原理
// hash:vue内部会有一个hashChange监听事件来监听hash值得变化
// history:原生API-->popStage监听URLChange的事件
})
导航传参
1.2 动态路由传参
问号传的query接,冒号传的params接
问题:点击按钮跳转如何实现?
编程式导航:用JS代码来进行跳转
两种语法:
①path 路径跳转
②name 命名路由跳转
//传参方式1: 单参数,固定值
this.$router.push('/search?key=123')
2.1.1.2第二种跳转方式(传参)
this.$router.push({
path:‘路径’
})
传参方式2:完整写法
//传参方式2:等同于简单写法的传参(不推荐)
this.$router.push({
path:'/search?key=456'
})
// 传参方式3:多参数,变量值
this.$router.push({
path:'./search',
query:{
key:123,
val:this.num
}
})
//index.js
{ name: 'searchPage',path:'/search/:word?',component:Search },
//传值方式4(动态路由参数)
this.$router.push({
name:'searchPage',
params:{
key:123
}
})
查询参数怎么传?问号传的query接,query传的query接
动态参数怎么传?冒号传的params接,params传的params接推荐的两种方式:1. path搭配query 2.name搭配params
分析:配路由 + 实现功能
keep-alive
<keep-alive>//包裹路由出口,
<router-view></router-view>
</keep-alive>