小丁的屋舍
Vue | 前端学习笔记(更新中)
2025-01-12
查看标签
0次浏览

兜兜转转,又回到了前端开发!

易学易用,基于标准 HTML、CSS 和 JavaScript 构建,提供容易上手的 API 和一流的文档。
性能出色,经过编译器优化、完全响应式的渲染系统,几乎不需要手动优化。
灵活多变,丰富的、可渐进式集成的生态系统,可以根据应用规模在库和框架间切换自如。

创建一个 Vue 程序

 使用 Node.js 安装 Vue !

npm create vue@latest

这一指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具。

npm init vue@latest

这样也行。

然后使用安装命令对下载下来的脚手架进行安装。

npm install

最后进入项目目录,让项目运行起来!

npm run dev

项目目录结构

认识一个项目先从他的目录结构开始!

文件名描述
node_modulesVue 项目的运行依赖文件夹
public资源文件夹
src源码文件夹
index.html入口 Html 文件
package.json信息描述文件
vite.config.jsVue 配置文件

模板语法

Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。

在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。

文本插值​

最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):

template

<span>Message: {{ msg }}</span>

双大括号标签会被替换为相应组件实例中 msg 属性的值。同时每次 msg 属性更改时它也会同步更新。

使用 JavaScript 表达式

至此,我们仅在模板中绑定了一些简单的属性名。但是 Vue 实际上在所有的数据绑定中都支持完整的 JavaScript 表达式:

template

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div :id="`list-${id}`"></div>

这些表达式都会被作为 JavaScript ,以当前组件实例为作用域解析执行。

在 Vue 模板内,JavaScript 表达式可以被使用在如下场景上:

  • 在文本插值中 (双大括号)
  • 在任何 Vue 指令 (以 v- 开头的特殊 attribute) attribute 的值中

仅支持表达式

每个绑定仅支持单一表达式,也就是一段能够被求值的 JavaScript 代码。一个简单的判断方法是是否可以合法地写在 return 后面。

因此,下面的例子都是无效的:

template

<!-- 这是一个语句,而非表达式 -->
{{ var a = 1 }}

<!-- 条件控制也不支持,请使用三元表达式 -->
{{ if (ok) { return message } }}

调用函数

可以在绑定的表达式中使用一个组件暴露的方法:

template

<time :title="toTitleDate(date)" :datetime="date">
  {{ formatDate(date) }}
</time>
绑定在表达式中的方法在组件每次更新时都会被重新调用,因此应该产生任何副作用,比如改变数据或触发异步操作。

受限的全局访问

模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。该列表中会暴露常用的内置全局对象,比如 Math 和 Date

没有显式包含在列表中的全局对象将不能在模板内表达式中访问,例如用户附加在 window 上的属性。然而,你也可以自行在 app.config.globalProperties 上显式地添加它们,供所有的 Vue 表达式使用。

原始 HTML

双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html 指令

<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

这里我们遇到了一个新的概念。这里看到的 v-html attribute 被称为一个指令。指令由 v- 作为前缀,表明它们是一些由 Vue 提供的特殊 attribute,你可能已经猜到了,它们将为渲染的 DOM 应用特殊的响应式行为。这里我们做的事情简单来说就是:在当前组件实例上,将此元素的 innerHTML 与 rawHtml 属性保持同步。

绑定属性

数据绑定的一个常见需求场景是操纵元素的 CSS class 列表和内联样式。因为 class 和 style 都是 attribute,我们可以和其他 attribute 一样使用 v-bind 将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue 专门为 class 和 style 的 v-bind 用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组。

绑定对象

我们可以给 :class (v-bind:class 的缩写) 传递一个对象来动态切换 class:

<div :class="{ active: isActive }"></div>

上面的语法表示 active 是否存在取决于数据属性 isActive 的正假值。

你可以在对象中写多个字段来操作多个 class。此外,:class 指令也可以和一般的 class attribute 共存。举例来说,下面这样的状态:

const isActive = ref(true)
const hasError = ref(false)

