Vue全家桶系~1.Vue2基础(兼容)

  • Vue全家桶系~1.Vue2基础(兼容)已关闭评论
  • 110 次浏览
  • A+
所属分类:Web前端
摘要

HTML+CSS+JS基础文档:https://developer.mozilla.org/zh-CN/docs/WebVue3官网文档:https://cn.vuejs.org/ | Vue2文档:https://v2.cn.vuejs.org/v2/guide


Vue基础学习

HTML+CSS+JS基础文档:https://developer.mozilla.org/zh-CN/docs/Web

Vue3官网文档:https://cn.vuejs.org/ | Vue2文档:https://v2.cn.vuejs.org/v2/guide

个人建议:对于小白和新手,以及只会HTML+CSS+JS基础的人来说,别上来就搞那一套高度封装的开发方式,开发是很方便,调bug也是非常麻烦的。先以这种脚本引入+HTML的混合开发入手,熟悉之后再用组件化开发方式

这样做两个好处:1.原来HTML+JQ的后端开发程序员,用这种开发方式反而更容易入手、2.市面上很多Vue2的项目,不至于直接懵圈

PS:我下面案例只是使用Vue2的脚本引入的方式便捷开发,语法和知识点都是参考最新Vue3最新文档(贴的地址也都是Vue3的文档)

如果有了前端开发基础,比如开发的时候早就抛弃HTML这种老一辈开发方式已经有TS、Vue之类的基本经验 ==> 【跳过这篇文章,看下篇

1.环境配置

1.1.IDE配置(必配)

VSCode官方插件Vue Language Features (Volar)

TypeScript支持:TypeScript Vue Plugin (Volar)

Vue全家桶系~1.Vue2基础(兼容)

Vue快速开发Vue VSCode Snippets(输入缩写快速生成代码段)

Vue全家桶系~1.Vue2基础(兼容)

错误高亮提示Error Lens

Vue全家桶系~1.Vue2基础(兼容)


1.2.第一个demo

练手先使用传统的开发方式:把vue当做一个脚本js文件引入,然后再开发(官方也推荐这样入门)

Vue全家桶系~1.Vue2基础(兼容)

开发的时候可以引用开发版vue.js(更多友好提示),发布的时候替换为vue.mini.js(更轻量级)

首先说下大致流程:

1首先要在HTML文件里面创建一个存放宿主文件的标签(Vue所有DOM操作都是这个里面进行)可以是id,也可以是class

<div id="app"> </div> 

2然后引入vuejs(Vue2基础语法和Vue3基本上一样)

 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.9/vue.js"></script> 

3接着就要在script标签里面实例化vue对象了:

<script>     // 3.创建一个vue示例     const app = new Vue({         el: '#app' // 【必须】指定宿主id或者class         }     }); </script> 

下面进行案例实战,需求:在HMTL页面里面显示小明的自我介绍(变量赋值),1s后补充下介绍(变量修改

<!-- 1.创建一个宿主文件 --> <div id="app"> 自我介绍:{{name}} </div> <!-- 2.引入vue.js --> <script src="../assets/vue2.js"></script> <script>     // 3.创建一个vue示例     const app = new Vue({         el: '#app', // 【必须】指定宿主id或者class         data() {// 输入vdata可以快速生成             return {                 name: '大家好,我叫小明'// 变量初始化赋值             }         }     });     // 4.一秒后修改下文本内容     setTimeout(() => {         // 直接变量修改,不用DOM操作了         app.name = '谢谢大家'; // 变量修改     }, 1000); </script> 

输出:

自我介绍:大家好,我叫小明(刚开始) 自我介绍:谢谢大家(1s后) 

Vue不只是一个模板引擎,它其实是响应式的,数据和DOM已经关联了。要验证也很简单,控制台改变下变量,页面就实时改变了:

Vue全家桶系~1.Vue2基础(兼容)

2.模板语法

Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。

在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。

2.1.文本渲染(文本插值)

数据绑定最常见的形式就是使用“Mustache”语法{{ 变量 }}的文本插值,这种方式分两种:

  1. 当数据改变时,插值文本跟着改变(常用):
    1. <span>Message: {{ msg }}</span>
  2. 能执行一次性的插值,,当数据改变时,内容不会更新
    1. <span v-once>这个将不会改变: {{ msg }}</span>
    2. 注意:这会影响到该节点上的其它数据绑定

贴一下第一个demo的例子:

<!-- 1.创建一个宿主文件 --> <div id="app"> <span>{{name}}</span> </div> <!-- 2.引入vue.js --> <script src="../assets/vue2.js"></script> <script>     // 3.创建一个vue示例     const app = new Vue({         el: '#app', // 【必须】指定宿主id或者class         data() {// 输入vdata可以快速生成             return {                 name: '大家好,我叫小明'             }         }     });     // 4.一秒后修改下文本内容     setTimeout(() => {         // 直接变量修改,不用DOM操作了         app.name = '谢谢大家';     }, 1000); </script> 

如果其他不变,就是把<span>{{name}}</span>改成:<span v-once>{{name}}</span>,你会发现只显示第一次赋值的内容

app.name的变量其实已经修改了,但是页面文本内容因为设置了v-once,不会修改

Vue全家桶系~1.Vue2基础(兼容)

补充:HTML渲染(v-html)

双大括号会将数据解释为普通文本,为了输出HTML,可以使用v-html 指令:<span v-html="变量"></span></p>

示例:

<div id="app">     <!-- 默认是文本 -->     <p>{{htmlone}}</p>     <!-- 想渲染html就要这样写 -->     <p v-html="htmltwo"></p> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 htmlone: '<h2>我是HTML</h2>',                 htmltwo: '<h2>我是HTML</h2>'             }         },     }) </script> 

