江湖夜雨十年灯

Vue 应用初始化

李二花 / 2023-03-15


有这么三个 Vue 文件,然后看看初始化时走的各个流程是做了什么,以及一些原理。

// main.js
import Vue from 'vue'
import App from './App.vue'
// import { Row, Button } from 'element-ui';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import router from './router'
import store from './store'
import './api/mock'

Vue.config.productionTip = false

// 全局引入
Vue.use(ElementUI)


new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app')

// App.vue
<template>
  <div id="app">
    <!-- 路由出口 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: 'App'
}
</script>
<style lang="less">
  html,body,h3, p {
    margin: 0;
    padding: 0
  }
</style>
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import tab from './tab'

Vue.use(Vuex)

// 创建vuex的实例
export default new Vuex.Store({
    modules: {
        tab
    }
})

Vue 应用初始化

new Vue({
    router,
    store,
    render: h => h(App),
}).$mount('#app')

介绍下这个代码在干什么,虚拟 DOM 是在这里初始化的吗?

这段代码是Vue.js框架中的一个常见用法,用于初始化和挂载一个Vue应用。Vue.js是一个用于构建用户界面的渐进式JavaScript框架。下面我会逐一解释这段代码的各个部分:

  1. new Vue({...}): 这是在创建一个新的Vue实例。Vue实例是Vue应用的根,所有的组件都会挂载在这个实例下。

  2. router: 这是Vue Router的实例,用于Vue应用的页面路由。将其传入Vue实例,意味着该Vue应用将使用Vue Router进行页面的路由管理。这使得单页面应用(SPA)的开发变得简单和高效。

  3. store: 这是Vuex的实例,Vuex是Vue的状态管理模式。它提供了一种集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。这对于大型应用尤其重要,因为它使得状态管理变得更加有条不紊。

  4. render: h => h(App): 这是一个渲染函数,用来渲染一个Vue组件。这里的App通常是一个根组件,它是所有其他组件的父组件。hcreateElement的别名,一个用来创建虚拟DOM的函数。这行代码的含义是,使用h函数来创建App组件的虚拟DOM。

  5. .$mount('#app'): 这是Vue实例的一个方法,用于将Vue实例挂载到DOM上。#app是一个选择器,它指向HTML中的一个元素。这意味着Vue实例将挂载到这个元素上,而这个元素通常是页面上的一个容器,Vue应用将在这个容器内进行渲染。

至于虚拟DOM的初始化,它是在Vue内部进行的,当Vue实例被创建并挂载时。虚拟DOM是Vue的一个核心概念,它允许Vue在内存中以JavaScript对象的形式模拟DOM结构,然后Vue的渲染函数会基于虚拟DOM来创建真实的DOM。这种机制使得Vue能够高效地更新DOM,因为它只更新那些实际发生变化的部分,而不是重建整个DOM树。这段代码中的render函数就是虚拟DOM初始化和更新的起点。

Vue.use

import Vue from 'vue'
import App from './App.vue'
// import { Row, Button } from 'element-ui';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import router from './router'
import store from './store'
import './api/mock'

Vue.config.productionTip = false

// 全局引入
Vue.use(ElementUI)


new Vue({
    router,
    store,
    render: (h) => h(App),
}).$mount('#app')

为什么可以先 Vue.use 然后再创建一个 Vue 对象?

在Vue.js中,Vue.use()方法用于安装Vue插件。当你调用Vue.use(plugin)时,它会执行plugin对象的install方法(如果plugin是一个对象并且提供了install方法的话),或者直接执行plugin(如果plugin是一个函数)。这个过程通常用于注册全局组件、指令、过滤器,或者向Vue实例添加全局方法和属性等。

关于为什么可以先使用Vue.use()然后再创建一个Vue实例,主要是因为Vue.use()方法的作用是在全局范围内注册插件,这意味着一旦插件被安装,它将影响到之后创建的所有Vue实例。这是一种设计选择,允许开发者在应用初始化阶段就配置好所需的插件,而不必在每个Vue实例中重复配置。

在你提供的代码示例中,Vue.use(ElementUI)用于全局安装Element UI组件库。Element UI是一个基于Vue 2.0的桌面端组件库,它提供了一套丰富的Vue组件,非常适合在Vue项目中使用。通过全局安装(Vue.use(ElementUI)),你无需在每个组件中单独引入Element UI的组件,可以直接在任何地方使用它们,这极大地简化了开发流程。

