一路繁花似锦绣前程
失败的越多,成功才越有价值

导航

 

一、Vue3初体验

1、初体验案例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue3+ts</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app"></div>
<script>
    /**
     * 1、Vue的引入方式
     *     - CDN
     *     - 下载到本地
     *     - npm
     *     - Vue CLI
     * 2、编程范式
     *     - 命令式(原生:how to do)
     *     - 声明式(Vue:what to do,how由框架完成)
     * 3、模型
     *     - MVC:Model-View-Controller
     *     - MVVM:Model-View-ViewModel
     */
    Vue.createApp({
        template: `
            <div>{{message}}</div>
            <button @click="btnClick">按钮</button>
        `,
        // data:Vue3必须是函数,Vue2可以是对象(会报警告)
        data() {
            return {
                message: "黄婷婷"
            }
        },
        methods: {
            btnClick() {
                this.message = '孟美岐'
            }
        }
    }).mount("#app")
</script>
</body>
</html>
2、template写法
<div id="app"></div>

<!--写法一-->
<script type="x-template" id="comp">
    <div>{{message}}</div>
    <button @click="btnClick">按钮</button>
</script>
<!--写法二-->
<template id="comp">
    <div>{{message}}</div>
    <button @click="btnClick">按钮</button>
</template>

<script>
    Vue.createApp({
        template: "#comp",
        data() {
            return {
                message: "黄婷婷"
            }
        },
        methods: {
            btnClick() {
                this.message = '孟美岐'
            }
        }
    }).mount("#app")
</script>
3、调试vue3源码
* 下载Vue3源码:
    - https://codeload.github.com/vuejs/core/zip/refs/tags/v3.0.11
    - 初始化git版本库
* Vue3.0.11使用yarn包管理工具:
    - npm install yarn -g
    - yarn install
* package.json脚本修改:
    - "dev": "node scripts/dev.js --sourcemap"
    - yarn dev
* html引入:
    - packages/vue/dist/vue.global.js

二、Vue3基本指令

1、methods方法绑定this
* 问题一:为什么不能使用箭头函数?
    - 箭头函数不绑定this,this会指向window
* 问题二:this的指向是什么?
    - this指向组件实例的代理对象
      (packages/runtime-core/src/componentOptions.ts:627)
2、webstorm设置文件模板
* file -> settings -> file and code templates
    - name:模板名
    - extension:扩展名
    - enable live templates:启用实时模板(右击创建文件时显示文件模板)
3、双大括号语法
<template id="comp">
    <!--
    * 双大括号语法(mustache语法):
        - data函数返回的对象的属性
        - 表达式
        - methods中的方法(需手动调用)
        - computed中的方法(无需手动调用)
        - 三元运算符
    * mustache语法错误用法:
        - 赋值语句
        - if语句
      -->
    <div>{{message}}</div>
</template>
4、v-once
<template id="comp">
    <!--  元素或者组件以及其所有的子元素只渲染一次  -->
    <div v-once>{{message}}</div>
</template>
5、v-text
<template id="comp">
    <div>{{message}}</div>
    <!--  等价  -->
    <div v-text="message"></div>
</template>
6、v-html
<template id="comp">
    <!--  解析成html  -->
    <div v-html="message"></div>
</template>
7、v-pre
<template id="comp">
    <!--  元素和它的子元素不解析  -->
    <div v-pre>{{message}}</div>
</template>
8、v-cloak
<template id="comp">
    <!--  解析之前会有v-cloak属性,解析之后会去掉v-cloak属性,
          常和css规则如[v-clock]{display:none}一起用  -->
    <div v-cloak>{{message}}</div>
</template>

三、v-bind和v-on

1、v-bind基本使用
<!--  vue2 template标签只能有一个根元素
      vue3 template标签可以有多个根元素  -->
<template id="comp">
    <!--  对属性值进行动态绑定  -->
    <img v-bind:src="imgSrc">
    <!--  v-bind语法糖:  -->
    <a :href="link"></a>