效果:

Vue全家桶系~1.Vue2基础(兼容)

这个 p 的内容将会被替换成为 property 值 htmltw,直接作为 HTML——会忽略解析 property 值中的数据绑定。注意,你不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎。反之,对于用户界面 (UI),组件更适合作为可重用和可组合的基本单位。

你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。


2.2.属性绑定(v-bind)

1.常用绑定案例

HTML特性不能用Mustache语法(双大括号),要使用v-bind指令:v-bind:属性值="变量名"

举个title属性值的例子:<p v-bind:title="title1">鼠标移上去,停顿几秒</p>

Vue还提供了一种简写方式:<p :title="title2">鼠标移上去,停顿几秒</p>

完整案例:

<div id="app">     <p v-bind:title="title1">         鼠标移上去,停顿几秒看看     </p>     <!-- 可以缩写 -->     <p :title="title2">         鼠标移上去,停顿几秒看看     </p> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 title1: '我是一个标题1',                 title2: '我是一个标题2'             }         },     }) </script> 

2.布尔类型属性

布尔型 attribute 依据 true / false 值来决定 attribute 是否应该存在于该元素上,如果 btnDisabled 的值是 nullundefinedfalsedisabled attribute直接不会出现在 <button>

<button :disabled="btnDisabled">按钮</button>

btnDisabled为真值或一个空字符串 (即 <button disabled="">) 时,元素会包含这个 disabled attribute。而当其为其他假值时 attribute 将被忽略。

3.动态绑定多个值

如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:

const objectOfAttrs = {   id: 'container',   class: 'wrapper' } 

通过不带参数的 v-bind,你可以将它们绑定到单个元素上:

<div v-bind="objectOfAttrs"></div> 

案例:

<div id="app">     <div v-bind="objectOfAttrs">F12查看我的源码</div> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 objectOfAttrs: {                     id: 'container',                     class: 'wrapper'                 }             }         },     }) </script> 

效果:

Vue全家桶系~1.Vue2基础(兼容)


扩展:JavaScript 表达式

这边贴一下官方的文档:https://cn.vuejs.org/guide/essentials/template-syntax.html#using-javascript-expressions

对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。

{{ number + 1 }}  {{ ok ? 'YES' : 'NO' }}  {{ message.split('').reverse().join('') }}  <div v-bind:id="'list-' + id"></div> 

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

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

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

案例:

<div id="app">{{num + 1}}</div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 num: 3             }         },     }) </script> 

结果是4


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

下面的例子都是无效的:

<!-- 这是语句,不是表达式 --> {{ var a = 1 }}  <!-- 流控制也不会生效,请使用三元表达式 --> {{ if (ok) { return message } }} 

更多参考:https://cn.vuejs.org/guide/essentials/template-syntax.html#using-javascript-expressions


2.3.条件渲染(v-if)

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

  2. v-else 相当于为 v-if 添加一个“else 区块”

    1. 使用 v-else 元素必须跟在一个 v-if 或者 v-else-if 元素后面,否则它将不会被识别
  3. v-else-if 提供的是相应于 v-if 的“else if 区块”

    1. 使用 v-else-if 的元素必须紧跟在一个 v-if 或一个 v-else-if 元素后面

举个例子:

<div id="app">     <div v-if="str === 'A'"> A </div>     <div v-else-if="str === 'B'"> B </div>     <div v-else-if="str === 'C'"> C </div>     <div v-else> other </div> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 str: 'D'             }         },      }); </script> 

输出:other


Vue3新增了v-showhttps://cn.vuejs.org/guide/essentials/conditional.html#v-show

v-if惰性渲染的:如果在初次渲染时条件值为 false,元素是不存在的。

v-show 是通过css属性display控制元素显示,无论初始条件如何,始终会被渲染。

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

2.4.列表渲染(v-for)

同时使用 v-ifv-for不推荐的,因为这样二者的优先级不明显(Vue2和Vue3两者优先级恰恰相反)

1.遍历数组

v-for 指令基于一个数组来渲染一个列表。 v-for 指令需要使用item in items形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名(用item of items迭代遍历也行)【items是一个数组,而不能是一个方法可以是计算属性)】

eg:<p v-for="item in items"> {{ item }} </p>

示例:

<!-- div#app --> <div id="app">     <!--快速生成: ul>li*2 -->     <ul>         <!-- vfor -->         <li v-for="item in items">             {{ item }}         </li>     </ul> </div> <!-- script:src --> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 items: ['狮子头', '韭菜', '鸡腿']             }         },     }) </script> 

效果:Vue全家桶系~1.Vue2基础(兼容)

2.第二参数

还可以(value,index)的方式遍历:

<!-- div#app --> <div id="app">     <ul>         <li v-for="(value,index) in items">             {{index}}.{{value}}         </li>     </ul> </div> <!-- script:src --> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 items: ['狮子头', '韭菜', '鸡腿'],             }         },     }) </script> 

效果:Vue全家桶系~1.Vue2基础(兼容)

3.遍历对象

举个遍历小明个人信息的案例:

<div id="app">     <ul>         <li v-for="(value,key,index) in items">             {{index}}.{{key}}:{{value}}         </li>     </ul> </div> <!-- script:src --> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 items:{                     'name':'小明',                     'age':'32',                     'gender':'男'                 }             }         },     }) </script> 

效果:Vue全家桶系~1.Vue2基础(兼容)


如果你直接遍历,则只会输出value值。我们只修改上面代码的html部分:

<div id="app">     <ul>         <li v-for="value in items">             {{value}}         </li>     </ul> </div> 

效果:Vue全家桶系~1.Vue2基础(兼容)


如果想要获取key和value,可以写2个参数:v-for="(value,key) in items

<div id="app">     <ul>         <li v-for="(value,key) in items">             {{key}}:{{value}}         </li>     </ul> </div> <!-- script:src --> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 items: {                     'name': '小明',                     'age': '32',                     'gender': '男'                 }             }         },     }) </script> 

效果:Vue全家桶系~1.Vue2基础(兼容)


4.对象列表★

来个实际的案例,v-if + v-for遍历班上的同学,并显示他们的个人信息

<div id="app">     <p v-if="models.length==0">该班级没有学生</p>     <ul v-else>         <li v-for="model in models" :key="model.id">             {{ model.id }}.{{model.name}}.{{model.age}}.{{model.gender}}         </li>     </ul> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app', data() {             return {                 models: [                     { 'id': 1, 'name': '小明', 'age': '32', 'gender': '男' },                     { 'id': 2, 'name': '小华', 'age': '22', 'gender': '男' },                     { 'id': 3, 'name': '小花', 'age': '28', 'gender': '女' }                 ]             }         },     }) </script> 

效果:Vue全家桶系~1.Vue2基础(兼容)

PS:如果models的数值是空数组,就会显示:该班级没有学生

4.总结扩展

如果遍历的是列表,就两个参数,第1个是value值,第2个是index。

如果遍历的是对象,第1个参数是value值,第2个是key,第3个是index。

为了v-for有更高的渲染性能,需要给 Vue 一个提示,需要为每项提供一个唯一 key 属性(DOM中没有key属性,是给vue用的)eg:

建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。

<div v-for="item in items" v-bind: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-for 特别关联,它还具有其它用途

key 绑定的值期望是一个基础类型的值,例如字符串number类型。不要用对象作为 v-for 的 key

更多用法:https://cn.vuejs.org/guide/essentials/list.html#displaying-filtered-sorted-results

key文档:https://cn.vuejs.org/guide/essentials/list.html#maintaining-state-with-key


2.5.事件处理(v-on)

官方文档:https://cn.vuejs.org/guide/essentials/event-handling.htm

1.简单调用

先来个简单案例:单击后调用show方法:v-on:click="show"

完整写法:<div id="app"><button v-on:click="show">点我弹框</button></div>  简写用法:<div id="app"><button @click="show">点我弹框</button></div> 
const app = new Vue({     el: '#app',     methods: {         show() {             alert('v-on test');         }     }, }); 

效果:

Vue全家桶系~1.Vue2基础(兼容)

2.按键修饰符

在监听键盘事件时,我们经常需要检查特定的按键,Vue 为一些常用的按键提供了别名:

  • .enter
  • .tab
  • .delete (捕获“Delete”和“Backspace”两个按键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

系统按键修饰符

系统按键修饰符和常规按键不同。与 keyup 事件一起使用时,该按键必须在事件发出时处于按下状态。换句话说,keyup.ctrl 只会在你仍然按住 ctrl 但松开了另一个键时被触发。若你单独松开 ctrl 键将不会触发。

  • .ctrl
  • .alt
  • .shift
  • .meta:微软的Windows 键、苹果的Command 键

来个案例:

Alt+Enter键触发事件@keyup.alt.enter="xxx"Ctrl+Click按键触发@click.ctrl="xxx"

<div id="app">     <input type="text" @keyup.alt.enter="show('Alt + Enter')" value="Alt + Enter 试试?">     <button @click.ctrl="show('点击 + Ctrl')">Ctrl+单击试试?</button>     <!-- @keyup.ctrl.click这样写是没用的 --> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         methods: {             show(msg) { alert(msg); }         },     }); </script> 

.exact 修饰符,允许控制触发一个事件所需的确定组合的系统按键修饰符。 ==> 就是只按下了什么键

<!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发 --> <button @click.ctrl="onClick">A</button>  <!-- 仅当按下 Ctrl 且未按任何其他键时才会触发 --> <button @click.ctrl.exact="onCtrlClick">A</button>  <!-- 仅当没有按下任何系统按键时触发 --> <button @click.exact="onClick">A</button> 

鼠标按键修饰符

  • .left
  • .right
  • .middle

这些修饰符将处理程序限定为由特定鼠标按键触发的事件


3.事件修饰符(了解)

专注于数据逻辑而不用去处理 DOM 事件的细节,Vue 为 v-on 提供了事件修饰符

事件修饰符官方文档:https://cn.vuejs.org/guide/essentials/event-handling.html#event-modifiers

修饰符是用 . 表示的指令后缀,包含以下这些:

使用修饰符时需要注意调用顺序,因为相关代码是以相同的顺序生成的。eg:使用 @click.prevent.self 会阻止元素及其子元素的所有点击事件的默认行为,而 @click.self.prevent 则只会阻止对元素本身的点击事件的默认行为。

  • .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> 

.capture.once.passive 修饰符与原生 addEventListener 事件相对应:

.passive 修饰符一般用于触摸事件的监听器,可以用来改善移动端设备的滚屏性能

<!-- 添加事件监听器时,使用 `capture` 捕获模式 --> <!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 --> <div @click.capture="doThis">...</div>  <!-- 点击事件最多被触发一次 --> <a @click.once="doThis"></a>  <!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 --> <!-- 以防其中包含 `event.preventDefault()` --> <div @scroll.passive="onScroll">...</div> 

2.6.表单绑定(v-model)

可以用 v-model 指令在表单 <input> <textarea><select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素

v-model 会忽略任何表单元素上初始的 valuecheckedselected attribute。它将始终将当前绑定的 JavaScript 状态视为数据的正确来源。你应该在 JavaScript 中使用响应式系统的 API来声明该初始值。

看个简单v-model的案例:(v-model后面的item要先定义下

<!-- div#app>input --> <div id="app">     <input v-model="item" type="text" @keyup.enter="addItem" />     <button @click="addItem">按钮添加</button>     <div v-if="items.length==0">         食堂里暂时没有菜了     </div>     <!-- v-if尽量不和v-for嵌套使用 -->     <div v-else>         <p v-for="name in items">             {{ name }}         </p>     </div> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 item: '', // 变量要定义一下                 items: ["狮子头", "韭菜", "鸡腿"]             }         },         methods: {             addItem() {                 if (this.item == '') { return; }                 this.items.push(this.item); // 数组中加一个值                 this.item = ''; // 清空文本框内容             }         },     }) </script> 

效果:在文本框输入内容直接回车,或者按后面的按钮,就可以添加内容

Vue全家桶系~1.Vue2基础(兼容)

更多可以查看文档:https://cn.vuejs.org/guide/essentials/forms.html

2.7.样式绑定(:class)

主要是ClassStyle,平时class用的比较多,Style如果要用可以直接绑定到一个样式对象(比较直观)

代码::class="{类名:bool}

1.简单案例

先来个简单案例:首先列出学生列表,然后点到哪个学生,哪个学生名字变红

  • :class="{active:selectedStudent==name}:设置一个类active,当变量selectedStudent和当前name相同的时候生效

  • @click="selectedStudent=name:当li被单击的时候,把name赋值给selectedStudent

<div id="app">     <div v-if="students.length==0">这个班级还没有学生</div>     <div v-else>         <ul>             <li v-for="name in students" :key="name" :class="{active:selectedStudent==name}"                 @click="selectedStudent=name">                 {{ name }}             </li>         </ul>     </div> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 student: '', // v-model的变量                 selectedStudent: '',// 初始化                 students: ['张三', '李四', '王二', '赵五', '老六']             }         },     }) </script> 

点了老六一下的效果:Vue全家桶系~1.Vue2基础(兼容)

2.多个class值

你可以在对象中写多个字段来操作多个 class。此外,:class 指令也可以和一般的 class attribute 共存。eg:

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

如果isActive是true,hasError是false,最后显示出来就是这样的:

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

3.Sytle案例

列出学生列表,然后点到哪个学生,哪个学生背景变黄

background-color这种有-的在js中容易有问题,所以需要'xx'引号包裹,或者写成backgroundColor驼峰命名的样子,然后Vue会帮我们转换

<div id="app">     <div v-if="students.length==0">这个班级还没有学生</div>     <div v-else>         <ul>             <!-- background-color这种有-的在js中容易有问题,所以需要引号包裹,或者写成backgroundColor驼峰命名的样子,然后Vue会帮我们转换 -->             <!-- @click="selectedStudent=name:当li被单击的时候,把name赋值给selectedStudent -->             <li v-for="name in students" :key="name"                  :style="{'background-color':selectedStudent==name?'yellow':'transparent'}"                  @click="selectedStudent=name">                 {{ name }}             </li>         </ul>     </div> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 student: '', // v-model的变量                 selectedStudent: '',// 一定要先初始化                 students: ['张三', '李四', '王二', '赵五', '老六']             }         },     }) </script> 

更多参考官方文档:https://cn.vuejs.org/guide/essentials/class-and-style.html

2.8.计算属性(computed)

对于任何复杂逻辑,你都应当使用计算属性

1.表达式实现

先看一个简单案例引入一下:现在有个数组,里面数据有点错乱,需要处理后显示

这个案例没有使用计算属性,是表达式的方法实现

  • split(','):以,进行字符串分割,结果返回一个数组
  • reverse():把分割后的数组翻转下
  • join('-'):把翻转后的数组重新以-连接为一个字符串
<div id="app">     <ul>         <li v-for="str in students" :key="str">             {{ str.split(',').reverse().join('-') }}         </li>     </ul> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 students: ['男,23,张三,1', '男,22,李四,2', '女,30,王二,3']             }         },     }); </script> 

效果:Vue全家桶系~1.Vue2基础(兼容)

2.计算属性改写★

这种明显就违背了Vue的初衷,表达式明显过于复杂,而且这样效率也低(数据量大的时候还会有性能瓶颈)下面改写一下:

<div id="app">     <ul>         <li v-for="str in updateStudents" :key="str">             {{ str }}         </li>     </ul> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 students: ['男,23,张三,1', '男,22,李四,2', '女,30,王二,3']             }         },         computed: {             updateStudents() {                 return this.students.map(s => s.split(',').reverse().join('-'));             }         },     }); </script> 

