title: vue入门浅析
author: Sun-Wind
date: May 14,2022

写这篇博文的目的在于为初学vue的同学对vue有一些更进一步的了解

读这篇博文前,您应该至少安装了vue环境,能在本地运行一个简单的demo

本文将浅析vue项目工程的结构,以及用npm运行项目的过程中发生的一些事件

注明:该文本应在2022.5.14发表,由于博主有其他安排耽搁后面忘了,现在补上。

项目的文件结构

主文件结构

一般的vue工程项目核心部分都在src里

存放 vue 项目的源代码。其文件夹下的各个文件(文件夹)分别为:

  • assets​:资源文件,比如存放 css,图片等资源
  • component​:组件文件夹,用来存放 vue 的公共组件(注册于全局,在整个项目中通过关键词便可直接输出)。
  • router​:用来存放 ​index.js​,这个 js 用来配置路由
  • tool​:用来存放工具类 js,将 js 代码封装好放入这个文件夹可以全局调用(比如常见的​ api.js​,​http.js​ 是对 http 方法和 api 方法的封装)
  • views​:用来放主体页面,虽然和组件文件夹都是 vue 文件,但 views 下的 vue 文件是可以用来充当路由 view 的。
  • main.js​:是项目的入口文件,作用是初始化 vue 实例,并引入所需要的插件。
  • app.vue​:是项目的主组件,所有页面都是在该组件下进行切换的.

其他文件结构

  • public:用于存放静态文件
  • public/index.html:是一个模板文件,作用是生成项目的入口文件,webpack打包的js,css也会自动注入到该页面中。我们浏览器访问项目的时候就会默认打开生成好的index.html
  • package.json: 模块基本信息项目开发所需要模块,版本,项目名称
  • vue.config.js:包含vue项目的其他配置,包括端口等信息
  • node_modules:项目的依赖模块
  • dist:打包文件

npm run serve/dev浅析

我们在本地运行vue项目,常见的指令就是npm run serve/dev;与其说是指令,不如说是脚本

我们通常会在package.json中配置 script 字段作为 NPM 的执行脚本。

以个人开发项目为例,Vue.js 源码构建的脚本如下:

  "scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"stylelint": "stylelint src/css/*.* --fix",
"htmlhint": "htmlhint **.html",
"eslint": "eslint src/**/*.js src/**/*.vue",
"eslint-fix-js": "eslint src/**/*.js --fix",
"eslint-fix-vue": "eslint src/**/*.vue --fix"
},

所以当我们在终端运行npm run serve时,实际上运行的是vue-cli-service serve

通过这个脚本去构建整个vue项目

构建的过程中发生了什么

public/index.html

之前我们提到过,这个文件作为项目的入口文件,首先加载这个html文件

下面这些代码是个例子

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<link rel="icon" href="./icon.png">
<title></title>
</head>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

我们注意到一个特别的div块,它的id为app

src/main.js

这里的app其实与src/main.js文件有关

import Vue from 'vue';
new Vue({
el: '#app',
render: h => h(app)
});

我们都知道,new 关键字在 Javascript 语言中代表实例化是一个对象,而 Vue 实际上是一个类,类在 Javascript 中是用 Function 来实现的,在vue.js源码中是这样定义的

function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}

可以看到vue只能通过关键字初始化,this._init函数这里就不再具体介绍

Vue 初始化主要就干了几件事情,合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等等。

在初始化的最后,检测到如果有 el 属性,则调用 vm.$mount 方法挂载 vm,挂载的目标就是把模板渲染成最终的DOM

在compiler版本的$mount实现中,它对 el 做了限制,Vue 不能挂载在 body、html 这样的根节点上。

接下来的是很关键的逻辑 —— 如果没有定义 render 方法,则会把 el 或者 template 字符串转换成 render 方法。

