# vue 实例对象

# 组件如何被浏览器识别的?

配置文件中的 vue-template-compiler 就是 vue 模板解析的用来将.vue 文件编译成 js 文件交给浏览器去执行,把组件中的标签等编译成 js 交给浏览器去执行

# 组件的生命周期(创建 > 运行 > 销毁)

其中呢包含多个生命周期函数,是 vue 内置的函数,伴随着生命周期自动依次执行。

当使用一个组件时就是创建组建的实例对象

1
new Vue()

init Events 和 Lifecycle 初始化事件和生命周期函数

初始化事件和函数完成后触发 BeforCreate

beforeCreate 时组件的 props/data/methods 都还未被创建,处于不可用状态,

然后开始初始化 props/data/methods,

初始化完成后立即调用 created 函数这时候这些对象就都已经创建好了,但是组建的 template 模板还没有构成,

tip: 在 create 中发起 Axios 请求

判断实例里面有没有 el 没有就去找 $mount, 有 el 就去判断有没有 template 模板

没有 template 的话就会基于数据和模板在内存中编译生成 html 结构

有的话就编译 template 结构

准备把内存中编译好的 Html 结构渲染到浏览器上。此时还没有渲染到浏览器上 Dom 结构

创建 VM.$el 属性替换掉 el,用内存中生成的 HTML 结构替换掉 el 属性指定的 DOM 元素

执行完上面的流程后,此时已经把内存中的 HTML 结构成功的渲染到了浏览器中。此时浏览器已经包含了当前组件的 Dom 结构,此时已经可以操作 dom 元素了

组件创建阶段就已经结束了。

拿到 data 数据的时候也会触发一次 beforeUpdate

当 data 数据发生改变时触发 beforeUpdate,将 要 根据变化前后最新的数据,重新渲染组件的模板结构

根据最新的数据重新渲染组件的 Dom 结构

渲染完成

# 组件之间的数据共享

父传子 props, 子组件自定义属性接收

msg 传 data 中的数据需要加:(v-bind) , 传纯文字就不要用 v-bind

# props 数据验证(全局组件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].includes(value)
}
}
}
})

子传父(自定义事件 $emit)

子传父,在父组件的子组件上自定义属性用于子组件子向父传的指向

属性值就是父组件接受子组件传值的方法名,参数就是传过来的数据

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
父组件中

<子组件 @自定义事件属性名='触发监听事件'/>






触发事件执行函数(子组件传过来的值也就是$emit(a,b)中的b){

操作数据
}

也可以传对象数据

1
this.$emit(a,{a:b,b:c})

$listeners property,配合 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。

插入:.sync 的作用

子组件用 Update 和 emit 向父组件传递数据一次性传递多个

# 兄弟之间的数据共享 $eventBus

1.vue 实例创建的时候就已经引入了 $EventBus

2. 数据发送方调用 bus.emit (' 事件名称 ',要发送的数据) 触发自定义事件

1
2
//在发送数据的组件自定义事件,发送数据
bus.$emit('hellow','这是all组件传过来的数据')

3. 在接收方调用 bus.$on (事件名称,回调函数) 注册自定义事件

1. 创建 js 文件在文件中引入 vue, 并且向外暴露实例,在用的的接收或发送的组件上引入 js 文件

在正常的项目中是

1
2
3
// beforeCreate() {
// Vue.prototype.$bus = this //安装全局事件总线
// }

也就是全局事件总线

小总结:1.props 是只读的

2. 生命周期函数中 created 是发起 ajax 请求的位置

3.mounted 是组件第一次被渲染到页面上去的,是操作 Dom 的最早时机

4.updated 能够操作到最新的 dom 元素

组件数据共享:父传子:子组件通过在 props 中自定义属性来接受从父组件中子组件的标签上通过 v-bind 传值到子组件中

子传父:$emit () 在子组件发送数据指定属性名,父组件的子组件标签 @自定义事件属性,并且值为父组件中监听的方法名,通过监听的方法接收子组件中传过来的值

1
兄弟传值:利用$emit发送,$on接收,将bus挂载到vue原型对象上利用全局事件实现传值 

