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. CSS pointer-events 属性

    pointer-events 属性用于设置元素是否对鼠标事件做出反应. CSS 语法 pointer-events: auto|none; 属性值 属性值 描述 auto 默认值,设置该属性链接可以正 ...

  2. 漫谈Python魔术方法,见过的没见过的都在这里了

    漫谈Python魔术方法,见过的没见过的都在这里了 就说一下,不深入 假的一览 提到魔术方法,学过python都应该知道一些.至少你得会__init__吧. 在我之前写的博文中有很多都涉及魔术方法.比 ...

  3. 对于利用Java Script实现的判断的基础浅层总结

    各种判断,请收好啦! 判断输入框中的数据是否为整数: 点击删除按钮,出现判断是否删除的弹窗 在button里面加上这样一句话就能够实现删除判断弹窗啦!

  4. Spring框架中都用到了哪些设计模式 ?

    1.简单工厂模式 简单工厂模式的本质就是一个工厂类根据传入的参数,动态的决定实例化哪个类.Spring 中的 BeanFactory 就是简单工厂模式的体现,根据传入一个唯一的标识来获得 bean 对 ...

  5. webpack踩坑日记

    webpack 4.x 详细入门这是一个大佬的总结,但是我用webpack5重写该demo时,发现了几个有问题的地方1:CleanWebpackPlugin 应该这样: const { CleanWe ...

  6. node.js解决跨域方案

    服务端 1.通过使用cors模块解决跨域问题 var express = require('express') , cors = require('cors') , app = express(); ...

  7. InnoDB 是如何解决幻读的

    前言 大部分人在日常的业务开发中,其实很少去关注数据库的事务相关问题,基本上都是 CURD 一把梭.正好最近在看 MySQL 的相关基础知识,其中对于幻读问题之前一直没有理解深刻,今天就来聊聊「Inn ...

  8. python入门教程之二十三Python3 MySQL 数据库连接 - PyMySQL 驱动

    MySQL 是最流行的关系型数据库管理系统,如果你不熟悉 MySQL,可以阅读我们的 MySQL 教程. 本章节我们为大家介绍使用 mysql-connector 来连接使用 MySQL, mysql ...

  9. [Maven]Maven聚合工程

    一直对此问题好奇,正好有这兴致和时间,有必要了解一下. 所谓聚合项目,实际上就是对项目分模块. 互联网项目一般来说按照业务分(订单模块.VIP模块.支付模块.CMS模块-): 传统的软件项目,大多采用 ...

  10. Docker介绍下载安装、制作镜像及容器、做目录映射、做端口映射

    在计算机中,虚拟化(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源,如服务器.网络.内存及存储等,予以抽象.转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以 ...