vue.js初学习
Node.js
在学习VUE.js之前,非常有必要理解什么是node.js,因为vue.js依赖于node.js进行运行。
什么是node。js?
实际上node.js是一个C++编写的服务器,这个服务器使用了chrome的V8引擎来解释运行javascript,作为服务器应用。
所以node。js能够运行javascript代码,但是不同的在node。js环境下js不能使用document等浏览器才有的环境,可以直接使用http对象,等等于浏览器环境有一点不同。
什么是npm?
npm是一个简写,全称是node package manager意思是node的包管理器。事实上npm已经成为了世界上最大的软件包仓库。
在安装好node.js之后,可以直接使用npm命令下载仓库中的软件包。
npm install moment
什么是Vue?
Vue和Vue.js不同,Vue包含Vue.js,这里先介绍一下Vue.js是什么.
Vue.js是什么?
Vue.js是一个javaScript库,包含很多的js对象,函数…
这个库不依赖于其他的库,可以独立引入使用。
Vue.js是vue框架的核心
什么是vue框架?
vue框架是开发前端的一套工具,包含html,js,css的开发,使用vue语法来加速开发,最后通过编译(vue CLI等工具)生成html,css,js文件
将这些文件部署在服务器上即可,事实上他们就是普通的静态资源文件.所以可见vue框架的适用性.
更流行的方式
现在的web开发更倾向于前后端分离设计.
这意味着,前端(静态资源)和后端API是独立开发,部署和运行的.
解释一下独立部署运行,意思是可能运行在不同的服务器上,这意味着后端不能直接操作,返回资源,而更多的是被动回应前端的数据请求(返回json).
所以需要使用到RESTful API形式
在 Vue.js 应用中,通过 AJAX 请求或者现代的 Fetch API 与 Java 后端的 API 进行通信。这包括发送 HTTP 请求到后端,并处理后端返回的数据。
跨域资源共享(CORS)处理: 由于前后端分离架构,前端和后端可能运行在不同的域名下,因此可能会遇到跨域问题。在 Java 后端中进行相应的跨域资源共享(CORS)配置,以允许来自 Vue.js 前端的跨域请求。
前后端分离的架构,其中Vue.js应用程序作为一个独立的客户端应用程序,与后端API进行通信,而后端API则托管在另一个域名下的服务器上。
例如,你可以将Vue.js应用程序部署在www.example.com
,而后端API则托管在api.example.com
,两者分别使用不同的域名。这样做的好处包括更好的代码分离、更灵活的部署选项以及更好的性能和安全性管理。
跨域访问攻击
跨站请求伪造(Cross-Site Request Forgery,CSRF)攻击是一种网络安全攻击,它利用了用户当前已经认证的会话来执行未经用户授权的操作。攻击者通过引诱用户访问恶意网站或者点击包含恶意代码的链接,来触发用户在已登录的受信任网站上的操作。
具体来说,CSRF 攻击通常包括以下步骤:
- 用户登录受信任网站: 用户在浏览器中登录了一个受信任的网站,并获得了有效的会话凭证,如 Cookie 或 Session ID。
- 攻击者构造恶意请求: 攻击者在另一个网站上(通常是恶意网站)构造了一个恶意请求,其中包含了对目标网站的操作请求,如修改用户账户信息、发起资金转账等。
- 用户访问恶意网站: 用户在浏览器中访问了包含恶意请求的网站,触发了该请求。
- 浏览器发送请求: 由于用户已经在目标网站登录并具有有效的会话凭证,浏览器会在发送请求时自动携带会话凭证。
- 目标网站接收请求: 目标网站收到了包含恶意操作请求的请求,并认为是合法的请求,因为请求包含了有效的会话凭证。
- 执行未经授权的操作: 目标网站在接收到恶意请求后,执行了其中包含的操作,导致了未经用户授权的操作的发生,如修改用户信息、发起资金转账等。
为了防范 CSRF 攻击,常见的防御方法包括使用 CSRF Token、检查请求的来源头(如 Origin 头)等。
所以不允许跨域请求的原因是因为容易被恶意网站绑架客户端发请请求而用户不知道的情况
安全存储: CSRF Token 应该以安全的方式存储在客户端,并且不能被轻易获取或篡改。通常情况下,CSRF Token 应该存储在不可访问于 JavaScript 的 HttpOnly Cookie 中,这样可以防止恶意脚本访问 Token。HttpOnly cookie使得这个cookie只能在当前浏览器使用,而不被远程获取是吗?
Vue的基本语法
实际上Vue一款javascript前端框架,便于用户轻松地构建交互性强、可维护性高的现代 Web 应用
文本插值
1 |
|
基础语法
文本插值,就是在innerText中使用语法从vue中实例中获取文本,进行替换
具体的语法就是
双花括号
,还可以在双括号里面写javascript代码
v-bind 指令 (可以简写为:)
前面是文本替换的解决方案,而v-bind就是标签属性替换的解决方案.
- 属性值替换
我们使用 v-bind:原属性
这样的属性 性
这样就需要通过vue.js进行解释才能获得属性具体的值
1 |
|
可以注意到,实际上无论是文本还是属性替换都是共用vue实例中的data()返回对象中的属性的
v-bind与文本替换不同的一点是,v-bind中的值,那串字符串本身就是一段javascript代码,vue会执行
v-bind是智能检测值位置的字符串,如果是在vue中定义好的属性,那么就会进行替换
1 |
|
如果在vue实例中有size属性,那么就会将size替换,如果没有就会设置为默认值
- 使用javascript语法
在这个字符串内,也支持javascript语法
1 |
|
- 使用
对象
表示是否启用
1 |
|
原来这里写的应该是v-bind:class=”myClass”,现在可以通过这种形式来选择是否启用这个属性
1 |
|
v-if指令
v-if可以使用vue实例中的值,来进行dom,template中的标签 的控制
例如
1 |
|
这里v-if通过判断后面的boolean值来工作,所以后面的实际上是javascript表达式,不过会先经过vue的解析
1 |
|
像这种直接使用的javascript原生的函数也是可以的
一共有这么几种
指令 | 详细信息 |
---|---|
v-if |
可以单独使用,也可以与 v-else-if 和/或 v-else 一起使用。 如果 v-if 内的条件为”true”,则不考虑 v-else-if 或 v-else 。 |
v-else-if |
必须在 v-if 或另一个 v-else-if 之后使用。 如果 v-else-if 内的条件为”true”,则不考虑后面的 v-else-if 或 v-else 。 |
v-else |
如果 if 语句的第一部分为 false,则这部分将会发生。 必须放置在 if 语句的最末尾,在 v-if 和 v-else-if 之后。 |
v-show指令
这个是改变元素的可见性,不会改变dom文档,所以更加快捷,相比v-if
不过v-show只有两种,show与hidden
语法和v-if是是一样的
v-for指令
会循环在dom中生成节点
1 |
|
注意到这里定义了一个数组,使用了javascript中的in语法
v-for中的表达式是一个vue表达式,会先经过vue解析变成javascript再执行
x in manyFoods
实际上变成了 const x; for(x in manyFoods)
这令定义的变量与在data()返回的对象中定义的属性是同等地位,等都能v-bind之类的匹配到
实际上可以获得更多信息
1 |
|
将当前循环,赋予给一个元组,可以得到当前的迭代对象和在数组中的索引
vue事件
事件响应是javascript的一大功能,所以vue也有支持
vue有三种方式来全方位支持事件响应
- Vue
v-on
指令 最基本的 - Vue 方法 就是指令中调用方法
- Vue
v-on
修饰符 控制了事件的粒度
v-on指令(可以简写为@)
很像普通的javascript时间响应,但是这里可以调用vue实例内部的函数
v-on:原事件
=”表达式
“
1 |
|
也可以是调用vue中定义的方法
v-on:原事件
=”方法名
“
1 |
|
注意这里在vue实例中定义方法的语法是
data()方法的级别中,有一个methods属性,这个属性中定义方法对象
方法对象中有很多方法,这些方法以属性的形式存在于方法对象中
传递参数
我们的函数可以传递一些参数
例如,定义成这样子
1 |
|
也可以传递多个参数
传递参数和事件对象
但是当我们既要又要的时候,我们就需要注意语法,不然编译器不知道event是事件对象还是形参
1 |
|
主要就是在传递的时候使用$event
来传递事件对象
事件修饰符
主要是为了更加精确的监听某一个事件
例如,我要监听enter键按下的情况
1 |
|
按键修饰符 | 详细信息 |
---|---|
.[*Vue key alias*] |
Vue 中最常见的键都有自己的别名:.enter``.tab``.delete``.esc``.space``.up``.down``.left``.right |
.[*letter*] |
指定按下该键时出现的字母。 例如:使用 .s 键修饰符来监听”S”键。 |
.[*system modifier key*] |
.alt 、.ctrl 、.shift 或 .meta 。 这些键可以与其他键结合使用,或者与鼠标单击结合使用。 |
可以任意组合.例如
1 |
|
除了按键修饰符之外,还有
鼠标修饰符
可以使用 .left
, .center
或 .right
修饰符表示具体按下哪一个键
1 |
|
.prevent
修饰符可以防止点击的时候出现默认下拉菜单
实际上可以通过event.preventDefault()来阻止出现默认响应,但是使用事件修饰符更加可读和维护
1)prevent: 阻止默认事件(常用);
2)stop:阻止事件冒泡(常用);
3)once:事件只触发一次(常用);
4)capture:使用事件的捕获模式;
5)self:只有event.target是当前操作的元素时才触发事件;
6)passive: 时间的默认行为立即执行,无需等待事件回调执行完毕。
vue 表单
表单当然是很重要的一块了,先看看下面的段代码
1 |
|
注意v-model提供的是双向绑定,也就是可以在vue中改变自身字段值从而改变文档中的输入框中的值
提供的双向绑定 v-model
:
v-model
当 HTML 输入改变时更新 Vue 实例数据v-model
当 Vue 实例数据发生变化时也会更新 HTML 输入
v-model指令
根据上面的例子可以知道v-model是一个与表单元素相互绑定的指令
vue css绑定
实际上就是之前提到的v-bind:style
以及v-bind:class
需要注意的是,v-bind:style是修改的元素的内联样式,优先级非常高
以下是关于使用 v-bind:class
和 v-bind:style
的不同方面,我们之前在本教程中没有见过:
- 当 CSS 类分配给同时具有
class=""
和v-bind:class=""
的 HTML 标签时,Vue 会合并这些类,而不是二选一。 - 包含一个或多个类的对象会被分配
v-bind:class="{}"
。 在对象内部,可以打开或关闭一个或多个类。
-> 记得v-bind:class=”{yourClass: false}”吗,可以用来启用类,这里一个类就是一个属性. - 对于内联样式 (
v-bind:style
),定义 CSS 属性时首选驼峰命名法,但如果将其写在引号内,也可以使用”kebab-case”。例如 v-bind:style=”{‘font-size’:100px, fontSize:100px}” - CSS 类可以使用数组/数组表示法/语法进行分配
<div v-bind:class="[{ impClass: isImportant }, 'yelClass' ]"> 此 div 标签属于一个或两个类,具体取决于 isImportant 属性。</div>
因为我们知道,v-bind:class中的类可以用{}表示,那么;类值集合就要用另外的符号,所以就用了数组的括号
vue 计算属性
实际上就是要我们不要直接引用vue实例中的字段值,而是要通过方法来访问,这样有很多的好处
但是计算属性和methods重定义的方法有一些不同
在 Vue 中,computed 和 methods 都是用来定义方法的,但有一些重要的区别:
- computed:
- 计算属性 (computed) 是
基于它们的依赖进行缓存
的。 - computed 方法是响应式依赖追踪的,只有依赖数据
发生改变时,才会重新计算
。 - computed 属性本质上是一个 getter 函数,
会返回计算后的值
,类似于 data 中的数据属性。
- 计算属性 (computed) 是
- methods:
- 方法 (methods) 每次被调用时都会执行代码块。
- methods 中的函数在每次触发时都会执行,
不会进行缓存
。 - methods 主要用于处理用户交互,或
执行一些具体的操作
。
因此,如果需要根据依赖动态计算属性值,并且希望这个值在依赖没有改变时进行缓存,建议使用 computed。而如果需要在用户交互或事件触发时执行某些逻辑操作,则应该使用 methods。
1 |
|
所以一定要注意我们将methods和computed区分开定义
另一点
如果methods中的方法没有写()表示引用函数地址而不用调用函数,而computed中不写()也表示调用
Vue watch观察者
主要用于记录旧值
就例如我们数据库中的触发器,有一个new和old表一样,保存值给我们使用
具体的语法是
1 |
|
有一个特殊的属性,watch,这个对象中定义一个与实例中属性同名属性的方法,两个参数,第一个参数是新值,第二个参数是旧值
当属性的值发生变化的时候,这个函数就是被调用
观察者和方法都写成函数,但有很多区别:
- 方法从 HTML 调用。
- 方法通常在事件发生时被调用。
- 方法自动接收事件对象作为输入。
- 我们还可以将我们选择的其他值发送给方法。
- 观察者仅在观察的数据属性值发生变化时才会被调用,并且这种情况会自动发生。
- 观察者自动从被观察的属性接收新值和旧值。
- 我们无法选择以观察者作为输入发送任何其他值.
所以观察者中定义的方法不是给我们自己调用的,是给程序自动调用的
观察者只能依赖一个属性的改变
Vue 拓展
为了开发更加大型的项目(大型项目往往有更为复杂的html,css部分),获得更好的开发环境
就像jsp,将html作为主题,将逻辑代码作为修改一样,vue提供了新的文件格式来编写vue代码
一种就是SFC
,另一个是*.vue
文件
简单理解*.vue
文件组成
所有 *.vue 文件仅由三部分组成:
<template>
代表 HTML 内容所在的位置。<script>
代表我们的 Vue 代码。<style>
代表我们编写 CSS 样式的位置。
如何起作用?
SFC和.vue文件不能直接被浏览器理解运行,所以我们编写的SFC和.vue文件需要先经过编译,得到对应的 .html、.css 和 .js 文件,以便浏览器可以运行我们的 Vue 应用程序。
所以我们需要下载编译器和支持vue模板语法的编辑器
这里使用的是vscode和vue的官方插件volar
- 先下载Vs code
有许多不同的编辑器可用于 Vue 项目。 我们使用 VS Code 编辑器。 下载 VS Code 并安装。
- 添加volar插件
要在编辑器中使用 *.vue 文件突出显示和自动完成,请打开 VS Code,转到左侧的”扩展”。 搜索”Volar”并安装下载次数最多且描述为”Vue 3 的语言支持”的扩展。
- 下载node.js
这是因为我们的构建工具,编译器vite要使用node.js
下载并安装最新版本的 Node.js,因为 Vue 构建工具”Vite”在此基础上运行。
Node.js 是一个开源服务器端 JavaScript 运行时环境。
创建我们的项目
使用终端,进入到我们的工作目录
- 初始化vue项目
1 |
|
- 填写项目配置
会让你填写项目名称,项目是否支持typescript语法之类的…
项目名中不要存在大写
新手项目我们直接都选择否即可
- 现在项目创建完毕,安装依赖运行项目
我们先进入项目内部cd firstsfc
然后安装依赖npm install
或者cnpm install
(cnmp install是npm的国内镜像,安装的更快)
然后使用开发服务器node.js中的,运行这个项目npm run dev
然后就可以看到输出了项目的网址
项目文件快速介绍
在我们创建的vue工程中,可以看到在顶级目录下src文件夹中的文件就是源文件
main.js
告诉vite构建器如何基于App.vue(这个里面就是vue实例代码)构建vue项目,告诉了这个项目由那些文件构成
我们在下面的index.html中导入这个main.js即可,实际上在导入的时候main.js已经编程编译后的js文件了
1 |
|
可以看到,变成了对于本地.vite中的vue.js的导入
App.vue也变成了对于编译后的文件的导入
就和我们之前使用script标签给出CDN链接告诉浏览器如何运行我们的vue代码是一样的,同时告诉浏览器如何将vue实例挂载到标签上
App.vue
在同一示例项目文件夹中,找到”App.vue”文件并将其打开。 与所有其他 *.vue 文件一样,”App.vue”包含三个部分:<script>
部分、<template>
部分和 <style>
部分。
页面介绍
在App.vue中,一共有三部分
<template>
<script>
<style>
template标签中书写我们的html的内容
在script中直接书写vue实例中的内容
style中书写css样式
1 |
|
注意,export default
使得 ‘main.js’ 可以用 import App from './App.vue'
捕获数据,以便将其挂载到 ‘index.html’ 内的 <div id="app">
标签上。
但是这是之前的选项式API风格
在Vue3之后推出了组合式API,就不需要使用export default了
可以直接通过defineProps,defineComponent直接定义
Vue组件
使得我们的可以动态拓展我们的网页
1 编写组件
创建一个组件并将其添加到我们的项目中。
- 在
src
文件夹内创建一个新文件夹components
。 - 在
components
文件夹中,创建一个新文件FoodItem.vue
。 通常使用 PascalCase 命名约定来命名组件,不带空格,并且所有新单词都以大写字母(也是第一个单词)开头。 - 确保
FoodItem.vue
文件如下所示:
1 |
|
每一个组件自成一派
有自己的html表现
有自己的vue实例逻辑
有自己的css样式
小坑提示
实际上标签自己会默认加上一层
所以我们在使用css定位template中的元素的时候,注意最外层有一层没写出来的div标签包裹
2 添加组件
在main.js中,先导入组件,再向vue实例中添加组件
1
2
3
4
5
6
7
8
9
10
import { createApp } from 'vue'
import App from './App.vue'
// 导入组件
import FoodItem from './components/FoodItem.vue'
const app = createApp(App)
// 添加组件
app.component('food-item', FoodItem)
app.mount('#app')添加组件的函数中的,第一个参数是组件的标签名
添加了该组件,以便我们可以将其用作
App.vue
文件中<template>
标签内的自定义标签<food-item/>
,如下所示:
1
2
3
4
5
6
7
8
9
10<template>
<h1>Food</h1>
<food-item/>
<food-item/>
<food-item/>
</template>
<script></script>
<style></style>单独的组件
意思是每个组件的
<script>
中的vue代码只管这个组件中的<template>
所以不需要额外指定ID之类的
<style>
标签中的css样式也只作用于当前的组件templatevue props属性
我们的组件可以接受来自容器传入的参数,就像函数一样,我们需要定义接受参数的列表
我们可以在任何我们想要的地方使用这些 props,就像使用数据属性一样。
但是prop是readonly
具体的语法就是
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
export default {
props: [
'foodName'
//注意这里要是字符串,必须使用单引号引起来
],
data(){
return {
//这里不能再定义同名变量,否则会冲突
}
}
}
</script>props属性,定义了一个数组,这个数组中的元素就是我们组件所代表的那个元素标签的prop属性
Props 属性用破折号
-
编写,以分隔<template>
标签中的单词(kebab-case),但 kebab-case 在 JavaScript 中是不合法的。 因此,我们需要在 JavaScript 中将属性名称编写为驼峰命名法,Vue 会自动理解这一点!
1
2
3
4
5
6
<template>
<h1>Food</h1>
<food-item food-name="Apples"/>
<food-item food-name="Pizza"/>
<food-item food-name="Rice"/>
</template>在html中使用这个prop属性的时候就要使用破折号的形式
boolean类型的prop属性
只需要在我们使用prop属性传值的时候加上v-bind即可
1
<MyComponent message="big message" v-bind:message-expired="true"/>
例如这里的message-expired和其他的prop没什么不同,只是我加上了v-bind传值的时候就会将这个属性当做boolean类型进行传递
props界面
我们需要设置属性为必须传递,或者验证prop属性是否是合法值
这个就叫做props界面,实际上就是对prop的一系列校验和加工处理
除了 prop 名称之外,我们还可以定义每个 prop 的数据类型,如下所示:
通过对象定义prop
1
2
3
4
5
6
7
8
9
10
<script>
export default {
// props: ['foodName','foodDesc','isFavorite']
props: {
foodName: String,
foodDesc: String,
isFavorite: Boolean
}
}
</script>这个对象中的属性名就是我们的prop名,属性的值就是prop的类型
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<template>
<p v-show="expired">message is :<br/>{{ message }}</p>
<div v-on:click.prevent="clickDiv">
点击次数为{{ count }}
</div>
</template>
<script>
export default {
props:{
message:String,
messageExpired:Boolean
},
data(){
return {
count:0,
expired: this.messageExpired
};
},
methods:{
clickDiv(){
this.count++;
this.expired=!this.expired;
}
}
};
</script>
<style>
div div:hover{
cursor: pointer;
}
div div{
border: 1px red solid;
}
</style>小坑
必须使用this关键字
在 JavaScript 中,
this
关键字用于引用当前对象。在 Vue 组件的方法中,this
关键字指向 Vue 实例,因此您需要使用this
来访问组件的属性和方法。在您提供的代码中,如果省略了
this
,比如直接写expired = !expired;
,那么 JavaScript 将会尝试在当前作用域中查找名为expired
的变量。由于您并没有在该作用域中定义expired
变量,JavaScript 将会抛出一个错误,指示找不到变量。因此,在 Vue 组件的方法中,如果要访问组件的属性或方法,必须使用
this
关键字来引用 Vue 实例的上下文,这样才能正确访问到组件内部的数据和方法。修正后的代码中使用了
this.expired
和this.count
来引用组件的数据属性,确保了正确的访问方式。如果没用使用规定类型传值,在浏览器的控制台会输出警告信息
必要的prop
我们使用组件必须传递的prop属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
export default {
// props: ['foodName','foodDesc','isFavorite']
props: {
//可以看到关键就是这个值又变成了对象,这个对象有一个Type属性和一个required属性
foodName: {
type: String,
required: true
},
foodDesc: String,
isFavorite: Boolean
}
}
</script>设置默认值
实际上和上面的设置required是一样的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
export default {
props: {
foodName: {
type: String,
required: true
},
foodDesc: {
type: String,
required: false,
//设置default
default: 'This is the default description.'
}
isFavorite: {
type: Boolean,
required: false,
default: false
}
}
}就是有一个default属性
Props 验证器函数
我们还可以定义一个验证器函数来决定 prop 值是否有效。
当我们在开发者模式下运行页面时,无效的 prop 值会在浏览器控制台中生成警告,该警告是确保按预期使用组件的有用提示。
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
<script>
export default {
props: {
foodName: {
type: String,
required: true
},
foodDesc: {
type: String,
required: false,
default: 'This is the default description.',
//有一个验证属性 validator
validator: function(value) {
if( 20<value.length && value.length<50 ) {
return true;
}
else {
return false;
}
}
}
isFavorite: {
type: Boolean,
required: false,
default: false
}
}
}
</script>修改 Props
前面提到了,组件接收到的prop值是只读的,所以实际上我们没法修改
但是我们可以不直接使用prop的值,而是通过自定义属性来代替引用prop值,从而通过修改自定义属性达到相同的效果
vue v-for组件
之前有学过v-for,知道v-for是用来迭代生成文档元素节点的
现在有一个问题,当我们的属性发生变化,需要重新更新v-for的节点时候
有两种选择
- 全部重新生成
- 重用之前的dom元素
vue选择了第二种,但是会出现一些问题,重用dom节点可能导致出现问题
我们可以给dom节点一个key属性来区分不同的dom节点,这样的话vue就可以定位不同的dom节点而不受为之影响
1
2
3
4
5
6
7
<food-item
v-for="x in foods"
:key="x.name"
:food-name="x.name"
:food-desc="x.desc"
:is-favorite="x.favorite"
/>这里就是加上了一个key属性,使得vue能够正确处理dom的变化,对于dom的复用也没有影响
$emit()方法
有时候自带的事件并不能满足我们的需求,我们需要自定义一些事件,发出并捕获
所以这个$emit()方法就是自定义事件的关键方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default {
props: ['foodName','foodDesc','isFavorite'],
data() {
return {
foodIsFavorite: this.isFavorite
}
},
methods: {
toggleFavorite() {
// 这里就是发出了一个名为toggle-favoite的事件
this.$emit('toggle-favorite');
}
}
};在组件外边进行捕获
1
2
3
4
5
6
7
8
9
10
<div id="wrapper">
<food-item
v-for="x in foods"
:key="x.name"
:food-name="x.name"
:food-desc="x.desc"
:is-favorite="x.favorite"
<!-- 这里就是vue的捕获代码 -->
@toggle-favorite="receiveEmit"/>
</div>除了发出事件之外,我们还希望事件携带一定的信息
我们可以直接在$emit(‘事件名称’, 参数1, …)这样来传递参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
methods: {
toggleFavorite() {
// 就像这样直接传递
this.$emit('toggle-favorite', this.foodName);
}
}
...
//事件的处理函数也可以直接接受,通过参数位置来确定参数的传递
methods: {
// 直接写上参数
receiveEmit(foodId) {
alert( 'You clicked: ' + foodId );
}
}emits选项
就像props一样,我们可以用一个emits属性来记录我们的组件会发生事件
1
2
3
4
5
6
7
8
9
10
11
12<script>
export default {
props: ['foodName','foodDesc','isFavorite'],
// 最外层是数组的形式,实际上的元素是对象的形式,如果是字符串最自动包装成对象
emits: ['toggle-favorite'],
methods: {
toggleFavorite() {
this.$emit('toggle-favorite', this.foodName);
}
}
};
</script>Vue Fallthrough 属性
我们可以在调用组件的时候传入样式,这个时候从外部传入的样式对于组件来说就是fallthrough属性
包括class和style属性都是fallthrough
如果不做任何事情,那么外部传入的样式只能给一个元素,如果这个元素是在最外边的话
1
2
3
4
5
6
7
8<template>
<!-- 只有一个元素 最外层 -->
<div>
<div> this is first div in component.</div>
<p> and there have a p</p>
<div> this is end of component.</div>
</div>
</template>如果是这样的话,外部的样式会直接应用到最外层的div
1
2
3
4
5
6<template>
<!-- 最外层有多个元素 -->
<div> this is first div in component.</div>
<p> and there have a p</p>
<div> this is end of component.</div>
</template>但是如果是这样,外部传入的样式就会丢失,因为vue无法确定你想要将样式应用到哪一个标签上
所以我们可以通过$attrs来指定应用样式到哪一个标签上
就像这样
1
2
3
4
5<template>
<div v-bind="$attrs"> this is first div in component.</div>
<p> and there have a p</p>
<div v-bind="$attrs"> this is end of component.</div>
</template>样式将会用到两边的div上而不会影响中间的p标签
vue 作用域样式
为了避免一个组件中定义的样式影响到其他组件
我们可以在<style>标签中加上 scoped属性,就下面这样
1
2
3
<style scoped>
...
</style>vue 组件
我们定义的组件,被添加到哪里是很重要的,在之前的main.js里面可以看到,实际上我们的组件是app.component()中添加到全局去了,所以app中的所有组件相互可见,可以调用
如果我们只想要组件被一些组件可见,我们可以使用局部组件
局部组件
我们可以直接在一个vue文件里面通过import导入组件,这样这个组件就只有本vue组件导入了,其他组件都不可见
该组件只能在该文件中本地访问。
我们可以将导入的组件写在components属性里,这个属性的值是一个数组,数组元素就是组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<h3>Local Component</h3>
<p>CompOne.vue 组件是局部组件,只能在 App.vue 内部使用。</p>
<comp-one />
<comp-two />
</template>
<script>
import CompOne from './components/CompOne.vue';
export default {
// 关键在这里#v#
components: {
'comp-one': CompOne
}
}
</script>vue 插槽 slot
之前我们可以通过props来传递一些简单的属性值
这里的插槽,可以让我们传递html内容,帮助我们构建html也就是<template>标签
实际上的内容被父级组件放在组件的标签内部就是inner Text那部分,然后组件**使用<slot>标签(占位)**表示父级写的东西
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<template>
<div>
<!-- 写在inner Text中的html会被替换到<slot> -->
<slot></slot>
</div>
</template>
<script></script>
<style scoped>
div {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
border-radius: 10px;
margin: 10px;
}
</style>设置默认值
如果父级调用没有写inner Text,那么组件中的<slot>标签默认值就是标签中的内容
1
2
3
4
5
6
7
8<template>
<div>
<slot>
<!-- 如果没有,这里就会代替 <slot> -->
<h4>This is fallback content</h4>
</slot>
</div>
</template>vue 的 v-slot指令
如果一个组件中有多个插槽,我们需要将内容放到指定插槽内,通过指令
我们可以给不同的插槽,给一个name属性,然后在传入内容的时候指定name
就像这样
1
2
3
4
5
6
7
8
9
10<!-- 在定义的时候像这样 -->
<h3>组件</h3>
<div>
<slot name="topSlot"></slot>
</div>
<div>
<slot name="bottomSlot"></slot>
</div>
<!-- 在调用的时候,像这样,通过v-slot指定 -->
<slot-comp v-slot:bottomSlot>'Hello!'</slot-comp>默认插槽
如果<slot>没有指定name属性那么v-slot:default将会匹配到他,还有不使用v-slot的传递也会匹配到他
<template>部分传递
如果我们想传递部分,可以使用这个标签这个标签不会渲染只是起到占位标识的作用
1
2
3
4
5
6
7
8
9
10<h1>App.vue</h1>
<p>该组件有两个 div 标签,每个标签中有一个插槽。</p>
<slot-comp>
<!-- 这里指定了部分内容 给到bottomSlot -->
<template v-slot:bottomSlot>
<h4>到底部插槽!</h4>
<p>这个 p 标签和上面的 h4 标签通过模板标签上使用的 v-slot 指令定向到底部插槽。</p>
</template>
<p>这会进入默认插槽</p>
</slot-comp>v-slot:可以简写为#
作用域插槽
也许我我们需要从组件中获取一些数据
就像props一样,不过这里是从组件传递到上层
通过组件中的<slot>标签,通过v-bind一些属性,这些属性就组成了一个对象
父级通过v-slot
=
“对象名”来获取对象
注意之前父级通过v-slot: name来指定插槽,而这里是用=引号引起来的表示获取对象!
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<template>
<h1>Local Component</h1>
<p>App.vue controls how local data from the scoped slot is rendered.</p>
<!-- 将组件中的slot 的 v-bind属性封装到一个对象中 -->
<slot-comp v-slot="dataFromSlot">
<!-- 获取这个对象中的值 -->
<h2>{{ dataFromSlot.lclData }}</h2>
</slot-comp>
</template>
<!-- 组件中这样写 -->
<template>
<slot v-bind:lclData="data"></slot>
</template>
<script>
export default {
data() {
return {
data: 'This is local data'
}
}
}
</script>
<style></style>组件具有多个对象
多个<slot标签,形成多个对象
这样的话实际上父级会依照每一个对象一个template
1
2
3
4
5<slot-comp v-slot="food">
<hr>
<h2>{{ food.foodName }}<img :src=food.foodUrl></h2>
<p>{{ food.foodDesc }}</p>
</slot-comp>这个是父级看似只有一个 组件
实际上当组件中传递出来的是一个数组,那么就会形成
多个
vue.js初学习https://wainyz.online/wainyz/2024/04/02/vue.js初学习/