终于学到了之前涉及但不理解的 ref 了,后面会用到要理解⭐

# ref 引用操作 DOM

vue 中采用了 M V VM,通过维护数据来驱动视图,很少地方需要操作 dom, 不建议操作 DOM;

需要拿到页面上的某个 dom 元素,vue 提供了 ref 用来辅助开发者不依赖于 jq 的情况下获取 DOM 元素或组件的引用,每个 vue 组件的实例上都包含一个 $ref 对象,储存着对应的 DoM 元素或组件的引用,默认指向一个空对象

在子组件 div 上添加 ref=hellow, 在父组件的实例上查看子组件的 ref

子组件的 ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<template>
<button @click="add" ref="all">
{{ msg }}
</button>
</template>

<script>
import bus from '../bus';
export default {
name: 'All',
props: {
msg: {
type: [String, Number],
default: '0',
},
},
data() {
return {
msgk: this.msg,
};
},
methods: {
add() {
console.log(this.$refs);
this.$refs.all.style.color = 'red';
this.$emit('update:msg', this.msgk++);
//在发送数据的组件自定义事件,发送数据
bus.$emit('hellow', '这是all组件传过来的数据');
},
},
};
</script>

在父组件通过 refs 可以操作到子组件的方法属性值,在父组件中查阅子组件的 refs 属性

结果

操作子组件中的 data 中的数据

操作 style 需要到当前下的 all 下的 $refs.all.style

# this.$nextTick 的应用场景

当在控制两个元素的消失出现的时候元素没有展示无法获取 DOM 节点

这时候就用到了 this.$nextTick(回调函数)延迟执行(当前调用栈执行完毕后再触发回调函数,类似于定时器)也就是宏任务

把回调函数推迟到下一个 DOM 更新周期之后执行,保证回调函数可以操作到最新的 DOM 元素

# Some 循环(节省资源)

foreach 循环一开始就会执行完毕,some 更适合在数组中查找对象合适

# every 数组循环方法(用于购物车全选)

# reduce 方法 (计算总和)

1
2
reduce((累加的值,item)=>{
})

简化写法

1
2
3
4
5
6
7
8
9
const list = this.arr2
.filter((item) => item.state)
//previousValue以前的值累加的值,currentValue当前的值
.reduce(
(previousValue, currentValue) =>
(previousValue += currentValue.price * currentValue.id),
0,
);
console.log(list);

动态组件的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<template>
<div id="app">
<Left msg="Welcome to Your Vue.js App" />
<Right />
<component :is="comName"></component>
</div>
</template>

<script>
import Left from './components/Left.vue';
import Right from './components/Right.vue';
export default {
name: 'App',
components: {
Left,
Right,
},
data() {
return {
comName: 'Left',
};
},
};
</script>

<style lang="less">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
/* background-color: rgb(0, 36, 36); */
display: flex;
}

#left,
#right {
flex: 1;
height: 500px;
}
</style>

component 是组件占位符,不使用 v-bind:is 可以直接把组件名写入,使用动态的值就需要使用 v-bind 动态绑定数据,可以实现效果:点击 left,left 展示点击 right 按钮 right 展示

Keep-alive 是一个用于缓存组件的功能性组件,它可以保持组件状态或避免重新渲染。

避免离开组件时被销毁。

组件被缓存时触发 dectivted 生命周期函数,被激活时会触发 activated 函数

第一次创建的时候既会执行 created 也会执行 activated 函数,

1.includle 指定组件被缓存指定多个组件用逗号隔开

2.exclude 哪些组件不被缓存,不能与 include 同时使用

组件创建的时候没有添加 name 属性则组件名称默认时注册时的组件名称・

声明时的 name 名称是主要用于组件的缓存 keepalive 标签上实现缓存功能,以及在调试工具中看到组件名称。

---------------------------------------------------------------------------------------------------------------------------------

# 插槽 slot

主要是由组件封装者使用的,允许开发者在封装组件的时候,把不确定的、希望由用户指定的内容部分定义为插槽类似于卡槽

在 app 中的组件中间默认是插不进去内容的,但是在子组件上提前用 slot 占位,添加的内容就会添加到 slot 所在的位置里

