生活札记

Vue3学习笔记 - 基础(二)

copylian    1 评论    14005 浏览    2022.06.01

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

文档:https://cn.vuejs.org/guide/introduction.html

教程:https://www.bilibili.com/video/BV1QA4y1d7xf


1、Vue-Router:基于路由和组件,将路径和组件组合成一张映射表

    官网:https://router.vuejs.org/zh/introduction.html

    安装:npm install vue-router@4

    代码实例:


            1)、main.js:

            

            import { createApp } from 'vue'

            import App from './App.vue'

            

            // import './assets/main.css'

            

            // createApp(App).mount('#app')

            

            // 导入路由:默认自动加载index.js

            import router from "./router/";

            

            // 创建实例

            const app = createApp(App)

            

            // 使用路由

            app.use(router)

            

            // 挂载

            app.mount('#app')

            

            2)、index.js:

            

            // 导入vue-router

            import createRouter,createWebHashHistory,createWebHistory } from "vue-router"

            

            // 1. 定义路由组件

            // import Home from '../views/Home.vue'

            // import About from '../views/About.vue'

            // import User from '../views/User.vue'

            // import NoFound from '../views/NoFound.vue'

            // import News from '../views/News.vue'

            // import Parent from '../views/Parent.vue'

            // import SonOne from '../views/SonOne.vue'

            // import SonTwo from '../views/SonTwo.vue'

            // import Page from '../views/Page.vue'

            // import ShopMain from '../views/ShopMain.vue'

            // import ShopHeader from '../views/ShopHeader.vue'

            // import ShopFooter from '../views/ShopFooter.vue'

            

            // 路由懒加载

            const Home () => import('../views/Home.vue')

            const User  () => import('../views/User.vue')

            const NoFound  () => import('../views/NoFound.vue')

            const News  () => import('../views/News.vue')

            const Parent  () => import('../views/Parent.vue')

            const SonOne  () => import('../views/SonOne.vue')

            const SonTwo  () => import('../views/SonTwo.vue')

            const Page  () => import('../views/Page.vue')

            const ShopMain  () => import('../views/ShopMain.vue')

            const ShopHeader  () => import('../views/ShopHeader.vue')

            const ShopFooter  () => import('../views/ShopFooter.vue')

            

            // 2. 定义一些路由

            // 每个路由都需要映射到一个组件

            const routes = [

                // 重定向

                {

                    path: '/',

                    // redirect: "/home" // 重定向

                    redirect: {name: 'home'} // 命名重定向

                    // redirect:(to) => { // 方法重定向

                    //     console.log(to)

                    //     return {name: 'home'}

                    // }

                },

                {

                    name:"home",

                    path: '/home', 

                    component: Home

                },

                {

                    path: '/about',

                    // component: About,

                    component() => import("../views/About.vue"), // 懒加载

            

                    // 单个路由守卫

                    beforeEnter: (tofromnext) => {

                        // console.log(to)

                        // console.log(from)

                        if (1 == 1){

                            next() // 通行证,有这个才会往下执行

                        }

                    }

                },

                {

                    name:"user",

                    path:'/user/:id',

                    component: User,

                    props:true // 路由组件传参,通过url如:/abc/123 获取 参数 123

                },

                {

                    // 正则:+:一个以上、*:任意、?:一个或者没有

                    // path:'/news/:id(\\d+)',

                    path:'/news/:id*',

                    // path:'/news/:id(\\d?)',

                    component: News

                },

                // 嵌套路由

                {

                    path:'/parent',

                    component: Parent,

                    children:[

                        {

                            path:"sonone",

                            component:SonOne

                        },

                        {

                            path:"sontwo",

                            component:SonTwo

                        }

                    ]

                },

                // 别名

                {

                    name:"page",

                    alias:"/page1", // 别名

                    // alias:["/page2","/page3"], // 别名

                    path:'/page',

                    component: Page

                },

                // 多级视图路由,一个路由里有多个组件

                {

                    name:'shop',

                    path:"/shop",

                    components:{

                        default: ShopMain, // 多级默认组件

                        ShopHeader, // 名称与router-view 的 name 属性一致

                        ShopFooter

                    },

                    props:{default:true,ShopHeader:true,ShopFooter:false} // 路由组件传参

                },

                // 404 

                {

                    path:'/:pathMatch(.*)',

                    // path:'/:path(.*)',

                    component: NoFound

                }

            ]

            

            // 3. 创建路由实例并传递 `routes` 配置

            const router createRouter({

                  // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。

                  // historycreateWebHashHistory(), // Hash 模式,内部传递的实际 URL 之前使用了一个哈希字符(#)

                  historycreateWebHistory(), // HTML5 模式,如果没有适当的服务器配置,用户在浏览器中直接访问,就会得到一个 404 错误

                  routes// `routes: routes` 的缩写

            })

            

            // 全局路由守卫,全局路由中间件

            router.beforeEach((tofromnext) => {

                // console.log(to)

                // console.log(from)

            

                next() // 通行证

            })

            

            // 渲染导出router

            export default router

            

            3)、App.vue:

            

            <script>

            // 每个组件都是独立的实例

            

            // 申明式渲染

            export default {

              data(){

                return {

                  title:"Hello App!"

                }

              }

            }

            </script>

            

            <template>

              <div>

                <h1>{{ title }}</h1>

                <p>

                  <!--使用 router-link 组件进行导航 -->

                  <!--通过传递 `to` 来指定链接 -->

                  <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->

                  <router-link to="/">Home</router-link>

                  <br>

                  <router-link to="/about">About</router-link>

                  <br>

                  <router-link to="/user/1">用户</router-link>

                  <br>

                  <router-link :to="{name:'user',params:{id: 2 }}">用户1</router-link>

                  <br>

                  <router-link to="/news/1">News</router-link>

                  <br>

                  <router-link to="/parent">Parent</router-link>

                  <br>

                  <router-link to="/page">Page</router-link>

                  <br>

                  <router-link to="/shop">Shop</router-link>

                  <br>

                </p>

                <!-- 路由出口 -->

                <!-- 路由匹配到的组件将渲染在这里 -->

                <router-view name="ShopHeader"></router-view>

                <router-view></router-view>

                <router-view name="ShopFooter"></router-view>

              </div>

            </template>

            

            4)、About.vue:

            

            <template>

                <div>

                    <p>{{ title }}</p>

                </div>

            </template>

            

            <script>

            export default {

                data() {

                    return {

                        title: "About"

                    }

                },

            

                // 进入路由组件守卫

                beforeRouteEnter: (to, from, next) => { // 拿不到 this 实例里数据,只能通过 next() 方法获取

                    console.log('enter')

                    next((vm) => { // 通行

                        console.log(vm.title)

                    })

                },

            

                // 更新组件守卫

                beforeRouteUpdate: (to, from, next) => {

                    console.log('update')

                    next() // 通行

                },

            

                // 离开路由组件守卫

                beforeRouteLeave: (to, from, next) => {

                    console.log('leave')

                    next() // 通行

                }

            }

            </script>

            

            

            5)、Page.vue:

            

            <template>

                <div>

                    <p>{{ title }}</p>

                    <p><button type="button" @click="goHome">跳转到Home</button></p>

                </div>

            </template>

            

            <script>

            export default {

                data() {

                    return {

                        title: "Page"

                    }

                },

                methods:{

                    goHome(){

                        // 跳转

                        // this.$router.push("/")  // this.$router 是获取所有的路由,this.$route 是获取当前路由

                        // this.$router.push("/home")

                        // this.$router.push({path:"/"})

                        // this.$router.push({name:"home", replace:true})

            

                        // 带参数(params)跳转,pathinfo模式

                        // this.$router.push({ name: "user", params: {id:123}})

            

                        // 带参数(query)问号模式

                        // this.$router.push({ name: "home", query: { id: 123 } })

            

                        // 前进、后退

                        // this.$router.go(-1)

                        // this.$router.back()

                        // this.$router.forward()

                    }

                }

            }

            </script>

            

            6)、User.vue:

            

            <template>

                <div>

                    <p>用户</p>

                </div>

            </template>

            

            <!--<script>

                export default {

                    data(){

                        return {

                            title: "用户",

                        }

                    },

                    mounted() {

                        // console.log(this.$route.params.id)

                        console.log(this.id)

                    },

            

                    // 路由组件传参

                    props:{

                        id:{

                            type:String

                        } 

                    }

                }

            </script>-->

            

            <script setup>

            import useRoute } from "vue-router"

            

            // console.log(useRoute().params.id)

            

            // 属性

            const props = defineProps({

                id:{

                    type: String

                }

            })

            console.log(props.id)

            </script>



