本质上, webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。
当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个代码块(bundle),webpack默认只具备打包js的能力。
名词解释
bundle: 一个或多个编译后的代码块
entry: 入口,webpack编译的入口
output 出口,webpack编译的出口
loader 加载器,webpack默认只支持js的打包,如果需要处理其他类型文件,则需要引入相应的loader。
plugins 插件,可以看做是webpack能力的扩充,用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。
mode 模式,指定开发环境的模式,可以对特定环境的编译进行优化。
HMR 模块热替换(hot module replacement),无需浏览器刷新,即可局部更新代码以及样式。
安装
> npm init -y> npm install webpack webpack-cli
npm init -y npm初始化一个项目,创建package.json
npm install -D webpack webpack-cli 安装webpack开发依赖。
使用
初始项目结构如下:
build
webpack.config.js
src
module
a.js
index.js
package.json
创建webpack的配置文件
// build/webpack.config.jsconst path = require('path');module.exports = { mode: process.env.NODE_ENV, entry: '../src/index.js', output: { path: path.resolve(__dirname, '../dist'), filename: 'my-first-webpack.bundle.js' }};
mode 配置打包环境,取值
entry 打包的入口js文件,可以是多个,多个就填数组。
output 打包编译后出口
path 编译出口目录
filename 编译后js文件名
process.env.NODE_ENV 这个是node的环境变量,由运行的时候决定。
下面瞎写一个入口文件:
// src/index.jsimport say from './module/a' //随便引入个模块say.sayName();// src/module/a.jsexport default { name:'hzz', sayName(){ console.log(this.sayName); }}
然后再在package.json添加编译命令:
{ "name": "webpack-tutorial", "version": "1.0.0", "description": "webpack tutorial", "main": "index.js", "author": "terryvvan", "license": "MIT", "private": false, //重点 "scripts": { "start": "npm run dev", "dev": "set NODE_ENV=development&&webpack --config ./build/webpack.config.js", "build": "set NODE_ENV=production&&webpack --config ./build/webpack.config.js" }, "devDependencies": { "webpack": "^4.43.0", "webpack-cli": "^3.3.11" }}
set NODE_ENV=development 这句命令是windows环境下设置环境变量,目的是为了决定webpack的mode环境,以便其进行特定优化编译。
webpack --config 执行配置文件交给webpack编译。
最后执行命令:
> npm startwebpack-tutorial@1.0.0 start C:\Users\24244\Desktop\study\webpack-tutorialnpm run devwebpack-tutorial@1.0.0 dev C:\Users\24244\Desktop\study\webpack-tutorialset NODE_ENV=development&&webpack --config ./build/webpack.config.jsHash: 8e8662107e05e5c7ab43Version: webpack 4.43.0Time: 243msBuilt at: 2020-05-24 18:46:26 Asset Size Chunks Chunk Namesmain.js 19.5 KiB main [emitted] mainEntrypoint main = main.js[./build/webpack.config.js] 277 bytes {main} [built] + 2 hidden modules
至此完成最小化打包,我们继续完善这个例子,目前只能打包js,web环境离不开html和css,接下来加入html和css。
css及html支持
<html> <head> <meta charset="UTF-8"> <link rel="shortcut icon" type="images/x-icon" href="./favicon.ico"> <title>webpack Apptitle> head> <body> <div id="app"> <p>hello worldp> div> body>html>
新建public目录,存放html文件以及一些公共资源,这常在vue-cli和create-react-app的目录结构中看到,该目录下的文件不参与编译,仅仅只是拷贝到输出(output>path)目录。
然后在新增assets目录,存放css或图片等静态资源文件,该目录下的文件会参与编译。
#app{ position: absolute; left: 0; right: 0; bottom: 0; top: 0; background: white; display: flex; justify-content: center; align-items: center; flex-direction: column;}p{ color:blue;}
目前已建立的目录结构如下:
build
webpack.config.js
public
favicon.ico
index.html
src
assets
app.css
module
a.js
index.js
package.json
为了解析html和css,修改配置文件如下:
const HtmlWebpackPlugin = require('html-webpack-plugin'); //html生成const path = require('path');const CopyWebpackPlugin = require('copy-webpack-plugin'); //复制文件目录let env = process.env.NODE_ENV; //当前环境let isProd = env=='production';let rootPath = path.resolve(__dirname, '../'); //项目根路径let distPath = path.resolve(rootPath, 'dist'); //项目编译输出路径let srcPath = path.resolve(rootPath, 'src'); //源码路径module.exports = { mode: env, entry: path.join(srcPath,'index.js'), output: { path: distPath, filename: 'js/[name].js', // publicPath: './' }, resolve: { extensions: ['.js'], // 自动解析扩展。(import导入文件时可省略此处定义的扩展) alias: { '@': srcPath // 在项目中使用@符号代替src路径,导入文件路径更方便 } }, module: { rules: [ //配置loader处理相应文件 { test: /\.css$/, //正则匹配导入的css文件 use: [ 'style-loader', //将处理好css文件通过内联方式导入html 'css-loader' //处理css文件 ] } ] }, plugins:[ new HtmlWebpackPlugin({ //生成html minify:isProd, //是否压缩 hash:isProd, //是否为资源生成hash值,避免缓存 template:path.join(rootPath,'public/index.html') //模板路径,可以是ejs文件 }), new CopyWebpackPlugin({ //复制public目录的所有文件到dist目录 patterns:[ {from:path.resolve(rootPath,'public'),to:distPath} ], options:{} }), ]};
安装解析html和css所需依赖,并执行构建:
> npm install -D html-webpack-plugin css-loader copy-webpack-plugin> npm run start
此时,我们的项目已经可以编译html+css+js三大件了,但有一个缺陷,我们现在的项目每次修改只能手动进行编译。我们在使用vue或者react脚手架时,都会自动启用一个开发环境的服务器,便于预览,甚至可以在修改文件时,自动执行编译。
接下来我们加入webpack-dev-server插件:
> npm install -D webpack-dev-server1修改package.json的dev命令,改由webpack的开发服务器启动:"scripts": { "start": "npm run dev", "dev": "set NODE_ENV=development&&webpack-dev-server --config ./build/webpack.config.js" },
修改webpack的配置文件,以支持启动:
//略...module.exports = { //略... devServer: { historyApiFallback: true, //找不到地址回滚到index.html contentBase: './dist', //默认是项目根目录,服务器开启目录即index.html所在目录 // publicPath:'./dist', progress:true, //开启进度 compress: isProd, //开启gzip压缩 host:'localhost', //主机地址,填局域ip或者0.0.0.0可局域网内访问 hot:true, //开启热更新,即无需刷新即可更新改动 hotOnly:true, //只启动热更新,将不会自动刷新页面 port: 9000, //端口 open: !isProd, //是否自动打开浏览器 // https: true, //启用https服务器 overlay: true, //错误是否显示在页面上 stats:{ //一些状态的提示信息 warnings: true, errors: true, errorDetails: true, colors: true, performance: true, //当文件大小超过 `performance.maxAssetSize` 时显示性能提示 }, proxy: { //开启代理,不需要可以不配置 "/api": { // /api下请求将被代理 target: "http://localhost:3000", //要代理的域名 pathRewrite: {"^/api" : ""}, // secure: false //关闭安全验证,这样可以使用代理服务器不安全的https证书 } } },}
入口文件首行新增:
// src/index.jsif (module.hot) { //接受热更新,如果是vue单页环境不需要这个也可以 module.hot.accept();}//...省略无关代码
接下来启动项目:
>npm start> webpack-tutorial@1.0.0 start C:\Users\Administrator\Desktop\temp\webpack-tutorial> npm run dev> webpack-tutorial@1.0.0 dev C:\Users\Administrator\Desktop\temp\webpack-tutorial> set NODE_ENV=development&&webpack-dev-server --config ./build/webpack.config.js10% building 1/1 modules 0 activei 「wds」: Project is running at http://localhost:9000/i 「wds」: webpack output is served from /i 「wds」: Content not from webpack is served from ./dist11% building 13/14 modules 1 active C:\Users\Administrator\Desktop\temp\webpack-tutorial\node_modules\babel-loader\lib\index.js!C:\Users\Administrator\Desktop\temp\webpack-tutorial\src\index.jsi 「wdm」: wait until bundle finished: /i 「wdm」: Hash: 6c51adf39d371da68654Version: webpack 4.43.0Time: 3867msBuilt at: 2020-05-27 15:19:42 Asset Size Chunks Chunk Names favicon.ico 3.08 KiB [emitted] index.html 361 bytes [emitted] js/main.js 1.57 MiB main [emitted] mainjs/main.js.map 1.81 MiB main [emitted] [dev] mainEntrypoint main = js/main.js js/main.js.map[0] multi (webpack)-dev-server/client?http://localhost:9000 (webpack)/hot/only-dev-server.js ./src/index.js 52 bytes {main} [built][./node_modules/react-dom/index.js] 1.33 KiB {main} [built][./node_modules/react/index.js] 190 bytes {main} [built][./node_modules/vue/dist/vue.runtime.esm.js] 222 KiB {main} [built][./node_modules/webpack-dev-server/client/index.js?http://localhost:9000] (webpack)-dev-server/client?http://localhost:9000 4.29 KiB {main} [built][./node_modules/webpack-dev-server/client/overlay.js] (webpack)-dev-server/client/overlay.js 3.51 KiB {main} [built][./node_modules/webpack-dev-server/client/socket.js] (webpack)-dev-server/client/socket.js 1.53 KiB {main} [built][./node_modules/webpack-dev-server/client/utils/createSocketUrl.js] (webpack)-dev-server/client/utils/createSocketUrl.js 2.91 KiB {main} [built][./node_modules/webpack-dev-server/client/utils/log.js] (webpack)-dev-server/client/utils/log.js 964 bytes {main} [built][./node_modules/webpack-dev-server/client/utils/reloadApp.js] (webpack)-dev-server/client/utils/reloadApp.js 1.59 KiB {main} [built][./node_modules/webpack-dev-server/client/utils/sendMessage.js] (webpack)-dev-server/client/utils/sendMessage.js 402 bytes {main} [built][./node_modules/webpack-dev-server/node_modules/strip-ansi/index.js] (webpack)-dev-server/node_modules/strip-ansi/index.js 161 bytes {main} [built][./node_modules/webpack/hot sync ^\.\/log$] (webpack)/hot sync nonrecursive ^\.\/log$ 170 bytes {main} [built][./node_modules/webpack/hot/only-dev-server.js] (webpack)/hot/only-dev-server.js 2.52 KiB {main} [built][./src/index.js] 662 bytes {main} [built] + 53 hidden modulesChild HtmlWebpackCompiler: Asset Size Chunks Chunk Names __child-HtmlWebpackPlugin_0 4.6 KiB HtmlWebpackPlugin_0 HtmlWebpackPlugin_0 favicon.ico 3.08 KiB [emitted] index.html 327 bytes Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0 [./node_modules/html-webpack-plugin/lib/loader.js!./public/index.html] 619 bytes {HtmlWebpackPlugin_0} [built]i 「wdm」: Compiled successfully.
然后webpack会自动打开浏览器,浏览器的控制台会显示下面这句话:
[HMR] Waiting for update signal from WDS...
这个表示webpack-dev-server的热更新已开启,从此改动js,css就不需要重新编译,也不需要刷新浏览器,它会帮你在改动的时候自动更新。
vue支持
这样就够了嘛?不够,来加入vue,安装vue三大件依赖:
> npm install -D vue-style-loader vue-loader vue-template-compiler> npm install vue
vue-style-loader 处理vue组件的style标签
vue-loader 点击右侧传送门,即可前往文档=>传送门
vue-template-compiler 处理vue组件template标签
修改webpack配置以支持vue:
// webpack.config.jsconst VueLoaderPlugin = require('vue-loader/lib/plugin');//...省略无关代码module.exports = { //...省略无关代码 resolve: { extensions: ['.js','.vue'], // 解析扩展。 alias: { '@': srcPath // 在项目中使用@符号代替src路径,导入文件路径更方便 } }, module: { rules: [ { test: /\.vue$/, use:{ loader: 'vue-loader' } }, { test: /\.css$/, use: [ 'vue-style-loader', //style-loader替换成vue的 'css-loader' ] } ] }, plugins:[ //...省略无关代码 new VueLoaderPlugin(), ]}
现在增加一个vue的测试组件:
// src/components/HelloWorld.vue<template> <div class="root"> hello {{this.name}} div>template><script> export default { name:'HelloWorld', data(){ return { name:'vue' } } }script><style> .root{ color: aquamarine; }style>
入口文件引入vue组件:
// src/components/HelloWorld.vueimport say from '@/module/a'import '@/assets/app.css'import Vue from 'vue'import HelloVue from '@/components/helloWorld'say.sayName();console.log(1334);new Vue({ el: '#helloVue', render: h => h(HelloVue)})
至此vue系列加入完毕,运行即可看到效果:
> npm start
react支持
这就够了嘛,不!既然vue加了,作为国内同样流行的react理应也加入进来,我们先来安装依赖:
npm install -D babel-loader @babel/preset-env @babel/preset-react @babel/core @babel/plugin-proposal-class-properties @babel/plugin-proposal-decoratorsnpm install react react-dom
babel-loader 用来处理js
@babel/preset-env 用于支持es6的语法
@babel/preset-react 支持react,不用说
@babel/core 提供给其他插件转码使用
@babel/plugin-proposal-class-properties class支持
@babel/plugin-proposal-decorators 装饰器支持
还有一些其他的babel插件支持就不说了,给个=>传送门
https://www.babeljs.cn/docs/
接下来我们修改webpack的配置文件以支持react:
// webpack.config.js// ...省略无关代码module.exports = { resolve: { extensions: ['.js','.vue','.jsx'], // 解析扩展。 alias: { '@': srcPath // 在项目中使用@符号代替src路径,导入文件路径更方便 } }, module: { rules: [ { // 以 .js 或者 .jsx 结尾的文件, 使用 babel-loader test: /\.(js|jsx)$/, // 编译时, 不去node_modules 目录下找 exclude: /node_modules/, use: { loader: 'babel-loader', } } // ...省略无关代码 ] } // ...省略无关代码}
项目根目录创建babel的配置文件:
// babel.config.jsmodule.exports = { presets: ["@babel/preset-env", "@babel/preset-react"], plugins: [ ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties", { "loose": true }] ]};
我们来随便写个react组件:
// src/components/HelloReact.jsximport React from 'react'export default class HelloReact extends React.Component { render() { return ( <div> Hello {this.props.name} div> ); } }
入口文件引入react:
// src/index.jsif (module.hot) { module.hot.accept(); //接受热更新}import say from '@/module/a'import '@/assets/app.css'import Vue from 'vue'import React from 'react'import ReactDOM from 'react-dom'import HelloVue from '@/components/helloWorld'import HelloReact from '@/components/HelloReact'say.sayName();console.log(1334);ReactDOM.render( "Taylor" />, document.getElementById('helloReact'));new Vue({ el: '#helloVue', render: h => h(HelloVue)})
修改index.html:
<html> <head> <meta charset="UTF-8"> <link rel="shortcut icon" type="images/x-icon" href="./favicon.ico"> <title>webpack Apptitle> head> <body> <div id="app"> <p>hello worldp> <div id="helloReact">div> <div id="helloVue">div> div> body>html>
现在我们可以运行了:
npm start
大功告成!
以上示例代码地址:
https://github.com/terryvince/webpack-tutorial.git
如果对你有帮助记得点个star。
尾声
当然在实际工作中,还需添加wepack更多的loader以及plugin的支持才能满足需要,比如webpack的配置文件需要分开,抽离开发环境(webpack.dev.config.js),抽离生产环境(webpack.dev.config.js),抽离基础配置(webpack.base.config.js)。
比如sass支持,需要引入sass-loader,图片处理,需要引入file-loader等等,这些就留给大家去探索。如果实在有兴趣的可以安装笔者写的cli工具,来拉取webpack模板研究,这个webpack模板乃笔者在生产环境中得到检验,目前支持vue以及ts或者多页开发环境。下面给出获取方法:
方法一
通过cli拉取
npm install vvan-cli -gvvan init
vvan-cli 是笔者发布在npm的包,方便平时下载使用。
vvan init 如果不写名字,会通过问询让你确定项目的名字以及其他配置
项目生成好后,你可以在项目根目录的build目录找到所有的配置信息。
方法二
从github地址拉取:
git clone https://github.com/terryvince/webpack-build-template.git
更多高级教程请移步webpack中文网,有能力的可以去读英文官网 ?