Vue基础与相关使用
Vue
简单介绍
Vue是一款用于构建用户界面的JavaScript框架内,基于标准的HTML、CSS和JavaScript构建,同时提供了一套声明式的、组件化的编程模型。相比于传统的前端三件套,Vue框架提供了更为优秀的开发体验,能够帮助开发人员高效地开发用户界面。
项目创建
接下来可以创建一个Vue项目。项目创建可以通过npm来完成,在此之前我们需要确保Node.js相关环境已经安装成功。之后运行如下命令:
1 |
|
这一指令将会安装并执行 create-vue,它是 Vue
官方的项目脚手架工具。它会使用vite来进行项目构建。vite是一个轻量级、速度极快的构建工具。执行指令之后,我们将会看到诸如
TypeScript
和测试支持之类的可选功能提示,之后按照提示进行操作即可。这里需要注意的是,vite在引入组件的时候默认不支持省略.vue
,为了配合它,需要在pycharm中设置引入时保留后缀.vue
。相关设置可以查看参考文章。下面是一些目录说明:
node_modules
:第三方库public
:首页html(index.html)等,一些静态资源可能要放到这个目录下面才可以被访问到src
:源码存储assets
:项目中的静态资源,css样式表、图片资源等components
:可复用的组件main.ts
:项目的入口文件,整个项目的运行需要先执行main.ts
App.vue
:项目的根组件
在项目的实际开发过程中,我们是离不开安装外部依赖的,通常我们使用npm install
进行。在使用npm install
安装模块或插件时,有两种参数会把它们写入到
package.json
文件中去,在
package.json
里面体现出的区别就是,使用
--save
安装的插件,会被写入到
dependencies
(需要发布到生产环境)对象里面去,使用--save -dev
安装的插件,会被写入到
devDependencies
(这里的插件只用于开发环境)对象里面去。
一个比较方便的工具,可以在本地模拟服务器。
1npm install -g live-server
首先使用npm安装live-server,然后将Vue项目进行打包。项目打包之后会生成一个dist文件夹,在dist文件夹中直接输入live-server,就可以得到作为一个前端的简易服务器。
vue组件
简介
一个Vue项目的起始组件是src
目录下的App.vue
组件,这也是整个项目的根组件,这个根组件需要在main.ts
中进行注册。而一个真实的项目大多都是由一棵嵌套的,可重用的组件树构成的。组件可以理解为是一个自定义标签,其中可以放入对应的内容,同时我们可以在其他地方通过标签引用这个组件,显示相应的内容。Vue项目中的组件统一放在src/components
目录下。一个Vue组件通常遵循下面的模板:
1 |
|
在template
标签中,定义了该组件的基本内容模板,在script
标签中,定义了组件可以使用的一些相关属性和方法,其中基础的有name,data和methods,分别代表组件的名称,组件数据以及组件方法。在data中定义的数据可以在template中使用插值表达式{{}}
进行获取,同时这里的数据,如果在data中发生变化,在页面上也会同步发生变化。而在style
标签中,则定义了该组件使用的css风格。默认情况下是scoped,代表css仅在这个组件中局部生效。
组件注册
定义完成组件之后,如果需要在其他组件中使用,则需要经过一下步骤。首先使用import语法导入需要的组件,之后使用components
注册组件,之后就可以以标签形式使用注册的组件了。注意,通过components注册的是私有子组件。如果在声明组件的时候,没有为组件指定name名称,则组件的名称默认是注册时候的名称。
1 |
|
不过在<script setup>
的单文件组件中,导入的组件可以直接在模板中使用,无需注册。
如果想要全局注册,则需要在vue项目的main.ts
入口文件中,通过Vue.component()
方法来完成,这个方法接收两个参数(名称字符串,对应组件),其中名称字符串就是给组件注册的名字,也就是后面使用的标签名称。
组件数据传递
父向子传递
组件之间的相互使用形成了父子组件的关系,在一些场景中就会涉及到组件之间传值的需求。其中完成数据从父组件传递到子组件的是prop
。
在子组件中指定prop,其中中可以指定字符串数组,也可以指定对象形式(其中包括各自的名称和类型),表示在组件中可以接收到外面的数据。在组件内部的模板中,需要使用插值表达式来将属性值展开{{}}
。
1 |
|
之后父组件可以在调用的时候通过标签进行数据传递:
1 |
|
这里的关键就在于props
。props
是组件的自定义属性,在封装通用组件的时候,合理地使用props可以极大提高组件的复用性。props属性的值可以直接在插值表达式中使用,同时props是只读的。props可以使用列表,也可以使用一个对象,用于指定默认值:
1 |
|
关于props中相关命名,有如下的说明:HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名)。也就是说在向子组件传递值的时候,需要使用短横线风格。
从父级prop到子组件中的数据流动是单向的。父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
子向父传递
父组件向子组件传值,使用props。子组件向父组件传值,则可以使用自定义命令。
1 |
|
兄弟传递
兄弟组件之间的数据共享方案:EventBus
首先定义一个中间方,创建eventBus.js模块,并向外共享一个Vue的实例对象
1 |
|
在数据发送方,调用bus.$emit('事件名称',发送数据)方法触发自定义事件
1 |
|
在数据接收方,调用bus.$on('事件名称',事件处理函数)
方法注册一个自定义事件(需要在created生命周期中完成。自定义事件不同于组件和
prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。也就是说需要完全匹配。这里建议不使用驼峰命名法,而是使用小写+短横线命名法(
kebab-case )
1 |
|
动态组件
动态组件指的是组件之间的动态切换,动态的显示和隐藏。vue提供了一个内置的<component>
组件,专门用来实现动态组件的渲染
1 |
|
这里的<component>
是一个占位符,给组件占用位置。其中is
指定组件的名称,动态修改is的值,就可以完成组件的动态切换。通常是绑定事件来修改is对应的值。
默认情况下,组件切换会重新渲染,在每次进行组件切换的时候,隐藏组件会被销毁,显示组件会重新生成。我们可以使用keep-alive
防止这种情况。使用keep-alive标签将上面的component包起来,就可以把内部组件缓存而不是销毁。
keep-alive包含相关的的生命周期函数,组件被缓存(deactivated)以及组件被激活(activated)。keep-alive作为一个标签,也有一些属性:
include
:指定缓存哪些属性,多个属性使用逗号分隔。默认不指定表示缓存所有属性exclude
:指定哪些属性不被缓存,用法与include一致,但是不能和上面的include同时使用
vue指令
Vue指令是一种以v-
开头的特殊语法,下面介绍一些常用的vue指令。
v-text:写在标签的属性当中,设置标签的内容,默认写法会替换全部内容(原有的标签的内容被覆盖),可以使用插值表达式{{}}
来替换部分内容
v-html:设置标签的内嵌innerHTML,如果是普通字符,则和上面的v-text类似;如果是内嵌的html标签格式,那么可以渲染成html对应格式
v-on:为元素绑定事件,绑定触发条件以及调用函数,下面是一个示例,其中使用了一个语法糖,将v-on:
替换为@
,完整的写法为v-on:click='add'
1 |
|
v-show:根据表达式的真假,切换元素的显示和隐藏。这里v-show接收的是一个字符串,里面可以的东西最终都是转化成布尔值,例如可以写'false' 'true' 'data' 'data<1'
,也可以写表达式。原理为修改标签的display来操纵样式。并且数据改变,元素同步更新
v-if:根据表达式的真假,切换元素的显示和隐藏,效果与v-show类似,但是这个的原理是直接操作DOM树,在审查元素中直接看不到该元素了。建议频繁的切换使用v-show,反之使用v-if。前者的切换消耗更小
v-bind:设置元素的属性,多用于动态设置,来绑定元素的attribute。完整使用为在标签中添加v-bind:src="data中的一个key"
,语法糖可以将v-bind:
替换为:
。如果需要动态绑定多个值,则可以使用不带参数的v-bind
v-for:根据数据生成列表结构,用于迭代遍历生成多个标签。这个指令通常与数组结合使用。
1 |
|
v-model:获取和设置表单元素的值,注意是双向数据绑定,一般与表单元素一起使用。
1 |
|
axios
axios是一个开源的可以用在浏览器端和NodeJs的异步通信框架,主要作用是实现AJAX异步通信。当然也可以用来存取本地存在的json文件。axios专注于网络请求,以较为简单的方式进行网络请求。下面是axios的基础语法。当然在使用之前,还需要安装axios模块,同时在需要使用的地方进行引入。
1 |
|
调用axios方法得到的返回值是一个Promise对象,其中返回的不只是接口的数据,axios在请求得到数据之后,在这个数据之上进行了一层包装,因此其中还会有一系列其他参数数据。实际我们需要的业务数据放在data属性中。在获取到数据之后,我们可以在回调中进行处理。如果在获取过程中出现了错误,也可以通过catch进行获取并处理。
在axios的使用过程中,经常会结合async/await使用。async/await是一种基于Promise的解决异步的方案。async是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值。因此对async函数可以直接then,返回值就是then方法传入的函数。
await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待。await 修饰的如果是Promise对象:可以获取Promise中返回的内容(resolve或reject的参数),且取到值后语句才会往下执行;如果不是Promise对象:把这个非promise的东西当做await表达式的结果。
路由router
基本使用
路由router指的是地址与页面之间的对应关系。Vue官方推荐使用vue-router
完成路由的使用。官方文档地址为:Vue Router
(vuejs.org)。在使用之前需要安装对应的包,不过通常这一步在创建项目的配置过程中已经选中了。
要使用vue-router,首先需要配置路由模块。创建src/router/index.ts
路由模块,并书写如下的初始化代码:
1 |
|
在这个路由模块中,最重要的是routes
列表,这里面定义了所有的映射关系。例如我们可以如下定义。这里的component组件在使用之前需要引入。
1 |
|
定义好路由之后,就可以在代码中使用,分别使用vue-router提供的路由组件进行链接声明和占位符定义:
1 |
|
除了router-link
标签之外,也可以通过$router
来访问路由实例。因此我们可以调用this.$router.push()
来进行路由跳转。这点在实现按钮点击跳转的时候非常常用。这个方法可以接收是一个字符串路径,或者一个描述地址的对象,下面是官方文档中的示例:
1 |
|
动态路由
动态路由指的是可以在路由中传入参数,例如/test_3/:id
,任何形如/test_3/xxx
的路由都会进行匹配,同时我们可以获取这个id参数。我们可以在路由模块中定义动态路由:
1 |
|
这个参数可以通过$route.params.id
获取。同时我们可以通过v-bind来指定router-link中的跳转路径,使其能够动态变化:
1 |
|
views文件夹
通过vite创建出的默认项目中会存在一个views目录。这个目录中通常会存放我们已经写好的各种页面,例如login、main等。src/components
和src/views
中均包含了Vue组件,区别在于views中存放的组件通常是一些已经写好的具体页面,是页面级组件,会被至少一个router使用,而在src/components
中存放的是各种公共组件,例如header,sider等。在开发的时候通常建议将可重用的组件放在src/components
中,而将能够通过路由访问到的视图组件存放在src/views
中。
插槽Slot
Slot,意为插槽。这是vue为组件的封装者提供的能力,允许开发者在封装组件的时候,把不确定的,希望由用户指定的部分定义为插槽。我们可以把插槽认为是组件封装期间,为用户预留的内容的占位符。下面是插槽的基本使用:
1 |
|
需要注意的是,没有预留插槽的自定义内容会被丢弃。同时在封装组件的时候,可以为预留的slot插槽提供默认内容,如果组件的使用者没有为插槽提供任何默认内容,则后备内容生效。
如果在封装组件的时候需要预留多个插槽节点,则需要为每一个slot插槽指定更具体的name名称,这种带有具体名称的插槽叫做具名插槽:
1 |
|
在具名插槽中,有一些规则,没有指定name名称的插槽,会有隐含的名称叫做“defalut”。如果没有指定填充到哪一个插槽,则填充到默认插槽。同时我们可以将v-slot:header
简写为#header
在封装组件的过程中,可以为预留的slot插槽绑定props数据,这种带有props数据的slot插槽就叫做作用域插槽
1 |
|
状态管理 [deprecated]
注意,目前在Vue中推荐使用Pinia进行状态管理,vue-x的使用已经过时。Pinia的使用可以参考
在Vue中可以实现组件之间的数据共享,包括父子组件之间传值,兄弟组件之间传值。但是这种数据的传输只适合在小范围内进行,如果范围扩大的话,就会变得非常的繁琐。我们希望能有一种管理更大范围数据状态,同时复杂度较低的机制。Vuex就是这样一种实现组件全局状态(数据)管理的机制,可以方便的实现组件之间的数据共享。Vuex有如下优点:
- 能够在vuex中集中管理共享的数据,易于开发和后期维护
- 能够高效地实现组件之间的数据共享,提高开发效率
- 存储在vuex中的数据都是响应式的,能够实时保持数据与页面的同步
一般来说,只有组件之间共享的数据才有必要存储到vuex中。
Vuex的基本安装只需要使用npm install即可。
1 |
|
安装完成之后,需要导入Vuex包:
1 |
|
创建store对象:
1 |
|
将store对象挂载到vue实例中:
1 |
|
下面将介绍Vuex中的核心概念
State:state提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储。在组件中访问State数据有如下的方式:
1 |
|
Mutations:vuex不允许组件直接修改store中的公有数据,而是利用Mutations来变更store中的数据。虽然通过这种方式虽然操作起来稍微繁琐,但是可以统一监控所有的数据的变化,相当于提供一种统一管理的方法来修改store中的值
1 |
|
Actions:mutations中不能使用异步函数,触发异步任务需要actions。如果通过异步操作变更数据,必须通过Action,而不能使用Mutation,但是在Action中还是要通过触发Mutation的方法间接变更数据
1 |
|
Getters:getter用于对Store中的数据进行加工处理形成新的数据。getter可以对store中已有的数据进行加工处理之后形成新的数据,类似Vue的计算属性。Store中数据发生改变,getter的数据也会跟着变化
1 |
|
监听器
在Vue中数据的改变会实时反应在页面上,数据是双向绑定的。同时在Vue中也可以使用watch监听器来监听某个对象的变化,当对象发生变化时,就会触发对应的行动。例如在下面的例子中,我们为count数据绑定了一个监听器方法,同时可以分别接收这个数据的新值和旧值。
1 |
|
如果想要监听一个对象中的值,则可以使用相应的路径,注意这里的引号。
1 |
|
不过watch监听器默认是浅层的,只有当在被监听的属性赋新值的时候才会触发,而嵌套属性的变化并不会被监听到。例如在上面的例子中,如果我们只监听object的话,该对象中count的变化并不会触发监听器。如果想要做到嵌套属性的监听,则需要深层监听器。不过仍然需要注意的是,如果只是内部嵌套属性变化,而object本身没有变化的话,那么这里的newObject和oldObject是相同的。
1 |
|
watch相关还有一些知识,例如即使回调,回调触发时机,监听器停止等,更多细节可以参考官方文档。