登录
现代前端开发,都已经开始基于各种框架来开发,比如 vue、react 等等,即使不使用框架,我们写原始的 htm + css + js,也会经常想使用便捷的开发语法,比如使用 scss 或 less 开发css,使用 ES6+ 版本的 js 语法。
要知道,无论是框架的文件目录结构,还是 scss、less、ES6等,浏览器要么是无法支持,要么是有严重的兼容性问题,需要进一步处理后,生成常规的静态的原始的,且浏览器能兼容的 html + css + js。
这就需要一个用来编译的工具,webpack 就是当前最流行的前端编译工具,和他类似的工具还有 gulp、vite 等等,但 webpack 统治前端编译界时间最长,口碑也相当不错。
我现在就来整理一下 webpack 的一些基本使用。
webpack 基于 nodejs,使用是需要用 npm 安装。
这是 webpack 的本体,不安装就没有 webpack,无法使用。
依赖:
npm install -D webpack webpack-cli
再创建一个文件 webpack.config.js,作为 webpack 编译的配置文件,一下给了注释说明:
const path = require('path');
module.exports = {
// 编译的入口文件配置,会取这些文件来编译
entry: {
'main': './src/main.js',
'util': './src/util/index.js',
'component': './src/component/index.js',
},
// entry: './src/main.js', // 如果只用编译一个文件,可以直接写文件地址
// 出口文件配置,编译完后文件的去向
output: {
// 编译完成后放入这个文件夹
path: path.resolve(__dirname, 'dist'),
// 编译完成后,生成的文件名
// [name] 表示 entry 配置中,某个文件前面的 key
// [hash] 表示生成的一个随机 hash,可以省略
// 比如:
// entry 中配置了: 'myName': './src/main.js
// 那么最终生成的文件名,是: myName.8413e4f0204915bc9c29.js 这种形式的
filename: '[name].[hash].js',
},
};
安装完成后,在 package.json 写下 build 指令:
"scripts": {
"build": "webpack-cli --mode production --config ./webpack.config.js"
},
之后就可以使用 npm run build ,来使用 webpack 编译 js 了。
这一条编译指令中:
这个配置文件,因为没有任何有实际意义的配置,比如使用插件啦、使用loader啦,所以编译完成的文件,看上去内容和源文件可能不一样,但其实执行效果是一模一样的。
依赖:
指令指令安装依赖。
npm install -D babel-loader @babel/core @babel/preset-env core-js
修改 webpack.config.js,添加 babel 配置:
const path = require('path');
module.exports = {
entry: {
'main': './src/main.js',
'util': './src/util/index.js',
'component': './src/component/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash].js',
},
// 配置模块
module: {
rules: [ // 定义规则
{
test: /\.js$/, // 命中这条规则的文件们,是 js 格式的文件需要使用
include: [path.resolve(__dirname, 'src')], // 包含的文件,不设置就是所有用到的文件都编译
// exclude: /node_modules/, // 忽略的文件们,不写则无
loader: 'babel-loader', // 使用 loader
},
],
},
};
添加 .babelrc 文件:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage", // "usage": 按需引入
"bugfixes": true, // 一项优化,默认为false,Babel 8版本将默认为true
"corejs": 3 // 指定要使用的 core-js 版本,建议 3
}
]
]
}
开发时,不希望每次改了代码,都要重新打包,这个依赖可以启动一个 webpack 服务器,实时编译,保存编译更新。
依赖:
npm install -D webpack-dev-server
添加完成后,可以修改 package.json 中的指令了:
"scripts": {
"start": "npm run dev",
// 用于启动开发环境服务
"dev": "webpack serve --mode development --config ./webpack.config.js",
// 用于打包
"build": "webpack-cli --mode production --config ./webpack.config.js"
},
修改 webpack.config.js:
const path = require('path');
module.exports = {
entry: {
'main': './src/main.js',
'util': './src/util/index.js',
'component': './src/component/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
devServer: {
publicPath: '/dist/', // 这个服务的根路由
// open: true, // 自动打开浏览器
// openPage 配置自动打开时,打开的路由
// 但是,由于上面我们使用的 output 的 filename 是 [name].[hash].js,使用了 随机 hash
// 这里无法得知 hash 的值,无法准确的打开 main.js
// 这个功能其实主要是开发 html 时,自动打开的
// openPage: 'dist/main.js',
// host: '192.168.0.104', // 服务器监听的地址
port: 8098, // 设定服务的端口
hot: true, // 热替换
// proxy 代理
proxy: {
// 设置完此代理后,当我们访问 http://localhost:8098/a/main.js,会走一下代理,最终效果相当于访问 http://localhost:8098/dist/main.js
// 更多教程,搜索 webpack proxy 查询
'/a': { // 命中 /a 路由
target: 'http://localhost:8098', // 代理到这个域名下
logLevel: 'debug', // 每次代理,都打印代理日志
pathRewrite: { // 路由替换
'^/a': '/dist', // 把 /a 替换为 /dist
},
}
},
},
module: {
rules: [
{
test: /\.js$/,
include: [path.resolve(__dirname, 'src')],
loader: 'babel-loader',
},
],
},
};
而且,由于开发环境和正式打包时,配置可能不同,我们可以给测试环境和正式环境设置不同的 webpack 配置文件,来差异化处理。
const path = require('path');
module.exports = {
entry: {
'main': './src/main.js',
'util': './src/util/index.js',
'component': './src/component/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.js$/,
include: [path.resolve(__dirname, 'src')],
loader: 'babel-loader',
},
],
},
// 配置全局路径变量
resolve: {
extensions: ['.js'], // 引入可以不加后缀名
alias: {
'@': path.join(__dirname, './src'),
},
},
};
设置完 resolve 后,内部 js 引入文件时,就可以使用了,比如:
// src/view/home.js 中,一下两个引入 math 的路径写法是等效的
import math from '@/util/math';
import math from '../util/math';
有种情况,比如网站部署后,我们想知道这个网站被打包生成的具体时间,这就需要我们在打包时,把当前打包的时间提供给网站,供网站读取。
且无论何时打开网站,读取到的都是我们打包时塞入的那个时间。
此时就可以使用 webpack.DefinePlugin 这个插件配置一下了:
const path = require('path');
const webpack = require('webpack'); // 引入 webpack
module.exports = {
entry: {
'main': './src/main.js',
'util': './src/util/index.js',
'component': './src/component/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.js$/,
include: [path.resolve(__dirname, 'src')],
loader: 'babel-loader',
},
],
},
plugins: [
// 插入编译后代码中的全局变量
new webpack.DefinePlugin({
// 注意,这里的值都需要用 JSON.stringify() 转译一下,否则会报错
'NAME': JSON.stringify('我的项目'),
'BUILD_TIME': JSON.stringify(Date.now()),
'MY_INFO': JSON.stringify({age: 18}),
}),
],
};
然后,在项目中任何一个文件中,都可以读取这些常量:
// src/main.js 中
console.log(NAME) // 我的项目 String
console.log(BUILD_TIME) // 1616748873584 Number
console.log(MY_INFO.age) // 18 Number
使用webpack打包项目,当我们调试时,会发现在浏览器中看到的源码,是已经被 webpack 打包编译过的、一个文件只有一行的代码,根本不能清晰的调试。
这时就需要 source-map 登场了,它能在编译时,同时生成编译后和编译前的代码对应关系,当我们使用浏览器调试时,他们展示我们实际的代码。
需要使用 devtool 字段,默认情况无 source-map,可以给他赋值不同的值,来生成不同的 source-map,有的 source-map 编译快,有的体积小。。。值很多。
推荐开发时设置 eval-cheap-module-source-map,打包时设置值为 source-map。
const path = require('path');
module.exports = {
entry: {
'main': './src/main.js',
'util': './src/util/index.js',
'component': './src/component/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js', // 这里不能使用 hash 了,否则下面 openPage 无法正常执行
},
// 建议开发环境使用
// devtool: 'eval-cheap-module-source-map',
// 建议打包时使用
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
include: [path.resolve(__dirname, 'src')],
loader: 'babel-loader',
},
],
},
};