文章目录
- Vue创建elementUI项目
- Vue 两大特性
- Vue 指令
- 监听器(watch)
- 计算属性(computed)
- axios
- vue-cli 的使用
- 使用组件的三个步骤
- 注册全局组件
- 组件的 props
- scoped 和 deep
- 组件的生命周期
- 组件之间的数据共享
- ref 引用
- this.$nextTick(cb) 方法
- 动态组件渲染
- 插槽
- 自定义指令
- 挂载 axios 到 Vue 原型上
- 路由
Vue创建elementUI项目
vue create my-app
cd my-app
vue add element-plus
Vue 两大特性
-
数据驱动视图
- 数据的变化会驱动视图自动更新
- 好处: 程序员只管把数据维护好, 那么页面结构就会被 vue 自动渲染出来
-
双向数据绑定
- 在网页中, form 表单负责采集数据, Ajax 负责提交数据
- js 数据的变化,会自动被渲染到页面上
- 页面上表单财季的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中
注意: 数据驱动视图和数据双向数据绑定的底层原理是 MVVM(Model 数据源, View 视图, ViewModle 就是 vue 的实例)
Vue 指令
1. 内容渲染指令:用来辅助开发者渲染 DOM 元素的文本内容
- v-text(几乎不用)
- 缺点:会覆盖元素内部原有的内容
- {{}} 插值表达式(Mustache)(使用较多)
- v-html: 可以把带有标签的字符串, 渲染成真正的 HTML 内容
2. 属性绑定指令(注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!)
- v-bind 可以为元素的属性动态绑定值, 简写成冒号( : )
- 在使用 v-bind 属性绑定期间, 如果绑定内容需要进行动态拼接, 则字符串的外面应该包裹单引号
3. 事件绑定
- v-on:简写是@
- 语法格式为:
<button @click="add"></button>
methods: {
add() {
this.count += 1
}
}
- e v e n t 的 应 用 场 景 : 如 果 默 认 的 事 件 对 象 e 被 覆 盖 了 , 则 可 以 手 动 传 递 一 个 event 的应用场景: 如果默认的事件对象 e 被覆盖了, 则可以手动传递一个 event的应用场景:如果默认的事件对象e被覆盖了,则可以手动传递一个event
<button @click="add(3, $evnet)"></button>
methods: {
add(n, e) {
this.count += 1
}
}
-
事件修饰符
- .prevent 阻止默认事件
<a @click.prevent="xxx">url</a>
- .stop 阻止冒泡事件
<button @click.stop="xxx">button</button>
- .capture 以捕捉模式触发当前的时间处理函数
- once 绑定的时间只触发一次
- self 只有在 event.target 是当前元素自身时触发事件处理函数
4. v-model 指令
- input 输入框
- type=“radio”
- type=“checkbox”
- type=“xxxx”
- textarea
- select
- 为了方便对用户输入的内容进行处理, vue 为 v-model 指令提供了 3 个修饰符
修饰符 | 作用 | 示例 |
---|---|---|
.number | 自动将用户的输入值转为数值类型 |
5. 条件渲染指令
- 条件渲染指令用来辅助开发者按需控制 DOM 的显示和隐藏。
- v-if: 每次动态创建或移除元素, 实现元素的显示和隐藏(实际开发常用)
- 如果刚进入页面的时候,某些元素默认不需要被展示,而且后期这个元素很可能也不需要被展示出来,此时 v-if 性能更好
- v-if 的配套指令: v-else-if / v-else
- v-show: 动态为元素添加或移出 display:none 样式,实现元素的显示和隐藏
- 如果要频繁的切换元素的显示状态, 用 v-show 性能会更好
6. 循环渲染指令
- 要循环哪个元素就给它加 v-for
- 官方建议: 只要用到了 v-for 指令,那么一定要绑定一个:key 属性, 而且, 尽量把 id 作为 key 的值
- 官方对 key 的值类型,要求为: 字符串或数字类型
- key 的值不能重复, 否则终端会报错
<div id="app">
<table class="table table-bordered table-hover">
<thead>
<th>索引</th>
<th>Id</th>
<th>姓名</th>
</thead>
<tbody>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ index }}</td>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
</div>
监听器(watch)
- 方法格式的监听器
- 缺点 1: 无法再刚进入页面的时候, 自动触发
- 缺点 2: 如果监听的是一个对象, 如果对象中的属性发生了变化, 不会触发监听器
- 对象格式的监听器
- 好处 1: 可以通过 immediate 选项, 让监听器自动触发
- 好处 2: 可以通过 deep 选项, 让监听器深度监听对象中每个属性的变化
计算属性(computed)
- 特点
- 定义的时候, 要被定义为方法
- 在使用计算属性的时候, 当普通的属性使用即可
- 好处
- 实现了代码的复用
- 只要计算属性中依赖的数据源变化了, 则计算属性会自动重新求值!
axios
axios 是一个专注于网络请求的库
1. 发起 get 请求
axios({
// 请求方式
method: 'GET',
// 请求地址
url: 'http://www.liulongbin.top:3006/api/getbooks',
// url 中的查询参数(一般 get 请求)
params: {
id: 1
},
// 请求体参数(一般 post 请求)
data: {}
}).then(function (result) {
console.log(result);
});
// Get 请求简化
document.querySelector('#btnGet').addEventListener('click', async () => {
// 解构赋值的时候, 使用 : 进行重命名
// 1. 调用 axios 之后, 使用 async/await 进行简化
// 2. 使用解构赋值, 从 axios 封装的大对象中, 把 data 属性解构出来
// 3. 把解构出来的 data 属性, 使用冒号进行重命名, 一般都重命名为 { data: res}
const {data: res} = await axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/getbooks'
});
console.log(res.data);
});
// Get 请求模板
document.querySelector('#btnGet').addEventListener('click', async () => {
const {data: res} = await axios.get(
// 请求 url
'',
// 请求参数
{
params: {}
}
);
console.log(res.data);
});
2. 发起 post 请求
document.querySelector('#btnPost').addEventListener('click',
async () => {
// 如果调用某个方法的返回值是 Promise 实例, 则可以前面添加 await
// await 只能用在被 async 修饰的方法中
// 1. 调用 axios 方法得到的返回值是 Promise 对象
const {data} = await axios({
// 请求方式
method: 'POST',
// 请求地址
url: 'http://www.liulongbin.top:3006/api/post',
// url 中的查询参数(一般 get 请求)
params: {},
// 请求体参数(一般 post 请求)
data: {
name: 'zs',
age: 20
}
});
console.log(data);
});
// Post 请求模板
document.querySelector('#btnPost').addEventListener('click', async () => {
// axios.post('url',{/*Post 请求体数据*/})
const {data: res} = await axios.post(
// 请求 url
'',
// 请求体
{});
console.log(res);
});
3. 在 Vue 中使用 axios
methods: {
async btnGetBooks() {
const { data: res } = await axios.get('/api/getbooks')
console.log(res)
}
}
vue-cli 的使用
- 在终端下运行如下的命令, 创建指定名称的项目
vue create 项目的名称
- vue 项目中 src 目录的构成
assets 文件夹: 存放项目中用到的静态资源文件, 例如: css 样式表, 图片资源
components 文件夹: 程序员封装的, 可复用的组件, 都要方法哦 components 目录下
main.js 是项目的入口文件.整个项目的运行, 都要先执行 main.js
App.vue 是项目的根组件
使用组件的三个步骤
- 使用 import 语法导入需要的组件
import Left from '@/components/Left.vue'
- 使用 components节点注册组件
export default {
components: {
Left
}
}
- 以标签形式使用刚才注册的组件
<div class="box">
<Left></Left>
</div>
注册全局组件
在 vue 项目的 main.js 入口文件中, 通过 Vue.component()方法, 可以注册全局组件
import Count from '@/components/Count.vue'
Vue.component('MyCount', Count)
// 参数 1:字符串格式, 表示组件的"注册名称"
// 参数 2:需要被全局注册的那个组件
组件的 props
props 是组件的自定义属性, 在封装通用组件的时候, 合理地使用 props 可以极大地提高组件的复用性!
- props 是只读的: 若想要修改 props 的值, 可以把 props 的值转存到 data 中
export default {
props: ['自定义属性A','自定义属性B','其他自定义属性...']
}
// props 设置 default 值, 设置 type 值类型, 设置 required 必填项
export default {
props: {
init: {
default: 0,
type: Number,
required: true
}
},
}
scoped 和 deep
- scoped 范围样式
- deep: 样式前加 /deep/ 可以在父组件中修改子组件的样式(常用来修改第三方组件的默认样式)
组件的生命周期
生命周期(Life Cycle)是指一个组件从 创建->运行->销毁 的整个阶段, 强调的是一个时间段
- 组件创建阶段
-
new Vue() ->
beforeCreate
->created
->breforeMount
->mounted
-
new Vue() ->
- 组件运行阶段
-
beforeUpdate
->updated
-
- 组件销毁阶段
-
beforeDestroy
->destroyed
-
阶段 | 说明 | 特点 | 是否常用 |
---|---|---|---|
beforeCreate | 创建阶段的第1个生命周期函数 | 不常用 | |
created | 发起Ajax请求 (调用methods中的方法, 请求服务器的数据, 并且把请求到的数据, 转存到data*template渲染使用) | 初始化 props/data/methods | 非常常用 |
beforeMount | 极少用 | ||
mounted | 如果要操作 DOM,最早要在 mounted 阶段 | 初始化 DOM | 常用 |
beforeUpdate | 一般 | ||
updated | 当数据发生变化后, 为了能够操作最新的 DOM 结构,必须把代码写到 updated 生命周期函数中 | 常用 | |
beforeDestroy | 一般 | ||
destroyed | 一般 |
组件之间的数据共享
-
父组件向子组件共享数据
- 需要在子组件使用自定义属性(props), 在父组件中用 v-bind 传值
-
子组件向父组件共享数据
- 子组件向父组件共享数据使用自定义事件
// 子组件
methods: {
add() {
this.count += 1;
this.$emit('numChange', this.count);
}
}
// 父组件
<h1>{{ countFromSon }}</h1>
<Right @numChange="getNewCount"></Right>
data() {
return {
// 定义数据项来接收子组件传递过来的值
countFromSon: 0
};
},
methods: {
// 获取子组件传递过来的数据
getNewCount(val) {
console.log('numChange 事件被触发了!', val);
this.countFromSon = val;
}
}
- 兄弟组件之间的数据共享
- 在 vue 2.x 中, 兄弟组件之间数据共享的方案是 EventBus
// eventBus.js
import Vue from 'vue'
export default new Vue()
// 子组件
<template>
<div class="Count">
<button type="button" @click="sub">-</button>
<span class="number-box">{{ num }}</span>
<button type="button" @click="add">+</button>
</div>
</template>
<script>
import bus from '@/components/eventBus.js';
export default {
name: "Counter",
props: {
id: {
type: Number,
required: true
},
num: {
type: Number,
default: 1
}
},
methods: {
add() {
bus.$emit('share',
{
id: this.id,
value: this.num + 1
});
},
sub() {
bus.$emit('share',
{
id: this.id,
value: this.num - 1
});
}
}
};
</script>
// 兄弟组件
import bus from '@/components/eventBus.js';
created() {
bus.$on('share', val => {
this.list.some(item => {
if (item.id === val.id) {
item.goods_count = val.value;
return true
}
});
});
}
ref 引用
- 用来辅助开发者在不依赖jQuery 的情况下, 获取 DOM 元素或组件的引用
- 每个 vue 的组件实例上, 都包含一个** r e f s 对 象 ∗ ∗ , 里 面 存 储 着 对 应 的 D O M 元 素 或 组 件 的 引 用 . 默 认 情 况 下 , ∗ ∗ 组 件 的 refs 对象**, 里面存储着对应的 DOM 元素或组件的引用. 默认情况下, **组件的 refs对象∗∗,里面存储着对应的DOM元素或组件的引用.默认情况下,∗∗组件的refs 指向一个空对象**
- 可以利用 ref 实现组件之间的数据共享
<h1 ref="myh1">App 根组件</h1>
console.log(this.$refs.myh1);
this.$nextTick(cb) 方法
- 组件的$nextTick(cb) 方法, 会把 cb 回调推迟到下一个 DOM 更新周期之后执行. 通俗的理解是: 等组件 DOM 更新完成后, 再执行 cb 回调函数. 从而保证 cb 回调函数可以操作到最新的 DOM 元素
this.$nextTick(() => {
this.$refs.iptRef.focus();
})
动态组件渲染
- vue 提供了一个内置的组件, 专门用来实现动态组件的渲染
- 动态组件每次切换的时候都会重新销毁和创建-- keep-alive解决
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr/>
<button @click="comName='Left'">展示 Left</button>
<button @click="comName='Right'">展示 Right</button>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 1. component 标签是vue内置的, 作用: 组件的占位符-->
<!-- 2. is 属性的值, 表示要渲染的组件的名字-->
<!-- 3 is 属性的值, 应该是组件要再components节点下的注册名称-->
<!-- keep-alive 可以把内部的组件进行缓存, 而不是销毁组件-->
<keep-alive include="Left, Right">
<component :is="comName"></component>
</keep-alive>
</div>
</div>
</template>
data() {
return {
// comName: 表示要展示的组件的名字
comName: 'Left'
};
}
-
keep-alive 对应的生命周期函数
- 当组件被缓存时, 会自动触发组件的 deactivated 生命周期函数
- 当组件被激活时, 会自动触发组件的 activated 生命周期函数
-
keep-alive 的include 属性: 指定名称匹配的组件会被缓存, 多个组件之间使用英文的逗号分隔
-
keep-alive 的exclude 属性: 排除指定名称组件的缓存(include 和 exclude 二选一)
插槽
- 插槽(slot) 是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。
- 如果要把内容填充到指定名称的插槽中,需要使用 v-slot: 这个指令
- v-slot: 后面要跟上插槽的名称
- v-slot: 指令不能直接用在元素身上, 必须用在template标签页上(或者直接用在组件上!)
- template 这个标签, 它是一个虚拟的标签, 只起到包裹性质的作用, 但是不会被渲染为任何实质性的 html 元素
- v-slot 简写是 #
// 子组件
<!-- vue 官方规定, 每一个slot插槽, 都要有一个 name 名称, 省略默认为 default-->
<slot name="default">这是 default 插槽的默认内容</slot>
// 父组件
<Left>
<template #default>
<p>这是在Left组件的内容区域, 声明的p标签</p>
</template>
</Left>
作用域插槽
- 在封装组件时,为预留的 slot 提供属性对应的值,这种用法,叫做"作用域插槽"
// 子组件
<slot name="content" msg="hello vue" :user="userinfo"></slot>
// 父组件:用法 1
<template #content="obj">
<div>
<p>{{ obj.msg }}</p>
<p>{{ obj.user}}</p>
</div>
</template>
// 父组件:用法 2
<template #content="{msg, user}">
<div>
<p>{{ msg }}</p>
<p>{{ user }}</p>
</div>
</template>
自定义指令
1. 私有自定义指令
<h1 v-color="color">App 根组件</h1>
<h3 v-color="'red'">测试</h3>
data() {
return {
color: 'blue'
};
},
// 私有自定义指令的节点
directives: {
// 定义名为 color 的自定义指令,指向一个配置对象
color: {
// 当指令第一次被绑定到元素上的时候,会立即触发 bind 函数
// 形参中的 el 表示当前指令所绑定的那个 DOM 对象
bind(el, binding) {
console.log('触发了 v-color 的 bind 函数');
el.style.color = binding.value;
},
// 每次 DOM 更新时被调用
update(el, binding) {
console.log('触发了 v-color 的 update 函数');
el.style.color = binding.value;
}
}
}
// 如果 bind 和 update 事件一样,可以简写
directives: {
color(el, binding) {
el.style.color = binding.value;
}
}
2. 全局自定义指令
// 全局自定义指令
Vue.directive('color', function (el, binding) {
el.style.color = binding.value;
});
挂载 axios 到 Vue 原型上
// main.js
import axios from 'axios'
// 全局配置 axios 的请求根路径
axios.defaults.baseURL = 'http://localhost:8080'
// 把 axios 挂载到 Vue.prototype 上,供每个 .vue 组件的实例直接使用
Vue.prototype.$http = axios
// 今后,在每个 .vue 组件中要发起请求,直接调用 this.$http.xxx
// 但是,把 axios 挂载到 Vue 原型上,有一个缺点:不利于 API 接口的复用!!!
路由
- 路由(router)就是对应关系
- 前端路由:Hash 地址与组件之间的对应关系
- 前端路由的工作方式
- 用户点击了页面上的路由链接
- 导致了 URL 地址栏中的 Hash 值发生了变化
- 前端路由监听到了 Hash 地址的变化
- 前端路由把当前 Hash 地址对应的组件渲染到浏览器中
-
Vue-router 安装和配置的步骤
- 安装 vue-router 包
- 创建路由模块
- 导入并挂载路由模块
- 声明路由链接和占位符
// src/router/index.js 就是当前项目的路由模块
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from '@/components/Home';
import Movie from '@/components/Movie';
import About from '@/components/About';
import Tab1 from '@/components/tabs/Tab1';
import Tab2 from '@/components/tabs/Tab2';
Vue.use(VueRouter);
// 创建路由的实例对象
const router = new VueRouter({
// routes 是一个数组,作用:定义 hash 地址与组件之间的对应关系
routes: [
// 重定向的路由规则
{path: '/', redirect: '/home'},
// 路由规则
{path: '/home', component: Home},
{path: '/movie/:id', component: Movie, props: true},
{// about 页面的路由规则(父级路由规则)
path: '/about',
component: About,
redirect: '/about/tab1',
children: [
// 子路由规则
// 默认子路由:如果 children 数组中,某个路由规则的 path 值为空,字符串,则这条路由规则,叫做默认子路由
{path: 'tab1', component: Tab1},
{path: 'tab2', component: Tab2}
]
}
]
});
export default router;
<!-- 当安装和配置了 vue-router 后,就可以用 router-link 来替代普通的 a 链接了-->
<router-link to="/home">首页</router-link>
<!--只要在项目中安装和配置了 vue-router,就可以使用 router-view 这个组件了-->
<!--作用:占位符-->
<router-view></router-view>
-
路由重定向:用户在访问地址 A 的时候,强制用户跳转到地址 C,从而展示特定的组件页面。通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向。
-
动态路由:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。在 vue-router 中使用英文的冒号(:)来定义路由的参数项。
-
props: true :为路由规则开启 props 传参,从而方便的拿到动态参数的值
<!-- 当安装和配置了 vue-router 后,就可以用 router-link 来替代普通的 a 链接了-->
<router-link to="/home">首页</router-link>
<!-- 注意:在 hash 地址中,/后面的参数项,叫做"路径参数"-->
<!-- 在路由参数对象中,需要使用 this.$route.params 来访问路径参数-->
<!-- 注意2:在 hash 地址中,?后面的参数项,叫做"查询参数"-->
<!-- 在路由参数对象中,需要使用 this.$route.query 来访问查询参数-->
<!-- 注意3:在 this.$route中,path 只是路径部分,fullPath 是完整的地址-->
<!--例如:-->
<!--fullPath:/movie/2?name=zs&age=20-->
<!--/movie/2 是 path 的值-->
<router-link to="/movie/1">电影1</router-link>
<router-link to="/movie/2?name=zs&age=20">电影2</router-link>
<router-link to="/movie/3">电影3</router-link>
<router-link to="/about">关于</router-link>
Vue-router 中的编程式导航 API
- this.$router.push(‘hash 地址’)
- 跳转到指定 hash 地址,并增加一条历史记录
- this.$router.replace(‘hash 地址’)
- 跳转到指定 hash 地址,并替换掉当前的历史记录
- this.$router.go(数值 n) (前进后退)
- $router.go 的简化用法
- $router.back()
- $router.forward()
路由守卫
- 全局前置守卫
- 每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由访问权限的控制。
router.beforeEach((to, from, next) => {
// to 是将要访问的路由信息对象
// from 是将要离开的路由的信息对象
// next 是一个函数,调用 next()表示放行,允许这次路由导航
// 分析:
// 1. 要拿到用户将要访问的 hash 地址
// 2. 判断 hash 地址是否等于 /main
// 2.1 如果等于/main,证明需要登录之后,才能访问成功
// 2.2 如果不等于 /main,则不需要登录,直接放行 next()
// 3. 如果访问的地址是 /main,则需要读取 localStorage 中的 token 值
// 3.1 如果有 token,则放行
// 3.2 如果没有 token,则强制跳转到/login 登录界面
if (to.path === '/main') {
const token = localStorage.getItem('token');
if (token) {
next();
} else {
next('/login');
}
} else {
next();
}
});