2、状态管理:数据集中在某个文件管理,提供统一数据;fetch() 、axios() 获取数据;vite 通过 proxy 实现解决跨域

           axios官网:http://www.axios-js.com/

           axios安装:npm install axios


        1)、index.js:

        

        // 状态集中管理:数据集中管理

        // provide/inject跨级通讯

        import {ref, reactive} from 'vue'

        

        // 配置公共数据

        const store = {

            state:reactive({

                msg:"Store",

                bannerList:[]

            }),

            updateStateMsg:function(val){

                this.state.msg = val

            },

            changeBanner:function(val){

                this.state.bannerList = val

            }

        }

        

        // 导出

        export default store

        

        2)、App.vue:

        

        <script>

        // 每个组件都是独立的实例

        import store from './store'

        import StoreNew from './views/Store.vue'

        

        // 申明式渲染

        export default {

          data(){

            return {

              title:"Hello App!"

            }

          },

          // 提供出去

          provide:{

            store

          },

          components:{

            StoreNew

          }

        }

        </script>

        

        <template>

          <div>

            <h1>{{ title }}</h1>

            <StoreNew></StoreNew>

          </div>

        </template>

        

        3)、store.vue:

        

        <template>

            <div>

                <p>{{ title }}</p>

                <p>Store:{{store.state.msg}}</p>

                <p><button type="button" @click="changeStateMsg">改变stateMsg</button></p>

                <p>

                <ul>

                    <li v-for="(item, index) in store.state.bannerList" :key="index">

                        <a :href="item.url" target="_blank"><img :src="item.imgurl" :alt="item.name"></a>

                    </li>

                </ul>

                </p>

            </div>

        </template>

        

        <script>

        import axios from 'axios'

        export default {

            data() {

                return {

                    title: "Store"

                }

            },

            inject:['store'], // 接受数据

            methods: {

                changeStateMsg(){

                    this.store.updateStateMsg("Store New")

                }

            },

            created() {

                // fetch():原生js的http请求数据,返回promise对象

                // fetch("http://127.0.0.1:5173/data.json").then((res) => {

                //     console.log(res)

                //     return res.json()

                // }).then((res) => {

                //     this.store.changeBanner(res.data.list)

                // })

        

                // axios:是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中

                // 官网:http://www.axios-js.com/

                // axios.get("http://127.0.0.1:5173/data.json").then((res) => {

                //     console.log(res.data.data)

                //     this.store.changeBanner(res.data.data.list)

                // }).catch((err) => {

                //     console.log(err)

                // });

        

                // 跨域请求:

                axios.get("/path/api/mmdb/movie/v3/list/hot.json?ct=%E6%B5%B7%E6%B2%A7&ci=964&channelId=4").then((res) => {

                    console.log(res.data.data)

                    this.store.changeBanner(res.data.data.list)

                }).catch((err) => {

                    console.log(err)

                });

            },

        }

        </script>


         4)、配置文件vite.config.js:


        import { fileURLToPath, URL } from 'node:url'

        import { defineConfig } from 'vite'

        import vue from '@vitejs/plugin-vue'

        

        // https://vitejs.dev/config/

        export default defineConfig({

          plugins: [vue()],

          resolve: {

            alias: {

              '@': fileURLToPath(new URL('./src', import.meta.url))

            }

          },

          server:{

            // vite的proxy代理服务,实现跨域

            proxy:{

              "/path":{

                target:"https://i.maoyan.com", // 替换服务器的地址

                changeOrigin:true// 开启代理,允许跨域

                rewrite: path => path.replace(/^\/path/,""), // 设置path重写

              }

            }

          }

        })


