vue3学习随便记2-声明式渲染、处理用户输入、条件与循环

本文介绍了从MVC模式到MVVM模式的转变,重点讲解了Vue.js的基础概念,包括数据劫持、数据代理、数据编译和发布订阅模式。通过实例展示了Vue如何实现声明式渲染、处理用户输入、条件与循环以及组件化。文章强调了Vue在前后端分离中的优势,特别是在业务型SPA应用中的表现,并提供了几个简单的Vue代码示例来帮助理解。
摘要由CSDN通过智能技术生成

引子

初步学习 Vue的概念,一上来就用 vue-cli 的方式的确是找死,会让自己陷入很多复杂的坑。初次尝试,最好是直接引入。

在开始了解 Vue 的概念之前,我们要先来回顾一下 MVC模式、MVVM模式的初步思想。

MVC 模式是一种后端考虑思维,是厚重后端的思维。Model负责对来自用户的数据、从数据库获取的数据进行加工,有关业务逻辑的处理,把一定的数据通过Controller提供给 View 层。View 层负责利用所给的数据生成展示用的 HTML、CSS、JS 发送给客户浏览器,如果需要一些前端的效果,则在 JS 中通过操作 DOM 实现。Controller 层是很瘦的连接器,负责沟通 View 和 Model。当然,Controller 层是用户端的第一入口,所以往往和路由有关。MVC思维中,用户界面view上的变化都是事件(例如点击了某个链接,提交了表单),我们针对每个事件写代码(对应controller中的action),把用户输入转换到model中处理。

MVVM模式是一种前后端分离的思维,是厚重前端的思维。前后端分离后,后端只是扔一堆数据给前端,后面的事(呈现逻辑)都是前端的事。Model 从前端看就是API请求获得数据View就是单纯的 HTML、CSS 展示大量的呈现逻辑都是 ViewModel 负责(ViewModel是专门为View服务的Model,即View的数据对象)。MVVM思维中,view里面的各种控件有一个对应的数据对象,这样,只要修改这个数据对象,view里面显示的内容就自动同步刷新,反之,view里面做了任何操作,这个数据对象也跟着同步修改(双向绑定与响应式组件),这些功能的实现都是底层处理的,开发者不用直接操作DOM。

在 MVC 中,View需要呈现的东西具体数据是怎样的,还是需要较多和后端联系,起码有详细文档说明。而 MVVM 中,View 设计者可以关注设计,关于数据和事件,就是一堆声明,它面向ViewModel中的数据即可。ViewModel开发者可以来协调怎么从后台获取数据,怎么调整成更适合View的数据,换句话说,通过ViewModel,View和Model解耦了。在 MVVM 下,组件的可复用性更强。

MVVM中的bug调试比MVC中的要困难一些(前端调试本就比较困难),压力也主要在浏览器一侧,大的 ViewModel 构建和维护的成本也比较高。某种程度上,MVVM模式下的SPA应用,基本上和传统的C/S风格的窗口程序无异了。静态内容偏多的网站,可能用 MVVM 没有多少优势,而典型的业务型 SPA 才是能体现它优点的地方。

MVVM的主要魔法是 数据劫持数据代理数据编译和“发布订阅模式”。

数据劫持:主要是给对象的(data中的那些)属性添加 get、set 钩子函数

数据代理:把 data、methods、computed 上的数据挂载到 vm 实例上,这些地方定义的数据,就都是vm实例的数据,从而不用 vm._data.a.b 这样去访问,直接用 vm.a.b 即可。

数据编译:把 {{}},v-model,v-html,v-on 里面的对应的变量用 data 里面的数据进行替换

发布订阅订阅就是将订阅者(就是函数)添加到订阅队列(就是数组)中发布就是让订阅者得到调用执行在数据发生改变的时候,订阅者收到通知,执行相应的操作。一般 get 钩子函数被调用时进行数据的订阅,在 set 钩子函数被调用时进行数据的发布。

双向绑定主要是数据劫持发布订阅模式的作用。

介绍

声明式渲染

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="counter">
        计数器: {{ counter }}
    </div>
    <script>
        const Counter = {
            data() {
                return {
                    counter: 0
                }
            },
            mounted() {
                setInterval(() => {
                    this.counter++
                }, 1000)
            }
        }
        Vue.createApp(Counter).mount('#counter')
    </script>
</body>

</html>

Vue.createApp 根据 Counter 定义的数据对象配置创建数据对象vm(相当于ViewModel),然后用 mount 方法和 DOM建立关联。数据和DOM建立关联后,所有东西都是响应式的。

data() 返回的对象,其中的键值对直接成为vm的数据(数据代理),之所以插一层 data(),这里意味着 data()内的数据都是vm的被“监视”的变量,一旦这些变量改变,(订阅者收到通知)背后相应的方法调用,该替换修改的地方得到修改(模板编译)。

不仅文本插值可以实现响应式替换,元素的属性也可以绑定到变量,响应式自动替换。

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="bind-attribute">
        <span v-bind:title="message">
            鼠标悬停几秒钟查看此处动态绑定的提示消息!
        </span>
    </div>
    <script>
        const AttributeBinding = {
            data() {
                return {
                    message: '你加载此页的时间 ' + new Date().toLocaleString()
                }
            },
            mounted() {
                setInterval(() => {
                    this.counter++
                }, 1000)
            }
        }
        Vue.createApp(AttributeBinding).mount('#bind-attribute')
    </script>
</body>

