前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue3 解密 (持续更新中) - wuuconix's blog

Vue3 解密 (持续更新中) - wuuconix's blog

作者头像
wuuconix
发布2023-03-16 15:54:53
5040
发布2023-03-16 15:54:53
举报
文章被收录于专栏:wuuconix

背景

用Vue也写过不少项目了,科技立项的个人网盘以及这个寒假写的短链站、禁书目录等等。

但是会写了,也只是依样画葫芦,更多的是在Element Plus里复制粘贴,再加点自己的东西,没有系统的学习和了解Vue。

懒惰的武丑兄便打算给自己开个新坑,以Vue3官方文档为基础,真正去理解Vue,形成更加长远的记忆。

本博客将持续更新,具体形式为提出某个问题,并对该问题进行解析。

app是什么

每当我们用脚手架新建一个vue项目,脚手架会给你一个模板项目,一些固定的代码总是那样,比如下面的main.js里的代码,你一定很熟悉。

代码语言:javascript
复制
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
app.mount('#app')

这里面一共出现了8个app,让人眼花缭乱,让人看不懂它们到底是什么。

首先我们看第二行的App,它是从单文件组件 App.vue 里引入的,所以我们可以把App称为一个组件。而通常情况下,这个App总是最外层的,我们可以把它叫做rootComponent,即根组件,它将是渲染的起点。

再看第一行的createApp,它是从vue这个模块里通过ES6中的解构语法导出的一个函数。

而第三行的app变量就是这个函数的返回值。那它是什么呢?在之前,我一直把这个app看作一个组件,因为createApp的参数里是App,是根组件。

但是查阅文档 应用 API | Vue.js (vuejs.org) 之后,我们可以知道,该函数的返回值,即这里的app,我们应该叫它为 应用实例

这个实例可以干很多事情。

比如全局注册一个组件,让该应用实例挂在的组件树都能够共享这个组件。

代码语言:javascript
复制
import AnComponent from './components/AnComponent.vue'
//...
app.component('AnComponent',  AnComponent)

使用一个插件

代码语言:javascript
复制
import router from './router'
import ElementPlus from 'element-plus'
//...
app.use(ElementPlus)
app.use(router)

这里就是使用了我们常见的Element Plus UI库,以及Vue Router路由,它们都是以插件的形式引入到项目中的。

我们最后看第四行的'#app'。它实际上是项目 public/index.html里面的一个div。

它算什么呢?我们从它给我们的注释里也可以知道,项目的根组件渲染的结果实际上是会放到这个<div id="app">中的内部的。

所以它应该被叫为 根组件实例

我们再来重新看app.mount('#app')这个语句,它是把app这个应用实例绑定到了 根组件实例上了嘛?不,app这个应用实例只是一个工具人,它干的事情是,把应用实例对应的根组件绑定到了根组件实例里。

即把App绑定到了#app上。

神秘的ref

在写项目的时候,当我还在写类似下面的结果的时候

代码语言:javascript
复制
<script>
export default ({
	data() {
		return {
            
        }
	}
})
</script>

Vue3已经推出了一种setup标签的语法糖,它可以让我们少些很多繁琐的结构,就比如上面的export default等等。

我还不太会写这种高阶的语法,但是我在看许多案例的时候都发现它们都使用了 vue module里面的ref,而它们干的事情好像又很简单,我就无法理解这个ref的作用。比如下面的例子。

代码语言:javascript
复制
<script setup>
import { ref } from 'vue'

const msg = ref('Hello World!')
</script>

<template>
  <h1>{{ msg }}</h1>
  <input v-model="msg">
</template>

你可以把这段代码复制到 Vue SFC PlayGround 里进行尝试。该单文件组件的功能就是将input输入的内容和msg绑定起来,输入框的内容一变,msg变量就会变。

我们注意到,msg变量定义的时候,用的是const,按理说msg的值是不可以改变的,除非它是一个对象。所以ref的返回值是一个对象。

通过查阅 Refs | Vue.js (vuejs.org) 文档,我们印证了这个观点。

文档:ref接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property,指向该内部值。

