前端工程化实践

最近将重构系统,需要重新搭建前端项目,遂将前端工程化相关知识进行整理。

模块化规范

传统开发模式的弊端

还记得早期没使用模块化的项目,一旦项目复杂起来,依赖变多,就会出现各种各样莫名奇妙的问题。

  1. 命名冲突,这会导致本来运行良好的代码,突然有一天不行了,那你就要找找是不是别人把你的方法覆盖了。
  2. 依赖问题,有依赖关系的js代码,必须按照正确的顺序通过script标签进行加载。

模块化规范

通过模块化的方式,可以把单独的功能封装到模块中,通过api向外暴露方法或者变量,同时也可以依赖别的模块。
增强了代码的可重用性,可维护性。

我们看看有哪些模块化规范
在这里插入图片描述
AMD,CMD以及CommonJs都并非官方规范,只有ES6中模块化规范才是语言层面的通用化标准。

AMD

AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

引入requireJs

//通过async保证 requireJs的异步加载,defer保证IE兼容
<script src="js/require.js" defer async="true" ></script>

通过define函数定义模块

//可以指定依赖,加载当前模块之前先加载 mylib
define(['myLib'], function(myLib){
   function foo(){
     myLib.doSomething();
   }
   return {
    foo : foo
   };
});

通过require加载模块

require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
});
CMD

CMD 是 sea.js 在推广过程中对模块定义的规范化产出

加载入口模块