配合以下模板:

<div
  class="static"
  :class="{ active: isActive, 'text-danger': hasError }"
></div>

渲染的结果会是:

<div class="static active"></div>

当 isActive 或者 hasError 改变时,class 列表会随之更新。举例来说,如果 hasError 变为 true,class 列表也会变成 "static active text-danger"

绑定一个对象

绑定的对象并不一定需要写成内联字面量的形式,也可以直接绑定一个对象:

const classObject = reactive({
  active: true,
  'text-danger': false
})
<div :class="classObject"></div>

这将渲染:

<div class="active"></div>

我们也可以绑定一个返回对象的计算属性。这是一个常见且很有用的技巧:

const isActive = ref(true)
const error = ref(null)

const classObject = computed(() => ({
  active: isActive.value && !error.value,
  'text-danger': error.value && error.value.type === 'fatal'
}))
<div :class="classObject"></div>

绑定多个对象

<template>
<div v-bind="objectofAttrs">往来无白丁</div>
</template>

<script>
export default{
    data(){
        return{
            objectofAttrs:{
                id:'container',
                class: 'wrapper'
            }
        }
}
</script>

条件渲染

用于条件性地渲染一块内容。这块内容只会在指令的表达式返回真值时才被渲染

v-if

v-if 指令用于条件性地渲染一块内容。

<h1 v-if="awesome">Vue is awesome!</h1>

v-else

v-else也可以使用 v-else 为 v-if 添加一个“else 区块”。

<button @click="awesome = !awesome">Toggle</button>

<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>

v-else-if

v-else-if 顾名思义,v-else-if 提供的是相应于 v-if 的“else if 区块”。它可以连续多次重复使用:

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

和 v-else 类似,一个使用 v-else-if 的元素必须紧跟在一个 v-if 或一个 v-else-if 元素后面。

v-show

另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样:

<h1 v-show="ok">Hello!</h1>

不同之处在于 v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。

v-show 不支持在 <template> 元素上使用,也不能和 v-else 搭配使用。

v-if Vs. v-show

v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。

v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。

相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。

总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。

列表渲染

我们可以使用 v-for 指令基于一个数组来渲染一个列表。v-for 指令的值需要使用 item in items 形式的特殊语法,其中 items 是源数据的数组,而 item 是迭代项的别名

<script>
  export default {
    data(){
      return {
        goods:[
          {title:"Air Jordan CMFT 11", price:603},
          {title:"Nike ZoomX Invincible Run Flyknit 3", price:1135},
          {title:"李宁 反伍二代", price:328},
          {title:"Converse/匡威 1970s", price:299},
          {title:"Nike Zoom Freak 3", price:839}
        ],
        style_price:{
          class: 'price',
        }
      }
    }
  }
</script>
<template>
  <h3>Tish is a List!</h3>
  <li v-for="good in goods">{{good.title}} <span v-bind="style_price">¥{{good.price}}</span></li>
</template>

<style>
.price{
  color:orange;
  font-style: italic;
  font-weight: bold;
}
</style>

在 v-for 块中可以完整地访问父作用域内的属性和变量。v-for 也支持使用可选的第二个参数表示当前项的位置索引。

当然我们也可以用 of 来代替 in 进行相关循环

v-for 与 对象

v-for 也可以遍历对象

<p v-for="(item,key,index) of userInfo">{{key}}|{{index}}|{{item}}</p>

v-for 可以直接接受一个整数值。在这种用例中,会将该模板基于 1...n 的取值范围重复多次。

<span v-for="n in 10">{{ n }}</span>

注意此处 n 的初值是从 1 开始而非 0

通过 Key 管理状态

Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。

默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况

为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key attribute:

<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>

当你使用 <template v-for> 时,key 应该被放置在这个 <template> 容器上:

<template v-for="todo in todos" :key="todo.name">
  <li>{{ todo.name }}</li>
</template>
key 在这里是一个通过 v-bind 绑定的特殊 attribute。请不要和在 v-for 中使用对象里所提到的对象属性名相混淆。

推荐在任何可行的时候为 v-for 提供一个 key attribute,除非所迭代的 DOM 内容非常简单 (例如:不包含组件或有状态的 DOM 元素),或者你想有意采用默认行为来提高性能。

key 绑定的值期望是一个基础类型的值,例如字符串或 number 类型。不要用对象作为 v-for 的 key。关于 key attribute 的更多用途细节,请参阅 key API 文档

事件处理

我们可以使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="handler" 或 @click="handler"

事件处理器 (handler) 的值可以是:

  1. 内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与 onclick 类似)。
  2. 方法事件处理器:一个指向组件上定义的方法的属性名或是路径。