忽略了 name 属性默认是 default

1
<slot name='default'></slot>

只有一个插槽可以省略 name,多个插槽需要制定 name,

使用的时候只能用在组件和 template 标签上,不能用在真实的标签上去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<slot name='loader'></slot>









<template v-slot:loader>




内容


</template>

这个 template 不会渲染成真实的 dom 元素,是一个虚拟的容器简写#

1
2
3
4
5
6
7
8
9
10
 
<template #loader>




内容


</template>

# 插槽 - 具名插槽(有名字(name)的插槽)

# 作用域插槽 (插槽自定义属性可以传值 【子 +》父】)

在封装属性时对预留的插槽上自定义属性值可以在父组件中用到传过来的值,就是作用域插槽

1
2
3
4
5
6
7
8
data() {
return {
user: {
name: '叔叔三韩国',
age: 20,
bosy: 185,
},
};

# 自定义指令(directive)

# (私有(组件中使用)和全局(全局使用))

私有:

1
2
3
4
5
6
7
8
9
10
//定义私有属性节点指令
directives: {
//color
color: {
//绑定到元素上的时候立即触发
bind(el) {
el.style.color = 'red';
},
},
},

查看 binding 的值发现是一个对象通过 binding.value 可以把传过来的变量的值给自定义指令

也可以直接传值进去,注意引号嵌套,expression 后面是用户传什么值就显示什么,是 = 号后面的表达式,而 value 是真正传过来的值

1
v-color="'red'"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 <GoShoping :msg="arr2"
><Footer
v-color="color"
:getFull="getFull"
:sumPrice="sumPrice"
:counts="counts"
@events="checkes"
>
</Footer>
</GoShoping>
</div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';
import GoShoping from './components/GoShoping';
import Footer from './components/Footer';

export default {
name: 'App',
components: {
HelloWorld,
GoShoping,
Footer,
},
//定义私有属性节点指令
directives: {
//color
color: {
//绑定到元素上的时候立即触发
bind(el, binding) {
el.style.color = binding.value;
},
},
},
data() {
return {
color: 'pink',

由于 bind 只在第一次绑定时触发,数据发生改变时不触发 bind

1
2
3
4
//绑定到元素上的时候立即触发
bind(el, binding) {
el.style.color = binding.value;
},

所以需要写一个 update 在 dom 更新的时候也可以改变颜色数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//定义私有属性节点指令
directives: {
//color
color: {
//绑定到元素上的时候立即触发,dom元素(第一次在页面渲染的时候触发)
bind(el, binding) {
console.log(binding);
el.style.color = binding.value;
},
//在DOM更新的时候触发(第一次不触发,DOM更新的时候触发)
update(el, binding) {
console.log(binding);
el.style.color = binding.value;
},
},
},

# 简写形式:bind 函数和 update 函数的内容是相同的可以简写成函数格式

1
2
3
4
5
6
directives:{

color(el,binding){
el.style.color=binding.value
}
}

自定义全局指令

1
2
3
4
5
6
Vue.directive('color', function(el, binding, vnode) {
// `bind`, `update`
console.log(binding);
el.style.width = binding.value + 'px'

});

Vnode

# Eslint 常用语法规范

序号规则名称规则约束 / 默认约束
1quotes默认:字符串需要使用单引号包裹
2key-spacing默认:对象属性和值之间,需要有一个空格分割
3comma-dangle默认:对象或数组末尾,不允许出现多余的逗号
4no-multiple-empty-lines不允许出现多个空行
5no-trailing-spaces不允许在末尾出现多余的空格
6eol-last默认:文件的末尾必须保留一个空行
7spaced-comment在注释中 // 或 /* 后强制使用一样的间距
8indent强制一致的缩进
9import/firstimport 导入模块必须声明在文件顶部

​ 10 space-before-function-paren 强制在方法的左括号之前是否需要使用一致的空格

vue.config.js 配置的代理服务器是实现跨域请求(服务器端不可操作)

设置一个代理服务器,它的 协议名、主机名、端口号 和前端的一模一样,这样前端访问该代理服务器就没有 跨域问题 了。 当该代理服务器收到前端的请求,再向其他服务器发送 该请求 ,获取数据,之后将数据返回给前端。 (服务器之间不是通过 ajax 请求发送信息自然也没有跨域这个问题了)

1
2
3
4
5
6
7
8
9
10
11
 await axios
.get('http://127.0.0.1:8080/article/midsen')
.then((result) => {
console.log(result)
})





1.项目运行在8080端口,服务器运行在8081端口,

代理服务器

1
2
3
4
 devServer: {
proxy: 'http://127.0.0.1:8081'
}
配置代理服务器,8081

全局挂载 axios,在 main.js 中导入 axios 插件挂载到 vue 的构造函数上,就可以在全局中使用 axios 插件了(不利于 api 接口的复用)

1
2
3
import axios from 'axios'

Vue.prototype.axios = axios

设置请求根路径

1
axios.defaults.baseUrl='请求根路径'

# 路由 Router

SPA 单页面应用和前端路由

前端路由就是 hash 地址和组建的关系

锚链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>毛i蛋白</title>
<style>
.header {
height: 55px;
background-color: white;
position: fixed;
top: 0;
right: 0;
}
#b1,
#b2,
#b3,
#b4 {
height: 600px;
}
#b1 {
background-color: yellow;
}
#b2 {
background-color: green;
}
#b3 {
background-color: blue;
}
#b4 {
background-color: purple;
}
</style>
</head>
<body>
<div class="header">
<a href="#b1">b1</a>
<a href="#b2">b2</a>
<a href="#b3">b3</a>
<a href="#b4">b4</a>
</div>