所以ref存在的目的就是为了实现Vue中响应式数据的特点。那为什么我们用普通的写法不需要用到ref呢?这里我将语法改写为以下。

代码语言:javascript
复制
<script>
export default({
  data() {
    return {
	    msg: '123'
    }
  }
})
</script>

<template>
  <h1>{{ msg }}</h1>
  <input v-model="msg">
</template>

我们发现,同样可以实现和之前一样的效果。

我们观察这个普通写法中的data 实际上返回了一个对象,msg是这个对象里的一个键,由于对象的特性,msg的值可以被随意更改,实现响应式。

而高阶语法里没有这种data对象,我们便需要用ref来创造一个只有value值的对象。实现数据的响应。

以上观点可以在Vue SFC Playground里得到印证,它向我们展示出我们这段js代码最终被编译后的样子。

SFC After Complier

data() 与 $data

还是拿这个常见的代码举例子。

代码语言:javascript
复制
<script>
export default ({
	data() {
		return {
            
        }
	}
})
</script>

这里的data是什么呢?我们如果以一个没有学过js的同学的视角看,它就是一个函数,和我们在C、C++定义函数的结构一致。

它确实是个函数,但是有几点值得说明。

首先这个它在一个对象内部,因为export default 导出了一个对象 {}。按理说一个对象都是键值的形式,那它就放一个函数,它的键和值都是什么呢?

实际上这是ES6 对于对象内部方法名的一种简写,请参考 3.2.3 ES6 对象 | 菜鸟教程 (runoob.com)

它实际上的样子应该是这样的。

代码语言:javascript
复制
<script>
export default ({
	data: function() {
		return {
            
        }
	}
})
</script>

这样就符合对象内部的键值关系了,它的键是data,它是一个function,返回值是一个对象。

我们都知道,在这个return里面定义的属性,我们可以在别的地方使用。

比如在模板里使用插值的形式。

插值

代码语言:javascript
复制
<template>
<h1>{{ wuuconix }}</h1>
</template>

<script>
export default ({
	data() {
		return {
            wuuconix: 'yyds'
        }
	}
})
</script>

再比如在methods方法里使用属性

methods

代码语言:javascript
复制
<template>
<h1 @click="test">{{ wuuconix }}</h1>
</template>

<script>
export default ({
	data() {
		return {
            wuuconix: 'yyds'
        }
	},
	methods: {
		test() {
			console.log(this.wuuconix)
		}
	}
	
})
</script>

所以一直一来我就认为组件实例的数据就是来自data,但是它又只是个函数,不是个对象,让我觉得非常奇怪。

在阅读 实例 property | Vue.js (vuejs.org) 后我发现,原来还有一个$data的东西,它是一个实例Property,应该可以叫为实例属性。

而之前一直写的data它只是一个函数,用来返回组件实例的data对象,即$data。

所以data只是一个函数,而它的返回值,一般来说它的返回值必须是一个对象,这个对象就会成为组件实例的$data,作为一个实例属性供之后调用。

这里看了文档以后后还明白了一点,我们平常在methods里调用属性,都会写this.wuuconix来使用属性,那这里的this指什么呢?我以前认为应该是指向组件本身,而看了 Data | Vue.js (vuejs.org) 后我发现它指向的是组件实例。

那这样就引出了另一个问题,组件实例的属性都存在$data这个对象里,那为什么我们可以使用this.wuuconix的形式来调用组件实例的属性呢?

实际上这里vue大概是为了方便操作为我们做了一层代理。我们把vm(取义自viewModel)看作组件实例。访问vm.wuuconnix等价于访问vm.$data.wuuconix

为了印证它,我在SFC Playground里做了测试,发现使用 this.wuuconix和this.$data.wuuconix 效果一致。

对$data的测试

代码语言:javascript
复制
<template>
<h1 @click="test">{{ wuuconix }}</h1>
</template>

<script>
export default ({
	data() {
		return {
            wuuconix: 'yyds'
        }
	},
	methods: {
		test() {
			console.log(this.$data.wuuconix)
		}
	}
	
})
</script>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年1月13日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • app是什么
  • 神秘的ref
  • data() 与 $data
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档