五、模块化开发与规范化标准

一、模块化规范

1.1 CommonJs的特征

  • 一个文件就是一个模块

  • 每个模块都有单独的作用域

  • 通过module.exports导出成员

  • 通过require函数载入模块

但是commonJs是以同步方式加载模块的。

1.2 AMD

AMD是异步处理模块的一种规范

  • AMD使用起来相对复杂

  • 模块JS文件请求频繁

1.3 ESModules 和 CommonJs相结合的规范

在浏览器中使用ESModule,在NodeJs中使用CommonJs规范

可以在package.json中添加type: ‘module’,这个会指定整个文件夹js文件使用ES Modules规范,如果在这个文件下想使用commonjs规范,就要文件后缀名改为.cjs

1.4 ESModules

  • 通过在script标签上定义type = 'module’来定义ESModule

  • 自动采用严格模式,忽略‘user strict

  • 每个ESM都是单独的私有作用域

  • ESM都是CORS请求外部JS模块

  • ESM的script标签会延迟执行脚本(等页面渲染完,再执行JS文件)

导入、导出

import 和 export

  • export和import导入导出并不是对象的解构赋值,而是一种特殊的用法

  • 并且export导出的只是值的引用,并不是导出值

// a.js
let name = 'aaa'
export {  name }


// b.js
import {name} from './a.js'
  • import 导入可以产生一个promise
import('./foo.js').then((module) =>{console.log(module)})

ES Modules in node

  • ES Modules 中可以导入CommonJs模块

  • CommonJs中不能导入ES Modules模块

  • CommonJs始终只会导出一个默认成员

  • 注意import并不是解构导出对象

1.5 polyfill

cdn服务网址https://unpkg.com/

二、Webpack

webpack是一个模块化打包工具,通过loader去处理非javascript文件,plugins扩展

yarn add  webpack@4.44.2   webpack-cli@3.3.12  -D

2.1 模块化打包工具的由来

  • ES Modules 每个模块都会请求,浪费请求资源

  • ES Modules 存在兼容问题

  • 前端所有资源都需要打包

模块化打包工具就是解决这些问题的

2.2 webpack概要

  • webpack是一个模块化打包工具

  • 通过loader编译代码

  • 具有spliting code功能

  • 前端项目中所有资源都可以才有模块化的方式引入

2.3 webpack使用

webpack是一款打包工具,通过loader加载编译资源文件,通过plugins实现除了加载资源以外其他自动化的操作。

webpack推荐根据代码的需要动态导入资源,因为真正需要资源的不是应用,而是正在编写的代码

  • 逻辑合理,建立了代码与资源文件的依赖关系,Js确实需要这些资源文件

  • 确保上线资源不确实,都是必要的。

学习新东西,要学习新思想,这样才能更好的提高。

对于大的文件就用file-loader,分离文件,对于小的文件可以用Data Urls的形式,打包到代码中。

2.4 webpack常用loader加载器分类

  • 编译转换类

  • 文件操作类

  • 代码检查类

2.5 webpack核心工作原理

webpack会通过入口文件,推断出每个文件所需要的资源依赖关系,形成一个依赖关系树,然后交给rules去分析,哪个模块需要哪个loader处理。

2.6 loader实现的简单工作原理

const markd = require('marked')
module.exports = source => {
  console.log(source)
  let htmlStr = markd(source)
  return `module.exports = ${JSON.stringify(htmlStr)}`
}
  • 首先module.exports 函数

  • source变量接收资源文件

  • 然后通过return 一个js代码字符串

最后webpack输出的代码为:

...
 ((module) => {

module.exports = "<h1 id=\"标题\">标题</h1>\n<h2 id=\"二级标题\">二级标题</h2>\n<blockquote>\n<p>哈哈哈</p>\n</blockquote>\n"

/***/ })
...

并且loader是通过流的方式进行的,一个loader处理完,可以将处理完的结果给另外一个loader处理,比如css-loader,style-loader

2.7 plugins

