Vue基础
Vue介绍
渐进式JavaScript框架,一套拥有自己规则的语法
官网地址: https://cn.vuejs.org/ (作者:尤雨溪)
Vue从基础开始,会循序渐进向前学习
库和框架
库:封装的属性或方法(例jQuery)
框架:拥有自己的规则和元素,比库强大的多(例Vue)
Vue学习方式
- 传统开发模式:基于HTML文件开发Vue
- 工程化开发方式:在webpack环境中开发Vue,这是最推荐,企业常用的方式
@vue/cli和脚手架介绍
@vue/cli是Vue官方提供的一个全局模块包(得到vue命令),此包用于创建脚手架项目
脚手架好处
- 开箱即用
- 0配置webpack
@vue/cli安装
- 全局安装@vue/cli模块包
yarn global add @vue/cli
- 查看是否成功
vue -V
@vue/cli 4.5. 12
创建脚手架项目
- 创建项目 (项目名不能有大写字母,中文和特殊符号)
vue create vuecli-demo
- 选择模板和包管理器,等待脚手架项目创建完毕
Default([Vue 2] babel, eslint)
Default(Vue 3 Preview)([Vue 3] babel, eslint)
Mannually select features
- 初始化设置
- 切换到项目文件夹下
cd vuecli-demo
- 启动vue项目
yarn serve || npm run serve
@vue/cli目录和代码分析
项目机构了解
import Vue from 'vue' // 引入vue对象,类似于<script src="vue.js"></script>
import App from './App.vue' // App.vue文件里对象引入过来(vue项目页面入口)
new Vue({
render: h => h(App), // 准备渲染App页面
}).$mount('#app') // 渲染到index.html 文件里id叫app的标签上
修改服务器端口号
Vue脚手架项目用配置文件名字是vue.config.js
src并列处新建vue.config.js
, 填入配置,重启webpack
开发服务器
module.exports = {
devServer: { // 自定义服务器配置
port: 3000,
open: true
}
}
暂时关闭eslint设置
eslint是一种代码检查的工具,如果写代码违反了eslint的规则-报错
解决方式:
- 手动解决掉错误,在以后的项目中会讲到
- 暂时关闭eslint检查,在
vue.config.js
中配置后重启服务
module.exports = {
// ...其他配置
lintOnSave: false // 关闭eslint检查
}
单vue文件讲解
- Vue推荐采用.vue文件来开发项目
- template里只能有一个根标签
- js独立作用域互不影响
- style配合scoped属性,保证样式只针对当前template内标签生效
清理欢迎页面
- assets和components文件夹下的一切都删除掉(不要默认的欢迎页面)
- src/App.vue默认有很多内容,可以全部删除留下template,script和style
Vue语法
插值表达式
又叫:声明式渲染/文本插值
语法:{{ 表达式 }}
<template>
<div>
<h1>{{msg}}</h1>
<h2>{{obj.name}}</h2>
<h3>{{obj.age}}</h3>
<h4>{{obj.team}}</h4>
<h5>{{obj.age > 18? "去网吧":"伊藤美诚你妈叫你回家吃饭"}}</h5>
</div>
</template>
<script>
export default {
data(){
return {
msg:"Hello,Vue!",
obj:{
name:"沈梦瑶",
age: 23,
team:"HII"
}
}
}
}
</script>
MVVM设计模式
用数据驱动视图改变,操作dom的事,vue源码已经处理了
-
设计模式:是一套被反复使用的、多数人知晓的,经过分类编目的、代码设计经验的总结
-
MVVM(模型,视图,视图模型双向关联的一种设计模式)
-
好处:减少DOM操作,提高开发效率
v-bind
给标签属性设置Vue变量的值
<template>
<div>
<a :href="url">跳转去百度</a>
</div>
</template>
<script>
export default {
data () {
return {
url:"https://www.baidu.com"
}
}
}
</script>
v-on绑定事件
v-on: 可以简写成@
<template>
<div>
<p>你要购买商品的数量{{count}}</p>
<button @click="addOne">+1</button><br>
<button @click="abstractOne">-1</button><br>
<button @click="addFive">+5</button><br>
</div>
</template>
<script>
export default {
data () {
return {
count:0
}
},
// 定义函数
// this指向export default的{}
// data函数会把对象挂到当前组件对象上
methods: {
addOne(){
this.count++;
},
abstractOne(){
this.count--;
},
addFive(){
this.count +=5
}
}
}
</script>
v-on事件对象
Vue事件处理函数中,拿到事件对象
- 无传参,通过形参直接接收
- 传参,通过
$event
指代事件对象传给事件处理函数
<template>
<div>
<a @click="one" href="https://www.baidu.com">Baidu</a><br>
<a @click="two(5,$event)" href="https://www.taobao.com">Taobao X {{count}}</a>
</div>
</template>
<script>
export default {
data () {
return {
count:0
}
},
methods: {
one(e){
e.preventDefault()
},
two(num,e){
e.preventDefault()
this.count += num
}
}
}
</script>
v-on事件修饰符
在事件后面.修饰符名-给事件带来更强大的功能
修饰符:
- .stop -阻止事件冒泡
- .prevent -阻止默认行为
- .once -程序运行期间,只触发一次事件处理函数
<template>
<div>
<div @click="fatherFn">点击父盒子
<p @click.stop="sonFn">点击子盒子</p>
<a @click.prevent="preventFn" href="https://www.baidu.com">Baidu</a>
</div>
</div>
</template>
<script>
export default {
methods: {
fatherFn(){
console.log('你点击了父盒子')
},
sonFn(){
console.log("你点击了子盒子")
},
preventFn(){
console.log("阻止了页面跳转")
}
}
}
</script>
v-on按键修饰符
- @keydown.enter - 监测回车按键
- @keydown.esc - 监测返回按键
<template>
<div>
<input type="text" @keydown="enterFn">我是回车键
<input type="text" @keydown="escFn">我是退出键
</div>
</template>
<script>
export default {
methods: {
enterFn(){
console.log("用户按下了回车键")
},
escFn(){
console.log("用户按下了退出键")
}
}
}
</script>
Vue指令:v-model
双向数据绑定
- 变量变化 -> 视图自动同步
- 视图变化 -> 变量自动同步
下拉菜单
<template>
<div>
<!-- 下拉表单 -->
<select v-model="from">
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="hz">杭州</option>
</select>
</div>
</template>
<script>
export default {
data () {
return {
from:''
}
}
}
</script>
复选框
非数组 -关联的是复选框的checked属性
数组 -关联的是复选框的value属性
<template>
<div>
<!-- 复选框 -->
<div>我的爱好:</div>
<input type="checkbox" value="b站" v-model="hobby">b站
<input type="checkbox" value="追星" v-model="hobby">追星
<input type="checkbox" value="写代码" v-model="hobby">写代码
</div>
</template>
<script>
export default {
data () {
return {
hobby:[] // 数组
}
}
}
</script>
单选框
<template>
<div>
<!-- 单选框 -->
<span>性别:</span>
<input type="radio" value="male" name="sex" v-model="gender">男
<input type="radio" value="female" name="sex" v-model="gender">女
</div>
</template>
<script>
export default {
data () {
return {
gender:''
}
}
}
</script>
文本域
<template>
<div>
<!-- 文本域 -->
<span>自我介绍</span>
<textarea name="" cols="30" rows="10" v-model="intro">
</textarea>
</div>
</template>
<script>
export default {
data () {
return {
intro:''
}
}
}
</script>
v-model修饰符
语法:v-model.修饰符 = “Vue数据变量”
- .number 以parseFloat转成数字类型
- .trim 去除首尾空白字符
- .lazy 利用onChange事件,只有失去焦点时才同步
只有当失去焦点时,才会将数据同步到v-model
中
<template>
<div>
<span>自我介绍</span>
<textarea v-model.lazy="intro" cols="30" rows="10"></textarea>
</div>
</template>
<script>
export default {
data () {
return {
intro:''
}
}
}
</script>
Vue指令 v-text/v-html
更新DOM对象的innerText/innerHTML
- v-text = ‘Vue数据变量’
- v-html = ‘Vue数据变量’
注意:会覆盖默认值
<template>
<div>
<p v-text="str"></p>
<p v-html="str"></p>
<!-- 会覆盖前面的 -->
<p v-html="str">{{msg}}</p>
</div>
</template>
<script>
export default {
data () {
return {
msg:"硬糖少女303",
str:'<span>火箭少女101</span>'
}
}
}
</script>
Vue指令 v-if/v-show
控制标签的隐藏或出现
- v-show = ‘Vue变量’
- v-if = ‘Vue变量’
原理
- v-show用的display:none 隐藏(频繁切换使用)
- v-if 直接从DOM树上移除
<template>
<div>
<p v-if="isOk">披荆斩棘天使翼</p>
<p v-show="isTrue">火箭少女101</p>
</div>
</template>
<script>
export default {
data () {
return {
isOk:true,
isTrue:true
}
}
}
</script>
Vue指令 v-for循环渲染
列表渲染,所在标签结构,按照数据数量,循环生成
v-for = “(item , index) in 目标结构”
<template>
<div id="app">
<!-- 省略其他 -->
<p>学生详细信息</p>
<ul>
<li v-for="obj in stuArr" :key="obj.id">
<span>{{obj.name}}</span>
<span>{{obj.sex}}</span>
<span>{{obj.hobby}}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
stuArr: [
{
id: 1001,
name: "孙悟空",
sex: "男",
hobby: "吃桃子",
},
{
id: 1002,
name: "猪八戒",
sex: "男",
hobby: "背媳妇",
}
]
}
}
}
</script>
Vue指令 v-for的更新检测
- 数组变更方法,才会导致v-for更新,页面更新
- push() pop() shift() unshift() splice() sort() reverse()
this.$set() v-for可以更新检测
<template>
<div>
<ul>
<li v-for="(item,index) in list" :key="index">{{item}}</li>
</ul>
<button @click="reBtn">翻转数组</button>
<button @click="jBtn">截取前3个</button>
<button @click="upBtn">点击改掉第一个元素的值</button>
</div>
</template>
<script>
export default {
data () {
return {
list:[5,3,6,7,2]
}
},
methods: {
reBtn(){
this.list = this.list.reverse()
},
jBtn(){
this.list = this.list.slice(0,3)
},
upBtn(){
this.$set(this.list,0,1000) // 只修改其中一个值
}
}
}
</script>
<style>
</style>
v-for就地更新
v-for更新时,是如何操作DOM的?
循环出新的虚拟DOM结构,和旧的虚拟DOM结构对比,尝试复用标签就地更新内容
真实DOM
虚拟DOM
存在于内存中的一个js对象,保存了DOM关键信息
<template>
<div id="box">
<p class="my_p">123</p>
</div>
</template>
const dom = {
type:'div',
attributes:[ { id: 'box'}],
children:{
type:'p',
attributes:[{ class:'my_p'}],
text:'123'
}
}
好处:
- 提高DOM更新的性能,不频繁操作真实DOM,在内存中找到变化部分,再更新真实DOM(打补丁)
diff算法
同级比较-根元素变化-整个DOM树删除重建
同级比较-根元素不变-属性改变更新属性
diff算法如何比较新旧虚拟DOM
- 同级比较
- 根元素变化,删除重建整个DOM树
- 根元素不变,属性改变,DOM复用,只更新属性
v-for中key的作用
- 无key
最大限度尝试就地修改/复用相同类型元素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpjDRxDk-1638097686610)(C:/Users/Changnie/AppData/Roaming/Typora/typora-user-images/image-20211114165925155.png)]
- 有key,值为索引
有key属性,基于key来比较新旧虚拟DOM,移除key不存在元素
- 有key,值唯一不重复的字符串或数字
有key属性,基于key来比较新旧虚拟DOM,移除key不存在元素
动态class
:class=“{类名: 布尔值}”
<template>
<div>
<p :class = " {red : bool }">Vue基础</p>
</div>
</template>
<script>
export default {
data () {
return {
bool:true
}
}
}
</script>
<style>
.red{
color: red;
}
</style>
动态style
:style="{css属性: 值}"
<template>
<div>
<p :style="{color:str}">Vue基础</p>
</div>
</template>
<script>
export default {
data () {
return {
str:'red'
}
}
}
</script>
<style>
</style>
过滤器
过滤器只能用在,插值表达式和v-bind动态属性里
- 全局过滤器
Vue.filter(“过滤器名”,(val) => {return “返回处理后的值”})
在main.js文件中
// 过滤器要写在new Vue上面
Vue.filter('reverse',val => val.split("").reverse().join(""))
<template>
<div>
<p>原来的样子{{msg}}</p>
<p>使用过滤器后的样子{{msg | reverse}}</p>
</div>
</template>
<script>
export default {
data () {
return {
msg:"Hello,Vue"
}
}
}
</script>
<style>
</style>
- 局部过滤器
filters:{过滤器名字: (val) => {return “返回处理后的值”}}
<template>
<div>
<p>需要翻转的message{{msg}}</p>
<p>使用过滤器后的样子{{msg | reverse}}</p>
<p :title="msg | toUp">鼠标长停字母大写</p>
</div>
</template>
<script>
export default {
data () {
return {
msg:"Hello,Vue"
}
},
filters: {
toUp(val){
return val.toUpperCase()
}
}
}
</script>
<style>
</style>
- 过滤器传参
vue变量 | 过滤器名(val)
// main.js
Vue.filter('reverse',(val,s) => val.split("").reverse().join(s))
<template>
<div>
<p>需要翻转的message{{msg}}</p>
<p>使用过滤器后的样子{{msg | reverse('|')}}</p>
<p :title="msg | toUp">鼠标长停字母大写</p>
</div>
</template>
- 多个过滤器
vue变量 | 过滤器1 | 过滤器2
<template>
<div>
<p>需要翻转的message{{msg}}</p>
<p>使用过滤器后的样子{{msg | reverse('|')}}</p>
<p :title="msg | toUp | reverse('|')">鼠标长停字母大写</p>
</div>
</template>
计算属性computed
一个变量的值,依赖另外一些数据计算而来的结果
计算属性也是vue数据变量,所以不要和data里重名,用法和data相同
<template>
<div>
<p>a的值为{{a}}</p>
<p>b的值为{{b}}</p>
<p>计算后的值为{{num}}</p>
</div>
</template>
<script>
export default {
data () {
return {
a:10,
b:20
}
},
computed: {
num(){
return this.a + this.b
}
}
}
</script>
<style>
</style>
- 计算属性的特点
函数内使用的变量改变,重新计算结果返回
- 计算属性的好处
带缓存,只要依赖项不变,都直接从缓存取
依赖项改变函数自动执行并重新缓存
计算属性的完整写法
计算属性的赋值和取值
<template>
<div>
<span>姓名:</span>
<input type="text" v-model="full">
</div>
</template>
<script>
export default {
computed: {
full:{
// 赋值过程
set(val){
console.log(val)
},
// 取值过程
get(){
return '刘昊然'
}
}
}
}
</script>
<style>
</style>
侦听器watch
可以侦听data/computed属性值的改变
- 简单类型侦听器
<template>
<div>
<span>用户名</span>
<input type="text" v-model="name">
</div>
</template>
<script>
export default {
data () {
return {
name:''
}
},
watch: {
name(newVal,oldVal){
console.log(newVal,oldVal)
}
}
}
</script>
<style>
</style>
- 引用类型侦听器
<template>
<div>
<span>用户名</span>
<input type="text" v-model="user.name"><br>
<span>密码</span>
<input type="password" v-model="user.pwd">
</div>
</template>
<script>
export default {
data () {
return {
user:{
name:'',
pwd:0
}
}
},
watch: {
user:{
immediate:true, //立即开启侦听
deep:true, //开启深度监听
handler(newVal,oldVal){
console.log(newVal,oldVal)
}
}
}
}
</script>
<style>
</style>
Vue组件基础
组件概念
- 组件是可复用的Vue实例,封装标签,样式和JS代码
- 组件化:封住的思想,把页面上可重用的部分封装为组件,从而方便项目的开发和维护
(1)全局注册
import Vue from 'vue'
import 组件对象 from 'vue文件路径'
Vue.component('组件名', 组件对象)
(2)局部注册
import Pannel from "./components/Pannel.vue"
export default {
components: {
Pannel:Pannel
}
}
组件内的scoped是如何工作的
scoped保证所添加样式只针对当前文件有效
- 准备:当前组件内标签都被添加
data-v-hash值
的属性 - 获取:css选择器都被添加
data-v-hash值
的属性选择器
Vue组件通信
- 父 -> 子 通过props来传递
- 步骤:
子组件 - props - 变量(准备接收)
父组件 - 传值进去
组件循环
<template>
<div>
<my-product
v-for="obj in list" :key="obj.id"
:title="obj.proname"
:price="obj.proprice"
:intro="obj.info"
></my-product>
</div>
</template>
<script>
import MyProduct from "./components/MyProduct.vue"
export default {
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: '炎热的夏天, 来个冰激凌了' },
],
}
},
components: {
MyProduct
}
}
</script>
<style>
</style>
组件通信-单向数据流
从父到子的数据流向,叫单向数据流
Vue规定props里的变量,本身是只读的
错误:
- 子组件改父传入的数据不通知父组件,数据的不一致性
- vue规定props本身只读的,不允许重新赋值
子向父-自定义事件
子组件内,恰当的时机,触发父给我绑定的自定义事件,导致父methods里事件处理函数执行
- 父组件
<template>
<div>
<my-product
v-for="(obj,index) in list" :key="obj.id"
:title="obj.proname"
:price="obj.proprice"
:intro="obj.info"
:index="index"
@subPrice="fn"
></my-product>
</div>
</template>
<script>
import MyProduct from "./components/MyProduct.vue"
export default {
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: '炎热的夏天, 来个冰激凌了' },
],
}
},
components: {
MyProduct
},
methods: {
fn(index,price){
this.list[index].proprice -= price
}
}
}
</script>
<style>
</style>
- 子组件
<template>
<div class="my-product">
<h3>标题: {{title}}</h3>
<p>价格: {{price}}元</p>
<p>{{intro}}</p>
<button @click="kan">砍价1元</button>
</div>
</template>
<script>
export default {
props:['index','title','price','intro'],
methods: {
kan(){
this.$emit("subPrice",this.index,1)
console.log(this.index)
}
}
}
</script>
<style>
.my-product {
width: 400px;
padding: 20px;
border: 2px solid #000;
border-radius: 5px;
margin: 10px;
}
</style>
注意:
- 为了区分砍价的组件,在props中传入index
- 子组件传入this.index 给父组件用来区分到底是给哪个对象砍价
组件传值总结
组件是什么?
是一个vue实例,封装标签,样式和js代码
组件好处?
便于复用,易于扩展
组件通信有哪几种?
父 -> 子
子 -> 父
兄弟间的组件传值
跨组件传值-EventBus
常用于跨组件通信时使用
语法:
- src/EventBus/index.js 创建空白Vue对象并导出
import Vue from 'vue'
// 导出空白vue对象
export default new Vue()
- 在要接收值的组件(List.vue) eventBus.$on(‘事件名’,函数体)
import EventBus from '../EventBus'
export default {
props:['arr'],
created () {
// 数据的接收方
EventBus.$on("subPrice",(index,price)=>{
this.arr[index].proprice > 1 && (this.arr[index].proprice = this.arr[index].proprice - price)
})
}
}
- 在要传递值的组件 eventBus.$emit(‘事件名’,值)
// 引入空白vue实例
import EventBus from "../EventBus"
export default {
props:['index','title','price','intro'],
methods: {
kan(){
// 数据传递方
EventBus.$emit("subPrice",this.index,1)
}
}
}