<div id="b1"></div>
<div id="b2"></div>
<div id="b3"></div>
<div id="b4"></div>
</body>
</html>

前端路由原理:用户点击路由链接导致 URL 的哈希地址发生改变,前端路由监听到 Hash 地址的变化,把对应的组件渲染到页面中去

# vue-router 路由的基本使用

1. 安装

1
2
npm view vue-router version
npm i vue-router@4.3.0

依赖项出现错误记住了 eslint 气死我了 -_-

大概意思就是依赖项版本冲突标准 6.1.0 的只支持 7.0.0 以上的但是我的是 8.0.3 以上的所以需要降到 7.0.0 执行命令:

1
npm install eslint-plugin-vue@^7.0.0

问题解决

1
2
3
4
5
6
7
8
9
10
11
S D:\down\桌面\Vue-study\rustu> npm install vue-router@3.5.2 -S

added 1 package, and audited 1060 packages in 4s

1 package is looking for funding
run `npm fund` for details

4 moderate severity vulnerabilities

To address all issues (including breaking changes), run:
npm audit fix --force

在 src 文件夹下创建 router/index.js 文件,并初始化代码

1
2
3
4
5
6
7
8
9
10
11
12
13
// 导入依赖包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,并且挂在到 vue 实例上去 --------------------------

1
2
3
4
5
6
7
8
9
10
11
12
import Vue from 'vue'
import App from './App.vue'
//导入路由实例
//index可以省略,因为在导入组件的时候默认导入文件下的index文件
import router from './router/index'
Vue.config.productionTip = false

new Vue({
render: (h) => h(App),
//挂载到vue实例
router
}).$mount('#app')

router 的使用

1
2
3
4
5
6
7
8
// 创建路由实例对象

const router = new VueRouter({
routes: [
{ path: '/home', component: Home },
{ path: '/movie', component: Movie }
]
})

app.vue

1
2
3
4
5
6
7
<template>
<div id="app">
<a href="#/home">Home</a>
<a href="#/movie">Movie</a>
<router-view></router-view>
</div>
</template>

router-link 替换普通的 a

# redirect 路由重定向

例如,当用户访问的是 / 让用户自动跳转到首页

1
2
3
4
5
6
7
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/home'},
{ path: '/home', component: Home },
{ path: '/movie', component: Movie }
]
})

# 嵌套路由 (路由视图子组件里面还嵌套了路由)

通过 children 属性声明子路由

