D1
基础语法,指令
1.入门
1.为什么学习vue
答:vue是现在主流的学习框架,
vue提供了diff算法,
优异的视图渲染功能,提高了用户体验,
vue提供了优异的技术生态,极大地提高了前端开发效率
vue同时开发多个版本,包括市场主流版本和未来的使用趋势
2.什么是声明式渲染
答:数据在生命的同时就已经完成了在页面的渲染,这样声明即渲染的方式称为声明式渲染
2.基础语法
1.插值表达式,双花括号完成页面
2.指令是包含简单DOM操作功能的组件
3.vue提供了自己的指令
v-text 文本渲染指令
v-html 超文本渲染指令
v-once 单次渲染指令
v-bind 属性绑定指令 简写为 :bind
v-show v-if 条件渲染指令
v-for 列表循环指令
v-on 事件绑定指令
v-model 表单双向绑定指令
面试题:v-if v-show 区别
答:v-if v-show 都是条件渲染指令,不同的是,v-if通过条件控制的方式进行显示隐藏;v-show通过dom对象display的方式进行显示或隐藏;在项目使用中,一般频繁的显示隐藏使用v-show,保持较长时间的显示或隐藏一般使用v-if
D2
class样式 style样式 侦听器watch 过滤器filter(全局)filters computed 事件绑定 时间修饰符 事件对象
1.class绑定样式
1.字符串拼接方式
2.数组格式 数组格式中使用变量名
3.对象格式 对象格式中使用变量(样式名称)和变量名
注意:变量(样式名称中有-连接符时要使用双引号)
2.style样式绑定
1.可以直接使用style 样式名称:变量名称
2.变量为对象格式 :style="对象名称"
1.class样式绑定
1.字符串格式 :class="样式名称"
2.数据格式 :class=[ 变量名]
3.对象格式 :class={ 样式名:变量 } 注:当变量名中间有 - 时 需要加双引号
2. style样式绑定
1.对象格式 :style="{css名:变量}"
2.对象格式 :style="对象名"
3.侦听器
1.基本使用
watch: { 被监听的变量: function(oldVal, newVal) { 当变量中的数据一旦发生改变,自动执行的函数 } }
2.深度监听
loginForm: { handler(newVal, oldVal){ console.log(newVal.username, oldVal.username, "对象监听器生效") }, deep: true // 开启深度监听,可以监听对象内部的数据是否发生变化 }
4.计算属性
computed: { 计算属性名称: 函数() { 自动运算并返回结果 } }
面试题1.简要描述计算属性和普通函数的区别?
答:计算属性自动监听参与运算的多个变量,当变量发生改变时就计算并返回结果,如果变量没有发生改变就返回上次运算的结果。普通函数不管变量有没有发生改变都会调用函数并计算
面试题2:描述计算属性和侦听器的区别?
答:计算属性可以监听多个变量,并缓存变量的运算结果
侦听器智能监听一个变变量的变化
5.过滤器
页面的数据格式可以通过固定的函数进行规范,对展示的数据进行格式规范
1.全局过滤器
Vue.filter('过滤器名称', function(val) { 格式处理函数 })
2.局部/私有过滤器
filters: { '过滤器名称': function(val) { 格式处理函数 } }
6.事件处理
1.事件绑定 v-on: 简写 @click=" "
7.事件对象
1.兼容性处理
e=e || window.event
2.相对于浏览器窗口的位置
clientX clientY
3.先对于文档的位置
PageX PageY
4.相对于点击元素的位置
offsetX offsetY
8.事件修饰符
1.阻止事件冒泡
.stop
2.阻止事件默认行为
.prevent
3.事件的触发:捕获阶段、触发阶段、冒泡阶段,触发机制默认是冒泡触发;Vue提供了一种操作可以让事件在捕获阶段直接触发
.capture
4.单次加载
.once
5.自身加载
.self
6.
.passive
9.按键修饰符
内建按键修饰符
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
系统按键修饰符
.ctrl
.alt
.shift
.meta
10. 表单元素
表单元素的绑定使用 v-model 实现双向绑定
注意:文件域 需要使用ref 通过 this.refs.name.files[0] 获取文件与中选择的二进制文件
D3
MVVM 组件(全局组件,局部组件,动态组件,组件缓存) 生命周期
1.MVVM
mvvm时视图和逻辑之间的数据相互关联的关系,专业术语是数据双向绑定,MVVM的底层是数据劫持 即object.defineproperty()
面试题:请简单描述一下
mvvm
和mvc
你是怎么理解的?答:
mvvm
和mvc
都是程序开发中的一种编程的设计方式,是一种设计思想
mvvm
主要体现在前端应用中,将页面结构和数据模型进行分离,同时通过中间模块vm
实现数据的双向绑定,提高数据的加载和渲染效率,同时让开发人员的精力主要集中在业务处理上而不是技术同步数据上,提高开发的效率
mvc
主要体现在后端应用中,将客户端请求和对应的业务模块进行规则映射,根据不同的请求分发到不同的业务模块完成数据处理,可以在固定的模式下完成复杂的业务功能处理的软件结构,提高后端应用开发效率
2.组件
1.全局组件
Vue.component('组件名称', { template: '组件的页面结构', data() { // 组件中的data选项,必须是一个返回数据对象的函数;不能直接是一个对象 return { title: '首页' } }, methods: {}, filters: {}, ... })
2.局部组件
外部创建组件,在一个vue实例 components中 挂在组件
3.动态组件
<!-- 动态组件,动态切换不同的页面-->
<component :is="page"></component>
<ul class="nav">
<li :class="{active:page==='Home'}" @click="page='Home'">首页</li>
<li :class="{active:page==='List'}" @click="page='List'">列表</li>
<li :class="{active:page==='Ucenter'}" @click="page='Ucenter'">用户中心</li>
<li :class="{active:page==='About'}" @click="page='About'">关于我们</li>
</ul>
4.组件缓存
keep-alive
用在动态组件外 include exclude 控制缓存的组件
3.生命周期
1.生命周期包括
beforecreate() created() 创建实例
beforemount() mounted() DOM加载 能访问
beforeupdate() updated() 数据修改
beforedestory() destoryed() 组件销毁
activated()显示
、deactivated()隐藏
注意:created() 能访问data 不能访问DOM
mounted() 能访问data 能访问DOM
D4
vue-cli 组件传值 插槽(匿名,具名,作用域)ref
1.脚手架
1.什么是脚手架
是一种快捷构建的工具,是Vue
技术生态链中的一部分,Vue CLI
是一个基于 Vue.js
框架,执行快捷项目构建的工具插件,
官方目前共提供了两种脚手架模块
1、
vue-cli
:适用于vue1.x、vue2.x
项目构建2、
@vue/cli 4.x
:适用于vue2.x、vue3.x
项目构建3、
@vue/cli 5.x
:更加侧重于vue3.x
项目构建
2.安装脚手架步骤
1.检查脚手架版本
$ npm view @vue/cli versions
2.安装指定版本的脚手架:
$ npm install @vue/cli@4 -g
3.通过命令创建vue2/3
版本项目
打开命令行,执行命令创建项目
$ vue create 项目名称
4.命令执行后,选择Vue2
版本
5.启动项目
$ npm run serve
2.SFC页面的基本使用
1.动态组件切换
<!-- 动态组件,动态切换不同的页面-->
<component :is="page"></component>
<ul class="nav">
<li :class="{active:page==='Home'}" @click="page='Home'">首页</li>
<li :class="{active:page==='List'}" @click="page='List'">列表</li>
<li :class="{active:page==='Ucenter'}" @click="page='Ucenter'">用户中心</li>
<li :class="{active:page==='About'}" @click="page='About'">关于我们</li>
</ul>
3.组件传值
1.父传子
自定义属性
1.子组件中通过自定义属性 props:[ '自定义属性名' ]
2.父组件引入自组建后 :自定义属性名=“父组件的数据”
//子组件
<template>
<div>
<p>来自父组件的数据:{{ mfp }}</p>
</div>
</template>
<script>
export default {
// 自定义属性,当前组件通过mfp,可以接受来自父组件的数据
props: ['mfp']
}
</script>
//父组件
<template>
<div>
<!-- 使用子组件 -->
<Child1 :mfp="msgFromParent"/>
</div>
</template>
<script>
// 引入子组件
import Child1 from './child/Child1.vue'
export default {
data() {
return {
msgFromParent: '父组件数据'
}
},
components: {
// 注册子组件
Child1
}
}
</script>
2.子传父
自定义事件
1.子组件中 this.$emit("自定义事件名称",子组件数据)
2.父组件中 引入子组件 @子组件中自定义事件名称 =接受数据的事件名
//子组件
<template>
<div>
<p>当前组件数据:{{ msgFromChild }}</p>
<button @click="sendData">发送数据给父组件</button>
</div>
</template>
<script>
export default {
data() {
return {
msgFromChild: 'Child数据'
}
},
methods: {
sendData() {
// 触发一个自定义事件
this.$emit('devent', this.msgFromChild)
}
}
}
</script>
//父组件
<template>
<div>
<p>子组件的数据:{{ mfc }}</p>
<Child1 @devent="getData"/>
</div>
</template>
<script>
// 引入子组件
import Child1 from './child/Child1.vue'
export default {
data() {
return {
mfc: '等待接受数据'
}
},
methods: {
getData(dat) {
// 事件中接受子组件传递的数据
this.mfc = dat
}
},
components: {
Child1
}
}
</script>
3.组件之间传值
事件中心传值
1.创建事件中心
//实践中心
import Vue from "vue"
const vm = new Vue()
// 导出一个空白对象
export default vm
2.传数据的组件中
①导入事件中心
②vm.$emit("自定义事件名称",组件内要发送的数据)
//发送数据组件
<template>
<button @click="sendDataToOther">
发送数据给其他组件
</button>
</template>
<script>
import vm from '../../utils/events'
export default {
data() {
return {
msgFromChild: 'Child1数据'
}
},
methods: {
sendDataToOther() {
// 事件中心通过自定义事件 发送数据
vm.$emit('oevent', this.msgFromChild)
}
}
}
</script>
3.接收数据的组件中
①导入实践中心
②vm.$on("自定义事件名称",dat=>{ 操作数据的函数 })
//接受数据组件
<template>
<div>
<p>接受其他组件的数据:{{ mfo }}</p>
</div>
</template>
<script>
// 导入事件中心
import vm from '../../utils/events'
export default {
data() {
return {
mfo: '等待接受其他组件的数据'
}
},
created() {
// 事件中心监听其他组件是否发送了数据
vm.$on('oevent', dat => {
// 将数据保存起来
this.mfo = dat
})
}
}
</script>
4.插槽
1.匿名插槽
1.子组件插槽中 <slot></slot>
2.父组件中 引入插槽并使用
//插槽组件中
<template>
<div class="s-container">
<h3>功能组件</h3>
<div>
<!-- 插槽:匿名插槽 -->
<slot></slot>
</div>
<div>
公共底部内容
</div>
</div>
</template>
//父组件
<template>
<div class="list-container">
<h2>列表页面</h2>
<!-- 1、没有插入内容的子组件-->
<DataList/>
<!-- 2、新闻数据-->
<DataList>
<!-- 给匿名插槽添加内容 -->
<ul>
<li v-for="news in newsList" :key="news.id">{{ news.content}} </li>
</ul>
</DataList>
<!-- 3、通知内容 -->
<DataList>
<ol>
<li v-for="notice in noticeList" :key="notice.id">{{notice.content}}</li>
</ol>
</DataList>
<!-- 4、通知内容-->
<DataList>
<div v-for="advice in adviceList" :key="advice.id">{{advice.body}}</div>
</DataList>
</div>
</template>
<script>
import DataList from '../components/DataList.vue'
export default {
data() {
return {
newsList: [
{id: 2, content: "苹果系统升级"},
{id: 1, content: "全球互联网大会"}
],
noticeList: [
{id: 1, content: "周末大家一定要加强练习"}
],
adviceList: [
{id: 3, body: "完成周内的晨测"},
{id: 2, body: "梳理一周的学习内容"},
{id: 1, body: "完成项目任务"}
]
}
},
components: {
DataList
}
}
</script>
<style>
</style>
2.具名插槽
1.插槽组件内 在匿名插槽基础上加上name 即为具名插槽
//具名插槽组件
<template>
<div class="d-container">
<!-- 弹窗标题-->
<h3><slot name="title"></slot></h3>
<!-- 弹窗内容-->
<p><slot></slot></p>
<!-- 弹窗操作-->
<div><slot name="opera"></slot></div>
</div>
</template>
<script>
export default {
}
</script>
<style>
.d-container{
width: 300px;
height: 100px;
box-shadow: #000 0 0 2px;
}
</style>
2.负组件中引入插槽组件
3.template 标签内 使用
<template v-slot:title> 警告信息对话框 </template>
<template #title> 确认信息 </template>
<template slot-scope:title> 危险操作提示 </template>
//父组件
<template>
<div class="list-container">
<!-- 具名插槽-->
<Dialog>
<template v-slot:title>
警告信息对话框
</template>
<template v-slot:opera>
<button>确认</button>
</template>
<p>
警告信息的提示内容
</p>
</Dialog>
<Dialog>
<template #title>
确认信息
</template>
<template #opera>
<button>编辑</button>
<button>确认</button>
</template>
<p>
确认信息的提示内容
</p>
</Dialog>
<Dialog>
<template slot-scope:title>
危险操作提示
</template>
<template slot-scope:opera>
<button>确认操作</button>
</template>
<p>
微信操作信息的提示内容
</p>
</Dialog>
</div>
</template>
<script>
import DataList from '../components/DataList.vue'
import Dialog from '../components/Dialog.vue'
export default {
data() {
return {...},
components: {
DataList,
Dialog
}
}
</script>
<style>
</style>
3.作用域插槽
1.作用域插槽内 :rows="任意信息"
//作用域插槽
<template>
<div class="sc-container">
<ul>
<li v-for="news in newsList" :key="news.id">
<p>
<span>{{ news.id }}</span>
<span>{{ news.content }}</span>
<span>
<!-- 需要插槽给父组件传递正在操作的数据 -->
<!-- 通过插槽的属性进行传递:作用域插槽-->
<slot :row="news"></slot>
</span>
</p>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['newsList']
}
</script>
<style scoped>
.sc-container{
padding:20px;
box-shadow: #000 0 0 2px;
width: 400px;
}
</style>
2.父组件内 template 标签内 要使用 v-slot="props" 或 slot-scope="attr" 接受插槽内的数据
//父组件
<!-- 作用域插槽-->
<ScopeList :newsList="newsList">
<template v-slot="props">
<button @click="checkDetail(props)">查看详情</button>
</template>
</ScopeList>
<ScopeList :newsList="newsList">
<template v-slot="attr">
<!-- <template slot-scope="attr"> -->
<button @click="edit(attr)">编辑</button>
<button @click="del(attr)">删除</button>
</template>
</ScopeList>
5.ref
1.ref可以获取文件域DOM对象
2.ref可以获取子组件的对象
3.ref可以实现父组件给子组件传值和设置子组件的数据
父组件引入子组件并使用ref="child"
this.$refs.child.data
D5
props 子组件数据传入父组件数据时 props的一些外加操作
组件边界情况 过渡动画 DOM加载延时
1.data是一个函数,需要用return
2.props
1.父组件数据传给子组件时 子组件中用props用来接收父组件中的数据
2.props可以自定义数据的类型
3.可以指定默认值,如果是数组或者String需要通过函数指定默认值
4.porps父组件向子组件传值时,不能直接操作父组件的数据,子组件中可以在data中对父组件传过来的数据重新赋值;如果数据参与运算,可以在子组件computed中进行运算
3.组件边界处理
1.$root 一般设置全局颜色模式
2.$parent (不推荐) 对父组件内数据进行操作
3.ref 父组件直接操作子组件内的数据
4.$forceUpdate 对数组数据的添加 可以通过push 或者index进行添加,但index数据底层进行操作,vue2中不能双向绑定,因此需要forceUpdate
5. 对数组数据的添加 也可以通过 this.$set("对象",索引,数据)
或者 vue.set("对象",索引,数据)
4.过渡动画
1.下方样式会针对组件中出现的所有包含在<transition></transition>
标签的单个元素产生动画效果
v-enter{} 进入前 .v-enter-active{} 进入中 .v-enter-to{} 进入后 .v-leave{} 离开前 .v-leave-active{} 离开中 .v-leave-to{} 离开后
5.DOM延时加载
1.setTimeout
2.Vue.nextTick()
以及this.$nextTick()
都是等待当前组件DOM
更新结束后再去执行的函数,一般用于等待DOM
更新结束后执行对应的操作,通过回调函数实现
nextTick()
底层工作原理等待当前组件mounted()
执行结束再去执行回调函数
- 子组件在某个单击事件发生时,加载子组件;父组件
nextTick()
会等待自组件加载完成后再去执行代码吗?nextTick()
一般会在created()
和mounted()
生命周期中使用
D6
1.路由
1.表示根据映射规则,切换到不同的业务模块,执行不同的业务流程
足有在项目中的作用是,根据用户url请求路径,进入不同的页面
2.为什么要使用路由
1.组件切换页面,无法通过浏览器url切换到不同页面
2.动态组件中的自定义属性和监听自定义时间比较繁琐
3.页面中出现二级路由时,动态组件的切换方式不太友好
2.路由的下载安装
1.创建项目
vue create app01
2.进入项目,安装路由模块
cd app01
npm i vue-router@3 -S
3.配置路由规则
1.创建路由视图组件 在page下
2.配置路由规则 在src/router/index.js
// 引入依赖
import Vue from "vue"
import VueRouter from "vue-router"
// 挂载插件
Vue.use(VueRouter)
import Home from '../pages/Home'
import List from '../pages/List'
import Ucenter from '../pages/Ucenter'
import About from '../pages/About'
// 创建映射规则
const routes = [
{
path: '/home', // 配置用户访问url路径
component: Home // url路径要访问的页面组件
},
{
path: '/list',
component: List
},
{
path: '/ucenter',
component: Ucenter
},
{
path: '/about',
component: About
}
]
// 创建路由对象
const router = new VueRouter({
routes
})
// 导出路由
export default router
3.在main.js中引入路由,注册路由
4.在视图中添加路由导航链接并添加router-view路由视图
<div id="app">
<!-- 路由导航:本质上就是一个超链接-->
<ul>
<!-- vue-router提供了一种固定语法,用于添加导航链接-->
<li><router-link to="/home">首页</router-link></li>
<li><router-link to="/list">列表</router-link></li>
<li><router-link to="/ucenter">用户</router-link></li>
<li><router-link to="/about">关于我们</router-link></li>
</ul>
<!-- 指定显示路由组件的位置 -->
<router-view></router-view>
</div>
4.导航高亮
1.直接修饰默认样式,在App.vue中添加高亮样式
.router-link-active{
display: block;
background: orangered;
color: white;
}
2.自定义高亮样式
在src/router/index.js中
const router = new VueRouter({
routes,
linkActiveClass: 'active' // 指定高亮样式名称
})
在App.vue中添加高亮样式
/* .router-link-active{ */
.active{
display: block;
background: orangered;
color: white;
}
4.严格匹配
因为路径的包含关系,导致只要有 / 的导航一直高亮
解决方案:路由链接中添加 exact
5.路由别名
某个组件可以通过多个路径进行访问,可以使用路别名的方式,使一个组件拥有多个名字进行访问
语法:在路由中添加 alias
6.路由重定向
用户访问某个路径时,可以将访问的路径重新指向了另外一个路径进行访问
语法:在路由中添加 redirect
面试题:路由别名和路由重定向的区别?
(总)路由别名、路由重定向都是路由模块中,为了提高用户体验将用户的一个空白请求路径映射到指定路由规则和页面组件的一种操作方式。
(分)别名方式相当于给一个路由规则添加了一种访问路径,和本身的路径一起都可以访问到当前组件;用户发起请求的
URL
地址从始至终都没有发生变化(分)重定向相当于将用户发起的请求
URL
地址进行了重写,转换成指定的另一个URL
重新发起请求获取页面组件;用户发起的请求URL
地址发生了改变(总)项目中我们别名的方式偶尔使用,但是使用重定向比较多一些
7.路由匹配模式
history 和 hash
8.命名路由
如果遇到路径变更的情况,传统的路径修改起来工作量大,且比较繁琐,明明路由就解决了这个问题,只需要修改路由中的路径就可以实现整个项目的修改
1.路由中添加 name:
2.路由连接中 router-link :to="{name:'路由中name的值'}"
9.命名视图
如果不同页面中,比如页头,页脚的页面内容不同,可以使用命名视图
1.创建组件
2.在显示组件的地方 用
<router-view name="xxx"> </router-view>
<router-view > </router-view>
3.引入路由,并将原路由中的component修改为 components
4.components内部加入 xxx:命名视图
default:默认视图
10.嵌套路由
在对应路由规则内加入children,在children中加入二级路由组件,其他与一级路由一致
11.编程式导航
1.路由对象
$route 当前组件的路由对象
$router 全局路由组件对象 包含当前路由对象 描述了整个项目的路由规则
2.编程式导航
①后退 this.$router.back()
②前进 this.$router.forward()
③回到首页 this.$router.push({name:" "})
④回到首页 this.$router.replace({name:" "})
面试题:编程式导航中 push 与replace 的区别
答:push保留访问记录,replace 不保存访问记录
D7
1.动态路由
1.动态路由主要是路径中动态包含参数 比如: role/1 或者 role/id=1,name=2
2.动态路由传递参数
①创建组件并在路由规则中添加
②在路由规则path中添加
{
name: 'userdetail',
path: '/udetail/:id/:name', // url路径中包含了一个id变量占位的数据,
component: UsersDetail // 对应显示的组件
}
③在要展示的页面中添加 router-link 并配置参数
<router-link :to="'/udetail/' + user.id + '/' + user.name">查看详情</router-link>
<!-- 用户点击时,请求链接:/udetail/1 -->
<router-link :to="{name: 'userdetail', params: {id: user.id, name: user.name}}">查看详情</router-link>
<!-- 用户点击时,请求链接:/udetail/2 -->
④在对应页面中展示数据
<template>
<p>
标签中直接写接受的数据:{{ $route.params.id }} {{ $route.params.name }}
</p>
</template>
<script>
export default {
created() {
// 声明周期中接受数据
console.log(this.$route.params.id, this.$route.params.name)
}
}
</script>
⑤解耦合展示数据
路由规则中,设置 props:true
展示数据页面中用props接收数据,并直接使用解耦合的数据
3.查询字符串传递参数
①在路由规则中配置
import MenusDetail from '../pages/MenusDetail'
...
{
name: 'main',
path: '/main',
component: Main,
children: [
...
{
name: 'menusdetail',
path: '/mdetail', // 这里配置路由时,忽略?后面拼接的查询字符串
component: MenusDetail
}
]
}
②在要展示的页面发起链接
<router-link :to="'/mdetail?id=' + menu.id + '&name=' + menu.name">
查看菜单详情
</router-link>
<!-- 用户点击后,发起的请求:/mdetail?id=1&name=首页 -->
<router-link :to="{name: 'menusdetail', query:{id: menu.id, name: menu.name}}">
查看菜单详情
</router-link>
③接收数据
<template>
<p>
页面接受数据: {{ $route.query.id }} {{ $route.query.name }}
</p>
</template>
<script>
export default {
created() {
console.log("生命周期中接受数据:", this.$route.query.id, this.$route.query.name)
}
}
</script>
④路由规则中解耦合
{
name: 'menusdetail',
path: '/mdetail',
component: MenusDetail,
// 将路由组件传递的查询字符串参数,解析成当前组件的自定义属性传递
props: route=> ({id: route.query.id, name:route.query.name})
}
⑤在要展示的页面直接使用解耦合的数据
4.路由懒加载
直接在路由规则中将原来引入页面的方式换为 ()=>import("页面路径")
5.路由元数据
1.在路由规则中添加元数据
{
name: 'home',
path: '/home',
component:Home,
meta: { // 路由元信息/数据
title: '系统首页'
}
}
2.在页面created()生命周期中获取元数据并使用
created() {
let title = this.$route.meta.title // 获取元数据
document.title = title // 设置页面标题
}
6.混入
类似于 小程序behavior
1.抽取公共部分在plugins文件中
// 声明 一个 混入模块
let mixins = {
created() { // 公共生命周期
// 如果当前路由对象,包含meta元数据,获取元数据上的title数据
if(this.$route.meta && this.$route.meta.title){
// 获取标题数据
let title = this.$route.meta.title
// 给网页设置标题
document.title = title
}
}
}
// 导入混入模块
export default mixins
2.在main.js文件中引入mixin文件并挂载
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import mixins from './plugins/mixins' // 导入混入模块
Vue.config.productionTip = false
Vue.mixin(mixins) // 添加公共混入模块
new Vue({
router,
render: h => h(App),
}).$mount('#app')
7.滚动行为
1.大部分场景
const router = new VueRouter({
routes,
scrollBehavior: (to, from, savePosition) => {
console.log(to, from, savePosition)
// 常规的一种做法,直接禁止滚动行为,每个页面切换时自动回到顶部
return {x: 0, y:0}
}
})
2.部分特殊页面
const router = new VueRouter({
routes,
scrollBehavior: (to, from, savePosition) => {
console.log("to组件:", to)
console.log("from组件:", from)
console.log("位置:", savePosition)
// 常规的一种做法,直接禁止滚动行为,每个页面切换时自动回到顶部
// return {x: 0, y:0}
// 针对特殊页面,独立保存它们的滚动位置
// 1、从to/form获取通过访问路径 判断是哪个路由页面
if(from.fullPath === "/home") {
// 记录位置
let y = savePosition && savePosition.y
// let y = savePosition.y // bug
localStorage.setItem('homey', y)
}
if(to.fullPath === "/home"){
// 获取位置
let y = localStorage.getItem('homey')
if(y) {
return {x: 0, y: parseFloat(y)}
}
}
// 其他页面路径
return {x: 0, y: 0}
}
})
8.导航守卫
1.导航守卫分类
- 全局前置守卫:
beforeEach()
- 全局解析守卫:
beforeResolve()
- 全局后置钩子:
afterEach() 路由规则模块
- 路由独享守卫:beforeEnter() 路由规则中 和path 同级
- 组件内守卫: 页面组件中,和页面中data同级
beforeRouteEnter()
,导航路由进入组件守卫beforeRouteUpdate()
,导航路由组件数据更新守卫beforeRouteLeave()
,导航路由离开组件守卫
2.导航守卫执行顺序
brforeEach() => brforeEnter() => beforeRouteEnter() =>beforeResolve() =>afterEach()
用户点击页面上的某个导航链接,导航被触发。 在失活/隐藏的组件里调用 beforeRouteLeave 守卫。 调用全局的 beforeEach 守卫。 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。 在路由配置里调用 beforeEnter。 解析异步路由组件。 在被激活的组件里调用 beforeRouteEnter。 调用全局的 beforeResolve 守卫 (2.5+)。 导航被确认。 调用全局的 afterEach 钩子。 触发 DOM 更新。 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
9.过渡动效
1.默认动画
原过渡动画 理解基础上,将transition 标签内部的 component 换为 router-view标签
2.自定义动画
transition 添加name
3.动态绑定动画属性
transition 添加 :name="xxx" 动态改变name的值就可以实现
4.vue-router4中 通过插槽实现动画效果
<router-view v-slot="prop">
<transition name="fade">
<component :is="prop.Component" />
</transition>
</router-view>
<router-view v-slot="{ Component }">
<transition name="fade">
<component :is="Component" />
</transition>
</router-view>
10.数据获取
在这个请求过程中前端页面如何显示视图,提高用户体验?一般会显示类似**加载中...**视图,提醒用户正在获取获取
1.页面视图
<template>
<div class="post">
<div v-if="loading" class="loading">
加载中...
</div>
<div v-if="error" class="error">
请求失败!
</div>
<div v-if="post" class="content">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
</div>
</template>
2.脚本处理
export default {
data () {
return {
loading: false,
post: null,
error: null
}
},
created () {
// 组件创建完后获取数据,
// 此时 data 已经被 observed 了
this.fetchData()
},
watch: {
// 如果路由有变化,会再次执行该方法
'$route': 'fetchData'
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
// replace getPost with your data fetching util / API wrapper
getPost(this.$route.params.id, (err, post) => {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.post = post
}
})
}
}
}
D8
1.原生ajax
① 创建实例 let http = new XMLHttpRequest()
②连接服务器 http.open("GET",“http://www.example.com”,true)
③处理相应数据 http.onreadystatechange=function(){
if(http.readyState===4&&http.status===400){
alert(" ")
}
}
④发送请求 http.send()
2.jQuery Ajax
$.ajax({
url: 'http://www.example.com',
type: 'POST',
data: {username:"admin", password: "123"},
success: function(res) {},
error: function() {}
})
$.get(url, {}, function() {})
$.post(url, {}, function() {})
...
注意事项:
jQuery ajax
封装的非常完善,但是vue
项目中不推荐使用jQuery
jQuery
核心操作,主要简化的是DOM
操作,提高编码效率Vue
不推荐DOM
操作,Vue
推荐使用更加单纯的axios
完成异步请求处理
3.axios ajax
1.什么是axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js项目 中
Promise
封装,将异步操作封装成了可以执行同步操作的功能函数XHR
封装,底层封装了原生JS
异步对象
2.axios如何发送请求
// 底层
axios.request({配置选项})
// get请求
axios.get('http://example.com'
{params: {keyword: '搜索字符串'}})
.then(res=> console.log(res))
.catch(res => console.log("请求错误"))
// post请求
axios.post('http://example.com',
{username: 'admin', password: '123'})
.then(res=> console.log(res))
.catch(res => console.log("请求错误"))
// put请求
// delete请求
// 其他请求...
3.axios请求配置选项
url,
用于创建请求时使用的方法 methods,
url前缀 baseURL,
请求头 headers,
请求发送的参数 params,
作为请求体发送的数据 data,
请求超时的毫秒数 timeout,
4.请求和响应拦截
// 请求拦截器
axios.interceptors.request.use( request => {
// 拦截到请求,针对请求进行处理
return request
})
// 响应拦截器
axios.interceptors.response.use( response => {
// 拦截到响应数据,针对响应数据进行处理
return rseponse
})
面试题:axios
拦截器和导航守卫的区别
- 拦截器,拦截的是前端项目发送给数据接口的请求
- 导航守卫,拦截的是页面切换路由请求
5.响应选择
axios针对服务器返回的数据 data
服务器相应的状 status
4.axios运行项目
参考 :days03~18Vue/days08请求和UI/note/days08随堂笔记.md · 大牧莫邪/bz2107web - Gitee.com
1.vue create app01
2.npm i axios -S
5.UI库
1.安装
npm i element-ui -S
2.项目中配置
main.js中
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui' // 引入ui模块
import 'element-ui/lib/theme-chalk/index.css' // 引入主题文件
Vue.use(ElementUI) // 挂载
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
D9
1.vue3
1.创建vue3项目
1.创建项目
$ npm init vue@latest2.$ npm run dev
3.访问网址 http://localhost:3000
2.选项式开发
选项式组件与普通vue2差不多,区别是vue2中只有一个根标签,选项式组件开发中可以有多个根标签
3.组合式开发
①组合式开发也可以有多个根标签
②组合式开发中,script标签中要添加setup属性
③声明响应式数据时要引入对应依赖模块 relative 和 ref
// 1、引入声明响应式数据的依赖模块
import { ref } from 'vue';
// 2、声明一个响应式数据
const counter = ref(0)
④声明操作函数
// 3、声明操作函数
const increment = () => {
counter.value ++
}
const decrement = () => {
counter.value --
}
4.组合式开发语法强化
组合式开发中 数据 加入ts语法后 为强类型语言
引入Ref 类型依赖模块
通过name 设置数据的类型
<template>
<h3>组合式语法 + TypeScript</h3>
<p>数据:{{ name }}</p>
<button @click="changeName">修改数据</button>
</template>
<script setup lang="ts">
import {ref} from "vue"
import type {Ref} from "vue"
// 声明一个响应式数据
const name:Ref<string|number> = ref('大牧')
// 操作函数
const changeName = () => {
name.value = 20
}
</script>
<style>
</style>
5.小总结:项目开发的时候,选择选项式开发?选择组合式开发?
理论上来说,选项式
API
和组合式API
并没有太大的运行原理的区别,差异化的东西基本都集中在语法上;官方并没有非常明确的推荐
- 如果开发
Vue2.x
升级项目:推荐使用选项式语法直接升级,技术成本最低- 如果开发全新
Vue3.x
项目:推荐使用组合式语法 +TypeScript
语法支持
6.响应式数据
可以通过relatve,和 ref声明 ref本质也是通过relative来包装声明的 ref 的数据本质为: {value: 1}
①使用relative声明的数据需要用对象( 即: user.name )的方式在组件中渲染 函数修改数据时也是使用( 即: user.name )进行数据的修改
②使用ref声明的数据 组件中渲染时只需要使用生声明的方式 但修改数据是需要 xxx.value的形式
2.监听器
支持侦听一个数值 或者 对象中的某个属性 或者一个对象和多个,默认深度监听
// 声明一个页码
const page = ref(0)
// 声明一个侦听器
watch(page, (newValue, oldValue) => {
console.log(newValue, oldValue, "页码改变,获取新数据")
})
const pageObj = reactive({value: 0})
// 深度监听
// watch(pageObj, (newVal, oldVal) => {
// console.log(newVal.value, oldVal.value, "深度监听")
// })
// 监听某个属性
watch(() => pageObj.value, (newVal, oldVal) => {
console.log(newVal, oldVal, "深度监听")
})
// 数组方式,监听多个变量的变化
// watch([page, () => pageObj.value], (newVal, oldVal) => {
// console.log(newVal, oldVal, "深度监听")
// })
3.计算属性
1.引入computed
import { reactive, computed } from 'vue';
2.用一个变量接受计算属性,在组件中使用
const library = reactive({
author: '杰克·罗琳',
books: []
})
// 计算属性的声明方式
const booksCnt = computed(() => {
return library.books.length
})
4.vue3.x生态
1.vue3项目中vuex已经被淘汰 ,由Pinia替代
2.界面组件库
vue2中的组件库为 Element-ui,vue3项目中的组件库为
Element-plus
:界面组件库Vant
:移动端组件库
3.main.ts中的设置
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router' // 引入路由模块
const app = createApp(App)
app.use(createPinia())
app.use(router) // 挂载路由模块
app.mount('#app')