这里我们要牢记,在 Vue 2.0 版本中,所有 Vue 的组件的渲染最终都需要 render 方法,无论我们是用单文件 .vue 方式开发组件,还是写了 el 或者 template 属性,最终都会转换成 render 方法,那么这个过程是 Vue 的一个在线编译的过程。

最后,调用原先原型上的 $mount 方法挂载。

结合之前public/index.html中的例子

<div id="app">
</div>

实际上是编写了如下render函数

render: function (createElement) {
return createElement('div', {
attrs: {
id: 'app'
},
})
}

vm._render 最终是通过执行 createElement 方法并返回的是 vnode,它是一个虚拟 Node

Virtual DOM介绍

浏览器真正的DOM通常是非常庞大的,因为浏览器产生DOM的标准本身就比较复杂,当我们频繁地进行DOM更新,就会产生一系列的性能问题

而 Virtual DOM 就是用一个原生的 JS 对象去描述一个 DOM 节点,所以它比创建一个 DOM 的代价要小很多。在 Vue.js 中,Virtual DOM 是用 VNode 这么一个 Class 去描述

在 Vue.js 中,VNode 的 create 是通过之前提到的 createElement 方法创建的。

生命周期

这也是一张比较经典的图了

在开发过程中,我们会频繁地跟vue的生命周期打交道

beforeCreate 和 created 函数都是在实例化 Vue 的阶段

在vue.js源码中 beforeCreate 和 created 的钩子调用是在 initState 的前后,initState 的作用是初始化 props、data、methods、watch、computed 等属性

在执行 vm._render() 函数渲染 VNode 之前,执行了 beforeMount 钩子函数,在执行完 vm._update() 把 VNode patch 到真实 DOM 后,执行 mouted 钩子。

beforeUpdate 和 updated 的钩子函数执行时机都应该是在数据更新的时候,比如双向绑定等等

export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
// ...
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
// ...
}

可以看到这里有一个vm._isMounted的判断,也就是说组件在mounted后才会去执行这个钩子函数

同时这里实例化了一个watcher去监听vm上的数据变化重新渲染

beforeDestroy 和 destroyed 钩子函数的执行时机在组件销毁的阶段

注意mounted和destroyed的执行过程都是先子后父

从下图可以看到初始化vue到最终渲染的整个过程

注册组件

在开发一个组件的过程中往往会用到其他的组件

组件注册的语法如下

Vue.component('my-component', {
// 选项
})

import HelloWorld from './components/HelloWorld'
export default {
components: {
HelloWorld
}
}

注册组件实际上是一个合并的过程,合并option再创建vnode。

由于博主在这一部分学识尚浅,暂不做过多的描述

下载插件

开发的过程中很可能需要一些其他的插件如Element等使用

一般来说通过Vue.use()来下载插件,并且会阻止多次注册相同的插件

export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
const args = toArray(arguments, 1)
args.unshift(this)
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
}

这是use方法的源码,可以看到其参数只能是object或者function,然后判断其是否被注册过

然后再调用该插件的install方法

可以看到 Vue 提供的插件注册机制很简单,每个插件都需要实现一个静态的 install 方法,当我们执行 Vue.use 注册插件的时候,就会执行这个 install 方法,并且在这个 install 方法的第一个参数我们可以拿到 Vue 对象,这样的好处就是作为插件的编写方不需要再额外去import Vue 了。

路由

路由的主要作用是根据不同的路径映射到不同的视图,一般我们用官方插件vue-router来解决路由的问题

Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
Object.defineProperty(Vue.prototype, '$route', {
get () { return this._routerRoot._route }
})

在vue-router的类定义中有在vue原型上定义的 $router 和 $route 两个属性的get方法,这也是为什么可以在组件实例上访问 this.$router和this.$route

在new一个vueRouter后会返回它的实例,在beforecreate()中有这样一段代码

beforeCreate() {
if (isDef(this.$options.router)) {
// ...
this._router = this.$options.router
this._router.init(this)
// ...
}
}

所以在执行该钩子函数时,如果有传入router实例,则会执行router.init方法

