# Vue第一天

# 1. vue初始/创建vue实例对象

  1. 让vue工作, 就必须创建一个vue实例 且要传入一个配置对象

  2. box容器的代码依然符合html规范 只是混入了特殊vue语法

  3. box容器里的代码称为: Vue模板

  4. vue实例和容器是一一对应的

  5. {xxx} 中要写js表达式 且xxx可自动读取data中所有属性

  6. 一旦data中数据改变 页面用到的数据也会自动更新

<div class="box">
    <!-- 3. {{对象名}} 为插件表达式 插入vue实例数据 -->
    <h1>Hello, {{name.toUpperCase()}} {{age}}</h1>
</div>
// 1. 设置为false 以阻止vue在启动时生成生产提示
Vue.config.productionTip = false
// 最好在配置调整好后 在继续写Vue代码

// 2. 创建vue实例 {} 是配置对象
new Vue({
    el: '.box', // el是指定当前vue实例为哪个容器服务 值为css选择器
    data: { // data是存储数据的 为指定容器使用  
        name: '小城故事xc',
        age: 18
    }
})

# 2. vue的模板/指令语法

# Vue模板语法有2大类:

# 1. 插值语法

  1. 功能: 用于解析标签体内容
  2. 写法: {xxx} 中要写js表达式 且xxx可自动读取data中所有属性
  3. 多级对象这样写: {all.name}

# 2. 指令语法

  1. 功能: 用于解析标签(包括: 标签属性、内容、绑定事件)
  2. 举例: <a v-bind:href="xx" 或简写为 :href="xx">
  3. 且可以直接读取到data中所有属性
  4. vue中有很多的指令, 且形式都是: v-???
<div class="box">
    <h1>插值语法</h1>
    <h2>你好, {{name}}</h2>
    <hr>
    <h1>指令语法</h1>
    <a v-bind:href="url" v-bind:x="age">百度搜索</a>
    <!-- 简写指令 -->
    <a :href="url" :x="age">{{all.name}}</a>
</div>
new Vue({
    el: '.box',
    data: {
        name: 'jack',
        url: 'https://baidu.com',
        age: 18,
        all: {
            name: '小城故事'
        }
    }
})

# 3. vue的单项/双项数据绑定

  1. Vue有2中数据绑定方式:
  2. 单选数据绑定(v-bind): 数据只能从data流向页面
  3. 双选数据绑定(v-model): 数据不仅能从data流向页面, 还可从页面流向data
  4. 双向绑定一般应用在表单类元素上(input、select...)
  5. v-model:value 可简写为v-model 因为默认收集的就是value值
<div class="box">
    <!-- 普通写法 -->
    单项数据绑定: <input type="text" v-bind:value="name"><br>
    双项数据绑定: <input type="text" v-model:value="name"><br>
    <!-- 简写 -->
    单项数据绑定: <input type="text" :value="name"><br>
    双项数据绑定: <input type="text" v-model="name">
    <!-- 下面代码是错误的 v-model只能应用在表单元素上(输入类元素) -->
    <!-- <h2 v-model:x="name">你好</h2> -->
</div>
new Vue({
    el: '.box',
    data: {
        name: '小城故事'
    }
})

# 4. el和data的两种写法

# 1. el有两种写法:

  1. new Vue时候配置el属性
  2. 先创建vue实例, 再通过vm.$mount('#root')指定el的值

# 2. data有两种写法: 1. 对象式 2.函数式

  1. 目前哪种写法都可以, 但组件化必须使用函数式, 否则报错
  2. 原则: 由Vue管理的函数, 一定不要写箭头函数, 写了this就不是vue实例了
let v = new Vue({
    // el: '.box', 
    data:function () {    // data函数式写法
        console.log(this) // 指向vue实例对象
        return {
            name: '小城故事'
        }
    }
})
// 01. el的两种写法
// 1. 第一种写法: el: '.box'
// 2. 第二种写法: $mount
console.log(v)
// 配合定时器使用
setTimeout(() => {
    v.$mount('.box')
}, 500)
// 02. data的两种写法
// 1. 第一种写法: 对象式 data: {}
// 2. 第二种写法: 函数式 组件化会用到

# 5. Vue的MVVM的模型

  1. M(模型): data中的数据
  2. V(视图): 模板代码
  3. VM(视图模型): Vue实例
  4. data中所有属性, 最后都出现在vm身上
  5. vm身上所有属性 及vue原型上所有属性, 在vue模板中都可以直接使用
<div class="box">
    <h1>你好, {{name}}</h1>
    <h4>测试一下: {{1+1}}</h4>
    <h4>测试一下: {{$options}}</h4>
    <h4>测试一下: {{$emit}}</h4>
</div>
let vm = new Vue({
    el: '.box',
    data: {
        name: '小城故事'
    }
})
console.log(vm)

