webpack入门
webpack是什么
一个现代JavaScript应用程序的静态模块打包器
默认:只对js进行处理,其他类型文件需要配置loader或者插件进行处理。
打包:将各个依赖文件进行梳理打包,形成一个JS依赖文件。

webpack产生的背景
为什么打包?
因为:
各个依赖文件的关系难以梳理,耦合程度较高,代码难以维护。
把所有依赖包都打包成为一个js文件(bundle.js)文件,会有效降低文件请求次数,一定程度提升性能。
逻辑多、文件多,项目复杂度提高
为什么要用webpack?
因为:
webpack除提供上述功能外,还充当了“翻译官”的角色,例如将ES6翻译为低版本的语法,将less、sass翻译为css等功能。
强大而灵活。
强大的插件(plugin)功能,webpack 自身也是构建于你在 webpack 配置中用到的相同的插件系统之上!
理解前端模块化
类似于一个公司由各个部门组成,一个工程也由各个模块组成,高内聚低耦合,各司其职。
作用域
定义:运行时变量、函数、对象可访问性
作用域决定了代码中变量和其他资源的可见性
全局作用域:
var a = 1;
window.a; // 1
global.a; // 1
局部作用域:
function a(){
var v = 1;
}
window.v; // undefined
如果在传统js写法中,引入多个script,就很容易造成全局作用域冲突而导致不可预测的问题:
<body>
<script scr="./moduleA.js"></sciprt>
<script scr="./moduleB.js"></sciprt>
<script scr="./moduleC.js"></sciprt>
</body>
改进步骤一,使用变量作用域形成局部作用域:
// 定义模块内的局部作用域,以moduleA为例
var Susan = {
name: "susan",
sex: "female",
tell: function(){
console.log("im susan")
}
}
但是步骤一无法保证模块属性内部安全性,比如可能不小心改掉属性值,可以通过立即执行函数进行改写,形成闭包。
那么可以进行改进步骤二:
ps:
什么是立即执行函数?可以点击这里进行参考。
什么是自由变量?简单来说是跨作用域的变量,可以点击这里进行参考。(里面有一个句很好的知识点:创建这个函数的时候,这个函数的作用域就已经决定了,而是不是在调用的时候)
// 定义模块内的闭包作用域(模块作用域),以moduleA为例
var SusanModule = (function(){
var Susan = {
// 自由变量
name: "susan",
// 自由变量
sex: "female",
// 只允许访问tell方法,不能访问和修改其他属性
return {
tell: function(){
console.log("im susan")
}
}
})()
对于步骤二还有一种写法,推荐使用这种写法,也是早期模块实现的方法:
// 定义模块内的闭包作用域(模块作用域),以moduleA为例
(function(window){
var name = "susan"
var sex = "female"
functioon tell(){
console.log("im ", this.name)
}
window.susanModule = {tell}
})(window)// window作为参数传给
调用验证:
window.susanModule.tell(); //im susan
模块化的优点
- 模块化的封装
- 重用性
- 解除耦合
模块化方案进化史
随着模块化优势体现,开发者更倾向于使用模块化协同开发项目,于是在发展过程中形成了很多规范:AMD、COMMONJS、ES6 MODULE
AMD
Asynchronous Module Definition(异步模块定义)
目前很少使用
// 求和模块
define("getSum", ["math"], funtion(math){
return function (a,b){
log("sum:"+ math.sum(a, b))
}
})
COMMONJS
2009年出的规范,原本是为服务端的规范,后来nodejs采用commonjs模块化规范
// 通过require函数来引用
const math = require("./math");
// 通过exports将其导出
exports.getSum = function(a,b){
return a + b;
}
ES6 MODULE
目前使用最多的便是这个
// 通过import函数来引用
import math from "./math";
// 通过export将其导出
export function sum(a, b){
return a + b;
}
webpack的打包机制
根据import引入等关键字,将依赖文件打包成一个文件。
输出文件
输出文件的大体结构:
(function(module) {
var installedModules = {};
function __webpack_require__(moduleId){
// SOME CODE
}
// 。。。
return __webpack_require__(0); // entry file
})([ /* modules array */])
上述结构中的核心方法:
function __webpack_require__(moduleId){
// check if module is in cache
if(installedModules[moduleId]){
return installedModules[moduleId].exports;
}
// create a new module (and put into cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// exe the module func
modules[moduleId].call{
module.exports,
module,
module.exports,
__webpack_require__
};
// flag the module as loaded
module.l = true;
// return the exxports of the module
return module.exports;
}
webpack打包过程
- 从入口文件开始,分析整个应用的依赖树
- 将每个依赖模块包装起来,放到一个数组中等待调用
- 实现模块加载的方法,并把它放到模块执行的环境中,确保模块间可以互相调用
- 把执行入口文件的逻辑放在一个函数表达式中,并立即执行这个函数
npm install过程
-
寻找报版本信息文件(package.json),依照它来进行安装
-
查找package.json中的依赖,并检查项目中其他的版本信息文件
-
如果发现了新包,就更新版本信息文件
webpack实战
基础准备
选择合适的目录,执行npm init -y 生成 package.json 文件
npm init -y
建议本地安装,安装依赖文件(更容易分别升级项目):
yarn add webpack webpack-cli -D
手动打包一个文件
创建文件 src/main.js
class Student{
getName(){
return 'xiaoming'
}
}
手动单文件打包方式:
webpack ./src/main.js --mode development
loader
翻译官
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
详细参见:https://www.webpackjs.com/concepts/#loader
plugins
loader的进一步扩展,可以执行的任务更为广泛
详细参见:https://www.webpackjs.com/concepts/#%E6%8F%92%E4%BB%B6-plugins-
scss支持
配置webpack.config.js
安装
yarn add css-loader style-loader sass-loader node-sass -D
配置文件修改如下:
module:{
rules: [{
test: /\.scss$/,
use: [{
loader: "style-loader" // 将 JS 字符串生成为 style 节点
}, {
loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
}, {
loader: "sass-loader" // 将 Sass 编译成 CSS
}]
}]
}
html-webpack-plugin
yarn add html-webpack-plugin -D
上面的配置,会把css以内联式的方式插入到head里面,如果需要提取样式,可以用 mini-css-extract-plugin
yarn add mini-css-extract-plugin -D
修改配置,如下:
plugins: [
...
new miniExtractCss({
filename:'common-[hash].css'
})
],
module: {
rules: [{
test: /\.scss$/,
use: [
// {
// loader: "style-loader" // 将 JS 字符串生成为 style 节点
// },
{
loader: miniExtractCss.loader
},
{
loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
}, {
loader: "sass-loader" // 将 Sass 编译成 CSS
}]
}]
}
热更新支持
需要安装webpack-dev-server
yarn add webpack-dev-server -D
修改 webpack.config.js配置
//参考:https://webpack.docschina.org/configuration/dev-server/
devServer: {
//打开浏览器
open: true
},
修改 package.json
"scripts": {
...
"dev": "webpack-dev-server --config ./webpack.config.js"
},
图片转 base64
yarn add url-loader file-loader -D
module: {
rules: [
...
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 81920
}
}
]
}
...
]
完整的webpack.config.js
webpack.config.js 配置
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
//开发模式:development,代码调试, 生产模式:production ,代码压缩,优化
//如果mode不配置,会有警告提示信息
mode: "development",
//代码调试,可以看到源代码
devtool: 'inline-source-map',
//入口文件,如果不配置,默然找根目录下 /src/index.js
// entry:"./src/js/index.js",
entry: {
'main': "./src/js/index.js",
'detail': "./src/js/detai.js"
},
//打包完成,输出文件的位置(不能写相对路径),可以不配置,默认输出到dist目录
output: {
path: path.resolve(__dirname, './dev'),
filename: '[name]-[hash].js'
},
devServer: {
port: 9099,
//打开浏览器
open: true
},
plugins: [
new htmlWebpackPlugin({
title: 'webpack demo home',
//模板文件路径
template: "./src/views/index.html",
//自动存储到output 配置的目录
filename: 'index.html',
chunks: ['main']
}),
new htmlWebpackPlugin({
title: 'webpack demo home',
//模板文件路径
template: "./src/views/detail.html",
//自动存储到output 配置的目录
filename: 'detail.html',
chunks: ['detail']
}),
new MiniCssExtractPlugin({
//页面引用的样式文件名
filename: '[name]-[hash].css',
// chunkFilename: 'common.css',
})
],
module: {
rules: [
{
test: /\.s[ac]ss$/,
//执行顺序:从右向左
use: [
// 将 JS 字符串生成为 style 节点
'style-loader',
// MiniCssExtractPlugin.loader,
//将 CSS 转化成 CommonJS 模块
'css-loader',
//把.scss文件文件转换为.css文件
'sass-loader'
]
}
]
}
}
babel配置
为了支持低版本的浏览器需要加入babel相关的配置:
yarn add babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/runtime -D
module:{
rules:[
...
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime']
}
}
}
...
]
}
@babel/plugin-transform-runtime
运行时引入 generators/async、babel-runtime/core-js(ES6->includes....)不会污染全局环境。
@babel/preset-env
转化最新语法如箭头函数, class, 扩展运算符,想要转换最新的api还需引入babel-polyfill(eg: includes)
@babel/polyfill
一些新的api:Iterator、Generator、Set、Map、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。
ES6 在Array对象上新增了Array.from方法。
@babel-core
babel-core 的作用是把 js 代码分析成 ast ,方便各个插件分析语法进行相应的处理。
参考:
https://webpack.docschina.org/loaders/babel-loader/
https://www.babeljs.cn
浙公网安备 33010602011771号