MVVM
-
M - Model,数据
-
V - View,视图
-
VM - ViewModel,关联 Model 与 View,实现数据双向绑定:View 的变动会自动反映到 ViewModel,ViewModel 会自动通知 Model 更新,反之,Molde 的变化也会自动反映到 ViewModel 中,ViewModel 会通知 View 实现重新渲染。
Vue
尤雨溪
现行两种版本:v2.x 与 v3.x,目前 3.x 的版本成为了默认版本。
渐进式 JavaScript 框架
兼容性:Vue 2.x 不支持 IE8 及以下版本,因为 Vue2.x 使用了 IE8 无法模拟的 ECMAScript 5 特性(Object.defineProperty()
)
安装
- 使用
<script src="">
方式在页面中引入
开发版本:包含完整的警告和调试模式
生产版本:经过压缩混淆,删除了警告等信息,有更小的体积
npm install
Vue CLI
声明式渲染
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统
Vue 实例
var vm = new Vue({
// 选项对象 option
el: '', // element
data: { // 是在模板语法中需要处理渲染的数据
message: ''
},
methods: { // 是在 vue 实例中需要调用到的一些方法
methodName() {}
}
})
说明:
- data 是需要在模板中渲染处理的数据
- 添加到 data 中的各属性(字段)会被自动挂载到创建的 vue 实例下(但以 $ 或 _ 开头的不会被挂载到 vue 实例下)
- data 中数据更新会触发页面响应式渲染,只有当实例被创建时就已经存在于
data
中的 property 才是响应式的。 - 创建的 vue 实例中可以使用 $data 获取到选项中 data 对象的引用
- 创建的 vue 实例中可以使用 $options 获取到选项对象
- 创建的 vue 实例中可以调用 $el 获取到渲染后的 DOM 节点
- 添加到 methods 中的各方法会被自动挂载到 vue 实例下,在方法中的 this 指向的是当前所创建的 Vue 实例本身
模板语法
Vue.js 使用了基于 HTML 的模板语法
插值
文本
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值
<div>
{{ JS expression }}
</div>
注意,{{ exp }}
内部添加的是 JS 的表达式,如:{{ 5 }}
、{{ message }}
、{{ 3 + 2 - 5 * 0 }}
,不能使用如 if-else
、while
等语句。
{{ exp }}
不会解析 html 文本内容,而是会将 html 文本转义为普通文本渲染。目的是避免 XSS 攻击。
**v-text 指令:**渲染普通文本
原始 html
要渲染原始 html,使用 v-html
指令:
<div v-html="htmlText">
</div>
请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值(v-html)。
属性(Attribute)
使用 v-bind
指令来动态绑定属性值:在绑定属性值时,不能使用 {{}}
语法。
v-bind
可以缩写为 :
,如:
<div v-bind:title="title">这是一个 div</div>
<div :title="title">这是一个 div</div>
指令(directive)
指令 (Directives) 是带有 v-
前缀的特殊 attribute(是在标签中添加的有特殊意义的属性,这个属性以 v- 开头),指令值是一个 JS 表达式。
指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
参数:
是在 指令名称 后使用 :
连接的。
修饰符:
修饰符 (modifier) 是以半角句号 .
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
所有指令:
文本与html渲染:
- v-text:更新元素的
textContent
- v-html:更新元素的
innerHTML
**条件渲染:**根据条件显示/隐藏元素,通常布尔值 true 表示显示、false 表示隐藏
- v-show:
- v-if:
- v-else-if:
- v-else:
列表渲染:
- v-for:循环列表渲染
<li v-for="item in array"></li>
<li v-for="item of array">in 与 of 作用相同</li>
<li v-for="(item, index) in array"></li>
<li v-for="value in object"></li>
<li v-for="(value, key) in object"></li>
<li v-for="(value, key, index) in object"></li>
<li v-for="n in number">固定循环 number 指定次数,n 从 1 开始依次递增计数</li>
事件绑定:
- v-on:绑定事件,可缩写为
@
属性绑定:
- v-bind:动态绑定标签的属性值,可缩写为
:
双向绑定:
- v-model:表单双向绑定
插槽:
- v-slot:
其它:
- v-pre:
- v-cloak:这个指令保持在元素上直到关联实例结束编译。通常将这个指令与 CSS 规则结合一起使用:
[v-cloak] { display: none; }
,可以隐藏未编译的 Mustache 标签直到实例准备完毕。(解决从未编译显示html模板到编译完毕显示渲染结果之间的闪烁效果)。 - v-once:只渲染一次
计算属性(computed)
模板内的表达式应该是进行简洁运算操作,如果运算逻辑比较复杂,就可以使用 计算属性 来实现。
在选项对象中,使用 computed
字段来表示计算属性。
const options = {
computed: {
completedCount: { // 已完成数量
get() {
return this.todos.reduce((result, todo) => todo.completed ? result + 1 : result, 0)
},
},
// 可简写为
// completedCount() { // 已完成数量
// return this.todos.reduce((result, todo) => todo.completed ? result + 1 : result, 0)
// },
}
}
计算属性(computed) VS 方法(methods)(面试)
- 计算属性有缓存,而方法没有缓存(计算属性是基于它们的响应式依赖进行缓存的),即只要依赖项不发生变化,计算属性就会一直使用之前运算的结果,而调用方法将总会再次执行函数。
- 计算属性通常是无副作用的,而方法中可以包含副作用操作(如:发送网络请求)
侦听属性(watch)
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。
在选项对象中使用 watch
来表示。
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
条件渲染
v-if、v-show、v-else-if、v-else
v-if 与 v-show 区别(面试):
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。(v-if 操作的是 DOM 节点的销毁与重建)
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。(v-show 操作的是节点的 css 属性中的 display)
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
列表渲染
v-for
建议尽可能在使用 v-for
时提供 key
attribute。
通常列表渲染时,每项绑定的 key 值应该唯一,一般使用当着遍历项的 id 属性作为 key 值绑定。
v-for 列表渲染时绑定 key 通常可以提升渲染性能,它能跟踪每个节点的身份,从而重用和重新排序现有元素。
在列表渲染时,通常不使用数组的 index 作为 key 值进行绑定。(面试)
数组更新
变更方法
- push() / pop()
- unshift() / shift()
- splice(index, len, replace)
- sort() / reverse()
调用这些方法后,原数组本身会受影响,所以视图会响应式渲染
替换数组
当调用非变更方法时,可以使用新数组替换旧数组,来实现渲染
v-for 与 v-if 一起使用(面试)
注意我们不推荐在同一元素上使用
v-if
和v-for
Vue2.x 中当它们处于同一节点,v-for
的优先级比 v-if
更高,Vue3.x 中当它们处于同一节点,v-if
的优先级比 v-for
更高
事件处理
- 在事件指令值中直接书写表达式
- 引用事件方法名称
- 内联处理器中调用方法,显式传递参数,可以用特殊变量
$event
把它传入方法,在方法体内部就可以使用到事件event对象了
事件修饰符:
- .stop - 阻止事件传播
- .prevent - 阻止默认行为
表单输入绑定
v-model
创建双向数据绑定
v-model
本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
v-model
在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
- text 和 textarea 元素使用
value
property 和input
事件; - checkbox 和 radio 使用
checked
property 和change
事件; - select 字段将
value
作为 prop 并将change
作为事件。
组件化应用构建
组件系统:它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用
在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例
定义并使用组件
定义选项对象
const options = {
template: '', // 视图模板
data() { // 组件内部需要使用到的数据
return {}
},
computed: {},
methods: {},
}
- template 定义的是组件的视图模板,在 template 中应该使用唯一的单个根元素包裹布局结构
- data 必须是一个函数**(面试)**,在函数体内部返回一个普通对象。原因:如果 data 是一个对象,对象是引用类型,组件是可被复用的,当同一个组件创建出多个实例时,每个实例都会引用到同一个 data 对象,则当任意一个实例修改 data 中的数据时,其它实例都会受到影响,通常这与实际业务不相符。将 data 定义成一个函数,在函数中返回一个新的对象,则在创建组件实例时,每个实例会调用 data() 函数,获得到自己独立于其它实例的数据对象,则当修改实例数据时,不会影响到其它实例。
注册组件
全局注册:
Vue.component(componentName, options)
局部注册:
const options = {
components: {
componentName: options
}
}
局部组件注册,是在其父组件的选项对象中,使用 components
字段来实现注册,局部注册的组件,只在其注册的父组件内部可以使用,其它组件中不能使用到。
使用组件
使用组件名称作为自定义标签名,在模板中引用标签。注意,在书写标签时,应该使用短横线命名规范来使用标签。
组件通信(面试)
组件之间进行数据的传递。
父子组件通信
- 父传子:使用属性(props)。
- 子传父:利用事件,在子组件中向父组件抛出数据。在父组件中使用子组件标签时,绑定一个自定义的事件,在子组件中,当需要传递数据时,调用
$emit(eventName, params)
方法,触发在父组件中使用子组件时绑定的自定义事件并传递需要父组件接收的数据。
跨组件层级通信
- 转换为父子组件通信(繁琐)
- event-bus(事件总线):借助 Vue 实例的
$on()
与$emit()
方法来实现数据传递。在需要接收数据的组件中,调用$on()
绑定一个自定义的事件,在需要传递数据的组件中,调用$emit()
触发绑定的自定义事件。 - vuex
插槽
利用插槽,可以在父组件中向子组件分发内容(在使用组件时,可自定义组件的局部布局)。
命名插槽(具名插槽):
<slot name="slot-name"></slot>
使用插槽,2.6 之前:
<div slot="slot-name"></div>
2.6 中:
<template v-slot:slot-name><div></div></template>
或
<template #slot-name><div></div></template>
生命周期钩子
从组件实例创建开始,到最终销毁,所经历的整个过程,称为生命周期。
生命周期钩子函数,给了用户在不同阶段添加自己的代码的机会。
生命周期
create 阶段
- beforeCreate():创建前,不能直接使用到 data 中的数据
- created():创建后,可以使用到 data 中的数据(data 对象中的数据被挂载到 Vue 实例下)。这个方法是我们发送初始网络请求的好地方。
如:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WzjQSiCG-1647786045292)(./note-images/create-lifecycle)]
mount 阶段
- beforeMount():挂载前
- mounted():挂载后,如果需要使用生成好的 DOM 节点进行操作,应该在这个钩子中进行操作。
如:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GYN9vjAK-1647786045306)(./note-images/mount-lifecycle)]
update 阶段
- beforeUpdate()
- updated()
update 阶段是在数据更新(发生改变)后才进入,初始渲染不会进入 upadte 阶段。
destroy 阶段
- beforeDestory()
- destroyed()
当调用 vm.$destroy() 方法时,会进入销毁阶段。
通常这个阶段会销毁的资源有:启动的定时器,有自己注册的 DOM 事件监听,有订阅的其它任务,有未完成的 ajax 请求,未关闭的 websocket 连接等…
父子组件层级关系生命周期
示例:
<father>
<son></son>
<!-- <son :message="message"></son> -->
</father>
创建与挂载阶段顺序:
father before create...
father created...
father before mount...
son before create...
son created...
son before mount...
son mounted...
father mounted...
更新阶段顺序:
/* 父子组件没有数据传递 */
// 修改父组件数据
father before update...
father updated...
// 修改子组件数据
son before update...
son updated...
/* 父组件传递属性数据给子组件 */
// 修改父组件数据
father before update...
son before update...
son updated...
father updated...
销毁阶段顺序:
father before destroy...
son before destroy...
son destroyed...
father destroyed...
单文件组件(SFC)
single-file components
文件后缀为:.vue
文件模板:
<template>
组件视图的html模板
</template>
<script>
// JS 脚本,导出组件的选项对象
// export default {}
</script>
<style lang="scss" scoped>
/*
* 样式
* lang 表示使用何种 CSS 预处理器,有 scss 或 less 可使用
* scoped 表示所书写的样式仅在当前组件内部可使用
*/
</style>
在 VSCode 中安装扩展程序:Vetur
,可实现 .vue 单文件组件代码高亮。
Webpack 中可使用 vue-loader
来处理 .vue 单文件组件。
Vue CLI
Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统
安装
$ npm i @vue/cli -g
# 或
$ yarn add global @vue/cli
安装完毕后,可在命令行中测试:
$ vue --version
如果能够查看到版本提示信息,则说明安装成功。
创建项目
GUI
$ vue ui
打开 GUI 图形化用户界面进行项目管理
命令行
- 进入需要创建项目的目录,打开命令行窗口
- 执行创建项目命令:
$ vue create vue-cli-demo
- 手动选择创建项目的特性:
Vue CLI v5.0.3
? Please pick a preset:
Default ([Vue 3] babel, eslint)
Default ([Vue 2] babel, eslint)
> Manually select features
- 确认选择特性:
Vue CLI v5.0.3
? Please pick a preset: Manually select features
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection, and
<enter> to proceed)
(*) Babel
( ) TypeScript
( ) Progressive Web App (PWA) Support
( ) Router
( ) Vuex
>(*) CSS Pre-processors
(*) Linter / Formatter
( ) Unit Testing
( ) E2E Testing
- 选择 vue 的版本:
Vue CLI v5.0.3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with (Use arrow keys)
3.x
> 2.x
- 选择 CSS 预处理器的使用:
Vue CLI v5.0.3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): (Use arrow keys)
> Sass/SCSS (with dart-sass)
Less
Stylus
- 选择 ESLint 规范:
Vue CLI v5.0.3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config:
ESLint with error prevention only
ESLint + Airbnb config
> ESLint + Standard config
ESLint + Prettier
- 保存代码时验证规范:
Vue CLI v5.0.3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config: Standard
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to pr
oceed)
>(*) Lint on save
( ) Lint and fix on commit
- 选择在独立文件中保存 babel、eslint 等的配置
Vue CLI v5.0.3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
> In dedicated config files
In package.json
- 是否预设上述选择
Vue CLI v5.0.3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N) n
- 如果是第一次使用 vue cli 创建项目,还会询问使用 yarn 还是 npm 安装依赖:
Vue CLI v5.0.3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
? Pick the package manager to use when installing dependencies: (Use arrow keys)
> Use Yarn
Use NPM
- 根据创建项目时选择的各项特性,自动安装项目的依赖资源
- 依赖资源安装完毕后,进入创建的项目目录:
$ cd vue-cli-demo
- 运行项目
$ npm run serve
# 或
$ yarn serve
- 启动成功:
> vue-cli-demo@0.1.0 serve
> vue-cli-service serve
INFO Starting development server...
DONE Compiled successfully in 19169ms 下午2:48:41
App running at:
- Local: http://localhost:8080/
- Network: http://10.7.164.77:8080/
Note that the development build is not optimized.
To create a production build, run yarn build.
项目结构
|-- public html文件目录
|-- src 源代码目录
|-- assets 静态媒体资源
|-- components 公用组件
|-- App.vue 单文件组件
|-- main.js 入口 JS 文件
|-- .eslintrc.js eslint 配置文件
|-- babel.config.js babel 配置文件
|-- package.json 项目配置文件
|-- vue.config.js 脚手架配置文件
package.json
npm scripts:
{
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
}
- serve: 开发环境任务,会启动 webpack-dev-server 服务器
- build: 生产环境任务
- lint: lint任务,可进行规范化验证,格式化代码
Vue-Router
SPA
single page application,SPA,单页面应用程序
只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序
浏览器一开始会加载必需的HTML、CSS和JavaScript,所有的操作都在这张页面上完成,都由JavaScript来控制。
前端路由
SPA 单页面应用程序,在进行页面切换时,不会发送新的HTML页面请求,而是通过 JavaScript 来实现页面内容的切换渲染。
- hash 模式:利用 URL 中 hash 改变不会向服务器发送新的请求的特点,在 JS 中监听 hash 值的变化。Hash 路由在 url 中有明显的
#
号标识 - history 模式:利用 H5 中 history 新增的 pushState() / replaceState() 来实现路由。history 实现路由时,也不会向服务器发送新的 HTML 页面访问请求。
使用 Vue-Router
安装
$ npm i vue-router
# 或
$ yarn add vue-router
vue-router@4 的版本是主要用于 Vue@3,如果在 vue@2 中使用,则安装 vue-router@3
创建VueRouter实例
在 ./src
目录下创建 router
目录,添加 index.js
文件:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home'
// Vue 使用 插件
Vue.use(VueRouter)
// 创建 VueRouter 实例
const router = new VueRouter({
mode: 'hash', // 路由模式,可取 hash 与 history,默认为 hash
routes: [ // 静态路由配置
{
path: '/index', // 路径,自定义名称
component: Home, // 对应应该渲染的路由组件
},
],
})
// 导出模块
export default router
Vue.use(VueRouter) 是在 Vue 中使用核心插件 VueRouter,这时,我们可以在组件中使用到两个与路由相关的组件:
<router-link>
: 导航,相当于<a href="">
超级链接<router-view>
: 路由视图,当访问指定 path 的路径时,在该组件位置渲染对应的路由组件
在根实例中注入 router
修改 main.js 文件:
// .......
import router from './router'
new Vue({
router,
// ......
})
在 Vue 根实例中注入 router 后,可在所有的 Vue 组件中使用到:
- $router: 指代的是 VueRouter 实例
- $route: 当前激活路由