内联事件处理器

<script>
  export default {
    data(){
      return {
        clickCount: 0,
      }
    }
  }
</script>

<template>
  <button @click="clickCount++">😁Add Me~</button>
  <p>The count is:{{clickCount}}</p>
</template>

方法事件处理器

随着事件处理器的逻辑变得愈发复杂,内联代码方式变得不够灵活。因此 v-on 也可以接受一个方法名或对某个方法的调用。

<script>
  export default {
    data(){
      return {
        clickCount: 0,
      }
    },
    // 所有函数方法放这里
    methods:{
      addClick(){
        this.clickCount = this.clickCount + 100;
      }
    }
  }
</script>

<template>
  <button @click="clickCount++">😁Add Me~</button>
  <button @click="addClick">😁Add Me + 100~</button>
  <p>The count is:{{clickCount}}</p>
</template>

我们可以使用 <script> 中的 methods 来在里面添加函数!

事件传参

<script>
  export default {
    data(){
      return {
        clickCount: 0,
      }
    },
    // 所有函数方法放这里
    methods:{
      removeClick(e){
        this.clickCount = this.clickCount - 1;
        e.target.innerHTML = '😅Add Me - ' + this.clickCount;
      }
    }
  }
</script>

<template>
  <button @click="removeClick">😅Add Me - 1~</button>
  <p>The count is:{{clickCount}}</p>
</template>

在 Vue 中的 event 对象,就是原生JS的 Evet 对象

传参到事件函数

<script>
  export default {
    data(){
      return {
        goods:[{
          "order_id": 1,
          "goods_id": "43220",
          "name": "Asics/亚瑟士 Gel-Flux 4 包裹性 舒适缓冲 跑鞋",
          "price": "0",
          "style_id": "46004336",
          "style_name": "GS/白色/红色/蓝色",
        },
          {
            "order_id": 7,
            "goods_id": "5975629",
            "name": "匹克 态极闪现6 低帮 减震支撑 防滑耐磨 回弹 篮球鞋",
            "price": "389",
            "style_id": "230837174",
            "style_name": "开心果绿/贻贝绿",
          },
          {
            "order_id": 8,
            "goods_id": "6532",
            "name": "New Balance 878 舒适百搭 防滑耐磨 缓震 网布透气 运动 复古 休闲鞋",
            "price": "402",
            "style_id": "4239338",
            "style_name": "黑灰色",
          },
          {
            "order_id": 9,
            "goods_id": "462661",
            "name": "Puma/彪马 Clyde Hardwood Team 缓震 篮球鞋",
            "price": "281",
            "style_id": "5333625",
            "style_name": "纯白/黑色",
          }]
      }
    },
    methods: {
      getGoodsDetail(good) {
        alert("["+ good.style_name + "]" + good.name + "¥" + good.price)
      }
    }
  }
</script>

<template>
  <div>
    <h3>商品清单</h3>
    <p @click="getGoodsDetail(good)" v-for="good of goods" :key="good.goods_id">{{good.name}} | ¥ {{good.price}}</p>
  </div>
</template>

<style>

</style>

直接使用 getGoodsDetail(good) 进行传参

如果想获取 event 对象,又想传参,这时候我们可以将上述代码稍作修改

