# 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
## 生命周期

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'`

## 最简单的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` 的语法糖, 针对下面的这些场景做了内置简化
* **限制**:
* ``
* `