6 Vue2

Vue2 概述


Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架;与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用;Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合;另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动;

1. Vue 最大的优势

Vue 作为一款轻量级框架、简单易学、数据绑定、组件化、数据和结构的分离、虚拟DOM、运行速度快,并且作者是中国人尤雨溪,对应的API文档对国内开发者优化,作为前端开发人员的首选入门框架, Vue有很多优势

  • Vue.js 可以进行组件化开发,提升代码的复用性,使代码编写量大大减少,同项目的开发人员更加易于理解
  • Vue.js 最突出的优势在于可以对数据进行双向绑定,并对数据作数据代理操作,方便开发人员操作
  • 相比传统的页面通过超链接实现页面的切换和跳转, Vue使用路由技术使页面局部刷新,不用每次跳转页面都要请求所有数据和dom,这样大大加快了访问速度和提升用户体验
  • 官方的 Vue-cli,Vue-Router,Vuex 等工具使用方便,极大的提升了开发效率
  • 配合使用的第三方UI组件库使用方便,节省了很多开发时间

2. MVVM 模型

MVVM 是 Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,其核心是提供对 View 和 ViewModel 的双向数据绑定,这使得 Model 的状态改变可以通过 ViewModel 自动传递给 View
在MVVM的架构下,View 层和 Model 层并没有直接联系,而是通过 ViewModel 层进行交互;ViewModel 层通过双向数据绑定将 View 层和 Model 层连接了起来,使得 View 层和 Model 层的同步工作完全是自动的;因此开发者只需关注业务逻辑,无需手动操作 DOM,复杂的数据状态维护交给 MVVM 统一来管理

Vue 中 MVVM 的体现.png

3. MVVM 与 MVC 的区别

MVVM基本定义

MVVM 即Model-View-ViewModel的简写。即模型-视图-视图模型。模型(Model)指的是后端传递的数据。视图(View)指的是所看到的页面。视图模型(ViewModel)是mvvm模式的核心,它是连接view和model的桥梁。它有两个方向:一是将模型(Mode1)转化成视图(View),即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将视图(View)转化成模型(Model),即将所看到的页面转化成后端的数据。实现的方式是: DOM事件监听。这两个方向都实现的,我们称之为数据的双向绑定

MVC基本定义

MVC是Model-View- Controller的简写。即模型-视图-控制器。M和V指的意思和MVVM中的M和V意思一样。C即Controller指的是页面业务逻辑。使用MVC的目的就是将M和V的代码分离。MVC是单向通信。也就是View跟Model,必须通过Controller来承上启下。MVC和MVVM的区别并不是VM完全取代了C,只是在MVC的基础上增加了一层VM,只不过是弱化了C的概念,ViewModel存在目的在于抽离Controller中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现。也就是说MVVM实现的是业务逻辑组件的重用,使开发更高效,结构更清晰,增加代码的复用性

使用场景

MVC和MVVM其实区别并不大。都是一种设计思想。主要就是MVC中Controller演变成 MVVM中的viewModel, MVVM主要解决了MVC中大量的DOM操作使页面宣染性能降低,加载速度变慢,影响用户体验。
区别: Vue数据驱动,通过数据来显示视图层而不是节点操作
场景: 数据操作比较多的场景,需要大量操作DOM元素时,采用MVVM的开发方式,会更加便捷,让开发者更多的精力放在数据的变化上,解放繁琐的操作DOM元素。

Vue2 组成


1. vue-cli 脚手架项目目录

├─.browserslistrc -- 浏览器兼容文件
├─.editorconfig -- EditorConfig 规则配置
├─.eslintrc.js --  eslint 校验规则配置
├─.gitignore --  git 忽略配置
├─.prettierrc.js --  prettierrc 插件配置
├─babel.config.js --  babel 配置文件
├─jsconfig.json -- 项目配置文件
├─package-lock.json -- 包版本控制配置文件(名称、具体版本、下载地址 加快下载速度)
├─package.json -- 包配置文件(名称、版本)
├─README.md -- 项目说明
├─vue.config.js -- Vue 项目配置
├─src -- 源文件
|  ├─App.vue -- 根组件
|  ├─main.js -- 程序入口文件
|  ├─views -- 路由组件目录
|  ├─style
|  |   └index.scss -- scss 样式文件
|  ├─store
|  |   └index.js -- vuex 配置文件
|  ├─router
|  |   └index.js -- router 配置文件
|  ├─pages -- 特有组件目录
|  ├─components -- 公共组件目录
|  ├─assets -- 静态资源文件目录(打包压缩)
|  |   └logo.png -- Logo
├─public -- 静态资源文件目录(打包不压缩)
|   ├─favicon.ico -- 网页ICO文件
|   └index.html -- 首页
├─node_modules -- 第三方插件目录
├─dist -- 打包输出目录

2. Vue2 组件构成

// 组件模板结构
<template>
  <div class="app-container">
    <!-- 路由占位符 -->
    <router-view></router-view>
  </div>
