Vue
1、vue概述
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架,发布于2014年2月。与其它大型框架不同的是,Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库(如:vue-router:跳转,vue -resource:通信,vuex:管理)或既有项目整合
构建用户界面
- 指令:用于辅助开发者渲染页面的模板语法
- 数据驱动视图:数据源变化,页面结构自动重新渲染
- 事件绑定:用于处理用户和网页之间的交互行为
官方给 vue 的定位是前端框架,因为它提供了构建用户界面的一整套解决方案(俗称 vue 全家桶)
- vue(核心库)
- vue-router(路由方案)
- vuex(状态管理方案)
- vue 组件库(快速搭建页面 UI 效果的方案)
以及辅助 vue 项目开发的一系列工具:
- vue-cli(npm 全局包:一键生成工程化的 vue 项目 – 基于 webpack、大而全)
- vite(npm 全局包:一键生成工程化的 vue 项目 – 小而巧)
- vue-devtools(浏览器插件:辅助调试的工具)
- vetur(vscode 插件:提供语法高亮和智能提示)
1.1、前端三板斧
- HTML (结构) :超文本标记语言(Hyper Text Markup Language),决定网页的结构和内容
- CSS (表现) :层叠样式表(Cascading Style Sheets),设定网页的表坝样式
- JavaScript (行为) :是一种弱类型脚本语言,其源代码不需经过编译,而是由浏览器解释运行,用于控制网页的行为
1.2、JavaScript框架
- jQuery:众所周知的JavaScript框架,优点是简化了DOM操作,缺点是频繁操作DOM,影响前端性能;在前端眼里使用它仅仅是为了兼容IE6、7、8
- Angular:Google收购的前端框架,由一群Java程序员开发,基特点是将后台的MVC模式搬到了前端并增加了模块化开发的理念,与微软合作,采用TypeScript语法开发;对后台程序员友好,对前端程序员不太友好
- React:Facebook 出品,一款高性能的JS前端框架;特点是提出了**新概念【虚拟DOM】**于减少真实DOM操作,在内存中模拟DOM操作,有效的提升了前端渲染效率;缺点是使用复杂,需要额外学习一门【JSX】语言
Vue
:一款渐进式JavaScript框架,所谓渐进式就是逐步实现新特性的意思,如实现模块化开发、路由、状态管理等新特性。其特点是综合了Angular (模块化)和React (虚拟DOM)的优点Axios
:前端通信框架,因为Vue的边界很明确,就是为了处理DOM,所以并不具备通信能力,此时就需要额外使用一个通信框架与服务器交互;当然也可以直接选择使用jQuery提供的Ajax通信功能
1.3、UI框架
- Ant-Design:阿里巴巴出品,基于React的UI框架
- ElementUI: iview、ice,饿了么出品,基于Vue的UI框架
- Bootstrap: Twitter 推出的一个用于前端开发的开源工具包
- AmazeUI:又叫”妹子UI”,一款HTML5跨屏前端框架
1.4、JavaScript 构建工具
- Babel:JS 编译工具,主要用于浏览器不支持的ES新特性,比如用于编译TypeScript
- webpack:前端项目工程化的具体解决方案
1.5、MVVM模式
MVVM源自于经典的MVC (Model-View-Controller) 模式。MVVM的核心是ViewModel层,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起
- 该层向上与视图层进行双向数据绑定
- 向下与Model层通过接口请求进行数据交互
MVVM已经较为成熟,当下流行的MVVM框架有vue.js
,Angular.js
1.5.1、View
View是视图层,也就是看到的用户界面。前端主要由
HTML
和CSS
来构建,为了更方便地展现ViewModel
或者Model
层的数据,已经产生了各种各样的前后端模板语言,比如FreeMarker、Thymeleaf等等,各大MVVM框架如Vue.js, AngularJS, EJS 等也都有自己用来构建用户界面的内置模板语言
1.5.2、Model
Model是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控主要围绕数据库系统展开。这里的难点主要在于需要和前端约定统一的
接口规则
1.5.3、ViewModel
ViewModel是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的Model数据进行转换处理,做二次封装,以生成符合View层使用预期的视图数据模型。MVVM框架已经把最繁琐的一块做好了,开发者只需要处理和维护ViewModel,更新数据视图就会自动得到相应更新,真正实现
事件驱动编程
View层展现的不是
Model
层的数据,而是ViewModel
的数据,由ViewModel
负责与Model
层交互,这就完全解耦了View层和Model层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环
1.6、为什么要使用vue.js
- 轻量级、体积小:压缩后只有33k
- 更高的运行效率:基于虚拟DOM,一种可以预先通过JavaScript进行各种计算,把最终的DOM操作计算出来并优化的技术,由于这种DOM操作属于预处理操作,并没有真实的操作DOM,所以叫做虚拟DOM
- 双向数据绑定:让开发者不用再去操作DOM对象,把更多的精力投入到业务逻辑上
- 生态丰富、学习成本低:市场上拥有大量成熟、稳定的基于vue.js的ui框架及组件,拿来即用实现快速开发;对初学者友好、入门容易、学习资料多
1.7、MVVM的实现者
- M:模型层(Model),表示当前页面渲染时所依赖的数据源
- V:视图层(View),表示当前页面所渲染的 DOM 结构
- VM:视图模型(ViewModel):连接视图和数据的中间件,Vue实例对象
在MVVM架构中,是不允许数据和视图直接通信的,只能通过ViewModel来通信,而ViewModel就是定义了一个Observer观察者
- ViewModel能够观察到数据的变化。并对视图对应的内容进行更新
- ViewModel能够监听到视图的变化,并能够通知数据发生改变
Vue.js就是一个MVVM的实现者。他的核心就是实现了DOM监听与数据双向绑定
核心:数据驱动视图
,双向数据绑定
,组件化
优点:借鉴了AngulaJS 的模块化开发和React的虚拟Dom ,虚拟Dom就是把Dom操作放到内存中去执行!(集大成者)
遵循SoC关注度分离原则,Vue是纯粹的视图框架,并不包含比如Ajax之类的通信功能,为了解决通信问题,需要使用Axios
框架做异步通信;
Vue的开发都是要基于Node.js,实际开发采用vue-cli脚手架
开发,vue-router路由;vuex做状态管理; Vue UI界面我们一般使用**ElementUl(饿了么出品)**来快速搭建前端项目
2、双向数据绑定
Vue.js是一个MVVM枢架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也是Vue.js较为特殊的功能
需要注意的是:v-model
会忽略所有表单元素的 value
、checked
、selected
attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data
选项中声明初始值
2.2.1、单行文本框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>数据双向绑定</title>
<!-- 导入vue.js -->
<script src="../../js/vue.min.js"></script>
</head>
<body>
<!-- 容器 -->
<div id="app">
<p>
<label>用户名:</label>
<input type="text" v-model="value"/>
<!-- {
{ }} 插值表达式 主要用于渲染标签体内容 -->
<span style="color: coral">值为:{
{value}}</span>
</p>
</div>
<script> let vm = new Vue({
el: "#app", data: {
value: "hello vue", }, }); </script>
</body>
</html>
2.2.2、多行文本
<!--view层:模板-->
<div id="app">
<!--输入框的值和{
{value}}的值动态绑定,实时刷新,可以使用v-model绑定value-->
<p>
<label>个人简介:</label>
<textarea cols="20" rows="10" v-model="value"></textarea>
<span style="color: coral">值为:{
{value}}</span>
</p>
</div>
<script> let vm = new Vue({
el: "#app", data: {
value: "hello vue", }, }); </script>
2.2.3、单选框
<!--view层:模板-->
<div id="app">
<!--输入框的值和{
{value}}的值动态绑定,实时刷新,可以使用v-model绑定value-->
<p>
<label>性别:</label>
<input type="radio" name="gender" value="男" v-model="checked"/> 男
<input type="radio" name="gender" value="女" v-model="checked"/> 女
<span style="color: coral">选中了:{
{checked}}</span>
</p>
</div>
<script> let vm = new Vue({
el: "#app", data: {
checked: "男", }, }); </script>
2.2.4、复选框
<!--view层:模板-->
<div id="app">
<!--输入框的值和{
{value}}的值动态绑定,实时刷新,可以使用v-model绑定value-->
<p>
<label>爱好:</label>
<input type="checkbox" value="swim" v-model="checkValue"/> 游泳
<input type="checkbox" value="football" v-model="checkValue"/> 篮球
<input type="checkbox" value="playGame" v-model="checkValue"/> 游戏
<span style="color: coral">选中了:{
{checkValue}}</span>
</p>
</div>
<script> let vm = new Vue({
el: "#app", data: {
checkValue: [], }, }); </script>
2.2.5、下拉列表
<!--view层:模板-->
<div id="app">
城市:
<select name="city" v-model="selected">
<option value="" disabled>---请选择---</option>
<option name="henan">河南</option>
<option name="luoyang">洛阳</option>
<option name="yichuan">伊川</option>
</select>
<p>选中了:{
{selected}}</p>
</div>
<script> let vm = new Vue({
el: "#app", // Model:数据 data: {
selected: '', }, }); </script>
2.3、单向数据绑定与双向数据绑定
单向数据绑定
- 具体语法:
v-bind:href ="xxx" 或简写为 :href
- 特点:数据只能从 data 流向页面
双向数据绑定
- 具体语法:
v-model:value="xxx" 或简写为 v-model="xxx"
- 特点:数据不仅能从 data 流向页面,还能从页面流向data
2.4、过滤器&计算属性&watch侦听
何为过滤器?
过滤器(Filters)是 vue 为开发者提供的功能,常用于文本的格式化。过滤器可以用在两个地方:插值表达式 和 v-bind 属性绑定
过滤器应该被添加在 JavaScript 表达式的尾部,由“管道符”进行调用
<script> new Vue({
el: "#app", data: {
time: new Date() }, filters: {
// 过滤器本质也是一个函数 dateFormat(value) {
// 过滤器形参中的value值,永远是管道符前面的那个值 console.log(value) // 过滤器必须要有返回值 return dayjs(this.time).format("YYYY-MM-DD HH:mm:ss") } } }) </script>
在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。 如果希望在多个 vue 实例之间共享过滤器,则可以按照如下的格式定义全局过滤器:
<script> // 使用 Vue.filter() 定义全局过滤器 // 参数一:全局过滤器的名称 // 参数二:全局过滤器的处理函数 Vue.filter('cap', (str) => {
const first = str.substring(0, 2).toUpperCase() const other = str.slice(2) return first + other }) new Vue({
el: "#app1", data: {
msg: 'mr zhao' } }) new Vue({
el: "#app2", data: {
msg: 'hello Vue.js' } }) </script>
注:一定要在Vue实例对象创建之前定义全局过滤器,如果全局过滤器和私有过滤器名称一致,按照就近原则,调用的是私有过滤器!
何为计算属性?
计算属性指的是通过一系列运算之后,最终得到一个属性值,一个数据的结果要依赖其他数据的变化来动态计算!
计算属性的特点
- 计算属性的本质是一个函数,虽然计算属性在声明的时候被定义为方法,但在使用的时候被当做属性
- 计算属性会缓存计算的结果,只有计算属性依赖的数据变化时,才会重新进行运算
- 计算属性必须返回一个计算的结果
<div id="app">
<div>
<span>R:</span>
<input type="text" v-model.number="r">
</div>
<div>
<span>G:</span>
<input type="text" v-model.number="g">
</div>
<div>
<span>B:</span>
<input type="text" v-model.number="b">
</div>
<hr>
<div class="box" :style="{ backgroundColor: getColor }">
{
{ getColor}}
</div>
<button @click="show">按钮</button>
</div>
<script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({
el: '#app', data: {
r: 0, g: 0, b: 0 }, methods: {
show() {
console.log(this.getColor) } }, computed: {
getColor() {
// 动态生成 RGB 字符串 rgb(x,x,x) return `rgb(${
this.r},${
this.g},${
this.b})` } } }); </script>
何为watch侦听?
watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作
<div id="app">
<input type="text" v-model.lazy="uname">
<span>{
{uname}}</span>
</div>
<script> new Vue({
el: "#app", data: {
uname: '小白' }, watch: {
// 函数形式 // uname(newValue, oldValue) {
// console.log(newValue, oldValue) // 判断用户名是否被占用,假定数据库的用户名是 admin // if (!newValue) return alert('用户名不能为空!') // if (newValue === 'admin') return alert('用户名已被占用,请更换一个') //console.log('ok') // }, // 对象形式 uname: {
// 立即触发当前的 watch 侦听器处理函数 immediate: true, // 开启深度监视(监视对象) deep: true, // 侦听器的处理函数 handler(newValue, oldValue) {
if (!newValue) return alert('用户名不能为空!') if (newValue === 'admin') return alert('用户名已被占用,请更换一个') console.log('ok') } } } }) </script>
- immediate 选项:默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要将 immediate 的值改为true,默认为false
- deep 选项:如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要将 deep 的值改为true,默认为false
如果要侦听对象中的某个属性变化,可以采用'对象名.属性名'
的方式!
<script> new Vue({
el: "#app", data: {
user: {
uname: 'admin' } }, watch: {
'user.uname': {
handler(newVal) {
console.log(newVal) } } } }) </script>
计算属性 vs 侦听器
计算属性和侦听器侧重的应用场景不同:
- 计算属性侧重于监听多个值的变化,最终计算并返回一个新值
- 侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值
2.5、常用内置指令
常用属性:
-
内容渲染指令
- v-text:更新元素的 textContent,会替换掉节点文本内容
- v-html:把带有 HTML 标签的字符串,渲染为真正的 DOM 元素
- {
{ }}:插值只能用在元素的内容节点,无法用在属性节点
-
条件渲染指令
- v-if:如果为 true,当前标签才会输出到页面(动态创建和移除元素)
- v-else-if:如果为 true,当前标签才会输出到页面
- v-else:如果为前面判断为 false,当前标签才会输出到页面
- v-show:通过控制 display 样式来控制显示/隐藏
-
属性绑定指令
v-bind:属性名
:绑定解析表达式,简写::href
-
双向数据绑定指令
v-model:value
:双向数据绑定,简写:v-model
,因为 v-model 默认收集的就是 value 值
-
循环渲染指令
- v-for:遍历数组/对象
-
事件绑定指令
- v-on:绑定事件监件,简写:
@事件名称
- v-on:绑定事件监件,简写:
-
v-cloak:防止闪现,与 css 配合
-
隐含属性对象:
$event
事件修饰符:
- 具体格式:
@事件名称.prevent/stop
.prevent
:阻止事件的默认行为 event.preventDefault().stop
:停止事件冒泡 event.stopPropagation().once
:事件只触发一次
按键修饰符
- 具体格式:
@事件名称.enter/delete/esc/space/tab/up/down/left/right
- keycode:获取某个键的 keyCode 值(keyCode编码),不推荐使用
- key:获取某个键的名称
表单修饰符:
- 具体格式:
@model.lazy/number/trim
- lazy:输入框的值与数据同步,默认是在输入时触发(input事件),较为频繁,添加lazy修改符,在“change”时而非“input”时更新
- number:自动将用户的输入值转为数值类型
- trim:自动过滤用户输入的首尾空白字符
组件化:
- 计算属性的特色,缓存计算数据
- 组件内部绑定事件需要使用到
this.$emit("事件名",参数);
2.6、Vue 实例生命周期
生命周期(Life Cycle)是指一个组件从创建 —> 运行 —> 销毁的整个阶段,强调的是一个时间段
生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行,强调的是一个时间点
vue 生命周期分析
- 组件创建阶段
- beforeCreate()
- created()
- beforeMount()
- mounted()
- 组件运行阶段
- beforeUpdate()
- updated()
- 组件销毁阶段
- beforeDestory()
- destoryed()
常用的生命周期方法
- created:组件在内存中被创建完毕,发送 ajax/axios最早的时期,请求数据
- mounted:组件初次被渲染到浏览器中,操作DOM的最早时期
- updated:组件在页面中被重新渲染(组件中的数据发生改变)
- destoryed:组件在页面和内存中被销毁
3、Vue-cli
3.1、什么是Vue-cli?
vue-cli是Vue 官方提供的标准化开发工具(开发平台),它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程
主要功能:
- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
需要环境:Node.js
确认nodejs安装成功:
- cmd输入
node -v
测试是否安装成功,显示版本号 - cmd输入
npm -v
测试是否安装成功,显示版本号
安装位置:C:\Users\86186\AppData\Roaming\npm
安装vue-cli
vue-cli 是 npm 上的一个全局包,使用 npm install 命令,即可方便的把它安装到自己的电脑上
- 全局安装@vue/cli(仅第一次执行),使用
vue-V
命令,查看当前脚手架版本号- npm install -g @vue/cli
- 切换到要创建项目的目录,然后使用命令创建项目
- vue create xxx
- 启动项目
- npm run serve
4、Vue 组件
4.1、前端工程化
- 模块化(js的模块化、CSS 的模块化、资源的模块化)
- 组件化(复用现有的UI结构、样式、行为)
- 规范化(目录结构的划分、编码规范化、接口规范化、文档规范化、Git 分支管理)
- 自动化(自动化构建、自动部署、自动化测试)
前端工程化指的是:在企业级的前端项目开发中,把前端开发所需的工具、技术、流程、经验等进行规范化、标准化
好处:复用编码,简化项目编码,提高运行效率,利于维护!
4.2、什么是组件?
组件是可复用的Vue
实例,它是一组可以重复使用的模板,通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册
全局注册:在 vue 项目的 main.js 入口文件中,通过方法Vue.component('注册的名称',导入的组件名称)
,注册全局组件
局部注册:使用 components 节点注册组件
4.3、单文件组件
vue 组件的三个组成部分(3 个部分)
- template —> 组件的模板结构
- script —> 组件的 JavaScript 行为
- style —> 组件的样式
其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分
Goods.vue
<template>
<div>
<h2>商品名称:{
{ gname }}</h2>
<h2>商品价格:{
{ gprice }}</h2>
<h2>商品描述:{
{ gdesc }}</h2>
</div>
</template>
<script> export default {
name: 'Goods', // 可以使用name配置项指定组件在开发者工具中呈现的名字 data() {
return {
gname: '红米K40', gprice: '1999', gdesc: '有点狠的真旗舰' } }, } </script>
<style lang="less" scoped> div {
h2 {
font-weight: normal; color: salmon; } } </style>
User.vue
<template>
<div>
<h2>欢迎你,{
{ uname }}</h2>
</div>
</template>
<script> export default {
name: 'User', data() {
return {
uname: '小白' } }, } </script>
App.vue
<template>
<div>
<!-- 使用组件 -->
<User />
<Goods />
</div>
</template>
<script> // 导入 Goods 组件 import Goods from './components/Goods.vue' export default {
name: 'App', // 可以使用name配置项指定组件在开发者工具中呈现的名字 components: {
Goods } // 注册组件(局部注册) } </script>
main.js
// 该文件是整个项目的入口文件
// 引入 Vue
import Vue from 'vue'
// 引入 App 根组件,它是所有组件的父组件
import App from './App.vue'
// 关闭生产提示
Vue.config.productionTip = false
// 导入 User 组件
import user from './components/User.vue'
// 注册组件(全局注册)
Vue.component('User', user)
// 创建 Vue 实例对象
new Vue({
el: '#app',
// 把 render 函数指定的组件,渲染到 HTML 页面中
render: h => h(App),
})
- template
- template 是 vue 提供的容器标签,只起到包裹性质的作用,它并不会被渲染为真正的 DOM 元素
- template 中只能有一个根节点
- script
- 可以在该节点中封装组件的 JavaScript 业务逻辑
- vue 组件中的 data 必须是一个函数,不能直接指向一个数据对象,因为那样会导致多个组件实例共用同一份数据的问题
- style
- 可以在该节点下编写样式,美化当前组件的 UI 结构
- 如果在<style>标签上添加 lang=“less” 属性,即可使用 less 语法编写组件的样式
4.4、组件之间的通信
组件之间的关系
在项目开发中,组件之间的最常见的关系,分为如下两种:
- 父子关系
- 兄弟关系
父子组件之间的数据共享又分为:
- 父 —> 子共享数据
- 子 —> 父共享数据
4.4.1、ref 与 props
ref 引用
ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用
每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下, 组件的 $refs 指向一个空对象
- 作用:用于给元素/组件打标识
- 读取方式:
this.$refs.xxx
<template>
<div>
<h1 v-text="msg" ref="title"></h1>
<Goods ref="gds" />
</div>
</template>
<script> // 导入 Goods 组件 import Goods from './components/Goods.vue' export default {
name: 'App', components: {
Goods }, data() {
return {
msg: '欢迎学习Vue.js' } }, mounted(){
console.log(this.$refs.title) // 获取的是真实DOM元素 console.log(this.$refs.gds) // 获取的是 Goods 组件的实例对象(vc) } } </script>
props 自定义属性
- 作用:用于父组件给子组件传递数据(通信)
- 方式一:只指定名称
props:['name', 'gender', 'age']
- 方式二:指定名称和类型
props: { name: String, gender: String, age: Number}
- 读取方式三:指定名称/类型/必要性/默认值
props: { name: {type: String, required: true, default:xxx},... }
App.Vue
<template>
<div>
<User name="赵云" gender="男" :age="18" />
</div>
</template>
<script> // 导入 User 组件 import User from './components/User.vue' export default {
name: 'App', components: {
User }, } </script>
User.vue
<template>
<div>
<h2>{
{ msg }}</h2>
<h2>学生姓名:{
{ name }}</h2>
<h2>学生性别:{
{ gender }}</h2>
<h2>学生年龄:{
{ myAge }}</h2>
</div>
</template>
<script> export default {
data() {
return {
msg: '用户信息', myAge: this.age } }, props: ['name', 'gender', 'age'], // 接收同时对数据进行类型限制 // props: {
// name: String, // age: Number, // gender: String // } // 接收的同时对数据进行类型限制+默认值的指定+必要性的限制 // props: {
// name: {
// type: String, // name的类型是 String // require: true, // name是必要的 // }, // age: {
// type: Number, // require: true // }, // gender: {
// type: String, // default: '女' // 默认值 // } // } } </script>
- 自定义属性是只读的,不能直接修改 props 的值。否则会直接报错!
- 要想修改 props 的值,可以把 props 的值转存到 data 中,因为 data 中的数据是可读可写的
- 在声明自定义属性时,可以通过 default 来定义属性的默认值
- 在声明自定义属性时,可以通过 type 来定义属性的值类型
- 在声明自定义属性时,可以通过 required 选项,将属性设置为必填项,强制用户必须传递属性的值
4.4.2、自定义事件
- 父组件中绑定自定义事件,并指定一个处理函数,子组件中触发自定义事件($emit)
Son.vue
<template>
<div>
<h2>子组件</h2>
<button @click="send(uid)">点我给父组件传递数据</button>
</div>
</template>
<script> export default {
name: 'Son', data() {
return {
uid: 8866 } }, methods: {
send(uid) {
// 通过 $emit() 触发自定义事件 // 参数一:自定义事件的名称 // 参数二:传递的值,要发送给父组件的数据 this.$emit('diyEvent', uid) } } } </script>
<style scoped> div {
width: 600px; height: 300px; margin: 0 auto; text-align: center; background-color: sandybrown; } </style>
Father.vue
<template>
<div class="father">
<h2>父组件</h2>
<Son :msg="msg" @diyEvent="getId" />
<h3>接收到子组件发送过来的数据:{
{ id }}</h3>
</div>
</template>
<script> import Son from '@/components/Son.vue' export default {
name: 'Father', components: {
Son }, data() {
return {
id: 0 } }, methods: {
getId(val) {
// 把子组件传递过来的数据赋值给父组件的数据中 this.id = val } } } </script>
<style scoped> .father {
width: 800px; height: 415px; text-align: center; background-color: salmon; } </style>
4.4.3、EventBus
- 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
- 在数据发送方,调用 bus.$emit(‘触发自定义事件名称’, 要发送的数据) 方法触发自定义事件
- 在数据接收方,调用 bus.$on(‘声明自定义事件名称’, 事件处理函数) 方法注册一个自定义事件
eventBus.js
import Vue from 'vue'
// 向外共享 Vue 的实例对象
export default new Vue()
BrotherOne.vue
<template>
<div class="b1">
<h2>兄弟组件1</h2>
<button @click="sendData()">点我向兄弟组件发送数据</button>
</div>
</template>
<script> // 导入 eventBus.js 模块 import bus from '@/eventBus' export default {
name: 'BrotherOne', data() {
return {
user: {
uname: '小白', age: 26, email: 'xiaobai@qq.com' } } }, methods: {
sendData() {
// 通过 eventBus 发送数据 bus.$emit('share', this.user) } } } </script>
BrotherTwo.vue
<template>
<div class="b2">
<h2>兄弟组件2</h2>
<h3>接收到兄弟组件的数据(user):{
{ receive }}</h3>
</div>
</template>
<script> import bus from '@/eventBus' export default {
name: 'BrotherTwo', data() {
return {
receive: {
} } }, created() {
// 接收传递过来的数据 bus.$on('share', val => {
this.receive = val }) } } </script>
数据共享三种方案
- 父—>子:自定义属性
- 子—>父:自定义事件
- 任意组件:eventBus
Vue 原型对象上包含事件处理的方法
- $on(eventName, listener):绑定自定义事件监听
- $once(eventName, listener):绑定自定义事件监听,但只能处理一次
- $emit(eventName, data):触发自定义事件
- $off(eventName):解绑自定义事件监听
解绑事件
- 解绑单个事件:
this.$off('事件名称')
- 解绑多个事件:
this.$off(['事件名称1','事件名称2','事件名称n'])
注:组件上如果绑定原生DOM事件,那么需要使用native修饰符!
5、Axios异步通信
5.1、什么是Axios?
Axios是一个开源的可以用在浏览器端和NodeJS
的异步通信框架,它是一个专注于网络请求的库,其功能特点如下:
- 从浏览器中创建
XMLHttpRequests
- 从 node.js 创建http请求
- 支持Promise API(JS中的链式编程)
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御XSRF(跨站请求伪造)
5.2、为什么要使用Axios?
由于Vue.js
是一个视图层框架,并且作者(尤雨溪)严格准守SoC (关注度分离原则)
,所以Vue.js
并不包含Ajax的通信功能,为了解决通信问题,作者单独开发了一个名为vue-resource
的插件,不过在进入2.0版本以后就停止了对该插件的维护并推荐了Axios
框架,少用jQuery,因为它操作Dom太频繁!
5.3、使用 Axios 发送请求
<body>
<button>发起GET请求</button>
<button>发起POST请求</button>
<script> const result = axios({
method: "GET", url: 'http://localhost:8086/api/getUser', // URL 中的查询参数 ?id = 1002 params: {
id: 1002 }, // 请求体参数 data: {
} }) // axios 默认返回的是一个 Promise 对象 console.log(result) result.then((resp) => {
console.log(resp.data) }) document.querySelectorAll('button')[1].addEventListener('click', async () => {
// 如果调用某个方法的返回值是 Promise 对象,则前面可以添加 await! // await 只能用在被 async 修饰的方法中 // 解构赋值的时候,使用 : 重命名 const {
data: res } = await axios({
method: "GET", url: 'http://localhost:8086/api/getUser', }) console.log(res.data) }) document.querySelectorAll('button')[0].addEventListener('click', async () => {
const {
data } = await axios({
method: "POST", url: 'http://localhost:8086/api/addUser', // 请求体参数 data: {
name: '小昭', age: 22 } }) console.log(data) }) </script>
</body>
6、动态组件与插槽
6.1、什么是动态组件?
动态组件指的是动态切换组件的显示与隐藏
vue 提供了一个内置的<component>组件,专门用来实现动态组件的渲染
<template>
<div id="app">
<p>
<button @click="cpName = 'Father'">展示 Father 组件</button>
<button @click="cpName = 'Son'">展示 Son 组件</button>
</p>
<div class="box">
<!-- is 属性的值,表示要渲染的组件的名称 -->
<!-- keep-live 会把内部的组件缓存 而不是销毁组件 -->
<keep-alive>
<component :is="cpName"></component>
</keep-alive>
</div>
</div>
</template>
<script> import Father from './components/Father.vue' import Son from './components/Son.vue' export default {
name: 'App', data() {
return {
// 默认要渲染的组件名称 cpName: 'Father' } }, components: {
Father, Son } } </script>
<style scoped> #app {
background-color: gray; padding: 20px; } .box {
display: flex; justify-content: space-between; color: white; text-align: center; } </style>
keep-alive
默认情况下,切换动态组件时无法保持组件的状态。此时可以使用 vue 内置的<keep-alive>组件保持动态组件的状态
<div class="box">
<!-- keep-live 会把内部的组件缓存 而不是销毁组件 -->
<keep-alive>
<component :is="cpName"></component>
</keep-alive>
</div>
keep-alive 对应生命周期函数
当组件被激活时,会自动触发组件的 activated 生命周期函数
当组件被缓存时,会自动触发组件的 deactivated 生命周期函数
<script> created() {
console.log('Left 组件被创建了 ^-^') }, destroyed() {
console.log('Left 组件被销毁了-_-') }, // 组件第一次被创建的时候,即会执行 created 生命周期,也会执行 activated 声明周期 activated(){
console.log('组件被激活了 ^-^') }, deactivated(){
console.log('组件被缓存了 -_-') } </script>
include 属性
include 属性用来指定:只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号,
分隔
<div class="box">
<keep-alive include="Father,Son">
<component :is="cpName"></component>
</keep-alive>
</div>
6.2、什么是插槽?
插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽
可以把插槽认为是组件封装期间,为用户预留的内容的占位符(就好比榨汁机,用户放什么水果,就榨出来什么果汁)
如果在封装组件时没有预留任何<slot>插槽,则用户提供的任何自定义内容都会被丢弃!
插槽基本使用
App.vue
<template>
<div id="app">
<h2>App 根组件</h2>
<hr>
<div class="box">
<Left>
<!-- 默认情况下,在使用组件的时候,提供的内容都会被填充到名字为 default 的插槽之中 -->
<!-- 1.v-slot 指令不能用在元素身上,必须使用template标签包裹 -->
<!-- 2.v-slot: 指令 简写为 # -->
<template #default>
<p>这是在 Left 组件定义的内容区域</p>
</template>
</Left>
</div>
</div>
</template>
<script> import Left from '@/components/Left.vue' export default {
name: 'App', components: {
Left } } </script>
Left.vue
<template>
<div class="left">
<h3>Left</h3>
<!-- 声明一个插槽区域 -->
<!-- 每一个 slot 插槽,都要有一个 name 名称-->
<!-- 如果省略了 slot 的 name 属性,则有一个默认名称叫做 default -->
<slot name="default"></slot>
</div>
</template>
<script> export default {
name: 'Left', } </script>
<style scoped> .left {
width: 500px; height: 300px; background-color: salmon; } </style>
具名插槽
没有指定 name 名称的插槽, 会有隐含的名称叫做 “default”,叫做默认插槽!
如果在封装组件时需要预留多个插槽节点,则需要为每个<slot>插槽指定具体的 name 名称。带有具体名称的插槽叫做“具名插槽”
App.vue
<template>
<div id="app">
<h2>App 根组件</h2>
<hr>
<Article>
<template v-slot:title>
<h2>增广贤文·上集</h2>
</template>
<template #content>
<p>十年寒窗无人问</p>
<p>一举成名天下知</p>
</template>
</Article>
</div>
</template>
<script> import Article from '@/components/Article.vue' export default {
name: 'App', components: {
Article } } </script>
Article.vue
<template>
<div class="article-container">
<!-- 文章标题 -->
<div class="art-title">
<slot name="title"></slot>
</div>
<!-- 文章内容 -->
<div class="art-content">
<slot name="content"></slot>
</div>
<!-- 文章作者 -->
<div class="art-author">
<slot name="author"></slot>
</div>
</div>
</template>
<script> export default {
name: 'Article' } </script>
<style lang="less" scoped> .article-container {
width: 600px; color: #fff; text-align: center; div {
height: 200px; } .art-title {
background-color: salmon; } .art-content {
background-color: slateblue; } .art-author {
background-color: sandybrown; } } </style>
7、Vue-router 路由
7.1、什么是路由?
路由(英文:router)就是 Hash 地址与组件之间的对应关系
SPA 与前端路由
SPA 指的是一个 web 网站只有唯一的一个 HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。 此时,不同组件之间的切换需要通过前端路由来实现
Vue Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成,能够轻松的管理 SPA 项目 中组件的切换,让构建单页面应用变得易如反掌
7.2、路由基本使用
安装和配置的步骤
- 安装 vue-router 包
- 创建路由模块
- 导入并挂载路由模块
- 声明路由链接和占位符
安装:npm install vue-router@3.5.2 -S
注:
- vue3对应的 vue-router 版本是4
- vue2对应的 vue-router 版本是3
创建路由模块
在 src 源代码目录下,新建 router/index.js
路由模块,并初始化如下代码:
<script> // 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 </script>
导入并挂载路由模块
在 src/main.js
入口文件中,导入并挂载路由模块
<script> import Vue from 'vue' import App from './App.vue' // 导入路由模块 拿到路由的实例对象 import router from '@/router/index' Vue.config.productionTip = false new Vue({
render: h => h(App), // 在 Vue 项目中,想要使用路由,必须把路由实例对象,挂载到 Vue 上 router }).$mount('#app') </script>
声明路由链接和占位符
在 src/App.vue
组件中,使用 vue-router 提供的<router-link>和<router-view> 声明路由链接和占位符:
<template>
<div class="app-container">
<h1>App 组件</h1>
<!-- 只要在项目中配置了 vue-router 就可以使用 router-link/router-view 这个组件 定义路由链接 -->
<router-link to="/friend">我的朋友</router-link>
<router-link to="/music">我的音乐</router-link>
<router-link to="/about">关于</router-link>
<router-link to="/download">下载客户端</router-link>
<!-- 定义路由的占位符 -->
<router-view></router-view>
</div>
</template>
声明路由的匹配规则
在 src/router/index.js
路由模块中,通过 routes 数组声明路由的匹配规则
<script> // 导入需要的组件 import Friend from '@/components/Friend' import Music from '@/components/Music' import About from '@/components/About' import Download from '@/components/Download' // 3.创建路由实例对象 const router = new VueRouter({
// routers 是一个数组,作用:用来定义路由规则 routes: [ {
path: '/friend', component: Friend }, {
path: '/music', component: Music }, {
path: '/about', component: About }, {
path: '/download', component: Download } ] }) </script>
路由重定向
路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面。 通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:
<script> const router = new VueRouter({
routes: [ // 重定向路由规则 {
path: '/', redirect: '/music' }, {
path: '/friend', component: Friend }, {
path: '/music', component: Music }, {
path: '/about', component: About }, {
path: '/download', component: Download } ] }) </script>
嵌套路由
通过路由实现组件的嵌套展示,叫做嵌套路由
声明子路由链接和子路由占位符
在 Music.vue 组件中,声明 NewSong 和 PopMusic 的子路由链接以及子路由占位符
<template>
<div class="music-container">
<h3>Music 组件</h3>
<!-- 子级路由链接 -->
<h3>歌曲分类</h3>
<router-link to="/music/new">新歌</router-link>
<router-link to="/music/pop">流行音乐</router-link>
<hr />
<!-- 子级路由占位符 -->
<router-view></router-view>
</div>
</template>
通过 children 属性声明子路由规则
在 src/router/index.js
路由模块中,导入需要的组件,并使用 children 属性声明子路由规则:
<script> // 导入所需子路由组件 import NewSong from '@/components/type/NewSong.vue' import PopMusic from '@/components/type/PopMusic.vue' const router = new VueRouter({
routes: [ {
path: '/', redirect: '/music' }, {
path: '/friend', component: Friend }, {
path: '/music', component: Music, redirect: '/music/pop', // 通过 children 属性,嵌套声明子路由规则 children: [ {
path: 'new', component: NewSong }, // 访问 #/music/new 展示 新歌 组件 {
path: 'pop', component: PopMusic } // 访问 #/music/pop 展示 流行音乐 组件 ] }, {
path: '/about', component: About }, {
path: '/download', component: Download } ] }) </script>
注:子路由不建议编写以/
开头路径,直接写映射地址值即可
动态路由匹配
动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性
在 vue-router 中使用英文的冒号(:)来定义路由的参数项
<router-link to="/music/1001">纸短情长</router-link>
<router-link to="/music/1002">我来人间一趟</router-link>
<router-link to="/music/1003">刚好遇见你</router-link>
<script> // 路由中的动态参数以 : 声明,冒号后面的是动态参数的名称(可自定义) routes: [ {
path: '/music/:mid',component: Music } ] </script>
在动态路由渲染出来的组件中,可以使用 this.$route.params 对象访问到动态匹配的参数值
<template>
<div class="music-container">
<!-- this.$route 是路由的"参数对象" -->
<h3>Music 组件 --- {
{ this.$route.params.mid }}</h3>
</div>
</template>
<script> export default {
name: 'Music' } </script>
使用 props 接收路由参数
为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参
<template>
<!-- 3.直接使用 props 接收到的路由参数 -->
<p>{
{ mid }}</p>
</template>
<script> // index.js {
path: '/music/:mid', component: Music, // 1.开启 props 路由传参,从而方便的拿到动态参数的值 props: true } // music.vue // 2.接收 props 数据 props: ['mid'], </script>
7.3、导航
声明式导航 & 编程式导航
- 在浏览器中,点击链接实现导航的方式,叫做声明式导航
- 普通网页中点击<a>链接、vue 项目中点击<router-link>,都属于声明式导航
- 在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航
- 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航
vue-router 中的编程式导航 API
vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是:
- this.$router.push(‘hash 地址’)
- 跳转到指定 hash 地址,并增加一条历史记录
- this.$router.replace(‘hash 地址’)
- 跳转到指定的 hash 地址,并替换掉当前的历史记录
- this.$router.go(数值 n)
- 实现导航历史前进、后退
调用 this.$router.push() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面
调用 this.$router.replace() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面
<template>
<div class="friend-container">
<h3>Friend 组件</h3>
<button @click="goToDownload">点我跳转到下载客户端页面</button>
</div>
</template>
<script> export default {
name: 'Friend', methods: {
goToDownload() {
// 通过编程式 API 跳转到指定的页面 this.$router.push('/download') } } } </script>
push 和 replace 的区别:
- push 会增加一条历史记录
- replace 不会增加历史记录,而是替换掉当前的历史记录
调用 this.$router.go() 方法,可以在浏览历史中前进和后退,跟原生JS history 方法基本一致!
在实际开发中,一般只会前进和后退一层页面。因此 vue-router 提供了如下两个便捷方法:
- $router.back()
- 在历史记录中,后退到上一个页面
- $router.forward()
- 在历史记录中,前进到下一个页面
<template>
<div class="friend-container">
<p>
<button @click="$router.forward()">前进</button>
<button @click="$router.back()">后退</button>
</p>
</div>
</template>
导航守卫
导航守卫可以控制路由的访问权限
全局前置守卫
每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限的控制
全局前置守卫的回调函数中接收 3 个形参
- to 表示将要访问的路由信息对象
- from 表示已经离开的路由信息对象
- next是一个函数,调用 next() 表示放行,允许本次路由导航
<script> import Vue from 'vue' import VueRouter from 'vue-router' // 导入需要的组件 import Friend from '@/components/Friend' import About from '@/components/About' import Download from '@/components/Download' // 2.调用 Vue.use() 函数,把 VueRouter 安装为 Vue 的插件 Vue.use(VueRouter) // 3.创建路由实例对象 const router = new VueRouter({
routes: [ {
path: '/', redirect: '/friend' }, {
path: '/friend', component: Friend }, {
path: '/about', component: About }, {
path: '/download', component: Download } ] }) // 4.为 router 实例对象声明 全局前置导航守卫 // 只要发送路由跳转,必定会触发 beforeEach 的 function 回调 router.beforeEach((to, from, next) => {
console.log(to) console.log(from) console.log(next) next() if (to.path === '/main') {
const token = localStorage.getItem('token') if (!token) {
// 如果没有登录 跳转到 login 页面 next('/login') } // 如果用户登录了 放行 next() } }) // 4.向外共享路由的实例对象 export default router </script>
今天的文章Vue框架分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/29837.html