# 6. Object.defineProperty数据代理方法

  1. defineProperty(对象名, '添加的值') 可以为对象添加属性值
  2. 但添加的对象不能被遍历
  3. 数据代理: 当读取obj的age属性时, get函数会被调用, 且返回值是age的值
  4. 数据劫持: 当修改obj的age属性时, set函数会被调用, 且会收到修改的具体指
let num = 18
let obj = {
    name: '小城'
}
Object.defineProperty(obj, 'age', {
    // value: 18,
    // enumerable: true,  // 控制属性是否可以枚举 默认false
    // writable: true,    // 控制属性是否可以被修改 默认false
    // configurable: true, // 控制属性是否可以被删除 默认false
    // 数据代理 当读取obj的age属性时, get函数会被调用, 且返回值是age的值
    get() {
        console.log('你读取了age属性')
        return num
    },
    // 数据劫持 当修改obj的age属性时, set函数会被调用, 且会收到修改的具体指
    set(value) {
        console.log('你修改了age属性, 且值为', value)
        return num = value
    }
})
console.log(obj)
console.log(Object.keys(obj))

# 7. 理解数据代理

  1. 数据代理: 通过一个对象代理对另一个对象中属性的操作(读/写)
  2. 可通过obj1访问和修改obj的属性
let obj = {x: 10}
let obj1 = {y: 20}
Object.defineProperty(obj1, 'x', {
    get() {
        return obj.x
    },
    set(value) {
        obj.x = value
    }
})
console.log(obj)
console.log(obj1)

# 8. vue中的数据代理

  1. Vue中的数据代理: 通过vm对象来代理data对象属性的操作(读/写)
  2. Vue数据代理的好处: 更加方便的操作data中的数据
  3. 基本原理:
  4. 通过Object.defineProperty()把data对象中所有属性添加到vm上
  5. 为每个添加到vm上的属性, 都指定一个getter/setter方法
  6. 在getter/setter内部去操作(读/写)data中对应的属性
  7. vm中的data就是Vue实例对象中的_data属性
<div class="box">
    <h1>你好, {{name}}</h1>
</div>
let vm = new Vue({
    el: '.box',
    data: {
        name: '小城故事'
    }
})
console.log(vm)

# 9. vue的事件绑定

  1. 使用v-on:xxx 或 @xxx 绑定事件, 其中xxx是事件名
  2. 事件的回调需要配置在methods对象中, 最终会在vm上
  3. methods中配置的函数, 不要用箭头函数, 否则this就不是vm
  4. methods中配置的函数, 都是被Vue所管理的函数, this指向是vm或组件实例对象
  5. @click='show' 和 @click='show($event)' 作用一致, 但后者可以传参
<div class="box">
    <h1>你好, {{name}}</h1>
    <button v-on:click="show">点击我啊</button>
    <!-- 简写 -->
    <button @click="show">点击我啊 (不带传参)</button>
    <button @click="show1($event, 666)">点击我啊 (带传参)</button>
</div>   
el: '.box',
    data: {
        name: '小城故事'
    },
    methods: {
        show(e) {
            console.log(e.target)
            console.log(this) // 指向vm实例对象
            alert('你好')
        },
        show1(event, number) {
            console.log(event, number)
            alert(`你好, ${number}`)
        }
    }
})

# 10. vue的事件修饰符

<div class="box">
    <h3>你好, {{name}}</h3>
    <!-- 1. 阻止事件默认行为(常用) -->
    <a href="https://baidu.com" @click.prevent="show">点我提示信息</a>
    <!-- 小技巧: 修饰符可以连续写入: 阻止默认行为和阻止冒泡 -->
    <a href="https://baidu.com" @click.prevent.stop="show">修饰符可以连续写入</a>
    <!-- 2. 阻止事件冒泡(常用) -->
    <div class="demo" @click="show">
        <button @click.stop="show">阻止事件冒泡</button>
    </div>
    <!-- 3. 事件只触发一次(常用) -->
    <button @click.once="show">点我只触发一次</button>
    <!-- 4. 使用事件的捕获模式 -->
    <div class="demo1" @click.capture="show1(1)">
        使用事件的捕获模式
        <div class="demo2" @click="show1(2)">
            使用事件的捕获模式
        </div>
    </div>
    <!-- 5. 只有e.target是当前操作的元素时才触发事件 -->
    <div class="demo" @click.self="show">
        <button @click="show">e.target阻止事件冒泡</button>
    </div>
    <!-- 6. 事件的默认行为立即执行, 无需等待事件回调完成 -->
    <ul @wheel.passive="show2">
        <li>1</li>
        <li>2</li>
    </ul>
