【webpack 系列】基础篇
source link: http://www.cnblogs.com/alsy/p/12594946.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Webpack 基础篇
基本概念
Webpack
是一个现代 JavaScript
应用程序的静态模块打包器。当 webpack
处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle
。
四个核心概念
- 入口(Entry): 构建
Webpack
内部依赖图的入口模块 - 输出(Output): 输出
Webpack
打包好的Bundles
- Loader: 加载器,
Webpack
原生只能解析JavaScript
文件,Loader
让webpack
拥有了加载和解析非JavaScript
文件的能力。 - 插件(Plugins): 扩展
Webpack
的功能,让Webpack
具有更多的灵活性。在Webpack
运行的生命周期中会广播出许多事件,Plugin
可以监听这些事件,在合适的时机通过Webpack
提供的API
改变输出结果。
Webpack 基础配置
初始化项目
新建一个文件夹 webpack-demo
,在该目录中使用 npm init -y
进行项目初始化。
mkdir webpack-demo && cd webpack-demo npm init -y
运行以下命令安装最新版本或特定版本
npm i -D webpack npm i -D webpack@<version>
如果你使用 webpack 4+
版本,你还需要安装 CLI
。
npm i -D webpack-cli
-
npm i -D
为npm install --save-dev
的缩写,安装一个用于开发环境的安装包 -
npm i -S
为npm install --save
的缩写,安装一个要打包到生产环境的安装包
现在安装的 webpack
版本号是:
├── [email protected] └── [email protected]
新建 src/index.js
文件:
// src/index.js class HelloComponent { constructor (content = '') { this.content = content; this.render(); } render () { const element = document.createElement('div'); element.innerHTML = this.content; document.body.appendChild(element); } } new HelloComponent('hello webpack');
现在可以直接执行 npx webpack
,默认是 production
模式。
也可以在 package.json
中的 scripts
里配置一个 build
命令,模式指定为 production
。
webpack
默认会将 ./src/index.js
作为入口文件,默认打包到 dist/main.js
。
// ... "scripts": { "build": "webpack --mode=production" } // ...
通过 npm run build
可以执行我们定义的命令,这是可以多了 dist/main.js
文件,这就是打包之后的 js
代码。
webpack 配置文件
上面例子中使用的是 webpack
的默认配置,下面我们来定义更加丰富的自定义配置。
根目录下新建 webpack.config.js
文件
const path = require('path'); module.exports = { mode: 'development', // 模式 entry: path.resolve(__dirname, 'src/index.js'), // 入口文件 output: { path: path.resolve(__dirname, 'dist'), // 输出目录 filename: 'bundle.js' // 输出文件名 } }
更改我们的 build
命令,指定 webpack
按照我们的配置文件来打包文件
"scripts": { "build": "webpack --config webpack.config.js" }
执行 npm run build
可以看到, dist
目录下新增了 bundle.js
文件。并且 bundle.js
是在开发模式下打包的,可以看到更多的信息。
html-webpack-plugin 插件
现在我们已经有了打包好的 js
文件了,需要添加个 html
文件来引入这个 js
文件在浏览器查看效果了。
在实际开发中,为了避免每次修改打包的 js
文件被浏览器缓存而看不到最新的代码,我们会给打包文件加上 hash
,相当于这个文件的版本号。这样每次修改后打包的 js
文件名都会不同,如果人工去修改 html
中的 js
文件名就太麻烦了,我们可以借助 html-webpack-plugin
插件来自动完成这些事情。
安装 html-webpack-plugin
npm i -D html-webpack-plugin
新建 public/index.html
文件,修改我们的 webpack.config.js
// webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { //... plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, 'public/index.html'), // 指定模板文件,不指定会生成默认的 index.html 文件 filename: 'index.html' // 打包后的文件名 }) ] }
执行 npm run build
可以看到 dist
目录下新增了 index.html
文件,并且自动将打包好的 bundle.js
文件通过 script
标签引入了。
webpack-dev-server 开发工具
现在可以通过浏览器直接打开 dist/index.html
查看了,但是这样每次改完代码我们都需要手动 npm run build
一下,这样太麻烦了。
我们可以借助 webpack-dev-server
来解决这个问题。 webpack-dev-server
会提供了一个简单的 web
服务器,并且能够实时重新加载。
安装 webpack-dev-server
npm i -D webpack-dev-server
修改 package.json 文件
// package.json "scripts": { "dev": "webpack-dev-server --config webpack.config.js", "build": "webpack --config webpack.config.js" }
npm run dev
之后,默认会在 localhost:8080
下建立服务,通过访问这个地址可以访问到 dist
目录下的文件。
可以在 webpack.config.js
对 devServer
进行配置
// webpack.config.js module.exports = { // ... devServer: { contentBase: path.join(__dirname, 'dist'), port: '9000', // 指定端口,默认是8080 compress: true // 是否启用 gzip 压缩 } //... }
关于 webpack-dev-server
更多的配置可以 点击查看 。
mode
我们在 package.json
定义了两条命令,但是 mode
都为 development
。我们可以通过设置 process.env.NODE_ENV
的值来区分开发还是生产环境。
我们需要安装一下 cross-env
, 来实现跨平台设置 NODE_ENV
npm i -D cross-env
// package.json "scripts": { "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js", "build": "cross-env NODE_ENV=production webpack --config webpack.config.js" }
修改 webpack.config.js
// webpack.config.js const isProduction = process.env.NODE_ENV == 'production'; module.exports = { mode: isProduction ? 'production' : 'development', // 模式 // ... }
设置 mode
的不同值可以启用相应模式下的 webpack
内置的优化
development
会将 process.env.NODE_ENV
的值设为 development
。启用 NamedChunksPlugin
和 NamedModulesPlugin
。
production
会将 process.env.NODE_ENV
的值设为 production
。启用 FlagDependencyUsagePlugin
, FlagIncludedChunksPlugin
, ModuleConcatenationPlugin
, NoEmitOnErrorsPlugin
, OccurrenceOrderPlugin
, SideEffectsFlagPlugin
和 UglifyJsPlugin
.
用 babel 向后兼容 js 语法
现在我们的代码虽然已经完成了打包,但是并没有被转义为低版本的代码。我们需要通过 Babel
来将 ECMAScript 2015+
版本的代码转换为向后兼容的 JavaScript
语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
安装 babel-loader
npm i -D babel-loader
此外我们还需要安装以下依赖
npm i -D @babel/core @babel/preset-env @babel/plugin-transform-runtime npm i -S @babel/runtime @babel/runtime-corejs3
在 webpack.config.js
配置 babel-loader
// webpack.config.js module.exports = { // ... module: { rules: [ { test: /\.jsx?$/, use: ['babel-loader'], exclude: /node_modules/ // 排除 node_modules 目录 } ] } // ... }
建议给 loader
指定 include
或是 exclude
,排除一些不需要编译的目录可以提高编译效率,比如 node_modules
目录。
有两种方式配置 babel
-
通过
.babelrc
文件配置根目录下新建一个
.babelrc
文件,配置如下:
// .babelrc { "presets": ["@babel/preset-env"], "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": 3 } ] ] }
- 在
webpack
中配置babel
// webpack.config.js module.exports = { module: { rules: [ { test: /\.jsx?$/, use: { loader: 'babel-loader', options: { presets: ["@babel/preset-env"], plugins: [ [ "@babel/plugin-transform-runtime", { "corejs": 3 } ] ] } }, exclude: /node_modules/ } ] } }
通过执行 npm run dev
, 我们查看 http://localhost:9000/bundle.js
发现已经是转义之后的低版本代码了。
使用 source map
当 webpack
打包源代码时,会很难追踪到错误和警告在源代码中的原始位置。为了更容易地追踪错误和警告, JavaScript
提供了 source map
功能,将编译后的代码映射回原始源代码。
在开发环境中,可以设置 devtool
值为 inline-source-map
,生产环境设置为 none
或者 source-map
。
// webpack.config.js const isProduction = process.env.NODE_ENV == 'production'; module.exports = { // ... devtool: isProduction ? 'source-map' : 'inline-source-map', }
使用 source-map
最终会单独打包出一个 .map
文件,我们可以根据报错信息和 map
文件定位到源代码。
但是一般不会直接将 .map
文件部署到 CDN
,而是将 .map
文件传到错误监控系统,以便我们可以解析到出错的源码位置。
处理样式文件
webpack
只能处理 js
文件,如果要处理 css
需要借助 loader
。
如果是 .css
,我们需要的 loader
有: style-loader
、 css-loader
,考虑到兼容性问题,还需要 postcss-loader
、 autoprefixer
如果是 .less
, 还需要 less-loader
、 less
如果是 .sass
的话,还需要 sass-loader
、 node-sass
安装相应的依赖
npm i -D style-loader css-loader postcss-loader autoprefixer less-loader less sass-loader node-sass
webpack.config.js
添加 css
、 less
、 sass loader
// webpack.config.js module.exports = { // .. module: { rules: [ // ... { test: /\.(c|le)ss$/, use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'], exclude: /node_modules/ // 排除 node_modules 目录 }, { test: /\.sass$/, use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'], exclude: /node_modules/ // 排除 node_modules 目录 } ] } }
根目录新建 postcss.config.js
// postcss.config.js module.exports = { plugins: [ // 兼容浏览器,添加前缀 require('autoprefixer')({ overrideBrowserslist: [ "Android 4.1", "iOS 7.1", "Chrome > 31", "ff > 31", "ie >= 8" // 'last 10 versions', // 所有主流浏览器最近10版本用 ], grid: true }) ] }
配置完成,新建几个文件测试一下
/* src/index.css */ div { width: 200px; height: 100px; display: flex; }
// src/index.less @color: yellow; body { background: @color; display: flex; }
// src/index.sass $text-color: orange; div color: $text-color; display: flex;
再在入口文件中引入三个文件
// src/index.css import './index.css'; import './index.less'; import './index.sass';
我们可以看到样式生效并且 flex
也自动加上浏览器前缀了。
需要注意的是 loader
的执行顺序是从右向左执行的,执行顺序为:
less-loader
/ sass-loader
=> postcss-loader
=> css-loader
=> style-loader
-
less-loader
处理编译.less
文件,将其转为css
-
sass-loader
处理编译.sass
文件,将其转为css
-
postcss-loader
和autoprefixer
,自动生成浏览器兼容性前缀 -
css-loader
处理css
中的@import
、url(...)
等语句 -
style-loader
动态创建style
标签,将css
插入到head
中
处理图片、字体等媒体文件
我们可以使用 url-loader
或者 file-loader
来处理本地的资源文件。
file-loader
就是将文件在进行一些处理后(主要是处理文件名和路径、解析文件 url
),将文件移动到输出的目录中,同时在 require
文件的地方会返回文件的绝对路径。
url-loader
一般与 file-loader
搭配使用,功能与 file-loader
类似,如果文件小于限制的大小,则会返回 base64
编码。
需要同时安装 file-loader
和 url-loader
npm i -D file-loader url-loader
配置 webpack.config.js
// webpack.config.js module.exports = { // .. module: { rules: [ // ... { test: /\.(jpe?g|png|gif|webp|svg|eot|ttf|woff|woff2)$/i, use: [ { loader: 'url-loader', options: { limit: 10240, // 10K 资源大小小于 10K 时,将资源转换为 base64,超过 10K,将图片拷贝到 dist 目录 name: '[name]_[hash:6].[ext]', // 设置文件名,默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名 outputPath: 'assets', // 输出目录 esModule: false // 表示是否使用es6模块的导出,默认是启用的 } } ], exclude: /node_modules/ } ] } }
我们修改 src/index.sass
文件
// src/index.sass $text-color: orange; div color: $text-color; display: flex; background: url('../images/author.jpg');
修改了配置文件,我们重新 npm run dev 一下,可以看到图片地址已经被替换了
npm run build
可以看到 dist/assets
有这个文件
注意此时如果需要在 html
文件中引用这个图片需要这样写
<body> <img src="<%= require('../images/author.jpg') %>"> </body>
最终打包之后的路径是
打包前清空 dist 目录
我们修改文件打包之后,生成的 hash
值和之前 dist
中的不一样,会导致 dist
下的文件越来越多,所以我们需要在打包前先清空 dist
目录。
安装 clean-webpack-plugin
npm i -D clean-webpack-plugin
// webpack.config.js const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { //... plugins: [ // ... new CleanWebpackPlugin() ] }
这样每次打包前就会自动清除 dist
目录下的文件了。
最后
通过上面的实践,我们对 webpack
的基础配置有了一个初步的了解。本文所有代码可以 查看github 。
后续将会继续推出 webpack
系列的其他内容哦~
喜欢本文的话点个赞吧~
更多精彩内容,欢迎关注微信公众号~
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK