@SovietPower
2022-06-11T15:53:55.000000Z
字数 31961
阅读 1947
学习笔记
作业部落链接:https://zybuluo.com/SovietPower/note/1826585
参考:
https://www.runoob.com/vue3/vue3-tutorial.html
官方文档:https://v3.vuejs.org/
https://v3.cn.vuejs.org/guide/introduction.html
目前:https://v3.cn.vuejs.org/guide/component-attrs.html#attribute-%E7%BB%A7%E6%89%BF
因为内容太多,很花时间所以就不全部记录了,平时直接看官网。只将框架写下来,若有补充内容则写到对应位置去。
Vue(/vjuː/)是一套用于构建用户界面的渐进式框架。
与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用,目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
类似jQuery,Vue也是一个.js文件。
在 https://unpkg.com/vue@3.2.20/dist/vue.global.js 中下载。
将该文件放在当前网页的目录中,在HTML中使用<script>即可使用该文件:
<head><script src="vue.global.js"></script></head>
Staticfile CDN: https://cdn.staticfile.org/vue/3.0.5/vue.global.js
unpkg: https://unpkg.com/vue@next
cdnjs: https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.5/vue.global.js
例:
<head><script src="https://cdn.staticfile.org/vue/3.0.5/vue.global.js"></script></head>
见:https://www.runoob.com/vue3/vue3-install.html
Vue 提供了一个官方命令行工具vue-cli,用于快速搭建大型单页应用。
通过createAPP()创建Vue的一个应用实例。应用实例是用来在应用中注册“全局”组件的。
const app = Vue.createApp({ /* 选项 */ })
传递给createApp()的选项参数会用于配置根组件。在使用mount()挂载应用时,根组件是渲染的起点。
一个应用需要被挂载到一个 DOM 元素中,通过mount('ElementSelector')。
应用不能直接被重新挂载。
应用的大部分方法会返回应用本身,所以支持链式:
Vue.createApp({}).component('SearchInput', SearchInputComponent).directive('focus', FocusDirective).use(LocalePlugin)
但mount()不返回应用本身,而是返回根组件实例。
文档中经常会使用变量名vm(ViewModel)表示组件实例,以表示参考了MVVM模型。
应用被组织成一个嵌套的、可重用的组件树。每个组件将有自己的组件实例vm。
比如一个Todo应用组件树:
Root Component 根组件└─ TodoList├─ TodoItem│ ├─ DeleteTodoButton│ └─ EditTodoButton└─ TodoListFooter├─ ClearTodosButton└─ TodoListStatistics
有的组件,如TodoItem,在任何时候都可能有多个实例渲染,这个应用中的所有组件实例将共享同一个应用实例。
例:
Vue.createApp(Hello).mount('#qwq')
该应用的根组件为Hello,该应用被挂载到了id="qwq"的元素中。
较完整的例子:
<body><div id="app">cnt: {{count}}</div> <!-- 输出:cnt: 6 --><script>const APP = Vue.createApp({data() {return { count: 4 }}})const vm = APP.mount('#app')document.write(vm.$data.count) // => 4document.write("<br>")document.write(vm.count) // => 4document.write("<br>")// 修改 vm.count 的值也会更新 $data.countvm.count = 5document.write(vm.$data.count) // => 5document.write("<br>")// 反之亦然vm.$data.count = 6document.write(vm.count) // => 6</script></body>
Vue应用APP被挂载到了<div id="app"></div>元素上。
挂载元素中的{{}},会被替代为应用的对应对象的属性或函数返回值(只要对应值改变,输出就改变)。{{count}}即应用中的count属性值。
data组件选项是一个函数,将用户定义的属性property,以$data的形式存储在组件实例中。
Vue 在创建新组件实例时会调用该函数。它应返回一个对象,然后 Vue 会通过响应性系统将其包裹起来。
通过vm.$data.count,就可访问实例vm的data元素。也可直接用vm.count访问元素(即该对象的任何顶级 property 也会直接通过组件实例暴露出来)。访问元素时可修改该元素。
实例属性仅在实例首次创建时被添加,所以需确保要用的属性都在 data 函数返回的对象中(也可暂时赋值为null, undefined等)。
Vue 使用$前缀通过组件实例暴露自己的内置 API,且为内部 property 保留了_作为前缀。所以应避免使用这$, _开头的的顶级 data property 名称。
组件的methods组件选项,可添加方法到组件实例中。
const app = Vue.createApp({data() {return { count: 4 }},methods: {increment() {// `this` 指向该组件实例this.count++}}})const vm = app.mount('#app')document.write(vm.count) // => 4document.write("<br>")vm.increment()document.write(vm.count) // => 5
Vue 自动为methods绑定this,使它始终指向组件实例。
定义methods时应避免使用箭头函数,因为这会阻止 Vue 绑定恰当的this指向。
methods和组件实例的其它所有 property 一样可以在组件的模板中被访问。
在模板中,它们通常被当做事件监听使用,如:<button @click="increment">Up vote</button>在点击button时,会调用increment方法。
也可以直接从模板中调用方法,即在模板支持 JavaScript 表达式的任何地方调用方法:
<span :title="toTitleDate(date)">{{ formatDate(date) }}</span>
如果toTitleDate或formatDate访问了任何响应式数据,则将其作为渲染依赖项进行跟踪,就像直接在模板中使用一样。
从模板调用的方法不应该有任何副作用,如更改数据或触发异步进程等。如果需要,应使用生命周期钩子函数而不是直接调用的方法。
防抖和节流
见:https://v3.cn.vuejs.org/guide/data-methods.html#%E9%98%B2%E6%8A%96%E5%92%8C%E8%8A%82%E6%B5%81
其他一些组件选项,如props, computed, inject, setup,也可将用户定义的属性添加到组件实例中。
每个组件在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。在这个过程中会运行叫做生命周期钩子的函数,以便用户在不同阶段可添加功能。
钩子函数就类似C++的析构函数?或者Python的
__init__, __del__?
例:
created()钩子用于在一个实例创建后执行代码:
Vue.createApp({data() {return { count: 1}},created() {// `this` 指向 vm 实例console.log('Count generated: ' + this.count) // => "Count generated: 1"}})
钩子函数还有mounted, updated, unmounted等。生命周期钩子的this指向调用它的当前活动实例。
应用的生命周期图示:
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层组件实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。
文本
数据绑定最常见的形式就是使用 Mustache (双大括号) 语法的文本插值。
<span>Message: {{ msg }}</span>
Mustache 标签会被替代为对应组件实例中 对应的property(如msg)的值。只要绑定的组件实例上 property(如msg)发生了改变,插值处的内容都会更新。
使用v-once,可执行一次性地插值。当组件数据改变时,插值处的内容不会更新。但注意这会影响整个节点,包括其它数据。
<span v-once>这个元素将不会改变: {{ msg }}</span>
原始HTML插值
双大括号会将数据解释为普通文本,而非 HTML 代码。要作为真正的 HTML 输出,需使用v-html。
下面的span的内容会被替换成rawHtml属性的值,且直接作为 HTML 运行,忽略解析属性值中的数据绑定。
注意,不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎。反之,对于用户界面 (UI),组件更适合作为可重用和可组合的基本单位。
在站点上动态渲染任意的 HTML 是非常危险的,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要将用户提供的内容作为插值。
<div id="example1" class="demo"><p>Using mustaches: {{ rawHtml }}</p><!-- 会输出文本<span style="color: red">This should be red.</span> --><p>Using v-html directive: <span v-html="rawHtml"></span></p><!-- 会输出红色的This should be red. --></div><script>const RenderHtmlApp = {data() {return {rawHtml: '<span style="color: red">This should be red.</span>'}}}Vue.createApp(RenderHtmlApp).mount('#example1')</script>
Attribute
双大括号不能在HTML属性(attribute)中使用,但可通过v-bind绑定。
<div v-bind:id="dynamicId"></div>
其中dynamicId是一个可变property。
如果绑定了值null或undefined,则该属性将不会被包含在渲染的元素上。
对于布尔属性(属性存在则值为true,否则为false)规则略有不同。如果赋值为truthy或空字符串(即与HTML中attr_name=""一致),则该布尔属性会被包含在内。对于其他falsy的值,该属性将被省略。
<div v-bind:id="dynamicId"></div><button v-bind:disabled="isDisabled">按钮</button> <!-- 根据isDisabled是否为truthy或"",决定是否为真 -->
truthy见:https://developer.mozilla.org/en-US/docs/Glossary/Truthy
使用JavaScript表达式
除绑定 property 键值外,还可绑定JS表达式。表达式会在当前活动实例的数据作用域下作为 JavaScript 被解析。
<body><div id="app">{{5+5}}<br>{{ ok ? 'YES' : 'NO' }}<br>{{ message.split('').reverse().join('') }}<div v-bind:id="'list-' + id">用JS表达式动态绑定id</div></div><script>const app = {data() {return {ok: true,message: 'abc!',id: 1}}}Vue.createApp(app).mount('#app')</script></body>
注意,每个绑定都只能包含单个表达式。
无效绑定的例子:
<!-- 这是语句,不是表达式 -->{{ var a = 1 }}<!-- 流程控制也不会生效,请使用三元表达式 -->{{ if (ok) { return message } }}
此外,模板表达式都被放在沙盒中,只能访问一个受限的全局变量列表,如 Math 和 Date。不应在模板表达式中试图访问用户定义的全局变量。
指令 (Directives) 是带有v-前缀的特殊属性(attribute)。指令属性的值预期是单个 JavaScript 表达式 (除了v-for和v-on)。
指令的功能为:在表达式的值改变时,将其产生的影响,响应式地作用于DOM。
v-if语句会根据表达式(如下面的seen)的真假,插入或移除<p>元素。
<p v-if="seen">现在你看到我了</p>
参数
一些指令可接收一个参数,指令名称冒号后面的即为参数。
例:
<a v-bind:href="url"> ... </a>
v-bind用于响应式地更新HTML属性。参数href表示,v-bind会将该元素的href属性与表达式url的值绑定。
<a v-on:click="doSomething"> ... </a>
v-on用于监听DOM事件。参数click表示其监听的事件名为click。
动态参数
指令的参数也可用JS表达式动态确定。用方括号括起。
动态参数应该为一个字符串,也可为null,用于显式地移除绑定。其它非字符串类型的值会触发警告。
注:
某些字符,如空格和引号,在 HTML attribute 名里是无效的。所以应使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式。
不要使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写。
例:
<a v-bind:[attributeName]="url"> ... </a>
此处的attributeName是一个动态求值的JS表达式,会绑定的属性名由该表达式的值确定。
<a v-on:[eventName]="doSomething"> ... </a>
用[eventName]为一个动态的事件名绑定处理函数。
修饰符
修饰符 (modifier) 为以.开头的特殊后缀,用于指定一个指令应以特殊方式绑定。
例:
.prevent修饰符告诉v-on指令对于触发的事件调用event.preventDefault()。
v-前缀作为一种视觉提示,用来识别模板中 Vue 特定的 attribute。
Vue 为v-bind和v-on这两个最常用的指令,提供了特定简写:
v-bind缩写:
<!-- 完整语法 --><a v-bind:href="url"> ... </a><!-- 缩写 --><a :href="url"> ... </a><!-- 动态参数的缩写 --><a :[key]="url"> ... </a>
v-on缩写:
<!-- 完整语法 --><a v-on:click="doSomething"> ... </a><!-- 缩写 --><a @click="doSomething"> ... </a><!-- 动态参数的缩写 --><a @[event]="doSomething"> ... </a>
v-slot缩写:
<!-- 完整语法 --><template v-slot:header><h1>Here might be a page title</h1></template><template v-slot:footer><p>Here's some contact info</p></template><!-- 缩写 --><template #header><h1>Here might be a page title</h1></template><template #footer><p>Here's some contact info</p></template>
看一个例子:
Vue.createApp({data() {return {A: {B: ['qwq','abc','123']}}}}).mount('#computed-basics')
使用如下模板内的表达式,可判断B的长度是否为0:
<div id="computed-basics"><p>Is B empty?</p><span>{{ A.B.length == 0 ? 'Yes' : 'No' }}</span></div>
但此时,模板不再是简单的和声明性的,并不能直观地看出其计算取决于A.B。
所以,对于任何包含响应式数据的复杂逻辑,都应使用计算属性:
Vue.createApp({data() {return {A: {B: ['qwq','abc','123']}}},computed: {// 计算属性的 getterisBEmptyMessage() {// `this` 指向 vm 实例return this.A.B.length == 0 ? 'Yes' : 'No'}}}).mount('#computed-basics')
此时在模板内,可直接使用计算属性isBEmptyMessage获取值。
<div id="computed-basics"><p>Is B empty?</p><span>{{ isBEmptyMessage }}</span></div>
可以像普通属性一样将数据绑定到模板中的计算属性。Vue 知道vm.isBEmptyMessage依赖于vm.A.B,因此当vm.A.B发生改变时,所有依赖vm.isBEmptyMessage的绑定也会更新。
在组件中声明与计算属性类似的方法,一样可实现上述效果:
...methods: {calcIsBEmpty() {return this.A.B.length == 0 ? 'Yes' : 'No'}}
在模板中使用方法获取值:
<div id="computed-basics"><p>Is B empty?</p><span>{{ calcIsBEmpty() }}</span></div>
计算属性和方法的直观效果是相同的,但计算属性将基于其响应依赖关系缓存。
计算属性只会在相关响应式依赖发生改变时重新求值,即只要A.B未发生改变,计算属性isBEmptyMessage都会返回之前的计算结果,不会被重新计算。
但当触发重新渲染时,方法总会重新执行计算函数。
计算属性一般更为高效。但当不能或不希望使用缓存时,需使用方法。
计算属性默认只有 getter,但可定义setter:
...computed: {// 默认只有gettergetFirstName() {return this.firstName;}fullName: {// getterget() {return this.firstName + ' ' + this.lastName},// setterset(newValue) {const names = newValue.split(' ')this.firstName = names[0]this.lastName = names[names.length - 1]}}}
使用vm.fullName = 'Alice Smith'时,会调用setter处理给定的参数。
类似Python的@propertyName.setter。
Vue 通过watch选项提供了一个更通用的方法来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
watch选项允许执行异步操作 (访问一个 API),并设置一个执行该操作的条件。
除了watch选项之外,你还可以使用命令式的vm.$watch API。
注意区分计算属性和侦听器的使用情况,适合计算属性的情况下,侦听器代码可能是命令式且重复的。
元素的 class 列表和内联样式也是 attribute,所以可使用v-bind绑定。
对 class 和 style 使用v-bind时,表达式不仅可以是字符串,还可是对象或数组。
给:class赋值一个对象,可动态控制class的属性。
如:<div :class="{ active: isActive }"></div>,该class的存在与否取决于data property isActive是否为truthy。
对象中也可传入更多字段,且:class也可与class属性共存。
如:<div class="static" :class="{ active: isActive, 'text-danger': hasError }"></div>,且有data isActive: true, hasError: false,则该class会渲染为:<div class="static active"></div>。
当isActive或hasError变化时,该class列表会相应更新。
绑定的数据对象也可定义在data内,而不必内联定义在模板里。
如:
<div :class="classObject"></div>
data() {return {classObject: {active: true,'text-danger': false}}}
绑定的数据也可是一个计算属性。
如:
<div :class="classObject"></div>
data() {return {isActive: true,error: null}},computed: {classObject() {return {active: this.isActive && !this.error,'text-danger': this.error && this.error.type === 'fatal'}}}
给:class赋值一个数组,可使用一个class列表。
如:
<div :class="[activeClass, errorClass]"></div>
data() {return {activeClass: 'active',errorClass: 'text-danger'}}
若要根据条件切换某个列表元素,可使用三元表达式。
如:<div :class="[isActive ? activeClass : '', errorClass]"></div>,会根据isActive是否为truthy决定是否添加activeClass。
数组元素也可使用对象。
如:<div :class="[{ active: isActive }, errorClass]"></div>,效果同上。
Vue 可自动添加前缀:
在:style中使用需要一个 vendor prefix (浏览器引擎前缀) 的 CSS property 时,Vue 将自动侦测并添加相应的前缀。Vue 是通过运行时检测来确定哪些样式的 property 是被当前浏览器支持的。如果浏览器不支持某个 property,Vue 会进行多次测试以找到支持它的前缀。
:style的对象语法就是使用一个JS对象。
CSS属性可用原属性的驼峰式命名,或用短横线分隔(但需加引号)。
例:
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data() {return {activeColor: 'red',fontSize: 30}}
直接绑定到一个JS对象:
<div :style="styleObject"></div>
data() {return {styleObject: {color: 'red',fontSize: '13px'}}}
:style的数组语法,可在一个元素上应用多个样式对象。
例:
<div :style="[baseStyles, overridingStyles]"></div>
可以为 style 绑定的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值。
浏览器只会渲染数组中最后一个被浏览器支持的值。
例:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
如果浏览器支持不带浏览器前缀的flexbox,那么就只会渲染display: flex。
v-if指令用于条件性地渲染某块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
还可以用v-else添加一个“else 块”。
例:
<h1 v-if="awesome">Vue is awesome!</h1><h1 v-else>Oh no</h1>
v-else
v-if失败时使用的块。v-else元素必须紧跟在带v-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-if渲染分组
v-if是一个指令,所以一次只能将其添加到一个元素上。
若想在多个元素上同时使用,可使用<template>将其划分为一组。<template>是不可见的、只用于包裹的元素。
例:
<template v-if="ok"><h1>Title</h1><p>Paragraph 1</p><p>Paragraph 2</p></template>
v-show同样可用于条件性展示元素。
使用与v-if类似,但v-show元素始终会被渲染,且保留在DOM中,只是简单切换元素的display属性。且v-show不支持<template>包裹,不支持v-else。
例:
<h1 v-show="ok">Hello!</h1>
v-if是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。
v-if也是惰性的,如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。
而v-show很简单,不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。如果需要非常频繁地切换,则应使用v-show;如果在运行时条件很少改变,则应使用v-if。
v-if
不推荐在一个元素中同时使用v-if和v-for。当v-if与v-for一起使用时,v-if优先级更高,会使v-if没有访问v-for中变量的权限。可将v-for放到外层<template>中。
如:
<template v-for="todo in todos" :key="todo.name"><li v-if="!todo.isComplete">{{ todo.name }}</li></template>
v-for指令可基于一个数组来渲染一个列表。v-for指令使用item in items或item of items形式的特殊语法,其中items是源数据数组名,item是被迭代的数组元素的别名。
注意:如果要绑定元素的标签是item,需用v-bind,即:,否则是绑定不上的(会被视为字符串)。
如下,用:label而不是本来的label:
<el-tab-panev-for="(label, index) in ['所有', '未完成', '已完成']":key="index":label="label">这是{{label}}, {{index}}</el-tab-pane>
例:
下面的代码会生成包含elem1和elem2两个元素的无序列表。
<ul id="array-rendering"><li v-for="x in array">{{ x.message }}</li></ul>
Vue.createApp({data() {return {array: [{ message: 'elem1' }, { message: 'elem2' }]}}}).mount('#array-rendering')
v-for支持可选参数当前项的索引,使用形式(value, index) in items(或of)。
v-for块中,可以访问所有父作用域的property。
例:
下面的代码会生成包含ParentMessage - 0 - elem1和ParentMessage - 1 - elem2两个元素的无序列表。
<ul id="array-with-index"><li v-for="(v, i) of arr">{{ parentMessage }} - {{ i }} - {{ v.message }}</li></ul>
Vue.createApp({data() {return {parentMessage: 'ParentMessage',arr: [{ message: 'elem1' }, { message: 'elem2' }]}}}).mount('#array-with-index')
使用形式value in object或value of object遍历对象的属性值。
同样支持可选参数当前项的键值key,使用形式(value, key) in items(或of)。
也支持第三个可选参数当前项的索引,使用形式(value, key, index) in items(或of)。
在遍历值时,会按object.keys()的结果遍历,但不能保证它在不同 JavaScript 引擎下的结果都一致。
例:
<ul id="v-for-object"><li v-for="(value, name) in obj">{{ name }}: {{ value }}</li></ul>
Vue.createApp({data() {return {obj: {title: 'How to do lists in Vue',author: 'Jane Doe',publishedAt: '2020-03-22'}}}}).mount('#v-for-object')
使用计算属性筛选数组中的偶数:
<li v-for="n in evenNumbers" :key="n">{{ n }}</li>
data() {return {numbers: [ 1, 2, 3, 4, 5 ]}},computed: {evenNumbers() {return this.numbers.filter(number => number % 2 === 0)}}
使用方法筛选数组中的偶数:
可传递参数,比计算属性更自由。
<ul v-for="numbers in sets"><li v-for="n in even(numbers)" :key="n">{{ n }}</li></ul>
data() {return {sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]}},methods: {even(numbers) {return numbers.filter(number => number % 2 === 0)}}
Vue 使用“就地更新”策略更新v-for渲染的元素列表,即如果数据项的顺序被改变,Vue 不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,使它们在每个索引位置正确渲染。
该默认模式是高效的,但只适用于不依赖子组件状态或临时 DOM 状态的列表渲染输出。
使用v-for时,最好应为每个节点提供唯一的key属性,使 Vue 能确定每个节点的身份,从而重用和重排现有的元素。除非遍历输出的 DOM 内容非常简单,或是需要默认行为以获取性能上的提升。
如:<div v-for="x in obj" :key="x.id">...</div>,或<div v-for="x in arr" :key="x">...</div>。
key应使用字符串或数值类型。
当使用索引更新数组元素时,Vue 不能检测到数据更改。
可以使用v-if重新渲染v-for(v-if取消时,会销毁元素,所以不推荐在同一元素上使用两个标签),使数据更新。
也可用要更改的数据作为key。
或:
// Vue.setVue.set(vm.items, indexOfItem, newValue)//别名:vm.$setvm.$set(vm.items, indexOfItem, newValue)
深入了解:https://blog.csdn.net/m0_37960566/article/details/107056433
变更方法
Vue 对被侦听的数组的变更方法(会变更调用了这些方法的原始数组)进行了包裹,即调用这些变更方法时,会触发视图更新。
被包裹的变更方法有:push(),pop(),shift(),unshift(),splice(),sort(),reverse()等。
替换数组
非变更方法不会更改原始数组,而是返回一个新数组。所以使用非变更方法时,可使用新数组替换旧数组。
Vue 会使 DOM 元素得到最大范围的重用,所以用一个含有部分相同元素的数组去替换原来的数组是非常高效的,不会导致 Vue 丢弃现有 DOM 并重新渲染整个列表。
被变更方法有:filter(),concat(),slice()等。
注意,props中父组件传给子组件的也不能实时监听,可以拷贝到data中
子组件不应修改父组件。所以要么用计算属性,要么拷贝过来。
父组件的修改可以用深度监听。
props监听变化的几种方式
在vue中父子组件是通过props传递数据的。通常有以下几种场景:
子组件展示父组件传递过来的props,一般是字符串
子组件通过父组件的props计算得到某个值
子组件修改父组件传递过来的props
1.computed 计算属性
使用场景:当pros传递过来的值不能直接使用的时候,就可以使用计算属性了。
比如:完整的地址需要将省市区3个字段拼接后展示。
计算属性是基于依赖进行缓存的,当props不变时,多次访问addrese计算属性,会立即返回结果,而不必再次执行计算。
props: {province: String, // 省city: String, // 市area: String // 区},computed: {address(){return `${this.province}${this.city}${this.area}`}}
2.methods 方法
上面计算属性,通过methods也可以实现,但methods不会进行缓存,而且methods通常用来处理业务逻辑。
3.watch 侦听器
使用场景:将传递过来的props作为初始值,比如城市选择列表的默认城市,我们希望选择城市时,改变这个值。
单向数据流:父级 prop 的更新会向下流动到子组件中。当我们需要在子组件中修改父组件传递过来的props时,如果直接更改,vue会报错的,因为如果子组件直接更改父组件的props,会使应用的数据流向难以理解。
解决方案:在data中,用props初始化一个data属性,以后更改这个data属性即可。
但是这会导致,父组件变化时,该data属性不会更新(注意:只针对基本类型,如果是引用类型,仍旧是变化的。因为引用类型的内存地址没变)
此时为了更新这个data属性,就需要使用侦听器来监听props的变化。
深度监听
使用场景:当监听到某个值变化后,执行异步请求。此时computed和methods都不太合适。
基本类型
watch: {msg(newV,oldV) {// do somethingconsole.log(newV,oldV)}}
引用类型
1.计算属性(推荐)
先通过计算属性得到引用类型的内部某个字段,再使用侦听器监听该字段的变化
props:{config:Object},computed: {isShow() {return this.config.role.isShow}},watch: {isShow(newV,oldV) {// do somethingconsole.log(newV,oldV)}}
2.deep
props:{config:Object},watch: {isShow:{ //深度监听,可监听到对象、数组的变化handler (newV, oldV) {// do something, 可使用thisconsole.log(newV,oldV)},deep:true}},
监听对象中的某个值
props:{option:Object},watch:{"option.text"(newVal,oldVal){console.log(newVal,oldVal);}}
使用形式i in x(或of),其中x为整数,会将模板重复对应次数。
key:https://blog.csdn.net/qq_41609404/article/details/84064064
例:
下面的代码会生成数字1到10,并依次输出。
<div id="range" class="demo"><span v-for="n in 10" :key="n">{{ n }} </span></div>
使用<template>和v-for,可以循环渲染一段包含多个元素的内容。
例:
<ul><template v-for="item in items" :key="item.msg"><li>{{ item.msg }}</li><li class="divider" role="presentation"></li></template></ul>
https://v3.cn.vuejs.org/guide/list.html#%E5%9C%A8%E7%BB%84%E4%BB%B6%E4%B8%8A%E4%BD%BF%E7%94%A8-v-for
https://v3.cn.vuejs.org/guide/events.html#%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86
https://v3.cn.vuejs.org/guide/forms.html#%E8%A1%A8%E5%8D%95%E8%BE%93%E5%85%A5%E7%BB%91%E5%AE%9A
只可用于表单form。
复选框 checkbox
复选框比较特殊。
当v-model绑定到data中的数值时,会绑定到布尔值,即是否选中,且选中一个所有绑定了同一值的复选框都会更新(即选就全选,不选全不选)。
当绑定到数组时,会绑定到选中的复选框的value,且绑定了同一值的复选框是独立的。
绑定到布尔值时,可修改属性true-value和false-value改变绑定值的名称。
这里的 true-value 和 false-value attribute 并不会影响输入控件的 value attribute,因为浏览器在提交表单时并不会包含未被选中的复选框。如果要确保表单中这两个值中的一个能够被提交,(即“yes”或“no”),请换用单选按钮。
例:
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />
单选框 radio,选择框 select
会绑定到选择的<label>的value(如果select没有则为<label>间的innerHTML)。
https://v3.cn.vuejs.org/guide/component-basics.html#%E7%BB%84%E4%BB%B6%E5%9F%BA%E7%A1%80
每个组件都会各自独立维护它的 count。因为每用一次组件,就会有一个它的新实例被创建。
例:动态标签切换:https://v3.cn.vuejs.org/guide/component-basics.html#%E5%8A%A8%E6%80%81%E7%BB%84%E4%BB%B6
https://v3.cn.vuejs.org/guide/component-registration.html#%E7%BB%84%E4%BB%B6%E6%B3%A8%E5%86%8C
https://v3.cn.vuejs.org/guide/component-props.html#prop-%E7%B1%BB%E5%9E%8B
一般有两种方式定义:
export default {// 不检测类型,全盘接受props: ["customer_id"],}
export default {props: {// 基础类型检测 (`null` 意思是任何类型都可以)propA: null,// 多种类型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) {return value > 10}},G: {type: Array,default: function () {return [];}}}}
https://v3.cn.vuejs.org/guide/component-attrs.html#attribute-%E7%BB%A7%E6%89%BF
https://v3.cn.vuejs.org/style-guide/#prop-%E5%AE%9A%E4%B9%89%E5%BF%85%E8%A6%81
https://v3.cn.vuejs.org/guide/component-slots.html#%E6%8F%92%E6%A7%BD%E5%86%85%E5%AE%B9
https://v3.cn.vuejs.org/guide/transitions-list.html
使用transition(-group)。
其中的name可以指定(或动态绑定)动画方式(在css中定义):
出现的过程: name-enter-from(初始态) => name-enter-active(中间态) => name-enter-to(终止态)
消失的过程: name-leave-from => name-leave-active => name-leave-to
其中name是指定或绑定的任意字符串,如left-enter-from, left-enter-active。
(注意初始态有-from,网上的都没有,可能是新加的)
1.设置进入时需要过渡的属性(动画发生前的状态).name-enter-from { opacity: 0; transform: translateY(30px); }2.在 name-enter-active中设置过渡时间.name-enter-active { transition: all .3s; }3.在 name-enter-to 中写上终止态属性(动画结束后的状态)(终止态的opacity: 1;transform: none; 是默认的,可以不用写).name-enter-to { opacity: 1; transform: translateY(0); }如果要给列表中的元素设置交错的效果, 元素不多的话可以添加 delay 属性.name-enter-active:nth-child(3n+1) { transition-delay: 0s; }.name-enter-active:nth-child(3n+2) { transition-delay: .1s; }.name-enter-active:nth-child(3n+3) { transition-delay: .2s; }
多个动画列表过渡时,必须设置key值。要注意这个key不应使用index索引。
如果使用index索引,在删除时Vue的diff算法去寻找dom的差异性,就会按照index从上往下去找其中的区别,就导致删除的永远是最后一项?如果没有删除则无所谓(好像没有删除也不行)?
transition-group与transition不同,它会将v-for的每个元素包裹。默认是一个span标签。
网页版饿了么:https://github.com/bailicangdu/vue2-elm
https://www.bilibili.com/video/BV1at411p7P3?p=14
下载安装nodejs:https://nodejs.org/zh-cn/
注意安装完后,修改一下安装路径(默认为C:\USERS\user_name\AppData\Roaming\npm):
npm config set prefix "...\npm"npm config set cache "...\npm-cache"
其中...是想设置的安装目录位置。
然后设置环境变量中的C:\USERS\user_name\AppData\Roaming\npm为...\npm\。
此外添加系统变量NODE_PATH,路径为...\npm\node_modules,以用于运行模组。
若npm出现
ERR_SOCKET_TIMEOUT错误(http://uusama.com/873.html):
查看自己的安装源:npm config get registry
更换npm源为国内淘宝镜像:npm config set registry http://registry.npm.taobao.org/
或者更换为国内npm官方镜像:npm config set registry http://registry.cnpmjs.org/
还原npm源:npm config set registry https://registry.npmjs.org/
安装vue cli:npm install -g @vue/cli
卸载vue cli:npm uninstall -g @vue/cli
创建项目:vue create ProfileName
运行:npm run serve(在package.json中将那个key的serve改为dev,就可改为npm run dev。没区别,只是在Vue 2中更常用dev)
https://www.jianshu.com/p/df464b26ae58
router-link
路由默认是hash模式,所以url中会带#。另一种是history模式。
router-link可以直接传入to跳转,也可以通过动态绑定传递name和参数。
<router-link to='/home'><router-link :to="{name:'home'}"><router-link :to="{path:'/home'}">
router-link中to链接如果是/开始,则从根路由开始;如果不带/,则从当前路由开始。
跳转到查询某ID的网页
使用router-link附带query结构体跳转(name path都可)
不可配置路由(不能带params)
html 取参:$route.query.id,js 取参:this.$route.query.id
<router-link:to="{ path: '/order/details', query: {orderNum:item.order_num} }"><el-button plain class="button-detail">订单详情</el-button></router-link>
在跳转后,可通过this.$route.query.orderNum查询当前网址query中的orderNum(可在activated时进行,然后更新网页数据)。
具体例子:
data: {return {orderNum: 0,}}activated() {if (this.$route.query.orderNum != undefined) {this.orderNum = this.$route.query.orderNum}},watch: {// 监听商品id的变化,请求后端获取商品数据orderNum: function() {this.load()}},methods: {load() {... // 根据query参数获取页面数据}}
也可以带params参数
需路由配置path: "/home/:id"或者path: "/home:id"
不配置path ,第一次可请求,刷新页面id会消失;配置path,刷新页面id会保留。
html 取参:$route.params.id,js 取参:this.$route.params.id
<router-link :to="{name:'home', params: {id:123456}}">
this.$router.push()
格式与router-link基本相同。
无参:
this.$router.push('/home')this.$router.push({path:'/home'})this.$router.push({name:'home'})
query传参:
1. 路由配置:name: 'home',path: '/home'2. 跳转:this.$router.push({name:'home',query: {id:'1'}})this.$router.push({path:'/home',query: {id:'1'}})3. 在转跳到的页面接收获取参数html取参: $route.query.idjs取参: this.$route.query.id
params传参(params需使用name,不能用path;query在路由配置不需要设置参数,而params必须设置):
1. 路由配置:name: 'home',path: '/home/:id' 或者 path: '/home:id' 与query不同不这样配置,网页刷新后参数会消失2. 跳转:this.$router.push({name:'home', params: {id:'1'}})注意:只能用 name 匹配路由不能用path3. 在转跳到的页面接收获取参数html取参:$route.params.idjs取参:this.$route.params.id
直接通过path传参:
1. 路由配置:name: 'home',path: '/home/:id'2. 跳转:this.$router.push({path:'/home/123'})或 this.$router.push('/home/123')3. 在转跳到的页面接收获取参数:this.$route.params.id
在后台打开另一页面
(router-link好像不支持)
用resolve获取path,然后用window.open。
let path = this.$router.resolve({name: "AdminOrderDetail",query: {order_id: table[index].order_id}})console.log('jump,', path)window.open(path.href, '_blank')
标签选择,如 全部订单|已完成订单|未完成订单
同上,也可使用router-link附带query结构体跳转(地址仍是当前地址,但多了query,如/order?type=1)。
<router-link:to="{ path: '/order', query: {type: 1} }"><el-button plain>已完成订单</el-button></router-link>
也可以全部存下来,使用v-show。
用ref给某个标签命名
对于一个包含属性ref="abc"的标签,在js中可通过this.$refs[abc]或this.$refs.abc获取该元素。
在组件上使用,可返回该组件;在原生html标签上使用,只会返回该标签。
ref属性值绑定元素都是唯一的,如果一个ref属性绑定了多个dom节点,那么这个ref属性将会默认绑定到最后设置该ref属性值的DOM节点。
如果使用Vue 4及以上的版本,不要用element ui因为不支持,用element plus!
安装与使用见:https://element-plus.org/zh-CN/guide/quickstart.html#%E5%AE%8C%E6%95%B4%E5%BC%95%E5%85%A5
src:写代码。
assets:静态文件,图片、css等。
components:小组件。
views:页面。
App.vue:入口文件。
main.js:入口文件。
router:路由。
store:状态管理仓库。储存数据,不像一般文件的数据在路由时会改变。
``:。
state:类似数据,存储状态。在其它文件html中通过$store.state.name调用数据。
mutations:修改state数据的方法,需包含参数state,使用state.name修改。在其它文件js中通过this.$store.commit('mutation_function_name')使用mutations中的函数,修改state数据。
actions:异步修改state数据的方法,需包含参数context,使用context.commit('mutation_function_name')修改。在其它文件js中通过this.$store.dispatch('action_function_name')使用actions中的函数。
getters:在获取state数据前执行的方法,类似计算属性,可再对数据进行处理。在其它文件html中通过$store.getters.name调用数据。
mutations和actions中的方法,可接收state/context之外的其它参数,传的参放在commit()/dispatch()的第二个及以后参数位置。常用对象作为第二个参数。
可使用modules对其进行分组,语法为:
modules: {groupName: {state: {...},mutations: {...},...}}
即传入一个对象。可以在外边或外部文件(需export const、import {groupName} from './groupName.js')const该groupName对象(只需格式一样,含state, mutations...)。
此时原本对数据,如$store.state.x的访问,就变成了$store.state.groupName.x,因为$store.state变成了对象。方法还不变。
若要同时分组方法,在groupName中加namespace: true,,此时原本对方法,如commit('func')的调用,改为commit('groupName/func');对getter的调用,如$store.getters.x,改为$store.getters['groupName/name']。
https://blog.csdn.net/weixin_33672109/article/details/85945765
使用mapActions将当前methods中的某些方法,映射到vuex中的dispatch。
import { mapActions } from 'vuex'export default {// ...methods: {...mapActions(['increment' // 映射 this.increment() 到 this.$store.dispatch('increment')]),...mapActions({add: 'increment' // 映射 this.add() to this.$store.dispatch('increment')})}}
从qwq.js中引入一个export了的对象api(export default api):
<script>import api from '../../assets/api/qwq.js'</script>
引入css(可不加scoped;要加分号,避免识别多条import时出错):
<style scoped>@import "../assets/common/common.css";</style>
但@import是动态加载,并没有scoped。如果要局部引入:
<style scoped src="../assets/style/login.css"></style>
单个页面body属性(会被app.vue影响):
https://segmentfault.com/q/1010000008828013
在router-link to和path中,/addr表示/#/addr。
在/par的子路由中,addr表示/#/par/addr。
在href中,/addr表示/addr!需使用/#/addr。
https://blog.csdn.net/asdfghjkl521947/article/details/108665312
https://blog.csdn.net/sps900608/article/details/79502171
el-form-item中绑定的prop,必须和el-input绑定的x.attr的attr相同(或必须在x中定义了?不知道为什么)
在rules中,用该prop定义validator。
如:
<el-form-item prop="address" label="详细地址"><el-input type="textarea" rows="5" v-model="form.address" /></el-form-item>
如果缺少运行环境,使用npm install更新。
element ui安装失败:unable to resolve dependency tree
https://blog.csdn.net/weixin_45922023/article/details/113797664
7.0版本npm新版安装多会有此依赖树错误的问题出现,所以用旧版本npm安装:npm install npm@6.14.8,然后重新npm i --save element-ui。
不过应直接安装element plus?
更改运行端口号(默认8080)
在项目名\node_modules\@vue\cli-service\lib中,将options.js下面的
devServer: {/*open: process.platform === 'darwin',host: '0.0.0.0',port: 8080,https: false,hotOnly: false,proxy: null, // string | Objectbefore: app => {}*/}
中的这段注释取消,然后将port改为任意端口号。
或自己在根目录创建vue.config.js配置文件。
module.exports = {devServer: {proxy: null, // string | Objectopen: process.platform === 'darwin',host: '0.0.0.0',port: 8000,https: false,hotOnly: false,before: app => {}}}
跨域访问
vue 3.x中需自己在根目录创建vue.config.js,配置proxy(不是proxyTable,那是Vue 2.x中的)。
原理:
https://www.cnblogs.com/wasbg/p/12659610.html
https://blog.csdn.net/weixin_43972437/article/details/107291071
浏览器的同源策略:https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
https://blog.csdn.net/MicroAnswer/article/details/102913571
https://www.jianshu.com/p/89a377c52b48/
浏览器的同源策略约定我们只能在同源的情况下(即协议,域名,端口相同),才能成功请求数据。
跨域并不会阻止请求的发出,也不会阻止请求的接受,跨域是浏览器为了保护当前页面,你的请求得到了响应,浏览器不会把响应的数据交给页面上的回调,取而代之的是去提示你这是一个跨域数据。
将自定义函数在全局注册(即每个组件都可用this.func调用)
(任意目录都可)创建plugin.js,定义一个全局函数:
import { ElMessage } from 'element-plus'const func = function(msg) {this.$router.push({name: 'Login'})ElMessage.error({title: '登录已过期,请重新登录',message: msg})}export default {install(app) {app.config.globalProperties.loginExpired = loginExpired}}
main.js中导入并注册:
import plugin from './plugin'app.use(plugin)
<router-view> can no longer be used directly inside <transition> or <keep-alive>. Use slot props instead...
https://next.router.vuejs.org/zh/guide/migration/index.html#router-view-%E3%80%81-keep-alive-%E5%92%8C-transition
其它参考:https://blog.csdn.net/junjiahuang/article/details/108249960
注:三个都是Vue的标签。
<transition>可实现类似CSS transition的过渡。
<keep-alive>可实现对路由组件的缓存,用于切换组件时保留隐藏组件的状态。
Extraneous non-props attributes (visible) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.
将组件中的特殊绑定,改为用v-model="..."(本质是通过v-model对其modelValue绑定)。
如:<el-drawer :visible.sync="showDrawer">,改为<el-drawer v-model="showDrawer">。
Element Plus:https://element-plus.gitee.io/zh-CN/component/layout.html
Element UI:https://element.eleme.cn/#/zh-CN/component/quickstart
https://www.jianshu.com/p/e672b246d9d1
el-table
设置el-form的inline为false,在单个el-form-item中,定义style为display:inline-flex(width可选),可以将指定元素在一行中渲染。
Icon
安装:npm install @element-plus/icons-vue。
需在main.js中,先全局注册组件:
import ElementPlus from 'element-plus'import 'element-plus/dist/index.css'import * as Icons from '@element-plus/icons-vue'app.use(ElementPlus)// 全局注册el-icon的组件Object.keys(Icons).forEach(key => {app.component(key, Icons[key])})
然后可像 https://element-plus.gitee.io/zh-CN/component/icon.html 中,使用对应标签引用,如:<el-icon><check /></el-icon>。
修改元素的默认样式
注意element元素设置的是类。
如果下面还不行,只能加!important。
使用deep样式穿透:
<style scoped>.my .el-input__inner{border-radius: 30px;/* 这个不起作用 */}.my /deep/ .el-input__inner{border-radius: 30px;/* 这个起作用 */}</style>
使用>>>穿透:
<style scoped>.my .el-input__inner{border-radius: 30px;/* 这个不起作用 */}.my >>> .el-input__inner{border-radius: 30px;/* 这些起作用 */border: 1px solid #eceef2;outline: 0;}</style>
注:现在推荐使用:deep()替换>>>或/deep/。
// the >>> and /deep/ combinators have been deprecated. Use :deep() instead.// 错误/deep/ .a{}// 正确/* deep selectors */::v-deep(.a) {}/* shorthand */:deep(.a) {}/* targeting slot content */::v-slotted(.a) {}/* shorthand */:slotted(.a) {}/* one-off global rule */::v-global(.a) {}/* shorthand */:global(.a) {}
给img的src绑定
直接使用:src="url"加载静态资源,会加载失败(url似乎可以),有两种解决方式:
1. 动态绑定需要在根目录的public下,新建一个static文件夹,存储静态文件。这样就可以直接访问到了。
<img :src="'/static/' + item.label.split(' ')[0].toLowerCase() + '.png'">
2. 每个url用require()包裹(data中的数据也是,作用与import相同),即使用require引入assets下的资源。
<img class="svg" :src="card.liked?require('../assets/svg/liked.svg'):require('@/assets/svg/like.svg')"><div :style="{background: 'url(' + require('@/assets/imgs/test.png') + ')'}">或是<img :src="imgUrl">data() {return {imgUrl: require('@/assets/imgs/test.png')}}
3. import每个url。
import testpng from '@/assets/imgs/test.png' // 别名路径data() {return {imgUrl: testpng}}
4. 将图片放到static目录下(未测试)
<img :src="'../../static/img/user.png'">
css中使用本地图片
暂未成功。可能需要flieLoader。
新窗口跳转
使用router-link标签:
<router-link target="_blank" :to="{ name: 'OrderDetail', query: {order_id: order.order_id} }">新窗口打开</router-link>
使用js:
<div @click="jumpToCard(card.id)"></div>...jumpToCard(cardID) {var url = this.$router.resolve({name: 'Card', // path: '/card'query: {id: cardID},})window.open(url.href, '_blank')},
注:下面的问题似乎是因为,我当时不知道 我看的是element-UI的文档。。
由于Vue的更新,部分Element官网上的语法已经不能使用。
Expected Number with value 5, got String with value "5"
在前面加:或使用变量即可。bool类型还可以直接写,表示true。
<el-uploadaction:file-list="imgList":on-remove="handleRemove":before-upload="beforeUpload":http-request="uploadRequest":limit="5" // 注:文件上传无需在此处用limit(不会有错误信息)。在beforeUpload中检查imgList长度可返回错误信息multiple:auto-upload="true">
slot
https://blog.csdn.net/weixin_45532746/article/details/118244876
用#,且只能在template上。写法变了。
如不是官网的<template slot="prepend">Http:</template>,或<span slot="...">,而是<template #prepend>Http:</template>,与<template #...>(span不能加slot,要用template)。
如dialog:
<!-- 注意要在template上加入具名为footer的slot。slot不可在span上加 --><template #footer><span class="dialog-footer"><el-button @click="showAdd = false">取 消</el-button><el-button type="primary" @click="createAddress">确 定</el-button></span></template>
如:
<el-input placeholder="请输入内容" v-model="input3" class="input-with-select"><el-select v-model="select" slot="prepend" placeholder="请选择"><el-option label="餐厅名" value="1"></el-option><el-option label="订单号" value="2"></el-option><el-option label="用户电话" value="3"></el-option></el-select><el-button slot="append" icon="el-icon-search"></el-button></el-input>改为:<el-inputplaceholder="enter a place to start your poetic journey"v-model="input"class="input-with-select"><template #prepend><el-select v-model="selection" placeholder="请选择"><el-option label="省市区" value="1"></el-option><el-option label="景点名称" value="2"></el-option><el-option label="景点类型" value="3"></el-option></el-select></template><template #append><el-buttonicon="el-icon-search"@click="searchHandler"></el-button></template></el-input>
ElementPlusError: [renderTrigger] trigger expects...
对应组件,比如el-popconfirm,标签内不能有注释,否则会报错。
Expected Boolean, got String with value "false"
设置bool型为true或false时,用:show="false"(表示绑定为JS表达式),而不是show="false"(被识别为字符串)。
el-row
外层el-row的style一般不用flex(默认),可以是display:block,否则可能效果很差。
el-table scope
通过 Scoped slot 可以获取到 row, column, $index 和 store(table 内部的状态管理)数据。
注意不是官网上的template slot-scope="scope",而是在el-table-column中v-slot="scope"。
且要修改template的display,否则表格里面的template内容都显示不出来。
如:
<el-table-columnlabel="操作"width="120px"v-slot="scope"><template style="display:table"><el-button@click.native.prevent="chooseRow(scope.$index, address)"type="text">选择</el-button></template></el-table-column>
el-drawer
如果镶嵌复杂内容,默认没有滚动条,要设置overflow: auto;。且不能通过style直接设置,要用... :deep() .el-drawer__body {overflow: auto;}。
注意用:deep()穿透el元素,初始的...要足够靠外(比如页面的最外层元素)。
此外,不是官网上的那样,用:visible.sync="showDrawer",而是v-model="showDrawer"。
如:
<el-drawertitle="选择地址"v-model="showDrawer"direction="rtl"size="50%"><el-table :data="address" stripe><el-table-column prop="name" label="姓名" width="80px"></el-table-column><el-table-column prop="phone" label="电话" width="140px"></el-table-column><el-table-column prop="address" label="地址" width="200px"></el-table-column></el-table></el-drawer>
el-tabs
注意,el-tabs的v-model,和el-tab-pane的name,都需要是String,不可是Number。
可以加空字符串''直接变为String。