首页 > 其他分享 >vue 2实战系列 —— 复习Vue

vue 2实战系列 —— 复习Vue

时间:2023-12-31 18:22:26浏览次数:34  
标签:vue 复习 value Vue 组件 message data

复习Vue

近期需要接手 vue 2的项目,许久未写,语法有些陌生。本篇将较全面复习 vue 2。

Tip: 项目是基于 ant-design-vue-pro

ant-design-vue-pro

由于 cms 是基于这个项目开发的,所以笔者就将其下载下来。

下载后运行

// 按照依赖
yarn install

// 本地启动
yarn run serve

根据提示输入 admin/admin 即可登录。

全局概览

直接通过script引入vue

就像这样:

<body>
    <div id='app'></div>
    <!-- V -->
    <script type="text/x-template" id="tpl">
        <section>
            {{ message }}
            <p v-if="seen">现在你看到我了</p>
        </section>
    </script>

    <script>
    // VM
    var app = new Vue({
        el: '#app',
        template: '#tpl',
        // M
        data: {
            message: 'Hello Vue!',
            seen: true
        }
    })
    </script>
</body>

有以下几点需要注意:

  • vue 是一个 mvvm 的框架,分数据模型M视图V视图模型VM三部分
  • Vue 实例作为 ViewModel,充当连接 Model 和 View 的桥梁。它负责将 Model 的数据绑定到 View 上,并且在数据发生变化时更新 View。因此,整个 Vue 实例是 ViewModel
  • VM部分主要是new Vue中的实参,该对象有 template、data
  • 模板中的 seen 属性直接取自 Vue 实例中的 data,没有添加什么 this
  • {{ message }} 属于模板语法,用于插入值。

将上述示例转成单文件组件。就像这样:

<template>
  <section>
    {{ message }}
    <p v-if="seen">现在你看到我了</p>
  </section>
</template>

<script>
// es6 导出模块语法
export default {
  data() {
    return {
      message: 'Hello Vue!',
      seen: true,
    };
  },
};
</script>

<style scoped>
section {
  color: red;
}
</style>

单文件组件通常由三部分组成:template、script、style。

工作中通常是单文件组件开发,相对于通过script引入vue的方式,其处理复杂项目更加方便。

前面我们知道单文件组件中的 template、script、style 会被 vue-loader 解析,然后交给不同的 loader 处理。

单文件组件在写法上和script引入vue的方式有差异。比如在单文件组件中 data 属性是一个方法,里面返回一个对象,而在script引入vue中,data选项可以直接是一个对象,而不需要通过函数返回。

template

vue 单文件组件中 template 有两种含义:

  • 根级的 <template> 标签用作整个模板的容器
  • 作为内置标签<template>,用于逻辑分组的容器,其自身不会被渲染
<template>
  <div>
    <template v-if="condition1">
      <h1>条件 1 成立</h1>
      <template v-if="nestedCondition1">
        <p>内嵌条件 1 成立</p>
      </template>
      <template v-else>
        <p>内嵌条件 1 不成立</p>
      </template>
    </template>
    
    <template v-else>
      <h1>条件 1 不成立</h1>
      <template v-if="condition2">
        <h2>条件 2 成立</h2>
        <template v-if="nestedCondition2">
          <p>内嵌条件 2 成立</p>
        </template>
        <template v-else>
          <p>内嵌条件 2 不成立</p>
        </template>
      </template>
      <template v-else>
        <h2>条件 2 不成立</h2>
        <p>默认内容</p>
      </template>
    </template>
  </div>
</template>

几点需要注意:

  • v-show 不能用于 <template>
  • v-if 和 v-for 可用于 <template>

Tip: 在 Vue 3 中,模板可以包含多个根级别的元素,这样更加灵活。但在 Vue 2 中,仍然需要使用一个根元素来包裹组件的内容。

{{ }}

可以在 {{ }} 进行简单运算、三元运算

<p>{{ 5 + 10 }}</p> 
<p>{{ message.split('').reverse().join('') }}</p> 
<p>{{ value1 > value2 ? value1 : value2 }}</p>

{{}} 期望的是一个单个表达式,而不是语句、流控制如 if、for

不能这样:

{{ var book = "西游记" }}
{{ if (value > 5) value = 10 }}
{{ for(let i=0; i<5; i++) total += i }}

正确的方式应该是使用 Vue 的指令:

<!-- 条件渲染 -->
<div v-if="value > 5">value is greater than 5</div>
<div v-else>value is not greater than 5</div>

<!-- 列表渲染 -->
<ul>
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
</ul>

指令

指令是 vue 模板中最常见的一项功能,前缀是 v-。比如 v-if

指令的主要职责是当表达式的值改变时,相应的某些行为应用到 dom 上。

比如 v-bind 最基本用途是动态更新html元素上的属性,比如 id、class:

<div v-bind:id="dynamicId"></div>

<!-- 缩写成 -->
<div :id="dynamicId"></div>

只挂载一次

el 或 .$mount() 在单文件组件项目中一般只出现一次,用来将 Vue 实例挂载到特定的 DOM 元素上。

直接通过script引入vue会这样写:

var app = new Vue({
    el: '#app',
    ...
})

单文件组件这么写:

new Vue({
  router,
  store,
  created: bootstrap,
  render: h => h(App),
  ...
}).$mount('#app')

生命周期

vue 中常用的生命周期钩子:

  • created: 常用于请求 ajax;DOM 还未生成
  • mounted:可以操作 dom 元素
  • beforeDestroy:销毁前,比如清除定时器

计算属性

对于任何复杂逻辑,你都应当使用计算属性。请看示例:

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

data: {
  message: 'Hello'
},
computed: {
  // 计算属性的 getter
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

Tip: 两个小技巧:

  • 计算属性可以依赖其他计算属性
  • 计算属性不仅可以依赖当前 Vue 实例的数据,还可以依赖其他实例的数据。
缓存

计算属性是基于它们的响应式依赖进行缓存的

下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:

computed: {
  now: function () {
    return Date.now()
  }
}

我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

setter

计算属性默认只有 getter,不过在需要时你也可以提供一个 setter(业务中很少用到):

/ ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...

现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。

过滤器

vue3 中废除了过滤器,vue2 可以使用计算属性替代。

下面是一个过滤器简单示例:

<!-- 在双花括号中 -->
{{ message | capitalize }}

filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

内置指令

v-html

有时候想输出 html,可以使用 v-html。

<div v-html="message"></div>

<script>
export default {
  data() {
    return {
      message: '<h1>Hello, Vue!</h1>'
    };
  }
}
</script>

如果将用户产生的内容使用 v-html 输出,可能导致 XSS 攻击,所以要在服务端对用户提交的内容进行处理,一般可将尖括号(<>)转义。

v-pre

如果只想显示 {{ }} 标签,而不是进行取值,使用 v-pre 可跳过这个元素和它的子元素的编译过程。

浏览器会原样显示绑定的内容,包括文本插值、指令和其他 Vue 特性

当有大量的静态内容需要处理,使用 v-pre 可以减少 Vue 编译的时间,从而提升性能

<div id="app">
  <p v-pre>{{ message }}</p>
  <p>{{ message }}</p>
</div>

<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Hello, Vue!'
    }
  });
</script>

v-if

v-if、v-else-if、v-else 与 if、else、else if 类似。

v-else-if 需要紧跟 v-if,v-else 需要紧跟 v-else-if 或 v-if。

比如阻隔了,就会报错:

<p v-if="aFlag == 1">早上好!</p>
<p>阻隔</p>
<p v-else-if="aFlag == 2">下午好!</p>
<p v-else>晚上好!</p>
用 key 管理可复用的元素

vue 渲染元素时,处于效率考虑,会尽可能复用已有元素而非重新渲染,比如:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address">
</template>

切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,<input> 不会被替换掉——仅仅是替换了它的 placeholder。

Vue 提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

v-show

v-show 用法和v-if类似,只不过 v-show 只改变元素的 css 属性 display。

v-if 是真正的条件渲染,而 v-show 总是渲染。

如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好

v-for

v-for 可以迭代数组对象,用法和 for...in 类似。

// for...in 循环
let obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
  console.log(key); // 输出 a, b, c
}

  • 遍历数组:
<li v-for="item in items" :key="item.message">
    {{ item.message }}
</li>

data: {
  items: [
    { message: 'Foo' },
    { message: 'Bar' }
  ]
}
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>

data: {
  parentMessage: 'Parent',
  items: [
    { message: 'Foo' },
    { message: 'Bar' }
  ]
}
  • 遍历对象:
<li v-for="value in object">
  {{ value }}
</li>

data: {
  object: {
    title: 'How to do lists in Vue',
    author: 'Jane Doe',
    publishedAt: '2016-04-10'
  }
}

/*
输出:
How to do lists in Vue
Jane Doe
2016-04-10
*/
<div v-for="(value, name) in object">
  {{ name }}: {{ value }}
</div>

/*
输出:
title: How to do lists in Vue
author: Jane Doe
publishedAt: 2016-04-10
*/
<div v-for="(value, name, index) in object">
  {{ index }}. {{ name }}: {{ value }}
</div>

/*
输出:
0. title: How to do lists in Vue
1. author: Jane Doe
2. publishedAt: 2016-04-10
*/

Tip: v-for 还可以迭代整数

// 输出:“Item 1”, “Item 2”, “Item 3”, “Item 4”, “Item 5”
<div id="app">
  <div v-for="i in 5" :key="i">
    Item {{ i }}
  </div>
</div>

数组更新

vue2 只有这7个方法将会触发视图更新:

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

filter()、concat() 和 slice() 不会触发视图更新。这些非变异方法总是返回一个新数组。

app.books[3] = {...} 不会触发视图更新,可以使用 vm.$set(this.$set(app.books, 3, {...}))触发

排序和过滤

计算属性非常适合用来处理排序和过滤逻辑

<div id="app">
  <ul>
    <li v-for="person in sortedPeople" :key="person.id">
      {{ person.name }}
    </li>
  </ul>
</div>

data: {
  people: [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
    { id: 3, name: 'Eve' }
  ]
},
computed: {
  sortedPeople() {
    return this.people.slice().sort((a, b) => a.name.localeCompare(b.name));
  }
}

事件

v-on 类似原生js 的 onclick 方法。

例如每点击一次,数字就会增加1:

<button @click="count++">{{count}}</button>

<button @click="show = false">点击隐藏</button>

Tip:@click 类似于 v-on:click,一个语法糖。

在大部分业务场景中,如果不需要传入参数,可以不写括号:@click=greet。以下是几种写法的区别:

<!-- 什么都不传 -->
<button v-on:click="greet()">Greet</button>
<!-- 默认会传递一个原生事件对象 event -->
<button v-on:click="greet">Greet</button>
<!-- $event 是Vue 提供的一个特殊变量,表示原生事件对象 -->
<button v-on:click="greet('hello', $event)">Greet</button>
事件修饰符

在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求,为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。修饰符是由开头的指令后缀来表示的。

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive

具体用法:

<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

v-once

定义它的元素和组件只渲染一次。在业务中很少使用,当需要进一步优化性能,可能会用到。

自定义指令

内置指令能满足我们大部分需求,但一些特殊功能,需要对 dom 进行底层操作,这时可以使用自定义指令。

一个自定义指令对象的基本结构是:

{
  bind(el, binding, vnode) {
    // 在指令绑定到元素时的逻辑
  },
  inserted(el, binding, vnode) {
    // 在元素插入到DOM中的逻辑
  },
  update(el, binding, vnode, oldVnode) {
    // 在包含组件的VNode更新时的逻辑
  },
  componentUpdated(el, binding, vnode, oldVnode) {
    // 在包含组件的VNode和其子VNode更新之后的逻辑
  },
  unbind(el, binding, vnode) {
    // 在指令与元素解绑时的逻辑
  }
}

比如我们在 ant-desigin-vue-pro 项目中增加一个 v-focus 的自定义指令:

// 定义指令文件
// ant-design-vue-pro-master\src\core\directives\focus.js
import Vue from 'vue'
const action = Vue.directive('focus', {
    // 当被绑定的元素插入到 DOM 中时……
    inserted: function (el) {
      // 聚焦元素
      el.focus()
    }
  })

export default action
// 引入指令。全局都可以使用
// ant-design-vue-pro-master\src\core\lazy_use.js
import './directives/focus'

用法很简单:<input v-focus/>。页面加载后就会自动聚焦到这个 input 元素。

样式

样式相关的是 class 和内联样式,都是属性,可以用 v-bind 处理。通过表达式计算,字符串拼接的方式比较麻烦,所以 v-bind 用于 class 和 style 时,Vue 专门做了增强,表达式除了是字符串,还可以是对象或数组。

class

对象语法
<div v-bind:class="{ active: isActive }"></div>

// isActive 为 true,渲染为
<div class="active"></div>
<div v-bind:class="classObject"></div>

data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}

当 :class 表达式过长或逻辑复杂,还可以绑定计算属性,这是一个常用且强大的模式:

<div v-bind:class="classObject"></div>

computed: {
  classObject: function () {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}
数组语法
<div v-bind:class="[activeClass, errorClass]"></div>

data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}

// 渲染为
<div class="active text-danger"></div>

可以用三元表达式切换列表中的 class

<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>

当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法:

<div v-bind:class="[{ active: isActive }, errorClass]"></div>
用在组件上

当在一个自定义组件上使用 class 时,这些 class 将被添加到该组件的根元素上面。

Vue.component('my-component', {
  template: '<p class="foo bar">Hi</p>'
})

<my-component class="baz boo"></my-component>

最终渲染:<p class="foo bar baz boo">Hi</p>。对于带数据绑定 class 也同样适用,例如:

<my-component v-bind:class="{ active: isActive }"></my-component>

// 当 isActive 为 true 时,HTML 将被渲染成为
<p class="foo bar active">Hi</p>

内联样式

:style 给元素添加内联样式,也有对象语法数组语法,实际业务中数组语法不那么常见。

// 无效。<p>组件宽度</p>
<p style="{color: 'red'}" >组件宽度</p>

// 有效:<p style="color: red;">组件宽度</p>
<p :style="{color: 'red'}" >组件宽度</p>

看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

data: {
  activeColor: 'red',
  fontSize: 30
}

直接绑定到一个样式对象或computed通常更好,这会让模板更清晰:

<div v-bind:style="styleObject"></div>

data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

数组语法:

<div v-bind:style="[baseStyles, overridingStyles]"></div>

:当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS property 时,如 transform,Vue.js 会自动侦测并添加相应的前缀 —— 笔者使用 <p style="transform: scale(2, 0.5);">test</p> 在chrome和edge 中没看见有什么前缀。

表单

原生表单比较难用,一些框架对表单做一些神奇的事情来减轻程序员的负担,有的需要我们了解很多框架的内部实现,有的很容易使用但是可能不够灵活。

下面我们看看在 vue 中如何使用表单。你对比原生(input、textarea、select)和vue提供的这种 v-model 方式,你会发现 vue 的这种方式和原生的套路有点不同,但确实方便一些。

可以用 v-model 指令在表单 <input><textarea><select> 元素上创建双向数据绑定。

// 可以不用单独指定 type="text", 因为 type="text" 是默认值。在浏览器中,如果没有明确指定 input 标签的 type 属性时,它的默认值就是 text
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>

data: {
  message: ''
}

在输入框中输入时,{{ message }} 内容也会实时更新。

对于 textare 也是相同用法:

<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>

Tip: 使用 v-model 后,表单控件的显示值只依赖绑定的数据,不在关心初始化时的 value,对于 <textarea></textarea>之间的插值,也不会生效。

// 初始值是 a
<input v-model="message" placeholder="edit me" value="2"> 
data() {
  return {
    message: 'a'
  }
},

v-model

v-model 本质上不过是语法糖(无需死机,按照 vue 提供的这种语法来写即可):

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件
<input v-model="message" placeholder="edit me"> 
等于
<input type="text" :value="message" @input="message = $event.target.value" placeholder="edit me">


<input type="radio" v-model="picked" :value="avalue">
等于
<input type="radio" :checked="picked === avalue" @change="picked = avalue">

Tip:v-model 也可以在组件上创建双向绑定。更多介绍请看下文的“组件”章节

单选按钮

首先回顾 html 单选按钮的基本用法:

<input type="radio" id="option1" name="color" value="red">
<label for="option1">Red</label>

<input type="radio" id="option2" name="color" value="blue" checked>
<label for="option2">Blue</label>

<input type="radio" id="option3" name="color" value="yellow">
<label for="option3">Yellow</label>

let selectedColor = document.querySelector("input[name='color']:checked").value;
console.log(selectedColor); // 输出被选中的 Radio 按钮的值,例如 `blue`

单选按钮单独使用时,不需要使用 v-model,为真时选中,否时不选中:

<input type="radio" :checked="picked">

data() {
  return {
    picked: true
  }
},

单选按钮单独使用时,如果需要绑定一个动态数据,可以使用 v-bind。比如下面这个例子,默认是未选中,选中后,picked 也变成 3。

// 选中后,picked 的值就是 avalue 的值
<input type="radio" v-model="picked" :value="avalue">
<p>picked={{ picked }}</p>
<p>value={{ value }}</p>

data() {
  return {
    picked: true,
    avalue: 3,
  }
},

组合实现互斥:

<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
{/*  */}
<span>Picked: {{ picked }}</span>

data: {
  picked: ''
}

复选框

复选框单独使用,使用 v-model,选中时为 true,取消勾选为 false:

<p>{{ picked }}</p>
<input type="checkbox" v-model="picked" >

组合使用时,绑定到同一个数组。

<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>
<br>
<span>Checked names: {{ checkedNames }}</span>

data: {
  checkedNames: []
}

都选中时,输出Checked names: [ "Jack", "John", "Mike" ],都未选中,输出Checked names: []

初始未勾选,勾选时 toggle 是 yes,取消勾选 toggle 是 no:

<p>{{ toggle }}</p>
<input type="checkbox" 
    v-model="toggle" 
    true-value="yes"
    false-value="no"
    />

return {
  toggle: true,
}

选择框

首先回顾原生 select 用法:

<select id="mySelect">
  <option value="option1">Option 1</option>
  <option value="option2" selected>Option 2</option>
  <option value="option3">Option 3</option>
</select>

<script>
  const selectElement = document.getElementById('mySelect');
  const selectedValue = selectElement.value;
  console.log(selectedValue);
</script>
  • 单选示例:
<select v-model="selected">
  <option disabled value="">请选择</option>
  <option>A</option>
  <option value="BBB">B</option>
  <option>C</option>
</select>
<span>Selected: {{ selected }}</span>

data() {
  return {
    // 首先和 value 熟悉匹配,如果没有则和 option 中的内容匹配
    selected: 'BBB',
  }
},

初始时输出 Selected: BBB,选中A则输出Selected: A

Tip: 选中 123 时,selected 的值是{ number: 123 }

<select v-model="selected">
  <!-- 内联对象字面量 -->
  <option v-bind:value="{ number: 123 }">123</option>
</select>
  • 多选示例:
<select v-model="selected" multiple>
  <option disabled value="">请选择</option>
  <option>A</option>
  <option value="BBB">B</option>
  <option>C</option>
</select>
<span>Selected: {{ selected }}</span>
  
data() {
  return {
    selected: ['A', 'C'],
  }
},

多选增加 multiple 属性,selected 变成数组,这里初始时选中A和C。

  • 用 for 渲染 option
<select v-model="selected">
  <option v-for="option in options" :value="option.value">
    {{ option.text }}
  </option>
</select>
<span>Selected: {{ selected }}</span>

data: {
  selected: 'A',
  options: [
    { text: 'One', value: 'A' },
    { text: 'Two', value: 'B' },
    { text: 'Three', value: 'C' }
  ]
}

Tip:select 因为样式依赖浏览器,样式无法统一,功能也受限,比如不支持搜索,所以常用 div 模拟。

修饰符

  • .lazy
<input v-model.lazy="message" placeholder="edit me">
    <p>Message is: {{ message }}</p>

失去焦点或回车时才更新。

Tip:比如有的同学做 cms(内容管理系统)时,页面上部分是搜索条件,下部分是表格,输入框输入字符,去取消 axios 请求,另一些同学则是通过 lodash 做延迟请求,是否直接使用 .lazy 就可以?

  • .number
    即使是 type=number,再次改变数字后,将输出 string。
<input v-model="number" type="number" min="0" max="100" step="5" />
<p>{{ typeof number }}</p>

data() {
  return {
    number: 10,
  }
},

使用修饰符 .numnber 可以将输入转为 Number。

  • .trim
    .trim 可以自动过滤首位空格:
<input v-model.trim="message" />
<p>{{ message.length }}</p>

data() {
  return {
    message: ''
  }
},

输入 a,将输出 1。

系统修饰符

.exact - 修饰符允许你控制由精确的系统修饰符组合触发的事件。

<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>

鼠标按钮修饰符 - 以下这些修饰符会限制处理函数仅响应特定的鼠标按钮。

.left
.right
.middle
按键修饰符
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

<input v-on:keyup.delete="submit">

组件

props、事件以及 slot 内容分发构成了 Vue 组件的 3 个 API 来源。

第一个组件

vue 单文件组件中,一个 .vue 文件就是一个组件。

开发过程中,我们应该要有意识的将一些常用的模块抽取成组件(组件的复用性),这对后续的迭代和维护是很有利的。

比如我们创建一个组件 MyComponent.vue:

<template>
    <div>
      <h1>{{ message }}</h1>
      <button @click="changeMessage">Change Message</button>
    </div>
  </template>
  
  <script>
  export default {
    data() {
      return {
        message: 'Hello, World!'
      };
    },
    methods: {
      changeMessage() {
        this.message = 'New Message!';
      }
    }
  };
  </script>

使用该组件也非常方便:

<template>
  <div>
    <my-component></my-component>
  </div>
</template>

<script>

import MyComponent from './MyComponent.vue';

export default {
  components: {
    MyComponent
  }
};
</script>

Tip: 比如 ant-design-vue-pro 在 src/components/index.js 就统一导出了这些组件。

// chart
import Bar from '@/components/Charts/Bar'
import ChartCard from '@/components/Charts/ChartCard'
import Liquid from '@/components/Charts/Liquid'
import MiniArea from '@/components/Charts/MiniArea'
import MiniSmoothArea from '@/components/Charts/MiniSmoothArea'
import MiniBar from '@/components/Charts/MiniBar'
import MiniProgress from '@/components/Charts/MiniProgress'
import Radar from '@/components/Charts/Radar'
import RankList from '@/components/Charts/RankList'
import TransferBar from '@/components/Charts/TransferBar'
import TagCloud from '@/components/Charts/TagCloud'