</div>
new Vue({
    el: '.box',
    data: {
        name: '小城故事'
    },
    methods: {
        show(e) {
            // e.preventDefault() vue里这样写: prevent
            // e.stopPropagation() vue里这样写: stop
            alert('你好')
        },
        show1(msg) {
            console.log(msg)
        },
        show2() {
            for (let i = 0; i <= 100000; i++) {
                console.log(1)
            }
            console.log('累坏了')
        }
    }
})

# 11. vue的键盘事件

  1. Vue中常用的按键别名: enter、delete、esc、space、tab、up、down、left、right
  2. Vue未提供别名按键, 可使用按键原始的key值去绑定, 但注意短横线命名(caps-lock)
  3. CapsLock是个特殊事件, 要写成caps-lock才行
  4. 系统修饰键(用法特殊): ctrl、alt、shift、meta
  5. 配合keyup使用: 按下修饰键的同时, 按下其他键, 事件才会触发
  6. 配合keydown使用: 正常触发事件
  7. 可定制按键别名: Vue.config.keyCodes.自定义键名 = 键码
  8. 使用keyCode指定具体按键(不推荐)
<div class="box">
    <h2>你好, {{name}}</h2>
    <input type="text" placeholder="按下回车提示" @keyup.enter="show">
    <!-- Ctrl虽然可配合其他键使用,但如果只能Ctrl+Y才能触发后面得.Y -->
    <input type="text" placeholder="按下回车提示" @keyup.ctrl.y="show">
</div>
new Vue({
    el: '.box',
    data: {
        name: '小城'
    },
    methods: {
        show(e) {
            // if (e.key !== 'Enter') return
            console.log(e.target.value)
        }
    }
})

# 12. 姓名案例-插值语法实现

<div class="box">
    : <input type="text" v-model="name"> <br><br>
    : <input type="text" v-model="names"> <br><br>
    全名: <span>{{name.slice(0, 3)}} - {{names}}</span>
</div>
new Vue({
    el: '.box',
    data: {
        name: '张',
        names: '三'
    }
})

# 13. 姓名案例-methods属性实现

  1. data数据发生变化, vue模板会重新解析一遍
  2. vue模板里如果调用函数了, 函数也会被插值语法调用
<div class="box">
    : <input type="text" v-model="name"> <br><br>
    : <input type="text" v-model="names"> <br><br>
    全名: <span>{{fn()}}</span>
</div>
new Vue({
    el: '.box',
    data: {
        name: '张',
        names: '三'
    },
    methods: {
        fn() {
            console.log('fn函数被调用了')
            return this.name + '-' + this.names
        }
    }
})

# 14. 姓名案例-计算属性实现

  1. 定义: 要用的属性不存在, 要通过已有属性计算得来
  2. 原理: 底层借助Object.defineProperty方法提供getter/setter
  3. get函数什么时候执行? (1) 初次读取时会执行一次 (2) 当依赖的数据发生改变会被再次调用
  4. 优势: 与methods实现相比: 内部有缓存机制, 效率更高, 调试方便
  5. 计算属性最终会出现在vm上, 直接读取使用即可
  6. 如果计算属性要被修改, 那必须要set函数去响应修改, 且set中要引起计算时依赖的数据发生改变
<div class="box">
    : <input type="text" v-model="name"> <br><br>
    : <input type="text" v-model="names"> <br><br>
    全名: <span>{{fullname}}</span> <br><br>
    全名: <span>{{fullname}}</span> <br><br>
    全名: <span>{{fullname}}</span>
</div>
let vm = new Vue({
    el: '.box',
    data: {
        name: '张',
        names: '三'
    },
    computed: {
        fullname: {
    // get作用: 当读取fullname时, get会被调用, 且返回值作为fullname的值
            get() {
                console.log('get被调用了')
                console.log(this) // 指向vm实例 vue把get的this指向了vm
                return this.name + '-' + this.names
            },
    // set什么时候调用: 当fullname被修改时调用
            set(value) {
                console.log(value)
                let arr = value.split('-')
                this.name = arr[0]
                this.names = arr[1]
            }
        }
    }
})
console.log(vm)

# 15. 姓名案例-计算属性简写实现

只有考虑读取, 不考虑修改才能用简写方式

<div class="box">
    : <input type="text" v-model="name"> <br><br>
    : <input type="text" v-model="names"> <br><br>
    全名: <span>{{fullname}}</span>
</div>
let vm = new Vue({
    el: '.box',
    data: {
        name: '张',
        names: '三'
    },
    computed: {
        // 1. 简写
        fullname() {
            console.log('get被调用了')
            return this.name + '-' + this.names
        }
        // 2. 完整写法
        // fullname: {
        //     get() {
        //         console.log('get被调用了')
        //         return this.name + '-' + this.names
        //     }
        // }
    }
})