介绍模块化
1、模块化
很久以前,开发网页要通过命名空间的方式来组织代码。JQuery库将他的API都放在window.$下。
这样做存在的问题:
(1)命名空间冲突
(2)无法合理管理项目的版本和依赖
(3)无法控制依赖的加载顺序
模块化
模块化规范 | 核心思想 |
---|---|
CommonJS | 通过require方法来同步加载依赖的其他模块,通过module.exports导出需要暴露的接口 |
AMD(require.js) | 通过异步加载依赖的其他模块,主要解决针对浏览器环境的模块化问题。 |
ES6模块化 | 通过import方法来导入模块,通过export导出需要暴露的接口 |
//导入
const moduleA = require('./moduleA');
//导出
module.exports = moduleA.someFunc
CommJS优点:代码可复用在Node.js环境下并运行,通过NPM发布的很多第三方模块都采用了CommonJS规范
缺点:代码无法直接在浏览器运行环境下,必须通过工具转换成标准的ES5
//定义一个模块
define('module',['dep'],function(dep){
return exports;
})
//导入和使用
require(['module'],function(module){
})
AMD优点:在可以不转换代码的情况下直接在浏览器中运行;可异步加载依赖,可并行加载多个依赖
缺点:Javascript 运行环境没有原生支持AMD,需要先导入实现了AMD的库后才能正常使用
//导入
import {readFile} from 'fs'
//导出
export function hello(){}
export default{
//...
}
ES6缺点:无法直接运行在大部分JavaScript运行环境上,必须通过工具转换成标准的ES5才能正常运行
webpack介绍
webpack是一个打包模块化Javascript的工具,在webpack里面一切文件皆模块,通过Loader转换文件,通过Plugin注入钩子,最后输出多个模块组合成的文件
使用
module.exports = {
//所有模块的入口,webpack从入口开始递归解析出所有依赖的模块
entry:‘./app.js',
output:{
//将入口所依赖的所有模块打包成一个文件bundle.js
filename:'bundle.js'
}
}
优点:
- 专注处理模块化的项目 开箱即用;
- 可通过Plugin扩展
安装Webpack
先保证安装了5.0.0以上的Node.js
//初始化最简单的采用了模块化开发的项目
npm init
npm i -D webpack
使用webpack
webpack在执行构建时默认会从项目根目录下的webpack/config.js文件中读取配置。还需要新建文件
const path = require('path');
module.exports = {
// JavaScript 执行入口文件
entry: './main.js',
output: {
// 把所有依赖的模块合并输出到一个 bundle.js 文件
filename: 'bundle.js',
// 输出文件都放到 dist 目录下
path: path.resolve(__dirname, './dist'),
}
};
运行项目:
先安装webpack-cli
再运行 npx webpack
这时目录下多出一个dist文件夹,里面有bundle.js
运行index.html
使用Loader
编写样式文件css.后去执行 Webpack 构建是会报错的,因为 Webpack 不原生支持解析 CSS 文件。要支持非 JavaScript 类型的文件,需要使用 Webpack 的 Loader 机制。Webpack的配置修改使用如下:
const path = require('path');
module.exports = {
// JavaScript 执行入口文件
entry: './main.js',
output: {
// 把所有依赖的模块合并输出到一个 bundle.js 文件
filename: 'bundle.js',
// 输出文件都放到 dist 目录下
path: path.resolve(__dirname, './dist'),
},
module: {
rules: [
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ['style-loader', 'css-loader'],
}
]
}
};
Loader 可以看作具有文件转换功能的翻译员,配置里的 module.rules 数组配置了一组规则,告诉 Webpack 在遇到哪些文件时使用哪些 Loader 去加载和转换。 如上配置告诉 Webpack 在遇到以 .css 结尾的文件时先使用 css-loader 读取 CSS 文件,再交给 style-loader 把 CSS 内容注入到 JavaScript 里。 在配置 Loader 时需要注意的是:
use 属性的值需要是一个由 Loader 名称组成的数组,Loader 的执行顺序是由后到前的;
每一个 Loader 都可以通过 URL querystring 的方式传入参数,例如 css-loader?minimize 中的 minimize 告诉 css-loader 要开启 CSS 压缩。
想知道 Loader 具体支持哪些属性,则需要我们查阅文档,例如 css-loader 还有很多用法,我们可以在 css-loader 主页 上查到。
在重新执行 Webpack 构建前要先安装新引入的 Loader:
npm i -D style-loader css-loader
使用Plugin
Plugin用来扩展功能,通过 Plugin 把注入到 bundle.js 文件里的 CSS 提取到单独的文件中,配置修改如下:
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// JavaScript 执行入口文件
entry: './main.js',
output: {
// 把所有依赖的模块合并输出到一个 bundle.js 文件
filename: 'bundle.js',
// 把输出文件都放到 dist 目录下
path: path.resolve(__dirname, './dist'),
},
module: {
rules: [
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ExtractTextPlugin.extract({
// 转换 .css 文件需要使用的 Loader
use: ['css-loader'],
}),
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 .js 文件中提取出来的 .css 文件的名称
filename: `[name]_[contenthash:8].css`,
}),
]
};
要让以上代码运行起来,需要先安装新引入的插件:
npm i -D extract-text-webpack-plugin
安装成功后重新执行构建,你会发现 dist 目录下多出一个 main_1a87a56a.css 文件,bundle.js 里也没有 CSS 代码了,再把该 CSS 文件引入到 index.html 里就完成了。
使用DevServer
实际开发中我们需要HTTP服务而不是使用本地文件预览;监听文件的变化并且自动刷新网页,做到实时预览。
DevServer会启动一个HTTP服务器用于服务网页请求,同时会帮助启动Webpack,并接受文件变更信号,通过webSocket协议自动刷新网页做到实时预览。
安装DevServer
npm i -D webpack-dev-server
这意味着 DevServer 启动的 HTTP 服务器监听在 http://localhost:8080/ ,DevServer 启动后会一直驻留在后台保持运行,访问这个网址你就能获取项目根目录下的 index.html。
页面并没有实时变化:解决办法
由于 DevServer 不会理会 webpack.config.js 里配置的 output.path 属性,所以要获取 bundle.js 的正确 URL 是 http://localhost:8080/bundle.js,
修改Index.html
<script src="bundle.js"></script>
保存后你会发现浏览器会被自动刷新,运行出修改后的效果。
核心概念
Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
Plugin:扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。
Output:输出结果,在 Webpack 经过一系列处理并得出最终想要的代码后输出结果。
配置
配置 Webpack 的方式有两种:
通过一个 JavaScript 文件描述配置,例如使用 webpack.config.js 文件里的配置;
执行 Webpack 可执行文件时通过命令行参数传入,例如 webpack --devtool source-map。
Entry
是配置模块的入口,必填
类型可以是字符串、数组或者对象。
output
输出最终想要的代码
配置项名称 | 作用 |
---|---|
filename | string类型,配置输出文件的名称 |
path | 配置输出文件存放在本地的目录,必须是 string 类型的绝对路径 |
Module
想自定义解析和转换文件的策略,配置 module,通常是配置 module.rules 里的 Loader。
rules 配置模块的读取和解析规则,通常用来配置 Loader。其类型是一个数组,数组里每一项都描述了如何去处理部分文件。 配置一项 rules 时大致通过以下方式:
条件匹配:通过 test 、 include 、 exclude 三个配置项来命中 Loader 要应用规则的文件。
应用规则:对选中后的文件通过 use 配置项来应用 Loader,可以只应用一个 Loader 或者按照从后往前的顺序应用一组 Loader,同时还可以分别给 Loader 传入参数。
重置顺序:一组 Loader 的执行顺序默认是从右到左执行,通过 enforce 选项可以让其中一个 Loader 的执行顺序放到最前或者最后。
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'],
include: path.resolve(__dirname, 'src')
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
exclude: path.resolve(__dirname, 'node_modules')
}
]
}
在loader需要传入很多参数的时候,我们还可以通过一个object来描述,如:
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true
},
// enforce:'post' 的含义是把该 Loader 的执行顺序放到最后
// enforce 的值还可以是 pre,代表把 Loader 的执行顺序放到最前面
enforce:'post'
}
]
Plugin
Plugin 用于扩展 Webpack 功能,各种各样的 Plugin 几乎让 Webpack 可以做任何构建相关的事情。
Plugin 的配置很简单,plugins 配置项接受一个数组,数组里每一项都是一个要使用的 Plugin 的实例,Plugin 需要的参数通过构造函数传入。
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
module.exports = {
plugins: [
// 所有页面都会用到的公共代码提取到 common 代码块中
new CommonsChunkPlugin({
name: 'common',
chunks: ['a', 'b']
}),
]
};