// pro components
import AvatarList from '@/components/AvatarList'
import Ellipsis from '@/components/Ellipsis'
import FooterToolbar from '@/components/FooterToolbar'
import NumberInfo from '@/components/NumberInfo'
import Tree from '@/components/Tree/Tree'
import Trend from '@/components/Trend'
import STable from '@/components/Table'
import MultiTab from '@/components/MultiTab'
import IconSelector from '@/components/IconSelector'
import TagSelect from '@/components/TagSelect'
import StandardFormRow from '@/components/StandardFormRow'
import ArticleListContent from '@/components/ArticleListContent'

import Dialog from '@/components/Dialog'

export {
  AvatarList,
  Bar,
  ChartCard,
  Liquid,
  MiniArea,
  MiniSmoothArea,
  MiniBar,
  MiniProgress,
  Radar,
  TagCloud,
  RankList,
  TransferBar,
  Trend,
  Ellipsis,
  FooterToolbar,
  NumberInfo,
  Tree,
  STable,
  MultiTab,
  IconSelector,
  TagSelect,
  StandardFormRow,
  ArticleListContent,
  Dialog
}

特殊的 is 属性

我们知道在html中,有的标签内规定只能是某些元素,比如 ul 子元素只能是 li。在 ul 内直接使用组件是无效的:

<ul>
  <my-component/>
</ul>

可以使用特殊的 is 属性:

<ul>
  <li is="my-component"></li>
</ul>

最后渲染成:

<ul>
  <div>
    <h1>Hello, World!</h1>
    <button>Change Message</button>
  </div>
</ul>

:在单文件组件中不受此限制。

is 结合 <component> 实现动态挂载组件。就像这样:

<div id='app'>
  <!-- vue 提供了 <component> 来动态的挂载组件 -->
  <component :is="currentComponent"></component>
  <button @click='switchHandle'>切换组件</button>
</div>

Tip: 更多介绍请看 vue 的基础应用(下)-动态组件

props

通常父组件的模板中包含子组件,父组件要正向地向子组件传递数据,子组件接根据接收参数的不同来渲染或执行,这个过程就是通过 props 实现 —— props 用于接收来自父组件的数据。

在组件中通过 props 申明需要从父组件接收的参数:

props: {
  prefixCls: {
    // 类型
    type: String,
    // 默认值
    default: 'ant-pro-global-header-index-action'
  },
  isMobile: {
    type: Boolean,
    default: () => false
  },
  topMenu: {
    type: Boolean,
    required: true
  },
  theme: {
    type: String,
    required: true
  }
},
data () {
  return {
    showMenu: true,
    currentUser: {}
  }
},

Tip:props 还可以是数组,通常对象类型的更常用。例如 ant-design-vue-pro 就是对象类型的 props,除了可以定义名称,还能定义类型和默认值。

// 数组类型
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

通常当你的组件提供给别人用时,推荐都进行数据校验。更多用法请看官网。例如:

Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        // <ValidationComponent :propF="'info'" /> 则会报一个警告,因为 info 不在这3个枚举中
        return ['success', 'warning', 'danger'].includes(value)
      }
    }
  }
})
null 和 undefined

基础的类型检查 (nullundefined 会通过任何类型验证),意思是当你使用 Vue 的 props 属性进行类型检查时,null 和 undefined 值将通过任何类型的验证。

请看示例:

<h1>{{ typeof postTitle }}</h1>
props: {
  postTitle: {
    type: String,
    default: ''
  },
},

通过下面三种方法使用组件:

  1. 控制台报错,提示期望字符串,传过来的是数字
  2. 控制台没报错,postTitle 的值是 null
  3. 控制台没报错,postTitle 的值是 ''
// Expected String with value "123", got Number with value 123.
 <my-component :post-title="123"/> 

// object
<my-component :post-title="null"/>
    
// string
<my-component :post-title="undefined"/>
required

定义了 required:true,虽然同时定义了默认值,但是不传该属性,控制台会报错:

// Missing required prop: "postTitle"
<my-component />

props: {
  postTitle: {
    type: String,
    default: '',
    required: true
  },
},

传递 null 报错:

// Invalid prop: type check failed for prop "postTitle". Expected String, got Null
<my-component :post-title="null"/>

:传递 undefined 不报错,postTitle 会取默认值

<my-component :post-title="undefined"/>
prop 大小写

定义的时使用驼峰,使用时使用 kebab-case (短横线分隔命名) 命名。就像这样:

props: {
  postTitle: {
    type: String,
    default: ''
  }
},

<my-component post-title="apple"/>

Tip:如果使用单文件组件就没有这种限制,例如可以 <my-component postTitle="apple3"/>。ant-design-vue-pro 使用的就是 kebab-case 这种用风格。

动态 prop

使用 v-bind: 传递动态 prop:

// 静态
<blog-post title="My journey with Vue"></blog-post>

// 动态
<blog-post v-bind:title="post.title"></blog-post>

// 动态赋予一个复杂表达式的值:字符串 `peng li`
<my-component :postTitle="'peng' + ' ' + 'li'"/>

:直接使用数字、布尔、对象、数组,而不使用 v-bind,传递的则是字符串。

// 字符串
<my-component number="10"/>

// 数字
<my-component :number="10"/>
单向数据流

vue2.x 与 vue1.x 比较大的一个改变是,vue2.x 通过 props 传递数据是单向的了。也就是父组件数据变化时会传递给子组件,但反过来就不行。而在 vue1.x 里提供的 .sync 修饰符来支持双向。vue2.x 中的的 sync 本质是单向的。

这么设计是将父子组件解耦,避免子组件无意修改父组件的状态。

业务中经常遇到两种改变 prop 的情况,一种是父组件传递初始值进来,子组件将它作为一个初始值保存起来,然后在自己的作用域中随意使用和修改。例如:

// 子组件
<div>
  <p>postTitle:{{ postTitle }}</p>
  <p>title:{{ title }}</p>
</div>

props: {
  postTitle: {
    type: String,
    default: '',
    required: true
  },
},
data() {
  return {
    title: this.postTitle
  };
},

父组件给子组件传递初始值 value 后,子组件只要维护 title,这样就避免直接操作 PostTitle:

// 父组件
<input v-model="value"/>
<my-component :post-title="value"/>

另一种就是 prop 作为需要被转变的原始值传入。这种情况用计算属性就可以了。请看示例:

<p :style="styleObject">组件宽度</p>

props: {
  width: {
    type: Number
  },
},
computed: {
  styleObject(){
    return {
      width: this.width + 'px',
      backgroundColor: 'pink',
    }
  }
},

随着父组件更改 value 的值,子组件的宽度也会随之变化:

<div>
  <input v-model.number="value"/>
  <my-component :width="value"/>
</div>

组件通信

组件的关系有父子、兄弟、子孙。

组件可以独立存在,也可用来创建其他组件

组件更多的是以灵活的方式被使用,应该关注其独立性和常常不带任何负担。所以组件只关注其父母和孩子,兄弟关系可以不管。

自定义事件

当子组件向父组件传递数据时,需要用到自定义事件。

js 中观察者模式,有被观察者和观察者,其中有 addEventListener(注册) 和 dispatchEvent(触发) 这两个方法。就像这样:

// 创建一个被观察者对象
const observer = {
  listeners: {},
  addEventListener(event, listener) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event].push(listener);
  },
  removeEventListener(event, listener) {
    if (this.listeners[event]) {
      this.listeners[event] = this.listeners[event].filter(l => l !== listener);
    }
  },
  dispatchEvent(event, data) {
    if (this.listeners[event]) {
      this.listeners[event].forEach(listener => listener(data));
    }
  }
};

// 创建观察者函数
function observer1(data) {
  console.log('观察者1收到通知:', data);
}

function observer2(data) {
  console.log('观察者2收到通知:', data);
}

// 将观察者注册到被观察者中
observer.addEventListener('event1', observer1);
observer.addEventListener('event2', observer2);

// 触发事件并通知观察者
observer.dispatchEvent('event1', 'Hello World');
observer.dispatchEvent('event2', { name: 'Alice', age: 25 });

// 注销观察者
observer.removeEventListener('event1', observer1);

// 再次触发事件并通知观察者
observer.dispatchEvent('event1', 'Hello Again');
observer.dispatchEvent('event2', { name: 'Bob', age: 30 });

Vue 组件也有一套类似模式,子组件用 $emit() 触发事件,父组件用 $on() 来监听子组件的事件。

我们从官网知道 vm.$on 监听当前实例上的自定义事件。从示例也很容易看出来:

vm.$on('test', function (msg) {
  console.log(msg)
})
vm.$emit('test', 'hi')
// => "hi"

现在有一个疑惑:上面我们写的观察者模式示例,监听和触发的方法都在被观察者中,而现在我们的用法是:在父组件中使用 v-on 注册,在子组件中使用 $emit。为什么不是在同一处注册和触发?

// 父组件
<button-counter v-on:chang-count='changCount'></button-counter>

// 子组件
<button @click='triggerHandle'>触发</button>
methods: {
  triggerHandle: function(){
    // 触发事件
    this.$emit('chang-count', this.num++);
  }
}

要回答这个问题,我们得了解模板编译和虚拟dom的一些事情,在模板编译阶段,会将模板编译成渲染函数,随后会创建 Vnode。在这个过程中会创建一些元素,也会判断当前这个标签是真的标签(例如 div)还是一个组件:如果是组件,就会将子组件实例化,并传递一些参数,其中就包含父组件在模板中使用 v-on 注册在子组件标签上的事件;如果是平台标签(例如 div),则创建元素并插入到dom中,同时将 v-on 注册的事件注册到浏览器事件中。

简单来说,如果 v-on 写在组件标签上,这个事件就会注册到子组件 Vue 的事件系统中,如果是平台标签,例如 div,事件就注册到浏览器事件中。

下面这个示例,子组件提供两个按钮,一个加1,一个减1,点击时会通知父组件更新数据:

// 子组件
<template>
  <div>
    <p><button @click="handleIncrease">加1</button> <button @click="handleReduce">减1</button></p>
  </div>
</template>
  
  <script>
export default {
  props: {},
  data() {
    return {
      counter: 0,
    }
  },
  computed: {},
  methods: {
    handleIncrease() {
      this.counter++
      this.$emit('increase', this.counter)
    },
    handleReduce() {
      this.counter--
      this.$emit('reduce', this.counter)
    },
  },
}
</script>

父组件
<template>
  <div>
    <p>value: {{ value }}</p>
    <my-component @increase="update" @reduce="update" />
  </div>
</template>

<script>
import MyComponent from './MyComponent.vue'

export default {
  components: {
    MyComponent,
  },
  methods: {
    update(value) {
      this.value = value
    },
  },
  data() {
    return {
      value: 0,
    }
  },
}
</script>
.native

v-on 用在组件上监听自定义事件,也可以监听 dom 事件,这是可以用 .native 修饰符,表示监听一个原生事件,监听该组件的根元素。就像这样:

// 父组件
<my-component @click.native="handleClick"/>

 methods: {
  handleClick(){
    console.log('apple');
  }
},

相当于在子组件的根元素上注册了一个原生的 click 事件,点击子组件,则会输出 apple。

爷孙传递数据

需求:爷组件给孙组件传递数据,孙组件给爷组件传递数据。

父给子传递数据可以用 props,子组件给父组件传递数据用 emit。

