前端模块化

关于模块化

  1. 模块化是什么?

    模块化是指将一个复杂的系统分解成多个独立的模块的代码组织方式。

  2. 基于node的模块化
    node作为后端语言,有自己的模块化方式,制定了一套规则,作为模块规范化的规则————CommandJs

    什么是CommandJs?
    CommandJs规定了如何规范的实现模块化。
    分为三个阶段:
    模块定义、模块标识、模块引用。

    • 模块定义&模块标识
      exports定义单个模块,一个单独的文件就是一个模块
      exports是module.exports的一个引用,相当于一个指针,只能使用下面这种方式进行定义

      1
      exports.funname=funtion(){}

      举个栗子:

      1
      2
      3
      4
      exports.add=function (a,b) {
      console.log(a+b);
      return a+b;
      }

      导出多个模块:module代表模块自身,模块里面的方法通过module的exports属性导出
      commandjs.js

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      // module.exports = {
      // add: function (a, b) {
      // console.log(a + b);
      // return a + b;
      // }
      // }

      // 再次抽象简洁:
      var add = function (a, b) {
      console.log(a + b);
      return a + b;
      }

      module.exports = {
      add: add
      }
    • 模块引用
      main.js

      1
      2
      3
      4
      5
      const commandjs=require('./commandjs.js');
      const add=function (a,b) {
      return commandjs.add(a,b);
      }
      add(1,2)

      运行 node main.js就能正确引用模块。

      总结:

      定义在服务端node的模块化规范————command.js

    • Command.js不存在变量污染
      在被引用模块中定义的变量不会在引用中产生影响
      require()方法模块引入
      module.exports/exports.funname模块定义

    • 解决了依赖问题

##### 缺点:
- command.js采用同步方式加载模块,不适合浏览器端的读取。

- node 在不借助任何框架库的情况下只支持commandjs,也就是module.exports或者exports导出模块,require引入模块
在服务器端,这样是没有问题的,但是对于前端浏览器来说,是同步加载,会影响响应时间。

- 目前最新的chrome版本已经支持import引入模块,但是需要在script标签的type中标明module属性,表示以模块方式引用
  1. 继续回忆,在前端,如果写了js函数,通过什么方式引入呢?
    最容易想到的,就是,script硬核引入。。。
    很多浏览器是不支持import,require的。
    最新版本的chrome浏览器支持ES6语法中的import,需要在使用import的js文件script标签上注明属性type=”module”。

    1
    <script type="module" src="import.js"></script>

    但是,很多浏览器,不支持这个type,怎么办?

    于是,requirejs推出了AMD~
    AMD!异步模块定义方式

    如何使用requireJs呢?
    require.js基本使用
    require.js配置,解决引用第三方模块/插件
    require.js下载,另存为保存即可

    requirejs使用define定义模块,require引入模块,根据其require函数的特点,传入依赖模块名,回调函数使用依赖模块:
    require()函数在加载依赖的函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。

    • AMD在加载完成定义(define)好的模块就会立即执行,所有执行完成后,遇到require才会执行主逻辑。(提前加载)
  2. AMD是提前加载的机制,还有另一种机制(延迟加载)的规范——CMD
    Common Module Definition
    CMD的使用方法:
    CMD对应的SeaJs

    • CMD在加载完成定义(define)好的模块,仅仅是下载不执行,在遇到require才会执行对应的模块。(按需加载)
  3. 对比
    AMD用户体验好,因为没有延迟,CMD性能好,因为只有用户需要的时候才执行。

  4. ES6

    • ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。
      ES6 模块是编译时加载,使得静态分析成为可能。- 有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。
  1. webpack

前端模块化