3、vue-cli:https://cli.vuejs.org/zh/guide/

Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,Vue CLI 现已处于维护模式现在官方推荐使用 create-vue 来创建基于 Vite 的新项目。

查看版本cli:vue --version

安装cli:npm install -g @vue/cli

升级cli:npm update -g @vue/cli

创建项目:vue create hello-world

启动项目:npm run serve


4、Vuex:https://vuex.vuejs.org/zh/index.html

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

vuex.png

安装:npm install vuex@next --save

代码实例:

        1)、main.js:

        

        import { createApp } from 'vue'

        import App from './App.vue'

        

        // 导入vuex

        import store from "./vuex/"

        

        // 创建实例

        const app = createApp(App)

        

        // 使用store,可以使用$store

        app.use(store)

        

        // 挂载

        app.mount('#app')

        

        2)、App.vue:

        

        <script>

        // 每个组件都是独立的实例

        import VuexNew from './views/Vuex.vue'

        

        // 申明式渲染

        export default {

          data(){

            return {

              title:"Hello App!"

            }

          },

          components:{

            VuexNew

          }

        }

        </script>

        

        <template>

          <div>

            <h1>{{ title }}</h1>

            <VuexNew></VuexNew>

            <!-- <Test></Test> -->

          </div>

        </template>

        

        3)、vuex/index.js:vuex仓库文件,存放公共的数据,以便其他组件调用

        

        // 导入vuex

        import { createStore } from "vuex";

        import axios from 'axios'

        import teststore from "./teststore";

        

        // 创建store实例

        const store = createStore({

            // 单一状态树,存储基本数据,state状态是与其他子模块独立的,没有进行合并

            state(){

                return {

                    msg:"Vuex",

                    count:0,

                    bannerList:[]

                }

            },

        

            // getters 可以认为是 store 的计算属性

            getters:{

                reverseMsg(state){

                    return state.msg.split("").reverse().join("")

                },

                msgLenth(state, getters){  // state表示当前实例的state对象,getters表示当前的getters对象

                    return getters.reverseMsg.length

                }

            },

        

            // mutations 改变状态,store.commit() 方法实现 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,实现状态的监听

            mutations:{

                increment(state, val){ // state 返回以上的state返回的对象,val参数可以是对象方式

                    state.count += val

                },

                changeBanner(state, val){

                    state.bannerList = val

                }

            },

        

            // actions 行为,提交的是 mutation,而不是直接变更状态,例如获取后端接口数据

            actions:{

                getData(context, val){ // context 是store的对象

                    console.log(val)

                    // 跨域请求:

                    axios.get("/path/api/mmdb/movie/v3/list/hot.json?ct=%E6%B5%B7%E6%B2%A7&ci=964&channelId=4").then((res) => {

                        console.log(res.data.data.hot)

                        // commit 调用 mutations 执行改变state

                        context.commit("changeBanner", res.data.data.hot)

                    }).catch((err) => {

                        console.log(err)

                    });

                }

            },

        

            //  为了避免臃肿,store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割,其中 state 不合并mutation、action、getter 都会与根store的合并在一起且不能重

            modules:{

                 //名称:模块

                // a:teststore,

                // b:test

                teststore

            }

        })

        

        // 导出

        export default store

        

        3)、vuex/teststore/index.js:子仓库模块文件,与根store的结构是一致的

        

        import axios from 'axios'

        // 这个是对象,必须要通过 createStore 创建,子模块不需要创建,只需要在根store的modules模块加载,createStore 由根store来创建

        const teststore = {

            // namespaced: true, // 命名空间

            // 单一状态树,存储基本数据,通过 $store.state.模块名称.属性

            state(){

                return {

                    username:"Test Store",

                    age:100

                }

            },

        

            // getters 可以认为是 store 的计算属性

            getters:{

                reverseUsername(state){

                    return state.username.split("").reverse().join("")

                },

                usernameLenth(state, getters, rootState){  // state表示当前实例的state对象,getters表示当前的getters对象, rootState 根store的对象

                    return getters.reverseUsername.length

                }

            },

        

            // mutations 改变状态,store.commit() 方法实现 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,实现状态的监听

            mutations:{

                incrementAge(state, val){ // state 返回以上的state返回的对象,val参数可以是对象方式

                    state.age += val

                }

            },

        

            // actions 行为,提交的是 mutation,而不是直接变更状态,例如获取后端接口数据

            actions:{

                getData2(context, val){ // context 是store的对象

                    console.log(val)

                    // 跨域请求:

                    axios.get("/path/api/mmdb/movie/v3/list/hot.json?ct=%E6%B5%B7%E6%B2%A7&ci=964&channelId=4").then((res) => {

                        console.log(res.data.data.hot)

                        // commit 调用 mutations 执行改变state

                        context.commit("changeBanner", res.data.data.hot)

                    }).catch((err) => {

                        console.log(err)

                    });

                }

            },

        

            //  为了避免臃肿,store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割,其中 state 不合并,mutation、action、getter 都会与根store的合并在一起

            modules:{

                

            }

        }

        

        // 导出

        export default teststore

        

        

        5)、Vuex.vue:组件

        

        <template>

            <div>

                <p>{{title}}</p>

                <p>Count:{{ $store.state.count }}</p>

                <p><button type="button" @click="changeCount">改变count++</button></p>

                <p>Getters计算属性:{{ $store.getters.reverseMsg }}</p>

                <p>Getters计算属性长度:{{ $store.getters.msgLenth }}</p>

                <h2>Actions返回接口数据:</h2>

                <p>

                <ul>

                    <li v-for="(item, index) in $store.state.bannerList" :key="index">

                        {{ index }}

                        <a :href="item.videourl" target="_blank">{{item.videoName}}</a>

                        <br>

                        <img :src="item.img" :alt="item.videoName" width="100">

                    </li>

                </ul>

                </p>

                <p>子模块store Username:{{ $store.state.teststore.username }}</p>

                <p>子模块store Age:{{ $store.state.teststore.age }}</p>

                <p><button type="button" @click="changeAge">改变子模块store count++</button></p>

                <p>子模块storeGetters计算属性:{{ $store.getters.reverseUsername }}</p>

                <p>子模块storeGetters计算属性长度:{{ $store.getters.usernameLenth }}</p>

        

                <!-- 开启命名空间 -->

                <p>子模块storeGetters计算属性长度:{{ $store.getters["teststore/usernameLenth"] }}</p>

        

                <!-- 辅助函数 -->

                <p>辅助函数username:{{ username }}</p>

                <p>辅助函数age:{{ age }}</p>

                <p>辅助函数reverseUsername:{{ reverseUsername }}</p>

                <p>辅助函数usernameLenth:{{ usernameLenth }}</p>

            </div>

        </template>

        

        <script>

            import {mapState,mapGetters,mapActions,mapMutations} from "vuex"

        

            export default {

                data(){

                    return {

                        title:"Vuex"

                    }

                },

                created() {

                    // console.log(this.$store.state.count)

                },

                mounted() {

                    // 组件转派发事件到store的actions

                    this.$store.dispatch("getData","aaa")

                    // this.getData("aaa") // 解构出来之后直接调用方法

                    // this.getData2('bbb')

                },

                computed: {

                    // ...mapState("teststore", {username: "username", age: state => state.age }), // 命名空间解构state状态

                    // ...mapGetters("teststore", ["reverseUsername", "usernameLenth"]) // 命名空间解构getters

        

                    ...mapState(["msg"]), // 解构state状态

                    ...mapState({ username: state => state.teststore.username, age: state => state.teststore.age }), // 解构子模块state状态

                    ...mapGetters(["reverseUsername", "usernameLenth"]) // 解构getters

                },

                methods: {

                    changeCount(){

                        // this.$store.state.count++ // 直接修改不建议

                        // this.$store.commit("increment", 5); // 通过store.commit来提交给对应的函数

                        this.increment(5); // 解构之后直接执行函数

                        // this.$store.commit({ // 对象方式必须保证参数也是对象方式

                        //     type:"increment", 

                        //     val:5

                        // })

                    },

                    changeAge() {

                        // this.$store.state.count++ // 直接修改不建议

                        this.$store.commit("incrementAge", 5); // 通过store.commit来提交给对应的函数

                        // this.$store.commit("teststore/incrementAge", 5); // 开启namespace模式,通过store.commit来提交给对应的函数

                        // this.$store.commit({ // 对象方式必须保证参数也是对象方式

                        //     type:"increment", 

                        //     val:5

                        // })

                    },

                    // ...mapActions("teststore", { getData2: actions => actions.getData2 }), // 命名空间解构actions

                    // ...mapMutations("teststore", { incrementAge: "incrementAge" }) // 命名空间解构mutations

        

                    ...mapActions({ getData: "getData", getData2: "getData2" }), // 解构actions

                    ...mapMutations({ incrementAge: "incrementAge", increment:"increment" }) // 解构mutations

                }

            }

        </script>


