webpack 项目构建工具
webpack简介
是一个前端项目自动化构建的工具,基于node.js的开发的,可以完美的实现前端资源的合并,打包,压缩,混淆等诸多功能。
安装webpack
npm 是 node.js的包管理工具,安装node.js 后可以通过npm安装webpack。
npm install webpack -g 全局安装webpack
npm install webpack --save-dev
webpack的主要的功能
通常一个页面中有许许多多的资源需要被加载,包括js, css,img, 字体等等文件,,每链接一个外部资源,意味着浏览器就不得不在页面渲染的同时向服务器发送大量的二次请求取获取这些资源,这样浏览器会多次访问服务器,如果有一种方式将这些需要二次请求的文件都打包为一个文件,访问页面时一次性获取所有需要的资源,问题将会简单许多。webpack 正是基于这样的需求实现的。使用webapack,我们可以将这些需要二次请求的 js,css 等文件打包到一个 bundle.js(通常这样命名)的文件中。前端页面只需要发送一次请求,获取这个bundle.js即可获得所有的文件的内容,浏览器可以正常的解析执行相同的功能。
webpack 使用了插件的方式来扩展功能,我们可以添加对应的插件和loader实现css文件的解析,添加css预处理器使用less和scss,开发时的自动热更新,添加babel 以至于我们可以使用最新的ES6语法便捷开发等等功能。而大部分的插件和loader 包 webpack官方已经提供,通过简单的安装模块和配置即可使用。
同时webpack还考虑到了浏览器的兼容性,这也是前端比较麻烦的问题,我们完成代码的书写后,经过webpack对项目进行打包编译,会生成新的 js 文件,这个文件可以被大部分的流行的浏览器识别执行,如果我们编码的 JS 文件没有经过 webpack 编译打包,直接使用到页面上,浏览器是大概率无法识别这个JS 文件中的内容的,因为里面可能会使用ES6的语法规则,而部分浏览器浏览器不知道如何解析这个语法,会直接显示语法错误。所以我们需要使用 webpack 打包后的 bundle.js文件,浏览器才可以正常使用。在项目目录下,这个文件被打包后通常放在 dist/bundle.js位置。dist则是项目最终打包编译后的文件输出的位置目录。
新建项目
新建一个目录作为项目目录,然后在当前目录执行 npm init 初始化项目即可,初始化配置后可以得到一个 package.json的配置问价,内部有项目的一些描述信息。
{ "name": "project_name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }
scripts 中配置的是一个命令映射关系,上面的配置中,执行 npm run test 就等同于执行 echo \"Error: no test specified\" && exit 1 命令,这是我们快捷执行命令的方式。
使用webpack 打包
直接执行命令打包
当我们安装好webpack 之后,使用webpack将一个js文件打包,命令如下
webpack ./src/main.js ./dist/bundle.js 指定入口文件和输出的文件位置 执行该指令后,该目录中将会存在该./dist/bundle.js文件, 直接在html中引入该文件即可 <script src="./dist/boudle.js"> </script> // 页面中只引入这个文件即可
在开发时,一旦我们对src中的原文件进行修改,就需要手动重新进行编译打包,来得到一个新的bundle.js文件。可以使用热加载插件实现自动编译,后续介绍。
通过配置文件进行编译
每次完成代码后,都需要执行 webpack ./src/main.js ./dist/bundle.js
命令完成编译,我们可以使用配置文件,执行webpack命令时,webpack 会自动查找当前目录的 webpack.config.js配置文件完成上述的编译。所以我们在根目录下,创建一个文件名为 webpack.config.js 的文件。这是一个 JS 文件,内部使用的node.js的语法构建,下面是简单的配置。
const path = require('path'); // 导入path模块实现路径拼接功能
module.exports = {
entry: path.join(__dirname, '.src/main.js'), // 指定入口路径
output: {
path: path.join(__dirname, 'dist'), // 指定出口路径
filename: 'bundle.js',
},
}
配置好上述文件之后,只需要在命令行执行 webpack 既可以自动完成编译打包,输出bundle.js文件。我们可以在 package中添加脚本命令。之后可以使用 npm run build 进行打包
{ "name": "project_name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" // 添加该行, 注意json不能写注释 }, "author": "", "license": "ISC" }
webpack 通过--config 可以指定的本次打包的使用的配置文件,根据开发环境和生产环境的不同,可以有两份配置文件分别配置,然后再编译时,使用不同的配置文件即可。例如
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack --config webpack.dev.config.js", "build": "webpack --config webpack.prod.config.js" },
通过这样的方式,可以在 webpack.dev.config.js 和 webpack.prod.config.js 配置文件中分别配置,编译时使用不同的命令即可实现打包时配置分离。
自动打包工具 webpack-dev-server
该工具时为了在避免开发时每次手动编译,上面的两种编译方式,在开发时候,无论是使用配置文件或者直接执行webpack打包命令,都需要在命令行手动执行,而这个工具就是来解决这样的问题,使用该工具后,当我们保存js文件,该工具将会自动的编译打包,生成 bundle.js文件,同时还支持 配置为 热更新(当修改一小部分内容时,会在原来的bundle.js文件基础上通过类似打补丁的方式对原bundle.js文件进行更新,避免每次生成新的bundle.js文件, 浏览器也不会整个页面全部刷新。浏览器也只会加载新生成的补丁文件)
安装工具
npm i webpack-dev-server -D // -D 表示项目中安装,不是全局,全局使用 -g
使用
webpack-dev-server 命令和webpack命令是相同的,读取配置文件的内容,对指定的js文件进行编译。
但由于webpack-dev-server是项目本地安装而不是全局安装,所以无法直接执行 webpack-dev-server
命令,可以通过 npm 的package.json 提供的脚本执行。
package.json的内容:
{
"name": "项目名",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"test": "jest",
"start": "webpack-dev-server --open --port:3000 --contentBase src --hot", // 配置webpack-dev-server 执行命令
// --open:打开浏览器 --port:设置端口 --contentBase src: 该服务的主页路径 src文件下,--hot:热更新,修改时使用增加差量文件的方式
},
"repository": {},
"devDependencies": {
"webpack": "^2.4.1", // 安装webpack-dev-server 时,本地必须安装了webpack才可以,其依赖与本地的webpack
"webpack-dev-server": "^2.4.2"
},
}
这样,可以使用 npm run dev
间接执行 webpack-dev-server
命令
执行成功后,webpack-dev-server 会开启服务,地址为本地地址 + 8080端口,这个服务会监听我们对js文件的修改,当我们每次保存正在编码的文件,webpack-dev-server都会自动执行一次编译,编译的文件放在根目录的 bundle.js文件中,但这是一个内存映射过来的虚拟文件,磁盘中并没有该文件。我们访问 localhost:8080/bundle.js地址看到这个内存中的bundle.js文件,但是在真实的物理磁盘中没有。而在我们的index.html文件中,起初我们引入的bundle.js 的路径是 <script scr="./dist/boudle.js"> </script>
,这个路径是我们配置的最终打包的路径。而webpack-dev-server自动更新的不是这个boudle.js,而是根路径下的boundle.js(使用内存中的bundle.js,因为内存远比磁盘更快,所以并没有使用真实的磁盘)。所以 html 页面想要自动刷新,需要更改引入的boudle.js的地址,将其指向内存中这个bundle.js,当项目开发完成,才将其打包为一个保存在磁盘上的真实bundle,js文件用于部署。下面是更改index.html中的 src 路径
<script scr="./dist/boudle.js"> </script> // 原来执行 webpack打包的文件路径 <script scr="/boudle.js"> </script> // 每次保存进行编译的文件路径,这里需要更改,页面才能看到实时更新的效果
热更新
热更新只需要导入配置一个插件即可,这个插件在 webpack模块中。热更新的实现可以看 https://juejin.im/post/6844904008432222215
const path = require('path');
const webpack = require('webpack'); // 导入webpack包
module.exports = {
entry: path.join(__dirname, './src/index.js')
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/assets/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(), // new 一个热更新的对象
],
devServer: {
open:true,
compress: true,
port: 3000,
contentBase: "src"
hot: true,
}
};
提供html模板
通常我们需要指定一个html模板,并在模板中使用我们生成的js文件,指定 html 模板需要使用 html-webpack-plugin 插件,由webpack官方提供。
使用 npm install html-webpack-plugin -D 安装
安装后在webpack.config.js文件中配置。
const path = require('path');
const webpack = require('webpack'); // 导入webpack包
const htmlWebpackplugin = require("html-webpack-plugin") // 导入插件包
module.exports = {
entry: path.join(__dirname, './src/index.js')
output: {...},
plugins: [
new webpack.HotModuleReplacementPlugin(), // new 一个热更新的对象
new htmlWebpackplugin({ // new一个对象,并构造配置项目
template: path.join(__dirname, "./src/index.html"), // 加载该路径下磁盘上的html文件 作为模板。
filename: "index.html" // 加载到内存上后的整个html文件的名字,
})
],
devServer: {
open:true,
compress: true,
port: 3000,
contentBase: "src"
hot: true,
}
};
使用这个插件后,index.html中的内容会加载到内存,并且会在内存的 中的这个 index.html文件中,增加一个对bundle.js的引用,所以即使源文件中没有指定引用一个bundle.js文件,可以不在引用bundle.js了。
loader
css , less, scss-loader
webpack 默认无法解析css文件,默认只能加载JS。加载css文件需要安装两个loader,安装并配置loader 即可
less或 scss 各自有自己的loader,如果需要使用,同样需要安装并配置的自己的loader
如果样式表中使用了 url的方式引入了外部的资源,webpack 也是无法解析的,同样需要引入能够解析这种url 的loader,
安装loader
找到对应的loader,使用npm 或者 yarn 安装即可。
npm i style-loader css-loader -D // 安装两个loader,提供对css文件的解析支持
npm i less-loader less -D // 安装 less-loader 同时需要的 less包
npm i scss-loader node-scss // 安装 scss-loader 同时需要 node-scss包
npm i url-loader file-loader // 样式表中的 url会下载对应的文件,需要该url-loader才能处理,file-loader是url-loader的内部依赖, 不需要在配置文件中指定,但需要安装
配置webpack.config.js
安装成功后,可以在配置文件中配置来使用 loader
module.exports = { entry: output: {}, plugins: [ new webpack.HotModuleReplacementPlugin(), // new 一个热更新的对象 new htmlWebpackplugin({}) ], devServer: { open:true, port: 3000, }, module: { rules: [ { test: /\.js$/, // 包名的正则规则 exclude: /node_modules/, use: [ { loader: 'babel-loader' } ] }, { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader" }, ] }, { test: /\.less$/, use: [ { loader: "style-loader" }, { loader: "css-loader" }, { loader: "less-loader" } ] }, { test: /\.scss$/, use: [ { loader: "style-loader" }, { loader: "css-loader" }, { loader: "scss-loader" } ] }, { test: /\.(jpg|png|gif|bmp|jpeg)$/, // 处理图片的url use: [ { loader: "url-loader?limit=4096&name=[hash:8]-[name].[ext]" }, // 图片在4096字节大小范围内使用 base64保存,否则直接保存图片文件 // 指定这个被打包后的名字,8位哈希值-原名-原后缀名。默认情况会对每张图片进行哈希 ] }, { test: /\.(ttf|eot|svg|woff|woff2)$/, // 处理字体的url文件 use: [ { loader: "url-loader" }, ] }, ] }, };
同一个test 指定的规则下配置多个loader,需要注意配置的顺序,loader 使用的顺序是 从右 到左使用。配置了loader后,当webpack检测到js文件中使用 import 的方式导入了一个无法解析的文件类型,webpack将会在配置文件中寻找是否有该文件的 loader ,寻找loader时,使用被导入的文件名依次进行匹配 上述配置的test 指定的规则,如果匹配到则使用该匹配规则下对应的loader进行处理。
babel-loader
webpack 只能处理一部分ES6语法的代码,需要借助第三方的loader进行转换,例如 面向对象的class代码。
使用babel-loader 需要装两套包
npm i babel-core babel-loader babel-plugin-trabsform-runtime -D // 对应的babel的转换处理包,用于转换
npm i babel-perset-env babel-perset-stage-0 -D // 对应的语法映射环境,储存了高级语法到低级语法的转换关系库
更改配置文件
添加babel-loader,注意需要配置排除包中的js文件。
module.exports = { entry: output: {}, plugins: [ new webpack.HotModuleReplacementPlugin(), // new 一个热更新的对象 new htmlWebpackplugin({}) ], devServer: { open:true, port: 3000, }, module: { rules: [ { test: /\.js$/, // 包名的正则规则 exclude: /node_modules/, // 排除库文件中的内容 use: [ { loader: 'babel-loader' } ] }, { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader" }, ] }, { test: /\.less$/, use: [ { loader: "style-loader" }, { loader: "css-loader" }, { loader: "less-loader" } ] }, { test: /\.scss$/, use: [ { loader: "style-loader" }, { loader: "css-loader" }, { loader: "scss-loader" } ] }, { test: /\.(jpg|png|gif|bmp|jpeg)$/, // 处理图片的url use: [ { loader: "url-loader?limit=4096&name=[hash:8]-[name].[ext]" }, // 图片在4096字节大小范围内使用 base64保存,否则直接保存图片文件 // 指定这个被打包后的名字,8位哈希值-原名-原后缀名。默认情况会对每张图片进行哈希 ] }, { test: /\.(ttf|eot|svg|woff|woff2)$/, // 处理字体的url文件 use: [ { loader: "url-loader" }, ] }, ] }, };
babel还需要自己的配置文件,在根目录下创建. babelrc
文件,需要使用json的格式进行配置。配置babel使用的插件和语法环境,也就是上面安装的包
// .babelrc
{ "presets": [ "env", "stage-0" ], // 语法环境包 "plugins": [ "transform-runtime", ] // 转义工具包 }