// 加载入口模块
seajs.use("...)

定义模块

// 所有模块都通过 define 来定义
define(function(require, exports, module) {

  // 通过 require 引入依赖
  var $ = require('jquery');
  var Spinning = require('./spinning');

  // 通过 exports 对外提供接口
  exports.doSomething = ...

  // 或者通过 module.exports 提供整个接口
  module.exports = ...

});
AMD和CMD的区别

CMD 推崇依赖就近,AMD 推崇依赖前置

AMD优点:加载快速,尤其遇到多个大文件,因为并行解析,所以同一时间可以解析多个文件。
AMD缺点:并行加载,异步处理,加载顺序不一定,可能会造成一些困扰,甚至为程序埋下大坑。

CMD优点:因为只有在使用的时候才会解析执行js文件,因此,每个JS文件的执行顺序在代码中是有体现的,是可控的。
CMD缺点:执行等待时间会叠加。因为每个文件执行时是同步执行(串行执行),因此时间是所有文件解析执行时间之和,尤其在文件较多较大时,这种缺点尤为明显。

CommonJs

服务端的模块化规范

模块导出

module.exports.add = function (x, y) {
  return x + y
}

模块导入

var myModule = require('./test')
myModule.add(1,2)
ES6模块化规范

ES6提出的模块化规范是语言层面的,也就是说在浏览器端和服务端都可以使用(理想情况下)

  • 每个js文件都是单独的模块
  • 导出模块用export
  • 导入模块用import

babel编译器

既然ES6都给出了模块化规范,为什么还需要babel编译器呢?
语言标准是标准,并不意味着了浏览器和node.js已经实现标准,即使从某个版本开始已经支持,但客户不一定更新客户端。通过babel我们可以尽情的使用 ES6的新语法,而不用考虑兼容性问题,babel会把新语法转换成老的js语法。

babel、babel-polyfill 和 babel-runtime
  • babel 将 Javascript 分为 synax(语法)和 api 两部分,babel 只负责编译 ECMAScript 的最新语法,比如 let、 const、 箭头函数等,而对于新的 api 比如 Promise、include、Object.assign 这种则不进行编译,而是交给 babel-polyfill 进行编译。
  • babel-polyfill 负责将新的 API 封装成浏览器可识别的 API 来覆盖原生,并将所有生成的覆盖代码插入项目源码。
  • babel-polyfill 虽然提供了新的 API 支持,但是缺陷也比较明显:
    全局注册覆盖的 API,污染全局变量
    会将新的 API 全部封装导出到源码,不管项目中有没有使用过相关内容,导致导出项目包增大。
  • babel-runtime 针对 babel-polyfill 的问题做了优化:
    通过导出包 / 引入包的方式提供新封装的 API ,解决了污染全局变量的问题
    结合 babel-transform-runtime-plugin 实现自动检测项目所用到的 API ,有选择的编译和引入,解决了导出包增大的问题。
    但是 babel-runtime 有个问题,就是不能支持实例上的新增 API 方法,例如[1,2,3].includes(1) 这种。
  • babel-polyfill 和 babel-runtime 对比:
    babel-polyfill 适合在比较大的项目中引入。
    babel-runtime 适合在组件、类库中引用。
babel的编译原理

转译分为三个阶段:

  • 解析,将代码解析生成抽象语法树AST,也就是词法分析与语法分析的过程
  • 转换,对语法树进行变换方面的一系列操作。通过babel-traverse,进行遍历并作添加、更新、删除等操作
  • 生成,通过babel-fenerator将变换后AST转换为JS代码。

在这里插入图片描述

babel插件的原理

babel插件介入的是上图的Transform过程,我们可以修改AST,从而影响最终AST

webpack打包

本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack
处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency
graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。

传统开发模式弊端
  • 文件依赖关系错综复杂
  • 静态资源请求效率低
  • 模块化支持不友好
  • 浏览器对高级javascript特性兼容程度较低
webpack的好处

webpack提供了友好的模块化支持,以及代码压缩混淆,处理js兼容问题,性能优化等强大功能,从而让程序员的工作重心放到具体的功能实现上。

loader 和 plugin 的区别
  • Loader直译为"加载器"。Webpack将⼀切⽂件视为模块,但是webpack原⽣是只能解析js⽂件,如果想将其他⽂件也打包的话,就会⽤到 loader 。 所以Loader的作⽤是让webpack拥有了加载和解析⾮JavaScript⽂件的能⼒。
  • Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运⾏的⽣命
    周期中会⼴播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
常用的loader

配置方式如下

 module:{
        rules :[
            {
                test:/\.css$/,
                use:['style-loader','css-loader']
            },
            {
                test:/\.less$/,
                use:['style-loader','css-loader','less-loader']
            },
            {
                test:/\.jpg|png|gif$/,
                use:['url-loader?limit=470?&outputPath=images']
            },
            {
                test:/\.js$/,
                use:'babel-loader',
                exclude :'/node_modules/'
            },
            {
                test:/\.vue$/,
                use:['vue-loader'],
            }

        ]
    },
  • file-loader:把⽂件输出到⼀个⽂件夹中,在代码中通过相对 URL 去引⽤输出的⽂件
  • url-loader:和 file-loader 类似,但是能在⽂件很⼩的情况下以 base64 的⽅式把⽂件内容注⼊到代码中去
  • source-map-loader:加载额外的 Source Map ⽂件,以⽅便断点调试
  • image-loader:加载并且压缩图⽚⽂件
  • babel-loader:把 ES6 转换成 ES5
  • css-loader:加载 CSS,⽀持模块化、压缩、⽂件导⼊等特性
  • style-loader:把 CSS 代码注⼊到 JavaScript 中,通过 DOM 操作去加载 CSS,通常配合css-loader一起使用
  • eslint-loader:通过 ESLint 检查 JavaScrip
常用的plugin

配置方式如下:

plugins : [htmlplugin,cleanPlugin,vueLoaderPlugin],
  • define-plugin:定义环境变量
  • html-webpack-plugin:简化html⽂件创建
  • uglifyjs-webpack-plugin:通过 UglifyES 压缩 ES6 代码
  • webpack-parallel-uglify-plugin: 多核压缩,提⾼压缩速度
  • webpack-bundle-analyzer: 可视化webpack输出⽂件的体积
  • mini-css-extract-plugin: CSS提取到单独的⽂件中,⽀持按需加载

引入vue框架

现在我们已经可以通过webpack打包应用,那我们如何引入vue框架呢?直接创建vue文件,webpack是不认识的,所以需要引入vue-loader,来解析vue文件

//添加插件
plugins : [htmlplugin,cleanPlugin,vueLoaderPlugin],

//配置加载器
module.rules:[
				{ test:/\.vue$/,
                use:['vue-loader'],}
               ]

配置好之后我们就可以在项目里使用vue框架了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

csw_coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值