这个需求我们当然可以借助 props 和 emit 来实现,但感觉父亲转发好累,而且假如某个属性命名父亲不用需要使用,但还是得在 props 中标明,容易给别人误解,因为父亲不需要使用这个属性。

介绍两个属性:

  • vm.$listeners - 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器
  • vm.$attrs - 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外)

我们先将实现写出来,再来分析这两个属性:

// 爷爷组件
<template>
    <Father :message="message" @changeMessage="handleMessage"/>
</template>

<script>
import Father from '@/views/result/Father'
export default {
    components:{
        Father,
    },
    data() {
        return {
            message: 'apple'
        }
    },
    methods: {
        handleMessage(v) {
            this.message = v
        }
    }
}
</script>
// 父亲组件
<template>
    <Grandson v-bind="$attrs" v-on="$listeners"/>
</template>

<script>
import Grandson from '@/views/result/Grandson'
export default {
    components:{
        Grandson,
    },
}
</script>
// 孙子组件
<template>
    <div>
        <p>我是孙子:</p>
        <p>来自爷爷的 message: {{message }}</p>
        <button @click="handleClick">点击改变 爷爷的 message</button>
    </div>
</template>

<script>
export default {
    props: {
        message: {
            type: [String, Date]
        }
    },
    methods: {
        handleClick(){
            this.$emit('changeMessage', new Date())
        }
    }
}
</script>

现在孙子组件接收到了来自爷爷的属性,通过点击按钮,也能改变爷爷的属性:

<div message="apple">
  <p>我是孙子:</p>
  <p>来自爷爷的 message: apple</p>
  <button>点击改变 爷爷的 message</button>
</div>
$attrs 和 $listeners

上文提到 $attrs “包含了父作用域中不作为 prop 被识别”,什么意思?请看示例:

爷爷组件给父亲组件多传一个 tel 属性

// 爷爷组件
<Father tel="131xxxx" :message="message" />

父亲组件输出 {tel: '131xxxx', message: 'apple'}

// 父亲组件
created(){
    console.log(this.$attrs);
},

如果给父组件增加 props: ['tel'] 来接收 tel 属性,父组件则输出 {message: 'apple'},tel 属性不在 $attrs 中。

Tip:attrs 还有不包含 class、style,也是同样的意思。另外 $listeners 中不含 .native 也不在冗余介绍。

v-model

在上文“表单”中我们使用过 v-model,用在组件中,本质也是一样的。请看示例:

// 父组件
<template>
  <div>
    <p>count: {{ count }}</p>
    <my-component v-model="count"/>
  </div>
</template>
  
<script>
  import MyComponent from './MyComponent.vue'
  export default {
    components: {
      MyComponent,
    },
    methods: {
    
    },
    data() {
      return {
        count: 10,
      }
    },
  }
</script>
// 子组件
<template>
  <div>
    <input :value="msg" @input="inputHandle" />
  </div>
</template>
  
<script>
export default {
  props: ['value'],
  data: function () {
    return {
      msg: this.value,
    }
  },
  methods: {
    inputHandle: function (e) {
      this.msg = e.target.value
      console.log('this.msg: ', this.msg);
      // 触发事件
      this.$emit('input', this.msg)
    },
  },
}
</script>

父组件中使用 <my-component v-model="count"/> 绑定一个数据 count,子组件用 value 接收数据,子组件数据变化时通过 this.$emit('input', this.msg) 传递数据给父元素,实现双向绑定。

sync

sync修饰符和v-model指令在某种程度上可以说是类似的,因为它们都用于在父子组件之间实现双向数据绑定。

<input v-model="message" />
等同于
<input :value="message" @input="message = $event.target.value" />
<text-document v-bind:title="doc.title" v-on:update:title="doc.title = $event" ></text-document>
等同于
<text-document v-bind:title.sync="doc.title"></text-document>

通过在传递给子组件的属性上使用.sync修饰符,Vue会自动将绑定的属性以一个名为update:propertyName的事件形式传递给子组件。子组件只需要在需要更新属性时触发该事件即可。

请看 sync 示例:

// 父组件
<template>
  <div>
    父组件: name = {{ name }}
    <my-component :name.sync="name"></my-component>
  </div>
</template>
  
<script>
import MyComponent from './MyComponent.vue'
export default {
  components: {
    MyComponent,
  },
  data() {
    return {
      name: 'hello',
    }
  },
}
</script>
// 子组件
<template>
   <div>
        <p>子组件: name = {{name}}</p>
        <p><button @click='handleClick'>change name</button></p>
      </div>
</template>
  
<script>
export default {
  props: ['name'],

  methods: {
    handleClick: function(){
        // update:name 就是一个事件名
        this.$emit('update:name', new Date())
      }
  },
}
</script>

Tip: 更多介绍请看 vue 的基础应用-.sync 修饰符

非父子组件通信

vue 1.x 中的 $dispatch 和 $broadcast 在 vue 2.x 中废除。在 vue 2.x 中可以使用 中央事件总线(bus) 来替代 —— 参考 vue 的基础应用-非父子组件通信

除了 bus 还有,父链(vm.$parent)、子链(vm.$children),以及子组件索引(vm.$refs)来实现组件之间通信 —— 参考 vue 的基础应用-父组件和子组件

vm.$refs - 一个对象,持有注册过 ref attribute 的所有 DOM 元素和组件实例。

Tip:在 ant-design-vue-pro 中没有使用 bus、$children,$parent 也只有一处。

插槽

ant-design-vue-pro 中没有使用插槽。搜不到 v-scope。但稍微使用了 $slots:

// Ellipsis.vue
<script>
import Tooltip from 'ant-design-vue/es/tooltip'
import { cutStrByFullLength, getStrFullLength } from '@/components/_util/util'
/*
    const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined;

    const TooltipOverlayStyle = {
      overflowWrap: 'break-word',
      wordWrap: 'break-word',
    };
  */

