vue的特性
- 数据驱动视图
- 双向数据绑定
底层原理是MVVM(Model 数据源,view 视图,model view Vue实例)
数据驱动视图
- 数据的变化会驱动视图自动更新
- 优点:只需要维护数据,页面结构会被vue自动刷新
双向数据绑定
- 填写表单时,在开发者不操作DOM的前提下,自动把用户填写的内容同步到数据源中
- js数据的变化,会被自动渲染到页面上
MVVM
在MVVM概念中:
- model代表当前页面渲染时所依赖的数据源
- View 代表当前页面渲染的DOM结构
- ViewModel 代表vue 的实例(mvvm的核心)
vue的基本使用
<script>
//创建 vue 的实例对象
new Vue({
//el 属性是固定写法,表示 vm 实例要控制哪个区域 ,接受的值是一个选择器
//注意:el实例只控制第一个符合条件的标签
el:'#name',
//dta 对象就是要渲染到页面的数据
data:{ name:'wukl',
username:'11',
gender:'男',
info:"<h style='color:blue'>你们好啊!</h>"
}
})
</script>
vue的指令与过滤器
指令
概念:vue提供的模板语法,辅助开发者渲染页面基本结构
vue中按照不同用途指令分为六大类:
- 内容渲染指令
- 属性绑定指令
- 事件绑定指令
- 双向绑定指令
- 条件渲染指令
- 列表渲染指令
内容渲染指令
渲染DOM元素的文本内容
- v-text 缺点:会覆盖元素内部原有的结构
- {{}}:在实际开发中用的最多,只是内容的占位符,不覆盖原有内容!
- v-html:可以把带有标签的字符串,渲染成真正的HTML内容
vue提供的模板渲染语法中,除了支持绑定简单的数据值,还支持Javascript表达式的运算
<div>1+2 的结果是 {{1+2}} </div>
<div>{{ tips}} 反转的结果是:{{tips.split('').reverse().join()}}</div>
<!--在使用v-bind绑定属性期间,如果绑定内容需动态拼接,则字符串外面应该包裹单引号--!>
<div :title="'box'+index">只是一个div</div>
v-text
<!-- 把username 对应的值,渲染到第一个 p 标签内 -->
<p v-text="username"></p>
<!-- 把gender 对应的值,渲染到第二个 p 标签内 -->
<!-- 第二个 p 标签中,默认文本“性别” 会被 gender 的值覆盖掉 -->
<p v-text="gender">性别</p>
<!--一定要放在vue作用域内--!>
{{}}语法
插值表达式,专门解决 v-text 会覆盖默认文本内容的问题
<p>姓名:{{username}}</p>
v-html
v-text只能渲染纯文本内容,若要把包含HTML标签的字符串渲染到页面内,则使用v-html
属性绑定指令
注意:插值表达式只能用在元素的内容节点中,不能用在属性节点中
解决方案: v-blind可以为属性节点绑定值
vue规定 v-bind: 可以简写为 :
<div id="i">
<input type="text" v-bind:placeholder="username" >
<!-- placeholder 属性提供可描述输入字段预期值的提示信息(hint)。
该提示会在输入字段为空时显示,并会在字段获得焦点时消失。 -->
</div>
<!-- vue规定 v-bind: 可以简写为 :-->
事件绑定指令
vue提供了v-on 事件绑定指令
<p>count的值是{{count}}</p>
<!-- v-on:事件名称=“事件处理函数的名称”-->
<button v-on:click="addCount">+1</button>
***************************************************************
<!--如果函数体只有一条语句,那么可以直接简写为: -->
<button @click="count +=1" > +1</button>
在Vue实例中,methods定义了时间的处理函数
const vm=new Vue({
el:'#i',
methods:{
addCount:function(){}
// 简写为addCount(){}
}
})
//vm对象是Vue的实例对象,自带data中的数据,可以通过this(vm.count)调用并修改
再绑定事件处理函数的时候,可以用小括号传递参数:
v-on:click="addCount(1)"
v-on可以被简写成@
@click="addCount(1)"
注意:原生DOM对象 onclick、oninput、onkeyup等原生对象,替换成 vue 的事件绑定形式后分别为v-on:click、v-on:input、v-on:keyup
事件绑定@event
<!-- vue 提供了内置变量,名字叫做 $event, 它就是原生 DOM 的事件对象 e 其中的target属性指向标签-->
<!--当不传递参数时无需设置$event,因为没有被覆盖-->
<button @click="addCount(1,$event)">+N</button>
事件修饰符
为应对事件处理函数中 event.preventDefault() 或event.stopPropagation()等需求,vue提出了事件修饰符的概念,辅助开发者对事件的触发控制。
<a href='http://' @click="show()"></a>
methods:{
show(e){
e.preventDefault();
//原生javascript:阻止末尾行为
}
}
//事件修饰符,加载在事件绑定后
<a href="" @click.prevent="show()">
常见的事件修饰符:
冒泡:当外层与内层部件同时绑定相同事件,触发内部事件时,会冒泡至外层部件。
按键修饰符
监听键盘事件时,可以为键盘相关的事件添加键盘修饰符
<!--只有在'key'是'Enter'时调用'vm.submit()'-->
<input @keyup.enter="vm.submit()">
<!--同理 支持多次绑定-->
<input @keyup.esc="clearInput" @keyup.enter="vm.submit()>
双向绑定指令
v-model:双向绑定指令
只能用于表单元素(能和用户产生交互)
-
input
type=“radio”
type=“checkbox”
type=“text”
-
textarea
-
select
<p>{{username}}</p>
<!-- 使用v-model进行双向数据绑定,修改input中的值,会导致数据源中属性的值变化,此时value值并没有意义-->
<input type="text" v-model="username">
<!--但是使用v-bind是单向绑定,修改input中的值,属性值不会变化-->
<input type="text" :value="username">
<!--对于select,v-model需要挂载到select上,属性的值是由value决定-->
<select v-model="city">
<option value="">请选择城市</option>
<option value="1">北京</option>
<option value="2">上海</option>
</select>
v-model指令的修饰符
.unmber :对于 input 中的数据,当内容仅为一位数字时,才会被当成number类型,多位数字默认为字符串。
.tirm : 绑定数据时自动去除两端字符串。
.lazy : 懒惰模式,忽略变化的中间过程。
条件渲染指令
辅助开开发者按需控制DOM的显示与隐藏。
- v-if
- v-show
<!--原理:v-if每一次都会动态移除和添加元素
默认情况是flase而且这个元素后期很可能不需要被展示,v-if性能好-->
<p v-if="flag">被 v-if 控制</p>
<!--原理:v-show只是运用了css的style="display"
频繁切换显示,v-show性能更好-->
<p v-show="flag">被 v-show 控制</p>
v-if可以配合v-else-if、v-else使用
<div v-if="type === 'A'">优秀</div>
<div v-else-if="type === 'B'">良好</div>
<div v-else="type === 'c'">一般</div>
列表渲染指令
vue提供了循环v-for指令,辅助开发者基于一个数组来循环渲染一个列表结构,v-for使用的是 item in items 形式的特殊语法。
还有第二个可选参数,代表当前索引**(item,index) in items**
- items是待循环数组
- items是被循环的每一项
data:{
items:[
{id:1,username:'kl'},
{id:2,username:'wu'}
]
}
-----------------------------------------------------------
<table class="table table-bordered table-hover table-striped">
<thead>
<th>索引</th>
<th>名称</th>
</thead>
<!--官方建议只要用到了v-for就要绑定一个:key属性-->
<!--尽量把 id 作为 key 的值,
key的值只能是字符串或者数字类型
!唯一指定,不可重复-->
<tbody v-for="item in items":key="item.id">
<tr>
<td>{{item.id}}</td>
<td>{{item.username}}</td>
</tr>
</tbody>
</table>
key的注意事项
使标签的状态和数据对应起来,防止标签状态紊乱
过滤器(vue3.0已删除功能)
过滤器功能:文本的格式化,常用于:插值表达式和v-bind属性绑定
<!--通过 | 管道符调用过滤器,调用capitalize过滤器,对message过滤后再渲染至网页-->
<p>{{ message | capitalize }}</p>
过滤器本质是个函数,定义到filters:{} Vue实例属性中
而且一定要有返回值,message的值可以被过滤器函数直接接收到
**********************************************************
new Vue({
filter:{
//val接收到的就是message的值
capitable(val){
const first=val.charAt(0).toUpperCase //charAt接受一个索引,表示从字符串把索引对应的字符获取过来
const other = val.slice(1) //slice方法可以截取字符串,从索引位置开始
return first+other
}
}
})
私有过滤器和全局过滤器
!!!!!全局过滤器应绑定在Vue实例之前!!!!!
定义在filter中的是私有过滤器,不能被其他区域的 Vue 实例访问
在多个 vue 实例之间共享过滤器,要定义为全局过滤器
//第一个参数是全局过滤器的“名字”
//第二个参数是全局过滤器的“处理函数”
//当全局过滤器和私有过滤器重名时,会优先调用私有过滤器(就近原则)
Vue.filter('capitalize',(str) => {
const first=val.charAt(0).toUpperCase
const other = val.slice(1)
return first+other
})
过滤器的其他特性
1.过滤器可以连续调用
<p>
{{ data | filter_1 | filter_2}}
</p>
2.过滤器函数可以传递参数(因为其本质是函数)
<p> {{ message | filter(arg_1,arg_2)}}</p>
****************************************************
Vue.filter('filter',(val,arg_1,arg_2){})
Vue基本用法
侦听器
Watch侦听器
Watch侦听器:监测数据变化,依据数据变化做出反应
new Vue({
//所有的侦听器都应该被定义到 watch 节点下
watch:{
//侦听器本质是个函数,要监听哪个数据的变化,就把哪个数据名作为方法名
//第一个是newVal 新值 后面是 oldVal 旧值
username(newval,oldval){
}
}
})
方法格式的侦听器:因为如果值没有被改变,侦听器不会立即执行
对象格式的侦听器:可以通过 immediate 选项,让侦听器自动触发
watch:{
//定义对象格式的侦听器
username:{
handler:function(newVal,oldVal){},
//true代表一进入页面就会触发,默认值是false,控制侦听器是否会自动触发一次
immediate:true
}
}
深度侦听
data:{
info:{
username:5
}
}
此时username不直接挂载在data中,不能通过info属性侦听到其值的改变
解决方法:在对象类型侦听器中,设置 deep:true ,只要对象中任何数据变化,就会导致侦听方法被调用
watch:{
//定义对象格式的侦听器
username:{
handler:function(newVal,oldVal){},
//默认为false
deep:true
}
}
**********************************************************
//如果要侦听对象的子属性的变化,则必须包裹一层单引号
'info.username'(){
}
计算属性
通过一系列运算最终得到一个属性值,这个属性值可以直接被模板结构或methods 方法使用
<!--:style 代表动态绑定一个对象,它的值是{ }样式对象-->
<!-- 在样式对象中,只包含 backgroundColor 背景颜色-->
<div class="box" :style="{ backgroundColor :`rgb(${r},${g},${b})`}">
</div>
<!--计算属性可以实现这段属性字符串的复用-->
**************************************************************
<!-- 1.所有的计算属性都要定义在 computed 节点下
2.计算属性在定义时,要定义为“方法格式”
3.使用计算属性时,当成普通的属性即可
4.只要它所依赖的数据源改变,它会被重新求值-->
computed:{
//这个方法要返回一个生成好的rgb字符串
rgb:function(){
return `rgb($(this.r),$(this.g),$(this.b))`
}
}
优点:
- 实现了代码的复用
- 只要它所依赖的数据源改变,它会被重新求值
axios
axios 只专注于网络请求。
基本语法(axios 在导入后就会在创建一个全局可用的函数,用来发起网络请求)
axios({
method:'请求类型',
url:'URL地址',
//params:{}, GET传参
data:{} //POST传参
}).then((result) => {
//.then 用来指定请求成功后的回调函数
})
//result 拿到的数据中的result.data 才是服务器真实的数据
// 因为axios 在真正的数据外,套了一层壳
axios 在真正的数据外,套了一层壳
{
config:{},
data:{真实的数据},
headers:{},
request:{},
status:xxx,
statusText:""
}
结合async和await调用 axios
document.querySelector('#btn').addEventListener('click',async function(){
//如果调用某个方法的返回值时 Promise 实例,则前面可以添加 await !
// await 只能用在被 async “修饰”的方法中
// 加await 后,result从一个Promise对象变成数据对象
await axios({
method:'GET',
url:'http://www.liulongbin.top:3006/api/getbooks',
params:{
id:2
}
}).then(function(result){
console.log(result)
})
})
解构赋值
//结合上层代码result 对象中有 config、data、status等属性
// 如果我们只关心 data 类型,可以使用解构赋值,提取我们期许的数据
// 解构赋值中使用冒号进行重命名
const { data:res }=await axios()
axios.get()和axios.post()
const {data:res}=await axios.get('URL'
(,{
//get参数
params:{}
})
)
***************************************************************
const {data:res}=await axios.post('URL',
(,{
//直接在花括号里写数据即可
})
)
vue-cli
单页面应用程序
单页面应用程序(Single Page Application)简称 SPA,即一个Web网站中只有一个HTML页面。
vue-cli
vue-cli 是Vue.js开发的标准工具,简化了Webpack创建工程化Vue项目的过程。
安装
npm i -g @vue/cli
创建项目
vue create 项目名称
vue项目中 src 目录的构成
assets 文件夹:存放项目中用到的静态资源,eg:css样式,图片资源
compoents 文件夹:程序员封装的、可复用的组件,都要放到 conponents 目录下
mian.js 是项目的入口文件,整个项目的运行,s要执行 main.js
App.vue 是项目的根组件
vue项目运行逻辑
main.js把app.vue渲染到index.html的页面中
//导入vue 这个包,得到vue 构造函数
import Vue from 'vue'
//导入 app.vue 根组件,将来要把 app.vue 中的模板结构,渲染到 html 页面中
import App from './App.vue'
Vue.config.productionTip = false
//创建 Vue 的实例对象
new Vue({
//把render 函数指定组件,渲染到 html 页面中
render: h => h(App),
//.$mount 或者 el 都可以指定 Vue 的作用区域
//el:'#app' div#app 就是个占位符,把其替换为模板内容
//.$mount 与el 作用完全一样
}).$mount('#app')
vue组件
组件化开发
就是对UI结构的封装和复用
vue中的组件化开发
App.vue本质都是就是一个vue的组件
Vue组件的组成部分
- template->组件的模板结构
- script->组件的Javescript行为
- style->组件的样式
<template>
<div>
<h1>test {{username}}</h1>
</div>
</template>
<script>
export default ({
//data信号源
//注意:vue组件中的 data 不能像之前一样,不能指向对象
//注意:组件中data 必须是一个函数
data(){
//这个 return 出去的{ },可以定义数据
return {
username:'keli'
}
}
})
</script>
<style>
h1{
background:pink
}
</style>
唯一根节点以及less预处理器
template只能包含一个根节点
解决方案:模板整体使用一个
整体包裹<style lang="less">
//启用less语法
</style>
组件之间的父子关系
组件的使用
//1.使用import 语法导入需要的组件
import Left from "./components/Left.vue"
//2.使用 components 节点注册组件
export default{
components:{
Left
}
}
//3.以标签形式使用刚才注册的组件
<div>
<Left></Left>
</div>
通过 components 注册的是私有子组件
私有组件只能使用在已在 components 注册过的组件内,未声明则无法使用。
注册全局组件
全局注册一次,不需重复注册
方法:在vue项目的 main.js 入口文件中,通过 Vue.comonent()方法,可以注册全局组件。
//导入需要全局注册的组件
import Count from'@/components/Count.vue'
//参数一:字符串格式,表示组件的“注册名称”
//参数二:需要被全局注册的组件
Vue.component('MyCount',Count)
组件的props
对于全局组件,可能有不同的初始值需求,而props 作为自定义属性,在封装组件时,可以极大的提高组件的复用性!
//这里是全局声明组件Count 中的vue.component 对象挂载的内容
export default{
//这里是数组
//自定义的属性的名字,是封装者自定义的(只要名称合法即可)
props:['init_vue1','init_vue2']
data(){
return count=init_vue1+init_vue2
}
}
***************************************************************
//在使用组件时,需要传递props 中声明的属性值
//这里的 9 是字符串
<count init_vue1='9'></count>
//这里的 9 是数字类型,因为 :(v-bind) 默认括号中的为表达式
<count :init_vue1='9'></count>
这时 init 值可以结合 v-bind 使用,传递javascript 中的属性值.
props中的数据,可以直接在模板结构中被使用,但是,props中的值是只读的,不能直接修改props 的值
解决方案:可以把 props 的值转存在 data 中,因为 data 中的数据都是可读可写的
props中的默认值
用户有可能不传入props中要求属性的默认值,所以要设置初始值。
//注意:数组格式的props不能定义初始值,所以要使用对象格式的 props
export default{
props:{
init:{
//如果外界使用 Count 组件的时候,没有传递 init 属性,初始值就会生效
default:0
}
}
}
props的type值类型
在声明自定义属性时,可以通过 type 来定义属性的值类型。
export default{
props:{
init:{
//用 default 属性定义属性的默认值
default:0,
//用 type 属性定义属性的值类型
//如果传递过来的值不符合此类型,则会在终端里报错
type:Number
}
}
}
props中的 required 必填项
export default{
props:{
init:{
//当调用该组件时,如果没有传递init 属性值,则会强制报错
required:true
}
}
}
组件之间的样式冲突
写在.vue 组件中的样式会全局生效,所以容易造成样式冲突(因为样式都会在一个 html 页面中呈现,样式会共用)
解决方法
使用 css 中的属性选择器 [ ],为组件添加统一的自定义属性(不同属性之间应该不同)
<template>
<div vue_1 id="app">
<img vue_1 alt="Vue logo" src="./assets/logo.png">
<HelloWorld vue_1 msg="Welcome to Your Vue.js App"/>
<test vue_1 ></test>
</div>
</template>
<style>
//使用css中的属性选择器,使之作用于具有 vue_1 属性的 img
img[vue_1]{
}
</style>
高级解决方法
在 style 标签中 添加 scoped属性,vue中自动生成并填充自定义属性。
<style lang="less" scoped>
</style>
/deep/样式穿透
要在父组件修改子组件中的值,而父组件已添加 scoped 属性,所以不能直接选中子属性,需要加 /deep/ 使用了后代选择器。
<style>
/deep/ h5{}
</style>
//样式不加 /deep/ 时: h5[data-v-3c83f0b7]
//样式修改为: [data-v-3c83f0b7] h5 后代选择器
//可以选中子元素中的 h5 标签
使用外部 UI 库时,不应修改 UI 库的源码,可以使用 /deep/ 选择子代组件,并修改样式。
组件的生命周期
生命周期:创建、运行、销毁的过程
为了在组件生命周期中关键的时间点,准确地执行某些操作,vue提供了生命周期函数,这些函数会被依次执行。
vue官方给出的生命周期流程图:
created
因为此时 vue 实例中的,peops、methods、data已经初始化,但是没有渲染,所以经常在此发送 ajax 请求(调用methods 方法),添加到 data 中,供模板使用。
注意:此时模板结构尚未生成到网页中,所以不能调用DOM操作
befroeMount
将要把生成好的模板结构渲染到 html 页面中,这时还不能操作DOM结构
mounted
这时 el 结构已被模板结构替换,成功渲染到 html 页面中,完成了DOM结构的渲染,可以获取并且操作DOM。
beforeUpdate
将要根据数据变化后、最新的数据重新渲染组建的模板结构
此时,数据是最新的,UI是旧的
document.elementSelect.innerhtml
updated
数据与UI 结构都是最新的
组件之间的数据共享
组件之间的关系
- 父子关系
- 兄弟关系(除了父子关系,剩下的都是兄弟关系)
父子之间的数据共享
父 -> 子: 自定义属性 props
子 -> 父:子组件向父组件共享数据使用自定义事件。
//子组件
methods:{
add(){
this.count+=1
this.$emit('numchange',this.count)
}
}
***************************************************************
//父组件
//只要 $emit 被调用,numChange就会被触发
<Son @numchange='getNewCount'></son>
export default{
data(){
return {this.countFromSon}
}
methods:{
// val 即子组件传来的参数值
getNewCount(val){
this.countFromSon = val
}
}
}
兄弟组件之间的数据共享
vue2.x 中,兄弟组件之间数据共享方案是EventBus。
EventBus的使用步骤
- 创建 EventBus.js 模块,并向外共享一个 Vue 的实例对象。
- 在数据发送方,调用 bus.$emit (‘事件名称’,要发送的数据)方法触发自定义事件。
- 在数据接收方,调用 bus.$on (‘事件名称’,事件处理函数)方法注册一个自定义事件。
ref 引用 DOM
在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用
在 Vue 组件实例对象中都有一个 refs空对象。
可以在组件中不同的标签内添加不同的 ref ,这个属性值会被添加到 refs 中,通过 this.refs.ref 值,便可以直接拿到这个 DOM 元素。
<template>
<h hef="h_first"></h>
</template>
<script>
export default{
methods:{
add(){
this.$refs.h_first.style.color='red'
}
}
}
</script>
<test ref="test_1"></test>
<script>
methods:{
show(){
console.log(this)
},
ch(){
//这里可以直接调用子组件中的方法和数据
// this.$refs.test 即子组件实例对象
this.$refs.test_1.username=0
}
}
}
</script>
this.$nextTick(cb)
组件的 $nextTick(cb) 方法,把回调函数中的内容延迟执行,等DOM渲染完毕后执行
this.$nextTick(() =>{
this.$refs.btnref.focus()
})
***************************************************************
//不能代替this.$nextTick() 因为一旦数据改变就会调用该函数
//而功能要求是: 每次 input 出现时调用,这样会导致按钮出现时也会调用 focus功能 ,导致报错
updated(){
this.$refs.btnref.focus()
}
动态组件
component标签
为了实现动态组件渲染:vue 提供了一个内置的 组件,专门用来实现动态组件的渲染。
//变量 left_val 用来存储组件名称
<component :is="left_val"></components>
data(){
return {
left_val:'left'
}
}
使用 keep-alive 保持状态
因为在使用 component 标签动态创建、销毁组件时,其中的data 数据会被重置,所以要使用 keep-alive 组件保持数据状态。
<!--keep-alive 可以把内部的组件进行缓存,而不是销毁组件-->
<keep-alive>
<component :is="left_val"></component>
</keep-alive>
被keep-alive 包裹的组件不会被销毁,只会被隐藏。( inactive 被缓存状态)
keep-alive 对应的生命周期
当组件被缓存时,会自动触发组件的 deactived 生命周期,(一开始会触发created,之后不会触发,因为之后都是被隐藏)
当组件被激活时,会自动触发组件的 activated 生命周期
keep-alive 的include 和 exclude属性
include:只有名称匹配的组件会被缓存,多个组件间用英文的逗号分隔。
<keep-alive include="MyLeft,MyRight>
<component :is="comName"></component>
</keep-alive>
exclude: 只有名称匹配的组件不被缓存,它和 include 只能存在一个
组件声明的名称和注册时的名称
如果在“声明组件”的时候,没有为组件指定 name 名称,则组件的名称就是“注册时候的名称”。
而当声明过 name 之后,就会把组件的注册名称覆盖。(include 、exclude 等属性中要使用 name 的名称。)
1.组件中的“注册名称”的主要应用场景:以标签的形式,把注册好的组件,渲染和使用到页面结构中
2.组件声明的时候,“name” 名称的主要应用:结合 标签实现组件缓存功能;以及在调试工具中看到组件的 name 名称。
插槽
插槽(slot )是vue 为组件封装者提供的能力。允许开发者在封装组件时,把不确定、希望由用户指定的部分定义为插槽
// change.vue
//vue 官方规定:每一个 slot 插槽都有一个 name 名称
//如果忽略了 solt 的 name 属性,则有一个默认的名称叫做 default
<slot name="default"> </slot>
//此时引用该组件时:会将内容渲染到插槽中
<change>
//默认渲染到 default 中,而要指定插槽,需要用 template 包裹,然后用 v-slot 指定插槽, v-slot 不能直接用在元素标签身上。
<template v-slot:default>
<p>这是插槽的内容</p>
<template>
</change>
***************************************************************
<template #default>
v-slot 简写是 #
后备内容
在 标签中声明一些内容,如果用户没有传入,则插槽部分会被后备内容所填充。
数据传递
<slot msg="sss"></slot>
<template #default="obj">
<p>
{{obj}} //obj={ 'msg':'sss'} 传过来 slot 中的属性值
</p>
</template>
***************************************************************
这样可以实现数据之间的传递
<slot :msg="obj"></slot>
自定义指令
- 私有自定义指令
- 全局自定义指令
私有自定义指令
directives 节点声明私有自定义属性。
//调用时,使用 v-color 来调用自定义属性。
directives:{
color:{
bind(el){
el.style.clor = 'red'
}
}
}
//当指令第一次被绑定到元素上的时候,会立即触发 bind 指令,形参中的el 表示绑定到的那个 DOM 对象
自定义指令传递值
<p v-color="'red'"></p>
directives:{
color(){
bind(el,binding)
//obj 或者其他名字都可以
//binding.value 对象中包含了自定义属性中传递的值
//binding.expression 是表达式的值 "'red'"
el.style.color = binding.value
}
}
update 函数
bind 函数只会在创建时调用一次,当数据更新时,不会被触发。而 update 函数会在每次数据更新时调用。
directives:{
color:{
update(el,binding){
el.style.color=binding.value
}
}
}
bind 和 update 都要设置,bind在第一次绑定时被执行, update 在数据刷新时被执行。
函数简写
如果 bind 和 update 函数中的逻辑完全形同,则对象格式可以写为函数格式。
directives:{
color(el,binding){
el.style.color = binding.value
}
}
//相当于在 bind 和 update 中都写了一份
全局自定义指令
全局自定义指令需要通过“Vue.directive()”声明(放到 main.js 中):
Vue.directive('color',(el,binding)=>{
el.style.color=binding.value
}
})
axios全局挂载
把 axios 挂载到 vue 组件 原型对象上,这样不需要组件重复导入。同时,可以为 axios 对象设置 defaults.baseURL 属性,确立默认根目录地址。
// 全局配置 axios 的请求根路径
axios.defaults.baseURL = 'http://www.liulongbin.top:3006'
// 把请求根路径挂载到 axios中
Vue.prototype.$http = axios
// 今后在每个 .vue组件中发起请求,只需要 this.$http.get()即可
但是,这种方法不利于实现 api 的复用,不建议使用
前端路由
前端路由:Hash 地址和组件之间的对应关系
工作方式:
- 用户点击页面中不同的路由链接。
- 导致了 URL 地址栏中的 Hash 值发生了变化。
- 前端路由监听到了 Hash 地址的变化
- 前端路由把当前的 Hash 地址对应的组件渲染到浏览器中
可以结合动态组件使用
// 为了监听 hash值的变化
window.onHashChange = () => {
// 当hash 值变化时,便会调用该函数
//location.hash 可以获取当前的hash 值
switch (locatin.hash){
case '#/home':
this.comName = 'Home'
break
case...
...
}
}
vue-router
vue-router 是 vue.js 官方给出的路由解决方案。
安装
// 注意,由于版本之间兼容问题,vue@2.需要安装 vue-router 的@3.系列版本
npm i vue-router
创建路由模块
在 src 源代码目录下,新建 router/inex.js 路由模块:
// src/router/index.js 就是到项目的路由模块
// 导入 VUE 和vue-router 的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// 使用 Vue.use() 函数,把VueRouter 安装为 Vue 的插件
Vue.use(VueRouter)
// 创建路由的实例对象
const router = new VueRouter()
export default router
在main.js 中挂载路由模块;
import router from './router/index.js'
new Vue({
render: (h) => h(App),
// 在Vue 项目中,想要把路由用起来,必须把路由实例对象,通过以下方式挂载
// router: router 属性名和属性值相同,可以简写
router
}).$mount('#app')
在进行模块化导入时,如果给定的是文件夹,则默认导入这个文件夹下,名字叫做 index.js 的文件
应用
<!-- 只要在项目中安装和配置了 vue-router ,就可以使用 router-view 这个组件 -->
<!-- 作用:占位符 -->
<router-view></router-view>
***************************************************************
const router = new VueRouter({
// routes 是一个数组,定义“hash 地址”和“组件”之间的对应关系
routes: [
// 这里要省略‘#’
{ path: '/left', component: left },
{ path: '/right', component: right }
]
})
router-link 代替 a
//#在 router-link中不需要添加
<router-link to="/home"><router-link>
redirect 重定向
路由重定向:用户在访问地址 A 时,强制用户跳转到地址C
{ path:'/',redirect:'/home'}
嵌套路由
通过路由实现组件的嵌套展示,叫做嵌套路由
子组件嵌套实现
// 在子组件 left 中嵌套声明 router-link ,接下来只剩下 router/index.js 中的路径配置了
<router-link to="/right/tab1"> tab1 </router-link>
<router-link to="/right/tab2"> tab2 </router-link>
<hr />
<router-view></router-view>
通过 children 属性声明子路由规则
注意:子路由规则不要加斜线
const router = new VueRouter({
// routes 是一个数组,定义“hash 地址”和“组件”之间的对应关系
routes: [
// 这里要省略‘#’
{
path: '/left',
component: left
},
{
path: '/right',
component: right,
//重定向为 /right/tab1 使切换到 right 时,就会默认出现 tab1 组件
redirect: '/right/tab1'
children: [
{ path: 'tab1', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
},
// redirect 重定向
{ path: '/', redirect: '/left' }
]
默认子路由
{
path: '/right',
component: right,
// redirect: '/right/tab1',
children: [
// 默认子路由:如果 children 数组中,某个路由规则的 path 值为
// 空字符串,则这条路由规则,叫做“ 默认子路由”
{ path: '', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
},
***************************************************************
//此时要在默认组件对应的 router-link 中
动态路由匹配
把 Hash 地址中的可变的部分定义为参数项,从而提高路由规则的复用性。
在 vue-router 中使用英文的冒号 (:)来定义路由的参数项。
//动态路由以 : 进行声明,冒号后面是动态参数的名称
{ path:'/movie/:id',component : Movie}
现在的需求是,希望根据 id 的值来展示对应电影的详情信息。
解决方法一:
在组件实例对象中,有一个 router 属性,里面的 params 记录了路径中的 id 值
this.$route.params.id //取值即可
this.$route 是路由的“参数对象”
this.$router 是路由的“导航对象”
解决方法二
为当前路由开启 props 传参
//在当前路由中开启 props 传参
{ path: ':id', component: movie, props: true }
***************************************************************
//在组件中接收
//在数组中,属性名都以字符串的形式表示
props:['id']
扩展 query 和 fullquery
- 在 hash 地址中,/ 后面的参数项,叫做“路径参数”
- 在路由“参数对象”中,需要使用 this.$route.params 访问路径参数
eg:
/right/:id
- 在 hash 地址中,?后面的参数项,叫做“ 查询参数 ”
- 在路由“参数对象”中,需要使用 this.$route.query 来获取查询参数
eg:
/right?id='1'&age=20
在 this.$route.fullpath 中,包括了之后的查询参数,而同级的 path 属性不包括查询参数。
声明式导航 & 编程式导航
在浏览器中,点击链接实现导航的方式,叫做声明式导航。
eg:
- 标签,vue 项目中点击 都属于声明式导航。
在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航。
eg:
- 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航。
vue-router中的编程式导航 API
- this.$router.push(‘hash 地址’)
跳转到指定的 hash 地址,并创造一条历史记录。
- this.$router.replace(‘hash 地址’)
跳转到指定的 hash 地址,并替换到当前的历史记录
- this.$router.go(数值 n)
调用 $router.go() 方法,可以在浏览历史中前进和后退。
// go(-1)
//表示后退一层,如果后退的层数超过上限,则原地不动
this.$router.go(-1) //后退到之前的组件页面
$router.go 的简化用法
在实际开发过程中,一般只会用到前进和后退一层页面,所以 vue-router 提供了如下两个方法:
- $router.back() 后退一个页面
- $router.forward() 前进一个页面
导航守卫
导航守卫可以控制路由的访问权限。
全局前置守卫
每次发生路由的导航跳转时,都会触发全局前置守卫,从而实现对每个路由进行访问权限的控制。
// 创建路由实例对象
const router = new VueRouter({ ... })
// 调用路由实例对象的 beforeEach 方法,即可声明“全局前置守卫”
// 每次发生路由导航跳转的时候,都会自动触发 fn 这个“回调函数”
router.beforeEach(fn)
守卫方法的三个形参
全局前置守卫的回调函数中接收 3 个形参,格式为:
const router = new VueRouter({ ... })
router.beforeEach((to,from,next) =>{
// to 时即将要访问的路由信息对象
// from 时将要离开的路由信息对象
// next 是一个函数,调用 next() 表示放行,允许此次导航
})
next函数的三种调用方式
控制后台主页的访问权限
router.beforeEach((to,from,next) =>{
if(to.path === '/main'){
//获取本地储存的 token 值
const token = localStorage.getItem('token')
if(token) {
next
}else {
next('/login')
}
} else {
next()
}
})
在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航。
eg:
- 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航。
vue-router中的编程式导航 API
- this.$router.push(‘hash 地址’)
跳转到指定的 hash 地址,并创造一条历史记录。
- this.$router.replace(‘hash 地址’)
跳转到指定的 hash 地址,并替换到当前的历史记录
- this.$router.go(数值 n)
调用 $router.go() 方法,可以在浏览历史中前进和后退。
// go(-1)
//表示后退一层,如果后退的层数超过上限,则原地不动
this.$router.go(-1) //后退到之前的组件页面
$router.go 的简化用法
在实际开发过程中,一般只会用到前进和后退一层页面,所以 vue-router 提供了如下两个方法:
- $router.back() 后退一个页面
- $router.forward() 前进一个页面
导航守卫
导航守卫可以控制路由的访问权限。
[外链图片转存中…(img-58R2B2lg-1665064366052)]
全局前置守卫
每次发生路由的导航跳转时,都会触发全局前置守卫,从而实现对每个路由进行访问权限的控制。
// 创建路由实例对象
const router = new VueRouter({ ... })
// 调用路由实例对象的 beforeEach 方法,即可声明“全局前置守卫”
// 每次发生路由导航跳转的时候,都会自动触发 fn 这个“回调函数”
router.beforeEach(fn)
守卫方法的三个形参
全局前置守卫的回调函数中接收 3 个形参,格式为:
const router = new VueRouter({ ... })
router.beforeEach((to,from,next) =>{
// to 时即将要访问的路由信息对象
// from 时将要离开的路由信息对象
// next 是一个函数,调用 next() 表示放行,允许此次导航
})
next函数的三种调用方式
控制后台主页的访问权限
router.beforeEach((to,from,next) =>{
if(to.path === '/main'){
//获取本地储存的 token 值
const token = localStorage.getItem('token')
if(token) {
next
}else {
next('/login')
}
} else {
next()
}
})