为了实现模块化,出现了用于JavaScript模块管理的两大流行规范:CommonJS和AMD。前者定义的是模块的同步加载,主要用于Node.js。同步加载在前端会导致整个页面等待,对前端并不适用,便出现了AMD。AMD采用异步加载方式,通过RequireJS等工具适用于前端。以RequireJS为例,这是一种在线“编译”模块的方案,相当于在浏览器中先加载一个AMD解释器,使浏览器认识define、exports、module等相关命令,来实现模块化。后来ES 6提供了对模块的原生支持,它的目标是创建一种CommonJS和AMD使用者都愿意接受的方式,既拥有简洁的语法,又支持异步加载和配置模块加载。

而目前更通用的是browserify、webpack等技术,是一个预编译模块的方案。这个方案更加智能,因为它是在发布前预编译好的,不需要在浏览器中加载解释器。另外,直接写AMD或ES 6的模块化代码,它都能编译成浏览器识别的JavaScript代码。甚至CommonJS规范的模块化,browserify、webpack也可以转换成浏览器使用的形式。

webpack是一个供浏览器环境使用的模块打包工具。webpack将项目中用到的一切静态资源都视为模块,模块之间可以互相依赖,webpack对它们进行统一的管理及打包发布。


如何使用webpack?

利用webpack编译支持import引入的模块
  1. npm init -y 生成默认脚手架文件 package.json
  2. npm i webpack –save –dev
  3. npm i jquery –save –dev
  4. 新建index.js
  5. 直接通过index.html引入index.js
  6. 浏览器打开index.html import模块失败
  7. 创建./src文件,在其中创建index.js
  8. 使用webpack npx webpack,在./dist中将编译结果保存为main.js,在index.html中引入main.js

./dist 为最终打包编译结果
./src 编写的源文件
./node_modules 引入包
./package.json 脚手架

css引入
  1. webpack module配置项,配置其他文件的引入
    使用test正则匹配相应文件的文件名
    使用use数组加载相应文件需要的插件
  2. npm i css-loader style-loader less-loader less –save-dev
img引入
  1. file-loader加载器
  2. import imgname from ‘./a.jpg’
配置webpack webpack.config.js

利用node模块定义文件配置commandjs

  1. package.json 配置命令简化
    scripts属性:build, start…
  2. module定义文件加载规则
  3. devtool ‘inline-source-map’ 找到错误源头,定位错误位置,不再是定位到bundle.js
多文件入口,出口(多页面)
  1. entry设置为对象,属性名为入口名,属性值为入口文件路径
  2. 出口filename设置为[name].bundle.js
  3. dist文件内容很多,导致文件混乱
    使用插件cleanwebpackplugin,htmlwebpackplugin
    清除出口目录dist乱七八糟的HTML文件,新建入口html文件
    插件可配置
  4. 开发服务器
    浏览器自动刷新
  5. webpack按需加载

最重要的两大配置文件:
package.json
webpack.config.js

package.json中最重要的是scripts配置

1
2
3
4
5
6
"scripts": {
"watch": "npx webpack --watch",
"build": "npx webpack",
"test": "echo \"Error: no test specified\" && exit 1",
"start":"npx webpack-dev-server --open"
}

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 处理路径
const path = require('path');
var HtmlWebpackPlugin=require('html-webpack-plugin')
var CleanWebpackPlugin=require('clean-webpack-plugin').CleanWebpackPlugin
// entry:入口文件
// output:出口文件
// modules:其他类型文件的处理
// --rules:规定相应文件的处理规则
module.exports = {
entry:{
app: path.resolve(__dirname, './src/index.js'),
print:path.resolve(__dirname,'./src/print.js')
},
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, './dist')
},
devtool:'inline-source-map',
module: {
rules: [
{
test:/\.css$/,
use:[
'style-loader',
'css-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
},{
test:/\.(png|jpg|gif|svg)$/,
use:[
'file-loader'
]
}
]
},
plugins:[
new HtmlWebpackPlugin({
title:'myapp',
template:'./public/index.html'
}),
new CleanWebpackPlugin()
]

}

参考资料:
模块化AMD,CMD
Require.js
Sea.js
es6,module
前端模块化