
Vue笔记
前端其实是个很大的范畴。简单点说,针对浏览器的开发,浏览器呈现出来的页面就是前端。它的实质是前端代码在浏览器端被编译、运行、渲染。前端代码主要由三个部分构成:HTML(超文本标记语言)、CSS(级联样式表)、JavaScript。Vue (发音为 /vjuː/,类似view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供
文章目录
Vue笔记
一、前后端分离思想
1、什么是前端?
前端其实是个很大的范畴。简单点说,针对浏览器的开发,浏览器呈现出来的页面就是前端。它的实质是前端代码在浏览器端被编译、运行、渲染。前端代码主要由三个部分构成:HTML(超文本标记语言)、CSS(级联样式表)、JavaScript。
2、前端的发展历程
上古时代
这个节点不得不说一下,世界上第一款浏览器 NCSAMosaic ,是网景公司(Netscape)在1994年开发出来的,它的初衷是为了方便科研人员查阅资料、文档(这个时候的文档大多是图片形式的)。那个时代的每一个交互,按钮点击、表单提交,都需要等待浏览器响应很长时间,然后重新下载一个新页面给你看,大概是这样:
铁器时代(小前端时代)
1995年,这是个好年份,又是这个搞事的网景公司,拜托一位叫布兰登·艾奇的大佬,希望开发出一个类似 Java 的脚本语言,用来提升浏览器的展示效果,增强动态交互能力。结果大佬喝着啤酒抽着烟,十来天就把这个脚本语言写出来了,功能很强大,就是语法一点都不像 Java。这样就渐渐形成了前端的雏形:HTML 为骨架,CSS 为外貌,JavaScript 为交互。同时期微软等一些公司也针对自家浏览器开发出了自己的脚本语言。浏览器五花八门,虽然有了比较统一的 ECMA 标准,但是浏览器先于标准在市场上流行开来,成为了事实标准。导致,现在前端工程师还要在做一些政府古老项目的时候,还要去处理浏览器兼容(万恶的 IE 系列)。
信息时代(大前端时代)
自 2003 以后,前端发展渡过了一段比较平稳的时期,各大浏览器厂商除了按部就班的更新自己的浏览器产品之外,没有再作妖搞点其他事情。但是我们程序员们耐不住寂寞啊,工业化推动了信息化的快速到来,浏览器呈现的数据量越来越大,网页动态交互的需求越来越多,JavaScript 通过操作 DOM 的弊端和瓶颈越来越明显(频繁的交互操作,导致页面会很卡顿),仅仅从代码层面去提升页面性能,变得越来越难。于是优秀的大佬们又干了点惊天动地的小事儿:
2008 年,谷歌 V8 引擎发布,终结微软 IE 时代。
2009 年 AngularJS 诞生、Node诞生。
2011 年 ReactJS 诞生。
2014 年 VueJS 诞生。
全能前端时代
2009年开始,大屏智能手机开始陆续出现,到后来 4G 移动网络的普及。使得前端从单一的基于的 PC 浏览器 展示的 web 应用,开始向手机、平板覆盖(HTML,CSS,JavaScript 也陆续推出了自己的新标准)。前端对于跨端浏览的需求越来越大,前端不再仅仅是 PC web 方面的开发,手机配置,与 app 进行 hybird 开发,变成了常态。甚至于 Facebook 推出了 React-Native,国内微信、支付宝推出小程序,试图整合web、native 开发。
3、前后端分离的概念
- 明晰概念
前后端分离是通过nginx+tomcat的方式(也可以中间加一个nodejs)有效的进行解耦,并且前后端分离会为以后的大型分布式架构、弹性计算架构、微服务架构、多端化服务(多种客户端,例如:浏览器,车载终端,安卓,IOS等等)打下坚实的基础。这个步骤是系统架构从猿进化成人的必经之路。它的核心思想是前端html页面通过ajax调用后端的restuful api接口并使用json数据进行交互。
- 没有前后端分离时的开发
以前的javaWeb项目大多数使用jsp作为页面层展示数据给用户,因为流量不高,因此也没有那么苛刻的性能要求。但现在是大数据时代,对于互联网项目的性能要求是越来越高,因此原始的前后端耦合在一起的架构模式已经逐渐不能满足我们,因此我们需要需找一种解耦的方式,来大幅度提升我们的负载能力。
4、前后端分离的意义
简单来说:前后端分离可以实现真正的前后端解耦,前端服务器使用nginx。
前端/WEB服务器放的是css,js,图片等等一系列静态资源(甚至你还可以css,js,图片等资源放到特定的文件服务器,例如阿里云的oss,并使用cdn加速),前端服务器负责控制页面引用、跳转、路由,前端页面异步调用后端的接口,后端/应用服务器使用tomcat(把tomcat想象成一个数据提供者),加快整体响应速度。(这里需要使用一些前端工程化的框架比如nodejs,react,router,react,redux,webpack) 发现bug,可以快速定位是谁的问题,不会出现互相踢皮球的现象。页面逻辑,跳转错误,浏览器兼容性问题,脚本错误,页面样式等问题,全部由前端工程师来负责。接口数据出错,数据没有提交成功,应答超时等问题,全部由后端工程师来解决。
5、前后端分离的工作流程
前端通过 Ajax 请求来访问后端的数据接⼝,将 Model 展示到 View 中即可。前后端开发者只需要提前约定好接⼝⽂档(URL、参数、数据类型…),然后分别独⽴开发即可,前端可以造假数据进⾏测试,完全不需要依赖于后端,最后完成前后端集成即可,真正实现了前后端应⽤的解耦合,极⼤地提升了开发效率。
二、Vue简介
什么是 Vue?
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。
渐进式框架
Vue 是一个框架,也是一个生态。其功能覆盖了大部分前端开发常见的需求。但 Web 世界是十分多样化的,不同的开发者在 Web 上构建的东西可能在形式和规模上会有很大的不同。考虑到这一点,Vue 的设计非常注重灵活性和“可以被逐步集成”这个特点。根据你的需求场景,你可以用不同的方式使用 Vue:
- 无需构建步骤,渐进式增强静态的 HTML
- 在任何页面中作为 Web Components 嵌入
- 单页应用 (SPA)
- 全栈 / 服务端渲染 (SSR)
- Jamstack / 静态站点生成 (SSG)
- 开发桌面端、移动端、WebGL,甚至是命令行终端中的界面
API 风格
Vue 的组件可以按两种不同的风格书写:选项式 API 和组合式 API。
选项式 API (Options API)
使用选项式 API,我们可以用包含多个选项的对象来描述组件的逻辑,例如 data
、methods
和 mounted
。选项所定义的属性都会暴露在函数内部的 this
上,它会指向当前的组件实例。
<script>
export default {
// data() 返回的属性将会成为响应式的状态
// 并且暴露在 `this` 上
data() {
return {
count: 0
}
},
// methods 是一些用来更改状态与触发更新的函数
// 它们可以在模板中作为事件处理器绑定
methods: {
increment() {
this.count++
}
},
// 生命周期钩子会在组件生命周期的各个不同阶段被调用
// 例如这个函数就会在组件挂载完成后被调用
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
组合式 API (Composition API)
通过组合式 API,我们可以使用导入的 API 函数来描述组件逻辑。在单文件组件中,组合式 API 通常会与<script setup>
搭配使用。这个 setup
attribute 是一个标识,告诉 Vue 需要在编译时进行一些处理,让我们可以更简洁地使用组合式 API。比如,<script setup>
中的导入和顶层变量/函数都能够在模板中直接使用。
下面是使用了组合式 API 与 <script setup>
改造后和上面的模板完全一样的组件:
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 用来修改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
如何选择?
- 当你不需要使用构建工具,或者打算主要在低复杂度的场景中使用 Vue,例如渐进增强的应用场景,推荐采用选项式 API。
- 当你打算用 Vue 构建完整的单页应用,推荐采用组合式 API + 单文件组件。
三、快速上手
使用一个框架,我们第一步要做什么呢?
安装下载它安装Vue的方式有很多:
方式一:直接CDN引入
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
这里我们使用了 unpkg,但你也可以使用任何提供 npm 包服务的 CDN,例如 jsdelivr 或 cdnjs。
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
方式二:下载和引入
<script src="js/v3.2.8/vue.global.prod.js" type="text/javascript" charset="utf-8"></script>
方式三:NPM安装(后续通过webpack和CLI的使用,我们使用该方式。)
方式四:使用 ES 模块构建版本
注意我们使用了 <script type="module">
,且导入的 CDN URL 指向的是 Vue 的 ES 模块构建版本。
<div id="app">{{ message }}</div>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app')
</script>
使用cdn方式:
新建文件index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!-- <script src="https://unpkg.com/vue@next"></script> -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
{{ counter }}
</div>
<script>
const App = {
data() {
return {
counter: 0
}
}
};
Vue.createApp(App).mount('#app');
</script>
</body>
</html>
页面效果:
说明
每个Vue应用都是使用 createApp函数创建一个新的应用实例:
Vue.createApp(App)
传入的对象App其实是一个组件,每个应用实例都需要一个根组件。
其中data()函数用于定义Vue的初始化数据。
const App = {
data() {
return {
counter: 0
}
}
};
应用实例必须在调用了 .mount()
方法后才会渲染出来。该方法接收一个“容器”参数,可以是一个实际的 DOM 元素或是一个 CSS 选择器字符串:
Vue.createApp(App).mount('#app')
应用根组件的内容将会被渲染在容器元素里面。容器元素自己将不会被视为应用的一部分。
.mount()
方法应该始终在整个应用配置和资源注册完成后被调用。同时请注意,不同于其他资源注册方法,它的返回值是根组件实例而非应用实例。
四、模板语法
Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。
在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。
1、{{}}文本插值
最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):
<div id="app">
{{ msg }}
</div>
<script>
const App = {
data() {
return {
msg: 0
}
}
};
Vue.createApp(App).mount('#app');
</script>
双大括号标签会被替换为相应组件实例中msg
属性的值。同时每次 msg
属性更改时它也会同步更新。
2、使用JavaScript表达式
Vue提供了完全的 JavaScript 表达式支持。
{{ number + 1 }}
{{ age >= 18 ? '成年' : '未成年' }}
{{ message.split('').reverse().join('') }}
data(){
return{
number:0,
age:17,
message:"我爱编程"
}
}
表达式将在所属的 Vue 实例的作用域内计算。每个绑定只能包含单个表达式。
<!-- 这是一个语句,不是一个表达式: -->
{{ var a = 1 }}
<!-- 流程控制也不可以,可改用三元表达式 -->
{{ if (ok) { return message } }}
3、v-html 原始HTML
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真
正的 HTML,你需要使用 v-html 指令
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
data(){
return{
rawHtml:"<a
href='https://www.itbaizhan.com'>百战</a>"
}
}
4、v-bind 属性绑定
双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind
指令:
<div v-bind:id="dynamicId"></div>
v-bind
指令指示 Vue 将元素的 id
attribute 与组件的 dynamicId
属性保持一致。如果绑定的值是 null
或者 undefined
,那么该 attribute 将会从渲染的元素上移除。
简写
因为 v-bind
非常常用,所以Vue提供了特定的简写语法:
<div :id="dynamicId"></div>
布尔型Attribute
布尔型 attribute 依据 true / false 值来决定 attribute 是否应该存在于该元素上。disabled
就是最常见的例子之一。
比如一个按钮的启用和禁用:
<button :disabled="isButtonDisabled">Button</button>
当 isButtonDisabled
为真值或一个空字符串 (即 <button disabled="">
) 时,元素会包含这个 disabled
attribute。而当其为其他假值时 attribute 将被忽略。
动态绑定多个值
包含多个 attribute 的 JavaScript 对象
data() {
return {
objectOfAttrs: {
id: 'container',
class: 'wrapper'
}
}
}
通过不带参数的v-bind ,将这组属性绑定到单个元素上:
<div v-bind="objectOfAttrs"></div>
5、computed 计算属性
在模板中绑定表达式是非常遍历的,但仅用于简单的操作。
比如:
const app = {
data(){
return {
books:[
'三国演义',
'西游记',
'水浒传',
'红楼梦'
]
}
}
}
想根据books中是否有书籍在展示不同信息:
<p>Has published books:</p>
<span>{{ books.length > 0 ? 'Yes' : 'No' }}</span>
如果在模板中不止一次这样的计算,推荐使用计算属性来描述依赖响应式状态的复杂逻辑
重构后:
const app = {
data(){
return {
books:[
'三国演义',
'西游记',
'水浒传',
'红楼梦'
]
}
},
computed:{
bookLength(){
return this.books.length > 0 ? 'Yes' : 'No';
}
}
}
<h1>books长度:{{books.length>0?'yes':'no'}}</h1>
<h1>books长度:{{bookLength}}</h1>
上面案例中定义了一个计算属性 bookLength,更改data中的books数组的值,bookLength也会随之改变。
在模板中使用计算属性的方式和一般的属性别无二致。
6、v-if 条件渲染
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回真值时才被渲染。
v-else-if
提供的是相应于 v-if
的“else if 区块”。它可以连续多次重复使用
使用 v-else
为 v-if
添加一个“else 区块”。一个 v-else
元素必须跟在一个 v-if
或者 v-else-if
元素后面,否则它将不会被识别。
<span v-if="score > 60">及格了</span>
<span v-else-if="score == 60">刚好及格</span>
<span v-else>没及格</span>
const app = {
data(){
return {
score:50
}
}
}
Vue.createApp(app).mount("#app");
因为 v-if
是一个指令,他必须依附于某个元素。但如果我们想要切换不止一个元素呢?在这种情况下我们可以在一个 <template>
元素上使用 v-if
,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个 <template>
元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
7、v-show条件渲染
v-show是另一个用于条件性展示元素的指令。
<h1 v-show="ok">Hello!</h1>
v-if 和 v-show的区别:
v-if
是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if
也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show
简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display
属性会被切换。
总的来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show
较好;如果在运行时绑定条件很少改变,则 v-if
会更合适。
8、v-for 列表渲染
v-for
指令基于一个数组来渲染一个列表。v-for
指令的值需要使用 item in items
形式的特殊语法,其中 items
是源数据的数组,而 item
是迭代项的别名。
下面我们用一个书籍数组来演示:
<div id="app" align="center">
<table border="1" colspacing="0" cellspacing="0">
<tr>
<th>序号</th>
<th>作者</th>
<th>书名</th>
<th>价格</th>
</tr>
<tr v-for="(book,index) in books" :key="index">
<td>{{index}}</td>
<td>{{author}}</td>
<td>{{book.name}}</td>
<td>{{book.price}}</td>
</tr>
</table>
</div>
<script>
const app = {
data(){
return {
author:"金庸",
books:[
{
name:"飞狐外传",price:46
},{
name:"雪山飞狐",price:46
},{
name:"连城诀",price:46
},{
name:"天龙八部",price:46
},{
name:"射雕英雄传",price:46
},{
name:"白马啸西风",price:46
},{
name:"鹿鼎记",price:46
},{
name:"笑傲江湖",price:46
},{
name:"书剑恩仇录",price:46
},{
name:"神雕侠侣",price:46
},{
name:"侠客行",price:46
},{
name:"倚天屠龙记",price:46
},{
name:"碧血剑",price:46
},{
name:"鸳鸯刀",price:46
},{
name:"越女剑",price:46
}
]
}
}
}
Vue.createApp(app).mount("#app");
</script>
通过 key 管理状态
Vue 默认按照“就地更新”的策略来更新通过 v-for
渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况。
为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key
attribute。
注意:
key
在这里是一个通过v-bind
绑定的特殊 attribute。且绑定一个基础类型的值,可以是数字或字符串,不能是对象。
9、v-on 事件处理
我们可以使用 v-on 指令 (通常缩写为 @ 符号) 来监听 DOM 事件,并在触发事件时执行一些 JavaScript。
用法为 v-on:click=“handler” 或使用快捷方式 @click=“handler”。
事件处理器 (handler) 的值可以是:
- 内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与
onclick
类似)。
<button @click="counter += 1">Add 1</button>
data() {
return {
counter: 0
}
}
- 方法事件处理器:一个指向组件上定义的方法的属性名或是路径。
<h1>{{ count }}</h1>
<button @click="clickbutton">单击</button>
const app = {
data(){
return {
count:0
}
},
methods:{
//event是DOM原生事件
clickbutton(event){
//this表示当前组件实例
this.count++;
if(event){
//显示触发事件的标签名
console.log(event.target.tagName);
}
}
}
}
方法事件处理器会自动接收原生 DOM 事件并触发执行。在上面的例子中,我们能够通过被触发事件的 event.target
访问到该 DOM 元素。
在内联处理器中调用方法
除了直接绑定方法名,你还可以在内联事件处理器中调用方法。这允许我们向方法传入自定义参数以代替原生事件:
methods: {
say(message) {
alert(message)
}
}
<button @click="say('hello')">Say hello</button>
<button @click="say('bye')">Say bye</button>
在内联事件处理器中访问事件参数
有时我们需要在内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event
变量,或者使用内联箭头函数:
<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
Submit
</button>
methods: {
warn(message, event) {
// 这里可以访问 DOM 原生事件
if (event) {
event.preventDefault()
}
alert(message)
}
}
10、v-model 表单输入绑定
v-model 指令在表单 、 及 元素上创建双向数据绑定。
v-model 指令会根据不同类型的输入, 及 元素,自动应用对应的DOM属性和事件组合。
- 文本类型的
<input>
和<textarea>
元素会绑定value
property 并侦听input
事件; <input type="checkbox">
和<input type="radio">
会绑定checked
property 并侦听change
事件;<select>
会绑定value
property 并侦听change
事件。
且该指令会忽略任何表单元素上初始的 value
、checked
或 selected
attribute。所以需要在data()中声明一个初始化值。
下面演示基本用法:
文本:
<p>内容是:{{msg}}</p>
<input v-model="msg" placeholder="文本内容"/>
data(){
return{
msg:0
}
}
多行文本:
<span>多行文本:</span>
<p style="white-space: pre-line;">{{ msg }}</p>
<textarea v-model="msg" placeholder="多行文本"></textarea>
复选框:
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
data(){
return{
checked:true
}
}
将多个复选框绑定到同一个数组或集合的值:
data() {
return {
checkedNames: []
}
}
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames" />
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
<label for="mike">Mike</label>
单选框:
data() {
return {
picked: ""
}
}
<div>单选: {{ picked }}</div>
<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>
选择器:
data() {
return {
selected: "",
selected2:[]
}
}
<div>选择器: {{ selected }}</div>
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<div>多项选择器: {{ selected2 }}</div>
<select v-model="selected2" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
修饰符:
.lazy | 默认情况下,v-model 会在每次 input 事件后更新数据,添加 lazy 修饰符来改为在每次 change 事件后更新数据 |
---|---|
.number | 将用户输入自动转换为数字,如果内容无法被parseFloat() 处理,那么将返回原始值。number 修饰符会在输入框有 type="number" 时自动启用。 |
.trim | 自动去除用户输入内容中两端的空格 |
五、侦听器
在选项式API中,可以使用watch()选项在每次响应式属性(普通属性和计算属性)发生变化时触发一个函数。
data(){
return{
msg:0
}
},
watch:{
msg(){
console.log(this.msg);
}
}
在侦听器中,msg为方法名,且和data中的属性名保持一致。不要在侦听器中改变属性值,否则将发生循环调用。
六、开发环境搭建
安装nodejs
可进入网址下载:https://nodejs.org/zh-cn/download/package-manager
开发工具使用VScode,安装拓展,搜索Vetur 或者 搜索vue-official
安装好后打开命令行工具,输入:
node -v
npm -v
显示此结果表示安装成功。
设置镜像源
在对nodejs的npm工具设置国内的镜像源以便于提高资源的下载速度,如下:
执行以下命令安装cnpm工具用来代替npm工具:
npm install -g cnpm --registry=https://registry.npm.taobao.org
或者
将npm源设置为淘宝镜像
npm config set registry https://registry.npm.taobao.org
执行完成后执行以下命令
npm config get registry
安装vue-cli
设置好镜像源之后我们需要安装一个vue的脚手架工具(vue-cli)
执行以下命令安装vue-cli版本
npm install -g @vue/cli
或
cnpm install -g @vue/cli
安装之后你就可以在命令行中使用vue命令,你可以使用以下命令来检查vue-cli版本安装是否正确
vue --version
使用vue-cli创建vue项目
方式一:vue create
在一个空目录下,进入终端,执行一下命令:
sudo vue create 项目名称
选择Manually select features,按回车键确认。进入下一步。
将上面的两个绿点取消掉,按空格键。我们创建一个最简单的vue项目,按回车确认。
选择使用哪个版本的vue,我们选择3.x。
将依赖项的配置放置在package.json中。
是否将本次创建项目的配置设为模板供以后使用,我们不保存,输入n,再按回车键进入下一步。
至此,项目创建完成。
如何运行?
cd进入项目目录
cd first-vue-demo
输入并执行以下指令
npm run serve
运行后打开浏览器,输入上面的路径即可访问项目。
方式二:npm init
在一个空目录下,进入终端,输入并执行一下代码:
sudo npm init vue@latest --@latest表示使用最新版本的vue
这些我们暂时全选择否。这样,我们的项目就初始化好了,在键入提示的命令,执行即可
cd first-vue-demo2
npm install --mac在npm前加上sudo
npm run dev
访问上述网址即可进入首页。
**vue create和npm init vue@latest是两种不同的工具和命令,用于初始化Vue项目,它们之间存在明显的区别。**
- vue create 是Vue CLI 3.x的命令,用于创建和管理Vue.js项目。它提供了丰富的项目配置选项,允许用户交互式地选择项目预设(如默认预设或手动选择特性)以及其他配置选项,如路由、状态管理等。这个命令会引导用户完成项目的配置,生成相应的项目结构和配置文件。
- npm init vue@latest 则是使用Vue CLI 2.x通过npm包管理器创建Vue.js项目的一种方式。它会在当前目录下创建一个新的项目,并自动安装最新版本的Vue.js。这个命令会生成一个基本的项目结构,但不提供交互式的项目配置选项。
此外,vue create和npm init vue@latest还涉及到不同的构建工具和技术:
- vue create基于Webpack,这是一个模块打包工具,适用于复杂的大型项目,特别是需要大量自定义配置和复杂构建管道的项目。
- npm init vue@latest则基于Vite,这是一个现代化的前端构建工具,旨在提供快速的开发体验。Vite利用了浏览器的ES模块原生支持,能够快速构建现代化的前端应用,适用于小到中型项目或者需要快速开发原型和小型应用的场景。
总的来说,vue create提供了更多的配置选项和灵活性,适合创建复杂的Vue项目,而npm init vue@latest则提供了一个快速启动的基本项目结构,适合快速搭建简单的Vue应用或原型。选择哪个工具取决于项目的具体需求和开发者的偏好。
项目结构:
七、Vue的生命周期
每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。
生命周期图示:
为了方便记忆,可以分类为:
- 创建时:beforeCreate、created
- 渲染时:beforeMount、mounted
- 更新时:beforeUpdate、updated
- 卸载时:beforeUnmount、unmounted
八、ES6模块
什么是es6
ECMAScript 6是2015年正式发布的JavaScript语言的标准。
基本用法
模块导入导出各种数据类型的变量,比如字符串、数值、类、函数。
导出的函数声明与类声明必须要有名称(export default 命令另外考虑)。
不仅能导出声明还能导出引用(例如函数)。
export 命令可以出现在模块的任何位置,但必需处于模块顶层。
import 命令会提升到整个模块的头部,首先执行。
定义一个test.js
let name = "张三"
let age = 18
let myfn = function (){
return "my name is "+name+",age is "+age;
}
let myclass = class myclass{
static a = "yeah"
}
//导出对象
export {name,age,myfn,myclass}
新建一个hmtl,使用import引入test.js
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="module">
import {name,age,myfn,myclass} from "./test.js"
console.log(name);
console.log(age);
console.log(myfn());
console.log(myclass.a);
</script>
</body>
</html>
运行查看控制台打印
九、组件基础
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:
这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。
组件组成
组件最大的优势就是可复用性。
当使用构建步骤时,我们一般会将 Vue 组件定义在一个单独的 .vue
文件中,这被叫做单文件组件 (Single File Component简称 SFC):
<template>
</template>
<script>
</script>
<style scoped>
</style>
scoped属性代表作用域,style标签的样式仅在当前文件可用。若不添加,其他文件注册了该组件,也可以用这些定义好的样式。推荐添加,可以避免各文件css样式命名冲突等问题。
组件注册
1、编写组件
在components文件夹下新建一个vue文件,命名为top.vue
<template>
<div class="head">
头部
</div>
</template>
<script>
</script>
<style scoped>
.head{
width:100%;
height:100px;
line-height: 100px;
background-color: yellow;
}
</style>
2、组件注册
要使用一个子组件,需要在父组件上导入它,使其暴露给外部。若要将组件暴露给模板,需要在components选项中注册。
在App.vue中添加代码
<script>
//导入组件,使其暴露给外部,名称为Top
import Top from '@/components/top.vue'
export default {
name: 'App',
components: { //注册组件
Top
}
}
</script>
@/是在项目中jsconfig.json中配置的,表示从项目根路径‘src/’开始。
组件名规范:使用PascalCase格式(帕斯卡命名法),即各单词首字母大写。与之相近的camelCase(驼峰命名法),首字母小写,其他单词首字母大写。
3、使用组件
这个组件将会以其注册时的名字作为模板中的标签名。
<template>
<Top/>
</template>
组件嵌套
组件嵌套指的是组件之间像HTML标签一样,可以嵌套使用。如下图所示:
实现代码:
top.vue
<template>
<div class="head">
头部
</div>
</template>
<script>
</script>
<style>
.head{
width:100%;
height:100px;
line-height: 100px;
background-color: yellow;
}
</style>
content.vue
<template>
<div class="content">
<Art></Art>
<Art></Art>
</div>
</template>
<script>
import Art from "@/components/article.vue"
export default {
components:{
Art
}
}
</script>
<style scoped>
.content{
width:80%;
height:500px;
background-color: aqua;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
float:left;
}
</style>
tight.vue
<template>
<div class="right">
<Item/>
<Item/>
<Item/>
<div class="item"></div>
</div>
</template>
<script>
import Item from "@/components/item.vue"
export default{
components:{
Item
}
}
</script>
<style scoped>
.right{
width:20%;
height:500px;
background-color: bisque;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
float:right;
}
</style>
article.vue
<template>
<div class="article">文章内容</div>
</template>
<script>
</script>
<style scoped>
.article{
width:60%;
height:200px;
background-color: limegreen;
margin-top: 10px;
}
</style>
item.vue
<template>
<div class="item">项目1</div>
</template>
<script>
</script>
<style scoped>
.item{
width:60%;
height:100px;
background-color: blueviolet;
margin-top: 10px;
}
</style>
最后在App.vue中注册并使用top.vue,content.vue,right.vue
<template>
<Top/>
<Content/>
<Right/>
</template>
<script>
import Top from '@/components/top.vue'
import Content from '@/components/content.vue'
import Right from '@/components/right.vue'
export default {
name: 'App',
components: {
Top,
Content,
Right
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>
十、组件传递数据 Props
组件之间是需要交互的。父组件向子组件传递参数,可以在子组件中使用props选项来声明要接受的参数。
使用对象形式:
props: {
msg: String
}
使用字符串数组形式:
props: ['msg']
示例如下:
在子组件中声明要接受的参数:
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
在父组件中传递该值:
<Header msg="张三"/>
有时候子组件也需要传递值给父组件,子组件通过$emit触发父组件上的自定义事件,发送参数
父组件:
<template>
<!--@getValue getValue为自定义事件,由子组件通过$emit触发-->
<Header msg="张三" @getValue="getValue"/>
</template>
<script>
import Header from '@/components/header.vue'
export default{
name:'App',
components:{
Header
},
data(){
return{
val : ""
}
},
methods:{
//定义接收子组件传递参数的方法
getValue(value){
this.val=value;
}
}
}
</script>
<style>
</style>
子组件:
<template>
<div class="head">
<p>{{msg}}</p>
<input v-model="name"/><button @click="sendToFather">发送数据给父组件</button>
</div>
</template>
<script>
export default{
data(){
return {
name:""
}
},
props:{
msg:String
},
methods:{
//点击按钮调用该方法
sendToFather(){
//getValue为父组件的自定义事件,this.name是传递的参数
this.$emit("getValue",this.name);
}
}
}
</script>
<style scoped>
.head{
width:100%;
height:100px;
line-height: 100px;
background-color: yellow;
}
</style>
十一、Promise对象
什么是Promise
官方定义: Promise对象用于表示一个异步操作的最终完成(或者失败)及其结果值。它可以把异步操作最终的成功返回值或者失败原因和相对应的程序关联起来。这样就使得异步方法可以像同步方法那样返回值;但是异步方法不会立即返回最终的值,而是会返回一个Promise,在未来某个时刻把值交给使用者。通俗来说,Promise就是一个容器,里面存放着一个异步操作的状态。
Promise状态
Promise 异步操作有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态。
Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。
为什么使用Promise
使用Promise主要是为了解决JS中的回调地狱问题。
关于回调地狱问题,要从两个概念说起:
-
回调函数
当一个函数作为参数传入另一个函数中,并且它不会立即执行,只有在满足一定条件时,才可以执行,这种函数就称为回调函数。
setTimeout(function(){
console.log("执行了回调函数");
},3000)
在上述代码中,function就是一个回调函数,它作为一个参数传递到定时器函数中,只有在满足延时3秒的条件后,才会执行该回调函数。
-
异步任务
js的任务类型分为同步任务和异步任务:同步任务在主线程上排队执行,只有当前一个任务执行完毕之后,才能执行下一个任务;而异步任务不进入主线程,而是进入异步任务队列,前一个任务是否执行完毕不影响下一个任务的执行。
setTimeout(function(){
console.log("执行了回调函数");
},3000)
console.log("输出111,执行异步任务");
从上述的代码示例可以看出,定时器中的任务是否执行完毕不影响后面 任务的执行,当定时器需要等待3秒再去执行时,不会阻塞后面函数的执行。这就是简单的异步任务的实现。
回调地狱
根据上述对回调函数和异步任务的介绍我们可以得出一个结论。存在异步任务的代码,不能保证代码能够按照顺序执行,但是如果我们必须要让代码按照顺序执行呢?
假如要说一句话,语序是这样的:早上起床先睁开眼睛,再去洗漱,最后穿衣服。
如果我们要让上面的话按照语序输出,就需要这样操作:
setTimeout(function(){
console.log("早上起床先睁开眼睛");
setTimeout(function(){
console.log("再去洗漱");
setTimeout(function(){
console.log("最后穿衣服");
},1000)
},2000)
},3000)
从上面的实例可以看出,如果要按照语序输出一句话,就需要回调函数嵌套回调函数,上面代码就嵌套了三层回调函数,这种回调函数中嵌套回调函数的情况就称为回调地狱。
回调地狱是为了实现代码执行操作顺序而出现的一种操作,回调地狱会造成带代码的可读性非常差并且不利于后期代码的维护。
回调地狱的解决方案
使用promise
promise是js原生的一个对象,是解决异步编程的一种方案。
1、promise构造函数接收一个函数作为参数,在该函数体内就可以写我们要执行的异步任务,p该函数的两个参数是resolve和reject。当异步任务成功执行时调用resolve函数返回结果,如果失败就调用reject函数。
2、promise对象中的then方法用来接收处理成功时候的相应数据,catch方法用来接收处理失败时的相应数据。
3、promise的链式编程可以保证代码的执行顺序,但是前提是每一次在执行完then中的操作时,必须要返回一个promise对象,这样才能保证下一次的then时能够接收到数据.
function fn(str){
var p=new Promise(function(resolve,reject){
var flag=true;
setTimeout(function(){
if(flag){
resolve(str)
}
else{
reject('失败')
}
})
})
return p;
}
fn('早上起床先睁开眼睛').then((data)=>{
console.log(data);
return fn('再去洗漱')
})
.then((data)=>{
console.log(data);
return fn('最后穿上衣服')
})
.then((data)=>{
console.log(data);
})
.catch((data)=>{
console.log(data);
})
这样可以实现使用promise对象来实现按照代码顺序执行任务。
但是我们通过对代码的观察可以看出,使用promise的问题就是代码冗余,使用promise封装异步任务就会出现很多then,这样也是不利于代码维护。
使用async/await
async作为关键字放到一个声明函数的前面,声明该函数是一个异步任务,不会阻塞后面任务的执行
async function fn(){
console.log('你好 Promise');
}
console.log(fn());
我们可以看出使用async关键字将函数自动封装成了一个promise对象,所以就可以在使用async中的函数中使用promise的方法。
await关键字只能在使用async声明的函数中使用,await后面可以直接跟一个promise实例对象,可以直接拿到promise中resolve返回的数据。
//封装一个返回promise的异步任务
function fn(str){
var p=new Promise(function(resolve,reject){
var flag=true;
setTimeout(function(){
if(flag){
resolve(str)
}
else{
reject('处理失败')
}
},1000)
})
return p;
}
async function test(){
var res1=await fn('早上起床先睁开眼睛');
var res2=await fn('再去洗漱');
var res3=await fn('最后穿上衣服');
console.log(res1,res2,res3);
}
test();
async和await实际上是ES7提供给我们的语法糖,这样就解决了使用原生promise解决回调地狱中遇到的代码可读性不高,代码冗余和代码不利于维护的问题,使一个异步代码看起来更像是一个同步代码。
十二、axios
axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
安装
在项目根目录下执行命令:
npm install axios 或 cnpm install axios
使用
在需要使用axios的组件中导入:
import axios from 'axios'
getUsers(){
axios.get("https://jsonplaceholder.typicode.com/users")
.then(res=>{
console.log(res);
this.users=res.data;
})
.catch(err=>{
console.log(err);
});
}
axios api
axios(config)
// 发送 POST 请求
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
axios(url [,config])
// 发送 GET 请求(默认的方法)
axios('/user/12345');
请求方法的别名
- axios.request(config)
- axios.get(url [, config])
- axios.delete(url[, config])
- axios.head(url[, config])
- axios.options(url[, config])
- axios.post(url[, data[, config]])
- axios.put(url[, data[, config]])
- axios.patch(url[, data[, config]])
在使用别名方法时, url、method、data 这些属性都不必在配置中指定。
请求配置
这些是创建请求时可以用的配置选项。只有 url 是必需的。如果没有指定 method,请求将默认使用 get 方法。
-
url: string // 请求地址
-
method: string // 请求方法
-
baseUrl: string // 设置基本的url便于为axios实例的方法传递相对
-
urlheaders: array // 自定义请求头
-
params: object // 请求参数,必须是一个plain object或URLSearchParams 对象
-
data: object // 请求主体,用在put,post,patch方法
-
timeout: number // 请求超时毫秒数
响应结构
某个请求的响应包含以下信息
- data: any // 由服务器提供的响应数据
- status: number // 来自服务器的响应状态码
- statusText: string // 来自服务器响应的HTTP状态信息
- headers: object // 服务器响应头
- config: object // 为请求提供的配置信息
案例说明,想服务器发送请求获取信息,并渲染到页面上
在component文件夹新建一个users.vue:
<template>
<table align="center" border="1" cellspacing="0" cellpadding="0">
<tr>
<th>id</th>
<th>姓名</th>
<th>昵称</th>
<th>邮箱</th>
<th>地址</th>
<th>手机号</th>
<th>网址</th>
</tr>
<tr v-for="(user,index) in users" :key="index">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>{{ user.address.city }}</td>
<td>{{ user.phone }}</td>
<td>{{ user.website }}</td>
</tr>
</table>
</template>
<script>
export default {
name: 'Users',
props:['users']
}
</script>
<style scoped>
</style>
在App.vue中导入该组件:
<template>
<div>
<h3>点击获取用户信息</h3>
<button @click="getUsers">获取远程数据</button>
</div>
</template>
<script>
import axios from "axios"
import Users from './components/Users.vue'
export default {
name:"App",
data(){
return{
users:[]
}
},
components:{
Users
},
methods:{
getUsers(){
axios.get("https://jsonplaceholder.typicode.com/users")
.then(res=>{
console.log(res);
this.users=res.data;
})
.catch(err=>{
console.log(err);
});
}
}
}
</script>
<style>
</style>
跨域问题
JS采取的是同源策略
同源策略是浏览器的一项安全策略,浏览器只允许js 代码请求和当前所在服务器域名,端口,协议相同的数据接口上的数据,这就是同源策略.
也就是说,当协议、域名、端口任意一个不相同时,都会产生跨域问题
解决:
1、后台解决:cors
2 、前台解决:proxy
在vite.config.js中添加如下配置:
server:{
proxy:{
'/api':{//获取路径中包含了/api的请求
target:'http://localhost:8088',//后台服务所在的源
changeOrigin:true,//是否要更换源
rewrite:(path) =>path.replace(/^\/api/,'')//路径重写,将路径中的/api替换成空字符
},
}
}
未完成,持续更新中
更多推荐
所有评论(0)