原理
我们创建的前端项目会用到大都会用到vue或react这些前端框架,这些项目在运行是需要构建的,因为像es6,less和sass,模板语法,vue指令和jsx语法是无法在浏览器中直接执行的,需要构建这样的一个操作来保证项目运行,webpack就是这样一个构建的工具。
webpack是一个打包模块化JavaScript的工具,在webpack里一切文件皆模块,通过Loader转换文件,通过 Plugin 注入钩子,最后输出多个模块组合成的文件。webpack专注于构建模块化的项目。
上图为webpack在官网上的图片,一切文件:JavaScript、CSS、SCSS、图片、模板,在 Webpack 眼中都是一个个模块,这样的好处是能清晰的描述出各个模块之间的依赖关系,以方便 Webpack 对模块进行组合和打包。 经过 Webpack 的处理,最终会输出浏览器能使用的静态资源。
在项目中webpack做的事情就是:分析项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
webpack与node ,npm 的关系
webpack是npm生态中的一个模块,可以通过全局安装webpack来使用webpack对项目进行打包;webpack的运行依赖于node的环境,没有node是不能够打包的,但是webpack打包后的项目只是前端的静态资源和后台没有关系,也就是说不依赖于node,有后台能力的都可以部署项目;npm是于Node社区中产生的,是nodejs的官方包管理工具,当你下载安装好node的时候,npm cli 就自动安装好了。正是npm的包管理,使得项目可以模块化的开发,模块化开发大大提高了我们的开发效率,但是模块化开发的文件往往需要额外的处理才能让浏览器识别,手动处理非常繁琐,这就是webpack工具的意义。
目前最新的webpack版本是v4.43.0,安装最新版的webpack版本命令:
npm install --save-dev webpack
npm install 、npm install --save 和 npm install --save-dev 比较?
命令 | 描述 |
---|---|
npm install X | 模块安装到项目node_modules目录下;模块依赖不会写入devDependencies或dependencies 节点。如果删除node_modules目录,使用npm install 初始化项目时,运行 npm install 初始化项目时不会下载模块 |
– | – |
npm install --save X(等价于npm install -S X) | 模块安装到项目node_modules目录下;模块依赖被写进dependencies 节点;如果删除node_modules目录,使用npm install 初始化项目时,会将模块下载到项目目录(node_modules)下;运行npm install –production或者注明NODE_ENV变量值为production时,会自动下载模块到node_modules目录中;可以通过 require() 来引入本地安装的包。 |
– | – |
npm install --save-dev X(等价于npm install -D X) | 模块安装到项目node_modules目录下;模块依赖被写入devDependencies 节点。如果删除node_modules目录,使用npm install 初始化项目时,会将模块下载到项目目录(node_modules)下。运行npm install –production或者注明NODE_ENV变量值为production时,不会自动下载模块到node_modules目录中。可以通过 require() 来引入本地安装的包。 |
"dependencies":应用程序在生产环境中需要的包,线上环境需要用到的依赖
"devDependencies":应用程序开发测试需要的包,开发时依赖;//减少 node_modules目录的大小以及npm install花费的时间
webpack4.x和3.x比较大的区别:
1、Node环境的升级:不再支持node4.0的版本,最低支持6.11.5。
2、增加了mode的配置,mode:‘production’ | ‘development’ | ‘none’,mode配置当前的开发环境,内置了很多功能,相比之前的版本减少了很多专门的配置。
development:开发环境,就是我们写代码的环境。
production:生产环境,代码放到线上的环境。
对mode不同的配置会启用不同的插件,直观的区别是开发环境的代码不提供压缩,生产环境的代码提供压缩。
3、原生支持处理JSON文件,不需要json-loader
4、webpack4之前,使用extract-text-webpack-plugin插件将css从js文件中分离出来打包,webpack4官方推荐使用 MiniCssExtractPlugin。
5、内置插件uglifyjs-webpack-plugin压缩js
6、CommonsChunkPlugin废弃,代替的是optimization.splitChunks 和 optimization.runtimeChunk
CLI:Command Line Interface(命令行接口)
为了更合适且方便地使用配置,可以在webpack.config.js中对webpack进行配置,CLI 中传入的任何参数会在配置文件中映射为对应的参数。
不使用配置文件时
webpack [] -o
npm init
npm install --save-dev webpack webpack-cli//"webpack": "^4.43.0"
app/Greeter.js:定义一个返回包含问候信息的html元素的函数,并依据CommonJS规范导出这个函数为一个模块
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greet;
};
app/main.js:把Greeter模块返回的节点插入页面
const greeter = require('./Greeter.js');
document.querySelector("#root").appendChild(greeter());
public/index.html:最基础的html代码,它在这里目的在于引入打包后的js文件
Webpack Sample Project
执行:
node_modules/.bin/webpack app/main.js --output public/bundle.js //非全局安装时
使用webpack配置文件:配置文件也是简单的javascript模块,存放打包相关的信息。有了这个配置之后,再打包文件,只需在终端里运行webpack(非全局安装需使用node_modules/.bin/webpack)命令就可以了,这条命令会自动引用webpack.config.js文件中的配置选项。
module.exports = {
entry: __dirname + "/app/main.js",//唯一入口文件;“__dirname”是node.js中的一个全局变量,它指向当前执行脚本所在的目录。
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
}
}
module.exports = {
entry:'./src/index.js'
}
多入口:entry的值是一个对象
module.exports = {
entry:{
index:'./src/index.js',
search:'./src/search.js'
}
}
指定webpack如何将编译后的文件输出到磁盘。
单入口基本的配置是:
1、filename :输出文件的文件名
2、path:输出文件的绝对路径
output没有多入口的说法,不管是单个入口还是多个入口都只有一个output,通过占位符来保证文件名称的唯一。
举个:
const path = require('path')
module.exports = {
output:{
path:path.join(__dirname,'dist'),
filename:'[name].js'//占位符
}
}
Loader:
webpack开箱即用只支持js和json两种文件类型,当遇到其他格式的文件后,webpack并不知道如何去处理。此时,我们可以定义一种规则,告诉webpack当他遇到某种格式的文件后,去求助于相应的loader。通过使用不同的loader,webpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scss为css,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件,对React的开发而言,合适的Loader可以把React的中用到的JSX文件转换为JS文件。
loader本身是一个函数,接收源文件通过loader函数转换,返回下一步使用的结果。
Loader需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置,Loader的配置包括以下几方面:
1、test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
2、loader:loader的名称(必须)
3、include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
4、query:为loaders提供额外的设置选项(可选)
Plugins:
webpack之所以强大是因为它有丰富的插件系统,各种各样的插件几乎让 webpack 可以做任何构建相关的事情,任何loaders没有办法做的事情,都可以通过plugins来完成。
这些插件能够按照我们配置的方式工作,完全依赖于tapable模块,它将这些插件注册为一个个钩子函数,然后按照插件注册时告知的方式,在合适的时机安排它们运行,最终完成整个打包任务。
Mode:
是webpack4提出的新的概念,用来指定当前的构建环境是production、development还是none,设置mode的值可以去自动的触发webpack内置的函数,默认值是production。
Resolve
Webpack 在启动后会从配置的入口模块出发找出所有依赖的模块,Resolve 配置 Webpack 如何寻找模块所对应的文件。
1、alias配置项通过别名来把原导入路径映射成一个新的导入路径,import Button from 'components/button 导入时,实际上被 alias 等价替换成了 import Button from ‘./src/components/button’ 。
resolve:{
alias:{
components: './src/components/'
}
}
2、extensions在导入语句没带文件后缀时,Webpack 会自动带上后缀后去尝试访问文件是否存在。
extensions: ['.js', '.json']//默认情况
3、modules配置 Webpack 去哪些目录下寻找第三方模块,默认是只会去 node_modules 目录下寻找。有时你的项目里会有一些模块会大量被其它模块依赖和导入,由于其它模块的位置分布不定,针对不同的文件都要去计算被导入模块文件的相对路径, 这个路径有时候会很长,就像这样 import ‘…/…/…/components/button’ 这时你可以利用 modules 配置项优化,假如那些被大量导入的模块都在 ./src/components 目录下,把 modules 配置成
modules:['./src/components','node_modules']
externals: {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter'
}
1、解析ES6
Babel是一个JavaScript编译器,可以将ECMAScript 2015+ 版本的代码转换为向后兼容的JavaScript语法,核心功能位于称为babel-core的包里,使用babel-loader解析ES6,babel-loader依赖babel,使用babel要先配置babel的配置文件.babelrc。
安装
npm i @babel/core @babel/preset-env babel-loader -D
.babelrc配置(Babel的配置文件,存放在项目的根目录里,用来设置转码规则和插件,两个配置项presets和plugins,presets定义转码规则)
{
"presets": [
"@babel/preset-env"
]
}
webpack.config.js配置
module.exports = {
module:{
rules:[
{
test: /.js$/,//匹配js文件
use: 'babel-loader'//使用babel-loader解析
}
]
}
}
greet.js(ES6的语法)
export const greet = () => {
let add = (a,b)=>{
return a+b;
}
return add(5,5);
}
index.js(入口文件)
import {greet} from './greet'
document.write(greet());
Babel的babel-preset-react包可以转换React框架的JSX语法。
安装:npm i react react-dom @babel/preset-react -D
配置:
.babelrc文件的presets添加react的babel-preset-react包的配置"@babel/preset-react"
webpack.config.js
module.exports = {
module:{
rules:[
{
test: /.jsx$/,
use: 'babel-loader'
}
]
},
resolve: {
extensions: ['.js', '.jsx']
}
}
组件ReactDemo.jsx:
import React, { Component } from 'react';
class ReactDemo extends Component {
constructor(props) {
super(props);
this.state = {
list:['jsx语法','redux','react-router','reactHooks']
}
}
render() {
return (
React课程目录
{
this.state.list.map((item,index)=>{
return {item}
})
}
);
}
}
export default ReactDemo;
入口文件index.js
import React from 'react'
import {render} from 'react-dom'
import ReactDemo from './ReactDemo'
render( ,document.getElementById('root'))
3、CSS和Less
webpack解析css需要css-loader和style-loader,css-loader用于加载.css文件,转换成commonJS对象,然后style-loader将样式通过/style>标签插入到head中,然后样式才会在代码中显示出来。
安装npm i style-loader css-loader -D
配置,loader的调用是链式调用,执行顺序是从右到左,先写style-loader,再写css-loader实际执行的时候是css-loader先解析css,再将解析好的css传递给style-loader。
{
test: /.css$/,
use:[
'style-loader',
'css-loader'
]
}
Less
Less是CSS的预处理器,CSS预处理器为CSS增加一些编程的特性,无需考虑浏览器的兼容性问题,例如你可以在CSS中使用变量、简单的逻辑程序、函数等等在编程语言中的一些基本特性,可以让的CSS更加简洁、适应性更强、可读性更佳,更易于代码的维护等诸多好处。除了less之外,CSS的预处理器还有Sass(SCSS)、Stylus等。
webpack使用less-loader解析less,less-loader将less转换成css。
安装npm i less less-loader -D
配置,在配置css解析的基础上添加less-loader。
{
test: /.less$/,
use:[
'style-loader',
'css-loader',
'less-loader'
]
}
style-loader将样式通过