</template>

// 组件行为
<script>
export default {
    // * 生命周期函数
    beforeCreate() {} // 初始化: 声明周期,数据监听,数据代理 执行前
    created() {} // 初始化: 声明周期,数据监听,数据代理 执行完毕
    beforeMount() {} // 已在内存中生成虚拟 DOM,即将插入到真实 DOM 中
    mounted() {} // 将内存中的虚拟 DOM 插入真实 DOM 完毕
    beforeUpdate() {} // 监听到修改数据,更新页面结构前
    updated() {} // 数据,页面更新完毕后
    beforeDestroy() {} // 即将销毁组件之前
    destroyed() {} // 销毁组件后
  
  // * 配置项
  // 挂载配置
  el: '#app'
  // 组件名称
  name: 'App',
  // 数据(函数式)
  data() {
    return {}
  },
  // 方法
  methods: {},
  // 计算属性
  computed: {},
  // 侦听器
  watch: {},
  // 过滤器
  filters: {},
  // 自定义指令
  directives: {}
  // 组件注册
  components: {},
  // 自定义属性
  props: {}
}
</script>

// 组件样式
<style lang="scss" scoped> // lang=使用何种预处理器语言;scoped 防止样式冲突(为所有选择器添加组件特有属性)
// 样式穿透
/deep/ :deep ::v-deep
// 此处使用@ 需要在前方加上 ~ : ~@
</style>

3. vue-cli 脚手架工具

vue-cli 是 Vue.js 开发的标准工具;它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程;引用自 vue-cli 官网上的一句话: 程序员可以专注在撰写应用上,而不必花好几天去纠结 webpack 配置的问题

**特点: **快速构建单页面应用程序(英文名: Single Page Application)简称 SPA,顾名思义,指的是一个 Web 网站中只有唯一的一个 HTML 页面,所有的功能与交互都在这唯一的一个页面内完成

npm i -g @vue/cli -- 安装
vue -V -- 查看版本
vue crrate projectname -- 创建项目
vue ui -- UI 方式创建项目
    - UI 形式查看各类项目信息,生成打包报告
npm run serve -- 启动项目
npm run build -- 项目打包
    --report // --report 选项可以生成 report.html 以帮助分析包内容 

4. Vue 组件

组件化: 对某个功能所需代码和资源按照规则进行规范型的集合,以便复用
组件是可复用的 Vue 实例,且带有一个名字,组件解决的是 HTML CSS JS 相互引用,组织混乱以及复用性较差的问题
因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等;仅有的例外是像 el 这样根实例特有的选项

通常一个应用会以一棵嵌套的组件树的形式来组织

VueComponent (构造函数)

组件本质是一个名为 VueComponent 的构造函数,且不是程序员定义的,是通过调用 Vue.extend()API 返回的,Vue.extend() 可以省略,可以直接传入一个配置对象,Vue 底层判断后自动调用Vue.extend()
且每一次使用 <组件></组件> , Vue 解析时会帮我们创建组件的实例对象
组件支持嵌套!!!

注意: 每次调用 Vue.extend() ,返回的都是一个全新的 VueComponent !
注意: 一个组件的 data 选项必须是一个函数,组件中的 data 写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份 data,造成一变则全变的结果
this指向

  • 组件配置中: data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】
  • new Vue(options)配置中: data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】

重点: Vue 在创建实例对象时让实例对象的隐式原型属性(__proto__)指向了 Vue.prototype

// 一个重要的内置关系
// ? 组件实例对象(VueComponent)可以访问到 Vue原型上的属性、方法
VueComponent.prototype.__proto__ === Vue.prototype

Vue API


全局配置

Vue.config 是一个对象,包含 Vue 的全局配置;可以在启动应用之前修改下列 property

Vue.config.silent = true // 取消 Vue 所有的日志与警告
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示
Vue.config.keyCodes.自定义键名 = 键码 // 定制按键别名

全局 API

Vue.extend( options ) // 使用基础 Vue 构造器,创建一个“子类”;参数是一个包含组件选项的对象,书写时可以省略,直接传入配置对象

Vue.set( target, propertyName/index, value ) // 向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新;它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi')
    - {Object | Function} plugin
  - 注意: 对象不能是 Vue 实例,或者 Vue 实例的根数据对象

Vue.use( plugin ) // 安装 Vue.js 插件;如果插件是一个对象,必须提供 install 方法;如果插件是一个函数,它会被作为 install 方法;install 方法调用时,会将 Vue 作为参数传入
    - 注意: 该方法需要在调用 new Vue() 之前被调用,当 install 方法被同一个插件多次调用,插件将只会被安装一次
  
Vue.directive('myDirective',{  // 注册或获取(接收)全局指令
    // 指令与元素成功绑定时(一上来)
    bind(element,binding) { ...	}
    // 指令所在元素被插入页面时
    inserted(element,binding) { ...	}
    // 指令所在的模板被重新解析时
    update(element,binding) { ...	}
}) 