</template>
2、v-bind绑定class
<template id="comp">
    <!--  对象语法:{active:boolean}  -->
    <div :class="{static:true,active:true}">黄婷婷</div>
    <!--  数组语法:['static',{active:boolean}]  -->
    <div :class="['static',{active:true}]">孟美岐</div>
    <!--  动态绑定的class可与普通的class属性共存  -->
    <div class="static" :class="{active:true}">姜贞羽</div>
</template>
3、v-bind绑定style
<template id="comp">
    <!--  对象语法(对象属性有驼峰和短横线两种命名方式)  -->
    <div :style="{fontWeight:900,'font-size':'18px'}">黄婷婷</div>
    <!--  数组语法  -->
    <div :style="[{fontWeight:900},{'font-size':'18px'}]">孟美岐</div>
    <!--  动态绑定与普通属性共存  -->
    <div style="font-weight: 900" :style="{fontSize:'18px'}">姜贞羽</div>
</template>
4、v-bind动态属性名
<template id="comp">
    <div :[name]="value"></div>
</template>

<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                name: "attr",
                value: "黄婷婷"
            }
        }
    }).mount('#app')
</script>
5、v-bind值为对象
<template id="comp">
    <div v-bind="obj"></div>
    <div :="obj"></div>
</template>

<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                obj: {
                    attr: "孟美岐",
                }
            }
        }
    }).mount('#app')
</script>
6、v-on基本使用
<template id="comp">
    <!--  完整语法:v-on:事件="methods的方法"  -->
    <button v-on:click="clickHandler">按钮</button>
    <!--  语法糖  -->
    <button @click="clickHandler">按钮</button>
    <!--  绑定一个表达式:inline statement  -->
    <button @click="person='张婧仪'">按钮</button>
    <!--  绑定一个对象  -->
    <button v-on="{click:clickHandler,mousemove:mousemoveHandler}">按钮</button>
    <button @="{click:clickHandler,mousemove:mousemoveHandler}">按钮</button>
</template>

<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                person: "黄婷婷"
            }
        },
        methods: {
            clickHandler() {
                console.log('孟美岐')
            },
            mousemoveHandler() {
                console.log('姜贞羽')
            },
        }
    }).mount('#app')
</script>
7、v-on参数传递
<template id="comp">
    <!--  不传参时,默认传入Event对象  -->
    <button @click="clickHandler">按钮</button>
    <!--  传参时,需通过$event传入Event对象  -->
    <button @click="clickHandler($event,'孟美岐')">按钮</button>
</template>
8、v-on修饰符
<template id="comp">
    <!--
        .stop:调用event.stopPropagation()
        .prevent:调用event.preventDefault()
        .{keyAlias}:仅当事件是从特定键触发时才触发回调
        .capture:添加事件侦听器时使用capture模式
        .self:只当事件是从侦听器绑定的元素本身触发时才触发回调
        .once:只触发一次回调
        .left:只当点击鼠标左键时触发
        .right:只当点击鼠标右键时触发
        .middle:只当点击鼠标中键时触发
        .passive:{passive:true}模式添加侦听器
      -->
    <div @click="divClick">
        <button @click.stop="btnClick">按钮</button>
    </div>
    <input type="text" @keyup.enter="iptHandler">
</template>

四、条件渲染

1、v-if
<template id="comp">
    <div v-if="true">黄婷婷</div>
    <div v-else-if="true">孟美岐</div>
    <div v-else>姜贞羽</div>
</template>
2、v-if和template结合使用
<template id="comp">
    <!--  类似小程序的block,template元素不会被渲染出来  -->
    <template v-if="true">
        <div>黄婷婷</div>
    </template>
</template>
3、v-if和v-show区别
<template id="comp">
    <!--
    * 用法区别:
        - v-show不支持template
        - v-show不可以和v-else一起使用
    * 本质区别:
        - v-show通过css的display控制显示隐藏
        - v-if为false时DOM不渲染
    * 如何选择:
        - 显示隐藏需要频繁切换则使用v-show
      -->
    <div v-if="false">黄婷婷</div>
    <div v-show="false">孟美岐</div>
</template>

五、列表渲染