总结来说,Vue.use()方法允许在全局范围内安装和注册插件或组件库,这样做的好处是让所有的Vue实例都能够使用到这些功能,而不需要在每个实例或组件中单独进行配置。这就是为什么你可以在创建Vue实例之前调用Vue.use(),并且这样做是Vue插件安装的推荐方式。

Vue 是什么?

是不是可以认为 Vue 是一个类名,use 是一个类方法,可以先直接用类方法做一些类全局的处理,然后再初始化 Vue 实例时,这个 Vue 实例就可以直接拿到对应的类的属性了?

是的,你的理解是正确的。在Vue.js中,Vue确实可以被认为是一个类(在JavaScript中通常称为构造函数)。Vue.use()则是这个类的一个静态方法,它用于安装Vue插件。

静态方法是属于类本身而不是类的实例的方法。因此,当你调用Vue.use()时,你实际上是在调用Vue类的一个静态方法来进行全局的配置或安装插件。这些配置或插件将影响到之后创建的所有Vue实例,因为它们是在Vue的全局作用域中注册的。

这就像你所描述的那样:通过类的静态方法,可以在类级别上做一些全局的处理。当你随后创建该类的实例时(即通过new Vue({...})创建Vue实例),这些实例就能够自动继承或访问到之前通过静态方法注册或配置的属性和功能。

具体到Vue中,这意味着:

  1. 全局安装插件:当你使用Vue.use(plugin)安装插件时,你是在Vue的全局作用域中注册该插件。无论何时创建Vue实例,这个插件都会被应用到每个实例中,这使得插件中的组件或功能可以在任何新创建的Vue实例中使用。

  2. 全局注册组件:类似地,如果你通过Vue.component()全局注册一个组件,那么这个组件可以在任何Vue实例的模板中使用,而不需要在每个使用它的组件中单独进行导入和注册。

因此,Vue的设计允许通过类的静态方法进行全局配置,然后这些配置会自动应用到所有的Vue实例中,这极大地简化了Vue应用的配置和插件的使用。

Vuex

import Vue from 'vue'
import Vuex from 'vuex'
import tab from './tab'

Vue.use(Vuex)

// 创建vuex的实例
export default new Vuex.Store({
    modules: {
        tab
    }
})

既然这样的话,Vuex 是不是也是个类,但是 Vue 的类 use 一个 Vuex 的类而不是实例的原因是什么?之后为什么又创建了一个 Vuex.Store 的实例呢?为什么不是 Vue 去 use 或者挂载一个 Vuex.Store 实例,而是他的类,这个Vuex类在这里是一个什么角色?

你的问题涉及到Vue插件的使用方式以及Vuex的架构设计。让我们一步步解析这些问题。

Vuex 是不是也是个类?

是的,Vuex可以被视为一个包含了多个属性和方法(包括Store构造函数)的对象。在Vuex的上下文中,Vuex.Store是一个用于创建Vuex store实例的构造函数(或类)。这个构造函数负责管理应用的状态(state)和逻辑(通过actions、mutations、getters等)。

为什么Vue的类用Vue.use()加载Vuex的类而不是实例?

Vue.use()方法用于安装Vue插件。Vuex作为一个Vue插件,提供了一个install方法。这个install方法是由Vuex对象提供的,而不是Vuex.Store的实例提供的。当你调用Vue.use(Vuex)时,实际上是在调用Vuex的install方法。这个install方法会向Vue实例添加一些全局功能,最重要的是添加$store属性,这样在任何组件中都可以通过this.$store访问到Vuex store。

为什么又创建了一个Vuex.Store的实例?

虽然Vue.use(Vuex)负责安装Vuex插件,使Vue知道如何处理Vuex store,但它并不创建一个具体的store实例。你需要通过new Vuex.Store({...})创建一个具体的store实例,这个实例根据你提供的配置(状态、mutations、actions等)来管理你的应用状态。创建的store实例需要被传递给根Vue实例(通常是在new Vue({...})时通过store选项进行传递),这样,Vuex的状态管理就能被整个应用所使用。

Vuex类在这里是一个什么角色?

在这个上下文中,Vuex充当的是插件的角色,它提供了必要的接口(install方法)以及用于创建具体store实例的构造函数(Vuex.Store)。Vue.use(Vuex)负责安装Vuex插件,确保Vue知道如何处理Vuex store。而new Vuex.Store({...})则是根据你的应用具体需要创建一个Vuex store实例。

总结来说,Vue.use(Vuex)是在全局范围内安装Vuex插件,而new Vuex.Store({...})是创建一个具体的Vuex store实例。这两步是Vuex在Vue应用中正常工作所必需的。

为什么可以引入 .vue 文件