1
2
3
4
5
6
7
8
{
path: '/home',
component: Home,
children: [
{ path: 'tab1', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
},

设置默认子路由 rediret/

1
2
3
4
5
6
7
component: Home,
redirect: '/home/tab1',
children: [
{ path: 'tab1', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
},

path 等于空(只能用于默认路由不能再次点击)

1
2
3
4
5
6
component: Home,
// redirect: '/home/tab1',
children: [
{ path: '', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]

# 动态路由匹配

动态路是指把 hash 地址中的可变部分定义为参数项,从而提高路由规则的复用性。在 vuerouter 中用:来定义路由参数项,和正常 URL 动态传参一样

1
2
3
4
5
6
7
{path:'/home/:id',component:home}





router-link to='/home/1'

打印各个 movielink 所指向的 this,$route.params 上的参数

在组件中添加 props, 在路由里面开启路由传参

1
{ path: '/movie/:id', component: Movie,props:true }

在 vue 控制台可以查看到 movie 组件上的 props 属性已经接收到了值

$route.query 获取?后面的参数

在 $route 中 path 是路径部分,fullpath 是全部路径

# 声明式导航和编程式导航

a 链接,router-link 都属于声明式导航(点击链接实现跳转的)

location.href 调用 Api 实现页面跳转的方式叫做编程式导航

# vue-router 中的编程式导航

1.this.$router.push ('hash 地址 ')(会增加一条历史记录)

1
2
3
4
goMovie() {
this.$router.push('/movie/1')
}
}

2.this.$routerr.replace ('hash 地址 ')(不会增加一条历史记录)

1
2
3
goMovie() {
this.$router.replace('/movie/1')
}

3.this.$router.go (' 正数向前负数向后 ') 页面后退或前进

1
2
3
4
5
6
7
8
9
10
11
12
13
go_1() {
this.$router.go(-1)
},
go1() {
this.$router.go(1)
}


简化用法,
后退
$router.back()
前进
$router.forward()

# 导航守卫(控制路由访问权限)

全局前置守卫(家门口的保镖)只要发生路由跳转的时候都会触发全局前置守卫

1
2
3
4
5
6
7
8
9
10
11
12
13
// 为router实例对象创建全局前置守卫
router.beforeEach((to, from, next) => {


})


//to去哪里去
//from从哪里来
//next继续向下执行

一定要记得放next不然就不放你走
全局中间件-___-

# Next 函数的调用

1. 默认有权限直接调用

1
next()

2. 没有权限,强制其跳转到其他页面

1
next('/login')

3. 没有权限不允许跳转

1
next(false)

# 控制访问权限

1
2
3
4
5
6
7
8
9
10
if (to.path === '/movie/1') {
const token = localStorage.getItem('token')
if (token) {//判断有没有token
next()//有就继续,没有就跳home
} else {
next('/home')
}
} else {
next()
}

自定义代码片段指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
注:这些都是变量名,不是宏,在实际使用的时要加上 $ 符TM_LINE_INDEX:行号(从零开始);

TM_LINE_NUMBER:行号(从一开始);

TM_FILENAME:当前文档的文件名;

TM_FILENAME_BASE:当前文档的文件名(不含后缀名);

TM_DIRECTORY:当前文档所在目录;

TM_FILEPATH:当前文档的完整文件路径;

CLIPBOARD:当前剪贴板中内容。

CURRENT_YEAR: 当前年份;

CURRENT_YEAR_SHORT: 当前年份的后两位;

CURRENT_MONTH: 格式化为两位数字的当前月份,如 02

CURRENT_MONTH_NAME: 当前月份的全称,如 July;

CURRENT_MONTH_NAME_SHORT: 当前月份的简称,如 Jul;

CURRENT_DATE: 当天月份第几天;

CURRENT_DAY_NAME: 当天周几,如 Monday;

CURRENT_DAY_NAME_SHORT: 当天周几的简称,如 Mon;

CURRENT_HOUR: 当前小时(24 小时制);

CURRENT_MINUTE: 当前分钟;

CURRENT_SECOND: 当前秒数。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Xiao Yang Guo 微信支付

微信支付

Xiao Yang Guo 支付宝

支付宝

Xiao Yang Guo 贝宝

贝宝