1、v-for
<template id="comp">
    <!--  遍历数组  -->
    <div v-for="(item,index) in arr">{{item}}-{{index}}</div>
    <!--  遍历对象  -->
    <div v-for="(value,key,index) in obj">{{value}}-{{key}}-{{index}}</div>
    <!--  遍历数字  -->
    <div v-for="(num,index) in 3">{{num}}-{{index}}</div>
    <!--  in可用of替代  -->
    <div v-for="(num,index) of 3">{{num}}-{{index}}</div>
</template>

<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                arr: ['黄婷婷', "孟美岐", "鞠婧祎"],
                obj: {
                    name: "姜贞羽",
                    age: 18,
                    gender: "女"
                }
            }
        },
        methods: {}
    }).mount('#app')
</script>
2、v-for和template结合使用
<template id="comp">
    <template v-for="(item,index) in arr">
        <div>{{item}}-{{index}}</div>
    </template>
</template>
3、数组检测
<template id="comp">
    <!--
    * 会改变原数组的方法,Vue对这些方法进行了包裹,它们会触发视图的更新
        - push()
        - pop()
        - shift()
        - unshift()
        - splice()
        - sort()
        - reverse()
    * 不会改变原数组的方法,可以通过重新给数组赋值的方式触发视图更新
        - filter()
        - concat()
        - slice()
      -->
    <div v-for="(item,index) in arr">{{item}}-{{index}}</div>
</template>
4、v-for中的key
<template id="comp">
    <!--
    一、key属性的作用(packages/runtime-core/src/renderer.ts:1606)
        * 主要用做Vue的虚拟DOM算法的提示,以在比对新旧节点组时辨识VNodes。
        * 如果不使用key,Vue会使用一种算法来最小化元素的移动并且尽可能尝试就地修改/复用相同类型元素。
            1、获取旧VDOM和新VDOM的长度,取小的VDOM的长度用以遍历
            2、从0位置开始依次(patch(n1,n2))比较,如果VNode(n1、n2)对比有差异则进行更新
            3、旧VDOM长度大于新VDOM长度,则移除剩余的VNode,反之则创建新的VNode
        * 而使用key时,它会基于key的顺序变化重新排列元素,并且那些使用了已经不存在的key的元素将会被移除/销毁。
            1、新旧VDOM同时从头部开始遍历(patch),判断VNode的type和key是否有差异,不同则直接跳出循环
            2、新旧VDOM同时从尾部开始遍历(patch),判断VNode的type和key是否有差异,不同则直接跳出循环
            3、如果旧VDOM遍历完,新VDOM还有VNode没有遍历,则新增节点(patch(n1,n2),当n1为null时表示是挂载)
            4、如果新VDOM遍历完,旧VDOM还有VNode没有遍历,则移除节点(卸载)
            5、如果新旧VDOM都没有遍历完(是未知序列):
                - 遍历新VDOM剩余的VNode,剩余VNode的key作为键,索引作为值,存入Map
                - 遍历旧VDOM剩余的VNode,根据Map判断,哪些VNode可以复用,
                  并在长度是新VDOM剩余VNode长度的数组中做标记,哪些需要卸载
                - 新VDOM剩余的VNode可根据数组中的复用标记,决定哪些VNode要挂载,
                  哪些需要移动(使用最长递增子序列算法,以最小成本移动)
    二、认识VNode
        * 全称Virtual Node,也就是虚拟节点
        * VNode本质是一个JavaScript对象
            Node:<div class="title" style="color:red">黄婷婷</div>
            VNode:{
                       type:"div",
                       props:{
                           class:"title",
                           style:{
                               color:"red"
                           }
                       },
                       children:["黄婷婷"]
                   }
    三、虚拟DOM(VDOM):多个VNode形成的树结构
      -->
    <div v-for="item in arr" :key="item.id">{{item.name}}</div>
</template>

<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                arr: [{
                    id: "18",
                    name: "黄婷婷"
                }, {
                    id: "19",
                    name: "孟美岐"
                }, {
                    id: "20",
                    name: "鞠婧祎"
                }]
            }
        }
    }).mount('#app')
</script>

六、Vue3的Options-API

1、计算属性computed
<template id="comp">
    <div>{{person+"婷婷"}}</div>
    <div>{{personCompute}}</div>
    <div>{{personMethod()}}</div>