Vue.nextTick( [callback, context] ) // 在下次 DOM 更新循环结束之后执行延迟回调;在修改数据之后立即使用这个方法,获取更新后的 DOM

Vue.filter('myFilter',function(value){ // 注册或获取(接收)全局过滤器
    return ... // 返回处理后的值
})

Vue.component( id, [definition] ) // 注册或获取全局组件;注册还会自动使用给定的 id 设置组件的名称

实例 property

vm.$data // Vue 实例观察的数据对象;Vue 实例代理了对其 data 对象 property 的访问

vm.$props // 当前组件接收到的 props 对象;Vue 实例代理了对其 props 对象 property 的访问

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

实例方法/数据

// 全局侦听器;观察 Vue 实例上的一个表达式或者一个函数计算结果的变化;回调函数得到的参数为新值和旧值;表达式只接受简单的键路径;对于更复杂的表达式,用一个函数取代
vm.$watch( expOrFn, callback, [options] ) // 观察 Vue 实例上的一个表达式或者一个函数计算结果的变化;回调函数得到的参数为新值和旧值;表达式只接受简单的键路径;对于更复杂的表达式,用一个函数取代
    - 注意: 在变更 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组;Vue 不会保留变更之前值的副本

vm.$set( target, propertyName/index, value ) // 这是全局 Vue.set 的别名

vm.$delete( target, propertyName/index ) // 这是全局 Vue.delete 的别名

实例方法/事件

vm.$emit( eventName, […args] ) // 触发当前实例上的事件;附加参数都会传给监听器回调

vm.$on( event, callback ) // 监听当前实例上的自定义事件;事件可以由 vm.$emit 触发;回调函数会接收所有传入事件触发函数的额外参数

vm.$once( event, callback ) // 监听一个自定义事件,但是只触发一次;一旦触发之后,监听器就会被移除

vm.$off( [event, callback] ) // 移除自定义事件监听器
    - 如果没有提供参数,则移除所有的事件监听器
    - 如果只提供了事件,则移除该事件所有的监听器; 字符串<单个> || 数组<多个>
    - 如果同时提供了事件与回调,则只移除这个回调的监听器

实例方法/生命周期

vm.$mount('#app') // 如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素;可以使用 vm.$mount() 手动地挂载一个未挂载的实例,返回vm - 实例自身

vm.$nextTick( [callback] ) // 将回调延迟到下次 DOM 更新循环之后执行;在修改数据之后立即使用它,然后等待 DOM 更新;它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上

vm.$destroy() // 完全销毁一个实例;清理它与其它实例的连接,解绑它的全部指令及事件监听器,触发 beforeDestroy 和 destroyed 的钩子;在大多数场景中你不应该调用这个方法;最好使用 v-if 和 v-for 指令以数据驱动的方式控制子组件的生命周期;

特殊 attribute

key // 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes,有相同父元素的子元素必须有独特的 key;重复的 key 会造成渲染错误
ref // 用来给元素或子组件注册引用信息,引用信息将会注册在父组件的 $refs 对象上,如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
is // 用于动态组件且基于 DOM 内模板的限制来工作,使得组件标签可以写在特定标签中如<ul>
slot 废弃 // 用于标记往哪个具名插槽中插入子组件内容(推荐使用 v-slot )
scope 移除 // 用于表示一个作为带作用域的插槽的 <template> 元素,它在 2.5.0+ 中被 slot-scope 替代(推荐使用 v-slot )
slot-scope 废弃 // 用于将元素或组件表示为作用域插槽;attribute 的值应该是可以出现在函数签名的参数位置的合法的 JavaScript 表达式;这意味着在支持的环境中,你还可以在表达式中使用 ES2015 解构;它在 2.5.0+ 中替代了 scope(推荐使用 v-slot )

内置组件

component // 渲染一个“元组件”为动态组件;依 is 的值,来决定哪个组件被渲染
transition // 作为单个元素/组件的过渡效果;<transition> 只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在可被检查的组件层级中
transition-group // 作为多个元素/组件的过渡效果;<transition-group> 渲染一个真实的 DOM 元素;默认渲染 <span>,可以通过 tag attribute 配置哪个元素应该被渲染,每个 <transition-group> 的子节点必须有独立的 key,动画才能正常工作
keep-alive // 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们;和 <transition> 相似,<keep-alive> 是一个抽象组件: 它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中,当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行;主要用于保留组件状态或避免重新渲染
slot // 作为组件模板之中的内容分发插槽;<slot> 元素自身将被替换
    - name - string,用于命名插槽

Vue2 实例


1. Vue 实例概述

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的,并且在创建实例时需传入一个配置对象,配置对象中的 data 配置项中的属性, Vue 会对其进行数据代理,以实现响应式

