# VUE 2.x https://cn.vuejs.org/ ## 介绍 **mvvm**, 数据驱动视图的渐进式框架 * M: model, data中的数据 * V: view, 模板代码 * VM: ViewModel, 连接 View 和 Model, Vue实例 ## 安装 * 兼容性: Vue **不支持** IE8 及以下版本, 因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性 * 项目开发中一般通过vue-cli搭建开发环境 https://cli.vuejs.org/zh/guide/ ## 全家桶 脚手架: `vue-cli` 路由管理: `vueRouter` 状态管理: `vuex` http库: `axios` ui框架: `element`, `ant design vue` ## 优秀的相关项目 https://github.com/vuejs/awesome-vue#libraries--plugins ## 生命周期 ![|0x0](https://img.081024.xyz/1-vue2-1742521955178.png) 1. `beforeCreate `:在内存中创建出vue实例,数据观测 (data observer) 和 event/watcher 事件配置还没调用(data 和 methods 属性还没初始化) 2. 【执行数据观测 (data observer) 和 event/watcher 事件配置】 3. `created`: 实例已完成数据观测 (data observer),property 和方法的运算,watch/event 事件回调。(data 和 methods属性完成初始化,还没开始编译模板,可以进行Ajax请求) 4. beforeMount:模板编译完成,还没有挂载到页面中,相关的 render 函数首次被调用 5. 【挂载模板到页面】 6. `mounted `:模板已挂载到页面中,真实的DOM渲染完成。可以操作DOM了! 1. mounted 不保证所有子组件也一起被挂载, 需要子组件都挂载后才执行的操作, 使用 `vm.$nextTick` 7. 至此,实例结束创建期,进入运行期,等待数据发生变化。 8. 【数据变化】数据变化时,会触发beforeUpdate和updated,但一般用watch 9. `beforeUpdate`:状态更新之前执行此函数,此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为此时还没有开始重新渲染DOM节点 10. 【更新页面】 11. `updated`:实例更新完毕后调用此函数,此时 data 中的状态值和界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了。 1. 可用于子组件向父组件传递数据的变化 `updated(){ this.$emit('contentChange',this.content) }` 12. `beforeDestroy`:实例销毁之前调用。在这一步,实例仍然完全可用。常在这里清除定时器和事件绑定 13. 【销毁实例】 14. `destroyed`:Vue 实例销毁后调用。此时,Vue实例绑定的所有东西都会解绑,所有的事件监听器会被移除,所有的子实例也会被销毁 * 不要在选项 property 或回调上使用箭头函数,比如 `created: () => console.log(this.a)` 或 `vm.$watch('a', newValue => this.myMethod())`, 因为箭头函数并没有 `this` ## 术语 * 前缀 `$`, 表示Vue内部提供的一些属性或方法 * **vm** (ViewModel 的缩写) 这个变量名表示 Vue 实例 * {{}} 双大括号: 将数据解析成文本 * js表达式和js代码: 表达式会产生一个值, 可以放在需要值的地方. `x==y?'a':'b'` ![image-20211101172459354|1047x49](https://img.081024.xyz/20211101172517.png) ## 最简单的vue例子 ```html 调试
{{ message }}
``` el 和 data 的第二种写法: ```js const vm = new Vue({ data: function(){ // 函数式 // vue组件中必须使用函数式 return { message: 'Hello Vue!' } } }) vm.$mount('#app') // 先创建对象再通过mount指定el console.log(vm) ``` ps: 实际项目开发中以单文件模式开发 ## 模板语法 * 插值语法: `{{xxx}}`, 可以直接读取 data 中的值 * 指令语法: `v-bind:href="xxx"` ## 数据绑定 * 单向绑定: `v-bind` * 双向绑定: `v-model`, 一般在表单元素上(如: input, select) ## 响应式数据 1. data中的所有属性, 最后都出现在vm上 2. vm上的属性, 以及Vue原型上的方法, 都可以在Vue模板中直接使用 ### Object.defineProperty 数据劫持 ```js let number = 18 let person = { name: '张三', sex: 'man' } Object.defineProperty(person, 'age', { // value, // 有value时不能设置get和set // enumerable:false, 控制属性是否可以被枚举, 默认false // writable:false, //控制属性是否可以被修改,默认值是false // configurable:false //控制属性是否可以被删除,默认值是false get(){ console.log('读取age属性') return number }, set(val){ console.log(`设置age属性, 值为${val}`) number = val } }) ``` * 如图, age 属性不直接显示, 而是用通过 `getter` 获取, 通过 `setter` 设置. * set 和 get 叫 `存取描述符`, 有 value 就是 `数据描述符` (不能同时用) * 枚举属性: 不可枚举键是浅色的, 且 Object.keys 等方法读不到 * 控制是否可被修改 ```js let person = { name: '张三', sex: 'man' } Object.defineProperty(person, 'age', { value = 18, // 有value时不能设置get和set writable:true, //控制属性是否可以被修改,默认值是false }) ``` * configurable 为 false, 删除会返回 false ### Vue中的数据劫持 1. 通过vm对象来劫持data对象中属性的操作(读/写) 2. 通过 `Object.defineProperty()` 把data对象中所有属性添加到vm上。 ```js console.log(vm.$data === vm_data) // true ``` ## SFC > 单文件组件 * 模板 ```vue ``` ## vue 常见指令 ### v-bind(缩写 : ) 动态绑定 ```js ... // 缩写 ... ``` * v-bind的值当成js解析, `:rules="/(^1[035789]\d{3}$)|(^[A-Za-z]\w{2,10}$)/"`, 正则需要解析 * 动态参数 ```js ... // 方括号里面作为表达式进行计算, 把结果当成属性名 // 表达式不能有空格或者引号, 可以用计算属性替代 // 避免使用大写, 因为会被转成小写 ``` #### class 对象语法 数组语法 除了 string , 还可以是对象或者数组 ps: 其实也可以把复杂的字符串计算放到计算属性里 ```js
// 对象语法: isRed为true时绑定red类名
//Array语法: 绑定列表里的class
// 可以混合使用
//根据条件切换class,这样写将始终添加 errorClass,但是只有在 isActive 是 truthy时才添加activeClass。
//使用对象语法简写 //错误示范 // value不能使用对象语法绑定, 提示value不能赋值对象 //改正: 可以写成三目运算符, ""里面的当成js解析 ``` #### style 对象语法 数组语法 * 单个对象 ```js
// ... data: { activeColor: 'red', fontSize: 30 } ``` 更简洁 ```js
// ... data: { styleObject: { color: 'red', fontSize: '13px' } } ``` * 多个对象 ```js
``` #### .sync 双向绑定修饰符 > 在自定义组件中, 对 porps 进行双向绑定, 和 v-model 相比, 可以多个 ```js // 父组件中 // 子组件中 props: { prop: 类型 } // ... this.$emit('update:prop', newValue) ``` 父组件其实是下面的简写 ```vue ``` ### v-for > 遍历Array或者Object (并不支持可响应的 `Map` 和 `Set` 值,所以无法自动探测变更) ```vue
// 也可以用 of 代替 in, 更贴近js迭代器语法
``` * 不指定key编辑器会报错: key 用来区分每个元素, 确保高效更新和渲染. * key 不能用 index, 在增删时可能导致出错 * map set 可以通过计算属性间接使用 #### template 的包裹 ```vue
``` #### 数组的更新 `push() pop() shift() unshift() splice() sort() reverse()` 这类会改变原数据的方法, 都进行了包裹, 它们也会触发视图更新 `filter() concat() slice()` 这类返回新数组的方法, 则需要使用新数组代替旧数组 通过 `this.array[index] = newValue` 不能触发视图更新, 可以通过 ` this.array.splice(index, 1, newValue) ` 或者使用 `this.$set(this.array, index, newValue)` #### 对象的更新 对象属性的添加和删除无法触发更新, 因为初始化时就为对象的属性添加了 setter/getter 转化 * 添加属性 如果要为对象添加响应式的属性, 可以使用 `this.$set(this.obj, key , newValue)` 直接使用 `Object.assign()` 也无法添加相应式属性, 应该把混合后的对象赋值给原来的对象 (使用解构会丢失响应性) `this.obj=Object.assign({}, this.obj, {a:1, b:2})` * 删除属性 `this.$delete(this.obj, key)` ### v-show > 实际上是设置 `display: block;` / `display: none;`, 只是简单地基于 CSS 进行切换 ```vue

Hello!

``` ### v-if > 实际上是用``注释内容, "真正"的条件渲染 * v-if 和v-show 的区别: * 手段:v-if 是动态的向DOM树内添加或者删除DOM元素;v-show 是通过设置DOM元素的display样式属性控制显隐; * 编译过程:v-if切换有一个 `局部编译/卸载` 的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于 `css切换`; * 编译条件:v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译(编译被缓存?编译被缓存后,然后再切换的时候进行局部卸载); v-show是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素保留; * 性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗 * 场景: * 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。 * 因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。 * ===v-if和v-for的不能同时使用=== * `v-for` 具有比 `v-if` 更高的优先级, v-for 的每次迭代都会进行 v-if 判断, 造成性能浪费 * 逻辑混乱, 展示的数据和被迭代的数据不一样 * 正确方法: 先用计算属性代替v-if进行过滤, 再对计算属性进行遍历 #### template 包裹 同样可以把逻辑写在 template 里 ```vue ``` ### v-else 和v-if一起使用 ```vue
Now you see me
Now you don't
``` ### v-else-if 和v-if v-else-if 一起使用 ```vue
A
B
C
Not A/B/C
``` ### v-model `v-model` 本质上是 `.sync` 的语法糖, 针对下面的这些场景做了内置简化 * **限制**: * `` * `