数据驱动视图:
双向数据绑定:
在网页中,form 表单负责采集数据,Ajax 负责提交数据。
注意:数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例)
MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。
ViewModel
作为 MVVM 的核心,是它把当前页面的数据源
(Model)和页面的结构
(View)连接在了一起。
当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构。
当单元素的值发生变化表时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中
第一个Vue程序:
导入开发版本的 Vue.js
创建 Vue实例对象,设置el
属性和data
属性
使用简洁的模板语法把数据渲染到页面
上
基本代码与 MVVM 的对应关系
day.js 快速日期格式化
解决格式化Vue文件时 逗号、分号问题 :
在根目录中,添加一个 .prettierrc.json
配置文件,写入:
{
"singleQuote": true,
"semi": false,
"trailingComma": "none"
}
一劳永逸方法:
在电脑上的用户目录下新建一个 .prettierrc.json
文件,同理上面的参数。
在 **Vs code **的 setting.json
中添加配置路径:
// 设置 配置文件,解决格式化vue文件时逗号、分号爆红问题
// 这里注意配置路径,记得使自己计算机上的用户名
"prettier.configPath": "C:/Users/mi/.prettierrc.json",
解决格式化时函数括号前的空格问题 :
解决办法一、在当前项目里的 .eslintrc.js
里 rules 规则里添加忽略加空格爆红提示 :
'space-before-function-paren':['error','never']
解决办法二: 使用 Prettier now
插件可以解决此问题。
v-text
设置标签的内容, 默认写法会 覆盖元素内部原有的内容 , 内部支持写表达式。{{ }}
插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!v-html
指令的作用:可以把带有标签的字符串,渲染成真正的 HTML 内容!注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!
v-bind:
指令,为元素的属性动态绑定值;:
在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:
这是一个 div
使用 v-bind 在元素绑定时希望内传入 Number数值时,避免被解析成字符串
v-on:
简写是 @
语法格式为:
count的值是:{{ count }}
methods: {
add() {
// 如果在方法中要修改 data 中的数据,可以通过 this 访问到
this.count += 1
}
}
在事件绑定时,会有一个原生DOM 的事件对象 e,如果事件对象传入参数,默认的事件对象会被覆盖。
$event
的应用场景:如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event。
例如:
// 点击按钮让count值 递增,绑定点击事件并传参
methods: {
add(n, e) {
// 如果在方法中要修改 data 中的数据,可以通过 this 访问到
this.count += 1
}
}
事件绑定期间非常好玩的一个东西,对事件的触发进行控制。
.prevent
链接
.stop
事件修饰符
说明
.prevent
阻止默认行为
(如:阻止a链接的跳转,阻止表单提交等)
.stop
阻止冒泡事件
.capture
以捕获模式触发当前的事件处理函数
.once
绑定的事件只触发一次
.self
只有在event.target 是当前元素自身时触发事件处理函数
按键
键码值
使用
Enter
13
.enter
Tab
9
.tab
Delete
46
.delete (捕获“删除”和“退格”按键)
Esc
27
.esc
BackSpace
8
.space
Up Arrow
38
.up
Left Arrow
37
.left
Right Arrow
39
.right
Dw Arrow
40
.down
1.自定义其他的按键别名:
Vue.config.keyCodes.f6 = 118
// 只有单击f6键才会触发xxx的回调
2.多个按键一并触发该事件
@keyup.ctrl.enter="XXX" // 按下ctrl和enter才触发事件执行
【案例应用】:表单输入后按回车键添,按esc则清空表单
在不操作 DOM 的前提下,实现表单元素和数据的双向绑定。
v-model 指令的修饰符
修饰符
作用
示例
.number
自动将用户输入值转为 Number
number=“age” />
.trim
去除首尾空白字符
trim=“msg” />
.lazy
表单输入后失去焦点时更新页面数据,而非实时更新
lazy=“msg” />
input 输入框
type=“text”
type=“radio”
type=“checkbox”
type=“xxxx”
textarea
select
条件渲染指令用来辅助开发者按需控制 DOM 的显示与隐藏。
v-show
的原理是:动态为元素添加或移除style= " display: none; " 样式,从而控制元素的显示与隐藏。
v-if
的原理是:每次动态创建或移除 DOM 元素,实现元素的显示和隐藏。
v-if 指令在使用的时候,有两种方式:
直接给定一个布尔值 true 或 false
被 v-if 控制的元素
给 v-if 提供一个判断条件,根据判断的结果是 true 或 false,来控制元素的显示和隐藏
良好
优秀
良好
及格
不及格
基于一个数组来循环渲染一个列表结构。
v-for
指令需要使用 item in items
形式的特殊语法
items
是源数据 (待循环) 的数组,而item
被循环的每一项。【示例】:动态渲染表单数据:
索引
Id
姓名
{{ index }}
{{ item.id }}
{{ item.name }}
官方推荐, 使用 v-for 指令绑定一个
:key
属性,key的作用是为了高效的更新虚拟DOM。
key 的值类型,是有要求的:字符串或数字类型
把数据项 id 属性的值作为 key 的值(因为 id 属性的值具有唯一性)
改变 data 顺序,index 会重新排序,所以 index 的值不具有唯一性。
实时监听
data 中数据的变化,并return 一个计算后的新值
, 供组件渲染 DOM 时使用。可以被
模板结构
(插值、v-bind ) 或methods
方法使用。
实例 1:
{{getFullName()}}
{{fullName}}
const vm = new Vue({
el: '#app',
data: {
firstName: 'lin',
lastName: 'willen'
},
computed: {
fullName () {
return this.firstName + ' ' + this.lastName;
}
},
// 使用 methods: 每次都会调用方法
methods: {
getFullName () {
return this.firstName + ' ' + this.lastName;
}
}
})
特点:
实例2:
总价格:{{totalPrice}}
const vm = new Vue({
el: '#app',
data: {
books:[
{id: 1001, name: 'Unix编程艺术',price: 119},
{id: 1002, name: '代码大全',price: 105},
{id: 1003, name: '深入理解计算机原理',price: 99},
{id: 1004, name: '现代操作系统',price: 109}
]
},
computed: {
totalPrice () {
let totalPrice = 0;
for (let i in this.books) {
totalPrice += this.books[i].price;
}
// 也可以使用 for of
for (let book of this.books) {
totalPrice += book.price;
}
return totalPrice;
}
}
})
methods
和computed
看起来都可以实现我们的功能,那么为什么还要多一个计算属性
计算属性会缓存计算的结果
, 不变的情况下只调用一次, 除非原属性发生改变,才会重新调用.侧重的应用场景不同侧重的应用场景不同
:
多个值
的变化,最终计算并返回一个新值
。特定的业务处理
,不需要有任何返回值
。什么是组件化开发 ?
根据封装的思想,把页面上可重用的 UI 结构封装为组件,方便项目的开发和维护。
vue 中的组件化开发
.vue
每个 .vue 组件都由 3 部分构成,分别是:
template
:组件的模板结构,且每个组件中必须包含template模板结构。
script
: 组件的 JavaScript 行为
style
:组件的样式
// 标签上添加 lang=“less” 属性,即可使用 less 语法编写组件的样式
// scoped 防止样式冲突
(1)script 中的 name
节点
可以清晰的区分每个组件
。(2)script 中的 data
节点
必须是函数
, 不能直接指向对象数据。(3)script 中的 methods
节点
方法
),必须定义到 methods
节点中通过 components 注册的是私有子组件,被注册的组件只能用在当前组件中。
1.使用 import 语法导入需要的组件
import Left from '@/components/Left.vue'
2.在 script 标签中使用 components
节点注册组件
3.以标签的形式
使用刚才注册的组件
在 vue 项目的
main.js
入口文件中,通过Vue.component()
方法,可以注册全局组件。
注册:
import Vue from 'vue'
import App from './App.vue'
// 导入需要被全局注册的那个组件
import Count from '@/components/Count.vue'
// 参数一: 组件的 '注册名称',将来以标签形式使用时要求和这个名称一样。
// 参数二: 需要被全局注册的那个组件。
Vue.component('MyCount', Count)
// 消息提示的环境配置,设置为开发环境或者生产环境
Vue.config.productionTip = false
new Vue({
// render 函数中,渲染的是哪个 .vue 组件,那么这个组件就叫做 “根组件”
render: h => h(App)
}).$mount('#app')
使用:
// 这里需要注意 '注册名称'
在 Vue 定义组件注册名称的方式有两种:
my-swiper
和 my-search
MySwiper
和 MySearch
转化为短横线名称
进行使用。注意: 在开发中,推荐使用大驼峰命名法
为组件注册名称,因为它的适用性更强。
默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
让当前组件的样式对其子组件是不生效。
Vue 中提供了在style 节点添加 scoped
属性,来防止样式冲突问题:
轮播图组件件
为了提高开发效率和开发体验,直接在 style 节点使用 scoped 属性:
让某些样 式对子组件生效。
使用场景: 当使用第三方组件库的时候,需要修改第三方组件默认样式的时候。
通过
v-bind
动态操作元素样式。
通过三元表达式,动态的为元素绑定 class 的类名:
MyDeep 组件
data(){
return { isItalic:true }
.thin{
font-weight:200;
.italic{
font-style:italic;
}
如果元素需要动态绑定多个 class 的类名,此时可以使用数组的语法格式
MyDeep组件
MyDeep组件
命名可以用驼峰式
或短横线分隔
(记得用引号括起来) 来命名:
Hello world!!
data () {
return {
active: 'red',
fsize: 30,
bgcolor: 'pink'
}
}
props
是组件的自定义属性,允许使用者通过自定义属性,为当前组件指定初始值,极大的提高组件的复用性。
my-article 组件的定义如下:
标题:{{title}}
作者:{{author}}
父组件传递给my-article
组件的数据,必须在props
节点中声明 :
如果父组件给子组件
传递了未声明的 props 属性
,则这些属性会被忽略,无法被子组件使用。
使用 v-bind 属性绑定的形式,可以为组件动态绑定 props 的值。
<!--通过V-bind属性绑定,为author动态赋予一个表达式的值
组件中如果使用“camelCase (驼峰命名法)
”声明了 props 属性的名称,则有两种方式为其绑定属性的值:
使用时既可以用驼峰命名
,亦可以用短横线分隔命名
的形式为组件绑定属性的值 :
// 等价于
基础的类型检查 type
props:{
//支持的8种基础类型
propA:String, //字符串类型
propB:Number, //数字类型
propC:Boolean, //布尔值类型
propD:Array, //数组类型
propE:Object, //对象类型
propF:Date, //日期类型
propG:Function, //函数类型
propH:Symbol //符号类型
}
多个可能的类型
必填项校验 required
属性默认值 default
props:{
// 通过数组形式,为当前属性定义多个可能的类型
type: [String, Number],
required: true,
default: 0,
}
在封装组件时,可以为 prop 属性指定自定义的验证函数,从而对 prop 属性的值进行更加精确的控制:
props:{
type:{
// 通过 validataor函数,对type 属性进行校验,属性值 通过val形参接收
validataor(val){
// 必须匹配下列字符串中的一个
return ['success','warning','danger'].indexOf(val) !== -1
}
}
}
在实际开发中我们经常会碰到下面的情况:
使用同一个注册的组件
时候希望赋值一个不同的初始值
。1.组件的封装者通过 props 允许使用者自定义初始值:
Count是全局组件,将被Left 和 Right组件使用
count 的值是:{{ init }}
// 父组件
methods : {
getNewCount(val) {
console.log('监听到了 count 值的变化', val)
},
在调用 this.$emit() 方法触发自定义事件时,可以通过第 2 个参数为自定义事件传参:
methods: {
onBtnClick() {
this.count t= 1
this.$emit( 'numchange' , this.count)} // 触发自定义事件,通过第二个参数传参
}
在 vue 3.x 的版本中剔除了过滤器
相关的功能。
在 vue 3.x 使用计算属性或方法代替被剔除的过滤器功能。
过滤器(Filters)常用于文本的格式化。过滤器可以用在两个地方:
双括号插值表达式
和v-bind
属性绑定。
{{ message | capitalize }}
在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。
创建一个私有过滤器,示例代码如下:
const vm = new Vue({
el: '#app',
data: {
message: 'hello world!',
info: 'title info'
},
// 在 filters 节点下定义过滤器
filters: {
// 把首字母转换为大写的过滤器
capitalize (value) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
如果希望在多个 vue 实例之间共享过滤器,则可以按照如下的格式定义全局过滤器:
// Vue.filters()方法接收两个参数:
// 第一个参数:过滤器名字 第二个参数:过滤器的处理函数
Vue.filter('capitalize',(str)=>{
return value.charAt(0).toUpperCase() + value.slice(1)'
} )
{{ message | filterA | filterB }}
过滤器是 JavaScript 函数,因此可以接收参数:
{{ message | filterA('arg1', arg2) }}
// 第一个参数永远是 '管道符' 前面待处理的值,第二个参数开始才是调用过滤器时传递的参数
Vue.filter('filterA',(msg,arg1,arg2)=>{
// 过滤器的逻辑代码
})
这里,filterA
被定义为接收三个参数的过滤器函数。其中 message
的值作为第一个参数,普通字符串 'arg1'
作为第二个参数,表达式 arg2
的值作为第三个参数。
{{message | cap | maxl(5)}}
Vue.filter('cap', (str) => {
return str.charAt(0).toUpperCase() + str.slice(1) + '----'
})
Vue.filter('maxl', (str, len = 10) => {
if (str.length <= len) return str
return str.slice(0, len) + '....'
})
var app = new Vue({
el: '#app',
data: {
message: 'hello Vue 2021年10月30日00:07:45!'
}
使用lable自带的属性进行单选钮的启用和禁用:
watch 侦听器
监视数据的变化
,从而针对数据的变化做特定的操作
。
使用方法格式创建的侦听器:
监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用:
import axios from 'axios'
export default{
data(){
return{ username: ''}
}
},
watch: {
// newVal 是'变化后的新值',oldVal 是'变化之前的旧值'
async username(newVal,oldVal) {
if (newVal === '') return
// 使用 axios 发起请求,判断用户名是否可用
const { data: res } = await axios.get(`https://www.escook.cn/api/finduser/${newVal}` )
console.log(res)
}
}
使用方法创建时:组件在初次加载完毕后不会调用 watch 侦听器。
如果想让 watch 侦听器在浏览器打开时立即被调用,则需要使 用
immediate
选项。
使用对象格式创建的侦听器:
watch: {
username: {
// handler 是固定写法,表示当 username 的值变化时,自动调用 handler 处理函数
async handler(newVal,oldVal) {
if (newVal === '') return
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)
console.log(res)
},
// 表示页面初次渲染好之后,就立即触发当前的 watch 侦听器
immediate: true
}
}
handler
定义侦听器函数immediate
控制侦听器是否自动触发, 默认值为 false
不立即触发。如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 开启 深度监听。
data: {
info: {username: 'admin'}
},
watch:{
// 监听info对象的变化
info:{
handler(newVal){
console.log(newVal.username)
},
// 开启深度监听,监听每个属性值的变化,默认值为 false
deep: true
}
}
如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器。
const vm = new Vue({
el: '#app',
data: {
info: {username: 'admin',}
},
watch:{
'info.username':{
handler(newVal){
console.log(newVal)
},
}
}
})
生命周期
(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。生命周期函数
:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。
生命周期
强调的是时间段,生命周期函数
强调的是时间点。
生命周期图示:
需要注意的三个周期函数:
在实际开发中,
created
是最常用的生命周期函数 !
父组件通过
v-bind
属性绑定向子组件共享数据,子组件使用props
接收数据。
// 父组件
// 注册子组件
// 子组件
Son组件
父组件传递过来的 msg值是: {{ msg }
父组件传递过来的user值是: {{ user }}
export default {
props: [ 'msg' , 'user ']
}
子组件向父组件共享数据使用自定义事件。
// 子组件
export default {
data() {
// 子组件自己的数据,将来希望把 count 值传给父组件
return { count: 0 }
},
methods: {
add() {
this.count t= 1
//修改数据时,通过 $emit()触发自定义事件
this.$emit( 'numchange' , this.count)}
}
}
// 父组件
App 根组件
子组件传过来的数据是 : {{ countFromSon }}
在 vue2.x 中,兄弟组件之间数据共享的方案是
EventBus
。
EventBus 的使用步骤
eventBus.js
模块,并向外共享一个 Vue 的实例对象
bus.$emit('事件名称', 要发送的数据)
方法触发自定义事件 。bus.$on('事件名称', 事件处理函数)
方法注册一个自定义事件。不依赖于 jQuery 和调用 DOM API 的情况下,获取
DOM 元素
或组件的引用
。
每个 vue 的组件实例上,都包含一个 $refs 对象
,里面存储着对应的 DOM 元素或组件的引用。默认情况下,组件的 $refs 指向一个空对象。
<!--使用ref属性,为对应的DOM添加引用名称-->
MyRef 组件
methods:{
getRef(){
//通过this.$refs.引用的名称可以获取到DOM元素的引用
console.log(this.$refs.myh3)
//操作DOM元素,把文本颜色改为红色
this.$refs.myh3.style.color='red'
},
需求: 在根组件控制子组件
<!--使用ref属性,为对应的“组件”添加引用名称-->
methods:{
getRef(){
// 通过this.$refs.引用的名称可以引用组件的实例
console.log(this.$refs.counterRef)
// 引用到组件的实例之后,就可以调用 子组件上的 methods 方法
this.$refs.counterRef.add()
},
添加 ref 引用,并调用原生 DOM 对象的
.focus()
方法即可。
$nextTick(cb)
保证 cb 回调函数可以操作到最新的 DOM 元素(推迟到下一个 DOM 更新周期之后执行)。
点击按钮展示文本框,文本框输入时隐藏按钮:
实现不同组件之间的按需展示。( 动态切换组件显示和隐藏 ) ,类似于
Vue-Router
。
Vue 提供了一个内置的
组件,专门用来实现动态组件的渲染 :
component
标签是 vue 内置的,作用:组件的占位符
通过 :is
属性,动态指定要渲染的组件
is 属性的值,表示要渲染的组件的名字。
is 属性的值,应该是组件在 components 节点下的注册名称。
使用 keep-alive
保持组件的状态 (避免组件切换时重新渲染)。
通过 include
指定哪些组件需要被缓存。
通过 exclude
属性指定哪些组件不需要被缓存。
// 点击按钮,动态切换组件的名称
当提供了 name 属性之后,组件的名称就是 name 属性的值。
export default {
name: ‘MyRight’
}
声明 name 应用场景:结合
标签实现组件缓存功能;以及在调试工具中看到组件的 name 名称。
在签形式使用的
组件中内容节点
中插入内容
。
slot
元素 定义插槽
,从而为用户预留内容占位符。在
slot
标签内添加的内容会被作为后备内容。
// 子组件 my-com
这是后备内容
// 使用插槽
// 如果用户没有提供内容,上面 slot标签 内定义的内容会生效,此时页面会有 "这是后备内容"
// 如过提供了,下面的 img 将会被渲染到页面上
// MyArticle 组件
slot
插槽,都要有一个 name名称,一个不带 name
的
出口会带有隐含的名字“default”。如果要把内容填充到指定名称的插槽中,我们可以在一个 元素上使用
v-slot
指令,并以 v-slot
的参数的形式提供其名称:
// 把内容放在 MyArticle组件的 header 标签内
如今最好,没有来日方长。
// 上一个代码块中,main 标签未指定具名插槽,所以默认渲染到 main标签中
现在是2021年11月7日 22点05分 星期天
今天不仅是周日,也是冬至
最后,祝大家冬至快乐!
v-slot:
可以简写 #
。例如 v-slot:header 可以被重写为 #header。
v-slot
属性只能放在 组件标签和 元素上 (否则会报错)。
在封装组件的过程中,可以为预留的
插槽绑定 props 数据,这种带有 props 数据的
叫做“作用域插槽
”。示例代码如下:
这是 Left 组件
<!--下面的slot 是一个作用域插槽 -->
接收作用域插槽对外提供的数据, 使用解构赋值
简化数据的接收过程 :
<!-- 使用自定义组件 -->
<!--作用域插槽对外提供的数据对象,可以通过“解构赋值”简化接收的过程-->
{{user.id}}
{{user.name}}
{{user.state}}
vue 官方提供了 v-text、v-for、v-model、v-if 等常用的指令。除此之外 vue 还允许开发者自定义指令。
在每个 vue 组件中,可以在 directives
节点下声明私有自定义指令。
directives:{
color:{
//为绑定到的HTML元素设置红色的文字
bind(el){
//形参中的el是绑定了此指令的、原生的DOM对象
el.style.color=‘red'
}
使用自定义指令时,需要加上v-指令前缀
<!–声明自定义指令时,指令的名字是color 使用时就是 v-color -->
在 template
结构中使用自定义指令
时,可以通过等号 =
的方式,为当前指令动态绑定参数值:
<!--在使用指令时,动态为当前指令绑定参数值 color-->
App组件
data(){
return{
color:'red’
}
binding
获取指令的参数值:在上面的实例中我们为自定义指令绑定了一个动态的参数值,如何拿到这个值呢 ?
在声明自定义指令时,可以通过形参中的第二个参数,来接收指令的参数值
App组件
// 此时传入的是字符串,不会在data 数据中查找
directives:{
color:{
bind(el,binding){
//通过binding对象的.value属性,获取动态的参数值
el.style.color=binding.value
}
update
函数会在每次 DOM 更新时被调用。
注意:在 vue3 的项目中使用自定义指令时, bind 必须改为 mounted
、 update 改为 updated
。
bind 函数只调用 1 次:当指令第一次绑定到元素时调用,当 DOM 更新时 bind 函数不会被触发。
directives:{
color:{
//当指令第一次被绑定到元素时被调用
bind(el,binding){
el.style.color=binding.value
},
//每次DOM更新时被调用
update(el,binding){
el.style.color=binding.value
}
如果 bind
和 update
函数中的 逻辑完全相同
,则对象格式的自定义指令可以简写成函数格式:
directives:{
//在 bind 和 update 时,会触发相同的业务逻辑
color( el,binding ){
el.style.color=binding.value
}
通过“
Vue.directive()
” 进行声明 。
注意:在使用 Vue-cli ( 脚手架) 时,要把全局自定义指令写在main.js
文件中:
//参数1:字符串,表示全局自定义指令的名字
//参数2:对象,用来接收指令的参数值
Vue.directive('color',function(el,binding){
el.style.color=binding.value
})
// 简写
Vue.directive('color',(el,binding) =>{
el.style.color=binding.value
})
Vue CLI 是官方发布的vue.js项目脚手架, 可以快速搭建vue开发环境以及Webpack配置。
一、安装Vue脚手架
npm install -g @vue/cli
二、创建项目
vue create project(项目名称)
目录详解:
|- public // 静态页面目录
|- index.html // 项目入口
|- src // 源码目录
|- assets // 存放项目中用到的静态资源文件,例如:css 样式表、图片资源
|- components // 封装的、可复用的组件,都要放到 components 目录下
|- App.vue // 根组件
|- main.js // 项目的入口文件。整个项目的运行,要先执行
vue 项目的运行流程:
通过 main.js
把 App.vue
渲染到index.html
的指定区域中。
模板结构
el 区域
axios 是一个专注于网络请求的库, 调用 axios 方法得到的返回值是
Promise 对象
。
发起 GET 请求:
axios({
// 请求方式
method: 'GET',
// 请求的地址
url: 'http://www.liulongbin.top:3006/api/getbooks',
// URL 中的查询参数(GET请求传参)
params: {
id: 1
}
}).then(function (result) {
console.log(result)
})
params
表示传递到服务器端的数据,以url参数
的形式拼接在请求地址后面
发起 POST 请求:
document.querySelector('#btnPost').addEventListener('click', async function () {
// 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!
// await 只能用在被 async “修饰”的方法中
// 1. 调用 axios 之后,使用 async/await 进行简化
// 2. 使用解构赋值,从 axios 封装的大对象中,把 data 属性解构出来
// 3. 把解构出来的data属性,使用冒号进行重命名,一般都重命名为 { data: res }
const { data: res } = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
// POST请求体传参
data: {
name: 'zs',
age: 20
}
})
console.log(res)
})
axios 封装的 6 个属性:
axios 在请求到数据之后,在真正的数据之外,套了一层外壳。
{
config:{ },
// data 才是服务器返回的真实数据
data:{
status: 200,
msg: "获取数据成功!",
data: Array(6)
},
headers:{ ... },
request:{ },
status: 200,
statusText: 'OK',
}
document.querySelector('#btnGET').addEventListener('click', async function () {
/* axios.get('url地址', {
// GET 参数
params: {}
}) */
const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/getbooks', {
params: { id: 1 }
})
console.log(res)
})
document.querySelector('#btnPOST').addEventListener('click', async function () {
// axios.post('url', { /* POST 请求体数据 */ })
const { data: res } = await axios.post('http://www.liulongbin.top:3006/api/post', { name: 'zs', gender: '女' })
console.log(res)
})
一般我们会直接这么用:
// 在组件内
发起 POST 请求
缺点: 在每次使用时候都要导入 axios 文件,写请求地址 (对后期维护不友好) 。
避免重复导入axios 和重复写入完整请求地址。
在main.js
中配置
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
Vue.config.productionTip = false
// 全局配置 axios 的请求根路径 (官方提供配置项)
axios.defaults.baseURL = 'http://www.liaoyia.top:3006'
// 把 axios 挂载到 Vue.prototype 上,供每个 .vue 组件的实例直接使用
Vue.prototype.$http = axios
// 今后,在每个 .vue 组件中要发起请求,直接调用 this.$http.xxx
// 但是,把 axios 挂载到 Vue 原型上,有一个缺点:不利于 API 接口的复用!!!
new Vue({
render: h => h(App)
}).$mount('#app')
使用:
获取图书列表的数据
但是把 axios 挂载到 Vue原型上并配置请求根路径也有缺点:
无法实现API接口的复用
: 在多个组件想使用同一个请求方法(API)的时候,只能在每个组件重新定义一次。
1.如果项目中有多个请求地址,我们可以根据多个地址使用工厂模式封装 js 模块,创建多个 axios
实例对象,并设置请求根路径 (baseURL
) :
步骤如下: 在项目的 src
目录下创建utils
文件夹并新建一个 request.js
文件:
import axiox from 'axios'
const request =axiox.create({
//baseURL会在发送请求的时候拼接在url参数的前面
baseURL:'http://jsonplaceholder.typicode.com/',
timeout:5000
})
// 向外导出
export default request
axios
实例对象,并向外导出。2.为了实现复用性,我们还可以把所有请求,都封装在API模块里,在API模块中,按需导出一个方法,这个方法调用 request.get 或 request.post 来请求一个接口,最后return 一个Promise 对象。
比如想调用接口获取用户相关信息:
在根目录新建 utils
文件夹并在里面新建 userAPI.js
文件
//导入 utils 文件夹下的 request.js
import request from '@/utils/request.js'
export const getArticleListAPI = function(_page, _limit) {
return request.get('/articles', {
params: {
_page,
_limit
}
})
}
【实例】:点击按钮发起 GET 请求并自动调用请求拦截
和添加响应拦截器
:
调用封装的get请求
import axiox from 'axios'
const instance =axiox.create({
//baseURL会在发送请求的时候拼接在url参数的前面
baseURL:'http://jsonplaceholder.typicode.com/',
timeout:5000
})
//请求拦截
// 所有的网络请求都会先走这个方法
// 添加请求拦截器,所有的网络请求都会先走这个方法
// 我们可以在它里面为请求添加一些自定义的内容 比如 token 或者在 headers 提供信息
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
console.group('全局请求拦截')
console.log(config)
console.groupEnd()
config.headers.token ='12343'
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
//此处可以根据服务器的返回状态码做响应的处理
//404 404 500
instance.interceptors.response.use(function (response) {
// 对响应数据做点什么
console.group('全局响应拦截')
console.log(response)
console.groupEnd()
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
// 从服务器查看获取数据
export function get(url,params) {
return instance.get(url,{
params
})
}
// 向服务器创建数据
export function post(url,data) {
return instance.post(url,data)
}
// 从服务器删除数据
export function del(url) {
return instance.delete(url)
}
// 向服务器发送更新数据
export function put(url,data) {
return instance.put(url,data)
}
SPA (单页面网页)
,所有组件的展示与切换都在这唯一的一个页面内完成。
此时,不同组件之间的切换需要依赖 前端路由(router)
来实现。
什么是前端路由?
Hash 地址与组件之间的对应关系
,不同的Hash 展示不同的页面。前端路由的工作方式:
vue-router 是 vue.js 官方给出的路由解决方案。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目中组件的切换。
安装 vue-router 包
npm i [email protected] -S
此时 src 源码目录下会新增一个 router 文件夹。
创建路由模块
在 src 源代码目录下,新建 router/index.js
路由模块,并初始化:
//1.导入Vue和VueRouter的包import Vue from'vue'
import VueRouter from‘vue-router'
//2.调用Vue.use()函数,把VueRouter 安装为Vue的插件
Vue.use(VueRouter)
//3.创建路由的实例对象
const router=new VueRouter()
//4.向外共享路由的实例对象
export default router
导入并挂载路由模块
在 src/main.js
入口文件中,导入并挂载路由模块:
import Vue from'vue'
import App from'./App.vue'
//1.导入路由模块
import router from@/router'
new Vue ({
render:h=>h(App),
//2.挂载路由模块
router:router
}).$mount('#app')
声明路由链接和占位符
在 src/App.vue 组件中,使用 vue-router 提供的
和
声明路由链接和占位符:
App组件
<!--1.定义路由链接-->
首页
电影
关于
<!--2.定义路由的占位符-->
声明路由的匹配规则
在 src/router/index.js
路由模块中,通过 routes
数组声明路由的匹配规则:
//导入需要使用路由切换展示的组件
import Home from'@/components/Home.vue'
import Movie from'@/components/Movie.vue'
import About from'@/components/About.vue'
//2.创建路由的实例对象
const router=new VueRouter({
//在routes数组中,声明路由的匹配规则
routes:[
//path 表示要匹配的hash地址;component表示要展示的路由组件
{path:'/home',component: Home},
{path:'/movie',component: Movie},
{path:'/about',component: About}
]
})
当访问地址 A 的时候,
强制用户跳转
到地址C, 从而展示特定的组件页面。
redirect
属性,指定一个新的路由地址来实现路由的重定向。下面这个应用场景,当用户访问网页 / 目录时候,跳转到首页:
const router=new VueRouter({
//在routes数组中,声明路由的匹配规则
routes:[
//当用户访问 / 的时候,通过 redirect 属性跳转到 /home 对应的路由规则
{ path: '/',redirect:'/home' },
{ path: '/home',component: Home },
{ path: '/movie',component: Movie },
{ path: '/about',component: About }
})
可以通过如下的两种方式,将激活的路由链接进行高亮显示:
默认的
高亮 class 类自定义
路由高亮的 class 类被激活的路由链接,默认会应用一个叫做 router-link-active
的类名, 可以使用此类名选择器
,为激活 的路由链接设置高亮的样式:
/*在index.css全局样式表中,重新router-link-active的样式*/
.router-link-active {
color:#ef4238;
font-weight:bold;
border-bottom: 2px #ef4238 solid;
}
在创建路由的实例对象时,开发者可以基于 linkActiveClass
属性,自定义路由链接被激活时所应用的类名:
const router=createRouter({
history:createlebHashHistory(),
// 指定被激活的路由链接,会应用 router-active 这个类名,
// 默认的 router-link-active 类名会被覆盖掉
1inkActiveclass:'router-active',
routes:[
{ path: '/',redirect: '/home' },
{ path: '/home',component: Home },
{ path:'/movie',component: Movie },
{ path: '/about',component: About },
]
})
通过路由实现组件的嵌套展示,叫做嵌套路由。
上图中,我们在 About.vue
组件中套娃了 tab1 和 tab2 组件 ,如果我们想展示它,则需要声明子路由链接
以及子路由占位符
:
About 组件
<!--1.在关于页面中,声明两个子路由链接-->
tab1
tab2
<!--2.在关于页面中,声明子路由的占位符-->
到此为止,我们已经有了 子路由链接
、子路由占位符
,以及链接对应的组件
,但是要在页面显示,我们还缺少对应关系,也就是路由规则
。
在 src/router/index.js
路由模块中,导入需要的组件,并使用 children
属性声明子路由规则:
// 导入组件
import Tab1 from '@/components/tabs/Tab1.vue'
import Tab2 from '@/components/tabs/Tab2.vue'
const router=new VueRouter({
routes:[
{ //about 页面的路由规则(父级路由规则)
path: '/about',
component: About,
//1.通过children属性,嵌套声明子级路由规则
children:[
{ path: 'tab1',component: Tab1 },//2.访问/about/tab1时,展示Tab1组件
{ path: 'tab2',component: Tab2 } //2.访问/about/tab2时,展示Tab2组件
}]
})
children
属性嵌套声明子级路由规则时,path 名称不需要加 /
。如果
children
数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫作“默认子路由”。
// src/router/index.js
const router=new VueRouter({
routes:[
{
path: '/about',
component: About,
// rediect: 'about/table' // 通过重定向设置默认展示页面
children:[
// path值为空字符串,此时Tab1为默认子路由,一进入About页面,默认展示 Tab1
{ path: '',component: Tab1 },
{ path: 'tab2',component: Tab2 }
}]
})
默认子路由
和路由重定向
都可以用来设置 展示特定的组件页面
。动态路由指的是:把 Hash 地址中
可变的部分
定义为参数项
,从而提高路由规则的复用性
。
有如下3个路由链接:
电影1
电影2
电影3
如果定义下面3个路由规则,虽然可行,但太过繁琐且复用性差:
{path:'/movie/1',component:Movie}
{path:'/movie/2',component:Movie}
{path:'/movie/3',component:Movie}
使用动态路由,将上面创建3个路由规则,合并成一个,提高了路由规则的复用性:
// src/router/index.js 文件
//路由中的动态参数以 : 进行声明,冒号后面的是动态参数的名称
{ path:'/movie/:id',component:Movie}
:
)来定义路由的参数项 。$route.params
访问动态匹配的参数值在 动态路由
渲染出来的组件中,可以使用 this.$route.params
对象访问到动态匹配的参数值:
<!--this.$route是路由的“参数对象”-->
Movie 组件-- {{this.$route.params.id}}
为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props
传参, 在定义路由规则时,声明props : true
选项 ,
// router下的 index.js 文件
// 1. 声明 props : true 选项
{ path:'/movie/:id',component: Movie,props:true }
定义好后,即可在Movie组件中,以props的形式接收到路由规则匹配到的参数项。
<!-- 3、直接使用props中接收的路由参数 -->
MyMovie组件--{{id}}
vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是:
push
(‘hash 地址’)
增加
一条历史记录 。replace
(‘hash 地址’)
替换掉当前
的历史记录 。go
(数值 n)
back()
,后退到上一个页面。forward()
,前进到下一个页面。调用 this.$router.push()
或者 $router.replace()
方法,可以跳转到指定的 hash 地址,展示对应的组件页面 :
Home组件
跳转到Movie页面
push 和 replace 的区别:
增加一条历史记录
替换掉当前的历史记录
。调用 this.$router.go()
方法,可以在浏览历史中前进和后退:
MyMovie组件---{{id}}
后退
每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,我们可以对每个路由进行访问权限的控制:
//创建路由实例对象
const router = new VueRouter({..…})
//调用路由实例对象的beforeEach方法,即可声明“全局前置守卫"
//每次发生路由导航跳转的时候,都会自动触发这个“回调函数”
router.beforeEach((to,from,next) =>{
/* 必须调 next 函数 */
})
守卫方法的 3 个形参:
to
是将要访问的路由的信息 (对象)from
是将要离开的路由的信息对象next
是一个函数,一定要调用 next ()
才表示放行,允许这次路由导航 。注意:
不声明 next
形参,则默认允许用户访问每一个路由
!声明了 next 形参
,则必须调用 next() 函数
,否则不允许用户访问任何一个路由!next()
强制其停留在当前页面
:next(false
)强制其跳转到登录页面
:next(’/login’)router.beforeEach((to,from,next) => {
//获取浏览器缓存的用户信息
const userInfo = window.localStorage.getItem('token');
if (to.path === '/main' && !userInfo ){ //访问的是 main 页面且 token 不存在
// 访问的是后台主页,但是没有token的值,跳转到登入页
next('/login')
} else{
next()//访问的不是后台主页,直接放行
})
上面代码中,如果项目中有多个页面都需要设置访问权限怎么办呢
使用路由导航守卫 判断访问的是否是一个有权限的 Hash 地址问题:
// 第一种: 使用 || 多重判断,代码臃肿
router.beforeEach((to,from,next) => {
if (to.path === ‘/main’ || to.path === ‘/user’ || to.path === ‘seeting’){
// code…
} else{
// …
}
})
我们可以把多个地址存入到一个数组
或者一个json
文件、js
文件:
const patArr = ['/mian', '/home', '/home/users', '/home/rights']
router.beforeEach((to,from,next) => {
if (patArr.indexOf(to.path) !== -1){
// code....
} else{
// ....
}
})
// 存入 js 使用
// 1. 定义一个 pathArr.js文件
export default ['/mian', '/home', '/home/users', '/home/rights']
// 2. 使用时导入
import pathArr from '@/router'
上面的例子中,还是不够方便,vue-router 中给我们提供了 meta
字段,传入一个对象,设置requiresAuth: true
,就相当于开起来跳转验证。
const router = new VueRouter({
routes: [
{
path: '/blog',
component: Blog,
path: 'bar',
component: Bar,
// 设置 meta ,开启跳转验证
meta: { requiresAuth: true }
}
]
})
全局守卫中定义页面权限:
router.beforeEach((to, from, next) => {
console.log(to)
//判断 $route.matched数组 是否中有 meta 字段值
if (to.matched.some(record => record.meta.requiresAuth)) {
如果当前页面的 是否有 user
if (!localStorage.getItem("user")) {
next({
path: '/login',
//传入完整的路径
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 确保一定要调用 next() 这次放行说明在白名单
}
})
更改登入页面的逻辑
// Login.vue
methods:{
handLeLonge(){
// 获取用户名和密码
setTimeout(()=>{
let data = {
user: this.user
}
// 保存用户名到本地
localStorage.setItem('user',JSON.stringify(data))
this.$route.push({
path: this.$route.query.redirect
})
})
}
}
// APP 组件
洛基
雷神< /router-link>
复联
关于
Movie 组件 --- {{ $route.params.mid }} --- {{ mid }}
打印 this
back 后退
forward 前进
点击雷神电影打印出的 this 里,有一个 $route
对象, 如下:
/
后面的参数项,叫做 “路径参数”
this.$route.params
来访问路径参数props
传参来接收路径参数。?
后面的参数项,叫做**“查询参数”**
this.$route.query
来访问查询参数this.$route
中,path 只是路径部分;fullPath 是完整的地址。开发思路:
要想从一个地址跳到另一个地址 (如: 登录后立即跳转到首页,并且首页里设置默认的展示的页面)
离开谁
、要去哪儿
redirect
指向新地址。注意点 :
标签节点不平级时无法使用 v-if 和 v-else 结合來判断
在 vue 中:
在使用组件的时候,如果某个属性名是“小驼峰”形式,则绑定属性的时候,建议改写成“连字符”格式。例如 cmtCount
建议写成 cmt-count
vuex
是终极的组件之间的数据共享方案。
优点:
一般情况下,只有组件之间共享的数据,才有必要存储到 vuex 中;对于组件中的私有数据,依旧存储在组件自身的data中即可 !
安装 vuex 依赖包
npm install vuex --save
在 store / index.js
导入包 并创建 store 对象
//导入 vuex 包
import Vuex from 'vuex'
Vue.use(Vuex)
// 创建 store 对象
const store = new Vuex.Store({
// state 中存放的就是全局共享的数据
state: { count: 0 },
mutations: {},
actions: {},
modules: {}
})
将 store 对象挂载到 vue 实例main.js
中
import store from './store/index';
new Vue({
el: '#app',
render: h => h(app),
router,
// 将创建的共享数据对象,挂载到 Vue 实例中
// 所有的组件,就可以直接从 store 中获取全局的数据了
store
})
一张图看懂 vuex:
State 提供唯一的公共数据源,所有共享的数据都要统一放到
Store
的state
中进行存储。
访问 state 中数据两种方式 :
// this.$store.state.全局数据名称
当前最新的count值为:{{$store.state.count}}
通过 mapState 函数,将当前组件需要的全局数据,映射为 computed
计算属性:
// 1. 在想使用数据的组件中 从 vuex 中按需导入 mapState 函数
import { mapState } from 'vuex'
// 2. 将全局数据,映射为当前组件的计算属性
computed: {
...mapState(['count'])
}
mutation
用于变更 store 中的数据。
// 定义 Mutation
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
// 变更状态
state.count++
}
}
})
// 在组件中 触发 mutation
methods: {
handle1() {
// 触发 mutations 的第一种方式
this.$store.commit('add')
}
}
可以在触发 mutations 时传递参数
:
// 定义Mutation
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
addN(state, step) {
// 变更状态
state.count += step
}
}
})
// 触发mutation
methods: {
handle2() {
// 在调用 commit 函数,
// 触发 mutations 时携带参数
this.$store.commit('addN', 3)
}
}
从vuex中按需导入 mapMutations函数
import { mapMutations } from 'vuex'
通过刚才按需导入的 mapMutations 函数,映射为当前组件的methods
函数。
// 2. 将指定的 mutations 函数,映射为当前组件的 methods 函数
methods: {
...mapMutations(['add', 'addN'])
}
实例:
// store
mutations: {
add(state) {
// 变更状态
state.count++
},
sub(state) {
state.count--
},
addN(state, step) {
// 变更状态
state.count += step
},
subN(state, step) {
state.count -= step
}
},
// 组件A
import { mapState,mapMutations } from 'vuex'
methods:{
...mapMutations(['sub','subN']),
decrement(){
// 调用
this.sub()
},
decrementN(){
this.subN(5)
}
}
如果通过异步操作变更数据,必须通过 Action,而不能使用Mutation,但是在 Action中还是要通过触发Mutation的方式间接变更数据。
注意: 在Actions 中不能直接修改 state中的数据,要通过 mutations修改
。
// store/index.js 定义 Action
const store = new Vuex.store({
// ...省略其他代码
mutations: {
// 只有 mutations中的函数才有权利修改 state。
// 不能在 mutations里执行异步操作。
add(state) {
state.count++
}
},
actions: {
// 在Actions 中不能直接修改 state中的数据,要通过 mutations修改。
addAsync(context) {
setTimeout(() => {
context.commit('add')
}, 1000);
}
},
})
// 在组件中 触发 Action
methods:{
handle(){
// 触发 actions 的第一种方式
this.$store.dispatch('addAsync')
}
}
// 1. 从Vuex中按需导入 mapActions 函数。
import {mapActions} from 'vuex'
// 2. 将指定的 actions 函数,映射为当前组件 methods 的方法。
methods:{
...mapActions(['subAsync']),
decrementAsync(){
this.subAsync()
}
}
// store/index.js
actions: {
// 在Actions 中不能直接修改 state中的数据,要通过 mutations修改。
subAsync(context){
setTimeout(() => {
context.commit('sub')
}, 1000);
}
}
Getter 用于对 Store中的数据进行加工处理形成新的数据。筛选或者排序显示。
Getter 不会修改 Store 中的原数据,它只起到一个包装器的作用,将Store中的数据加工后输出出来。
Store 中数据发生变化, Getter 的数据也会跟着变化。
//定义 Getter
const store = new Vuex.Store({
state:{
count:0
},
getters: {
showNum(state) {
return ‘当前最新的数量是【’ + state.count + ‘】’
}
},
})
this.$store.getters.名称
import { mapGetters } from 'vuex'
computed:{
...mapGetters(['showNum'])
}
其实,通过mapState,mapMutations,mapActions,mapGetters
映射过来的计算属性,或者方法都可以直接调用,不用在 commit
或者 dispatch
。
正常写法:
-1Async
import {mapActions} from 'vuex'
methods: {
...mapActions(['subAsync']),
decrementAsync(){
this.subAsync()
}
},
其实可以简写成:
-1Async
import {mapActions} from 'vuex'
//...省略一些代码
methods: {
...mapActions(['subAsync']),
},
有参数的时候,也可以直接把参数带上,就像这样:
+5
import {mapActions} from 'vuex'
//...省略一些代码
methods: {
...mapActions(['addAsync']),
},
在vu3中使用使用自定义事相较vu2中多了一个 emits
节点声明:
vue 3中使用自定义事件:
在封装组件时:
1. 在`emits`节点声明自定义事件
2. `触发`监听事件
在使用组件时: 监听
自定义事件
声明自定义事件:
// 子组件
+1
触发自定义事件:
// 子组件
+1
监听自定义事件:
// 父组件
// 使用 v-on 指令绑定监听事件
methods : {
getNewCount(val) {
console.log('监听到了 count 值的变化', val)
},
当需要维护组件内外数据的同步时
,可以在组件上使用 v-model 指令。示意图:
实现父子组件数据双向同步。
子组件向父组件共享数据:
父组件通过 v-bind
属性绑定向子组件共享数据,子组件使用 props
接收数据。
父组件向子组件共享数据:
通过自定义事件 emits
,然后触发 $emit()
, 子组件监听。
使 v-model 实现子→父组件共享数据好处:
在父组件中 不用再监听自定义事件
了,也不用再额外声明事件处理
函数。
父组件:
// 使用双向数据绑定指令
data(){
return{
count: 0
}
}
子组件:
// 子组件
+1
指的是
父节点的组件
向其子孙组件
共享数据。
使用步骤:
父节点通过 provide
方法,对子孙组件共享数据。
export default {
data() {
return {
color: 'red', // 1. 定义"父组件"要向"子孙组件"共享的数据
}
},
provide() { // provide函数 返回要共享的数据对象
return {
color: this.color,
count: 1,
}
},
}
子孙节点通过 inject
接收数据。
三级组件 --- {{ color }} --- {{ count }}
父节点使用 provide 向下共享数据时并非响应式
,我们可以结合 computed 函数
向下共享响应式的数据
:
import { computed } from 'vue' // 1. 从vue中按需导入 computed 函数
export default {
data() {
return {
color: 'red',
}
},
provide() {
return {
// 2. 使用 computed 函数,把共享数据包装为"响应式"的数据
color: computed(() => this.color),
count: 1,
}
},
}
子孙节点使用响应式数据, 注意接收的响应式数据必须以 .value
的形式使用:
// 响应式数据,必须以.value 的形式进行使用
子孙组件 --- {{ color.value }} --- {{ count }}
vuex
是终极的组件之间的数据共享方案。
需要共享的数据
。父子关系:
自定义属性
自定义事件
v-model
兄弟关系: EvenBus
后代关系:provide
& inject
全局数据共享: vuex
import { createApp } from 'vue'
import './assets/css/bootstrap.css'
import './index.css'
import axios from 'axios'
// 创建一个单页面应用程序实例
const app = createApp(App)
// 配置全局请求根路径
axios.defaults.baseURL = 'https://www.escook.cn'
// 全局挂载到app 根路径中
// $http 是模仿 vue封装成员的方式
app.config.globalProperties.$http = axios
// 实现挂载
app.mount('#app')
在组件中使用:
methods: {
async getInfo() {
const { data: res } = await this.$http.get('/api/get', {
// get 请求必须通过 params 传参
params: {
name: 'ls',
age: 33,
},
})
console.log(res)
},
},
vue3 中需要安装 4.x的版本
在 src 目录下创建 router.js
文件并配置
import { createRouter, createWebHashHistory } from ‘vue-router’
import Login from ‘./components/MyLogin.vue’
import Home from ‘./components/Home.vue’
// 创建路由实例对象
const router = createRouter({
history: createWebHashHistory(), // 指定通过 hash 管理路由的切换
routes: [
{ path: ‘/home’, redirect: ‘/login’ },
{ path: ‘/login’, component: Login, name: ‘login’ },
],
})
export default router //4. 向外共享路由对象
在 src/main.js
入口文件中,导入并挂载路由模块:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 创建 app 实例
const app = createApp(App)
app.use(router)
// 挂载 app 实例
app.mount('#app')
事件:
// 父组件
// 使用 v-on 指令绑定监听事件
methods : {
getNewCount(val) {
console.log('监听到了 count 值的变化', val)
},
当需要维护组件内外数据的同步时
,可以在组件上使用 v-model 指令。示意图:
实现父子组件数据双向同步。
子组件向父组件共享数据:
v-bind
属性绑定向子组件共享数据,子组件使用 props
接收数据。父组件向子组件共享数据:
emits
,然后触发 $emit()
, 子组件监听。使 v-model 实现子→父组件共享数据好处:
在父组件中 不用再监听自定义事件
了,也不用再额外声明事件处理
函数。
父组件:
// 使用双向数据绑定指令
data(){
return{
count: 0
}
}
子组件:
// 子组件
+1
指的是
父节点的组件
向其子孙组件
共享数据。
使用步骤:
父节点通过 provide
方法,对子孙组件共享数据。
export default {
data() {
return {
color: 'red', // 1. 定义"父组件"要向"子孙组件"共享的数据
}
},
provide() { // provide函数 返回要共享的数据对象
return {
color: this.color,
count: 1,
}
},
}
子孙节点通过 inject
接收数据。
三级组件 --- {{ color }} --- {{ count }}
父节点使用 provide 向下共享数据时并非响应式
,我们可以结合 computed 函数
向下共享响应式的数据
:
import { computed } from 'vue' // 1. 从vue中按需导入 computed 函数
export default {
data() {
return {
color: 'red',
}
},
provide() {
return {
// 2. 使用 computed 函数,把共享数据包装为"响应式"的数据
color: computed(() => this.color),
count: 1,
}
},
}
子孙节点使用响应式数据, 注意接收的响应式数据必须以 .value
的形式使用:
// 响应式数据,必须以.value 的形式进行使用
子孙组件 --- {{ color.value }} --- {{ count }}
vuex
是终极的组件之间的数据共享方案。
需要共享的数据
。父子关系:
自定义属性
自定义事件
v-model
兄弟关系: EvenBus
后代关系:provide
& inject
全局数据共享: vuex
import { createApp } from 'vue'
import './assets/css/bootstrap.css'
import './index.css'
import axios from 'axios'
// 创建一个单页面应用程序实例
const app = createApp(App)
// 配置全局请求根路径
axios.defaults.baseURL = 'https://www.escook.cn'
// 全局挂载到app 根路径中
// $http 是模仿 vue封装成员的方式
app.config.globalProperties.$http = axios
// 实现挂载
app.mount('#app')
在组件中使用:
methods: {
async getInfo() {
const { data: res } = await this.$http.get('/api/get', {
// get 请求必须通过 params 传参
params: {
name: 'ls',
age: 33,
},
})
console.log(res)
},
},
vue3 中需要安装 4.x的版本
在 src 目录下创建 router.js
文件并配置
import { createRouter, createWebHashHistory } from ‘vue-router’
import Login from ‘./components/MyLogin.vue’
import Home from ‘./components/Home.vue’
// 创建路由实例对象
const router = createRouter({
history: createWebHashHistory(), // 指定通过 hash 管理路由的切换
routes: [
{ path: ‘/home’, redirect: ‘/login’ },
{ path: ‘/login’, component: Login, name: ‘login’ },
],
})
export default router //4. 向外共享路由对象
在 src/main.js
入口文件中,导入并挂载路由模块:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 创建 app 实例
const app = createApp(App)
app.use(router)
// 挂载 app 实例
app.mount('#app')
)