定义: 一个函数或者是一个包含apply方法的对象。

Webpack4不求人(5) ——编写自定义插件 - xialeistudio - 博客园

自定义插件工作流程:

Webpack在启动时,会实例化插件对象,在初始化compiler对象之后会调用插件实例的apply方法,传入compiler对象,插件对象会在aplly中注册响应的钩子,webpack执行过程中就会根据构建过程回应响应的钩子。

Compiler && Compilation对象

在编写Webpack插件过程中,最常用也是最主要的两个对象就是Webpack提供的Compiler和Compilation,Plugin通过访问Compiler和Compilation对象来完成工作。

  • Compiler 对象包含了当前运行Webpack的配置,包括entry、output、loaders等配置,这个对象在启动Webpack时被实例化,而且是全局唯一的。Plugin可以通过该对象获取到Webpack的配置信息进行处理。

  • Compilation对象可以理解编译对象,包含了模块、依赖、文件等信息。在开发模式下运行Webpack时,每修改一次文件都会产生一个新的Compilation对象,Plugin可以访问到本次编译过程中的模块、依赖、文件内容等信息。

常见钩子

Webpack会根据执行流程来回调对应的钩子,下面我们来看看都有哪些常见钩子,这些钩子支持的tap操作是什么。

钩子说明参数类型
afterPlugins启动一次新的编译compiler同步
compile创建compilation对象之前compilationParams同步
compilationcompilation对象创建完成compilation同步
emit资源生成完成,输出之前compilation异步
afterEmit资源输出到目录完成compilation异步
done完成编译stats同步

Tapable

Tapable是Webpack的一个核心工具,Webpack中许多对象扩展自Tapable类。Tapable类暴露了tap、tapAsync和tapPromise方法,可以根据钩子的同步/异步方式来选择一个函数注入逻辑。

  • tap 同步钩子
  • tapAsync 异步钩子,通过callback回调告诉Webpack异步执行完毕
  • tapPromise 异步钩子,返回一个Promise告诉Webpack异步执行完毕

apply方法中插入钩子的一般形式如下:

compileer.hooks.阶段.tap函数('插件名称', (阶段回调参数) => {

});

简单的实例

// 移除webpack打包注释的插件
class MyPlugin {
  constructor(options){}
  apply(compiler){
    // 插入钩子函数
    console.log('MyPlugin enter')
    compiler.hooks.emit.tap('MyPlugin', (compilation) => {
      for( let name in compilation.assets){
        if(name.endsWith('.js')){
          const contents = compilation.assets[name].source()
          const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
          compilation.assets[name] = {
            source: () => withoutComments,
            size: () => withoutComments.length
          }
        }
      }
    })
  }
}

module.exports = MyPlugin

2.8 webpack-dev-server

webapck实现自动编译和自动刷新浏览器

yarn add webpack-dev-server -D

运行时就用 yarn webpack-dev-server

webpack-dev-server并没有产生磁盘的读写操作,每次编译和刷新都是读的内存中的代码

2.9 webpack开发时,设置访问静态资源目录

告诉服务器从哪里提供静态资源

devServer: {
  contentBase: path.join(__dirname, "public"),
}

2.10 webpack配置代理

  devServer: {
    proxy: {
      '/api': {
        target: 'https://api.github.com/users',
        pathRewire: { '^/api': '' },
        changeOrigin: true,
      }
    }
  },

2.11 Source Map

Source Maps其实就是打包之后的代码与源代码的映射关系

  • eval-是否使用eval执行模块代码

  • cheap- source map 是否包含行信息

  • module- 是否能够得到Loader处理之前的源码

个人开发的选择Sourc Map模式

开发环境: cheap-module-eval-source-map

生产环境: none

2.12 HMR (Hot Module Replacement)模块热替换

webpack-dev-server的问题:

自动打包刷新,会造成页面状态的丢失