<script>
  export default {
    data(){
      return {
        goods:[
          {
            "order_id": 9,
            "goods_id": "462661",
            "name": "Puma/彪马 Clyde Hardwood Team 缓震 篮球鞋",
            "price": "281",
            "style_id": "5333625",
            "style_name": "纯白/黑色",
          }]
      }
    },
    methods: {
      getGoodsDetail(good,e) {
        alert("["+ good.style_name + "]" + good.name + "¥" + good.price)
      }
    }
  }
</script>

<template>
  <div>
    <h3>商品清单</h3>
    <p @click="getGoodsDetail(good,$event)" v-for="good of goods" :key="good.goods_id">{{good.name}} | ¥ {{good.price}}</p>
  </div>
</template>

<style>

</style>

加入  $event 即可传入 event 对象!

事件修饰符

在处理事件时调用 event.preventDefault() 或 event.stopPropagation() 是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑而不用去处理 DOM 事件的细节会更好。

<script>
  export default {
    methods: {
      getGoodsDetail(good) {
        alert("["+ good.style_name + "]" + good.name + "¥" + good.price)
      },
      clickHandler(e) {
        e.preventDefault()
        console.log('click');
      }
    }
  }
</script><template>
  <div>
    <h3>商品清单</h3>
    <div v-for="good of goods" :key="good.goods_id">
      <span>{{good.name}} | ¥ {{good.price}}  .</span>
      <a :href=good.goods_pic @click="clickHandler"><span style="color:green">Buy it!</span></a>
    </div>
  </div>
</template>

上面是外联模式,为解决这一问题,Vue 为 v-on 提供了事件修饰符。修饰符是用 . 表示的指令后缀,包含以下这些:

  • .stop 禁止冒泡(触发子元素,顺便把父元素也冒泡了)
  • .prevent 阻止默认事件
  • .self
  • .capture
  • .once
  • .passive
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>

<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>

<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>

<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>

<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>

stop 修饰符

<div@click="clickDiv">
<p@click="clickP">测试冒泡</p>
</div>

<script>
methods:{
    clickP(e){
        e.stopPropagation();
        console.log("p");
    }
}
</script>

当然我们不想要他触发 stopPropagation

数组变化侦测

Vue能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。

变更方法

这些变更方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

替换一个数组方法

变更方法,顾名思义,就是会对调用它们的原数组进行变更。相对地,也有一些不可变 (immutable) 方法,例如 filter()concat() 和 slice(),这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的:

this.items = this.items.filter((item) => item.message.match(/Foo/))

你可能认为这将导致 Vue 丢弃现有的 DOM 并重新渲染整个列表——幸运的是,情况并非如此。Vue 实现了一些巧妙的方法来最大化对 DOM 元素的重用,因此用另一个包含部分重叠对象的数组来做替换,仍会是一种非常高效的操作。

<script>
export default {
  data(){
    return {
      goods:[
        {id:1,title:"Air Jordan CMFT 11", price:603},
        {id:2,title:"Nike ZoomX Invincible Run Flyknit 3", price:1135},
        {id:3,title:"李宁 反伍二代", price:328},
        {id:4,title:"Converse/匡威 1970s", price:299},
        {id:5,title:"Nike Zoom Freak 3", price:839}
      ]
    }
  },
  methods: {
    addProduct(){
      //引起UI自动更新的方法
      //this.goods.push({id:6,title:"361° 飞燃2.5ET", price:299})
      //合并一个数组,不会让UI变化(只合并,不赋值)
      this.goods.concat([{id:6,title:"361° 飞燃2.5ET", price:299},{id:7,title:"Under Armour/安德玛 Curry 9", price:479}])
      console.log(this.goods)
    }
  }
}
</script>

<template>
  <h3>数组变化监听</h3>
  <button @click="addProduct">添加数据</button>
  <ul>
    <li v-for="good in goods" :key="good.id">{{good.title}}</li>
  </ul>
</template>

计算属性

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。比如说,我们有这样一个包含嵌套数组的对象:

export default {
  data(){
    return {
      goods:[
        {id:1,title:"Air Jordan CMFT 11", price:603},
        {id:2,title:"Nike ZoomX Invincible Run Flyknit 3", price:1135},
        {id:3,title:"李宁 反伍二代", price:328},
        {id:4,title:"Converse/匡威 1970s", price:299},
        {id:5,title:"Nike Zoom Freak 3", price:839},
        {id:6,title:"361° 飞燃2.5ET", price:299},
        {id:7,title:"Under Armour/安德玛 Curry 9", price:479}
      ]
    }
  }
}

我们想根据 goods 是否已有一些运动鞋来展示不同的信息:

<p>购物车是否有商品:</p>
<span>{{ goods.length > 0 ? 'Yes' : 'No' }}</span>

这里的模板看起来有些复杂。我们必须认真看好一会儿才能明白它的计算依赖于 goods。更重要的是,如果在模板中需要不止一次这样的计算,我们可不想将这样的代码在模板里重复好多遍。

因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。这是重构后的示例:

<script>
export default {
  data(){
    return {
      goods:[
        {id:1,title:"Air Jordan CMFT 11", price:603},
        {id:2,title:"Nike ZoomX Invincible Run Flyknit 3", price:1135},
        {id:3,title:"李宁 反伍二代", price:328},
        {id:4,title:"Converse/匡威 1970s", price:299},
        {id:5,title:"Nike Zoom Freak 3", price:839},
        {id:6,title:"361° 飞燃2.5ET", price:299},
        {id:7,title:"Under Armour/安德玛 Curry 9", price:479}
      ]
    }
  },
  computed:{
    totalPrice(){
      let total=0;
      for(let i =  0; i < this.goods.length; i++){
        total += this.goods[i].price;
      }
      return total;
    }
  }
}
</script>

<template>
  <h3>🛒我的购物车</h3>
  <ul>
    <li v-for="god in goods" :key="god.id">{{god.title}} | <span style="color: orange">Price:¥{{god.price}}</span></li>
  </ul>
  <h4>Total: ¥{{totalPrice}}</h4>
</template>

计算属性缓存 vs 方法

可能注意到我们在表达式中像这样调用一个函数也会获得和计算属性相同的结果。

若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。

重点区别:
计算属性:计算属性值会基于其响应式依赖被缓存。二个计算属性仅会在其响应式依赖更新时才重新计算
方法:方法调用总是会在重渲染发生时再次执行函数
留下足迹请遵守法律规则哦~
February 9th, 2025 at 02:17 amShveynaya_cket
February 9th, 2025 at 02:17 am

Оптимальные цены на швейную фурнитуру оптом в Москве, заказывайте оптовые партии.
Купить швейную фурнитуру оптом http://www.sewingsupplies.ru/ .

访客
February 9th, 2025 at 12:37 amSerebryany_zrPl
February 9th, 2025 at 12:37 am

Популярные мастерские по изготовлению серебряных колец в Саратове, которые станут украшением вашего стиля.
Купить кольцо серебро женское https://xn--b1acnbnotaei0k.xn--p1ai/ .

访客
January 30th, 2025 at 12:40 pmStomatolog_usel
January 30th, 2025 at 12:40 pm

Лучшие стоматологические клиники в Минске, современными методиками, и стать обладателем здоровой улыбки.
Прейскурант цен стоматология http://belamed.ru/ .

访客
January 27th, 2025 at 05:28 pmBazalnaya_qmMi
January 27th, 2025 at 05:28 pm

Базальная имплантация зубов с гарантией качества.
Базальная имплантация цена https://www.belfamilydent.ru/services/basal-implantation/ .

访客
January 27th, 2025 at 05:28 pmCirkonievy_xger
January 27th, 2025 at 05:28 pm

Лучшие специалисты в Минске установят циркониевые коронки, обеспечат безупречный результат.
Поставить циркониевую коронку https://www.belfamilydent.ru/services/koronki-iz-cirkoniya .

访客