注意: Vue 只会在实例创建时进行一次数据代理,后续添加的 data 属性不会代理
el and data 的两种配置方式

// el 配置
el:'#app' // new Vue时候配置 el 属性
vm.$mount('#app') // 通过 Vue 方法指定 el 属性
// data 配置
data: (	... ) // 对象式配置 data 数据项
data() { return { ... } } // 函数式配置 data 数据项 (组件/Vue3 强制使用)

2. Vue 模板

在 Vue "#app"容器中的代码被称为 Vue 模板,代码依然符合 html 规范,只不过混入了一些特殊的 Vue 语法

3. Vue 插值语法

用于解析标签体中的内容,内容只能填写 JS 表达式

<div>{{ 可以填写: JS 表达式; 访问 Vue 实例/原型上的属性 }}</div>

4. Vue 样式绑定

<div id="root">
  // class 样式绑定
    <!-- 绑定class样式--字符串写法,适用于: 样式的类名不确定,需要动态指定 -->
    <div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>
    <!-- 绑定class样式--数组写法,适用于: 要绑定的样式个数不确定、名字也不确定 -->
    <div class="basic" :class="classArr">{{name}}</div> <br/><br/>
    <!-- 绑定class样式--对象写法,适用于: 要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
    <div class="basic" :class="classObj">{{name}}</div> <br/><br/>
  // style 样式绑定
    <!-- 绑定style样式--对象写法 -->
    <div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
    <!-- 绑定style样式--数组写法 -->
    <div class="basic" :style="styleArr">{{name}}</div>
</div>

Vue2 指令语法


用特定的指令语法来解析标签

v-bind (属性绑定)

v-bind:style="is ? true : false"; :style="is ? true : false"; :style="Vue实例=>data属性";

数据只能从 data 流向页面,支持表达式
v-bind 可以简写为 : ,方便输入

v-model:value (双向数据绑定)

v-model:value="Vue实例=>data属性"; v-model="Vue实例=>data属性"

数据不仅能从 data 流向页面,还可以从页面流向 data;双向绑定一般都应用在表单类元素上(如: input、select等),不支持表达式
v-model:value 可以简写为 v-model ,因为v-model默认收集的就是 value

数据修饰符

lazy -- 失去焦点再收集数据
number -- 输入字符串转为有效的数字(通常配置 input type="number" 使用)
trim -- 输入首尾空格过滤

收集表单数据

<input type="text"/> -- 则v-model收集的是value值,用户输入的就是value值
<input type="radio"/> -- 则v-model收集的是value值,且要给标签配置value值
<input type="checkbox"/>
    -	没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
    - 配置input的value属性
        - v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
        - v-model的初始值是数组,那么收集的的就是value组成的数组

组件内更改默认 model 配置

model: {
  prop: '*', // 修改默认 prop 名称
  event: '*' // 修改默认 event.input 事件名称
}

v-on (事件绑定)

v-on:click="Vue实例=>methods方法"; v-on:click="showHi(target)"; v-on:click="showHi(target, $event)"; @click="showHi(target, $event)";

事件的回调需要配置在 methods 配置对象中,最终会在 vm 实例上,不支持表达式
v-on 可以简写为 @ ,方便输入

传参
在事件绑定时使用 () 就可以进行传参,默认传递事件对象,但显示指定传参会覆盖事件对象,$event可以显示指定传递事件对象
事件修饰符

prevent -- 阻止默认事件(常用)
stop -- 阻止事件冒泡(常用)
once -- 事件只触发一次(常用)
capture -- 使用事件的捕获模式
self -- 只有 event.target 是当前操作的元素时才触发事件
passive -- 事件的默认行为立即执行,无需等待事件回调执行完毕
native -- 调用组件根元素原生事件操作

键盘事件

1. Vue 中常用的按键别名
回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left 
右 => right

2. Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)

3. 系统修饰键(用法特殊): ctrl、alt、shift、meta
    - 配合keyup使用: 按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
    - 配合keydown使用: 正常触发事件

4. 也可以使用keyCode去指定具体的按键(不推荐)

与 sync 修饰符配置

<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 修饰符的 v-bind 不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是无效的)。取而代之的是,你只能提供你想要绑定的 property 名,类似 v-model

v-if/v-show (条件渲染)

v-if/v-else-if/v-else

v-if="is === true"; v-else-if="is === false"; v-else

使用于切换频率较低的场景,对不符合条件的 DOM 元素直接移除,如果后续再想获取 DOM 元素将报错
v-if 可以和 v-else-if、v-else 一起使用,但要求结构不能被“打断”
v-if 条件渲染与 v-for 列表渲染同时使用时,Vue2 先 for 后 if , Vue3 先 if 后 for(注意点),解决思路就是不一起使用
v-if 可以与 template 配合使用,在不破坏页面结构的情况下(不用添加用于布局的元素),显示/移除元素,template 在渲染时不显示

v-show

v-show="is ? true : false"