export default {
  name: 'Ellipsis',
  components: {
    Tooltip
  },
  props: {
    prefixCls: {
      type: String,
      default: 'ant-pro-ellipsis'
    },
    tooltip: {
      type: Boolean
    },
    length: {
      type: Number,
      required: true
    },
    lines: {
      type: Number,
      default: 1
    },
    fullWidthRecognition: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    getStrDom (str, fullLength) {
      return (
        <span>{ cutStrByFullLength(str, this.length) + (fullLength > this.length ? '...' : '') }</span>
      )
    },
    getTooltip (fullStr, fullLength) {
      return (
        <Tooltip>
          <template slot="title">{ fullStr }</template>
          { this.getStrDom(fullStr, fullLength) }
        </Tooltip>
      )
    }
  },
  render () {
    const { tooltip, length } = this.$props
    const str = this.$slots.default.map(vNode => vNode.text).join('')
    const fullLength = getStrFullLength(str)
    const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength)
    return (
      strDom
    )
  }
}
</script>

vm.$slots 用来访问被插槽分发的内容。每个具名插槽有其相应的 property (例如:v-slot:foo 中的内容将会在 vm.$slots.foo 中被找到)。default property 包括了所有没有被包含在具名插槽中的节点,或 v-slot:default 的内容。

vm.$slots 在业务中几乎用不到,在用 render 函数创建组件时比较有用,主要用在独立组件开发。

最简单的 render() 单文件组件:

// 渲染出:<div>Hello, rendering function!</div>

<script>
export default {
  data() {
    return {
      message: 'Hello, rendering function!'
    };
  },
  render(createElement) {
    return createElement('div', this.message);
  }
};
</script>

Tip: 更多介绍请看 vue 的基础应用-通过插槽分发内容

标签:vue,复习,value,Vue,组件,message,data
From: https://www.cnblogs.com/pengjiali/p/17937838

相关文章

  • 算法复习
    目录渐近记号O记号Ω记号Θ记号分治策略分治算法的效率分析迭代法求运行时间递归树法求运行时间主定理法求运行时间渐近记号O记号渐近上界记号O(大O)渐近地给出了一个函数在常量因子内的上界:O(g(n))={f(n):存在正常量c和n0,使得对所有n≥n0,有0≤f(n)≤cg(n)}f(n......
  • 生物统计复习
    1.绪论1.1统计学研究数据的收集、整理、分析和解释的科学,是处理数据中变异性的科学和艺术。统计分析可分为统计描述和统计推断两部分统计描述:用统计图表、统计指标或几个特征数描述资料的数据特征和分布规律统计推断:用样本信息来推断总体特征目的:求得可靠的结......
  • 【电影推荐系统】Spring Boot + Vue3 前后端分离项目
    目录0前言1项目前端介绍1.1项目启动和编译1.1.1项目启动1.1.2项目编译1.2前端技术栈1.3功能模块前端界面展示1.3.1基础功能模块1.3.2用户模块1.3.3特色功能展示1.3.4界面自适应实现2项目后端介绍2.1项目打包部署2.1.1项目打包2.1.2项目部署2.2后端功能实现3数据......
  • vue3的ref、reactive、toRef、toRefs
    用处:用于在setup中创建响应式变量导出:import{ref,reactive,toRef,toRefs}from'vue'区别:ref用来定义基础数据类型,String,Number,Boolean,Symbol;通过Object.defineProperty()的get和set来实现响应式;js操作数据需要.value,模版中读取不需要.valuereactive用来定义......
  • vue改错总结
    vue表单和表格一起使用的时候,表格里非编辑框不再使用v-model进行双向数据绑定,当tabledatase的数据发生变化时候,会自动渲染新的数据,但是可便捷table单元格需要用到插槽技术,当更新name的时候才会自动渲染<template><div><el-form:model="form"><el-table:data=......
  • 前端环境搭建到创建vue项目运行
    整体流程1.安装nvm管理工具,用于管理node的版本方便node版本的切换a.下载nvm1.官方下载地址:https://github.com/coreybutler/nvm-windows/releases2.备用下载地址:https://xiaocai1688.lanzouw.com/b04wvgimj密码:h33mb.直接双击安装,验证安装成功输入命令nvm-v......
  • 关于vue里面使用markdown插入视频前台渲染导致<video>等标签被转义成字符,导致视频加载
    markdown中插入视频前台渲染出来导致<video>等标签被转义成字符解决办法:如图:在markdown里面插入视频,可以按照下面写法,哈哈,虽然很捞,但是还是可以满足的。<videosrc="https://xxxxxxxxxxxxxx.mp4"controls="true"style="max-width:100%;height:auto;"></video> ......
  • 在Vue3中实现前端导出Excel功能
    在Vue3中,可以使用第三方库如xlsx来导出Excel文件。以下是一个基本示例:一、Vue3常规写法安装xlsx库首先,你需要安装xlsx库。使用以下命令进行安装:npminstallxlsx导入函数在Vue组件中,导入所需的函数:import{writeFile}from'xlsx';创建模拟数据创建一个函数来生成Excel文件的数......
  • 在 Django 中使用 Vue.js 组件的步骤如下³⁴: 1. **安装 Vue.js**:首先,你需要在你的开
    在Django中使用Vue.js组件的步骤如下³⁴:1.**安装Vue.js**:首先,你需要在你的开发环境中安装Vue.js³。2.**创建Vue组件**:在Vue.js中,你可以创建一个新的Vue组件。例如,你可以在`src/components`文件夹下新建一个名为`Home.vue`的组件¹。3.**在Django模板中引......
  • Vue 发布 3.4 「灌篮高手」版本
    本文翻译自AnnouncingVue3.4,作者:EvanYou,略有删改。今天我们很高兴地宣布Vue3.4......