</html>

处理用户输入

用户输入包括在文本框之类的东西输入数据,也包括点击按钮之类的动作。可以用 v-on 指令添加事件监听器,绑定到vm的实例方法。

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="event-handling">
        <p>{{ message }}</p>
        <button v-on:click="reverseMessage">反转 消息文本</button>
    </div>
    <script>
        const EventHandling = {
            data() {
                return {
                    message: 'Hello Vue3'
                }
            },
            methods: {
                reverseMessage() {
                    this.message = this.message.split('').reverse().join('')
                }
            }
        }
        Vue.createApp(EventHandling).mount('#event-handling')
    </script>
</body>

</html>

用 Vue 提供的 v-model 指令,可以将表单输入和vm变量之间双向绑定,用户输入可以被自动get到并同步到变量,变量被修改时也可以自动反映到表单输入。

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="two-way-binding">
        <p>{{ message }}</p>
        <input v-model="message" />
    </div>
    <script>
        const TwoWayBinding = {
            data() {
                return {
                    message: 'Hello Vue3'
                }
            }
        }
        Vue.createApp(TwoWayBinding).mount('#two-way-binding')
    </script>
</body>

</html>

 条件与循环

条件控制就是一个元素是否显示的控制,是否显示包含是否渲染(v-if)和渲染后是否隐藏(v-show)。

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="conditional-rendering">
        <div>
            <span v-if="seen">这个渲染了</span> <br>
            <span v-show="show">这个显示了</span>
        </div>
        <div>
            <button @click="toggleSeen">切换渲染的</button>
            <button @click="toggleShow">切换显示</button>
        </div>
    </div>
    <script>
        const ConditionalRendering = {
            data() {
                return {
                    seen: true,
                    show: true
                }
            },
            methods: {
                toggleSeen() {
                    this.seen = !this.seen
                },
                toggleShow() {
                    this.show = !this.show
                }
            }
        }
        Vue.createApp(ConditionalRendering).mount('#conditional-rendering')
    </script>
</body>

</html>

我们点击按钮,让两个文本都看不到,再观察实际的HTML元素,可以发现,同样是隐藏,v-if 是不渲染,v-show 是渲染而不显示

对于数组数据的显示,可以用 v-for 指令绑定数组数据来渲染,循环显示成列表或其他相同的块。

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="list-rendering">
        <ol>
            <li v-for="todo in todos">
                {{ todo.text }}
            </li>
        </ol>
    </div>
    <script>
        const ListRendering = {
            data() {
                return {
                    todos: [
                        { text: 'Learn JavaScript' },
                        { text: 'Learn Vue' },
                        { text: 'Build something awesome' }
                    ]
                }
            }
        }
        Vue.createApp(ListRendering).mount('#list-rendering')
    </script>
</body>

</html>

可以将可复用的,或者功能相对独立的块构建为独立的组件,以便提高复用效果或可维护性。

<html>

<head>
    <script src="vue.global.js"></script>
</head>

<body>
    <div id="list-rendering">
        <ol>
            <todo-item v-for="todo in todos"></todo-item>
        </ol>
    </div>
    <script>
        const TodoItem = {
            template: `<li>这是一个待办事项</li>`
        }
        const TodoList = {
            components: {
                TodoItem
            },
            data() {
                return {
                    todos: [
                        { text: 'Learn JavaScript' },
                        { text: 'Learn Vue' },
                        { text: 'Build something awesome' }
                    ]
                }
            }
        }
        Vue.createApp(TodoList).mount('#list-rendering')
    </script>
</body>

</html>

我们用声明式数据对象定义了组件

        const TodoItem = {
            template: `<li>这是一个待办事项</li>`
        }

然后在 TodoList 数据对象中注册了该组件 (TodoList 的 components 属性)

            components: {
                TodoItem
            },

然后在 TodoList 的 view 中使用该组件 (把它当作新元素)

<todo-item v-for="todo in todos"></todo-item>

最后的效果是

这样不能合乎逻辑,我们希望组件 TodoItem 渲染出来的是 todos 数组中的数据。我们必须有一种机制,可以让组件 TodoItem 这个儿子,能够接受父亲 TodoList 传给它的数据,并渲染出来。实现这一通信的是属性机制,我们修改注册组件部分,使组件包含 todo 属性,并且渲染的文本是该属性的 text 键值 todo.text

        const TodoItem = {
            props: ['todo'],
            template: `<li>{{ todo.text }}</li>`
        }

在使用组件时,把数据绑定到该属性(数据item----->属性todo)

        <ol>
            <todo-item v-for="item in todos"
                v-bind:todo="item"
            ></todo-item>
        </ol>

现在效果就是

 事实上,每个应用都有一个组件,即根组件,在上述例子中就是 TodoList 根组件,根组件下可以有子组件。我们尝试用 vue的 devtools 浏览器插件来查看,先要处理该插件,鉴于翻墙太麻烦,我们从 devtools 源码开始

git clone https://github.com/vuejs/devtools.git        // 克隆源码
cd devtools
yarn                        // 安装依赖
yarn build                    // 构建

然后 packages 子目录下有生成的各种适用于浏览器的东西,关注 shell-chrome 子目录,将它作为解压缩的扩展加载到 Edge 浏览器。重新启动浏览器加载前述页面,F12打开开发人员工具,选择 Vue tab页,可以看到如下页面结构:

 Vue应用页面总是一棵 DOM树,这里是最简单的,类似如下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值