适用于一些切换频率较高的场景,对不符合条件的 DOM 元素使用 display: none 隐藏,DOM 元素可以获取
v-show 无法与 template 配合,原因可想而知,无法找到 v_v

v-for (列表渲染)

v-for="(item, index) in/of dataList" :key="item.id/index"

列表循环可遍历: 数组、对象、字符串(用的很少)、指定次数(用的很少)
key 是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM旧虚拟DOM的差异比较,如果 Key 存在,判断内容是否变化,如有变化,重新渲染, Key 不存在,直接根据虚拟DOM重新渲染真实DOM
用index作为key可能会引发的问题: 1,若对数据进行: 逆序添加、逆序删除等破坏顺序操作,效率极低,需要全部重新渲染; 2,如果结构中还包含输入类的DOM,会产生错误DOM更新,界面渲染存在问题
开发中如何选择: 能够以 id 作为唯一的 Key 就选择 id,但如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,可以考虑使用 index 作为 Key

v-text (文本渲染)

<div v-text="str"></div>

向其所在的节点中渲染文本内容,与插值语法的区别: v-text 会替换掉节点中的内容,{{ … }} 则不会

v-html (HTML 渲染)

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

向指定节点中渲染包含html结构的内容, v-html 会替换掉节点中所有的内容,{{ … }} 则不会, v-html 可以识别html结构

注意: 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击; v-html 永要不要用在用户提交的内容上!!!

v-clock (控制显示)

[v-cloak]{
    display:none;
}
<div v-cloak>{{name}}</div>

本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉 v-cloak 属性
使用 css 配合 v-cloak 可以解决网速慢时页面展示出 {{ xxx }} 的问题

v-once (渲染一次)

<div v-once>初始化的n值是:{{n}}</div>

v-once所在节点在初次动态渲染后,就视为静态内容了
数据的改变不会引起v-once所在结构的更新,可以用于优化性能

v-pre (跳过编译)

<div v-pre>跳过编译过程</div>

跳过其所在节点的编译过程
可利用它跳过: 没有使用指令语法、没有使用插值语法的节点,会加快编译

v-slot (插槽传递)

<template v-slot:header>
    Header content
 </template>

提供具名插槽: v-slot:name
或需要接收 prop 的插槽 v-slot=“data”

注意: 此指令限用于 <template> 和 组件(对于一个单独的带 prop 的默认插槽)

Vue2 配置选项


template (模板)

  • 类型: string
  • 详细: 一个字符串模板作为 Vue 实例的标识使用;模板将会替换挂载的元素;挂载元素的内容都将被忽略,除非模板的内容有分发插槽;如果值以 # 开始,则它将被用作选择符,并使用匹配元素的 innerHTML 作为模板;常用的技巧是用 <script type="x-template"> 包含模板;出于安全考虑,你应该只使用你信任的 Vue 模板;避免使用其他人生成的内容作为你的模板;如果 Vue 选项中包含渲染函数,该模板将被忽略;
template: `<div>模板内容</div>`

components (注册组件)

  • 类型: Object
  • 详细: 包含 Vue 实例可用组件的哈希表;
components: {
  User:User
  // 可简写
  User
}

el (挂载配置)

  • 类型: string | Element
  • 限制: 只在用 new 创建实例时生效;
  • 详细: 提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标;可以是 CSS 选择器,也可以是一个 HTMLElement 实例;在实例挂载之后,元素可以用 vm.$el 访问如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显式调用 vm.$mount() 手动开启编译;提供的元素只能作为挂载点;不同于 Vue 1.x,所有的挂载元素会被 Vue 生成的 DOM 替换;因此不推荐挂载 root 实例到 <html> 或者 <body> 上如果 render 函数和 template property 都不存在,挂载 DOM 元素的 HTML 会被提取出来用作模板,此时,必须使用 Runtime + Compiler 构建的 Vue 库;
el: '#app'

name (名称)

  • 类型: string
  • 限制: 只有作为组件选项时起作用;
  • 详细: 允许组件模板递归地调用自身;注意,组件在全局用 Vue.component() 注册时,全局 ID 自动作为组件的 name;指定 name 选项的另一个好处是便于调试;有名字的组件有更友好的警告信息;另外,当在有 vue-devtools,未命名组件将显示成 <AnonymousComponent>,这很没有语义;通过提供 name 选项,可以获得更有语义信息的组件树;项目使用 keep-alive 时,可搭配组件 name 进行缓存过滤
name: 'App'

