推荐阅读
Vue3源码
- vue3源码:01.代码管理策略-monorepo
- vue3源码:02.项目构建流程和源码调试方法
- vue3源码:03.Vue3响应式核心原理
- vue3源码:04.Vue3响应式系统源码实现1
- vue3源码:05.Vue3响应式系统源码实现2
- vue3源码:06.reactive、ref相关api源码实现
- vue3源码:07.故事要从createApp讲起
- vue3源码:08.虚拟Node到真实Node的路其实很长
- vue3源码:09.组件渲染和更新流程
- vue3源码:10.名动江湖的diff算法
- vue3源码:11.编译优化之Block Tree 与 PatchFlags
- vue3源码:12.编译过程介绍及分析模版AST的生成过程
- vue3源码:13.从AST到render函数(transform与代码生成)
微前端源码
- 微前端01 : 乾坤的Js隔离机制原理剖析(快照沙箱、两种代理沙箱)
- 微前端02 : 乾坤的微应用加载流程分析(从微应用的注册到loadApp方法内部实现)
- 微前端03 : 乾坤的沙箱容器分析(Js沙箱机制建立后的具体应用)
- 微前端04 : 乾坤的资源加载机制(import-html-entry的内部实现)
- 微前端05 : 乾坤loadMicroApp方法实现以及数据通信机制分析
- 微前端06 : single-spa的注册机制
- 微前端07 : 对single-spa的路由管理及微应用状态管理的分析
- 微前端08 : single-spa中的reroute函数
React18源码
欢迎关注微信公众号:杨艺韬
更多前端源码剖析相关内容
有些非前端朋友希望介入一些前端的工作,可以参考本文,拟定一个初步的学习计划
大纲
- 现代前端的本质
- 前端工具链的脉络
- 参与基础开发需要掌握的最小知识量
- 介入实际开发工作的步骤
现代前端的本质
前端的原始形态
所需知识:html(页面元素) + css(页面元素的样式效果) + javascript(动态对元素增删改查,动态调整样式)
我们通过浏览器查看页面源码或者审查元素,可以直观的体验前端的原始形态。
示范
新建文件hello.html
,在编辑器中输入下面的内容:
<!-代码片段1-->
<html>
<head></head>
<body>
<div id="root"> hello world</div>
</body>
<script>
setTimeout(()=>{
document.getElementById("root").innerHTML = "修改过后的 hello world"
},3000)
</script>
</html>
输入完成后保存文件,在浏览器中打开这个页面,会发现页面上会展示hello world
,在3秒钟后,页面展示的内容变化为修改过后的 hello world
。
通过上面的体验,我们可以发现这样的前端程序有两个明显的特点:
特点一: 编写的代码不需要任何额外的处理,浏览器马上可以解析执行,程序只包含浏览器能识别的html
标签和js
代码(为了程序简洁,这里没写css
相关代码)。
特点二: 如果要修改页面上的内容,需要先直接操作DOM节点。举个例子,如果我们从服务器请求了一个很长的列表数据,我们需要手动根据这些数据创建不同的DOM节点,然后挂在到页面中去。这个过程如果处理的好,可以性能优良的运行,如果处理不好极有可能产生性能问题,同时编码复杂度会增加。
现代前端
所需知识:html + css/less/scss + javascript + (vue、vuex、pinia、 vue-router...)/react/angular + node + webpack/vite/rollup + babel + ...
从所需知识来看,现代前端需要掌握大量的知识,下面我们以vue2.6
为例(实际工作中很多旧项目用的还都是vue2.6,所以没用vue3或react来举例),实现一个和代码片段1类似的功能。新建文件hello.vue
,输入下面内容:
// 代码片段2
<template>
<div>{{textStr}}</div>
</template>
<script>
export default {
data(){
return {
textStr:'Hello World'
}
},
mounted(){
setTimeout(()=>{
this.textStr = '修改过后的 Hello World'
},3000)
}
}
</script>
<style lang="less" scoped>
</style>
注:实际上,用vue2.6实现代码片段1类似的功能,可以有很多种不同的写法,但在实际工程项目实践中通常是代码片段2所示的形态。
如果大家了解了html、css、javascript
的基础语法,会发现代码片段2虽然看起来和前端程序的原始状态有几分相似,但是实际上是不符合相关语法规范的(比如代码片段2中出现的template
、less
、{{textStr}}
等内容,对于浏览器来说都比较陌生),换句话说,浏览器是不能识别代码片段2的相关代码,浏览器只能识别符合html、css、javascript
语法规范的程序。
既然我们写的hello.vue
不能被浏览器正确识别,毫无疑问我们需要做一件事情:
将浏览器不能识别的内容,转化为浏览器可以识别的内容
那我们需要转化成什么样的内容,浏览器才可以识别呢?
答案其实很简单,转化成符合html、css、javascript
相关语法规范的代码即可。以代码片段2为例,我们应该将程序转化成下面两种形式之一:
形式一:使用带编译功能的vue版本,程序内部会解析处理下面template
所指向的内容
// 代码片段3
new Vue({
el:'#root',
template: '<div>{{helloTest}}</div>',
data(){
return {
helloTest: 'hello world'
}
}
})
注:这里代码片段3的
template
对应了代码片段2中的template
相关的内容,我们称之为模版。vue
在底层最终会将这些模版表示的元素转化成真正的html
元素。
形式二:提前进行编译处理,将template
转化为render
函数,只需要运行时
的vue版本
// 代码片段4
new Vue({
el:'#root',
data(){
return {
helloTest: 'hello world'
}
},
render:h=>{
return h('div',{},[this.helloTest])
}
})
相较于代码片段3,代码片段4中传给Vue
构造函数的参数中没了template
属性,多了一个render
函数,这个render
函数就是由template
转化而来,在该render
函数的底层,实际上会把模版所对应的内容转化为对应的Javascript
对象,我们称这些对象为虚拟DOM,然后再将这些虚拟DOM转化为真实DOM呈现在页面上。
注:
Vue
的编译时相关代码主要就是将模版转化为render
函数,如果这个转化的过程如果体现完成,我们的程序发布到线上环境的时候,我们所引入的Vue.js
就可以不用带编译相关功能,这也就是上文形式一和形式二根本的不同。
这时我们可能会产生一个疑问:
代码**片段3**和代码**片段4**为什么能被浏览器识别呢?
因为这里的new Vue({...})
操作,就是通过new
操作符初始化一个Vue
实例,而Vue
是一个普通的JavaScript
构造函数,只不过在初始化的过程中,Vue
在内部执行了许多逻辑。
注:所谓学习
Vue
,就是编写Vue
能识别的代码。比如代码片段3中的{{helloTest}}
,虽然浏览器不识别,但是Vue
知道如何将其转化为浏览器能识别的内容。
有了上面的认识,我们可能会产生一个新的疑问:
为什么我们不直接编写成代码片段3或代码片段4的代码,而要写成代码片段2的代码?
答案其实很简单,至少有下面几个原因:
- 代码片段3中的
template
写着体验不好- 代码不好组织管理
到这里,我们已经回答了上面的问题那我们需要转化成什么样的内容,浏览器才可以识别呢?
。但新问题是:
我们怎么去转化了呢?怎么把浏览器不识别的代码片段2的内容转化成浏览器能识别的代码片段3或代码片段4的内容?
答案是什么我们先不去深究,但我们这里要知道,肯定是通过一个程序去完成这个转化的过程。那既然是程序,我们可以是C++、Java、Python、Go、Rust
等各种各样的程序来实现这个转化的过程,但是在前端生态圈,这些转化程序一般都是通过Javascript
编写的。
这会使我们陷入新的疑惑,我们前面不是提到过浏览器可以正确的识别JavaScript
程序吗?那我们的这些转化程序不可能是到浏览器上面执行吧?其实从理论上讲,如果非要这么做也是可以实现的,但是这不具备工程实践意义,我们通常是在本地开发的时候就完成了这个转化的过程。既然是在本地开发过程中实现这个转化过程,那我们用JavaScript
编写的转化程序怎么才能执行呢?请看下文。
前端工具链的脉络
前端工具链的基石 - Node.js
Node是什么
Node
是一个基于Chrome V8引擎的JavaScript运行环境。
能够解析javascript,调用操作系统的能力,比如文件读取,端口监听等等。这其实也就回答了上文关于如何在非浏览器环境下执行JavaScript
程序的问题。
注:推荐nvm管理Node环境
有了执行JavaScript
程序的平台,那我们到底用哪个程序来转化呢,将浏览器不认识的hello.vue
转化成浏览器可以识别的内容。答案是多样的,webpack、vite、esbuild、rollup...
都是行业知名的前端程序构建工具。其实准确的讲,转化的过程来讲并不是由这些工具来实现的,而是在这些工具所属的体系下,集成某些能力来进行转化。甚至于我们可以自己编写一些特定的程序来进行特定的转化,但这在实际工作中不太普遍。鉴于工作中有很多项目还在用webpack
,本文以webpack
来简要介绍。
webpack
webpack的本质
可以这样来简单的理解其核心工作。webpack
以一个入口文件开始,把相互依赖的js、css集成在一起(当然也可以按规则拆分),产生出诸如xxx.js
、xx.css
的文件嵌入一个所配置的html文件中。
webpack
官网是这样解释的:
At its core,
webpack
is a static module bundler for modern JavaScript applications.
webpack
作为一个通用的前端程序构建工具,会在多种场景下工作,比如在js
文件中遇见代码import HelloWorld from './helloworld.vue'
, 正常也是无法识别的。但webpack
提供了可扩展的机制,其中loader
和plugin
是webpack
两个重要的概念,对于文件的识别一般通过loader
来实现。比如对于vue
而言,就有vue-loader
来进行处理,至于vue-loader
具体如何工作,大家可以读完本文后去查阅相关资料了解。
上面描述了那么多的内容来进行程序的转化,一个新问题可能从脑海中浮现: 既然经历了复杂的过程,最终还是要转化成前端程序的原始状态,那为什么要用Vue来写程序?
- 很少去手动操作DOM,专注于数据层面的变化,提高开发效率;
- 可以配合前端工具一定程度上提高兼容性处理的效率(如高版本
js
代码转低版本--bable
); - 给了特定的开发范式,代码可读性可维护性增强(组件化,css预处理等等);
- 完善的生态系统,可直接使用的成熟的库。
其实有了上面的知识,我们已经可以进行程序的开发了,但是还有许多工程实践问题需要我们处理:怎么对项目进行管理,具体的,比如怎么引入依赖,怎么启动项目??
如果没有工具,我们需要手动下载一些依赖包放到某个地方去,然后通过特定的路径去导入引用,这样做的效率会很低,同时繁琐且容易出错。这时我们需要借助一些程序的包管理工具。行业中有npm、yarn、pnpm...
等很多优秀的工具,本文以npm
为例进行介绍。
包管理工具——npm
安装、卸载、更新...
npm intall xxx # 安装什么依赖包,依赖包从哪里来,安装到哪里去,安装了怎么用
npm uninstall xxx
npm run xxx # 执行什么指令
package.json 一个npm用的包管理配置文件,记录了诸多信息,参看npm官方文档
node_modules package.json对应的目录下会自动创建改目录,存放下载的安装包
模块
import {xxx} from 'xxx'
import Xxx from 'xxx'
本质上就是引入了一个js文件中的某些暴露的方法或对象
注:推荐nrm工具来切换npm镜像源
当前需要掌握的知识
- js的基本语法《JavaScript权威指南》;
- vue基本语法、vuex/pinia、vue-router等等;
- node、npm 基本环境的配置,webpack的简单应用;
- 组件库 element-ui的了解。
有了上面的知识,可以支撑我们从事一些普通的管理后台系统的开发。
介入开发的工作安排
- 掌握上面的基础知识;
- 有兴趣的伙伴阅读项目代码,基本了解并将项目跑起来尝试写些代码;
- 自己评估,如果有信心就可以进入开发了;
- 建议
- 开发新功能,尽量不改旧功能,因为历史原因,有些代码相互关联很容易出错;
- 前期评估时间尽量充裕些,前期提交的代码最好CodeReview下,适应一段时间自己提交自己改bug;
- 可以网上找点教程听一听,不过需要注意公司实际项目中用的是Vue2还是Vue3又或者是React,别学错了。
更多前端文章,欢迎访问我的掘金主页