在Vue.js中,计算属性的使用方式是像数据属性一样使用,而不是作为函数调用

PS:不能通过{{ newStr(str) }}的方式来调用计算属性


3.计算属性 vs 方法

计算属性在数据不变的情况下,是有缓存的,能够提升效率。上面方法也可以用函数改写,但是更推荐计算属性的方式:

两者逻辑类似,就是少了缓存(计算属性值会基于其响应式依赖被缓存,它仅会在其响应式依赖更新时才重新计算)

<div id="app">     <ul>         <li v-for="str in students" :key="str">             {{ newStr(str) }}         </li>     </ul> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 students: ['男,23,张三,1', '男,22,李四,2', '女,30,王二,3']             }         },         methods: {             newStr(str) {                 return str.split(',').reverse().join('-');             }         },     }); </script> 

更多计算属性可以参考官方文档:https://cn.vuejs.org/guide/essentials/computed.html

2.9.侦听器(watch)

我们用一个案例来看下watch的一些特性:

PS:真实使用的时候{{studentCount}}不会通过这种方式实现,直接写{{students.length}}即可实时更新

1.watch不立刻执行的特性

<div id="app">     <input v-model="student" type="text" @keyup.enter="addStudent" />     <p>{{studentCount}}</p>     <ul>         <li v-for="(item,index) in students" :key="index">             {{index+1}}.{{ item }}         </li>     </ul> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 student: '', // v-model初始化                 studentCount: 0, // 初始化                 students: ['张三', '李四', '露西', '王五']             }         },         methods: {             addStudent() {                 this.students.push(this.student);                 this.student = '';             }         },         watch: {             // 不加特殊声明,变化之后才会执行             students(newValue, oldValue) {                 this.studentCount = newValue.length;             }         },     }); </script> 

你会发现刚运行的时候并没有统计学生数量Vue全家桶系~1.Vue2基础(兼容)

这是因为watch不特殊处理的话只会在数据改变的时候触发回调函数,现在我们新增一个学生看下效果:

数据一改变,立马就更新了Vue全家桶系~1.Vue2基础(兼容)

2.让watch立刻执行

如果你想立刻执行,就用watch-option的方式来指定一下immediate:true,代码和上面一样,就watch变下:

watch: {     students: {         immediate: true,             // deep: true, // 一般不启用,影响性能(特殊情况可以使用)             handler(newValue, oldValue) {             this.studentCount = newValue.length;         }     } }, 

这样一打开就自动计算好了:Vue全家桶系~1.Vue2基础(兼容)

3.深层侦听器

深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。

用watch-option的方式来指定一下deep: true(数据嵌套太深默认的watch是检测不到的,这时候你如果要侦听就开下,要考虑性能)

更多参考官方文档:https://cn.vuejs.org/guide/essentials/watchers.html


★ 计算属性 VS 侦听器 ★

  1. 计算属性场景:多个值的改变影响一个值

    1. 能用computed实现的,全部使用computed,不去使用watch
  2. 侦听器的场景:一个值的改变影响多个值

    1. 侦听器提供了通用方法,适合执行异步操作、较大开销的操作

举一个官方的侦听器案例:

<div id="watch-example">     <p>         提出一个问题,回答 yes or no         <input v-model="question">     </p>     <!-- 提示语 -->     <p>{{ answer }}</p> </div> <script src="../assets/vue2.js"></script> <script src="../assets/axios.1.3.6.js"></script> <!-- <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.js"></script> --> <script>     var watchExampleVM = new Vue({         el: '#watch-example',         data: {             question: '',             answer: '提出一个问题,以英文的问号结尾'         },         watch: {             // 如果 `question` 发生改变,这个函数就会运行             question: function (newQuestion, oldQuestion) {                 this.answer = '稍等,我去问下,不要打字了哈~';                 this.getAnswer();             }         },         methods: {             getAnswer: function () {                 if (this.question.indexOf('?') == -1) {                     this.answer = '请在最后加一个英文的?';                     return                 }                 this.answer = '思考ing...'                 console.log(this);                 var vm = this;                 // axios里面的this就不是vm了                 axios.get('https://yesno.wtf/api')                     .then(function (response) {                     console.log(response.data)                     console.log(this);                     vm.answer = response.data.answer;                 })                     .catch(function (error) {                     vm.answer = '没法访问API,错误信息:' + error;                 })             }         }     }) </script> 

2.10.生命周期

Vue2和Vue3生命周期的钩子其实是有变化的,但是我们经常使用的没什么变换,这边贴一下Vu3的生命周期

Vue2和Vue3这块是大体一样的:beforeCreate > created > beforeMount > mounted > beforeUpdate > updated(销毁有些变化)

Vue全家桶系~1.Vue2基础(兼容)

跑个测试案例看下:

<div id="app">     {{testData}} </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 testData: '初始化数据', // 初始化             }         },         beforeCreate() { console.log('beforeCreate'); },         // 【常用】组件实例已创建,尚未挂载,dom不存在         created() { console.log('created ' + this.$el); },         beforeMount() { console.log('beforeMount'); },         // 【常用】挂载结束,dom已经存在         mounted() {             console.log('mounted ' + this.$el);             setTimeout(() => {                 this.testData = '我被修改了';             }, 2000);         },         beforeUpdate() { console.log('beforeUpdate'); },         updated() { console.log('updated'); }     }); </script> 

执行效果:created的时候没有dom,mounted的时候已经有dom了

Vue全家桶系~1.Vue2基础(兼容)

如果不进行数据更新,下面两个方法是不会执行的:这个模拟了一个2s延迟的更新,于是就触发了

Vue全家桶系~1.Vue2基础(兼容)

应用场景

  • beforecreate // 执行时组件实例还未创建,通常用于插件开发中执行一些初始化任务
  • created // 组件初始化完毕,各种数据可以使用,常用于异步数据获取
  • beforeMounted // 未执行染、更新,dom未创建
  • mounted // 初始化(挂载)结束,dom已创建,可用于获取访问数据和dom元素
  • beforeupdate // 更新前,可用于获取更新前各种状态
  • updated // 更新后,所有状态已是最新
  • BeforeUnmount // 销毁前,可用于一些定时器或订阅的取消(组件还在
  • Unmounted // 组件已销毁,作用同上(组件不在了

更多参考官方文档:https://cn.vuejs.org/guide/essentials/lifecycle.html

生命周期钩子:https://cn.vuejs.org/api/composition-api-lifecycle.html

2.11.组件基础

组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:

Vue全家桶系~1.Vue2基础(兼容)

全局注册组件:Vue.component(name, options)

Vue.component('my-component-name', {
// ... options ...
})


1.子组件中的局部变量案例

我们先写个普通的功能:点击按钮数字+1

<div id="app">     <div>         {{count}}         <button @click="count++">点我+1</button>     </div> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 count: 0             }         },     }) </script> 

