• 第一章、Vue3高级


    一、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>
    
  • 相关阅读:
    Fiddler:在PC和移动设备上抓取HTTPS数据包
    WP8:在WinRT组件(C++)中调用C#类库的解决方案
    Windows Phone 7.5/8.0/8.1 WebBrowser 渲染异常的原因及解决方法
    Xamarin.iOS开发初体验
    WP8:Unity3D之间的值传递
    【Win10 UWP】QQ SDK(一):SDK基本使用方法
    WP8.1:关于屏幕尺寸和分辨率的那些事儿
    .NET vs2010中使用IrisSkin2.dll轻松实现winForm窗体换肤功能
    记事本的编辑问题,如何删除记事本中某一列不需要的值
    C#制作RDLC报表
  • 原文地址:https://www.cnblogs.com/linding/p/16070114.html
Copyright © 2020-2023  润新知