概念
一个动态构建用户界面的 渐进式 JS框架. 遵循MVVM模式
主流三大框架
VUE React Angular
动态构建用户界面(数据 -> 界面)
渐进式(自底向上慢慢改进)
MVVM模式(面试)
实现 数据 与 视图 的双向绑定
Model(数据模型)
View(视图)
VM(View Model控制器)
链接
官网 Vue.js - 渐进式 JavaScript 框架 | Vue.js
V2文档 介绍 — Vue.js
V3文档 简介 | Vue.js
优点
Angular的模板语法和数据绑定
React的组件化和虚拟DOM
特点
声明式渲染(声明式地描述最终输出的 HTML 和 JavaScript 状态间的关系)
变量:保存数据
函数:操作数据
响应式(即 数据驱动)
在数据改变时响应式地更新 DOM
数据改变时,DOM视图会随之更改
组件化开发(拼积木)
安装配置
卸载后要手动删掉nodemodules包
新建目录
初始化 npm init -y
安装2.x版本
在线版 CDN
下载版 npm i vue@2 vue - npm
vue三步曲
1. 引入vue.js
2. 编写页面容器
3. 创建vue实例
this:方法的this指向对应的实例
看this指向谁 = 看实例是谁
button.click 指向button
vm.handleClick 指向 vm
VUE选项
vue3 组合式api
vue2 选项式api(构造函数选项)
el
data 定义数据
methods 方法(改变数据)
Vue构造函数创建vue实例
1. data中声明的变量会挂载到vm实例上,作为vm的属性
2. methods中声明的函数会挂载到vm实例上,作为vm的方法
MVVM
数据,视图,ViewModel
核心思想:双向绑定
数据改变 影响 视图
视图改变 影响 数据

vm.$mount("#app")
编译模板,挂载(使用模板生成的DOM,替换旧的DOM节点)
innerHtml 与 outerHtml

模板:由vue解析的HTML字符串内容
如何确定模板(理解)
1. el选项:指定容器作为模板
2. template选项:指定模板
3. render函数选项(渲染函数):指定模板
优先级:render函数 > template > el
vue工作流程(理解)
1. 编译模板
2. 挂载
插值语法
主要用于文本节点
语法 {{}}
插值表达式
1. 是个js表达式
2. 写什么(vm实例上的属性和方法)
指定语法
语法 v-
属性节点
指令表达式
with语法
人为改变this指向
this指向window,但可以直接获取obj中的成员,不需要this.成员
with (obj) { }
- const obj = { name: 'xiaoming' }
-
- function test() {
- with (obj) {
- console.log(name) // 'xiaoming'
- }
- }
- test()
常用指令
单向绑定 v-bind :
样式绑定 :class='' :style=''
双向绑定 v-model
原始html v-html="content"
方法绑定 v-on:click='' @click=''
条件渲染 v-if='布尔值/变量'
列表渲染 v-for='(value, index) in obj' :key='唯一值(id)'
将DOM元素的属性和vue中的状态绑定
语法
v-bind:href="url"
简写(常用):href="url"
指令(属性名)=指令表达式(属性值)
1. 可以是基本数据 30 '30' true
2. 可以是vm上的属性和方法
3. 有效的 js表达式
语法
v-on:click='handleClick'
简写 @事件名='表达式'
表达式
不加括号(70%)
加括号(20%)目的:为了传参
加括号,使用$event(10%)
目的:传参同时传事件对象
@click="add(3, $event)"
add(n, e) { }
注意点
1. methods 中不要用箭头函数(不绑定this)(普通函数中this指向vm)
2. methosd 定义的函数不能和 data的变量同名。所以加个handle或get以避免同名
事件修饰符 可连写
.stop 阻止冒泡
.prevent 阻止默认行为
.once 事件只可执行一次
按键修饰符
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
键盘的左右 与鼠标的左右
系统按键 .ctrl .alt .shift .meta
.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
鼠标 .left .right .middle
语法
v-model="变量"
修饰符
.lazy
类似防抖
数据全部输入完(失焦)后,改变一次
.number
转换为数字类型
.trim
去除首尾空格
应用 (表单)🟢
1. 文本:text, password
绑定value属性
监听input事件
2. 单选/复选框
绑定checked属性
监听input事件
3. 下拉框
监听change事件
绑定option对应的value属性
- <script>
- const vm = new Vue({
- el: '#app',
- data: {
- name: '',
- password: '',
- age: 0,
- gender: '男',
- hobbies: ['sleep'], // 多选框跟一个数组绑定
- city: '',
- sign: '1111',
- test: 'abc',
- },
- })
- script>
-
- <div id="app">
- 用户名: <input type="text" v-model.trim.lazy="name" /> <br />
- 密码: <input type="password" v-model="password" /> <br />
- 年龄: <input type="text" v-model.number="age" /> <br />
-
-
- 性别: <input type="radio" id="male" v-model="gender" value="男" />
- <label for="male">男label>
- <input type="radio" id="female" v-model="gender" value="女" />
- <label for="female">女label> <br />
- 爱好:
- <input type="checkbox" id="eat" v-model="hobbies" value="eat" />
- <label for="eat">吃饭label>
- <input type="checkbox" id="sleep" v-model="hobbies" value="sleep" />
- <label for="sleep">睡觉label> <br />
- 城市:
-
- <select v-model="city">
-
- <option value="" disabled>请选择option>
- <option value="wuhan">武汉option>
- <option value="beijing">北京option>
- <option value="shanghai">上海option>
- select>
- <br />
- 个人签名: <textarea v-model="sign">textarea>
- div>
注意点
1. 单选
同一级单选按钮,绑定同一个属性名
通过value指定绑定的属性值
2. 复选
单个复选框 绑定一个布尔值
多个复选框(一组) 绑定同一个属性名(数组或集合set)
通过value指定绑定的属性值
3. 下拉框
没有指定option的value,使用option中的文本作属性值
指定option的value,使用value做属性值
常用法: value="" diabled
通过变量值与value匹配来指定默认选项
4. 文本域
使用v-model实现双向绑定
不能使用{{}}语法 :单向(数据 -> 视图)
定义
当指令表达式的返回值为真,渲染DOM
语法
v-if v-else-if v-else
有更高切换开销
需要实现逻辑时用
v-show
有更高初始渲染开销
需要频繁切换显示时用
注意事项
1. 当条件为真, v-if 和v-show没有区别
2. 当条件为假, v-if 不会创建DOM, v-show通过css的display属性隐藏
- <div id="app">
- <div v-if="flag2">通过v-if渲染div>
- <div v-else-if="flag">v-else-ifdiv>
- <div v-else>通过v-else渲染div>
-
- <div v-show="flag">通过v-show渲染div>
- div>
-
- <script>
- const vm = new Vue({
- el: '#app',
- data: {
- flag: true,
- flag2: false,
- },
- })
- script>
遍历语句
定义
循环遍历数组或对象,渲染多个元素
语法
数组
v-for="item in items"
获取元素
v-for="(item, index) in items"
获取元素和索引
对象
v-for="value in obj"
获取对象的值列表
v-for="(value, key) in obj"
值列表/键列表
v-for="(value, key, index) in items"
值列表/键列表/索引
key
定义: 虚拟DOM中表示
作用: 在更新数据时,依据key来对比新旧DOM,提高对比的效率
应用
在渲染列表时绑定唯一标识
数据不会更改(不增删打乱原序)时,可以使用index索引做key
注意
v-for不要与v-if在同一元素连用,如果需要,可以在v-if外面套一层
- <li v-for="item in items">{{item}}li>
- <li v-for="(item, index) in items">
- {{index}}-{{item}}
- li>
-
- <ul>
- <li v-for="value in obj">{{value}}li>
- <li v-for="(value, key) in obj">
- {{key}}-{{value}}
- li>
- <li v-for="(value, key, index) in obj">
- {{index}}-{{key}}-{{value}}
- li>
- ul>
-
- <tr v-for="stu in stus">
- <td v-for="item in stu">{{item}}td>
- tr>
-
- <script>
- const vm = new Vue({
- el: '#app',
- data: {
- // 数组名称一般使用名词的复数形式
- items: ['test1', 'test2', 'test3'],
- obj: {
- name: '小明',
- age: 20,
- },
- stus: [
- { id: 1, name: 'xiaoming', age: 20 },
- { id: 2, name: 'xiaomei', age: 18 },
- { id: 3, name: 'xiaopang', age: 2 },
- ],
- },
- })
- script>
绑定class属性
对象
obj: { red: true }
数组(推荐)
arr: ['red', 'blue']
绑定style属性
obj: {
color: 'blue',
// 小驼峰/加引号
fontSize: '40px',
'font-size': '40px'
}
- <style>
- .red {
- color: red;
- }
- .blue {
- color: blue;
- }
- style>
- head>
- <body>
- <div id="app">
- <div :class="str">绑定字符串div>
- <div :class="arr">绑定数组div>
- <div :class="flag?'red':'blue'">绑定表达式div>
- <div :class="obj">绑定对象div>
-
- <div :style="styleObj">绑定对象div>
- div>
- <script>
- const vm = new Vue({
- el: '#app',
- data: {
- str: 'red',
- arr: ['red', 'blue'],
- flag: true,
- obj: {
- red: true,
- blue: true,
- },
- styleObj: {
- color: 'white',
- // css中如果存在-, 要加''
- 'font-size': '100px',
- // 也可以使用小驼峰法书写
- backgroundColor: 'skyblue',
- },
- },
- })
- script>
- body>
css 优先级
样式绑定的三种写法
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Documenttitle>
-
- <script src="../node_modules/vue/dist/vue.js">script>
- <style>
- /* 初始化 重置样式 */
- * {
- margin: 0;
- padding: 0;
- }
- li {
- list-style: none;
- }
- /* 功能 */
- .tab {
- /* 居中的效果 */
- margin: 50px auto;
- width: 700px;
- height: 40px;
- min-width: 700px;
- /* 盒子模型 */
- border: 1px solid #eee;
- border-bottom: 1px solid #e4393c;
-
- background-color: #f7f7f7;
- box-sizing: border-box;
- }
- .tab-list {
- display: flex;
- height: 100%;
- }
- .tab-list .tab-item {
- /* flex: 作为tab-list的子元素, 平分剩余空间 */
- flex: 1;
- /* 控制子元素(水平垂直居中) */
- display: flex;
- justify-content: center;
- align-items: center;
-
- color: #666;
- cursor: pointer;
- }
- .tab-item:nth-of-type(4) {
- /* 设置第4个li元素的最小宽度 */
- flex-basis: 120px;
- }
- .tab-item:hover {
- color: #e4393c;
- }
- /* .tab-item.active 不能加空格. 表示同时选中*/
- .tab-list .current {
- color: #fff;
- background-color: #e4393c;
- }
- style>
- head>
- <body>
- <div id="app">
-
- <div class="tab">
- <ul class="tab-list">
-
-
-
- <li
- v-for="(item,index) in items"
- @click="active = index"
- :class="['tab-item', index==active?'current': '']"
- >
- {{item}}
- li>
- ul>
- div>
- div>
- <script>
- const vm = new Vue({
- el: '#app',
- data: {
- items: [
- '商品介绍', '规格与包装', '售后保障', '商品评价(100万+)', '手机社区'
- ],
- active: 0,
- },
- })
- script>
- body>
- html>
概念
是基于现有属性计算后的属性
作用
对原始数据进行加工,返回加工结果
复杂的内容运算尽量写在computed中
多因一果
优先于侦听
特点
有缓存
调试方便
- <div id="app">
- <h3>使用计算属性: {{computedMsg}}h3>
- div>
- <script>
- const vm = new Vue({
- el: '#app',
- computed: {
- // 计算属性名: 函数(求计算属性的值)
- computedMsg() {
- // 该函数的返回值, 做为访问计算属性的结果
- return this.msg.split('').reverse().join('')
- },
- }})
扩展 ( get set方法 ) ⭕
应用: get监听全选与否(arr.every), set进行全选与否动作


特点
新值和 旧值作用
使用
参数
深度监听的开启
vue2默认不开启深度侦听,vue3默认开启
开始侦听后立即调用一次
得到更新后的DOM
- //得到更新后的DOM
- this.$nextTick(function () {
- console.log('dom新:', app.innerHTML);
- });
- <div id="app">
- 姓:
- <input type="text" v-model="lastName" />
- <br />
- 名:
- <input type="text" v-model="firstName" />
- <br />
- 全名(使用watch实现): {{fullName}}
- div>
- <script>
- // 1. 默认是浅层次的侦听(只会侦听data中第一层数据的变化)
- // 2. 如果希望深层次侦听, 需要设置deep: true这个选项
- // 如果侦听对象(引用)类型, 是不能获取旧值
- const vm = new Vue({
- el: '#app',
- data: {
- lastName: '',
- firstName: '',
- fullName: '',
- obj: {
- a: {
- b: {
- c: '1',
- },
- },
- },
- },
- watch: {
- // 在侦听的回调中可以得到新值和旧值
- // 侦听的属性(vm上已经存在的属性): 回调函数
- lastName(newValue, oldValue) {
- console.log('更新之后: ', newValue)
- console.log('更新之前: ', oldValue)
- this.fullName = this.lastName + this.firstName
- },
- firstName() {
- this.fullName = this.lastName + this.firstName
- },
- // obj() {
- // console.log('只有当obj重新赋值时, 才会被侦听到')
- // },
- // watch 对象语法
- obj: {
- deep: true, // 开启深度侦听
- immediate: true, // 在侦听时立即执行回调函数
- handler: function (newValue, oldValue) {
- console.log('更新之前', oldValue)
- console.log('更新之后', newValue)
- // 通过handler指定回调
- console.log(
- '开启深度侦听, 不管多少层, 只要数据变化, 都可以被侦听到...'
- )
- },
- },
- },
- })
- script>
➕$nextTick 数据异步更新
- <div id="app">{{msg}}div>
- <script>
- const vm = new Vue({
- el: '#app',
- data: {
- msg: 'hello',
- },
- watch: {
- msg() {
- // watch回调在DOM更新前执行,回调中得到的是旧DOM
-
- // 使用$nextTick得到更新之后的DOM
- this.$nextTick(function () {
- // 注册的该函数会在DOM更新之后执行
- console.log(app.innerHTML)
- })
- },
- },
- })
- script>
是否会在vm实例中挂载新属性
computed会
watch不会
对应关系
computed是多对一, 可以同时监听多个值改变, 最终计算得到一个新的属性
watch是一对多, 主要监听一个属性的变化, 执行多种逻辑
能否获取新旧值?
computed不能
watch能
概念
用于对数据格式化的 一个 函数
语法
{{ 表达式 | 过滤器1 | 过滤器2 ...}}
分类
其它
vue3去掉了
用一个带参数的计算属性来替代
对数据格式化的一个函数
- <div id="app">{{ price | formatPrice }}div>
- <script>
- // 全局过滤器(就是一个函数)
- // Vue.filter('formatPrice', function (price) {
- // return '¥' + price.toFixed(2)
- // })
- // Vue.filter('test', function (value) {
- // return value + '元'
- // })
-
- // 局部过滤器
- const vm = new Vue({
- el: '#app',
- data: {
- price: 90,
- },
- filters: {
- formatPrice: function (price) {
- return '¥' + price.toFixed(2) + '元'
- },
- },
- })
- script>
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Documenttitle>
- head>
- <body>
-
- <script>
- // defineProperty: 定义属性
-
- const obj = {}
-
- let value
- // 第一个参数: 要操作的对象
- // 第二个参数: 要添加的属性
- // 第三个参数: 描述符对象
- Object.defineProperty(obj, 'msg', {
- // 自定义msg的访问和设置过程
- get: function () {
- // 当访问msg属性时, 会执行该函数, 返回值做为msg的属性值
- console.log('获取msg的值:', value)
- return value
- },
- set: function (newValue) {
- // 当设置msg属性时, 会执行该函数
- console.log('设置msg的值: ', newValue)
- value = newValue
- },
- })
-
- obj.msg = 200
- console.log(obj.msg)
- script>
- body>
- html>
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Documenttitle>
- head>
- <body>
- <div id="app">hellodiv>
- <script>
- // 定义响应式
- function defineReactive(target, key, value) {
- Object.defineProperty(target, key, {
- get: function () {
- return value
- },
- set: function (newValue) {
- value = newValue
- // 重新渲染页面 (执行render函数)
- app.innerHTML = newValue
- },
- })
- }
-
- function Vue(options) {
- // 1. 数据劫持(将data中的原始对象->转换成响应式对象)
- // 响应式对象: 自定义getter和setter
- const keys = Object.keys(options.data)
- keys.forEach((key) => {
- // 重新定义data上属性, 添加getter和setter
- defineReactive(options.data, key, options.data[key])
- })
-
- console.log(options.data)
- // 添加到this上, 叫_data(响应式对象)
- this._data = options.data
-
- // 2. 数据代理
- keys.forEach((key) => {
- defineReactive(this, key, this._data[key])
- })
- }
-
- const vm = new Vue({
- data: {
- msg: 'hello',
- name: 'xiaoming',
- },
- })
-
- console.log(vm)
- // vm._data.msg = 'world'
- // vm.msg = 'world'
- script>
- body>
- html>
1. 全局感知 (要求组件以外的知识)
2. 模板依赖 (模板内使用的资源)
3. 接口 (组件的接口)
4. 本地状态 (本地的响应式 property)
5. 事件 (通过响应式事件触发的回调)
6. 非响应式的 property (不依赖响应系统的实例 property)


- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Documenttitle>
- <script src="../node_modules/vue/dist/vue.js">script>
-
- <style>
- table,
- tr,
- th,
- td {
- border: 1px solid #ddd;
- border-collapse: collapse;
- border-spacing: 0;
- }
- .cart {
- min-width: 650px;
- }
- .cart caption {
- font-weight: 700;
- }
- .cart th {
- padding: 5px 20px;
- background-color: #f7f7f7;
- color: #666;
- }
- .cart td {
- padding: 5px 20px;
- }
- .cart .total {
- text-align: right;
- }
- style>
- head>
- <body>
- <div id="app">
- <table class="cart">
- <caption>
- 购物车
- caption>
-
- <tr>
- <th v-for="listItem in listItems">{{listItem}}th>
- tr>
- <tr v-for="(book,index) in books" :key="book.id">
- <td>{{book.id}}td>
- <td>{{book.name}}td>
- <td>{{book.publish_date}}td>
-
- <td>{{computedPrice(book.price)}}td>
- <td>
- <button @click="book.number>1 ? book.number-- : 1">-button>
- {{book.number}}
- <button @click="book.number++">+button>
- td>
- <td>
- <button @click="books.splice(index, 1)">移除button>
- td>
- tr>
- <tr v-if="books.length" class="total">
- <td colspan="6">总价格: {{total | format_price}}td>
- tr>
- <tr v-else>
- <td colspan="6">购物车中没有商品td>
- tr>
- table>
- div>
-
- <script>
- const vm = new Vue({
- el: '#app',
- filters: {
- format_price: function (price) {
- return '¥' + price.toFixed(2)
- },
- },
- data: {
- listItems: ['', '书籍名称', '出版日期', '价格', '购买数量', '操作'],
- books: [
- {
- id: 1,
- name: '算法导论',
- publish_date: '2006-09',
- price: 85,
- number: 5,
- },
- {
- id: 2,
- name: 'Unix编程艺术',
- publish_date: '2006-02',
- price: 59,
- number: 2,
- },
- {
- id: 3,
- name: '编程珠玑',
- publish_date: '2008-10',
- price: 39,
- number: 2,
- },
- ],
- },
- computed: {
- total() {
- // 总金额: 数组.reduce()
- let total = this.books.reduce((previous, current) => {
- previous += current.price * current.number
- return previous
- }, 0)
- return total
- },
- // 带参数的计算属性 (返回一个函数)
- computedPrice() {
- return (p) => {
- return '¥' + p.toFixed(2)
- }
- },
- },
- })
- script>
- body>
- html>
概念
自定义指令格式