</template>

<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                person: "黄"
            }
        },
        /**
         * 一:使用计算属性的好处
         *     - 对比插值语法:计算属性可对模板中的复杂逻辑进行封装
         *     - 对比methods:计算属性有缓存
         * 二:计算属性中所有的getter和setter的this上下文自动地绑定为组件实例
         * 三:计算属性的缓存:计算属性会基于它们的依赖关系进行缓存。
         *    数据不发生变化,计算属性不需要重新计算。
         *    如果依赖的数据发生变化,计算属性会重新进行计算。
         */
        computed: {
            personCompute() {
                return this.person + "婷婷"
            }
        },
        methods: {
            personMethod() {
                return this.person + "婷婷"
            }
        }
    }).mount('#app')
</script>
2、计算属性的getter和setter
<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                person: "黄婷婷"
            }
        },
        computed: {
            /**
             * (packages/runtime-core/src/componentOptions.ts:668)
             * 1、personCompute(){return ""}是getter的语法糖
             * 2、personCompute=""会调用setter方法
             */
            personCompute: {
                get() {
                    return this.person
                },
                set(val) {
                    this.person = val
                }
            }
        }
    }).mount('#app')
</script>
3、侦听器watch
<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                person: "黄婷婷"
            }
        },
        watch: {
            /**
             * 侦听器常用于数据变化时,进行一些逻辑的处理(执行函数、网络请求)
             * person:可以是data option,也可以是computed option
             * newValue:新的值
             * oldValue:旧的值
             */
            person(newValue, oldValue) {
                console.log(newValue, oldValue)
            }
        }
    }).mount('#app')
</script>
4、watch的配置选项
<template id="comp">
    <!--  v-model是input事件实现双向绑定
          v-model.lazy是change事件实现双向绑定  -->
    <input type="text" v-model.lazy="person.name">
    <hr>
    <input type="text" v-model="address">
    <hr>
    <input type="text" v-model="friend">
</template>

<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                person: {name: "黄婷婷", age: 18},
                address: "",
                friend: ""
            }
        },
        watch: {
            person: {
                handler(newValue, oldValue) {
                    // 修改对象本身:false,修改的是对象内部值时:true
                    console.log(newValue === oldValue)// true
                },
                deep: true,// 侦听对象内部值的变化,默认false
                immediate: true// 立即以当前值触发回调,默认false
            },
            address: 'addressInput',// 对应methods option
            friend: [// 会被逐一调用
                "friendInput",
                function (newValue, oldValue) {
                    console.log(newValue, oldValue)
                },
                {
                    handler(newValue, oldValue) {
                        console.log(newValue, oldValue)
                    },
                    deep: false,
                    immediate: false
                }
            ]
        },
        methods: {
            addressInput(newValue, oldValue) {
                console.log(newValue, oldValue)
            },
            friendInput(newValue, oldValue) {
                console.log(newValue, oldValue)
            }
        }
    }).mount('#app')
</script>
5、watch其他方式
<template id="comp">
    <input type="text" v-model="person.name">
    <hr>
    <input type="text" v-model="address">
</template>

<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                person: {name: "黄婷婷", age: 18},
                address: "",
                firends: [{name: "孟美岐"}]
            }
        },
        watch: {
            // 侦听对象的属性
            "person.name": function (newValue, oldValue) {
                console.log(newValue, oldValue)
            },
            // 侦听数组的元素
            "firends.0.name": function (newValue, oldValue) {
                console.log(newValue, oldValue)
            }
        },
        created() {
            this.firends[0].name = "姜贞羽"

            /**
             * 1、"person.age"不能侦听
             * 2、参数2可以是箭头函数
             * 3、返回值是函数,调用该函数可取消侦听
             */
            const unwatch = this.$watch("address", (newValue, oldValue) => {
                console.log(newValue, oldValue)
            }, {
                deep: false,
                immediate: false
            })
            // unwatch()
        }
    }).mount('#app')
</script>

七、Vue3的表单