javascript 中为什么可以直接这样引入一个非 js 的文件,import App from ‘./App.vue’,这样引入的过程中,实际是在干什么,里面的 template script 和 style 都分别会怎么样被处理?

在JavaScript中直接引入非JS文件,比如Vue单文件组件(.vue文件),实际上是依赖于构建工具(如Webpack或Vite)和相应的加载器(loader)或插件来实现的。这些构建工具在编译时会识别到特定类型的文件,并使用配置好的加载器对这些文件进行处理。对于.vue文件,通常使用vue-loader(对于Webpack)或相应的插件(对于其他构建工具)来处理。

引入过程中发生了什么?

  1. 识别文件类型:构建工具通过文件扩展名(.vue)识别出这是一个Vue单文件组件。

  2. 使用加载器处理文件vue-loader(或其他工具的等效插件)被调用来处理.vue文件。vue-loader会解析文件,将其拆分为独立的部分(模板、脚本、样式等)。

  3. 编译模板:模板部分(<template>标签内的内容)会被编译成JavaScript渲染函数。这个过程涉及到HTML模板的解析,将其转换为可以通过JavaScript动态生成DOM的代码。

  4. 处理脚本:脚本部分(<script>标签内的内容)会被当作普通的JavaScript代码处理。如果使用了ES6或其他JavaScript版本的特性,可能还会通过Babel等工具进行转译。

  5. 处理样式:样式部分(<style>标签内的内容)会被提取并可能被加工(例如,如果使用了预处理器如Sass,需要先将其编译成普通的CSS),然后根据配置可能会被打包到单独的CSS文件中,或者以<style>标签的形式直接插入到HTML中。

结果

最终,vue-loader(或等效工具)会生成一个JavaScript模块,这个模块导出了一个Vue组件对象。这个对象包含了处理后的模板渲染函数、脚本中定义的组件选项(如data、methods等),以及可能的样式信息。当你在其他组件或应用中import这个.vue文件时,实际上是在引入这个编译后的JavaScript模块。

因此,这个过程使得开发者能够以一种高效且模块化的方式开发Vue应用,同时利用现代JavaScript工具链的强大功能(如模块化导入、代码分割、预处理语言支持等)。

Vue应用初始化

javascript 的执行过程

javascript 代码执行的过程是什么?就比如在

new Vue({
    router,
    store,
    render: (h) => h(App),
}).$mount('#app') 

过程中,是在 import store 时就已经开始执行 store 里的代码也就是 import Vue from ‘vue’

import Vuex from 'vuex'
import tab from './tab'

Vue.use(Vuex)

// 创建vuex的实例
export default new Vuex.Store({
    modules: {
        tab
    }
})

还是说是在创建 Vue 实例时,加载到 store 时才去执行 store.js 里的那些命令?

JavaScript代码的执行过程,以及模块的导入和执行,遵循特定的规则。在你提到的Vue应用的上下文中,涉及到模块化JavaScript代码(使用ES6的import/export语法)。理解这一过程的关键在于明白JavaScript模块化系统的工作原理。

模块的导入和执行

当你使用import语句导入一个模块时,如import store from './store',这里发生的事情可以分为以下几个步骤:

  1. 解析模块路径:确定模块文件的位置。
  2. 加载模块文件:读取模块文件的内容。如果模块已经被加载过(存在于模块缓存中),则直接跳到执行步骤。
  3. 编译模块:将模块代码编译成可执行的JavaScript代码。这一步骤会处理模块内部的import语句,递归地加载和编译这些依赖。
  4. 执行模块代码:执行编译后的代码。模块内部的顶层代码(不在函数内部的代码)会在这一步骤执行。
  5. 导出模块接口:模块的export语句定义了模块的公开接口,其他模块可以通过import导入这些接口。

在你的例子中

当你在Vue应用的入口文件中导入store模块:

import store from './store'

这个导入语句会触发上述的加载和执行过程。也就是说,store.js中的代码(包括导入VueVuex,使用Vue.use(Vuex),以及创建Vuex.Store的实例)会在导入store模块时执行。这意味着,在你创建Vue实例之前,store.js中的代码已经执行了。

因此,当你创建Vue实例并通过store选项将Vuex store实例传递给Vue时:

new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app')

此时,store模块中的Vuex store实例已经被创建并配置好了,你在Vue实例中使用的是之前已经创建和配置好的Vuex store实例。

这个过程展示了模块化JavaScript代码的强大之处,允许你以清晰和可维护的方式组织代码,同时确保代码的执行顺序和依赖管理得到妥善处理。