HMR定义: 模块热替换功能会在应用程序运行过程中替换、添加、删除模块,无需重新加载整个页面。主要通过以下方式加快开发速度

  • 保留在完全重新加载页面时丢失的应用程序状态。

  • 只更新变更内容,以节约宝贵时间。

  • 调整样式更加快速-几乎相当于在浏览器中修改样式。

开启HMR

模块热替换 | webpack 中文网

2.13 DefinePlugin

DefinePlugin允许配置一个全局的常量

详见:DefinePlugin

2.14 Tree Shaking

定义: 移除javascript上下文中的未引用代码(dead-code)。

使用前提:webpack打包的代码必须使用ES Module(ESM)

最新版本的babel-loader并不会导致tree-shaking失效,可以通过查看源码来查

module.exports = {
    ...
  optimization: {
    usedExports: true,  // 给dead-code做标记
    minimize: true // 清除dead-code并压缩代码
  },
}

2.15 concatenateModules合并模块

定义: 告诉webpack哪些片段可以安全的合并到一个模块中。在生产模式自动开启

module.exports = {
    ...
  optimization: {
    usedExports: true,  // 给dead-code做标记
    concatenateModules: true 
  },
}

2.16 sideEffects副作用

定义: 告知 webpack 去辨识 package.json 中的 副作用 标记或规则,以跳过那些当导出不被使用且被标记不包含副作用的模块。

  • 一般用于npm的包标记是否有副作用

  • 副作用是指,做了,除导出成员以外的事情

详见webpack 官方中文文档optimizationsideeffects

注意: 除非确定了整个项目中没有副作用,才会在package.json配置sideEffects:false,否则,就要配置sideEffects: []数组的形式,标记哪些文件没有副作用。

2.15 Code Splitting(代码分包/代码分割)

有两种

  • 多入口打包

  • 采用ESM的动态导入按需加载 采用import().then()的方式动态导入按需加载,webpack会自动分包按需加载

2.16 魔法注释

给分包的bundel添加名字,用法如下,可以给把不同的模块打包到一起

import(/* webpackChunkName:  'posts'*/ './posts/posts').then(({default: posts}) => {
    //
})

2.17 MiniCssExtractPlugin

这个插件主要是将css提取到一个单独的css文件中,采用link的形式

一般css超过120k才会考虑单独拎出来,否则会适得其反。

提取之后需要OptimizeCssAssetsWebpackPlugin插件去压缩css文件

像这种压缩的可以配置optimization中的minimizer属性数组中,但是这样配置之后js代码就不会去压缩,需要重新安装TerserWebpackPlugin,配置在minimizer数组中。

配置minimizer中的好处就是只在需要压缩的时候才会调用插件,缺点就是css和js都要自定义安装插件去压缩

2.18 文件缓存问题,可以给文件配置hash

[contenthash] 文件变化就会更新掉文件名,最适合解决缓存问题

三、Rollup

3.1 定义

Rollup是一款ESM打包器,它的存在并不是为了webpack的竞争,而是为了提供一个充分利用ESM各项特性的高效打包器。

  • 默认开启tree-shaking优化代码

  • Rollup 只引入最基本最精简代码,所以可以生成轻量、快速,以及低复杂度的 library 和应用程序

  • 插件是rollup中的唯一途径

3 .2 Rollup和Webpack比较

rollup

优点:

  • 输出结果更扁平,执行效率更高

  • 自动移除未引用的代码

  • 打包结果完全可以读,基本和手写代码一致

缺点:

  • 加载非ESM的第三方模块比加复杂。

  • 分包必须要要使用AMD模式,需要require.js去加载打包后的结果。

  • 打包的后结果在立即执行函数里面,无法做HMR替换

四、规范化标准

eslint和stylelint、prettier、git-hooks(用Husky和lint-staged实现git hooks的需求)

eslint使用

在 .eslintrc.js中安装eslint-plugin-react 之后

module.exports = {
    extends: [
        'standard',
        'plugin:react/recommended' // 继承eslint-plugin-react中的配置
    ],
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值