我的想法
在我看来,虽然前端在不断的发展,但是前端的思想一直都是在延续,从前端的模块化的实现 到前端的组件化 再到前端的工程化,这些从前的东西都是在以一种更新的思想去发展而不是代替。
当今网络上大部分教学和部分人都在推荐 前端学完html,css和js之后就可以直接学习vue了,但是当你这么去做的时候,会发现在当你自己使用vue的东西的时候,你会充满迷茫以及很多东西不太理解。
我的建议是: 试着去了解一些前端的历史,了解为什么会出现模块化,组件化的诞生和工程化的诞生究竟是因为什么?他们是哪些东西?他们又是怎么去做的? 当你真正了解这些东西之后 一个大的知识面和体系会出现在你的大脑中。
在学习完html css之后,希望你打好js的基础,多用原生js写一些案例,解决一些问题,去学习node 知晓并运用npm这个包管理在js项目中。 然后可以去了解一下前端工程化管理的工具,如webpack 等等 最后再去进行框架的学习
模块化
函数封装
早期并无模块化的概念 函数的出现使得可以把代码封装在一起用于解决问题
function a(){
//代码块
}
function b(){
//代码块
}
<script src="////"></script>
<script src="////"></script>
<script src="////"></script>
很明显,当程序逐渐变大,污染全局变量,而且不同模块之间的变量有可能相互干扰,模块成员之间没有什么关系,随着我们通过在html中引入的js文件也会变多,容易出现命名冲突
对象封装
var moudle = {
// 代码块
}
优点 :利用对象封装既不会污染全局变量,也不会相互干扰,模块成员还可以彼此调用。
缺点 :是暴露了数据,可以通过直接从模块外部更改内部数据。
(IIFE - 自调用函数):
(function() { //代码块 })()
我们采用匿名函数直接调用的方法,在函数内部形成一个执行上下文,在内部封装函数、变量等,避免了引入文件增加而易导致的命名冲突 这里思想就很容易想到闭包
闭包的使用
function closure(){
let m = 9
funtion a(){}
return a() // 函数自执行行
}
优点: 保护函数内部变量的安全 防止变量流入其他环境发生命名冲突。在函数内有缓存的变量可供使用
缺点: 容易造成内存泄漏,因为内部的变量不会被回收,当这些变量太多,会加大内存的消耗
闭包的使用2
- 我们还能这样使用
function closure(){
let m = 2
function a {}
function b {}
return {
a,
b
}
}
这样我们可以在外部调用
比如:
let moudles = closure()
moudles.a()
moudles.b()
它允许我们轻松的组合对象
高层次上,这也是使像Node.js这样的东西成为可能的原因
Command的出现
我们知道,在NodeJS之前,由于没有过于复杂的开发场景,前端是不存在模块化的,后端才有模块化。NodeJS诞生之后,它使用CommonJS的模块化规范。从此,js模块化开始快速发展。
如果您了解了node,可以知道 node中用module.exports定义当前模块对外输出的接口(不推荐直接用exports),用require加载模块。
用法
文件:
module.js
module.exports.one = () => {
console.log('this is funciton one ')
}
module.exports.two = () => {
console.log('this is function two')
}
文件:
main.js
const { one, two } = require('./main')
one()
two()
运行结果
this is funciton one
this is function two
文件:
mainTwo.js
const all = require('./main')
all.one()
all.two()
运行结果
this is funciton one
this is function two
CommonJS用同步的方式加载模块。在服务端,模块文件都存放在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。 所以浏览器需要采用另一种方式模块化
ES6之前使用RequireJS或者seaJS实现模块化, requireJS是基于AMD规范的模块化库, 而像seaJS是基于CMD规范的模块化库, 两者都是为了为了推广前端模块化的工具。
AMD和require.js
AMD是RequireJS使用的规范,是为了浏览器中的模块加载而实现的。并且AMD是异步加载。而AMD的设计思路,也是参考了一部分CommandJS的。
优点:
适合在浏览器环境中异步加载模块。可以并行加载多个模块
缺点:
提高了开发成本,并且不能按需加载,而是必须提前加载所有的依赖。
CMD和sea.js
优点: 同样实现了浏览器端的模块化加载。 可以按需加载,依赖就近。 缺点: 依赖SPM打包,模块的加载逻辑偏重。 其实,这时我们就可以看出AMD和CMD的区别了,前者是对于依赖的模块提前执行,而后者是延迟执行。 前者推崇依赖前置,而后者推崇依赖就近
Es6(原生模块化) --现在常用--
通过export方式导出,在导入时要加{ },export default则不需要,使用export default命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名。 一个文件只能有一个export default
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="module">
import { one, two } from './main.js'
import three from './main.js'
one()
two()
three()
</script>
</body>
</html>
文件:
main.js
export const one = () => {
console.log('this is funciton one ')
}
export const two = () => {
console.log('this is function two')
}
const three = () => {
console.log('this is function three')
}
export default three
----------------------------------------
同样
我们可以导出对象
export default{
}
相信大家很熟悉这个
模块化的出现和思想代表着前端逐渐的发展,并且推动了组件化的思想的发展。因为有了模块化这种把方法的代码块组合在一起解决问题的思想,才有了把方法、模板、样式这些东西集合在一起形成单独的、可复用的组件化的思想,由这些组件拼凑成整个项目。
组件(未完成)
组件化思维也是现在前端最流行的思维模式 主流的框架 像vue react Angular ,前端页面可以借助这些框架来实现组件化开发
当我们使用 Vue 或 React 时,往往会将页面拆分为各种组件,通过拼装组件来形成页面和应用,就像搭积木一样。
那么 我们要去了解的问题是 组件从何而来?
在组件出现之前 我们最常用的可能是模板引擎了
我们可以使用 lodash.template 函数来回忆一下当年是如何用模板开发一个页面的
在 JQuery 盛行的年代,比起组件,“模板引擎”的概念要更加流行。
我们可以使用 lodash.template 函数来回忆一下当年是如何用模板开发一个页面的:
import { template } from 'lodash'
const compiler = template('<h1><%= title %></h1>')
const html = compiler({ title: 'My Component' })
document.getElementById('app').innerHTML = html
模板引擎的概念是:字符串 + 数据 => html。
lodash.template 函数虽然称不上是“引擎”,但足以说明问题。
我们将模板字符串传递给 template 函数,该函数返回一个编译器 compiler,只要把数据传入 compiler 函数,便能得到最终想要渲染的内容。
当数据发生变化时,我们需要使用新的数据重新编译模板:
const newHtml = compiler({ title: 'New Component' })
如果把上面的逻辑封装成一个函数,那么一个组件就诞生了:
const MyComponent = props => {
const compiler = MyComponent.cache || (MyComponent.cache = template('<h1><%= title %></h1>'))
return compiler(props)
}
MyComponent.cache = null
我们可以这样使用它:
document.getElementById('app').innerHTML = MyComponent({ title: 'MyComponent' })
MyComponent 组件也许会带给你这样的感觉:一个组件就是一个函数,给我什么样的数据,我就渲染对应的 html 内容。
这个概念,与我们如今谈论的 Vue 或 React 并没有什么不同。所以,这 就是 组件的本质。
显然易见的,Vue和React这些框架内置把虚拟Dom转换为真实Dom的渲染函数 让我们可以通过封装组件经过内置的渲染函数形成Dom视图,组件的产出就是虚拟Dom
如何去写组件
组件化是什么?
组件化就是把组件化就是把可以复用的、独立的、基础的、功能专一的代码封装起来成为一个单独的可复用的整体,是模板、样式和方法的结合。
比如说一个公司由多个部门 每一个部门就好比一个组件 将这些组件拼凑起来就能构成一个公司 组件化的优点: 低耦合 高复用 更好的团队分工合作 更易维护
工程化(待补充)
前端工程化是什么
前端工程化是从编码发布到运维整个前端开发的生命周期,面临的问题是如何提高编码->测试->维护阶段的生产效率。 前端工程化是使用软件工程的技术和方法来进行前端项目的开发、维护和管理。
前端工程化的任务
前端不仅要保证功能还要考虑性能,要减少http请求数量、压缩、合并、预处理、规范代码、清理、打包、转换等工作。
前端大部分情况下源代码无法直接运行,必须通过转换后才可以正常运行。构建就是做这件事情,把源代码转换成发布到线上的可执行 JavaScrip、CSS、HTML 代码,包括如下内容。
(1)、代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。
(2)、文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
(3)、代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
(4)、模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
(5)、自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。
(6)、代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
(7)、自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
构建其实是工程化、自动化思想在前端开发中的体现,把一系列流程用代码去实现,让代码自动化地执行这一系列复杂的流程。 构建给前端开发注入了更大的活力,解放了我们的生产力。
我们来说一下代码优化的方案
我们从哪个方向入手优化我们的项目呢
- lighthouse (谷歌浏览器)
- bundle分析
- network
使用lighthouse:
点击analyze pageload即可
我们会观察到
有了这些 我们就可以去优化我们的项目了。
那么具体怎么去做呢? 这里我们介绍两个常用的(这里用的工具是vite)
gzip优化 vite-plugin-compression
其他优化策略
bundle分析
vite如何使用bundle分析 下载插件 import visualizer from 'rollup-plugin-visualizer' 配置插件
export default defineConfig({
plugins: [
visualizer(),
]
})
使用npm run build 打包 打包之后会在本地出现一个stats.html的文件 打开之后是这样的:
这样,我们就可以直观的看到 哪个所占的资源多,是什么占据了资源,进而去优化。
network
这个想必大家都知道 所以直接放图
juejin.cn/post/684490… 有需要深入了解的可以看这篇文章
有了加载资源大小 和总时长 相信下手的方向就又多了
这一阶段出现很多工具 如:vite,webpack,Gulp,Grunt 这一时期 我们学习如何配置我们的项目,我们会遇到 Eslint的校验,如何压缩过大的图片以减少资源,如何提高请求速度
希望这篇文章可以对你有所帮助
小节:如果屏幕前的你有对前端学习历程的一些好的方法和思想,真切的希望你可以在评论区留下你宝贵的意见