data (数据)

  • 类型: Object | Function
  • 限制: 组件的定义只接受 function;
  • 详细: Vue 实例的数据对象;Vue 会递归地把 data 的 property 转换为 getter/setter,从而让 data 的 property 能够响应数据变化;对象必须是纯粹的对象 (含有零个或多个的 key/value 对): 浏览器 API 创建的原生对象,原型上的 property 会被忽略;大概来说,data 应该只能是数据 - 不推荐观察拥有状态行为的对象;一旦观察过,你就无法在根数据对象上添加响应式 property;因此推荐在创建实例之前,就声明所有的根级响应式 property;实例创建之后,可以通过 vm.$data 访问原始数据对象;Vue 实例也代理了 data 对象上所有的 property,因此访问 vm.a 等价于访问 vm.$data.a;以 _$ 开头的 property 不会被 Vue 实例代理,因为它们可能和 Vue 内置的 property、API 方法冲突;你可以使用例如 vm.$data._property 的方式访问这些 property;当一个组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例;如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象;如果需要,可以通过将 vm.$data 传入 JSON.parse(JSON.stringify(...)) 得到深拷贝的原始数据对象;
data() {
  return {
    ... // 数据会被代理到 vm 实例上
  }
}

methods (方法)

  • 类型: { [key: string]: Function }
  • 详细: methods 将被混入到 Vue 实例中;可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用;方法中的 this 自动绑定为 Vue 实例;注意,不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++);理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined;
methods: {
  getData() { ... } // 不要用箭头函数! 箭头函数会让原本指向 vm 实例的 this 指向 window
}

computed (计算属性)

  • 类型: { [key: string]: Function | { get: Function, set: Function } }
  • 详细: 计算属性将被混入到 Vue 实例中;所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例;注意如果你为一个计算属性使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问;计算属性的结果会被缓存,除非依赖的响应式 property 变化才会重新计算;注意,如果某个依赖 (比如非响应式 property) 在该实例范畴之外,则计算属性是不会被更新的;& computed 无法进行异步操作什么时候触发计算: 1. 页面第一次加载时; 2. 依赖的数据发生改变时
computed: { // 需要获取一个新属性,其获得依托于已有数据时,可以通过计算属性计算得出(最终返回结果会被代理到 vm 实例上,可以被直接访问)
    fullName: {
        // 当有人读取 fullName 时,get 就会被调用,且返回值就作为 fullName 的值
        // 调用时机: 1. 初次读取 fullName 时; 2. 所依赖的数据发生变化时
        get() {
            // console.log(this) //此处的this是vm
        },
    // 修改 fullName 时, set 就会被调用,且 set 中要引起计算时依赖的数据发生改变,不返回值
        set(value) { ... }
    }
}
// 在只进行 get 操作时,可以简写
computed: {
    ...
  return result
}

watch (侦听器)

  • 类型: { [key: string]: string | Function | Object | Array }
  • 详细: 一个对象,键是需要观察的表达式,值是对应回调函数;值也可以是方法名,或者包含选项的对象;Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个 property;& watch 侦听属性更改时触发,支持异步操作(计算属性能做的侦听器都能做,但侦听器能做的计算属性不一定能)& Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以(提升性能)!
watch: {
    isHot: { 
        immediate: true, // 初始化时是否调用
    deep: true, // 是否开启深度监视
        //handler什么时候调用?当isHot发生改变时;
        handler(newValue, oldValue) {
            console.log('isHot被修改了', newValue,oldValue)
        }
    }
}
// 在不进行配置的情况下,可以简写
isHot(newValue, oldValue) {
  console.log('isHot被修改了', newValue,oldValue,this)
}

filters (过滤器)

  • 类型: Object
  • 详细: 包含 Vue 实例可用过滤器的哈希表;
{{ date | timeFormater }}
// 过滤器
filters: { // 针对特有组件进行过滤,过滤器也可以接收额外参数、多个过滤器也可以串联,过滤完成返回新的值,不改变原有数据
    timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss') { // value:第一个参数总是需要需要过滤的值; str 不传递参数,默认赋值
        return dayjs(value).format(str)
    }
}

directive (自定义指令)

  • 类型: Object || Function
  • 详细: 包含 Vue 实例可用指令的哈希表& 指令定义时不加 v- ,但使用时要加 v-& 指令名如果是多个单词,要使用 kebab-case( - 分割) 命名方式,不要用 camelCase(小驼峰) 命名
fbind: {
    // 指令与元素成功绑定时(一上来),配置相关属性
    bind(element,binding){
        element.value = binding.value
    },
    // 指令所在元素被插入页面时,只有 DOM 元素插入页面后才能操作
    inserted(element,binding){
        element.focus()
    },
    // 指令所在的模板被重新解析时,更新时配置相关属性
    update(element,binding){
        element.value = binding.value
    }
}

// 不需要在插入页面之时操作数据时,可以简写,调用时机: 1.指令与元素成功绑定时(一上来); 2.指令所在的模板被重新解析时
big(element,binding) {
    console.log('big', this) // 注意此处的this是window
    element.innerText = binding.value * 10
}

props (自定义属性)

  • 类型: Array<string> | Object
  • 详细: props 可以是数组或对象,用于接收来自父组件的数据;props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值;你可以基于对象的语法使用以下选项:

注意: props属性值不可以更改!!! (可以强制更改但不推荐) ,如果传递的值是对象,修改对象中属性不会报错,Vue只检测引用类型地址有没有更改

  • type: 可以是下列原生构造函数中的一种: StringNumberBooleanArrayObjectDateFunctionSymbol、任何自定义构造函数、或上述内容组成的数组;会检查一个 prop 是否是给定的类型,否则抛出警告;Prop 类型的更多信息在此;
  • default: any
    为该 prop 指定一个默认值;如果该 prop 没有被传入,则换做用这个值;对象或数组的默认值必须从一个工厂函数返回;
  • required: Boolean
    定义该 prop 是否是必填项;在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出;
  • validator: Function
    自定义验证函数会将该 prop 的值作为唯一的参数代入;在非生产环境下,如果该函数返回一个 falsy 的值 (也就是验证失败),一个控制台警告将会被抛出;你可以在这里查阅更多 prop 验证的相关信息;
// 简单语法
Vue.component('props-demo-simple', {
  props: ['size', 'myMessage']
})

// 对象语法,提供验证
Vue.component('props-demo-advanced', {
  props: {
    // 检测类型
    height: Number,
    // 检测类型 + 其他验证
    age: {
      type: Number, // 类型检查
      default: 0, // 默认值
      required: true, // 必填项
      validator: function (value) { // 自定义验证函数
        return value >= 0
      }
    }
  }
})

mixins (混入)

  • 类型: Array<Object>
  • 详细: mixins 选项接收一个混入对象的数组;这些混入对象可以像正常的实例对象一样包含实例选项,这些选项将会被合并到最终的选项中,使用的是和 Vue.extend() 一样的选项合并逻辑;也就是说,如果你的混入包含一个 created 钩子,而创建组件本身也有一个,那么两个函数都会被调用;Mixin 钩子按照传入顺序依次调用,并在调用组件自身的钩子之前被调用;(有组件混入则以组件混入为主)
var mixin = {
  created: function () {
    console.log('混入对象的钩子被调用')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})

// => "混入对象的钩子被调用"
// => "组件钩子被调用"

Vue2 生命周期


每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等;同时在这个过程中也会运行一些叫做生命周期钩子的函数
Vue 的声明周期函数中的 this 自动关联到 Vue 实例上下文,所以在使用时不要使用箭头函数


重要的生命周期节点

  • mounted: 发起 ajax 请求,开启定时器,绑定自定义事件,订阅消息等初始化操作
  • updated: 在更新数据和页面后需要进行操作时
  • beforeDestroy: 清除定时器,解绑自定义事件,取消订阅信息等收尾工作

组件特有

  • activated: keep-alive组件激活时调用。该钩子在服务器端宣染期间不被调用
  • deactivated: keep-alive 组件停用时调用。该钩子在服务器端宣染期间不被调用

新增
errorCaptured (2. 5. 0+ 新增): 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播
errorHandler: 为全局钩子,使用Vue. config. errorHandler 配置,接收参数与errorCaptured一致,2.6后可捕捉v-on与promise链的错误,可用于统一错误处理与错误兜底

Vue2 组件数据通信


原则: 数据在哪里,操作数据的方法就在哪里

1. props 实现

props 自定义属性主要用于 父 ==> 子 传输数据; 也可以传递方法,子组件调用方法并传参从而使父组件取得子组件中的数据

// 父组件
<son :dataList="dataList" :getDataList="getData"></Son>
data(){
  return {
    dataList: [],
    listData: {}
  }
}
methods: {
  getData(data){
    this.listData = data
  }
}

// 子组件
props: ['dataList','getDataList']
methods: {
  setDataList(data){
    this.getDataList(data)
  }
}

2. 自定义事件实现

自定义事件主要用于 子 ==> 父 传输数据

  • 第一种实现: 父组件给子组件绑定事件( @eventName=“eventName” ),子组件触发事件( $emit(event,params) )
// 父组件
<Son @eventName="eventName"></Son>
...
methods: {
  getData(data){
    this.dataList = data
  }
}