匹配是利用matcher匹配,并且会生成用户的路由表

(具体细节暂时不表)

当我们点击router-link的时候,会通过一系列函数找到完整的url,执行pushState方法

export function pushState (url?: string, replace?: boolean) {
saveScrollPosition()
const history = window.history
try {
if (replace) {
history.replaceState({ key: _key }, '', url)
} else {
_key = genKey()
history.pushState({ key: _key }, '', url)
}
} catch (e) {
window.location[replace ? 'replace' : 'assign'](url)
}
}

该方法会更新浏览器的url地址,并且把当前url压入历史栈中

有一个专门的监听器会监听历史栈的变化情况

setupListeners () {
const router = this.router
const expectScroll = router.options.scrollBehavior
const supportsScroll = supportsPushState && expectScroll
if (supportsScroll) {
setupScroll()
}
window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', () => {
const current = this.current
if (!ensureSlash()) {
return
}
this.transitionTo(getHash(), route => {
if (supportsScroll) {
handleScroll(this.router, route, current, true)
}
if (!supportsPushState) {
replaceHash(route.fullPath)
}
})
})
}

当点击浏览器的返回按钮时,会触发popstate事件,通过同样的方法拿到当前要跳转的url并进行路径转换

在router-view中

data.routerView = true
// ...
while (parent && parent._routerRoot !== parent) {
if (parent.$vnode && parent.$vnode.data.routerView) {
depth++
}
if (parent._inactive) {
inactive = true
}
parent = parent.$parent
}
const matched = route.matched[depth]
// ...
const component = cache[name] = matched.components[name]

这个循环就是从当前的的父节点向上找,一直找到根节点(vue实例),遍历完成后,就根据当前遍历的深度和路径找到对应的组件并进行渲染

在router-view的最后有根据 component 渲染出对应的组件 vonde:

return h(component, data, children)
  • hash模式:单页应用标配,hash发生变化的url都会被浏览器记录下来
  • history模式:可以进行切换和修改(历史状态),使得路由配置更自由

其他

vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。

它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

该状态管理模式包含以下几个部分:

  • state:驱动数据的应用源
  • view:以声明方法将state映射到视图
  • actions:响应在view上用户的输入导致的状态变化

    以下是一个简单的数据流模式

需要注意以下两点

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  • 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

参考书籍《vue.js技术揭秘》

Vue入门浅析的更多相关文章

  1. wepack+sass+vue 入门教程(三)

    十一.安装sass文件转换为css需要的相关依赖包 npm install --save-dev sass-loader style-loader css-loader loader的作用是辅助web ...

  2. wepack+sass+vue 入门教程(二)

    六.新建webpack配置文件 webpack.config.js 文件整体框架内容如下,后续会详细说明每个配置项的配置 webpack.config.js直接放在项目demo目录下 module.e ...

  3. wepack+sass+vue 入门教程(一)

    一.安装node.js node.js是基础,必须先安装.而且最新版的node.js,已经集成了npm. 下载地址 node安装,一路按默认即可. 二.全局安装webpack npm install ...

  4. vue入门学习(基础篇)

    vue入门学习总结: vue的一个组件包括三部分:template.style.script. vue的数据在data中定义使用. 数据渲染指令:v-text.v-html.{{}}. 隐藏未编译的标 ...

  5. VUE 入门笔记

    前端的MVVM概念今年来也算是如火如荼,了解完 MVVM的概念,也该找个去尝试下 首先我先试了下 国内小而美的 VUE 试着照着文档敲出入门文件,内容都在注释里 <!doctype html&g ...

  6. Vue 入门指南 JS

    Vue 入门指南 章节导航 英文:http://vuejs.org/guide/index.html 介绍 vue.js 是用来构建web应用接口的一个库 技术上,Vue.js 重点集中在MVVM模式 ...

  7. Vue学习记录第一篇——Vue入门基础

    前面的话 Vue中文文档写得很好,界面清爽,内容翔实.但文档毕竟不是教程,文档一上来出现了大量的新概念,对于新手而言,并不友好.个人还是比较喜欢类似于<JS高级程序设计>的风格,从浅入深, ...

  8. parcel+vue入门

    一.parcel简单使用 npm install -D parcel-bundler npm init -y (-y表示yes,跳过项目初始化提问阶段,直接生成package.json 文件.) Pa ...

  9. Vue入门基础

    前面的话 Vue中文文档写得很好,界面清爽,内容翔实.但文档毕竟不是教程,文档一上来出现了大量的新概念,对于新手而言,并不友好.个人还是比较喜欢类似于<JS高级程序设计>的风格,从浅入深, ...

  10. 学习Vue 入门到实战——学习笔记

    闲聊: 自从进了现在的公司,小颖就再没怎么接触vue了,最近不太忙,所以想再学习下vue,就看了看vue相关视频,顺便做个笔记嘻嘻. 视频地址:Vue 入门到实战1.Vue 入门到实战2 学习内容: ...