5、其他:

1)、v-for 与 v-if 不建议在一起使用,v-for 优先级高于 v-if,如果将两者放在一起,会执行v-for循环列表,v-if 去判断造成性能浪费,建议用计算属性解决

        <template>

            <div>

                <ul>

                    <li v-for="item,index in listArr" :key="index">

                        {{item.name}} - {{item.name}}

                    </li>

                </ul>

            </div>

        </template>

        

        <script>

        export default {

            data() {

                return {

                    list:[

                        {

                            id:1,

                            name:"PHP",

                            isShow:true,

                        },

                        {

                            id: 1,

                            name: "GO",

                            isShow: true,

                        },

                        {

                            id: 1,

                            name: "JAVA",

                            isShow: false,

                        }

                    ]

                }

            },

            computed: {

                listArr(){

                    return this.list.filter(item => {

                        return item.isShow

                    })

                }

            }

        }

        </script>


2)、自定义指令:创建自己想要的指令,使用必须以v-为前缀,参考:https://www.jb51.net/article/257259.htm

        <template>

            <div>

                <!-- 自定义指令 -->

                <p><input type="text" name="aaa">非自定义指令</p>

                <p><input type="text" name="bbb" v-focus>自定义指令</p>

            </div>

        </template>

        

        <script>

        export default {

            data() {

                return {

                }

            },

        

            directives: { // 自定义指令

                focus: { // v-focus

                    // el:指令绑定到的元素。这可以用于直接操作 DOM。

                    // binding:一个对象,包含以下 property:

                    // value:传递给指令的值。例如在 v- my - directive=“1 + 1” 中,值是 2。

                            // oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。

                            // arg:传递给指令的参数(如果有的话)。例如在 v- my - directive: foo 中,参数是 “foo”。

                            // modifiers:一个包含修饰符的对象(如果有的话)。例如在 v- my - directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。

                            // instance:使用该指令的组件实例。

                            // dir:指令的定义对象。

                    // vnode:代表绑定元素的底层 VNode。

                    // prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。

                    mounted: (el, binding, vnode, prevNode) => el.focus()

                }

            }

        }

        </script>

        

        <script setup>

        // setup 模式指令 v-focus

        // 注:在<script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令,vFocus 即可以在模板中以 v-focus 的形式使用

        const vFocus = {

              mounted: (el) => el.focus()

        }

        </script>



        // 全局的自定义指令

        app.directive('focus', {

            mounted: (el) => el.focus()

        })