// 子组件
methods: {
  setData(){
    this.$emit('eventName',this.dataList)
  }
}
  • 第二种实现: 父组件给子组件绑定事件,获取组件示例,调用 o n 方法 , 将父组件的方法向子组件传递 , 实现 ( t h i s . on方法,将父组件的方法向子组件传递,实现( this. on方法,将父组件的方法向子组件传递,实现(this.refs.componentsName.$on(‘eventName’,this.fn) ),子组件触发事件( $emit(event,params) )
// 父组件
<Son ref="demo"/>
......
mounted(){
   this.$refs.demo.$on('eventName',this.test)
}

// 子组件
methods: {
  setData(){
    this.$emit('eventName',this.dataList)
  }
}

注意: $on() 方法绑定事件触发时,this指向默认是指向组件实例,如果想指向绑定事件(父)组件自身,一是绑定传递函数时使用 this.getData 方法,传递自身函数触发,二是使用 () => {} 使this向上寻找,指向组件自身

3. 全局事件总线 (GolbalEventBus)

一种组件间通信的实现方式,用于任意组件传递数据

安装事件总线

new Vue({
    ......
    beforeCreate() {
        Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
    },
    ......
}) 

使用事件总线

  • 接收数据

A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身

methods(){
  demo(data){......}
}
......
mounted() {
  this.$bus.$on('xxxx',this.demo)
}
  • 提供数据

在B组件中触发全局事件总线中的事件并传递参数,实现组件之间的数据传递

this.$bus.$emit('xxxx',数据)
  • 解绑事件

最好在 beforeDestroy 钩子中,用$off去解绑当前组件所用到的事件

beforeDestroy: {
  this.$bus.$off('xxxx')
}

4. 订阅与发布模式 (pubsub)

一种组件间通信的实现方式,用于任意组件传递数据

安装 pubsub

npm install pubsub-js

使用 pubsub

  • 接收数据

A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身

methods(){
  demo(data){......}
}
......
mounted() {
  this.pid = pubsub.subscribe('xxx',this.demo) // 订阅消息
}
  • 提供数据

在B组件中发布事件并传递参数,实现组件之间的数据传递

pubsub.publish('xxx',数据)
  • 解绑事件

最好在 beforeDestroy 钩子中,用 PubSub.unsubscribe(pid) 去取消订阅

PubSub.unsubscribe(pid)

5. ref引用

$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs

  • 父组件在使用子组件的时候设置ref
<son ref="sonRef" />
  • 父组件通过设置子组件ref来获取数据
this.$refs.sonRef // 获取子组件实例对象(以此来获取其身上的属性)

6. parent或root

$parent 在绝大多数情况下,触达父级组件会使得你的应用更难调试和理解,尤其是当你变更了父级组件的数据的时候。当我们稍后回看那个组件的时候,很难找出那个变更是从哪里发起的。
$root 对于 demo 或非常小型的有少量组件的应用来说这是很方便的。不过这个模式扩展到中大型应用来说就不然了。因此在绝大多数情况下,我们强烈推荐使用 Vuex 来管理应用的状态。

  • 通过共同祖辈 parent(不推荐(后期维护麻烦))或者root(根组件实例<小型可以使用(推荐使用 vuex)>) 搭建通信侨联
    • 兄弟组件
this.$parent.on ('add', this.add)
  • 另一个兄弟组件
this.$parent.emit ('add')

7. attrs与listeners

$attrs 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (classstyle 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (classstyle 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
$listeners 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

  • 适用场景:祖先传递数据给子孙
  • 设置批量向下传属性attrs和listeners
  • 包含了父级作用域中不作为prop被识别(且获取)的特性绑定(class和style除外)。
  • 可以通过v-bind="$attrs"传入内部组件

8. provide 与 inject

然而,依赖注入还是有负面影响的。它将你应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的 property 是非响应式的。这是出于设计的考虑,因为使用它们来创建一个中心化规模化的数据跟使用 $root做这件事都是不够好的。如果你想要共享的这个 property 是你的应用特有的,而不是通用化的,或者如果你想在祖先组件中更新所提供的数据,那么这意味着你可能需要换用一个像 Vuex 这样真正的状态管理方案了。

  • 在祖先组件定义provide属性,返回传递的值
  • 在后代组件通过inject接收组件传递过来的值
// 祖先组件
provide() {
  return {
    foo:'foo'
  }
}

// 后代组件
inject: ['foo']

9. Vuex

全局状态管理

Solt (插槽)


让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ==> 子组件

1. 默认插槽

父组件

<Son>
  <div>html结构</div>
</Son>

子组件

<!-- 定义插槽 -->
<solt>插槽默认内容...<solt>

2. 具名插槽

父组件

<Son>
  <template slot="center"> // 原有写法
    <div>html结构1</div>
  </template>

  <template v-slot:footer> // 新API
    <div>html结构2</div>
  </template>
</Son>

子组件

<!-- 定义插槽 -->
<slot name="center">插槽默认内容...</slot>
<slot name="footer">插槽默认内容...</slot>

3. 作用域插槽

数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定;games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定.作用域插槽其实就是带数据的插槽

父组件

<Category>
  <template scope="scopeData">
    <!-- 生成的是ul列表 -->
    <ul>
      <li v-for="g in scopeData.games" :key="g">{{g}}</li>
    </ul>
  </template>
</Category>

<Category>
  <template slot-scope="scopeData">
    <!-- 生成的是h4标题 -->
    <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
  </template>
</Category>

子组件

<template>
  <div>
    <slot :games="games"></slot>
  </div>
</template>

<script>
export default {
  name: 'Category',
  props: ['title'],
  // 数据在子组件自身
  data() {
    return {
      games: ['红色警戒', '穿越火线', '劲舞团', '超级玛丽']
    }
  }
}
</script>

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

taciturn丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值