vue基础
是什么—javascript框架
构建用户界面的前端框架
1.构建用户界面用vue往html页面中填充数据
2.框架现成的解决方案,遵守框架的规范去实现自己的业务功能学习vue 就是学习vue框架中规定的用法vue的指令组件(对ul结构的复用),路由,vuex,vue组件库
vue框架的主要特性
数据驱动视图
数据的变化驱动页面的更新变化(单向)程序员只需维护好数据,页面结构被vue自动渲染出来
双向数据绑定
在网页中,form表单负责采集数据,Ajax负责提交数据
两个特性的底层原理—MVVM
vue基础操作
el挂载点
data数据对象
methods方法
提供methods中的所有函数,其中的this都指向当前实例
与computed计算属性的区别
computed计算属性
一旦计算出结果,立刻缓存,下一次读取,直接读取缓存,性能较高
简写
<body><!-- 2.控制的dom元素区域 --><div id="app"><h3>小黑的礼物清单</h3><table border="1" cellspacing="0"><tr><th>名字</th><th>数量</th></tr><tr v-for="(item,index) in lists" :key="item.id"><td>{{item.name}} </td><td>{{item.num}} </td></tr></table><!-- 计算属性本质是属性,不能写成函数totalCount() --><p>礼物总数:{{totalCount}} 个</p></div><!-- 1.引入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- 3.vue实例对象 --><script>let app=new Vue({el:'#app',data:{lists:[{id:1,name:"篮球",num:1},{id:2,name:"足球",num:3},{id:3,name:"排球",num:5},]},computed:{// 对数组求和-->reduce(函数,初始值)//this.lists.reduce((sum,item)=>item.num+sum,0)totalCount(){return this.lists.reduce((sum,item)=>item.num+sum,0)}}})</script></body>
完整写法
<body><!-- 2.控制的dom元素区域 --><div id="app">姓: <input type="text" v-model="firstname">+名: <input type="text" v-model="lastname">={{fullname}}<br><button @click="changeName">改名卡</button></div><!-- 1.引入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- 3.vue实例对象 --><script>let app=new Vue({el:'#app',data:{firstname:"刘",lastname:"备"},methods:{changeName(){this.fullname='赵云'}},computed:{// //简写->获取// fullname(){// return this.firstname+this.lastname// }//完整写法-》获取+设置fullname:{// 1.当fullname计算属性,被获取求值时,执行get(由缓存读取缓存),将返回值作为最终的结果get(){return this.firstname+this.lastname},//2.当fullname计算属性,被修改赋值的时候,执行set,修改的值传给set方法的形参set(value){this.firstname=value.slice(0,1)this.lastname=value.slice(1) }}}})</script></body>
watch侦听器(监视器)
<body><!-- 2.控制的dom元素区域 --><div id="app"><textarea v-model="obj.words"></textarea></div><!-- 1.引入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- 3.vue实例对象 --><script>let app=new Vue({el:'#app',data:{// words:'',obj:{words:''}},//在事件发生变化的时候触发执行watch:{//old一般不用// words(newval,oldval){// console.log(oldval+"变为了"+newval);// }'obj.words'(newval,oldval){console.log(oldval+"变为了"+newval);}}})</script></body>
指令
Vue 根据不同的指令,针对标签实现不同的功能
指令:
带有V-前缀的特殊的标签属性
本质:属性
基本使用步骤
内容填充
插值表达式
v-text
v-html
能够解析标签
注册事件
v-on基础
<body><div id="app"><button v-on:click="count++">+</button>{{count}}<button v-on:click="count--">-</button></div><!-- 1.引入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- 3.vue 的实例对象--><script>let app=new Vue({//指定控制的dom区域el:'#app',//数据源data:{count:20,}})</script></body>
<body><!-- 操作的dom区域 --><div id="app"><input type="button" value="vue-on指令" v-on:click="doIt"><input type="button" value="vue-on简写" @click="doIt"><p @click="changeFood"> {{food}} </p></div><!-- 引入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- 创建vue实例对象 --><script>let app=new Vue({el:'#app',data:{food:'potato'},methods:{doIt:function(){alert('do it')},changeFood:function(){this.food+=' is tasty!'}}})</script></body>
v-on补充(传递参数,事件修饰符)
<body><div id="app"><input type="button" value="可乐5元" @click="buy(5)"><input type="button" value="咖啡10元" @click="buy(10)"><h2> 余额{{money}}</h2></div><!-- 1.引入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- 3.vue 的实例对象--><script>let app=new Vue({//指定控制的dom区域el:'#app',//数据源data:{money:990},methods:{buy(price){this.money-=price}}})</script></body>
元素的显示与隐藏
v-show
v-show :通过表达式的真假切换元素的显示状态本质: 改变元素的display属性,仍然存在 适用频繁切换的场景
<body><!-- 控制的元素dom区域 --><div id="app"><!-- <button @click="changeIsshow">切换显示状态</button> --><button @click="add">年龄触发</button><img src="../照片/014b4e62ccddaf0002c45e373609e6.webp.jpg" v-show="age>=18"></div><!-- 引入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>// 创建vue实例对象let app=new Vue({el:'#app',data:{isshow: false,age:17},methods:{changeIsshow: function(){this.isshow = ! this.isshow},add:function(){this.age++}}})</script></body>
v-if
v-if :通过表达式的真假切换元素的显示状态
本质:直接操作dom元素,添加或者删除元素
v-else
v-esle-if
要搭配V-if使用
<body><!-- 2.控制的dom元素区域 --><div id="app"><p v-if="score>=90">A</p><p v-else-if="score>=80">B</p><p v-else-if="score>=70">C</p><p v-else>D</p></div><!-- 1.导入vue.JS --><script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script><!-- 3.vue实例对象 --><script>let app=new Vue({el:'#app',data:{score:80}})</script></body>
<body><<!-- 2.控制的dom区域 --><div id="app"><input type="button" value="切换显示" @click="toggleIsShow"><p v-if="isShow">我是p标签</p></div><!-- 1.引入vue --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- 3.创建vue实例对象 --><script>let app=new Vue({el:'#app',data:{isShow:true},methods:{toggleIsShow:function(){this.isShow=!this.isShow}}})</script></body>
设置元素的属性
v-bind
v-bind : 设置html标签的属性,如src,url…
3.1完整写法:v-bind:属性名3.2简略写法::属性名
<body><!-- 3.v-bind : 设置元素的属性3.1完整写法:v-bind:属性名3.2简略写法::属性名--><!-- 2.vue操作的dom区域 --><div id="app"><!-- <img v-bind:src="imgSrc"><img v-bind:title="imgTitle"><img v-bind:class="isActive?'active':' ' "><img v-bind:src="{active:isActive}"> --><!-- 简化写法 --><!-- <img :src="imgSrc" alt=""><br><img :src="imgSrc" :title="imgTitle+'!!!' " alt=""><br> --><img :src="imgSrc" :class="isActive ? 'active' : ' ' " alt="" @click="changeColor"><!-- <br><img :src="imgSrc" :class="{active:isActive}" alt=""> --></div><!--1. 导入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- 3.创建Vue实例对象 --><script>let app=new Vue({el:"#app",//操作的dom区域data:{imgSrc:"../照片/014b4e62ccddaf0002c45e373609e6.webp.jpg",imgTitle:"my",isActive: false},methods:{changeColor:function(){this.isActive=!this.isActive}}})</script></body>
v-bind对样式控制的增强
操作class
操作style
<div id="app"><div class="big" :style="{backgroundColor:'red','background-color':'black' }"><p>MY TEST</p></div></div>
根据数据渲染,多次渲染整个元素
v-for
v-for="(item,index) in arr
<body><!-- 控制的dom元素区域 --><div id="app"><ul><li v-for="(item,index) in arr">{{index}} {{item}} 你好</li><li v-for="(item,indecex) in obj">{{ item.name }}</li></ul></div><!--导入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- vue实例对象区域 --><script>let app=new Vue({el:'#app',data:{arr:[2,31,2,5,6,6],obj:[{name:"alice"},{name:"bob"},{name:"sjeiwhr"}]}})</script></body>
v-for中的key
给元素添加的唯一标识
<body><h1>小黑的书架</h1><div id="app"><ul><li v-for="(item,index) in bookList" :key="item.id"><span>{{item.name}} </span><span>{{item.author}} </span><button @click="del(item.id)">删除</button></li></ul></div><!--导入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- vue实例对象区域 --><script>let app=new Vue({el:'#app',data:{bookList:[{id:1,name:'红楼梦',author:'曹雪芹'},{id:2,name:'三国演义',author:'罗贯中'},{id:3,name:'水浒传',author:'施耐庵'},{id:4,name:'西游记',author:'吴承恩'},]},methods:{del(id){//根据当前的id删除对应项//filter:返回满足条件的新数组this.bookList=this.bookList.filter(item => item.id!==id)}}})</script></body>
获取或设置表单元素的值
v-model
双向数据绑定,修改表单里的值会影响数据
应用于input
<body><!-- 控制的dom元素区域 --><div id="app"><input type="text" v-model="message" @keyup.enter="getM"><input type="button" value="修改message" @click="setM"><h2>{{message}} </h2></div><!--导入vue.js --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><!-- vue实例对象区域 --><script>let app=new Vue({el:'#app',data:{message:"my code"},methods:{setM:function(){alert(this.message)},getM:function(){alert(this.message)}}})</script></body>
应用于其他表单元素
指令修饰符
Vue生命周期
一个vue实例从创建到销毁的整个过程
分为四个阶段创建,:响应式数据挂载,:渲染模板更新,:数据修改,更新视图销毁,: 销毁实例
生命周期函数(钩子函数)
在vue生命周期中自动运行的一些函数
工程化开发
脚手架
组件化开发
组件化
将页面拆分成一个个组件,每个组件有自己独立的结构,样式,行为
好处:便于维护,复用
分类:普通组件,根组件
根组件
整个应用最上层的组件,包裹所有的普通组件
一个根组件App.vue,包含三部分:template结构:只有一个根节点style样式(支持less,要安装less与less-loader)script行为
组成
<template><div class="app">我是结构<div class="box" @click="fn">aaa</div></div></template><script>//导出当前组件的配置项//可以提供data(特殊) methods computed watch 生命周期八大钩子export default{created(){console.log("hello world");},methods:{fn(){alert("hello")}}}</script><style lang="less">/* 让style 支持less *//* 1.给style加上 lang='less' *//* 2.安装依赖包 less less-loader */.app{width: 200px;height: 200px;background-color: pink;.box{width: 100px;height: 100px;background-color: blue;}}</style>
普通组件的注册使用
注册方式
局部注册
只能在注册的组件内使用
1.创建组件创建.vue文件(三个部分:结构样式行为)
2.在使用的组件内导入并注册import 组件对象 from 'vue文件路径'export default {components:{//3.注册组件// '组件名': 组件对象HmHeader:HmHeader,}}
3.使用组件<组件名></组件名>(命名规范:大驼峰命名法)
<!-- 1.创建组件 -->
<template><div class="hmheader">i am hmheader</div></template><script>export default {}</script><style>.hmheader{height: 100px;line-height: 100px;text-align: center;font-size: 30px;background-color: #8064a2;color: white;}</style>
<template><div class="app"><!-- 4.使用组件 --><!-- 头部组件 --><HmHeader></HmHeader><!-- 主体组件 --><HmMain></HmMain><!-- 底部组件 --><HmFooter></HmFooter></div></template><script>//2.导入组件import HmHeader from './components/HmHeader.vue'import HmMain from './components/HmMain.vue'import HmFooter from './components/HmFooter.vue'export default {components:{//3.局部注册组件// '组件名': 组件对象HmHeader:HmHeader,HmMain:HmMain,HmFooter:HmFooter}}</script><style>.app{width: 600px;height: 700px;background-color: pink;}</style>
全局注册
所有组件内都可以使用
1.创建组件
2.在main.js 里面进行导入全局注册
import Vue from 'vue'import App from './App.vue'//1.导入全局的组件import HmButton from './components/HmButton'Vue.config.productionTip = false//2.进行全局注册Vue.component('HmButton',HmButton)new Vue({el:'#app',render:(createElement)=>{//基于app创建元素return createElement(App)}})
组件的样式冲突scoped
写在组件中的样式会全局生效-》容易造成多个组件之间的样式冲突问题
1.全局样式:默认组件中的样式会作用到全局
2.局部样式:给style 加上scoped属性,让样式只作用于当前组件
scoped原理
当前组件内的所有标签被添加data-v-hash值得属性
css选择器都是被添加[data-v-hash]的属性选择器
组件的data函数
一个组件的data选项-》必须是函数-》保证每一个组件实例拥有独立的数据对象
组件通信
组件与组件之间的数据传递组件中的数据是独立的,无法直接访问其他组件的数据=》使用组件通信技术
父子通信
父传子props
1.给子组件标签添加自定义属性,传值
2.在子组件内通过props进行接收
3.在子组件中渲染使用
子传父emit
1.子组件内使用$emit,给父组件发送消息通知
2.父组件监听事件
3.父组件提供处理函数,形参中获取参数
非父子通信-事件总线(拓展)
兄弟之间
1.创建一个都能访问的事件总线(空vue实例)utils/EventBus.js
//1.创建一个都能访问到的事件总线(空的vue实例)import Vue from 'vue'
const Bus=new Vue()
export default Bus
2.接收方监听Bus实例的事件
created(){//2.在接收放进行监听Bus的事件(订阅消息)Bus.$on('sendMsg',(msg)=>{console.log(msg);this.msg=msg})},
3.发送方触发Bus实例的事件
methods:{clickSend(){//3.发送方,触发事件的方式传递参数(发布消息)Bus.$emit('sendMsg',"hello world")}}
//1.创建一个都能访问到的事件总线(空的vue实例)import Vue from 'vue'const Bus=new Vue()export default Bus
非父子通信-provide&inject
实现跨层级地共享数据(爷孙之间)
传递数据的时候1.简单数据类型(非响应式的)2.复杂数据类型(响应式的)--可以往下传改变后的值
双向数据绑定
v-model
原理:
语法糖,例如在输入框上为value属性与input事件的合写
$event用于模板中,获取事件的实参
<input :value="msg" @input="msg=$event.target.value" type="text"><input type="text" v-model="msg">
实现
<template><div><BaseSelect :cityId="cityId" @handleChange="cityId=$event "></BaseSelect></div></template><script>import BaseSelect from './components/BaseSelect.vue'export default {components:{BaseSelect},data(){return {cityId:'102'}},}</script><style></style>
<template><select :value="cityId" @change="changeFn"><option value="101">北京</option><option value="102">上海</option><option value="103">深圳</option><option value="104">武汉</option><option value="105">广州</option><option value="106">重庆</option></select></template><script>export default {props:{cityId:String},methods:{changeFn(e){this.$emit('handleChange',e.target.value)}}}</script><style></style>
v-model简化代码
.sync修饰符
父组件:
:属性名.sync
子组件:
this.$emit(‘update:属性名’,)
获取dom元素
运用ref和$refs可以获取dom元素或者是组件实例
查找范围—当前的组件内
添加: ref="名字"
获取: this.$refs.名字
实现组件实例的方法:this.$refs.名字.方法名()
$ nextTick
vue 是异步更新的
想要在DOM更新完成后做某事,可以使用 n e x t T i c k t h i s . nextTick this. nextTickthis.nextTick()=>{
}
自定义指令
注册指令
1.全局注册指令
在main.js中
//1.全局注册指令Vue.directive('focus',{//inserted会在指令所在的元素,被插入到页面中触发inserted(el){//el 就是指令所绑定的元素console.log(el);}})
<script>export default {//2.局部注册的指令directives:{//指令名:指令的配置项focus:{inserted(el){el.focus()}}}}
2.使用指令
v-指令名(v-focus)
指令的值
binding-value 拿到指令的值
export default {data(){return {color1:'blue',color2:'red',}},directives:{//指令名:指令的配置项color:{inserted(el,binding){//binding-value 拿到指令的值el.style.color=binding.value},//update钩子,监听指令值得变化,进行dom更新操作update(el,binding){console.log('颜色改变了');el.style.color=binding.value}}},
指令的封装
v-loading指令
1.准备类名loading,通过伪元素提供遮罩层
2.添加或移除类名实现loading蒙层的添加或者移除
3.利用指令语法,封装v-loading 通用指令
插槽
默认插槽(一个定制的位置)
让组件内的一些结构支持自定义
组件内需要定制的结构部分 改用<slot占位</ slot>
使用组件的时候 ,在组件标签内部传入结构替换slot
后备内容(默认值)
封装组件的时候可以在‘<slot ’插槽中提供后备内容(默认显示内容)
当外部使用组件时,不传东西,则slot会显示后备内容
具名插槽(定制多处位置)
- 多个slot使用name属性区分名字
<div><slot name="head"></slot></div>
- template 配合v-slot:名字 来分发对应标签
<MyTest><template v-slot:head>//简写:#head<div>this is head</div></template></MyTest>
插槽传参语法(作用域插槽)
定义插槽的时候可以传值,给插槽上绑定数据,将来使用组件的时候可以用
1.给slot标签以添加属性的方式传值
2.所有的属性将会收集在一个对象中
3.通过template #插槽名=“变量名” 接收
单页应用程序(SPA)
所有功能在一个html页面上实现
路由
路径与组件之间的映射关系
import Vue from 'vue'import App from './App.vue'import My from './views/MyMusic'import Friend from './views/MyFind'import Message from './views/MyMessage'//1.1.引入import VueRouter from 'vue-router'//1.2.安装注册Vue.use(Vue插件)Vue.use(VueRouter)//1.3创建路由对象
const router=new VueRouter({
//2.1创建组件配置规则routes:[{path:'/friend',components:Friend},{path:'/my',component:My},{path:'/music',component:Message},]})Vue.config.productionTip = falsenew Vue({render: h => h(App),//1.4.注入到new Vue中,建立关联router:router}).$mount('#app')
<template><div><div class="footer_wrap"><a href="#/music">发现音乐</a><a href="#/my">我的音乐</a><a href="#/friend">朋友</a></div><div class="top"><!--2.2 路由出口 --><router-view></router-view></div></div></template><script>export default {}</script><style></style>
相关简便用法
- @ 用于绝对路径的书写,表示当前的src目录
2.router-link
导航链接 实现导航高亮效果(本质还是a标签,指定to属性,跳转)
自动添加类名
1)router-link-active2) router-link-exact-active
导航跳转传参
1)查询参数传参to="/path?参数名=值"(跳转)$route.query.参数名(接收)2)动态路由传参路由:/path/:参数名(必须传入参数)(/path/:参数名?可以不传入参数)跳转:to="path/值"接收:$route.params.参数名
重定向(redirect)
问题:匹配路径之后若未匹配到组件则则会显示空白
重定向:匹配path之后强制跳转path路径{path:'匹配路径',redirect:'重定向到的路径'}
404路由:
{path:'*',component:NotFind}
模式设置
hash 路由:(默认)
路径中含有#
history 路由:
(上线需要服务器支持)
{
routes,
mode:‘history’
}
编程式导航–基本跳转
路径跳转
1.this.$router.push('路由路径')
2.this.$router.push({
path:'路由路径'
})
路由名字跳转
this.$router.push({
name:'路由名'
})
{name:'路由名',path:'/path/xss'}
自定义创建项目
ESlint代码规范
代码规范:一套写代码的约定规则
规则
报错解决
手动
看报错自己找
自动
基于vscode 配置插件ESlint
vuex—仓库
vue的状态管理工具
一个插件,管理vue通用的数据(购物车数据,个人信息数据)
State 保存数据
初始化环境
1.安装vuex
npm i vuex@3 --legacy-peer-deps
2.新建vuex模板文件
store/index.js
// 导入vue
import Vue from 'vue'
// 导入Vuex
import Vuex from 'vuex'
// vuex 是vue的插件,需要use一下,进行插件的安装初始化
Vue.use(Vuex)
// 创建仓库 store
const store = new Vuex.Store()
// 导出仓库
export default store
3.main.js挂载
import Vue from 'vue'
import App from './App.vue'// 导入仓库import store from './store'Vue.config.productionTip = falsenew Vue({render: h => h(App),store: store}).$mount('#app')
使用仓库中的数据–store直接访问
4.定义数据–state
const store = new Vuex.Store({// state 状态,即数据,类似vue组件中的data// 区别:// 1.data 是组件自己的数据// 2.state 中的数据整个vue项目的组件都能访问到,state: {count: 100}})
5.使用数据
获取 store:1.Vue模板中获取 this.$store2.js文件中获取 import 导入 store模板中: {{ $store.state.xxx }}
组件逻辑中: this.$store.state.xxx
JS模块中: store.state.xxx
使用仓库中的数据----通过辅助函数(简化)
1 组件中导入mapState
import { mapState } from 'vuex'
2.计算属性中展开运算符展开
// computed: {// 将state中的数据定义在组件的计算属性中// 1.一个一个return,还是会重复写// count () {// return this.$store.state.count// }// }// 2.利用mapState()// computed: mapState(['count'])computed: {...mapState(['count', 'others'])}}
Mutations 同步更改数据
vuex遵循单向数据流,组件中不能直接修改数据
mutations修改State数据
1.在store.js中定义mutations对象,存放修改State对象的方法
const store = new Vuex.Store({// 开启严格模式(默认情况,vue不检测错误代码,减少成本)strict: true,// state 状态,即数据,类似vue组件中的data,提供整个Vue组件访问的数据// 区别:// 1.data 是组件自己的数据// 2.state 中的数据整个vue项目的组件都能访问到,state: {count: 100,others: '我是第二个数据'},// 定义mutations,提供修改数据的方法mutations: {// 方法里的第一个参数是当前store的state属性// payload 载荷 运输参数 使用mutations的时候 可以传递参数 传递载荷addCount (s,n) {s.count +=n}}})
2.在页面组件中提交调用
methods: {handleAdd (n) {this.$store.commit('addCount',n)}}
3.提交参数
只能提交一个参数,若有多个参数,需要包装成对象
mapMutation辅助函数
组件中导入从mutations中提取的方法
import { mapState, mapMutations } from 'vuex'
methods: {...mapMutations(['handleAdd'])// handleAdd (n) {// this.$store.commit('addCount', n)// }}
action 异步更改数据
// action 处理异步操作// 不能直接操作state中的数据,需要commit mutationactions: {handle (context, num) {// setTimeout模拟异步操作setTimeout(() => {context.commit('handleAdd', num)}, 1000)}}
methods: {handleChange (num) {// 调用actionthis.$store.dispatch('handle', num)}}
mapAction辅助函数
getters 获取state的数据
类似于计算属性
state: {list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}getters: {// getters函数的第一个参数是 state// 必须要有返回值filterList: state => state.list.filter(item => item > 5)}
//使用<div>{{ $store.getters.filterList }}</div>
//
mapGetters 辅助函数
computed: {...mapGetters(['filterList'])
}<div>{{ filterList }}</div>
module Vuex的模块化
如果把所有的状态都放在state中,当项目变得越来越大的时候,Vuex会变得越来越难以维护