随机推荐

  1. IBM MQ 配置SSL 连接

    图示为思路: 下面介绍具体的步骤 参考文档: https://www.jianshu.com/p/2865965a42d9 http://www.hackdig.com/?01/hack-7976.h ...

  2. VSCode 快捷键,简化操作

    一. 区域代码快捷键 1. 折叠所有 折叠所有区域代码的快捷: ctrl + k      ctrl + 0 ; 展开所有折叠区域代码的快捷:ctrl +k      ctrl + J ; 2. 按层 ...

  3. 对使用网站模板编写自己的jsp页面的收获

    收获与问题 先感叹一句模板的强大,模板只要按照我的上一篇博客的步骤进行修改,我们就能拥有一个自己写好看许多的界面了. 我目前套用模板还不是很熟练,套用的速度还需要加快,不过目前的我,已经掌握了基本要领 ...

  4. HDFS 组织架构

    一.HDFS 概述 HDFS 产生背景:随着数据量越来越多,一个系统存储不下所有的数据,那么就需要分配到多个操作系统的磁盘中进行存储,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就 ...

  5. 磁盘IO 基本常识

    计算机硬件性能在过去十年间的发展普遍遵循摩尔定律,通用计算机的 CPU主频早已超过3GHz,内存也进入了普及DDR4的时代.然而传统硬盘虽然在存储容量上增长迅速,但是在读写性能上并无明显提升,同时SS ...

  6. Netty 心跳检测与重连机制

    更多内容,前往个人博客 所谓心跳,即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包,通知对方自己还在线,以确保 TCP 连接的有效性.心跳包还有另一个作用,经常被忽略,即:一个连 ...

  7. 免费Midjourney AI绘画Prompt提示词平台合集

    Midjourney AI绘图最关键的地方在于Prompt提示词写的好,一个好的提示词可以让AI模型创造出更优质的绘图,以下是8个免费的Midjourney Prompt提示词辅助平台. ​ 编辑切换 ...

  8. pcm音频的录制、播放及转换

    操作系统 :Windows10_x64 pcm格式为原始音频数据,有时候会遇到需要录制.播放及转换的情况,这里记录下. 一.录制pcm音频 这里演示下使用Audacity进行pcm音频录音的过程. A ...

  9. [智能制造] 如何利用生产软件(MES)进行生产信息收集?

    1 如何保证生产管理软件所收集信息的准确性? 1.1 当前制造企业使用MES系统收集信息的现状 原以为使用了MES生产管理系统后,会得到稽核员的肯定. 但没想到,在实际的稽核过程中,稽核员还是发现目前 ...

  10. dfs实现

    1.思路:从图中的未访问的一个顶点开始,沿着一条路一直走到底,然后这条路尽头的节点,在从另外一条路走到底,不断递归此过程,直到所有遍历完成特点:不撞南墙不回头2.具体实现:当从一个未知的顶点出发,将这 ...