3)、$nextTick:回调函数延迟,在下一次dom更新之后调用

        <template>

            <div>

                <!-- $nextTick -->

                <p id="msg" ref="msg">消息1:{{ msg }}</p>

                <p id="msg2">消息2:{{ msg2 }}</p>

                <p><button type="button" @click="changeMsg">改变消息</button></p>

            </div>

        </template>

        

        <script>

        export default {

            data() {

                return {

                    msg:"哈哈哈",

                    msg2:"啦啦啦"

                }

            },

            methods: {

                changeMsg(){

                    // $nextTick:回调函数延迟,在下一次dom更新之后调用

                    // vue 是异步渲染框架,数据更新之后,dom不会立刻渲染

                    this.msg = "晚安啦,啊啊啊"

        

                    // 解决方式1:有点迟钝

                    // setTimeout(() => {

                    //     this.msg2 = this.$refs.msg.innerHTML

                    // },200)

        

                    // 解决方式2:$nextTick会在dom渲染之后触发,用来获取最新的dom节点

                    // 使用场景1:在生命周期函数created中进行dom操作,一定还要放到$nextTick函数中执行

                    // 使用场景2:在数据变化后要执行某个操作,而这个操作需要变化后的dom时,这个操作需要放到$nextTick中

                    this.$nextTick(() => {

                        this.msg2 = this.$refs.msg.innerHTML

                    })

                }

            }

        }

        </script>


只袄早~~~
感谢你的支持,我会继续努力!
扫码打赏,感谢您的支持!

文明上网理性发言!