效果:单击一下数字就会+1 Vue全家桶系~1.Vue2基础(兼容)


下面把这个案例改写成组件的方式(count这个变量主要是在组件中使用,那就可以单独封装在组件里面)

<div id="app">     <my-button></my-button> </div> <script src="../assets/vue2.js"></script> <script>     // 组件的名称最好是这种中划线的方式Vue(开头大写)     Vue.component('my-button', {         data() {             return {                 count: 0 // 这个变量和该组件强关联,定义在组件里面比较合适             }         },         // template里面的模板最外面用个单独的div比较好         template: ` <div> {{count}} <button @click="count++">点我+1</button>     </div> `     }                  );     // 组件定义后再实例化,如果在前面实例化,页面会不正常     const app = new Vue({ el: '#app' }); </script> 

几个注意事项:

  1. 组件的名称最好是中划线的方式
    1. Vue.component(V开头大写,c开头小写)
  2. template里面的模板字符串,最外层要加个父标签/根标签,比如用个单独的div

效果:单击一下数字就会+1 Vue全家桶系~1.Vue2基础(兼容)

如果你页面放好几个组件,也是可以的,而且相互不干扰:

<div id="app">     <my-button></my-button>     <my-button></my-button>     <my-button></my-button> </div> 

任意点击后效果:Vue全家桶系~1.Vue2基础(兼容)


2.父组件传递数据给子组件

一个展示学生列表的案例,这个是改成组件之前的代码:

<div id="app">     <li v-for="name in students" :key="name">         {{ name }}     </li> </div> <script src="../assets/vue2.js"></script> <script>     // 组件定义后再实例化,如果在前面实例化,页面会不正常     const app = new Vue({         el: '#app',         data() {             return {                 students: ["张三", "李四", "王二"]             }         }     }); </script> 

输出:Vue全家桶系~1.Vue2基础(兼容)


改成组件的方式(把students的数据传递到组件里面)

<div id="app">     <!-- 两个变量名不一定要完全一样,这边只是方便理解 -->     <my-list :students="students"></my-list> </div> <script src="../assets/vue2.js"></script> <script>     Vue.component('my-list', {         props: { // 对传过来的students进行约束和默认值设置(也可以直接props:['students'])             students: {                 type: Array, // 设置students的数据类型是数组                 default: [] // 默认值是空列表             },         },         template:         `<ul>             <li v-for="name in students" :key="name">             {{ name }}             </li>     	</ul>`     })     // 组件定义后再实例化,如果在前面实例化,页面会不正常     const app = new Vue({         el: '#app',         data() {             return {                 students: ["张三", "李四", "王二"]             }         },     }) </script> 

输出效果和上面一样:Vue全家桶系~1.Vue2基础(兼容)

3.子组件传递数据给父组件

比如现在要输入一个学生名称(子组件中的变量)然后数据(students)是在父组件中的,那就需要每次新增的student传递给父组件

案例改组件前的实现:两块内容,一个是文本框用来新增学生名、另一个是列表来列举学生

<div id="app">     <input v-model="student" type="text" @keyup.enter="addStudent" />     <ul>         <li v-for="name in students" :key="name">             {{ name }}         </li>     </ul> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 student: '',                 students: ['张三', '李四', '王二']             }         },         methods: {             addStudent() {                 this.students.push(this.student);                 this.student = '';             }         },     }); </script> 

Vue全家桶系~1.Vue2基础(兼容)


改成组件的方式:

$emit:触发当前实例上的事件(附加参数都会传给监听器回调)【记不得可以想提交是submit,事件提交就是emit】

list部分和上面一样,重点说下input的事件监听:如果不传参可以不自定义事件,传参就可以这样写:

<!-- `add-student`是自定义事件,当触发就会调用父容器中的`addStudent`方法 --> <enter-input @add-student="addStudent"></enter-input> 

自定义组件:

Vue.component('enter-input', {     data() {         return {             student: ''         }     },     methods: {         addStudent() {             // 注意:事件名称定义时不要有大写字母出现             // add-student是自定义事件,this.student是传给父容器addStudent的值             this.$emit('add-student', this.student);             this.student = ''; // 清空当前姓名         }     },     template:         `<input v-model="student" type="text" @keyup.enter="addStudent" />` }) 

完整代码:

<div id="app">     <!-- 监听组件事件 -->     <enter-input @add-student="addStudent"></enter-input>     <my-list :students="students"></my-list> </div> <script src="../assets/vue2.js"></script> <script>     Vue.component('enter-input', {         data() {             return {                 student: ''             }         },         methods: {             addStudent() {                 // 注意:事件名称定义时不要有大写字母出现                 // add-student是自定义事件,this.student是传给addStudent的值                 this.$emit('add-student', this.student);                 this.student = ''; // 清空当前姓名             }         },         template:         `<input v-model="student" type="text" @keyup.enter="addStudent" />`     })     Vue.component('my-list', {         props: {             students: {                 type: Array,                 default: []             },         },         template:         `<ul> <li v-for="name in students" :key="name"> {{ name }}     </li>     </ul>`     })     const app = new Vue({         el: '#app',         data() {             return {                 students: ['张三', '李四', '王二']             }         },         methods: {             addStudent(student) {                 this.students.push(student);             }         },     }); </script> 

Vue全家桶系~1.Vue2基础(兼容)

4.自定义组件使用v-model

先回顾下vue的v-model怎么使用的:

<div id="app">     <div><input v-model="name" type="text" /></div>     <div>{{name}}</div> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 name: '小明'             }         },     }) </script> 

当改变文本框内容时,文本内容立刻改变:Vue全家桶系~1.Vue2基础(兼容)

双向绑定的这个v-model其实只是一个语法糖,背后相当于这样的:

PS:为什么event要加个$ ==> 你不加他到底是字符串,还是vdata中的变量呢?vue就不知道了

<input type="text" value="name" @input="name=$event.target.value"> 

如果我们自定义的组件,在绑定v-model的时候是不会双向绑定的,需要自己处理下(valueinput

  1. 自定义组件可以直接写v-model="name"

  2. 在自定义组件时设置下props: ['name']

  3. 方法里面添加一个变量修改的返回方法:this.$emit('input', e.target.value);

  4. template里面代码:<input :value="name" @input="onInput" type="text" />

完整代码示意

<div id="app">     <my-input v-model="name"></my-input>     <div>{{name}}</div> </div> <script src="../assets/vue2.js"></script> <script>     Vue.component('my-input',     {         props: ['name'],         methods: {             onInput(e) {                 this.$emit('input', e.target.value);             }         },         template:         `<div> 			<input :value="name" @input="onInput" type="text" />     	 </div>`     })     const app = new Vue({         el: '#app',         data() {             return {                 name: '小明'             }         },     }) </script> 

当改变文本框内容时,文本内容立刻改变:Vue全家桶系~1.Vue2基础(兼容)


5.插槽分发内容(含多个)

对于插槽分发,vue提供了 <slot> 元素来给组件便捷的传递内容

这个类比弹框提示,弹框这个组件是子组件封装好的,内容是由父组件传过来的(父组件内容显示在子组件中)

5.1.封装前的弹框

先写一个弹框显示和关闭的案例:

.message-box {     width: 300px;     padding: 10px 20px;     color: white;     background: rgb(2, 214, 133); } .message-box-close {     float: right;     color: white; } 

代码部分:

<div id="app">     <div class="message-box" v-show="isShow">         {{msg}}         <span class="message-box-close" @click="isShow=false">X</span>     </div> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 isShow: true, // 是否显示弹框                 msg: '操作成功!' // 弹框提示内容             }         },     }) </script> 

打开后弹框默认是显示的,点击X后就关闭:Vue全家桶系~1.Vue2基础(兼容)

5.2.子组件版弹框

下面我们进行改写下:

PS:不能直接在子组件里面这样写:<span class="message-box-close" @click="show=false">X</span>这样容易导致父子组件show变量不一致,控制台也会警告提示:只要父组件重新渲染,该值就会被覆盖,所以还是要让父组件修改

子组件中的$emit('close',false)是触发自定义事件close,并把false传递给事件调用的函数。之前我们是一个单独的函数然后用参数接收的,这边父组件可以通过$event来接收传递的值

<div id="app">     <message-box :show="isShow" @close="isShow=$event">{{msg}}</message-box> </div> <script src="../assets/vue2.js"></script> <script>     Vue.component('message-box', {         props: ['show'],         template: `     <div class="message-box" v-show="show">         <!-- 相当于占位符 -->         <slot></slot>         <span class="message-box-close" @click="$emit('close',false)">X</span>     </div>`     })     const app = new Vue({         el: '#app',         data() {             return {                 isShow: true, // 是否显示弹框                 msg: '操作成功!' // 弹框提示内容             }         },     }) </script> 

结果和上面一样,默认是显示的,点击X后就关闭:

Vue全家桶系~1.Vue2基础(兼容)

5.3.精简具名插槽

Vue提供了一种简化的方式:xxx.sync来实现(相当于帮你写了自定义事件,事件的名称是固定的 @update:xxx

子组件只需要微调:<message-box :show.sync="isShow">{{msg}}</message-box>

Template也微调下:@click="$emit('update:show',false)"

完整代码如下:

<div id="app">     <message-box :show.sync="isShow">{{msg}}</message-box> </div> <script src="../assets/vue2.js"></script> <script>     Vue.component('message-box', {         props: ['show'],         template: ` <div class="message-box" v-show="show"> <!-- 相当于占位符 --> <slot></slot> <span class="message-box-close" @click="$emit('update:show',false)">X</span>     </div>`     })     const app = new Vue({         el: '#app',         data() {             return {                 isShow: true, // 是否显示弹框                 msg: '操作成功!' // 弹框提示内容             }         },     }) </script> 
5.4.多位插槽案例

如果一个组件中需要占位多个坑位,可以在一个 <template> 元素上使用 v-slot 指令

PS:一个不带 name<slot> 默认的名字是default

还是上面的例子,如果我们现在除了提示语外,还要有个提示的标题,封装前写法如下:

<div id="app">     <div class="message-box" v-show="isShow">         {{msgTitle}}         <strong>{{msgContent}}</strong>         <span class="message-box-close" @click="isShow=false">X</span>     </div> </div> </div> <script src="../assets/vue2.js"></script> <script>     const app = new Vue({         el: '#app',         data() {             return {                 isShow: true,                 msgTitle: '温馨提示:',                 msgContent: '操作成功'             }         },     }) </script> 

样式微调如下:

.message-box {     width: 300px;     padding: 10px 20px;     color: white;     background: rgb(2, 214, 133); } .message-box-close {     float: right;     color: white; } 

打开页面进行提示,单击x则被关闭Vue全家桶系~1.Vue2基础(兼容)

封装成子组件则和上面有些不同,子组件里面要这样写:template v-slot:名字

PS:要为具名插槽传入内容,我们需要使用一个含 v-slot 指令的 <template> 元素,并将目标插槽的名字传给该指令

<message-box :show.sync="isShow">     <template v-slot:title>{{msgTitle}}</template>     <template v-slot:content>{{msgContent}}</template> </message-box> 

也可以简写:v-slot 有对应的简写 #,因此 <template v-slot:title>可以简写为 <template #title>

<message-box :show.sync="isShow">     <template #title>{{msgTitle}}</template>     <template #content>{{msgContent}}</template> </message-box> 

然后Template里面是这样的:

<div class="message-box" v-show="show">     <slot name="title"></slot>     <strong>         <slot name="content"></slot>     </strong>     <span class="message-box-close" @click="$emit('update:show',false)">X</span> </div> 

代码如下:

<div id="app">     <message-box :show.sync="isShow">         <template v-slot:title>{{msgTitle}}</template>         <template v-slot:content>{{msgContent}}</template>     </message-box> </div> </div> <script src="../assets/vue2.js"></script> <script>     Vue.component('message-box', {         props: ['show'],         template:         `<div class="message-box" v-show="show"> <slot name="title"></slot> <strong> <slot name="content"></slot>     </strong> <span class="message-box-close" @click="$emit('update:show',false)">X</span>     </div>`     })     const app = new Vue({         el: '#app',         data() {             return {                 isShow: true,                 msgTitle: '温馨提示:',                 msgContent: '操作成功'             }         },     }) </script> 

6.组件知识概述

Q:谈一下你对Vue组件化的理解

A:组件化是Vue的精髓,Vue应用就是由一个个组件构成的。Vue的组件化涉及到的内容非常多,可以从以下几点进行阐述:

  1. 组件定义:组件是可复用的 Vue 实例,准确讲它们是VueComponent的实例,继承自Vue

  2. 组件优点:组件化可以增加代码的复用性可维护性可测试性

  3. 使用场景

    1. 通用组件:实现最基本的功能,具有通用性、复用性,例如按钮组件、输入框组件、布局组件等
    2. 业务组件:它们完成具体业务,具有一定的复用性,例如登录组件、轮播图组件
    3. 页面组件:组织应用各部分独立内容,需要时在不同页面组件间切换,例如列表页、详情页组件
  4. 组件应用

    1. 定义Vue.component()components选项,sfc
    2. 分类:有状态组件,functionalabstract
    3. 通信props$emit()/$on()provide/inject
      1. 不考虑耦合性可以使用:$children/$parent/$root/$attrs/$listeners
    4. 内容分发<slot><template>,v-slot
    5. 使用及优化iskeep-alive,异步组件
  5. 组件本质:组件的本质是产生虚拟DOM

    1. Vue中的组件经历如下过程:组件配置 => VueComponent实例 => render()转换为方法 => Virtual DOM=> DOM

3.一个vue3的案例

如果还是这种混合开发的风格,Vu3和Vue2差不多,就是引用的JS变成了Vue3的,然后创建语句变成了下面这个:

const app = Vue.createApp({}) // 创建app  app.mount('#app') // 挂载 

来个案例:通过两个按钮来控制count数值的加和减

<div id="app">     <h3>计数:{{count}}</h3>     <button @click="addCount">++</button>&nbsp;     <button @click="subCount">--</button> </div> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <!-- <script src="../assets/vue3.js"></script> --> <script>     const app = Vue.createApp({ // 创建app对象         data() {             return {                 count: 0             }         },         methods: {             addCount() {                 this.count++;             },             subCount() {                 this.count--;             }         },     })     app.mount('#app') // 挂载 </script> 

Vue全家桶系~1.Vue2基础(兼容)

Vue基础部分先到这,后面就不是这种传统的混合开发了,以上内容只是为了之前混合开发的朋友做过渡所写,Vue3内容请看下篇(完)