目录
简介
vue是一套用于构建用户界面的前端框架;具有数据驱动视图和双向数据绑定的特性。当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构,当表单元素的值发生变化时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中
MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。MVVM 指的是 Model、View 和 ViewModel,它把每个 HTML 页面都拆分成了这三个部分,
- Model 表示当前页面渲染时所依赖的数据源。
- View 表示当前页面所渲染的 DOM 结构。
- ViewModel 表示 vue 的实例,它是 MVVM 的核心,把当前页面的数据源(Model)和页面的结构(View)连接在了一起
基本使用
基本使用步骤
<body>
<!-- 希望 Vue 能够控制下面的这个 div,帮我们在把数据填充到 div 内部 -->
<div id="app">{{ username }}</div>
<!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2. 创建 Vue 的实例对象 -->
<script>
// 创建 Vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {
username: 'zhangsan'
}
})
</script>
</body>
基本代码与mvvm对应关系
调试工具
浏览器插件下载地址:https://chrome.zzzmh.cn/index
指令与过滤器
内容渲染指令
- v-text
- {{ }}
- v-html
<div id="app">
<p v-text="username"></p>
<p v-text="gender">性别:</p>
<hr>
<p>姓名:{{ username }}</p>
<p>性别:{{ gender }}</p>
<hr>
<div v-text="info"></div>
<div>{{ info }}</div>
<div v-html="info"></div>
</div>
属性绑定指令
<div id="app">
<input type="text" :placeholder="tips">
<hr>
<!-- vue 规定 v-bind: 指令可以简写为 : -->
<img :src="photo" alt="" style="width: 150px;">
<hr>
<div>1 + 2 的结果是:{{ 1 + 2 }}</div>
<div>{{ tips }} 反转的结果是:{{ tips.split('').reverse().join('') }}</div>
<div :title="'box' + index">这是一个 div</div>
</div>
此外,为了方便开发者进行样式控制,Vue 扩展了 v-bind 的语法,可以针对 class 类名 和 style 行内样式进行控制
//对象 → 键就是类名,值是布尔值。如果值为 true,有这个类,否则没有这个类
<div class="box" :class="{ 类名1: 布尔值, 类名2: 布尔值 }"></div>
//数组 → 数组中所有的类,都会添加到盒子上,本质就是一个 class 列表
<div class="box" :class="[ 类名1, 类名2, 类名3 ]"></div>
//操作style
<div class="box" :style="{ CSS属性名1: CSS属性值, CSS属性名2: CSS属性值 }"></div>
事件绑定指令
vue 提供了 v-on 事件绑定指令,用来辅助程序员为 DOM 元素绑定事件监听,通过 v-on 绑定的事件处理函数,需要在 methods 节点中进行声明
<div id="app">
<p>count 的值是:{{ count }}</p>
<!-- 在绑定事件处理函数的时候,可以使用 () 传递参数 -->
<!-- v-on: 指令可以被简写为 @ -->
<button @click="add(1)">+1</button>
<button @click="sub">-1</button>
</div>
...
data: {
count: 0
},
// methods 的作用,就是定义事件的处理函数
methods: {
add(n) {
// 在 methods 处理函数中,this 就是 new 出来的 vm 实例对象
// console.log(vm === this)
console.log(vm)
// vm.count += 1
this.count += n
},
sub() {
// console.log('触发了 sub 处理函数')
this.count -= 1
}
}
双向绑定指令
<p>用户的名字是:{{ username }}</p>
<input type="text" v-model="username">
<hr>
<input type="text" :value="username">
条件渲染指令
- v-if 指令会动态地创建或移除 DOM 元素,从而控制元素在页面上的显示与隐藏;
- v-show 指令会动态为元素添加或移除 style="display: none;" 样式,从而控制元素的显示与隐藏
- 如果需要非常频繁地切换,则使用 v-show 较好
- 如果在运行时条件很少改变,则使用 v-if 较好
<div id="app">
<p v-if="flag">这是被 v-if 控制的元素</p>
<p v-show="flag">这是被 v-show 控制的元素</p>
<hr>
<div v-if="type === 'A'">优秀</div>
<div v-else-if="type === 'B'">良好</div>
<div v-else-if="type === 'C'">一般</div>
<div v-else>差</div>
</div>
...
data: {
// 如果 flag 为 true,则显示被控制的元素;如果为 false 则隐藏被控制的元素
flag: true,
type: 'A'
}
列表渲染指令
<table class="table table-bordered table-hover table-striped">
<thead>
<th>索引</th>
<th>Id</th>
<th>姓名</th>
</thead>
<tbody>
<!-- 官方建议:只要用到了 v-for 指令,那么一定要绑定一个 :key 属性 -->
<!-- 而且,尽量把 id 作为 key 的值 -->
<!-- 官方对 key 的值类型,是有要求的:字符串或数字类型 -->
<!-- key 的值是千万不能重复的,否则会终端报错:Duplicate keys detected -->
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ index }}</td>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
指令修饰符
通过 "." 指明一些指令 后缀,不同后缀封装了不同的处理操作
- 按键修饰符:@keyup.enter → 键盘回车监听
- v-model修饰符:v-model.trim → 去除首尾空格 v-model.number → 转数字
- 事件修饰符:@事件名.stop → 阻止冒泡 @事件名.prevent → 阻止默认行为
过滤器
<div id="app">
<p>message 的值是:{{ message | capi }}</p>
</div>
...
// 过滤器函数,必须被定义到 filters 节点之下
// 过滤器本质上是函数
filters: {
// 注意:过滤器函数形参中的 val,永远都是“管道符”前面的那个值
capi(val) {
// 字符串有 charAt 方法,这个方法接收索引值,表示从字符串中把索引对应的字符,获取出来
// val.charAt(0)
const first = val.charAt(0).toUpperCase()
// 字符串的 slice 方法,可以截取字符串,从指定索引往后截取
const other = val.slice(1)
// 强调:过滤器中,一定要有一个返回值
return first + other
}
}
侦听器
watch: {
obj: {
deep: true, // 深度监视
immediate: true, // 立刻执行,一进入页面handler就立刻执行一次
handler (newValue) {
clearTimeout(this.timer)
this.timer = setTimeout(async () => {
const res = await axios({
url: 'https://applet-base-api-t.itheima.net/api/translate',
params: newValue
})
this.result = res.data.data
console.log(res.data.data)
}, 300)
}
}
计算属性
computed: {
// 简写 → 获取,没有配置设置的逻辑
// fullName () {
// return this.firstName + this.lastName
// }
// 完整写法 → 获取 + 设置
fullName: {
// (1) 当fullName计算属性,被获取求值时,执行get(有缓存,优先读缓存)
// 会将返回值作为,求值的结果
get () {
return this.firstName + this.lastName
},
// (2) 当fullName计算属性,被修改赋值时,执行set
// 修改的值,传递给set方法的形参
set (value) {
// console.log(value.slice(0, 1))
// console.log(value.slice(1))
this.firstName = value.slice(0, 1)
this.lastName = value.slice(1)
}
}
}
生命周期
工程化开发入门
- 全局安装 (一次) :yarn global add @vue/cli 或 npm i @vue/cli -g
- 查看 Vue 版本:vue --version
- 创建项目架子:vue create project-name(项目名-不能用中文)
- 启动项目: yarn serve 或 npm run serve(找package.json)
项目运行流程:
组件通信
组件通信, 就是指 组件与组件之间的数据传递
父 → 子:
子 → 父:
什么是 prop
- 可以 传递 任意数量 的prop
- 可以 传递 任意类型 的prop
props 校验:
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
}
},
},
},
- data 的数据是自己的 → 随便改
- prop 的数据是外部的 → 不能直接改,要遵循 单向数据流
自定义指令
// 1. 全局注册指令
Vue.directive('focus', {
// inserted 会在 指令所在的元素,被插入到页面中时触发
inserted (el) {
// el 就是指令所绑定的元素
// console.log(el);
el.focus()
}
})
<div>
<h1 v-color="color1">指令的值1测试</h1>
<h1 v-color="color2">指令的值2测试</h1>
</div>
directives: {
color: {
// 1. inserted 提供的是元素被添加到页面中时的逻辑
inserted (el, binding) {
// binding.value 就是指令的值
el.style.color = binding.value
},
// 2. update 指令的值修改的时候触发,提供值变化后,dom更新的逻辑
update (el, binding) {
console.log('指令的值修改了');
el.style.color = binding.value
}
}
}
插槽
作用:让组件内部的一些 结构 支持 自定义
具名插槽:
路由
- 优点:按需更新性能高,开发效率高,用户体验好
- 缺点:学习成本,首屏加载慢,不利于SEO
Vue中路由:路径和组件的映射关系,根据路由就能知道不同路径的,应该匹配渲染哪个组件。
VueRouter是Vue 官方的一个路由插件,是一个第三方包 。
5个基础使用步骤:
2个核心步骤:
一般src/views文件夹存放页面组件,src/components文件夹存放复用组件
声明式导航:
vue-router 提供了一个全局组件 router-link (取代 a 标签)
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/friend">朋友</router-link>
const router = new VueRouter({
routes: [...],
// link自定义高亮类名
linkActiveClass: 'active', // 配置模糊匹配的类名
linkExactActiveClass: 'exact-active' // 配置精确匹配的类名
})
1. 查询参数传参 (比较适合传多个参数)
① 跳转:to="/path?参数名=值&参数名2=值"
② 获取:$route.query.参数名
<router-link to="/search?key=如何成为前端大牛">如何成为前端大牛</router-link>
console.log(this.$route.query.key);
2. 动态路由传参 (优雅简洁,传单个参数比较方便)
① 配置动态路由:path: "/path/:参数名"
② 跳转:to="/path/参数值"
③ 获取:$route.params.参数名
{ path: '/search/:words', component: Search }
<router-link to="/search/如何成为前端大牛">如何成为前端大牛</router-link>
console.log(this.$route.params.words);
路由重定向:
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/home' },//匹配path后, 强制跳转path路径
{ path: '/home', component: Home },
{ path: '/search/:words?', component: Search }
{ path: '*', component: NotFound }//path: "*" (任意路径) – 前面不匹配就命中最后这个
]
})
编程式导航:
// 1. 通过路径的方式跳转
// (1) this.$router.push('路由路径') [简写]
this.$router.push('/search')
// (2) this.$router.push({ [完整写法]
// path: '路由路径'
// })
this.$router.push({
path: '/search'
})
// 2. 通过命名路由的方式跳转 (需要给路由起名字) 适合长路径
// this.$router.push({
// name: '路由名'
// })
this.$router.push({
name: 'search'
})
// 1. 通过路径的方式跳转
// (1) this.$router.push('路由路径') [简写]
// this.$router.push('路由路径?参数名=参数值')
// this.$router.push('/search')
this.$router.push(`/search?key=${this.inpValue}`)
this.$router.push(`/search/${this.inpValue}`)
// (2) this.$router.push({ [完整写法] 更适合传参
// path: '路由路径'
// query: {
// 参数名: 参数值,
// 参数名: 参数值
// }
// })
this.$router.push({
path: '/search',
query: {
key: this.inpValue
}
})
this.$router.push({
path: `/search/${this.inpValue}`
})
// 2. 通过命名路由的方式跳转 (需要给路由起名字) 适合长路径
// this.$router.push({
// name: '路由名'
//query传参 query: { 参数名: 参数值 },
//动态路由传参 params: { 参数名: 参数值 }
// })
this.$router.push({
name: 'search',
query: {
key: this.inpValue
}
params: {
words: this.inpValue
}
})
}
Vuex
// 创建仓库
const store = new Vuex.Store({
// 严格模式 (有利于初学者,检测不规范的代码 => 上线时需要关闭)
strict: true,
// 1. 通过 state 可以提供数据 (所有组件共享的数据)
state: {
title: '仓库大标题',
count: 100,
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
},
从vuex中获取的值:
- {{ title }}
- {{ count }}
import { mapState } from 'vuex'
computed: {
...mapState(['count', 'title'])
},
修改数据:
// 通过 mutations 可以提供修改数据的方法
mutations: {
// 所有mutation函数,第一个参数,都是 state
// 注意点:mutation参数有且只能有一个,如果需要多个参数,包装成一个对象
addCount (state, obj) {
state.count += obj.count
},
subCount (state, n) {
state.count -= n
},
changeTitle (state, newTitle) {
state.title = newTitle
}
},
//提交mutation,调用mutation函数
handleAdd (n) {
// 错误代码(vue默认不会监测,监测需要成本)
// this.$store.state.count++
// console.log(this.$store.state.count)
// 应该通过 mutation 核心概念,进行修改数据
// 需要提交调用mutation
// this.$store.commit('addCount')
// 调用带参数的mutation函数
this.$store.commit('addCount', {
count: n,
msg: '哈哈'
})
},