1、v-model的基本使用
<!--  <input type="text" :value="searchText" @input="searchText=$event.target.value">  -->
<!--  等价(packages/runtime-dom/src/directives/vModel.ts:53)  -->
<input type="text" v-model="searchText">
2、v-model绑定其他表单
<template id="comp">
    <!--  1、textarea  -->
    <label>自我介绍:<textarea rows="3" cols="20" v-model="intro"></textarea></label>
    <h2>intro:{{intro}}</h2>

    <!--  2、checkbox  -->
    <!--  2.1、单选框  -->
    <label><input type="checkbox" v-model="isAgree">统一协议</label>
    <h2>isAgree:{{isAgree}}</h2>
    <!--  2.2、多选框  -->
    <span>你的爱好:</span>
    <label><input type="checkbox" value="basketball" v-model="hobbies">篮球</label>
    <label><input type="checkbox" value="football" v-model="hobbies">足球</label>
    <label><input type="checkbox" value="tennis" v-model="hobbies">网球</label>
    <h2>hobbies:{{hobbies}}</h2>

    <!--  3、radio  -->
    <span>你的爱好:</span>
    <label><input type="radio" value="male" v-model="gender">男</label>
    <label><input type="radio" value="female" v-model="gender">女</label>
    <h2>gender:{{gender}}</h2>

    <!--  4、select  -->
    <select v-model="fruit" multiple size="2">
        <option value="apple">苹果</option>
        <option value="orange">橘子</option>
        <option value="banana">香蕉</option>
    </select>
    <h2>fruit:{{fruit}}</h2>
</template>

<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                intro: "孟美岐",
                isAgree: false,
                hobbies: [],
                gender: "",
                fruit: []
            }
        }
    }).mount('#app')
</script>
3、v-model的修饰符
<template id="comp">
    <!--  1、lazy修饰符:触发的是onchange事件  -->
    <!--  <input type="text" v-model.lazy="message">  -->
    <!--  <div>{{message}}</div>  -->

    <!--  2、number修饰符
              - 数字字符串与number类型进行逻辑判断时(不考虑===),
                数字字符串会隐式转化成number类型
              - 值只取开头连续的数字,为number类型  -->
    <!--  <input type="text" v-model.number="message">  -->
    <!--  <div>{{message}} | {{typeof message}}</div>  -->

    <!--  3、trim修饰符:两边去除空格  -->
    <input type="text" v-model.trim="message">
</template>

<script>
    Vue.createApp({
        template: '#comp',
        data() {
            return {
                message: ""
            }
        },
        watch: {
            message() {
                console.log(this.message)
            }
        }
    }).mount('#app')
</script>

八、Vue3的组件化(一)

1、注册全局组件
<template id="my-app">
    <component-a></component-a>
    <component-a></component-a>
</template>

<template id="component-a">
    <button @click="btnClick">{{btnText}}</button>
    <hr>
</template>

<script>
    const app = Vue.createApp({template: '#my-app'})

    // 注册全局组件:全局组件可在任何组件模板中使用。可注册多个全局组件
    app.component("component-a", {
        template: '#component-a',
        data() {
            return {
                btnText: "按钮"
            }
        },
        methods: {
            btnClick() {
                console.log("点击按钮")
            }
        }
    })

    app.mount('#app')
</script>
2、组件命名方式
* 短横线分割符(kebab-case):使用时<my-component-name>
* 驼峰标识符(PascalCase):使用时<my-component-name>或<MyComponentName>。
  直接在DOM中使用时,只有<my-component-name>有效
3、注册局部组件
<template id="my-app">
    <component-a></component-a>
    <component-a></component-a>
</template>

<template id="component-a">
    <button @click="btnClick">{{btnText}}</button>
    <hr>
</template>

<script>
    const app = Vue.createApp({
        template: '#my-app',
        // 注册局部组件:只能在#my-app组件模板中使用。
        // key:组件名;value:组件对象
        components: {
            ComponentA: {
                template: '#component-a',
                data() {
                    return {
                        btnText: "按钮"
                    }
                },
                methods: {
                    btnClick() {
                        console.log("点击按钮")
                    }
                }
            }
        }
    })

    app.mount('#app')
</script>
posted on 2022-03-29 08:52  一路繁花似锦绣前程  阅读(115)  评论(0编辑  收藏  举报